Log In  


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]