|
local tne |
|
|
|
|
|
|
|
local function csloc(level) |
|
if level == nil then level = 0 end |
|
|
|
|
|
|
|
|
|
|
|
|
|
local info = debug.getinfo(level+2, "Sl") |
|
local filename |
|
if string.sub(info.source, 1, 1) == "@" then |
|
filename = string.sub(info.source, 2) |
|
else |
|
filename = "data:" .. info.source |
|
end |
|
return |
|
{ |
|
filename = filename, |
|
line_number = info.currentline |
|
} |
|
end |
|
|
|
local tne |
|
|
|
local function fraction(num, den) |
|
if den == nil then den = 1 end |
|
return tne(num)/tne(den) |
|
end |
|
|
|
local function funcapp(name, arguments, sloc) |
|
local fixed_arguments = {} |
|
for k,v in pairs(arguments) do |
|
fixed_arguments[k] = tne(v) |
|
end |
|
return tne{ |
|
type = "function-application", |
|
source_location = sloc or csloc(1), |
|
function_name = name, |
|
arguments = fixed_arguments |
|
} |
|
end |
|
|
|
local noise_expression_metatable = |
|
{ |
|
__add = function(lhs, rhs) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "add", |
|
arguments = { tne(lhs), tne(rhs) } |
|
} |
|
end, |
|
__sub = function(lhs, rhs) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "subtract", |
|
arguments = { tne(lhs), tne(rhs) } |
|
} |
|
end, |
|
__unm = function(val) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "subtract", |
|
arguments = { tne(0), tne(val) } |
|
} |
|
end, |
|
__mul = function(lhs, rhs) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "multiply", |
|
arguments = { tne(lhs), tne(rhs) } |
|
} |
|
end, |
|
__div = function(lhs, rhs) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "divide", |
|
arguments = { tne(lhs), tne(rhs) } |
|
} |
|
end, |
|
__pow = function(lhs, rhs) |
|
return tne{ |
|
type = "function-application", |
|
source_location = csloc(1), |
|
function_name = "exponentiate", |
|
arguments = { tne(lhs), tne(rhs) } |
|
} |
|
end |
|
|
|
|
|
} |
|
|
|
function fixne(v) |
|
if v.type == nil then |
|
error("Tried to create noise expression with no 'type'") |
|
end |
|
if v.source_location == nil then |
|
error("Noise expression has no 'source_location'") |
|
end |
|
|
|
setmetatable(v, noise_expression_metatable) |
|
|
|
|
|
|
|
return v |
|
end |
|
|
|
function log2(power) |
|
return funcapp("log2", { power }) |
|
end |
|
|
|
local function fmod(lhs, rhs) |
|
return funcapp("modulo", { lhs, rhs }) |
|
end |
|
|
|
local function floor(value) |
|
return funcapp("floor", { value }) |
|
end |
|
|
|
local function ceil(value) |
|
return funcapp("ceil", { value }) |
|
end |
|
|
|
local function band(...) |
|
return funcapp("bitwise-and", { ... }) |
|
end |
|
|
|
local function bor(...) |
|
return funcapp("bitwise-or", { ... }) |
|
end |
|
|
|
local function bxor(...) |
|
return funcapp("bitwise-xor", { ... }) |
|
end |
|
|
|
local function bnot(value) |
|
return funcapp("bitwise-not", { value }) |
|
end |
|
|
|
local function sin(value) |
|
return funcapp("sin", { value }) |
|
end |
|
|
|
local function cos(value) |
|
return funcapp("cos", { value }) |
|
end |
|
|
|
local function atan2(y, x) |
|
return funcapp("atan2", { y, x }) |
|
end |
|
|
|
|
|
local function less_than(lhs, rhs) |
|
return funcapp("less-than", { lhs, rhs }) |
|
end |
|
|
|
|
|
local function less_or_equal(lhs, rhs) |
|
return funcapp("less-or-equal", { lhs, rhs }) |
|
end |
|
|
|
|
|
local function equals(lhs, rhs) |
|
return funcapp("equals", { lhs, rhs }) |
|
end |
|
|
|
|
|
|
|
|
|
function tne(v, sloc) |
|
if type(v) == "number" then |
|
return fixne{ |
|
type = "literal-number", |
|
source_location = sloc or csloc(1), |
|
literal_value = v |
|
} |
|
elseif type(v) == "boolean" then |
|
return fixne{ |
|
type = "literal-boolean", |
|
source_location = sloc or csloc(1), |
|
literal_value = v |
|
} |
|
elseif type(v) == "string" then |
|
return tne{ |
|
type = "literal-string", |
|
literal_value = v |
|
} |
|
elseif type(v) == "table" then |
|
if v.type == nil then |
|
error("Can't turn table without 'type' property into noise expression") |
|
end |
|
if v.source_location == nil then |
|
v.source_location = sloc or csloc(1) |
|
end |
|
return fixne(v) |
|
else |
|
error("Can't turn "..type(v).." into noise expression") |
|
end |
|
end |
|
|
|
local function nfvar(name, sloc) |
|
return tne{ |
|
type = "variable", |
|
source_location = sloc or csloc(1), |
|
variable_name = name |
|
} |
|
end |
|
|
|
local function literal_object(obj, sloc) |
|
return tne{ |
|
type = "literal-object", |
|
source_location = sloc or csloc(1), |
|
literal_value = obj |
|
} |
|
end |
|
|
|
local function literal_string(str, sloc) |
|
return tne{ |
|
type = "literal-string", |
|
source_location = sloc or csloc(1), |
|
literal_value = str |
|
} |
|
end |
|
|
|
local function absolute_value(x) |
|
return funcapp("absolute-value", {x}) |
|
end |
|
|
|
local function autoplace_probability(autoplace) |
|
return funcapp("autoplace-probability", { literal_object(autoplace) }) |
|
end |
|
|
|
local function autoplace_richness(autoplace) |
|
return funcapp("autoplace-richness", { literal_object(autoplace) }) |
|
end |
|
|
|
local function random_penalty(source, random_penalty_amplitude, opts) |
|
if opts == nil then opts = {} end |
|
if random_penalty_amplitude == nil then random_penalty_amplitude = 1 end |
|
return tne{ |
|
type = "function-application", |
|
function_name = "random-penalty", |
|
arguments = |
|
{ |
|
x = opts.x or nfvar("x"), |
|
y = opts.y or nfvar("y"), |
|
source = tne(source), |
|
amplitude = tne(random_penalty_amplitude) |
|
} |
|
} |
|
end |
|
|
|
local function random_between(lower, upper) |
|
return random_penalty(upper, upper-lower) |
|
end |
|
|
|
local function random(amplitude) |
|
return random_penalty(amplitude, amplitude) |
|
end |
|
|
|
|
|
|
|
|
|
local function define_noise_function( func ) |
|
local x = nfvar("x") |
|
local y = nfvar("y") |
|
local tile_props = |
|
{ |
|
x = x, |
|
y = y, |
|
distance = nfvar("distance"), |
|
tier = nfvar("tier_from_start") |
|
} |
|
local map_props = |
|
{ |
|
seed = nfvar("map_seed"), |
|
width = nfvar("map_width"), |
|
height = nfvar("map_height"), |
|
starting_area_radius = nfvar("starting_area_radius"), |
|
segmentation_multiplier = nfvar("segmentation_multiplier"), |
|
terrace_elevation_offset = nfvar("terrace_elevation_offset"), |
|
terrace_elevation_interval = nfvar("terrace_elevation_interval"), |
|
wlc_elevation_offset = nfvar("wlc_elevation_offset"), |
|
wlc_elevation_minimum = nfvar("wlc_elevation_minimum"), |
|
water_level = nfvar("water_level"), |
|
finite_water_level = nfvar("finite_water_level") |
|
} |
|
return tne(func(x,y,tile_props,map_props), csloc(0)) |
|
end |
|
|
|
local function clamp(v, min, max, sloc) |
|
return funcapp( |
|
"clamp", |
|
{ |
|
tne(v, sloc), |
|
tne(min, sloc), |
|
tne(max, sloc) |
|
}, |
|
sloc |
|
) |
|
end |
|
|
|
local function reduce(reducer, list) |
|
local result = list[1] |
|
for i=2,#list do |
|
result = reducer(result, list[i]) |
|
end |
|
return result |
|
end |
|
|
|
local function max(...) |
|
local sloc = csloc(1) |
|
return reduce(function(a,b) |
|
return clamp(a, b, math.huge, sloc) |
|
end, {...}) |
|
end |
|
|
|
local function min(...) |
|
local sloc = csloc(1) |
|
return reduce(function(a,b) |
|
return clamp(a, -math.huge, b, sloc) |
|
end, {...}) |
|
end |
|
|
|
local function ridge(v, min, max, sloc) |
|
return tne{ |
|
type = "function-application", |
|
function_name = "ridge", |
|
source_location = sloc or csloc(1), |
|
arguments = |
|
{ |
|
tne(v), |
|
tne(min), |
|
tne(max) |
|
} |
|
} |
|
end |
|
|
|
local function terrace(v, offset, width, strength) |
|
return tne{ |
|
type = "function-application", |
|
function_name = "terrace", |
|
arguments = |
|
{ |
|
tne(v), tne(offset), tne(width), tne(strength) |
|
} |
|
} |
|
end |
|
|
|
|
|
|
|
|
|
local function terrace_for_cliffs(v, strength, map) |
|
if strength == nil then |
|
strength = clamp(nfvar("cliffiness"), 0, 1) |
|
end |
|
return max( |
|
terrace(v, map.terrace_elevation_offset, map.terrace_elevation_interval, strength), |
|
min(v, 4) |
|
) |
|
end |
|
|
|
local function make_array(list) |
|
local value_expressions = {} |
|
|
|
for i=1,#list do |
|
value_expressions[i] = tne(list[i]) |
|
end |
|
|
|
return tne{ |
|
type = "array-construction", |
|
value_expressions = value_expressions |
|
} |
|
end |
|
|
|
local function make_point_list(list) |
|
local value_expressions = {} |
|
|
|
for i=1,#list do |
|
value_expressions[i] = make_array(list[i]) |
|
end |
|
|
|
return tne{ |
|
type = "array-construction", |
|
value_expressions = value_expressions |
|
} |
|
end |
|
|
|
local function distance_from(x, y, points, max_distance) |
|
local arguments = |
|
{ |
|
x = x, |
|
y = y, |
|
points = points |
|
} |
|
if max_distance then |
|
arguments["maximum_distance"] = tne(max_distance) |
|
end |
|
|
|
return funcapp("distance-from-nearest-point", arguments) |
|
end |
|
|
|
local function get_control_setting(name) |
|
local vars = {} |
|
for i,attr in ipairs{"frequency","size","richness"} do |
|
for j,attrattr in ipairs{"multiplier"} do |
|
vars[attr.."_"..attrattr] = nfvar("control-setting:"..name..":"..attr..":"..attrattr) |
|
end |
|
end |
|
return vars |
|
end |
|
|
|
local function if_else_chain(...) |
|
local args = {...} |
|
local arg_count = 0 |
|
local arg_expressions = {} |
|
for i,ar in ipairs(args) do |
|
arg_expressions[i] = tne(ar) |
|
arg_count = arg_count + 1 |
|
end |
|
if arg_count % 2 ~= 1 then |
|
error("if_else_chain requires an odd number of arguments") |
|
end |
|
return tne |
|
{ |
|
type = "if-else-chain", |
|
arguments = arg_expressions |
|
} |
|
end |
|
|
|
local function literal_object(val, sloc) |
|
return tne |
|
{ |
|
type = "literal-object", |
|
source_location = sloc or csloc(1), |
|
literal_value = val |
|
} |
|
end |
|
|
|
local function literal_expression(x) |
|
return tne |
|
{ |
|
type = "literal-expression", |
|
literal_value = tne(x) |
|
} |
|
end |
|
|
|
local function noise_layer_name_to_id(name) |
|
return funcapp("noise-layer-name-to-id", {tne(name)}) |
|
end |
|
|
|
local function delimit_procedure(expression) |
|
return tne |
|
{ |
|
type = "procedure-delimiter", |
|
expression = tne(expression) |
|
} |
|
end |
|
|
|
local function compile_time_log(...) |
|
local args = {...} |
|
arg_expressions = {} |
|
for i,arg in ipairs(args) do |
|
if type(arg) == "string" then |
|
arg = literal_string(arg) |
|
end |
|
arg_expressions[i] = tne(arg) |
|
end |
|
return funcapp("compile-time-log", arg_expressions) |
|
end |
|
|
|
|
|
|
|
|
|
local function ident(x) |
|
return x |
|
end |
|
|
|
return |
|
{ |
|
csloc = csloc, |
|
to_noise_expression = tne, |
|
define_noise_function = define_noise_function, |
|
clamp = clamp, |
|
compile_time_log = compile_time_log, |
|
ident = ident, |
|
min = min, |
|
max = max, |
|
ridge = ridge, |
|
terrace = terrace, |
|
terrace_for_cliffs = terrace_for_cliffs, |
|
make_array = make_array, |
|
make_point_list = make_point_list, |
|
distance_from = distance_from, |
|
var = nfvar, |
|
get_control_setting = get_control_setting, |
|
absolute_value = absolute_value, |
|
autoplace_probability = autoplace_probability, |
|
autoplace_richness = autoplace_richness, |
|
fraction = fraction, |
|
function_application = funcapp, |
|
if_else_chain = if_else_chain, |
|
literal_expression = literal_expression, |
|
literal_object = literal_object, |
|
literal_string = literal_string, |
|
noise_layer_name_to_id = noise_layer_name_to_id, |
|
random = random, |
|
random_between = random_between, |
|
random_penalty = random_penalty, |
|
delimit_procedure = delimit_procedure, |
|
log2 = log2, |
|
fmod = fmod, |
|
floor = floor, |
|
ceil = ceil, |
|
band = band, |
|
bor = bor, |
|
bxor = bxor, |
|
bnot = bnot, |
|
sin = sin, |
|
cos = cos, |
|
atan2 = atan2, |
|
less_than = less_than, |
|
less_or_equal = less_or_equal, |
|
equals = equals |
|
} |
|
|