import pygame import random # from viecher import Skeleton, Zombie from pygame import mixer """ cultistattack_sound = mixer.Sound('audio/soundeffects/cultistattack.mp3') """ pygame.font.init() fonts = { 'medieval': 'medieval.ttf', 'minecraft': 'Minecraft Evenings.otf', '3dpixel': '3D-Pixel.ttf', '8bit': '8bitlim.ttf', '8bito': '8blimro.ttf', 'arcade': 'ARCADECLASSIC.ttf', 'modern_game': 'astron boy video.otf', 'modern': 'astron boy.otf', 'wonder': 'Beyond Wonderland.ttf', 'curved': 'Digitag.ttf', 'simple': 'DisposableDroidBB.ttf', 'rounded': 'dpcomic.ttf', 'playfull': 'Endalian Script.ttf', 'blocky': 'FREAKSOFNATURE.ttf', 'catchy': 'Future TimeSplitters.otf', 'simple_wide': 'Halo3.ttf', 'simple_fat': 'INVASION2000.ttf', 'very_gamy': 'ka1.ttf', 'simple_round': 'Karma Suture.otf', 'mono': 'manaspc.ttf', 'damaged': 'Merchant Copy.ttf', 'big_natural': 'MorialCitadel.TTF', 'spacy': 'nasalization-rg.otf', 'sci-fi': 'neuropol.otf', 'hollow_big_edge': 'papercut.ttf', 'space_shuttle': 'pdark.ttf', 'thin': 'PixelFJVerdana12pt.ttf', 'random': 'Seattle Avenue.ttf', 'pixel': 'yoster.ttf' } class Button(): def __init__(self, x, y, width, height, image, font, font_size, buttonText='', onclickFunction=None, onePress=False, attributes=None): self.font = pygame.font.Font(f'fonts/{fonts[font]}', font_size) self.x = x self.y = y self.width = width self.height = height self.attributes = attributes self.onclickFunction = onclickFunction self.onePress = onePress self.alreadyPressed = False with open(f'art/images/box/{image}', 'r') as tb: self.box = pygame.image.load(tb) self.box = pygame.transform.scale(self.box, (width, height)) self.buttonRect = pygame.Rect(self.x, self.y, self.width, self.height) self.buttonSurf = self.font.render(buttonText, True, '#baab80') def update(self, screen): mousePos = pygame.mouse.get_pos() if self.buttonRect.collidepoint(mousePos): if pygame.mouse.get_pressed(3)[0]: if self.onePress: self.onclickFunction() elif not self.alreadyPressed: if self.attributes: self.onclickFunction(*self.attributes) self.alreadyPressed = True else: self.onclickFunction() self.alreadyPressed = True else: self.alreadyPressed = False self.box.blit(self.buttonSurf, [ self.buttonRect.width/2 - self.buttonSurf.get_rect().width/2, self.buttonRect.height/2 - self.buttonSurf.get_rect().height/2 ]) screen.blit(self.box, self.buttonRect) class Label(): def __init__(self, x, y, width, height, text, font='simple', font_size=20, font_color = '#1e90ff', sprite = 'label.png') -> None: self.x = x self.y = y self.width = width self.height = height self.font = pygame.font.Font(f'fonts/{fonts[font]}', font_size) self.font_color = font_color self.text = text self.hidden = False self.sprite = sprite with open(f'art/images/box/{sprite}', 'r') as tb: self.box = pygame.image.load(tb) self.box = pygame.transform.scale(self.box, (width, height)) def draw(self, screen): if self.hidden: return with open(f'art/images/box/{self.sprite}', 'r') as tb: self.box = pygame.image.load(tb) self.box = pygame.transform.scale(self.box, (self.width, self.height)) self.labelRect = pygame.Rect(self.x, self.y, self.width, self.height) self.labelSurf = self.font.render(self.text, True, self.font_color) self.box.blit(self.labelSurf, [ self.labelRect.width / 2 - self.labelSurf.get_rect().width / 2, self.labelRect.height / 2 - self.labelSurf.get_rect().height / 2 ]) screen.blit(self.box, self.labelRect) class HealthBar(Label): def __init__(self, text, hp, x, y=8, width=256, height=24, font='simple', font_size=20, font_color='#e3d807', sprite='bossbar') -> None: super().__init__(x, y, width, height, text, font, font_size, font_color) self.text = text.capitalize() self.max = hp self.sprite = [f'{sprite}_empty.png', f'{sprite}_full.png', f'{sprite}_edge.png'] self.box = [] self.filling_should = self.width self.filling_is = self.filling_should self.rect = pygame.Rect(0, 0, 0, 0) self.percentage = 100 for sprite in self.sprite: with open(f'art/images/box/{sprite}', 'r') as tb: tmp = pygame.image.load(tb) self.box.append(pygame.transform.scale(tmp, (width, height))) def draw(self, screen): if self.hidden: return for i, sprite in enumerate(self.sprite): with open(f'art/images/box/{sprite}', 'r') as tb: if i != 1: self.box[i] = pygame.image.load(tb) self.box[i] = pygame.transform.scale(self.box[i], (self.width, self.height)) else: self.box[i] = pygame.image.load(tb) if not self.filling_is <= self.filling_should: self.filling_is -= 1.5 if self.filling_should >= self.filling_is: self.filling_is += 1.5 self.box[i] = pygame.transform.scale(self.box[i], (self.filling_is, self.height)) self.labelRect = pygame.Rect(self.x, self.y, self.width, self.height) self.labelSurf = self.font.render(f'{self.text}: {round(self.percentage, 1)}%', True, self.font_color) self.box[-1].blit(self.labelSurf, [ self.labelRect.width / 2 - self.labelSurf.get_rect().width / 2, self.labelRect.height / 2 - self.labelSurf.get_rect().height / 2 ]) for box in self.box: screen.blit(box, self.labelRect) def update(self, objects): for mob in objects[1]: if isinstance(mob, Boss): self.percentage = mob.health / (self.max / 100) self.filling_should = self.width * (self.percentage / 100) class DropDown(): def __init__(self, x, y, width, height, font, font_size, color_menu, color_option, main, options): self.rect = pygame.Rect(x, y, width, height) self.font = pygame.font.Font(f'fonts/{fonts[font]}', font_size) self.main = main self.options = options self.draw_menu = False self.menu_active = False self.active_option = -1 with open('art/images/box/textbox.png', 'r') as tb: self.box = pygame.image.load(tb) self.box = pygame.transform.scale(self.box, (width, height)) def draw(self, screen): #pygame.draw.rect(screen, self.color_menu[self.menu_active], self.rect, 0) surface = self.font.render(self.main, 1, (0, 0, 0)) self.box.blit(surface, [ self.rect.width/2 - surface.get_rect().width/2, self.rect.height/2 - surface.get_rect().height/2 ]) screen.blit(self.box, surface.get_rect(center = self.rect.center)) if self.draw_menu: for i, text in enumerate(self.options): rect = self.rect.copy() rect.y += (i+1) * self.rect.height rect.x = self.rect.x #pygame.draw.rect(screen, self.color_option[1 if i == self.active_option else 0], rect, 0) #msg = self.font.render(text, 1, (0, 0, 0)) #screen.blit(msg, msg.get_rect(center = rect.center)) surface = self.font.render(text, 1, (0, 0, 0)) self.box.blit(surface, [ rect.width/2 - surface.get_rect().width/2, rect.height/2 - surface.get_rect().height/2 ]) screen.blit(self.box, rect) def update(self, event_list): mpos = pygame.mouse.get_pos() self.menu_active = self.rect.collidepoint(mpos) self.active_option = -1 for i in range(len(self.options)): rect = self.rect.copy() rect.y += (i+1) * self.rect.height if rect.collidepoint(mpos): self.active_option = i break if not self.menu_active and self.active_option == -1: self.draw_menu = False #self.draw_menu = True #return -1 if pygame.mouse.get_pressed(num_buttons=3)[0]: if self.menu_active: self.draw_menu = not self.draw_menu elif self.draw_menu and self.active_option >= 0: self.draw_menu = False return self.active_option return -1 class GameObjects(): def __init__(self, name:str, _type:str, bg, objects:list, WIDTH, HEIGHT) -> None: self.name = name self.type = _type self.background = bg if bg != None: if WIDTH != None and HEIGHT != None: with open(bg, 'r') as bg: self.background = pygame.transform.scale(pygame.image.load(bg), [WIDTH, HEIGHT]) else: with open(bg, 'r') as bg: self.background = pygame.transform.scale2x(pygame.image.load(bg)) self.objects = objects def update(self, objects): return def draw(self, screen): return class Scene(GameObjects): def __init__(self, name:str, _type:str, bg, objects:list, WIDTH, HEIGHT, level:list) -> None: super().__init__(name, _type, bg, objects, WIDTH, HEIGHT) self.level = level self.current_level = 0 self.update() def update(self, change:int=None, objects=None): obj = self.level[self.current_level].update(change, objects) self.background = self.level[self.current_level].background """if isinstance(self.objects, list): for obj in self.objects[0] + self.objects[1] + self.objects[2]: obj.update()""" return obj def draw(self, screen): """if isinstance(self.objects, list): for obj in self.objects[0] + self.objects[1] + self.objects[2] + self.objects[3]: obj.draw(screen)""" self.level[self.current_level].draw(screen) def getObjects(self): return self.level[self.current_level].getObjects() class Stage(GameObjects): def __init__(self, name: str, _type: str, bg, objects: list, WIDTH, HEIGHT, stage:str, rooms:list) -> None: super().__init__(name, _type, bg, objects, WIDTH, HEIGHT) self.WIDTH = WIDTH self.HEIGHT = HEIGHT self.stage = stage self.rooms = rooms self.current = 0 self.sortRooms(WIDTH) for room in self.rooms: if room.id == self.current: room.objectCreation(WIDTH, HEIGHT) def update(self, target, objects): if target is not None: self.current = int(target) for room in self.rooms: if room.id == self.current: obj = room.objectCreation(self.WIDTH, self.HEIGHT) self.background = room.background return obj for room in self.rooms: if room.id == self.current: room.update(objects) self.background = room.background def draw(self, screen): for room in self.rooms: if room.id == self.current: room.draw(screen) def getObjects(self): for room in self.rooms: if room.id == self.current: return room.getObjects() def sortRooms(self, WIDTH): rooms = self.rooms for i, room in enumerate(rooms): if room.type != 'boss': i += 1 rand = random.randint(0, 25) if rand < 7.5: if rooms[i].id <= room.id: i += 1 room.exits.append([rooms[i].id, rooms[i].type]) elif rand < 20: if rooms[i].id <= room.id: i += 1 room.exits.append([rooms[i].id, rooms[i].type]) if rand % 2 == 0: i += 1 if not i >= len(self.rooms) - 2: room.exits.append([rooms[i + 1].id, rooms[i + 1].type]) else: if rooms[i].id <= room.id: i += 1 room.exits.append([rooms[i].id, rooms[i].type]) if rand % 2 == 0: i += 1 if not i >= len(self.rooms) - 2: room.exits.append([rooms[i + 1].id, rooms[i + 1].type]) if not i >= len(self.rooms) - 3: room.exits.append([rooms[i + 2].id, rooms[i + 2].type]) for room in self.rooms: room.createDoors(WIDTH) class Room(GameObjects): def __init__(self, name:str, _type:str, bg, objects:list, WIDTH, HEIGHT, id:int) -> None: super().__init__(name, _type, bg, objects, WIDTH, HEIGHT) self.exits = [] self.id = id self.doors = [] if self.type == 'normal' or self.type == 'boss': self.locked = True #self.objectCreation(WIDTH, HEIGHT) else: self.locked = False def genWalls(self, WIDTH, HEIGHT): walls = [] walls.append(Obstacle('wall_l', 'wall', None, True, 32, 32, True, WIDTH=4, HEIGHT=HEIGHT)) walls.append(Obstacle('wall_r', 'wall', None, True, WIDTH + 28, 32, True, WIDTH=4, HEIGHT=HEIGHT)) walls.append(Obstacle('wall_t', 'wall', None, True, 32, 32, True, WIDTH=WIDTH, HEIGHT=4)) walls.append(Obstacle('wall_b', 'wall', None, True, 32, HEIGHT + 28, True, WIDTH=WIDTH, HEIGHT=4)) return walls def createDoors(self, WIDTH): if not self.type == 'boss': if len(self.exits) == 1: self.doors.append(Door(f'door{self.id}', self.exits[0][1], random.randint(64, round(WIDTH * 0.75)), self.exits[0][0])) elif len(self.exits) == 2: self.doors.append(Door(f'door{self.id}', self.exits[0][1], random.randint(64, round(WIDTH * 0.45)), self.exits[0][0])) self.doors.append(Door(f'door{self.id}', self.exits[1][1], random.randint(round(WIDTH * 0.5), round(WIDTH * 0.9)), self.exits[1][0])) else: self.doors.append(Door(f'door{self.id}', self.exits[0][1], random.randint(64, round(WIDTH * 0.3)), self.exits[0][0])) self.doors.append(Door(f'door{self.id}', self.exits[1][1], random.randint(round(WIDTH * 0.33), round(WIDTH * 0.6)), self.exits[1][0])) self.doors.append(Door(f'door{self.id}', self.exits[2][1], random.randint(round(WIDTH * 0.63), round(WIDTH * 0.95)), self.exits[2][0])) self.objects.append(self.doors) def objectCreation(self, WIDTH, HEIGHT): self.objects[4] = [wall for wall in self.genWalls(WIDTH, HEIGHT)] if self.type != 'boss': self.objects[1] = [Skeleton('skeleton', random.randint(40, 60), random.randint(50, WIDTH - 50), random.randint(50, HEIGHT - 50), 5, 1, 1, 2, 200) for i in range(0,random.randint(2, 5))] + [Zombie('zombie', random.randint(40, 60), random.randint(50, WIDTH-50), random.randint(50, HEIGHT-50), 5, 1, 1, 1, 25) for i in range(0,random.randint(2, 5))] npcs = [NPC('vivi', 100, 'people/vivi.png', 0, 400, 600), NPC('fairy', 100, 'people/fairy.png', 0, 200, 200)] rand = random.randint(0, 30) if rand <= 25 and rand >= 15: self.objects[2] = [random.choice(npcs)] else: self.objects[1] = [Boss('reddy', 40, WIDTH / 2 - 16, HEIGHT /2 - 32, 50, 0.1, 1, 5, 5000)] self.objects[4].append(HealthBar(self.objects[1][0].name, self.objects[1][0].health, WIDTH / 2 - 128)) return self.objects def update(self, objects): if objects is not None: self.objects = objects if self.type == 'boss' and not self.objects: print('yeahhh, you killed the boss') self.objects[0][0].level.level = 100 if len(self.objects[1]) <= 3: self.locked = False for door in self.doors: door.update(False) #Tür Sound return def draw(self, screen): """screen.blit(self.background, (32, 32)) if isinstance(self.objects, list): for obj in self.objects[3] + self.objects[0] + self.objects[1] + self.objects[2]: obj.draw(screen)""" for door in self.doors: door.draw(screen) def getObjects(self): return self.objects class Obstacle(GameObjects): def __init__(self, name: str, _type: str, bg, collision: bool, x: int, y: int, hidden: bool=False, objects: list = None, WIDTH=None, HEIGHT=None) -> None: super().__init__(name, _type, bg, objects, WIDTH, HEIGHT) self.collision = collision self.hidden = hidden self.width = WIDTH self.height = HEIGHT if self.background is not None: self.rect = pygame.Rect((x, y), self.background.get_size()) else: self.rect = pygame.Rect(x, y, WIDTH, HEIGHT) def draw(self, screen): if not self.hidden: screen.blit(self.background, self.rect) class Door(GameObjects): def __init__(self, name: str, _type: str, x: int, target:int, y: int=16, objects: list=None, WIDTH=None, HEIGHT=None, islocked=True) -> None: super().__init__(name, _type, f'art/images/background/door_{_type}.png', objects, WIDTH, HEIGHT) self.rect = pygame.Rect((x, y), self.background.get_size()) self.locked = islocked self.target = target def draw(self, screen): screen.blit(self.background, self.rect) def update(self, islocked=True): self.locked = islocked #Viecher vec = pygame.math.Vector2 fps = 60 class Objects(): def __init__(self, name, ms, sprite, x, y) -> None: self.name = name self.speed = ms with open(f'art/images/{sprite}') as i: self.sprite = pygame.transform.scale2x(pygame.image.load(i)) self.x = x self.y = y self.hidden = False self.rect = pygame.Rect(self.x, self.y, self.sprite.get_width(), self.sprite.get_height()) def draw(self, screen): if self.hidden: return self.rect.x, self.rect.y = self.x, self.y screen.blit(self.sprite, self.rect) #pygame.draw.rect(screen, '#ef0120', self.rect, 2) class NPC(Objects): def __init__(self, name, ms, sprite, convo_scene, x, y) -> None: super().__init__(name, ms, sprite, x, y) self.talking = False self.hidden = False self.conversation = Convo(self, convo_scene) self.lastUpdate = pygame.time.get_ticks() def talk(self, objects): self.talking = True objects[0][0].talking = True def draw(self, screen): super().draw(screen) if self.talking: self.conversation.draw(screen) def update(self, keys, objects): if self.name == 'oldlady': if self.conversation.convo_scene == 0 and 'rat' in objects[0][0].killed and objects[1]==[]: self.conversation.convo_scene = 1 if self.lastUpdate + 200 < pygame.time.get_ticks(): if self.talking: self.conversation.update(keys, objects) self.lastUpdate = pygame.time.get_ticks() else: touches = pygame.sprite.spritecollideany(self, objects[0]) if touches is not None and keys[pygame.K_f] and isinstance(touches, MainCharacter): self.talk(objects) self.lastUpdate = pygame.time.get_ticks() class Convo(Label): def __init__(self, npc, convo_scene, text='', x = 140, y = 600, width = 1000, height = 100, font='simple', font_size = 20) -> None: super().__init__(x, y, width, height, text, font, font_size) self.convo_act=0 self.npc = npc self.convo_scene = convo_scene self.convos = [ ['oldlady', 0, ['There are so many rats here.', 'I wish someone would to something against that.','An experienced fighter could kill them.', 'For them it only takes a mouseclick.']], ['oldlady', 1, ['Oh, did you kill all the rats?', 'You must be the chosen one!', 'It would be nice if you would go and talk to the village elder.']], ['elder', 0, ['Who are you?', 'You want to help us?', 'We have a serious problem with monsters.', 'One day they appeared out of nowhere and started attacking.', 'When you jump into the portal over there,', 'You will be send to a place with monsters.', 'PLEASE help us!']], ['elder', 1, ['Who are you?', 'You want to help us?', 'We have a serious problem with monsters.', 'One day they appeared out of nowhere and started attacking.', 'When you jump into the portal over there,', 'You will be send to a place with monsters.', 'PLEASE help us!']], ['vivi', 0, ['Wer bist du denn?', 'Aber du kannst gerne aus dem Fenster springen, solange es nicht in meinem Unterricht ist.']], ['fairy', 0, ['Hello fellow traveler.', 'I am Aurelia, a Whisperwind Fairy.', '''Do not worry about me. I won't harm anybody.''', 'You should still be careful around other fairies, many of them are insidious.', 'But fear not lonely traveller, I will help you survive.', 'Please take this spell for your further journey']] ] def draw(self, screen): self.text = self.findConversation()[2][self.convo_act] super().draw(screen) def findConversation(self): for convo in self.convos: if convo[0] == self.npc.name and convo[1] == self.convo_scene: return convo return ['ERROR'] def update(self, keys, objects): if keys[pygame.K_f]: convo = self.findConversation() if self.convo_act + 1 < len(convo[2]): self.text = convo[2][self.convo_act] self.convo_act += 1 else: if convo[0] == 'oldlady': if convo[1] == 0: for i in range(0,5): objects[1].append(Rat('rat', random.randint(150,250), 800, 400+i*20, 1, 1, 1, 100, 25)) elif convo[1] == 1: objects[0][0].level.level = 5 while 'rat' in objects[0][0].killed: objects[0][0].killed.remove('rat') elif convo[0] == 'elder': if convo[1] == 0: objects[4].append(Obstacle('portal', 'interactable', 'art/images/background/portal.png', False, 700, 300)) self.convo_scene += 1 elif convo[0] == 'vivi': objects[0][0].health.health = 0 self.convo_act = 0 self.npc.talking = False objects[0][0].talking = False class Drops(): def __init__(self, type, level, mult) -> None: table = { 'boss': { 'xp': 150, 'gold': 500, 'artifacts': [ 100, #drop-chance 20, #common 30, #uncommon 35, #rare 14, #epic 1 #legendary ], 'will': 5 }, 'mob': { 'xp': 5, 'gold': 20, 'artifacts': [ 10, #drop-chance 65, #common 25, #uncommon 7.5, #rare 2.25, #epic 0.25 #legendary ], 'will': 0.1 }, 'add': { 'xp': 0, 'gold': 3, 'artifacts': [ 1, #drop-chance 90, #common 9.9, #uncommon 0.1, #rare 0, #epic 0 #legendary ], 'will': 0 } } self.table = table[type] self.multiplicator = level * mult def drop(self, main): main.skill_xp += self.table['xp'] * self.multiplicator main.gold += self.table['gold'] * self.multiplicator class Fighter(Objects): def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr) -> None: super().__init__(name, ms, sprite, x, y) self.health = health self.damage = damage self.level = level self.attack_speed = asp self.attack_range = atr self.lastHurt = pygame.time.get_ticks() self.lastAttack = pygame.time.get_ticks() self.hurtCooldown = 0 class MainCharacter(Fighter): def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr, killed=[]) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr) self.book = Book(0, 0, [], None, None) self.talking = False self.level = Level(1000, 38, 150, 40, level, f'will to live: {round(level, 1)}%', 'simple', 20) self.health = Hearts(health, sprite=['fullheart.png', 'fullheart.png', 'fullheart.png', 'fullheart.png', 'fullheart.png'], x=900, y=50, hurtCooldown=self.hurtCooldown) self.thinks = Thinks(self.x + 20, self.y - 50, 150, 100, 'brr I\'m freezing') self.freezing = True self.killed = killed # amount of mobs that were killed self.load_frames(f'art/images/people/oldmanwalk.png', f'art/images/people/oldmanattack.png') self.current_frame = 0 self.animation_speed = 0.1 self.last_frame_update = pygame.time.get_ticks() self.rect = pygame.Rect(x, y, self.walk_frames[0].get_width(), self.walk_frames[0].get_height()) self.is_attacking = False self.skill_xp = 0 self.gold = 0 def load_frames(self, walk_sprite_sheet, attack_sprite_sheet): self.walk_frames = self.load_animation_frames(walk_sprite_sheet, frame_width=40, frame_height=66) self.attack_frames = self.load_animation_frames(attack_sprite_sheet, frame_width=66, frame_height=64) # Adjust frame width and height as needed def load_animation_frames(self, sprite_sheet, frame_width, frame_height): sprite_sheet = pygame.transform.scale2x(pygame.image.load(sprite_sheet)) animation_frames = [] if frame_width == 40: frames_coordinates = [ (frame_width, 0), (frame_width*2, 0), (frame_width*3, 0), (frame_width*4, 0), (frame_width*5, 0), (frame_width*6, 0), (frame_width*7, 0), (frame_width*8, 0), ] else: frames_coordinates = [ (frame_width, 0), (frame_width*2, 0), (frame_width*3, 0), (frame_width*4, 0), (frame_width*5, 0), (frame_width*6, 0) ] for x, y in frames_coordinates: frame = pygame.Surface((frame_width, frame_height), pygame.SRCALPHA) frame.blit(sprite_sheet, (0, 0), (x, y, frame_width, frame_height)) animation_frames.append(frame) return animation_frames def draw(self, screen): if self.hidden: return current_time = pygame.time.get_ticks() if self.is_attacking: animation_frames = self.attack_frames else: animation_frames = self.walk_frames if current_time - self.last_frame_update > self.animation_speed * 1000: self.current_frame = (self.current_frame + 1) % len(animation_frames) self.last_frame_update = current_time current_frame_image = animation_frames[self.current_frame] screen.blit(current_frame_image, (self.x, self.y)) self.rect.x, self.rect.y = self.x, self.y self.health.draw(screen) self.level.draw(screen) self.book.draw(screen) #pygame.draw.rect(screen, '#e900fa', self.rect, 2) if self.thinks.hidden == False: self.thinks.draw(screen, self.x + 20, self.y - 100) def hurt(self, damage, objects): hit_sound = mixer.Sound('audio/soundeffects/hitsound.mp3') hit_sound.set_volume(0.5) if not self.talking: hit_sound.play() self.health.hurt(damage) def obstacle_interaction(self, objects): portal_sound = mixer.Sound('audio/soundeffects/portalsound.mp3') door_sound = mixer.Sound('audio/soundeffects/door.mp3') if len(objects) <= 5: objects.append([]) touches = pygame.sprite.spritecollideany(self, objects[4] + objects[5]) if touches is not None: if touches.name == 'fireplace': self.freezing = False elif touches.name == 'portal' and self.level.level != 1: portal_sound.play() return 'play' elif touches.name == 'house' and self.level.level != 1: door_sound.play() self.x = 500 self.y = 400 return 'house' elif 'wall' in touches.name: return 'wall' elif isinstance(touches, Door): if not touches.locked: door_sound.play() return f'door-{touches.target}' else: return True def walk(self, keys, objects): moveto = vec(0, 0) if keys[pygame.K_w] or keys[pygame.K_UP]: moveto += vec(0, -1) if keys[pygame.K_a] or keys[pygame.K_LEFT]: moveto += vec(-1, 0) if keys[pygame.K_s] or keys[pygame.K_DOWN]: moveto += vec(0, 1) if keys[pygame.K_d] or keys[pygame.K_RIGHT]: moveto += vec(1, 0) if not moveto == vec(0, 0): moveto.scale_to_length(self.speed) self.x += moveto[0] / fps self.y += moveto[1] / fps touches = pygame.sprite.spritecollideany(self, objects[4]) if touches is not None and not isinstance(touches, Weapons): if isinstance(touches, Obstacle): if not touches.collision: # print(touches.name) return if touches.type == 'wall': if touches.name == 'wall_l': self.x += (2 + (self.x - touches.rect.x)) elif touches.name == 'wall_r': self.x -= (2 + self.rect.width - (touches.rect.x - self.x)) if touches.name == 'wall_t': self.y += (2 + (self.y - touches.rect.y)) elif touches.name == 'wall_b': self.y -= (2 + self.rect.height - (touches.rect.y - self.y)) return if self.x <= touches.rect.x: self.x -= (self.rect.width - (touches.rect.x - self.x)) elif self.x > touches.rect.x: self.x += (self.rect.width - (self.x - touches.rect.x - touches.rect.width * 0.7)) #if self.y <= touches.y: pass #elif self.y > touches.y: pass #self.x -= moveto[0] * 2 / fps #self.y -= moveto[1] * 2 / fps """ if self.x <= 32: self.x = 33 elif self.x >= objects[3][0].width - 32: self.x = objects[3][0].width - 32 - self.rect.width + 1 if self.y <= 32: self.y = 33 elif self.y >= objects[3][0].height - 32: self.y = objects[3][0].height - 32 - self.rect.height + 1 """ def attack(self, obj, mouse): fireball_sound = mixer.Sound('audio/soundeffects/firebalhitl.mp3') wind_sound = mixer.Sound('audio/soundeffects/wind.mp3') oldmanattack_sound = mixer.Sound('audio/soundeffects/oldmanattack.mp3') fireball_sound.set_volume(0.2) wind_sound.set_volume(0.2) oldmanattack_sound.set_volume(0.2) if self.lastAttack + self.attack_speed * 1000 < pygame.time.get_ticks(): moveto = mouse - vec(self.x, self.y) if self.book.current_sp == 'fireball': fireball_sound.play() weapon = Fireball('fb1', 100, self.x, self.y, moveto, 5) elif self.book.current_sp == 'windslash': wind_sound.play() weapon = Windslash('ws1', 100, self.x, self.y, moveto, 10) else: oldmanattack_sound.play() weapon = Punch('punch', 100, self.x, self.y, moveto, 1, Mobs, life_ticks=500) obj[3].append(weapon) self.lastAttack = pygame.time.get_ticks() if not self.is_attacking: self.current_frame = 0 self.is_attacking = True def update(self, keys, mouse, objects): if not self.talking: is_moving = False if keys[pygame.K_w] or keys[pygame.K_UP] or keys[pygame.K_a] or keys[pygame.K_LEFT] or keys[pygame.K_s] or keys[pygame.K_DOWN] or keys[pygame.K_d] or keys[pygame.K_RIGHT]: is_moving = True if is_moving: self.walk(keys, objects) self.is_attacking = False else: current_time = pygame.time.get_ticks() if current_time - self.last_frame_update > self.animation_speed * 1000: self.current_frame = (self.current_frame + 1) % len(self.walk_frames) self.last_frame_update = current_time if not self.is_attacking: self.current_frame = 0 if pygame.mouse.get_pressed()[0]: self.attack(objects, vec(mouse)) if self.is_attacking: if self.current_frame == len(self.attack_frames) - 1: self.is_attacking = False self.current_frame = 0 self.thinks.update(objects, self) if self.health.health <= 0: return 'village' else: return self.obstacle_interaction(objects) class Hearts(): def __init__(self, health, sprite, x, y, hurtCooldown) -> None: self.x = x self.y = y self.health = health self.lastHurt = pygame.time.get_ticks() self.hurtCooldown = hurtCooldown self.hidden = False self.sprite=[] for parts in sprite: with open(f'art/images/main_attributes/{parts}') as i: self.sprite.append(pygame.image.load(i)) self.rect = [] for each in self.sprite: self.rect.append(pygame.Rect(self.x, self.y, each.get_width(), each.get_height())) def hurt(self,damage): if self.lastHurt + self.hurtCooldown < pygame.time.get_ticks(): self.health -= damage self.lastHurt = pygame.time.get_ticks() self.update() def draw(self, screen): if self.hidden: return for i in range(0, 5): self.rect[i].x, self.rect[i].y = self.x + i * 20, self.y screen.blit(self.sprite[i], self.rect[i]) def update(self): sprite = [] for i in range(0, 5): if round(self.health) >= 4 + 4 * i: sprite.append('fullheart.png') elif round(self.health) == 3 + 4 * i: sprite.append('dreiviertelheart.png') elif round(self.health) >= 2 + 4 * i: sprite.append('halfheart.png') elif round(self.health) >= 1 + 4 * i: sprite.append('viertelheart.png') elif round(self.health) <= 4 * i: sprite.append('noheart.png') self.sprite = [] for parts in sprite: with open(f'art/images/main_attributes/{parts}') as i: self.sprite.append(pygame.image.load(i)) class Level(Label): def __init__(self, x, y, width, height, level, text, font='simple', font_size=20, font_color='#1e90ff', sprite='label.png') -> None: super().__init__(x, y, width, height, text, font, font_size, font_color, sprite) self.level = level def draw(self, screen): self.text = f'will to live: {round(self.level)}%' super().draw(screen) class Thinks(Label): def __init__(self, x, y, width, height, text, font='simple_round', font_size=12, font_color='#000000', sprite='thinks.png') -> None: super().__init__(x, y, width, height, text, font, font_size, font_color, sprite) self.scene = 0 def draw(self, screen, x, y): if self.hidden: return self.x = x self.y = y super().draw(screen) def update(self, objects, main): if not self.hidden: if self.scene == 0 and not main.freezing: self.scene = 1 self.hidden = True elif self.scene == 1 and main.talking: self.scene = 2 self.hidden = True if self.scene == 1: touches = pygame.sprite.spritecollideany(main, objects[2]) if touches is not None and isinstance(touches, NPC): self.text = 'I should press "f"' self.hidden = False else: self.hidden = False self.text = 'the lady over there' class Book(): def __init__(self, x, y, spells, current_spell, current_shield) -> None: with open(f'art/images/main_attributes/book.png') as i: self.sprite = pygame.image.load(i) self.sprite = pygame.transform.scale(self.sprite, (1280, 720)) self.x = x self.y = y self.hidden = False self.rect = pygame.Rect(self.x, self.y, self.sprite.get_width(), self.sprite.get_height()) self.sp_list = spells self.current_sp = current_spell self.text_left = ["Dear User, ", "in case you fell on the ground too hard,", "here is a quick reminder:", "You are a homeless person. One cold day","you went to the library to warm up yourself.", "There you got bored and found and opened me.", "This lead to you being thrown into this world.", "But you can find a way out of here again."] self.text_right = ["This book will help you to survive.", "You can open and close me when pressing e.", "Click on a picture to choose your spell.", "Talk to fairies to unlock new spells!"] self.buttons=[] self.buttons_y = 400 self.buttons_x = 800 def draw(self, screen): if self.hidden: return self.rect.x, self.rect.y = self.x, self.y screen.blit(self.sprite, self.rect) text_left_y = 100 text_right_y = 100 for text in self.text_left: label = Label(100, text_left_y, 500, 50, text, font_color='#000000', sprite='empty.png') label.draw(screen) text_left_y += 50 for text in self.text_right: label = Label(680, text_right_y, 500, 50, text, font_color='#000000', sprite='empty.png') label.draw(screen) text_right_y += 50 for button in self.buttons: button.update(screen) def addspell(self, spell): if spell not in self.sp_list: self.sp_list.append(spell) self.current_sp = spell self.buttons.append(Button(self.buttons_x, self.buttons_y, 58, 50, f'{spell}_icon.png', 'medieval', 23, attributes=[spell], onclickFunction=self.update_spell)) self.buttons_y += 100 def update_spell(self, spell): self.current_sp = spell def update(self): pass class Mobs(Fighter): def __init__(self, name, ms, sprite, x, y, health, damage, level, asp, atr, drops) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr) self.drops = Drops('mob', self.level, drops) def chase(self, obj): x = obj[0][0].x y = obj[0][0].y moveto = vec(x, y) - vec(self.x, self.y) if not (moveto).length() <= self.attack_range: moveto.scale_to_length(int(self.speed)) self.x += moveto[0] / fps self.y += moveto[1] / fps touches = pygame.sprite.spritecollideany(self, obj[4]) if touches is not None and not isinstance(touches, Weapons): if isinstance(touches, Obstacle): if not touches.collision: return if touches.type == 'wall': if touches.name == 'wall_l': self.x += (2 + (self.x - touches.rect.x)) elif touches.name == 'wall_r': self.x -= (2 + self.rect.width - (touches.rect.x - self.x)) if touches.name == 'wall_t': self.y += (2 + (self.y - touches.rect.y)) elif touches.name == 'wall_b': self.y -= (2 + self.rect.height - (touches.rect.y - self.y)) if self.x <= touches.rect.x: self.x -= (self.rect.width - (touches.rect.x - self.x)) elif self.x > touches.rect.x: self.x += (self.rect.width - (self.x - touches.rect.x - touches.rect.width * 0.66)) else: self.attack(moveto, obj) def hurt(self, damage, objects): self.health -= damage if self.health <= 0: objects[0][0].killed.append(self.name) self.hidden = True self.drops.drop(objects[0][0]) objects[1].remove(self) def update(self, obj): self.chase(obj) class Skeleton(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite = 'people/skeleton.png', drops=2) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops) def attack(self, moveto, obj): arrow_sound = mixer.Sound('audio/soundeffects/arrowsound.mp3') arrow_sound.set_volume(0.3) if self.lastAttack + self.attack_speed * 1000 < pygame.time.get_ticks(): arrow_sound.play() obj[3].append(Arrow("arrow", 200, self.x, self.y, moveto, self.damage)) self.lastAttack = pygame.time.get_ticks() class Zombie(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite_sheet='people/zombiewalk.png', drops=1) -> None: super().__init__(name, ms, sprite_sheet, x, y, health, damage, level, asp, atr, drops) self.load_frames(f'art/images/{sprite_sheet}') self.current_frame = 0 self.animation_speed = 250 self.last_frame_update = pygame.time.get_ticks() self.hidden = False self.rect = pygame.Rect(x, y, 40, 64) def load_frames(self, sprite_sheet): sprite_sheet = pygame.transform.scale2x(pygame.image.load(sprite_sheet).convert_alpha()) frame_width = 40 frame_height = 64 frames_coordinates = [(40, 0),(80, 0),(120, 0),(160, 0),(200, 0),(240, 0),(280, 0), (320, 0)] self.animation_frames = [] for x, y in frames_coordinates: frame = pygame.Surface((frame_width, frame_height), pygame.SRCALPHA) frame.blit(sprite_sheet, (0, 0), (x, y, frame_width, frame_height)) self.animation_frames.append(frame) def draw(self, screen): if self.hidden: return current_time = pygame.time.get_ticks() if current_time - self.last_frame_update > self.animation_speed: self.current_frame = (self.current_frame + 1) % len(self.animation_frames) self.last_frame_update = current_time current_frame_image = self.animation_frames[self.current_frame] screen.blit(current_frame_image, (self.x, self.y)) self.rect.topleft = (self.x, self.y) #pygame.draw.rect(screen, '#ef0120', self.rect, 2) def attack(self, moveto, obj): zombieattack_sound = mixer.Sound('audio/soundeffects/zombieattack.mp3') zombieattack_sound.set_volume(0.3) if self.lastAttack + self.attack_speed * 1000 < pygame.time.get_ticks(): zombieattack_sound.play() obj[3].append(Punch('punch', 100, self.x, self.y, moveto, self.damage, MainCharacter)) self.lastAttack = pygame.time.get_ticks() class Rat(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite='people/rat.png', drops=1) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops) def attack(self, moveto, obj): if self.lastAttack + self.attack_speed * 1000 < pygame.time.get_ticks(): obj[3].append(Punch('punch', 100, self.x, self.y, moveto, self.damage, MainCharacter)) self.lastAttack = pygame.time.get_ticks() class Adds(Mobs): def __init__(self, name, ms, sprite, health, damage, level, asp, atr, drops, x, y) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops) self.drops = Drops('add', self.level, drops) def attack(self, moveto, obj): pass class Boss(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite='people/reddy.png', drops=1) -> None: super().__init__(name, ms, sprite, x, y, health, damage, level, asp, atr, drops) self.max_hp = health self.drops = Drops('boss', self.level, drops) self.adds = SpawnAdds(15, ['add', '125', sprite, 3, 0.5, 1, 1.5, 250, 1], [3, 7]) self.ability = Heal(10, 25, 5) self.adds.last_used = pygame.time.get_ticks() - 12500 def attack(self, moveto, obj): reddyattack_sound = mixer.Sound('audio/soundeffects/reddyattack.mp3') reddyattack_sound.set_volume(0.8) if self.lastAttack + self.attack_speed * 1000 < pygame.time.get_ticks(): reddyattack_sound.play() obj[3].append(RedBlob("blob", 50, self.x, self.y, moveto, self.damage)) self.lastAttack = pygame.time.get_ticks() def hurt(self, damage, objects, ticks): if ticks > 1000: damage /= 2 self.health -= damage if self.health <= 0: objects[0][0].killed.append(self.name) self.hidden = True objects[1].remove(self) def update(self, obj): super().update(obj) self.adds.spawn(obj) event = self.ability.cast(obj) if event.type == 'heal': self.health += event.num if self.health > self.max_hp: self.health = self.max_hp elif self.health < 0: self.health = 0 class Event(): def __init__(self, _type: str, num: int) -> None: self.type = _type self.num = num class Ability(): def __init__(self, cd, stage = 0) -> None: self.cooldown = cd self.stage = stage self.last_used = 0 def cast(self, objects): if self.last_used + self.cooldown * 1000 <= pygame.time.get_ticks(): self.last_used = pygame.time.get_ticks() class SpawnAdds(Ability): def __init__(self, cd, add:list, count:list, stage=0) -> None: super().__init__(cd, stage) self.add = add self.count = count def spawn(self, objects): if self.last_used + self.cooldown * 1000 <= pygame.time.get_ticks(): for i in range(random.randint(*self.count)): objects[1].append(Adds(*self.add, random.randint(32, 1888), random.randint(32, 1048))) self.last_used = pygame.time.get_ticks() class Heal(Ability): def __init__(self, cd, amount, time, stage=0) -> None: super().__init__(cd, stage) self.amount = amount self.time = time def cast(self, objects): if self.time * 1000 + self.last_used > pygame.time.get_ticks(): return Event('heal', (self.amount / self.time / 1000)) elif self.time * 1000 + self.last_used == pygame.time.get_ticks(): self.last_used = pygame.time.get_ticks() return Event('heal', (self.amount / self.time / 1000)) elif random.randint(0, 88) % 5 == 0: if self.last_used + self.cooldown * 1000 <= pygame.time.get_ticks(): self.last_used = pygame.time.get_ticks() # else: # self.last_used return Event('None', 0) class Weapons(Objects): def __init__(self, name, ms, sprite, x, y, moveto, damage, life_ticks) -> None: super().__init__(name, ms, sprite, x, y) self.moveto = moveto self.damage = damage self.life_ticks= life_ticks self.spawn_tick = pygame.time.get_ticks() pos = vec(1,0) angle = pos.angle_to(moveto) self.sprite = pygame.transform.rotate(self.sprite, -angle) def die(self, objects, kills): touches = pygame.sprite.spritecollideany(self, objects[0] + objects[1]) if kills == Mobs: if touches is not None and isinstance(touches, Boss): touches.hurt(self.damage, objects, self.life_ticks) self.hidden = True if self in objects[3]: objects[3].remove(self) return if touches is not None and isinstance(touches, kills): touches.hurt(self.damage, objects) self.hidden = True if self in objects[3]: objects[3].remove(self) def move(self, objects): self.moveto.scale_to_length(self.speed) self.x += self.moveto[0] / fps self.y += self.moveto[1] / fps if pygame.time.get_ticks() - self.spawn_tick > self.life_ticks: self.hidden = True objects[3].remove(self) class Spells(Weapons): def __init__(self, name, ms, sprite, x, y, moveto, damage, life_ticks) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) class Fireball(Spells): def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/fireball.png', life_ticks=5000) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) def update(self, objects): self.move(objects) self.die(objects, Mobs) class Windslash(Spells): def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/windslash.png', life_ticks=1000) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) def update(self, objects): self.move(objects) self.die(objects, Mobs) def move(self, objects): super().move(objects) self.moveto = self.moveto.rotate(5) class RedBlob(Spells): def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/redblob.png', life_ticks=50000) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) def die(self, objects, kills): touches = pygame.sprite.spritecollideany(self, objects[0] + objects[1]) if touches is not None and isinstance(touches, kills): touches.hurt(self.damage, objects) def update(self, objects): self.move(objects) self.die(objects, MainCharacter) class Arrow(Weapons): def __init__(self, name, ms, x, y, moveto, damage, sprite = 'weapons/arrow.png', life_ticks=5000) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) def update(self, objects): self.move(objects) self.die(objects, MainCharacter) class Punch(Weapons): def __init__(self, name, ms, x, y, moveto, damage, kills, sprite = 'weapons/empty.png', life_ticks=100) -> None: super().__init__(name, ms, sprite, x, y, moveto, damage, life_ticks) self.kills = kills # if self.kills == Mobs: # self.rect.width *= 2 # self.rect.height *= 2 def update(self, objects): self.move(objects) self.die(objects, self.kills)