house.py

Created by jlevon

Created on November 22, 2025

12.9 KB

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()

During your visit to our site, NumWorks needs to install "cookies" or use other technologies to collect data about you in order to:

With the exception of Cookies essential to the operation of the site, NumWorks leaves you the choice: you can accept Cookies for audience measurement by clicking on the "Accept and continue" button, or refuse these Cookies by clicking on the "Continue without accepting" button or by continuing your browsing. You can update your choice at any time by clicking on the link "Manage my cookies" at the bottom of the page. For more information, please consult our cookies policy.