.\Zelda-with-Python\Code\Debug.py
import pygame
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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)
.\Zelda-with-Python\Code\Enemy.py
import pygame
from Settings import *
from Entity import Entity
from Support import *
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
.\Zelda-with-Python\Code\Entity.py
from cmath import rect
import pygame
from math import sin
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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:
self.hitbox.right = sprite.hitbox.left
if self.direction.x < 0:
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:
self.hitbox.bottom = sprite.hitbox.top
if self.direction.y < 0:
self.hitbox.top = sprite.hitbox.bottom
def wave_value(self):
value = sin(pygame.time.get_ticks())
if value >= 0:
return 255
else:
return 0
.\Zelda-with-Python\Code\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
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
class YSortCameraGroup(pygame.sprite.Group):
def __init__(self):
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()
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.offset.x = player.rect.centerx - self.half_width
self.offset.y = player.rect.centery - self.half_height
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)
.\Zelda-with-Python\Code\Magic.py
import pygame
from Settings import *
from random import randint
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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 + pygame.math.Vector2(0, -60), 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.create_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.create_particles("flame", (x, y), groups)
.\Zelda-with-Python\Code\Main.py
import pygame, sys
from Settings import *
from Level import Level
import os
os.chdir(os.path.dirname(os.path.abspath(__file__))
class Game:
def __init__(self):
pygame.init()
self.screen = pygame.display.set_mode((WIDTH, HEIGTH))
pygame.display.set_caption("Zelda with Python")
pygame_icon = pygame.image.load("../Graphics/Test/Player.png")
pygame.display.set_icon(pygame_icon)
self.clock = pygame.time.Clock()
self.level = Level()
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()
.\Zelda-with-Python\Code\Particles.py
import pygame
from Support import import_folder
from random import choice
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
class AnimationPlayer:
def __init__(self):
self.frames = {
"flame": import_folder("../Graphics/Particles/Flame/Frames"),
"aura": import_folder("../Graphics/Particles/Aura"),
"heal": import_folder("../Graphics/Particles/Heal/Frames"),
"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"),
"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"),
"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()
.\Zelda-with-Python\Code\Player.py
import pygame
from Support import import_folder
from Settings import *
from Entity import Entity
import os, sys
os.chdir(os.path.dirname(os.path.abspath(__file__)))
.\Zelda-with-Python\Code\Settings.py
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
WIDTH = 1280
HEIGTH = 720
FPS = 60
TILESIZE = 64
HITBOX_OFFSET = {
"player": -26,
"object": -40,
"grass": -10,
"invisible": 0
}
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
WATER_COLOR = "#71ddee"
UI_BG_COLOR = "#222222"
UI_BORDER_COLOR = "#111111"
TEXT_COLOR = "#EEEEEE"
HEALTH_COLOR = "Red"
ENERGY_COLOR = "Blue"
UI_BORDER_COLOR_ACTIVE = "Gold"
TEXT_COLOR_SELECTED = "#111111"
BAR_COLOR = "#EEEEEE"
BAR_COLOR_SELECTED = "#111111"
UPGRADE_BG_COLOR_SELECTED = "#EEEEEE"
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_data = {
"flame": {"strength": 5, "cost": 20, "graphic": "../Graphics/Particles/Flame/Fire.png"},
"heal": {"strength": 20, "cost": 10, "graphic": "../Graphics/Particles/Heal/Heal.png"}
}
monster_data = {
"squid": {"health": 100, "exp": 180, "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": 300, "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": 200, "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": 150, "damage": 6, "attack_type": "leaf_attack", "attack_sound": "../Audio/Attack/Slash.wav", "speed": 3, "resistance": 3, "attack_radius": 50, "notice_radius": 300}
}
.\Zelda-with-Python\Code\Support.py
from csv import reader
import os
from os import walk
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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
.\Zelda-with-Python\Code\Tile.py
import pygame
from Settings import *
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
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)
.\Zelda-with-Python\Code\UI.py
import pygame
from Settings import *
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
class UI:
def __init__(self):
self.display_surface = pygame.display.get_surface()
self.font = pygame.font.Font(UI_FONT, UI_FONT_SIZE)
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)
self.weapon_graphics = []
for weapon in weapon_data.values():
path = weapon["graphic"]
weapon = pygame.image.load(path).convert_alpha()
self.weapon_graphics.append(weapon)
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):
pygame.draw.rect(self.display_surface, UI_BG_COLOR, bg_rect)
ratio = current / max_amount
current_width = bg_rect.width * ratio
current_rect = bg_rect.copy()
current_rect.width = current_width
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(100, 630, 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)
.\Zelda-with-Python\Code\Upgrade.py
import imp
from traceback import print_tb
import pygame
from Settings import *
import os
os.chdir(os.path.dirname(os.path.abspath(__file__))
class Upgrade:
def __init__(self, player):
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)
self.height = self.display_surface.get_size()[1] * 0.8
self.width = self.display_surface.get_size()[0] // 6
self.create_items()
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)):
full_width = self.display_surface.get_size()[0]
increment = full_width // self.attribute_nr
left = (item * increment) + (increment - self.width) // 2
top = self.display_surface.get_size()[1] * 0.1
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):
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_surf = self.font.render(name, False, color)
title_rect = title_surf.get_rect(midtop=self.rect.midtop + pygame.math.Vector2(0, 20))
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))
surface.blit(title_surf, title_rect)
surface.blit(cost_surf, cost_rect)
def display_bar(self, surface, value, max_value, selected):
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
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)
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)
.\Zelda-with-Python\Code\Weapon.py
import os
import pygame
os.chdir(os.path.dirname(os.path.abspath(__file__)))
class Weapon(pygame.sprite.Sprite):
def __init__(self, player, groups):
super().__init__(groups)
self.sprite_type = "weapon"
direction = player.status.split("_")[0]
full_path = f"../Graphics/Weapons/{player.weapon}/{direction}.png"
self.image = pygame.image.load(full_path).convert_alpha()
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))