diff --git a/game/core.py b/game/core.py index ec99dde..1745fbc 100644 --- a/game/core.py +++ b/game/core.py @@ -1,17 +1,39 @@ from game.personnage import Personnage, ClassType +from graphics.layers import Sprite, PopUp + +from random import randint + class Game: def __init__(self): - self.personnage = Personnage(" ", ClassType.GUERRIER) - self.ennemy = Personnage("Zombie", ClassType.ZOMBIE) + self.personnage = None + self.ennemy = None + + def late_init(self): + self.personnage = Personnage(" ", ClassType.GUERRIER, "player") + self.ennemy = self.rand_ennemy() + + def rand_ennemy(self): + classes = ["GUERRIER", "MAGICIEN", "VOLEUR", "ELFE"] + noms = ["Bill Gates", "Dark Vador", "Gargamel", "G-Man"] + return Personnage(noms[randint(0, 3)], ClassType[classes[randint(0, 3)]], "opponant") + + def init_personnage(self, nom, class_type): + self.personnage = Personnage(nom, ClassType[class_type], "player") def attack(self, attacker, victim): - atk_dmg = attacker.jet_attaque() - def_dmg = victim.jet_defense() - victim.change_hp(atk_dmg) - attacker.change_hp(def_dmg) + atk_jet = attacker.jet_attaque() + def_jet = victim.jet_defense() + if (atk_jet >= def_jet): + rand = randint(1, 8) + victim.change_hp(rand + rand * ((attacker.class_type.atkP + attacker.stats.atkP)//100)) + else: + rand = randint(1, 4) + attacker.change_hp(rand + rand * ((victim.class_type.defP + victim.stats.defP)//100)) + if self.personnage.get_hp() == 0: for i in range(50): - print("FIN") + PopUp(1, "Vous avez perdu! Relancez le jeu pour continuer...", True) if self.ennemy.get_hp() == 0: - self.ennemy = Personnage("Zombie", ClassType.ZOMBIE) \ No newline at end of file + self.ennemy = self.rand_ennemy() + PopUp(1, "Ennemi vaincu!") \ No newline at end of file diff --git a/game/personnage.py b/game/personnage.py index 4011e76..c54e32a 100644 --- a/game/personnage.py +++ b/game/personnage.py @@ -1,50 +1,48 @@ from random import randint from enum import Enum +from graphics.engine import Screen +from graphics.layers import Sprite + class StatsSet: - #HP, ATK%, DEF%, CRIT, CRITRATE%, XPCOEF - def __init__(self, hp, atkP, defP, crit, critrateP, xpcoef): + #HP, ATK, DEF, INITIATIVE, XPCOEF + def __init__(self, hp, atkP, defP, initiative, xpcoef): self.hp = hp self.atkP = atkP self.defP = defP - self.crit = crit - self.critrateP = critrateP + self.initiative = initiative self.xpcoef = xpcoef class ClassType(Enum): - GUERRIER = StatsSet(20, 10, 12, 5, 10, 8) - MAGICIEN = StatsSet(16, 12, 6, 8, 15, 7) - #TODO: Changer stats - VOLEUR = StatsSet(16, 12, 6, 8, 15, 7) - ELF = StatsSet(16, 12, 6, 8, 15, 7) - - #Enemy - ZOMBIE = StatsSet(16, 12, 6, 8, 15, 7) + GUERRIER = StatsSet(20, 10, 15, 5, 8) + MAGICIEN = StatsSet(15, 15, 6, 5, 7) + VOLEUR = StatsSet(17, 22, 9, 7, 9) + ELFE = StatsSet(12, 30, 1, 5, 10) class Personnage: - def __init__(self, nom, class_type): + def __init__(self, nom, class_type, place): self.nom = nom self.class_name = class_type.name self.class_type = class_type.value self.__hp = self.class_type.hp - self.stats = StatsSet(0, 0, 0, 0, 0, 0) + self.stats = StatsSet(0, 0, 0, 0, 0) self.__xp = 1 + + Sprite(3, self.class_name, place) #self.inventaire = - #self.element = def jet_attaque(self): - #TODO: ajouter crit damage = randint(1, 20) self.change_exp(self.class_type.xpcoef * self.__xp) - return damage + damage * (self.class_type.atkP//100 + self.stats.atkP//100) + return damage + self.class_type.initiative + self.stats.initiative def jet_defense(self): damage = randint(1, 20) self.change_exp(self.class_type.xpcoef * self.__xp) - return damage + damage * (self.class_type.defP//100 + self.stats.defP//100) + return damage + self.class_type.initiative + self.stats.initiative def get_hp(self): return self.__hp @@ -71,10 +69,12 @@ class Personnage: f"- HP: {self.class_type.hp} + {self.stats.hp}", f"- ATK%: {self.class_type.atkP} + {self.stats.atkP}", f"- DEF%: {self.class_type.defP} + {self.stats.defP}", - f"- CRIT: {self.class_type.crit} + {self.stats.crit}", - f"- CRITRATE%: {self.class_type.critrateP} + {self.stats.critrateP}", + f"- INITIATIVE: {self.class_type.initiative} + {self.stats.initiative}", f"- XPCOEF: {self.class_type.xpcoef} + {self.stats.xpcoef}" ) def reduced_stats(self): - return("Ennemi:", f"Type: {self.class_name}", f"Vie: {self.__hp}",) \ No newline at end of file + return(f"Ennemi: {self.nom}", + f"Type: {self.class_name}", + f"Vie: {self.__hp}", + ) \ No newline at end of file diff --git a/game/sprites/ELFE.txt b/game/sprites/ELFE.txt new file mode 100644 index 0000000..f94ca1b --- /dev/null +++ b/game/sprites/ELFE.txt @@ -0,0 +1,16 @@ + @@@@@@& + @@@&&@&@@@@& + &@@%&@@%&@*@@@& + &@%&@@@%*,,,,@@% + &@&&*@@#,,@@@,@% + *&&**%@/*#*,,,#*/@,&* + %@@&@&&***,,,%&& + %&@@@@@#@@&%%%&#@& + &%#@&/##&%**,,,,,/%@& + ***,,,,,,,,,***%%,,,,,****%&&&@@ @% + %#&@@@@@%@((((&@@@ @ + ** (%&@@/(##%(//*#* ( + %@*/#%/&%(//*% + %*//(#% @#(/**% + @%&&&&@ @&&&&%@ + @%%###@ @###%%@ diff --git a/game/sprites/GUERRIER.txt b/game/sprites/GUERRIER.txt new file mode 100644 index 0000000..719b773 --- /dev/null +++ b/game/sprites/GUERRIER.txt @@ -0,0 +1,14 @@ + + @/, + @/### + %#/%%&@@@%& + @@*(%##&&@@%& + /@@@@@@& &&% + &%,@@,,@@ @( + @%&@&%&&@%&@@@% + @//**@ *%%%&%%&% + .@//*/& /#% %&/ + %@ &%/ + @% @&/ + %% &@ + &%#% @% \ No newline at end of file diff --git a/game/sprites/MAGICIEN.txt b/game/sprites/MAGICIEN.txt new file mode 100644 index 0000000..35a67e7 --- /dev/null +++ b/game/sprites/MAGICIEN.txt @@ -0,0 +1,16 @@ + + &&&&&& + &...*&(&& + ., .%.%%%* + ..... %%&&*(#&&####&&& + (##%%%&&%**#%&##&&#&& + ((/&&&&&&%*##&&&#%&&&#( + .& .&&&(#*&&&&#&& + @@&&&%%/@(&&&#&#//** + .@ &&&&##%@@###&% ## + &&%&&%##%%@####% + %%&&&%%@@@@&### + %&&&&&&& @@@%% + **((( *((((@ + ((### .%#%%# + (((#( .%%%%% diff --git a/game/sprites/VOLEUR.txt b/game/sprites/VOLEUR.txt new file mode 100644 index 0000000..e6fff19 --- /dev/null +++ b/game/sprites/VOLEUR.txt @@ -0,0 +1,16 @@ + + + + ,,, + ,,.%%%. + , ,.%%%. + ,,,..,,. + ,,..(((.,. + ,.,,.///,. + ,./(////,. + ./,,*,,,. + .*,.**,, + ,, *, + . . + + \ No newline at end of file diff --git a/graphics/engine.py b/graphics/engine.py index 6beb1e8..f2bdbc5 100644 --- a/graphics/engine.py +++ b/graphics/engine.py @@ -3,17 +3,23 @@ from graphics.colors import Colors class Screen: """Moteur graphique basé sur des claques""" + instance = None + def __init__(self, game): + Screen.instance = self + size = os.get_terminal_size() + if size.columns < 120 or size.lines < 30: + raise OSError("terminal is too small") self.x = size.columns self.y = size.lines - 2 self.frame = {} self.__layers = {} + self.handlers = [] + game.late_init() self.game = game - self.handlers = [] - def set_layer(self, layer): self.__layers[layer.name] = layer @@ -53,16 +59,15 @@ class Screen: class Layer: - def __init__(self, screen, z_index, name): + def __init__(self, z_index, name): self.name = name self.frame = {} self.z_index = z_index - self.x = screen.x - self.y = screen.y - self.screen = screen + self.x = Screen.instance.x + self.y = Screen.instance.y self.handle_keys = True - screen.set_layer(self) + Screen.instance.set_layer(self) def put_char(self, char, x, y, colors=[]): if x > self.x or x < 0 or y > self.y or y < 0: diff --git a/graphics/layers.py b/graphics/layers.py index 423855d..5d851f6 100644 --- a/graphics/layers.py +++ b/graphics/layers.py @@ -1,40 +1,37 @@ -from game.personnage import Personnage, ClassType - from graphics.colors import Colors from graphics.engine import Layer +from graphics.engine import Screen + from getkey import keys from re import match class GUI(Layer): - def __init__(self, screen, z_index, buttons): - super().__init__(screen, z_index, "gui") + """Calque du menu principal""" + def __init__(self, z_index, buttons): + super().__init__(z_index, "gui") self.buttons = buttons self.current = 0 self.handle_keys = False - #self.personnage = Personnage(" ", ClassType.GUERRIER) - #self.ennemy = Personnage("Zombie", ClassType.ZOMBIE) def draw(self): super().draw() x = 1 for button in self.buttons: - color = (Colors.WHITEBG, Colors.BLACK) - if self.buttons[self.current] == button: - color = (Colors.REDBG, Colors.BLACK) + color = (Colors.REDBG, Colors.BLACK) if self.buttons[self.current] == button else (Colors.WHITEBG, Colors.BLACK) self.put_string(button, x, self.y-10, color) x += len(button)+1 - stats = self.screen.game.personnage.affiche_caracteristiques() + stats = Screen.instance.game.personnage.affiche_caracteristiques() for i in range(4): - self.put_string(stats[i], 0, self.y-8+i, [Colors.RED2]) - for i in range(7): - self.put_string(stats[i+4], 40, self.y-8+i, [Colors.RED2]) + self.put_string(stats[i], 1, self.y-8+i, [Colors.RED2]) + for i in range(6): + self.put_string(stats[i+4], (self.x-2)//3, self.y-8+i, [Colors.RED2]) - ennemy_stats = self.screen.game.ennemy.reduced_stats() + ennemy_stats = Screen.instance.game.ennemy.reduced_stats() for i in range(3): - self.put_string(ennemy_stats[i], 80, self.y-8+i, [Colors.BLUE2]) + self.put_string(ennemy_stats[i], self.x-(self.x-2)//3, self.y-8+i, [Colors.BLUE2]) return self @@ -44,33 +41,33 @@ class GUI(Layer): elif key == keys.LEFT and self.current > 0: self.current -= 1 elif key == keys.ENTER and self.current == 0: - attacker = self.screen.game.personnage - victim = self.screen.game.ennemy - self.screen.game.attack(attacker, victim) + attacker = Screen.instance.game.personnage + victim = Screen.instance.game.ennemy + Screen.instance.game.attack(attacker, victim) class StartPopUp(Layer): - def __init__(self, screen, z_index): - super().__init__(screen, z_index, "popup") - self.__classes = ["GUERRIER", "MAGICIEN", "VOLEUR", "ELF"] + """Calque du menu de lancement""" + def __init__(self, z_index): + super().__init__(z_index, "popup") + self.__classes = ["GUERRIER", "MAGICIEN", "VOLEUR", "ELFE"] self.__choosen_class = 0 self.__username = "" + self.__missing_un = False def draw(self): super().draw() #bg - self.rect(self.x//3, self.y//6, self.x//3 + self.x//4, self.y//4 + self.y//3, Colors.WHITEBG) + self.rect(self.x//3, self.y//6, self.x*2//3, self.y//4 + self.y//3, Colors.WHITEBG) #name - self.put_string("Nom: [A-z]", self.x//3 + 1, self.y//6 + 1, [Colors.WHITEBG, Colors.BLACK]) - self.rect(self.x//3 + 1, self.y//6 + 2, self.x//3 + self.x//4 - 1, self.y//6 + 2, Colors.BLACKBG) + self.put_string("Nom: (A-z)", self.x//3 + 1, self.y//6 + 1, [Colors.WHITEBG, Colors.BLACK]) + self.rect(self.x//3 + 1, self.y//6 + 2, self.x*2//3 - 2, self.y//6 + 2, Colors.REDBG if self.__missing_un else Colors.BLACKBG) self.put_string(self.__username, self.x//3 + 1, self.y//6 + 2, [Colors.WHITE, Colors.BLACKBG]) #Classes - self.put_string("Classe perso.: SHIFT+[1-4]", self.x//3 + 1, self.y//6 + 4, [Colors.WHITEBG, Colors.BLACK]) + self.put_string("Classe perso.: (Flèches)", self.x//3 + 1, self.y//6 + 4, [Colors.WHITEBG, Colors.BLACK]) y = 0 for user_class in self.__classes: - colors = [Colors.GREYBG, Colors.WHITE] - if y == self.__choosen_class: - colors = [Colors.REDBG, Colors.BLACK] + colors = colors = [Colors.REDBG, Colors.BLACK] if y == self.__choosen_class else [Colors.GREYBG, Colors.WHITE] self.put_string(user_class, self.x//3 + 2, self.y//6 + 5 + y, colors) y += 1 @@ -83,10 +80,62 @@ class StartPopUp(Layer): self.__username += key elif key == keys.BACKSPACE: self.__username = self.__username[:-1] - elif match("[1-4]", key): - self.__choosen_class = int(key)-1 + elif key == keys.UP and self.__choosen_class > 0: + self.__choosen_class -= 1 + elif key == keys.DOWN and self.__choosen_class < 3: + self.__choosen_class += 1 elif key == keys.TAB: - self.screen.get_layer("gui").handle_keys = True - self.screen.del_layer("popup") - self.screen.game.personnage = Personnage(self.__username, ClassType[self.__classes[self.__choosen_class]]) - #self.screen.get_layer("gui").personnage = Personnage(self.__username, ClassType[self.__classes[self.__choosen_class]]) \ No newline at end of file + if len(self.__username) == 0: + self.__missing_un = True + return + Screen.instance.get_layer("gui").handle_keys = True + Screen.instance.del_layer("popup") + Screen.instance.game.init_personnage(self.__username, self.__classes[self.__choosen_class]) + +class PopUp(Layer): + """Calque du menu de lancement""" + def __init__(self, z_index, message, block=False): + super().__init__(z_index, "popup") + self.message = message + self.block = block + Screen.instance.get_layer("gui").handle_keys = False + + def draw(self): + super().draw() + self.rect(self.x//3, self.y//6, self.x*2//3, self.y//4 + self.y//3, Colors.WHITEBG) + length = (self.x*2//3-1) - (self.x//3+1) + x = 0 + y = 0 + for char in self.message: + self.put_char(char, self.x//3 + 1 + x, self.y//6 + 1 + y, [Colors.WHITEBG, Colors.BLACK]) + x += 1 + if x == length: + x = 0 + y += 1 + + if not self.block: + self.put_string("TAB pour confirmer", self.x//3 + 1, self.y//4 + self.y//3 -1, [Colors.WHITEBG, Colors.BLACK]) + + return self + + def key_handler(self, key): + if key == keys.TAB and not self.block: + Screen.instance.get_layer("gui").handle_keys = True + Screen.instance.del_layer("popup") + +class Sprite(Layer): + def __init__(self, z_index, sprite_name, place): + if place != "player" and place != "opponant": + raise ValueError("wrong sprite name") + super().__init__(z_index, place) + self.place = place + self.sprite_name = sprite_name + + def draw(self): + super().draw() + with open(f"game/sprites/{self.sprite_name}.txt", "r") as sprite: + pos = (1, 1) if self.place == "player" else (Screen.instance.x*2//3+1, 1) + self.put_string(sprite.read(), *pos) + + def key_handler(self, key): + pass \ No newline at end of file diff --git a/main.py b/main.py index 4a71994..f422061 100644 --- a/main.py +++ b/main.py @@ -11,8 +11,8 @@ if __name__ == "__main__": #Initialise la partie graphique game = Game() screen = Screen(game) - layers.StartPopUp(screen, 1) - layers.GUI(screen, 2, ["Attaquer", "Inventaire"]) + layers.StartPopUp(1) + layers.GUI(2, ["Attaquer", "Inventaire"]) listener.build_thread(screen).start()