-- -- Bundle file -- Code changes will be overwritten -- -- title: Knucklebones -- author: Simon Cambier -- desc: A game of risk and reward, from "The Cult of the Lamb" -- script: lua -- Some sprites from https://piiixl.itch.io/frames-1-bit -- [TQ-Bundler: utils.utils] -- [TQ-Bundler: globals] -- [TQ-Bundler: events] EVENT_CHANGE_TURN = "change_turn" EVENT_SET_STEP = "set_step" EVENT_REMOVE_DIE = "remove_die" EVENT_RESET_DIE = "reset_die" -- [/TQ-Bundler: events] trace = function(msg) end ALIGN = { Left = 0, Center = 1, Right = 2 } PALETTE_MAP = 0x3ff0 COLOR_PLAYER = 8 COLOR_ENEMY = 1 dt = 1 / 60 pt = 0 die_spr = { 1, 3, 5, 7, 9, 11 } --- @class PLACED_DICE PLACED_DICE = { player = { -- Each line represents a column { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, -- { 1, 2, 3 }, -- { 4, 5, 6 }, -- { 1, 2, 0 }, }, enemy = { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, -- { 4, 5, 6 }, -- { 1, 2, 3 }, -- { 4, 5, 0 }, }, ---@param self PLACED_DICE ---@param player 'player'|'enemy' ---@param x number ---@param y number ---@param score number set = function(self, player, x, y, score) self[player][x][y] = score end, ---@param self PLACED_DICE ---@param player 'player'|'enemy' ---@param x number ---@param y number ---@return number get = function(self, player, x, y) return self[player][x][y] end, ---returns true if a column has a free slot ---@param self PLACED_DICE ---@param player 'player'|'enemy' ---@param col number has_free_slot = function(self, player, col) return table.count(self[player][col], 0) >= 1 end, ---Returns if one of the boards is full ---@param self PLACED_DICE is_game_over = function(self) for _, player in ipairs({ "player", "enemy" }) do local game_over = true for col = 1, 3 do for row = 1, 3 do if self[player][col][row] == 0 then game_over = false end end end if game_over then return true end end return false end } --- Score values for each column --- Must be manually updated SCORES = { player = { 0, 0, 0 }, enemy = { 0, 0, 0 }, update = function(self) local players = { "player", "enemy" } for _, player in ipairs(players) do for col = 1, 3 do local dice_col = PLACED_DICE[player][col] local score = 0 for row = 1, 3 do local value = dice_col[row] local combo = table.count(dice_col, value) local mul = combo == 2 and 1.5 or combo == 3 and 2 or 1 -- A 4-4-4 combo == 4 + 4*2 + 4*3 -- score = score + value * mul --- A 4-4-4 combo == 4*3 + 4*3 + 4*3 score = score + combo * value end self[player][col] = math.floor(score) end end end } -- [/TQ-Bundler: globals] -- [TQ-Bundler: utils.tables] --- Finds the first value in a table that satisfies a condition ---@param table any ---@param cb any @function(v, i) return boolean end ---@return unknown @value or nil ---@return unknown @index or nil function table.find(table, cb) for i, v in pairs(table) do if cb(v) then return v, i end end return nil, nil end function table.filter(table, cb) local new = {} for _, v in pairs(table) do if cb(v) then table.insert(new, v) end end return new end function table.remove_item(list, item) for i, v in pairs(list) do if v == item then table.remove(list, i) return end end end ---needle can be a value or a function ---@param list table ---@param needle function|number ---@return integer function table.count(list, needle) local count = 0 for _, v in pairs(list) do if type(needle) == "function" and needle(v) or v == needle then count = count + 1 end end return count end function table.max(list, cb) local max = -math.huge for _, v in pairs(list) do local value = cb(v) if value > max then max = value end end return max end function math.randomitem(tbl) return tbl[math.random(#tbl)] end function table.random(tbl) return tbl[math.random(#tbl)] end -- [/TQ-Bundler: utils.tables] -- [TQ-Bundler: utils.rendering] function draw9box(corner, edge, x, y, width, height) -- edges for i = 8, width - 9, 8 do spr(edge, x + i, y, 0) -- top spr(edge, x + i, y + height - 8, 0, 1, 0, 2) -- bottom end for i = 8, height - 8,8 do spr(edge, x, y + i, 0, 1, 0, 3) -- left spr(edge, x + width - 8, y + i, 0, 1, 0, 1) -- right end -- corners spr(corner, x, y, 0) -- top left spr(corner, x + width - 8, y, 0, 1, 0, 1) -- top right spr(corner, x, y + height - 8, 0, 1, 0, 3) -- bottom left spr(corner, x + width - 8, y + height - 8, 0, 1, 0, 2) -- bottom right end local function rot(x, y, rad) local sa = math.sin(rad) local ca = math.cos(rad) return x * ca - y * sa, x * sa + y * ca end --- Draw a sprite using two textured triangles. --- Apply affine transformations: scale, shear, rotate, flip --- https://cxong.github.io/tic-80-examples/affine-sprites ---comment ---@param id number ---@param x number ---@param y number ---@param colorkey? number ---@param sx? number scale x ---@param sy? number scale y ---@param flip? number ---@param rotate? number ---@param w? number ---@param h? number ---@param ox? number ---@param oy? number ---@param shx1? number ---@param shy1? number ---@param shx2? number ---@param shy2? number function aspr( id, x, y, colorkey, sx, sy, flip, rotate, w, h, ox, oy, shx1, shy1, shx2, shy2 ) colorkey = colorkey or -1 sx = sx or 1 sy = sy or 1 flip = flip or 0 rotate = math.rad(rotate or 0) w = w or 1 h = h or 1 ox = ox or w * 8 // 2 oy = oy or h * 8 // 2 shx1 = shx1 or 0 shy1 = shy1 or 0 shx2 = shx2 or 0 shy2 = shy2 or 0 -- scale / flip if flip % 2 == 1 then sx = -sx end if flip >= 2 then sy = -sy end ox = ox * -sx oy = oy * -sy -- shear / rotate shx1 = shx1 * -sx shy1 = shy1 * -sy shx2 = shx2 * -sx shy2 = shy2 * -sy local rx1, ry1 = rot(ox + shx1, oy + shy1, rotate) local rx2, ry2 = rot(((w * 8) * sx) + ox + shx1, oy + shy2, rotate) local rx3, ry3 = rot(ox + shx2, ((h * 8) * sy) + oy + shy1, rotate) local rx4, ry4 = rot(((w * 8) * sx) + ox + shx2, ((h * 8) * sy) + oy + shy2, rotate) local x1 = x + rx1 local y1 = y + ry1 local x2 = x + rx2 local y2 = y + ry2 local x3 = x + rx3 local y3 = y + ry3 local x4 = x + rx4 local y4 = y + ry4 -- UV coords local u1 = (id % 16) * 8 local v1 = id // 16 * 8 local u2 = u1 + w * 8 local v2 = v1 + h * 8 ttri(x1, y1, x2, y2, x3, y3, u1, v1, u2, v1, u1, v2, 0, colorkey) ttri(x3, y3, x4, y4, x2, y2, u1, v2, u2, v2, u2, v1, 0, colorkey) end -- [/TQ-Bundler: utils.rendering] --- Print with outline function print_border(text, x, y, color, outline, fixed, scale, smallfont) local outline = outline == nil and ((color == 0 or color == nil) and 12 or 0) or outline -- diagonals print(text, x + 1, y + 1, outline, fixed, scale, smallfont) print(text, x + 1, y - 1, outline, fixed, scale, smallfont) print(text, x - 1, y + 1, outline, fixed, scale, smallfont) print(text, x - 1, y - 1, outline, fixed, scale, smallfont) -- cardinals print(text, x + 1, y, outline, fixed, scale, smallfont) print(text, x - 1, y, outline, fixed, scale, smallfont) print(text, x, y + 1, outline, fixed, scale, smallfont) print(text, x, y - 1, outline, fixed, scale, smallfont) print(text, x, y, color, fixed, scale, smallfont) end --- Print debug -- function printd(text, x, y) -- print_border(text, x, y, 12, 2) -- end --- Like print(), but with font() and alignment ---@param txt string ---@param x number ---@param y number ---@param color number ---@param align? number ---@param scale? number ---@return number function prn(txt, x, y, color, align, scale) align = align or ALIGN.Left scale = scale or 1 set1bpp() if align == ALIGN.Right then x = x - prn_len(txt, scale) elseif align == ALIGN.Center then x = x - prn_len(txt, scale) / 2 end if color ~= nil then swap_color(1, color) end local len = font(txt, x, y, 0, 4, 0, false, scale) swap_color(1, 1) set4bpp() return len end ---Like prn() but with an outline ---@param any string ---@param x number ---@param y number ---@param color number ---@param align? number ---@param border_color? number ---@param scale? number ---@return number function prn_border(txt, x, y, color, align, border_color, scale) if not border_color then return prn(txt, x, y, color, align, scale) end align = align or ALIGN.Left border_color = border_color or 0 scale = scale or 1 -- diagonals prn(txt, x + 1, y + 1, border_color, align, scale) prn(txt, x + 1, y - 1, border_color, align, scale) prn(txt, x - 1, y + 1, border_color, align, scale) prn(txt, x - 1, y - 1, border_color, align, scale) -- cardinals prn(txt, x + 1, y, border_color, align, scale) prn(txt, x - 1, y, border_color, align, scale) prn(txt, x, y + 1, border_color, align, scale) prn(txt, x, y - 1, border_color, align, scale) return prn(txt, x, y, color, align, scale) end ---like prn_border() but with floaty text ---@param txt any ---@param x any ---@param y any ---@param color any ---@param align any ---@param border_color? any ---@param scale? any function prn_border_floaty(txt, x, y, color, align, border_color, scale, delay) delay = delay or 0 align = align or ALIGN.Left border_color = border_color or 0 local len = prn_len(txt, scale) prn_len(txt, scale) if align == ALIGN.Right then x = x - len elseif align == ALIGN.Center then x = x - len / 2 end for i = 1, #txt do local c = txt:sub(i, i) local y = y + math.sin(((i+delay) * 100 + time()) / 200) * 3 x = x + prn_border(c, x, y, color, ALIGN.Left, border_color, scale) end return len end function swap_color(index, color) poke4(PALETTE_MAP * 2 + index, color) end function reset_colors(...) args = { ... } if #args > 0 then for _, c in ipairs(args) do swap_color(c, c) end else for i = 0, 16 do poke4(PALETTE_MAP * 2 + i, i) end end end local _cachedPrintLen = {} function print_len(txt) if _cachedPrintLen[txt] then return _cachedPrintLen[txt] end local len = print(txt, -1000, -1000) _cachedPrintLen[txt] = len return len end local _cachedFontLen = {} function prn_len(txt, scale) scale = scale or 1 if scale == 0 then scale = 1 end local bpp = peek4(2 * 0x3ffc) -- get the current bpp value to reset it after set1bpp() if _cachedFontLen[txt] then return _cachedFontLen[txt] end local len = font(txt, -1000, -1000, 0, 4, 0, false, scale) _cachedFontLen[txt] = len poke4(2 * 0x3ffc, bpp) return len end function set1bpp() poke4(2 * 0x3ffc, 8) -- 0b1000 end function set4bpp() poke4(2 * 0x3ffc, 2) -- 0b0010 end function point_in_rect(x, y, rx, ry, rw, rh) return x >= rx and x <= rx + rw and y >= ry and y <= ry + rh end function mouse_in_rect(rect) local mx, my = mouse() return point_in_rect(mx, my, rect.x, rect.y, rect.width, rect.height) end function clamp(min, val, max) return math.max(math.min(max, val), min) end function normalize(val, min, max) min = min or 0 max = max or val return (val - min) / (max - min) end -- [/TQ-Bundler: utils.utils] -- [TQ-Bundler: utils.input] -- manage clicks, since tic-80 only returns a "pressed" state Input = { --- @type boolean lclick_prev_state = false, --- @type 'none'|'pressed'|'released' lclick_state = "none" } function Input.update_mouse(self) local x, y, left = mouse() -- left btn down if left then -- was not pressed before if not self.lclick_prev_state then self.lclick_state = "pressed" self.lclick_prev_state = true -- was already pressed elseif self.lclick_prev_state then self.lclick_state = "none" end -- left btn up elseif not left then if self.lclick_prev_state then self.lclick_state = "released" self.lclick_prev_state = false elseif not self.lclick_prev_state then self.lclick_state = "none" end end end function Input.mouse_pressed(self) return self.lclick_state == "pressed" end function Input.mouse_released(self) return self.lclick_state == "released" end -- [/TQ-Bundler: utils.input] -- [TQ-Bundler: utils.tween] -- https://github.com/kikito/tween.lua --[[ MIT LICENSE Copyright (c) 2014 Enrique GarcĂ­a Cota, Yuichi Tateno, Emmanuel Oga Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] -- Modified by Simon Cambier to better fit TIC-80 restrictions ---@return Tween function require_tween() -- easing -- Adapted from https://github.com/EmmanuelOga/easing. See LICENSE.txt for credits. -- For all easing functions: -- t = time == how much time has to pass for the tweening to complete -- b = begin == starting property value -- c = change == ending - beginning -- d = duration == running time. How much time has passed *right now* ---@class Tween local Tween = {} local Tween_mt = { __index = Tween } local pow, sin, cos, pi, sqrt, abs, asin = math.pow, math.sin, math.cos, math.pi, math.sqrt, math.abs, math.asin -- linear local function linear(t, b, c, d) return c * t / d + b end -- quad local function inQuad(t, b, c, d) return c * pow(t / d, 2) + b end local function outQuad(t, b, c, d) t = t / d return -c * t * (t - 2) + b end local function inOutQuad(t, b, c, d) t = t / d * 2 if t < 1 then return c / 2 * pow(t, 2) + b end return -c / 2 * ((t - 1) * (t - 3) - 1) + b end local function outInQuad(t, b, c, d) if t < d / 2 then return outQuad(t * 2, b, c / 2, d) end return inQuad((t * 2) - d, b + c / 2, c / 2, d) end -- cubic local function inCubic(t, b, c, d) return c * pow(t / d, 3) + b end local function outCubic(t, b, c, d) return c * (pow(t / d - 1, 3) + 1) + b end local function inOutCubic(t, b, c, d) t = t / d * 2 if t < 1 then return c / 2 * t * t * t + b end t = t - 2 return c / 2 * (t * t * t + 2) + b end local function outInCubic(t, b, c, d) if t < d / 2 then return outCubic(t * 2, b, c / 2, d) end return inCubic((t * 2) - d, b + c / 2, c / 2, d) end -- quart local function inQuart(t, b, c, d) return c * pow(t / d, 4) + b end local function outQuart(t, b, c, d) return -c * (pow(t / d - 1, 4) - 1) + b end local function inOutQuart(t, b, c, d) t = t / d * 2 if t < 1 then return c / 2 * pow(t, 4) + b end return -c / 2 * (pow(t - 2, 4) - 2) + b end local function outInQuart(t, b, c, d) if t < d / 2 then return outQuart(t * 2, b, c / 2, d) end return inQuart((t * 2) - d, b + c / 2, c / 2, d) end -- quint local function inQuint(t, b, c, d) return c * pow(t / d, 5) + b end local function outQuint(t, b, c, d) return c * (pow(t / d - 1, 5) + 1) + b end local function inOutQuint(t, b, c, d) t = t / d * 2 if t < 1 then return c / 2 * pow(t, 5) + b end return c / 2 * (pow(t - 2, 5) + 2) + b end local function outInQuint(t, b, c, d) if t < d / 2 then return outQuint(t * 2, b, c / 2, d) end return inQuint((t * 2) - d, b + c / 2, c / 2, d) end -- sine local function inSine(t, b, c, d) return -c * cos(t / d * (pi / 2)) + c + b end local function outSine(t, b, c, d) return c * sin(t / d * (pi / 2)) + b end local function inOutSine(t, b, c, d) return -c / 2 * (cos(pi * t / d) - 1) + b end local function outInSine(t, b, c, d) if t < d / 2 then return outSine(t * 2, b, c / 2, d) end return inSine((t * 2) - d, b + c / 2, c / 2, d) end -- expo local function inExpo(t, b, c, d) if t == 0 then return b end return c * pow(2, 10 * (t / d - 1)) + b - c * 0.001 end local function outExpo(t, b, c, d) if t == d then return b + c end return c * 1.001 * (-pow(2, -10 * t / d) + 1) + b end local function inOutExpo(t, b, c, d) if t == 0 then return b end if t == d then return b + c end t = t / d * 2 if t < 1 then return c / 2 * pow(2, 10 * (t - 1)) + b - c * 0.0005 end return c / 2 * 1.0005 * (-pow(2, -10 * (t - 1)) + 2) + b end local function outInExpo(t, b, c, d) if t < d / 2 then return outExpo(t * 2, b, c / 2, d) end return inExpo((t * 2) - d, b + c / 2, c / 2, d) end -- circ local function inCirc(t, b, c, d) return (-c * (sqrt(1 - pow(t / d, 2)) - 1) + b) end local function outCirc(t, b, c, d) return (c * sqrt(1 - pow(t / d - 1, 2)) + b) end local function inOutCirc(t, b, c, d) t = t / d * 2 if t < 1 then return -c / 2 * (sqrt(1 - t * t) - 1) + b end t = t - 2 return c / 2 * (sqrt(1 - t * t) + 1) + b end local function outInCirc(t, b, c, d) if t < d / 2 then return outCirc(t * 2, b, c / 2, d) end return inCirc((t * 2) - d, b + c / 2, c / 2, d) end -- elastic local function calculatePAS(p, a, c, d) p, a = p or d * 0.3, a or 0 if a < abs(c) then return p, c, p / 4 end -- p, a, s return p, a, p / (2 * pi) * asin(c / a) -- p,a,s end local function inElastic(t, b, c, d, a, p) local s if t == 0 then return b end t = t / d if t == 1 then return b + c end p, a, s = calculatePAS(p, a, c, d) t = t - 1 return -(a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end local function outElastic(t, b, c, d, a, p) local s if t == 0 then return b end t = t / d if t == 1 then return b + c end p, a, s = calculatePAS(p, a, c, d) return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) + c + b end local function inOutElastic(t, b, c, d, a, p) local s if t == 0 then return b end t = t / d * 2 if t == 2 then return b + c end p, a, s = calculatePAS(p, a, c, d) t = t - 1 if t < 0 then return -0.5 * (a * pow(2, 10 * t) * sin((t * d - s) * (2 * pi) / p)) + b end return a * pow(2, -10 * t) * sin((t * d - s) * (2 * pi) / p) * 0.5 + c + b end local function outInElastic(t, b, c, d, a, p) if t < d / 2 then return outElastic(t * 2, b, c / 2, d, a, p) end return inElastic((t * 2) - d, b + c / 2, c / 2, d, a, p) end -- back local function inBack(t, b, c, d, s) s = s or 1.70158 t = t / d return c * t * t * ((s + 1) * t - s) + b end local function outBack(t, b, c, d, s) s = s or 1.70158 t = t / d - 1 return c * (t * t * ((s + 1) * t + s) + 1) + b end local function inOutBack(t, b, c, d, s) s = (s or 1.70158) * 1.525 t = t / d * 2 if t < 1 then return c / 2 * (t * t * ((s + 1) * t - s)) + b end t = t - 2 return c / 2 * (t * t * ((s + 1) * t + s) + 2) + b end local function outInBack(t, b, c, d, s) if t < d / 2 then return outBack(t * 2, b, c / 2, d, s) end return inBack((t * 2) - d, b + c / 2, c / 2, d, s) end -- bounce local function outBounce(t, b, c, d) t = t / d if t < 1 / 2.75 then return c * (7.5625 * t * t) + b end if t < 2 / 2.75 then t = t - (1.5 / 2.75) return c * (7.5625 * t * t + 0.75) + b elseif t < 2.5 / 2.75 then t = t - (2.25 / 2.75) return c * (7.5625 * t * t + 0.9375) + b end t = t - (2.625 / 2.75) return c * (7.5625 * t * t + 0.984375) + b end local function inBounce(t, b, c, d) return c - outBounce(d - t, 0, c, d) + b end local function inOutBounce(t, b, c, d) if t < d / 2 then return inBounce(t * 2, 0, c, d) * 0.5 + b end return outBounce(t * 2 - d, 0, c, d) * 0.5 + c * .5 + b end local function outInBounce(t, b, c, d) if t < d / 2 then return outBounce(t * 2, b, c / 2, d) end return inBounce((t * 2) - d, b + c / 2, c / 2, d) end Tween.easing = { linear = linear, inQuad = inQuad, outQuad = outQuad, inOutQuad = inOutQuad, outInQuad = outInQuad, inCubic = inCubic, outCubic = outCubic, inOutCubic = inOutCubic, outInCubic = outInCubic, inQuart = inQuart, outQuart = outQuart, inOutQuart = inOutQuart, outInQuart = outInQuart, inQuint = inQuint, outQuint = outQuint, inOutQuint = inOutQuint, outInQuint = outInQuint, inSine = inSine, outSine = outSine, inOutSine = inOutSine, outInSine = outInSine, inExpo = inExpo, outExpo = outExpo, inOutExpo = inOutExpo, outInExpo = outInExpo, inCirc = inCirc, outCirc = outCirc, inOutCirc = inOutCirc, outInCirc = outInCirc, inElastic = inElastic, outElastic = outElastic, inOutElastic = inOutElastic, outInElastic = outInElastic, inBack = inBack, outBack = outBack, inOutBack = inOutBack, outInBack = outInBack, inBounce = inBounce, outBounce = outBounce, inOutBounce = inOutBounce, outInBounce = outInBounce } -- private stuff local function copyTables(destination, keysTable, valuesTable) valuesTable = valuesTable or keysTable local mt = getmetatable(keysTable) if mt and getmetatable(destination) == nil then setmetatable(destination, mt) end for k, v in pairs(keysTable) do if type(v) == "table" then destination[k] = copyTables({}, v, valuesTable[k]) else destination[k] = valuesTable[k] end end return destination end local function checkSubjectAndTargetRecursively(subject, target, path) path = path or {} local targetType, newPath for k, targetValue in pairs(target) do targetType, newPath = type(targetValue), copyTables({}, path) table.insert(newPath, tostring(k)) if targetType == "number" then assert(type(subject[k]) == "number", "Parameter '" .. table.concat(newPath, "/") .. "' is missing from subject or isn't a number") elseif targetType == "table" then checkSubjectAndTargetRecursively(subject[k], targetValue, newPath) else assert(targetType == "number", "Parameter '" .. table.concat(newPath, "/") .. "' must be a number or table of numbers") end end end local function checkNewParams(duration, subject, target, easing) assert(type(duration) == "number" and duration > 0, "duration must be a positive number. Was " .. tostring(duration)) local tsubject = type(subject) assert(tsubject == "table" or tsubject == "userdata", "subject must be a table or userdata. Was " .. tostring(subject)) assert(type(target) == "table", "target must be a table. Was " .. tostring(target)) assert(type(easing) == "function", "easing must be a function. Was " .. tostring(easing)) checkSubjectAndTargetRecursively(subject, target) end local function getEasingFunction(easing) easing = easing or "linear" if type(easing) == "string" then local name = easing easing = Tween.easing[name] if type(easing) ~= "function" then error("The easing function name '" .. name .. "' is invalid") end end return easing end local function performEasingOnSubject(subject, target, initial, clock, duration, easing) local t, b, c, d for k, v in pairs(target) do if type(v) == "table" then performEasingOnSubject(subject[k], v, initial[k], clock, duration, easing) else t, b, c, d = clock, initial[k], v - initial[k], duration subject[k] = easing(t, b, c, d) end end end -- Tween methods ---@param clock number ---@return boolean # true if the tween has expired function Tween:set(clock) assert(type(clock) == "number", "clock must be a positive number or 0") self.initial = self.initial or copyTables({}, self.target, self.subject) self.clock = clock if self.clock <= 0 then self.clock = 0 copyTables(self.subject, self.initial) elseif self.clock >= self.duration then -- the tween has expired self.clock = self.duration copyTables(self.subject, self.target) else performEasingOnSubject(self.subject, self.target, self.initial, self.clock, self.duration, self.easing) end return self.clock >= self.duration end function Tween:reset() return self:set(0) end ---@param dt number ---@return boolean # true if the tween has expired function Tween:update(dt) assert(type(dt) == "number", "dt must be a number") return self:set(self.clock + dt) end -- Public interface function Tween.new(duration, subject, target, easing) easing = getEasingFunction(easing) checkNewParams(duration, subject, target, easing) return setmetatable({ duration = duration, subject = subject, target = target, easing = easing, clock = 0 }, Tween_mt) end return Tween end -- [/TQ-Bundler: utils.tween] -- [TQ-Bundler: coroutines] -- -- coroutines manager -- local coroutines = {} function addcoroutine(fn) local c = coroutine.create(fn) table.insert(coroutines, c) return c end function startcoroutine(c) coroutine.resume(c) end function stopcoroutine(c) for k, v in ipairs(coroutines) do if c == v then table.remove(coroutines, k) break end end end function _coresolve() for k, co in ipairs(coroutines) do if coroutine.status(co) ~= "dead" then local _, msg = coroutine.resume(co) assert(msg == nil, msg) else stopcoroutine(co) end end end --- @param frames number function waitframes(frames) local frame = 0 while frame < frames do frame = frame + 1 coroutine.yield() end end --- @param seconds number function waitsecs(seconds) return waitframes(seconds * 60) end -- [/TQ-Bundler: coroutines] -- [TQ-Bundler: states.state_manager] --- States stack --- @class StateManager StateManager = { states = {}, --- ---@param self StateManager new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self return o end, --- ---@param self StateManager ---@return unknown _get_current_state = function(self) return self.states[#self.states] end, --- ---@param self StateManager ---@param state any _set_current_state = function(self, state) if #self.states == 0 then self.states = { state } else local last = self.states[#self.states] if last._leave then last:_leave() end self.states[#self.states] = state end end, --- ---@param self StateManager ---@param state any change_state = function(self, state, ...) local current = self:_get_current_state() if current and current._leave then current:_leave() end self:_set_current_state(state) if state._enter then state:_enter(...) end end, --- ---@param self StateManager push_state = function(self, state, data) table.insert(self.states, state) if state._enter then state:_enter(data) end end, --- ---@param self StateManager pop_state = function(self) local state = table.remove(self.states) if #self.states == 0 then trace("No state left", 2) end if state._leave then state:_leave() end end, --- ---@param self StateManager update = function(self) local current = self:_get_current_state() if not current then return end if current.update then current:update() end end, } -- [/TQ-Bundler: states.state_manager] -- [TQ-Bundler: states.state_main_menu] function state_main_menu() local covering = {} local falling = {} local states local buttons = { { hover = false, label = "Play", box = { x = 120 - prn_len("Play") / 2, y = 100, w = prn_len("Play"), h = 10 }, cb = function() trace("play") for i = 0, 240, 16 do for j = 0, 135, 16 do table.insert(covering, Die:new({ value = math.random(1, 6), angle = 360, x = i, y = j, scale = 0 })) end end end } } local function show_transition() -- slightly scale up the transition dice for i, die in ipairs(covering) do die.scale = die.scale + 0.03 / i * 80 die.angle = die.angle * 0.90 if die.scale > 1 then die.scale = 1 end swap_color(12, 15) swap_color(0, 15) die:draw(-1) end if table.count(covering, function(d) return d.scale == 1 end) == #covering then state_manager:change_state(states.game, { covering = covering }) end end local function falling_dice() local remove = {} for i, die in ipairs(falling) do die.y = die.y + 1 die.angle = die.angle + die.rot_speed if die.y > 160 then table.insert(remove, i) end end for _, i in ipairs(remove) do table.remove(falling, i) end end local function draw_dice() for _, die in ipairs(falling) do swap_color(12, 15) swap_color(13, 15) swap_color(14, 15) die:draw() reset_colors(12) end end local function _enter(self, data) assert(data.states, "needs a states table") states = data.states covering = {} end local function update() -- random sign if math.random() < 0.05 then local sign = math.random() < 0.5 and -1 or 1 -- Spawn a random die local die = Die:new({ value = math.random(1, 6), x = math.random(240), y = -20, angle = math.random(10, 40), rot_speed = sign * math.random(6, 10) / 10, }) table.insert(falling, die) end falling_dice() cls() draw_dice() -- prn_border("KNUCKLEBONES", 118, 10, 1, ALIGN.Center, 0, 2, 0) -- prn_border("KNUCKLEBONES", 122, 10, 2, ALIGN.Center, 0, 2, 1) prn_border("KNUCKLEBONES", 120, 10, 12, ALIGN.Center, 0, 2) local l = prn_len("A game of risk and reward") local x = 120 - l / 2 local y = 50 x = x + prn_border("A game of ", x, y, 13, ALIGN.Left, 0) x = x + prn_border_floaty("risk ", x, y + 5, 12, ALIGN.Left, 1, 1, 20) x = x + prn_border("and ", x, y, 13, ALIGN.Left, 0) x = x + prn_border_floaty("reward", x, y - 5, 12, ALIGN.Left, 8) print_border("Originally from \"The Cult of the Lamb\"", 104, 130, 14, 0, false, 1, true) local mx, my = mouse() for i, button in ipairs(buttons) do -- Print button local x, y, w, h = button.box.x, button.box.y, button.box.w, button.box.h local hover = mx >= x and mx <= x + w and my >= y and my <= y + h button.hover = hover local color = hover and 12 or 13 -- rect(x, y, w, h, color) prn_border(button.label, x, y, color, ALIGN.Left, 0) -- Click button if hover and Input:mouse_pressed() then button.cb() end end if #covering > 0 then show_transition() end end return { update = update, _enter = _enter } end -- [/TQ-Bundler: states.state_main_menu] -- [TQ-Bundler: states.state_game] -- [TQ-Bundler: board] ---@diagnostic disable: lowercase-global -- [TQ-Bundler: scoring] function get_total_score(scores) local sum = 0 for col = 1, 3 do local value = scores[col] sum = sum + value end return sum end -- [/TQ-Bundler: scoring] local cell_width = 22 local cell_height = 18 function draw_board() local players = { "player", "enemy" } for _, player in ipairs(players) do -- columns for col = 1, 3 do local dice_col = PLACED_DICE[player][col] for row = 1, 3 do local pos = get_cell_coords(player, col, row) -- cell background rectb(pos.x, pos.y, cell_width, cell_height, 15) -- dice local value = dice_col[row] local combo = table.count(dice_col, value) if value > 0 then swap_color(8, 0) if combo == 2 then swap_color(12, 4) swap_color(13, 3) elseif combo == 3 then swap_color(12, 3) swap_color(13, 2) end -- dice sprite spr(die_spr[value], pos.x + 3, pos.y + 1, 0, 1, 0, 0, 2, 2) reset_colors(8, 12, 13) end end end end end function draw_dice_boxes() -- small trick to make the corners look better swap_color(3, 0) -- player swap_color(12, COLOR_PLAYER) -- bones color local r = get_dice_box("player") draw9box(51, 52, r.x - 4, r.y - 4, r.width + 8, r.height + 8) -- enemy swap_color(12, COLOR_ENEMY) -- bones color r = get_dice_box("enemy") draw9box(51, 52, r.x - 4, r.y - 4, r.width + 8, r.height + 8) -- reset colors swap_color(3, 3) swap_color(12, 12) end function print_names() prn_border("You", 1, 50, 12, ALIGN.Left, COLOR_PLAYER) prn_border("Opponent", 239, 135-50-8, 12, ALIGN.Right, COLOR_ENEMY) end function print_scores() local players = { "player", "enemy" } local sums = {} for _, player in ipairs(players) do local scores = SCORES[player] local y for col = 1, 3 do local value = scores[col] local pos = get_cell_coords(player, col, 1) pos.y = pos.y + (player == "player" and -9 or 20) y = pos.y pos.x = pos.x + 11 prn_border(value, pos.x, pos.y, 12, ALIGN.Center) end -- sums local sum = get_total_score(scores) local x = player == "player" and 12 or 240 - 12 local align = player == "player" and ALIGN.Left or ALIGN.Right table.insert(sums, { sum = sum, x = x, y = y, align = align, bg = player == "player" and COLOR_PLAYER or COLOR_ENEMY }) end for _, sum in ipairs(sums) do prn_border(sum.sum, sum.x, sum.y, 12, sum.align, sum.bg) end end ---comment ---@param player 'player'|'enemy' ---@param col number 1-3 ---@param row number 1-3 ---@return table function get_cell_coords(player, col, row) row = row - 1 -- fix 1-based index for y, makes it easier to calculate coords local p = 14 -- padding local cols = { 120 - cell_width - p, 120 - (cell_width / 2), 120 + p } if player == "enemy" then row = row == 0 and 2 or row == 2 and 0 or 1 -- "invert" the board for the enemy return { x = cols[col], y = (cell_height * row + (row * 1)) + 1 } else return { x = cols[col], y = (cell_height * row + (row * 1)) + 79 } end end --- @param player 'player'|'enemy' --- @param column 1|2|3 function get_column_rect(player, column) local c = get_cell_coords(player, column, player == "player" and 1 or 3) return { x = c.x - 1, y = c.y - 1, width = cell_width + 2, height = cell_height * 3 + 4 } end --- @param player 'player'|'enemy' function get_dice_box(player) if player == "player" then return { x = 10, y = 89, width = 60, height = 36 } else return { x = 170, y = 9, width = 60, height = 36 } end end -- [/TQ-Bundler: board] -- [TQ-Bundler: classes.die] --- @class Die Die = { x = 0, y = 0, size = 16, dx = math.random(0, 1) == 0 and -1 or 1, dy = math.random(0, 1) == 0 and -1 or 1, value = nil, -- box = nil, bouncing = false, angle = 0, scale = 1, -- bg = nil, -- --- ---@param self Die set_random_value = function(self) -- Don't change the value if the die is barely moving if (math.abs(self.dx)<0.25 or math.abs(self.dy)<0.25) then return end local n repeat n = math.random(1, 6) until n ~= self.value self.value = n end, --- --- @param self Die update = function(self) -- bounce off walls if math.abs(self.dx) < 0.05 then self.dx = 0 end if math.abs(self.dy) < 0.05 then self.dy = 0 end if self.box then if self.x < self.box.x then self.x = self.box.x self.dx = -self.dx self:set_random_value() end if self.x > self.box.x + self.box.width - self.size then self.x = self.box.x + self.box.width - self.size self.dx = -self.dx self:set_random_value() end if self.y < self.box.y then self.y = self.box.y self.dy = -self.dy self:set_random_value() end if self.y > self.box.y + self.box.height - self.size then self.y = self.box.y + self.box.height - self.size self.dy = -self.dy self:set_random_value() end end if not self.bouncing then -- reduce speed if self.dx ~= 0 then self.dx = self.dx * 0.85 end if self.dy ~= 0 then self.dy = self.dy * 0.85 end end self.x = self.x + self.dx self.y = self.y + self.dy end, --- ---@param self Die draw = function(self, colorkey) colorkey = colorkey or 0 if self.bg then swap_color(12, self.bg) end swap_color(8, 0) -- aspr() uses the center as anchor point, so we offset the drawing position -- while keeping the scaling centered local offset = 8 aspr(die_spr[self.value], self.x + offset, self.y + offset, colorkey, self.scale, self.scale, 0, self.angle, 2, 2) -- rectb(self.x, self.y, 16, 16, 2) reset_colors(12, 8) end } ---comment ---@param o Die ---@return Die function Die:new(o) o = o or {} -- create object if user does not provide one setmetatable(o, self) self.__index = self if o.box then o.angle = math.random() < .5 and 180 or -180 o.x = o.box.x + math.random(0, o.box.width - o.size) o.y = o.box.y + math.random(0, o.box.height - o.size) end return o end -- [/TQ-Bundler: classes.die] -- [TQ-Bundler: states.game.loading] function state_game_loading() local covering local event_bus local turn local banner local function show_transition() -- slightly scale up the transition dice for i, die in ipairs(covering) do die.scale = die.scale - 0.03 / i * 80 die.angle = die.angle - i / 50 if die.scale < 0 then die.scale = 0 end swap_color(12, 15) swap_color(13, 15) swap_color(14, 15) swap_color(0, 15) die:draw(-1) end -- clean if table.count(covering, function(d) return d.scale == 0 end) == #covering then covering = {} end end local function _enter(self, data) assert(data.covering, "needs data.covering") assert(data.event_bus, "needs data.event_bus") covering = data.covering event_bus = data.event_bus turn = data.turn banner = { h = 0, w = 1, } addcoroutine(function() while banner.w < 240 do banner.w = banner.w *1.1 waitframes(1) end if banner.w > 240 then banner.w = 240 end while banner.h < 20 do banner.h = banner.h + 1 waitframes(1) end waitsecs(1.5) while banner.h > 0.5 do banner.h = banner.h - 1 waitframes(1) end while banner.w > 1 do banner.w = banner.w * 0.9 waitframes(1) end waitsecs(0.5) event_bus:emit(EVENT_RESET_DIE) event_bus:emit(EVENT_SET_STEP, "rolling") end) end local function update(self) -- black banner in the middle rect(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h, 0) rectb(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h, 15) clip(120 - banner.w / 2, 67 - banner.h / 2, banner.w, banner.h) -- show who's going first local text local color if turn == "player" then color = COLOR_PLAYER text = "You begin this round" else color = COLOR_ENEMY text = "Your opponent begins this round" end prn_border(text, 120, 67 - 4, 12, ALIGN.Center, color) clip() if #covering > 0 then show_transition() end end return { _enter = _enter, update = update } end -- [/TQ-Bundler: states.game.loading] -- [TQ-Bundler: states.game.rolling] function state_game_rolling() --- @type Die local die --- @type EventBus local event_bus local function roll_die() die.bouncing = true local rot = Tween.new(0.7, die, { angle = (die.angle + math.random(180,360))-- die.angle + (math.random()<.5 and 1 or -1)* math.random(270, 360) }, "outSine") local done = false repeat done = rot:update(dt) coroutine.yield() until done die.bouncing = false event_bus:emit(EVENT_SET_STEP, "placing") end ---@param self any ---@param _die Die ---@param _event_bus EventBus local _enter = function(self, _die, _event_bus) assert(_die, "states.game.rolling needs a die") assert(_event_bus, "states.game.rolling needs an event_bus") die = _die event_bus = _event_bus addcoroutine(roll_die) end local update = function(self) end return { _enter = _enter, update = update } end -- [/TQ-Bundler: states.game.rolling] -- [TQ-Bundler: states.game.placing] -- [TQ-Bundler: ai] --[[ List of actions, by order of priority: - Complete a 3 dice combo 4-6 - Cancel a 3 dice combo 4-6 - Complete a 3 dice combo 1-3 - Complete a 2 dice combo 4-6 - Cancel a 3 dice combo 1-3 - Complete a 2 dice combo 1-3 - Cancel a 2 dice combo 4-6 - Cancel a 2 dice combo 1-3 - Look at the column's sum before removing a die. It if is <=15, try to keep it as is, unless you're removing a 6 (or a 5) ]] ---Returns the column indexes (1-based) with completable combos ---@param dice table The 2d table with dice values ---@param value integer The dice value to place ---@param len 2|3 The possible combo length ---@return table The columns list (1-based) function get_combos_to_complete(dice, value, len) local ids = {} for col = 1, 3 do if table.count(dice[col], 0) >= 1 and table.count(dice[col], value) == len - 1 then table.insert(ids, col) end end return ids end ---@param mine table "My" 2d table with dice values ---@param other table The "other" (opponent) 2d table with dice values ---@param value integer The dice value to place ---@param len 2|3 The possible combo length ---@return table The columns list (1-based) function get_combos_to_cancel(mine, other, value, len) local ids = {} for col = 1, 3 do if table.count(mine[col], 0) >= 1 and table.count(other[col], value) == len - 1 then table.insert(ids, col) end end return ids end function get_most_empty_cols(dice) local ids = {} for z = 3, 1, -1 do for col = 1, 3 do if table.count(dice[col], 0) == z then table.insert(ids, col) end end if #ids > 0 then return ids end end end ---Place the die in the best possible column ---@param mine table The 2d table of "my" dice ---@param other any The 2d table of the "other" dice ---@param die integer The die to place ---@return integer # The column where to place the die function ai_pick_column(mine, other, die) trace("--") trace("AI") trace("--") if die >= 4 then --#region Complete a 3 combo 4-5-6 local combos = get_combos_to_complete(mine, die, 3) if #combos > 0 then trace("Making a 3 combo with " .. die) return math.randomitem(combos) end --#endregion Complete a 3 combo 4-5-6 -- #region Cancel a 3-dice combo 4-5-6 combos = get_combos_to_cancel(mine, other, die, 3) if #combos > 0 then trace("Cancelling a 3 combo with " .. die) return math.randomitem(combos) end --#endregion Cancel a 3-dice combo 4-5-6 -- #region Complete a 2 combo 4-5-6 combos = get_combos_to_complete(mine, die, 2) if #combos > 0 then trace("Making a 2 combo with " .. die) return math.randomitem(combos) end -- #endregion Complete a 2 combo 4-5-6 end -- #region Complete a 3 combo 1-2-3 combos = get_combos_to_complete(mine, die, 3) if #combos > 0 then trace("Making a 3 combo with " .. die) return math.randomitem(combos) end -- #endregion Complete a 3 combo 1-2-3 -- #region Complete a 2 combo 1-2-3 combos = get_combos_to_complete(mine, die, 2) if #combos > 0 then trace("Making a 2 combo with " .. die) return math.randomitem(combos) end -- #endregion Complete a 2 combo 1-2-3 -- #region Random column -- TODO: if 5-6, attack the opponent. -- TODO: else, chose the column with the lowest sum -- TODO: it's best to AVOID attacking the opponent when his sum is <= 15 -- TODO: it's best to avoid canceling 1-2 values trace("Placing " .. die .. " at random") return math.randomitem(get_most_empty_cols(mine)) -- #endregion Random column end -- [/TQ-Bundler: ai] function state_game_placing() --- @type Die local die --- @type EventBus local event_bus --- @type 'player'|'enemy' local turn local placing = false local waiting = 30 --- Click on a column to place the die local function place_die(col_id) if placing then return end placing = true local cell_id = 0 for i = 1, 3 do if PLACED_DICE[turn][col_id][i] == 0 then cell_id = i break end end step = "waiting" addcoroutine(function() -- move the die to its cell local cell = get_cell_coords(turn, col_id, cell_id) die.box = nil die.angle = die.angle % 360 -- get value closest to 0. e.g. 270 -> -90 if die.angle > 180 then die.angle = die.angle - 360 end local tween = Tween.new(0.5, die, { x = cell.x + 3, y = cell.y + 1, angle = 0 }, "outQuad") repeat coroutine.yield() until tween:update(dt) PLACED_DICE:set(turn, col_id, cell_id, die.value) -- Animate the dice and update the scores event_bus:emit(EVENT_REMOVE_DIE) event_bus:emit(EVENT_SET_STEP, "scoring") end) end ---@param self any ---@param _die Die ---@param _event_bus EventBus ---@param _turn 'player'|'enemy' local _enter = function(self, _die, _event_bus, _turn) assert(_die, "missing die") assert(_event_bus, "missing event_bus") assert(_turn, "missing turn") die = _die event_bus = _event_bus turn = _turn waiting = 30 placing = false end local update = function(self) waiting = waiting - 1 if turn == "enemy" then if waiting <= 0 and not placing then local col = ai_pick_column(PLACED_DICE.enemy, PLACED_DICE.player, die.value) place_die(col) end else -- turn == "player" -- Highlight the hovered column local columns = { get_column_rect(turn, 1), get_column_rect(turn, 2), get_column_rect(turn, 3), } local col, col_id = table.find(columns, function(c) return mouse_in_rect(c) end) if col then if PLACED_DICE:has_free_slot(turn, col_id) then -- draw hovered column rect(col.x, col.y, col.width, col.height, 14) -- place the die if clicked if Input:mouse_released() then place_die(col_id) end end end end -- Redraw the board because the hovered column is drawn on top of it -- and i'm too lazy to fix this properly. draw_board() end return { _enter = _enter, update = update, } end -- [/TQ-Bundler: states.game.placing] -- [TQ-Bundler: states.game.scoring] function state_game_scoring() local dice_to_destroy = {} --- @type EventBus local event_bus local destroyed = 0 local function update_board_and_score(player, col, row) assert(player) assert(col) assert(row) -- Create a Die and place it on the cell local cell = get_cell_coords(player, col, row) local die = Die:new({ value = PLACED_DICE:get(player, col, row), x = cell.x + 3, y = cell.y + 1 }) -- Set the score to 0 PLACED_DICE:set(player, col, row, 0) table.insert(dice_to_destroy, die) addcoroutine(function() waitframes(10) local tween = Tween.new(0.5, die, { scale = 0.1 }, "inOutQuad") repeat coroutine.yield() until tween:update(dt) table.remove_item(dice_to_destroy, die) end) end ---comment ---@param self any ---@param _die Die|nil ---@param _event_bus EventBus ---@param player 'player'|'enemy' local function _enter(self, _die, _event_bus, player) assert(_event_bus) assert(player == "player" or player == "enemy") event_bus = _event_bus destroyed = 0 local other_player = player == "player" and "enemy" or "player" -- Check if the newly placed die must remove other dice local current_dice = PLACED_DICE[player] local other_dice = PLACED_DICE[other_player] -- Check the dice for each column, and remove corresponding dice in the opposing column for col = 1, 3 do for row = 1, 3 do local value = current_dice[col][row] for row2 = 1, 3 do local value2 = other_dice[col][row2] if value > 0 and value2 > 0 and value2 == value then trace("destroy " .. other_player .. " " .. col .. " " .. row2 .. " = " .. value2) update_board_and_score(other_player, col, row2) destroyed = destroyed + 1 end end end end -- Update the scores and move on to next state addcoroutine(function() waitsecs((destroyed > 0) and 0.6 or 0.3) SCORES:update() -- Check if it's game over if PLACED_DICE:is_game_over() then SCORES:update() event_bus:emit(EVENT_SET_STEP, "game_over") else event_bus:emit(EVENT_CHANGE_TURN) end end) end local function update(self) for _, die in ipairs(dice_to_destroy) do die:draw() end end local function _leave(self) end return { _enter = _enter, update = update, _leave = _leave } end -- [/TQ-Bundler: states.game.scoring] -- [TQ-Bundler: states.game.gameover] function state_game_gameover() local box local event_bus local y = 90 local buttons = { -- { -- hover = false, -- label = "Play Again", -- box = { x = 120 - prn_len("Play Again") / 2, y = y, width = prn_len("Play Again"), height = 10 }, -- cb = function() -- trace("again") -- end -- }, { hover = false, label = "Main Menu", box = { x = 120 - prn_len("Main Menu") / 2, y = y + 15, width = prn_len("Main Menu"), height = 10 }, cb = function() reset() end } } local function open_box() while box.w < 180 do box.w = box.w * 1.1 waitframes(1) end if box.w > 180 then box.w = 180 end while box.h < 20 do box.h = box.h + 1 waitframes(1) end end local function show_buttons() -- show a button "again", and another "main menu" -- right under the box for _, button in ipairs(buttons) do button.hover = false -- rect(button.box.x - 1, button.box.y - 1, button.box.width + 2, button.box.height + 2, 2) -- Hover button if mouse_in_rect(button.box) then button.hover = true -- Click button if Input:mouse_released() then button.cb() end end prn_border(button.label, button.box.x, button.box.y, button.hover and 12 or 13, ALIGN.Left, 0) end end local function update(self) -- draw box rect(120 - box.w / 2, 67 - box.h / 2, box.w, box.h, 0) rectb(120 - box.w / 2, 67 - box.h / 2, box.w, box.h, 15) clip(120 - box.w / 2, 67 - box.h / 2, box.w, box.h) -- print who won according to score local text local color local score_player = get_total_score(SCORES.player) local score_enemy = get_total_score(SCORES.enemy) if score_player > score_enemy then color = COLOR_PLAYER text = "YOU WON " .. score_player .. " - " .. score_enemy elseif score_player < score_enemy then color = COLOR_ENEMY text = "YOU LOST " .. score_player .. " - " .. score_enemy else color = 14 text = "DRAW " .. score_player .. " - " .. score_enemy end prn_border(text, 120, 67 - 3, 12, ALIGN.Center, color) clip() show_buttons() end ---comment ---@param self any ---@param die Die ---@param _event_bus EventBus ---@param turn "player"|"enemy" local function _enter(self, die, _event_bus, turn) assert(_event_bus, "event_bus is nil") assert(turn, "turn is nil") event_bus = _event_bus event_bus:emit(EVENT_REMOVE_DIE) box = { h = 0, w = 1 } addcoroutine(open_box) end return { update = update, _enter = _enter } end -- [/TQ-Bundler: states.game.gameover] -- [TQ-Bundler: utils.event_bus] --- @class EventBus EventBus = { _handlers = {}, new = function(self, o) o = o or {} setmetatable(o, self) self.__index = self return o end, --- ---@param self EventBus ---@param event any ---@param handler any ---@param index any on = function(self, event, handler, index) if not self._handlers[event] then self._handlers[event] = {} end self:off(event, handler) if not index then table.insert(self._handlers[event], handler) else -- insertIntoTable(this.handlers[event], handler, index) end end, --- ---@param self EventBus ---@param event any ---@param handler any off = function(self, event, handler) if not self._handlers[event] then return end self._handlers[event] = table.filter(self._handlers[event], function(o) return o ~= handler end) end, --- ---@param self EventBus ---@param event any ---@param ... unknown emit = function(self, event, ...) trace("[EventBus] Emitted " .. event) if not self._handlers[event] then return end for k, handler in pairs(self._handlers[event]) do handler(...) end end } -- [/TQ-Bundler: utils.event_bus] function state_game() local game = {} local event_bus = EventBus:new() --- @type "player"|"enemy" local turn = "player" --- @type "waiting"|"rolling"|"placing" local step = "rolling" --- @type Die|nil local die local function reset_die() die = Die:new({ box = get_dice_box(turn), bouncing = false }) end -- -- #region State management -- --- Sub-states --- @type StateManager local state_manager = StateManager:new() local states = { loading = state_game_loading(), rolling = state_game_rolling(), placing = state_game_placing(), scoring = state_game_scoring(), game_over = state_game_gameover(), } event_bus:on(EVENT_SET_STEP, function(new_step) trace("[Event] set_step: " .. step .. " -> " .. new_step) assert(states[new_step], "state " .. new_step .. " does not exist") state_manager:change_state(states[new_step], die, event_bus, turn) step = new_step end) event_bus:on(EVENT_CHANGE_TURN, function() turn = turn == "player" and "enemy" or "player" reset_die() state_manager:change_state(states.rolling, die, event_bus) step = "rolling" end) event_bus:on(EVENT_REMOVE_DIE, function() die = nil end) event_bus:on(EVENT_RESET_DIE, function() reset_die() end) -- -- #endregion State management -- game._enter = function(self, data) assert(data.covering, "needs data.covering") trace("entering game state") turn = math.random(1, 2) == 1 and "player" or "enemy" state_manager:change_state(states.loading, { turn = turn, event_bus = event_bus, covering = data.covering }) end game.update = function() cls() if die then die:update() end draw_board() print_scores() print_names() draw_dice_boxes() state_manager:update() if die and die.value then die:draw() end -- prn_border("Turn: " .. turn, 1, 1, 2) -- prn_border("Step: " .. step, 1, 10, 2) reset_colors() end game._leave = function() end return game end -- [/TQ-Bundler: states.state_game] Tween = require_tween() --- @type StateManager state_manager = StateManager:new() local states = { main_menu = state_main_menu(), game = state_game() } frames = 0 function BOOT() -- Save original palette memcpy(0x14e04, 0x3fc0, 48) state_manager:change_state(states.main_menu, { states = states }) end function TIC() dt = (time() - pt) / 1000 pt = time() frames = frames + 1 cls(13) Input:update_mouse() _coresolve() state_manager:update() -- mouse position -- local mx, my = mouse() -- print_border(mx .. "," .. my, mx + 1, my - 5) end -- function BDR(scn) -- local cl = (math.floor(frames / 1.2) % (135 + 40)) - 20 -- greyGlitch(scn, cl) -- end function vhsGlitch(scn, cl) local height = 10 local ampl = 10 local n = clamp(0, normalize(cl, scn - height, scn + height), 1) * 2 * math.pi local s = math.cos(n) local r = math.random() * (s * ampl - ampl) * s * s -- 0x3ff9: screen offset poke(0x3ff9, r) poke(0x3ffa, r / 2) end function greyGlitch(scn, cl) -- Reset rgb values memcpy(0x3fc0, 0x14e04, 48) local h = 10 -- If current scanline is in range if (scn >= cl - h and scn <= cl + h) then -- Loop through colors in ram if (math.random() < 0.8) then if (math.random() > 0.01) then for i = 0, 16 do local c = i * 3 local grey = peek(0x3fc0 + c + 0) * 0.2126 + peek(0x3fc0 + c + 1) * 0.7152 + peek(0x3fc0 + c + 2) * 0.0722 -- Alter the color poke(0x3fc0 + c + 0, grey) poke(0x3fc0 + c + 1, grey) poke(0x3fc0 + c + 2, grey) end else -- line(0, scn, SCREEN_WIDTH, scn, RNG.random() * 15) end end end end -- -- 001:0cccccccccccccccccdcccccccccccccccccdccccccccdccccccccc8cccccc88 -- 002:cccccce0cccccccecccccdcecccccccecccdccceccdcccce8cccccce88ccccce -- 003:0ccccccccccccccccccccccccccddcccccdccdccccccccdcccc88ccccc8888cc -- 004:cccccce0cccccccecccccccecccddcceccdccdcecdccccceccc88ccecc8888ce -- 005:0cccccccccccccddccc88ccccc8888cccc8888cdccc88ccccdccccc8cdccdc88 -- 006:cccccce0ccccdccedccccdcecccdccceddccccceccdcccce8ccdccce88cdcdce -- 007:0cccccccccccccdcccc88ccdcc8888cccc8888ccccc88ccccccccccdcdcccddc -- 008:cccccce0cdcccccedcc88ccecc8888cecc8888ceccc88ccedccccccecddcccde -- 009:0ccdddccccccccdcccc88ccdcc8888cccc8888ccccc88cdccccccdc8cccccc88 -- 010:cccccce0ccccccceccc88ccecc8888cecc8888cecdc88cce8cdcccde88cccdce -- 011:0cccccccccc88ccccc8888cccc8888ccccc88cccccccccdcccc88ccdcc8888cd -- 012:cccccce0ccc88ccedc8888cedc8888cedcc88ccecdccccceccc88ccecc8888ce -- 017:cdcddc88ccccccc8cccccdccccccdcccccccccccccdccccceccccccc0eeeeeee -- 018:88cddcde8cccccceccdccccecccdcccecccccccecccccdcecccccceeeeeeeee0 -- 019:cc8888ccccc88cccccccccdcccdccdcccccddccccccccccceccccccc0eeeeeee -- 020:cc8888ceccc88ccecdccccceccdccdcecccddccecccccccecccccceeeeeeeee0 -- 021:ccdcdc88ccccdcc8cccccdcccccdccddcdccccccccdccccdeccccccc0eeeeeee -- 022:88cdccde8cccccdeccc88ccedc8888cecc8888ceccc88cceddcccceeeeeeeee0 -- 023:cdccccccccccccccccc88ccdcc8888cccc8888ccccc88ccdeccccccc0eeeeeee -- 024:ccccccdecccccccedcc88ccecc8888cecc8888cedcc88ccecccccceeeeeeeee0 -- 025:ccdccc88cdcccdc8dcc88cdcdc8888ccdc8888ccccc88ccceccccccc0eeeeeee -- 026:88ccccce8cdccccecdc88ccecc8888cecc8888cedcc88ccecdcccceeeeeeeee0 -- 027:cc8888ccccc88cccccccccdcccc88ccdcc8888cdcc8888cdecc88ccc0eeeeeee -- 028:dc8888cedcc88ccecdccccceccc88ccecc8888cecc8888ceccc88ceeeeeeeee0 -- 032:ccccccccc0000000c0000000c0000000c0000000c0000000c0000000c0000000 -- 033:cccccccc00000000000000000000000000000000000000000000000000000000 -- 034:ccccc0000000c0000000c0000000c0000000c0000000c0000000c0000000c000 -- 035:000cc0c0000cccc00000cc00cc00cc00ccc3cc3c0cc3cc3ccc00cc000000cc00 -- 036:00000000000000000000000000000000cccccccccccccccc0000000000000000 -- 037:0cccccd0ccc22ccdcc2cc2cdccccc2cdcccc2ccdcccccccddccc2cdd0dddddd0 -- 048:c0000000c0000000c0000000c0000000c0000000c0000000c0000000c0000000 -- 050:0000c0000000c0000000c0000000c0000000c0000000c0000000c0000000c000 -- 051:000ccc00000ccc00000ccc00cc3ccc3ccc3ccc3ccc3ccc3c000ccc00000ccc00 -- 052:000000000000000000000000cccccccccccccccccccccccc0000000000000000 -- 064:c0000000cccccccc000000000000000000000000000000000000000000000000 -- 065:00000000cccccccc000000000000000000000000000000000000000000000000 -- 066:0000c000ccccc000000000000000000000000000000000000000000000000000 -- -- -- 000:00c3c3000024e763005abdf70018fff7005affe30099bdc10024668000c3c300 -- 001:80808000c1c1c100e3a2e300f7f7f7c0e3a2f7c0c180800080c1c10000000000 -- 002:ffc13e0fff22dd0cff14eb0a7e14ebe97e14eb11ff22dd11ffc13e11ff00ffe0 -- 003:c1c0c380224142a22242c3c122424277c14142c1804072a2e370b38080308100 -- 016:100880f3300c8021700e8021f00fe321700e8071300c8001100880a000008040 -- 017:e3c100405120004051c000e06121c3404121c3e041c000404101004000e00000 -- 018:ff000c30ff000c3000000c3000000c3000000c3000000c3000ff0c3000ff0c30 -- 019:000000f7804000e380a000c1ff11008080a080008040c1000000e3000000f700 -- 032:0080414100804141008041e300800041000000e3000000410080004100000000 -- 033:80604001c362a080a0014040c180a20082402100e12323008003c00000000000 -- 034:014080008080a2804001c180400180e34001c1808080a2800140800000000000 -- 035:00000000000000800000008000c30040000000408000c0208000c02040000000 -- 048:c180c1c122c0222223a00202a28001816280c00222802022c1e3e3c100000000 -- 049:01e381e38120402241e020012101e180e30222800101228001e0c18000000000 -- 050:c1c100002222000022228080c1c300002202000022018080c1c0008000000040 -- 051:030060c18100c022c0e3810260000301c0e381808100c0000300608000000000 -- 064:c180e1812241424202224220c222c120a2e34220a2224242c122e18100000000 -- 065:e0e3e3c1412020224220202042e1e1a34220202241202022e0e320c100000000 -- 066:22c1832222800121228001a0e3800160228021a02280212122c1c02200000000 -- 067:202222c12063622220a2622220a2a2222022232220222322e32222c100000000 -- 080:e1c1e1c12222222222222220e122e1c120a2a0022021212220c222c100000000 -- 081:e32222228022222280222222802222a2802241a28022416380c1802200000000 -- 082:2222e3c1222202404122014080c1804041804040228020402280e3c100000000 -- 083:00c18000000141002001220040010000800100000101000002c100e300000000 -- 096:400020008000200001c1a1c10002622200c322200022622200c3a1c100000000 -- 097:0200010002008200c2c180c22322e32322e38023232080c2c2c18002000000c1 -- 098:008001404000004040c08142c1800141428001c04280014142c121420000c000 -- 099:c0000000800000008061a1c180a2622280a2222280a22222c1a222c100000000 -- 112:0000000000000000a1c2a1c362236220622320e1a1c22002200220e120020000 -- 113:4000000040000000e1212222402122a2402122a2422141a281c2804100000000 -- 114:00000003000000802222e380412201408023808041c240802202e30300c10000 -- 115:806040008080a2008080018000010041808000e3808000008060000000000000 -- 128:c1210180220080412000c1c1202122022221e3c3c121202280c2c1c3c0000000 -- 129:42808000000100c1c1c1c12002020220c3c3c320222222c1c3c3c301000000c0 -- 130:8041804141000100c1c1c100222222c0e3e3e38020202080c1c1c1c100000000 -- 131:804041804180000000008080c0c04141808022228080e3e3c1c1222200000000 -- 144:01008fc080004121e3c62100200927c0e1cfe12120212121e3ce2fc000000000 -- 145:214040400080a08000000000c0c0a0a02121a0a02121a0a0c0c0414100000000 -- 146:214141800000008021c122c321222220a1222220412222c301c1c180e0000080 -- 147:032260038441a0808080a080e3e362e380802780e8e322804780228000002640 -- 160:0101808080804040c100000002c0c021c380212122802121c3c1c0c200000000 -- 161:41820000a041c1c000000221a062c32161a222212123c3c0212200000000e3e1 -- 162:80000024000000228000002140f1f1a22010014522100124c100000200000007 -- 163:248000002200000021008421a280424243802184a28042428780842102000000 -- 176:88ccee8044ccdd802233bb801133778088ccee8044ccdd802233bb8011337780 -- 177:808041008080410080f04100f08071f180f04141808041418080414180804141 -- 178:0041410000414100f07141f180014101f0714171804141418041414180414141 -- 179:41418000414180007141f00001f180f0f100f080000000800000008000000080 -- 192:8080008080800080808000808fffff8f00008080000080800000808000008080 -- 193:008080410080804100808f41ffff804f00808f41008080410080804100808041 -- 194:82004100820041008ecf7fff804000008f4fff7f004100410041004100410041 -- 195:41004180410041804fff7fff400000004fff7fff410041004100410041004100 -- 208:410000434100004141ff0041ff00ffcf00ff4100008041000080410000804100 -- 209:80000041800000418f8f00418080cfff8f8f4141008041410080414100804141 -- 210:808000ff808000ffff8000ff80f08fffff0080ff800080ff800080ff800080ff -- 211:00f00fff00f00fff00f00fff00f00ffffff00f00fff00f00fff00f00fff00f00 -- 224:0081e3e300422241c242204121c120412142204121422041c2c1202300600000 -- 225:e30000002200414140c341a080214180402141802221c280e3c0208000002000 -- 226:e380c18180412240c1222240a2e32280a2224141c141414180806341e3000080 -- 227:000283c100c1402200a2202241a2e322a2a22022a2c140224120832200000000 -- 240:00806003e38081c000e30220e38081c000806003e300000000e3e3e300000000 -- 241:0180000082808041808000a08080e30080800041808080a080a0000080400000 -- 242:0000008f0381008084c3008084c30080038181a0000000c00000008000000000 -- 243:a040000041a08300418083004140830000e08300000083000000830000000000 -- -- -- 000:00000000ffffffff00000000ffffffff -- 001:0123456789abcdeffedcba9876543210 -- 002:0123456789abcdef0123456789abcdef -- -- -- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000 -- -- -- 000:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 001:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 002:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 003:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 004:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 005:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 006:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 007:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 008:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 010:000000000000000000000000000000000000000000000000cc000000cc00cc000000cc00cc000000cc000000cccc0000cc000000cc00cc0000000000cccccccccc00cccccccc000000cccccc0000cc000000cc00cccccccccc0000cccccc0000000000000000000000000000000000000000000000000000 -- 011:000000000000000000000000000000000000000000000000cc000000cc00cc000000cc00cc000000cc000000cccc0000cc000000cc00cc0000000000cccccccccc00cccccccc000000cccccc0000cc000000cc00cccccccccc0000cccccc0000000000000000000000000000000000000000000000000000 -- 012:000000000000000000000000000000000000000000000000cc0000cc0000cccc0000cc00cc000000cc0000cc0000cc00cc0000cc0000cc0000000000cc000000000000cc0000cc00cc000000cc00cccc0000cc00cc0000000000cc000000cc00000000000000000000000000000000000000000000000000 -- 013:000000000000000000000000000000000000000000000000cc0000cc0000cccc0000cc00cc000000cc0000cc0000cc00cc0000cc0000cc0000000000cc000000000000cc0000cc00cc000000cc00cccc0000cc00cc0000000000cc000000cc00000000000000000000000000000000000000000000000000 -- 014:000000000000000000000000000000000000000000000000cc00cc000000cccc0000cc00cc000000cc00cc0000000000cc00cc000000cc0000000000cc000000000000cc0000cc00cc000000cc00cccc0000cc00cc0000000000cc0000000000000000000000000000000000000000000000000000000000 -- 015:000000000000000000000000000000000000000000000000cc00cc000000cccc0000cc00cc000000cc00cc0000000000cc00cc000000cc0000000000cc000000000000cc0000cc00cc000000cc00cccc0000cc00cc0000000000cc0000000000000000000000000000000000000000000000000000000000 -- 016:000000000000000000000000000000000000000000000000cccc00000000cc00cc00cc00cc000000cc00cc0000000000cccc00000000cc0000000000cccccccc000000cccccc0000cc000000cc00cc00cc00cc00cccccccc000000cccccc0000000000000000000000000000000000000000000000000000 -- 017:000000000000000000000000000000000000000000000000cccc00000000cc00cc00cc00cc000000cc00cc0000000000cccc00000000cc0000000000cccccccc000000cccccc0000cc000000cc00cc00cc00cc00cccccccc000000cccccc0000000000000000000000000000000000000000000000000000 -- 018:000000000000000000000000000000000000000000000000cc00cc000000cc0000cccc00cc000000cc00cc0000000000cc00cc000000cc0000000000cc000000000000cc0000cc00cc000000cc00cc0000cccc00cc000000000000000000cc00000000000000000000000000000000000000000000000000 -- 019:000000000000000000000000000000000000000000000000cc00cc000000cc0000cccc00cc000000cc00cc0000000000cc00cc000000cc0000000000cc000000000000cc0000cc00cc000000cc00cc0000cccc00cc000000000000000000cc00000000000000000000000000000000000000000000000000 -- 020:000000000000000000000000000000000000000000000000cc0000cc0000cc0000cccc00cc000000cc0000cc0000cc00cc0000cc0000cc0000000000cc000000000000cc0000cc00cc000000cc00cc0000cccc00cc0000000000cc000000cc00000000000000000000000000000000000000000000000000 -- 021:000000000000000000000000000000000000000000000000cc0000cc0000cc0000cccc00cc000000cc0000cc0000cc00cc0000cc0000cc0000000000cc000000000000cc0000cc00cc000000cc00cc0000cccc00cc0000000000cc000000cc00000000000000000000000000000000000000000000000000 -- 022:000000000000000000000000000000000000000000000000cc000000cc00cc000000cc0000cccccc00000000cccc0000cc000000cc00cccccccccc00cccccccccc00cccccccc000000cccccc0000cc00f000cc00cccccccccc0000cccccc0000000000000000000000000000000000000000000000000000 -- 023:000000000000000000000000000000000000000000000000cc000000cc00cc000000cc0000cccccc00000000cccc0000cc000000cc00cccccccccc00cccccccccc00cccccccc000000cccccc0000cc0ffff0cc00cccccccccc0000cccccc0000000000000000000000000000000000000000000000000000 -- 024:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 025:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000fffffff00000000000000000000000000000000000000000000000000000000000000000000 -- 026:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000ffffffff0000000000000000000000000000000000000000000000000000000000000000000 -- 027:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff0fffffffff0000000000000000000000000000000000000000000000000000000000000000000 -- 028:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000 -- 029:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000ffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000 -- 030:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff00000000000000000000000000000000000000000000000fffffffff0ffffffff00000000000000000000000000000000000000000000000000000000000000000 -- 031:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffff0000000000000000000000000000000000000000000000ffffffff0000ffffff00000000000000000000000000000000000000000000000000000000000000000 -- 032:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0ffff0000000000000000000000000000000000000000000000fffffff0000ffffff00000000000000000000000000000000000000000000000000000000000000000 -- 033:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff000ffff00000000000000000000000000000000000000000000fffffff0000ffff0000000000000000000000000000000000000000000000000000000000000000000 -- 034:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff000fffff00000000000000000000000000000000000000000000fffffffffffff00000000000000000000000000000000000000000000000000000000000000000000 -- 035:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffff000ffffff0000000000000000000000000000000000000000000fffffffffff0000000000000000000000000000000000000000000000000000000000000000000000 -- 036:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffff0000000000000000000000000000000000000000000ffffffff000000000000000000000000000000000000000000000000000000000000000000000000 -- 037:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffff000000000000000000000000000000000000000000fffff00000000000000000000000000000000000000000000000000000000000000000000000000 -- 038:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffff00fff0000ff000000000000000000000000000000000000000000fff0000000000000000000000000000000000000000000000000000000000000000000000000000 -- 039:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff000ff0000ff000ffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 040:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff000ff0000ff000fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 041:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000fff00ffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 042:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 043:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffff0000000000000000000000000000000000088888800000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 044:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff000fffff000000000000000000000000000000000008d8dd880000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 045:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffff000ffff0000000000000000000000000000000000008dd88d88888800000000000000000000000000000000000000000000000000000000000000000000000000000 -- 046:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff000fff00000000000000000000000000000000000008d888888ddd888808880000000000000008880000000000000000000000000000000000000000000000000000 -- 047:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff0ff000000000000000000000000000000000000008d80008d888d8d888d88888800000000008d80000000000000000000000000000000000000000000000000000 -- 048:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffff0000000000000000000000000000000000000008d80008ddddd8d8d8d88ddd888888808888d80000000000000000000000000000000000000000000000000000 -- 049:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff0000000000000000000000000000000000000008880008d88888d8d8d88888d8d8dd888dd8d80000000000000000000000000000ffffffffffffff0000000000 -- 050:00000000000000000000000000000000000000000000000000000d00000000000000000000000000000000000000000000d0000000000f000000000000000000000000000000000000d000000000088ddd88d8d8d88dddd8dd88d8d88dd8000000000000000000000000000ffffffffffffffff000000000 -- 051:0000000000000000000000000000000000000000000000000000d0d000000000000000000000000000000000000000000d0d0000000000000000000011100000000000000000000000d000000000008888888d8d88d888d8d88888d888d8000000000000000000000000000fff00fffffffffff000000000 -- 052:000000000000000000000000000000000000000000000000000d000d000000dd0d00ddd00dd0d000ddd0000000ddd0000d00000000000000000000001d100000000ddd00d0dd000dd0d000000000000000008888888dddd8d80008d88dd8000000000000000000000000000ff0000ffffffffff000000000 -- 053:000000000000000000000000000000000000000000000000000d000d00000d00dd00000d0d0d0d0d000d00000d000d0ddddd000000000001110111111d111100000000d0dd00d0d00dd00000000000000000000000888888d800088dd8d8000000000000000000000000000ff0000ffffffffff000000000 -- 054:000000000000000000000000000000000000000000000000000ddddd00000d00dd00dddd0d0d0d0ddddd00000d000d000d00000000000001d111dddd1d11d100000dddd0d000d0d000d00000000000000000000000000008880000888888000000000000000000000000000fff00fffffffffff000000000 -- 055:000000000000000000000000000000000000000000000000000d000d000000dd0d0d000d0d0d0d0d000000000d000d000d00000000000011111d11111d1d110000d000d0d000d0d00dd00000000000000000000000000000000000000000000000000000000000000000000fffffff00fffffff000000000 -- 056:000000000000000000000000000000000000000000000000000d000d000000000d00dddd0d0d0d00ddd0000000ddd0000d0000001111111dd11dddd11dd11000000dddd0d000d00dd0d00000000000000000000000000000000000000000000000000000000000000000000ffffff0000ffffff000000000 -- 057:00000000000000000000000000000000000000000000000000000000000000ddd0000000000000000000000000000000000000001d1dd111d111111d1d1d1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000ffffff000000000 -- 058:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001dd11d11d11dddd11d11d100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffff00fffffff000000000 -- 059:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d111111d111111111111100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffff00fff000000000 -- 060:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d10001ddd10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffff0000ff000000000 -- 061:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d1000111110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffff0000ff000000000 -- 062:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000111000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffff00fff000000000 -- 063:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff000000000 -- 064:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff0000000000 -- 073:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 074:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 075:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 076:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff0000ffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 077:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff0000ffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 078:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffff0fffffff0ff000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 079:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff000fff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 080:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff000ffffff0000ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 081:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff0000fffff0000ff000000000000000000000000000000000000f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 082:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff000fffffffffff000000000000000000000000000000000000ffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 083:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffff000ffff000000000000000000000000000000000000fffffffffffffff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 084:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000fffff0000fff0000000000000000000000000000000000000fff00ffffff0ffff00000000000000000000000000000000000000000000000000000000000000000000000000 -- 085:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffffff000fff000000000000000000000000000000000000ff0000ffff000fff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 086:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff000ffffff00fff0000000000000000000000000000000000000fff000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 087:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0fffffff0fffff0000000000000000000000000000000000000fff00ffffff000ff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 088:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000fff00000000000000000000000000000000000000fffffff00fffffff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 089:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff0000fff00000000000000000000000000000000000000ffffff0000ffffff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 090:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ff000000000000000000000000000000000000000ffffff0000ffffff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 091:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff000000000000000000000000000000000000000fffffff00fffffff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 092:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff0000000000000000000000000000000000000000ff000ffffff00fff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 093:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff000fff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 094:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff000ffff0000ff000000000000000000000000000000000000000000000000000000000000000000000000000 -- 095:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffff0ffffff00fff0000000000000000000000000000000000000000000000000000000000000000000000000000 -- 096:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000 -- 097:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000 -- 098:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f00000000000000000000000000000000000000000000000000000000000000000000000000000 -- 100:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dddd00dd000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 101:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000d00d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 102:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000d00d000ddd00d000d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 103:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dddd000d000000d0d000d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 104:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000d000dddd0d00dd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffff -- 105:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d000000d00d000d00dd0d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 106:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d00000ddd00dddd00000d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 107:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ddd0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 108:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 109:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 110:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffff00fffff -- 111:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000ffff -- 112:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffff0000ffff -- 113:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffff00fffff -- 114:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 115:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 116:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 117:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 118:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff -- 119:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000fffffffffffff -- 123:00000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 124:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 125:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 126:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 127:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 128:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 129:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- 130:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff0000e000000e00000e000000000ee00ee0000000000e000000000000000e0e0eee0e0000000000ee00000ee000e0000000000e0000e00e000000000e00000000000e000e0e0 -- 131:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff000e0e0e0e0000ee000ee00ee000e000e00e0e0000e00e0e00e00eee000e0e00e00ee000ee000e000e0e00e00eee0000e000e0000eee0ee000ee000e000ee00eee0ee00e0e0 -- 132:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff000e0e0ee00e0e0e0e0e0e00ee00e000e00e0e000eee0ee00e0e0eee00000000e00e0e0e0e000e000e0e00e000e0000e0e0eee0000e00e0e0e0e000e0000ee0eee0e0e00000 -- 133:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffff000e0e0e000e0eee0e0e0e0e0e00e000e000ee0000e00e000e0e0e0e00000000e00e0e0ee0000e000e0e00e000e0000e0e00e00000e00e0e0ee0000e000e0e0e0e0e0e00000 -- 134:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000fff00ffffff00fff0000e00e000e000e0e0e0e0eee0eee0eee000e0000e00e0000e00e0e00000000e00e0e00ee0000ee00ee0eee000e0000e000e000000e0e0e00ee000eee0eee0e0e0ee000000 -- 135:0000000000000000000000000000000000000000000000000000000000000000000000000000000000000ff0000ffff0000ff00000000000000e000000000000000000000e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 -- -- -- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57 --