class Sword: """ coup d'épée venant du joueur """ DELTA_POSS = list(Vect(0, 0).g_rect(Vect(1, 1))) def __init__(self, pos, dammage): self.pos = pos self.cpt = len(self.DELTA_POSS) - 1 self.dammage = dammage def update(self, mat_collide, player_pos): """ Met à jour l'enemie """ if self.cpt < 0: return True self.pos = player_pos + self.DELTA_POSS[self.cpt] self.cpt -= 1 return False def render(self): """ render """ return chars.C_SWORDS[-self.cpt % len(chars.C_SWORDS)]
def __init__(self): """ Personnage """ self.pos = Vect(0, 0) self.direction = Vect(1, 0) self.distance_view = 7 self.bullet = self.BULLET_MAX self.hp = self.HP_MAX self.level = 0 self.money = self.START_MONEY self.sword_damage = 1 self.gun_damage = 2
def __init__(self): """ Initialise l'affichage """ # Sauvegarde du terminal self._my_fd = sys.stdin.fileno() self._old = termios.tcgetattr(self._my_fd) # Désactivation echo stdin new = termios.tcgetattr(self._my_fd) new[3] = new[3] & ~termios.ECHO # lflags termios.tcsetattr(self._my_fd, termios.TCSADRAIN, new) # Lancement ecran second + Désactivation curseur print("\033[?1049h\033[H" + "\033[?25l", end="") sys.stdout.flush() # Bufferisarion de stdout self._BUFFER_SIZE = 500 * 500 sys.stdout = open(sys.stdout.fileno(), "w", self._BUFFER_SIZE) # Méthodes d'édition self.my_print = sys.stdout.write self.my_flush = sys.stdout.flush # Taille de l'écran self.size = Vect(0, 0)
def update(self, mat_collide, depl_vect): """ Met à jour la position du personnage en fonction des evenements et de mat_collide """ if depl_vect != Vect(0, 0): self.direction = depl_vect new_pos = self.pos + depl_vect # Tests de collision (Diagonales) if mat_collide[new_pos.x][self.pos.y]: # premier chemin libre en x if mat_collide[new_pos.x][new_pos.y]: # deuxieme chemin libre en y self.pos = new_pos else: # deuxieme chemin bloque en y self.pos.x = new_pos.x elif mat_collide[self.pos.x][new_pos.y]: # premier chemin libre en y if mat_collide[new_pos.x][new_pos.y]: # deuxieme chemin libre en x self.pos = new_pos else: # deuxieme chemin bloque en x self.pos.y = new_pos.y else: # Aucun chemin libre # Do nothind pass
def g_xy(self): """ Generateur sur tous les points de la salle """ for x in range(self.size.x): for y in range(self.size.y): yield self.p[0] + Vect(x, y)
def main(): """ TU """ tab = [[1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 0, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]] start = Vect(0, 0) end = Vect(7, 6) path = calc_path_astart(tab, start, end) assert len(path), 'erreur A*' print(path)
def get_user_depl(events): """ retourne le vecteur de deplacement """ # position desire (componsation des fleches opposées) return Vect( int(Key.right in events) - int(Key.left in events), int(Key.up in events) - int(Key.down in events))
def g_pos(self): """ Retourne un générateur sur tous les position de l'écran dans l'ordre d'affichage """ for scr_y in range(self.size.y - 1, 0, -1): for scr_x in range(self.size.x): yield Vect(scr_x, scr_y)
def g_xyCollide(self): """ Retourne les points accessible a @ Retourne un génerateur de Vect Permet la création de matrice des collision """ for x in range(1, self.size.x - 1): for y in range(1, self.size.y - 1): yield self.p[0] + Vect(x, y)
def calc_path_astart(mat_collide, start, end): """ Couche de compatibilite """ return list( map( lambda tup: Vect(tup[0], tup[1]), astar(numpy.array(mat_collide), (start.x, start.y), (end.x, end.y))))
def get_size(self): """ Met a jour la taille de l'écran """ # bof ... H, W = map(int, os.popen('stty size', 'r').read().split()) assert H * W <= self._BUFFER_SIZE self.size = Vect(W, H) return self.size
def g_case_visible(self, mat_collide): """ retourne sur toutes les cases visibles par self dans mat_collide """ # Nb : prend les segments depuis un cercle et non un rect # n'est pas OK border = self.pos.g_rect(Vect(self.distance_view, self.distance_view)) for bordure_pos in border: for pos in self.pos.g_bresenham_line(bordure_pos): if self.pos.distance(pos) >= self.distance_view: break if not Vect(0, 0) <= pos < Vect(len(mat_collide), len(mat_collide[0])): break if not mat_collide[pos.x][pos.y]: yield pos break yield pos
def g_case_visible(tab_collide, center, radius): """ retourne sur toutes les cases visibles depuis un point center dans un rayon radius """ # Nb : prend les segments depuis un cercle et non un rect # n'est pas OK border = center.g_rect(Vect(radius, radius)) for bordure_pos in border: for pos in center.g_bresenham_line(bordure_pos): if center.distance(pos) >= radius: break if not tab_collide[pos.x][pos.y]: break yield pos
def render_menu(): """ Render state = menu """ # for scr in g_scr_pos: # self.buffer_window[scr.x][scr.y] = ' ' scr_center = scr_size // 2 box_cote = Vect(scr_size.x // 4, scr_size.y // 8) for pos in scr_center.g_rect(box_cote): self.buffer_window[pos.x][pos.y] = chars.C_PAUSE_BORDER for pos in scr_center.g_rect_fill_no_border(box_cote): self.buffer_window[pos.x][pos.y] = chars.C_PAUSE_FILL for i, char in enumerate("texte"): self.buffer_window[scr_center.x - 3 + i][scr_center.y] = char
def main(): """ Entry point """ try: SIZE_Y = 30 SIZE_X = 60 screen = [[True for _ in range(SIZE_Y + 2)] for _ in range(SIZE_X + 2)] for _ in range(4): p1 = Vect(0, 0) | Vect(SIZE_X, SIZE_Y) p2 = Vect(0, 0) | Vect(SIZE_X, SIZE_Y) for pos in p1.g_bresenham_line(p2): screen[pos.x][pos.y] = False screen[pos.x + 1][pos.y] = False # Calculs center = Vect(30, 15) for pos in g_case_visible(screen, center, 10): screen[pos.x][pos.y] = 'F' screen[center.x][center.y] = '@' # affichage for y in range(SIZE_Y): for x in range(SIZE_X): char = screen[x][SIZE_Y - y - 1] if char is False: print_char = "\033[0;34;41m" + ' ' + "\033[0m" if char is True: print_char = ' ' if char == 'F': print_char = "\033[0;34;43m" + ' ' + "\033[0m" if char == '@': print_char = "\033[0;31;45m" + ' ' + "\033[0m" if char == 'B': print_char = "\033[0;34;40m" + 'X' + "\033[0m" print(print_char, end='') print("") finally: print("END")
def __init__(self, pos_xmax, pos_ymax): """ initialise une pièce p[1] p[2] +---------+ (X2, Y2) : self.p[2] | L | |H C H| X2 = X0 + L - 1 | L | Y2 = X0 + H - 1 +---------+ (L, H) : self.size (X0, Y0)p[0] p[3] pos_max est un point existant """ pmax = Vect(pos_xmax, pos_ymax) self.size = (self._VECT_COTE_MIN | self._VECT_COTE_MAX) + Vect(2, 2) self.p = [None for _ in range(4)] self.p[0] = Vect(0, 0) | (pmax - self.size) norm_size = self.size - Vect(1, 1) self.p[1] = self.p[0] + (norm_size & Vect(False, True)) self.p[2] = self.p[0] + (norm_size & Vect(True, True)) self.p[3] = self.p[0] + (norm_size & Vect(True, False)) self.center = self.p[0] + norm_size // 2
def connect(self, other_room): """ crée le chemin entre les deux salles """ assert not self.isRoomCollide(other_room), "Les salles se superposent" door1 = self.newRandomPointInRoom() door2 = other_room.newRandomPointInRoom() path = [] m = randint(0, 1) depl = door1.dirrectionTo(door2) & Vect(m, 1 - m) if depl == Vect(1, 0): door1.x = self.p[2].x door2.x = other_room.p[0].x elif depl == Vect(-1, 0): door1.x = self.p[0].x door2.x = other_room.p[2].x elif depl == Vect(0, 1): door1.y = self.p[2].y door2.y = other_room.p[0].y elif depl == Vect(0, -1): door1.y = self.p[0].y door2.y = other_room.p[2].y cur_point = door1 + depl # On crée un chemin tant que l'on arrive pas dans la other_room while cur_point != door2: path.append(cur_point) # Deplacement en x ou y aléatoire m = randint(0, 1) cur_point += cur_point.dirrectionTo(door2) & Vect(m, 1 - m) assert len(path) >= self._OFFSET_COLLIDE, 'Chemin trop court' # On retourne un tripet porte, chemin, porte return (door1, path, door2)
def vect(self): vect = Vect(self._name, self._executions) return vect
class Player(): """ Classe Player : """ BULLET_MAX = 10 HP_MAX = 10 START_MONEY = 0 def __init__(self): """ Personnage """ self.pos = Vect(0, 0) self.direction = Vect(1, 0) self.distance_view = 7 self.bullet = self.BULLET_MAX self.hp = self.HP_MAX self.level = 0 self.money = self.START_MONEY self.sword_damage = 1 self.gun_damage = 2 def level_up(self, pos): """ Le personnage gagne un level """ self.level += 1 self.pos = pos def g_case_visible(self, mat_collide): """ retourne sur toutes les cases visibles par self dans mat_collide """ # Nb : prend les segments depuis un cercle et non un rect # n'est pas OK border = self.pos.g_rect(Vect(self.distance_view, self.distance_view)) for bordure_pos in border: for pos in self.pos.g_bresenham_line(bordure_pos): if self.pos.distance(pos) >= self.distance_view: break if not Vect(0, 0) <= pos < Vect(len(mat_collide), len(mat_collide[0])): break if not mat_collide[pos.x][pos.y]: yield pos break yield pos def shoot(self): """ Tire une nouvelle balle """ self.bullet -= 1 return Bullet(self.pos, self.direction, self.gun_damage) def strike(self, mat_collide): """ Donne un coup d'épée """ return Sword(self.pos + self.direction, self.sword_damage) def add_money(self, value): """ Ajoute des pièces au Player """ assert value >= 0 self.money += value return True def add_hp(self, value): """ Ajoute des HP au Player """ assert value >= 0 if self.hp == self.HP_MAX: return False self.hp = min(self.hp + value, self.HP_MAX) return True def add_bullets(self, value): """ Ajoute des balles au Player """ assert value >= 0 if self.bullet == self.BULLET_MAX: return False self.bullet = min(self.bullet + value, self.BULLET_MAX) return True def update(self, mat_collide, depl_vect): """ Met à jour la position du personnage en fonction des evenements et de mat_collide """ if depl_vect != Vect(0, 0): self.direction = depl_vect new_pos = self.pos + depl_vect # Tests de collision (Diagonales) if mat_collide[new_pos.x][self.pos.y]: # premier chemin libre en x if mat_collide[new_pos.x][new_pos.y]: # deuxieme chemin libre en y self.pos = new_pos else: # deuxieme chemin bloque en y self.pos.x = new_pos.x elif mat_collide[self.pos.x][new_pos.y]: # premier chemin libre en y if mat_collide[new_pos.x][new_pos.y]: # deuxieme chemin libre en x self.pos = new_pos else: # deuxieme chemin bloque en x self.pos.y = new_pos.y else: # Aucun chemin libre # Do nothind pass def render(self): """ Retourne le char à afficher """ return chars.C_PLAYER def __str__(self): """ Retourne une chaine d'affichage """ heal_str = ('\u2665' * int(self.hp / self.HP_MAX * 10) + ' ' * (10 - int(self.hp / self.HP_MAX * 10))) bullet_str = ('|' * int(self.bullet) + ' ' * int(self.BULLET_MAX - self.bullet)) return ('Position : {} | HP : [' + heal_str + '] | Bullets [' + bullet_str + ']')
def newRandomPointInRoom(self): """ Retourne un point dans la pièce (Pas dans les murs) """ return (self.p[0] + Vect(1, 1)) | (self.p[2] - Vect(1, 1))
def isPointCollideWithMargin1(self, point): """ Retourne si le point touche la salle avec une marge de 1 vers l'exterieur """ return self.p[0] - Vect(1, 1) <= point <= self.p[2]
def isScrPosInScr(scr_pos): return Vect(0, 0) <= scr_pos < scr_size
def render_run(): """ Render state = run """ def scr2mat(scr_pos): return scr_pos + self.player.pos - scr_size // 2 def mat2scr(mat_pos): return mat_pos - self.player.pos + scr_size // 2 def isScrPosInScr(scr_pos): return Vect(0, 0) <= scr_pos < scr_size # Rendu du fond for scr in g_scr_pos: mat_pos = scr2mat(scr) # Dans matrice et Visible if Vect(0, 0) <= mat_pos < Vect(self._XMAX, self._YMAX) and \ (self.mat_view[mat_pos.x][mat_pos.y] or self.RULE_VISION): self.buffer_window[scr.x][scr.y] = \ self.mat_render[mat_pos.x][mat_pos.y] else: self.buffer_window[scr.x][scr.y] = ' ' # Rendu des entitées for entity in chain(self.bullets, self.swords, self.monsters, self.treasure, [self.player], [self.door]): scr_pos = mat2scr(entity.pos) if isScrPosInScr(scr_pos) \ and self.mat_view[entity.pos.x][entity.pos.y] \ or self.RULE_VISION: self.buffer_window[scr_pos.x][scr_pos.y] = entity.render() # Rendu du joueur scr_pos = mat2scr(self.player.pos) self.buffer_window[scr_pos.x][scr_pos.y] = self.player.render() # Text top_bar = "Town : Koku <> ./ [***] " + os_info bot_bat1 = str(self.player) + " \ | Monsters : " + str(len(self.monsters)) bot_bat2 = ("[" + str(chars.C_BAR_DECORATIONS[self.cpt_monster % 4]) + "]" + "| Level : " + str(self.player.level) + "| Gold : " + str(self.player.money) + "| Sword : " + str(self.player.sword_damage) + "| Gun : " + str(self.player.gun_damage) + "| Monster lvl : " + str(self.monster_life)) for i, ch in enumerate( zip_longest(top_bar, bot_bat1, bot_bat2, range(scr_size.x), fillvalue=' ')): self.buffer_window[i][scr_size.y - 1] = (ch[0]) self.buffer_window[i][1] = (ch[1]) self.buffer_window[i][2] = (ch[2]) # Bousolle for y, string in enumerate( [" N ", " ^ ", "W<-o->E", " v ", " S "]): for x, char in enumerate(string): self.buffer_window[scr_size.x - 7 + x][5 - y] = (char)
class Room(): """ Classe définissant une pièce. Elle permet de l'initialiser et de la connecter à d'autres. """ # Dimentions maximales minimales _VECT_COTE_MIN = Vect(10, 6) _VECT_COTE_MAX = Vect(10, 8) # Espace autour de la pièce _OFFSET_COLLIDE = 2 def __init__(self, pos_xmax, pos_ymax): """ initialise une pièce p[1] p[2] +---------+ (X2, Y2) : self.p[2] | L | |H C H| X2 = X0 + L - 1 | L | Y2 = X0 + H - 1 +---------+ (L, H) : self.size (X0, Y0)p[0] p[3] pos_max est un point existant """ pmax = Vect(pos_xmax, pos_ymax) self.size = (self._VECT_COTE_MIN | self._VECT_COTE_MAX) + Vect(2, 2) self.p = [None for _ in range(4)] self.p[0] = Vect(0, 0) | (pmax - self.size) norm_size = self.size - Vect(1, 1) self.p[1] = self.p[0] + (norm_size & Vect(False, True)) self.p[2] = self.p[0] + (norm_size & Vect(True, True)) self.p[3] = self.p[0] + (norm_size & Vect(True, False)) self.center = self.p[0] + norm_size // 2 def newRandomPointInRoom(self): """ Retourne un point dans la pièce (Pas dans les murs) """ return (self.p[0] + Vect(1, 1)) | (self.p[2] - Vect(1, 1)) def g_corners(self): """ Retourne les 4 coins de la salle """ return (point for point in self.p) def isPointCollide(self, point): """ Retourne si le point touche la salle (mur inclus) """ return self.p[0] <= point <= self.p[2] def isPointCollideWithMargin1(self, point): """ Retourne si le point touche la salle avec une marge de 1 vers l'exterieur """ return self.p[0] - Vect(1, 1) <= point <= self.p[2] def isRoomCollide(self, other_room): """ Retourne si l'autre salle est en Collision A une distance (offset) près """ offset = self._OFFSET_COLLIDE # La distance minimale entre les salles if other_room.p[0].x >= self.p[0].x + self.size.x + offset or \ other_room.p[0].x + other_room.size.x + offset <= self.p[0].x or \ other_room.p[0].y >= self.p[0].y + self.size.y + offset or \ other_room.p[0].y + other_room.size.y + offset <= self.p[0].y: return False return True def connect(self, other_room): """ crée le chemin entre les deux salles """ assert not self.isRoomCollide(other_room), "Les salles se superposent" door1 = self.newRandomPointInRoom() door2 = other_room.newRandomPointInRoom() path = [] m = randint(0, 1) depl = door1.dirrectionTo(door2) & Vect(m, 1 - m) if depl == Vect(1, 0): door1.x = self.p[2].x door2.x = other_room.p[0].x elif depl == Vect(-1, 0): door1.x = self.p[0].x door2.x = other_room.p[2].x elif depl == Vect(0, 1): door1.y = self.p[2].y door2.y = other_room.p[0].y elif depl == Vect(0, -1): door1.y = self.p[0].y door2.y = other_room.p[2].y cur_point = door1 + depl # On crée un chemin tant que l'on arrive pas dans la other_room while cur_point != door2: path.append(cur_point) # Deplacement en x ou y aléatoire m = randint(0, 1) cur_point += cur_point.dirrectionTo(door2) & Vect(m, 1 - m) assert len(path) >= self._OFFSET_COLLIDE, 'Chemin trop court' # On retourne un tripet porte, chemin, porte return (door1, path, door2) def distance(self, other_room): """ Retourne la distance entre deux salles (Des centres) """ return self.p[0].distanceSquare(other_room.p[0]) def g_xy(self): """ Generateur sur tous les points de la salle """ for x in range(self.size.x): for y in range(self.size.y): yield self.p[0] + Vect(x, y) def g_xyRender(self, middle_char): """ Indique le type de char a chaque xy : Retourne un couple ! (Vect , char) """ from tableborder import BORDERS line_style = 2 for pos in self.g_xy(): if pos == self.p[0]: type_char = BORDERS[line_style].low_left elif pos == self.p[1]: type_char = BORDERS[line_style].top_left elif pos == self.p[2]: type_char = BORDERS[line_style].top_right elif pos == self.p[3]: type_char = BORDERS[line_style].low_right elif pos.x == self.p[0].x or pos.x == self.p[2].x: type_char = BORDERS[line_style].vertical elif pos.y == self.p[0].y or pos.y == self.p[2].y: type_char = BORDERS[line_style].horizontal else: type_char = middle_char yield (pos, type_char) def g_xyCollide(self): """ Retourne les points accessible a @ Retourne un génerateur de Vect Permet la création de matrice des collision """ for x in range(1, self.size.x - 1): for y in range(1, self.size.y - 1): yield self.p[0] + Vect(x, y)