Created
December 9, 2019 00:51
-
-
Save TheTomster/d0bda50de04547c21541715f8b5ac3df to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ufo santa candy blaster | |
-- cc-by-sa (c) 2019 tom wright | |
-- minbytes.com | |
--[[ | |
notes and ideas | |
music | |
spike balls that make you drop coins | |
dropped coins bounce | |
spike-b-gone upgrade | |
sync gift throwing to music | |
sfx is annoying | |
random % things for spawning a gift | |
giant present | |
throw elf instead of present | |
reindeer from side as obstacle | |
if you let too many presents through | |
big mint-like thing that chases after you and bumps you and bullies you | |
]] | |
gravity = 0.05 | |
-- 1 in n chance of returning true | |
function chance(n) | |
return flr(rnd(n)) == 0 | |
end | |
-- call flip for n frames | |
function stall(n) | |
for i=1,n do | |
flip() | |
end | |
end | |
-- prints a string (s) using the big font, at position x,y in color c | |
function bigprint(s, x, y, c) | |
scale = scale or 8 | |
s = tostr(s) | |
pal() | |
pal(7, c) | |
for idx = 1,#s do | |
local char = sub(s, idx, idx) | |
local num = tonum(char) | |
sspr(num * 8, 16, 8, 8, x, y) | |
x += scale | |
end | |
end | |
-- outlined print functions | |
function printol_helper(pstring,px,py,pcol,fn) | |
for printx = 0, 2 do | |
for printy = 0, 2 do | |
fn(pstring, px + printx, py + printy, 0) | |
end | |
end | |
fn(pstring, px + 1, py + 1, pcol) | |
end | |
function printol(pstring,px,py,pcol) | |
printol_helper(pstring, px, py, pcol, print) | |
end | |
function bigprintol(s, x, y, c) | |
printol_helper(s, x, y, c, bigprint) | |
end | |
-- chooses a random element from the list. it's really handy to call this with | |
-- the special table call syntax: pick{1,2,3} | |
function pick(l) | |
local i = flr(rnd(#l)) + 1 | |
return l[i] | |
end | |
-- broad-phase step for mint collision split the screen into a grid of "areas". | |
-- each mint will update the area list whenever it moves. it will add itself to | |
-- any grid squares that overlap with it. then, gifts / coins can quickly look | |
-- up which mints overlap with the grid square they are in. | |
area_split = 4 | |
area_size = 128 / area_split | |
-- math to get the index of a particular x/y grid location in the areas array. | |
function area_idx(x, y) | |
local i = flr(x / area_size) | |
i += flr(y / area_size) * area_split | |
return i | |
end | |
-- initialize empty arrays for each grid square | |
function init_areas() | |
areas = {} | |
for i = 0, area_split * area_split do | |
areas[i] = {} | |
end | |
end | |
-- removes the mint from the grid | |
function area_rm(mint) | |
for i = 0, #areas do | |
del(areas[i], mint) | |
end | |
end | |
-- adds the mint to each area that it overlaps with. | |
function area_update(mint) | |
-- if a mint is smaller than area_size, then we end up with an off-by-one | |
-- problem. we should always step at least one area beyond the top-left | |
-- corner, even if that means smaller mints don't actually overlap the second | |
-- grid square. | |
local w = max(mint.r, area_size) | |
-- start at top left corner of mint, and step one area-width at a time. add | |
-- ourselves to any area we overlap with. | |
local y = max(0, mint.y) | |
while y <= mint.y + w and y < 128 do | |
local x = max(0, mint.x) | |
while x <= mint.x + w and x < 128 do | |
add(areas[area_idx(x, y)], mint) | |
x = x + area_size | |
end | |
y = y + area_size | |
end | |
end | |
-- get mints that overlap the grid square at the given screen point | |
function in_area(x, y) | |
return areas[area_idx(x, y)] | |
end | |
-- draws the area grid and a count of how many mints are in each area. | |
function debug_areas() | |
for x = 0, 127, area_size do | |
for y = 0, 127, area_size do | |
local c = 7 | |
local n = #areas[area_idx(x, y)] | |
if n > 0 then | |
c = 12 | |
print(n, x+3, y+2, 12) | |
end | |
rect(x, y, x + area_size, y + area_size, c) | |
end | |
end | |
end | |
-- peppermint collision logic | |
function collide_mints(e, effect) | |
local ex, ey = e.x + 3, e.y + 3 | |
ex = mid(0, ex, 127) | |
ey = mid(0, ey, 127) | |
for p in all(in_area(ex, ey)) do | |
-- ex, ey represents center point of the gift | |
-- and px, py are the center point of the current peppermint | |
local px, py = p.x + p.r / 2, p.y + p.r / 2 | |
-- measure the x and y deltas, and the distance | |
local dx, dy = ex - px, ey - py | |
local dist2 = dx * dx + dy * dy | |
-- cache divided size value to avoid repeated calculations | |
local r = p.r / 2 | |
-- the distance value by itself frequently overflows and causes problems | |
-- with a simple comparison... so first we check if the distance is close | |
-- enough in the x and y directions. if x and y are close then we know | |
-- overflow won't be a problem, and we can do the normal distance check. | |
if abs(dx) < r and abs(dy) < r and dist2 < r * r then | |
-- spawn a small spark effect | |
mksparks(flr(rnd(3)+3), ex, ey, p.pal) | |
-- calculate bounce angle. | |
local a = atan2(dx, dy) | |
local spd = sqrt(e.dx * e.dx + e.dy * e.dy) | |
-- a minimum bounce speed helps keep gifts from getting stuck vibrating | |
-- next to a peppermint. if they somehow overlap a peppermint it helps | |
-- make sure they get ejected. | |
spd = max(spd, 1.5) | |
-- converting the bounce angle back to dx/dy speed | |
e.dx = cos(a) * spd | |
e.dy = sin(a) * spd | |
sfx(pick{10, 15, 16}) | |
-- tell the peppermint to flash, and subtract life from it | |
if effect == 'damage' then | |
-- a little variety in sfx helps a lot | |
p.flash = 2 | |
p.life -= 1 | |
elseif effect == 'coin_damage' then | |
p.flash = 2 | |
p.life -= 2 | |
stat_coin_damage += 2 | |
elseif effect == 'power' then | |
p.flash = 2 | |
p.life -= 5 | |
mkexp(ex, ey, 4) | |
elseif effect == 'hyper' then | |
p.flash = 2 | |
p.life -= 20 | |
end | |
end | |
end | |
end | |
-- constructs a gift. starts out at the given location with 0 speed, and a | |
-- random color palette. | |
function mkgift(x, y) | |
stat_gifts += 1 | |
local effect = 'damage' | |
local pal = pick{ | |
{3,8}, | |
{12,7}, | |
{7,12}, | |
{8,3}, | |
} | |
if have_upgrade(ug_hyper_gifts) and chance(6) then | |
effect = 'hyper' | |
pal = {14, 7} | |
elseif have_upgrade(ug_power_gifts) and chance(4) then | |
effect = 'power' | |
pal = {10, 9} | |
end | |
return { | |
x = x, y = y, | |
dx = 0, dy = 0, | |
-- gifts rotate over time, this is the angle of rotation | |
a=rnd(), | |
-- choose a random two color palette | |
pal=pal, | |
-- gifts draw a little trail behind them... every frame the gift's position | |
-- will be added to the tail table. we can use the last few points to draw | |
-- the tail | |
tail = {}, | |
effect = effect | |
} | |
end | |
-- update function for gifts | |
function upgift(g) | |
-- constant rotation, turns out the simplest solution looks just fine | |
g.a+=1/60 | |
-- apply movement speed | |
g.x+=g.dx | |
g.y+=g.dy | |
-- accelerate the gift downward based on gravity, but limit the maximum fall | |
-- speed | |
g.dy+=gravity | |
g.dy=min(g.dy,3) | |
-- gifts bounce off the sides of the screen | |
if g.x < 0 or g.x > 128 then | |
g.dx = -g.dx | |
end | |
collide_mints(g, g.effect) | |
-- tail handling. add current position to tail, and clip it to contain the | |
-- last 4 positions. we can adjust that constant to increase the length of the | |
-- tail. | |
add(g.tail, {x = g.x, y = g.y}) | |
local max_tail = 4 | |
if g.effect == 'power' then | |
max_tail = 8 | |
elseif g.effect == 'hyper' then | |
max_tail = 16 | |
end | |
if #g.tail > max_tail then | |
del(g.tail, g.tail[1]) | |
end | |
end | |
-- delete gifts that have fallen off the bottom of the screen | |
function killgifts() | |
local alive = {} | |
for g in all(gifts) do | |
if g.y < 128 then | |
add(alive, g) | |
end | |
end | |
gifts = alive | |
end | |
-- draws gifts | |
function drawgift(g) | |
-- set up palette for drawing the tail. the sprite uses a black outline and | |
-- orange background, so we need to adjust the transparency. | |
pal() | |
palt(0,false) | |
palt(14,true) | |
-- draw tail with flickering trasnparency | |
if t % 2 == 0 then | |
-- setting a fill pattern (Sean Leblanc's fillp tool is great for these) | |
fillp(bor(0b101111101011111.1)) | |
local c = 0 | |
if g.effect == 'hyper' then | |
c = 7 | |
end | |
-- draw all the tail points | |
for t in all(g.tail) do | |
circfill(t.x+3, t.y+3, 3, c) | |
end | |
fillp() | |
end | |
if g.effect == 'power' then | |
circfill(g.x + rnd(8) - 1, g.y + rnd(8) - 1, 4, 0) | |
circfill(g.x + rnd(8) - 1, g.y + rnd(8) - 1, 4, 7) | |
elseif g.effect == 'hyper' then | |
circfill(g.x + rnd(8) - 1, g.y + rnd(8) - 1, 8, pick{0, 2}) | |
circfill(g.x + rnd(8) - 1, g.y + rnd(8) - 1, 8, pick{14, 7}) | |
end | |
-- setup palette for drawing the actual sprite | |
pal(3, g.pal[1]) | |
pal(8, g.pal[2]) | |
-- we have 8 possible rotations we can draw. so we start by converting the | |
-- angle to an integer from 0 to 7 | |
local i = flr((g.a%1)*8) + 1 | |
-- we use look up tables to identify the correct sprite number and flip flags | |
-- for the angle we're drawing. | |
local sprs = {1,2,3,2,1,2,3,2} | |
local fx = {false,false,false,false,true,true,true,true} | |
local fy = {false,false,false,true,true,true,false,false} | |
-- sprite and flags pulled from lookup tables | |
spr(sprs[i],g.x,g.y,1,1,fx[i],fy[i]) | |
end | |
-- explosions! draws a flashing circle for a few frames. | |
function mkexp(x, y, r) | |
add(explosions, { | |
x = x, y = y, | |
r = r, t = 5 | |
}) | |
end | |
-- update is just a timer to control the drawing. | |
function upexp() | |
for e in all(explosions) do | |
e.t -= 1 | |
end | |
end | |
-- the first frew frames draw a black circle, then it switches to white. gives a | |
-- nice visual pop. | |
function drexp() | |
pal() | |
for e in all(explosions) do | |
if e.t > 2 then | |
circfill(e.x, e.y, e.r, 7) | |
elseif e.t > 0 then | |
circfill(e.x, e.y, e.r, 0) | |
end | |
end | |
end | |
-- kill off the explosion objects after their timer runs out. | |
function killexp() | |
local alive = {} | |
for e in all(explosions) do | |
if e.t >= 0 then | |
add(alive, e) | |
end | |
end | |
explosions = alive | |
end | |
-- peppermint initializer | |
function mkmint() | |
-- setting up a table for randomizing the size of peppermints. pick selects a | |
-- value at random uniformly... if we add a bunch of copies of a value, it is | |
-- more likely to be chosen. | |
-- | |
-- most mints will be size 16-48... occasionally larger mints will spawn. | |
-- originally 'r' was going to mean 'radius', but it turns out it's more like | |
-- diameter based on how i implemented the drawing. whoops. | |
local rs = {} | |
for i = 1, 40 do add(rs, 16) end | |
for i = 1, 60 do add(rs, 32) end | |
for i = 1, 20 do add(rs, 48) end | |
for i = 1, 5 do add(rs, 64) end | |
for i = 1, 1 do add(rs, 128) end | |
local r = pick(rs) | |
-- spawn most mints in the middle, but occasionally allow one to spawn closer | |
-- to the edge. i found that truly random spawns made it awkward to set up | |
-- shots so that a gift bounced back and forth between the wall and a mint. | |
-- but we do occasionally want to spawn them on the edge, or else it looks | |
-- weird. spawning them at exactly 0 or 128 when they're on the edge works | |
-- really well. | |
-- | |
-- so, by default cluster them in the middle. (subtracting r / 2 makes sure | |
-- the mint is centered on the chosen point) | |
local x = rnd(96) + 16 - r / 2 | |
-- but 10% of the time put them on the edges | |
if rnd() < 0.1 then | |
x = pick{0, 128} - r / 2 | |
end | |
-- give each mint a random life total | |
local life = flr(rnd(5) + 3) | |
-- bigger mints have more life | |
if r > 32 then | |
life *= 2 | |
end | |
-- and the biggest boys are just guaranteed to have a ton of life | |
if r == 128 then | |
life = 40 | |
end | |
-- y position. if there aren't a lot of mints, we want to spawn them close to | |
-- the edge of the screen so that the player sees them right away. but when | |
-- there are more mints we need to spread them out a bit more. | |
local y = rnd(256) + 128 | |
if #mints < 6 then | |
y = rnd(64) + 128 | |
end | |
-- different colored mints with more health | |
local pal = {7, 8, 2} | |
local power = have_upgrade(ug_power_gifts) or have_upgrade(ug_fast_gifts) | |
local hyper = have_upgrade(ug_hyper_gifts) or have_upgrade(ug_super_fast) | |
if hyper and chance(10) then | |
pal = {2, 13, 0} | |
life *= 16 | |
elseif chance(30) then | |
pal = {7, 12, 1} | |
life *= 8 | |
elseif hyper and chance(4) then | |
pal = {7, 12, 1} | |
life *= 8 | |
elseif chance(30) then | |
pal = {7, 11, 3} | |
life *= 4 | |
elseif power and chance(5) then | |
pal = {7, 11, 3} | |
life *= 4 | |
elseif hyper and chance(2) then | |
pal = {7, 11, 3} | |
life *= 4 | |
end | |
return { | |
-- random initial rotation angle used for drawing | |
a = rnd(), da = pick{1 / 45, 1 / 60, 1 / 90}, | |
r = r, | |
x = x, | |
-- the first batch of mints starts some ways off screen | |
y = y, | |
-- and they glide upwards quite slowly | |
dy = rnd() * 0.1 + 0.1, | |
-- number of hits to destroy the mint | |
life = life, | |
-- palette to draw the mint | |
pal = pal, | |
-- used to make the mint flash when it is hit. 0 means no flash, a positive | |
-- value flashes for that many frames. | |
flash = 0 | |
} | |
end | |
function reward_more_coins(x, y) | |
if chance(500) then | |
-- jackpot! | |
sfx(21) | |
mkcoins(x, y, 40) | |
for mint in all(mints) do | |
mint.life = 0 | |
end | |
for i = 1, 16 do | |
mkmint() | |
end | |
elseif chance(8) then | |
mkcoins(x, y, 8) | |
elseif chance(4) then | |
mkcoins(x, y, 4) | |
elseif chance(2) then | |
mkcoins(x, y, 2) | |
else | |
mkcoins(x, y, 1) | |
end | |
end | |
function reward_coins(x, y) | |
if chance(500) then | |
-- once in a great while, spawn a ton | |
mkcoins(x, y, 20) | |
elseif chance(8) then | |
-- occasionally spawn 4 | |
mkcoins(x, y, 4) | |
elseif timer <= 0 then | |
-- if the timer is out, guarantee we make one | |
mkcoin(x, y) | |
elseif chance(4) then | |
-- one in 4 mints drops a coin | |
mkcoin(x, y) | |
end | |
end | |
function upmint(p) | |
local alive = {} | |
for p in all(mints) do | |
area_rm(p) | |
-- movement | |
p.y -= p.dy | |
-- speed them up once the timer has run out | |
if timer <= 0 then | |
p.dy += 0.02 | |
p.dy = min(p.dy, 0.4) | |
if p.life > 0 then p.life = 1 end | |
end | |
-- rotation | |
p.a += p.da | |
-- check if we're dead. | |
if p.life <= 0 then | |
-- double up the sfx for impact. a bass hit plus a noise. | |
sfx(13) | |
sfx(14) | |
-- spawn a whole bunch of sparks. | |
local colors = p.pal | |
if timer <= 0 then | |
colors = {8,12,7,13,14,11,10,9,7,11,12,14} | |
end | |
mksparks(5, p.x - 8, p.y - 8, colors) | |
mksparks(5, p.x - 8, p.y + 16, colors) | |
mksparks(5, p.x + 16, p.y - 8, colors) | |
mksparks(5, p.x + 16, p.y + 16, colors) | |
-- screen shake! | |
shake += 3 | |
freeze = 3 | |
-- spawn additional peppermints... unless there are already too many. so | |
-- the more the player blows up the more show up. we also stop spawning | |
-- mints if the timer has run down. once the timer is at 0 and all | |
-- mints are dead the game moves on to the next stage. | |
local max_mints = 20 | |
if have_upgrade(ug_fast_gifts) then | |
max_mints = 30 | |
elseif have_upgrade(ug_super_fast) then | |
max_mints = 40 | |
end | |
if timer > 0 and #mints < 20 then | |
add(mints, mkmint()) | |
add(mints, mkmint()) | |
add(mints, mkmint()) | |
if have_upgrade(ug_fast_gifts) then | |
add(mints, mkmint()) | |
add(mints, mkmint()) | |
end | |
end | |
-- explode! | |
mkexp(p.x + p.r / 2, p.y + p.r / 2, p.r / 1.2) | |
-- update the timer | |
timer -= 1 | |
-- random chance of spawning a coin! | |
if have_upgrade(ug_more_coins) then | |
reward_more_coins(p.x, p.y) | |
else | |
reward_coins(p.x, p.y) | |
end | |
stat_sweets += 1 | |
end | |
-- if the mint has floated off the top of the screen, remove it | |
if p.y < -(p.r) then | |
p.life = -1 | |
stat_missed_mints += 1 | |
if timer > 0 then | |
add(mints, mkmint()) | |
end | |
end | |
if p.life > 0 then | |
add(alive, p) | |
area_update(p) | |
end | |
end | |
mints = alive | |
end | |
-- sort the peppermints. we want smaller mints to draw on top of larger mints, | |
-- so that the huge mints don't hide them. makes a big difference when you can | |
-- see where the small mints are, instead of being surprised when a big one | |
-- blows up and reveals a bunch more hidden mints. | |
function sortmints() | |
if #mints < 2 then | |
-- nothing to sort | |
return | |
end | |
-- do a single pass of bubble sort each frame. thanks pico8 discord for this | |
-- gem! | |
for i = 1, #mints - 1 do | |
-- we want smaller mints to be last in the list so that they draw last and | |
-- end up on top. so, if we are smaller than our neighbor we should swap and | |
-- move further back in the list. | |
if mints[i].r < mints[i + 1].r then | |
mints[i], mints[i + 1] = mints[i + 1], mints[i] | |
end | |
end | |
end | |
-- save rotated copies of peppermint in sprite sheet. because of symmetry, | |
-- saving 4 rotations lets us smoothly draw 16 different angles. | |
function cacherot() | |
-- saving 4 rotations. | |
for i = 0, 4 do | |
-- each rotation is (360 / 16 = 22.5 degrees) | |
local a = i / 16 | |
-- loop over each pixel of the 16x16 sprite | |
for x = 0, 15 do | |
for y = 0, 15 do | |
-- calculate rotation. (thanks trasevol_dog: | |
-- https://trasevol.dog/2017/04/27/di14/) | |
local dx, dy = x - 8, y - 8 | |
local aa = atan2(dx, dy) + a | |
local l = sqrt(dx * dx + dy * dy) | |
local rx = 8 + l * cos(aa) | |
local ry = 8 + l * sin(aa) | |
-- use the rotated coordinates to fetch the color from the base sprite. | |
local c | |
if rx <= 0 or rx > 15 or ry < 0 or ry > 15 then | |
-- if we're outside the bounds of the base sprite, force it to the | |
-- transparent color (orange this time) | |
c = 14 | |
else | |
-- we're inside the sprite, so grab teh color | |
c = sget(32 + flr(rx + 0.5), flr(ry + 0.5)) | |
-- fudge a bit to try and change red and white pixels to black for | |
-- cleaner outlines. basically we're feeling around to some adjacent | |
-- pixels, replacing the current color with the outline color. this | |
-- helps ensure the outline is continuous, but makes it a bit chunky. | |
-- i like how it ended up looking though. | |
if (c ~= 14) and ( | |
sget(32 + flr(rx), flr(ry)) == 2 | |
or sget(32 + flr(rx), ceil(ry)) == 2 | |
or sget(32 + ceil(rx), flr(ry)) == 2 | |
or sget(32 + ceil(rx), ceil(ry)) == 2 | |
) then | |
c = 2 | |
end | |
end | |
-- write to some free space on the sprite sheet. now drawpepp can use | |
-- sspr to copy this rotated version of the sprite to the screen. | |
sset(x+i*16, y+64, c) | |
end | |
end | |
end | |
end | |
-- drawing mints. each sprite is taken from the cached copies created by | |
-- `cacherot`. i also experimented with drawing the peppermint in code, so that | |
-- i could have smoother scaling and rotation... but it was very inefficient | |
-- compared to sspr. | |
function drmint(p) | |
-- figure out which rotation sprite matches up with the current angle. | |
local a = flr(p.a%1 * 4) | |
-- set up palette | |
pal() | |
palt(0, false) | |
palt(14, true) | |
-- if we're flashing, force the whole palette to be the flash color. we'll do | |
-- an orange flash if we're almost dead, otherwise white. | |
if p.flash > 0 then | |
for i = 0, 16 do | |
if p.life < 3 then | |
pal(i, 9) | |
else | |
pal(i, 7) | |
end | |
end | |
p.flash -= 1 | |
else | |
pal(7, p.pal[1]) | |
pal(8, p.pal[2]) | |
pal(2, p.pal[3]) | |
end | |
-- now that the palette is finally set, sspr copies the sprite to the screen. | |
sspr( | |
a * 16, 64, 16, 16, | |
p.x, p.y, p.r, p.r | |
) | |
end | |
-- make santa! the player! he's in a ufo for some reason! | |
function mksanta() | |
return { | |
-- position | |
x = 58, y = 24, | |
-- velocity | |
dx = 0, dy = 0, | |
-- hand position, relative to x/y | |
hx = 8, hy = 5, | |
-- gift throwing timer | |
gt = 120, | |
-- show tutorial popups for the buttons | |
tutgift = true, | |
tutmove = true, | |
} | |
end | |
function upsanta(s) | |
-- movement input. we'll adjust the hand position subtly based on the player's | |
-- input, so it looks like santa's pushing a joystick to move. we also | |
-- accelerate in the x direction. if there's no x directional input, apply | |
-- some drag to slow santa down. | |
if btn(0) then | |
s.hx = 7 | |
s.dx -= 0.1 | |
s.tutmove = false | |
elseif btn(1) then | |
s.hx = 9 | |
s.dx += 0.1 | |
s.tutmove = false | |
else | |
s.dx *= 0.95 | |
s.hx = 8 | |
end | |
-- now y movement, no hand movement here as it looked pretty weird. | |
if btn(2) then | |
s.dy -= 0.1 | |
s.tutmove = false | |
elseif btn(3) then | |
s.dy += 0.1 | |
s.tutmove = false | |
else | |
s.dy *= 0.95 | |
end | |
-- clamp the speed. vertical movement being a little slower than horizontal | |
-- felt right. | |
s.dx = mid(-2, s.dx, 2) | |
s.dy = mid(-1, s.dy, 1) | |
-- and if we're shooting, clamp the speed so it's real slow | |
if btn(4) then | |
s.dx = mid(-0.2, s.dx, 0.2) | |
s.dy = mid(-0.2, s.dy, 0.2) | |
end | |
-- bounce off mints. very similar collision logic to the gifts. | |
for p in all(mints) do | |
local sx, sy = s.x + 3 + s.dx, s.y + 3 + s.dy | |
local px, py = p.x + p.r / 2, p.y + p.r / 2 | |
local dx, dy = sx - px, sy - py | |
local dist = sqrt(dx * dx + dy * dy) | |
if abs(dx) < p.r / 2 and abs(dy) < p.r / 2 and dist < p.r / 2 then | |
-- play a small bump sound. | |
sfx(10) | |
-- make a few sparks. looks hilarious if santa gets wedged between a mint | |
-- and the edge of the screen. | |
mksparks(flr(rnd(3)+3), sx, sy, {7, 8}) | |
-- small screenshake | |
shake = 1 | |
-- calculate the bounce angle. speed is hardcoded to get you away from | |
-- mints. | |
local a = atan2(dx, dy) | |
s.dx = cos(a) * 2 | |
s.dy = sin(a) * 2 | |
end | |
end | |
-- keep on screen. if our movement is about to push us off screen flip it to | |
-- push us back into the screen instead, and do a small screenshake. | |
if s.x + s.dx < -6 or s.x + s.dx > 124 then | |
s.dx = -s.dx | |
shake = 2 | |
end | |
if s.y + s.dy < -10 or s.y + s.dy > 124 then | |
s.dy = -s.dy | |
shake = 2 | |
end | |
-- apply movement | |
s.x += s.dx | |
s.y += s.dy | |
-- add a small y movement to make you do a little bob when you're sitting | |
-- still. | |
s.y += sin(t / 120) * 0.08 | |
-- gift throwing. if the timer is > 0 then santa can't drop a gift yet... and | |
-- the timer ticks down. | |
if s.gt > 0 then | |
s.gt -= 1 | |
end | |
-- waits until the player presses the gift drop button to start the game. | |
if btn(4) and s.gt <= 0 and s.tutgift then | |
s.tutgift = false | |
mktimer() | |
round_state = 'playing' | |
local num_mints = round_number > 1 and 16 or 4 | |
for i = 1, num_mints do | |
add(mints, mkmint()) | |
end | |
end | |
-- if the timer is 0 or lower, and the player is holding the button, drop a | |
-- gift | |
if btn(4) and s.gt <= 0 then | |
-- reset gift drop timer. timing is based on which upgrades we have | |
if have_upgrade(ug_super_fast) then | |
s.gt = 10 | |
elseif have_upgrade(ug_fast_gifts) then | |
s.gt = 30 | |
else | |
s.gt = 60 | |
end | |
-- spawn the gift | |
local g = mkgift(s.x + 3, s.y + 6) | |
add(gifts, g) | |
if g.effect == 'power' then | |
sfx(19) | |
elseif g.effect == 'hyper' then | |
sfx(20) | |
else | |
sfx(12) | |
end | |
end | |
-- really really keep on screen though. sometimes when you're being pushed | |
-- around by a mint the check we had above breaks down. so last thing we do is | |
-- make extra sure you're on screen. | |
s.x = mid(-6, s.x, 124) | |
s.y = mid(-10, s.y, 124) | |
end | |
function drawsanta(s) | |
pal() | |
-- highlight for when the game gets crazy towards the end | |
if have_upgrade(ug_golden) and #gifts + #mints + #coins > 10 then | |
circ(s.x + 6, s.y + 6, 12, 10) | |
end | |
-- adjust palette | |
pal() | |
palt(0, false) | |
palt(11, true) | |
-- draw the tiny hand sprite | |
sspr( | |
64, 0, 4, 4, | |
s.x + s.hx, s.y + s.hy | |
) | |
if have_upgrade(ug_golden) then | |
pal(6, 10) | |
pal(5, 9) | |
end | |
-- and draw santa ufo sprite on top | |
sspr( | |
48, 0, 13, 13, | |
s.x, s.y | |
) | |
-- and sunglasses if we have the upgrade | |
if have_upgrade(ug_golden) then | |
sspr(72, 0, 7, 2, s.x + 3, s.y + 4) | |
end | |
-- draw the tutorial if it hasn't been dismissed prompt isn't shown until you | |
-- can actually drop a gift. | |
if s.gt <= 0 and s.tutgift then | |
printol("\x8E gift", s.x - 9, s.y + 21, 7) | |
end | |
if s.tutmove then | |
printol("+ move", s.x - 9, s.y + 14, 7) | |
end | |
end | |
-- spark spawner. spawns n sparks with random velocity, at the given location, | |
-- choosing a random color from the list `c`. | |
function mksparks(n, x, y, cs) | |
for i = 1, n do | |
add(sparks, mkspark(x, y, pick(cs))) | |
end | |
end | |
-- individual spark spawn | |
function mkspark(x, y, c) | |
return { | |
x = x, | |
y = y, | |
c = c, | |
dx = rnd(4) - 2, | |
dy = rnd(4) - 2, | |
life = 60, -- sparks last this many frames | |
tail = {} -- sparks are drawn using a tail | |
} | |
end | |
-- spark updater | |
function upsparks() | |
-- this one includes the kill functionality as part of the update function | |
local alive = {} | |
for s in all(sparks) do | |
-- subtract a frame from the life counter and determine if it should be kept | |
-- alive. | |
s.life -= 1 | |
if s.life > 0 then | |
add(alive, s) | |
end | |
-- movement | |
s.x += s.dx | |
s.y += s.dy | |
-- y acceleration | |
s.dy += gravity | |
-- add a point to the tail | |
add(s.tail, {x = s.x, y = s.y}) | |
-- keep the last 4 tail points. can change this to change the length of the | |
-- spark. | |
if #s.tail > 4 then | |
del(s.tail, s.tail[1]) | |
end | |
end | |
sparks = alive | |
end | |
-- spark drawing | |
function drspark(s) | |
pal() | |
-- draw line along the tail points. | |
for i = 1, #s.tail - 1 do | |
local st = s.tail[i] | |
local ed = s.tail[i + 1] | |
line(st.x, st.y, ed.x, ed.y, s.c) | |
end | |
end | |
-- let it snow | |
function mksnow() | |
snow = {} | |
for i = 1, 200 do | |
add(snow, { | |
x = rnd(128), y = rnd(128), | |
dx = rnd(2) - 1, dy = rnd(0.5) + 0.5, | |
}) | |
end | |
end | |
-- snow updater | |
function upsnow() | |
for s in all(snow) do | |
-- movement | |
s.x += s.dx | |
s.y += s.dy | |
-- screen wrapping | |
if s.x < 0 then s.x += 128 end | |
if s.x > 128 then s.x -= 128 end | |
if s.y > 128 then s.y = -rnd(16) end | |
-- random drift | |
s.dx += (rnd(1) - 0.5) * 0.25 | |
s.dx = mid(-1, s.dx, 1) | |
s.dy += 0.1 | |
s.dy = min(s.dy, 1) | |
end | |
end | |
-- snow is just drawn as pixels | |
function drsnow() | |
for s in all(snow) do | |
pset(s.x, s.y, 7) | |
end | |
end | |
-- create some clouds to decorate the background. we spawn several cloud | |
-- objects... each one consists of a bunch of blobs clustered around the cloud's | |
-- origin. | |
function mkclouds() | |
clouds = {} | |
for i = 1,8 do | |
c = { | |
-- spawn on one of the edges of the screen | |
x = pick{-8, 120}, | |
-- random y location... can be on screen or below the screen | |
y = rnd(256), | |
bits = {} | |
} | |
-- spawn the cloud bits | |
for i = 1,16 do | |
add(c.bits, { | |
-- spread them out a little taller than the cloud is wide | |
x = rnd(32), y = rnd(48), | |
-- random sizes... tending towards a middle size instead of being purely | |
-- uniform. | |
r = rnd(6) + rnd(6) + 4 | |
}) | |
end | |
add(clouds, c) | |
end | |
end | |
function upclouds() | |
for c in all(clouds) do | |
-- clouds float upwards | |
c.y -= 0.4 | |
-- once they are off the top of the screen, respawn them below | |
if c.y < -64 then | |
c.y = rnd(128) + 128 | |
c.bits = {} | |
for i = 1,16 do | |
add(c.bits, { | |
x = rnd(32), | |
y = rnd(48), | |
r = pick{4, 4, 4, 6, 6, 6, 8, 8, 8, 16} | |
}) | |
end | |
end | |
end | |
end | |
function drclouds() | |
pal() | |
for c in all(clouds) do | |
for b in all(c.bits) do | |
-- draw a base circle with a shaded pattern | |
fillp(0b101111101011111) | |
circfill(c.x + b.x, c.y + b.y, b.r, 118) | |
fillp() | |
-- now, offset and draw a lighter highlight. the offset is different | |
-- depending which side of the screen the cloud is on. | |
local x | |
if c.x == -8 then | |
x = c.x + b.x - 3 | |
else | |
x = c.x + b.x + 3 | |
end | |
circfill(x, c.y + b.y - 3, b.r - 1, 7) | |
end | |
end | |
end | |
-- easing fn, by sean | |
function ease(t) | |
t = mid(0, t, 1) | |
if t >= 0.5 then | |
return (t - 1) * (2 * t - 2) * (2 * t - 2) + 1 | |
else | |
return 4 * t * t * t | |
end | |
end | |
function lerp(a, b, t) | |
return a + (b - a) * t | |
end | |
function mkbonus() | |
bonus = { | |
y = -64, | |
t = 0 | |
} | |
end | |
function upbonus() | |
if bonus == nil then | |
return | |
end | |
if bonus.t < 30 then | |
bonus.y = lerp(-64, 32, bonus.t / 30) | |
elseif bonus.t == 30 then | |
shake = 8 | |
for x = 32, 96 do | |
mksparks(1, x, 64, {8,12,7,13,14,11,10,9,7,11,12,14}) | |
end | |
sfx(22) | |
elseif bonus.t < 60 then | |
elseif bonus.t < 120 then | |
local t = bonus.t - 120 | |
bonus.y = lerp(32, 160, ease(t / 30)) | |
end | |
bonus.t += 1 | |
if bonus.t > 80 then | |
bonus = nil | |
end | |
end | |
function drbonus() | |
if bonus == nil then | |
return | |
end | |
pal() | |
palt(0, false) | |
palt(14, true) | |
sspr(0, 24, 63, 29, 33, bonus.y) | |
end | |
function mktimer() | |
timer = 90 | |
timerobj = { | |
t = 0 | |
} | |
end | |
function uptimer() | |
if timerobj then | |
timerobj.t += 1 | |
end | |
if timer > 0 and t % 60 == 0 then | |
timer -= 1 | |
end | |
if timer <= 0 and round_state == 'playing' then | |
round_state = 'bonus' | |
mkbonus() | |
end | |
end | |
function drtimer() | |
if timer > 0 then | |
local y = lerp(-16, 4, ease(timerobj.t / 40)) | |
bigprintol(timer, 56, y, 7) | |
end | |
end | |
function mkcoin(x, y) | |
add(coins, { | |
x = x, y = y, | |
dx = rnd(2) - 1, dy = -rnd(3), | |
tail = {}, | |
t = 60 * 7 | |
}) | |
end | |
function mkcoins(x, y, n) | |
for i=1,n do | |
mkcoin(x, y) | |
end | |
end | |
function upcoin() | |
local alive = {} | |
for c in all(coins) do | |
add(alive, c) | |
-- tick down timer. kill the coin if the timer reaches 0. | |
c.t -= 1 | |
if c.t <= 0 then | |
stat_missed_coins += 1 | |
mkexp(c.x, c.y, 4) | |
del(alive, c) | |
end | |
local magnet_dist = 20 | |
if have_upgrade(ug_magnet) then | |
magnet_dist = 80 | |
end | |
-- if a coin is close to santa, collect it and add it to the cash counter. | |
local dist = abs(c.x - santa.x) + abs(c.y - santa.y) | |
if dist < 4 then | |
cash += 1 | |
stat_coins += 1 | |
sfx(17) | |
del(alive, c) | |
elseif timer <= 0 and #mints == 0 then | |
-- at the end of the round, slurp all coins towards santa | |
c.dx = mid(-2, santa.x - c.x, 2) | |
c.dy = mid(-2, santa.y - c.y, 2) | |
elseif dist < magnet_dist then | |
-- for coins that are slightly further away, change their movement so they | |
-- get slurped into santa. | |
c.dx = mid(-2, santa.x - c.x, 2) | |
c.dy = mid(-2, santa.y - c.y, 2) | |
end | |
-- apply movement speed | |
c.x+=c.dx | |
c.y+=c.dy | |
-- accelerate the coin downward based on gravity | |
c.dy += gravity / 2 | |
-- coins have a pretty slow movement speed clamped on them. goal is to make | |
-- it easier to predict where they're going so you can catch them. | |
c.dx=mid(-1, c.dx, 1) | |
c.dy=mid(-3, c.dy, 1) | |
-- coins bounce off the sides of the screen, and the bottom | |
if c.x < 0 or c.x > 128 then | |
c.dx = -c.dx | |
end | |
-- the bounce off the bottom is exaggerated | |
if c.y > 128 then | |
c.dy = -c.dy * 2 | |
end | |
local effect = 'none' | |
if have_upgrade(ug_crusher_coins) then | |
effect = 'coin_damage' | |
end | |
collide_mints(c, effect) | |
-- guarantee coins stay on screen | |
c.x = mid(0, c.x, 128) | |
c.y = min(c.y, 128) | |
-- glittery tail | |
if t % 3 == 0 then | |
add(c.tail, { | |
x = c.x + rnd(8), y = c.y + 4, | |
c = pick{7, 9, 9, 10, 10}, | |
-- set a random int between 0 and 15... used to change some pixels into | |
-- $ signs | |
t = flr(rnd(16)) | |
}) | |
end | |
if #c.tail > 4 then | |
del(c.tail, c.tail[1]) | |
end | |
for t in all(c.tail) do | |
t.y -= 0.3 | |
end | |
end | |
coins = alive | |
end | |
function drcoin() | |
for c in all(coins) do | |
if chance(60) then | |
circfill(c.x + 4, c.y + 4, 8, 7) | |
end | |
for t in all(c.tail) do | |
if t.t == 0 then | |
pal() | |
pal(7, t.c) | |
spr(19, t.x, t.y) | |
else | |
circ(t.x, t.y, 1, t.c) | |
end | |
end | |
if c.t < 60 and c.t % 2 == 0 then | |
goto next | |
end | |
pal() | |
palt(0, false) | |
palt(14, true) | |
local sprs = {16, 17, 18, 17} | |
local i = flr(t / 6) % #sprs | |
local s = sprs[i + 1] | |
spr(s, c.x, c.y) | |
::next:: | |
end | |
end | |
function mkcash() | |
cash_particles = {} | |
end | |
function upcash() | |
if t % 9 == 0 then | |
add(cash_particles, { | |
x = rnd(8), y = rnd(8), | |
c = pick{7, 7, 9, 9}, | |
-- set a random int between 0 and 15... used to change some pixels into | |
-- $ signs | |
t = flr(rnd(16)) | |
}) | |
end | |
if #cash_particles > 4 then | |
del(cash_particles, cash_particles[1]) | |
end | |
end | |
function drcash() | |
if cash > 0 then | |
pal() | |
palt(0, false) | |
palt(14, true) | |
spr(16, 1, 1) | |
pal() | |
for t in all(cash_particles) do | |
if t.t == 0 then | |
pal() | |
pal(7, t.c) | |
spr(19, t.x, t.y) | |
else | |
circ(t.x, t.y, 1, t.c) | |
end | |
end | |
printol(cash, 10, 2, 10) | |
end | |
end | |
function fade() | |
local t = 0 | |
local fade = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15} | |
local darks = { | |
0, 0, 0, 1, | |
2, 0, 5, 6, | |
4, 8, 9, 3, | |
6, 6, 13, 14 | |
} | |
while t < 60 do | |
t += 1 | |
if t % 6 == 0 then | |
all_black = true | |
for i = 1,#fade do | |
fade[i] = darks[fade[i]+1] | |
end | |
end | |
for i = 0,15 do | |
pal(i, fade[i+1], 1) | |
end | |
flip() | |
end | |
end | |
function init_game() | |
state = 'game' | |
-- screen shake timer! | |
shake = 0 | |
-- freeze timer | |
freeze = 0 | |
-- initialize objects | |
gifts = {} | |
mints = {} | |
santa = mksanta() | |
sparks = {} | |
explosions = {} | |
coins = {} | |
-- game state | |
timer = 0 | |
round_state = 'start' | |
mkcash() | |
music(0, 0, 3) | |
end | |
function update_game() | |
if freeze > 0 then | |
freeze -= 1 | |
return | |
end | |
t += 1 -- update the global frame counter | |
-- update all the objects | |
upsanta(santa) | |
foreach(gifts, upgift) | |
killgifts() | |
upmint() | |
sortmints() | |
upsparks() | |
-- upsnow() | |
upclouds() | |
upexp() | |
uptimer() | |
upcoin() | |
upcash() | |
upbonus() | |
if round_state == 'bonus' and #mints == 0 and #coins == 0 then | |
fade() | |
init_shop() | |
end | |
-- decrease screen shake | |
shake -= 0.4 | |
shake = mid(0, shake, 4) | |
end | |
function drround() | |
local s = 'round ' .. tostr(round_number) | |
pal() | |
printol(s, 126 - 4 * #s, 3, 7) | |
end | |
-- draw upgrades | |
function drbought() | |
local y = 120 | |
for i = 1,#bought do | |
local u = upgrades[bought[i]] | |
printol(u[5], 1, y, 7) | |
y -= 8 | |
end | |
end | |
function draw_game() | |
cls(5) | |
-- shake screen at a random angle | |
local a = rnd() | |
local cx = cos(a) * shake | |
local cy = sin(a) * shake | |
camera(cx, cy) | |
-- draw background | |
-- drsnow() | |
drclouds() | |
-- draw objects | |
foreach(mints, drmint) | |
drtimer() | |
drcash() | |
drround() | |
drbought() | |
drexp() | |
foreach(gifts, drawgift) | |
drcoin() | |
foreach(sparks, drspark) | |
drawsanta(santa) | |
drbonus() | |
end | |
function init_title() | |
cls(5) | |
sfx(22) | |
printol('minbytes.com', 40, 56, 7) | |
printol('copyright 2019 tom wright', 14, 96, 7) | |
printol('creative commons cc-by-sa', 14, 104, 7) | |
stall(20) | |
fade() | |
t = 0 | |
-- mksnow() | |
mkclouds() | |
title_x = -75 | |
title_timer = nil | |
music(17) | |
end | |
function update_title() | |
t += 1 | |
-- upsnow() | |
upclouds() | |
title_x -= 0.5 | |
if title_x <= -300 then | |
title_x = 0 | |
end | |
if title_timer ~= nil then | |
title_timer -= 1 | |
end | |
if title_timer == nil and (btnp(4) or btnp(5)) then | |
music(-1) | |
title_timer = 60 | |
sfx(21) | |
end | |
if title_timer ~= nil and title_timer <= 0 then | |
init_game() | |
end | |
end | |
function draw_title() | |
cls(5) | |
-- drsnow() | |
drclouds() | |
printol('ufo santa candy blaster', 16, 8, 7) | |
palt(0, false) | |
palt(11, true) | |
spr(6, 58, 24, 1.75, 1.75) | |
pal() | |
printol('drop gifts', 42, 44, 7) | |
printol('destroy sweets', 34, 54, 8) | |
printol('collect coins', 36, 64, 10) | |
if t % 60 < 30 then | |
printol('press \x97 or \x8E to play', 22, 94, 7) | |
end | |
printol('a game by tom wright for advent calendar 2019 - twitter:@thetomster3', title_x, 120, 7) | |
printol('a game by tom wright for advent calendar 2019 - twitter:@thetomster3', title_x + 300, 120, 7) | |
end | |
-- list of upgrades available | |
-- each item has a sprite, price, name, description, letter for hud | |
upgrades = { | |
{14, 20, "power gifts", " random chance to throw a super gift ", "p"}, | |
{10, 50, "speed up", " santa drops gifts more quickly ", "s"}, | |
{46, 50, "coin up", " coins are more likely to appear ", "c+"}, | |
{44, 30, "coin magnet", " coins are attracted from farther away ", "m"}, | |
{74, 60, "crusher coins", " coins can deal damage to mints ", "c"}, | |
{42, 90, "hyper gifts", " random chance to throw a hyper super gift ", "p+"}, | |
{12, 120, "super speed", "santa drops gifts even more quickly ", "s+"}, | |
{78, 500, "golden ufo", " ", "$"} | |
-- {76, 60, "spike-b-gone", " protects santa from spike balls ", "x"}, | |
} | |
-- setting up global variables for upgrade indices, so that have_upgrade calls | |
-- can look nice. have_upgrade(ug_magnet) instead of have_upgrade(4) | |
ug_power_gifts = 1 | |
ug_fast_gifts = 2 | |
ug_more_coins = 3 | |
ug_magnet = 4 | |
ug_crusher_coins = 5 | |
ug_hyper_gifts = 6 | |
ug_super_fast = 7 | |
ug_golden = 8 | |
ug_nospike = 9 | |
-- a cheat code :) | |
function unlock_all() | |
for i=1,#upgrades do | |
add(bought, i) | |
end | |
end | |
function init_shop() | |
t = 0 | |
round_state = 'done' | |
state = 'shop' | |
-- these upgrades are available in the shop currently. each list item is an | |
-- index into the upgrade table. | |
shop = {} | |
for i = 1, #upgrades do | |
-- check if bought | |
if not have_upgrade(i) then | |
add(shop, i) | |
end | |
-- stop when we have 3 items in shop | |
if #shop >= 3 then | |
break | |
end | |
end | |
if #shop == 0 then | |
-- game over | |
init_game_over() | |
return | |
end | |
-- which shop item is currently selected | |
selected = 1 | |
santa = {x = 32} | |
music(12) | |
end | |
function update_shop() | |
t += 1 | |
local target_x = 26 + (selected - 1) * 32 | |
santa.x = lerp(santa.x, target_x, 0.4) | |
if btnp(0) then | |
sfx(18) | |
selected -= 1 | |
elseif btnp(1) then | |
sfx(18) | |
selected += 1 | |
end | |
selected = mid(1, selected, #shop) | |
local upgrade_idx = shop[selected] | |
local price = upgrades[upgrade_idx][2] | |
if btnp(4) and cash >= price then | |
-- if we're buying the more powerful version of a powerup, also include the | |
-- lower powered version. | |
if upgrade_idx == ug_hyper_gifts and not have_upgrade(ug_power_gifts) then | |
add(bought, ug_power_gifts) | |
elseif upgrade_idx == ug_super_fast and not have_upgrade(ug_fast_gifts) then | |
add(bought, ug_fast_gifts) | |
end | |
add(bought, upgrade_idx) | |
cash -= price | |
sfx(22) | |
fade() | |
round_number += 1 | |
init_game() | |
elseif btnp(5) then | |
sfx(23) | |
fade() | |
round_number += 1 | |
init_game() | |
end | |
end | |
function have_upgrade(i) | |
for j = 1, #bought do | |
if bought[j] == i then | |
return true | |
end | |
end | |
return false | |
end | |
function draw_shop() | |
cls(5) | |
local s = 'upgrade time' | |
local x = 40 | |
for i = 1, #s do | |
local c = sub(s, i, i) | |
local y = sin((t + x) / 30) * 1.5 + 5 | |
printol(c, x, y, 7) | |
x += 4 | |
end | |
printol('you have ', 36, 16, 10) | |
pal() | |
palt(0, false) | |
palt(14, true) | |
spr(16, 72, 15) | |
printol(cash, 81, 16, 10) | |
pal() | |
local y = 32 + cos(t / 120) * 1.5 | |
palt(0, false) | |
palt(11, true) | |
sspr( | |
64, 0, 4, 4, | |
santa.x + 8, y + 5 | |
) | |
spr(6, santa.x, y, 2, 2) | |
pal() | |
palt(0, false) | |
x, y = 24, 50 | |
for i = 1, #shop do | |
local u = upgrades[shop[i]] | |
spr(u[1], x, y, 2, 2) | |
local price = tostr(u[2]) | |
printol(price, x + 7 - #price * 2, 68, 10) | |
x += 32 | |
end | |
local sel = upgrades[shop[selected]] | |
local name, desc = sel[3], sel[4] | |
printol(name, 64 - #name * 2, 85, 7) | |
local d1, d2 = sub(desc, 0, 28), sub(desc, 28, 999) | |
printol(d1, 64 - #d1 * 2, 95, 6) | |
printol(d2, 64 - #d2 * 2, 103, 6) | |
if cash >= sel[2] and t % 60 < 30 then | |
printol('press \x8E to purchase', 24, 112, 7) | |
elseif cash < sel[2] then | |
printol('you need more coins!', 24, 112, 6) | |
end | |
printol('press \x97 to skip', 30, 120, 6) | |
end | |
function init_game_over() | |
state = 'game_over' | |
game_over_t = 0 | |
mksnow() | |
music(16) | |
end | |
function update_game_over() | |
t += 1 | |
game_over_t += 1 | |
upsnow() | |
end | |
function draw_game_over() | |
cls(5) | |
pal() | |
drsnow() | |
printol('game over', 46, min(game_over_t / 2, 16), 7) | |
if (game_over_t > 40) printol('gifts dropped: '.. stat_gifts, 16, 32, 7) | |
if (game_over_t > 70) printol('sweets destroyed: '.. stat_sweets, 16, 40, 8) | |
if (game_over_t > 100) printol('sweets missed: ' .. stat_missed_mints, 16, 48, 6) | |
if (game_over_t > 130) printol('coins collected: '.. stat_coins, 16, 56, 10) | |
if (game_over_t > 160) printol('coins missed: ' .. stat_missed_coins, 16, 64, 6) | |
if (game_over_t > 190) printol('coin damage: ' .. stat_coin_damage, 16, 72, 7) | |
if (game_over_t > 220) printol('ended on round ' .. round_number, 16, 80, 7) | |
if (game_over_t > 250) printol('thanks for playing!', 26, 112, 7) | |
if (game_over_t > 280) printol('tom wright - @thetomster3', 14, 120, 7) | |
end | |
function _init() | |
-- generate cached rotations | |
cacherot() | |
-- global frame timer, useful for simple animations | |
t=0 | |
round_number = 1 | |
cash = 0 | |
stat_gifts = 0 | |
stat_sweets = 0 | |
stat_coins = 0 | |
stat_missed_coins = 0 | |
stat_coin_damage = 0 | |
stat_missed_mints = 0 | |
-- upgrade system: upgrades are defined in init_shop. when an upgrade is | |
-- purchased, its index in the upgrades list is added to the `bought` array. | |
-- logic throughout the game checks the array with have_upgrade and acts | |
-- accordingly. | |
bought = {} | |
-- collision helper | |
init_areas() | |
state = 'title' | |
init_title() | |
end | |
function _update60() | |
if state == 'title' then | |
update_title() | |
elseif state == 'game' then | |
update_game() | |
elseif state == 'shop' then | |
update_shop() | |
elseif state == 'game_over' then | |
update_game_over() | |
end | |
update_cpu = stat(1) | |
end | |
function _draw() | |
if state == 'title' then | |
draw_title() | |
elseif state == 'game' then | |
draw_game() | |
elseif state == 'shop' then | |
draw_shop() | |
elseif state == 'game_over' then | |
draw_game_over() | |
end | |
draw_cpu = stat(1) - update_cpu | |
max_cpu = max(max_cpu, stat(1)) | |
-- printol('\x96'..flr(stat(1) * 100) .. '%', 26, 1, 12) | |
-- printol('\x85'..flr(update_cpu * 100) .. '%', 26, 9, 9) | |
-- printol('\x82'..flr(draw_cpu * 100) .. '%', 26, 17, 11) | |
-- printol('\x96'..flr(max_cpu * 100) .. '%', 76, 3, 8) | |
-- print(stat_gifts, 0, 108, 12) | |
-- print(stat_sweets, 0, 114, 12) | |
-- print(stat_coins, 0, 120, 12) | |
-- debug_areas() | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment