2524 lines
100 KiB
Lua
2524 lines
100 KiB
Lua
--
|
|
-- 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
|
|
|
|
-- <TILES>
|
|
-- 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
|
|
-- </TILES>
|
|
|
|
-- <SPRITES>
|
|
-- 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
|
|
-- </SPRITES>
|
|
|
|
-- <WAVES>
|
|
-- 000:00000000ffffffff00000000ffffffff
|
|
-- 001:0123456789abcdeffedcba9876543210
|
|
-- 002:0123456789abcdef0123456789abcdef
|
|
-- </WAVES>
|
|
|
|
-- <SFX>
|
|
-- 000:000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000304000000000
|
|
-- </SFX>
|
|
|
|
-- <SCREEN>
|
|
-- 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
|
|
-- </SCREEN>
|
|
|
|
-- <PALETTE>
|
|
-- 000:1a1c2c5d275db13e53ef7d57ffcd75a7f07038b76425717929366f3b5dc941a6f673eff7f4f4f494b0c2566c86333c57
|
|
-- </PALETTE>
|
|
|