import pygame as pg from classes import * from main import * import random vec = pg.math.Vector2 fps = 60 pg.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 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 = pg.transform.scale2x(pg.image.load(i)) self.x = x self.y = y self.hidden = False self.rect = pg.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) pg.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 = pg.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 < pg.time.get_ticks(): if self.talking: self.conversation.update(keys, objects) self.lastUpdate = pg.time.get_ticks() else: touches = pg.sprite.spritecollideany(self, objects[0]) if touches is not None and keys[pg.K_f] and isinstance(touches, MainCharacter): self.talk(objects) self.lastUpdate = pg.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!']] ] 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[pg.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') if 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 self.convo_act = 0 self.npc.talking = False objects[0][0].talking = False 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 = pg.time.get_ticks() self.lastAttack = pg.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: {level}%', '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 def draw(self, screen): if self.hidden: return self.rect.x, self.rect.y = self.x, self.y screen.blit(self.sprite, self.rect) self.health.draw(screen) self.level.draw(screen) self.book.draw(screen) pg.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): if not self.talking: self.health.hurt(damage) def obstacle_interaction(self, objects): touches = pg.sprite.spritecollideany(self, objects[4]) if touches is not None: if touches.name == 'fireplace': self.freezing = False elif touches.name == 'portal' and self.level.level != 1: return 'play' elif touches.name == 'house' and self.level.level != 1: self.x = 500 self.y = 400 return 'house' elif 'wall' in touches.name: return 'wall' else: return True def walk(self, keys, objects): moveto = vec(0, 0) if keys[pg.K_w] or keys[pg.K_UP]: moveto += vec(0, -1) if keys[pg.K_a] or keys[pg.K_LEFT]: moveto += vec(-1, 0) if keys[pg.K_s] or keys[pg.K_DOWN]: moveto += vec(0, 1) if keys[pg.K_d] or keys[pg.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 = pg.sprite.spritecollideany(self, objects[2] + 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 elif isinstance(touches, NPC): 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.66)) #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): if self.lastAttack + self.attack_speed * 1000 < pg.time.get_ticks(): moveto = mouse - vec(self.x, self.y) if self.book.current_sp == 'fireball': weapon = Fireball('fb1', 100, self.x, self.y, moveto, 5) elif self.book.current_sp == 'windslash': weapon = Windslash('ws1', 100, self.x, self.y, moveto, 10) else: weapon = Punch('punch', 100, self.x, self.y, moveto, 1, Mobs, life_ticks=500) obj[3].append(weapon) self.lastAttack = pg.time.get_ticks() def update(self, keys, mouse, objects): if not self.talking: self.walk(keys, objects) if pg.mouse.get_pressed()[0]: self.attack(objects, vec(mouse)) 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 = pg.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(pg.image.load(i)) self.rect = [] for each in self.sprite: self.rect.append(pg.Rect(self.x, self.y, each.get_width(), each.get_height())) def hurt(self,damage): if self.lastHurt + self.hurtCooldown < pg.time.get_ticks(): self.health -= damage self.lastHurt = pg.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 self.health >= 4 + 4 * i: sprite.append('fullheart.png') elif self.health == 3 + 4 * i: sprite.append('dreiviertelheart.png') elif self.health >= 2 + 4 * i: sprite.append('halfheart.png') elif self.health >= 1 + 4 * i: sprite.append('viertelheart.png') elif 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(pg.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: {self.level}%' super().draw(screen) class Thinks(Label): def __init__(self, x, y, width, height, text, font='simple', font_size=15, 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 = pg.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 = pg.image.load(i) self.sprite = pg.transform.scale(self.sprite, (1280, 720)) self.x = x self.y = y self.hidden = False self.rect = pg.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 * (self.level / 2) 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(self.speed) self.x += moveto[0] / fps self.y += moveto[1] / fps touches = pg.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 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=0) -> 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 < pg.time.get_ticks(): obj[3].append(Arrow("arrow", 200, self.x, self.y, moveto, self.damage)) self.lastAttack = pg.time.get_ticks() class Zombie(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite='people/zombie.png', drops=0) -> 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 < pg.time.get_ticks(): obj[3].append(Punch('punch', 100, self.x, self.y, moveto, self.damage, MainCharacter)) self.lastAttack = pg.time.get_ticks() class Rat(Mobs): def __init__(self, name, ms, x, y, health, damage, level, asp, atr, sprite='people/rat.png', drops=0) -> 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 < pg.time.get_ticks(): obj[3].append(Punch('punch', 100, self.x, self.y, moveto, self.damage, MainCharacter)) self.lastAttack = pg.time.get_ticks() 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 = pg.time.get_ticks() pos = vec(1,0) angle = pos.angle_to(moveto) self.sprite = pg.transform.rotate(self.sprite, -angle) def die(self, objects, kills): touches = pg.sprite.spritecollideany(self, objects[0] + objects[1]) 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 pg.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 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 def update(self, objects): self.move(objects) self.die(objects, self.kills)