decided to share the cs lua runtime:
RUNTIME_LUA = r'''-- cs2p8: choicescript runtime for pico-8
o_txt,o_ch,o_gt,o_lb,o_gs,o_fn,o_pb,o_st,o_if,o_cr,o_gsb,o_rt,o_end,o_lk=
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13
v={}
sl={}
si=1
es={}
rs={}
function cur_lvl() return es[#es] end
function sc() return cur_lvl()[1] end
function ci() return cur_lvl()[2] end
function lb() return cur_lvl()[3] end
-- display state
st="text"
buf={}
bl=1
ch_opts=nil
ch_sel=1
maxl=16
ww=30
function mklb(insts)
local l={}
if insts then
for i=1,#insts do
if insts[i][1]==o_lb then
l[insts[i][2]]=i
end
end
end
return l
end
function pushlvl(insts)
if #es>0 then
cur_lvl()[2]=ci()
end
add(es,{insts,1,mklb(insts)})
end
function poplvl()
if #es>1 then
es[#es]=nil
end
end
function findlb(name)
for i=#es,1,-1 do
local idx=es[i][3][name]
if idx then return i,idx end
end
return nil,nil
end
function _init()
sl=scene_list
si=1
load_scene(sl[si])
end
function load_scene(name)
local insts=s[name]
es={}
rs={}
buf={}
bl=1
st="text"
ch_opts=nil
pushlvl(insts)
step()
end
function getv(name)
return v[name] or 0
end
function setv(name,val)
v[name]=val
end
function isalnum(c)
return (c>="a" and c<="z") or (c>="A" and c<="Z") or (c>="0" and c<="9") or c=="_"
end
function isopchar(c)
return c=="<" or c==">" or c=="=" or c=="!"
end
function evcond(cond)
local i=1
while i<=#cond and cond[i]==" " do i=i+1 end
local var=""
while i<=#cond and isalnum(cond[i]) do
var=var..cond[i]
i=i+1
end
while i<=#cond and cond[i]==" " do i=i+1 end
local op=""
while i<=#cond and isopchar(cond[i]) do
op=op..cond[i]
i=i+1
end
while i<=#cond and cond[i]==" " do i=i+1 end
local val=sub(cond,i)
while #val>0 and val[#val]==" " do
val=sub(val,1,#val-1)
end
while #val>0 and val[1]==" " do
val=sub(val,2)
end
if var=="" or op=="" then return false end
local lv=getv(var)
local rv=tonum(val)
if not rv then rv=getv(val) end
if op==">" then return lv>rv
elseif op=="<" then return lv<rv
elseif op==">=" then return lv>=rv
elseif op=="<=" then return lv<=rv
elseif op=="=" or op=="==" then return lv==rv
elseif op=="!=" then return lv~=rv
end
return false
end
function evset(var,expr)
if type(expr)=="number" then
v[var]=expr
else
local s=expr
if s[1]=="+" then
v[var]=getv(var)+tonum(sub(s,2))
elseif s[1]=="-" then
v[var]=getv(var)-tonum(sub(s,2))
else
v[var]=tonum(s) or 0
end
end
end
function subvars(txt)
local r=""
local i=1
while i<=#txt do
if txt[i]=="$" and i<#txt and txt[i+1]=="{" then
local j=i+2
while j<=#txt and txt[j]~="}" do
j=j+1
end
if j<=#txt then
local vn=sub(txt,i+2,j-1)
r=r..tostr(getv(vn))
i=j+1
else
r=r..sub(txt,i)
i=#txt+1
end
else
r=r..txt[i]
i=i+1
end
end
return r
end
function wrp(txt)
local lines={}
local pos=1
while pos<=#txt do
local nl=nil
for k=pos,#txt do
if txt[k]=="\n" then
nl=k
break
end
end
local line
if nl then
line=sub(txt,pos,nl-1)
pos=nl+1
else
line=sub(txt,pos)
pos=#txt+1
end
if #line==0 then
add(lines,"")
else
while #line>ww do
local br=ww
local found=false
for j=ww,1,-1 do
if line[j]==" " then
br=j
found=true
break
end
end
if found then
add(lines,sub(line,1,br-1))
line=sub(line,br+1)
else
add(lines,sub(line,1,ww))
line=sub(line,ww+1)
end
if line[1]==" " then
line=sub(line,2)
end
if #line<=0 then break end
end
if #line>0 then
add(lines,line)
end
end
end
return lines
end
function add_text(txt)
if txt=="" then
add(buf,"")
return
end
local lines=wrp(subvars(txt))
for i=1,#lines do
add(buf,lines[i])
end
end
function screen_full()
return #buf - bl + 1 > maxl
end
function step()
while true do
local insts=sc()
local pos=ci()
if not insts or pos>#insts then
if #es>1 then
poplvl()
else
si=si+1
if si<=#sl then
load_scene(sl[si])
return
else
st="done"
return
end
end
else
local inst=insts[pos]
local op=inst[1]
if op==o_txt then
add_text(inst[2])
cur_lvl()[2]=pos+1
if screen_full() then
st="text"
return
end
elseif op==o_ch then
st="choice"
ch_opts=inst[2]
ch_sel=1
cur_lvl()[2]=pos+1
return
elseif op==o_gt then
local target=inst[2]
local lvl,idx=findlb(target)
if lvl then
while #es>lvl do poplvl() end
cur_lvl()[2]=idx
else
cur_lvl()[2]=pos+1
end
elseif op==o_lb then
cur_lvl()[2]=pos+1
elseif op==o_gs then
load_scene(inst[2])
return
elseif op==o_fn then
while #es>1 do poplvl() end
si=si+1
if si<=#sl then
load_scene(sl[si])
return
else
st="done"
return
end
elseif op==o_pb then
cur_lvl()[2]=pos+1
st="pagebreak"
return
elseif op==o_st then
evset(inst[2],inst[3])
cur_lvl()[2]=pos+1
elseif op==o_if then
if evcond(inst[2]) then
local then_insts=inst[3]
cur_lvl()[2]=pos+1
if then_insts and #then_insts>0 then
pushlvl(then_insts)
end
else
cur_lvl()[2]=pos+1
end
elseif op==o_cr then
v[inst[2]]=inst[3]
cur_lvl()[2]=pos+1
elseif op==o_gsb then
local target=inst[2]
local lvl,idx=findlb(target)
if lvl then
rs[#rs+1]={lvl=#es,pos=pos+1}
while #es>lvl do poplvl() end
cur_lvl()[2]=idx
else
cur_lvl()[2]=pos+1
end
elseif op==o_rt then
if #rs>0 then
local ret=rs[#rs]
rs[#rs]=nil
-- pop to return level
while #es>ret.lvl do poplvl() end
cur_lvl()[2]=ret.pos
else
cur_lvl()[2]=pos+1
end
elseif op==o_end then
st="ending"
return
elseif op==o_lk then
local link_text=inst[3] or inst[2]
add_text("[link]"..link_text)
cur_lvl()[2]=pos+1
else
cur_lvl()[2]=pos+1
end
end
end
end
function restart_game()
v={}
rs={}
si=1
load_scene(sl[si])
end