Finished refactoring code

This commit is contained in:
Vasilis Valatsos 2023-10-04 04:37:28 +02:00
parent cd0333cbff
commit b0a925292e
25 changed files with 429 additions and 194 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/audio/death.wav Normal file

Binary file not shown.

BIN
assets/audio/flame.wav Normal file

Binary file not shown.

BIN
assets/audio/heal.wav Normal file

Binary file not shown.

BIN
assets/audio/hit.wav Normal file

Binary file not shown.

BIN
assets/audio/main.ogg Normal file

Binary file not shown.

BIN
assets/audio/sword.wav Normal file

Binary file not shown.

View file

@ -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 = { 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}, '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,'damage':40,'attack_type': 'claw', 'attack_sound':'../Graphics/audio/attack/claw.wav','speed': 2, 'resistance': 3, 'attack_radius': 120, 'notice_radius': 400}, '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,'damage':8,'attack_type': 'thunder', 'attack_sound':'../Graphics/audio/attack/fireball.wav', 'speed': 4, 'resistance': 3, 'attack_radius': 60, 'notice_radius': 350}, '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,'damage':6,'attack_type': 'leaf_attack', 'attack_sound':'../Graphics/audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 50, 'notice_radius': 300}} '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}}

View file

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

View file

@ -13,37 +13,3 @@ HITBOX_OFFSET = {
# general colors # general colors
WATER_COLOR = '#71ddee' 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}}

View file

@ -1,24 +1,26 @@
import os
import pygame import pygame
from random import randint from random import randint
from configs.system.window_config import TILESIZE from configs.system.window_config import TILESIZE
from configs.game.spell_config import *
class MagicPlayer: class MagicPlayer:
def __init__(self, animation_player): def __init__(self, animation_player):
self.animation_player = 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 # Sound Setup
# self.sounds = { self.sounds = {
# 'heal': pygame.mixer.Sound('../Graphics/audio/heal.wav'), 'heal': pygame.mixer.Sound(f'{asset_path}/audio/heal.wav'),
# 'flame': pygame.mixer.Sound('../Graphics/audio/flame.wav') 'flame': pygame.mixer.Sound(f'{asset_path}/audio/flame.wav')
# } }
def heal(self, player, strength, cost, groups): def heal(self, player, strength, cost, groups):
if player.stats.energy >= cost: if player.stats.energy >= cost:
# self.sounds['heal'].play() self.sounds['heal'].play()
player.stats.health += strength player.stats.health += strength
player.stats.energy -= cost player.stats.energy -= cost
if player.stats.health >= player.stats.stats['health']: if player.stats.health >= player.stats.stats['health']:
@ -31,13 +33,13 @@ class MagicPlayer:
def flame(self, player, cost, groups): def flame(self, player, cost, groups):
if player.stats.energy >= cost: if player.stats.energy >= cost:
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) 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) 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) direction = pygame.math.Vector2(0, -1)
else: else:
direction = pygame.math.Vector2(0, 1) direction = pygame.math.Vector2(0, 1)

View file

@ -12,7 +12,7 @@ class Weapon(pygame.sprite.Sprite):
script_dir, '../..', 'assets') script_dir, '../..', 'assets')
self.sprite_type = 'weapon' self.sprite_type = 'weapon'
direction = player.status.split('_')[0] direction = player._input.status.split('_')[0]
# Graphic # Graphic
full_path = f"{asset_path}/graphics/weapons/{player._input.combat.weapon}/{direction}.png" full_path = f"{asset_path}/graphics/weapons/{player._input.combat.weapon}/{direction}.png"

View file

@ -10,21 +10,22 @@ from .combat import CombatHandler
class InputHandler: class InputHandler:
def __init__(self, sprite_type, status): # , status):
self.status = status def __init__(self, sprite_type, animation_player):
self.status = 'down'
self.sprite_type = sprite_type
# Setup Movement # Setup Movement
self.movement = MovementHandler(sprite_type) self.movement = MovementHandler(self.sprite_type)
self.move_cooldown = 15 self.move_cooldown = 15
self.can_move = True self.can_move = True
self.move_time = None self.move_time = None
# Setup Combat # Setup Combat
self.combat = CombatHandler() self.combat = CombatHandler(animation_player)
self.attacking = False self.attacking = False
self.attack_cooldown = 400 self.attack_cooldown = 400
self.attack_time = None self.attack_time = None
self.can_move = True
# Setup Special Actions # Setup Special Actions
self.can_rotate_weapon = True self.can_rotate_weapon = True
@ -37,28 +38,30 @@ class InputHandler:
def check_input(self, speed, hitbox, obstacle_sprites, rect, player): def check_input(self, speed, hitbox, obstacle_sprites, rect, player):
if not self.attacking and self.can_move: if not self.attacking and self.can_move:
keys = pygame.key.get_pressed() keys = pygame.key.get_pressed()
button = randint(0, 7)
# button = randint(0, 5)
self.move_time = pygame.time.get_ticks() self.move_time = pygame.time.get_ticks()
# Movement Input # 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.movement.direction.y = -1
self.status = 'up' self.status = 'up'
self.can_move = False 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.movement.direction.y = 1
self.status = 'down' self.status = 'down'
self.can_move = False self.can_move = False
else: else:
self.movement.direction.y = 0 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.movement.direction.x = -1
self.status = 'left' self.status = 'left'
self.can_move = False 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.movement.direction.x = 1
self.status = 'right' self.status = 'right'
self.can_move = False self.can_move = False
@ -67,15 +70,16 @@ class InputHandler:
self.movement.move(speed, hitbox, obstacle_sprites, rect) self.movement.move(speed, hitbox, obstacle_sprites, rect)
if self.sprite_type == 'player':
# Combat Input # Combat Input
if button == 4 and not self.attacking: # keys[pygame.K_e] if keys[pygame.K_e] and not self.attacking: # keys[pygame.K_e]
self.attacking = True self.attacking = True
self.attack_time = pygame.time.get_ticks() self.attack_time = pygame.time.get_ticks()
self.combat.create_attack_sprite(player) self.combat.create_attack_sprite(player)
# self.weapon_attack_sound.play() self.combat.weapon_attack_sound.play()
# Magic Input # Magic Input
if button == 5: # keys[pygame.K_q]: if keys[pygame.K_q]: # keys[pygame.K_q]:
self.attacking = True self.attacking = True
self.attack_time = pygame.time.get_ticks() self.attack_time = pygame.time.get_ticks()
@ -87,12 +91,12 @@ class InputHandler:
cost = list(magic_data.values())[ cost = list(magic_data.values())[
self.combat.magic_index]['cost'] self.combat.magic_index]['cost']
print(self.combat.magic)
self.combat.create_magic_sprite( self.combat.create_magic_sprite(
player, self.combat.magic, strength, cost) player, self.combat.magic, strength, cost)
# Rotating Weapons # Rotating Weapons
if button == 6 and self.can_rotate_weapon: # keys[pygame.K_LSHIFT] # keys[pygame.K_LSHIFT]
if keys[pygame.K_LSHIFT] and self.can_rotate_weapon:
self.can_rotate_weapon = False self.can_rotate_weapon = False
self.weapon_rotation_time = pygame.time.get_ticks() self.weapon_rotation_time = pygame.time.get_ticks()
if self.combat.weapon_index < len(list(weapon_data.keys())) - 1: if self.combat.weapon_index < len(list(weapon_data.keys())) - 1:
@ -104,7 +108,8 @@ class InputHandler:
self.combat.weapon_index] self.combat.weapon_index]
# Swap Spells # Swap Spells
if button == 7 and self.can_swap_magic: # keys[pygame.K_LCTRL] : # keys[pygame.K_LCTRL] :
if keys[pygame.K_LCTRL] and self.can_swap_magic:
self.can_swap_magic = False self.can_swap_magic = False
self.magic_swap_time = pygame.time.get_ticks() self.magic_swap_time = pygame.time.get_ticks()
if self.combat.magic_index < len(list(magic_data.keys())) - 1: if self.combat.magic_index < len(list(magic_data.keys())) - 1:
@ -114,6 +119,7 @@ class InputHandler:
def cooldowns(self, vulnerable): def cooldowns(self, vulnerable):
current_time = pygame.time.get_ticks() current_time = pygame.time.get_ticks()
self.vulnerable = vulnerable
if self.attacking: if self.attacking:
if current_time - self.attack_time > self.attack_cooldown + weapon_data[self.combat.weapon]['cooldown']: 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 self.can_swap_magic = True
if not vulnerable: if not vulnerable:
if current_time - self.hurt_time >= self.invulnerability_duration: if current_time - self.combat.hurt_time >= self.combat.invulnerability_duration:
vulnerable = True self.combat.vulnerable = True
if not self.can_move: if not self.can_move:
if current_time - self.move_time >= self.move_cooldown: if current_time - self.move_time >= self.move_cooldown:

View file

@ -3,20 +3,25 @@ import pygame
from math import sin from math import sin
from utils.resource_loader import import_folder from utils.resource_loader import import_folder
from configs.system.window_config import HITBOX_OFFSET from configs.system.window_config import HITBOX_OFFSET
class AnimationHandler: class AnimationHandler:
def __init__(self): def __init__(self, sprite_type, name=None):
self.frame_index = 0 self.frame_index = 0
self.animation_speed = 0.15 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__)) script_dir = os.path.dirname(os.path.abspath(__file__))
asset_path = os.path.join( asset_path = os.path.join(
script_dir, '../../..', 'assets', 'graphics') script_dir, '../../..', 'assets', 'graphics')
if sprite_type == 'player':
if self.sprite_type == 'player':
character_path = f"{asset_path}/player" character_path = f"{asset_path}/player"
@ -24,7 +29,8 @@ class AnimationHandler:
self.image = pygame.image.load( self.image = pygame.image.load(
f"{character_path}/down/down_0.png").convert_alpha() f"{character_path}/down/down_0.png").convert_alpha()
self.rect = self.image.get_rect(topleft=position) 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 = { self.animations = {
'up': [], 'down': [], 'left': [], 'right': [], 'up': [], 'down': [], 'left': [], 'right': [],
'up_idle': [], 'down_idle': [], 'left_idle': [], 'right_idle': [], 'up_idle': [], 'down_idle': [], 'left_idle': [], 'right_idle': [],
@ -34,21 +40,30 @@ class AnimationHandler:
full_path = f"{character_path}/{animation}" full_path = f"{character_path}/{animation}"
self.animations[animation] = import_folder(full_path) 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': []} self.animations = {'idle': [], 'move': [], 'attack': []}
main_path = f"{asset_path}/monsters/{name}"
for animation in self.animations.keys(): for animation in self.animations.keys():
self.animations[animation] = import_folder( 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] animation = self.animations[status]
self.frame_index += self.animation_speed self.frame_index += self.animation_speed
if self.frame_index >= len(animation): if self.frame_index >= len(animation):
if self.sprite_type == 'enemy':
if status == 'attack':
self.can_attack = False
self.frame_index = 0 self.frame_index = 0
# Set the image # Set the image
@ -61,6 +76,10 @@ class AnimationHandler:
else: else:
self.image.set_alpha(255) 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): def wave_value(self):
value = sin(pygame.time.get_ticks()) value = sin(pygame.time.get_ticks())
if value >= 0: if value >= 0:

View file

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

View file

@ -3,7 +3,6 @@ import pygame
from effects.weapon_effects import Weapon from effects.weapon_effects import Weapon
from effects.magic_effects import MagicPlayer from effects.magic_effects import MagicPlayer
from effects.particle_effects import AnimationPlayer
from configs.game.weapon_config import weapon_data from configs.game.weapon_config import weapon_data
from configs.game.spell_config import magic_data from configs.game.spell_config import magic_data
@ -11,11 +10,12 @@ from configs.game.spell_config import magic_data
class CombatHandler: class CombatHandler:
def __init__(self): def __init__(self, animation_player):
self.animation_player = animation_player
# Setup Combat # Setup Combat
self.animation_player = AnimationPlayer() self.magic_player = MagicPlayer(animation_player)
self.magic_player = MagicPlayer(self.animation_player)
self.current_attack = None self.current_attack = None
# Spell and Weapon Rotation # Spell and Weapon Rotation
@ -31,13 +31,13 @@ class CombatHandler:
self.invulnerability_duration = 300 self.invulnerability_duration = 300
# Import Sounds # Import Sounds
# script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
# asset_path = os.path.join( asset_path = os.path.join(
# script_dir, '../../..', 'assets', 'audio') script_dir, '../../..', 'assets', 'audio')
#
# self.weapon_attack_sound = pygame.mixer.Sound( self.weapon_attack_sound = pygame.mixer.Sound(
# f"{asset_path}/sword.wav") f"{asset_path}/sword.wav")
# self.weapon_attack_sound.set_volume(0.2) self.weapon_attack_sound.set_volume(0.2)
def create_attack_sprite(self, player): def create_attack_sprite(self, player):
self.current_attack = Weapon( self.current_attack = Weapon(
@ -49,7 +49,6 @@ class CombatHandler:
self.current_attack = None self.current_attack = None
def create_magic_sprite(self, player, style, strength, cost): def create_magic_sprite(self, player, style, strength, cost):
print(style)
if style == 'heal': if style == 'heal':
self.magic_player.heal(player, strength, cost, [ self.magic_player.heal(player, strength, cost, [
player.visible_sprites]) player.visible_sprites])

View file

@ -1,29 +1,18 @@
from configs.game.player_config import stats, max_stats, upgrade_costs
from configs.game.monster_config import monster_data
class StatsHandler: class StatsHandler:
def __init__(self): def __init__(self, sprite_type, monster_name=None):
self.stats = {
'health': 100,
'energy': 60,
'attack': 10,
'magic': 4,
'speed': 5
}
self.max_stats = { if sprite_type == 'player':
'health': 300,
'energy': 150,
'attack': 20,
'magic': 10,
'speed': 10
}
self.upgrade_costs = { self.stats = stats
'health': 100,
'energy': 100, self.max_stats = max_stats
'attack': 100,
'magic': 100, self.upgrade_costs = upgrade_costs
'speed': 100
}
self.health = self.stats['health'] self.health = self.stats['health']
self.energy = self.stats['energy'] self.energy = self.stats['energy']
@ -32,9 +21,21 @@ class StatsHandler:
self.speed = self.stats['speed'] self.speed = self.stats['speed']
self.exp = 10000 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): def energy_recovery(self):
if self.energy < self.stats['energy']: if self.energy < self.stats['energy']:
self.energy += 0.01 * self.stats['magic'] self.energy += 0.01 * self.magic
else: else:
self.energy = self.stats['energy'] self.energy = self.stats['energy']

92
game/entities/enemy.py Normal file
View file

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

View file

@ -1,14 +1,19 @@
import pygame 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.stats import StatsHandler
from .components._input import InputHandler from .components._input import InputHandler
from .components.animaton import AnimationHandler from .components.animaton import AnimationHandler
from effects.particle_effects import AnimationPlayer
class Player(pygame.sprite.Sprite): 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) super().__init__(groups)
# Setup Sprites # Setup Sprites
@ -16,32 +21,33 @@ class Player(pygame.sprite.Sprite):
self.visible_sprites = visible_sprites self.visible_sprites = visible_sprites
self.attack_sprites = attack_sprites self.attack_sprites = attack_sprites
self.obstacle_sprites = obstacle_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 # Setup Inputs
self._input = InputHandler( self._input = InputHandler(
self.sprite_type, self.status) self.sprite_type, self.animation_player) # , 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
# Setup Stats # Setup Stats
self.stats = StatsHandler() self.stats = StatsHandler(self.sprite_type)
self.distance_direction_from_enemy = None
def get_status(self): def get_status(self):
if self._input.movement.direction.x == 0 and self._input.movement.direction.y == 0: 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' self.status += '_idle'
if self._input.attacking: if self._input.attacking:
self._input.movement.direction.x = 0 self._input.movement.direction.x = 0
self._input.movement.direction.y = 0 self._input.movement.direction.y = 0
if not 'attack' in self.status: if 'attack' not in self.status:
if 'idle' in self.status: if 'idle' in self.status:
self.status = self.status.replace('idle', 'attack') self.status = self.status.replace('idle', 'attack')
else: else:
@ -50,10 +56,38 @@ class Player(pygame.sprite.Sprite):
if 'attack' in self.status: if 'attack' in self.status:
self.status = self.status.replace('_attack', '') 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): def update(self):
# Refresh objects based on input # Refresh objects based on input
self._input.check_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 self.status = self._input.status
# Animate # Animate

View file

@ -1,6 +1,6 @@
import pygame 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: class Upgrade:

View file

@ -1,11 +1,8 @@
import os import os
import pygame 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 configs.system.window_config import TILESIZE
from utils.debug import debug 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.ui import UI
from interface.upgrade import Upgrade 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.observer import Observer
from entities.player import Player from entities.player import Player
from entities.enemy import Enemy
from .terrain import Tile from .terrain import Tile
from .camera import Camera from .camera import Camera
@ -48,6 +42,9 @@ class Level:
self.ui = UI() self.ui = UI()
self.upgrade = Upgrade(self.player) self.upgrade = Upgrade(self.player)
self.get_players_enemies()
self.get_distance_direction()
def create_map(self): def create_map(self):
script_dir = os.path.dirname(os.path.abspath(__file__)) script_dir = os.path.dirname(os.path.abspath(__file__))
asset_path = os.path.join( asset_path = os.path.join(
@ -92,28 +89,85 @@ class Level:
elif col == '394': elif col == '394':
# Player Generation # Player Generation
self.player = Player( 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: 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): def toggle_menu(self):
self.game_paused = not self.game_paused self.game_paused = not self.game_paused
def run(self): def run(self, who='observer'):
# Draw the game # Draw the game
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.visible_sprites.custom_draw(self.player)
self.ui.display(self.player) self.ui.display(self.player)
debug(self.player.status) debug('v0.5')
if not self.game_paused: if not self.game_paused:
# Update the game # 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.update()
# self.visible_sprites.enemy_update(self.player) # self.visible_sprites.enemy_update(self.player)
# self.player_attack_logic() # self.player_attack_logic()
else: else:
if self.visible_sprites.sprite_type == 'player':
self.upgrade.display() self.upgrade.display()
if self.player.stats.health <= 0: if self.player.stats.health <= 0:

View file

@ -2,10 +2,12 @@ import pygame
import sys import sys
from configs.system.window_config import WIDTH, HEIGHT, WATER_COLOR, FPS from configs.system.window_config import WIDTH, HEIGHT, WATER_COLOR, FPS
from utils.debug import debug
from level.level import Level from level.level import Level
import os
import psutil
class Game: class Game:
@ -13,16 +15,17 @@ class Game:
pygame.init() pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) self.screen = pygame.display.set_mode(
(WIDTH, HEIGHT))
pygame.display.set_caption('Pneuma') pygame.display.set_caption('Pneuma')
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
self.level = Level() self.level = Level()
# # Sound # Sound
# main_sound = pygame.mixer.Sound('../assets/audio/main.ogg') main_sound = pygame.mixer.Sound('../assets/audio/main.ogg')
# main_sound.set_volume(0.4) main_sound.set_volume(0.4)
# main_sound.play(loops=-1) main_sound.play(loops=-1)
def run(self): def run(self):
@ -35,7 +38,7 @@ class Game:
self.level.toggle_menu() self.level.toggle_menu()
self.screen.fill(WATER_COLOR) self.screen.fill(WATER_COLOR)
self.level.run() self.level.run('player')
pygame.display.update() pygame.display.update()
self.clock.tick(FPS) self.clock.tick(FPS)

View file

@ -1,5 +1,6 @@
import pygame import pygame
import sys import sys
import os
from utils.settings import * from utils.settings import *
from utils.debug import debug from utils.debug import debug
@ -7,13 +8,15 @@ from utils.debug import debug
from objects.level import Level from objects.level import Level
class Game: class Game:
def __init__(self): def __init__(self):
pygame.init() pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGHT)) self.screen = pygame.display.set_mode(
(WIDTH, HEIGHT))
pygame.display.set_caption('Pneuma') pygame.display.set_caption('Pneuma')
self.clock = pygame.time.Clock() self.clock = pygame.time.Clock()
@ -43,6 +46,5 @@ class Game:
if __name__ == '__main__': if __name__ == '__main__':
game = Game() game = Game()
figure_file = 'rl/plots/pneuma.png'
while True: while True:
game.run() game.run()