# title: game title # author: game developer, email, etc. # desc: short description # site: website link # license: MIT License (change this to your license of choice) # version: 0.1 # script: ruby # constants TILE_SIZE = 16 PLAYER_SIZE = 12 BOMB_TIMER = 90 EXPLOSION_TIMER = 30 EMPTY = 0 SOLID_WALL = 1 BREAKABLE_WALL = 2 # player (grid position and pixel position for animation) $player = { gridX: 1, gridY: 1, pixelX: 16, pixelY: 16, moving: false, maxBombs: 1, activeBombs: 0 } # powerups (extra bombs hidden under breakable walls) $powerups = [] # game objects $bombs = [] $explosions = [] # enemy (grid position and pixel position for animation) $enemy = { gridX: 13, gridY: 7, pixelX: 208, pixelY: 112, moving: false, moveTimer: 0 } # animation speed (pixels per frame) MOVE_SPEED = 2 # 1=solid wall, 2=breakable wall $map = [ [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1], [1,0,0,2,2,2,0,2,0,2,2,2,0,0,1], [1,0,1,2,1,2,1,2,1,2,1,2,1,0,1], [1,2,2,2,0,2,2,0,2,2,0,2,2,2,1], [1,2,1,0,1,0,1,0,1,0,1,0,1,2,1], [1,2,2,2,0,2,2,0,2,2,0,2,2,2,1], [1,0,1,2,1,2,1,2,1,2,1,2,1,0,1], [1,0,0,2,2,2,0,2,0,2,2,2,0,0,1], [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1] ] def init_powerups $powerups = [] # find all breakable walls and randomly place powerups under some (0..8).each do |row| (0..14).each do |col| if $map[row][col] == BREAKABLE_WALL && rand < 0.3 $powerups << { gridX: col, gridY: row, type: :bomb } end end end end init_powerups def TIC cls(0) # handle player movement update_player # place bomb (only if we have available bombs) if btnp(4) && $player[:activeBombs] < $player[:maxBombs] bombX = $player[:gridX] * TILE_SIZE bombY = $player[:gridY] * TILE_SIZE # check if there's already a bomb at this position already_bomb = $bombs.any? { |b| b[:x] == bombX && b[:y] == bombY } unless already_bomb $bombs << { x: bombX, y: bombY, timer: BOMB_TIMER } $player[:activeBombs] += 1 end end # update bombs $bombs.reverse_each do |bomb| bomb[:timer] -= 1 if bomb[:timer] <= 0 explode(bomb[:x], bomb[:y]) $bombs.delete(bomb) $player[:activeBombs] -= 1 end end # update explosions $explosions.reverse_each do |expl| expl[:timer] -= 1 $explosions.delete(expl) if expl[:timer] <= 0 end # draw map (0..8).each do |row| (0..14).each do |col| tile = $map[row][col] drawX = col * TILE_SIZE drawY = row * TILE_SIZE if tile == SOLID_WALL rect(drawX, drawY, TILE_SIZE, TILE_SIZE, 8) elsif tile == BREAKABLE_WALL rect(drawX, drawY, TILE_SIZE, TILE_SIZE, 4) end end end # draw powerups (only visible when wall is destroyed) $powerups.each do |pw| if $map[pw[:gridY]][pw[:gridX]] == EMPTY drawX = pw[:gridX] * TILE_SIZE drawY = pw[:gridY] * TILE_SIZE # draw bomb powerup as small circle with B rect(drawX + 3, drawY + 3, 10, 10, 6) print("B", drawX + 5, drawY + 5, 0) end end # check powerup pickup $powerups.reverse_each do |pw| if $map[pw[:gridY]][pw[:gridX]] == EMPTY && $player[:gridX] == pw[:gridX] && $player[:gridY] == pw[:gridY] $player[:maxBombs] += 1 $powerups.delete(pw) end end # draw bombs $bombs.each do |bomb| circ(bomb[:x] + 8, bomb[:y] + 8, 6, 2) end # draw explosions $explosions.each do |expl| rect(expl[:x], expl[:y], TILE_SIZE, TILE_SIZE, 6) end # update enemy update_enemy # draw player (centered in tile) rect($player[:pixelX] + 2, $player[:pixelY] + 2, PLAYER_SIZE, PLAYER_SIZE, 12) # draw enemy (centered in tile) rect($enemy[:pixelX] + 2, $enemy[:pixelY] + 2, PLAYER_SIZE, PLAYER_SIZE, 2) # check player death by explosion (grid-based) $explosions.each do |expl| explGridX = (expl[:x] / TILE_SIZE).floor explGridY = (expl[:y] / TILE_SIZE).floor if $player[:gridX] == explGridX && $player[:gridY] == explGridY reset_player end end # check player death by enemy (grid-based) if $player[:gridX] == $enemy[:gridX] && $player[:gridY] == $enemy[:gridY] reset_player end # check enemy death by explosion (grid-based) $explosions.each do |expl| explGridX = (expl[:x] / TILE_SIZE).floor explGridY = (expl[:y] / TILE_SIZE).floor if $enemy[:gridX] == explGridX && $enemy[:gridY] == explGridY reset_enemy end end print("ARROWS:MOVE A:BOMB", 50, 2, 15) available = $player[:maxBombs] - $player[:activeBombs] print("BOMBS:#{available}/#{$player[:maxBombs]}", 170, 2, 11) end def update_player targetX = $player[:gridX] * TILE_SIZE targetY = $player[:gridY] * TILE_SIZE # animate toward target position if $player[:pixelX] < targetX $player[:pixelX] = [$player[:pixelX] + MOVE_SPEED, targetX].min $player[:moving] = true elsif $player[:pixelX] > targetX $player[:pixelX] = [$player[:pixelX] - MOVE_SPEED, targetX].max $player[:moving] = true elsif $player[:pixelY] < targetY $player[:pixelY] = [$player[:pixelY] + MOVE_SPEED, targetY].min $player[:moving] = true elsif $player[:pixelY] > targetY $player[:pixelY] = [$player[:pixelY] - MOVE_SPEED, targetY].max $player[:moving] = true else $player[:moving] = false end # only accept input when not moving return if $player[:moving] newGridX = $player[:gridX] newGridY = $player[:gridY] if btn(0) newGridY = $player[:gridY] - 1 elsif btn(1) newGridY = $player[:gridY] + 1 elsif btn(2) newGridX = $player[:gridX] - 1 elsif btn(3) newGridX = $player[:gridX] + 1 end # check if new grid position is valid if can_move_to?(newGridX, newGridY) $player[:gridX] = newGridX $player[:gridY] = newGridY end end def update_enemy targetX = $enemy[:gridX] * TILE_SIZE targetY = $enemy[:gridY] * TILE_SIZE # animate toward target position if $enemy[:pixelX] < targetX $enemy[:pixelX] = [$enemy[:pixelX] + MOVE_SPEED, targetX].min $enemy[:moving] = true elsif $enemy[:pixelX] > targetX $enemy[:pixelX] = [$enemy[:pixelX] - MOVE_SPEED, targetX].max $enemy[:moving] = true elsif $enemy[:pixelY] < targetY $enemy[:pixelY] = [$enemy[:pixelY] + MOVE_SPEED, targetY].min $enemy[:moving] = true elsif $enemy[:pixelY] > targetY $enemy[:pixelY] = [$enemy[:pixelY] - MOVE_SPEED, targetY].max $enemy[:moving] = true else $enemy[:moving] = false end # only move when animation is complete return if $enemy[:moving] $enemy[:moveTimer] += 1 return if $enemy[:moveTimer] < 30 $enemy[:moveTimer] = 0 # pick random direction dirs = [[0, -1], [0, 1], [-1, 0], [1, 0]] dir = rand(4) newGridX = $enemy[:gridX] + dirs[dir][0] newGridY = $enemy[:gridY] + dirs[dir][1] if can_move_to?(newGridX, newGridY) $enemy[:gridX] = newGridX $enemy[:gridY] = newGridY end end def can_move_to?(gridX, gridY) return false if gridX < 0 || gridY < 0 || gridX > 14 || gridY > 8 return false if $map[gridY][gridX] >= SOLID_WALL true end def reset_player $player[:gridX] = 1 $player[:gridY] = 1 $player[:pixelX] = 16 $player[:pixelY] = 16 $player[:moving] = false $player[:maxBombs] = 1 $player[:activeBombs] = 0 end def reset_enemy $enemy[:gridX] = 13 $enemy[:gridY] = 7 $enemy[:pixelX] = 208 $enemy[:pixelY] = 112 $enemy[:moving] = false end def explode(bombX, bombY) $explosions << { x: bombX, y: bombY, timer: EXPLOSION_TIMER } # horizontal explosion [-1, 1].each do |dir| explX = bombX + dir * TILE_SIZE gridX = (explX / TILE_SIZE).floor gridY = (bombY / TILE_SIZE).floor if gridX >= 0 && gridX <= 14 tile = $map[gridY][gridX] if tile == EMPTY $explosions << { x: explX, y: bombY, timer: EXPLOSION_TIMER } elsif tile == BREAKABLE_WALL $map[gridY][gridX] = EMPTY $explosions << { x: explX, y: bombY, timer: EXPLOSION_TIMER } end end end # vertical explosion [-1, 1].each do |dir| explY = bombY + dir * TILE_SIZE gridX = (bombX / TILE_SIZE).floor gridY = (explY / TILE_SIZE).floor if gridY >= 0 && gridY <= 8 tile = $map[gridY][gridX] if tile == EMPTY $explosions << { x: bombX, y: explY, timer: EXPLOSION_TIMER } elsif tile == BREAKABLE_WALL $map[gridY][gridX] = EMPTY $explosions << { x: bombX, y: explY, timer: EXPLOSION_TIMER } end end end end # # 001:eccccccccc888888caaaaaaaca888888cacccccccacc0ccccacc0ccccacc0ccc # 002:ccccceee8888cceeaaaa0cee888a0ceeccca0ccc0cca0c0c0cca0c0c0cca0c0c # 003:eccccccccc888888caaaaaaaca888888cacccccccacccccccacc0ccccacc0ccc # 004:ccccceee8888cceeaaaa0cee888a0ceeccca0cccccca0c0c0cca0c0c0cca0c0c # 017:cacccccccaaaaaaacaaacaaacaaaaccccaaaaaaac8888888cc000cccecccccec # 018:ccca00ccaaaa0ccecaaa0ceeaaaa0ceeaaaa0cee8888ccee000cceeecccceeee # 019:cacccccccaaaaaaacaaacaaacaaaaccccaaaaaaac8888888cc000cccecccccec # 020:ccca00ccaaaa0ccecaaa0ceeaaaa0ceeaaaa0cee8888ccee000cceeecccceeee # # # 000:00000000ffffffff00000000ffffffff # 001:0123456789abcdeffedcba9876543210 # 002:0123456789abcdef0123456789abcdef # # # 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000 # # # 000:100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 # # # 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57 #