maison hante avec fantomes et autres
from kandinsky import fill_rect, draw_string, set_pixel from ion import * import time, random # --------------------------- # CONFIGURATION STYLE GAME BOY # --------------------------- TILE = 12 MAP_W = 20 MAP_H = 11 SCREEN_W = MAP_W * TILE SCREEN_H = MAP_H * TILE HUD_H = 20 COLOR_BG = (0,0,0) COLOR_WALL = (50,50,50) COLOR_FLOOR = (120,150,120) COLOR_CHEST = (180,180,50) COLOR_ENEMY = (180,50,50) COLOR_VENDOR = (50,180,50) COLOR_SECRET = (120,0,120) COLOR_PLAYER_BODY = (180,180,180) COLOR_HUD_BG = (0,0,0) # --------------------------- # SPRITES # --------------------------- SPRITE_PLAYER = [ ".....##.....", "..#######...", ".###RRR#.#..", "#..#RRR#..#.", "....#R#...#.", "....###.....", "....#.#.....", "....#.#.....", "....#.#.....", "...#...#....", "##......##...", "............", ] COL_MAP_PLAYER = {"#":(180,180,180),"R":COLOR_PLAYER_BODY} MANOR = [ "....RRRR....", "...RWWWWR...", "..RWWWWWWR..", ".RWWWWWWWWR.", "RRRRRRRRRRRR", "R........R..", "R..WW..WW.R.", "R........R..", "RRRRRRRRRR..", "....RRRR...." ] COL_MANOR = {"R":(100,100,100),"W":(180,180,180),".":COLOR_BG} # --------------------------- # MAPS (3 niveaux) # --------------------------- MAPS = [ ["####################", "#.....C......#.....#", "#..####..###..#..E.#", "#..#..#.......#....#", "#..#..#####.###....#", "#..#......#........#", "#..####S###.C......#", "#.........#........#", "#....V....#........#", "#.........#........#", "####################"], ["####################", "#S..V....#.....C...#", "#..###..#..###..#..#", "#..#....#..E......##", "#..#..#####.###...##", "#..#..#......#.....#", "#..##.###.###.##C..#", "#....E.........#...#", "#.....C...#S.......#", "#.................##", "####################"], ["####################", "#C..#.....#....#...#", "#..##.####.#.##.#E.#", "#..#..#..#.#....#..#", "#..#..#..#.#.##.#..#", "#..#..#..#.#.##.#..#", "#..#..##S#.#..C.#..#", "#..E.......#......V#", "#......C.....#.....#", "#S................S#", "####################"] ] # --------------------------- # ÉTAT JOUEUR # --------------------------- player = {"x":2,"y":2,"hp":12,"coins":15,"level":0,"attack":2,"inventory":["Potion"],"equip":None,"px":2,"py":2} opened_chests = set() bonus_items = ["Potion","Sword","Axe"] ghosts_levels = [ [ {"x":5,"y":7,"alive":True,"hp":3,"px":5,"py":7}, {"x":10,"y":5,"alive":True,"hp":4,"px":10,"py":5}, {"x":15,"y":8,"alive":True,"hp":6,"px":15,"py":8} ], [ {"x":6,"y":2,"alive":True,"hp":4,"px":6,"py":2}, {"x":12,"y":7,"alive":True,"hp":5,"px":12,"py":7} ], [ {"x":3,"y":8,"alive":True,"hp":5,"px":3,"py":8}, {"x":14,"y":3,"alive":True,"hp":6,"px":14,"py":3}, {"x":9,"y":6,"alive":True,"hp":7,"px":9,"py":6} ] ] bosses = [ {"x":12,"y":6,"alive":True,"hp":20,"max_hp":20,"phase":1,"px":12,"py":6}, None, {"x":16,"y":4,"alive":True,"hp":30,"max_hp":30,"phase":1,"px":16,"py":4} ] # --------------------------- # DESSIN / HUD # --------------------------- def draw_sprite(sprite, cmap, x0, y0): px = x0 * TILE py = y0 * TILE for j,row in enumerate(sprite): for i,ch in enumerate(row): if ch != ".": set_pixel(px+i, py+j, cmap.get(ch,(0,0,0))) def draw_tile(x,y,color): fill_rect(x*TILE, y*TILE, TILE, TILE, color) def draw_tile_from_map(x,y): if not (0 <= x < MAP_W and 0 <= y < MAP_H): draw_tile(x,y,COLOR_BG) return c = MAPS[player["level"]][y][x] if c == "#": draw_tile(x,y,COLOR_WALL) elif c == ".": draw_tile(x,y,COLOR_FLOOR) elif c == "C": draw_tile(x,y,COLOR_CHEST) elif c == "E": draw_tile(x,y,COLOR_ENEMY) elif c == "V": draw_tile(x,y,COLOR_VENDOR) elif c == "S": draw_tile(x,y,COLOR_SECRET) else: draw_tile(x,y,COLOR_FLOOR) def draw_player(): draw_sprite(SPRITE_PLAYER, COL_MAP_PLAYER, player["x"], player["y"]) def draw_bold_string(s, x, y, color=(255,255,255)): draw_string(s,x,y,color) draw_string(s,x+1,y,color) def draw_hud(): fill_rect(0, SCREEN_H, SCREEN_W, HUD_H, COLOR_HUD_BG) eq = player["equip"] if player["equip"] else "None" potions = player["inventory"].count("Potion") draw_bold_string('HP:{} CO:{} ATK:{} [{}]'.format(player['hp'], player['coins'], player['attack'], eq), 2, SCREEN_H) draw_bold_string('Potions:{}'.format(potions), SCREEN_W-90, SCREEN_H) # --------------------------- # FONCTIONS DU JEU # --------------------------- # DRAW MAP FULL def draw_map_full(level): fill_rect(0,0,SCREEN_W,SCREEN_H+HUD_H,COLOR_BG) for y,row in enumerate(MAPS[level]): for x,c in enumerate(row): draw_tile_from_map(x,y) for g in ghosts_levels[level]: if g["alive"]: draw_tile(g["x"], g["y"], COLOR_ENEMY) b = bosses[level] if b and b.get("alive",False): draw_tile(b["x"],b["y"],(150,0,150)) draw_player() draw_hud() # MOVE PLAYER def is_valid(x,y): if not (0 <= x < MAP_W and 0 <= y < MAP_H): return False return MAPS[player["level"]][y][x] != "#" def move_player(dx,dy): nx, ny = player["x"]+dx, player["y"]+dy if is_valid(nx,ny): draw_tile_from_map(player["x"],player["y"]) player["x"], player["y"] = nx, ny draw_player() c = MAPS[player["level"]][ny][nx] if c == "C": open_chest(nx,ny) elif c == "S": old_level = player["level"] player["level"] = (old_level+1)%len(MAPS) # trouver point safe placed = False for yy,row in enumerate(MAPS[player["level"]]): for xx,ch in enumerate(row): if ch == ".": player["x"],player["y"]=xx,yy placed=True break if placed: break draw_map_full(player["level"]) draw_hud() # INVENTAIRE def use_potion(): if "Potion" in player["inventory"]: player["hp"]+=5 player["inventory"].remove("Potion") draw_bold_string("Potion utilisée!",2,SCREEN_H-15,(0,255,0)) else: draw_bold_string("Pas de potion...",2,SCREEN_H-15,(255,0,0)) def equip_weapon(weapon): player["equip"]=weapon if weapon=="Sword": player["attack"]=5 elif weapon=="Axe": player["attack"]=7 draw_bold_string("Arme équipée:{}".format(weapon), 2, SCREEN_H-15, (255,255,0)) def inventory_loop(): idx=0 while True: fill_rect(0,0,SCREEN_W,SCREEN_H+HUD_H,(0,0,0)) draw_bold_string("=== INVENTAIRE ===",60,20,(255,255,0)) items=[] if player["equip"]: items.append("Arme:"+player["equip"]) items+=player["inventory"] if not items: draw_string("Inventaire vide",20,60,(150,150,150)) else: for i,item in enumerate(items): color=(255,255,0) if i==idx else (255,255,255) draw_string(item,20,60+i*20,color) draw_string("UP/DOWN | EXE utiliser | BACK quitter",10,SCREEN_H-15,(255,0,0)) if items: if keydown(KEY_UP): idx=(idx-1)%len(items);time.sleep(0.25) elif keydown(KEY_DOWN): idx=(idx+1)%len(items);time.sleep(0.25) elif keydown(KEY_EXE): chosen=items[idx] if chosen=="Potion": use_potion() elif "Sword" in chosen: equip_weapon("Sword") elif "Axe" in chosen: equip_weapon("Axe") time.sleep(0.25) if keydown(KEY_BACKSPACE) or keydown(KEY_BACK): return time.sleep(0.08) # COFFRES def open_chest(x,y): key=(player["level"],x,y) if key in opened_chests: draw_bold_string("Coffre vide...",2,SCREEN_H-15,(150,150,150)) return item=random.choice(bonus_items) if item=="Potion": player["inventory"].append("Potion"); draw_bold_string("Potion trouvée!",2,SCREEN_H-15,(0,255,0)) else: equip_weapon(item) opened_chests.add(key) # FANTOMES def fight_ghost(g): g["hp"]-=player["attack"] draw_bold_string("Tu attaques!",2,SCREEN_H-15,(255,255,0)) if g["hp"]<=0: g["alive"]=False; draw_bold_string("Fantôme vaincu!",2,SCREEN_H-30,(0,255,0)); ghost_loot(g) else: player["hp"]-=1; draw_bold_string("Le fantôme riposte!",2,SCREEN_H-45,(255,0,0)) def ghost_loot(g): drop=random.choice(["coins","potion","nothing"]) if drop=="coins": gain = random.randint(1,5) player["coins"] += gain draw_bold_string("+{} pièces!".format(gain), 2, SCREEN_H-15, (255,255,0)) def step_ghosts(): for g in ghosts_levels[player["level"]]: if not g["alive"]: continue oldx,oldy=g["x"],g["y"] dx=player["x"]-g["x"]; dy=player["y"]-g["y"] move_x=1 if dx>0 else -1 if dx<0 else 0 move_y=1 if dy>0 else -1 if dy<0 else 0 moved=False if is_valid(g["x"]+move_x,g["y"]): g["x"]+=move_x; moved=True elif is_valid(g["x"],g["y"]+move_y): g["y"]+=move_y; moved=True if moved: draw_tile_from_map(oldx,oldy); draw_tile(g["x"],g["y"],COLOR_ENEMY) if g["x"]==player["x"] and g["y"]==player["y"]: fight_ghost(g) # BOSS def fight_boss(): b=bosses[player["level"]] if not b or not b.get("alive",False): return b["hp"]-=player["attack"] draw_bold_string("Tu attaques le BOSS!",2,SCREEN_H-15,(255,255,0)) if b["hp"]<=0: b["alive"]=False; draw_bold_string("Boss vaincu!",2,SCREEN_H-30,(0,255,0)); boss_loot(b) else: dmg=2 if b["phase"]==1 else 3 player["hp"]-=dmg draw_bold_string("Le boss frappe!",2,SCREEN_H-45,(255,0,0)) if b["hp"]<=(b["max_hp"]//2) and b["phase"]==1: b["phase"]=2; draw_bold_string("Le boss change de phase!",2,SCREEN_H-60,(255,50,200)) def boss_loot(b): rare_drop=random.choice(["Potion+","Épée légendaire"]) if rare_drop=="Potion+": player["inventory"]+=[ "Potion","Potion"]; draw_bold_string("Potion+ obtenue!",2,SCREEN_H-15,(0,255,0)) else: equip_weapon("Sword"); player["attack"]+=3; draw_bold_string("Épée légendaire équipée!",2,SCREEN_H-15,(255,255,0)) def step_boss(): b=bosses[player["level"]] if not b or not b.get("alive",False): return if random.randint(0,5)==0: dx=random.choice([-1,0,1]); dy=random.choice([-1,0,1]) nx,ny=b["x"]+dx,b["y"]+dy if 0<=nx<MAP_W and 0<=ny<MAP_H and is_valid(nx,ny): oldx,oldy=b["x"],b["y"]; b["x"],b["y"]=nx,ny draw_tile_from_map(oldx,oldy); draw_tile(b["x"],b["y"],(150,0,150)) if b["x"]==player["x"] and b["y"]==player["y"]: fight_boss() # HAUNTED FLASH def haunted_flash(): if random.randint(0,25)==0: coords=[(x,y) for y,row in enumerate(MAPS[player["level"]]) for x,c in enumerate(row) if c=="#" and random.randint(0,6)==0] for x,y in coords: draw_tile(x,y,(100,0,0)) draw_player(); step_ghosts(); step_boss(); draw_hud() time.sleep(0.04) for x,y in coords: draw_tile_from_map(x,y) # GAME OVER def check_game_over(): if player["hp"]<=0: fill_rect(0,0,SCREEN_W,SCREEN_H+HUD_H,(0,0,0)) draw_bold_string("GAME OVER",100,80,(255,0,0)) draw_string("Les fantômes t'ont eu...",60,120,(200,50,50)) time.sleep(2) return True return False # --------------------------- # MAIN LOOP # --------------------------- def main(): draw_map_full(player["level"]) while True: if keydown(KEY_UP): move_player(0,-1); time.sleep(0.08) elif keydown(KEY_DOWN): move_player(0,1); time.sleep(0.08) elif keydown(KEY_LEFT): move_player(-1,0); time.sleep(0.08) elif keydown(KEY_RIGHT): move_player(1,0); time.sleep(0.08) elif keydown(KEY_EXE): inventory_loop(); draw_hud(); draw_player(); step_ghosts(); step_boss() elif keydown(KEY_BACKSPACE) or keydown(KEY_BACK): return step_ghosts(); step_boss(); haunted_flash() if check_game_over(): return draw_hud() time.sleep(0.06) # --------------------------- # MENU # --------------------------- menu_items=["Jouer","Quitter"] selected=0 def draw_menu(): fill_rect(0,0,SCREEN_W,SCREEN_H,COLOR_BG) draw_string("MANOIR HANTÉ",80,20,(180,180,180)) draw_sprite(MANOR,COL_MANOR,10,5) for i,item in enumerate(menu_items): color=(0,255,0) if i==selected else (180,180,180) draw_string(item,140,150+i*20,color) def menu_loop(): global selected while True: draw_menu() time.sleep(0.1) if keydown(KEY_UP): selected = (selected - 1) % len(menu_items) time.sleep(0.15) elif keydown(KEY_DOWN): selected = (selected + 1) % len(menu_items) time.sleep(0.15) elif keydown(KEY_EXE): if menu_items[selected] == "Jouer": break else: fill_rect(0, 0, SCREEN_W, SCREEN_H, COLOR_BG) draw_string("Au revoir!", 100, 100, (180, 180, 180)) time.sleep(1) # NumWorks n'aime pas exit(), on peut faire return depuis la fonction menu return # --------------------------- # LANCEMENT # --------------------------- menu_loop() draw_map_full(player["level"]) main()