Module:Damage: Difference between revisions

From Space Station 14 Wiki
Aliser (talk | contribs)
No edit summary
Aliser (talk | contribs)
support distribution param
 
(10 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
local getArgs = require('Module:Arguments').getArgs
local getArgs = require('Module:Arguments').getArgs
local round_to_digit_formatted = require('Module:Utils/number').round_to_digit_formatted


local damage_types_json = mw.loadJsonData("Module:Damage/damage types.json")
local damage_types_json = mw.loadJsonData("Module:Damage/damage types.json")
local damage_groups_json = mw.loadJsonData("Module:Damage/damage groups.json")
-- how many digits to round damage to with numbers after comma.
local round_damage_to_digit = 1
-- =========================


local function assert_not_nil(value, error_message)
local function assert_not_nil(value, error_message)
Line 12: Line 19:
         end
         end
     end
     end
end
local function numeric_table_length(t)
    local count = 0
    for _ in ipairs(t) do count = count + 1 end
    return count
end
end


-- =========================
-- =========================


-- Lookups damage type from the json. If a damage type is defined, returns the object defining it, or `nil` otherwise.
-- Lookup a field in a json table by key.
local function lookup_damage_type(damage_type)
-- Json tables are assumed to be tables with lowercase keys. The `key` is automatically lowercased for the lookup.
local result = damage_types_json[string.lower(damage_type)]
-- If a value is a string, it's treated as an alias (another key), triggering a new lookup.
--
-- Returns the value by the specified key, or by aliased key if the value was an alias.
-- If value doesn't exist, returns `nil`.
local function lookup_json_table(t, key)
local result = t[string.lower(key)]
if type(result) == "string" then
if type(result) == "string" then
-- alias. lookup actual definition.
-- alias. lookup actual definition.
return damage_types_json[result]
return t[result]
else
else
return result
return result
Line 27: Line 45:
end
end


-- Generates a term element with a tooltip and/or a link.
-- ======================
function p.main(frame)
 
local args = getArgs(frame)
-- Generates a damage type/group element.
local damage_type = args[1]
-- @param class Class.
assert_not_nil(damage_type, "failed to generate damage type: damage type not provided")
-- @param label Label.
-- @param amount [Optional] Amount.
-- @param color [Optional] Color.
-- @param sign [Optional] Deal/heal damage sign.
local function generate_damage_element(class, label, amount, color, sign)
local label_wrapper_el = mw.html.create('span')
:addClass(class)
 
if amount then
local amount_el = label_wrapper_el:tag('span')
:addClass("damage-amount")
:wikitext(amount .. " ")
 
if sign then
if sign == "+" then
amount_el:addClass("heal")
elseif sign == "-" then
amount_el:addClass("deal")
else
error("failed to generate a damage label component: unsupported sign '" .. sign .. "'")
end
end
end


-- ==================
local label_el = label_wrapper_el:tag('span')
:addClass("damage-label")
:wikitext(label)


local damage_type_lookup = lookup_damage_type(damage_type);
if color then
if not damage_type_lookup then
label_el:css("color", color);
error("failed to generate a damage type: unknown damage type '" .. damage_type .. "'")
end
end


local display = damage_type_lookup.display
return label_wrapper_el
end
 
 
-- Generate a damage type element.
-- @param damage_type Damage type.
-- @param damage_type_lookup Lookup result for the damage type.
-- @param amount [Optional] Amount.
-- @param sign [Optional] Deal/heal sign.
local function generate_damage_type(damage_type, damage_type_lookup, amount, sign)
local label = damage_type_lookup.display
local color = damage_type_lookup.color
local color = damage_type_lookup.color


if not display then
if not label then
error("failed to generate a damage type: damage type is defined, but it does not have a 'display' property")
error("failed to generate a damage type: damage type '" .. damage_type .. "' is defined, but it does not have a 'display' property")
end
 
return generate_damage_element("damage-type", label, amount, color, sign)
end
 
-- Generate a damage group element (containing damage type elements).
-- @param damage_group Damage group.
-- @param damage_group_lookup Lookup result for the damage group.
-- @param amount [Optional] Amount.
-- @param sign [Optional] Deal/heal sign.
-- @param distribution [Optional] Damage distribution for damage types.
local function generate_damage_group(damage_group, damage_group_lookup, amount, sign, distribution)
local label = damage_group_lookup.display
local damage_types = damage_group_lookup["damage types"]
local color = damage_group_lookup.color
 
if not label then
error("failed to generate a damage group: damage group '" .. damage_group .. "' is defined, but it does not have a 'display' property")
elseif not damage_types then
error("failed to generate a damage group: damage group '" .. damage_group .. "' is defined, but it does not have a 'damage types' property")
end
 
local wrapper_el = mw.html.create('span')
:addClass("damage-group-wrapper")
 
local damage_group_el = generate_damage_element("damage-group", label, amount, color, sign)
wrapper_el:node(damage_group_el)
wrapper_el:wikitext(" (")
 
local damage_types_count = numeric_table_length(damage_types)
local amount_per_damage_type_str
if amount then
if distribution == "each" then
local exact_amount_per_damage_type = amount / damage_types_count
amount_per_damage_type_str = round_to_digit_formatted(exact_amount_per_damage_type, round_damage_to_digit)
elseif distribution == "shared" then
amount_per_damage_type_str = amount
else
error("unknown distribution: " .. distribution)
end
end
end
for i, damage_type in ipairs(damage_types) do
if distribution == "shared" and amount and damage_types_count > 1 and i == 1 then
wrapper_el:wikitext(amount .. " damage between ")
end


local el = mw.html.create('span')
local damage_type_el = generate_damage_type(
:addClass("damage-type")
damage_type,
:wikitext("<b>" .. display .. "</b>")
lookup_json_table(damage_types_json, damage_type),
-- do not provide amount for the shared because the amount gets specified only once at the start
distribution == "each" and amount_per_damage_type_str or nil, -- or shared
sign
)
wrapper_el:node(damage_type_el)


if color then
-- if 2 damages and not on last damage
el:css("color", color);
if damage_types_count >= 2 and i < damage_types_count then
-- add 'and' instead of a comma in between last and second to last items
if i == damage_types_count - 1 then
wrapper_el:wikitext(" and ")
else
wrapper_el:wikitext(", ")
end
end
end
 
wrapper_el:wikitext(")")
 
return wrapper_el
end
 
-- Generates a damage label element.
function p.main(frame)
local args = getArgs(frame)
local label = args[1]
assert_not_nil(label, "failed to generate a damage label: damage type/group not provided")
 
local amount = args[2]
 
local sign = args[3]
 
local distribution = string.lower(args.split or args.distribution or args.distrib or "each")
 
-- ==================
 
-- if damage group, format based as follows: <group> (<type 1>, <type 2>, <type 3>)
-- -- if distribution is set to "shared" and an amount is provided, format as follows: <group> (N damage between <type 1>, <type 2>, <type 3>)
-- if damage type, format as follows: <amount> <type>
 
local damage_group_lookup = lookup_json_table(damage_groups_json, label);
if damage_group_lookup then
return generate_damage_group(label, damage_group_lookup, amount, sign, distribution)
end
 
local damage_type_lookup = lookup_json_table(damage_types_json, label);
if damage_type_lookup then
return generate_damage_type(label, damage_type_lookup, amount, sign)
end
end


return el:allDone()
error("failed to generate a damage label: unknown damage type/group '" .. label .. "'")
end
end


return p
return p

Latest revision as of 14:05, 26 May 2025

Module documentation
View or edit this documentation (about module documentation)
Uses JSON data

Implements {{Damage}}.


local p = {}
local getArgs = require('Module:Arguments').getArgs
local round_to_digit_formatted = require('Module:Utils/number').round_to_digit_formatted

local damage_types_json = mw.loadJsonData("Module:Damage/damage types.json")
local damage_groups_json = mw.loadJsonData("Module:Damage/damage groups.json")

-- how many digits to round damage to with numbers after comma.
local round_damage_to_digit = 1

-- =========================

local function assert_not_nil(value, error_message)
    if value == nil then
        if error_message == nil then
            error("value is nil")
        else
            error(error_message)
        end
    end
end

local function numeric_table_length(t)
    local count = 0
    for _ in ipairs(t) do count = count + 1 end
    return count
end

-- =========================

-- Lookup a field in a json table by key.
-- Json tables are assumed to be tables with lowercase keys. The `key` is automatically lowercased for the lookup.
-- If a value is a string, it's treated as an alias (another key), triggering a new lookup.
-- 
-- Returns the value by the specified key, or by aliased key if the value was an alias.
-- If value doesn't exist, returns `nil`.
local function lookup_json_table(t, key)
	local result = t[string.lower(key)]
	if type(result) == "string" then
		-- alias. lookup actual definition.
		return t[result]
	else
		return result
	end
end

-- ======================

-- Generates a damage type/group element.
-- @param class Class.
-- @param label Label.
-- @param amount [Optional] Amount.
-- @param color [Optional] Color.
-- @param sign [Optional] Deal/heal damage sign.
local function generate_damage_element(class, label, amount, color, sign)
	local label_wrapper_el = mw.html.create('span')
		:addClass(class)

	if amount then
		local amount_el = label_wrapper_el:tag('span')
			:addClass("damage-amount")
			:wikitext(amount .. " ")

		if sign then
			if sign == "+" then
				amount_el:addClass("heal")
			elseif sign == "-" then
				amount_el:addClass("deal")
			else
				error("failed to generate a damage label component: unsupported sign '" .. sign .. "'")
			end
		end
	end

	local label_el = label_wrapper_el:tag('span')
		:addClass("damage-label")
		:wikitext(label)

	if color then
		label_el:css("color", color);
	end

	return label_wrapper_el
end


-- Generate a damage type element.
-- @param damage_type Damage type.
-- @param damage_type_lookup Lookup result for the damage type.
-- @param amount [Optional] Amount.
-- @param sign [Optional] Deal/heal sign.
local function generate_damage_type(damage_type, damage_type_lookup, amount, sign)
	local label = damage_type_lookup.display
	local color = damage_type_lookup.color

	if not label then
		error("failed to generate a damage type: damage type '" .. damage_type .. "' is defined, but it does not have a 'display' property")
	end

	return generate_damage_element("damage-type", label, amount, color, sign)
end

-- Generate a damage group element (containing damage type elements).
-- @param damage_group Damage group.
-- @param damage_group_lookup Lookup result for the damage group.
-- @param amount [Optional] Amount.
-- @param sign [Optional] Deal/heal sign.
-- @param distribution [Optional] Damage distribution for damage types.
local function generate_damage_group(damage_group, damage_group_lookup, amount, sign, distribution)
	local label = damage_group_lookup.display
	local damage_types = damage_group_lookup["damage types"]
	local color = damage_group_lookup.color

	if not label then
		error("failed to generate a damage group: damage group '" .. damage_group .. "' is defined, but it does not have a 'display' property")
	elseif not damage_types then
		error("failed to generate a damage group: damage group '" .. damage_group .. "' is defined, but it does not have a 'damage types' property")
	end

	local wrapper_el = mw.html.create('span')
		:addClass("damage-group-wrapper")

	local damage_group_el = generate_damage_element("damage-group", label, amount, color, sign)
	wrapper_el:node(damage_group_el)
	
	wrapper_el:wikitext(" (")

	local damage_types_count = numeric_table_length(damage_types)
	local amount_per_damage_type_str
	if amount then
		if distribution == "each" then
			local exact_amount_per_damage_type = amount / damage_types_count
			amount_per_damage_type_str = round_to_digit_formatted(exact_amount_per_damage_type, round_damage_to_digit)
		elseif distribution == "shared" then
			amount_per_damage_type_str = amount
		else
			error("unknown distribution: " .. distribution)
		end
	end
	
	for i, damage_type in ipairs(damage_types) do
		if distribution == "shared" and amount and damage_types_count > 1 and i == 1 then
			wrapper_el:wikitext(amount .. " damage between ")
		end

		local damage_type_el = generate_damage_type(
			damage_type, 
			lookup_json_table(damage_types_json, damage_type), 
				-- do not provide amount for the shared because the amount gets specified only once at the start
			distribution == "each" and amount_per_damage_type_str or nil, -- or shared
			sign
		)
		wrapper_el:node(damage_type_el)

		-- if 2 damages and not on last damage
		if damage_types_count >= 2 and i < damage_types_count then
			-- add 'and' instead of a comma in between last and second to last items
			if i == damage_types_count - 1 then
				wrapper_el:wikitext(" and ")
			else
				wrapper_el:wikitext(", ")
			end
		end
	end

	wrapper_el:wikitext(")")

	return wrapper_el
end

-- Generates a damage label element.
function p.main(frame)
	local args = getArgs(frame)
	local label = args[1]
	assert_not_nil(label, "failed to generate a damage label: damage type/group not provided")

	local amount = args[2]

	local sign = args[3]

	local distribution = string.lower(args.split or args.distribution or args.distrib or "each")

	-- ==================

	-- if damage group, format based as follows: <group> (<type 1>, <type 2>, <type 3>)
	-- -- if distribution is set to "shared" and an amount is provided, format as follows: <group> (N damage between <type 1>, <type 2>, <type 3>)
	-- if damage type, format as follows: <amount> <type>

	local damage_group_lookup = lookup_json_table(damage_groups_json, label);
	if damage_group_lookup then
		return generate_damage_group(label, damage_group_lookup, amount, sign, distribution)
	end

	local damage_type_lookup = lookup_json_table(damage_types_json, label);
	if damage_type_lookup then
		return generate_damage_type(label, damage_type_lookup, amount, sign)
	end

	error("failed to generate a damage label: unknown damage type/group '" .. label .. "'")
end

return p