Skip to content

Instantly share code, notes, and snippets.

@rostok
Created September 9, 2024 18:44
Show Gist options
  • Save rostok/299929640e6ea08ba5e386c76a719d39 to your computer and use it in GitHub Desktop.
Save rostok/299929640e6ea08ba5e386c76a719d39 to your computer and use it in GitHub Desktop.
heat signature pico1k 2024 jam game source
pico-8 cartridge // http://www.pico-8.com
version 42
__lua__
-- "Heat signature"
-- You hide behind the rubble and watch the kid, who listens for drones. Vigilant, focused, and obedient; not curious or joyful like kids used to be. Was it worth it, leaving a broken homeland? The border is close, just one more push. Just avoid the machines.
--
-- game by rostok / @von_rostok / rostok.itch.io
--
-- O all objects,
-- x,y pos
-- u,v movement
-- r random shift, 2 for characters, 3 and more when dead which is semi progression in blow up animation
-- w width
-- d half diameter of drone targetting area
-- P player O[1]
-- Q kid O[2]
-- D drawable objects in proper order, up to down
-- B border's x, value hardcoded to conserve space
-- T table of drone's targets
-- L previous frame's T count
srand(5) -- initialize random, 5 gives nice map with 3 separating ways, and only one going to the border
D={{x=0,y=0}} -- for initialization this will hold relative objects we derive new positions from
P={x=46,y=-32,r=2,w=0} -- player
Q={x=41,y=-32,r=2,w=2} -- kid
O={P,Q} -- all objects
L=0 -- last target table
while#O<75do -- loop till there are 75 houses, game is oppressive enough
o=deli(D,1) -- remove from temp table, and create a new object in the next line, x,y are not far from last one
o=add(O,{x=o.x+(20+#O\2)*rnd{2,3},y=o.y+11*rnd{2,3,-2,-3},w=rnd{30,35,40,45},r=rnd()}) -- ,d=64 but if charactes start behind house this can be skipped
if(rnd()<.05)add(D,O[#O\4+1]) -- on rare ocasions add extra objects to fork paths
add(D,o) -- make path
end
-- calculate map size and index of rightmost house
-- l=9999 r=0 t=0 b=0 rightmost_index=nil for i,o in pairs(O) do l=min(o.x,l) if o.x > r then r=o.x rightmost_index=i end t=min(o.y,t) b=max(o.y,b) end cls() stop(" left:"..l.."\n right:"..r.."\n top:"..t.."\n bottom:"..b.."\n border:"..(r+128).."\n rightmost index:"..(rightmost_index or "none"))
-- B=2400 -- border x was hardcoded
-- P.x=O[#O].x P.y=O[#O].y-8 Q.x=O[#O].x-5 Q.y=O[#O].y-8 -- check last house before border
-- B=2400 P.x=B+10 Q.x=B+20 -- check happy ending
for i=1,128do add(O,{x=i*16,y=rnd(-1)>>7,r=-rnd()})end -- add some trees
while 1 do -- while compresses better than label/goto
----------------------------------
----- SCREEN SNOW AND STATUS -----
----------------------------------
-- if(P.x>B and Q.x>B)do ?"¹a peace & ptsd⁶1⁶c1⁶!5f1033⬅️33?5",P.x-64,P.y-64,7 -- if both passed border print status for happy ending and change palette
if(P.x>2400 and Q.x>2400)do ?"¹a peace & ptsd⁶1⁶c1⁶!5f113⬅️33?3",P.x-64,P.y-64 -- if both passed border print status for happy ending and change palette
else for i=24576,32767do if(rnd()<.2)poke(i,@i+1)end
?"⁶1⁶c1⁶!5f11█🐱░49:" -- otherwise snow screen and set heat signature pal
end
P.u=@24396\2%2*2-@24396%2*2 -- left/right arrows correspond to -2,2 assigned to u
P.v=@24396\8%2-@24396\4%2 -- and so are up/down but -1,1
Q.u=@24396\16%2*sgn(P.x-Q.x-6)*2 -- kid follows player if z is pressed, will stand beside
Q.v=@24396\16%2*sgn(P.y-Q.y)
D={} -- drawable objetcs
T={} -- targets
--------------------
---- RENDER MAP ----
--------------------
-- if(btn(4,1)or mapIndex) then
-- xi=O[1].x
-- xx=O[1].x
-- yi=O[1].y
-- yx=O[1].y
-- for i=2,#O do
-- xi=min(xi,O[i].x-256)
-- xx=max(xx,O[i].x+256)
-- yi=min(yi,O[i].y-256)
-- yx=max(yx,O[i].y+256)
-- end
-- width=xx-xi
-- height=yx-yi
-- h_blocks=width\128+1
-- v_blocks=height\128+1
-- mapIndex=(mapIndex or -1)+1
-- local current_block_x=mapIndex%h_blocks
-- local current_block_y=mapIndex\h_blocks
-- d=xi+current_block_x*128
-- e=yi+current_block_y*128
-- savedPx = P.x
-- savedPy = P.y
-- P.x,P.y = d,e
-- end
for o in all(O)do
if max(abs(o.y-P.y),abs(o.x-P.x))<128do -- first filter stuff outside of screen, 128 instead of 64 as drawing part has movement logic
j=1
while j<=#D and o.y>D[j].y do j+=1 end -- sort it by y coord
add(D,o,j) -- insert object into D and
if(o.r==2)add(T,o) -- visible alive characters are automatically targetted
for p in all({P,Q})do
if(o.r<1 and o.w and abs(p.x-o.x+p.u)<o.w\2+2 and abs(p.y-o.y)<2)p.u=0 -- simple collisions
if(o.r<1 and o.w and abs(p.x-o.x)<o.w\2+2 and abs(p.y-o.y+p.v)<2)p.v=0 -- and simple sliding
-- if(o.r<1 and o.w and (abs(p.x-o.x)<o.w\2+2 and abs(p.y-o.y+5)<6) or p.x>B)p.d=66del(T,p) -- hidden behind house or across border, target is nil, diameter is big
if(o.r<1 and o.w and (abs(p.x-o.x)<o.w\2+2 and abs(p.y-o.y+5)<6) or p.x>2400)p.d=66del(T,p) -- hidden behind house or across border, target is nil, diameter is big
end
end
end
---------------------
------ DRAWING ------
---------------------
srand(5)
camera(P.x-64,P.y-64) -- fix on player
-----------------------
-- GROUND AND BORDER --
-----------------------
for i=1,128do
?"\"",P.x-64+(rnd(128)-P.x)%128,P.y-64+(i-P.y)%128,2 -- draw some grass
-- ?".",B-rnd(64),P.y-64+(i-P.y)%128,5 -- draw the border
?".",2400-rnd(64),P.y-64+(i-P.y)%128,5 -- draw the border
end
--------------------
---- RENDER MAP ----
--------------------
-- if(btn(4,1)or mapIndex) then P.x,P.y = savedPx,savedPy end
srand(t())
for x,o in ipairs(D)do -- draw all visible objects
if(o.r>2)do -- draw dead characters
o.r=min(o.r+1,31) -- character exploded, o.r is a animation progress
for i=-8,8 do
?rnd{",","."},o.x+o.r/i/4,o.y-3+sin((o.r+i/3)/64)*12-i/o.r,5 -- randomize body parts as dot and comma
end
?"¹a death & despair",P.x-64,P.y-64 -- sad ending
elseif(o.r==2)do -- draw live characters
rect(o.x-1,o.y-4,1+o.x,o.y+o.w-9,5) -- body
rect(o.x,o.y-4,o.x,o.y+o.w-11) -- head and spine
for i=1,-1,-2do line(i+o.x,o.y-3,i+o.x+o.u*i*sin(t()*3),o.y+o.v*2*i*sin(t()*3))end -- legs
o.x+=o.u -- apply movement
o.y+=o.v -- if kid is too far he cant be called to follow
-- if(rnd()<.5)add(O,{x=o.x+o.y%2*2,y=o.y,r=0}) -- add heat signature for footsteps
elseif(o.r<=0)do -- draw trees
-- ?"⁶:1010105438109254\n⁶:3810925438101010",o.x-4,o.y-16,3
-- ?"⁶:1010105438101054\n⁶:3810105438101010",o.x-4,o.y-16,3
?"⁶. そp そ\n⁶.p そp ",o.x-4,o.y-16,3
-- draw heat fading footsteps, dropped from the release
-- o.r-=1
-- if(o.r<-20)del(O,o)
-- ?".",o.x-2,o.y-4,6+o.r/4
else -- draw ruined houses
for x=-o.w\2,o.w\2,2do -- step by 2 as this takes some cpu cycles
k=16-sin(o.r+x/32+sin(x/8)/16)*6 -- wobbly semi random sine to limit front wall max height
-- houses are made of 5px horizontal blocks: wall, window, doors
-- each block is defined as up to 3 starting vertical positions 0,5,10 starting from house y
-- then and goes typically by 5 pixels or more if this is last segment and roof should be drawn
-- door (10..roof)
-- window: (0..5) + door
-- wall (5..10) + window + door
-- so single table {10,0,5} holds starting positions and number of iterations determines type of segment
-- however this table can be converted into expression i%3*5 assuming i starts from 2
-- h, below, returns house table index, which is be wall for first/last segment, doors in the middle, and windows otherwise (more or less)
-- btw, initially this was a table H={{10},{10,0},{10,0,5}}
h=5-(x+o.w\2+1)\5%2-((x+o.w\2+1)\5==o.w\9 and 2 or 1)
for i=2,h do
-- the min section below also limits top max heigt so houses have gaping holes AND inclined roof at the edges
rect(o.x+x,o.y-i%3*5,1+o.x+x,o.y-min(5+i%3*5+i%3\2*min(5,o.w\2-abs(x)),k),3)
end
if(h>3)?"-",o.x+x,o.y-o.x*o.y%k,2 -- finally, a pseudo random bullet holes only on walls
end
end
end
--------------------
---- RENDER MAP ----
--------------------
-- if(btn(4,1)or mapIndex)do
-- if(mapIndex>=h_blocks*v_blocks) then
-- cls()
-- color(7)
-- stop("blocks: "..h_blocks.."x"..v_blocks.."\nwidth height:"..width.." "..height .."\n".."bounds:"..xi.." "..yi.." "..xx.." "..yx.."\n")
-- else
-- mi = tostr(mapIndex)
-- if(#mi<4)mi="0"..mi
-- if(#mi<4)mi="0"..mi
-- if(#mi<4)mi="0"..mi
-- extcmd('set_filename','map-'.. mi)
-- extcmd('screen')
-- end
-- end
-----------------------
----- TARGET RECT -----
-----------------------
for o in all(T)do -- for all characters are targetted
if(L==1 and #T>1)o.d+=24 -- if a new target is in view the drone is disoriented and has larger diameter for a moment
o.d=min(o.d-1.2,66) -- make the targetting diameter smaller, run you fools!
rect(o.x-o.d,o.y-5-o.d,o.x+o.d,o.y-5+o.d,8) -- target rectangle
-- ?"⁷x0v1i3s0c",0,0 -- play buzzing sound strange slowdown at 3561 -69
?"⁷s000",0,128 -- play buzzing sound strange slowdown at 3561 -69
if(o.d<9)o.r=3?"⁷x5v6i6c<c<c<c<c<c" -- explode player or kid and shake the screen
end
L=#T
---------------------
---- RESET DRONE ----
---------------------
-- if(btn(5)and T)o.d=60
--------------------
----- MINI MAP -----
--------------------
-- if(btn(5,1))do
-- camera()
-- xi=O[1].x
-- xx=O[1].x
-- yi=O[1].y
-- yx=O[1].y
-- for i=2,#O do xi=min(xi,O[i].x)xx=max(xx,O[i].x)yi=min(yi,O[i].y)yx=max(yx,O[i].y)end
-- rh=8+((yx-yi)/(xx-xi))*127
-- rectfill(0,0,127,rh,0)
-- for i=#O,1,-1 do
-- local x,y
-- x=(O[i].x-xi)/(xx-xi)*127
-- y=(O[i].y-yi)/(yx-yi)*rh
-- c=8
-- if(O[i].r>1)c=10
-- if(O[i].r<0)c=11
-- pset(x,y,c)
-- end
-- ?P.x.." "..P.y,0,0,7
-- ?xx\1,0,rh-6,13
-- end
end -- keep those poor bastards here for eternity or until user graciously closes the application
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment