Improved tensor logic

This commit is contained in:
Vasilis Valatsos 2023-11-13 19:09:41 +01:00
parent ba0cc46841
commit d20f46bb9d
240 changed files with 96 additions and 2108 deletions

View file

@ -3,9 +3,11 @@ import torch
from numpy.random import default_rng from numpy.random import default_rng
#from rl.brain import PPONet
from rl.brain import ActorNetwork, CriticNetwork, PPOMemory from rl.brain import ActorNetwork, CriticNetwork, PPOMemory
class Agent: class Agent:
def __init__(self, n_actions, input_dims, gamma = 0.99, alpha = 0.0003, policy_clip = 0.2, batch_size = 64, N=2048, n_epochs = 10, gae_lambda = 0.95): def __init__(self, n_actions, input_dims, gamma = 0.99, alpha = 0.0003, policy_clip = 0.2, batch_size = 64, N=2048, n_epochs = 10, gae_lambda = 0.95):
self.gamma = gamma self.gamma = gamma
@ -27,13 +29,13 @@ class Agent:
def save_models(self): def save_models(self):
print('... saving models ...') print('... saving models ...')
self.actor.save_checkpoint() self.actor.save_checkpoint()
self.critic.save_chaeckpoint() self.critic.save_checkpoint()
print('... done ...') print('... done ...')
def load_models(self): def load_models(self):
print('... loadng models ...') print('... loadng models ...')
self.actor.load_checkpoint() self.actor.load_checkpoint()
self.critic.load_chaeckpoint() self.critic.load_checkpoint()
print('.. done ...') print('.. done ...')
def choose_action(self, observation): def choose_action(self, observation):
@ -95,40 +97,3 @@ class Agent:
self.critic.optimizer.step() self.critic.optimizer.step()
self.memory.clear_memory() self.memory.clear_memory()
# def __init__(self, actions, inputs, player_info, reward, save_dir, checkpoint = None):
# self.inputs = inputs
#
# self.input_dim = len(inputs) + len(player_info)
#
# self.output_dim = len(actions)
# self.reward = reward
#
# if torch.cuda.is_available():
# self.device = "cuda"
# elif torch.backends.mps.is_available():
# self.device = "mps"
# else:
# self.device="cpu"
# self.net = PPONet(self.input_dim, self.output_dim)
# self.net = self.net.to(device=self.device)
#
# self.rng = default_rng()
#
#
# ## DEFINING PARAMETERS
# pass
#print(f"Model ready, using {self.device}")
# if checkpoint:
# print(f"chkpt at {checkpoint}")
# self.load(checkpoint)
# else:
# print('No chkpt passed')
#
# def act(self, distance_direction_to_player):
# print(distance_direction_to_player)

View file

@ -17,6 +17,7 @@ class PPOMemory:
self.batch_size = batch_size self.batch_size = batch_size
def generate_batches(self): def generate_batches(self):
n_states = len(self.states) n_states = len(self.states)
batch_start = np.arange(0, n_states, self.batch_size) batch_start = np.arange(0, n_states, self.batch_size)
indices = np.arange(n_states, dtype = np.int64) indices = np.arange(n_states, dtype = np.int64)

View file

@ -35,8 +35,13 @@ class InputHandler:
self.can_swap_magic = True self.can_swap_magic = True
self.magic_swap_time = None self.magic_swap_time = None
# Setup Action Space
self.action = 10
def check_input(self, speed, hitbox, obstacle_sprites, rect, player): def check_input(self, speed, hitbox, obstacle_sprites, rect, player):
self.action = 10
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()
@ -50,10 +55,14 @@ class InputHandler:
self.movement.direction.y = -1 self.movement.direction.y = -1
self.status = 'up' self.status = 'up'
self.can_move = False self.can_move = False
self.action = 0
elif button == 1: # keys[pygame.K_s]: elif 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
self.action = 1
else: else:
self.movement.direction.y = 0 self.movement.direction.y = 0
@ -61,10 +70,14 @@ class InputHandler:
self.movement.direction.x = -1 self.movement.direction.x = -1
self.status = 'left' self.status = 'left'
self.can_move = False self.can_move = False
self.action = 2
elif button == 3: # keys[pygame.K_d]: elif button == 3: # 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
self.action = 3
else: else:
self.movement.direction.x = 0 self.movement.direction.x = 0
@ -77,6 +90,7 @@ class InputHandler:
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.combat.weapon_attack_sound.play() self.combat.weapon_attack_sound.play()
self.action = 4
# Magic Input # Magic Input
if button == 5: # keys[pygame.K_q]: if button == 5: # keys[pygame.K_q]:
@ -93,6 +107,7 @@ class InputHandler:
self.combat.magic_index]['cost'] self.combat.magic_index]['cost']
self.combat.create_magic_sprite( self.combat.create_magic_sprite(
player, self.combat.magic, strength, cost) player, self.combat.magic, strength, cost)
self.action = 5
# Rotating Weapons # Rotating Weapons
# keys[pygame.K_LSHIFT] # keys[pygame.K_LSHIFT]
@ -106,6 +121,7 @@ class InputHandler:
self.combat.weapon = list(weapon_data.keys())[ self.combat.weapon = list(weapon_data.keys())[
self.combat.weapon_index] self.combat.weapon_index]
self.action = 6
# Swap Spells # Swap Spells
# keys[pygame.K_LCTRL] : # keys[pygame.K_LCTRL] :
@ -116,6 +132,7 @@ class InputHandler:
self.combat.magic_index += 1 self.combat.magic_index += 1
else: else:
self.combat.magic_index = 0 self.combat.magic_index = 0
self.action = 7
def cooldowns(self, vulnerable): def cooldowns(self, vulnerable):
current_time = pygame.time.get_ticks() current_time = pygame.time.get_ticks()

View file

@ -1,9 +0,0 @@
import pygame
from math import sin
import random
class Entity():
def __init__(self, groups, position, sprite_type):
super().__init__(groups)

View file

@ -27,10 +27,22 @@ class Game:
main_sound.play(loops=-1) main_sound.play(loops=-1)
def extract_features(self): def extract_features(self):
self.state_features = []
self.reward_features = []
self.action_features = []
self.features = [] self.features = []
for i, player in enumerate(self.level.player_sprites): for i, player in enumerate(self.level.player_sprites):
player_features = { player_action_features = {
"player_action": player._input.action
}
player_reward_features = {
"player_exp": player.stats.exp
}
player_state_features = {
"player_position": player.rect.center, "player_position": player.rect.center,
"player role": player.stats.role_id, "player role": player.stats.role_id,
"player_health": player.stats.health, "player_health": player.stats.health,
@ -38,7 +50,6 @@ class Game:
"player_attack": player.stats.attack, "player_attack": player.stats.attack,
"player_magic": player.stats.magic, "player_magic": player.stats.magic,
"player_speed": player.stats.speed, "player_speed": player.stats.speed,
"player_exp": player.stats.exp,
"player_vulnerable": int(player._input.combat.vulnerable), "player_vulnerable": int(player._input.combat.vulnerable),
"player_can_move": int(player._input.can_move), "player_can_move": int(player._input.can_move),
"player_attacking": int(player._input.attacking), "player_attacking": int(player._input.attacking),
@ -62,35 +73,40 @@ class Game:
"enemy_direction": direction "enemy_direction": direction
}) })
player_features["enemies"] = distances_directions player_state_features["enemies"] = distances_directions
self.features.append(player_features) self.reward_features.append(player_reward_features)
self.state_features.append(player_state_features)
self.action_features.append(player_action_features)
def convert_features_to_tensor(self): def convert_features_to_tensor(self):
self.tensors = [] self.state_tensors = []
for player_features in self.features: self.action_tensors = []
self.reward_tensors = []
for features in self.state_features:
info_array = [] info_array = []
# Adding player features to tensor # Adding player features to tensor
player_info = [ player_info = [
player_features['player_position'][0], features['player_position'][0],
player_features['player_position'][1], features['player_position'][1],
player_features['player role'], features['player role'],
player_features['player_health'], features['player_health'],
player_features['player_energy'], features['player_energy'],
player_features['player_attack'], features['player_attack'],
player_features['player_magic'], features['player_magic'],
player_features['player_speed'], features['player_speed'],
player_features['player_exp'], features['player_vulnerable'],
player_features['player_vulnerable'], features['player_can_move'],
player_features['player_can_move'], features['player_attacking'],
player_features['player_attacking'], features['player_can_rotate_weapon'],
player_features['player_can_rotate_weapon'], features['playercan_swap_magic'],
player_features['playercan_swap_magic'],
] ]
info_array.extend(player_info) info_array.extend(player_info)
for enemy in player_features['enemies']: # Adding enemy features per player
for enemy in features['enemies']:
enemy_info = [ enemy_info = [
enemy['enemy_id'], enemy['enemy_id'],
enemy['enemy_status'], enemy['enemy_status'],
@ -106,9 +122,37 @@ class Game:
] ]
info_array.extend(enemy_info) info_array.extend(enemy_info)
player_tensor = torch.tensor( state_tensor = torch.tensor(
np.array(info_array, dtype=np.float32)) np.array(info_array, dtype=np.float32))
self.tensors.append(player_tensor)
self.state_tensors.append(state_tensor)
for features in self.action_features:
info_array = []
# Adding action features
action_info = [
features["player_action"]
]
action_tensor = torch.tensor(
np.array(action_info, dtype=np.float32))
self.action_tensors.append(action_tensor)
for features in self.reward_features:
info_array = []
# Adding reward features
reward_info = [
features["player_exp"]
]
reward_tensor = torch.tensor(
np.array(reward_info, dtype=np.float32))
self.reward_tensors.append(reward_tensor)
def run(self): def run(self):
@ -133,7 +177,11 @@ class Game:
if __name__ == '__main__': if __name__ == '__main__':
game = Game() game = Game()
for _ in range(0, 10000): for i in range(0, 10000):
game.run() game.run()
game.extract_features() game.extract_features()
game.convert_features_to_tensor() game.convert_features_to_tensor()
if i == 100:
print(game.reward_tensors)
print(game.action_tensors)
print(game.state_tensors)

View file

@ -1,100 +0,0 @@
import pygame
from utils.settings import *
class UI:
def __init__(self):
# General info
self.display_surface = pygame.display.get_surface()
self.font = pygame.font.Font(UI_FONT, UI_FONT_SIZE)
# Bar setup
self.health_bar_rect = pygame.Rect(10, 10, HEALTH_BAR_WIDTH, BAR_HEIGHT)
self.energy_bar_rect = pygame.Rect(10, 34, ENERGY_BAR_WIDTH, BAR_HEIGHT)
# Convert weapon dictionary
self.weapon_graphics = []
for weapon in weapon_data.values():
path = weapon['graphic']
weapon = pygame.image.load(path).convert_alpha()
self.weapon_graphics.append(weapon)
# Convert weapon dictionary
self.magic_graphics = []
for spell in magic_data.values():
path = spell['graphic']
spell = pygame.image.load(path).convert_alpha()
self.magic_graphics.append(spell)
def show_bar(self, current_amount, max_amount, bg_rect, color):
# Draw background
pygame.draw.rect(self.display_surface, UI_BG_COLOR, bg_rect)
# Convert stat amount to pixels
ratio = current_amount / max_amount
current_width = bg_rect.width * ratio
current_rect = bg_rect.copy()
current_rect.width = current_width
# Draw stat bar
pygame.draw.rect(self.display_surface, color, current_rect)
pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, bg_rect, 4)
def show_exp(self, exp):
if exp >= 0:
text_surf = self.font.render(f"EXP: {str(int(exp))}", False, TEXT_COLOR)
x = self.display_surface.get_size()[0] - 20
y = self.display_surface.get_size()[1] - 20
text_rect = text_surf.get_rect(bottomright = (x,y))
pygame.draw.rect(self.display_surface, UI_BG_COLOR, text_rect.inflate(10, 10))
self.display_surface.blit(text_surf, text_rect)
pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, text_rect.inflate(10, 10), 4)
else:
text_surf = self.font.render(f"OBSERVER", False, TEXT_COLOR)
x = self.display_surface.get_size()[0] - 20
y = self.display_surface.get_size()[1] - 20
text_rect = text_surf.get_rect(bottomright = (x,y))
pygame.draw.rect(self.display_surface, UI_BG_COLOR, text_rect.inflate(10, 10))
self.display_surface.blit(text_surf, text_rect)
pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, text_rect.inflate(10, 10), 4)
def selection_box(self, left, top, has_rotated):
bg_rect = pygame.Rect(left, top, ITEM_BOX_SIZE, ITEM_BOX_SIZE)
pygame.draw.rect(self.display_surface, UI_BG_COLOR, bg_rect)
if not has_rotated:
pygame.draw.rect(self.display_surface, UI_BORDER_COLOR_ACTIVE, bg_rect, 4)
else:
pygame.draw.rect(self.display_surface, UI_BORDER_COLOR, bg_rect, 4)
return bg_rect
def weapon_overlay(self, weapon_index, has_rotated):
bg_rect = self.selection_box(10, 630, has_rotated)
weapon_surf = self.weapon_graphics[weapon_index]
weapon_rect = weapon_surf.get_rect(center = bg_rect.center)
self.display_surface.blit(weapon_surf, weapon_rect)
def magic_overlay(self, magic_index, has_swaped):
bg_rect = self.selection_box(100, 630, has_swaped)
magic_surf = self.magic_graphics[magic_index]
magic_rect = magic_surf.get_rect(center = bg_rect.center)
self.display_surface.blit(magic_surf, magic_rect)
def display(self, player):
if player.sprite_type == 'player':
self.show_bar(player.health, player.stats['health'], self.health_bar_rect, HEALTH_COLOR)
self.show_bar(player.energy, player.stats['energy'], self.energy_bar_rect, ENERGY_COLOR)
self.show_exp(player.exp)
self.weapon_overlay(player.weapon_index, player.can_rotate_weapon)
self.magic_overlay(player.magic_index, player.can_swap_magic)
if player.sprite_type == 'camera':
self.show_exp(player.exp)

View file

@ -1,49 +0,0 @@
import pygame
from utils.settings import *
from random import randint
class MagicPlayer:
def __init__(self, animation_player):
self.animation_player = animation_player
# Sound Setup
self.sounds = {
'heal': pygame.mixer.Sound('../Graphics/audio/heal.wav'),
'flame': pygame.mixer.Sound('../Graphics/audio/flame.wav')
}
def heal(self, player, strength, cost, groups):
if player.energy >= cost:
self.sounds['heal'].play()
player.health += strength
player.energy -= cost
if player.health >= player.stats['health']:
player.health = player.stats['health']
self.animation_player.generate_particles('aura', player.rect.center, groups)
self.animation_player.generate_particles('heal', player.rect.center + pygame.math.Vector2(0, -50), groups)
def flame(self, player, cost, groups):
if player.energy >= cost:
player.energy -= cost
self.sounds['flame'].play()
if player.status.split('_')[0] == 'right':
direction = pygame.math.Vector2(1,0)
elif player.status.split('_')[0] == 'left':
direction = pygame.math.Vector2(-1,0)
elif player.status.split('_')[0] == 'up':
direction = pygame.math.Vector2(0,-1)
else:
direction = pygame.math.Vector2(0,1)
for i in range(1, 6):
if direction.x:
offset_x = direction.x * i * TILESIZE
x = player.rect.centerx + offset_x + randint(-TILESIZE // 3, TILESIZE //3)
y = player.rect.centery + randint(-TILESIZE // 3, TILESIZE //3)
self.animation_player.generate_particles('flame', (x,y), groups)
else:
offset_y = direction.y * i * TILESIZE
x = player.rect.centerx + randint(-TILESIZE // 3, TILESIZE //3)
y = player.rect.centery + offset_y + randint(-TILESIZE // 3, TILESIZE //3)
self.animation_player.generate_particles('flame', (x,y), groups)

View file

@ -1,76 +0,0 @@
import pygame
from utils.support import import_folder
from random import choice
class AnimationPlayer:
def __init__(self):
self.frames = {
# magic
'flame': import_folder('../Graphics/graphics/particles/flame/frames'),
'aura': import_folder('../Graphics/graphics/particles/aura'),
'heal': import_folder('../Graphics/graphics/particles/heal/frames'),
# attacks
'claw': import_folder('../Graphics/graphics/particles/claw'),
'slash': import_folder('../Graphics/graphics/particles/slash'),
'sparkle': import_folder('../Graphics/graphics/particles/sparkle'),
'leaf_attack': import_folder('../Graphics/graphics/particles/leaf_attack'),
'thunder': import_folder('../Graphics/graphics/particles/thunder'),
# monster deaths
'squid': import_folder('../Graphics/graphics/particles/smoke_orange'),
'raccoon': import_folder('../Graphics/graphics/particles/raccoon'),
'spirit': import_folder('../Graphics/graphics/particles/nova'),
'bamboo': import_folder('../Graphics/graphics/particles/bamboo'),
# leafs
'leaf': (
import_folder('../Graphics/graphics/particles/leaf1'),
import_folder('../Graphics/graphics/particles/leaf2'),
import_folder('../Graphics/graphics/particles/leaf3'),
import_folder('../Graphics/graphics/particles/leaf4'),
import_folder('../Graphics/graphics/particles/leaf5'),
import_folder('../Graphics/graphics/particles/leaf6'),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf1')),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf2')),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf3')),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf4')),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf5')),
self.reflect_images(import_folder('../Graphics/graphics/particles/leaf6'))
)
}
def reflect_images(self, frames):
new_frames = []
for frame in frames:
flipped_frame = pygame.transform.flip(frame, True, False)
new_frames.append(flipped_frame)
return new_frames
def create_grass_particles(self, position, groups):
animation_frames = choice(self.frames['leaf'])
ParticleEffect(position, animation_frames,groups)
def generate_particles(self, animation_type, position, groups):
animation_frames = self.frames[animation_type]
ParticleEffect(position, animation_frames, groups)
class ParticleEffect(pygame.sprite.Sprite):
def __init__(self, position, animation_frames, groups):
super().__init__(groups)
self.frame_index = 0
self.animation_speed = 0.15
self.frames = animation_frames
self.image = self.frames[self.frame_index]
self.rect = self.image.get_rect(center = position)
self.sprite_type = 'magic'
def animate(self):
self.frame_index += self.animation_speed
if self.frame_index >= len(self.frames):
self.kill()
else:
self.image = self.frames[int(self.frame_index)]
def update(self):
self.animate()

View file

@ -1,23 +0,0 @@
import pygame
class Weapon(pygame.sprite.Sprite):
def __init__(self, player, groups):
super().__init__(groups)
self.sprite_type = 'weapon'
direction = player.status.split('_')[0]
# Graphic
full_path = f"../Graphics/weapons/{player.weapon}/{direction}.png"
self.image = pygame.image.load(full_path).convert_alpha()
# Sprite Placement
if direction == 'right':
self.rect = self.image.get_rect(midleft = player.rect.midright + pygame.math.Vector2(0, 16))
elif direction == 'left':
self.rect = self.image.get_rect(midright = player.rect.midleft + pygame.math.Vector2(0, 16))
elif direction == 'down':
self.rect = self.image.get_rect(midtop = player.rect.midbottom + pygame.math.Vector2(-10, 0))
else:
self.rect = self.image.get_rect(midbottom = player.rect.midtop + pygame.math.Vector2(-10, 0))

View file

@ -1,122 +0,0 @@
import pygame
from random import choice, randint
from utils.settings import *
from utils.debug import debug
from utils.support import *
from UI.ui import UI
from effects.particles import AnimationPlayer
from effects.magic import MagicPlayer
from effects.weapon import Weapon
from terrain.tiles import Tile
from view.observer import Observer
from view.camera import Camera
class Level:
def __init__(self):
# General Settings
self.game_paused = False
# Get display surface
self.display_surface = pygame.display.get_surface()
# Sprite Group setup
self.visible_sprites = Camera()
self.obstacle_sprites = pygame.sprite.Group()
self.attack_sprites = pygame.sprite.Group()
self.attackable_sprites = pygame.sprite.Group()
# Sprite setup and entity generation
self.create_map()
# UI setup
self.ui = UI()
def create_map(self):
layouts = {
'boundary': import_csv_layout('../Map/FloorBlocks.csv'),
'grass': import_csv_layout('../Map/Grass.csv'),
'objects': import_csv_layout('../Map/Objects.csv'),
'entities': import_csv_layout('../Map/Entities.csv')
}
graphics = {
'grass': import_folder('../Graphics/grass'),
'objects': import_folder('../Graphics/objects')
}
for style, layout in layouts.items():
for row_index, row in enumerate(layout):
for col_index, col in enumerate(row):
if col != '-1':
x = col_index * TILESIZE
y = row_index * TILESIZE
if style == 'boundary':
Tile((x, y), [self.obstacle_sprites], 'invisible')
if style == 'grass':
random_grass_image = choice(graphics['grass'])
Tile((x, y), [self.visible_sprites, self.obstacle_sprites,
self.attackable_sprites], 'grass', random_grass_image)
if style == 'objects':
surf = graphics['objects'][int(col)]
Tile((x, y), [self.visible_sprites,
self.obstacle_sprites], 'object', surf)
if style == 'entities':
# The numbers represent their IDs in the map .csv files generated from TILED.
if col == '395':
self.observer = Observer(
(x, y), [self.visible_sprites])
elif col == '394':
pass
# player generation
else:
pass
# monster generation
def create_attack_sprite(self):
self.current_attack = Weapon(
self.player, [self.visible_sprites, self.attack_sprites])
def delete_attack_sprite(self):
if self.current_attack:
self.current_attack.kill()
self.current_attack = None
def create_magic_sprite(self, style, strength, cost):
if style == 'heal':
self.magic_player.heal(self.player, strength, cost, [
self.visible_sprites])
if style == 'flame':
self.magic_player.flame(
self.player, cost, [self.visible_sprites, self.attack_sprites])
def run(self):
# Draw the game
self.visible_sprites.custom_draw(self.observer)
self.ui.display(self.observer)
if self.game_paused:
if self.visible_sprites.sprite_type == 'player':
self.upgrade.display()
pass
else:
# Update the game
self.player.distance_direction_to_player = self.get_state()
self.visible_sprites.update()
self.visible_sprites.enemy_update(self.player)
self.player_attack_logic()
if self.player.health <= 0:
self.__init__()

View file

@ -1,46 +0,0 @@
import pygame
import sys
from utils.settings import *
from utils.debug import debug
from level import Level
class Game:
def __init__(self):
pygame.init()
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('../Graphics/audio/main.ogg')
# main_sound.set_volume(0.4)
# main_sound.play(loops = -1)
def run(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
self.level.toggle_menu()
self.screen.fill(WATER_COLOR)
self.level.run()
pygame.display.update()
self.clock.tick(FPS)
if __name__ == '__main__':
game = Game()
while True:
game.run()

View file

@ -1,6 +0,0 @@
# AI setup
N = 20
batch_size = 5
n_epochs = 4
alpha = 0.0003

View file

@ -1,17 +0,0 @@
import pygame
from utils.settings import *
class Tile(pygame.sprite.Sprite):
def __init__(self, position, groups, sprite_type, surface = pygame.Surface((TILESIZE, TILESIZE))):
super().__init__(groups)
self.sprite_type = sprite_type
self.image = surface
if sprite_type == 'object':
# Offset
self.rect = self.image.get_rect(topleft = (position[0], position[1] - TILESIZE))
else:
self.rect = self.image.get_rect(topleft = position)
self.hitbox = self.rect.inflate(HITBOX_OFFSET[sprite_type])

View file

@ -1,12 +0,0 @@
import pygame
pygame.init()
font = pygame.font.Font(None,30)
def debug(info, y =10, x = 10):
display_surface = pygame.display.get_surface()
debug_surf = font.render(str(info), True, 'White')
debug_rect = debug_surf.get_rect(topleft = (x,y))
pygame.draw.rect(display_surface, 'Black', debug_rect)
display_surface.blit(debug_surf, debug_rect)

View file

@ -1,57 +0,0 @@
# game setup
WIDTH = 1280
HEIGHT = 720
FPS = 60
TILESIZE = 64
HITBOX_OFFSET = {
'player': (-6, -26),
'camera': (-50, -50),
'object': (0, -40),
'grass': (0, -10),
'invisible': (0, 0)
}
# ui
BAR_HEIGHT = 20
HEALTH_BAR_WIDTH = 200
ENERGY_BAR_WIDTH = 140
ITEM_BOX_SIZE = 80
UI_FONT = '../Graphics/font/joystix.ttf'
UI_FONT_SIZE = 18
# 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/weapons/sword/full.png'},
'lance': {'cooldown': 400, 'damage': 30,'graphic':'../Graphics/weapons/lance/full.png'},
'axe': {'cooldown': 300, 'damage': 20, 'graphic':'../Graphics/weapons/axe/full.png'},
'rapier':{'cooldown': 50, 'damage': 8, 'graphic':'../Graphics/weapons/rapier/full.png'},
'sai':{'cooldown': 80, 'damage': 10, 'graphic':'../Graphics/weapons/sai/full.png'}}
# magic
magic_data = {
'flame': {'strength': 5,'cost': 20,'graphic':'../Graphics/particles/flame/fire.png'},
'heal' : {'strength': 20,'cost': 10,'graphic':'../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,21 +0,0 @@
import pygame
from csv import reader
from os import walk
def import_csv_layout(path):
terrain_map = []
with open(path) as level_map:
layout = reader(level_map, delimiter = ',')
for row in layout:
terrain_map.append(list(row))
return terrain_map
def import_folder(path):
surface_list = []
for _, __, img_files in walk(path):
for image in img_files:
full_path = f"{path}/{image}"
image_surf = pygame.image.load(full_path).convert_alpha()
surface_list.append(image_surf)
return surface_list

View file

@ -1,35 +0,0 @@
import pygame
class Camera(pygame.sprite.Group):
def __init__(self):
super().__init__()
# General Setup
self.display_surface = pygame.display.get_surface()
self.half_width = self.display_surface.get_size()[0] // 2
self.half_height = self.display_surface.get_size()[1] // 2
self.offset = pygame.math.Vector2(100, 200)
# Creating the floor
self.floor_surf = pygame.image.load('../Graphics/tilemap/ground.png').convert()
self.floor_rect = self.floor_surf.get_rect(topleft = (0,0))
def custom_draw(self, player):
self.sprite_type = player.sprite_type
#Getting the offset
self.offset.x = player.rect.centerx - self.half_width
self.offset.y = player.rect.centery - self.half_height
# Drawing the floor
floor_offset_pos = self.floor_rect.topleft - self.offset
self.display_surface.blit(self.floor_surf, floor_offset_pos)
for sprite in sorted(self.sprites(), key = lambda sprite: sprite.rect.centery):
offset_pos = sprite.rect.topleft - self.offset
self.display_surface.blit(sprite.image, offset_pos)
def enemy_update(self, player):
enemy_sprites = [sprite for sprite in self.sprites() if hasattr(sprite, 'sprite_type') and sprite.sprite_type == 'enemy']
for enemy in enemy_sprites:
enemy.enemy_update(player)

View file

@ -1,61 +0,0 @@
import pygame
import random
from utils.settings import *
from utils.support import import_folder
class Observer(pygame.sprite.Sprite):
def __init__(self, position, groups):
super().__init__(groups)
self.sprite_type = 'camera'
self.image = pygame.image.load('../Graphics/observer.png').convert_alpha()
self.rect = self.image.get_rect(topleft = position)
self.hitbox = self.rect.inflate(HITBOX_OFFSET[self.sprite_type])
# Stats
self.exp = -1 # This prints OBSERVER in the UI
self.speed = 10 # Speed for moving around
#Movement
self.direction = pygame.math.Vector2()
def input(self):
keys = pygame.key.get_pressed()
# Movement Input
if keys[pygame.K_w]:
self.direction.y = -1
self.status = 'up'
self.can_move = False
elif keys[pygame.K_s]:
self.direction.y = 1
self.status = 'down'
self.can_move = False
else:
self.direction.y = 0
if keys[pygame.K_a]:
self.direction.x = -1
self.status = 'left'
self.can_move = False
elif keys[pygame.K_d]:
self.direction.x = 1
self.status = 'right'
self.can_move = False
else:
self.direction.x = 0
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.hitbox.x += self.direction.x * speed
self.hitbox.y += self.direction.y * speed
self.rect.center = self.hitbox.center
def update(self):
self.input()
self.move(self.speed)

View file

View file

@ -1,50 +0,0 @@
import pygame
import sys
import os
from utils.settings import *
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))
pygame.display.set_caption('Pneuma')
self.clock = pygame.time.Clock()
self.level = Level()
# Sound
main_sound = pygame.mixer.Sound('../Graphics/audio/main.ogg')
main_sound.set_volume(0.4)
main_sound.play(loops=-1)
def run(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_m:
self.level.toggle_menu()
self.screen.fill(WATER_COLOR)
self.level.run()
pygame.display.update()
self.clock.tick(FPS)
if __name__ == '__main__':
game = Game()
while True:
game.run()

View file

@ -1,62 +0,0 @@
import pygame
import random
from utils.settings import *
from utils.support import import_folder
class Camera(pygame.sprite.Sprite):
def __init__(self, position, groups):
super().__init__(groups)
self.sprite_type = 'camera'
self.image = pygame.image.load(
'../Graphics/graphics/camera.png').convert_alpha()
self.rect = self.image.get_rect(topleft=position)
self.hitbox = self.rect.inflate(HITBOX_OFFSET[self.sprite_type])
# Stats
self.exp = -1 # This prints OBSERVER in the UI
self.speed = 10 # Speed for moving around
# Movement
self.direction = pygame.math.Vector2()
def input(self):
keys = pygame.key.get_pressed()
# Movement Input
if keys[pygame.K_w]:
self.direction.y = -1
self.status = 'up'
self.can_move = False
elif keys[pygame.K_s]:
self.direction.y = 1
self.status = 'down'
self.can_move = False
else:
self.direction.y = 0
if keys[pygame.K_a]:
self.direction.x = -1
self.status = 'left'
self.can_move = False
elif keys[pygame.K_d]:
self.direction.x = 1
self.status = 'right'
self.can_move = False
else:
self.direction.x = 0
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.hitbox.x += self.direction.x * speed
self.hitbox.y += self.direction.y * speed
self.rect.center = self.hitbox.center
def update(self):
self.input()
self.move(self.speed)

View file

@ -1,162 +0,0 @@
import pygame
from utils.settings import *
from utils.support import import_folder
from objects.entity import Entity
class Enemy(Entity):
def __init__(self, monster_name, position, groups, obstacle_sprites, damage_player, trigger_death_particles, add_exp, is_AI, state):
super().__init__(groups, is_AI, state)
# General setup
self.sprite_type = 'enemy'
# Graphics setup
self.import_graphics(monster_name)
self.status = 'idle'
self.image = self.animations[self.status][self.frame_index]
# Movement
self.rect = self.image.get_rect(topleft=position)
self.hitbox = self.rect.inflate(0, -10)
# Stats
self.monster_name = monster_name
monster_info = monster_data[self.monster_name]
self.health = monster_info['health']
self.exp = monster_info['exp']
self.speed = monster_info['speed']
self.attack_damage = monster_info['damage']
self.resistance = monster_info['resistance']
self.attack_radius = monster_info['attack_radius']
self.notice_radius = monster_info['notice_radius']
self.attack_type = monster_info['attack_type']
# Sounds
self.attack_sound = pygame.mixer.Sound(monster_info['attack_sound'])
self.death_sound = pygame.mixer.Sound('../Graphics/audio/death.wav')
self.hit_sound = pygame.mixer.Sound('../Graphics/audio/hit.wav')
self.death_sound.set_volume(0.2)
self.hit_sound.set_volume(0.2)
self.attack_sound.set_volume(0.2)
# Player Interaction
self.can_attack = True
self.attack_time = None
self.attack_cooldown = 400
self.damage_player = damage_player
self.trigger_death_particles = trigger_death_particles
self.add_exp = add_exp
# Invincibility times
self.vulnerable = True
self.hit_time = None
self.invincibility_duration = 300
self.obstacle_sprites = obstacle_sprites
def import_graphics(self, name):
self.animations = {'idle': [], 'move': [], 'attack': []}
main_path = f"../Graphics/graphics/monsters/{name}"
for animation in self.animations.keys():
self.animations[animation] = import_folder(
f"{main_path}/{animation}")
def get_player_distance_direction(self, player):
enemy_vector = pygame.math.Vector2(self.rect.center)
player_vector = pygame.math.Vector2(player.rect.center)
distance = (player_vector - enemy_vector).magnitude()
if distance > 0:
direction = (player_vector - enemy_vector).normalize()
else:
direction = pygame.math.Vector2()
return (distance, direction)
def get_status(self, player):
distance = self.get_player_distance_direction(player)[0]
if distance <= self.attack_radius and self.can_attack:
if self.status != 'attack':
self.frame_index = 0
self.status = 'attack'
elif distance <= self.notice_radius:
self.status = 'move'
else:
self.status = 'idle'
def actions(self, player):
if self.status == 'attack':
self.attack_sound.play()
self.attack_time = pygame.time.get_ticks()
self.damage_player(self.attack_damage, self.attack_type)
elif self.status == 'move':
self.direction = self.get_player_distance_direction(player)[1]
else:
self.direction = pygame.math.Vector2()
def animate(self):
animation = self.animations[self.status]
self.frame_index += self.animation_speed
if self.frame_index >= len(animation):
if self.status == 'attack':
self.can_attack = False
self.frame_index = 0
self.image = animation[int(self.frame_index)]
self.rect = self.image.get_rect(center=self.hitbox.center)
if not self.vulnerable:
alpha = self.wave_value()
self.image.set_alpha(alpha)
else:
self.image.set_alpha(255)
def cooldowns(self):
current_time = pygame.time.get_ticks()
if not self.can_attack:
if current_time - self.attack_time >= self.attack_cooldown:
self.can_attack = True
if not self.vulnerable:
if current_time - self.hit_time >= self.invincibility_duration:
self.vulnerable = True
def get_damage(self, player, attack_type):
if self.vulnerable:
self.hit_sound.play()
self.direction = self.get_player_distance_direction(player)[1]
if attack_type == 'weapon':
self.health -= player.get_full_weapon_damage()
else:
self.health -= player.get_full_magic_damage()
self.hit_time = pygame.time.get_ticks()
self.vulnerable = False
def check_death(self):
if self.health <= 0:
self.trigger_death_particles(self.rect.center, self.monster_name)
self.add_exp(self.exp)
self.death_sound.play()
self.kill()
def hit_reaction(self):
if not self.vulnerable:
self.direction *= -self.resistance
def update(self):
self.hit_reaction()
self.move(self.speed)
self.animate()
self.cooldowns()
self.check_death()
def enemy_update(self, player):
self.get_status(player)
self.actions(player)

View file

@ -1,147 +0,0 @@
import pygame
from math import sin
import random
from utils.settings import *
class Entity(pygame.sprite.Sprite):
def __init__(self, groups, is_AI, state=None):
super().__init__(groups)
# Animation
self.frame_index = 0
self.animation_speed = 0.15
# Movement
self.direction = pygame.math.Vector2()
self.move_cooldown = 150
self.can_move = True
self.move_time = None
# AI Setup
if is_AI:
self.possible_actions = {
0: ('up', -1, 0),
1: ('down', 1, 0),
2: ('left', 0, -1),
3: ('right', 0, 1),
4: ('attack', None, None),
5: ('magic', None, None),
6: ('rotate_weapon', None, None),
7: ('swap_magic', None, None)
}
self.state = state
self.distance_direction_to_player = [
float('inf'), 0, 0, None, None, None, None, None, None, None, None, None]*5
# self.agent = Agent(self.possible_actions, self.distance_direction_to_player, self.stats, self.exp, None, None)
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.hitbox.x += self.direction.x * speed
self.collision('horizontal')
self.hitbox.y += self.direction.y * speed
self.collision('vertical')
self.rect.center = self.hitbox.center
def collision(self, direction):
if direction == 'horizontal':
for sprite in self.obstacle_sprites:
# The following works for static obstacles only
if sprite.hitbox.colliderect(self.hitbox):
# Moving Right
if self.direction.x > 0:
self.hitbox.right = sprite.hitbox.left
# Moving Left
if self.direction.x < 0:
self.hitbox.left = sprite.hitbox.right
if direction == 'vertical':
for sprite in self.obstacle_sprites:
# The following works for static obstacles only
if sprite.hitbox.colliderect(self.hitbox):
# Moving Down
if self.direction.y > 0:
self.hitbox.bottom = sprite.hitbox.top
# Moving Up
if self.direction.y < 0:
self.hitbox.top = sprite.hitbox.bottom
def input(self):
if not self.attacking and self.can_move:
keys = pygame.key.get_pressed()
button = random.randint(0, 5)
self.move_time = pygame.time.get_ticks()
# Movement Input
if button == 0: # keys[pygame.K_w]:
self.direction.y = -1
self.status = 'up'
self.can_move = False
elif button == 1: # keys[pygame.K_s]:
self.direction.y = 1
self.status = 'down'
self.can_move = False
else:
self.direction.y = 0
if button == 2: # keys[pygame.K_a]:
self.direction.x = -1
self.status = 'left'
self.can_move = False
elif button == 3: # keys[pygame.K_d]:
self.direction.x = 1
self.status = 'right'
self.can_move = False
else:
self.direction.x = 0
# Combat Input
if button == 4: # keys[pygame.K_e]:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
self.create_attack_sprite()
self.weapon_attack_sound.play()
# Magic Input
if button == 5: # keys[pygame.K_q]:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
style = list(magic_data.keys())[self.magic_index]
strength = list(magic_data.values())[
self.magic_index]['strength'] + self.stats['magic']
cost = list(magic_data.values())[self.magic_index]['cost']
self.create_magic_sprite(style, strength, cost)
# Rotating Weapons
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.weapon_index < len(list(weapon_data.keys())) - 1:
self.weapon_index += 1
else:
self.weapon_index = 0
self.weapon = list(weapon_data.keys())[self.weapon_index]
# Swap Spells
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.magic_index < len(list(magic_data.keys())) - 1:
self.magic_index += 1
else:
self.magic_index = 0
self.magic = list(magic_data.keys())[self.magic_index]
def wave_value(self):
value = sin(pygame.time.get_ticks())
if value >= 0:
return 255
else:
return 0

View file

@ -1,205 +0,0 @@
import pygame
from random import choice, randint
from utils.settings import *
from utils.debug import debug
from utils.support import *
from UI.ui import UI
from UI.upgrade import Upgrade
from effects.particles import AnimationPlayer
from effects.magic import MagicPlayer
from effects.weapon import Weapon
from objects.tile import Tile
from objects.player import Player
from objects.enemy import Enemy
from objects.camera import Camera
class Level:
def __init__(self):
# General Settings
self.game_paused = False
# Get the display surface
self.display_surface = pygame.display.get_surface()
# Sprite Group setup
self.visible_sprites = YSortCameraGroup()
self.obstacle_sprites = pygame.sprite.Group()
self.attack_sprites = pygame.sprite.Group()
self.attackable_sprites = pygame.sprite.Group()
# Combat Sprite setup
self.current_attack = None
# Sprite setup
self.create_map()
# UI setup
self.ui = UI()
self.upgrade = Upgrade(self.player)
# Particle setup
self.animation_player = AnimationPlayer()
self.magic_player = MagicPlayer(self.animation_player)
def create_map(self):
layouts = {
'boundary': import_csv_layout('../Graphics/map/map_FloorBlocks.csv'),
'grass': import_csv_layout('../Graphics/map/map_Grass.csv'),
'objects': import_csv_layout('../Graphics/map/map_Objects.csv'),
'entities': import_csv_layout('../Graphics/map/map_Entities.csv')
}
graphics = {
'grass': import_folder('../Graphics/graphics/grass'),
'objects': import_folder('../Graphics/graphics/objects')
}
for style, layout in layouts.items():
for row_index, row in enumerate(layout):
for col_index, col in enumerate(row):
if col != '-1':
x = col_index * TILESIZE
y = row_index * TILESIZE
if style == 'boundary':
Tile((x, y), [self.obstacle_sprites], 'invisible')
if style == 'grass':
random_grass_image = choice(graphics['grass'])
Tile((x, y), [self.visible_sprites, self.obstacle_sprites,
self.attackable_sprites], 'grass', random_grass_image)
if style == 'objects':
surf = graphics['objects'][int(col)]
Tile((x, y), [self.visible_sprites,
self.obstacle_sprites], 'object', surf)
# The numbers represent their IDs in the map .csv files generated from TILED.
if style == 'entities':
if col == '394':
self.player = Player((x, y), [self.visible_sprites], self.obstacle_sprites, self.create_attack_sprite,
self.delete_attack_sprite, self.create_magic_sprite, is_AI=True, state=self.get_state)
elif col == '395':
self.camera = Camera(
(x, y), [self.visible_sprites])
else:
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.obstacle_sprites,
self.damage_player, self.trigger_death_particles, self.add_exp, is_AI=False, state=None)
def create_attack_sprite(self):
self.current_attack = Weapon(
self.player, [self.visible_sprites, self.attack_sprites])
def delete_attack_sprite(self):
if self.current_attack:
self.current_attack.kill()
self.current_attack = None
def create_magic_sprite(self, style, strength, cost):
if style == 'heal':
self.magic_player.heal(self.player, strength, cost, [
self.visible_sprites])
if style == 'flame':
self.magic_player.flame(
self.player, cost, [self.visible_sprites, self.attack_sprites])
def player_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_damage(
self.player, attack_sprite.sprite_type)
def get_state(self):
state = []
enemy_sprites = [sprite for sprite in self.visible_sprites if hasattr(
sprite, 'sprite_type') and sprite.sprite_type == 'enemy']
for enemy in enemy_sprites:
distance, direction = enemy.get_player_distance_direction(
self.player)
state.append([(distance, direction.x, direction.y, enemy.monster_name, enemy.health, enemy.exp, enemy.speed,
enemy.attack_damage, enemy.resistance, enemy.attack_radius, enemy.notice_radius, enemy.attack_type)])
# Sort by distance
state = sorted(state, key=lambda x: x[0])
# Consider only the closest 5 enemies
state = state[:5]
# If there are fewer than 5 enemies, pad the state with placeholder values
while len(state) < 5:
state.append((float('inf'), 0, 0, None, None, None,
None, None, None, None, None, None))
# Flatten the state to be a single list of numbers and strings
state = [item for sublist in state for item in sublist]
return state
def damage_player(self, amount, attack_type):
if self.player.vulnerable:
self.player.health -= amount
if self.player.health < 0:
self.player.health = 0
self.player.vulnerable = False
self.player.hurt_time = pygame.time.get_ticks()
self.animation_player.generate_particles(
attack_type, self.player.rect.center, [self.visible_sprites])
def trigger_death_particles(self, position, particle_type):
self.animation_player.generate_particles(
particle_type, position, [self.visible_sprites])
def add_exp(self, amount):
self.player.exp += amount
def toggle_menu(self):
self.game_paused = not self.game_paused
def run(self):
# Draw the game
self.visible_sprites.custom_draw(self.camera)
self.ui.display(self.camera)
if self.game_paused:
if self.visible_sprites.sprite_type == 'player':
self.upgrade.display()
pass
else:
# Update the game
self.player.distance_direction_to_player = self.get_state()
self.visible_sprites.update()
self.visible_sprites.enemy_update(self.player)
self.player_attack_logic()
if self.player.health <= 0:
self.__init__()

View file

@ -1,203 +0,0 @@
import pygame
import random
import numpy as np
from utils.settings import *
from utils.support import import_folder
from objects.entity import Entity
from rl.agent import Agent
from rl.rl_settings import *
class Player(Entity):
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)
self.image = pygame.image.load(
'../Graphics/graphics/player/down/down_0.png').convert_alpha()
self.rect = self.image.get_rect(topleft=position)
self.hitbox = self.rect.inflate(HITBOX_OFFSET['player'])
self.sprite_type = 'player'
# 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
# Magic
self.create_magic_sprite = create_magic_sprite
# 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
# 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
# Stats
self.stats = {
'health': 100,
'energy': 60,
'attack': 10,
'magic': 4,
'speed': 5
}
self.max_stats = {
'health': 300,
'energy': 150,
'attack': 20,
'magic': 10,
'speed': 10
}
self.upgrade_costs = {
'health': 100,
'energy': 100,
'attack': 100,
'magic': 100,
'speed': 100
}
# AI setup
self.is_AI = is_AI
if self.is_AI:
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)
self.health = self.stats['health']
self.energy = self.stats['energy']
self.exp = 0
self.speed = self.stats['speed']
# Damage timer
self.vulnerable = True
self.hurt_time = None
self.invulnerability_duration = 300
self.obstacle_sprites = obstacle_sprites
# Import Sounds
self.weapon_attack_sound = pygame.mixer.Sound(
'../Graphics/audio/sword.wav')
self.weapon_attack_sound.set_volume(0.2)
def import_player_assets(self):
character_path = '../Graphics/graphics/player'
self.animations = {
'up': [], 'down': [], 'left': [], 'right': [],
'up_idle': [], 'down_idle': [], 'left_idle': [], 'right_idle': [],
'up_attack': [], 'down_attack': [], 'left_attack': [], 'right_attack': []
}
for animation in self.animations.keys():
full_path = f"{character_path}/{animation}"
self.animations[animation] = import_folder(full_path)
def get_status(self):
# 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'
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', '')
def get_full_weapon_damage(self):
base_damage = self.stats['attack']
weapon_damage = weapon_data[self.weapon]['damage']
return (base_damage + weapon_damage)
def get_full_magic_damage(self):
base_damage = self.stats['magic']
spell_damage = magic_data[self.magic]['strength']
return (base_damage + spell_damage)
def get_value_by_index(self, index):
return list(self.stats.values())[index]
def get_cost_by_index(self, index):
return list(self.upgrade_costs.values())[index]
def cooldowns(self):
current_time = pygame.time.get_ticks()
if self.attacking:
if current_time - self.attack_time > self.attack_cooldown + weapon_data[self.weapon]['cooldown']:
self.attacking = False
self.delete_attack_sprite()
if not self.can_rotate_weapon:
if current_time - self.weapon_rotation_time > self.rotate_attack_cooldown:
self.can_rotate_weapon = True
if not self.can_swap_magic:
if current_time - self.magic_swap_time > self.rotate_attack_cooldown:
self.can_swap_magic = True
if not self.vulnerable:
if current_time - self.hurt_time >= self.invulnerability_duration:
self.vulnerable = True
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']
def animate(self):
animation = self.animations[self.status]
self.frame_index += self.animation_speed
if self.frame_index >= len(animation):
self.frame_index = 0
# Set the image
self.image = animation[int(self.frame_index)]
self.rect = self.image.get_rect(center=self.hitbox.center)
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()
self.distance_direction_to_player = self.state()
# if self.is_AI:
# self.agent.act(self.distance_direction_to_player)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 994 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1,012 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 747 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 384 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 267 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 266 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 269 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 971 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 962 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 886 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 811 B

Some files were not shown because too many files have changed in this diff Show more