sorcery  tblgen-metals.lua at [b96185e88b]

File util/tblgen-metals.lua artifact dd4ba1df04 part of check-in b96185e88b


local metals = dofile('data/metals.lua')
local affinities = dofile('data/affinities.lua')
sorcery = { lib = { class = dofile('lib/class.lua') } }
local u = {
	tbl = dofile('lib/tbl.lua');
	color = dofile('lib/color.lua');
	str = dofile('lib/str.lua');
}

local tag = function(t,p,c)
	if not c then c = p p = nil end
	if p then p = ' ' .. p end
	return string.format('<%s%s>%s</%s>', t,p or '',c,t)
end

local nulcat = function(a,b,c)
	if a then
		if b then
			return a..(c or '')..b
		else return a end
	else return b end
end
local html_table = function(tbl,colprops,tblprop)
	local html = ''
	local header = true
	local totalcols = 0
	for k,v in ipairs(tbl) do
		local t = 'td'
		if header then t='th' end
		html = html .. '<tr>'
		for col,text in ipairs(v) do
			local sp
			if header then totalcols = totalcols + 1 else
				if col == #v and #v < totalcols then
					local colspan = 1 + (totalcols - #v)
					sp = string.format('colspan="%u" ',colspan)
				end
			end
			if colprops and colprops[col] then
				html = html .. tag(t, nulcat(sp,colprops[col][header and 2 or 1],' '), text)
			else
				html = html .. tag(t, sp, text)
			end
		end
		html = html .. '</tr>'
		header = false
	end
	return tag('table',tblprop,html)
end

local function colorscale(scale)
	return function(i)
		local fac = math.max(scale.min,math.min(i,scale.max)) / scale.max
		return u.color{
			hue = scale.lowhue + ((scale.highhue - scale.lowhue) * fac);
			saturation = 1;
			luminosity = scale.lowlum + ((scale.highlum - scale.lowlum) * fac);
		}
	end
end

local propscales = {
	props = {
		durability = {10,88};
		speed = {226,175};
		maxenergy = {4,58};
		level = {185,320};
		sharpness = {240,351};
	};
	extents = {};
	scalers = {};
};
for name,metal in pairs(metals) do
	for _,p in pairs(u.tbl.keys(propscales.props)) do
		if not propscales.extents[p] then propscales.extents[p] = {max=0} end
		if metal[p] then
			if metal[p] > propscales.extents[p].max then propscales.extents[p].max = metal[p] end
			if propscales.extents[p].min == nil or 
			   metal[p] < propscales.extents[p].min then propscales.extents[p].min = metal[p] end
		end
	end
end
for _,p in pairs(u.tbl.keys(propscales.props)) do
	propscales.scalers[p] = colorscale {
		min = propscales.extents[p].min;
		max = propscales.extents[p].max;
		lowhue = propscales.props[p][1];
		highhue = propscales.props[p][2];
		lowlum = 0.3, highlum = 0.8;
	}
end

local function abbreviate(prop,i)
	if i == nil then return '' end
	local color = propscales.scalers[prop](i)
	local sc = function(c,t)
		return tag('span','style="color:'..c:hex()..'"',t)
	end
	if i >= 1000 then
		return sc(color,tostring(i/1000)) .. sc(color:darken(0.2), 'k')
	end
	return sc(color,tostring(i)) 
end
local function maketbl(fn)
	local mtbl = {
		{'tier','name','durability','speed','sharp','reservoir','spell slots'}
	}
	local mnames = u.tbl.keys(metals)
	table.sort(mnames, function(a,b)
		local score = function(name)
			local m = metals[name]
			return ((m.durability or 1) * (m.speed or 1) * (m.slots and (#m.slots+1) or 1)) + (m.maxenergy or 0)
		end
		if metals[a].level < metals[b].level then return true end
		if metals[a].level > metals[b].level then return false end
		return score(a) < score(b)
	end)
	local tone_overrides = {
		steel = u.color(230,230,230);
		tin = u.color(190,190,190);
	}
	local cnfscale = colorscale {
		min = 0.4, max = 3;
		lowhue = 240, highhue = 326;
		lowlum = 0.1, highlum = 0.8;
	}
	for idx,name in ipairs(mnames) do
		local metal = metals[name]
		if fn(metal) then
			local tone = u.color(metal.tone):brighten(1.3)
			local slots = '' if metal.slots then for _,s in pairs(metal.slots) do
				if slots ~= '' then slots = slots .. ' ' end
				local afflabels = {}
				for _,a in pairs(s.affinity) do
					local aff = affinities[a]
					local c = u.color(aff.color):brighten(1.3)
					afflabels[#afflabels+1] = string.format(tag('span','style="color:%s"','%s'),c:hex(),string.upper(string.sub(a,1,1)))
				end
				local ch = cnfscale(s.confluence):hex()
				local popup = u.str.capitalize(table.concat(s.affinity,' or ')) .. string.format(' at %u%% confluence', math.floor(s.confluence*100))
				slots = slots ..  tag('span',
					string.format('title="%s" style="display:inline-block; text-shadow: 0 1px black; cursor:help; padding: 0px 2.5px; border: 1px solid %s; background:radial-gradient(circle at 50%% 150%%, %s,transparent)"', popup, ch, ch),
					table.concat(afflabels,''))
			end end
			if tone_overrides[name] then tone = tone_overrides[name] end
			local tier = ''
			if metal.level > 0 or metal.maxlevel and metal.maxlevel > 0 then
				for i=1,metal.level or 0 do tier = tier .. '🛡' end
				for i=1,(metal.maxlevel or metal.level or 0) - metal.level do
					tier = '⛏' .. tier
				end
			end
			local image = string.format('<img style="vertical-align:middle" width=16 height=16 src="doc/tip/textures/sorcery_%s_fragment.png">', name)
			mtbl[#mtbl+1] = {
				tier;
				image .. ' ' .. tag('a',string.format('href="wiki?name=%s" style="color:%s"',name,tone:hex()),name);
				abbreviate('durability',metal.durability);
				abbreviate('speed',metal.speed);
				abbreviate('sharpness',metal.sharpness);
				abbreviate('maxenergy',metal.maxenergy or 0);
				slots;
			}
		end
	end
	return(html_table(mtbl, {
		[1] = {'align=right style="font-size:60%"','width="18%"'};
		[2] = {nil,'style="text-align:left;padding-left:22px" width="21%"'};
		[3] = {'align=right','width="9%"'};
		[4] = {'align=right','width="9%"'};
		[5] = {'align=right','width="9%"'};
		[6] = {'align=right','width="9%"'};
		[7] = {'align=center','style="text-align:center" width="25%"'};
	}, 'style="width:80%;border-spacing: 0 5px;margin:auto"'))
end

local function genindex(out)
	out:write [[
<!-- generated by util/tblgen-metals.lua -- do not edit by hand! -->
# metals
metals are materials found by mining. `sorcery` extends the properties of metals and adds many more than are included in the base game, as well as alloys, materials formed from a combination of multiple metals.

## properties
a metal is characterized by a number of key properties.

 * its **durability** governs how many blocks you can mine with a tool made of this metal before the tool will break
 * its **speed** determines how fast you can break blocks with a tool
 * its **sharpness** determines how severe wounds inflicted with a sword made of this metal can be
 * its **reservoir** is the maximum ley-charge tools can hold
 * its **spell slots** control what kinds of spells can be placed on a tool, in what combination, and at what level of efficacy

## elemental metal index
these metals are found through mining in the form of ore, which can be smelted in the Furnace to purify it and turn it into ingots, which are very useful crafting ingredients.

]]
	out:write(maketbl(function(m) return m.artificial ~= true end))
	out:write [[


## alloy index
these metals are created by combining metals with the [Smelter](wiki?name=smelting).
]]
	out:write(maketbl(function(m) return m.artificial == true end))
	out:close()
end

local function conjoin(list,word)
	local s
	for i,w in ipairs(list) do
		if s then
			if i == #list then
				s = s .. ', ' .. word .. ' ' .. w
			else
				s = s .. ', ' .. w
			end
		else s = w end
	end
	return s
end

local function gentpl(metalname)
	local m = metals[metalname]
	local tone = u.color(m.tone)
	local origin
	if m.artificial then
		if m.mix and m.mix.metals then
			origin = 'an alloy of ' .. conjoin(u.tbl.keys(m.mix.metals), 'and')
		elseif m.sinter then
			origin = 'an alloy produced by sintering ' .. conjoin(u.tbl.uniq(m.sinter), 'and') .. ' powder'
		else
			origin = 'an artificial metal'
		end
	else
		origin = 'a naturally occurring element found at ' .. tostring((m.depth) / 1000) .. 'km below the surface'
	end
	local facts = {}
	local frag_uses, ing_uses = '', ''
	if m.conduct then
		frag_uses = frag_uses .. tag('li', 'crafting cables')
		facts[#facts+1] = 'it conducts ley current at ' .. tostring(m.conduct) .. ' thaum.'
	end
	if not m.no_tools then ing_uses = ing_uses .. tag('li', 'crafting tools and weapons') end
	if not m.no_armor then ing_uses = ing_uses .. tag('li', 'crafting armor') end

	local page = string.format(
		'# %s\n' ..
		'%s is %s.%s\n\n',
			metalname, metalname,
			origin,
			(#facts ~= 0) and (' ' .. table.concat(facts,' ')) or '')
	if m.artificial then

	end
	local img = function(p)
		return string.format('<img src="doc/tip/textures/sorcery_%s.png" style="image-rendering:crisp-edges;image-rendering:pixelated;width:2em;height:2em;vertical-align:middle;">',p)
	end
	local nm = u.str.capitalize(metalname)
	local forms = {
		{'','form','produced by'};
		{'',tag('strong',nm .. ' Ore'), m.artificial and 'alloying' or 'mining'};
		{'',tag('ul',tag('li','can be smelted to produce ingots'))};
		{'',tag('strong',nm .. ' Ingot'), 'smelting ore'};
		{'',tag('ul',tag('li','used in crafting recipes') .. ing_uses)};
		{img(metalname..'_fragment'), tag('strong', nm .. ' Fragment'), 'cooking ingot or powder in Furnace'};
		{'',tag('ul',tag('li','used in smelting to achieve proper metal ratios in alloys') .. frag_uses)};
		{img(metalname..'_powder'), tag('strong', nm .. ' Powder'), 'grinding metal in Mill'};
	};
	page = page ..
		html_table(forms,{
			[1] = {'style="text-align:right"','style="width:2em"'};
			[2] = {nil, 'style="text-align:center;width:20%"'};
			[3] = {'style="font-style:italic"', 'style="text-align:center"'};
		},string.format('style="border-spacing:0;padding:0.5em;border:1px solid %s"', tone:hex()))
	return page
end

local out
local args = {...}
if args[1] == 'test' then
	genindex(io.stdout)
elseif args[1] == 'commit' then
	genindex(io.popen('fossil wiki commit metals -M markdown','w'))
elseif args[1] == 'tpl' then
	print(gentpl(args[2]))
else
	print('usage: lua util/tblgen-metals.lua (test|commit)')
	os.exit(64)
end