Programmes intégrés

minecraft
Minecraft 2D dans Minecraft
Tapez ceci à l'invite d'un ordinateur pour le copier sur le support :
> examples minecraft > edit minecraft

Le programme
-- Minecraft dans Minecraft : un monde à explorer, miner et bâtir, dessiné avec gfx.
-- Clic gauche : casser un bloc. Clic droit : poser le bloc choisi.
-- Sur un moniteur (un seul clic) : la pioche casse, un bloc se pose.
-- Le ciel suit l'heure de votre monde : la nuit tombe ici aussi.
local size = gfx.size()
local PW = size.w
local PH = size.h
local T = 8 -- un bloc fait 8 x 8 pixels
local TW = PW // T -- blocs sur la largeur de l'écran
local WH = (PH - 14) // T -- lignes de blocs ; la barre d'objets est dessous
local WW = TW * 4 -- le monde fait quatre écrans de large
local HUD = WH * T + 1 -- première ligne de pixels de la barre
local AIR = 0
local GRASS = 1
local DIRT = 2
local STONE = 3
local SAND = 4
local WOOD = 5
local LEAVES = 6
local PLANKS = 7
local WATER = 8
local COAL = 9
local IRON = 10
local DIAMOND = 11
local BEDROCK = 12
-- Textures : un chiffre hexa par pixel, les couleurs de term.colors : 0 blanc,
-- 1 orange, 4 jaune, 5 vert clair, 6 rose, 7 gris, 8 gris clair, 9 cyan,
-- b bleu, c marron, d vert, f noir. "." laisse voir le ciel.
local TEX = {}
TEX[GRASS] = {"55d5555d", "5ddd5dd5", "dcdcddcd", "cc1ccccc", "ccccc1cc", "c1cccccc", "cccc1ccc", "ccccccc1"}
TEX[DIRT] = {"cc1ccccc", "ccccc1cc", "c1ccccc7", "ccc7cccc", "cccccc1c", "c1cccccc", "cccc1ccc", "7ccccc1c"}
TEX[STONE] = {"87778777", "77787787", "78777777", "77777877", "77877778", "87778777", "77777787", "78777777"}
TEX[SAND] = {"44404444", "44444414", "40444444", "44441444", "44444404", "41444444", "44440444", "44444441"}
TEX[WOOD] = {"cfcccfcc", "cfcccfcc", "ccfccfcc", "ccfcccfc", "cfccccfc", "cfcccfcc", "ccfccfcc", "ccfcccfc"}
TEX[LEAVES] = {"d5dd.dd5", "dd5ddd.d", "5dd.d5dd", "dd5dddd.", ".ddd5d5d", "d5d.ddd5", "dd5dd5dd", "5d.dd.dd"}
TEX[PLANKS] = {"11111111", "1111c111", "cccccccc", "11c11111", "11111111", "111111c1", "cccccccc", "1c111111"}
TEX[WATER] = {"bbbbbbbb", "b33bbb3b", "bbbbbbbb", "bbb33bbb", "bbbbbbbb", "33bbbb33", "bbbbbbbb", "bbb3bbbb"}
TEX[COAL] = {"87778777", "7ff77787", "7fff7777", "777778f7", "778777ff", "8777f777", "77fff787", "78777777"}
TEX[IRON] = {"87778777", "76677787", "77167777", "77777667", "77877716", "86777777", "71677787", "78777777"}
TEX[DIAMOND] = {"87778777", "79977787", "77097777", "77777997", "77877709", "89777777", "70977787", "78777777"}
TEX[BEDROCK] = {"f7ff7f8f", "7ff7fff7", "ff8f7fff", "7fff7f7f", "f7f8ff7f", "ff7fff7f", "7fff8fff", "ff7f7ff7"}
local STARS = {"ffffffff", "ffffff0f", "ffffffff", "ff8fffff", "ffffffff", "fffff8ff", "f0ffffff", "ffffffff"}
local PICKAXE = {".8888...", "8...88..", "....c8..", "...c..8.", "..c....8", ".c......", "c.......", "........"}
local STEVE = {
"cccccccc", "cccccccc", "c666666c", "66666666", "60f66f06", "66666666", "666cc666", "66666666",
"99999999", "69999996", "69999996", "69999996", ".bbbbbb.", ".bbbbbb.", ".bb..bb.", ".77..77.",
}
local SLOTS = {0, DIRT, STONE, SAND, WOOD, PLANKS, LEAVES} -- 0 est la pioche
local ORES = {COAL, IRON, DIAMOND}
local world = {}
local top = {} -- ligne de la surface de chaque colonne
local tree = {} -- colonnes avec un arbre
local made = {} -- colonnes dont les blocs existent
local sea = 0
local inv = {}
local slot = 1
local px = 0
local py = 0 -- les pieds ; la tête est le bloc au-dessus
local cx = 0 -- l'écran montre les colonnes cx + 1 à cx + TW
local jump = 0
local hang = 0
local night = false
-- La forme du monde est décidée d'un coup : la hauteur de chaque colonne
-- (des collines, et des plages sous le niveau de la mer) et où poussent les arbres.
local function plan()
sea = math.floor(WH * 0.6)
local s = math.floor(WH * 0.45)
local last = -10
for x = 1, WW do
if math.random(3) == 1 then
s = s + math.random(-1, 1)
end
s = math.max(math.floor(WH * 0.3), math.min(WH - 4, s))
top[x] = s
if x >= 3 and x <= WW - 2 and s < sea and s >= 6 and x - last >= 5 and math.random(4) == 1 then
tree[x] = true
last = x
end
end
end
-- Les blocs d'une colonne ne sont faits que la première fois qu'on en a besoin,
-- comme les chunks de Minecraft : le jeu démarre tout de suite, quelle que soit la taille du monde.
local function make(x)
made[x] = true
local s = top[x]
local i = x
for y = 1, WH do
local id = AIR
if y == WH then
id = BEDROCK
elseif y < s then
if y >= sea then
id = WATER
end
elseif y <= s + 2 then
if s >= sea then
id = SAND
elseif y == s then
id = GRASS
else
id = DIRT
end
else
id = STONE
local r = math.random(100)
if r <= 6 then
id = COAL
elseif r <= 9 then
id = IRON
elseif r >= 98 and y >= WH - 3 then
id = DIAMOND
end
end
world[i] = id
i = i + WW -- la même colonne, une ligne plus bas
end
-- le tronc de son arbre, et les feuilles des arbres voisins
for c = x - 1, x + 1 do
if tree[c] then
local ts = top[c]
for k = 3, 4 do
if world[(ts - k - 1) * WW + x] == AIR then
world[(ts - k - 1) * WW + x] = LEAVES
end
end
if c == x then
for k = 1, 3 do
world[(ts - k - 1) * WW + x] = WOOD
end
world[(ts - 6) * WW + x] = LEAVES
end
end
end
end
local function get(x, y)
if x < 1 or x > WW or y > WH then
return BEDROCK
end
if y < 1 then
return AIR
end
if not made[x] then
make(x)
end
return world[(y - 1) * WW + x]
end
local function set(x, y, id)
world[(y - 1) * WW + x] = id
end
-- Le joueur ne passe que dans l'air et l'eau.
local function free(x, y)
local id = get(x, y)
return y >= 1 and (id == AIR or id == WATER)
end
-- Dessine un bloc du monde s'il est à l'écran : le ciel d'abord, puis sa texture.
local function drawTile(x, y)
local sx = x - cx
if sx < 1 or sx > TW or y < 1 or y > WH then
return
end
local left = (sx - 1) * T + 1
local up = (y - 1) * T + 1
local id = get(x, y)
if id == AIR or id == LEAVES then
if night then
gfx.image(left, up, STARS)
else
gfx.rect(left, up, T, T, "light_blue", true)
end
end
if id ~= AIR then
gfx.image(left, up, TEX[id])
end
end
local function drawPlayer()
gfx.image((px - cx - 1) * T + 1, (py - 2) * T + 1, STEVE)
end
-- Une colonne entière d'un coup : un rectangle de ciel, puis seulement les blocs qui ne sont pas de l'air.
local function drawColumn(x)
local sx = x - cx
if sx < 1 or sx > TW then
return
end
if not made[x] then
make(x)
end
local left = (sx - 1) * T + 1
if night then
gfx.rect(left, 1, T, WH * T, "black", true)
else
gfx.rect(left, 1, T, WH * T, "light_blue", true)
end
local i = x
for y = 1, WH do
local id = world[i]
if id ~= AIR then
gfx.image(left, (y - 1) * T + 1, TEX[id])
elseif night then
gfx.image(left, (y - 1) * T + 1, STARS)
end
i = i + WW -- une ligne plus bas
end
end
local function drawWorld()
for sx = 1, TW do
drawColumn(cx + sx)
end
drawPlayer()
end
-- Déplace le joueur. Près d'un bord le monde glisse d'un bloc : gfx.scroll
-- décale l'image, puis seule la nouvelle colonne est dessinée (rapide).
local function moveTo(x, y)
local ox = px
local oy = py
px = x
py = y
local d = 0
if px - cx <= TW // 3 and cx > 0 then
d = -1
elseif px - cx > TW - TW // 3 and cx < WW - TW then
d = 1
end
if d ~= 0 then
cx = cx + d
gfx.scroll(-d * T, 0, 1, 1, TW * T, WH * T)
if d > 0 then
drawColumn(cx + TW)
else
drawColumn(cx + 1)
end
end
drawTile(ox, oy)
drawTile(ox, oy - 1)
drawPlayer()
end
local function onGround()
return not free(px, py + 1)
end
local function walk(dx)
local x = px + dx
if free(x, py) and free(x, py - 1) then
moveTo(x, py)
elseif onGround() and free(x, py - 1) and free(x, py - 2) and free(px, py - 2) then
moveTo(x, py - 1) -- monte d'un bloc, comme le saut automatique
end
end
local function tryJump()
if onGround() or get(px, py) == WATER then
jump = 1
end
end
-- Toutes les 0,15 s : saut, gravité, et jour ou nuit.
local function physics()
if jump > 0 then
jump = jump - 1
if free(px, py - 2) then
moveTo(px, py - 1)
end
hang = 1 -- un instant en haut, pour atterrir sur un bloc
elseif hang > 0 then
hang = hang - 1
elseif free(px, py + 1) then
moveTo(px, py + 1)
if get(px, py) == WATER then
hang = 1 -- coule lentement dans l'eau
end
end
local t = os.day_time()
local dark = t >= 13000 and t < 23000
if dark ~= night then
night = dark
drawWorld()
end
end
-- Le sable tombe, comme dans Minecraft.
local function settle(x, y)
while get(x, y) == SAND and y < WH and free(x, y + 1) and not (x == px and y + 1 >= py - 1 and y + 1 <= py) do
set(x, y + 1, SAND)
set(x, y, AIR)
drawTile(x, y)
drawTile(x, y + 1)
y = y + 1
end
end
local function reach(x, y)
return math.abs(x - px) <= 4 and y >= py - 5 and y <= py + 3
end
local function count(x, n)
gfx.rect(x, HUD + 6, 8, 5, "gray", true)
gfx.text(x, HUD + 6, tostring(math.min(n, 99)), "white")
end
local function drawSlot(i)
local x = 2 + (i - 1) * 22
local id = SLOTS[i]
gfx.rect(x, HUD + 2, 12, 12, "black", true)
if i == slot then
gfx.rect(x, HUD + 2, 12, 12, "white")
end
if id == 0 then
gfx.image(x + 2, HUD + 4, PICKAXE)
else
gfx.image(x + 2, HUD + 4, TEX[id])
count(x + 13, inv[id])
end
end
local function drawHud()
gfx.rect(1, HUD, PW, PH - HUD + 1, "gray", true)
for i = 1, #SLOTS do
drawSlot(i)
end
for i = 1, #ORES do
local x = 2 + #SLOTS * 22 + (i - 1) * 22
gfx.image(x, HUD + 4, TEX[ORES[i]])
count(x + 10, inv[ORES[i]])
end
end
local function mine(x, y)
local id = get(x, y)
if not reach(x, y) or id == AIR or id == WATER or id == BEDROCK then
return
end
local item = id
if id == GRASS then
item = DIRT -- l'herbe donne de la terre
end
inv[item] = inv[item] + 1
set(x, y, AIR)
drawTile(x, y)
local k = y - 1
while k >= 1 and get(x, k) == SAND do
settle(x, k)
k = k - 1
end
drawHud()
end
local function place(x, y)
local id = SLOTS[slot]
if id == 0 or inv[id] < 1 or not reach(x, y) or not free(x, y) then
return
end
if x == px and (y == py or y == py - 1) then
return
end
inv[id] = inv[id] - 1
set(x, y, id)
drawTile(x, y)
settle(x, y)
drawSlot(slot)
end
local function choose(i)
local old = slot
slot = i
drawSlot(old)
drawSlot(slot)
end
-- C change un bois en quatre planches, comme la grille d'artisanat.
local function craft()
if inv[WOOD] > 0 then
inv[WOOD] = inv[WOOD] - 1
inv[PLANKS] = inv[PLANKS] + 4
drawHud()
end
end
local function onKey(e)
if e.name == "key" then
if e.key == "left" then
walk(-1)
elseif e.key == "right" then
walk(1)
elseif e.key == "up" then
tryJump()
end
return
end
local ch = string.lower(e.char)
if ch == "q" or ch == "a" then
walk(-1)
elseif ch == "d" then
walk(1)
elseif ch == " " or ch == "z" or ch == "w" then
tryJump()
elseif ch == "c" then
craft()
else
local n = tonumber(ch)
if n and n >= 1 and n <= #SLOTS then
choose(n)
end
end
end
-- e.px, e.py : le pixel cliqué ; e.button : 1 gauche, 2 droit.
local function onClick(e)
if e.py >= HUD then
local i = (e.px - 2) // 22 + 1
if i >= 1 and i <= #SLOTS then
choose(i)
end
return
end
local x = cx + (e.px - 1) // T + 1
local y = (e.py - 1) // T + 1
if e.button == 2 or (e.source == "monitor" and SLOTS[slot] ~= 0) then
place(x, y)
else
mine(x, y)
end
end
-- Démarre sur l'herbe la plus proche du milieu du monde.
local function spawn()
local mid = WW // 2
local d = 0
while d < mid - 1 and get(mid + d, top[mid + d]) ~= GRASS and get(mid - d, top[mid - d]) ~= GRASS do
d = d + 1
end
px = mid - d
if get(mid + d, top[mid + d]) == GRASS then
px = mid + d
end
py = top[px] - 1
while py > 2 and not (free(px, py) and free(px, py - 1)) do
py = py - 1 -- sur l'arbre s'il y en a un
end
cx = math.max(0, math.min(WW - TW, px - TW // 2))
end
-- L'écran titre : un fond de terre et un logo en pixels.
term.set_bg(term.colors.black)
term.clear()
for y = 1, PH, T do
for x = 1, PW, T do
gfx.image(x, y, TEX[DIRT])
end
end
local logo = (PW - 105) // 2
gfx.text(logo + 2, 8, "MINECRAFT", "black", 3)
gfx.text(logo, 6, "MINECRAFT", "light_gray", 3)
gfx.text(logo + 70, 25, "ALSO IN 2D!", "yellow")
gfx.rect(1, 37, PW, 63, "black", true)
term.set_fg(term.colors.white)
term.set_cursor(2, 5)
term.write("Flèches, Q ou D : marcher")
term.set_cursor(2, 6)
term.write("Espace ou Z : sauter")
term.set_cursor(2, 7)
term.write("Clic gauche : casser un bloc")
term.set_cursor(2, 8)
term.write("Clic droit : poser le bloc choisi")
term.set_cursor(2, 9)
term.write("1 à 7 : choisir, C : planches")
term.set_cursor(2, 10)
term.set_fg(term.colors.yellow)
term.write("Une touche ou un clic pour jouer")
local started = false
while not started do
local e = os.pull_event()
started = e.name == "key" or e.name == "char" or e.name == "click"
end
term.clear()
term.set_cursor(2, 2)
term.write("Création du monde...")
plan()
term.clear()
for id = 0, BEDROCK do
inv[id] = 0
end
spawn()
gfx.clear("black")
drawWorld()
drawHud()
os.start_timer(0.15)
while true do
local e = os.pull_event()
if e.name == "timer" then
physics()
os.start_timer(0.15)
elseif e.name == "key" or e.name == "char" then
onKey(e)
elseif e.name == "click" then
onClick(e)
end
end