0,]']±>Sï}WÿÍu§ðp8·d%qy)6o;]ÉA¦ösï÷ôôô”°ÂVl†3 EFFECT_DURATION + LOGO_HOLD then self:resetEffect() end\n\n if SHOW_DEBUG_HUD then self:drawDebugHud(elapsed) end\nend\n\nreturn artem_intro\n"; ["effects.fsk_logo"] = "local fsk_logo = {}\n\nlocal SCREEN_WIDTH = 240\nlocal SCREEN_HEIGHT = 136\nlocal SPRITE_SIZE = 8\n\n-- The logo sits in the tile page as a 16x14 block starting at tile 32, i.e. rows\n-- 2-15 of the sheet. Rows 0 and 1 (tiles 0-4 and 16-20) are in use by other\n-- effects and are left alone. spr() advances a block by the sheet width, so each\n-- row of the block steps 16 tiles.\nlocal LOGO_FIRST_TILE = 32\nlocal LOGO_TILES_WIDE = 16\nlocal LOGO_TILES_HIGH = 14\nlocal LOGO_WIDTH = LOGO_TILES_WIDE * SPRITE_SIZE\nlocal LOGO_HEIGHT = LOGO_TILES_HIGH * SPRITE_SIZE\n\n-- BG tile RAM, nibble-addressed; the sheet is 16 tiles wide.\nlocal TILE_NIBBLES = 0x8000\nlocal NIBBLES_PER_TILE = SPRITE_SIZE * SPRITE_SIZE\nlocal SHEET_TILES_WIDE = 16\n\nlocal TRANSPARENT_COLOR = 0\nlocal BACKGROUND_COLOR = 0\n\n-- Timeline (seconds). Two music frames are ~11.3s, so this finishes a little\n-- early and holds black until the next effect takes over.\nlocal FADE_IN_DURATION = 2.0\nlocal HOLD_DURATION = 7.8\nlocal FADE_OUT_DURATION = 1.2\n\nlocal FRAMES_PER_SECOND = 60\n\n-- sweetie-16 palette:\nlocal PALETTE_ADDRESS = 0x3FC0\nlocal PALETTE = {\n 0x1a1c2c, 0x5d275d, 0xb13e53, 0xef7d57,\n 0xffcd75, 0xa7f070, 0x38b764, 0x257179,\n 0x29366f, 0x3b5dc9, 0x41a6f6, 0x73eff7,\n 0xf4f4f4, 0x94b0c2, 0x566c86, 0x333c57,\n}\n\n-- Own frame counter rather than the harness `t`, which counts from the start of\n-- the whole demo while the fade is timed from this effect's BOOT.\nfsk_logo.frame = 0\n\n-- Where spr() is told to put the block, set by measureContent().\nfsk_logo.drawX = (SCREEN_WIDTH - LOGO_WIDTH) // 2\nfsk_logo.drawY = (SCREEN_HEIGHT - LOGO_HEIGHT) // 2\n\n-- Nibble address of block pixel (blockX, blockY) in tile RAM.\nfunction fsk_logo:tileMaskAddress(blockX, blockY)\n local tile = LOGO_FIRST_TILE\n + (blockY // SPRITE_SIZE) * SHEET_TILES_WIDE\n + (blockX // SPRITE_SIZE)\n local pixel = (blockY % SPRITE_SIZE) * SPRITE_SIZE + (blockX % SPRITE_SIZE)\n return TILE_NIBBLES + tile * NIBBLES_PER_TILE + pixel\nend\n\n-- Centre the artwork by its own bounding box, not by the block: most of the\n-- block is empty margin, and where the art sits inside it moves every time the\n-- logo is redrawn. Scanning the mask once at BOOT keeps it centred regardless.\nfunction fsk_logo:measureContent()\n local minX = LOGO_WIDTH\n local maxX = -1\n local minY = LOGO_HEIGHT\n local maxY = -1\n\n for blockY = 0, LOGO_HEIGHT - 1 do\n for blockX = 0, LOGO_WIDTH - 1 do\n if peek4(self:tileMaskAddress(blockX, blockY)) ~= TRANSPARENT_COLOR then\n if blockX < minX then minX = blockX end\n if blockX > maxX then maxX = blockX end\n if blockY < minY then minY = blockY end\n if blockY > maxY then maxY = blockY end\n end\n end\n end\n\n if maxX < 0 then\n return\n end\n\n self.drawX = (SCREEN_WIDTH - (maxX - minX + 1)) // 2 - minX\n self.drawY = (SCREEN_HEIGHT - (maxY - minY + 1)) // 2 - minY\nend\n\n-- The fade is done in the palette, not per pixel: every entry is scaled toward\n-- black, index 0 included, so the cleared background fades with the artwork and\n-- the whole screen reaches true black.\nfunction fsk_logo:applyPalette(brightness)\n for index = 0, 15 do\n local rgb = PALETTE[index + 1]\n local address = PALETTE_ADDRESS + index * 3\n poke(address, math.floor((rgb // 0x10000) * brightness + 0.5))\n poke(address + 1, math.floor(((rgb // 0x100) % 0x100) * brightness + 0.5))\n poke(address + 2, math.floor((rgb % 0x100) * brightness + 0.5))\n end\nend\n\n-- Quick ramp up, flat hold, slower ramp down; each stage claims the moment and\n-- returns.\nfunction fsk_logo:brightnessAt(elapsed)\n if elapsed < FADE_IN_DURATION then\n return elapsed / FADE_IN_DURATION\n end\n\n local afterFadeIn = elapsed - FADE_IN_DURATION\n if afterFadeIn < HOLD_DURATION then\n return 1.0\n end\n\n local afterHold = afterFadeIn - HOLD_DURATION\n if afterHold < FADE_OUT_DURATION then\n return 1.0 - afterHold / FADE_OUT_DURATION\n end\n\n return 0.0\nend\n\nfunction fsk_logo:BOOT()\n self.frame = 0\n self:measureContent()\n self:applyPalette(0.0)\n cls(BACKGROUND_COLOR)\nend\n\nfunction fsk_logo:BDR(scanline, t)\n return nil\nend\n\n-- `t` and `row` are unused: the fade runs off self.frame so it is frame-exact,\n-- and the effect is not music-synced beyond the slot main.lua gives it.\nfunction fsk_logo:TIC(t, row)\n local elapsed = self.frame / FRAMES_PER_SECOND\n self.frame = self.frame + 1\n\n self:applyPalette(self:brightnessAt(elapsed))\n cls(BACKGROUND_COLOR)\n spr(LOGO_FIRST_TILE, self.drawX, self.drawY, TRANSPARENT_COLOR,\n 1, 0, 0, LOGO_TILES_WIDE, LOGO_TILES_HIGH)\nend\n\nreturn fsk_logo\n"; ["effects.cave_3dcube"] = "cv3d = {}\n\ncv3d.start_frame = -1\n\ncv3d.translated = 0\ncv3d.nodes ={\n {-10, -10, -10},\n {-10, -10, 10},\n {-10, 10, -10},\n {-10, 10, 10},\n { 10, -10, -10},\n { 10, -10, 10},\n { 10, 10, -10},\n { 10, 10, 10},\n}\ncv3d.textures = {\n {{0,0}, {16,0}, {0,16}},\n {{16,16}, {0,16}, {16,0} }\n}\ncv3d.triangles = {\n -- vertex 1,2,3 + texture id\n {1,2,3,1},\n {4,2,3,2},\n\n {6,2,5,1},\n {1,2,5,2},\n\n {8,6,7,1},\n {5,6,7,2},\n\n {3,4,7,1},\n {8,4,7,2},\n\n {4,2,8,1},\n {6,2,8,2},\n\n {5,1,7,1},\n {3,1,7,2}\n}\n\ncv3d.tx = 0\ncv3d.ty = 0\ncv3d.tz = 0\n\ncv3d.x_rev = 0.01--.01\ncv3d.y_rev = 0.01--.015\ncv3d.z_rev = 0.01\n\ncv3d.t = 0\ncv3d.p_frame = -1\n\ncv3d.scale_factor = 0.2\ncv3d.scale_value = 6\n\nfunction cv3d:rotateZ3D(theta)\n local sinTheta = math.sin(theta);\n local cosTheta = math.cos(theta);\n for n=1, #self.nodes do\n local x = self.nodes[n][1]\n local y = self.nodes[n][2]\n self.nodes[n][1] = x * cosTheta - y * sinTheta;\n self.nodes[n][2] = y * cosTheta + x * sinTheta;\n end\nend\n\nfunction cv3d:rotateX3D(theta)\n local sinTheta = math.sin(theta);\n local cosTheta = math.cos(theta);\n for n=1, #self.nodes do\n local y = self.nodes[n][2]\n local z = self.nodes[n][3]\n self.nodes[n][2] = y * cosTheta - z * sinTheta;\n self.nodes[n][3] = z * cosTheta + y * sinTheta;\n end\nend\n\nfunction cv3d:rotateY3D(theta)\n local sinTheta = math.sin(theta);\n local cosTheta = math.cos(theta);\n for n=1, #self.nodes do\n local x = self.nodes[n][1]\n local z = self.nodes[n][3]\n self.nodes[n][1] = x * cosTheta + z * sinTheta;\n self.nodes[n][3] = z * cosTheta - x * sinTheta;\n end\nend\n\nfunction cv3d:translate(dx, dy, dz)\n --for n=1, #nodes do\n self.tx = self.tx + dx\n self.ty = self.ty + dy\n self.tz = self.tz + dz\n --end\nend\n\nfunction cv3d:scale(m)\n for n=1, #self.nodes do\n self.nodes[n][1] = self.nodes[n][1] * m\n self.nodes[n][2] = self.nodes[n][2] * m\n self.nodes[n][3] = self.nodes[n][3] * m\n end\nend\n\nfunction cv3d:BDR(scanline, t)\n return nil\nend\n\nfunction cv3d:BOOT2()\n return nil\nend\n\nfunction cv3d:BOOT()\n trace(\"Override palette for 3d cube\")\n\n local default_palette = {\n 26, 28, 44,\n 93, 39, 93,\n 177, 62, 83,\n 239, 125, 87,\n 255, 211, 186,\n 167, 240, 112,\n 56, 183, 100,\n 37, 113, 121,\n 41, 54, 111,\n 59, 93, 201,\n 65, 166, 246,\n 115, 239, 247,\n 244, 244, 244,\n 148, 176, 194,\n 86, 108, 134,\n 51, 60, 87\n }\n\n local pal_addr = 0x3FC0\n for i = 1, #default_palette do\n poke(pal_addr + i - 1, default_palette[i])\n end\nend\n\nfunction cv3d:gen()\n local new_triangles = {}\n for t=1, #self.triangles do\n local p0i = self.triangles[t][1]\n local p1i = self.triangles[t][2]\n local p2i = self.triangles[t][3]\n\n local txi = self.triangles[t][4]\n local pnexti = #self.nodes + 1\n\n local p0 = self.nodes[p0i]\n local p1 = self.nodes[p1i]\n local p2 = self.nodes[p2i]\n\n local d1 = (p0[1] - p1[1]) ^ 2 + (p0[2] - p1[2]) ^ 2 + (p0[3] - p1[3]) ^ 2\n local d2 = (p0[1] - p2[1]) ^ 2 + (p0[2] - p2[2]) ^ 2 + (p0[3] - p2[3]) ^ 2\n\n local pp0, pp1, pp2\n local pp0i, pp1i, pp2i\n if d1 > d2 then\n pp0 = p0\n pp0i = p0i\n\n pp1 = p1\n pp1i = p1i\n\n pp2 = p2\n pp2i = p2i\n elseif d2 > d1 then\n pp0 = p0\n pp0i = p0i\n\n pp1 = p2\n pp1i = p2i\n\n pp2 = p1\n pp2i = p1i\n else\n pp0 = p1\n pp0i = p1i\n\n pp1 = p2\n pp1i = p2i\n\n pp2 = p0\n pp2i = p0i\n end\n\n local nx = (pp0[1] + pp1[1]) / 2\n local ny = (pp0[2] + pp1[2]) / 2\n local nz = (pp0[3] + pp1[3]) / 2\n\n table.insert(self.nodes, {nx,ny,nz})\n\n table.insert(new_triangles, {pp0i, pp2i, pnexti, txi})\n table.insert(new_triangles, {pp1i, pp2i, pnexti, txi})\n end\n self.triangles = new_triangles\nend\n\nfunction cv3d:background(t)\n cls(0)\n t = t + 1000\n for g=1,25 do\n for i=0,25 do\n local ss = (math.sin(t/10 + i*10)+1.1) * 3\n --rect((math.cos(t*g/200+i)+1)*240, i*12.3+5, ss, ss, 15)\n rect((math.cos(t*g/400+i*50))*240, i*8+5+3*math.sin(i*t/40), ss, ss, 2)\n end\n end\nend\n\nfunction cv3d:TIC(time, row)\n self:background(time)\n\n local frame = peek(0x13ffd)\n if self.start_frame < 0 then\n self.start_frame = frame\n end\n if frame ~= self.p_frame then\n if self.p_frame >= 0 then\n self:gen()\n end\n self.p_frame = frame\n trace(\"New triangles count: \" .. tostring(#self.triangles))\n end\n\n self:rotateZ3D(self.z_rev)\n self:rotateY3D(self.y_rev)\n self:rotateX3D(self.x_rev)\n\n if self.translated == 0 then\n self:scale(6)\n --trace(\"translate\")\n self:translate(120, 70, 0)\n self.translated = 1\n end\n if frame - self.start_frame > 2 then\n self:scale(0.996)\n else\n self:scale(1.001)\n end\n\n -- increase amount of triangles\n local t_array = {}\n for t=1, #self.triangles do\n local z0 = self.nodes[self.triangles[t][1]][3]\n local z1 = self.nodes[self.triangles[t][2]][3]\n local z2 = self.nodes[self.triangles[t][3]][3]\n --trace(tostring(z0) .. \" \" .. tostring(z1) .. \" \" .. tostring(z2))\n\n local mz = (z0+z1+z2)/3\n --trace(\"middle: \" .. tostring(mz))\n table.insert(t_array, {mz, t})\n end\n\n local function compareAsc(a, b)\n return (a[1] < b[1])\n end\n table.sort(t_array, compareAsc)\n\n for i=1, #t_array do\n local t = t_array[i][2]\n\n local v0i, v1i, v2i = self.triangles[t][1], self.triangles[t][2], self.triangles[t][3]\n local txi = self.triangles[t][4]\n local v0, v1, v2 = self.nodes[v0i], self.nodes[v1i], self.nodes[v2i]\n local u0, u1, u2 = self.textures[txi][1], self.textures[txi][2], self.textures[txi][3]\n\n --[[\n tri(\n v0[1]+self.tx, v0[2]+self.ty,\n v1[1]+self.tx, v1[2]+self.ty,\n v2[1]+self.tx, v2[2]+self.ty,\n t\n )\n ]]--\n --trace(tostring(v0[3]) .. tostring(v1[3]))\n ttri(\n v0[1]+self.tx, v0[2]+self.ty,\n v1[1]+self.tx, v1[2]+self.ty,\n v2[1]+self.tx, v2[2]+self.ty,\n u0[1], u0[2],\n u1[1], u1[2],\n u2[1], u2[2],\n 0, 10--,\n --v0[3], v1[3], v2[3]\n )\n end\nend\n\nreturn cv3d\n"; ["effects.cave_switch"] = "local cv_sw = {}\n\ncv_sw.widht = 240\ncv_sw.height = 136\n\n-- BOOT as boot called once when effect started\nfunction cv_sw:BOOT()\n math.randomseed(123456)\n trace(\"Boot cv_sw\")\n local default_palette = {\n 26, 28, 44\n }\n local pal_addr = 0x3FC0\n for i = 1, #default_palette do\n poke(pal_addr + i - 1, default_palette[i])\n end\nend\n\n-- This function should be in all of the effects\n-- if you do not use it keep empty\nfunction cv_sw:BDR(scanline, t)\n return nil\nend\n\n-- Input arguments are time and current music trac row\nfunction cv_sw:TIC(t, row)\n for x=0, self.widht do\n for y = 0, self.height do\n local curr_color = pix(x, y)\n if math.random(1, 100) > 80 and curr_color > 0 then\n pix(x - 60, y, curr_color)\n pix(x, y, 0)\n end\n end\n end\nend\n\nreturn cv_sw\n"; ["effects.example"] = "local example = {}\n\nexample.t=0\nexample.x=96\nexample.y=24\n\n-- BOOT as boot called once when effect started\nfunction example:BOOT()\n trace(\"Boot example\")\n -- Remember to set up your palette here\n -- might be corrupted by previous effect\nend\n\n-- This function should be in all of the effects\n-- if you do not use it keep empty\nfunction example:BDR(scanline, t)\n return nil\nend\n\n-- Input arguments are time and current music trac row\nfunction example:TIC(t, row)\n if btn(0) then self.y=self.y-1 end\n if btn(1) then self.y=self.y+1 end\n if btn(2) then self.x=self.x-1 end\n if btn(3) then self.x=self.x+1 end\n\n cls(13)\n spr(1+t%60//30*2,self.x,self.y,14,3,0,0,2,2)\n print(\"HELLO WORLD!\", 85, 85)\nend\n\nreturn example\n"; ["effects.cave_vortex"] = "local cv = {}\n\ncv.widht = 240\ncv.height = 136\ncv.limit = 10\ncv.row = 0\ncv.start_frame = -1\ncv.frame = 0\n\ncv.cos = math.cos\ncv.sin = math.sin\ncv.ceil = math.ceil\ncv.floor = math.floor\ncv.abs = math.floor\ncv.seed = math.randomseed\ncv.rand = math.random\ncv.min = math.min\ncv.max = math.max\ncv.st = tostring\n\nfunction cv:BOOT()\n self.seed(1234)\n local colors = { { 34, 230, 230 }, { 24, 214, 214 }, { 22, 191, 191 }, { 19, 168, 168 }, { 16, 145, 145 }, { 14, 122, 122 }, { 11, 99, 99 }, { 9, 97, 97 }, { 255, 0, 0 }, { 0, 0, 0 }, { 255, 255, 255 } }\n for i, val in ipairs(colors) do\n for j, bval in ipairs(val) do\n poke((16320 + (i * 3) + (j - 1)), bval)\n end\n end\n return nil\nend\n\nfunction cv:BDR(scanline, t)\n if ((scanline % 10) == 0) then\n local v = self.cos((scanline * (t / 200)))\n local r = ((v * 12) + 127)\n poke((16320 + (10 * 3) + 0), r)\n poke((16320 + (10 * 3) + 1), self.row)\n return poke((16320 + (10 * 3) + 2), 68)\n else\n return nil\n end\nend\n\nfunction cv:TIC(t, row)\n cls(10)\n self.row = row\n self.frame = peek(0x13ffd)\n if self.start_frame < 0 then\n self.start_frame = self.frame\n end\n poke((16320 + (9 * 3) + 0), (213 + (self.sin(row) * 42)))\n poke((16320 + (9 * 3) + 1), 0)\n poke((16320 + (9 * 3) + 2), 0)\n self.limit = self.min((0.5 + self.limit), 1000)\n local j = 0\n local dots = {}\n local mnzc = 100\n local mxzc = 0\n while (j < self.limit) do\n local i = (j % 40)\n local x = (self.widht / 2)\n local y = (self.height / 2)\n local z = 0\n local rr = (50 - (i * 3) - (self.sin(((t / 100) * i)) * 10))\n local tshift = (t - (i * 50) - (j * 5))\n local tslow = (tshift / 20)\n --local x_off = (x + (self.sin((t / 50)) * 100 + self.sin(row / 2) * 10))\n\n local x_off = (x + (self.sin((t / 50)) * 100))\n if self.frame - self.start_frame >= 2 and row % 20 == 0 then\n x_off = x_off + self.sin(row / 2) * (row)\n end\n\n local y_off = (y - 60)\n local z_off = 0\n local circ_x = rr\n local circ_y = 0\n local circ_z = z\n local cost = self.cos(tslow)\n local sint = self.sin(tslow)\n local z_angle = (math.pi / 8)\n local cosz = self.cos(z_angle)\n local sinz = self.sin(z_angle)\n local xc = ((circ_x * cost) + (circ_z * sint))\n local zc = ((circ_z * cost) - (circ_x * sint))\n local yc = circ_y\n local yc2 = ((yc * cosz) - (zc * sinz))\n local zc2 = ((yc * sinz) + (zc * cosz))\n local yc0 = yc2\n local zc0 = zc2\n local xc0 = (xc + x_off)\n local yc1 = (yc0 + (i * 5) + y_off)\n local zc1 = (zc0 + z_off)\n local z_proj = (1 + ((zc1 + 40) / 80))\n table.insert(dots, {zc1, xc0, yc1, (4 / z_proj), 0})\n mnzc = self.min(mnzc, zc1)\n mxzc = self.max(mxzc, zc1)\n if ((j % 3) == 0) then\n table.insert(dots, {0, x_off, yc1, 2, 9})\n else\n end\n j = (j + 1)\n end\n local function cmp(a, b)\n return (a[1] > b[1])\n end\n table.sort(dots, cmp)\n local zc_int = (mxzc - mnzc)\n local zc_off = ( - mnzc)\n local zc_step = (zc_int / 7)\n for index, dot in ipairs(dots) do\n local zc = dot[1]\n local xc = dot[2]\n local yc = dot[3]\n local r = dot[4]\n local c = dot[5]\n local c0\n if (c > 0) then\n c0 = c\n else\n c0 = self.ceil((1 + ((zc + zc_off) / zc_step)))\n end\n circ(xc, yc, r, c0)\n if (c0 < 5) then\n circ((1 + xc), (1 + yc), (r - 2), 11)\n else\n end\n end\n --t = (t + 1)\n return nil\nend\nreturn cv\n"; ["effects.vir2s_stars"] = "-- script: lua\n\nvir2s = {\n frame = nil,\n cx = 120, \n cy = 68,\n stars = {},\n nebulas = {},\n screenWidth = 240,\n screenHight = 136,\n fov = 50,\n speed = 0.3,\n angle = 0,\n}\n\nfunction vir2s:project(x, y, z, sa, ca)\n local fov = vir2s.fov\n local cx = vir2s.cx\n local cy = vir2s.cy\n\n local c = ca \n local s = sa \n\n local rot_x = x * c - y * s \n local rot_y = x * s + y * c\n\n return (rot_x / z) * vir2s.fov + cx, (rot_y / z) * vir2s.fov + cy \nend\n\n-- function vir2s:getColorAddress(colorId)\n-- local base_addr = 16320\n-- local addr = base_addr + (colorId * 3) \n-- return addr, addr + 1, addr + 2 \n-- end\n--\n-- function vir2s:getRGB(colorId)\n-- local r_addr, g_addr, b_addr = vir2s:getColorAddress(colorId)\n-- return peek(r_addr), peek(g_addr), peek(b_addr) \n-- end\n--\nfunction vir2s:genNenulaStar(x, y, a, b)\n local rAngl = math.random(0, 360)\n local distance = math.sqrt(math.random())\n \n return x + a * distance * math.cos(rAngl), y + b * distance * math.sin(rAngl)\nend\n\nfunction vir2s:genNenula(x, y, a, b, count)\n count = count or math.random(150, 200)\n local s = {}\n for j = 0, count do\n local nx, ny = vir2s:genNenulaStar(x, y, a, b)\n table.insert(s, {\n a = a,\n b = b,\n ad = 1 / (a * a),\n bd = 1 / (b * b),\n x = nx,\n y = ny,\n f = math.random(1, 500),\n move = function(self, x, y)\n self.x = self.x + x\n self.y = self.y + y\n end\n })\n end\n\n return s\nend\n\nvir2s.default_palette = {\n 26, 28, 44,\n 93, 39, 93,\n 177, 62, 83,\n 239, 125, 87,\n 255, 211, 186,\n 167, 240, 112,\n 56, 183, 100,\n 37, 113, 121,\n 41, 54, 111,\n 59, 93, 201,\n 65, 166, 246,\n 115, 239, 247,\n 244, 244, 244,\n 148, 176, 194,\n 86, 108, 134,\n 0, 0, 0\n}\n\nfunction vir2s:printBordered(text, color, x, y, scale)\n print(text, x+2, y, 15, true, scale)\n print(text, x, y+2, 15, true, scale)\n print(text, x, y-2, 15, true, scale)\n print(text, x-2, y, 15, true, scale)\n print(text, x, y, color, false, scale)\nend\n\nfunction vir2s:BOOT()\n local pal_addr = 0x3FC0 \n \n for i = 1, #self.default_palette do\n poke(pal_addr + i - 1, self.default_palette[i])\n end\n\n math.randomseed(2345)\n\n for i = 1, 900 do\n table.insert(vir2s.stars, {\n reverse = false,\n x = math.random(-150, 150),\n y = math.random(-150, 150),\n z = math.random(1, 100) \n })\n\n end\n\n vir2s.nebulas = { \n {\n x = 60,\n y = 30,\n xs = 0.01,\n ys = 0.01,\n frame = 12,\n row_start = 0,\n row_end = 31,\n s = vir2s:genNenula(60, 30, 30, 15, 500),\n color = 5,\n title = 'GFX',\n text = 'Sheinar',\n movment = function(self, t, frame, row)\n -- self.x = self.x + math.sin(t/10)\n -- self.y = self.y + math.cos(t/10)\n end,\n print = function(self, t)\n local tx, ty = self.x - 20, self.y - 10\n local txx, txy = self.x - 26, self.y \n\n vir2s:printBordered(self.title, self.color, tx, ty, 1)\n vir2s:printBordered(self.text, self.color, txx, txy, 2)\n end\n },\n {\n x = 160,\n y = 15,\n xs = 0.1,\n ys = 0.04,\n s = vir2s:genNenula(160, 15, 35, 18, 700),\n color = 5,\n title = 'CODE',\n text = 'CAVE',\n row_start = 32,\n row_end = 64,\n frame = 12, \n movment = function(self, t, frame, row)\n return nil \n end,\n print = function(self, t)\n local tx, ty = self.x + 15, self.y - 5\n local txx, txy = self.x - 22, self.y + 3 \n\n vir2s:printBordered(self.title, self.color, tx, ty, 1)\n vir2s:printBordered(self.text, self.color, txx, txy, 2)\n end\n },\n {\n x = 30,\n y = 100,\n xs = 0.04,\n ys = 0.01,\n s = vir2s:genNenula(30, 100, 30, 7, 500),\n title = 'CODE',\n text = 'VIR2S',\n color = 5,\n row_start = 0,\n row_end = 32,\n frame = 13,\n movment = function(self, t, frame, row)\n end,\n print = function(self, t)\n local tx, ty = self.x - 14, self.y - 10\n local txx, txy = self.x - 10, self.y + 5\n vir2s:printBordered(self.title, self.color, tx, ty, 1)\n vir2s:printBordered(self.text, self.color, txx, txy, 2)\n end\n },\n {\n x = 200,\n y = 100,\n xs = 0.04,\n ys = 0.01,\n s = vir2s:genNenula(200, 100, 35, 12, 800),\n title = 'MUSIC',\n text = 'a0',\n color = 5,\n row_start = 33,\n row_end = 64,\n frame = 13,\n movment = function(self, t, frame, row)\n return nil\n end,\n print = function(self, t)\n local tx, ty = self.x - 20, self.y - 10\n local txx, txy = self.x - 2, self.y\n\n vir2s:printBordered(self.title, self.color, tx, ty, 1)\n vir2s:printBordered(self.text, self.color, txx, txy, 2)\n end\n }\n }\n\nend\n\nfunction vir2s:BDR(scanline, t)\n return nil\nend\n\nfunction vir2s:TIC(t, row)\n cls(0)\n row = tonumber(row) or 0\n self.frame = peek(0x13ffd)\n if self.frame <= 13 then\n vir2s.angle = vir2s.angle + 0.01\n end\n\n local sinAngle = math.sin(vir2s.angle)\n local cosAngle = math.cos(vir2s.angle) \n\n for i, star in ipairs(vir2s.stars) do\n if star.reverse then\n star.z = star.z - vir2s.speed \n else\n star.z = star.z + vir2s.speed \n end\n\n if star.z >= 100 then\n star.reverse = true\n end\n\n if star.reverse == true and star.z <= 95 then\n star.z = 0 \n star.reverse = false\n star.x = math.random(-150, 150) \n star.y = math.random(-150, 150)\n end\n \n\n local screen_x, screen_y = vir2s:project(star.x, star.y, star.z, sinAngle, cosAngle)\n \n if screen_x >= 0 and screen_x <= 240 and screen_y >= 0 and screen_y <= 136 then\n local color = 8\n local size = 1\n \n if star.z < 50 then color = 9 end \n if star.z < 20 then \n color = 10\n size = 2\n end\n\n if star.reverse == true and star.z <= 97 then\n color = 0\n end\n \n rect(screen_x, screen_y, size, size, color)\n end\n end\n\n for i, nebula in ipairs(self.nebulas) do\n if nebula.frame == self.frame and nebula.row_start <= row and nebula.row_end > row then\n for i,n in ipairs(nebula.s) do\n local c = 8\n local s = 1 \n local dx = n.x - nebula.x\n local dy = n.y - nebula.y \n local res =(dx * dx) * n.ad + (dy * dy) * n.bd \n\n if res < 0.5 then c = 9 end\n\n if res < 0.3 then c = 10 end\n\n rect(n.x + math.sin(t/100 + n.f) * 2, n.y + math.cos(t/100 + n.f) * 2, s, s, c)\n\n n:move(nebula.xs, nebula.ys)\n\n if res > 1 then\n n.x, n.y = vir2s:genNenulaStar(nebula.x, nebula.y, n.a, n.b)\n end\n end\n nebula:print(t)\n nebula:movment(t, frame, row)\n end\n end\n \n if self.frame == 14 then\n vir2s:drawPoint( 140, 100)\n\n print(\"you are here\", 150, 110, 2, false, 1)\n end\nend\n\nfunction vir2s:drawPoint(x, y)\n rect(x, y, 2, 2, 2) \n\n rect(x+2, y, 1, 2, 12) \n\n rect(x, y-1, 2, 1, 12) \n\n rect(x-1, y, 1, 2, 12) \n\n rect(x, y+2, 2, 1, 12) \nend\n\nreturn vir2s\n"; } table.insert(package.searchers,1,function(modname) if __tic80cc_packages[modname] then return load(__tic80cc_packages[modname],"="..modname) end return nil, ("no such tic80cc module %q"):format(modname) end) -- END AUTO-GENERATED CODE -- title: Demo for assembly 2026 -- author: ForSaKen Design [FSK] -- license: MIT License (change this to your license of choice) -- version: 0.1 -- script: lua t = 0 -- Music parameters -- ready = 1 music_playing = 0 music_part = 0 track = 0 frame = 0 row = 0 BDR_EMPTY_FUNCTION = function(scanline, t) return nil end BDR_FUNCTION = BDR_EMPTY_FUNCTION local artem_intro = require("effects.artem_intro") local cave_vortex = require("effects.cave_vortex") local cave_3dcube = require("effects.cave_3dcube") local example = require("effects.example") local fsk_logo = require("effects.fsk_logo") local vir2s_stars = require("effects.vir2s_stars") -- Switch is special, executed between the effects local switch = require("effects.cave_switch") local switch_t_delay = 60 local is_in_switch = 0 local switch_start_t = 0 local switch_effect_id = 0 local effects = { -- frame_from starts with 0 -- effect variable, frame_from, frame_to, is_boot = 0, is_switched = 0 { fsk_logo, 0, 2, 0, 0 }, { artem_intro, 2, 4, 0, 0}, { cave_vortex, 4, 8, 0, 0}, { cave_3dcube, 8, 12, 0, 0}, { vir2s_stars, 12, 16, 0, 0}, } function BDR(scanline) BDR_FUNCTION(scanline, t) end function TIC() ------- MUSIC ROUTINE ------- local track = peek(0x13ffc) local frame = peek(0x13ffd) + music_part * 16 local row = peek(0x13ffe) ----------------------------- if ready == 1 then music(0, 0, 0, false, false) music_playing = 1 ready = 0 elseif music_playing == 1 then if (track == 255) then music_part = music_part + 1 if music_part == 1 then music(1, 0, 0, false, false) else exit() end end end if is_in_switch == 1 then trace("SWITCH.......") switch:TIC(t, row) if t - switch_start_t >= switch_t_delay then is_in_switch = 0 effects[switch_effect_id][5] = 1 end end trace("frame: " .. tostring(frame)) local active_effect = 0 for index, data in ipairs(effects) do local ef = data[1] local from = data[2] local to = data[3] local is_boot = data[4] local is_switched = data[5] if active_effect == 0 and frame >= from and frame < to then if is_switched == 0 and index > 2 then if is_in_switch == 0 then switch_effect_id = index switch_start_t = t is_in_switch = 1 switch:BOOT() end else if is_boot == 0 then trace("Boot effect: " .. tostring(index)) ef:BOOT() effects[index][4] = 1 BDR_FUNCTION = function(scanline, t) return ef:BDR(scanline, t) end end ef:TIC(t, row) end active_effect = active_effect + 1 end end if active_effect == 0 then trace("We were here: Assembly 2026") exit() end -- trace(frame) t = t + 1 end