189 lines
6.4 KiB
Lua

require('scripts.prng')
require('scripts.math_utils')
--- @alias position { x?: number, y?: number }
--- @alias sprite_segment
--- | { filename?: string, width?: number, height?: number, frame_count?: number, scale?: number, shift?: position, tint?: Color }
--- | position
--- @alias sprite_segment_variants sprite_segment | sprite_segment[]
--- @alias widths { left: number, middle: number, right: number }
--- @alias heights { top: number, middle: number, bottom: number }
--- @alias sprite_definition
--- | { top_left?: sprite_segment_variants }
--- | { top?: sprite_segment_variants }
--- | { top_right?: sprite_segment_variants }
--- | { left?: sprite_segment_variants }
--- | { middle?: sprite_segment_variants }
--- | { right?: sprite_segment_variants }
--- | { bottom_left?: sprite_segment_variants }
--- | { bottom?: sprite_segment_variants }
--- | { bottom_right?: sprite_segment_variants }
--- | { top_center?: sprite_segment_variants }
--- | { left_center?: sprite_segment_variants }
--- | { center?: sprite_segment_variants }
--- | { right_center?: sprite_segment_variants }
--- | { bottom_center?: sprite_segment_variants }
--- | { filename: string, scale: number, shift: position, widths: widths, heights: heights, tint?: Color }
--- | { shadow?: boolean }
--- @alias entity_sprite
--- | { entity: sprite_definition, shadow?: sprite_definition }
--- @alias segments_data
--- | { wide_segments?: entity_sprite }
--- | { high_segments?: entity_sprite }
--- | { warehouse_segments?: entity_sprite }
--- | { trashdump_segments?: entity_sprite }
local function postprocess_sprite(sprite)
local lcm = 1
for _, layer in ipairs(sprite) do
if layer.frame_count then
lcm = math.lcm(lcm, layer.frame_count)
end
end
if lcm > 1 then
for _, layer in ipairs(sprite) do
layer.repeat_count = lcm / (layer.frame_count or 1)
end
end
end
-- top left corner of sprite will be placed onto center of entity (plus shifts)
-- random decals may be used
-- shiftX, shiftY = local segment tile shift
-- shifts in segment(s) are pixel shifts
--- @param segments sprite_definition
--- @param segment sprite_segment_variants
--- @param shift_x number
--- @param shift_y number
--- @param width number | nil
--- @param height number | nil
--- @return table
local function create_sprite_tile(segments, segment, shift_x, shift_y, width, height)
local main_segment = segment
if segment[1] ~= nil then
if segment[2] ~= nil and prng.range(1, 100) < MergingChests.get_mod_settings().sprite_variation_chance then
main_segment = segment[1]
segment = segment[prng.range(2, table_size(segment))]
else
main_segment = segment
segment = segment[1]
end
end
width = segment.width or width
height = segment.height or height
return
{
filename = segment.filename or segments.filename,
priority = 'medium',
x = (segment.x or main_segment.x or 0),
y = (segment.y or main_segment.y or 0),
width = width,
height = height,
shift =
{
shift_x + (width / 2.0 * (segments.scale or 1) + (segment.shift and segment.shift.x or 0) + segments.shift.x) / 32.0,
shift_y + (height / 2.0 * (segments.scale or 1) + (segment.shift and segment.shift.y or 0) + segments.shift.y) / 32.0
},
scale = segment.scale or segments.scale or 1,
frame_count = segment.frame_count or 1,
draw_as_shadow = segments.shadow or false,
tint = segment.tint or segments.tint
}
end
--- @param width number
--- @param height number
--- @param segments sprite_definition
--- @param sprite_layers table
local function create_entity_sprite(width, height, segments, sprite_layers)
local x0 = -width / 2
local y0 = -height / 2
local xM = width / 2 - 1
local yM = height / 2 - 1
-- do top line
if segments.top_left then
table.insert(sprite_layers, create_sprite_tile(segments, segments.top_left, x0, y0, segments.widths.left, segments.heights.top))
end
if segments.top then
for x = 1, width - 2 do
table.insert(sprite_layers, create_sprite_tile(segments, segments.top, x0 + x, y0, segments.widths.middle, segments.heights.top))
end
end
if segments.top_right then
table.insert(sprite_layers, create_sprite_tile(segments, segments.top_right, xM, y0, segments.widths.right, segments.heights.top))
end
-- do middle horizontal lines
for y = 1, height - 2 do
if segments.left then
table.insert(sprite_layers, create_sprite_tile(segments, segments.left, x0, y0 + y, segments.widths.left, segments.heights.middle))
end
if segments.middle then
for x = 1, width - 2 do
table.insert(sprite_layers, create_sprite_tile(segments, segments.middle, x0 + x, y0 + y, segments.widths.middle, segments.heights.middle))
end
end
if segments.right then
table.insert(sprite_layers, create_sprite_tile(segments, segments.right, xM, y0 + y, segments.widths.right, segments.heights.middle))
end
end
-- do bottom line
if segments.bottom_left then
table.insert(sprite_layers, create_sprite_tile(segments, segments.bottom_left, x0, yM, segments.widths.left, segments.heights.bottom))
end
if segments.bottom then
for x = 1, width - 2 do
table.insert(sprite_layers, create_sprite_tile(segments, segments.bottom, x0 + x, yM, segments.widths.middle, segments.heights.bottom))
end
end
if segments.bottom_right then
table.insert(sprite_layers, create_sprite_tile(segments, segments.bottom_right, xM, yM, segments.widths.right, segments.heights.bottom))
end
-- centers
if segments.top_center then
table.insert(sprite_layers, create_sprite_tile(segments, segments.top_center, (x0 + xM) / 2, y0, nil, nil))
end
if segments.left_center then
table.insert(sprite_layers, create_sprite_tile(segments, segments.left_center, x0, (y0 + yM) / 2, nil, nil))
end
if segments.center then
table.insert(sprite_layers, create_sprite_tile(segments, segments.center, (x0 + xM) / 2, (y0 + yM) / 2, nil, nil))
end
if segments.right_center then
table.insert(sprite_layers, create_sprite_tile(segments, segments.right_center, xM, (y0 + yM) / 2, nil, nil))
end
if segments.bottom_center then
table.insert(sprite_layers, create_sprite_tile(segments, segments.bottom_center, (x0 + xM) / 2, yM, nil, nil))
end
end
--- @param width number
--- @param height number
--- @param segments entity_sprite
local function create_sprite(width, height, segments)
local sprite_layers = { }
create_entity_sprite(width, height, segments.entity, sprite_layers)
if segments.shadow then
create_entity_sprite(width, height, segments.shadow, sprite_layers)
end
postprocess_sprite(sprite_layers)
return sprite_layers
end
return create_sprite