Zurück
RPG Game by Bekim Jahmurataj
debug.py
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)
enemy.py
import pygame
from settings import *
from entity import Entity
from support import *
class Enemy(Entity):
def __init__(self,monster_name,pos,groups,obstacle_sprites,damage_player,trigger_death_particles,add_exp):
# general setup
super().__init__(groups)
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 = pos)
self.hitbox = self.rect.inflate(0,-10)
self.obstacle_sprites = obstacle_sprites
# 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']
# 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 timer
self.vulnerable = True
self.hit_time = None
self.invincibility_duration = 300
# sounds
self.death_sound = pygame.mixer.Sound('../audio/death.wav')
self.hit_sound = pygame.mixer.Sound('../audio/hit.wav')
self.attack_sound = pygame.mixer.Sound(monster_info['attack_sound'])
self.death_sound.set_volume(0.6)
self.hit_sound.set_volume(0.6)
self.attack_sound.set_volume(0.6)
def import_graphics(self,name):
self.animations = {'idle':[],'move':[],'attack':[]}
main_path = f'../graphics/monsters/{name}/'
for animation in self.animations.keys():
self.animations[animation] = import_folder(main_path + animation)
def get_player_distance_direction(self,player):
enemy_vec = pygame.math.Vector2(self.rect.center)
player_vec = pygame.math.Vector2(player.rect.center)
distance = (player_vec - enemy_vec).magnitude()
if distance > 0:
direction = (player_vec - enemy_vec).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_time = pygame.time.get_ticks()
self.damage_player(self.attack_damage,self.attack_type)
self.attack_sound.play()
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.kill()
self.trigger_death_particles(self.rect.center,self.monster_name)
self.add_exp(self.exp)
self.death_sound.play()
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)
entity.py
import pygame
from math import sin
class Entity(pygame.sprite.Sprite):
def __init__(self,groups):
super().__init__(groups)
self.frame_index = 0
self.animation_speed = 0.15
self.direction = pygame.math.Vector2()
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:
if sprite.hitbox.colliderect(self.hitbox):
if self.direction.x > 0: # moving right
self.hitbox.right = sprite.hitbox.left
if self.direction.x < 0: # moving left
self.hitbox.left = sprite.hitbox.right
if direction == 'vertical':
for sprite in self.obstacle_sprites:
if sprite.hitbox.colliderect(self.hitbox):
if self.direction.y > 0: # moving down
self.hitbox.bottom = sprite.hitbox.top
if self.direction.y < 0: # moving up
self.hitbox.top = sprite.hitbox.bottom
def wave_value(self):
value = sin(pygame.time.get_ticks())
if value >= 0:
return 255
else:
return 0
level.py
import pygame
from settings import *
from tile import Tile
from player import Player
from debug import debug
from support import *
from random import choice, randint
from weapon import Weapon
from ui import UI
from enemy import Enemy
from particles import AnimationPlayer
from magic import MagicPlayer
from upgrade import Upgrade
class Level:
def __init__(self):
# get the display surface
self.display_surface = pygame.display.get_surface()
self.game_paused = False
# sprite group setup
self.visible_sprites = YSortCameraGroup()
self.obstacle_sprites = pygame.sprite.Group()
# attack sprites
self.current_attack = None
self.attack_sprites = pygame.sprite.Group()
self.attackable_sprites = pygame.sprite.Group()
# sprite setup
self.create_map()
# user interface
self.ui = UI()
self.upgrade = Upgrade(self.player)
# particles
self.animation_player = AnimationPlayer()
self.magic_player = MagicPlayer(self.animation_player)
def create_map(self):
layouts = {
'boundary': import_csv_layout('../map/map_FloorBlocks.csv'),
'grass': import_csv_layout('../map/map_Grass.csv'),
'object': import_csv_layout('../map/map_Objects.csv'),
'entities': import_csv_layout('../map/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 == 'object':
surf = graphics['objects'][int(col)]
Tile((x,y),[self.visible_sprites,self.obstacle_sprites],'object',surf)
if style == 'entities':
if col == '394':
self.player = Player(
(x,y),
[self.visible_sprites],
self.obstacle_sprites,
self.create_attack,
self.destroy_attack,
self.create_magic)
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)
def create_attack(self):
self.current_attack = Weapon(self.player,[self.visible_sprites,self.attack_sprites])
def create_magic(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 destroy_attack(self):
if self.current_attack:
self.current_attack.kill()
self.current_attack = None
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(pos - offset,[self.visible_sprites])
target_sprite.kill()
else:
target_sprite.get_damage(self.player,attack_sprite.sprite_type)
def damage_player(self,amount,attack_type):
if self.player.vulnerable:
self.player.health -= amount
self.player.vulnerable = False
self.player.hurt_time = pygame.time.get_ticks()
self.animation_player.create_particles(attack_type,self.player.rect.center,[self.visible_sprites])
def trigger_death_particles(self,pos,particle_type):
self.animation_player.create_particles(particle_type,pos,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):
self.visible_sprites.custom_draw(self.player)
self.ui.display(self.player)
if self.game_paused:
self.upgrade.display()
else:
self.visible_sprites.update()
self.visible_sprites.enemy_update(self.player)
self.player_attack_logic()
class YSortCameraGroup(pygame.sprite.Group):
def __init__(self):
# general setup
super().__init__()
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()
# 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):
# 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 self.sprites():
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)
magic.py
import pygame
from settings import *
from random import randint
class MagicPlayer:
def __init__(self,animation_player):
self.animation_player = animation_player
self.sounds = {
'heal': pygame.mixer.Sound('../audio/heal.wav'),
'flame':pygame.mixer.Sound('../audio/Fire.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.create_particles('aura',player.rect.center,groups)
self.animation_player.create_particles('heal',player.rect.center,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: #horizontal
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.create_particles('flame',(x,y),groups)
else: # vertical
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.create_particles('flame',(x,y),groups)
main.py
import pygame, sys
from settings import *
from level import Level
class Game:
def __init__(self):
# general setup
pygame.init()
self.screen = pygame.display.set_mode((WIDTH,HEIGTH))
pygame.display.set_caption('BELDA the King of Jahmurataj')
self.clock = pygame.time.Clock()
self.level = Level()
# sound
main_sound = pygame.mixer.Sound('../audio/main.ogg')
main_sound.set_volume(0.5)
main_sound.play(loops = -1)
def run(self):
while True:
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()
game.run()
particles.py
import pygame
from support import import_folder
from random import choice
class AnimationPlayer:
def __init__(self):
self.frames = {
# magic
'flame': import_folder('../graphics/particles/flame/frames'),
'aura': import_folder('../graphics/particles/aura'),
'heal': import_folder('../graphics/particles/heal/frames'),
# attacks
'claw': import_folder('../graphics/particles/claw'),
'slash': import_folder('../graphics/particles/slash'),
'sparkle': import_folder('../graphics/particles/sparkle'),
'leaf_attack': import_folder('../graphics/particles/leaf_attack'),
'thunder': import_folder('../graphics/particles/thunder'),
# monster deaths
'squid': import_folder('../graphics/particles/smoke_orange'),
'raccoon': import_folder('../graphics/particles/raccoon'),
'spirit': import_folder('../graphics/particles/nova'),
'bamboo': import_folder('../graphics/particles/bamboo'),
# leafs
'leaf': (
import_folder('../graphics/particles/leaf1'),
import_folder('../graphics/particles/leaf2'),
import_folder('../graphics/particles/leaf3'),
import_folder('../graphics/particles/leaf4'),
import_folder('../graphics/particles/leaf5'),
import_folder('../graphics/particles/leaf6'),
self.reflect_images(import_folder('../graphics/particles/leaf1')),
self.reflect_images(import_folder('../graphics/particles/leaf2')),
self.reflect_images(import_folder('../graphics/particles/leaf3')),
self.reflect_images(import_folder('../graphics/particles/leaf4')),
self.reflect_images(import_folder('../graphics/particles/leaf5')),
self.reflect_images(import_folder('../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,pos,groups):
animation_frames = choice(self.frames['leaf'])
ParticleEffect(pos,animation_frames,groups)
def create_particles(self,animation_type,pos,groups):
animation_frames = self.frames[animation_type]
ParticleEffect(pos,animation_frames,groups)
class ParticleEffect(pygame.sprite.Sprite):
def __init__(self,pos,animation_frames,groups):
super().__init__(groups)
self.sprite_type = 'magic'
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 = pos)
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()
player.py
import pygame
from settings import *
from support import import_folder
from entity import Entity
class Player(Entity):
def __init__(self,pos,groups,obstacle_sprites,create_attack,destroy_attack,create_magic):
super().__init__(groups)
self.image = pygame.image.load('../graphics/test/player.png').convert_alpha()
self.rect = self.image.get_rect(topleft = pos)
self.hitbox = self.rect.inflate(-6,HITBOX_OFFSET['player'])
# graphics setup
self.import_player_assets()
self.status = 'down'
# movement
self.attacking = False
self.attack_cooldown = 400
self.attack_time = None
self.obstacle_sprites = obstacle_sprites
# weapon
self.create_attack = create_attack
self.destroy_attack = destroy_attack
self.weapon_index = 0
self.weapon = list(weapon_data.keys())[self.weapon_index]
self.can_switch_weapon = True
self.weapon_switch_time = None
self.switch_duration_cooldown = 200
# magic
self.create_magic = create_magic
self.magic_index = 0
self.magic = list(magic_data.keys())[self.magic_index]
self.can_switch_magic = True
self.magic_switch_time = None
# stats
self.stats = {'health': 100,'energy':60,'attack': 10,'magic': 4,'speed': 5}
self.max_stats = {'health': 300, 'energy': 140, 'attack': 20, 'magic' : 10, 'speed': 10}
self.upgrade_cost = {'health': 100, 'energy': 100, 'attack': 100, 'magic' : 100, 'speed': 100}
self.health = self.stats['health'] * 0.5
self.energy = self.stats['energy'] * 0.8
self.exp = 5000
self.speed = self.stats['speed']
# damage timer
self.vulnerable = True
self.hurt_time = None
self.invulnerability_duration = 500
# import a sound
self.weapon_attack_sound = pygame.mixer.Sound('../audio/sword.wav')
self.weapon_attack_sound.set_volume(0.4)
def import_player_assets(self):
character_path = '../graphics/player/'
self.animations = {'up': [],'down': [],'left': [],'right': [],
'right_idle':[],'left_idle':[],'up_idle':[],'down_idle':[],
'right_attack':[],'left_attack':[],'up_attack':[],'down_attack':[]}
for animation in self.animations.keys():
full_path = character_path + animation
self.animations[animation] = import_folder(full_path)
def input(self):
if not self.attacking:
keys = pygame.key.get_pressed()
# movement input
if keys[pygame.K_UP]:
self.direction.y = -1
self.status = 'up'
elif keys[pygame.K_DOWN]:
self.direction.y = 1
self.status = 'down'
else:
self.direction.y = 0
if keys[pygame.K_RIGHT]:
self.direction.x = 1
self.status = 'right'
elif keys[pygame.K_LEFT]:
self.direction.x = -1
self.status = 'left'
else:
self.direction.x = 0
# attack input
if keys[pygame.K_SPACE]:
self.attacking = True
self.attack_time = pygame.time.get_ticks()
self.create_attack()
self.weapon_attack_sound.play()
# magic input
if keys[pygame.K_LCTRL]:
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(style,strength,cost)
if keys[pygame.K_q] and self.can_switch_weapon:
self.can_switch_weapon = False
self.weapon_switch_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]
if keys[pygame.K_e] and self.can_switch_magic:
self.can_switch_magic = False
self.magic_switch_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 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 = 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 = self.status + '_attack'
else:
if 'attack' in self.status:
self.status = self.status.replace('_attack','')
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.destroy_attack()
if not self.can_switch_weapon:
if current_time - self.weapon_switch_time >= self.switch_duration_cooldown:
self.can_switch_weapon = True
if not self.can_switch_magic:
if current_time - self.magic_switch_time >= self.switch_duration_cooldown:
self.can_switch_magic = True
if not self.vulnerable:
if current_time - self.hurt_time >= self.invulnerability_duration:
self.vulnerable = True
def animate(self):
animation = self.animations[self.status]
# loop over the frame index
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)
# flicker
if not self.vulnerable:
alpha = self.wave_value()
self.image.set_alpha(alpha)
else:
self.image.set_alpha(255)
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_cost.values())[index]
def energy_recovery(self):
if self.energy < self.stats['energy']:
self.energy += 0.01 * self.stats['magic']
else:
self.energy = self.stats['energy']
def update(self):
self.input()
self.cooldowns()
self.get_status()
self.animate()
self.move(self.stats['speed'])
self.energy_recovery()
settings.py
# game setup
WIDTH = 1280
HEIGTH = 720
FPS = 60
TILESIZE = 64
HITBOX_OFFSET = {
'player': -26,
'object': -40,
'grass': -10,
'invisible': 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':'../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':'../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':'../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':'../audio/attack/slash.wav', 'speed': 3, 'resistance': 3, 'attack_radius': 50, 'notice_radius': 300}}
support.py
from csv import reader
from os import walk
import pygame
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 = path + '/' + image
image_surf = pygame.image.load(full_path).convert_alpha()
surface_list.append(image_surf)
return surface_list
tile.py
import pygame
from settings import *
class Tile(pygame.sprite.Sprite):
def __init__(self,pos,groups,sprite_type,surface = pygame.Surface((TILESIZE,TILESIZE))):
super().__init__(groups)
self.sprite_type = sprite_type
y_offset = HITBOX_OFFSET[sprite_type]
self.image = surface
if sprite_type == 'object':
self.rect = self.image.get_rect(topleft = (pos[0],pos[1] - TILESIZE))
else:
self.rect = self.image.get_rect(topleft = pos)
self.hitbox = self.rect.inflate(0,y_offset)
ui.py
import pygame
from settings import *
class UI:
def __init__(self):
# general
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 magic dictionary
self.magic_graphics = []
for magic in magic_data.values():
magic = pygame.image.load(magic['graphic']).convert_alpha()
self.magic_graphics.append(magic)
def show_bar(self,current,max_amount,bg_rect,color):
# draw bg
pygame.draw.rect(self.display_surface,UI_BG_COLOR,bg_rect)
# converting stat to pixel
ratio = current / max_amount
current_width = bg_rect.width * ratio
current_rect = bg_rect.copy()
current_rect.width = current_width
# drawing the bar
pygame.draw.rect(self.display_surface,color,current_rect)
pygame.draw.rect(self.display_surface,UI_BORDER_COLOR,bg_rect,3)
def show_exp(self,exp):
text_surf = self.font.render(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(20,20))
self.display_surface.blit(text_surf,text_rect)
pygame.draw.rect(self.display_surface,UI_BORDER_COLOR,text_rect.inflate(20,20),3)
def selection_box(self,left,top, has_switched):
bg_rect = pygame.Rect(left,top,ITEM_BOX_SIZE,ITEM_BOX_SIZE)
pygame.draw.rect(self.display_surface,UI_BG_COLOR,bg_rect)
if has_switched:
pygame.draw.rect(self.display_surface,UI_BORDER_COLOR_ACTIVE,bg_rect,3)
else:
pygame.draw.rect(self.display_surface,UI_BORDER_COLOR,bg_rect,3)
return bg_rect
def weapon_overlay(self,weapon_index,has_switched):
bg_rect = self.selection_box(10,630,has_switched)
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_switched):
bg_rect = self.selection_box(80,635,has_switched)
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):
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,not player.can_switch_weapon)
self.magic_overlay(player.magic_index,not player.can_switch_magic)
upgrade.py
import pygame
from settings import *
class Upgrade:
def __init__(self,player):
# general setup
self.display_surface = pygame.display.get_surface()
self.player = player
self.attribute_nr = len(player.stats)
self.attribute_names = list(player.stats.keys())
self.max_values = list(player.max_stats.values())
self.font = pygame.font.Font(UI_FONT, UI_FONT_SIZE)
# item creation
self.height = self.display_surface.get_size()[1] * 0.8
self.width = self.display_surface.get_size()[0] // 6
self.create_items()
# selection system
self.selection_index = 0
self.selection_time = None
self.can_move = True
def input(self):
keys = pygame.key.get_pressed()
if self.can_move:
if keys[pygame.K_RIGHT] and self.selection_index < self.attribute_nr - 1:
self.selection_index += 1
self.can_move = False
self.selection_time = pygame.time.get_ticks()
elif keys[pygame.K_LEFT] and self.selection_index >= 1:
self.selection_index -= 1
self.can_move = False
self.selection_time = pygame.time.get_ticks()
if keys[pygame.K_SPACE]:
self.can_move = False
self.selection_time = pygame.time.get_ticks()
self.item_list[self.selection_index].trigger(self.player)
def selection_cooldown(self):
if not self.can_move:
current_time = pygame.time.get_ticks()
if current_time - self.selection_time >= 300:
self.can_move = True
def create_items(self):
self.item_list = []
for item, index in enumerate(range(self.attribute_nr)):
# horizontal position
full_width = self.display_surface.get_size()[0]
increment = full_width // self.attribute_nr
left = (item * increment) + (increment - self.width) // 2
# vertical position
top = self.display_surface.get_size()[1] * 0.1
# create the object
item = Item(left,top,self.width,self.height,index,self.font)
self.item_list.append(item)
def display(self):
self.input()
self.selection_cooldown()
for index, item in enumerate(self.item_list):
# get attributes
name = self.attribute_names[index]
value = self.player.get_value_by_index(index)
max_value = self.max_values[index]
cost = self.player.get_cost_by_index(index)
item.display(self.display_surface,self.selection_index,name,value,max_value,cost)
class Item:
def __init__(self,l,t,w,h,index,font):
self.rect = pygame.Rect(l,t,w,h)
self.index = index
self.font = font
def display_names(self,surface,name,cost,selected):
color = TEXT_COLOR_SELECTED if selected else TEXT_COLOR
# title
title_surf = self.font.render(name,False,color)
title_rect = title_surf.get_rect(midtop = self.rect.midtop + pygame.math.Vector2(0,20))
# cost
cost_surf = self.font.render(f'{int(cost)}',False,color)
cost_rect = cost_surf.get_rect(midbottom = self.rect.midbottom - pygame.math.Vector2(0,20))
# draw
surface.blit(title_surf,title_rect)
surface.blit(cost_surf,cost_rect)
def display_bar(self,surface,value,max_value,selected):
# drawing setup
top = self.rect.midtop + pygame.math.Vector2(0,60)
bottom = self.rect.midbottom - pygame.math.Vector2(0,60)
color = BAR_COLOR_SELECTED if selected else BAR_COLOR
# bar setup
full_height = bottom[1] - top[1]
relative_number = (value / max_value) * full_height
value_rect = pygame.Rect(top[0] - 15,bottom[1] - relative_number,30,10)
# draw elements
pygame.draw.line(surface,color,top,bottom,5)
pygame.draw.rect(surface,color,value_rect)
def trigger(self,player):
upgrade_attribute = list(player.stats.keys())[self.index]
if player.exp >= player.upgrade_cost[upgrade_attribute] and player.stats[upgrade_attribute] < player.max_stats[upgrade_attribute]:
player.exp -= player.upgrade_cost[upgrade_attribute]
player.stats[upgrade_attribute] *= 1.2
player.upgrade_cost[upgrade_attribute] *= 1.4
if player.stats[upgrade_attribute] > player.max_stats[upgrade_attribute]:
player.stats[upgrade_attribute] = player.max_stats[upgrade_attribute]
def display(self,surface,selection_num,name,value,max_value,cost):
if self.index == selection_num:
pygame.draw.rect(surface,UPGRADE_BG_COLOR_SELECTED,self.rect)
pygame.draw.rect(surface,UI_BORDER_COLOR,self.rect,4)
else:
pygame.draw.rect(surface,UI_BG_COLOR,self.rect)
pygame.draw.rect(surface,UI_BORDER_COLOR,self.rect,4)
self.display_names(surface,name,cost,self.index == selection_num)
self.display_bar(surface,value,max_value,self.index == selection_num)
weapon.py
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()
# 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))