Here's a basic CSV parser I use to save tokens in my games. It uses split
for the heavy lifting.
Basic version
The most basic version is 66 tokens.
function data(str) local lines=split(str,"\n") local props,d=split(deli(lines,1)),{} for l in all(lines) do local o,v={},split(l) for i=1,#v do o[props[i]]=v[i] end if(o.name)d[o.name]=o add(d,o) end return d end |
It can be used like this (4 tokens):
spritetypes=data [[name,sx,sy,sw,sh alien,0,0,2,2 boss,4,0,4,4 player,0,2,4,2 bomb1,3,0,1,1 bomb2,2,1,1,1 bullet,2,0,1,1]] |
The data
function returns an array of objects, with properties that map to the CSV columns.
If objects have a name
property, then they will be added as a property to the main object with that name.
Which means we can reference the "player" sprite type as:
print(spritetypes.player.sx) |
References
This version allows you to reference objects defined in other CSV tables. It's 117 tokens.
glob={ ["true"]=true, ["false"]=false } function data(str,globname) function fixval(v) if v=="" then return nil elseif glob[v]~=nil then return glob[v] end return v end local lines=split(str,"\n") local props,d=split(deli(lines,1)),{} for l in all(lines) do local o,v={},split(l) for i=1,#v do o[props[i]]=fixval(v[i]) end if o.name then d[o.name]=o if(globname)glob[globname.."."..o.name]=o end add(d,o) end return d end |
It can be used like this:
spritetypes=data( [[name,sx,sy,sw,sh alien,0,0,2,2 boss,4,0,4,4 player,0,2,4,2 bomb1,3,0,1,1 bomb2,2,1,1,1 bullet,2,0,1,1]],"spritetype") enemytypes=data( [[name,spritetype,health boss,spritetype.boss,100 alien,spritetype.alien,10]],"enemytype") enemies=data [[typ,x,y,weapons enemytype.boss,112,40 enemytype.alien,20,50 enemytype.alien,80,50]] |
The routine maintains a glob
variable containing objects that can be referenced, keyed by their name.
The second parameter tells data
to add objects to the glob
table, so that they can be referenced from other CSV tables. For example passing "spritetype" to data
means that the "boss" sprite type will be added as "spritetype.boss".
This version also maps "true" and "false" as their corresponding boolean values.
Arrays
And if you need array properties, this final version supports them. It's 170 tokens.
glob={ ["true"]=true, ["false"]=false } function maparray(a,fn) local r={} for e in all(a) do add(r,fn(e)) end return r end function data(str,globname) function fixval(v) if v=="" then return nil elseif type(v)=="string" and sub(v,1,1)=="{" then return maparray(split(sub(v,2,#v-1),":"),fixval) elseif glob[v]~=nil then return glob[v] end return v end local lines=split(str,"\n") local props,d=split(deli(lines,1)),{} for l in all(lines) do local o,v={},split(l) for i=1,#v do o[props[i]]=fixval(v[i]) end if o.name then d[o.name]=o if(globname)glob[globname.."."..o.name]=o end add(d,o) end return d end |
It's used like so:
spritetypes=data( [[name,sx,sy,sw,sh alien,0,0,2,2 boss,4,0,4,4 player,0,2,4,2 bomb1,3,0,1,1 bomb2,2,1,1,1 bullet,2,0,1,1]],"spritetype") enemytypes=data( [[name,spritetype,weapons,health boss,spritetype.boss,{spritetype.bomb1:spritetype.bomb2},100 alien,spritetype.alien,{spritetype.bullet},10]],"enemytype") enemies=data [[typ,x,y,weapons enemytype.boss,112,40 enemytype.alien,20,50 enemytype.alien,80,50]] |
Array values are enclosed in braces {
}
, and separated by semicolons :
.
(Commas would have been nicer, but that would require handling nesting, which couldn't use split
and would need way more code.)
I've used this routine pretty heavily in games like Trial of the Sorcerer and Combat Chopper, and it quickly pays for itself token wise. (Bonus if you can reuse the maparray
function in other code.)
For large CSV tables in complex projects I even store them in a separate CSV file that can be edited with a CSV editor, and paste it back into the Pico-8 source after making any changes.
[Please log in to post a comment]