vacation/vacation.p8
Kistaro Windrider 135d6ea4eb
use a font small enough it might be possible to miss sometimes
packing two custom fonts into the cartridge is going to be a problem
for character count and compressed size. I'll figure something out
2024-02-05 01:24:42 -08:00

859 lines
25 KiB
Lua
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

pico-8 cartridge // http://www.pico-8.com
version 41
__lua__
-- vacation (18+)
-- kistaro windrider
--------------------------------
-- copyright (C) 2024 kistaro windrider
--
-- this program is free software; you can redistribute it and/or modify
-- it under the terms of the gnu general public license as published by
-- the free software foundation; either version 2 of the license, or
-- (at your option) any later version.
--
-- this program is distributed in the hope that it will be useful,
-- but without any warranty; without even the implied warranty of
-- merchantability or fitness for a particular purpose. see the
-- gnu general public license for more details.
--------------------------------
-- addtional credits
-- dogica font by roberto mocci
-- https://www.dafont.com/es/dogica.font
--
-- converted by josれた aular
-- https://jaular.itch.io/pico-8-dogica-font
-- tab 0: library
-- tab 1: p8 entry points
-- tabs 2...F: actual code
function usplit(str)
return unpack(split(str))
end
function csv(s)
local ret = split(s, "\n")
for i, v in ipairs(ret) do
ret[i] = type(v) == "string" and split(v) or { v }
end
return ret
end
-- generate standard "overlay"
-- constructor for type tt.
-- if more is defined, generated
-- new calls more(ret) after
-- ret is definitely not nil
-- before calling setmetatable.
-- use to initialize mutables.
--
-- if there was a previous new,
-- it is invoked on the new
-- object *after* more, because
-- this works better with the
-- `more` impls i use.
function mknew(tt, more)
local mt, oldnew={__index=tt},tt.new
tt.new=function(ret)
if (not ret) ret = {}
if (more) more(ret)
if (oldnew) oldnew(ret)
setmetatable(ret, mt)
return ret
end
end
event_list = {is_event_list=true}
mknew(event_list, function(x)
x.next=nil
x.tail=x
end)
function event_list:push_back(x)
self.tail.next = x
self.tail = x
end
function event_list:update()
local p, n = self, self.next
while n do
if n:update() then
p.next = n.next
else
p = n
end
n = n.next
end
self.tail = p
return p
end
function event_list:draw()
local n = self.next
while n do
n:draw()
n = n.next
end
end
-- if t[mname] is a thing,
-- invoke it as a method. else,
-- try each object in t
function outer_or_each_opt(t, mname)
local fun = t[mname]
if fun then
fun(t)
return
end
foreach(t, function(o)
local f = o[mname]
if(f) f(o)
end)
end
function nop() end
-- puke emits a verbose string
-- describing item, indented to
-- the specified depth (0 by
-- default). used for table
-- debugging. table-type keys
-- are not legible here
function puke(item, indent, seen, hidekey)
if (type(item) ~= "table") return tostr(item)
seen = seen or {}
if (seen[item]) return "<<...>>"
seen[item] = true
indent = indent or 0
local pfx = "\n"
for _=1,indent do
pfx ..= " "
end
local xpfx = pfx.." "
if item.is_event_list then
local ret,n = "event_list <",0
item:strip(function(x)
n += 1
ret ..= xpfx..tostr(n)..": "..puke(x, indent+2, seen, "next")
end)
return ret..pfx..">"
end
local ret = "{"
for k, v in pairs(item) do
if (k ~= hidekey) ret ..= xpfx..tostr(k)..": "..puke(v, indent+2, seen)
end
return ret..pfx.."}"
end
-- convenience for debugging
function puketh(item, ...)
printh(puke(item), ...)
end
function pukeboard(item)
puketh(item, "@clip")
end
-------------------------------
-- view
-------------------------------
-- composits drawable items.
-- add items to .views to
-- composit them. x and y are
-- relative reverse camera
-- offsets. drawable items will
-- observe appropriate incoming
-- camera and clip state.
-- clipping respects existing
-- clipping so stacked views
-- intersect.
-------------------------------
view = {
x=0,
y=0,
w=128,
h=128,
}
mknew(view, function(x)
if (not x.views) x.views = {}
end)
function view.of(subviews)
return view.new{views=subviews}
end
function view:update()
outer_or_each_opt(self.views, "update")
end
function view:draw()
local oldcam, oldclip = $0x5f28, $0x5f20
poke2(0x5f28, %0x5f28-self.x)
poke2(0x5f2a, %0x5f2a-self.y)
clip(-%0x5f28, -%0x5f2a, self.w, self.h, true)
outer_or_each_opt(self.views, "draw")
poke4(0x5f20, oldclip)
poke4(0x5f28, oldcam)
end
-- draws opaque rectangles.
-- default bg is equivalent to
-- clip-aware cls.
-- restores prior fill pattern.
bg = {
x=0,y=0,w=128,h=128,fp=0,c=0
}
mknew(bg)
function bg:draw()
local oldfp=fillp(self.fp)
rectfill(self.x,self.y,self.x+self.w,self.y+self.h,self.c)
fillp(oldfp)
end
-->8
-- setup and p8 entry points
function _init()
-- custom font: dogica
poke(0x5600,unpack(split"6,8,9,0,0,1,0,0,0,0,0,0,0,0,0,0,69,16,32,81,85,0,117,116,0,0,0,0,0,84,22,6,2,0,0,0,96,6,38,0,0,7,7,16,112,87,80,16,5,0,1,0,112,7,39,0,0,0,0,32,0,112,116,1,0,0,96,102,96,0,96,96,0,96,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,1,0,0,10,10,5,0,0,0,0,0,18,63,18,18,63,18,0,0,14,21,5,14,20,21,14,34,21,21,10,40,84,84,34,6,9,9,6,41,17,46,0,0,2,2,1,0,0,0,0,2,1,1,1,1,1,2,0,1,2,2,2,2,2,1,0,0,4,21,10,21,4,0,0,0,4,4,31,4,4,0,0,0,0,0,0,0,2,2,1,0,0,0,0,15,0,0,0,0,0,0,0,0,0,1,0,8,8,4,4,2,2,1,1,14,17,21,21,21,17,14,0,4,7,4,4,4,4,31,0,14,17,16,8,4,2,31,0,15,16,16,14,16,16,15,0,8,12,10,9,31,8,8,0,31,1,15,16,16,17,14,0,14,17,1,15,17,17,14,0,31,17,16,8,4,2,2,0,14,17,17,14,17,17,14,0,14,17,17,30,16,8,6,0,0,0,1,0,0,1,0,0,0,0,0,2,0,2,2,1,0,4,2,1,2,4,0,0,0,0,63,0,63,0,0,0,0,1,2,4,2,1,0,0,14,17,16,12,2,0,4,0,30,33,45,41,29,65,62,0,0,14,16,30,17,17,30,0,1,1,15,17,17,17,15,0,0,14,17,1,1,1,30,0,16,16,30,17,17,17,30,0,0,14,17,31,1,1,30,0,28,2,2,15,2,2,2,0,0,30,17,17,17,30,16,14,1,1,13,19,17,17,17,0,2,0,3,2,2,2,7,0,0,7,2,2,2,2,2,1,0,17,9,5,7,9,17,0,0,1,1,1,1,1,6,0,0,54,73,73,73,73,73,0,0,12,19,17,17,17,17,0,0,14,17,17,17,17,14,0,0,15,17,17,17,15,1,1,0,30,17,17,17,30,16,16,0,13,3,1,1,1,1,0,0,14,1,14,16,17,14,0,1,1,15,1,1,1,14,0,0,17,17,17,17,25,22,0,0,17,17,17,10,10,4,0,0,32,37,37,37,37,26,0,0,17,10,4,4,10,17,0,0,0,9,9,9,6,4,3,0,15,8,4,2,1,15,0,3,1,1,1,1,1,3,0,1,1,2,2,4,4,8,8,3,2,2,2,2,2,3,0,4,10,17,0,0,0,0,0,0,0,0,0,0,0,0,63,0,1,2,2,0,0,0,0,14,17,17,17,31,17,17,0,15,17,17,15,17,17,15,0,14,17,1,1,1,17,14,0,31,34,34,34,34,34,30,0,31,1,1,15,1,1,31,0,31,1,1,15,1,1,1,0,30,1,1,25,17,17,30,0,17,17,17,31,17,17,17,0,7,2,2,2,2,2,7,0,15,4,4,4,4,4,3,0,17,9,5,7,9,9,17,0,1,1,1,1,1,1,15,0,33,33,51,45,33,33,33,0,17,19,21,25,17,17,17,0,14,17,17,17,17,17,14,0,15,17,17,15,1,1,1,0,14,17,17,17,21,9,22,0,15,17,17,15,9,17,17,0,14,17,1,14,16,17,14,0,31,4,4,4,4,4,4,0,17,17,17,17,17,17,14,0,17,17,17,17,10,10,4,0,65,65,73,73,73,85,34,0,17,17,10,4,10,17,17,0,17,17,17,10,4,4,4,0,31,16,8,4,2,1,31,0,4,10,2,1,1,2,10,4,1,1,1,1,1,1,1,1,2,5,4,8,8,4,5,2,0,0,0,38,25,0,0,0,0,0,0,0,0,0,0,0,0,0,127,127,127,127,127,0,0,0,85,42,85,42,85,0,0,0,65,127,93,93,62,0,0,0,62,99,99,119,62,0,0,0,17,68,17,68,17,0,0,0,2,30,14,15,8,0,0,0,14,23,31,31,14,0,0,0,27,31,31,14,4,0,0,0,28,54,119,54,28,0,0,0,14,14,31,14,10,0,0,0,28,62,127,42,58,0,0,0,62,103,99,103,62,0,0,0,127,93,127,65,127,0,0,0,28,4,4,7,7,0,0,0,62,99,107,99,62,0,0,0,4,14,31,14,4,0,0,0,0,0,85,0,0,0,0,0,62,115,99,115,62,0,0,0,8,28,127,62,34,0,0,0,31,14,4,14,31,0,0,0,62,119,99,99,62,0,0,0,0,5,82,32,0,0,0,0,0,17,42,68,0,0,0,0,62,107,119,107,62,0,0,0,127,0,127,0,127,0,0,0,85,85,85,85,85,0"))
-- to use custom fonts, poke(0x5f58, 0x81)
-- disable btnp repeat
-- TODO: set back to 30 frames
-- outside of game mode
poke(0x5f5c, 255)
-- complex fill API mode
poke(0x5f34, 1)
mainview = newtitle()
end
function _update60()
mainview:update()
end
function _draw()
mainview:draw()
end
-->8
-- text rendering
-- text colors for zonk mode:
-- 8 -- standard
-- 9 -- delayed fade
-- 10 -- currently fading in
txtbox = {
x=0,
y=0,
text="???",
col=8,
mode=0x81,
}
mknew(txtbox)
function txtbox:update() end
function txtbox:draw()
poke(0x5f58, self.mode)
print(self.text, self.x, self.y, self.col)
end
function txtbox:xmax()
return print(self.text, self.x, -9999)
end
spring = {
from = 128,
to = 0,
frames=120,
f = 0,
}
mknew(spring)
function spring:update()
local v = self.v
self.v:update()
if self.f >= self.frames then
v.y=self.to
return true
end
local t, range = self.f/self.frames, self.to - self.from
v.y = self.to-range*(2^(-10*t)*cos(2*t))
self.f += 1
end
function spring:draw()
self.v:draw()
end
scoot = {
from=0,
to=-128,
frames=60,
f=0,
}
mknew(scoot)
function scoot:update()
local v = self.v
self.v:update()
if self.f >= self.frames then
v.y=self.to
return true
end
self.f += 1
if self.f < 0 then
v.y=self.from
return
end
local t, range = self.f/self.frames, self.to - self.from
v.y = self.from + range * t * t * t
end
function scoot:draw()
self.v:draw()
end
scootbox = {}
mknew(scootbox, function(x)
x.v = view.new()
x.s = scoot.new{
from=x.from or scoot.from,
to=x.to or scoot.to,
frames=x.frames or scoot.frames,
v=x.v
}
end)
function scootbox:update()
if self.go then
self.s:update()
else
self.v:update()
end
end
function scootbox:push(drawable)
add(self.v.views, drawable)
end
function scootbox:done()
return self.s.f >= self.s.frames
end
function scootbox:draw()
return self.v:draw()
end
-->8
-- zonk renderer
-->8
-- awakener
-->8
-- consent screens
-->8
-- title screen
-- currently just loading
-- whatever view I want to debug
function newtitle()
return arcade_level.new()
end
-->8
-- dolphin sprite renderer
phinstate_nrm = {
s={4, 36, 4, 9},
ws=3,
hs=2,
idle=true,
xo=-12,
yo=-8,
}
phinstate_jump_full = {
s={7},
ws=2,
hs=3,
xo=-4,
yo=-8,
}
phinstate_jump_wane = {
s={1},
ws=3,
hs=3,
xo=-12,
yo=-8,
}
phinstate_crest = {
s={4},
ws=3,
hs=2,
xo=-12,
yo=-8,
}
phinstate_fall_wax = phinstate_jump_wane
phinstate_fall_full = phinstate_jump_full
phinstate_dive_full = {
s={7},
ws=2,
hs=3,
xo=-4,
yo=-16,
}
phinstate_dive_wane = {
s={1},
ws=3,
hs=3,
xo=-12,
yo=-16,
}
phinstate_return = {
s={4},
ws=3,
hs=2,
xo=-12,
yo=-8,
}
phinstate_rise_wax = phinstate_dive_wane
phinstate_rise_full = phinstate_dive_full
phinstate_error = {
s={0},
ws=1,
hs=2,
}
-- coordinates are the notional
-- center point of the dolphin.
-- many states are off-center.
toyphin = {
x=-12,
y=64,
dy=0,
state=phinstate_nrm
}
mknew(toyphin)
function toyphin:update()
local x, y, dy, splash = self.x, self.y, self.dy, self.splasher
-- entry mode?
if not self.entered then
x += 1
self.entered = x >= 16
elseif self.exiting then
if x > 128 then
self.exited = true
else
x += 1
end
end
-- button handling
if self.entered and not self.exiting then
if y >= 61 and y <= 67 and dy < 1 and dy > -1 then
if (btn(2)) then
splash:jump_splash(x)
dy=-3.8
elseif (btn(3)) then
splash:dive_splash(x)
dy=3.8
end
else
dy += (btn(3) and 0.125 or 0) - (btn(2) and 0.125 or 0)
end
end
if (y > 64) dy -= 0.3
if (y < 64) dy += 0.3
local new_y = y + dy
if new_y <= 64 and y > 64 then
-- surfacing
splash:surfacing_splash(x, -dy, btn(2) and (dy > -3.8))
if btn(2) then
-- maybe boost
if dy > -3.8 then
new_y = 64 + ((dy + y - 64)/dy * -3.8)
dy = -3.8
else
dy = (dy - 7.6) / 3
end
else
-- brake
if dy > -1 then
--stabilize
new_y = 64
dy = 0
else
dy /= 2
end
end
elseif new_y >= 64 and y < 64 then
-- landing
splash:landing_splash(x, dy, btn(3) and (dy < 3.8))
if btn(3) then
-- maybe boost
if dy < 3.8 then
new_y = 64 - ((dy - y + 64)/dy * 3.8)
dy = 3.8
else
dy = (7.6 + dy) / 3
end
else
--brake
if dy < 1 then
--stabilize
new_y = 64
dy = 0
else
dy /= 2
end
end
end
y=new_y
local wet, st = y > 64, phinstate_error
if dy < -2.5 then
st = wet and phinstate_rise_full or phinstate_jump_full
elseif dy <= -1.5 then
st = wet and phinstate_rise_wax or phinstate_jump_wane
elseif dy < 1.5 then
-- handle idle special case later
st = wet and phinstate_return or phinstate_crest
elseif dy <= 2.5 then
st = wet and phinstate_dive_wane or phinstate_fall_wax
else
st = wet and phinstate_dive_full or phinstate_fall_full
end
if (y == 64 and dy == 0) st = phinstate_nrm
-- test mode
--if (btn(5)) self.exiting = true
self.x, self.y, self.dy, self.state = x, y, dy, st
end
-- hitbox for current state
function toyphin:box()
local st = self.state
return {self.x + st.xo, self.y + st.yo, st.ws * 8, st.hs * 8}
end
function toyphin:draw()
local st, y = self.state, self.y
if (st.idle) y += wave()
spr(st.s[1+(((t()<<1)&0x0.FFFF*#st.s)&0x7FFF)], self.x + st.xo, y + st.yo, self.state.ws, self.state.hs)
end
-->8 word target
wordtarget = {
x = 129,
y = 60,
str = "GOOD TOY!",
on_hit = nop,
}
mknew(wordtarget, function(x)
poke(0x5f58, 0x81)
x.w = print(x.str or wordtarget.str, 0, -9999)-1
end)
function collides(b1, b2)
if (b1[1] > b2[1] + b2[3]) return false
if (b1[1] + b1[3] < b2[1]) return false
if (b1[2] > b2[2] + b2[4]) return false
return not (b1[2] + b1[4] < b2[2])
end
function wordtarget:update()
if collides({self.x, self.y, self.w, 7}, self.phin:box()) then
self:on_hit()
return true
end
self.x -= 1
return self.x < -self.w
end
function wordtarget:draw()
poke(0x5f58, 0x81)
-- debug: show hitbox
--rect(self.x-1, self.y-1, self.x+self.w+1, self.y + 8, 0x10F1.5a5a)
print(self.str, self.x+1, self.y+1, 0x100b)
print(self.str, self.x, self.y, 0x100a)
end
-->8
-- arcade mode
-- palette use:
-- 0: shallow sea blue (1)
-- 1: black (for sprites)
-- 2: dolphin shading
-- 3: azure water, maybe score display? (140)
-- 4, 5, 6: unassigned, layer-specific
-- 7: dolphin highlights
-- 8, 9: unassigned, layer specific
-- 10: word primary (yellow 10?)
-- 11: word shadow (wood 132?)
-- 12: dolphin eye
-- 13: common sky color
-- 14: dolphin primary color
-- 15: highlight white (all layers)
--
-- dolphin colors are different
-- between zones; correlated.
-- wave, text, and black colors
-- are the same between zones.
-- other colors are for
-- elements that only ever
-- appear in one zone.
--
-- TODO: consider changing sky
-- colors in different stages;
-- document what colors those
-- are (nrm_pal) if so.
game_nrm_pal = {
[0] = 1, 0, 2, 140, 4, 5, 6, 7, 8, 9, 10, 132, 12, 12, 14, 7
}
-- undersea palette local decisions:
-- 4: deeper sea blue (129)
game_uw_pal = {
[0]=1, 0, 130, 140, 129, 5, 6, 13, 8, 9, 10, 132, 131, 12, 141, 7
}
function setup_arcade_pal()
-- per-line color mode
poke(0x5f5f, 0x10)
-- rows 72 and lower: sea
memset(0x5f79,0xff,7)
pal()
pal(game_nrm_pal, 1)
pal(game_uw_pal, 2)
end
-- global wave amplitude clock
-- wave per 2 seconds
-- optional toff: offset. at
-- toff==0 draw directly under
-- the dolphin
function wave(toff)
toff = toff or 0
return 2.5 * sin((t()+toff)>>1)
end
sea = {}
mknew(sea)
function sea:draw()
local w = wave()
rectfill(0, 72+w, 128, 80+w, 0)
poke2(0x5f78, 0xFF00.00FF <<> w)
rectfill(0, 81+w, 128, 89+w, 0x1040.a842)
rectfill(0, 90+w, 128, 97+(w>>1), 0x1004.e169)
rectfill(0, 98+(w>>1), 128, 104+(w>>2), 0x1004.a842)
rectfill(0, 104+(w>>2), 128, 110+(w>>2), 0x1004)
rectfill(0, 111+(w>>2), 128, 118+(w>>3), 0x1041.e169)
rectfill(0, 119+(w>>3), 128, 124+(w>>3), 0x1041.a842)
rectfill(0, 125+(w>>3), 128, 128, 0x1001)
end
arcade_level = {
score=0,
wordcount=15,
wordwait = 90,
}
mknew(arcade_level, function(x)
x.phin = toyphin.new{splasher=x}
-- TODO: decent looking sky and sea
x.sky = bg.new{c=13}
x.sea = sea.new()
x.bg = event_list.new()
x.fg = event_list.new()
x.words = event_list.new()
-- TODO: score renderer
x.t0 = t()
x.wordremain = x.wordcount or arcade_level.wordcount
x.wordtimer = x.wordwait or arcade_level.wordwait
x.v = view.of{
x.sky,
x.sea,
x.waves,
{draw=function()
poke(0x5f58, 0)
local s = tostr(x.score)
print(s,1,2,3)
print(s,2,1,3)
print(s,0,1,3)
print(s,1,0,3)
print(s,1,1,15)
end},
x.bg,
x.phin,
x.fg,
x.words,
}
x.s = scootbox.new()
x.s:push(x.v)
-- TODO: fade in level music
-- Switch to small font
-- (Dogica is too large to ever miss!)
?"\^@56000800\0\0\0\0\0\0\0\0\0`w\0g \0aw\0\0\0\0\0\0fw\0\0\0`'\0\0\0 \0pp\0\0\0`\0 \0!\0\0q333333wf\0V\0ヨu◆gw☉vP●\"s◝◆◝◝◝メ◝▒◝◆▒☉ヲ\0◝◆◝◝◝モ◝☉☉☉▒☉☉ユ\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\n\n\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\r\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\t\t\0\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\t\t\0\0\0\0\0\0\0\0\0\r\t\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t \r\t\0\0\0\t\t\t\0\0\0\0\0\0\t\t\r\0\0\0\0\0\0\0\0\0\0\0\t\t\t\t\0\0\0\t\t\t\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\0U*U*U*\0\0A]]>\0\0\0>ccw>\0\0\0DDD\0\0\0\0\0\0\0\0\0\0\06w6\0\0\0\n\0\0\0>*:\0\0\0>gcg>\0\0\0?-?!?\0\0\0\0\0\0>ckc>\0\0\0\0\0\0\0\0U\0\0\0\0\0>scs>\0\0\0>\"\0\0\0\0\0\0>wcc>\0\0\0\0R \0\0\0\0\0*D\0\0\0\0>kwk>\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \0\0\0\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\r\t\0\0\0\0\0\0\0\0\t\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0 \n\n\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\t\0\0\0\0\t*9 \0\0\t\t\0\0!\nUr@\0\0\0\0\0\0\0\0\0\0\0\0\n\0\0\0\0\0\0\0\0\0 \r\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\n\0\t\t\0\0\0\0\0\0\0\0\0\0\0\n\0\t\0\0\0\0\0\0\0\0\0\0\0\r\0\0\0\0\t\t\0\0\0\t\t\0\0\t\0\t\t\0\0\t\0\0\t\t\0\0\0\0 \0\0\t\t\0\0\0\0\0\0\0\0\0\n\t\t\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\t\t\0\0\n\t \r\t\0\0\t\t\0\0\t\t\0\0\t\t\0\0\n\t\t\0\0\t\0\t\t\0\0\0\0\0\0\0\t\r\0\0\0\t\t\t\0\0\t\t\t\0\0\t\t\0\0\t\0\t\t\t\0\0\0\0\t\t\0\0\0\0"
poke(0x5f58,0x81) -- enable custom font
end)
function arcade_level:update()
if self.phin.entered then
if self.wordtimer <= 0 and self.wordremain > 0 then
self:spawn_word()
self.wordtimer = self.wordwait
self.wordremain -= 1
end
self.wordtimer -= 1
end
if self.wordremain <= 0 and self.words.next == nil then
self.phin.exiting = true
end
if self.phin.x > 90 then
-- TODO: done callback
self:on_level_done()
end
-- TODO: timers, word loop,
-- level state tracking and
-- progression, etc.
self.s:update()
end
function arcade_level:spawn_word()
-- TODO: basically everything.
-- word lists
-- pattern generator
self.words:push_back(wordtarget.new{
y=32+rnd(64), -- real game uses pattern generator!
phin=self.phin,
on_hit = function(word)
self:word_hit(word)
end,
})
end
function arcade_level:word_hit(word)
self.score += 1
-- TODO: sfx
-- TODO: sparkle
end
function arcade_level:draw()
setup_arcade_pal()
self.s:draw()
end
function arcade_level:draw_splash(x, force)
if (force < 1) return
local n = (force + rnd(force)) & 0x7FF
for i=0,n do
local d = droplet.new{x=x, force=force, y=72+wave(), f=i/n-0.5}
if rnd() < 0.5 then
self.bg:push_back(d)
else
self.fg:push_back(d)
end
end
end
function arcade_level:dive_splash(x)
-- TODO: sfx, vfx for dive input
self:draw_splash(x, 3.8)
end
function arcade_level:jump_splash(x)
-- TODO: sfx, vfx for jump input
self:draw_splash(x, 3.8)
end
function arcade_level:surfacing_splash(x, force, harder)
-- TODO: sfx, vfx for surfacing from a dive
self:draw_splash(x, force)
end
function arcade_level:landing_splash(x, force, harder)
-- TODO: sfx, vfx for landing from a jump
self:draw_splash(x, force)
end
splashcols = {0x1003, 0x103d.a5a5}
droplet = {}
mknew(droplet, function(d)
d.dx = (d.f * d.force >> 2) - 0.75
d.x += 16 * d.f
d.dy = -rnd(d.force*0.66)
d.r = 1 + rnd(0.75 + (d.force >> 4))
d.c = rnd(splashcols)
end)
function droplet:update()
self.x += self.dx
self.y += self.dy
self.dy += 0.3
return self.y >72 + wave()
end
function droplet:draw()
circfill(self.x, self.y, self.r, self.c)
local r2 = self.r >> 1
pset(self.x+r2, self.y-r2, 0x100F)
end
-->8
-- game sequencer
__gfx__
00888800777777777777777777777777777777777777777777777777777777777777777777777777777777777777777700000000000000000000000000000000
0888e780700000000000000000000007700000000000000000000007700000000000000770000000000000000000000700000000000000000000000000000000
88888878700000000000000000000007700000000000000000000007700002222220000770000000000000000000000700000000000000000000000000000000
88a8a8e8700000000000000000000007700000000000000000000007700002eeee20000770000000000000000000000700000000000000000000000000000000
888a888870000000000000000000000770000000000000000000000770000222e220000770222200000000000000000700000000000000000000000000000000
88a8a888700000000000000000000007702222022222222222222207700002eeee200007702ee222222222222222220700000000000000000000000000000000
08888880700000000000000000000007702ee222e22e22ee22e2e2077000022222200007702e2e22e22e22ee22e2e20700000000000000000000000000000000
00888800700000000000000000000007702e2e2e2e2e22e2e2e2e2077000022e22000007702e2e2e2e2e22e2e2e2e20700000000000000000000000000000000
00000000700000000000000000000007702e2e2e2e2e22ee22eee207700002e2e2200007702eee2e2e2e22ee22eee20700000000000000000000000000000000
00000000702222022222222222222207702eee22e22ee2e222e2e207700002eeee20000770222222e22ee2e222e2e20700000000000000000000000000000000
00000000702ee222e22e22ee22e2e207702222222222222202222207700002222220000770000022222222220222220700000000000000000000000000000000
00000000702e2e2e2e2e22e2e2e2e207700000000000000000000007700002222e20000770000000000000000000000700000000000000000000000000000000
00000000702e2e2e2e2e22ee22eee207700000000000000000000007700002eeee20000770000000000000000000000700000000000000000000000000000000
00000000702eee22e22ee2e222e2e207700000000000000000000007700002222220000770000000000000000000000700000000000000000000000000000000
000000007022222222222222022222077000000000000000000000077000022ee220000770000000000000000000000700000000000000000000000000000000
00000000700000000000000000000007777777777777777777777777700002e22e20000777777777777777777777777700000000000000000000000000000000
000000007000000000000000000000077777777777777777777777777000022ee220000700000000000000000000000000000000000000000000000000000000
00000000700000000000000000000007700000000000000000000007700000222220000700000000000000000000000000000000000000000000000000000000
000000007000000000000000000000077000000000000000000000077000022eee20000700000000000000000000000000000000000000000000000000000000
00000000700000000000000000000007700000000000000000000007700002e22e20000700000000000000000000000000000000000000000000000000000000
00000000700000000000000000000007700000000000000000000007700002eeee20000700000000000000000000000000000000000000000000000000000000
00000000700000000000000000000007700000022222222222222207700002222220000700000000000000000000000000000000000000000000000000000000
0000000070000000000000000000000770222222e22e22ee22e2e207700000000000000700000000000000000000000000000000000000000000000000000000
00000000777777777777777777777777702ee22e2e2e22e2e2e2e207777777777777777700000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000702e2e2e2e2e22ee22eee207000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000702e2e22e22ee2e222e2e207000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000702eee222222222202222207000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000702222200000000000000007000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000700000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000700000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000700000000000000000000007000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000777777777777777777777777000000000000000000000000000000000000000000000000000000000000000000000000