update PicoCalc SD

This commit is contained in:
cuu
2026-03-01 15:25:55 +08:00
parent c1b8f3fc3e
commit d5c0a8531a
101 changed files with 4913 additions and 58 deletions

View File

@@ -0,0 +1,260 @@
-- asteroids game
-- written by maple "mavica" syrup <maple@maple.pet>
local poly_ship = {{0, -10}, {5, 5}, {0, 0}, {-5, 5}, {0, -10}}
local poly_thrust = {{-2.5, 2,5}, {0, 4}, {2.5, 2.5}}
local poly_shot = {{0, 0}, {0, -5}}
local poly_rock = {{-1,0},{-0.8,-0.6},{-0.4,-0.5},
{0,-1},{0.5,-0.9},{0.3,-0.4},{0.8,-0.5},
{1,0},{0.7,0.4},{0.7,0.6},{0.3,0.8},{0,0.7},
{-0.3,0.9},{-0.7,0.8},{-0.7,0.5},{-1,0}}
local bg = colors.fromRGB(0,0,0)
local fg = colors.fromRGB(0,255,0)
local width = 320
local height = 320
local max_shots = 5
local shot_speed = 7
local rock_speed = 0.6
local rock_size = 30
local rock_step = 10
local player = {
x=width/2,
y=height/2,
accel = 0.06,
vx=0,
vy=0,
ang=0,
cdown=0,
inv=0
}
local shots = {}
local rocks = {}
local score = 0
local lives = 3
local last_frame = 0
local game_over = false
local function sign(number)
return (number > 0 and 1) or (number == 0 and 0) or -1
end
local function distance(x1, y1, x2, y2)
return math.sqrt((x2-x1)^2+(y2-y1)^2)
end
local function draw_rotated(shape, x, y, ang, scale, color)
processed = {}
local sin = math.sin(ang) * scale
local cos = math.cos(ang) * scale
for _,v in pairs(shape) do
local px = x + v[1] * cos - v[2] * sin
local py = y + v[1] * sin + v[2] * cos
table.insert(processed,math.floor(px+0.5))
table.insert(processed,math.floor(py+0.5))
end
draw.polygon(processed, color)
end
local shot_meta = {
erase = function(self)
draw_rotated(poly_shot, self.x, self.y, self.ang, 1, bg)
end,
update = function(self, k)
self.x = self.x + self.vx
self.y = self.y + self.vy
if self.x > width or self.x < 0 or self.y > height or self.y < 0 then
self:erase()
table.remove(shots,k)
else
for rockid,rock in pairs(rocks) do
if distance(self.x, self.y, rock.x, rock.y) < rock.scale then
rock:hit(rockid,self,k)
end
end
end
end,
draw = function(self)
draw_rotated(poly_shot, self.x, self.y, self.ang, 1, fg)
end
}
local rock_meta = {} --hoist
local rock_create = function(x, y, ang, size)
rock = {
x = x,
y = y,
ang = ang,
vx = math.sin(ang) * rock_speed,
vy = -math.cos(ang) * rock_speed,
scale = size
}
setmetatable(rock, { __index = rock_meta })
table.insert(rocks, rock)
end
local function level_init()
for i=1,4 do
rock_create(math.random(30,width-30), math.random(30,height-30), math.random()*6.28, 30)
end
player.inv = 50
end
rock_meta = {
erase = function(self)
draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, bg)
end,
update = function(self, k)
self.x = (self.x + self.vx) % width
self.y = (self.y + self.vy) % height
if player.inv == 0 and distance(self.x, self.y, player.x, player.y) < self.scale then
player:hit()
end
end,
hit = function(self, k, shot, sk)
sound.playPitch(1, 0.5, sound.drums.snare2)
self:erase()
score = score + ((rock_size + rock_step) - self.scale)
rock_speed = rock_speed + 0.06
if self.scale > rock_step then
rock_create(self.x, self.y, shot.ang - math.random()*3.14, self.scale-rock_step)
rock_create(self.x, self.y, shot.ang + math.random()*3.14, self.scale-rock_step)
end
table.remove(rocks, k)
table.remove(shots, sk)
if #rocks == 0 then
rock_speed = rock_speed - 0.6
score = score + 500
level_init()
end
end,
draw = function(self)
draw_rotated(poly_rock, self.x, self.y, self.ang, self.scale, fg)
end
}
function player:fire()
if self.cdown == 0 and #shots < max_shots then
sound.playPitch(1, 2, sound.drums.tom)
shot = {
x = self.x,
y = self.y,
ang = self.ang,
vx = math.sin(self.ang) * shot_speed,
vy = -math.cos(self.ang) * shot_speed,
}
setmetatable(shot, { __index = shot_meta })
table.insert(shots, shot)
self.cdown = 8
end
end
function player:erase()
draw_rotated(poly_ship, self.x, self.y, self.ang, 1, bg)
draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, bg)
end
function player:update()
local angle_d = (keys.getState(keys.right) and 0.15 or 0) - (keys.getState(keys.left) and 0.15 or 0)
self.ang = self.ang + angle_d
if keys.getState(keys.up) then
self.vx = (self.vx + math.sin(self.ang) * self.accel)
self.vy = (self.vy - math.cos(self.ang) * self.accel)
elseif keys.getState(keys.down) then
self.vx = (self.vx - sign(self.vx) * self.accel)
self.vy = (self.vy - sign(self.vy) * self.accel)
end
self.x = (self.x + self.vx) % width
self.y = (self.y + self.vy) % height
if self.cdown > 0 then
self.cdown = self.cdown - 1
else
if keys.getState(keys.enter) then
self:fire()
end
end
if self.inv > 0 then self.inv = self.inv - 1 end
end
function player:hit()
sound.playPitch(1, 0.4, sound.drums.snare1)
if lives == 0 then game_over = true return end
lives = lives - 1
self.x = width/2
self.y = height/2
self.vx = 0
self.vy = 0
self.ang = 0
self.inv = 50
end
function player:draw()
if math.floor(self.inv / 3) % 2 == 0 then
draw_rotated(poly_ship, self.x, self.y, self.ang, 1, fg)
end
if keys.getState(keys.up) then
draw_rotated(poly_thrust, self.x, self.y, self.ang, 1, fg)
end
end
local function hud_draw()
draw.text(15,15," ",fg,bg)
draw.text(15,15,score,fg,bg)
for i = 0, 3 do
local color = i < lives and fg or bg
draw_rotated(poly_ship, width - 20 - 20*i, 20, 0, 1, color)
end
end
term.clear()
--draw.enableBuffer(true)
draw.rectFill(0, 0, width, height, bg)
level_init()
while true do
if keys.getState(keys.esc) or game_over then break end
local now = os.clock()
if now >= last_frame + 0.05 then
last_frame = now
-- erase
player:erase()
for _,v in pairs(shots) do v:erase() end
for _,v in pairs(rocks) do v:erase() end
-- move player
player:update()
for k,v in pairs(shots) do v:update(k) end
for k,v in pairs(rocks) do v:update(k) end
--draw.clear()
hud_draw()
player:draw()
for _,v in pairs(shots) do v:draw() end
for _,v in pairs(rocks) do v:draw() end
--draw.blitBuffer()
collectgarbage()
end
end
draw.enableBuffer(false)
if game_over then
term.clear()
print("game over!")
print("final score: "..score)
end

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -0,0 +1,448 @@
-- BOXWORLD
-- original game and sprites by Jeng-Long Jiang, 1992
-- picocalc lua conversion by maple "mavica" syrup, 2025
local sprite_size = 20
local sprites = draw.loadBMPSprites("lua/boxworld.bmp", sprite_size, sprite_size)
local fg,bg = colors.fromRGB(255,255,255),colors.fromRGB(0,0,0)
local screen = nil
local quit = false
local function switch_screen(to)
screen = to
if screen.entry then screen.entry() end
end
local music = {
tick = 0.10,
instrument = sound.instrument(sound.presets.square),
notes = {
c = 0,
C = 1,
d = 2,
D = 3,
e = 4,
f = 5,
F = 6,
g = 7,
G = 8,
a = 9,
A = 10,
b = 11
},
tracks = {
intro = {
"l2o3el1<el2gl1el2>cl1<el2>el3dl1c<bafl3>c<bl2al1dl2fl1el2gl1fl2b>c",
"l3o1cga>cel2dl3c<l4bl3fa>cl2<a>c"
},
victory = {
"l2o2el1Gl2al3bl1>dl2ee",
"l3o1aGel1gl2de"
}
},
playing = nil
}
music.instrument.decay = 250
function music.perform()
if not music.playing then return end
local done_playing = true
for k,v in pairs(music.playing) do
local ch = music.channels[k]
local track = music.playing[k]
if ch.cursor <= #track then done_playing = false end
if ch.timer > 0 then ch.timer = ch.timer - 1 end
if ch.timer == 0 then
while ch.cursor <= #track do
local input = string.sub(track, ch.cursor, ch.cursor)
ch.cursor = ch.cursor + 1
if input == 'l' then
input = string.sub(track, ch.cursor, ch.cursor)
ch.cursor = ch.cursor + 1
ch.length = tonumber(input)
elseif input == 'o' then
input = string.sub(track, ch.cursor, ch.cursor)
ch.cursor = ch.cursor + 1
ch.octave = tonumber(input)
elseif input == '<' then
ch.octave = ch.octave - 1
elseif input == '>' then
ch.octave = ch.octave + 1
elseif music.notes[input] then
ch.timer = ch.length
sound.play(k-1, music.notes[input] + (ch.octave+2) * 12, music.instrument)
break
end
end
end
end
if done_playing then music.playing = nil sys.stopTimer() end
end
function music.play(track)
music.playing = track
music.channels = {}
for k,v in pairs(track) do
music.channels[k] = {}
music.channels[k].cursor = 1
music.channels[k].length = 4
music.channels[k].octave = 3
music.channels[k].timer = 0
end
sys.repeatTimer(music.tick * 1000, music.perform)
end
local game = {
level = 1,
levelstrings = {
"00111\n00131\n00121111\n11142431\n13246111\n111141\n000131\n000111\n",
"11111\n16221\n124410111\n124210131\n111211131\n011222231\n012221221\n012221111\n011111\n",
"01111111\n0122222111\n1141112221\n1262422421\n1233124211\n113312221\n011111111\n",
"01111\n11221\n16421\n114211\n112421\n134221\n133531\n111111\n",
"011111\n0162111\n0124221\n11121211\n13121221\n13422121\n13222421\n11111111\n",
"0001111111\n1111222221\n1222311121\n12121222211\n12124241321\n12122522121\n12314242121\n1122221212111\n0121113222261\n0122222112221\n0111111111111\n",
"0001111111\n0011221261\n0012221221\n0014242421\n0012411221\n1112421211\n133333221\n111111111\n",
"000111111\n011122221\n1132411211\n1334242261\n1332424211\n111111221\n000001111\n",
"0111111111\n0122112221\n0122242221\n0142111241\n0121333121\n11213331211\n12422422421\n12222212621\n11111111111\n",
"00111111\n00122221\n11144421\n16243321\n12433311\n1111221\n0001111\n",
"011110011111\n112210012221\n124211114221\n122433332421\n112222126211\n01111111111\n",
"0011111\n1112261\n12243211\n12234321\n11125421\n00122211\n0011111\n",
"001111\n001331\n0112311\n0122431\n11242211\n12214421\n12262221\n11111111\n",
"11111111\n12212221\n12433421\n16435211\n12433421\n12212221\n11111111\n",
"0111111\n11222211\n12424421\n13333331\n12442421\n11126111\n001111\n",
"00111111\n0012222111\n0012422221\n1112421121\n1333242221\n1333414211\n1111212421\n0001226221\n0001111111\n",
"111111\n122221\n1244411\n122133111\n112233421\n012622221\n011111111\n",
"0011111111\n0012221321\n0112243331\n0122421531\n1121141211\n1222422421\n1222122221\n1111111621\n0000001111\n",
"01111111\n01333321\n1113334111\n1224142421\n1244221421\n1222212221\n1111262111\n00011111\n",
"1111111\n1334331\n1331331\n1244421\n1224221\n1244421\n1221621\n1111111\n",
"000111111\n000123331\n111133331\n12211142111\n12424224421\n16242422221\n12221112221\n11111011111\n",
"11111111\n12222221\n12144221\n12333121\n113334211\n012112421\n014224221\n012212261\n011111111\n",
"0011111\n1112221111\n1222424221\n1242224261\n1114411111\n00122331\n00133331\n00111111\n",
"11111100011111\n12222111012231\n12242421013331\n12122421112231\n12244422242631\n11122422412231\n00122414213331\n00112222212231\n00011111111111\n",
"00000111111\n01111132221\n01221331121\n01224332221\n01221231211\n11121141221\n12422224421\n12141221221\n16221111111\n11111\n",
"0111111111\n0122211221111\n0124222222221\n0114111211221\n0122112521211\n012433333321\n112111232121\n122222411141\n122212222461\n111114121111\n000012221\n000011111\n",
"000000111111111\n000000122222221\n000000121212121\n000000122424121\n111111122242221\n133122112424121\n133222112424221\n133122112111111\n133121242421\n133222224221\n122111262111\n1111011111\n",
"00001111\n11111221\n1224242101111111\n1222422101535351\n1124242111353531\n0142422122535351\n0164242222353511\n0142422122535351\n1124242111353531\n1222422101535351\n1224242101111111\n11111221\n00001111\n",
"11111111\n13333331\n122421211\n124212421\n114242421\n012262221\n011111111\n",
"001111111111\n111222322221\n122211411221\n126432323411\n11241141121\n01222232221\n01111111111\n",
"000111111\n111132261\n122444221\n131131131\n122242221\n122431211\n11112221\n00011111\n",
"0111111\n0132331\n0132431\n11122411\n12422421\n12141121\n12226221\n11111111\n",
"0000111111\n001112222111\n00122214222111\n00122242224421\n00124421422221\n00112224222421\n11111121411111\n1336214221\n1313322411\n133334121\n133332221\n111111111\n",
"111111111111111\n122222212222221\n124214212411421\n121224212222221\n122211414114421\n121212333212221\n124223212342121\n124164333121221\n122223212322421\n121134111432121\n121243333321121\n122222222222221\n111111111111111\n",
"111111111\n122211221\n121242421\n122531221\n112136311\n1141115111\n1222222221\n1222112121\n1111112221\n0000011111\n",
"11111111\n12222221\n1244222111\n12242444211111\n112112333222211\n012161333111421\n012124333222221\n112124333421211\n12211111211121\n12222224222421\n11111111111221\n00000000001111\n",
"00011111\n00012621\n00014441\n11112221\n122231411\n124343231\n122131311\n11111111\n",
"111111111111\n133321222221\n133221211221\n133222221221\n133221241121\n133321424221\n111111224421\n011224244221\n016244422121\n011242112221\n001222222221\n001111111111\n",
"111111111\n122222221\n122424241\n112141121\n0123323311\n0113323321\n00121141211\n00142424221\n00122222261\n00111111111\n",
"111110000001111\n162211111111221\n112422222224221\n012121221111221\n012242221111411\n01421121242421\n11242241222221\n12221222222121\n12221111141111\n1111122212221\n0000133322421\n0000133331221\n0000133331111\n0000111111\n",
"0000011111\n0111112221\n0123324121\n0121352221\n1125314211\n124224221\n122211261\n111111111\n",
"1111101111111\n1222111221221\n1242222242621\n1121411311221\n0122333532421\n0124121312121\n0112222422221\n0012211111111\n001111\n",
"000000000111\n0000111111611\n00001333314211\n00001333312421\n00001333324221\n00001233312221\n111111211111211\n124242224221221\n122224422242421\n111242424221111\n001122242421\n000122111111\n0001111\n",
"00011111\n0111222111\n1122642421\n12211211211\n12431342221\n12131512221\n12433322111\n111412111\n0012221\n0011111\n",
"0000001111\n000000122111111\n000000122221221\n000000124422221\n111111141221221\n122132332111411\n122131534222221\n122131351212221\n124433331211111\n12642121121\n12444122221\n12222111111\n111111\n",
"1111\n122111\n12422111\n1242422111\n124242422111\n124242422221\n1242422122211\n1242211244421\n16211112222211\n11210134444431\n012111333333311\n012223555555531\n011113333333331\n000011111111111\n",
"1111111\n12612211111\n12442242221\n12213114121\n11413332221\n11233311411\n12211311221\n12242242221\n12212221221\n11111111111\n",
"00011111\n00012621\n00012421\n00014341\n011134311\n11234343111\n12243434221\n12222322221\n11111111111\n",
"1111111111111\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1242424253331\n1224242435331\n1642424253331\n1111111111111\n",
"00000000000001\n00000000000011\n00000000000111\n000000000012221\n000111111112121\n001242424242221\n01121313131641\n111333333322211\n01121212121411\n001242424242221\n000111111112121\n000000000012221\n00000000000111\n00000000000011\n",
"1111111\n122342111\n123434221\n154343621\n123434211\n12234221\n11111111\n",
"00000000011111\n00000000012221\n1111111111252111\n1222222222232221\n1244445555433361\n1222222222232221\n1111111111252111\n00000000012221\n00000000011111\n",
"000001111\n1111112211111\n1642222422421\n1411124212121\n1221221242221\n1241222212111\n122421412221\n133333333321\n111111112221\n000000011111\n",
"0111111111\n0122211221111\n0124222222221\n0114111211221\n0122113421211\n012433333321\n112111322121\n122222411141\n122212222461\n111114121111\n000012221\n000011111\n",
"11111111111\n12222122221\n12464444421\n12222222221\n11111211111\n00013221\n00013221\n00013331\n00013221\n00011111\n",
"0011111\n0012621\n0012421\n111232111\n122252221\n125555521\n122252221\n111454111\n0012321\n0012521\n0012321\n0011111\n",
"11111111111111\n13222222222221\n13424242424221\n13111111111221\n13135242334511\n1312424253461\n1313224233441\n1311111111131\n1322222222221\n1314141414141\n1322222222221\n1111111111111\n",
"111111111111\n13322122222111\n13322124224221\n13322141111221\n13322226211221\n13322121224211\n11111121142421\n00124224242421\n00122221222221\n00111111111111\n",
"00000001111\n11111111221111\n12221133333221\n12242211333121\n11224221112121\n01212422122221\n01221242212221\n01222124221221\n01222212421211\n0111122124221\n0000112212421\n0000011612221\n0000001111111\n",
"11110000001111\n13311111111331\n15353333353531\n12424242424241\n14242464242421\n12424242424241\n14242424242421\n13535333335351\n13311111111331\n11110000001111\n",
"000011111\n000112221111\n000123352421\n111121312221\n122223531611\n12141141121\n12222242421\n11221222111\n011111111\n",
"111111\n122221\n124221111\n124533521\n125335421\n111122421\n000126221\n000111111\n",
"00011111\n111132211\n124343221\n164121421\n124323221\n111141421\n001323221\n001111111\n",
"111111111111\n122223332421\n124445552461\n122223332421\n111111111111\n",
"1111111111\n1122222221\n1222141421\n1244223431\n1261113331\n1111111111\n",
"01111\n012211111\n114211221\n122464221\n122211421\n1113112111\n0133342421\n0113322221\n0011111111\n",
"1111101111\n1333101221111\n1333111224221\n133331124224111\n113333112224221\n111333211242421\n121122221224221\n1221121211121111\n1242121422422221\n1224262422224221\n1222124244242111\n12211111122111\n121100001111\n111\n",
"1111111\n1323231\n1244421\n1346431\n1244421\n1323231\n1111111\n",
"0000001111\n1111111221\n1222224221\n1222411641\n1141333121\n0124333221\n01213231211\n01222121421\n01422422221\n01221111111\n01111\n",
"000111111111\n000122212221\n000122222221\n111115111211\n12223332221\n121215111411\n124222242221\n111116212221\n000011111111\n",
"11111\n13331011111\n13331112221\n133332224411111\n1333322122122211\n1331411112141221\n1124221222224421\n1224126242441221\n1242424212224211\n122212242112221\n111111222111111\n0000011111\n",
"111111111111111\n111312222221111\n113312422421221\n133312112421221\n133333221442221\n113333422221421\n111121111111221\n122242222222211\n122421224124211\n124111242124421\n122261221122221\n111111111111111\n",
"000000011111111\n000000012212221\n011111112443331\n012222222213331\n012111111413331\n112122222213331\n122121424211111\n12124242421\n12622421221\n11111424421\n00001222221\n00001111111\n",
"000000011111\n001111112221\n111222232421\n124221431411\n122122631221\n112111132221\n012422151111\n01211213221\n01222223121\n01114222221\n00012211111\n0001111\n",
"00001111\n111112211111\n122224222421\n122414112221\n111213532111\n0012333261\n0011214111\n00012221\n00011111\n",
"00011111111\n11112222321\n12242424321\n12231111311\n1243424261\n1223221111\n1111111\n",
"0011111\n0012221\n11143411111\n12223242221\n12114112621\n12223211111\n1112321\n0012221\n0011111\n",
"011111\n01262111111\n01213352221\n01233312221\n11411242421\n12221411111\n122242221\n111112121\n000012221\n000011111\n",
"00000111111\n000111222211\n000122211221\n011141122121\n112222233121\n122414153121\n12446213512111\n12244213312221\n11222213342221\n01114113212111\n000122111221\n000112222211\n00001111111\n",
"000001111111\n000001221221\n000001224421\n111111241221\n1333111212211\n1322122421221\n1322224242421\n1322122421221\n1333111212211\n111111242221\n000001621221\n000001111111\n",
"0000000000111111\n0000000000122221\n1111100011121121\n1332111112422121\n1332222242224121\n1332211211222121\n1332112421424121\n1332122222422121\n1332122421114221\n1332124242242111\n111211212422221\n001222216112421\n001111111112211\n00000000001111\n",
"00000001\n0000011111\n000111262111\n000122424221\n000125353521\n0011234243211\n01112535352111\n111122424221111\n000111111221\n00001100011\n",
"011111111111111\n0126252525212211\n0141225252212221\n0121252525222221\n0121225252211211\n012125252521121\n012122525221121\n012125252521121\n012122525221121\n0121252325211211\n1121111111111221\n1222222222222221\n1222111111111221\n1111100000001111\n",
"01111111111111\n01222212211221\n014442124422411\n012422122333321\n012242214311321\n012212412333311\n114242214311321\n124224264333321\n122211122111111\n1111101111\n",
"00000111111111\n00000122222221\n00000122414121\n01111112212421\n01222124224221\n11242222211121\n12221411112221\n12222421112111\n1111133261211\n0001333424421\n0001333122221\n0001333111111\n00011111\n",
"1111000001111\n122111111133111\n124242422133331\n124222442155531\n124242422133531\n122424242153531\n1124242423535311\n1224242423535361\n1224242421535311\n124242422133531\n124222442155531\n124242422133331\n122111111133111\n1111000001111\n",
"00000011111\n11111112221\n12221132221\n122413322111\n1122333144211111\n0124313422222221\n0124111411212421\n0122212222244121\n0114412114142221\n0133324624222111\n01333141222111\n013332211111\n01111111\n",
"0111100111111\n0122111122221\n1152225255221\n1242522225121\n1232221112221\n1111112221611\n1252325225521\n1222122212221\n1152225214121\n0122111112221\n0111100011111\n",
"0111111111\n0122212221\n0124444421\n1124242421\n1242262221\n12421111211\n12213333321\n11223333321\n01111111111\n",
"0001111111\n0001222221\n00012424211\n000111113311111\n111111335322421\n122464333314421\n122242141112221\n111112222222111\n0000111221111\n0000001221\n0000001111\n",
"11111\n1222111111\n1242332421\n1142334461\n0122332421\n0111111111\n",
"0011111\n001222111111\n111413222221\n124233312421\n162431542221\n111122221111\n000111111\n",
"000011111\n111112221\n122242621\n122421311111\n114211311221111\n012233333241221\n012411311221421\n012221311222221\n011124211111211\n000121422222421\n000122221112221\n000111111011111\n",
"1111111\n1225221\n1263421\n1243221\n1535551\n1225221\n1245421\n1223221\n1111111\n",
"00111111111\n00122221221\n11121422421111\n1224221133122111\n1212242133424221\n1244224133221221\n1122122333142221\n0142621333124221\n0122221333422211\n012114211122211\n01222422222111\n012211111111\n01111\n",
"0001111111111\n1111333333221\n1222333331221\n1221333333211\n112111141141\n16422424222111\n12442222112221\n12122441122121\n12224221244221\n12242242221421\n11112221242421\n00012221222221\n00011111111111\n",
"000000111111111\n000000122222221\n000000142444221\n000000122212421\n000000122246421\n000011142421211\n00001222414121\n11111212212221\n13332212412111\n133333222121\n133333124121\n111111112221\n000000011111\n",
"0111110111111\n0122211122221\n1124242142141\n12242624224211\n12122112133331\n12211242131131\n11224222233331\n01244214133331\n01222122214211\n0111112422221\n0000011112211\n000000001111\n",
"1111\n1221\n1221111111111\n1222211222221\n1331222244121\n133221122242111\n133122114124221\n133222126424221\n133122124242221\n123222124242111\n1221221222111\n12212222111\n111111111\n",
"00000000011111\n000000001222221\n0000000122411221\n0000001221222421\n1001001212212121\n0101001242422421\n0011111112141221\n001224242222221\n00163342553111\n000133333311111\n0000111111111111\n"
},
anim_timer = 0,
player_flip = 0,
player_sprites = {
U = {7, 8},
D = {5, 6},
L = {11, 12},
R = {9, 10},
W = {13, 13}
}
}
game.push_sound = sound.instrument(sound.drums.tom)
game.push_sound.volume = 0.5
game.push_sound.decay = 150
game.push_sound.sustain = 0
local title = {}
function title.input()
local state, _, code = keys.poll()
if state == keys.states.pressed then
if code == keys.left then
game.level = ((game.level - 2) % #game.levelstrings) + 1
screen.redraw = true
elseif code == keys.right then
game.level = ((game.level) % #game.levelstrings) + 1
screen.redraw = true
elseif code == keys.enter then
switch_screen(game)
elseif code == keys.esc then
quit = true
end
end
end
function title.entry()
draw.enableBuffer(false)
music.play(music.tracks.intro)
draw.clear()
screen.redraw = true
end
function title.draw()
-- boxworld logo
for j = 0, 1 do
for i = 0, 6 do
sprites:blit(160 - 70 + i * sprite_size, 80 + j * sprite_size, 14 + i + j * 7)
end
end
draw.text(160, 140, "Original by Jeng-Long Jiang, 1992",fg,bg,draw.align_center)
draw.text(160, 160, "Reprogram by maple \"mavica\" syrup, 2025",fg,bg,draw.align_center)
draw.text(160, 180, "Select level",fg,bg,draw.align_center)
draw.text(160, 190, " < " .. game.level .. " > ",fg,bg,draw.align_center)
end
function game.entry()
-- load level
game.leveltable = {}
local row = {}
local longest = 0
local str = game.levelstrings[game.level]
for i = 1, #str do
if str:sub(i,i) == '\n' then
table.insert(game.leveltable, row)
if #row > longest then longest = #row end
row = {}
elseif str:sub(i,i) == '6' then -- player
game.player_pos = {#row + 1, #game.leveltable + 1}
game.player_direction = "D"
table.insert(row, '2') -- empty floor under player
else
table.insert(row, str:sub(i,i))
end
end
game.draw_offset = {140 - longest/2*sprite_size, 140 - #game.leveltable/2*sprite_size}
game.moves = {}
collectgarbage()
draw.enableBuffer(1)
draw.clear()
screen.redraw = true
end
function game.draw()
for j = 1, #game.leveltable do
local row = game.leveltable[j]
for i = 1, #row do
local pos = {game.draw_offset[1] + i * sprite_size, game.draw_offset[2] + j * sprite_size}
if row[i] == "1" then -- wall
sprites:blit(pos[1], pos[2], 0)
elseif row[i] == "2" then -- floor
sprites:blit(pos[1], pos[2], 1)
elseif row[i] == "3" then -- floor with goal
sprites:blit(pos[1], pos[2], 1)
sprites:blit(pos[1], pos[2], 4)
elseif row[i] == "4" then -- box
sprites:blit(pos[1], pos[2], 2)
elseif row[i] == "5" then -- box on goal
sprites:blit(pos[1], pos[2], 3)
end
end
end
local pos = {
game.draw_offset[1] + game.player_pos[1] * sprite_size,
game.draw_offset[2] + game.player_pos[2] * sprite_size
}
sprites:blit(pos[1], pos[2], game.player_sprites[game.player_direction][game.player_flip+1])
if game.player_direction == "W" then
draw.text(160, 160, "You win !!!",fg,bg,draw.align_center)
draw.text(160, 170, "Press ENTER for next level",fg,bg,draw.align_center)
end
draw.text(0, 300, "Box World! #" .. game.level .. " - Moves: " .. #game.moves .. " ")
draw.text(0, 310, "(U)ndo, (R)estart, (ESC) Quit")
draw.blitBuffer()
end
function game.check_win()
for j = 1, #game.leveltable do
local row = game.leveltable[j]
for i = 1, #row do
if row[i] == "4" then -- box not on goal
return false -- quit as soon as found any
end
end
end
-- didn't quit? must mean we won
game.player_direction = "W"
music.play(music.tracks.victory)
end
function game.process_push(code)
if game.player_direction == "W" then return end -- don't move if we've already won
local ahead, ahead2
if code == keys.up then
game.player_direction = "U"
ahead = {game.player_pos[1], game.player_pos[2]-1}
ahead2 = {game.player_pos[1], game.player_pos[2]-2}
elseif code == keys.down then
game.player_direction = "D"
ahead = {game.player_pos[1], game.player_pos[2]+1}
ahead2 = {game.player_pos[1], game.player_pos[2]+2}
elseif code == keys.left then
game.player_direction = "L"
ahead = {game.player_pos[1]-1, game.player_pos[2]}
ahead2 = {game.player_pos[1]-2, game.player_pos[2]}
elseif code == keys.right then
game.player_direction = "R"
ahead = {game.player_pos[1]+1, game.player_pos[2]}
ahead2 = {game.player_pos[1]+2, game.player_pos[2]}
end
local whats_ahead = game.leveltable[ahead[2]]
if whats_ahead ~= nil then whats_ahead = whats_ahead[ahead[1]] end
local whats_ahead2 = game.leveltable[ahead2[2]]
if whats_ahead2 ~= nil then whats_ahead2 = whats_ahead2[ahead2[1]] end
if whats_ahead == '2' or whats_ahead == '3' then -- ahead is floor or empty goal
game.player_pos = ahead
table.insert(game.moves, string.lower(game.player_direction))
elseif whats_ahead == '4' or whats_ahead == '5' then -- ahead is box, whether or not on a goal
if whats_ahead2 == '2' or whats_ahead2 == '3' then -- box pushed onto floor or goal
sound.playPitch(2,1,game.push_sound)
local box_leaves = whats_ahead == '4' and '2' or '3'
local box_becomes = whats_ahead2 == '2' and '4' or '5'
game.player_pos = ahead
game.leveltable[ahead[2]][ahead[1]] = box_leaves
game.leveltable[ahead2[2]][ahead2[1]] = box_becomes
table.insert(game.moves, game.player_direction)
game.check_win()
end
end
screen.redraw = true
end
function game.process_undo()
if #game.moves < 1 then return false end
local return_to, pushed
local move = table.remove(game.moves)
game.player_direction = string.upper(move)
if string.upper(move) == 'D' then
return_to = {game.player_pos[1], game.player_pos[2]-1}
pushed = {game.player_pos[1], game.player_pos[2]+1}
elseif string.upper(move) == 'U' then
return_to = {game.player_pos[1], game.player_pos[2]+1}
pushed = {game.player_pos[1], game.player_pos[2]-1}
elseif string.upper(move) == 'R' then
return_to = {game.player_pos[1]-1, game.player_pos[2]}
pushed = {game.player_pos[1]+1, game.player_pos[2]}
elseif string.upper(move) == 'L' then
return_to = {game.player_pos[1]+1, game.player_pos[2]}
pushed = {game.player_pos[1]-1, game.player_pos[2]}
end
if move == string.upper(move) then -- if move was recorded upper means we moved a box
local what_box_became = game.leveltable[pushed[2]][pushed[1]]
local box_leaves = what_box_became == '4' and '2' or '3'
local box_becomes = game.leveltable[game.player_pos[2]][game.player_pos[1]] == '2' and '4' or '5'
game.leveltable[pushed[2]][pushed[1]] = box_leaves
game.leveltable[game.player_pos[2]][game.player_pos[1]] = box_becomes
end
game.player_pos = return_to
screen.redraw = true
end
function game.input()
local state, _, code = keys.poll()
if state == keys.states.pressed then
if code == keys.up or code == keys.down or code == keys.left or code == keys.right then
game.process_push(code)
elseif code == 'u' then
game.process_undo()
elseif code == 'r' then
game.entry()
elseif code == keys.esc then
switch_screen(title)
elseif game.player_direction == "W" and code == keys.enter then
-- go to next level when won and pressed enter
game.level = ((game.level) % #game.levelstrings) + 1
game.entry()
end
end
collectgarbage()
end
function game.update(now)
if game.player_direction ~= 'W' and now > game.anim_timer then
game.player_flip = (game.player_flip + 1) % 2
game.anim_timer = now + 1
screen.redraw = true
end
end
switch_screen(title)
while true do
now = os.clock()
if screen.input then screen.input() end
if quit then break end
if screen.update then screen.update(now) end
if screen.redraw and screen.draw then screen.draw() screen.redraw = false end
end

View File

@@ -0,0 +1,74 @@
local current_dir = "/"
local cursor_y = 1
local width, height = term.getSize()
function string:endswith(suffix)
return self:sub(-#suffix) == suffix
end
term.clear()
while true do
local dir = fs.list(current_dir)
term.setCursorPos(1,1)
table.sort(dir, function(a, b) return string.upper(a.name) < string.upper(b.name) end)
local folders = {}
local files = {}
for _,v in pairs(dir) do
if v.isDir then table.insert(folders, v)
else table.insert(files, v) end
end
table.sort(files, function(a, b) return a.name < b.name end)
dir = folders
for _,v in ipairs(files) do
table.insert(dir, v)
end
if current_dir > "/" then
table.insert(dir, 1, {name="..", isDir=true})
end
local off = 0
if #dir > height-2 then
off = math.floor(cursor_y / #dir * (#dir - height + 3))
end
for y = 1, height - 2 do
local yoff = y + off
local v = dir[yoff]
if v then
local str = ""
if v.isDir then str = str .. "\27[93m"
elseif v.name:endswith(".lua") then str = str .. "\27[96m"
else str = str .. "\27[97m" end
if yoff == cursor_y then str = str .. "\27[7m" end
term.write(str .. v.name .. "\27[m\27[K\n")
else
term.write("\27[K\n")
end
end
term.write("\n\27[93mCurrent directory: " .. current_dir .. "\27[m\27[K")
local state, _, code = keys.wait(true, true)
if code == keys.up then
cursor_y = ((cursor_y - 2) % #dir) + 1
elseif code == keys.down then
cursor_y = ((cursor_y) % #dir) + 1
elseif code == keys.enter then
if dir[cursor_y].isDir then
if dir[cursor_y].name == ".." then
current_dir = current_dir:match("(.*/).*/") or '/'
else
current_dir = current_dir .. dir[cursor_y].name .. '/'
end
cursor_y = 1
elseif dir[cursor_y].name:endswith(".lua") then
edit(current_dir .. dir[cursor_y].name)
end
elseif code == keys.esc then
break
end
end
term.clear()

View File

@@ -0,0 +1,50 @@
-- Bubble Universe
-- inspired by https://forum.clockworkpi.com/t/bubble-universe/19907
local w = 320
local w2 = w/2
local rad = 70
local n = 20
local nc = 120
local step = 2
local r = math.pi*2/n
local x,y,t = 0,0,6
local u,v,px,py
local res = 100
local sint = {}
for i = 1, math.floor(math.pi*2*res) do
sint[i] = math.sin(i/res)
end
local function fsin(i)
return sint[(math.floor(i*res)%#sint)+1]
end
local function fcos(i)
return sint[((math.floor((math.pi/2-i)*res))%#sint)+1]
end
draw.enableBuffer(2)
while true do
if keys.getState(keys.esc) then break end
draw.clear()
for i = 0, n-1 do
for c = 0, nc-1, step do
u = fsin(i+y)+fsin(r*i+x)
v = fcos(i+y)+fcos(r*i+x)
x = u + t
y = v
px = u * rad + w2
py = y * rad + w2
draw.point(px, py,
--draw.rectFill(px, py, 2, 2,
colors.fromRGB(math.floor(63+i/n*192),
math.floor(63+c/nc*192),168))
end
end
t=t+0.02
draw.blitBuffer()
end
draw.enableBuffer(false)

View File

@@ -0,0 +1,107 @@
-- partially adapted from https://bisqwit.iki.fi/jutut/kuvat/programming_examples/mandelbrotbtrace.pdf
-- arrow keys, 9 and 0 to control view, escape to quit
local maxIter = 64
local chunk = 8
local center = {-1,0}
local radius = 1
local minX, maxX, minY, maxY
local wid, hei = 320, 320
local function iterate(zr, zi, max)
local cnt, r, i, r2, i2, ri
cnt = 0
r,i = zr,zi
while cnt < max do
r2 = r*r; i2 = i*i
if r2+i2 >= 4 then break end
ri = r*i
i = ri+ri + zi
r = r2-i2 + zr
cnt = cnt + 1
end
return cnt
end
local function is_control_key()
local state, _, code = keys.peek()
if state == keys.states.pressed then
if code == keys.up
or code == keys.down
or code == keys.left
or code == keys.right
or code == '9'
or code == '0'
or code == '['
or code == ']'
or code == keys.esc then
return true
end
end
keys.flush()
return false
end
local function drawScanlineMandelbrot(max)
local zr, zi, cnt, clr
local st = chunk
local proc = {}
local stepR = (maxX - minX) / wid
local stepI = (maxY - minY) / hei
while st >= 1 do
for y = 0, hei - 1, st do
zi = minY + y * stepI
if proc[(y % (st*2))] then goto next end
for x = 0, wid - 1 do
zr = minX + x * stepR
cnt = iterate(zr, zi, max)
if cnt == max then clr = 0
else
cnt = math.floor(cnt/max*255)
clr = colors.fromHSV((-cnt-32)%256,255,127+math.floor(cnt/2))
end
draw.line(x, y, x, y+st-1, clr)
if is_control_key() then return end
end
::next::
end
proc[st%chunk] = true
st = math.floor(st/2)
end
end
while true do
minX, maxX, minY, maxY = center[1]-radius, center[1]+radius, center[2]-radius, center[2]+radius
drawScanlineMandelbrot(maxIter)
while true do
local _, _, code = keys.wait(false, true)
if code == keys.up then
center[2] = center[2] - radius * 0.25
break
elseif code == keys.down then
center[2] = center[2] + radius * 0.25
break
elseif code == keys.left then
center[1] = center[1] - radius * 0.25
break
elseif code == keys.right then
center[1] = center[1] + radius * 0.25
break
elseif code == '9' then
radius = radius * 4
break
elseif code == '0' then
radius = radius * 0.25
break
elseif code == '[' then
maxIter = maxIter - 10
break
elseif code == ']' then
maxIter = maxIter + 10
break
elseif code == keys.esc then
goto exit
end
end
end
::exit::

View File

@@ -0,0 +1,84 @@
-- kalimba / piano demo
-- written by maple "mavica" syrup <maple@maple.pet>
local pkeys = {}
local keymap = {
"5","6","4","7","3","8","2","9",
"t","y","r","u","e","i","w","o",
"g","h","f","j","d","k","s","l",
"b","n","v","m","c",",","x","."
}
local qwertymap = {
"2","3","4","5","6","7","8","9",
"w","e","r","t","y","u","i","o",
"s","d","f","g","h","j","k","l",
"x","c","v","b","n","m",",","."
}
for k,v in pairs(keymap) do
pkeys[v] = k - 1
end
local last = nil
local ch = 0
local octave = 3
local inst_num = 0
local insts = {}
local inst_names = {}
for k,v in pairs(sound.presets) do
table.insert(insts, v)
table.insert(inst_names, k)
end
local function noteString(note)
local notes = {"C-", "C#", "D-", "D#", "E-", "F-", "F#", "G-", "G#", "A-", "A#", "B-"}
return notes[(note % 12)+1] .. math.floor(note / 12)
end
local function redraw()
term.setCursorPos(1,1)
term.write("Piano / Kalimba demo\n\n")
for j = 0, 24, 8 do
for i = 1, 8 do
if qwertymap[i+j] == last then term.write("\27[7m") end
term.write(qwertymap[i+j].."\27[m ")
end
term.write("\n")
end
term.write("\n")
if pkeys[last] then term.write(noteString(pkeys[last] + octave * 12)) end
term.write("\n\n")
term.write("Octave: " .. octave .. " | Instrument: " .. inst_names[inst_num+1] .. "\n\x1b[K")
term.write("/ \\ - Change octave\n[ ] - Change instrument\nEsc - Quit\n")
end
term.clear()
while true do
redraw()
local state, _, code = keys.wait(false, false)
if state == keys.states.pressed and code ~= last then
if pkeys[code] then
last = code
sound.play(ch, pkeys[code] + octave * 12, insts[inst_num+1])
ch = (ch+1) % 4
elseif code == "[" then
inst_num = (inst_num - 1) % #insts
elseif code == "]" then
inst_num = (inst_num + 1) % #insts
elseif code == "/" and octave > 0 then
octave = (octave - 1)
elseif code == "\\" and octave < 6 then
octave = (octave + 1)
elseif code == keys.esc then
break
end
elseif state == keys.states.released then
last = nil
end
end