16 Commits

Author SHA1 Message Date
7e4833d567 very silly shot pattern 2025-06-28 23:11:27 -07:00
c752e8e1e3 fix hpcols_init
note that I need to re-call this whenever I have a new max hp

also note that vertmeter does not handle 1024 correctly. find out what max hp functions and hardcap it
2025-06-28 23:10:57 -07:00
507f06fb8c more efficient bullet spawning 2025-06-28 22:59:51 -07:00
14849101dd skip call to sparks if there are no sparks
Then don't check for nil spark colors in sparks itself.

Also uses a more verbose but more efficient exit check, and fixes a bug.
2025-06-28 22:49:10 -07:00
2cd7c64dd9 micro-optimize bullet details
Shots that miss are most likely to not be there yet, so check Y-top first.

If I assume shots will never be larger than 16 pixels per side, there is
no reason to check the ship's genuine width and height.
2025-06-28 22:35:36 -07:00
2d5d392df0 reverse strip logic
saves a single cycle per bullet, but...
2025-06-24 23:16:47 -07:00
904fbe6b2e bullets don't expire 2025-06-24 23:06:09 -07:00
36b268e057 initialization is very slightly faster this way, I think 2025-06-24 22:54:03 -07:00
3d6ef03b37 attempt to optimize collision
it's actually worse
2025-06-24 22:36:58 -07:00
d7e029edb6 profile-guided optimization 2025-06-23 01:37:57 -07:00
5e6b98523b delete linked lists and use optimized array impls.
Also removes "suppress" table from collider, use candidate.dead instead
because the set of dead ships and the set of non-colliding ships is
identical.
2025-06-23 00:47:02 -07:00
67970a5164 add draw/update stat readout
surprisingly, update is most of my problem
2025-06-21 17:50:03 -07:00
eaea42f993 fix shot axis 2025-06-21 17:38:05 -07:00
929f47fc78 bullet microoptimization and velocity fix 2025-06-21 17:36:27 -07:00
430a0a4b14 ship_m:move micro-optimizations 2025-06-21 17:17:44 -07:00
e4062d3ccd back out of fast bullet changes, keep optimizations
in the "nothing but turrets" worst-case scenario, fast bullet logic costs 133% of slow bullet logic even when almost all shots on screen are slow. when I back out of this, that scenario is _still_ over 300% CPU but at least it's not 400%. note that this is with a screen mostly full of enemies, so processing all of them and their potential collisions also has cost, so the actual bullet-specific change is closer to 150%, maybe 200%. this is genuinely not as bad as I had thought but it doesn't feel like it will be workable; while my worst-case scenario is implausibly bad it's not actually 3x-likely-peak bad. so I'm going to need to find more optimizations, and probably give up on fast bullets. But I can keep the fast bullet branch around in case I find the headroom to reintroduce it later.
2025-06-21 17:05:36 -07:00
3 changed files with 1149 additions and 252 deletions

504
arrays_profiling.p8 Normal file
View File

@ -0,0 +1,504 @@
pico-8 cartridge // http://www.pico-8.com
version 42
__lua__
-- prof: cpu cycle counter v1.4
-- BY PANCELOR
--[[------------------------
use this cart to precisely
measure code execution time
--------------------------------
★ overview ★
--------------------------------
| tab 0 | usage guide |
| tab 1 | (internals) |
| tab 2 | your code here |
--------------------------------
-----------------------
-- ★ usage guide ★ --
-----------------------
웃: i have two code snippets;
which one is faster?
🐱: edit the last tab with your
snippets, then run the cart.
it will tell you precisely
how much cpu it takes to
run each snippet.
the results are also copied
to your clipboard.
웃: what do the numbers mean?
🐱: the cpu cost is reported
as lua and system cycle
counts. look up stat(1)
and stat(2) for more info.
if you're not sure, just
look at the first number.
lower is faster (better)
웃: why "{locals={9}}"
in the example?
🐱: accessing local variables
is faster than global vars.
so if your test involves
local variables, simulate
this by passing them in:
prof(function(a)
sqrt(a)
end,{ locals={9} })
/!\ /!\ /!\ /!\
local values from outside
the current scope are also
slower to access! example:
global = 4
local outer = 4
prof(function(x)
local _ = x --fast
end,function(x)
local _ = outer --slow
end,function(x)
local _ = global --slow
end,{ locals={4} })
/!\ /!\ /!\ /!\
웃: can i do "prof(myfunc)"?
🐱: no, this sometimes gives
wrong results! always use
inline functions:
prof(function()
--code for myfunc here
end)
as an example, "prof(sin)"
reports "-2" -- wrong! but
"prof(function()sin()end)"
correctly reports "4"
(see the technical notes at
the start of the next tab
for a brief explanation.
technically, "prof(myfunc)"
will work if myfunc was made
by the user, but you will
risk confusing yourself)
---------------
★ method 2 ★
---------------
this cart is based on
code by samhocevar:
https://www.lexaloffle.com/bbs/?pid=60198#p
if you do this method, be very
careful with local/global vars.
it's very easy to accidentally
measure the wrong thing.
here's an example of how to
measure cycles (ignoring this
cart and using the old method)
function _init()
local a=11.2 -- locals
local n=1024
flip()
local tot1,sys1=stat(1),stat(2)
for i=1,n do end --calibrate
local tot2,sys2=stat(1),stat(2)
for i=1,n do local _=sqrt(a) end --measure
local tot3,sys3=stat(1),stat(2)
function cyc(t0,t1,t2) return ((t2-t1)-(t1-t0))*128/n*256/stat(8)*256 end
local lua = cyc(tot1-sys1,tot2-sys2,tot3-sys3)
local sys = cyc(sys1,sys2,sys3)
print(lua.."+"..sys.."="..(lua+sys).." (lua+sys)")
end
run this once, see the results,
then change the "measure" line
to some other code you want
to measure.
note: wrapping the code inside
"_init()" is required, otherwise
builtin functions like "sin"
will be measured wrong.
(the reason is explained at
the start of the next tab)
---------------
★ method 3 ★
---------------
another way to measure cpu cost
is to run something like this:
function _draw()
cls(1)
local x=9
for i=1,1000 do
local a=sqrt(x) --snippet1
-- local b=x^0.5 --snippet2
end
end
while running, press ctrl-p to
see the performance monitor.
the middle number shows how much
of cpu is being used, as a
fraction. (0.60 = 60% used)
now, change the comments on the
two code snippets inside _draw()
and re-run. compare the new
result with the old to determine
which snippet is faster.
note: every loop iteration costs
an additional 2 cycles, so the
ratio of the two fractions will
not match the ratio of the
execution time of the snippets.
but this method can quickly tell
you which snippet is faster.
]]
-->8
--[[ profiler.lua
more info: https://www.lexaloffle.com/bbs/?tid=46117
usage:
prof(function()
memcpy(0,0x200,64)
end,function()
poke4(0,peek4(0x200,16))
end)
passing locals:
prof(
function(a,b)
local c=(a+1)*(b+1)-1
end,
function(a,b)
local c=a*b+a+b
end,
{locals={3,5}}
)
getting global/local variables exactly right
is very tricky; you should always use inline
functions like above; if you try e.g. prof(sin)
the results will be wrong.
# minutiae / notes to self:
---------------------------
doing this at top-level is awkward:
for _=1,n do end -- calibrate
for _=1,n do sin() end -- measure
b/c sin is secretly local at top-level,
so it gives a misleading result (3 cycles).
do it inside _init instead for a
more representative result (4 cycles).
## separate issue:
------------------
if you call prof(sin), it gives the wrong result (-2 cycles) because
it's comparing sin() against noop() (not truly nothing).
but we want the noop() there for normal inline prof() calls,
to avoid measuring the cost of the indirection
(calling func() from inside prof() is irrelevant to
how cpu-expensive func()'s body is)
]]
-- prof(fn1,fn2,...,fnN,[opts])
--
-- opts.locals: values to pass
-- opts.name: text label
-- opts.n: number of iterations
function prof(...)
local funcs={...}
local opts=type(funcs[#funcs])=="table" and deli(funcs) or {}
-- build output string
local msg=""
local function log(s)
msg..=s.."\n"
end
if opts.name then
log("prof: "..opts.name)
end
for fn in all(funcs) do
local dat=prof_one(fn,opts)
log(sub(" "..dat.total,-3)
.." ("
..dat.lua
.." lua, "
..dat.sys
.." sys)")
end
-- copy to clipboard
printh(msg,"@clip")
-- print + pause
cls()
stop(msg)
end
function prof_one(func, opts)
opts = opts or {}
local n = opts.n or 0x200 --how many times to call func
local locals = opts.locals or {} --locals to pass func
-- we want to type
-- local m = 0x80_0000/n
-- but 8MHz is too large to fit in a pico-8 number,
-- so we do (0x80_0000>>16)/(n>>16) instead
-- (n is always an integer, so n>>16 won't lose any bits)
local m = 0x80/(n>>16)
assert(0x80/m << 16 == n, "n is too small") -- make sure m didn't overflow
local fps = stat(8)
-- given three timestamps (pre-calibration, middle, post-measurement),
-- calculate how many more CPU cycles func() took compared to noop()
-- derivation:
-- T := ((t2-t1)-(t1-t0))/n (frames)
-- this is the extra time for each func call, compared to noop
-- this is measured in #-of-frames -- it will be a small fraction for most ops
-- F := 1/30 (seconds/frame) (or 1/60 if this test is running at 60fps)
-- this is just the framerate that the tests run at, not the framerate of your game
-- M := 256*256*128 = 0x80_0000 = 8MHz (cycles/second)
-- (PICO-8 runs at 8MHz; see https://www.lexaloffle.com/dl/docs/pico-8_manual.html#CPU)
-- cycles := T frames * F seconds/frame * M cycles/second
-- optimization / working around pico-8's fixed point numbers:
-- T2 := T*n = (t2-t1)-(t1-t0)
-- M2 := M/n = (M>>16)/(n>>16) := m (e.g. when n is 0x1000, m is 0x800)
-- cycles := T2*M2*F
local function cycles(t0,t1,t2)
local diff = (t2-t1)-(t1-t0)
local e1 = "must use inline functions -- see usage guide"
assert(0<=diff,e1)
local thresh = 0x7fff.ffff/(m/fps)
local e2 = "code is too large or slow -- try profiling manually with stat(1)"
assert(diff<=thresh,e2)
return diff*(m/fps)
end
local noop = function() end -- this must be local, because func is local
flip() --avoid flipping mid-measurement
local atot,asys=stat(1),stat(2)
for _=1,n do noop(unpack(locals)) end -- calibrate
local btot,bsys=stat(1),stat(2)
for _=1,n do func(unpack(locals)) end -- measure
local ctot,csys=stat(1),stat(2)
-- gather results
local tot=cycles(atot,btot,ctot)
local sys=cycles(asys,bsys,csys)
return {
lua=tot-sys,
sys=sys,
total=tot,
}
end
-->8
-- your code here
bigarr = (function()
local ret = {}
for i=1,100 do
ret[i]=i
end
return ret
end)()
--edit me:
prof(function()
local a, sum = bigarr, 0
for x=1,#a do
sum += a[x]
end
end,function()
local a, sum = bigarr, 0
for v in all(a) do
sum += v
end
end,function()
local a, sum = bigarr, 0
foreach(a, function(v) sum+= v end)
end,{ locals={} })
-- "locals" (optional) are
-- passed in as args. see the
-- usage guide for details.
__label__
00006660000000006660000000006660000006006000606066600000066060600660060000000000000000000000000000000000000000000000000000000000
00006000060000006060666000006000000060006000606060600600600060606000006000000000000000000000000000000000000000000000000000000000
00006660666000006060000000006660000060006000606066606660666066606660006000000000000000000000000000000000000000000000000000000000
00000060060000006060666000000060000060006000606060600600006000600060006000000000000000000000000000000000000000000000000000000000
00006660000000006660000000006660000006006660066060600000660066606600060000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00006060000000006660000000006060000006006000606066600000066060600660060000000000000000000000000000000000000000000000000000000000
00006060060000006060666000006060000060006000606060600600600060606000006000000000000000000000000000000000000000000000000000000000
00006660666000006060000000006660000060006000606066606660666066606660006000000000000000000000000000000000000000000000000000000000
00000060060000006060666000000060000060006000606060600600006000600060006000000000000000000000000000000000000000000000000000000000
00000060000000006660000000000060000006006660066060600000660066606600060000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
70000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
07000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
07000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
70000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__sfx__
030100003052500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505

495
collision_profiling.p8 Normal file
View File

@ -0,0 +1,495 @@
pico-8 cartridge // http://www.pico-8.com
version 42
__lua__
-- prof: cpu cycle counter v1.4
-- BY PANCELOR
--[[------------------------
use this cart to precisely
measure code execution time
--------------------------------
★ overview ★
--------------------------------
| tab 0 | usage guide |
| tab 1 | (internals) |
| tab 2 | your code here |
--------------------------------
-----------------------
-- ★ usage guide ★ --
-----------------------
웃: i have two code snippets;
which one is faster?
🐱: edit the last tab with your
snippets, then run the cart.
it will tell you precisely
how much cpu it takes to
run each snippet.
the results are also copied
to your clipboard.
웃: what do the numbers mean?
🐱: the cpu cost is reported
as lua and system cycle
counts. look up stat(1)
and stat(2) for more info.
if you're not sure, just
look at the first number.
lower is faster (better)
웃: why "{locals={9}}"
in the example?
🐱: accessing local variables
is faster than global vars.
so if your test involves
local variables, simulate
this by passing them in:
prof(function(a)
sqrt(a)
end,{ locals={9} })
/!\ /!\ /!\ /!\
local values from outside
the current scope are also
slower to access! example:
global = 4
local outer = 4
prof(function(x)
local _ = x --fast
end,function(x)
local _ = outer --slow
end,function(x)
local _ = global --slow
end,{ locals={4} })
/!\ /!\ /!\ /!\
웃: can i do "prof(myfunc)"?
🐱: no, this sometimes gives
wrong results! always use
inline functions:
prof(function()
--code for myfunc here
end)
as an example, "prof(sin)"
reports "-2" -- wrong! but
"prof(function()sin()end)"
correctly reports "4"
(see the technical notes at
the start of the next tab
for a brief explanation.
technically, "prof(myfunc)"
will work if myfunc was made
by the user, but you will
risk confusing yourself)
---------------
★ method 2 ★
---------------
this cart is based on
code by samhocevar:
https://www.lexaloffle.com/bbs/?pid=60198#p
if you do this method, be very
careful with local/global vars.
it's very easy to accidentally
measure the wrong thing.
here's an example of how to
measure cycles (ignoring this
cart and using the old method)
function _init()
local a=11.2 -- locals
local n=1024
flip()
local tot1,sys1=stat(1),stat(2)
for i=1,n do end --calibrate
local tot2,sys2=stat(1),stat(2)
for i=1,n do local _=sqrt(a) end --measure
local tot3,sys3=stat(1),stat(2)
function cyc(t0,t1,t2) return ((t2-t1)-(t1-t0))*128/n*256/stat(8)*256 end
local lua = cyc(tot1-sys1,tot2-sys2,tot3-sys3)
local sys = cyc(sys1,sys2,sys3)
print(lua.."+"..sys.."="..(lua+sys).." (lua+sys)")
end
run this once, see the results,
then change the "measure" line
to some other code you want
to measure.
note: wrapping the code inside
"_init()" is required, otherwise
builtin functions like "sin"
will be measured wrong.
(the reason is explained at
the start of the next tab)
---------------
★ method 3 ★
---------------
another way to measure cpu cost
is to run something like this:
function _draw()
cls(1)
local x=9
for i=1,1000 do
local a=sqrt(x) --snippet1
-- local b=x^0.5 --snippet2
end
end
while running, press ctrl-p to
see the performance monitor.
the middle number shows how much
of cpu is being used, as a
fraction. (0.60 = 60% used)
now, change the comments on the
two code snippets inside _draw()
and re-run. compare the new
result with the old to determine
which snippet is faster.
note: every loop iteration costs
an additional 2 cycles, so the
ratio of the two fractions will
not match the ratio of the
execution time of the snippets.
but this method can quickly tell
you which snippet is faster.
]]
-->8
--[[ profiler.lua
more info: https://www.lexaloffle.com/bbs/?tid=46117
usage:
prof(function()
memcpy(0,0x200,64)
end,function()
poke4(0,peek4(0x200,16))
end)
passing locals:
prof(
function(a,b)
local c=(a+1)*(b+1)-1
end,
function(a,b)
local c=a*b+a+b
end,
{locals={3,5}}
)
getting global/local variables exactly right
is very tricky; you should always use inline
functions like above; if you try e.g. prof(sin)
the results will be wrong.
# minutiae / notes to self:
---------------------------
doing this at top-level is awkward:
for _=1,n do end -- calibrate
for _=1,n do sin() end -- measure
b/c sin is secretly local at top-level,
so it gives a misleading result (3 cycles).
do it inside _init instead for a
more representative result (4 cycles).
## separate issue:
------------------
if you call prof(sin), it gives the wrong result (-2 cycles) because
it's comparing sin() against noop() (not truly nothing).
but we want the noop() there for normal inline prof() calls,
to avoid measuring the cost of the indirection
(calling func() from inside prof() is irrelevant to
how cpu-expensive func()'s body is)
]]
-- prof(fn1,fn2,...,fnN,[opts])
--
-- opts.locals: values to pass
-- opts.name: text label
-- opts.n: number of iterations
function prof(...)
local funcs={...}
local opts=type(funcs[#funcs])=="table" and deli(funcs) or {}
-- build output string
local msg=""
local function log(s)
msg..=s.."\n"
end
if opts.name then
log("prof: "..opts.name)
end
for fn in all(funcs) do
local dat=prof_one(fn,opts)
log(sub(" "..dat.total,-3)
.." ("
..dat.lua
.." lua, "
..dat.sys
.." sys)")
end
-- copy to clipboard
printh(msg,"@clip")
-- print + pause
cls()
stop(msg)
end
function prof_one(func, opts)
opts = opts or {}
local n = opts.n or 0x200 --how many times to call func
local locals = opts.locals or {} --locals to pass func
-- we want to type
-- local m = 0x80_0000/n
-- but 8MHz is too large to fit in a pico-8 number,
-- so we do (0x80_0000>>16)/(n>>16) instead
-- (n is always an integer, so n>>16 won't lose any bits)
local m = 0x80/(n>>16)
assert(0x80/m << 16 == n, "n is too small") -- make sure m didn't overflow
local fps = stat(8)
-- given three timestamps (pre-calibration, middle, post-measurement),
-- calculate how many more CPU cycles func() took compared to noop()
-- derivation:
-- T := ((t2-t1)-(t1-t0))/n (frames)
-- this is the extra time for each func call, compared to noop
-- this is measured in #-of-frames -- it will be a small fraction for most ops
-- F := 1/30 (seconds/frame) (or 1/60 if this test is running at 60fps)
-- this is just the framerate that the tests run at, not the framerate of your game
-- M := 256*256*128 = 0x80_0000 = 8MHz (cycles/second)
-- (PICO-8 runs at 8MHz; see https://www.lexaloffle.com/dl/docs/pico-8_manual.html#CPU)
-- cycles := T frames * F seconds/frame * M cycles/second
-- optimization / working around pico-8's fixed point numbers:
-- T2 := T*n = (t2-t1)-(t1-t0)
-- M2 := M/n = (M>>16)/(n>>16) := m (e.g. when n is 0x1000, m is 0x800)
-- cycles := T2*M2*F
local function cycles(t0,t1,t2)
local diff = (t2-t1)-(t1-t0)
local e1 = "must use inline functions -- see usage guide"
assert(0<=diff,e1)
local thresh = 0x7fff.ffff/(m/fps)
local e2 = "code is too large or slow -- try profiling manually with stat(1)"
assert(diff<=thresh,e2)
return diff*(m/fps)
end
local noop = function() end -- this must be local, because func is local
flip() --avoid flipping mid-measurement
local atot,asys=stat(1),stat(2)
for _=1,n do noop(unpack(locals)) end -- calibrate
local btot,bsys=stat(1),stat(2)
for _=1,n do func(unpack(locals)) end -- measure
local ctot,csys=stat(1),stat(2)
-- gather results
local tot=cycles(atot,btot,ctot)
local sys=cycles(asys,bsys,csys)
return {
lua=tot-sys,
sys=sys,
total=tot,
}
end
-->8
-- your code here
--edit me:
prof(function(b1,b2)
return
b1[1]<=b2[3]
and b1[2]<=b2[4]
and b1[3]>=b2[1]
and b1[4]>=b2[2]
end,function(b1, b2)
return
b1.x1<=b2.x2
and b1.y1<=b2.y2
and b1.x2>=b2.x1
and b1.y2>=b2.y1
end,{ locals={{1,1,1,1,x1=1,x2=1,y1=1,y2=1},{1,1,1,1,x1=1,x2=1,y1=1,y2=1}} })
-- "locals" (optional) are
-- passed in as args. see the
-- usage guide for details.
__label__
00006660000000006660000000006660000006006000606066600000066060600660060000000000000000000000000000000000000000000000000000000000
00006000060000006060666000006000000060006000606060600600600060606000006000000000000000000000000000000000000000000000000000000000
00006660666000006060000000006660000060006000606066606660666066606660006000000000000000000000000000000000000000000000000000000000
00000060060000006060666000000060000060006000606060600600006000600060006000000000000000000000000000000000000000000000000000000000
00006660000000006660000000006660000006006660066060600000660066606600060000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00006060000000006660000000006060000006006000606066600000066060600660060000000000000000000000000000000000000000000000000000000000
00006060060000006060666000006060000060006000606060600600600060606000006000000000000000000000000000000000000000000000000000000000
00006660666000006060000000006660000060006000606066606660666066606660006000000000000000000000000000000000000000000000000000000000
00000060060000006060666000000060000060006000606060600600006000600060006000000000000000000000000000000000000000000000000000000000
00000060000000006660000000000060000006006660066060600000660066606600060000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
70000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
07000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00700000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
07000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
70000000888800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
__sfx__
030100003052500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505005050050500505

View File

@ -59,90 +59,45 @@ function mknew(tt)
return tt
end
-- intrusive singly-linked list.
-- cannot be nested or crossed!
linked_list = mknew{
is_linked_list=true,
init = function(x)
x.next=nil
x.tail=x
end,
}
function linked_list:push_back(x)
self.tail.next = x
self.tail = x
end
function linked_list:push_front(x)
if (not self.next) self.tail = x
x.next = self.next
self.next = x
end
-- vore eats another linked list
-- by appending its contents.
-- the ingested linked is empty.
function linked_list:vore(x)
if (not x.next) return
self.tail.next = x.next
self.tail = x.tail
x.next = nil
x.tail = x
end
-- strip calls f(x) for each
-- node, removing each node for
-- which f(x) returns true.
function linked_list:strip(f)
local p, n = self, self.next
while n do
if f(n) then
p.next = n.next
else
p = n
-- call f on everything on list
-- and return a new list of
-- items for which f was true.
function filtered(list, f)
local ret, n = {}, 0
for v in all(list) do
if f(v) then
n += 1
ret[n] = v
end
n = n.next
end
self.tail = p
return ret
end
-- stripmove calls x:move() for
-- each node, removing each node
-- for which x:move() is true.
function linked_list:stripmove()
local p, n = self, self.next
while n do
if n:move() then
p.next = n.next
else
p = n
-- call :move on everything on
-- src and dump everything
-- for which it returned true
-- onto dest.
function appendmove(dest, src)
local n = #dest
for v in all(src) do
if v:move() then
n += 1
dest[n] = v
end
n = n.next
end
self.tail = p
end
-- optimized special case -
-- could be done with strip but
-- this avoids extra function
-- calls and comparisions since
-- draw isn't allowed to kill
-- the item
function linked_list:draw()
local n = self.next
while n do
n:draw()
n = n.next
end
end
function linked_list:pop_front()
local ret = self.next
if (not ret) return
self.next = ret.next
if (not ret.next) ret.tail = nil
-- like filtered, but calls
-- :move on stuff in the list
-- instead of taking a func arg.
function filtermoved(list)
local ret, n = {}, 0
for v in all(list) do
if v:move() then
n += 1
ret[n] = v
end
end
return ret
end
@ -156,12 +111,12 @@ function _init()
end
function once_next_frame(f)
new_events:push_back{
add(new_events, {
move = function()
f()
return true
end,
}
})
end
-- health gradients for 1..5 hp
@ -175,20 +130,20 @@ hpcols_lut = csv[[36
-- call after any change to maxhp
-- configures health gradient
function init_hpcols()
hpcols = hpcols_lut[min(primary_ship.maxhp,6)]
hpcols = hpcols_lut[min(primary_ship.maxhp,5)]
end
function wipe_game()
xpwhoosh = nil
primary_ship = player.new()
init_hpcols()
eships = linked_list.new()
pbullets = linked_list.new()
ebullets = linked_list.new()
intangibles_fg = linked_list.new()
intangibles_bg = linked_list.new()
events = linked_list.new()
new_events = linked_list.new()
eships = {}
pbullets ={}
ebullets ={}
intangibles_fg = {}
intangibles_bg = {}
events = {}
new_events = {}
primary_ship.main_gun = zap_gun_p.new()
primary_ship.main_gun:peel()
gframe = 0
@ -199,6 +154,7 @@ end
function _update60()
mode:update()
ustat = stat(1)
end
function ones(n)
@ -240,10 +196,11 @@ function updategame()
current_wave = flotilla.new()
current_wave:load(rnd() > 0.5 and 7 or 0, 0, min(ones(waves_complete)\2, 4))
end
events:vore(new_events)
for _, lst in ipairs{events, intangibles_bg, eships} do
lst:stripmove()
end
events = filtermoved(events)
appendmove(events, new_events)
new_events = {}
intangibles_bg = filtermoved(intangibles_bg)
eships = filtermoved(eships)
-- eship collider will be used
-- both for pship and pbullets.
@ -254,38 +211,30 @@ function updategame()
local pbox = hurtbox(ps)
for es in eship_collider:iterate_collisions(pbox) do
ps:hitship(es)
if(es:hitship(ps)) eship_collider:yoink(es)
es:hitship(ps)
end
ebullets:strip(function(eb)
local disposition
repeat
disposition=eb:step()
if collides(pbox, hurtbox(eb)) then
ps:hitbullet(eb)
if (eb:hitship(ps)) return true
end
until disposition
return disposition == "dead"
ebullets = filtered(ebullets, function(eb)
if (not eb:move()) return
if (not collides(pbox, hurtbox(eb))) return true
ps:hitbullet(eb)
return not eb:hitship(ps)
end)
else
ebullets:strip(function(eb) repeat until eb:step() end)
ebullets=filtermoved(ebullets)
end
pbullets:strip(function(pb)
local disposition
repeat
disposition=pb:step()
for es in eship_collider:iterate_collisions(hurtbox(pb)) do
if (es:hitbullet(pb)) eship_collider:yoink(es)
if (pb:hitship(es)) return true
end
until disposition
return disposition == "dead"
pbullets = filtered(pbullets, function(pb)
if (not pb:move()) return
for es in eship_collider:iterate_collisions(hurtbox(pb)) do
es:hitbullet(pb)
if (pb:hitship(es)) return
end
return true
end)
intangibles_fg:stripmove()
intangibles_fg = filtermoved(intangibles_fg)
if waves_complete == 32767 and not eships.next and not ebullets.next and not events.next then
if waves_complete == 32767 and #eships == 0 and #ebullets == 0 and #events == 0 then
game_state = win
end
if (ps.dead) game_state = lose
@ -304,6 +253,8 @@ end
function _draw()
mode:draw()
local ds = stat(1)
print(tostr(ustat).." + "..tostr(ds-ustat), 0, 122, 7)
end
function drawgame_top()
@ -351,15 +302,6 @@ function puke(item, indent, seen, hidekey)
end
local xpfx = pfx.." "
if item.is_linked_list then
local ret,n = "linked_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)
@ -379,8 +321,10 @@ end
function drawgame()
clip(0,0,112,128)
rectfill(0,0,112,128,0)
for drawable in all{intangibles_bg, pbullets, primary_ship, eships, ebullets, intangibles_fg} do
drawable:draw()
for drawables in all{intangibles_bg, pbullets, {primary_ship}, eships, ebullets, intangibles_fg} do
for d in all(drawables) do
d:draw()
end
end
clip(0,0,128,128)
drawhud()
@ -592,21 +536,27 @@ function ship_m:constrain(p, dp, pmin, pmax, want)
end
function ship_m:move()
if (self.dead) return;
self:refresh_shield()
local dx, dy, shoot_spec1, shoot_spec2 = self:act()
dx = self:constrain(self.x, self.xmomentum, self.xmin, self.xmax, dx)
dy = self:constrain(self.y, self.ymomentum, self.ymin, self.ymax, dy)
local sg, xm, ym, sp = self.special_guns, self.xmomentum, self.ymomentum, self.sparks
dx = self:constrain(self.x, xm, self.xmin, self.xmax, dx)
dy = self:constrain(self.y, ym, self.ymin, self.ymax, dy)
self:maybe_shoot(self.main_gun)
if (shoot_spec1 and self.special_guns) self:maybe_shoot(self.special_guns[1])
if (shoot_spec2 and self.special_guns) self:maybe_shoot(self.special_guns[2])
spark(self.sparks, self.x + 4*self.size, self.y + 4*self.size, dx*2.5, dy*2.5, self.sparkodds)
self.xmomentum = self:calc_velocity(self.xmomentum, dx)
self.ymomentum = self:calc_velocity(self.ymomentum, dy)
if sg then
if (shoot_spec1) self:maybe_shoot(sg[1])
if (shoot_spec2) self:maybe_shoot(sg[2])
end
if(sp) spark(sp, self.x + 4*self.size, self.y + 4*self.size, dx*2.5, dy*2.5, self.sparkodds)
xm = self:calc_velocity(xm, dx)
ym = self:calc_velocity(ym, dy)
self.x += self.xmomentum
self.y += self.ymomentum
self.x += xm
self.y += ym
self.xmomentum = xm
self.ymomentum = ym
return false
return true
end
function ship_m:draw()
@ -618,12 +568,8 @@ end
function hurtbox(ship)
local h = ship.hurt
return {
x=ship.x + h.x_off,
y=ship.y + h.y_off,
width=h.width,
height=h.height
}
local x1,y1 = ship.x + h.x_off, ship.y+h.y_off
return {x1=x1, y1=y1, x2=x1+h.width, y2=y1+h.height}
end
function ship_m:maybe_shoot(gun)
@ -672,14 +618,6 @@ end
-->8
-- bullet and gun behaviors
function player_blt_cat()
return pbullets
end
function enemy_blt_cat()
return ebullets
end
-- x, y: position
-- dx, dy: movement (linear)
-- f: frames remaining; nil for no limit
@ -692,18 +630,15 @@ end
-- details, check impl
-- damage -- damage to do to
-- a ship that gets hit
-- category -- function that
-- returns which bullet list
-- to spawn onto
-- category -- string naming
-- which bullet list to spawn
-- onto, from _ENV
-- hitship -- event handler,
-- takes ship as argument.
-- default: die, return true.
-- returns whether to delete
-- the bullet
bullet_base = mknew{
steps=1,
current_step=0
}
bullet_base = mknew{}
gun_base = mknew{
shoot_ready = -32768,
@ -829,35 +764,20 @@ function bullet_base:hitship(_)
return true
end
function bullet_base:step()
self.current_step=(self.current_step+1)%self.steps
self.x += self.dx
self.y += self.dy
if (self.f) self.f -= 1
if ((self.y > 130) or (self.y < -self.height*8) or (self.f and self.f < 0) or (self.x > 128) or (self.x < -self.width*8)) return "dead"
if (self.current_step == 0) return "stop"
function bullet_base:move()
local x,y = self.x + self.dx, self.y+self.dy
self.x,self.y=x,y
return (y<=128) and (y >= -16) and (x <= 128) and (x >= -16)
end
warmpal = {
[0]=0,1,2,1,2,1,5,10,2,4,9,3,13,5,8,9
}
function bullet_base:draw()
local s,bx,by,dx,dy,w,h,st = self.sprite,self.x,self.y,self.dx,self.dy,self.width,self.height,self.steps
if st > 1 then
pal(warmpal)
for n=st-1,1,-1 do
spr(s, bx-n*dx, by-n*dy, w, h)
end
pal()
end
spr(s, bx, by, w, h)
spr(self.sprite, self.x, self.y, self.width, self.height)
end
function bullet_base:spawn_at(x, y)
self.x = x - self.x_off
self.y = y - self.y_off
self.category():push_back(self)
add(_ENV[self.category], self)
end
function gun_base:shoot(x, y)
@ -889,25 +809,20 @@ function gun_base:actually_shoot(x, y)
self.shot_idx = idx
shots = shots[idx]
for s in all(shots) do
local a,xo,v = unpack(s)
v = v or veloc
xo = xo or 0
local a,xo,v = s[1]+aim, s[2] or 0, s[3] or veloc
-- reverse x-offset for negative base angle
if (aim < 0) xo = -xo
a += aim
-- todo: switch munition
-- depending on angle
-- (allows for non-round
-- sprites and hitboxes on
-- shots from guns with
-- widely varying angles)
local m = munition.new{}
-- todo: automatically make
-- high velocity shots do
-- multiple collision checks
m.dy = sin(a) * veloc
m.dx = cos(a) * veloc
m:spawn_at(x+(xo or 0), y)
local m = munition.new{
dx=cos(a)*v,
dy=sin(a)*v
}
m:spawn_at(x+xo, y)
end
end
@ -919,27 +834,26 @@ zap_p = mknew(bullet_base.new{
--shape
sprite = 8, --index of ammo sprite
width = 0.25, --in 8x8 blocks
height = 0.25,
height = 1,
hurt = { -- hurtbox - where this ship can be hit
x_off = 0, -- upper left corner
y_off = 0, -- relative to sprite
width = 2,
height = 2,
height = 8,
},
x_off = 1, -- how to position by ship
y_off = 0,
steps=4,
damage = 1,
hitship = const_fxn(true),
category = player_blt_cat,
category = "pbullets",
})
zap_gun_p = mknew(gun_base.new{
icon = 19,
cooldown = 0x0.0020, -- frames between shots
veloc = 2,
veloc = 7,
aim = 0.25,
munition = zap_p,
hdr = "mAIN gUN",
@ -975,7 +889,7 @@ blast = mknew(bullet_base.new{
if self.damage > 0 and not self.awaitcancel then
self.awaitcancel = true
once_next_frame(function()
new_events:push_back{
add(new_events, {
wait = 4,
obj = self,
saved_dmg = self.damage,
@ -986,13 +900,13 @@ blast = mknew(bullet_base.new{
return true
end
end,
}
})
self.damage = 0
self.awaitcancel = false
end)
end
end,
category=player_blt_cat
category="pbullets"
})
blast_gun = mknew(gun_base.new{
@ -1031,14 +945,14 @@ protron_e = mknew(bullet_base.new{
y_off = 4,
damage = 1,
category = enemy_blt_cat,
category = "ebullets",
})
protron_p = mknew(protron_e.new{
sprite=23,
dym = -1,
y_off = 0,
category=player_blt_cat,
category="pbullets",
})
protron_gun_e = mknew(gun_base.new{
@ -1085,13 +999,13 @@ vulcan_e = mknew(bullet_base.new{
y_off = 0,
damage = 0.5,
category=enemy_blt_cat
category="ebullets"
})
vulcan_p = mknew(vulcan_e.new{
sprite=22,
y_off = 4,
category=player_blt_cat
category="pbullets"
})
vulcan_gun_e = mknew(gun_base.new{
@ -1108,7 +1022,7 @@ machine_gun_e = mknew(vulcan_gun_e.new{
icon = 38,
clip_size = 12,
clip_interval = 0x0.005a,
shots = {{{0, 2}}, {{0, -2}}}
shots = {{{0.0625, 2}}, {{-0.0625, -2}}}
})
vulcan_gun_p = mknew(vulcan_gun_e.new{
@ -1150,8 +1064,8 @@ player = mknew(ship_m.new{
boss = true, -- dramatic special effects
-- health
hp = 3, -- current health, non-regenerating
maxhp = 3, -- player only; other ships never heal
hp = 1024, -- current health, non-regenerating
maxhp = 1024, -- player only; other ships never heal
shield = 2, -- regenerates
maxshield = 2,
@ -1427,21 +1341,19 @@ end
-->8
-- collisions
-- box: x, y, width, height
function collides(box1, box2)
return not (
box1.x>box2.x+box2.width
or box1.y>box2.y+box2.height
or box1.x+box1.width<box2.x
or box1.y+box1.height<box2.y)
-- box: x1, y1, x2, y2
function collides(b1, b2)
return
b1.y1<=b2.y2
and b1.y2>=b2.y1
and b1.x1<=b2.x2
and b1.x2>=b2.x1
end
collider = mknew{
init = function(x)
x.suppress = {}
local p, n = x.from, x.from.next
while n do
local from = x.from
for n in all(from) do
-- insert
for i in all(collider_indexes(hurtbox(n))) do
local a = x[i]
@ -1451,35 +1363,20 @@ collider = mknew{
end
add(a, n)
end
-- prepare yoink
n.prev = p
p = n
n = n.next
end
end,
}
function collider_indexes(box)
local ret = {}
for x = box.x\8, (box.x+box.width)\8 do
for y = box.y\8, (box.y+box.height)\8 do
add(ret, x+256*y)
local ret, ylo, yhi = {}, box.y1\8, box.y2\8
for x = box.x1\8, box.x2\8 do
for y = ylo, yhi do
add(ret, x+(y<<8))
end
end
return ret
end
function collider:yoink(item)
self.suppress[item]=true
local p,n = item.prev,item.next
p.next = n
if n then
n.prev = p
else
self.from.tail = p
end
end
function collider:iterate_collisions(box)
local seen = { }
local bucket_ids = collider_indexes(box)
@ -1495,7 +1392,7 @@ function collider:iterate_collisions(box)
bi += 1
if not seen[candidate] then
seen[candidate] = true
if (not self.suppress[candidate] and collides(box, hurtbox(candidate))) return candidate
if (not candidate.dead and collides(box, hurtbox(candidate))) return candidate
end
end -- done with this bucket
bi=1
@ -1582,7 +1479,7 @@ function flotilla:load(ulc_cx, ulc_cy, lvl)
for s in all(row) do
counts[s.ship_t] += 1
s.x,s.y=rnd_spawn_loc()
eships:push_back(s)
add(eships, s)
end
add(rows, row)
end
@ -1669,13 +1566,13 @@ blip_fx = mknew{
}
function blip_fx:move()
if (self.cancel) return true
if (self.cancel) return
self.frames -= 1
if self.frames < 0 then
self.obj.fx_pal = nil
return true
return
end
return false
return true
end
function blip_fx:abort()
@ -1696,7 +1593,7 @@ end
function blip(obj, col)
obj.fx_pal = blip_pals[col]
if (obj.___fx_pal_event) obj.___fx_pal_event:abort()
events:push_back(blip_fx.new{frames=3, obj=obj})
add(events, blip_fx.new{frames=3, obj=obj})
end
bossspark = split"7,7,10,10,9,9,9,8,8,8,2,2,5,5"
@ -1720,27 +1617,28 @@ spark_particle=mknew{}
function spark_particle:move()
if (rnd(4) < 1) self.sidx += 1
if (self.sidx > #self.sprs) return true
if (self.sidx > #self.sprs) return
self.x += self.dx
self.y += self.dy
self.dx -= mid(0.05,-0.05, self.dx)
self.dy -= mid(0.05,-0.05, self.dy)
return true
end
function spark_particle:draw()
pset(self.x,self.y,self.sprs[self.sidx])
end
function spark(sprs, x, y, dx, dy, odds, fg)
if (sprs==nil or flr(rnd(odds) or (abs(dx) < 0.5 and abs(dy))) ~= 0) return
if ((dx > -0.5 and dx < 0.5 and dy > -0.5 and dy < 0.5) or rnd(odds) >= 1) return
local target = fg and intangibles_fg or intangibles_bg
target:push_back(spark_particle.new{
target[#target+1] = spark_particle.new{
x = x + rnd(4) - 2,
y = y + rnd(4) - 2,
sprs = sprs,
sidx = 1,
dx = dx * rnd(2),
dy = dy * rnd(2),
})
}
end
-->8
-- powerups
@ -1750,7 +1648,7 @@ xp_gem = mknew(bullet_base.new{
dy = 0.75,
width=1, -- not used for spr but
height=1,-- bullet_base uses it
category = enemy_blt_cat,
category = "ebullets",
damage = 0,
hurt = {
x_off = -2,
@ -2032,14 +1930,14 @@ function rearm_mode:update()
end
__gfx__
00000000000650000000000000000000bb0b50b59909209200cc0c00000000007b00000082000000e00e8002e00e800200333300002222000000000000000000
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb00bb000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
00700700006d6500000000000cddddd00b33335009444420c00c000c0b33333000000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b3500000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b33500000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
0070070065666765000000000ddddd100b33355009444220c000000c0333335000000000a800000008888820048488203bbaabb3288aa8820000000000000000
000000006506506500000000001111000b0b5050090920200c0000c00055550000000000a2000000008882000048420003bbbb30028888200000000000000000
00000000650000650000000000000000000b50000009200000c0cc00000000000000000082000000000820000008200000333300002222000000000000000000
00000000000650000000000000000000bb0b50b59909209200cc0c00000000003b00000082000000e00e8002e00e800200333300002222000000000000000000
00000000006765000000000000cccc00b50b3055920940220c0000c000bbbb0037000000a2000000e0e8880240e8480403bbbb30028888200000000000000000
00700700006d6500000000000cddddd00b33335009444420c00c000c0b333330b7000000a8000000e88e2882e48e24823bbaabb3288aa8820000000000000000
00077000067c665000000000cdd10cd10b3dd350094dd42000c0000cb3350b35b7000000a8000000e88e2882484e24423ba77ab328a77a820000000000000000
00077000067d665000000000cd10cdd100b3350000944200c0000000b350b335b7000000a8000000e88e2882e84e28823ba77ab328a77a820000000000000000
0070070065666765000000000ddddd100b33355009444220c000000c03333350b7000000a800000008888820048488203bbaabb3288aa8820000000000000000
000000006506506500000000001111000b0b5050090920200c0000c000555500b7000000a2000000008882000048420003bbbb30028888200000000000000000
00000000650000650000000000000000000b50000009200000c0cc0000000000b700000082000000000820000008200000333300002222000000000000000000
0000000000065000000650000003b0000070070080000000700000000bb0000008800000000000000009200000000000cccccccd000650000000000000000000
000000000067500000076500000370000005500080000000b0000000b76300008a920000000000009009200200000000c111111d006765000000000000000000
00000000006d6500006d6500000b7000700660079000000030000000b663000089920000000550009994444200000000c111111d006d65000000000000000000