Skip to content

Instantly share code, notes, and snippets.

@boxmein
Last active August 29, 2015 14:12
Show Gist options
  • Select an option

  • Save boxmein/55ad7283b75f0a399f2a to your computer and use it in GitHub Desktop.

Select an option

Save boxmein/55ad7283b75f0a399f2a to your computer and use it in GitHub Desktop.
Procedural save-generator library/function for simtr/The-Powder-Toy.
-- Procedural Save Generator
-- -------------------------
--
-- A script that lets you create other scripts that create saves procedurally!
-- If you've ever done vertex shaders, this is kinda like it.
-- A few examples are in the bottom that you can uncomment to try.
--
-- NOTE: this script alone does seemingly nothing. It creates a function named
-- "runner" that can be called with another function that's described below.
--
-- Version 1.0
-- https://gist.github.com/boxmein/55ad7283b75f0a399f2a
-- (c) boxmein 2014 MIT licensed
-- http://opensource.org/licenses/MIT
--- nil runner ( table func(int x, int y) )
-- This function takes as its argument another function, called a callback.
-- It calls the callback for each X,Y coordinate combination, and decides if or
-- not to create a particle according to the return value of the callback.
--
-- The callback is a function that takes 2 arguments {number x, number y} and
-- returns a table that contains properties a particle might have.
--
-- The table MUST contain a 'type' property.
--
-- The table CAN contain particle properties from the following list:
-- life, ctype, x, y, vx, vy, temp, flags, tmp, tmp2, dcolour
--
-- Other properties are kind of useless, and might throw errors.
--
-- @param callback {function(int x, int y)} the generating callback thingummywut
-- @returns nil
-- @throws {assertion error} when callback returns with a truthy value that
-- has no 'type' property
function runner(callback)
for y = 0, 384, 1 do
for x = 0, 612, 1 do
local t = callback(x, y)
-- return a falsey value for no particle
if t then
-- set the type, then clear it to avoid re-setting it in the
-- loop-de-loop
assert(t.type)
if type(t.type) == 'string' then
t.type = tpt.element(t.type)
end
local id = sim.partCreate(-3, x, y, t.type)
for k, v in pairs(t) do
sim.partProperty(id, k, v)
end
-- done!
end
end
end
end
--[[ Example: Sierpinski triangle with DUST!
runner(function(x, y)
if bit.band(x, y) == 0 then
return { ['type'] = 'dust' }
end
end) --]]--
--[[ Example 2: A circle in the center!
runner(function(x, y)
local el = { ['type'] = 'dmnd' }
local hw = 612/2 -- center coordinates
local hh = 384/2
-- if distance from current cell to center > 30 and < 31 then OK
-- else nope.avi
local dist = math.sqrt(
(x - hw) * (x - hw) +
(y - hh) * (y - hh)
)
if dist > 30 and dist < 31 then
return el
end
end) --]]--
--[[ Example 3: Random particles!
runner(function(x, y)
if math.random() < 0.2 then
return { ['type'] = math.random(1, 255) }
end
end) --]]--
--[[ Example 4: Distance-determined particles!
runner(function(x, y)
local hw = 612/2 -- center coordinates
local hh = 384/2
-- if distance from current cell to center > 30 and < 31 then OK
-- else nope.avi
local dist = math.sqrt(
(x - hw) * (x - hw) +
(y - hh) * (y - hh)
)
-- concentric circles!
if dist < 255 then
return { ['type'] = math.floor(dist) }
end
end) --]]--
-- Might want to do this too
-- tpt.set_pause(1)
-- Minified version of proc_save.lua 1.0
-- https://gist.github.com/boxmein/55ad7283b75f0a399f2a
-- (c) boxmein 2014 MIT licensed
-- http://opensource.org/licenses/MIT
function runner(callback) for y = 0, 384, 1 do for x = 0, 612, 1 do local t = callback(x, y) if t then assert(t.type) if type(t.type) == 'string' then t.type = tpt.element(t.type) end local id = sim.partCreate(-3, x, y, t.type) for k, v in pairs(t) do sim.partProperty(id, k, v) end end end end end

To create a script for this library, you must define a function that looks something like this:

function myThing(x, y) 
  -- ...
end

As you may already have guessed, this function gets passed the X and Y coordinates for the particle it creates. This function must return either a falsey value (like nothing, or false) when you want no particle to be created in the X,Y location. Otherwise, this function must return a table of values that specify the particle about to be created. The only value you have to include is type.

(Other values which you can include are life, ctype, x, y, vx, vy, temp, flags, tmp, tmp2, dcolour.)

function myThing(x, y) 
  return {
    type = 'DMND',
    dcolour = 0xFF00FF00,
    tmp = 1
  }
end

The above code is actually a valid callback - it will make the entire screen into DMND.

function myThing(x, y) 
  if x == 200 and y == 300 then
    return { type = 'DMND' } 
  end
end

This script will make only the particle at (200, 300) into DMND. Everything else will be left untouched.

For a more complex example, see the script below:

function myThing(x, y)

  -- Define a center point for the circle.
  local hw = 612/2
  local hh = 384/2

  -- Calculate the distance between the current point and the center point.
  -- A = (x, y), B = (hw, hh) in this image: 
  -- http://mathurl.com/pnjv5wv
  local dist = math.sqrt(
    (x - hw) * (x - hw) +
    (y - hh) * (y - hh)
  )

  -- If said distance is more than 30 but less than 31 then create the particle. 
  if dist > 30 and dist < 31 then
    return { ['type'] = 'dmnd' }
  end
  
  -- Otherwise create no particle.
end

To run any of the above scripts, define the function and afterwards call the runner function defined in my script with the function as its only parameter.

runner(myThing)

Alternatively you could define the function inline and not even name it:

runner( function(x, y) ... end )

... And the rest is up to you. Good luck!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment