2023-06-14 12:15:05 +00:00
|
|
|
import pygame
|
|
|
|
import random
|
2023-07-11 00:25:44 +00:00
|
|
|
import numpy as np
|
2023-06-14 12:15:05 +00:00
|
|
|
|
|
|
|
from utils.settings import *
|
|
|
|
from utils.support import import_folder
|
|
|
|
|
|
|
|
from objects.entity import Entity
|
|
|
|
|
|
|
|
from rl.agent import Agent
|
2023-07-11 00:25:44 +00:00
|
|
|
from rl.rl_settings import *
|
2023-06-14 12:15:05 +00:00
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
class Player(Entity):
|
|
|
|
|
2023-07-11 00:25:44 +00:00
|
|
|
def __init__(self, position, groups, obstacle_sprites, create_attack_sprite, delete_attack_sprite, create_magic_sprite, is_AI, state):
|
|
|
|
super().__init__(groups, is_AI, state)
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
self.image = pygame.image.load(
|
|
|
|
'../Graphics/graphics/player/down/down_0.png').convert_alpha()
|
|
|
|
self.rect = self.image.get_rect(topleft=position)
|
2023-06-14 12:15:05 +00:00
|
|
|
self.hitbox = self.rect.inflate(HITBOX_OFFSET['player'])
|
|
|
|
self.sprite_type = 'player'
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Graphics Setup
|
|
|
|
self.import_player_assets()
|
|
|
|
self.status = 'down'
|
|
|
|
|
|
|
|
# Combat
|
|
|
|
self.attacking = False
|
|
|
|
self.attack_cooldown = 400
|
|
|
|
self.attack_time = None
|
|
|
|
|
|
|
|
# Weapons
|
|
|
|
self.create_attack_sprite = create_attack_sprite
|
|
|
|
self.delete_attack_sprite = delete_attack_sprite
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Magic
|
|
|
|
self.create_magic_sprite = create_magic_sprite
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Weapon rotation
|
|
|
|
self.weapon_index = 0
|
|
|
|
self.weapon = list(weapon_data.keys())[self.weapon_index]
|
|
|
|
self.can_rotate_weapon = True
|
|
|
|
self.weapon_rotation_time = None
|
|
|
|
self.rotate_attack_cooldown = 600
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Magic rotation
|
|
|
|
self.magic_index = 0
|
|
|
|
self.magic = list(magic_data.keys())[self.magic_index]
|
|
|
|
self.can_swap_magic = True
|
|
|
|
self.magic_swap_time = None
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Stats
|
|
|
|
self.stats = {
|
2023-09-27 18:03:37 +00:00
|
|
|
'health': 100,
|
|
|
|
'energy': 60,
|
|
|
|
'attack': 10,
|
|
|
|
'magic': 4,
|
|
|
|
'speed': 5
|
|
|
|
}
|
2023-06-14 12:15:05 +00:00
|
|
|
self.max_stats = {
|
2023-09-27 18:03:37 +00:00
|
|
|
'health': 300,
|
|
|
|
'energy': 150,
|
|
|
|
'attack': 20,
|
|
|
|
'magic': 10,
|
|
|
|
'speed': 10
|
2023-06-14 12:15:05 +00:00
|
|
|
}
|
|
|
|
self.upgrade_costs = {
|
2023-09-27 18:03:37 +00:00
|
|
|
'health': 100,
|
|
|
|
'energy': 100,
|
|
|
|
'attack': 100,
|
|
|
|
'magic': 100,
|
|
|
|
'speed': 100
|
2023-06-14 12:15:05 +00:00
|
|
|
}
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-07-11 00:25:44 +00:00
|
|
|
# AI setup
|
|
|
|
self.is_AI = is_AI
|
|
|
|
if self.is_AI:
|
2023-09-27 18:03:37 +00:00
|
|
|
self.agent = Agent(self.possible_actions, input_dims=(list(self.stats.values(
|
|
|
|
)) + self.distance_direction_to_player), batch_size=batch_size, alpha=alpha, n_epochs=n_epochs)
|
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
self.health = self.stats['health']
|
|
|
|
self.energy = self.stats['energy']
|
|
|
|
self.exp = 0
|
|
|
|
self.speed = self.stats['speed']
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Damage timer
|
|
|
|
self.vulnerable = True
|
|
|
|
self.hurt_time = None
|
|
|
|
self.invulnerability_duration = 300
|
|
|
|
|
|
|
|
self.obstacle_sprites = obstacle_sprites
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Import Sounds
|
2023-09-27 18:03:37 +00:00
|
|
|
self.weapon_attack_sound = pygame.mixer.Sound(
|
|
|
|
'../Graphics/audio/sword.wav')
|
2023-06-14 12:15:05 +00:00
|
|
|
self.weapon_attack_sound.set_volume(0.2)
|
|
|
|
|
|
|
|
def import_player_assets(self):
|
|
|
|
character_path = '../Graphics/graphics/player'
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
self.animations = {
|
2023-09-27 18:03:37 +00:00
|
|
|
'up': [], 'down': [], 'left': [], 'right': [],
|
|
|
|
'up_idle': [], 'down_idle': [], 'left_idle': [], 'right_idle': [],
|
|
|
|
'up_attack': [], 'down_attack': [], 'left_attack': [], 'right_attack': []
|
2023-06-14 12:15:05 +00:00
|
|
|
}
|
|
|
|
for animation in self.animations.keys():
|
|
|
|
full_path = f"{character_path}/{animation}"
|
|
|
|
self.animations[animation] = import_folder(full_path)
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def get_status(self):
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Idle Status
|
|
|
|
if self.direction.x == 0 and self.direction.y == 0:
|
|
|
|
if not 'idle' in self.status and not 'attack' in self.status:
|
|
|
|
self.status += '_idle'
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if self.attacking:
|
|
|
|
self.direction.x = 0
|
|
|
|
self.direction.y = 0
|
|
|
|
if not 'attack' in self.status:
|
|
|
|
if 'idle' in self.status:
|
|
|
|
self.status = self.status.replace('idle', 'attack')
|
|
|
|
else:
|
|
|
|
self.status += '_attack'
|
|
|
|
else:
|
|
|
|
if 'attack' in self.status:
|
|
|
|
self.status = self.status.replace('_attack', '')
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def get_full_weapon_damage(self):
|
|
|
|
base_damage = self.stats['attack']
|
|
|
|
weapon_damage = weapon_data[self.weapon]['damage']
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
return (base_damage + weapon_damage)
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def get_full_magic_damage(self):
|
|
|
|
base_damage = self.stats['magic']
|
|
|
|
spell_damage = magic_data[self.magic]['strength']
|
|
|
|
return (base_damage + spell_damage)
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def get_value_by_index(self, index):
|
|
|
|
return list(self.stats.values())[index]
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def get_cost_by_index(self, index):
|
|
|
|
return list(self.upgrade_costs.values())[index]
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def cooldowns(self):
|
|
|
|
current_time = pygame.time.get_ticks()
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if self.attacking:
|
|
|
|
if current_time - self.attack_time > self.attack_cooldown + weapon_data[self.weapon]['cooldown']:
|
|
|
|
self.attacking = False
|
|
|
|
self.delete_attack_sprite()
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if not self.can_rotate_weapon:
|
|
|
|
if current_time - self.weapon_rotation_time > self.rotate_attack_cooldown:
|
|
|
|
self.can_rotate_weapon = True
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if not self.can_swap_magic:
|
|
|
|
if current_time - self.magic_swap_time > self.rotate_attack_cooldown:
|
|
|
|
self.can_swap_magic = True
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if not self.vulnerable:
|
|
|
|
if current_time - self.hurt_time >= self.invulnerability_duration:
|
|
|
|
self.vulnerable = True
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if not self.can_move:
|
|
|
|
if current_time - self.move_time >= self.move_cooldown:
|
|
|
|
self.can_move = True
|
|
|
|
|
|
|
|
def energy_recovery(self):
|
|
|
|
if self.energy < self.stats['energy']:
|
|
|
|
self.energy += 0.01 * self.stats['magic']
|
|
|
|
else:
|
|
|
|
self.energy = self.stats['energy']
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
def animate(self):
|
|
|
|
animation = self.animations[self.status]
|
|
|
|
self.frame_index += self.animation_speed
|
|
|
|
if self.frame_index >= len(animation):
|
|
|
|
self.frame_index = 0
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
# Set the image
|
|
|
|
self.image = animation[int(self.frame_index)]
|
2023-09-27 18:03:37 +00:00
|
|
|
self.rect = self.image.get_rect(center=self.hitbox.center)
|
|
|
|
|
2023-06-14 12:15:05 +00:00
|
|
|
if not self.vulnerable:
|
|
|
|
alpha = self.wave_value()
|
|
|
|
self.image.set_alpha(alpha)
|
|
|
|
else:
|
|
|
|
self.image.set_alpha(255)
|
|
|
|
|
|
|
|
def update(self):
|
|
|
|
self.input()
|
|
|
|
self.cooldowns()
|
|
|
|
self.get_status()
|
|
|
|
self.animate()
|
|
|
|
self.move(self.stats['speed'])
|
|
|
|
self.energy_recovery()
|
2023-07-11 00:25:44 +00:00
|
|
|
self.distance_direction_to_player = self.state()
|
2023-09-27 18:03:37 +00:00
|
|
|
# if self.is_AI:
|
|
|
|
# self.agent.act(self.distance_direction_to_player)
|