0aC(X*-DӨ%qye3MFw! 02TvͫgE#2Tv2Tv wffwWUevfffffVUUffffVeffgfUvwffw,,,,,,,   `ff`ff`UU`ff""""""""""""""""""""""""""""""""@DD@D@DDDD@ 33333333333333333333333333333333wpwppppwpw@DD@L@DDDD@ DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD @DD@L@DLDD@UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU @DD@L@LDD@ffffffffffffffffffffffffffffffff@L@@L@ UUUU^UUUUUUUU^UUUUUUUU^UUUU]]___wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww DDDDTTUEUETUUUUTUUEUUUDUUDTTUTUTDUUUTEUDTUDUTTETTTUTDUUDTUUUDUUUUUUUTUEDETUEUTEDUTEUDUDUTUTUUUUUUUUUDTUETUETDTEUUTEUUDUDUEUEUUUUUUUDDUTEUDTEETUEEUUDDUDEUUTEUUUDEDUUEEUUEEDUUUETUUUUUDTUUTEUEUETEUETUTEUUDTUUUUUUUETEEDUEEUUEDUUUTUTUDTTUUDUUUEUETETUTUUTEUEDDDDUUUU^^UUUUUUUUUUUU^^UUUUUUUUUUUU^^UUUU^^^UUUUUUUU^^UUUUUUUUUUUU^^UUUUUUUUUUUU^^UUUU^^^UUUUUUUU^^UUUU^^^UUUU^^^UUUUUUUU^^^UUUUUUUU^^^UUUU^^^UUUU^^^UUUUUUUUUUUU^^UUUU^^^UUUUUUUU^^^UUUU^^^^^^UUUUUUUU^^^UUUUUUUU^^^UUUU^^^UUUU^^^UUUU^^^UUUU^^^UUUU^^^UUUU^^^UUUU^^^UUUU 3030<3<3"33"3033wpwvwpfgvwfgvwwwwwwfwpwf@D @@DDD DDDD ̼˻ нݻ ۽ gfvwwwwwwwwwwgffwwwwgvwwwwfwwwww 2 3" #2# #2# # ppp`ffllffff````DM@DDDDDD@  wwwwwfwwwwgvwwwwffvwwwwwwwwwwgfvfnD>NDn>  @ @DMDDD @DDDM wwwwwwfwgvwwwwwwwgffwwwwwwwwgfvw D@DD@DD@DM@D @ @@M@@DDDD@M@"" """""" """""""" """"wwwww|||ww|||ww|||wwwwwUUUU^UUUUUUUU^UUUUUUUU^UUUU]]___efgeffgefffgegegefgOOO3AQQA1OOOOOOss!!1!s!SOOO1s#s!A1s1A1AO1ACsQQss1A!A1A!1!A!CQOOAssss!1OO!ss1ss11A1OO1!QQAQ1As1scOO1QQQAs!!OOs1s11!1AQsQsAQA1s1QdsQA!!s1!Qs!!sdddddOOOOOOOOOOOOOOO#####d >m##!    ?UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^UUUUUUUUUUUU^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU]^_^_^UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU_UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^ODODOD^^UUUUUUUUDODODOOOODUUUUUUUU^OOODOOOOODO^^OOOOOOOOOO^^DOOOOOOO^UUUUUUUUOUUUUUUUU^^DDDDDDDD ^^^^TTUETTUE2^^^^UETUUETU 3^^UUUUUUUUUUUTUUUT"UUUUUUUU^UUEUUUEU #^^UUDUUUDU2# #^^UDTTUDTT2# #^UUUUUUUUUTUTUTUT UUUUUUUU^^ EDUUEDUU^^^^p2EEUUEEUU^^^^ 3EEDU@D @EEDU^^UUUUUUUUfff"UUET@DUUETUUUUUUUU^of #UUUUDD UUUU^^offf2# #UDTUDDUDTU^^ff2# #UTEUDDUTEU^UUUUUUUUoo EUETEUETUUUUUUUU^^UUUUUUUUEUETEUET^^^^^^UTEUUTEU^^^^^^UDTUUDTU^^UUUUUUUUUUUUUUUUUUUUUUUUfffUUUUUUUU^^UUETUUETof^^^EEDUEEDUofff^^^EEUUEEUUff^UUUUUUUUUUUUUUUUEDUUEDUUooUUUUUUUU^^^^UTUTUTUTUUUUUUUUUUUUUUUUUUUU^^^^^^UDTTUDTT^^^^^^^^^^^UUDUUUDU^^^^^^^UUUUUUUUUUUUUUUUUUEUUUEUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^ETETETET^^^^^UTUUUTUU^^^^^TEUETEUE^^^UUUUUUUUUUUUUUUUDDDDDDDDUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^^^^^^^^^^UUUUUUUUoffUUUUUUUUUUUUUUUUUUUU^ll^^^ffff^^^oo^^UUUUUUUUooUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^UUUUUUUUUUUUUUUU^^^^^^UUUUUUUUUUUUUUUU^^UUUUUUUU^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUoffUUUUUUUU^^ll^^^ffff^^^oo^UUUUUUUUUUUUUUUUooUUUUUUUU^^^^UUUU^^^^^^^^^^^^^^^^UUUUUUUUUUUUUUUU^UUUUUUUU^^UUUU^^^^^^^^^UUUUUUUUUUUUUUUU^UUUUUUUU^^^^UUUUUUUU^ ^^^^^^^^^2p^^^^^^^^^ 3^^UUUUUUUUUUUUUUUUUUUUUUUUUUUU"fffUUUUUUUU^^^ #of^^^^2# #offf^^^^2# #ff^UUUUUUUUUUUUUUUUUUUUUUUUUUUU ooUUUUUUUU^^ ^^^UUUUUUUUUUUU^^^^2^^^^^^^^^^ 3^^^^^^^^UUUUUUUU"UUUUUUUUUUUUUUUUUUUU^UUUUUUUU^ #^^^UUUU^^2# #^^^^^^2# #^^^^^UUUUUUUU UUUUUUUUUUUUUUUUUUUU^UUUUUUUU^^^^^^^^^^^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^^^^^^^UUUUUUUUUUUUUUUUUUUUUUUU^^^^^^^^^^^^UUUUUUUUUUUU?3UUUUDUUUUUUUU^^33O^^^^DD?3?<<MOD^^^^ODOODOOO?OM^^UUUUUUUUOOOOOOOO? enemy.targetTileX then dx = -math.min(delta, enemy.x - enemy.targetTileX) end if enemy.y < enemy.targetTileY then dy = math.min(delta, enemy.targetTileY - enemy.y) elseif enemy.y > enemy.targetTileY then dy = -math.min(delta, enemy.y - enemy.targetTileY) end -- Tenter de déplacer l'ennemi vers la tuile cible if not attemptMove(enemy, dx, dy) then -- Si le déplacement est bloqué, annuler la tuile cible pour choisir une nouvelle direction enemy.targetTileX = nil enemy.targetTileY = nil addDebugMessage("Mutant bloqué, changement de direction", colorPalette.red, "Entity") end end function updatePatrol(enemy) -- Vérifie si le timer de l'ennemi est inférieur à son délai initial if enemy.timer < enemy.initialDelay then enemy.timer = enemy.timer + 1 return end local delta = enemy.currentSpeed / FRAME_RATE -- Calcule le déplacement horizontal en fonction de la direction de l'ennemi local dx = enemy.direction * delta -- Vérifie si le chemin est bloqué dans la direction actuelle if not attemptMove(enemy, dx, 0) then -- Inverse la direction de l'ennemi en cas d'obstacle enemy.direction = enemy.direction * -1 addDebugMessage("Parasite change de direction à cause d'un obstacle", colorPalette.red, "Entity") return end -- Met à jour la position horizontale de l'ennemi enemy.x = enemy.x + dx -- Vérifie si l'ennemi a atteint la fin de sa patrouille if enemy.x >= enemy.patrolEnd then enemy.x = enemy.patrolEnd enemy.direction = -1 elseif enemy.x <= enemy.patrolStart then enemy.x = enemy.patrolStart enemy.direction = 1 end end function updateLinearMovement(wormHead) -- Calculer le déplacement par frame local delta = wormHead.currentSpeed / FRAME_RATE local dx = wormHead.direction * delta -- Vérifier si le ver touche un obstacle bloquant ou sort des limites if isPathBlocked(wormHead, dx, 0) or wormHead.x < 0 or wormHead.x > SCREEN_WIDTH or wormHead.y < 0 or wormHead.y > SCREEN_HEIGHT then despawnWorm(wormHead) return end -- Si le ver n'a pas de position cible, définir la prochaine position cible if not wormHead.targetX then wormHead.targetX = wormHead.x + (wormHead.direction * TILE_SIZE) end -- Déplacer la tête vers la position cible if wormHead.x < wormHead.targetX then wormHead.x = math.min(wormHead.x + delta, wormHead.targetX) elseif wormHead.x > wormHead.targetX then wormHead.x = math.max(wormHead.x - delta, wormHead.targetX) end -- Enregistrer la position actuelle dans l'historique table.insert(wormHead.positionHistory, {x = wormHead.x, y = wormHead.y}) -- Limiter la taille de l'historique pour éviter une utilisation excessive de mémoire local positionsPerTile = math.ceil(TILE_SIZE / delta) local maxHistorySize = wormHead.size * positionsPerTile + 1 if #wormHead.positionHistory > maxHistorySize then table.remove(wormHead.positionHistory, 1) end -- Mise à jour des segments en utilisant un tableau d'historique de positions for i, segment in ipairs(wormHead.segments) do -- Calculer le nombre de positions à retarder pour chaque segment local delay = i * positionsPerTile -- Calculer l'indice dans positionHistory local index = #wormHead.positionHistory - delay if index > 0 then local targetPosition = wormHead.positionHistory[index] segment.x = targetPosition.x segment.y = targetPosition.y else -- Si pas assez d'historique, placer le segment sur la tête segment.x = wormHead.x segment.y = wormHead.y end end -- Si la tête a atteint la position cible, définir une nouvelle position cible if wormHead.x == wormHead.targetX then wormHead.targetX = wormHead.x + (wormHead.direction * TILE_SIZE) end end function updateCyclicEmergence(enemy) -- Appliquer le délai initial si nécessaire if enemy.initialDelay > 0 then enemy.initialDelay = enemy.initialDelay - 1 return -- Attendre jusqu'à ce que le délai initial soit écoulé end -- Décrémenter le timer de l'ennemi enemy.timer = enemy.timer - 1 -- Changer d'état lorsque le timer atteint zéro if enemy.timer <= 0 then if enemy.state == "enfoncé" then -- State: Emerger enemy.state = "émerger" enemy.timer = FRAME_RATE * 1.5 enemy.spriteIndex = 2 elseif enemy.state == "émerger" then -- State: Emergé enemy.state = "émergé" enemy.timer = FRAME_RATE * 2.5 enemy.spriteIndex = 3 if collides(player, enemy) then handlePlayerDeath() addDebugMessage("Le joueur a été touché par le ver", colorPalette.red, "Entity") end elseif enemy.state == "émergé" then -- State: Disparaître enemy.state = "disparaître" enemy.timer = FRAME_RATE * 1 enemy.spriteIndex = 4 elseif enemy.state == "disparaître" then -- State: Enfoncé enemy.state = "enfoncé" enemy.timer = FRAME_RATE * 2 enemy.spriteIndex = 1 end end -- Gestion des mouvements en fonction de l'état -- local delta = enemy.currentSpeed / FRAME_RATE -- if enemy.state == "émerger" then -- -- Monter progressivement vers la position cible (émergence) -- enemy.y = math.max(enemy.y - delta, enemy.baseY - enemy.emergenceHeight) -- elseif enemy.state == "disparaître" then -- -- Descendre progressivement vers la position de base (disparition) -- enemy.y = math.min(enemy.y + delta, enemy.baseY) -- end end updateBehaviorFunctions = { randomMovement = updateRandomMovement, patrol = updatePatrol, linearMovement = updateLinearMovement, cyclicEmergence = updateCyclicEmergence } function updateCollectible(collectible) -- TODO: Implementer la logique de mise à jour des collectibles end function updateInteractable(interactable) -- TODO: Implementer la logique de mise à jour des objets interactifs end function updateSpecialTile(tile) -- TODO: Implementer la logique de mise à jour des tiles spéciales end function updateObstacle(obstacle) -- TODO: Implementer la logique de mise à jour des obstacles end updateFunctions = { enemy = updateEnemy, collectible = updateCollectible, interactable = updateInteractable, specialTile = updateSpecialTile, obstacle = updateObstacle } function drawEntities() for _, entity in ipairs(entities) do local spriteIndex = entity.spriteIndex or 1 spr(entity.sprites[spriteIndex], entity.x, entity.y, colorPalette.transparent) end end -- ============================= -- // --------------------------- -- // SECTION 6 : Système d'Effets -- // --------------------------- activeEffects = {} obstacleSpeedModifier = 1.0 function applyEffect(effectType) local effectConfig = effectTypes[effectType] if effectConfig then -- Vérifier si un effet de ce type est déjà actif local existingEffect = nil for _, effect in ipairs(activeEffects) do if effect.type == effectType then existingEffect = effect break end end if existingEffect then -- Mettre à jour la durée de l'effet existant existingEffect.duration = effectConfig.duration addDebugMessage("Effet mis à jour: " .. effectType .. " (" .. effectConfig.percentage .. "%) pendant " .. effectConfig.duration .. " frames", colorPalette.electricBlue, "Effect") else -- Ajouter un nouvel effet table.insert(activeEffects, {type = effectType, value = effectConfig.percentage, duration = effectConfig.duration}) addDebugMessage("Effet appliqué: " .. effectType .. " (" .. effectConfig.percentage .. "%) pendant " .. effectConfig.duration .. " frames", colorPalette.electricBlue, "Effect") end -- Mettre à jour la vitesse des entités après avoir appliqué l'effet updateEntitiesSpeed() else addDebugMessage("Type d'effet inconnu: " .. effectType, colorPalette.red, "Effect") end end function updateEffects() -- Réinitialiser le modificateur de vitesse de base local baseModifier = 1.0 + (player.prestigeLevel * 0.1) obstacleSpeedModifier = baseModifier -- Appliquer les effets actifs for _, effect in ipairs(activeEffects) do if effect.type == "speedUp" then obstacleSpeedModifier = obstacleSpeedModifier * (1 + effect.value / 100) elseif effect.type == "speedDown" then obstacleSpeedModifier = obstacleSpeedModifier * (1 - effect.value / 100) end end -- Mettre à jour la durée des effets et les supprimer s'ils sont expirés for i = #activeEffects, 1, -1 do local effect = activeEffects[i] effect.duration = effect.duration - 1 if effect.duration <= 0 then table.remove(activeEffects, i) addDebugMessage("Effet expiré: " .. effect.type, colorPalette.grey, "Effect") end end -- Mettre à jour la vitesse des entités après avoir mis à jour obstacleSpeedModifier updateEntitiesSpeed() end function updateEntitiesSpeed() for _, entity in ipairs(entities) do if entity.baseSpeed then entity.currentSpeed = entity.baseSpeed * obstacleSpeedModifier -- Optionnel : Mettre à jour delta si nécessaire if entity.delta then entity.delta = (entity.currentSpeed * (math.random(80, 120) / 100)) / FRAME_RATE end end end end -- ============================= -- // --------------------------- -- // SECTION 7 : Gestion des Collisions -- // --------------------------- function checkCollisions() local isCurrentlyColliding = false local collidedEntity = nil -- Détecte si il y a une collision avec une entité for _, entity in ipairs(entities) do if collides(player, entity) then isCurrentlyColliding = true collidedEntity = entity if entity.behavior == "cyclicEmergence" and entity.state == "émergé" then handlePlayerDeath() addDebugMessage("Collision fatale avec un ver émergé", colorPalette.red, "Collision") end break end end -- si il y a collision: Met à jour le flag de collision du joueur if isCurrentlyColliding and not player.isColliding then player.isColliding = true handleCollisions(collidedEntity) end -- Si aucune collision n'est trouvée: Réinitialiser le flag de collision if not isCurrentlyColliding then player.isColliding = false end end function handleCollisions(entity) if collisionHandlers[entity.type] then collisionHandlers[entity.type](entity) else addDebugMessage("Aucun gestionnaire de collision pour le type : " .. entity.type, colorPalette.red, "Collision") end end -- GESTIONNAIRES DE COLLISION function handleEnemyCollision(entity) if entity.behavior == "cyclicEmergence" and entity.state ~= "émergé" then -- Si l'entité est un ver en phase "émerger", "disparaître" ou "enfoncé", elle n'est pas dangereuse addDebugMessage("Collision avec un ennemi non dangereux : " .. entity.name, colorPalette.grey, "Collision") return end addDebugMessage("Collision avec un ennemi : " .. entity.name, colorPalette.red, "Collision") handlePlayerDeath() end function handleCollectibleCollection(entity) if entity.type == "collectible" and entity.name:find("fragment") then player.fragmentsCollected = player.fragmentsCollected + 1 addScoreForFragment() elseif entity.type == "collectible" and entity.subType == "bonus" then addScoreForCollectible() end -- Retirer le fragment de la carte for i, e in ipairs(entities) do if e == entity then table.remove(entities, i) break end end -- Active le portail de la transcendance si tous les fragments sont collectés if player.fragmentsCollected >= gameState.requiredFragments then for _, interactable in ipairs(entities) do if interactable.name == "portalTrans" then interactable.isActive = true addDebugMessage("Portail de la transcendance actif!", colorPalette.lightGreen, "Interactable") end end end -- Ajouter un message de debug addDebugMessage("Fragment collecté: " .. entity.name, colorPalette.yellow, "Collectible") end function handleInteractableCollision(entity) if entity.effect == "setSpawnPoint" then gameState.spawnX = entity.x gameState.spawnY = entity.y addDebugMessage("Point de spawn mis à jour à (" .. gameState.spawnX .. ", " .. gameState.spawnY .. ")", colorPalette.lightGreen, "Effect") elseif entity.name == "portalTrans" then if entity.isActive then addDebugMessage("Collision avec le portail de la transcendance", colorPalette.electricBlue, "Collision") local level = getCurrentLevel() if level.levelTag == "Final" then gameState.gameWon = true displayWinScreen() else _switchLevel() end else addDebugMessage("Le portail de la transcendance est inactif", colorPalette.red, "Effect") end elseif entity.name == "portalPrestige" then local level = getCurrentLevel() if level.levelTag == "Final" then entity.isActive = true end if entity.isActive and level.levelTag == "Final" then addDebugMessage("Collision avec le portail du recommencement", colorPalette.green, "Collision") _prestige() -- TODO: Placeholder, implementer le système de prestige else addDebugMessage("Le portail du recommencement est inactif", colorPalette.red, "Effect") end elseif entity.effect == "switchLevel" then addDebugMessage("Changement de niveau via la tuile de changement de niveau", colorPalette.green, "Effect") _switchLevel() elseif entity.effect == "speedUp" then -- Fanatique Fécaliste: Augmente la vitesse des obstacles applyEffect("speedUp") addDebugMessage("Fanatique Fécaliste a appliqué un buff de vitesse.", colorPalette.lightGreen, "Effect") -- Optionnel: Retirer ou désactiver l'entité fanatique après application entity.isActive = false elseif entity.effect == "speedDown" then -- Fanatique Transcendantaliste: Diminue la vitesse des obstacles applyEffect("speedDown") addDebugMessage("Fanatique Transcendantaliste a appliqué un debuff de vitesse.", colorPalette.lightGreen, "Effect") -- Optionnel: Retirer ou désactiver l'entité fanatique après application entity.isActive = false end end function applyTileEffect(tileType) if tileType then -- Vous pouvez implémenter des effets spécifiques ici -- Exemple : -- if tileType == "slippery" then -- player.speedMultiplier = player.speedMultiplier * 0.8 -- end end end function handleObstacleCollision(obstacle) local obstacleType = obstacle.obstacleType if obstacleCollisionHandlers[obstacleType] then obstacleCollisionHandlers[obstacleType](obstacle) else addDebugMessage("Aucun gestionnaire de collision pour l'obstacle de type : " .. obstacleType, colorPalette.red, "Collision") end end function handleVaporCollision(obstacle) addDebugMessage("Collision avec : " .. obstacle.name, colorPalette.white, "Collision") handlePlayerDeath() -- Déplacer le jet de vapeur vers la droite avec le modificateur de vitesse obstacle.x = obstacle.x + (obstacle.speed / FRAME_RATE) if obstacle.x > SCREEN_WIDTH then despawnEntity(obstacle) end end function handleRiverTileCollision(obstacle) addDebugMessage("Collision avec : " .. obstacle.name, colorPalette.lightGreen, "Collision") handlePlayerDeath() -- Déplacer la tuile de rivière vers le bas avec le modificateur de vitesse obstacle.y = obstacle.y + (obstacle.speed / FRAME_RATE) if obstacle.y > SCREEN_HEIGHT then despawnEntity(obstacle) end end function handleFloatingDebrisCollision(obstacle) -- Ici, on ne fait rien, car les débris flottants ne sont pas mortels addDebugMessage("Collision avec : " .. obstacle.name, colorPalette.darkGrey, "Collision") end obstacleCollisionHandlers = { vapor = handleVaporCollision, riverTile = handleRiverTileCollision, debris_floating = handleFloatingDebrisCollision } collisionHandlers = { enemy = handleEnemyCollision, collectible = handleCollectibleCollection, interactable = handleInteractableCollision, specialTile = applyTileEffect, obstacle = handleObstacleCollision } -- ============================= -- // --------------------------- -- // SECTION 8 : Gestion du joueur -- // --------------------------- function updatePlayer() -- Gestion des inputs du joueur if not player.isMoving then if btnp(2, 30, 10) then initiateMovement("left") end if btnp(3, 30, 10) then initiateMovement("right") end if btnp(0, 30, 10) then initiateMovement("up") end if btnp(1, 30, 10) then initiateMovement("down") end end local deltaSpeed = (PLAYER_SPEED * player.speedMultiplier) / FRAME_RATE local dx, dy = 0, 0 -- Calculer le déplacement horizontal vers la position cible if player.x < player.targetX then dx = math.min(deltaSpeed, player.targetX - player.x) elseif player.x > player.targetX then dx = -math.min(deltaSpeed, player.x - player.targetX) end -- Calculer le déplacement vertical vers la position cible if player.y < player.targetY then dy = math.min(deltaSpeed, player.targetY - player.y) elseif player.y > player.targetY then dy = -math.min(deltaSpeed, player.y - player.targetY) end -- Si un déplacement est nécessaire if dx ~= 0 or dy ~= 0 then -- Tenter de déplacer le joueur if not attemptMove(player, dx, dy) then -- Si le mouvement est bloqué, arrêter le déplacement player.targetX = player.x player.targetY = player.y player.isMoving = false addDebugMessage("Mouvement interrompu par un obstacle", colorPalette.red, "Collision") end end -- Arrêter le mouvement si on atteint la cible if player.x == player.targetX and player.y == player.targetY then player.isMoving = false -- Vérifier si le joueur se déplace vers le haut if dy < 0 then local currentTile = player.y addScoreForMovement(currentTile) end end -- Appliquer les effets de la tuile actuelle -- applyTileEffect(getTileType(player.x, player.y)) end function initiateMovement(direction) local dx, dy = 0, 0 if direction == "left" then dx = -TILE_SIZE elseif direction == "right" then dx = TILE_SIZE elseif direction == "up" then dy = -TILE_SIZE elseif direction == "down" then dy = TILE_SIZE end -- Vérifier si la nouvelle position cible est libre if not isPathBlocked(player, dx, dy) then player.targetX = player.x + dx player.targetY = player.y + dy player.isMoving = true end end function drawPlayer() spr(player.sprite, player.x, player.y, 0) end function handlePlayerDeath() if player.isInvincible then return end gameState.lives = gameState.lives - 1 if gameState.lives <= 0 then gameState.gameOver = true gameOver() else respawnPlayer() end end function respawnPlayer() if gameState.spawnX and gameState.spawnY then player.x = gameState.spawnX player.y = gameState.spawnY player.targetX = player.x player.targetY = player.y else -- Fallback to default local level = getCurrentLevel() player.x = level.playerStartX player.y = level.playerStartY player.targetX = player.x player.targetY = player.y end player.isMoving = false end -- ============================= -- // --------------------------- -- // SECTION 9 : Interface Utilisateur (UI) -- // ----------------=========== -- Positions de base pour chaque quadrant local UIPositions = { upperLeft = {x = 17, y = 10}, lowerLeft = {x = 17, y = SCREEN_HEIGHT - 15}, upperRight = {x = SCREEN_WIDTH - 55, y = 10}, lowerRight = {x = SCREEN_WIDTH - 67, y = SCREEN_HEIGHT - 17}, } -- Fonction principale de dessin de l'UI function drawUI() if not debugEnabled then drawUIUpperLeft() drawUILowerLeft() -- Affiche le score en bas à gauche drawUIUpperRight() drawUILowerRight() -- Affiche les vies et fragments en bas à droite end end -- Dessin du haut gauche (Prestige) function drawUIUpperLeft() local pos = UIPositions.upperLeft print("Prestige: " .. player.prestigeLevel, pos.x, pos.y, colorPalette.red, false, 1, true) end -- Dessin du bas gauche (Score) function drawUILowerLeft() local pos = UIPositions.lowerLeft -- Afficher le score local score = math.floor(gameState.score) print("Score: " .. score, pos.x, pos.y, colorPalette.yellow, false, 1, true) end -- Dessin du haut droit (Temps restant) function drawUIUpperRight() local pos = UIPositions.upperRight -- Afficher le temps restant local timeLeft = math.ceil(gameState.timeLeft) print("Temps: " .. timeLeft .. "s", pos.x, pos.y, colorPalette.yellow, false, 1, true) end -- Dessin du bas droit (Vies et Fragments) function drawUILowerRight() local pos = UIPositions.lowerRight -- Définir les constantes pour les tailles des sprites local spriteWidth = 8 -- Largeur des sprites en pixels local spriteHeight = 8 -- Hauteur des sprites en pixels (si nécessaire) -- Définir les variables d'espacement (facile à ajuster) local spacingBetweenLifeIconAndText = 5 -- Espacement entre l'icône des vies et le texte des vies local spacingBetweenLifeAndFragment = 5 -- Espacement entre la section des vies et l'icône des fragments -- Position de départ pour l'icône des vies local lifeIconX = pos.x local lifeIconY = pos.y -- Dessiner l'icône des vies spr(257, lifeIconX, lifeIconY, colorPalette.transparent) -- Texte des vies local lifeText = "x" .. gameState.lives local lifeTextX = lifeIconX + spriteWidth + spacingBetweenLifeIconAndText local lifeTextY = pos.y + 2 print(lifeText, lifeTextX, lifeTextY, colorPalette.red, false, 1, true) -- Calculer la largeur du texte des vies (4 pixels par caractère) local lifeTextWidth = #lifeText * 4 -- Position pour l'icône des fragments local fragmentIconX = lifeTextX + lifeTextWidth + spacingBetweenLifeAndFragment local fragmentIconY = pos.y -- Dessiner l'icône des fragments spr(323, fragmentIconX, fragmentIconY, colorPalette.transparent) -- Texte des fragments local fragmentsText = player.fragmentsCollected .. "/" .. gameState.requiredFragments local fragmentsTextX = fragmentIconX + spriteWidth + spacingBetweenLifeIconAndText local fragmentsTextY = pos.y + 2 print(fragmentsText, fragmentsTextX, fragmentsTextY, colorPalette.yellow, false, 1, true) end -- ============================= -- // --------------------------- -- // SECTION 10 : Système de Débogage -- // --------------------------- -- === CONFIGURATION DES CATEGORIES DE DEBUG === activeDebugCategories = { ["Effect"] = true, -- Active les messages liés aux buffs/debuffs ["Entity"] = true, -- Active les messages liés aux entités ["Spawner"] = true, -- Active les messages liés aux spawners ["Collision"] = true, ["Score"] = true, ["GameState"] = true, ["Player"] = true, -- Ajoutez d'autres catégories selon vos besoins } -- DEBUG VARIABLES debugMessages = {} debugEnabled = false -- Permet d'activer/desactiver le mode debug debugColor = colorPalette.yellow consoleLimit = 10 -- Augmenté pour afficher plus de messages -- DEBUG CONSOLE function addDebugMessage(message, color, category) if debugEnabled and activeDebugCategories[category] then table.insert(debugMessages, {text = message, duration = (FRAME_RATE * 5), color = color or debugColor}) if #debugMessages > consoleLimit then table.remove(debugMessages, 1) end end end function drawDebugConsole() local y = SCREEN_HEIGHT - (#debugMessages * 7) - 5 for i = #debugMessages, 1, -1 do local msg = debugMessages[i] print(msg.text, 10, y, msg.color, false, 0.5, true) y = y + 7 * 0.5 -- Ajuster en fonction de l'échelle -- Supprimer les messages de debug après un certain temps msg.duration = msg.duration - 1 if msg.duration <= 0 then table.remove(debugMessages, i) end end end -- DEBUG INPUTS function handleDebugInputs() -- Vérifie les touches spécifiques au mode debug uniquement si debugEnabled est true if debugEnabled then -- Changement de niveau avec la touche (TAB) if keyp(49) then _switchLevel() end -- Modifications du multiplicateur de vitesse par incréments de 0.1 avec les touches Q (-) et S (+) if keyp(19) then -- Touche S _updateSpeedMultiplier(1) elseif keyp(17) then -- Touche Q _updateSpeedMultiplier(0) end -- Ajouter un ennemi de debug avec la touche (E) if keyp(5) then _spawnDebugEnemy() end -- Ajuster le niveau de prestige avec la touche (P) if keyp(16) then _increasePrestige() end -- Mode invincible switch en pressant (I) if keyp(9) then player.isInvincible = not player.isInvincible addDebugMessage("Mode invincible : " .. tostring(player.isInvincible), colorPalette.yellow, "Player") end -- Switch au niveau dev ground avec la touche (G) if keyp(7) then gameState.currentLevel = 7 loadLevel() end -- Touche de toggle debug (D) if keyp(4) then debugEnabled = not debugEnabled addDebugMessage("Mode debug " .. (debugEnabled and "activé" or "désactivé"), colorPalette.lightGrey, "GameState") end else -- Touche de toggle debug (D) if keyp(4) then debugEnabled = not debugEnabled addDebugMessage("Mode debug " .. (debugEnabled and "activé" or "désactivé"), colorPalette.lightGrey, "GameState") end end end -- DEBUG UI function drawDebug() if debugEnabled then drawDebugConsole() drawDebugUI() end end function drawGeneralInfo(x, y, scale) print("Score: " .. gameState.score, x, y, debugColor, false, scale, true) y = y + 10 * scale print("Level: " .. levels[gameState.currentLevel].title, x, y, debugColor, false, scale, true) y = y + 10 * scale print("Prestige Lv: " .. player.prestigeLevel, x, y, debugColor, false, scale, true) end function drawPlayerInfo(x, y, scale) print("Player Pos:", x, y, debugColor, false, scale, true) y = y + 10 * scale print("(" .. math.floor(player.x) .. ", " .. math.floor(player.y) .. ")", x, y, debugColor, false, scale, true) y = y + 10 * scale print("Speed Mult.: " .. string.format("%.2f", player.speedMultiplier), x, y, debugColor, false, scale, true) y = y + 10 * scale print("Obst. Speed Mod: " .. string.format("%.2f", obstacleSpeedModifier), x, y, debugColor, false, scale, true) y = y + 10 * scale print("Lives: " .. gameState.lives, x, y, debugColor, false, scale, true) end function drawEffectsInfo(x, y, scale) print("Active efct.:", x, y, debugColor, false, scale, true) y = y + 10 * scale for _, effect in ipairs(activeEffects) do local effectColor = effect.type == "speedUp" and colorPalette.lightGreen or colorPalette.red print(effect.type .. " (" .. effect.value .. "%) - " .. effect.duration .. " frames", x, y, effectColor, false, scale, true) y = y + 10 * scale end end function drawEnemiesInfo(x, y, scale) print("Enemies sp.:", x, y, debugColor, false, scale, true) y = y + 10 * scale for _, entity in ipairs(entities) do if entity.type == "enemy" then -- Utiliser currentSpeed au lieu de speed print(entity.name .. ": " .. string.format("%.2f", entity.currentSpeed), x, y, debugColor, false, scale, true) y = y + 10 * scale end end end function drawDebugUI() local scale = 1 -- Informations Générales drawGeneralInfo(10, 10, scale) -- État du Joueur drawPlayerInfo(SCREEN_WIDTH - 80, 10, scale) -- Effets Actifs drawEffectsInfo(10, SCREEN_HEIGHT - 50, scale) -- Vitesse des Ennemis drawEnemiesInfo(SCREEN_WIDTH - 80, SCREEN_HEIGHT - 50, scale) end -- DEBUG UTILS function _increasePrestige() player.prestigeLevel = player.prestigeLevel + 1 addDebugMessage("Prestige level augmenté à : " .. player.prestigeLevel, colorPalette.yellow, "Player") end function _switchLevel() local timeLeft = gameState.timeLeft or 0 addScoreForTimeRemaining(timeLeft) gameState.currentLevel = gameState.currentLevel + 1 if gameState.currentLevel > #levels then gameState.currentLevel = #levels gameState.gameWon = true displayWinScreen() else loadLevel() end end function _prestige() -- Réinitialiser le niveau au début gameState.currentLevel = 1 -- Augmenter le niveau de prestige player.prestigeLevel = player.prestigeLevel + 1 addDebugMessage("Prestige level augmenté à : " .. player.prestigeLevel, colorPalette.yellow, "Effect") -- Charger le niveau avec les nouveaux paramètres de difficulté loadLevel() end function _spawnDebugEnemy() local debugEnemy = createEntity("mutant", 120, 68) -- Utiliser un type existant comme "mutant" if debugEnemy then debugEnemy.name = "debugEnemy" debugEnemy.baseSpeed = 0 debugEnemy.currentSpeed = 0 addDebugMessage("Debug enemy spawned!", colorPalette.lightGreen, "Entity") end end function _updateSpeedMultiplier(amount) if amount == 1 then player.speedMultiplier = player.speedMultiplier + 0.1 addDebugMessage("Speed Multiplier +0.1", colorPalette.lightGreen, "Player") elseif amount == 0 then player.speedMultiplier = player.speedMultiplier - 0.1 addDebugMessage("Speed Multiplier -0.1", colorPalette.red, "Player") end end -- ============================= -- // --------------------------- -- // SECTION 11 : Boucle principale -- // --------------------------- function TIC() cls(0) if gameState.isWinScreen then drawWinScreen() if keyp(18) then -- Touche R pour recommencer reset() end return end if gameState.isGameOver then drawGameOverScreen() if keyp(18) then -- Touche R pour recommencer reset() end return end handleDebugInputs() -- Mise à jour des éléments updateLevelTimer() -- Mise à jour du timer du niveau updateSpawners() -- Mise à jour des spawners updateEffects() -- Mise à jour des effets temporaires updateEntities() -- Mise à jour générique de toutes les entités updatePlayer() -- Mise à jour du joueur checkCollisions() -- Vérification des collisions -- Dessin de la frame drawMap() drawEntities() drawPlayer() drawUI() -- Appel à la fonction UI refactorisée -- Afficher la console de debug drawDebug() end -- ============================= -- // --------------------------- -- // SECTION 12 : Fonctions Utilitaires -- // --------------------------- -- UI Utils function drawIconWithText(iconSprite, iconX, iconY, text, textXOffset, textYOffset, textColor) spr(iconSprite, iconX, iconY, colorPalette.transparent) print(text, iconX + textXOffset, iconY + textYOffset, textColor, false, 1, true) end -- Gestion du score function addScoreForMovement(yCoord) -- Vérifier si la tuile en Y n'a pas été visitée et que le mouvement est vers le haut if not player.visitedTiles[yCoord] then player.visitedTiles[yCoord] = true local prestigeMultiplier = 1 + (player.prestigeLevel * 0.1) local scoreIncrease = 10 * prestigeMultiplier gameState.score = gameState.score + math.floor(scoreIncrease) addDebugMessage("Score augmenté de " .. math.floor(scoreIncrease) .. " pour un mouvement vers une nouvelle tuile en haut", colorPalette.lightGreen, "Score") end end function addScoreForTimeRemaining(timeLeft) local bonus = math.floor(timeLeft * 10) gameState.score = gameState.score + bonus addDebugMessage("Bonus de temps restant ajouté : " .. bonus .. " points", colorPalette.lightGreen, "Score") end function addScoreForFragment() local prestigeMultiplier = 1 + player.prestigeLevel * 0.1 local scoreIncrease = 100 * prestigeMultiplier gameState.score = gameState.score + math.floor(scoreIncrease) addDebugMessage("Score augmenté de " .. math.floor(scoreIncrease) .. " pour la collecte d'un fragment", colorPalette.yellow, "Score") if player.fragmentsCollected >= gameState.requiredFragments then local bonus = 500 * prestigeMultiplier gameState.score = gameState.score + math.floor(bonus) addDebugMessage("Bonus de " .. math.floor(bonus) .. " points pour avoir collecté tous les fragments", colorPalette.lightGreen, "Score") end end function addScoreForCollectible() gameState.score = gameState.score + 150 addDebugMessage("Score augmenté de 150 pour la collecte d'un collectible bonus", colorPalette.yellow, "Score") end -- Timers et gestion du temps function updateLevelTimer() if gameState.gameOver then return end local level = getCurrentLevel() -- Ignorer le timer pour les niveaux comme le Dev Ground if level.title == "Dev Ground" then return end -- Le reste de la fonction reste inchangé local deltaTime = 1 / FRAME_RATE if gameState.timeLeft > 0 then gameState.timeLeft = gameState.timeLeft - deltaTime end if gameState.timeLeft <= 0 then gameState.timeLeft = 0 handleTimeOut() end end -- Autres fonctions utilitaires function collides(entity1, entity2) return entity1.x < entity2.x + entity2.width and entity2.x < entity1.x + entity1.width and entity1.y < entity2.y + entity2.height and entity2.y < entity1.y + entity1.height end function isPathBlocked(entity, dx, dy) local futureX = entity.x + dx local futureY = entity.y + dy local futureX2 = futureX + entity.width - 1 local futureY2 = futureY + entity.height - 1 local tileXStart = math.floor(futureX / TILE_SIZE) local tileYStart = math.floor(futureY / TILE_SIZE) local tileXEnd = math.floor(futureX2 / TILE_SIZE) local tileYEnd = math.floor(futureY2 / TILE_SIZE) for ty = tileYStart, tileYEnd do for tx = tileXStart, tileXEnd do local tileX = tx + currentMapX local tileY = ty + currentMapY local tile = mget(tileX, tileY) if fget(tile, flagsConfig.BLOCKING_FLAG) then return true end end end return false end function attemptMove(entity, dx, dy) if isPathBlocked(entity, dx, dy) then return false -- Déplacement impossible else entity.x = entity.x + dx entity.y = entity.y + dy return true -- Déplacement réussi end end function getTileType(playerX, playerY) return "undefined" -- Valeur temporaire pour éviter les erreurs end function despawnWorm(wormHead) -- Supprimer chaque segment du worm for _, segment in ipairs(wormHead.segments) do for i, entity in ipairs(entities) do if entity == segment then table.remove(entities, i) break end end end -- Supprimer la tête du worm for i, entity in ipairs(entities) do if entity == wormHead then table.remove(entities, i) break end end end function despawnEntity(entity) for i, e in ipairs(entities) do if e == entity then table.remove(entities, i) entity.removed = true -- Marquer l'entité comme supprimée break end end end init()