

    ' Creat By Atomose Studio 2026 with LibertyBasic 4.51 pro

    nomainwin

    ' check si le jeu est déjà ouvert
    on error goto [file]
    open "system.txt" for input as #secu
    input #secu, pass$
    close #secu
    if pass$ <> "1" then end
    [file]
    on error goto 0
    pass$ = "0"
    open "system.txt" for output as #secu2
    print #secu2, pass$
    close #secu2

    ' détection de la langue du pc
    version = 2
    open "getlang.bat" for output as #bat
    q$ = chr$(34)
    print #bat, "reg query " + q$ + "HKCU\Control Panel\International" + q$ + " /v LocaleName > reglang.txt"
    close #bat
    run "getlang.bat"
    timer 500, [lireRegistre]
    wait

    [lireRegistre]
    timer 0
    on error goto [menuMultijoueur]
    open "reglang.txt" for input as #regcheck
    close #regcheck
    on error goto [erreurGenerale]
    open "reglang.txt" for input as #reg
    while not(eof(#reg))
        line input #reg, ligne$
        if instr(lower$(ligne$), "fr-") > 0 then version = 1
    wend
    close #reg
    kill "reglang.txt"
    kill "getlang.bat"

    ' correcteur DPI pour haute résolution
    DPI.AWARENESS.CONTEXT.PER.MONITOR.AWARE = -1 '-3
    CALLDLL #user32, "SetThreadDpiAwarenessContext", DPI.AWARENESS.CONTEXT.PER.MONITOR.AWARE AS LONG, RetVal AS LONG

    'variables du plein écran
    global fsSrcW, fsSrcH, fsDstW, fsDstH, fsOffX, fsOffY, hdcFs, hFsHwnd, pleinEcran

    GameVersion$ = "2.32" : sauvegardeExiste = 0
    global ws2Ouvert, dy, dx, direction, rotation, gaucheLibre, droiteLibre, murDevant, oldPerso1x, oldPerso1y, oldDirection, nbMonstres, enCombat, monstreActuel, timerMonstre, delaiMouvementMonstre, hero.nom$, hero.niveau, hero.pv, hero.pvMax, hero.attaque, hero.defense, hero.xp, hero.xpProchain, hero.kills, oldHero.pv, bossX, bossY, bossActif, bossPv, bossPvMax, bossVaincu, timerDegatsProximite, delaiDegatsProximite, timerRegeneration, delaiRegeneration, regenParSeconde, errCode, errMsg$, resultWinsock, resultBind, resultClose, resultIoctl, modeMulti, estServeur, estClient, socketServeur, socketClient, ipServeur$, portServeur, maxJoueurs, nbJoueursConnectes, timerReseau, delaiReseau, monIdJoueur, sockAddrFrom$, sockAddrFromGlobal$, statutConnexion$, tentativesConnexion, tempsDepuisConnexion, timerDerniereReponseServeur, timeoutServeur, dernierNbJoueurs, animationAttaque, timerAnimationAttaque, posAttaqueX, posAttaqueY, scaleAttaque, typeAnimation, timerAttaqueMonstre, compteurRespawn, fichierSauvegarde$, sauvegardeExiste, carteActuelle, teleportMap1X, teleportMap1Y, teleportMap2X, teleportMap2Y, timerRegenBoss, delaiRegenBoss, regenBossParCycle
    dim adressesClients$(10), joueur(10, 6), p(50,50), monstre(51, 8), oldMonstre(51, 2), joueurDistant(10, 5), oldJoueurDistant(10, 3), oldJoueurServeur(10, 3), portsATester(10)
    ws2Ouvert = 0 : modeMulti = 0 : estServeur = 0 : estClient = 0 : portServeur = 5555 : maxJoueurs = 4 : nbJoueursConnectes = 0 : timerReseau = 0 : delaiReseau = 2 : timerDerniereReponseServeur = 0 : timeoutServeur = 10 : ipServeur$ = "127.0.0.1" : tentativesConnexion = 0 : tempsDepuisConnexion = 0 : statutConnexion$ = "Déconnecté" : timerDegatsProximite = 0 : delaiDegatsProximite = 5 : regenParSeconde = 1 : timerRegeneration = 0 : delaiRegeneration = 5 : monIdJoueur = 0 : dernierNbJoueurs = 0 : animationAttaque = 0 : timerAnimationAttaque = 0 : posAttaqueX = 0 : posAttaqueY = 0 : scaleAttaque = 100 : typeAnimation = 0 : timerAttaqueMonstre = 0 : compteurRespawn = 0 : carteActuelle = 1 : teleportMap1X = 23 : teleportMap1Y = 16 : teleportMap2X = 23 : teleportMap2Y = 16 : fichierSauvegarde$ = "ancestral_save.txt" : timerRegenBoss = 0 : delaiRegenBoss = 50 : regenBossParCycle = 5

    gosub [obtenirIP]

    if exists(fichierSauvegarde$) then
        on error goto [erreurLectureSauvegarde]
        open fichierSauvegarde$ for input as #save
        input #save, hero.nom$
        input #save, hero.niveau
        input #save, hero.pvMax
        input #save, hero.attaque
        input #save, hero.defense
        input #save, hero.xp
        input #save, hero.xpProchain
        input #save, hero.kills
        input #save, perso1x
        input #save, perso1y
        input #save, direction
        input #save, carteActuelle
        close #save
        sauvegardeExiste = 1
        on error goto 0
        goto [menuMultijoueur]
        [erreurLectureSauvegarde]
        close #save
        sauvegardeExiste = 0
        on error goto 0
    end if

    [menuMultijoueur]
    WindowWidth = 520 : WindowHeight = 550 : UpperLeftX = int((DisplayWidth - WindowWidth) / 2) : UpperLeftY = int((DisplayHeight - WindowHeight) / 2)
    button #mainmenu.fr, "FR", [FR], UL, 5, 1, 25, 25
    button #mainmenu.en, "EN", [EN], UL, 30, 1, 25, 25
    statictext #mainmenu.titre, "Dungeon Crawler", 120, 46, 300, 40
    statictext #mainmenu.titre2, GameVersion$ , 390, 86, 300, 20
    if version = 1 then statictext #mainmenu.titre3, "Jeu créé par ALEXANDRE LOMUTO avec Liberty Basic 4.51" , 105, 488, 330, 20
    if version = 2 then statictext #mainmenu.titre3, "Game created by ALEXANDRE LOMUTO with Liberty Basic 4.51" , 100, 488, 330, 20
    statictext #mainmenu.infoPerso, " ", 60, 100, 400, 20
    if sauvegardeExiste = 1 and version = 1 then button #mainmenu.continuer, "Continuer la partie (niveau " + str$(hero.niveau) + ")" , [chargerSauvegarde], UL, 60, 128, 400, 45
    if sauvegardeExiste = 1 and version = 2 then button #mainmenu.continuer, "Continue the game (level " + str$(hero.niveau) + ")" , [chargerSauvegarde], UL, 60, 128, 400, 45
    if version = 1 then button #mainmenu.nouveau, "Nouvelle partie (Solo)", [lancerNouveauSolo], UL, 60, 185, 400, 45
    if version = 2 then button #mainmenu.nouveau, "New game (Solo)", [lancerNouveauSolo], UL, 60, 185, 400, 45
    if version = 1 then button #mainmenu.serveur, "Créer un serveur LAN", [lancerNouveauServeur], UL, 60, 240, 350, 45
    if version = 2 then button #mainmenu.serveur, "Create a LAN server", [lancerNouveauServeur], UL, 60, 240, 350, 45
    if version = 1 then button #mainmenu.rejoindre, "Rejoindre un serveur LAN", [rejoindreServeur2], UL, 60, 400, 400, 45
    if version = 2 then button #mainmenu.rejoindre, "Join a LAN server", [rejoindreServeur2], UL, 60, 400, 400, 45
    statictext #mainmenu.sep, "________________________________________", 80, 295, 440, 20
    if version = 1 then statictext #mainmenu.labelIP, "Adresse IP :", 65, 335, 130, 22
    if version = 2 then statictext #mainmenu.labelIP, "IP address :", 65, 335, 130, 22
    textbox #mainmenu.ip, 250, 332, 205, 24
    if version = 1 then statictext #mainmenu.labelPort, "Port de connexion :", 65, 368, 180, 22
    if version = 2 then statictext #mainmenu.labelPort, "Connection port :", 65, 368, 180, 22
    textbox #mainmenu.port, 250, 365, 205, 24
    button #mainmenu.aide, "?", [ouvrirAide], UL, 415, 240, 45, 45
    button #mainmenu.aide, "fullscreen", [FS], UL, 427, 1, 80, 25

    open "" for window_nf as #mainmenu
    #mainmenu, "trapclose [quitterMenu]"
    #mainmenu, "font Courier New 14"
    #mainmenu.fr, "!font Arial 8"
    #mainmenu.en, "!font Arial 8"
    #mainmenu.aide, "!font Arial 10 bold"
    #mainmenu.titre "!font Arial 25 bold"
    #mainmenu.titre2 "!font Arial 10 bold"
    #mainmenu.titre3 "!font Arial 8 italic"
    #mainmenu.ip, ipServeur$
    #mainmenu.port, str$(portServeur)
    #mainmenu.ip, "!font Arial 11"
    #mainmenu.port, "!font Arial 11"
    wait

    [FS]
    if pleinEcran = 0 then pleinEcran = 1 : notice "Fullscreen ON" : wait
    if pleinEcran = 1 then pleinEcran = 0 : notice "Fullscreen OFF" : wait
    [FR] version = 1 : close #mainmenu : goto [menuMultijoueur]
    [EN] version = 2 : close #mainmenu : goto [menuMultijoueur]
    [quitterMenu]
    pass$ = "1" : open "system.txt" for output as #secu2 : print #secu2, pass$ : close #secu2
    close #mainmenu : end

    [ouvrirAide]
    WindowWidth = 520 : WindowHeight = 560 : UpperLeftX = int((DisplayWidth - WindowWidth) / 2) : UpperLeftY = int((DisplayHeight - WindowHeight) / 2)
    if version = 1 then
        statictext #aide.t1, "Uniquement sur le PC serveur", 58, 30, 420, 30
        statictext #aide.t3, "Etape 1 : Ouvrir les paramètres du pare-feu", 115, 85, 460, 20
        statictext #aide.t4, "1. Appuyez sur Windows + R", 160, 108, 440, 18
        statictext #aide.t5, "2. Tapez : wf.msc", 160, 126, 440, 18
        statictext #aide.t6, "3. Appuyez sur Entree", 160, 144, 440, 18
        statictext #aide.t7, "Etape 2 : Creer une règle entrante", 115, 180, 460, 20
        statictext #aide.t8, "1. Cliquez sur Règles de trafic entrant", 160, 203, 440, 18
        statictext #aide.t9, "2. Cliquez sur Nouvelle règle...", 160, 221, 440, 18
        statictext #aide.t10, "Etape 3 : Configurer la règle", 115, 257, 460, 20
        statictext #aide.t11, "1. Type de règle : Port => Suivant", 160, 280, 440, 18
        statictext #aide.t12, "2. Protocole : UDP", 160, 298, 440, 18
        statictext #aide.t13, "3. Ports : 5555, 5556, 5557, 8888, 9999", 160, 316, 440, 18
        statictext #aide.t14, "Etape 4 : Autoriser la connexion => Suivant", 115, 352, 460, 20
        statictext #aide.t15, "Etape 5 : Cochez Domaine, Privé ET Public", 115, 388, 460, 20
        statictext #aide.t16, "Etape 6 : Nom de la règle", 115, 424, 460, 20
        statictext #aide.t17, "Nom : Ancestral Dungeon Server", 160, 447, 440, 18
        statictext #aide.t18, "Description : Autoriser UDP multijoueur LAN", 160, 465, 440, 18
    end if
    if version = 2 then
        statictext #aide.t1, "Only on the server PC", 106, 30, 420, 30
        statictext #aide.t3, "Step 1 : Open firewall settings", 115, 85, 460, 20
        statictext #aide.t4, "1. Press Windows + R", 160, 108, 440, 18
        statictext #aide.t5, "2. Type : wf.msc", 160, 126, 440, 18
        statictext #aide.t6, "3. Press Enter", 160, 144, 440, 18
        statictext #aide.t7, "Step 2 : Create an inbound rule", 115, 180, 460, 20
        statictext #aide.t8, "1. Click on Inbound Rules", 160, 203, 440, 18
        statictext #aide.t9, "2. Click on New Rule...", 160, 221, 440, 18
        statictext #aide.t10, "Step 3 : Configure the rule", 115, 257, 460, 20
        statictext #aide.t11, "1. Rule type : Port -> Next", 160, 280, 440, 18
        statictext #aide.t12, "2. Protocol : UDP", 160, 298, 440, 18
        statictext #aide.t13, "3. Ports : 5555, 5556, 5557, 8888, 9999", 160, 316, 440, 18
        statictext #aide.t14, "Step 4 : Allow the connection => Next", 115, 352, 460, 20
        statictext #aide.t15, "Step 5 : Check Domain, Private AND Public", 115, 388, 460, 20
        statictext #aide.t16, "Step 6 : Name the rule", 115, 424, 460, 20
        statictext #aide.t17, "Name : Ancestral Dungeon Server", 160, 447, 440, 18
        statictext #aide.t18, "Description : Allow UDP for LAN multiplayer", 160, 465, 440, 18
    end if
    if version = 1 then open "Configuration du Pare-feu" for window_nf as #aide
    if version = 2 then open "Firewall Configuration" for window_nf as #aide
    #aide, "trapclose [aideRetour]"
    #aide, "font Arial 10"
    #aide.t1, "!font Arial 20 bold"
    #aide.t3, "!font Arial 10 bold"
    #aide.t7, "!font Arial 10 bold"
    #aide.t10, "!font Arial 10 bold"
    #aide.t14, "!font Arial 10 bold"
    #aide.t15, "!font Arial 10 bold"
    #aide.t16, "!font Arial 10 bold"
    wait

    [aideRetour] close #aide : wait' : goto [menuMultijoueur]

    [chargerSauvegarde]
    close #mainmenu : hero.pv = hero.pvMax : modeMulti = 0 : estServeur = 0 : estClient = 0
    goto [demarrerJeu]

    [lancerNouveauSolo]
    close #mainmenu
    hero.nom$ = "Héros" : hero.niveau = 1 : hero.pvMax = 40 : hero.pv = hero.pvMax
    hero.attaque = 11 : hero.defense = 3 : hero.xp = 0 : hero.xpProchain = 50
    hero.kills = 0 : perso1x = 1 : perso1y = 1 : direction = 1 : carteActuelle = 1
    sauvegardeExiste = 0 : modeMulti = 0 : estServeur = 0 : estClient = 0
    goto [demarrerJeu]

    [lancerNouveauServeur]
    close #mainmenu
    hero.nom$ = "Héros" : hero.niveau = 1 : hero.pvMax = 40 : hero.pv = hero.pvMax
    hero.attaque = 11 : hero.defense = 3 : hero.xp = 0 : hero.xpProchain = 50
    hero.kills = 0 : perso1x = 1 : perso1y = 1 : direction = 1 : carteActuelle = 1
    sauvegardeExiste = 0 : modeMulti = 1 : estServeur = 1 : estClient = 0
    goto [demarrerJeu]

    [rejoindreServeur2]
    #mainmenu.ip, "!contents? ipServeur$"
    #mainmenu.port, "!contents? portStr$"
    portServeur = val(portStr$)
    if ipServeur$ = "" then ipServeur$ = "127.0.0.1"
    if portServeur = 0 then portServeur = 5555
    close #mainmenu
    hero.nom$ = "Héros" : hero.niveau = 1 : hero.pvMax = 40 : hero.pv = hero.pvMax
    hero.attaque = 11 : hero.defense = 3 : hero.xp = 0 : hero.xpProchain = 50
    hero.kills = 0 : perso1x = 1 : perso1y = 1 : direction = 1 : carteActuelle = 1
    sauvegardeExiste = 0 : modeMulti = 1 : estServeur = 0 : estClient = 1
    goto [demarrerJeu]

    [sauvegarderPersonnage]
    on error goto [erreurSauvegarde]
    open fichierSauvegarde$ for output as #save
    print #save, hero.nom$
    print #save, hero.niveau
    print #save, hero.pvMax
    print #save, hero.attaque
    print #save, hero.defense
    print #save, hero.xp
    print #save, hero.xpProchain
    print #save, hero.kills
    print #save, perso1x
    print #save, perso1y
    print #save, direction
    print #save, carteActuelle
    close #save
    on error goto 0
    return

    [erreurSauvegarde]
    on error goto 0
    on error goto [ignorerErrSave]
    close #save
    [ignorerErrSave]
    on error goto 0
    return

    [demarrerJeu]
    timer 0
    if sauvegardeExiste = 0 then
        perso1x = 1 : perso1y = 1 : direction = 1
        hero.nom$ = "Héros" : hero.niveau = 1 : hero.pvMax = 40 : hero.pv = hero.pvMax : carteActuelle = 1
        hero.attaque = 11 : hero.defense = 3 : hero.xp = 0 : hero.xpProchain = 50 : hero.kills = 0
    end if

    ' Fenetre source hors ecran - rendu upscale par FS_Render
    if pleinEcran = 1 then WindowWidth = 1110 : WindowHeight = 630 : UpperLeftX = -2000 : UpperLeftY = -2000
    if pleinEcran = 0 then WindowWidth = 1100 : WindowHeight = 630 : UpperLeftX=int((DisplayWidth-WindowWidth)/2) : UpperLeftY=int((DisplayHeight-WindowHeight)/2)

    oldPerso1x = perso1x : oldPerso1y = perso1y : oldDirection = direction : x = 50 : y = 50 : rotation = 1
    enCombat = 0 : monstreActuel = 0 : timerMonstre = 0 : delaiMouvementMonstre = 2 : bossActif = 1 : bossVaincu = 0 : bossX = 23 : bossY = 23 : bossPv = 200 : bossPvMax = 200

    for i = 1 to 50 : oldMonstre(i, 0) = -1 : oldMonstre(i, 1) = -1 : next i

    for i = 1 to 10
        joueurDistant(i, 0) = 0
        joueurDistant(i, 1) = 0
        joueurDistant(i, 2) = 0
        joueurDistant(i, 3) = 0
        joueurDistant(i, 4) = 0
        joueurDistant(i, 5) = 0
        oldJoueurDistant(i, 1) = -1
        oldJoueurDistant(i, 2) = -1
        oldJoueurDistant(i, 3) = -1
    next i

    for i = 1 to maxJoueurs
        oldJoueurServeur(i, 1) = -1
        oldJoueurServeur(i, 2) = -1
        oldJoueurServeur(i, 3) = -1
    next i

    sprites$ = "attaque mur1 mur2 mur3 mur4 mur5 mur6 mur7 mur8 mur11 mur12 mur13 mur14 mur15 perso1 garde1 boss1 perso2 perso2b"
    for s = 1 to 19 : loadbmp word$(sprites$,s), "data\"+word$(sprites$,s)+".bmp" : next

    if estServeur = 1 then titreJeu$ = "Ancestral Dungeon - SERVEUR HOST - IP : " + adresseIP$ + "  Port " + str$(portServeur) + " | niveau ";carteActuelle
    if estServeur = 0 and estClient = 1 then titreJeu$ = "Ancestral Dungeon - CLIENT - Connecté à : " + ipServeur$ + " | niveau ";carteActuelle
    if estServeur = 0 and estClient = 0 then titreJeu$ = "Ancestral Dungeon by Atomose" + " | niveau ";carteActuelle

    graphicbox #main.map, 2, 0, 900, 599
    graphicbox #main.minimap, 903, 0,188, 190
    graphicbox #main.info, 903, 193,188, 406
    open titreJeu$ for window_nf as #main
    #main, "trapclose [quit]"
    #main.map, "setfocus; down; fill black; when leftButtonDown [clicGauche]"
    #main.map, "addsprite mur13 mur13; addsprite mur1 mur1; addsprite mur2 mur2; addsprite mur3 mur3; addsprite mur4 mur4; addsprite mur5 mur5; addsprite mur6 mur6; addsprite mur7 mur7; addsprite mur8 mur8; addsprite mur9 mur8; addsprite mur10 mur8; addsprite mur11 mur11; addsprite mur12 mur12; addsprite mur14 mur14; addsprite mur15 mur15; addsprite perso1 perso1; addsprite garde1 garde1 ; addsprite boss1 boss1"
    #main.map, "addsprite attaque attaque; addsprite attaque2 attaque; addsprite perso2 perso2; addsprite perso3 perso2; addsprite perso4 perso2; addsprite perso2b perso2b; addsprite perso3b perso2b; addsprite perso4b perso2b"
    #main.map, "spriteXY mur13 0 0;spriteXY mur1 0 0; spriteXY mur2 0 0; spriteXY mur3 0 0; spriteXY mur4 0 0; spriteXY mur5 0 0; spriteXY mur6 0 0; spriteXY mur7 0 0; spriteXY mur8 0 0; spriteXY mur9 300 0; spriteXY mur10 300 0; spriteXY mur11 0 0; spriteXY mur12 0 0; spriteXY mur14 0 0; spriteXY mur15 0 0; spriteXY perso1 270 300; spriteXY garde1 0 0; spriteXY boss1 0 0"
    #main.map, "spriteXY attaque 0 0; spriteXY attaque2 0 0; spriteXY perso2 0 0; spriteXY perso3 0 0; spriteXY perso4 0 0; spriteXY perso2b 0 0; spriteXY perso3b 0 0; spriteXY perso4b 0 0"
    #main.map, "spritevisible mur13 off;spritevisible mur1 off; spritevisible mur2 off; spritevisible mur3 off; spritevisible mur4 off; spritevisible mur5 off; spritevisible mur6 off; spritevisible mur7 off; spritevisible mur8 off; spritevisible mur9 off; spritevisible mur10 off; spritevisible mur11 off; spritevisible mur12 off; spritevisible mur14 off; spritevisible mur15 off; spritevisible perso1 on; spritevisible garde1 off; spritevisible boss1 off "
    #main.map, "spritevisible attaque off; spritevisible attaque2 off; spritevisible perso2 off; spritevisible perso3 off; spritevisible perso4 off; spritevisible perso2b off; spritevisible perso3b off; spritevisible perso4b off"
    #main.map, "spritetofront perso1; spritescale perso1 200"

    gosub [map]
    gosub [placerMonstres]
    gosub [verifierMonstre]
    gosub [afficherInfo]
    gosub [drawMinimap]

    if pleinEcran = 1 then gosub [FS_Init]
    if pleinEcran = 1 then gosub [FS_Render]
    if pleinEcran = 0 then #main.map "drawsprites"

    if estServeur = 1 then
        gosub [initialiserServeur]
        if socketServeur = 0 then notice "Erreur: impossible de créer le serveur!" : close #main : goto [menuMultijoueur]
    end if

    if estClient = 1 then
        gosub [initialiserClient]
        if socketClient = 0 then notice "Erreur: impossible de se connecter au serveur!" : close #main : goto [menuMultijoueur]
    end if


    [endLEVEL]
    timer 0
    #main.map, "discard"

    if modeMulti > 0 then timerReseau = timerReseau + 1 : if timerReseau >= delaiReseau then timerReseau = 0 : gosub [miseAJourReseau]
    dy = 0 : dx = 0 : rotation = 0
    call readJStick 1

    if dx = 2 and pleinEcran = 1 then goto [quit]

    timerMonstre = timerMonstre + 1 : if timerMonstre >= delaiMouvementMonstre then timerMonstre = 0 : gosub [deplacerMonstres]
    timerDegatsProximite = timerDegatsProximite + 1 : if timerDegatsProximite >= delaiDegatsProximite then timerDegatsProximite = 0 : gosub [verifierDegatsProximite]
    if estServeur = 1 or estClient = 0 then
        compteurRespawn = (compteurRespawn + 1) mod 50
        if compteurRespawn = 0 then gosub [respawnMonstre]
    end if

    if enCombat = 1 and monstreActuel > 0 then
        if monstre(monstreActuel, 2) = 0 then
            enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
        else
            select case direction
                case 0: frontCheckX = perso1x : frontCheckY = perso1y - 1
                case 1: frontCheckX = perso1x + 1 : frontCheckY = perso1y
                case 2: frontCheckX = perso1x : frontCheckY = perso1y + 1
                case 3: frontCheckX = perso1x - 1 : frontCheckY = perso1y
            end select
            if monstre(monstreActuel, 8) = 1 then
                if bossActif = 0 or bossVaincu = 1 then
                    enCombat = 0 : monstreActuel = 0 : monstre(monstreActuel, 2) = 0 : gosub [afficherInfo]
                else
                    mX = INT(monstre(monstreActuel, 0)) : mY = INT(monstre(monstreActuel, 1))
                    if mX <> INT(frontCheckX) or mY <> INT(frontCheckY) then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
                end if
            else
                mX = INT(monstre(monstreActuel, 0)) : mY = INT(monstre(monstreActuel, 1))
                if mX <> INT(frontCheckX) or mY <> INT(frontCheckY) then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
            end if
        end if
    end if

    if hero.pv < hero.pvMax and enCombat = 0 then
        monstresProches = 0
        for checkDir = 0 to 3
            select case checkDir
                case 0: cX = perso1x : cY = perso1y - 1  ' Nord
                case 1: cX = perso1x + 1 : cY = perso1y  ' Est
                case 2: cX = perso1x : cY = perso1y + 1  ' Sud
                case 3: cX = perso1x - 1 : cY = perso1y  ' Ouest
            end select
            if cX >= 0 and cX < x and cY >= 0 and cY < y then
                for mCheck = 1 to nbMonstres
                    if monstre(mCheck, 2) = 1 and INT(monstre(mCheck, 0)) = INT(cX) and INT(monstre(mCheck, 1)) = INT(cY) then monstresProches = monstresProches + 1 : exit for
                next mCheck
            end if
        next checkDir
        if monstresProches = 0 then
            timerRegeneration = timerRegeneration + 1
            if timerRegeneration >= delaiRegeneration then
                timerRegeneration = 0
                regenParSeconde = 1 + int((hero.niveau - 1) * 0.5)
                ancienPV = hero.pv
                hero.pv = hero.pv + regenParSeconde
                if hero.pv > hero.pvMax then hero.pv = hero.pvMax
                if hero.pv <> ancienPV then gosub [afficherInfo]
            end if
        else
            timerRegeneration = 0
        end if
    end if

    if bossActif = 1 and bossVaincu = 0 and monstre(1, 2) = 1 then
    if monstre(1, 3) < monstre(1, 4) then
        bossEnCombat = 0
        if enCombat = 1 and monstreActuel = 1 then bossEnCombat = 1

        if estServeur = 1 then
            for checkJ = 1 to maxJoueurs
                if joueur(checkJ, 6) = 1 then
                    distBoss = abs(joueur(checkJ, 1) - monstre(1, 0)) + abs(joueur(checkJ, 2) - monstre(1, 1))
                    if distBoss <= 1 then bossEnCombat = 1 : exit for
                end if
            next checkJ
        end if

        if bossEnCombat = 0 then
            timerRegenBoss = timerRegenBoss + 1
            if timerRegenBoss >= delaiRegenBoss then
                timerRegenBoss = 0
                monstre(1, 3) = monstre(1, 3) + regenBossParCycle
                if monstre(1, 3) > monstre(1, 4) then monstre(1, 3) = monstre(1, 4)
            end if
        else
            timerRegenBoss = 0
        end if
    end if
    end if

    if animationAttaque = 1 then
        timerAnimationAttaque = timerAnimationAttaque + 1
        if timerAnimationAttaque >= 3 then
            if typeAnimation = 1 and enCombat = 1 and monstreActuel > 0 then
                animationAttaque = 1 : timerAnimationAttaque = 0 : typeAnimation = 2 : baseX = 320 : baseY = 280
                zoneVariationX = 50 : zoneVariationY = 40
                posAttaqueX = baseX + int(rnd(0) * zoneVariationX) - (zoneVariationX / 2)
                posAttaqueY = baseY + int(rnd(0) * zoneVariationY) - (zoneVariationY / 2)
                scaleAttaque = 130 + int(rnd(0) * 20)
            else
                animationAttaque = 0 : typeAnimation = 0
                #main.map "spritevisible attaque off"
            end if
        end if
    end if

    if enCombat = 1 then if GetAsyncKeyState(asc("F")) then gosub [fuir]
    if enCombat = 1 and monstreActuel > 0 and animationAttaque = 0 then
        timerAttaqueMonstre = timerAttaqueMonstre + 1
        if timerAttaqueMonstre >= 10 then
            timerAttaqueMonstre = 0
            animationAttaque = 1
            timerAnimationAttaque = 0
            typeAnimation = 2
            baseX = 320 : baseY = 280
            zoneVariationX = 50
            zoneVariationY = 40
            posAttaqueX = baseX + int(rnd(0) * zoneVariationX) - (zoneVariationX / 2)
            posAttaqueY = baseY + int(rnd(0) * zoneVariationY) - (zoneVariationY / 2)
            scaleAttaque = 130 + int(rnd(0) * 20)
            degatsMonstre = monstre(monstreActuel, 5) - hero.defense
            if degatsMonstre < 1 then degatsMonstre = 1
            degatsMonstre = degatsMonstre + int(rnd(0) * 2)
            hero.pv = hero.pv - degatsMonstre
            gosub [afficherInfo]
            if hero.pv <= 0 then gosub [gameOver]
        end if
    else
        if enCombat = 0 then timerAttaqueMonstre = 0
    end if

    if rotation <> 0 then
        direction = direction + rotation
        if direction < 0 then direction = 3
        if direction > 3 then direction = 0
        goto [skipMovement]
    end if
    if dy <> 0 or dx <> 0 then
        newX = perso1x : newY = perso1y
        select case direction
            case 0  ' Nord (haut)
                if dy = -1 then newY = perso1y - 1  ' Avancer
                if dy = 1 then newY = perso1y + 1   ' Reculer
            case 1  ' Est (droite)
                if dy = -1 then newX = perso1x + 1  ' Avancer
                if dy = 1 then newX = perso1x - 1   ' Reculer
            case 2  ' Sud (bas)
                if dy = -1 then newY = perso1y + 1  ' Avancer
                if dy = 1 then newY = perso1y - 1   ' Reculer
            case 3  ' Ouest (gauche)
                if dy = -1 then newX = perso1x - 1  ' Avancer
                if dy = 1 then newX = perso1x + 1   ' Reculer
        end select
        if newX >= 0 and newX < x and newY >= 0 and newY < y and (p(INT(newX),INT(newY)) = 0 or p(INT(newX),INT(newY)) = 2) then
            if p(INT(newX),INT(newY)) = 2 and bossVaincu = 0 then timer 0 : notice "La porte est scellée ! Vous devez vaincre le Boss pour l'ouvrir." : goto [skipMovement]
            perso1x = newX : perso1y = newY
            gosub [verifierTeleportation]
            gosub [verifierMonstre]
        end if
    end if
    if rotation <> 0 then gosub [verifierMonstre]

    [skipMovement]
    if estServeur = 1 then
        serveurABouge = 0
        if perso1x <> oldPerso1x or perso1y <> oldPerso1y or direction <> oldDirection then serveurABouge = 1
        if serveurABouge = 1 then
            joueur(1, 1) = perso1x
            joueur(1, 2) = perso1y
            joueur(1, 3) = direction
            joueur(1, 4) = hero.pv
            joueur(1, 5) = hero.niveau
            gosub [serveurEnvoyer] : timerReseau = 0
        end if
    end if
    if enCombat = 0 then gosub [verifierMonstre]
    joueurBouge = 0 : monstreBouge = 0
    if perso1x <> oldPerso1x or perso1y <> oldPerso1y or direction <> oldDirection then joueurBouge = 1 : oldPerso1x = perso1x : oldPerso1y = perso1y : oldDirection = direction
    if joueurBouge = 0 then
        mapSize = 5
        for i = 1 to nbMonstres
            if monstre(i, 2) = 1 then
                mx = monstre(i, 0) : my = monstre(i, 1)
                if mx >= perso1x - mapSize and mx <= perso1x + mapSize and my >= perso1y - mapSize and my <= perso1y + mapSize and (oldMonstre(i, 0) <> mx or oldMonstre(i, 1) <> my) then monstreBouge = 1 : exit for
            end if
        next i
    end if

    autreJoueurBouge = 0
    if modeMulti > 0 and joueurBouge = 0 and monstreBouge = 0 then
    if estClient = 1 then
        mapSize = 5
        for checkJ = 1 to 10
            if joueurDistant(checkJ, 0) > 0 then
                jX = joueurDistant(checkJ, 1) : jY = joueurDistant(checkJ, 2)
                if jX >= perso1x - mapSize and jX <= perso1x + mapSize then
                    if jY >= perso1y - mapSize and jY <= perso1y + mapSize then
                        if joueurDistant(checkJ, 1) <> oldJoueurDistant(checkJ, 1) or _
                           joueurDistant(checkJ, 2) <> oldJoueurDistant(checkJ, 2) or _
                           joueurDistant(checkJ, 3) <> oldJoueurDistant(checkJ, 3) then
                            autreJoueurBouge = 1
                            exit for
                        end if
                    end if
                end if
            end if
        next checkJ
    end if
    if estServeur = 1 then
        mapSize = 5
        for checkJ = 2 to maxJoueurs
            if joueur(checkJ, 6) = 1 then
                jX = joueur(checkJ, 1)
                jY = joueur(checkJ, 2)
                if jX >= perso1x - mapSize and jX <= perso1x + mapSize then
                    if jY >= perso1y - mapSize and jY <= perso1y + mapSize then
                        if joueur(checkJ, 1) <> oldJoueurServeur(checkJ, 1) or _
                           joueur(checkJ, 2) <> oldJoueurServeur(checkJ, 2) or _
                           joueur(checkJ, 3) <> oldJoueurServeur(checkJ, 3) then
                            autreJoueurBouge = 1
                            exit for
                        end if
                    end if
                end if
            end if
        next checkJ
    end if
    end if

    if joueurBouge = 1 or monstreBouge = 1 or autreJoueurBouge = 1 then
        gosub [drawMinimap]
        for i = 1 to nbMonstres
            if monstre(i, 2) = 1 then
                mx = monstre(i, 0) : my = monstre(i, 1)
                if mx >= perso1x - mapSize and mx <= perso1x + mapSize then
                    if my >= perso1y - mapSize and my <= perso1y + mapSize then
                        oldMonstre(i, 0) = mx : oldMonstre(i, 1) = my
                    else
                        oldMonstre(i, 0) = -1 : oldMonstre(i, 1) = -1
                    end if
                else
                    oldMonstre(i, 0) = -1 : oldMonstre(i, 1) = -1
                end if
            else
                oldMonstre(i, 0) = -1 : oldMonstre(i, 1) = -1
            end if
        next i
        if estClient = 1 then
            for i = 1 to 10 : oldJoueurDistant(i, 1) = joueurDistant(i, 1) : oldJoueurDistant(i, 2) = joueurDistant(i, 2) : oldJoueurDistant(i, 3) = joueurDistant(i, 3) : next i
        end if
        if estServeur = 1 then
            for i = 2 to maxJoueurs : oldJoueurServeur(i, 1) = joueur(i, 1) : oldJoueurServeur(i, 2) = joueur(i, 2) : oldJoueurServeur(i, 3) = joueur(i, 3) : next i
        end if
    end if

    #main.map "spritevisible attaque off; spritevisible attaque2 off; spritevisible garde1 off; spritevisible boss1 off ; spritevisible perso2 off; spritevisible perso3 off; spritevisible perso4 off; spritevisible perso2b off; spritevisible perso3b off; spritevisible perso4b off; spritevisible mur1 off; spritevisible mur2 off; spritevisible mur3 off; spritevisible mur4 off; spritevisible mur5 off; spritevisible mur6 off; spritevisible mur7 off; spritevisible mur8 off; spritevisible mur9 off; spritevisible mur10 off; spritevisible mur11 off; spritevisible mur12 off; spritevisible mur13 off; spritevisible mur14 off; spritevisible mur15 off"

    select case direction
        case 0  ' Nord
            frontX = perso1x : frontY = perso1y - 1
            front2X = perso1x : front2Y = perso1y - 2
            front3X = perso1x : front3Y = perso1y - 3
            leftX = perso1x - 1 : leftY = perso1y
            rightX = perso1x + 1 : rightY = perso1y
            leftFrontX = perso1x - 1 : leftFrontY = perso1y - 1
            rightFrontX = perso1x + 1 : rightFrontY = perso1y - 1
            leftFront2X = perso1x - 1 : leftFront2Y = perso1y - 2
            rightFront2X = perso1x + 1 : rightFront2Y = perso1y - 2
        case 1  ' Est
            frontX = perso1x + 1 : frontY = perso1y
            front2X = perso1x + 2 : front2Y = perso1y
            front3X = perso1x + 3 : front3Y = perso1y
            leftX = perso1x : leftY = perso1y - 1
            rightX = perso1x : rightY = perso1y + 1
            leftFrontX = perso1x + 1 : leftFrontY = perso1y - 1
            rightFrontX = perso1x + 1 : rightFrontY = perso1y + 1
            leftFront2X = perso1x + 2 : leftFront2Y = perso1y - 1
            rightFront2X = perso1x + 2 : rightFront2Y = perso1y + 1
        case 2  ' Sud
            frontX = perso1x : frontY = perso1y + 1
            front2X = perso1x : front2Y = perso1y + 2
            front3X = perso1x : front3Y = perso1y + 3
            leftX = perso1x + 1 : leftY = perso1y
            rightX = perso1x - 1 : rightY = perso1y
            leftFrontX = perso1x + 1 : leftFrontY = perso1y + 1
            rightFrontX = perso1x - 1 : rightFrontY = perso1y + 1
            leftFront2X = perso1x + 1 : leftFront2Y = perso1y + 2
            rightFront2X = perso1x - 1 : rightFront2Y = perso1y + 2
        case 3  ' Ouest
            frontX = perso1x - 1 : frontY = perso1y
            front2X = perso1x - 2 : front2Y = perso1y
            front3X = perso1x - 3 : front3Y = perso1y
            leftX = perso1x : leftY = perso1y + 1
            rightX = perso1x : rightY = perso1y - 1
            leftFrontX = perso1x - 1 : leftFrontY = perso1y + 1
            rightFrontX = perso1x - 1 : rightFrontY = perso1y - 1
            leftFront2X = perso1x - 2 : leftFront2Y = perso1y + 1
            rightFront2X = perso1x - 2 : rightFront2Y = perso1y - 1
    end select

    gosub [afficherMonstreDevant]
    gosub [afficherJoueursDevant]

    ' Mur 13 : mur au loin visible quand il y a exactement 1 case libre devant
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 0 then
            if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
                if p(INT(front2X),INT(front2Y)) = 1 then #main.map "spriteXY mur13 300 0; spritevisible mur13 on"
            else
                #main.map "spriteXY mur13 300 0; spritevisible mur13 on"
            end if
        end if
    end if

    ' Mur 14 : vue de face avec 3 cases libres ou plus devant
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 0 then
            if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
                if p(INT(front2X),INT(front2Y)) = 0 then
                    if front3X >= 0 and front3X < x and front3Y >= 0 and front3Y < y then
                        ' Mur14 s'affiche si la 3ème case est un portail (on voit le fond)
                        if p(INT(front3X),INT(front3Y)) = 0 or p(INT(front3X),INT(front3Y)) = 2 then #main.map "spriteXY mur14 300 0; spritevisible mur14 on"
                    else
                        #main.map "spriteXY mur14 300 0; spritevisible mur14 on"
                    end if
                end if
            end if
        end if
    else
        #main.map "spriteXY mur14 300 0; spritevisible mur14 on"
    end if

    ' Mur 2 : vue de face avec exactement 2 cases libres devant puis obstacle bloquant
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 0 then
            if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
                ' Cas 1 : portail directement à la 2ème case (à 1 case du joueur)
                if p(INT(front2X),INT(front2Y)) = 2 then #main.map "spriteXY mur2 300 0; spritevisible mur2 on"
                ' Cas 2 : 2ème case libre, puis obstacle à la 3ème case
                if p(INT(front2X),INT(front2Y)) = 0 then
                    if front3X >= 0 and front3X < x and front3Y >= 0 and front3Y < y then
                        if p(INT(front3X),INT(front3Y)) = 1 or p(INT(front3X),INT(front3Y)) = 2 then #main.map "spriteXY mur2 300 0; spritevisible mur2 on"
                    else
                        #main.map "spriteXY mur2 300 0; spritevisible mur2 on"
                    end if
                end if
            else
                ' front2 hors limites = mur
                #main.map "spriteXY mur2 300 0; spritevisible mur2 on"
            end if
        end if
    end if

    ' Mur 1 : vue proche gauche
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 0 then
            if leftFrontX >= 0 and leftFrontX < x and leftFrontY >= 0 and leftFrontY < y then
                if p(INT(leftFrontX),INT(leftFrontY)) = 1 then #main.map "spriteXY mur1 0 0; spritevisible mur1 on"
            else
                #main.map "spriteXY mur1 0 0; spritevisible mur1 on"
            end if
        end if
    end if
    if leftX >= 0 and leftX < x and leftY >= 0 and leftY < y then
        if p(INT(leftX),INT(leftY)) = 1 then
            if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
                if p(INT(frontX),INT(frontY)) = 0 then #main.map "spriteXY mur1 0 0; spritevisible mur1 on"
            end if
        end if
    else
        if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
            if p(INT(frontX),INT(frontY)) = 0 then #main.map "spriteXY mur1 0 0; spritevisible mur1 on"
        end if
    end if

    ' Mur 3 : vue proche droite
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 0 then
            if rightFrontX >= 0 and rightFrontX < x and rightFrontY >= 0 and rightFrontY < y then
                if p(INT(rightFrontX),INT(rightFrontY)) = 1 then #main.map "spriteXY mur3 600 0; spritevisible mur3 on"
            else
                #main.map "spriteXY mur3 600 0; spritevisible mur3 on"
            end if
        end if
    end if
    if rightX >= 0 and rightX < x and rightY >= 0 and rightY < y then
        if p(INT(rightX),INT(rightY)) = 1 then
            if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
                if p(INT(frontX),INT(frontY)) = 0 then #main.map "spriteXY mur3 600 0; spritevisible mur3 on"
            end if
        end if
    else
        if frontX >= 0 and frontX < x and frontY >=  0 and frontY < y then
            if p(INT(frontX),INT(frontY)) = 0 then #main.map "spriteXY mur3 600 0; spritevisible mur3 on"
        end if
    end if

    ' Mur 4 : virage droite éloigné
    if rightFrontX >= 0 and rightFrontX < x and rightFrontY >= 0 and rightFrontY < y and p(INT(rightFrontX),INT(rightFrontY)) = 0 then #main.map "spriteXY mur4 600 0; spritevisible mur4 on"

    ' Mur 5 : virage gauche éloigné
    if leftFrontX >= 0 and leftFrontX < x and leftFrontY >= 0 and leftFrontY < y and p(INT(leftFrontX),INT(leftFrontY)) = 0 then #main.map "spriteXY mur5 0 0; spritevisible mur5 on"

    ' Mur 6 : virage proche gauche
    if leftX >= 0 and leftX < x and leftY >= 0 and leftY < y and p(INT(leftX),INT(leftY)) = 0 then #main.map "spriteXY mur6 0 0; spritevisible mur6 on"

    ' Mur 7 : virage proche droite
    if rightX >= 0 and rightX < x and rightY >= 0 and rightY < y and p(INT(rightX),INT(rightY)) = 0 then #main.map "spriteXY mur7 600 0; spritevisible mur7 on"

    ' Mur 8 : mur de face (ne peut plus avancer) - PRIORITAIRE
    murDevant = 0
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 1 then murDevant = 1 : #main.map "spriteXY mur8 300 0; spritevisible mur8 on; spritevisible mur2 off; spritevisible mur6 off; spritevisible mur7 off"
    else
        murDevant = 1 : #main.map "spriteXY mur8 300 0; spritevisible mur8 on; spritevisible mur2 off; spritevisible mur6 off; spritevisible mur7 off"
    end if

    ' Si mur devant, vérifier les passages latéraux pour murs 9, 10, 11, 12
    if murDevant = 1 then
        gaucheLibre = 0 : droiteLibre = 0
        ' Vérifier si passage à gauche
        if leftX >= 0 and leftX < x and leftY >= 0 and leftY < y and p(INT(leftX),INT(leftY)) = 0 then gaucheLibre = 1
        ' Vérifier si passage à droite (ou si hors limites = bloqué)
        if rightX >= 0 and rightX < x and rightY >= 0 and rightY < y and p(INT(rightX),INT(rightY)) = 0 then droiteLibre = 1
        ' CUL-DE-SAC
        if gaucheLibre = 0 and droiteLibre = 0 then #main.map "spriteXY mur11 0 0; spritevisible mur11 on; spriteXY mur12 600 0; spritevisible mur12 on"
        '  mur de face avec passage gauche possible
        if gaucheLibre = 1 and droiteLibre = 0 then #main.map "spriteXY mur9 0 0; spritevisible mur9 on; spriteXY mur12 600 0; spritevisible mur12 on"
        '  mur de face avec passage droite possible
        if droiteLibre = 1 and gaucheLibre = 0 then #main.map "spriteXY mur10 600 0; spritevisible mur10 on; spriteXY mur11 0 0; spritevisible mur11 on"
        '  Passages des deux côtés
        if gaucheLibre = 1 and droiteLibre = 1 then #main.map "spriteXY mur9 0 0; spritevisible mur9 on; spriteXY mur10 600 0; spritevisible mur10 on"
    end if

    ' Mur 15 : portail de face (case type 2) - VÉRIFIÉ EN DERNIER pour ne pas masquer les murs de profondeur
    portalDevant = 0
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y then
        if p(INT(frontX),INT(frontY)) = 2 then
            portalDevant = 1
            ' Le portail remplace le mur8 mais PAS les murs de profondeur (mur2, mur14)
            #main.map "spriteXY mur15 300 0; spritevisible mur15 on; spritevisible mur8 off; spritevisible mur6 off; spritevisible mur7 off"

            ' Vérifier les passages latéraux pour le portail
            gaucheLibre = 0 : droiteLibre = 0
            if leftX >= 0 and leftX < x and leftY >= 0 and leftY < y and p(INT(leftX),INT(leftY)) = 0 then gaucheLibre = 1
            if rightX >= 0 and rightX < x and rightY >= 0 and rightY < y and p(INT(rightX),INT(rightY)) = 0 then droiteLibre = 1

            ' CUL-DE-SAC avec portail
            if gaucheLibre = 0 and droiteLibre = 0 then #main.map "spriteXY mur11 0 0; spritevisible mur11 on; spriteXY mur12 600 0; spritevisible mur12 on"
            ' Passage gauche possible
            if gaucheLibre = 1 and droiteLibre = 0 then #main.map "spriteXY mur9 0 0; spritevisible mur9 on; spriteXY mur12 600 0; spritevisible mur12 on"
            ' Passage droite possible
            if droiteLibre = 1 and gaucheLibre = 0 then #main.map "spriteXY mur10 600 0; spritevisible mur10 on; spriteXY mur11 0 0; spritevisible mur11 on"
            ' Passages des deux côtés
            if gaucheLibre = 1 and droiteLibre = 1 then #main.map "spriteXY mur9 0 0; spritevisible mur9 on; spriteXY mur10 600 0; spritevisible mur10 on"
        end if
    end if

    if pleinEcran = 1 then gosub [FS_Render]
    if pleinEcran = 0 then #main.map "drawsprites"
    timer 200, [endLEVEL] : wait

    [miseAJourReseau]
    on error goto [erreurReseau]
    if estServeur = 1 then gosub [serveurRecevoir] : gosub [serveurEnvoyer]
    if estClient = 1 then
        if monIdJoueur > 0 then
            timerDerniereReponseServeur = timerDerniereReponseServeur + 1
            if timerDerniereReponseServeur >= 30 then
                timer 0
                notice " " + chr$(13) + "Le serveur ne répond plus depuis 30 secondes." + chr$(13) + "Passage en mode Solo."
                raisonBasculement$ = "Serveur injoignable - Mode Solo activé"
                gosub [basculerModeSolo]
                on error goto 0
                return
            end if
        end if
        gosub [clientRecevoir]
        gosub [clientEnvoyer]
    end if
    on error goto 0
    return

    [erreurReseau]
    notice "Erreur réseau - Mode multijoueur désactivé"
    modeMulti = 0
    on error goto 0
    return

    [quit]
    timer 0
    pass$ = "1" : open "system.txt" for output as #secu2 : print #secu2, pass$ : close #secu2
    if fichierSauvegarde$ <> "" then gosub [sauvegarderPersonnage]
    if modeMulti > 0 and (socketServeur <> 0 or socketClient <> 0) then modeSilencieux = 0 : gosub [fermerReseau]
    if hdcFs <> 0 and pleinEcran = 1 then calldll #user32, "ReleaseDC", hFsHwnd as ulong, hdcFs as ulong
    on error goto [quitSansFs]
    close #fs
    [quitSansFs]
    on error goto 0
    close #main
    end


    function GetAsyncKeyState(key)
        calldll #user32, "GetAsyncKeyState",key as long,GetAsyncKeyState as long
        if GetAsyncKeyState<>0 then GetAsyncKeyState=1
    end function

    sub readJStick n
        if GetAsyncKeyState(asc("Z")) or GetAsyncKeyState(_VK_UP) then dy = -1
        if GetAsyncKeyState(asc("S")) or GetAsyncKeyState(_VK_DOWN) then dy = 1
        if GetAsyncKeyState(asc("Q")) or GetAsyncKeyState(_VK_LEFT) then rotation = -1
        if GetAsyncKeyState(asc("D")) or GetAsyncKeyState(_VK_RIGHT) then rotation = 1
        if GetAsyncKeyState(_VK_ESCAPE) then dx = 2
    end sub

    Sub Sleep ms
        CallDLL #kernel32, "Sleep",_
        ms as long,_
        ret as void
    End Sub

    [clicGauche]
    if enCombat = 0 or monstreActuel = 0 then wait
    if estClient = 1 then
        gosub [verifierMonstreDevantClient]
        if monstreActuel = 0 or enCombat = 0 then wait
    end if
    animationAttaque = 1
    timerAnimationAttaque = 0
    typeAnimation = 1
    baseX = 430 : baseY = 330
    zoneVariationX = 40 : zoneVariationY = 30
    posAttaqueX = baseX + int(rnd(0) * zoneVariationX) - (zoneVariationX / 2)
    posAttaqueY = baseY + int(rnd(0) * zoneVariationY) - (zoneVariationY / 2)
    scaleAttaque = 140 + int(rnd(0) * 20)
    #main.map "spriteXY attaque "; posAttaqueX; " "; posAttaqueY
    #main.map "spritescale attaque "; scaleAttaque
    #main.map "spritevisible attaque on"
    #main.map "spritetofront perso1"
    gosub [FS_Render]
    call Sleep 150
    if estClient = 1 and monIdJoueur > 0 then
        if monstre(monstreActuel, 2) = 0 then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo] : wait
        if socketClient = 0 or monIdJoueur = 0 then wait
        messageAEnvoyer$ = "ATTACK|" + str$(monIdJoueur) + "," + str$(monstreActuel)
        gosub [envoyerMessageServeur]
    end if
    degatsJoueur = hero.attaque - monstre(monstreActuel, 6)
    if degatsJoueur < 1 then degatsJoueur = 1
    degatsJoueur = degatsJoueur + int(rnd(0) * 3) - 1
    monstre(monstreActuel, 3) = monstre(monstreActuel, 3) - degatsJoueur
    if monstre(monstreActuel, 3) <= 0 then
        if monstre(monstreActuel, 8) = 1 then
            bossVaincu = 1 : bossActif = 0 : bossPv = 0
            timer 0
            notice "BOSS VAINCU ! La porte s'ouvre..."
            timer 200, [endLEVEL]
        end if
        monstre(monstreActuel, 2) = 0
        hero.xp = hero.xp + monstre(monstreActuel, 7)
        hero.kills = hero.kills + 1
        enCombat = 0
        monstreActuel = 0
        animationAttaque = 0
        typeAnimation = 0
        timerRegeneration = 0
        while hero.xp >= hero.xpProchain
            hero.niveau = hero.niveau + 1
            hero.xp = hero.xp - hero.xpProchain
            hero.xpProchain = int(hero.xpProchain * 1.25)
            hero.pvMax = hero.pvMax + int(rnd(0)*10+3)
            hero.pv = hero.pvMax
            hero.attaque = hero.attaque + int(rnd(0)*4+1)
            hero.defense = hero.defense + int(rnd(0)*2)
        wend
        gosub [drawMinimap]
    else
        #main.map "spriteXY attaque2 "; posAttaqueX; " "; posAttaqueY
        #main.map "spritescale attaque2 "; scaleAttaque
        #main.map "spritevisible attaque2 on"
        #main.map "spritetofront attaque2"
        gosub [FS_Render]
        degatsMonstre = monstre(monstreActuel, 5) - hero.defense
        if degatsMonstre < 1 then degatsMonstre = 1
        degatsMonstre = degatsMonstre + int(rnd(0) * 2)
        hero.pv = hero.pv - degatsMonstre
        if hero.pv <= 0 then gosub [gameOver] : wait
    end if
    gosub [afficherInfo]
    wait

    [verifierMonstreDevantClient]
    if monstreActuel = 0 or enCombat = 0 then return
    if monstre(monstreActuel, 2) = 0 then
        enCombat = 0 : monstreActuel = 0
        gosub [afficherInfo]
        return
    end if
    select case direction
        case 0: frontCheckX = perso1x : frontCheckY = perso1y - 1
        case 1: frontCheckX = perso1x + 1 : frontCheckY = perso1y
        case 2: frontCheckX = perso1x : frontCheckY = perso1y + 1
        case 3: frontCheckX = perso1x - 1 : frontCheckY = perso1y
    end select
    mX = INT(monstre(monstreActuel, 0))
    mY = INT(monstre(monstreActuel, 1))
    if mX <> INT(frontCheckX) or mY <> INT(frontCheckY) then
        enCombat = 0
        monstreActuel = 0
        gosub [afficherInfo]
    end if
    return


    [map]
    x = 25 : y = 25
    redim p(x,y)
    if carteActuelle = 1 then restore [dataMap1]
    if carteActuelle = 2 then restore [dataMap2]
    for b=0 to y-1
        for a=0 to x-1
            read valeur : p(a,b)= valeur
        next a
    next b
    return

    [dataMap1]
    data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1
    data 1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1
    data 1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1
    data 1,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1
    data 1,1,1,1,1,0,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,1
    data 1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,0,1
    data 1,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1,1,1,0,1
    data 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1
    data 1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1
    data 1,0,1,1,1,1,1,0,1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1
    data 1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,1,1
    data 1,0,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,0,1,2,1
    data 1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1
    data 1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1
    data 1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1
    data 1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1,0,1
    data 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1
    data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    [dataMap2]
    data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
    data 1,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,1
    data 1,0,1,1,1,1,1,1,1,0,1,0,1,0,1,0,1,1,1,1,1,1,1,0,1
    data 1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1
    data 1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1
    data 1,0,1,0,1,0,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1
    data 1,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,1
    data 1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,1,1
    data 1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,1
    data 1,0,1,1,1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,1,1,1,1,0,1
    data 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1
    data 1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1
    data 1,1,1,0,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,1,1
    data 1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,2,1
    data 1,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,1
    data 1,0,1,0,1,1,1,0,1,0,1,1,1,1,1,0,1,0,1,1,1,0,1,0,1
    data 1,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,1,0,0,0,0,0,1
    data 1,1,1,1,1,0,1,1,1,0,1,0,1,0,1,0,1,1,1,0,1,1,1,1,1
    data 1,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,1
    data 1,0,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1
    data 1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1
    data 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

    [verifierTeleportation]
    if p(INT(perso1x), INT(perso1y)) = 2 then
        if bossVaincu = 0 then
            timer 0
            notice "La porte est scellée ! Vous devez vaincre le Boss pour l'ouvrir"
            timer 200, [endLEVEL]
            return
        end if
        if carteActuelle = 1 then
            carteActuelle = 2
            select case direction
                case 0: perso1x = teleportMap2X : perso1y = teleportMap2Y + 1  ' Si on regardait Nord
                case 1: perso1x = teleportMap2X - 1 : perso1y = teleportMap2Y  ' Si on regardait Est
                case 2: perso1x = teleportMap2X : perso1y = teleportMap2Y - 1  ' Si on regardait Sud
                case 3: perso1x = teleportMap2X + 1 : perso1y = teleportMap2Y  ' Si on regardait Ouest
            end select
            direction = 2 : bossVaincu = 0: bossActif = 1
            gosub [map]
            gosub [placerMonstres]
            gosub [drawMinimap]
        else
            carteActuelle = 1
            select case direction
                case 0: perso1x = teleportMap1X : perso1y = teleportMap1Y + 1
                case 1: perso1x = teleportMap1X - 1 : perso1y = teleportMap1Y
                case 2: perso1x = teleportMap1X : perso1y = teleportMap1Y - 1
                case 3: perso1x = teleportMap1X + 1 : perso1y = teleportMap1Y
            end select
            direction = 2 : bossVaincu = 0 : bossActif = 1
            gosub [map]
            gosub [placerMonstres]
            gosub [drawMinimap]
        end if
    end if
    return

    [drawMinimap]
    mapSize = 5 : cellSize = 16
    totalCells = mapSize * 2 + 1  ' 11 cases
    totalWidth = totalCells * cellSize  ' 176 pixels
    totalHeight = totalCells * cellSize  ' 176 pixels
    offsetX = (188 - totalWidth) / 2 : offsetY = (190 - totalHeight) / 2
    #main.minimap, "down; fill black"
    for mapDY = 0 to mapSize * 2
        for mapDX = 0 to mapSize * 2
            mapX = perso1x - mapSize + mapDX : mapY = perso1y - mapSize + mapDY
            if mapX >= 0 and mapX < x and mapY >= 0 and mapY < y then
                screenX = offsetX + mapDX * cellSize : screenY = offsetY + mapDY * cellSize
                if p(INT(mapX), INT(mapY)) = 1 then
                    #main.minimap, "backcolor darkgray; color darkgray; place "; screenX; " "; screenY
                    #main.minimap, "boxfilled "; screenX + cellSize - 2; " "; screenY + cellSize - 2
                end if
                if p(INT(mapX), INT(mapY)) = 2 then
                    #main.minimap, "backcolor red; color yellow; place "; screenX; " "; screenY
                    #main.minimap, "boxfilled "; screenX + cellSize - 2; " "; screenY + cellSize - 2
                end if
                if p(INT(mapX), INT(mapY)) = 0 then
                    #main.minimap, "color lightgray; backcolor black; place "; screenX; " "; screenY
                    #main.minimap, "box "; screenX + cellSize - 2; " "; screenY + cellSize - 2
                end if
            end if
        next mapDX
    next mapDY
    for i = 1 to nbMonstres
        if monstre(i, 2) = 1 then
            mx = monstre(i, 0) : my = monstre(i, 1)
            if mx >= perso1x - mapSize and mx <= perso1x + mapSize then
                if my >= perso1y - mapSize and my <= perso1y + mapSize then
                    mapDX = mx - (perso1x - mapSize) : mapDY = my - (perso1y - mapSize)
                    screenX = offsetX + mapDX * cellSize : screenY = offsetY + mapDY * cellSize
                    if monstre(i, 8) = 1 then #main.minimap, "color yellow; backcolor red" : #main.minimap, "place "; screenX + 7; " "; screenY + 7 : #main.minimap, "circlefilled 5"
                    if monstre(i, 8) = 0 then #main.minimap, "color white; backcolor blue" : #main.minimap, "place "; screenX + 7; " "; screenY + 7 : #main.minimap, "circlefilled 3"
                end if
            end if
        end if
    next i
    if modeMulti > 0 then gosub [afficherAutresJoueurs]
    centerX = 188 / 2 : centerY = 190 / 2 : playerSize = 5
    #main.minimap, "color yellow; backcolor yellow; place "; centerX - playerSize+4; " "; centerY - playerSize+4
    #main.minimap, "circlefilled "; playerSize
    #main.minimap, "color red; size 2"
    select case direction
        case 0  ' Nord (haut)
            #main.minimap, "line "; centerX; " "; centerY; " "; centerX; " "; centerY - playerSize - 3
        case 1  ' Est (droite)
            #main.minimap, "line "; centerX; " "; centerY; " "; centerX + playerSize + 3; " "; centerY
        case 2  ' Sud (bas)
            #main.minimap, "line "; centerX; " "; centerY; " "; centerX; " "; centerY + playerSize + 3
        case 3  ' Ouest (gauche)
            #main.minimap, "line "; centerX; " "; centerY; " "; centerX - playerSize - 3; " "; centerY
    end select
    #main.minimap, "size 1; flush"
    return


    [placerMonstres]
    if carteActuelle = 1 then bossX = 23 : bossY = 23
    if carteActuelle = 2 then bossX = 4 : bossY = 4
    if bossActif = 1 then bossPv = 200 + (carteActuelle - 1) * 150  : bossPvMax = bossPv
    nbMonstres = 1
    monstre(1, 0) = bossX                           ' x
    monstre(1, 1) = bossY                           ' y
    monstre(1, 2) = bossActif                       ' actif (1 si pas encore vaincu)
    monstre(1, 3) = bossPv                          ' pv
    monstre(1, 4) = bossPvMax                       ' pvMax
    monstre(1, 5) = 15 + (carteActuelle - 1) * 5    ' attaque
    monstre(1, 6) = 8 + (carteActuelle - 1) * 3     ' defense
    monstre(1, 7) = 500 * carteActuelle             ' xpDonne
    monstre(1, 8) = 1                               ' 1 = BOSS, 0 = monstre normal
    tentatives = 0
    while nbMonstres < 20 and tentatives < 200
        tentatives = tentatives + 1 : mx = int(rnd(0) * (x - 2)) + 1 : my = int(rnd(0) * (y - 2)) + 1
        distanceBoss = abs(mx - bossX) + abs(my - bossY)
        if p(mx, my) = 0 and (mx <> perso1x or my <> perso1y) and distanceBoss > 3 then
            dejaPresent = 0
            for j = 1 to nbMonstres
                if monstre(j, 0) = mx and monstre(j, 1) = my then dejaPresent = 1 : exit for
            next j
            if dejaPresent = 0 then
                nbMonstres = nbMonstres + 1
                monstre(nbMonstres, 0) = mx
                monstre(nbMonstres, 1) = my
                monstre(nbMonstres, 2) = 1
                if carteActuelle = 2 then
                    monstre(nbMonstres, 3) = 40 + int(rnd(0) * 20)
                    monstre(nbMonstres, 4) = monstre(nbMonstres, 3)
                    monstre(nbMonstres, 5) = 8 + int(rnd(0) * 4)
                    monstre(nbMonstres, 6) = 5 + int(rnd(0) * 3)
                    monstre(nbMonstres, 7) = 35 + int(rnd(0) * 20)
                else
                    monstre(nbMonstres, 3) = 20 + int(rnd(0) * 10)
                    monstre(nbMonstres, 4) = monstre(nbMonstres, 3)
                    monstre(nbMonstres, 5) = 3 + int(rnd(0) * 3)
                    monstre(nbMonstres, 6) = 2 + int(rnd(0) * 2)
                    monstre(nbMonstres, 7) = 15 + int(rnd(0) * 10)
                end if
                monstre(nbMonstres, 8) = 0
            end if
        end if
    wend
    return


    [respawnMonstre]
    if bossVaincu = 1 then return
    nbMonstresActifs = 0
    for checkM = 1 to nbMonstres
        if monstre(checkM, 2) = 1 then nbMonstresActifs = nbMonstresActifs + 1
    next checkM
    if nbMonstresActifs >= 50 then return
    tentatives = 0 : monstrePlace = 0
    while monstrePlace = 0 and tentatives < 100
        tentatives = tentatives + 1 : mx = int(rnd(0) * (x - 2)) + 1 : my = int(rnd(0) * (y - 2)) + 1
        distanceJoueur = abs(mx - perso1x) + abs(my - perso1y)
        if p(mx, my) = 0 and distanceJoueur > 5 then
            dejaPresent = 0
            for j = 1 to nbMonstres
                if monstre(j, 2) = 1 and monstre(j, 0) = mx and monstre(j, 1) = my then dejaPresent = 1 : exit for
            next j
            if dejaPresent = 0 then
                slotLibre = 0
                for i = 1 to nbMonstres
                    if monstre(i, 2) = 0 then slotLibre = i : exit for
                next i
                if slotLibre = 0 then
                    nbMonstres = nbMonstres + 1 : slotLibre = nbMonstres
                    baseHP = 30 + int(rnd(0) * 15)
                    baseAtk = 5 + int(rnd(0) * 5)
                    baseDef = 2 + int(rnd(0) * 2)
                    baseXP = 15 + int(rnd(0) * 10)
                else
                    if monstre(slotLibre, 8) = 1 then
                        bonusHP = 2
                    else
                        bonusHP = 3 + int(rnd(0) * 4)
                    end if
                    bonusAtk = 2 + int(rnd(0) * 3)
                    bonusDef = 1 + int(rnd(0) * 2)
                    baseHP = monstre(slotLibre, 4) + bonusHP
                    baseAtk = monstre(slotLibre, 5) + bonusAtk
                    baseDef = monstre(slotLibre, 6) + bonusDef
                    baseXP = int(monstre(slotLibre, 7) * 1.1)
                end if
                facteurDifficulte = 1 + (hero.niveau * 0.2)
                monstre(slotLibre, 0) = mx
                monstre(slotLibre, 1) = my
                monstre(slotLibre, 2) = 1
                monstre(slotLibre, 3) = int(baseHP * facteurDifficulte)
                monstre(slotLibre, 4) = monstre(slotLibre, 3)
                monstre(slotLibre, 5) = int(baseAtk * facteurDifficulte)
                monstre(slotLibre, 6) = int(baseDef * facteurDifficulte)
                monstre(slotLibre, 7) = int(baseXP * facteurDifficulte)
                monstre(slotLibre, 8) = 0
                monstrePlace = 1
            end if
        end if
    wend
    return


    [afficherInfo]
    #main.info, "cls; down; backcolor darkgray; fill darkgray"
    #main.info, "backcolor white; color white; place 10 10; boxfilled 178 130"
    #main.info, "backcolor lightgray; color lightgray; place 12 12; boxfilled 176 128"

    #main.info, "backcolor lightgray; color black; place 20 30; font Arial 10 bold"
    if version = 1 then regenInfo$ = "Santé : " + str$(hero.pv) + "/" + str$(hero.pvMax)
    if version = 2 then regenInfo$ = "Health : " + str$(hero.pv) + "/" + str$(hero.pvMax)
    if regenParSeconde > 0 then regenInfo$ = regenInfo$ + " (+" + str$(regenParSeconde) + "/s)"
    #main.info, "\"; regenInfo$; "  "

    #main.info, "backcolor black; color black; place 20 38; boxfilled 168 50"
    #main.info, "backcolor darkred; color darkred; place 21 39; boxfilled 167 49"

    pvPourcent = hero.pv / hero.pvMax : largeurPV = int(144 * pvPourcent)
    #main.info, "backcolor red; color red; place 22 40; boxfilled "; 22 + largeurPV; " 48"

    #main.info, "backcolor lightgray; color black; place 20 98; font Arial 10 bold"
    if version = 1 then #main.info, "\Niveau : ";hero.niveau; " (";hero.xp;"/";hero.xpProchain;")  "
    if version = 2 then #main.info, "\Level : ";hero.niveau; " (";hero.xp;"/";hero.xpProchain;")  "

    #main.info, "backcolor black; color black; place 20 107; boxfilled 168 119"
    #main.info, "backcolor darkblue; color darkblue; place 21 108; boxfilled 167 118"

    xpPourcent = hero.xp / hero.xpProchain : largeurXP = int(144 * xpPourcent)
    #main.info, "backcolor blue; color blue; place 22 109; boxfilled "; 22 + largeurXP; " 117"

    #main.info, "backcolor lightgray; color black; place 20 70; font Arial 9 bold"
    if version = 1 then #main.info, "\Force : "; str$(hero.attaque)
    if version = 2 then #main.info, "\Strength : "; str$(hero.attaque)
    #main.info, "backcolor lightgray; color black; place 100 70; font Arial 9 bold"
    if version = 1 then #main.info, "\Armure : "; str$(hero.defense)
    if version = 2 then #main.info, "\Armor : "; str$(hero.defense)

    distancePortail = 9999 : portalX = 0 : portalY = 0
    for checkY = 0 to y - 1
        for checkX = 0 to x - 1
            if p(checkX, checkY) = 2 then
                portalX = checkX : portalY = checkY
                distancePortail = abs(perso1x - portalX) + abs(perso1y - portalY)
                exit for
            end if
        next checkX
        if portalX > 0 then exit for
    next checkY

    if distancePortail <= 2 then
        yOffset = 220 + (enCombat * 180) + (modeMulti * 40)
        #main.info, "backcolor white; color white; place 10 "; yOffset; "; boxfilled 178 "; yOffset + 54
        #main.info, "backcolor lightgray; color lightgray; place 12 "; yOffset + 2; "; boxfilled 176 "; yOffset + 52
        if bossVaincu = 1 then
            #main.info, "backcolor lightgray; color darkgreen; place 20 "; yOffset + 15; "; font Arial 9 bold"
            if version = 1 then #main.info, "\PORTAIL ACTIF"
            if version = 2 then #main.info, "\ACTIVE PORTAL"
            #main.info, "color black; place 20 "; yOffset + 32; "; font Arial 8"
            if carteActuelle = 1 and version = 1 then #main.info, "\Vers Niveau 2"
            if carteActuelle = 2 and version = 1 then  #main.info, "\Vers Niveau 1"
            if carteActuelle = 1 and version = 2 then #main.info, "\Towards Level 2"
            if carteActuelle = 2 and version = 2 then  #main.info, "\Towards Level 1"
        else
            #main.info, "backcolor lightgray; color red; place 48 "; yOffset + 20; "; font Arial 9 bold"
            if version = 1 then #main.info, "\PORTE SCELLÉE"
            if version = 2 then #main.info, "\SEALED DOOR"
            #main.info, "color darkred; place 23 "; yOffset + 40; "; font Arial 8"
            if version = 1 then #main.info, "\Vaincre le Boss pour l'ouvrir !"
            if version = 2 then #main.info, "\Defeat the Boss to open it!"
        end if
    end if

    if enCombat = 1 and monstreActuel > 0 then
        #main.info, "backcolor white; color white; place 10 220; boxfilled 178 394"
        #main.info, "backcolor darkred; color darkred; place 12 222; boxfilled 176 392"
        #main.info, "backcolor red; color red; place 20 230; boxfilled 168 250"
        #main.info, "color yellow; place 50 246; font Arial 12 bold"
        if version = 1 then #main.info, "\! COMBAT !"
        if version = 2 then #main.info, "\! FIGHT !"

        if monstre(monstreActuel, 8) = 1 then #main.info, "backcolor darkred; color white; place 50 273; font Arial 11 bold" : #main.info, "\*** BOSS ***"
        if monstre(monstreActuel, 8) = 0 and version = 1 then #main.info, "backcolor darkred; color white; place 35 273; font Arial 11 bold" : #main.info, "\Garde du Donjon"
        if monstre(monstreActuel, 8) = 0 and version = 2 then #main.info, "backcolor darkred; color white; place 35 273; font Arial 11 bold" : #main.info, "\Dungeon Guard"

        #main.info, "place 34 295; font Arial 9 bold"
        if version = 1 then mpv$ = "Santé : " + str$(monstre(monstreActuel, 3)) + "/" + str$(monstre(monstreActuel, 4))
        if version = 2 then mpv$ = "Health : " + str$(monstre(monstreActuel, 3)) + "/" + str$(monstre(monstreActuel, 4))
        #main.info, "\"; mpv$

        #main.info, "place 34 310; font Arial 8 bold; color yellow"
        if version = 1 then #main.info, "\Force "; str$(monstre(monstreActuel, 5))
        if version = 2 then #main.info, "\Strength "; str$(monstre(monstreActuel, 5))
        #main.info, "place 34 325; color cyan"
        if version = 1 then #main.info, "\Armure "; str$(monstre(monstreActuel, 6))
        if version = 2 then #main.info, "\armor "; str$(monstre(monstreActuel, 6))

        #main.info, "color white; size 2; line 20 345 168 345"
        #main.info, "size 1; backcolor darkred; color white; place 30 365; font Arial 9"
        if version = 1 then #main.info, "\Attaquer : Clic gauche"
        if version = 2 then #main.info, "\Attack: Left click"
        #main.info, "place 30 380"
        if version = 1 then #main.info, "\Fuir : Touche F"
        if version = 2 then #main.info, "\To flee: Press F"
    end if

    #main.info, "flush"

    if modeMulti > 0 then
        #main.info, "backcolor white; color white; place 10 "; 140 ; "; boxfilled 178 "; 170
        #main.info, "backcolor lightgray; color lightgray; place 12 "; 142 ; "; boxfilled 176 "; 168
        couleurStatut$ = "black"
        if instr(statutConnexion$, "Connecté") > 0 then couleurStatut$ = "darkgreen"
        if instr(statutConnexion$, "Erreur") > 0 then couleurStatut$ = "red"
        #main.info, "backcolor lightgray; color "; couleurStatut$; "; place 20 "; 158 ; "; font Arial 8 bold"
        #main.info, "\"; statutConnexion$
    end if
    return


    [verifierMonstre]
    if enCombat = 1 and monstreActuel > 0 then
        if monstre(monstreActuel, 2) = 0 then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo] : return
        if monstre(monstreActuel, 8) = 1 and (bossActif = 0 or bossVaincu = 1) then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo] : return
        select case direction
            case 0 : frontCheckX = perso1x : frontCheckY = perso1y - 1
            case 1 : frontCheckX = perso1x + 1 : frontCheckY = perso1y
            case 2 : frontCheckX = perso1x : frontCheckY = perso1y + 1
            case 3 : frontCheckX = perso1x - 1 : frontCheckY = perso1y
        end select
        mX = INT(monstre(monstreActuel, 0)) : mY = INT(monstre(monstreActuel, 1))
        if mX <> INT(frontCheckX) or mY <> INT(frontCheckY) then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
        return
    end if
    monstreActuel = 0
    select case direction
        case 0 : frontX = perso1x : frontY = perso1y - 1
        case 1 : frontX = perso1x + 1 : frontY = perso1y
        case 2 : frontX = perso1x : frontY = perso1y + 1
        case 3 : frontX = perso1x - 1 : frontY = perso1y
    end select
    for i = 1 to nbMonstres
        if monstre(i, 2) = 1 then
            if monstre(i, 8) = 1 and (bossActif = 0 or bossVaincu = 1) then goto [skipThisMonster]
            mx = INT(monstre(i, 0)) : my = INT(monstre(i, 1))
            if mx = INT(frontX) and my = INT(frontY) then monstreActuel = i : enCombat = 1 : gosub [afficherInfo] : exit for
            [skipThisMonster]
        end if
    next i
    return

    [afficherMonstreDevant]
    #main.map "spritevisible garde1 off; spritevisible boss1 off"
    if frontX >= 0 and frontX < x and frontY >= 0 and frontY < y and p(INT(frontX),INT(frontY)) = 2 then return

    if enCombat = 1 and monstreActuel > 0 then
        if monstre(monstreActuel, 2) = 1 then
            select case direction
                case 0 : frontCheckX = perso1x : frontCheckY = perso1y - 1
                case 1 : frontCheckX = perso1x + 1 : frontCheckY = perso1y
                case 2 : frontCheckX = perso1x : frontCheckY = perso1y + 1
                case 3 : frontCheckX = perso1x - 1 : frontCheckY = perso1y
            end select
            distMX = abs(INT(monstre(monstreActuel, 0)) - INT(frontCheckX))
            distMY = abs(INT(monstre(monstreActuel, 1)) - INT(frontCheckY))
            if distMX > 0.5 or distMY > 0.5 then
                enCombat = 0 : monstreActuel = 0
                gosub [afficherInfo]
                return
            end if
            if monstre(monstreActuel, 8) = 1 then #main.map "spriteXY boss1 160 180; spritevisible boss1 on; spritescale boss1 300"
            if monstre(monstreActuel, 8) = 0 then #main.map "spriteXY garde1 350 200; spritevisible garde1 on; spritescale garde1 250"
        else
            enCombat = 0 : monstreActuel = 0
            gosub [afficherInfo]
        end if
        return
    end if

    select case direction
    case 0  ' Nord (haut)
        front1X = perso1x : front1Y = perso1y - 1
        front2X = perso1x : front2Y = perso1y - 2
        front3X = perso1x : front3Y = perso1y - 3
    case 1  ' Est (droite)
        front1X = perso1x + 1 : front1Y = perso1y
        front2X = perso1x + 2 : front2Y = perso1y
        front3X = perso1x + 3 : front3Y = perso1y
    case 2  ' Sud (bas)
        front1X = perso1x : front1Y = perso1y + 1
        front2X = perso1x : front2Y = perso1y + 2
        front3X = perso1x : front3Y = perso1y + 3
    case 3  ' Ouest (gauche)
        front1X = perso1x - 1 : front1Y = perso1y
        front2X = perso1x - 2 : front2Y = perso1y
        front3X = perso1x - 3 : front3Y = perso1y
    end select

    monstreTrouve = 0 : monstreEstBoss = 0

    if monstreTrouve = 0 then
    if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
        for i = 1 to nbMonstres
            if monstre(i, 2) = 1 then
                if INT(monstre(i, 0)) = INT(front1X) and INT(monstre(i, 1)) = INT(front1Y) then
                    monstreEstBoss = monstre(i, 8)
                    if monstreEstBoss = 1 then #main.map "spriteXY boss1 210 200; spritevisible boss1 on; spritescale boss1 150"
                    if monstreEstBoss = 0 then #main.map "spriteXY garde1 350 200; spritevisible garde1 on; spritescale garde1 150"
                    monstreTrouve = 1
                    exit for
                end if
            end if
        next i
    end if
    end if
    if monstreTrouve = 0 then
    if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
        murBloque = 0
        if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
            if p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
            if p(INT(front1X),INT(front1Y)) = 2 then murBloque = 1
        else
            murBloque = 1
        end if

        if murBloque = 0 then
            for i = 1 to nbMonstres
                if monstre(i, 2) = 1 then
                    if INT(monstre(i, 0)) = INT(front2X) and INT(monstre(i, 1)) = INT(front2Y) then
                        monstreEstBoss = monstre(i, 8)
                        if monstreEstBoss = 1 then #main.map "spriteXY boss1 370 200; spritevisible boss1 on; spritescale boss1 80"
                        if monstreEstBoss = 0 then #main.map "spriteXY garde1 416 200; spritevisible garde1 on; spritescale garde1 80"
                        monstreTrouve = 1
                        exit for
                    end if
                end if
            next i
        end if
    end if
    end if
    if monstreTrouve = 0 then
    if front3X >= 0 and front3X < x and front3Y >= 0 and front3Y < y then
        murBloque = 0
        if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
            if p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
            if p(INT(front1X),INT(front1Y)) = 2 then murBloque = 1
        end if
        if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
            if p(INT(front2X),INT(front2Y)) = 1 then murBloque = 1
            if p(INT(front2X),INT(front2Y)) = 2 then murBloque = 1
        end if

        if murBloque = 0 then
            for i = 1 to nbMonstres
                if monstre(i, 2) = 1 then
                    if INT(monstre(i, 0)) = INT(front3X) and INT(monstre(i, 1)) = INT(front3Y) then
                        monstreEstBoss = monstre(i, 8)
                        if monstreEstBoss = 1 then #main.map "spriteXY boss1 400 200; spritevisible boss1 on; spritescale boss1 50"
                        if monstreEstBoss = 0 then #main.map "spriteXY garde1 430 200; spritevisible garde1 on; spritescale garde1 50"
                        monstreTrouve = 1
                        exit for
                    end if
                end if
            next i
        end if
    end if
    end if
    return

    [afficherJoueursDevant]
    #main.map "spritevisible perso2 off; spritevisible perso3 off; spritevisible perso4 off; spritevisible perso2b off; spritevisible perso3b off; spritevisible perso4b off"
    if modeMulti = 0 then return
    if enCombat = 1 then return
    select case direction
        case 0  ' Nord (haut)
            front1X = perso1x : front1Y = perso1y - 1
            front2X = perso1x : front2Y = perso1y - 2
            front3X = perso1x : front3Y = perso1y - 3
        case 1  ' Est (droite)
            front1X = perso1x + 1 : front1Y = perso1y
            front2X = perso1x + 2 : front2Y = perso1y
            front3X = perso1x + 3 : front3Y = perso1y
        case 2  ' Sud (bas)
            front1X = perso1x : front1Y = perso1y + 1
            front2X = perso1x : front2Y = perso1y + 2
            front3X = perso1x : front3Y = perso1y + 3
        case 3  ' Ouest (gauche)
            front1X = perso1x - 1 : front1Y = perso1y
            front2X = perso1x - 2 : front2Y = perso1y
            front3X = perso1x - 3 : front3Y = perso1y
    end select
    compteurJoueurs = 0
    if estServeur = 1 then
        for i = 2 to maxJoueurs
            if joueur(i, 6) = 1 and compteurJoueurs < 3 then
                jX = joueur(i, 1) : jY = joueur(i, 2) : jDir = joueur(i, 3)
                joueurTrouve = 0
                distanceJoueur = 0
                spriteNom$ = ""
                if jX = front1X and jY = front1Y then
                    joueurTrouve = 1 : distanceJoueur = 1
                    if jDir = direction then
                        compteurJoueurs = compteurJoueurs + 1
                        select case compteurJoueurs
                            case 1 : spriteNom$ = "perso2b"
                            case 2 : spriteNom$ = "perso3b"
                            case 3 : spriteNom$ = "perso4b"
                        end select
                        #main.map "spriteXY "; spriteNom$; " 350 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 200"
                    else
                        compteurJoueurs = compteurJoueurs + 1
                        select case compteurJoueurs
                            case 1 : spriteNom$ = "perso2"
                            case 2 : spriteNom$ = "perso3"
                            case 3 : spriteNom$ = "perso4"
                        end select
                        #main.map "spriteXY "; spriteNom$; " 275 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 200"
                    end if
                end if
                if joueurTrouve = 0 and jX = front2X and jY = front2Y then
                    murBloque = 0
                    if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
                        if p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
                    else
                        murBloque = 1
                    end if
                    if murBloque = 0 then
                        joueurTrouve = 1 : distanceJoueur = 2
                        if jDir = direction then
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2b"
                                case 2 : spriteNom$ = "perso3b"
                                case 3 : spriteNom$ = "perso4b"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 400 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 100"
                        else
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2"
                                case 2 : spriteNom$ = "perso3"
                                case 3 : spriteNom$ = "perso4"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 360 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 100"
                        end if
                    end if
                end if
                if joueurTrouve = 0 and jX = front3X and jY = front3Y then
                    murBloque = 0
                    if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
                        if p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
                    end if
                    if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y then
                        if p(INT(front2X),INT(front2Y)) = 1 then murBloque = 1
                    end if
                    if murBloque = 0 then
                        joueurTrouve = 1 : distanceJoueur = 3
                        if jDir = direction then
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2b"
                                case 2 : spriteNom$ = "perso3b"
                                case 3 : spriteNom$ = "perso4b"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 430 190; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 35"
                        else
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2"
                                case 2 : spriteNom$ = "perso3"
                                case 3 : spriteNom$ = "perso4"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 416 190; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 35"
                        end if
                    end if
                end if
            end if
        next i
    end if
    if estClient = 1 then
        for i = 1 to 10
            if joueurDistant(i, 0) > 0 and joueurDistant(i, 0) <> monIdJoueur and compteurJoueurs < 3 then
                jX = joueurDistant(i, 1) : jY = joueurDistant(i, 2) : jDir = joueurDistant(i, 3)
                joueurTrouve = 0 : distanceJoueur = 0 : spriteNom$ = ""
                if jX = front1X and jY = front1Y then
                    joueurTrouve = 1 : distanceJoueur = 1
                    if jDir = direction then
                        compteurJoueurs = compteurJoueurs + 1
                        select case compteurJoueurs
                            case 1 : spriteNom$ = "perso2b"
                            case 2 : spriteNom$ = "perso3b"
                            case 3 : spriteNom$ = "perso4b"
                        end select
                        #main.map "spriteXY "; spriteNom$; " 350 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 200"
                    else
                        compteurJoueurs = compteurJoueurs + 1
                        select case compteurJoueurs
                            case 1 : spriteNom$ = "perso2"
                            case 2 : spriteNom$ = "perso3"
                            case 3 : spriteNom$ = "perso4"
                        end select
                        #main.map "spriteXY "; spriteNom$; " 275 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 200"
                    end if
                end if
                if joueurTrouve = 0 and jX = front2X and jY = front2Y then
                    murBloque = 0
                    if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y then
                        if p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
                    else
                        murBloque = 1
                    end if
                    if murBloque = 0 then
                        joueurTrouve = 1 : distanceJoueur = 2
                        if jDir = direction then
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2b"
                                case 2 : spriteNom$ = "perso3b"
                                case 3 : spriteNom$ = "perso4b"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 400 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 100"
                        else
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2"
                                case 2 : spriteNom$ = "perso3"
                                case 3 : spriteNom$ = "perso4"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 360 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 100"
                        end if
                    end if
                end if
                if joueurTrouve = 0 and jX = front3X and jY = front3Y then
                    murBloque = 0
                    if front1X >= 0 and front1X < x and front1Y >= 0 and front1Y < y and p(INT(front1X),INT(front1Y)) = 1 then murBloque = 1
                    if front2X >= 0 and front2X < x and front2Y >= 0 and front2Y < y and p(INT(front2X),INT(front2Y)) = 1 then murBloque = 1
                    if murBloque = 0 then
                        joueurTrouve = 1 : distanceJoueur = 3
                        if jDir = direction then
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2b"
                                case 2 : spriteNom$ = "perso3b"
                                case 3 : spriteNom$ = "perso4b"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 430 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 35"
                        else
                            compteurJoueurs = compteurJoueurs + 1
                            select case compteurJoueurs
                                case 1 : spriteNom$ = "perso2"
                                case 2 : spriteNom$ = "perso3"
                                case 3 : spriteNom$ = "perso4"
                            end select
                            #main.map "spriteXY "; spriteNom$; " 416 180; spritevisible "; spriteNom$; " on; spritescale "; spriteNom$; " 35"
                        end if
                    end if
                end if
            end if
        next i
    end if
    return


    [deplacerMonstres]
    if estClient = 1 then return
    for i = 1 to nbMonstres
        if monstre(i, 2) = 1 then
            if enCombat = 1 and i = monstreActuel then goto [skipMonsterMove]
            mx = monstre(i, 0) : my = monstre(i, 1) : joueurCibleX = perso1x : joueurCibleY = perso1y : distanceMin = 9999
            if estServeur = 1 then
                for j = 1 to maxJoueurs
                    if joueur(j, 6) = 1 then
                        distTest = abs(joueur(j, 1) - mx) + abs(joueur(j, 2) - my)
                        if distTest < distanceMin then distanceMin = distTest : joueurCibleX = joueur(j, 1) : joueurCibleY = joueur(j, 2)
                    end if
                next j
            end if
            distX = joueurCibleX - mx : distY = joueurCibleY - my : distance = abs(distX) + abs(distY) : newMX = mx : newMY = my
            if distance <= 8 then
                pathStartX = INT(mx) : pathStartY = INT(my) : pathEndX = INT(joueurCibleX) : pathEndY = INT(joueurCibleY)
                if pathStartX = pathEndX and pathStartY = pathEndY then goto [skipMonsterMove]
                distX = pathEndX - pathStartX : distY = pathEndY - pathStartY
                nextStepX = pathStartX : nextStepY = pathStartY : etapeReussie = 0
                if abs(distX) > abs(distY) then
                    if distX > 0 then testX = pathStartX + 1 : testY = pathStartY
                    if distX <= 0 then testX = pathStartX - 1 : testY = pathStartY
                    if testX >= 0 and testX < x and testY >= 0 and testY < y and p(INT(testX), INT(testY)) = 0 then nextStepX = testX : nextStepY = testY : etapeReussie = 1
                end if
                if etapeReussie = 0 then
                    if distY > 0 then testX = pathStartX : testY = pathStartY + 1
                    if distY <= 0 then testX = pathStartX : testY = pathStartY - 1
                    if testX >= 0 and testX < x and testY >= 0 and testY < y and p(INT(testX), INT(testY)) = 0 then nextStepX = testX : nextStepY = testY : etapeReussie = 1
                end if
                if etapeReussie = 0 then
                    if distX > 0 then testX = pathStartX + 1 : testY = pathStartY
                    if distX <= 0 then testX = pathStartX - 1 : testY = pathStartY
                    if testX >= 0 and testX < x and testY >= 0 and testY < y and p(INT(testX), INT(testY)) = 0 then nextStepX = testX : nextStepY = testY : etapeReussie = 1
                end if
                if etapeReussie = 0 then
                    dirX = 0 : dirY = 0
                    if distX > 0 then dirX = 1
                    if distX < 0 then dirX = -1
                    if distY > 0 then dirY = 1
                    if distY < 0 then dirY = -1
                    testX = pathStartX + dirX : testY = pathStartY + dirY
                    if testX >= 0 and testX < x and testY >= 0 and testY < y and p(INT(testX), INT(testY)) = 0 then nextStepX = testX : nextStepY = testY : etapeReussie = 1
                end if
                if etapeReussie = 0 then
                    choixDir = int(rnd(0) * 4)
                    select case choixDir
                        case 0: testX = pathStartX : testY = pathStartY - 1
                        case 1: testX = pathStartX + 1 : testY = pathStartY
                        case 2: testX = pathStartX : testY = pathStartY + 1
                        case 3: testX = pathStartX - 1 : testY = pathStartY
                    end select
                    if testX >= 0 and testX < x and testY >= 0 and testY < y and p(INT(testX), INT(testY)) = 0 then nextStepX = testX : nextStepY = testY
                end if
                newMX = nextStepX : newMY = nextStepY
            else
                choixDir = int(rnd(0) * 4)
                select case choixDir
                    case 0: newMY = my - 1
                    case 1: newMX = mx + 1
                    case 2: newMY = my + 1
                    case 3: newMX = mx - 1
                end select
            end if
            mouvementValide = 0
            if newMX >= 0 and newMX < x and newMY >= 0 and newMY < y then
                if p(INT(newMX), INT(newMY)) = 0 then
                    caseLibre = 1
                    for j = 1 to nbMonstres
                        if j <> i and monstre(j, 2) = 1 and INT(monstre(j, 0)) = INT(newMX) and INT(monstre(j, 1)) = INT(newMY) then caseLibre = 0 : exit for
                    next j
                    if INT(newMX) = INT(perso1x) and INT(newMY) = INT(perso1y) then caseLibre = 0
                    if estServeur = 1 then
                        for j = 1 to maxJoueurs
                            if joueur(j, 6) = 1 and INT(newMX) = INT(joueur(j, 1)) and INT(newMY) = INT(joueur(j, 2)) then caseLibre = 0 : exit for
                        next j
                    end if
                    if caseLibre = 1 then mouvementValide = 1 : monstre(i, 0) = INT(newMX) : monstre(i, 1) = INT(newMY)
                end if
            end if
        end if
        [skipMonsterMove]
    next i
    return

    [verifierDegatsProximite]
    if enCombat = 1 then return
    nbMonstresAdjacents = 0 : nbBossAdjacents = 0
    for checkDir = 0 to 3
        select case checkDir
            case 0 : checkX = perso1x : checkY = perso1y - 1
            case 1 : checkX = perso1x + 1 : checkY = perso1y
            case 2 : checkX = perso1x : checkY = perso1y + 1
            case 3 : checkX = perso1x - 1 : checkY = perso1y
        end select
        if checkX >= 0 and checkX < x and checkY >= 0 and checkY < y then
            for i = 1 to nbMonstres
                if monstre(i, 2) = 1 then
                    if INT(monstre(i, 0)) = INT(checkX) and INT(monstre(i, 1)) = INT(checkY) then
                        if monstre(i, 8) = 1 then
                            nbBossAdjacents = nbBossAdjacents + 1
                        else
                            nbMonstresAdjacents = nbMonstresAdjacents + 1
                        end if
                    end if
                end if
            next i
        end if
    next checkDir
    if nbMonstresAdjacents > 0 or nbBossAdjacents > 0 then
        degatsProximite = (3 * nbMonstresAdjacents) + (6 * nbBossAdjacents)
        hero.pv = hero.pv - degatsProximite
        gosub [afficherInfo]
        if hero.pv <= 0 then gosub [gameOver] : return
    end if
    return

    [fuir]
    if enCombat = 0 then return
    animationAttaque = 0 : typeAnimation = 0
    #main.map "spritevisible attaque off"
    if rnd(0) < 0.5 then
        newFuiteX = perso1x : newFuiteY = perso1y
        select case direction
            case 0: newFuiteY = perso1y + 1
            case 1: newFuiteX = perso1x - 1
            case 2: newFuiteY = perso1y - 1
            case 3: newFuiteX = perso1x + 1
        end select
        fuiteValide = 0
        if newFuiteX >= 0 and newFuiteX < x and newFuiteY >= 0 and newFuiteY < y and p(INT(newFuiteX), INT(newFuiteY)) = 0 then fuiteValide = 1
        if fuiteValide = 1 then
            enCombat = 0 : monstreActuel = 0 : perso1x = newFuiteX : perso1y = newFuiteY
        else
            degatsMonstre = monstre(monstreActuel, 5) - hero.defense
            if degatsMonstre < 1 then degatsMonstre = 1
            degatsMonstre = degatsMonstre + int(rnd(0) * 2) : hero.pv = hero.pv - degatsMonstre
            if hero.pv <= 0 then gosub [gameOver] : return
        end if
    else
        degatsMonstre = monstre(monstreActuel, 5) - hero.defense
        if degatsMonstre < 1 then degatsMonstre = 1
        degatsMonstre = degatsMonstre + int(rnd(0) * 2) : hero.pv = hero.pv - degatsMonstre
        if hero.pv <= 0 then gosub [gameOver] : return
    end if
    gosub [afficherInfo]
    return


    [gameOver]
    timer 0
    notice "GAME OVER !"
    pass$ = "1" : open "system.txt" for output as #secu2 : print #secu2, pass$ : close #secu2
    if modeMulti > 0 and (socketServeur <> 0 or socketClient <> 0) then modeSilencieux = 0 : gosub [fermerReseau]
    if hdcFs <> 0 and pleinEcran = 1 then calldll #user32, "ReleaseDC", hFsHwnd as ulong, hdcFs as ulong
    on error goto [quitSansFs2]
    close #fs
    [quitSansFs2]
    on error goto 0
    close #main
    end



    ' ========================================================================================================================================================
    ' ========================================================================================================================================================
    ' ========================================================================================================================================================
    ' PARTIE MULTI JOUEUR
    ' ========================================================================================================================================================
    ' ========================================================================================================================================================
    ' ========================================================================================================================================================



    [rejoindreServeur]
    close #mode
    prompt "Entrez l'adresse IP du serveur:"; ipServeur$
    if ipServeur$ = "" or ipServeur$ = "CANCEL" then goto [menuMultijoueur]
    prompt "Entrez le port du serveur (par défaut 5555):"; portInput$
    if portInput$ = "CANCEL" then goto [menuMultijoueur]
    if portInput$ <> "" then portServeur = val(portInput$)
    if portInput$ = "" then portServeur = 5555
    modeMulti = 2 : estClient = 1
    goto [demarrerJeu]


    [initialiserServeur]
    open "ws2_32.dll" for dll as #ws2
    ws2Ouvert = 1 : WSAData$ = space$(400) : calldll #ws2, "WSAStartup", 514 as word, WSAData$ as ptr, resultWinsock as long
    if resultWinsock <> 0 then notice "Erreur Winsock: " + str$(resultWinsock) : socketServeur = 0 : return

    portsATester(1) = 5555
    portsATester(2) = 5556
    portsATester(3) = 5557
    portsATester(4) = 8888
    portsATester(5) = 9999

    portTrouve = 0 : tentativePort = 1
    while tentativePort <= 5 and portTrouve = 0
        portTest = portsATester(tentativePort)
        calldll #ws2, "socket", 2 as long, 2 as long, 17 as long, socketServeur as long
        if socketServeur <= 0 then notice "Erreur création socket" : socketServeur = 0 : return
        sockAddr$ = chr$(2) + chr$(0)
        portByteHigh = int(portTest / 256) : portByteLow = portTest mod 256
        sockAddr$ = sockAddr$ + chr$(portByteHigh) + chr$(portByteLow)
        sockAddr$ = sockAddr$ + chr$(0) + chr$(0) + chr$(0) + chr$(0)
        for i = 1 to 8
            sockAddr$ = sockAddr$ + chr$(0)
        next i
        calldll #ws2, "bind", socketServeur as long, sockAddr$ as ptr, 16 as long, resultBind as long
        if resultBind = 0 then
            portTrouve = 1 : portServeur = portTest
        else
            calldll #ws2, "closesocket", socketServeur as long, resultClose as long
            tentativePort = tentativePort + 1
        end if
    wend

    if portTrouve = 0 then notice "Impossible de trouver un port libre (5555-9999)!" + chr$(13) + "Fermez les autres instances et réessayez." : socketServeur = 0 : return
    nonBlocking$ = chr$(1) + chr$(0) + chr$(0) + chr$(0)
    calldll #ws2, "ioctlsocket", socketServeur as long, 2147772030 as long, nonBlocking$ as ptr, resultIoctl as long

    heureActuelle$ = time$()
    heures = val(word$(heureActuelle$, 1, ":"))
    minutes = val(word$(heureActuelle$, 2, ":"))
    secondes = val(word$(heureActuelle$, 3, ":"))
    tempSeed = (heures * 3600 + minutes * 60 + secondes)
    seedServeur = tempSeed mod 10000
    negativeSeed = 0 - seedServeur
    dummy = rnd(negativeSeed)
    dummy = rnd(1)

    gosub [placerMonstres]

    for i = 1 to 10
        joueur(i, 6) = 0 : adressesClients$(i) = ""
    next i
    joueur(1, 0) = 1
    joueur(1, 1) = perso1x
    joueur(1, 2) = perso1y
    joueur(1, 3) = direction
    joueur(1, 4) = hero.pv
    joueur(1, 5) = hero.niveau
    joueur(1, 6) = 1
    nbJoueursConnectes = 1 : monIdJoueur = 1 : statutConnexion$ = "Serveur actif - Port " + str$(portServeur)
    return


    [initialiserClient]
    on error goto [erreurInitClient]
    open "ws2_32.dll" for dll as #ws2
    ws2Ouvert = 1
    WSAData$ = space$(400)
    calldll #ws2, "WSAStartup", 514 as word, WSAData$ as ptr, resultWinsock as long
    if resultWinsock <> 0 then
        notice "Erreur Winsock client: " + str$(resultWinsock)
        socketClient = 0
        gosub [fermerSocketsSecurise]
        close #main
        goto [menuMultijoueur]
        return
    end if
    calldll #ws2, "socket", 2 as long, 2 as long, 17 as long, socketClient as long
    if socketClient <= 0 then
        notice "Erreur création socket client"
        socketClient = 0
        gosub [fermerSocketsSecurise]
        close #main
        goto [menuMultijoueur]
        return
    end if
    nonBlocking$ = chr$(1) + chr$(0) + chr$(0) + chr$(0)
    calldll #ws2, "ioctlsocket", socketClient as long, 2147772030 as long, nonBlocking$ as ptr, resultIoctl as long
    statutConnexion$ = "Connexion en cours..."
    on error goto 0
    gosub [envoyerConnexion]
    return

    [erreurInitClient]
    on error goto 0
    notice "Erreur lors de l'initialisation du client"
    socketClient = 0
    ws2Ouvert = 0
    if ws2Ouvert = 1 then gosub [fermerSockets]
    close #main
    goto [menuMultijoueur]
    return


    [construireSockAddrServeur]
    on error goto [erreurIPInvalide]
    sockAddrServeur$ = chr$(2) + chr$(0)
    portByteHigh = int(portServeur / 256)
    portByteLow = portServeur mod 256
    sockAddrServeur$ = sockAddrServeur$ + chr$(portByteHigh) + chr$(portByteLow)
    nbPoints = 0
    for checkChar = 1 to len(ipServeur$)
        if mid$(ipServeur$, checkChar, 1) = "." then nbPoints = nbPoints + 1
    next checkChar
    if nbPoints <> 3 then goto [erreurIPInvalide]
    ipPart1$ = "" : ipPart2$ = "" : ipPart3$ = "" : ipPart4$ = ""
    pointCount = 0 : currentPart$ = ""
    for i = 1 to len(ipServeur$)
        currentChar$ = mid$(ipServeur$, i, 1)
        if currentChar$ = "." then
            pointCount = pointCount + 1
            select case pointCount
                case 1: ipPart1$ = currentPart$
                case 2: ipPart2$ = currentPart$
                case 3: ipPart3$ = currentPart$
            end select
            currentPart$ = ""
        else
            currentPart$ = currentPart$ + currentChar$
        end if
    next i
    ipPart4$ = currentPart$
    ipByte1 = val(ipPart1$)
    ipByte2 = val(ipPart2$)
    ipByte3 = val(ipPart3$)
    ipByte4 = val(ipPart4$)
    if ipByte1 < 0 or ipByte1 > 255 then goto [erreurIPInvalide]
    if ipByte2 < 0 or ipByte2 > 255 then goto [erreurIPInvalide]
    if ipByte3 < 0 or ipByte3 > 255 then goto [erreurIPInvalide]
    if ipByte4 < 0 or ipByte4 > 255 then goto [erreurIPInvalide]
    sockAddrServeur$ = sockAddrServeur$ + chr$(ipByte1) + chr$(ipByte2) + chr$(ipByte3) + chr$(ipByte4)
    for i = 1 to 8
        sockAddrServeur$ = sockAddrServeur$ + chr$(0)
    next i
    on error goto 0
    return

    [erreurIPInvalide]
    on error goto 0
    notice "Adresse IP invalide : " + ipServeur$ + chr$(13) + "Format attendu : XXX.XXX.XXX.XXX (ex: 192.168.1.1 ou 127.0.0.1)"
    socketClient = 0
    gosub [fermerSockets]
    close #main
    goto [menuMultijoueur]
    return

    [serveurRecevoir]
    if socketServeur = 0 then return
    buffer$ = space$(1024) : sockAddrFrom$ = space$(16) : fromLen$ = chr$(16) + chr$(0) + chr$(0) + chr$(0)
    on error goto [serveurRecevoirErreur]
    calldll #ws2, "recvfrom", socketServeur as long, buffer$ as ptr, 1024 as long, _
        0 as long, sockAddrFrom$ as ptr, fromLen$ as ptr, bytesReceived as long
    on error goto 0
    if bytesReceived <= 0 then return
    sockAddrFromGlobal$ = sockAddrFrom$ : message$ = left$(buffer$, bytesReceived) : pipePos = instr(message$, "|")
    if pipePos > 0 then typeMsg$ = left$(message$, pipePos - 1) : data$ = mid$(message$, pipePos + 1)
    if pipePos <= 0 then  typeMsg$ = message$ : data$ = ""
    select case typeMsg$
        case "CONNECT"
            gosub [serveurAjouterJoueur]
        case "UPDATE"
            idJoueur = val(word$(data$, 1, ","))
            if idJoueur > 0 and idJoueur <= maxJoueurs then
                if joueur(idJoueur, 6) = 1 then
                    joueur(idJoueur, 1) = val(word$(data$, 2, ","))  ' x
                    joueur(idJoueur, 2) = val(word$(data$, 3, ","))  ' y
                    joueur(idJoueur, 3) = val(word$(data$, 4, ","))  ' direction
                    joueur(idJoueur, 4) = val(word$(data$, 5, ","))  ' pv
                    joueur(idJoueur, 5) = val(word$(data$, 6, ","))  ' niveau
                    adressesClients$(idJoueur) = sockAddrFromGlobal$
                end if
            end if
        case "DISCONNECT"
            idJoueur = val(data$)
            if idJoueur > 0 and idJoueur <= maxJoueurs then
                if joueur(idJoueur, 6) = 1 then
                    joueur(idJoueur, 6) = 0
                    adressesClients$(idJoueur) = ""
                    nbJoueursConnectes = nbJoueursConnectes - 1
                    if estServeur = 1 then
                        oldJoueurServeur(idJoueur, 1) = -1
                        oldJoueurServeur(idJoueur, 2) = -1
                        oldJoueurServeur(idJoueur, 3) = -1
                    end if
                    statutConnexion$ = "Serveur : " + str$(nbJoueursConnectes) + " joueur(s)"
                    gosub [afficherInfo]
                    gosub [drawMinimap]
                    gosub [serveurEnvoyer]
                end if
            end if
        case "ATTACK"
            idJoueurAttaquant = val(word$(data$, 1, ","))
            monstreID = val(word$(data$, 2, ","))
            if idJoueurAttaquant < 1 or idJoueurAttaquant > maxJoueurs then goto [skipAttackProcessing]
            if joueur(idJoueurAttaquant, 6) = 0 then goto [skipAttackProcessing]
            if monstreID < 1 or monstreID > nbMonstres then goto [skipAttackProcessing]
            if monstre(monstreID, 2) = 0 then goto [skipAttackProcessing]
            joueurX = joueur(idJoueurAttaquant, 1)
            joueurY = joueur(idJoueurAttaquant, 2)
            joueurDir = joueur(idJoueurAttaquant, 3)
            select case joueurDir
                case 0: frontCheckX = joueurX : frontCheckY = joueurY - 1
                case 1: frontCheckX = joueurX + 1 : frontCheckY = joueurY
                case 2: frontCheckX = joueurX : frontCheckY = joueurY + 1
                case 3: frontCheckX = joueurX - 1 : frontCheckY = joueurY
            end select
            monstreX = INT(monstre(monstreID, 0))
            monstreY = INT(monstre(monstreID, 1))
            if monstreX <> INT(frontCheckX) or monstreY <> INT(frontCheckY) then
                if len(adressesClients$(idJoueurAttaquant)) = 16 then
                    reponse$ = "ATTACK_FAILED|Monstre trop loin"
                    sockAddrFromGlobal$ = adressesClients$(idJoueurAttaquant)
                    gosub [serveurEnvoyerA]
                end if
                goto [skipAttackProcessing]
            end if
            attaqueJoueur = 10 + (joueur(idJoueurAttaquant, 5) - 1) * 3
            defenseMonstreAtt = monstre(monstreID, 6)
            degatsJoueur = attaqueJoueur - defenseMonstreAtt
            if degatsJoueur < 1 then degatsJoueur = 1
            degatsJoueur = degatsJoueur + int(rnd(0) * 3) - 1
            monstre(monstreID, 3) = monstre(monstreID, 3) - degatsJoueur
            if monstre(monstreID, 3) <= 0 then
                xpGagne = monstre(monstreID, 7)
                estBoss = monstre(monstreID, 8)
                monstre(monstreID, 2) = 0
                if idJoueurAttaquant > 1 and idJoueurAttaquant <= maxJoueurs then
                    if len(adressesClients$(idJoueurAttaquant)) = 16 then
                        reponse$ = "REWARD|" + str$(xpGagne) + "," + str$(estBoss)
                        sockAddrFromGlobal$ = adressesClients$(idJoueurAttaquant)
                        gosub [serveurEnvoyerA]
                    end if
                end if
            else
                defenseJoueur = 5 + (joueur(idJoueurAttaquant, 5) - 1) * 2
                degatsMonstre = monstre(monstreID, 5) - defenseJoueur
                if degatsMonstre < 1 then degatsMonstre = 1
                degatsMonstre = degatsMonstre + int(rnd(0) * 2)
                if idJoueurAttaquant > 1 and idJoueurAttaquant <= maxJoueurs then
                    if len(adressesClients$(idJoueurAttaquant)) = 16 then
                        reponse$ = "DAMAGE|" + str$(degatsMonstre)
                        sockAddrFromGlobal$ = adressesClients$(idJoueurAttaquant)
                        gosub [serveurEnvoyerA]
                    end if
                end if
            end if
            [skipAttackProcessing]
        case "KILL_MONSTER"
            monstreID = val(data$) : if monstreID > 0 and monstreID <= nbMonstres then monstre(monstreID, 2) = 0
    end select
    [serveurRecevoirErreur]
    on error goto 0
    return


    [serveurAjouterJoueur]
    ipByte1 = asc(mid$(sockAddrFromGlobal$, 5, 1))
    ipByte2 = asc(mid$(sockAddrFromGlobal$, 6, 1))
    ipByte3 = asc(mid$(sockAddrFromGlobal$, 7, 1))
    ipByte4 = asc(mid$(sockAddrFromGlobal$, 8, 1))
    ipNouveau$ = str$(ipByte1) + "." + str$(ipByte2) + "." + str$(ipByte3) + "." + str$(ipByte4)
    for checkDupli = 2 to maxJoueurs
        if joueur(checkDupli, 6) = 1 and len(adressesClients$(checkDupli)) = 16 then
            ipByte1Old = asc(mid$(adressesClients$(checkDupli), 5, 1))
            ipByte2Old = asc(mid$(adressesClients$(checkDupli), 6, 1))
            ipByte3Old = asc(mid$(adressesClients$(checkDupli), 7, 1))
            ipByte4Old = asc(mid$(adressesClients$(checkDupli), 8, 1))
            ipExistant$ = str$(ipByte1Old) + "." + str$(ipByte2Old) + "." + str$(ipByte3Old) + "." + str$(ipByte4Old)
            if ipExistant$ = ipNouveau$ then slotLibre = checkDupli : goto [slotTrouve]
        end if
    next checkDupli
    slotLibre = 0
    for i = 2 to maxJoueurs
        if joueur(i, 6) = 0 then slotLibre = i : exit for
    next i
    [slotTrouve]
    if slotLibre > 0 then
        nomJoueur$ = word$(data$, 1, ",")
        joueur(slotLibre, 0) = slotLibre
        joueur(slotLibre, 1) = val(word$(data$, 2, ","))
        joueur(slotLibre, 2) = val(word$(data$, 3, ","))
        joueur(slotLibre, 3) = val(word$(data$, 4, ","))
        joueur(slotLibre, 4) = 100
        joueur(slotLibre, 5) = 1
        joueur(slotLibre, 6) = 1
        adressesClients$(slotLibre) = sockAddrFromGlobal$
        if checkDupli > maxJoueurs then nbJoueursConnectes = nbJoueursConnectes + 1
        statutConnexion$ = "Serveur : " + str$(nbJoueursConnectes) + " joueur(s)"
        gosub [afficherInfo]
        reponse$ = "WELCOME|" + str$(slotLibre) + "," + str$(seedServeur)
        gosub [serveurEnvoyerA]
        listeMonstresInit$ = "INIT_MONSTERS|"
        nbMonstresActifsInit = 0
        for initM = 1 to nbMonstres
            if monstre(initM, 2) = 1 then
                if nbMonstresActifsInit > 0 then listeMonstresInit$ = listeMonstresInit$ + ";"
                listeMonstresInit$ = listeMonstresInit$ + str$(initM) + "," + _
                    str$(monstre(initM, 0)) + "," + str$(monstre(initM, 1)) + "," + _
                    str$(monstre(initM, 3)) + "," + str$(monstre(initM, 4)) + "," + _
                    str$(monstre(initM, 5)) + "," + str$(monstre(initM, 6)) + "," + _
                    str$(monstre(initM, 7)) + "," + str$(monstre(initM, 8))
                nbMonstresActifsInit = nbMonstresActifsInit + 1
            end if
        next initM
        reponse$ = listeMonstresInit$
        gosub [serveurEnvoyerA]
    else
        reponse$ = "FULL|0"
        gosub [serveurEnvoyerA]
    end if
    return


    [serveurEnvoyerA]
    if socketServeur = 0 then return
    longueurMsg = len(reponse$)
    calldll #ws2, "sendto", socketServeur as long, reponse$ as ptr, longueurMsg as long, _
            0 as long, sockAddrFromGlobal$ as ptr, 16 as long, bytesSent as long
    return


    [serveurEnvoyer]
    if socketServeur = 0 then return
    joueur(1, 1) = perso1x
    joueur(1, 2) = perso1y
    joueur(1, 3) = direction
    joueur(1, 4) = hero.pv
    joueur(1, 5) = hero.niveau
    if nbJoueursConnectes < 2 then return
    listeJoueurs$ = "PLAYERS|" : nbActifs = 0
    for i = 1 to maxJoueurs
        if joueur(i, 6) = 1 then
            if nbActifs > 0 then listeJoueurs$ = listeJoueurs$ + ";"
            listeJoueurs$ = listeJoueurs$ + str$(joueur(i, 0)) + "," + _
                       str$(joueur(i, 1)) + "," + str$(joueur(i, 2)) + "," + _
                       str$(joueur(i, 3)) + "," + str$(joueur(i, 4)) + "," + _
                       str$(joueur(i, 5))
            nbActifs = nbActifs + 1
        end if
    next i
    if nbActifs > 0 then
        longueurMsg = len(listeJoueurs$)
        for i = 2 to maxJoueurs  ' COMMENCE À 2 pour ne pas envoyer au serveur lui-même
            if joueur(i, 6) = 1 and len(adressesClients$(i)) = 16 then
                adresseClient$ = adressesClients$(i)
                calldll #ws2, "sendto", socketServeur as long, listeJoueurs$ as ptr, _
                    longueurMsg as long, 0 as long, adresseClient$ as ptr, _
                    16 as long, bytesSent as long
            end if
        next i
    end if
    listeMonstres$ = "MONSTERS|" : nbMonstresActifs = 0
    for i = 1 to nbMonstres
        if monstre(i, 2) = 1 then
            if nbMonstresActifs > 0 then listeMonstres$ = listeMonstres$ + ";"
                listeMonstres$ = listeMonstres$ + str$(i) + "," + _
                       str$(monstre(i, 0)) + "," + str$(monstre(i, 1)) + "," + _
                       str$(monstre(i, 3)) + "," + str$(monstre(i, 4)) + "," + _
                       str$(monstre(i, 5)) + "," + str$(monstre(i, 6)) + "," + _
                       str$(monstre(i, 7)) + "," + str$(monstre(i, 8))
            nbMonstresActifs = nbMonstresActifs + 1
        end if
    next i
    longueurMsg = len(listeMonstres$)
    for i = 2 to maxJoueurs
        if joueur(i, 6) = 1 and len(adressesClients$(i)) = 16 then
            adresseClient$ = adressesClients$(i)
            calldll #ws2, "sendto", socketServeur as long, listeMonstres$ as ptr, _
                longueurMsg as long, 0 as long, adresseClient$ as ptr, _
                16 as long, bytesSent as long
        end if
    next i
    return


    [envoyerConnexion]
    if socketClient = 0 then return
    messageAEnvoyer$ = "CONNECT|" + hero.nom$ + "," + str$(perso1x) + "," + _
                   str$(perso1y) + "," + str$(direction)
    gosub [envoyerMessageServeur]
    return


    [envoyerMessageServeur]
    if socketClient = 0 then return
    gosub [construireSockAddrServeur]
    if len(sockAddrServeur$) < 16 then return
    longueurMsg = len(messageAEnvoyer$)
    calldll #ws2, "sendto", socketClient as long, messageAEnvoyer$ as ptr, longueurMsg as long, _
        0 as long, sockAddrServeur$ as ptr, 16 as long, bytesSent as long
    if bytesSent > 0 then
        if instr(messageAEnvoyer$, "CONNECT|") > 0 then
            tentativesConnexion = tentativesConnexion + 1
            statutConnexion$ = "Connexion serveur :  " + str$(tentativesConnexion) + " / 10"
            gosub [afficherInfo]
        end if
    else
        calldll #ws2, "WSAGetLastError", errCode as long
        if instr(messageAEnvoyer$, "CONNECT|") > 0 then
            tentativesConnexion = tentativesConnexion + 1
            statutConnexion$ = "Erreur envoi #" + str$(tentativesConnexion) + "/10 (code:" + str$(errCode) + ")"
            gosub [afficherInfo]
        end if
    end if
    return


    [clientEnvoyer]
    if socketClient = 0 then return
    if monIdJoueur = 0 then
        tempsDepuisConnexion = tempsDepuisConnexion + 1
        if tempsDepuisConnexion > 5 then
            tempsDepuisConnexion = 0
            if tentativesConnexion < 10 then gosub [envoyerConnexion]
            if tentativesConnexion >= 10 then
                timer 0
                notice " " + chr$(13) + "Le serveur ne répond plus depuis 30 secondes." + chr$(13) + "Passage en mode Solo."
                raisonBasculement$ = "Échec de connexion - Mode Solo activé"
                gosub [basculerModeSolo]
            end if
        end if
        return
    end if
    messageAEnvoyer$ = "UPDATE|" + str$(monIdJoueur) + "," + str$(perso1x) + "," + _
                   str$(perso1y) + "," + str$(direction) + "," + str$(hero.pv) + "," + _
                   str$(hero.niveau)
    gosub [envoyerMessageServeur]
    return


    [clientRecevoir]
    if socketClient = 0 then return
    tentativesReception = 0
    while tentativesReception < 5
        tentativesReception = tentativesReception + 1
        buffer$ = space$(2048)
        sockAddrFrom$ = space$(16)
        fromLen$ = chr$(16) + chr$(0) + chr$(0) + chr$(0)
        on error goto [clientRecevoirErreur]
        calldll #ws2, "recvfrom", socketClient as long, buffer$ as ptr, 2048 as long, _
            0 as long, sockAddrFrom$ as ptr, fromLen$ as ptr, bytesReceived as long
        on error goto 0
        if bytesReceived <= 0 then exit while
        message$ = left$(buffer$, bytesReceived)
        timerDerniereReponseServeur = 0
        pipePos = instr(message$, "|")
        if pipePos > 0 then
            typeMsg$ = left$(message$, pipePos - 1) : data$ = mid$(message$, pipePos + 1)
        else
            typeMsg$ = message$ : data$ = ""
        end if
        select case typeMsg$
            case "WELCOME"
                monIdJoueur = val(word$(data$, 1, ","))
                seedRecu = val(word$(data$, 2, ","))
                negativeSeed = 0 - seedRecu
                dummy = rnd(negativeSeed)
                dummy = rnd(1)
                gosub [placerMonstres]
                statutConnexion$ = "Connecté! (ID:" + str$(monIdJoueur) + ")"
                tentativesConnexion = 0
                gosub [afficherInfo]
            case "INIT_MONSTERS"
                if data$ = "" then goto [skipInitMonstres]
                i = 1
                while word$(data$, i, ";") <> ""
                    monstreData$ = word$(data$, i, ";")
                    mID = val(word$(monstreData$, 1, ","))
                    mX = val(word$(monstreData$, 2, ","))
                    mY = val(word$(monstreData$, 3, ","))
                    mPV = val(word$(monstreData$, 4, ","))
                    mPVMax = val(word$(monstreData$, 5, ","))
                    mAtt = val(word$(monstreData$, 6, ","))
                    mDef = val(word$(monstreData$, 7, ","))
                    mXP = val(word$(monstreData$, 8, ","))
                    mBoss = val(word$(monstreData$, 9, ","))
                    if mID > 0 and mID <= nbMonstres then
                        monstre(mID, 0) = mX
                        monstre(mID, 1) = mY
                        monstre(mID, 2) = 1
                        monstre(mID, 3) = mPV
                        monstre(mID, 4) = mPVMax
                        monstre(mID, 5) = mAtt
                        monstre(mID, 6) = mDef
                        monstre(mID, 7) = mXP
                        monstre(mID, 8) = mBoss
                    end if
                i = i + 1
                wend
                [skipInitMonstres]
            case "FULL"
                statutConnexion$ = "Serveur plein!" : gosub [afficherInfo]
            case "SERVER_CLOSED"
                raisonBasculement$ = "Serveur déconnecté - Mode Solo activé" : gosub [basculerModeSolo]
            case "PLAYERS"
                nbJoueursRecus = 0
                for clearJ = 1 to 10
                    if joueurDistant(clearJ, 0) > 0 then joueurDistant(clearJ, 0) = -1
                next clearJ
                i = 1
                while word$(data$, i, ";") <> ""
                    joueurData$ = word$(data$, i, ";")
                    jID = val(word$(joueurData$, 1, ","))
                    jX = val(word$(joueurData$, 2, ","))
                    jY = val(word$(joueurData$, 3, ","))
                    jDir = val(word$(joueurData$, 4, ","))
                    jPV = val(word$(joueurData$, 5, ","))
                    jNiv = val(word$(joueurData$, 6, ","))
                    if jID <> monIdJoueur and jID > 0 and jID <= 10 then
                        joueurDistant(jID, 0) = jID
                        joueurDistant(jID, 1) = jX
                        joueurDistant(jID, 2) = jY
                        joueurDistant(jID, 3) = jDir
                        joueurDistant(jID, 4) = jPV
                        joueurDistant(jID, 5) = jNiv
                        nbJoueursRecus = nbJoueursRecus + 1
                    end if
                i = i + 1
                wend
                for clearJ = 1 to 10
                    if joueurDistant(clearJ, 0) = -1 then
                        joueurDistant(clearJ, 0) = 0
                        oldJoueurDistant(clearJ, 1) = -1
                        oldJoueurDistant(clearJ, 2) = -1
                        oldJoueurDistant(clearJ, 3) = -1
                    end if
                next clearJ
                if nbJoueursRecus <> dernierNbJoueurs then
                    dernierNbJoueurs = nbJoueursRecus
                    statutConnexion$ = "Connecté (" + str$(nbJoueursRecus + 1) + " joueur(s))"
                    gosub [afficherInfo]
                end if
            case "MONSTERS"
                if data$ = "" then
                    if enCombat = 1 and monstreActuel > 0 then
                    enCombat = 0 : monstreActuel = 0
                    gosub [afficherInfo]
                end if
                for clearM = 1 to nbMonstres
                    monstre(clearM, 2) = 0
                next clearM
                goto [skipMonstresUpdate]
                end if
                ancienMonstreActuel = monstreActuel : ancienEnCombat = enCombat
                for clearM = 1 to nbMonstres
                    monstre(clearM, 2) = 0
                next clearM
                i = 1 : nbRecu = 0 : monstreActuelTrouve = 0
                while word$(data$, i, ";") <> ""
                    monstreData$ = word$(data$, i, ";")
                    mID = val(word$(monstreData$, 1, ","))
                    mX = val(word$(monstreData$, 2, ","))
                    mY = val(word$(monstreData$, 3, ","))
                    mPV = val(word$(monstreData$, 4, ","))
                    mPVMax = val(word$(monstreData$, 5, ","))
                    mAtt = val(word$(monstreData$, 6, ","))
                    mDef = val(word$(monstreData$, 7, ","))
                    mXP = val(word$(monstreData$, 8, ","))
                    mBoss = val(word$(monstreData$, 9, ","))
                    if mID > 0 and mID <= nbMonstres then
                        monstre(mID, 0) = mX
                        monstre(mID, 1) = mY
                        monstre(mID, 2) = 1
                        monstre(mID, 3) = mPV
                        monstre(mID, 4) = mPVMax
                        monstre(mID, 5) = mAtt
                        monstre(mID, 6) = mDef
                        monstre(mID, 7) = mXP
                        monstre(mID, 8) = mBoss
                        nbRecu = nbRecu + 1
                        if mID = ancienMonstreActuel and ancienEnCombat = 1 then
                            monstreActuelTrouve = 1
                            select case direction
                                case 0: frontCheckX = perso1x : frontCheckY = perso1y - 1
                                case 1: frontCheckX = perso1x + 1 : frontCheckY = perso1y
                                case 2: frontCheckX = perso1x : frontCheckY = perso1y + 1
                                case 3: frontCheckX = perso1x - 1 : frontCheckY = perso1y
                            end select
                            if INT(mX) <> INT(frontCheckX) or INT(mY) <> INT(frontCheckY) then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
                        end if
                    end if
                    i = i + 1
                wend
                if ancienEnCombat = 1 and monstreActuelTrouve = 0 then enCombat = 0 : monstreActuel = 0 : gosub [afficherInfo]
                [skipMonstresUpdate]
            case "REWARD"
                xpGagne = val(word$(data$, 1, ","))
                estBoss = val(word$(data$, 2, ","))
                if estBoss = 1 then bossVaincu = 1 : bossActif = 0
                enCombat = 0 : monstreActuel = 0 : hero.xp = hero.xp + xpGagne : hero.kills = hero.kills + 1
                while hero.xp >= hero.xpProchain
                    hero.niveau = hero.niveau + 1
                    hero.xp = hero.xp - hero.xpProchain
                    hero.xpProchain = int(hero.xpProchain * 1.25)
                    hero.pvMax = hero.pvMax + int(rnd(0)*10+3)
                    hero.pv = hero.pvMax
                    hero.attaque = hero.attaque + int(rnd(0)*4+1)
                    hero.defense = hero.defense + int(rnd(0)*2)
                wend
                gosub [afficherInfo]
                gosub [drawMinimap]
            case "DAMAGE"
                animationAttaque = 1 : timerAnimationAttaque = 0
                posAttaqueX = 400 : posAttaqueY = 250 : scaleAttaque = 200
                degatsRecus = val(data$) : hero.pv = hero.pv - degatsRecus
                if hero.pv <= 0 then gosub [gameOver]
                if hero.pv > 0 then gosub [afficherInfo]
            case "ATTACK_FAILED"
                enCombat = 0 : monstreActuel = 0 : animationAttaque = 0 : typeAnimation = 0
                #main.map "spritevisible attaque off"
                gosub [afficherInfo]
                timerAttaqueMonstre = 0
        end select
    wend
    [clientRecevoirErreur]
    on error goto 0
    return


    [basculerModeSolo]
    for clearJ = 1 to 10
        joueurDistant(clearJ, 0) = 0
        oldJoueurDistant(clearJ, 1) = -1
        oldJoueurDistant(clearJ, 2) = -1
        oldJoueurDistant(clearJ, 3) = -1
    next clearJ
    modeMulti = 0 : estClient = 0 : estServeur = 0 : monIdJoueur = 0 : timerDerniereReponseServeur = 0
    gosub [fermerSockets]
    statutConnexion$ = raisonBasculement$
    if statutConnexion$ = "" then statutConnexion$ = "Mode Solo activé"
    gosub [afficherInfo]
    gosub [drawMinimap]
    return


    [fermerSockets]
    on error goto [socketsDejaFermes]
    if ws2Ouvert = 1 then
    if socketServeur <> 0 then
        calldll #ws2, "closesocket", socketServeur as long, resultClose as long
        socketServeur = 0
    end if

    if socketClient <> 0 then
        calldll #ws2, "closesocket", socketClient as long, resultClose as long
        socketClient = 0
    end if

    calldll #ws2, "WSACleanup", resultWinsock as long
    close #ws2
    ws2Ouvert = 0
    end if

    [socketsDejaFermes]
    on error goto 0
    socketServeur = 0 : socketClient = 0
    return


    [afficherAutresJoueurs]
    mapSize = 5 : cellSize = 16 : totalCells = mapSize * 2 + 1
    totalWidth = totalCells * cellSize : totalHeight = totalCells * cellSize
    offsetX = (188 - totalWidth) / 2 : offsetY = (190 - totalHeight) / 2
    if estServeur = 1 then
    for i = 1 to maxJoueurs
        if joueur(i, 6) = 1 and i <> monIdJoueur then
            jX = joueur(i, 1) : jY = joueur(i, 2)
            if jX >= perso1x - mapSize and jX <= perso1x + mapSize then
                if jY >= perso1y - mapSize and jY <= perso1y + mapSize then
                    mapDX = jX - (perso1x - mapSize) : mapDY = jY - (perso1y - mapSize)
                    screenX = offsetX + mapDX * cellSize : screenY = offsetY + mapDY * cellSize
                    #main.minimap, "color green; backcolor green"
                    #main.minimap, "place "; screenX + 7; " "; screenY + 7
                    #main.minimap, "circlefilled 3"
                end if
            end if
        end if
    next i
    return
    end if
    if estClient = 1 then
    for i = 1 to 10
        if joueurDistant(i, 0) > 0 and joueurDistant(i, 0) <> monIdJoueur then
            jX = joueurDistant(i, 1) : jY = joueurDistant(i, 2)
            if jX >= perso1x - mapSize and jX <= perso1x + mapSize then
                if jY >= perso1y - mapSize and jY <= perso1y + mapSize then
                    mapDX = jX - (perso1x - mapSize) : mapDY = jY - (perso1y - mapSize)
                    screenX = offsetX + mapDX * cellSize : screenY = offsetY + mapDY * cellSize
                    #main.minimap, "color green; backcolor green"
                    #main.minimap, "place "; screenX + 7; " "; screenY + 7
                    #main.minimap, "circlefilled 3"
                end if
            end if
        end if
    next i
    return
    end if
    return

    [fermerReseau]
    on error goto [erreurFermetureReseau]
    if estServeur = 1 then
        modeSilencieux = 1
        if socketServeur = 0 then goto [finFermetureServeur]
        messageServeurFerme$ = "SERVER_CLOSED|0"
        longueurMsg = len(messageServeurFerme$)
        for i = 2 to maxJoueurs
            if joueur(i, 6) = 1 and len(adressesClients$(i)) = 16 then
                adresseClient$ = adressesClients$(i)
                calldll #ws2, "sendto", socketServeur as long, messageServeurFerme$ as ptr, _
                    longueurMsg as long, 0 as long, adresseClient$ as ptr, _
                    16 as long, bytesSent as long
            end if
        next i
        for i = 1 to maxJoueurs
            joueur(i, 6) = 0
            adressesClients$(i) = ""
            oldJoueurServeur(i, 1) = -1
            oldJoueurServeur(i, 2) = -1
            oldJoueurServeur(i, 3) = -1
        next i
        nbJoueursConnectes = 0
        if modeSilencieux = 0 then statutConnexion$ = "Serveur fermé" : gosub [afficherInfo]
        [finFermetureServeur]
    end if
    if estClient = 1 then
        modeSilencieux = 1
        if socketClient = 0 or monIdJoueur = 0 then goto [finFermetureClient]
        messageAEnvoyer$ = "DISCONNECT|" + str$(monIdJoueur)
        gosub [envoyerMessageServeur]
        monIdJoueur = 0
        for clearJ = 1 to 10
            joueurDistant(clearJ, 0) = 0
            oldJoueurDistant(clearJ, 1) = -1
            oldJoueurDistant(clearJ, 2) = -1
            oldJoueurDistant(clearJ, 3) = -1
        next clearJ
        if modeSilencieux = 0 then statutConnexion$ = "Déconnecté du serveur" : gosub [afficherInfo]
        [finFermetureClient]
    end if
    modeMulti = 0 : estServeur = 0 : estClient = 0
    [erreurFermetureReseau]
    on error goto 0
    gosub [fermerSockets]
    return


    [obtenirIP]
    adresseIP$ = "Indisponible" : methodeUtilisee = 0
    on error goto [methodeDLL]
    kill "iptemp.txt"
    open "getip.bat" for output as #bat
    print #bat, "@echo off"
    print #bat, "ipconfig | findstr /i "; chr$(34); "IPv4"; chr$(34); " > iptemp.txt"
    close #bat
    run "getip.bat", HIDE
    timer 500, [obtenirIP2] : wait
    [obtenirIP2]
    timer 0
    if exists("iptemp.txt") then
        open "iptemp.txt" for input as #ipfile
        while not(eof(#ipfile))
            line input #ipfile, ligneComplete$
            posDeuxPoints = instr(ligneComplete$, ":")
            if posDeuxPoints > 0 then
                ipBrute$ = mid$(ligneComplete$, posDeuxPoints + 1)
                ipBrute$ = trim$(ipBrute$)
                if instr(ipBrute$, ".") > 0 and ipBrute$ <> "127.0.0.1" and left$(ipBrute$, 4) <> "169." then
                    adresseIP$ = ipBrute$ : methodeUtilisee = 1
                    exit while
                end if
            end if
        wend
        close #ipfile
    end if
    on error goto [methodeDLL]
    kill "getip.bat"
    kill "iptemp.txt"
    [methodeDLL]
    if adresseIP$ = "Indisponible" then
        on error goto [methodeManuelle]
        open "getip.vbs" for output as #vbs
        print #vbs, "Set objWMI = GetObject("; chr$(34); "winmgmts:"; chr$(34); ")"
        print #vbs, "Set colItems = objWMI.ExecQuery("; chr$(34); "Select * from Win32_NetworkAdapterConfiguration Where IPEnabled = True"; chr$(34); ")"
        print #vbs, "For Each objItem in colItems"
        print #vbs, "  If Not IsNull(objItem.IPAddress) Then"
        print #vbs, "    For Each strAddress in objItem.IPAddress"
        print #vbs, "      If InStr(strAddress, "; chr$(34); "."; chr$(34); ") > 0 And Left(strAddress, 4) <> "; chr$(34); "169."; chr$(34); " And strAddress <> "; chr$(34); "127.0.0.1"; chr$(34); " Then"
        print #vbs, "        WScript.Echo strAddress"
        print #vbs, "        WScript.Quit"
        print #vbs, "      End If"
        print #vbs, "    Next"
        print #vbs, "  End If"
        print #vbs, "Next"
        close #vbs
        open "getip2.bat" for output as #bat2
        print #bat2, "@echo off"
        print #bat2, "cscript //nologo getip.vbs > iptemp2.txt"
        close #bat2
        run "getip2.bat", HIDE
        timer 500, [obtenirIP3] : wait
    end if
    [obtenirIP3]
    timer 0
    if exists("iptemp2.txt") then
        open "iptemp2.txt" for input as #ipfile2
        if not(eof(#ipfile2)) then
            line input #ipfile2, ipFromVBS$
            ipFromVBS$ = trim$(ipFromVBS$)
            if instr(ipFromVBS$, ".") > 0 and ipFromVBS$ <> "127.0.0.1" and left$(ipFromVBS$, 4) <> "169." then adresseIP$ = ipFromVBS$ : methodeUtilisee = 2
        end if
        close #ipfile2
    end if
    on error goto [methodeManuelle]
    kill "getip.vbs"
    kill "getip2.bat"
    kill "iptemp2.txt"
    goto [methodeManuelle]
    [methodeManuelle]
    if adresseIP$ = "Indisponible" then
        prompt "Impossible de détecter votre adresse IP automatiquement." + chr$(13) + _
               "Voulez-vous l'entrer manuellement ?" + chr$(13) + _
               "(Laissez vide pour utiliser 127.0.0.1 - localhost uniquement)"; ipManuelle$
        if ipManuelle$ <> "" then
            if instr(ipManuelle$, ".") > 0 then
                adresseIP$ = ipManuelle$
                methodeUtilisee = 3
            else
                adresseIP$ = "127.0.0.1"
                methodeUtilisee = 3
            end if
        else
            adresseIP$ = "127.0.0.1"
            methodeUtilisee = 3
        end if
    end if
    [finCleanIP]
    on error goto 0
    return


    [FS_Init]
    fsSrcW = 1091 : fsSrcH = 599      ' Dimensions totales de la composition source
    fsRatioX = DisplayWidth / fsSrcW  ' Ratio en conservant les proportions
    fsRatioY = DisplayHeight / fsSrcH
    if fsRatioX < fsRatioY then fsRatio = fsRatioX else fsRatio = fsRatioY

    fsDstW = int(fsSrcW * fsRatio)    ' Dimensions et position de la fenetre plein ecran
    fsDstH = int(fsSrcH * fsRatio)
    fsOffX = int((DisplayWidth - fsDstW) / 2)
    fsOffY = int((DisplayHeight - fsDstH) / 2)

    fsMapW  = int(900 * fsRatio)      ' screen joueur : 900/1091 de la largeur totale
    fsMapH  = fsDstH
    fsMmW   = fsDstW - fsMapW         ' Minimap : 188/1091 de la largeur, 190/599 de la hauteur
    fsMmH   = int(190 * fsRatio)
    fsInfoW = fsMmW                   ' Info : meme largeur que minimap, reste de la hauteur
    fsInfoH = fsDstH - fsMmH

    WindowWidth  = DisplayWidth : WindowHeight = DisplayHeight : UpperLeftX = 0 : UpperLeftY = 0
    graphicbox #fs.view, 0, 0, DisplayWidth, DisplayHeight
    open "Ancestral Dungeon" for window_popup as #fs

    ' Retirer de la barre des taches sans cacher le rendu
    hMainHwnd = hwnd(#main)
    calldll #user32, "SetWindowLongA", hMainHwnd as ulong, -20 as long, 128 as long, r as long
    ' Déplacer hors écran (déjà fait via UpperLeftX = -2000 normalement)
    hFsHwnd = hwnd(#fs.view)
    calldll #user32, "SetForegroundWindow", hFsHwnd as ulong, r as long

    #fs, "trapclose [quit]"
    #fs.view, "down; fill black; setfocus"
    #fs.view, "when leftButtonDown [clicGauche]; when keyPress [keyHandler]"
    hFsHwnd = hwnd(#fs.view)
    calldll #user32, "GetDC", hFsHwnd as ulong, hdcFs as ulong
    calldll #gdi32, "SetStretchBltMode", hdcFs as ulong, 3 as long, fsR as long
    return

    [FS_Render]
    #main.map "drawsprites" ' --- 1. Rendu sprites dans #main.map ---
    #main.map "getbmp fsFrameMap 0 0 900 599" ' --- 2. Capture et upscale de zone jeu 900x599 ---
    hBmpMap = hbmp("fsFrameMap") : hMapHwnd = hwnd(#main.map)
    calldll #user32, "GetDC", hMapHwnd as ulong, hdcMap as ulong
    calldll #gdi32, "CreateCompatibleDC", hdcMap as ulong, hMemMap as ulong
    calldll #gdi32, "SelectObject", hMemMap as ulong, hBmpMap as ulong, oldBmpMap as ulong
    calldll #gdi32, "SetStretchBltMode", hdcFs as ulong, 3 as long, fsR as long
    calldll #gdi32, "StretchBlt", _
        hdcFs   as ulong, _
        fsOffX  as long,  _
        fsOffY  as long,  _
        fsMapW  as long,  _
        fsMapH  as long,  _
        hMemMap as ulong, _
        0       as long,  _
        0       as long,  _
        900     as long,  _
        599     as long,  _
        13369376 as ulong, _
        fsResult as long
    calldll #gdi32, "SelectObject", hMemMap as ulong, oldBmpMap as ulong, fsR2 as ulong
    calldll #gdi32, "DeleteDC", hMemMap as ulong, fsR3 as long
    calldll #user32, "ReleaseDC", hMapHwnd as ulong, hdcMap as ulong

    #main.minimap "getbmp fsFrameMm 0 0 188 190" ' --- 3. Capture et upscale de la minimap 188x190 ---
    hBmpMm = hbmp("fsFrameMm") : hMmHwnd = hwnd(#main.minimap)
    calldll #user32, "GetDC", hMmHwnd as ulong, hdcMm as ulong
    calldll #gdi32, "CreateCompatibleDC", hdcMm as ulong, hMemMm as ulong
    calldll #gdi32, "SelectObject", hMemMm as ulong, hBmpMm as ulong, oldBmpMm as ulong
    fsPanelX = fsOffX + fsMapW
    calldll #gdi32, "StretchBlt", _
        hdcFs    as ulong, _
        fsPanelX as long,  _
        fsOffY   as long,  _
        fsMmW    as long,  _
        fsMmH    as long,  _
        hMemMm   as ulong, _
        0        as long,  _
        0        as long,  _
        188      as long,  _
        190      as long,  _
        13369376 as ulong, _
        fsResult as long
    calldll #gdi32, "SelectObject", hMemMm as ulong, oldBmpMm as ulong, fsR2 as ulong
    calldll #gdi32, "DeleteDC", hMemMm as ulong, fsR3 as long
    calldll #user32, "ReleaseDC", hMmHwnd as ulong, hdcMm as ulong

    #main.info "getbmp fsFrameInfo 0 0 188 406" ' --- 4. Capture et upscale de la zone info 188x406 ---
    hBmpInfo = hbmp("fsFrameInfo") : hInfoHwnd = hwnd(#main.info)
    calldll #user32, "GetDC", hInfoHwnd as ulong, hdcInfo as ulong
    calldll #gdi32, "CreateCompatibleDC", hdcInfo as ulong, hMemInfo as ulong
    calldll #gdi32, "SelectObject", hMemInfo as ulong, hBmpInfo as ulong, oldBmpInfo as ulong
    fsInfoY = fsOffY + fsMmH
    calldll #gdi32, "StretchBlt", _
        hdcFs    as ulong, _
        fsPanelX as long,  _
        fsInfoY  as long,  _
        fsInfoW  as long,  _
        fsInfoH  as long,  _
        hMemInfo as ulong, _
        0        as long,  _
        0        as long,  _
        188      as long,  _
        406      as long,  _
        13369376 as ulong, _
        fsResult as long
    calldll #gdi32, "SelectObject", hMemInfo as ulong, oldBmpInfo as ulong, fsR2 as ulong
    calldll #gdi32, "DeleteDC", hMemInfo as ulong, fsR3 as long
    calldll #user32, "ReleaseDC", hInfoHwnd as ulong, hdcInfo as ulong
    return

    ' ============================================================

        function exists(filename$)
        on error goto [noExist]
        open filename$ for input as #testExist
        close #testExist
        exists = 1
        exit function
        [noExist]
        exists = 0
        on error goto 0
    end function
