2023-09-27 18:03:37 +00:00
|
|
|
import pygame
|
2023-10-04 02:37:28 +00:00
|
|
|
from random import randint
|
|
|
|
|
|
|
|
from configs.game.weapon_config import weapon_data
|
|
|
|
from configs.game.spell_config import magic_data
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
from .components.stats import StatsHandler
|
|
|
|
from .components._input import InputHandler
|
|
|
|
from .components.animaton import AnimationHandler
|
|
|
|
|
2023-10-04 02:37:28 +00:00
|
|
|
from effects.particle_effects import AnimationPlayer
|
|
|
|
|
2023-11-14 21:44:43 +00:00
|
|
|
from agent.agent import Agent
|
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
class Player(pygame.sprite.Sprite):
|
|
|
|
|
2023-11-14 21:44:43 +00:00
|
|
|
def __init__(self, position, groups, obstacle_sprites, visible_sprites, attack_sprites, attackable_sprites, role, player_id, extract_features, convert_features_to_tensor):
|
2023-09-27 18:03:37 +00:00
|
|
|
super().__init__(groups)
|
|
|
|
|
|
|
|
# Setup Sprites
|
|
|
|
self.sprite_type = 'player'
|
2023-11-14 21:44:43 +00:00
|
|
|
self.status = 'down'
|
|
|
|
self.player_id = player_id
|
2023-09-27 18:03:37 +00:00
|
|
|
self.visible_sprites = visible_sprites
|
|
|
|
self.attack_sprites = attack_sprites
|
|
|
|
self.obstacle_sprites = obstacle_sprites
|
2023-10-04 02:37:28 +00:00
|
|
|
self.attackable_sprites = attackable_sprites
|
2023-09-27 18:03:37 +00:00
|
|
|
|
|
|
|
# Setup Graphics
|
2023-10-04 02:37:28 +00:00
|
|
|
self.animation_player = AnimationPlayer()
|
|
|
|
self.animation = AnimationHandler(self.sprite_type)
|
|
|
|
self.animation.import_assets(position)
|
2023-09-27 18:03:37 +00:00
|
|
|
self.image = self.animation.image
|
|
|
|
self.rect = self.animation.rect
|
|
|
|
|
2023-10-04 02:37:28 +00:00
|
|
|
# Setup Inputs
|
|
|
|
self._input = InputHandler(
|
|
|
|
self.sprite_type, self.animation_player) # , self.status)
|
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
# Setup Stats
|
2023-11-13 12:34:22 +00:00
|
|
|
self.role = role
|
|
|
|
self.stats = StatsHandler(self.sprite_type, self.role)
|
2023-10-04 02:37:28 +00:00
|
|
|
|
|
|
|
self.distance_direction_from_enemy = None
|
2023-09-27 18:03:37 +00:00
|
|
|
|
2023-11-14 21:44:43 +00:00
|
|
|
# Setup AI
|
|
|
|
self.extract_features = extract_features
|
|
|
|
self.convert_features_to_tensor = convert_features_to_tensor
|
|
|
|
self.agent = Agent(input_dims=398, n_actions=self._input.num_actions)
|
|
|
|
self.state_tensor = None
|
|
|
|
self.action_tensor = None
|
|
|
|
self.reward_tensor = None
|
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
def get_status(self):
|
|
|
|
if self._input.movement.direction.x == 0 and self._input.movement.direction.y == 0:
|
2023-10-04 02:37:28 +00:00
|
|
|
if 'idle' not in self.status and 'attack' not in self.status:
|
2023-09-27 18:03:37 +00:00
|
|
|
self.status += '_idle'
|
|
|
|
|
|
|
|
if self._input.attacking:
|
|
|
|
self._input.movement.direction.x = 0
|
|
|
|
self._input.movement.direction.y = 0
|
2023-10-04 02:37:28 +00:00
|
|
|
if 'attack' not in self.status:
|
2023-09-27 18:03:37 +00:00
|
|
|
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-10-04 02:37:28 +00:00
|
|
|
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)
|
|
|
|
|
2023-11-14 21:44:43 +00:00
|
|
|
def get_current_state(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def is_dead(self):
|
|
|
|
if self.stats.health == 0:
|
|
|
|
self.stats.exp = -10
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
return False
|
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
def update(self):
|
2023-11-14 21:44:43 +00:00
|
|
|
self.extract_features()
|
|
|
|
self.convert_features_to_tensor()
|
|
|
|
|
|
|
|
# Choose action based on current state
|
|
|
|
action, probs, value = self.agent.choose_action(self.state_tensor)
|
|
|
|
|
|
|
|
print(action)
|
|
|
|
# Apply chosen action
|
|
|
|
self._input.check_input(action, self.stats.speed, self.animation.hitbox,
|
|
|
|
self.obstacle_sprites, self.animation.rect, self)
|
|
|
|
|
|
|
|
done = self.is_dead()
|
|
|
|
|
|
|
|
self.extract_features()
|
|
|
|
self.convert_features_to_tensor()
|
|
|
|
|
|
|
|
self.agent.remember(self.state_tensor, self.action_tensor,
|
|
|
|
probs, value, self.reward_tensor, done)
|
|
|
|
|
|
|
|
if done:
|
|
|
|
self.agent.learn()
|
|
|
|
self.agent.memory.clear_memory()
|
|
|
|
|
2023-09-27 18:03:37 +00:00
|
|
|
# Refresh objects based on input
|
|
|
|
self.status = self._input.status
|
|
|
|
|
|
|
|
# Animate
|
|
|
|
self.get_status()
|
|
|
|
self.animation.animate(self.status, self._input.combat.vulnerable)
|
|
|
|
self.image = self.animation.image
|
|
|
|
self.rect = self.animation.rect
|
|
|
|
|
|
|
|
# Cooldowns and Regen
|
|
|
|
self.stats.energy_recovery()
|
|
|
|
self._input.cooldowns(self._input.combat.vulnerable)
|