From feafbd1e1a6a1846ab5fe416ee31e2e14f603edd Mon Sep 17 00:00:00 2001 From: ne_mene Date: Mon, 30 Mar 2026 18:23:07 +0200 Subject: moved files around --- src/objs/speck_entity.lua | 34 ++++++ src/objs/specks.lua | 296 ---------------------------------------------- src/specks.lua | 283 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 317 insertions(+), 296 deletions(-) create mode 100644 src/objs/speck_entity.lua delete mode 100644 src/objs/specks.lua create mode 100644 src/specks.lua (limited to 'src') diff --git a/src/objs/speck_entity.lua b/src/objs/speck_entity.lua new file mode 100644 index 0000000..cf81130 --- /dev/null +++ b/src/objs/speck_entity.lua @@ -0,0 +1,34 @@ + +register_comp("Speck_System", function(ent) + ent.speck_sys = Speck_Sys.new() +end) + +function speck_update_sys(ent, dt) + ent.speck_sys.x = ent.x + ent.speck_sys.y = ent.y + + ent.speck_sys:update(dt) +end + +function speck_draw_sys(ent) + ent.speck_sys:draw() +end + +function new_speck_entity(x, y, filepath) + local ent = new_entity() + add_comp(ent, "Position", x, y) + add_comp(ent, "Speck_System") + ent.speck_sys = load_speck_sys(filepath) + ent.speck_sys.oneshot = true + + add_comp(ent, "Speck_Entity") +end + +register_comp("Speck_Entity", TAGCOMP) + +function speck_entity_system(ent, dt) + local sys = ent.speck_sys + if not sys.emitting and sys:is_empty() then + queue_entity_kill(ent) + end +end diff --git a/src/objs/specks.lua b/src/objs/specks.lua deleted file mode 100644 index baa9a14..0000000 --- a/src/objs/specks.lua +++ /dev/null @@ -1,296 +0,0 @@ -Speck_Sys = {} -Speck_Sys.__index = Speck_Sys - -local SPAWN_FUNCTIONS = { - Rectangle = function (speck_sys) - local w = (speck_sys.spawn_width or 0) / 2 - local h = (speck_sys.spawn_height or 0) / 2 - - return randf_range(-w, w), randf_range(-h, h) - end, - - Circle = function (speck_sys) - local angle = randf_range(0, math.pi * 2) - local r = speck_sys.spawn_radius or 0 - r = r * lmath.random() - return r*math.cos(angle), r*math.sin(angle) - end, - - Point = function (speck_sys) - return 0, 0 - end -} - -function Speck_Sys.new() - local self = setmetatable({}, Speck_Sys) - - self.data = { - size = 0, - pos = {}, - vel = {}, - - alive = {}, - lifetime = {}, - lifetime_max = {}, - - scale_start = {}, - scale_end = {} - } - - self.free_ids = {} - self.free_ids_size = 0 - - self.bounce = false - self.gradient = "res/img/test_gradient.png" - - self.spawn_shape = "Point" - - self.spawn_amount_min = 1 - self.spawn_amount_max = 1 - - self.scale_curve = "Lerp" - self.scale_start_min = 0.8 - self.scale_start_max = 1.25 - - self.scale_end_min = 0 - self.scale_end_max = 0 - - self.forcex = 0 - self.forcey = 0 - - self.initial_velx = 0 - self.initial_vely = 0 - self.spread = 0 - - self.lifetime_min = 1 - self.lifetime_max = 5 - - self.interval = 0.1 - - self.texture_path = "res/img/speck.png" - - self.x = 0 - self.y = 0 - self.spawn_timer = self.interval - - self.oneshot = false - self.emitting = true - return self -end - -function Speck_Sys:check_bounce(i) - if not self.bounce then - return false - end - - local scn = get_current_scene() - assert(scn, "No scene set.") - - return has_tile( - scn.tilemap, - to_tile_coords(self.data.pos[i].x, self.data.pos[i].y)) -end - -function Speck_Sys:spawn_particles() - local data = self.data - local amt = lmath.random(self.spawn_amount_min, self.spawn_amount_max) - - local shape_func = SPAWN_FUNCTIONS[self.spawn_shape] - for i = 1, amt do - local id - if self.free_ids_size > 0 then - id = self.free_ids[self.free_ids_size] - self.free_ids_size = self.free_ids_size - 1 - else - self.data.size = self.data.size + 1 - id = self.data.size - end - - data.alive[id] = true - - local offx, offy = shape_func(self) - data.pos[id] = { x = self.x + offx, y = self.y + offy } - data.vel[id] = { x = self.initial_velx, y = self.initial_vely } - - local beta = self.spread * math.pi / 180 - beta = beta / 2 - beta = randf_range(-beta, beta) - - local rvx = (data.vel[id].x * math.cos(beta)) - - (data.vel[id].y * math.sin(beta)) - local rvy = (data.vel[id].x * math.sin(beta)) - + (data.vel[id].y * math.cos(beta)) - data.vel[id].x = rvx - data.vel[id].y = rvy - - data.lifetime[id] = randf_range( - self.lifetime_min, self.lifetime_max) - data.lifetime_max[id] = data.lifetime[id] - - data.scale_start[id] = randf_range( - self.scale_start_min, self.scale_start_max) - data.scale_end[id] = randf_range( - self.scale_end_min, self.scale_end_max) - end -end - -function Speck_Sys:update(dt) - local data = self.data - - -- particle spawning - self.spawn_timer = self.spawn_timer - dt - if self.spawn_timer <= 0 and self.emitting then - self.spawn_timer = self.interval - - self:spawn_particles() - if self.oneshot then - self.emitting = false - end - end - - -- particles processing - for i = 1, data.size do - if not data.alive[i] then - goto next_speck_update - end - - if data.lifetime[i] <= 0 then - data.alive[i] = false - - self.free_ids_size = self.free_ids_size + 1 - self.free_ids[self.free_ids_size] = i - goto next_speck_update - end - - data.vel[i].x = data.vel[i].x + self.forcex * dt - data.vel[i].y = data.vel[i].y + self.forcey * dt - - -- move and bounce - data.pos[i].x = data.pos[i].x + data.vel[i].x * dt - if self:check_bounce(i) then - data.vel[i].x = -data.vel[i].x - data.pos[i].x = data.pos[i].x + data.vel[i].x * dt - end - - data.pos[i].y = data.pos[i].y + data.vel[i].y * dt - if self:check_bounce(i) then - data.vel[i].y = -data.vel[i].y - data.pos[i].y = data.pos[i].y + data.vel[i].y * dt - end - - data.lifetime[i] = data.lifetime[i] - dt - - ::next_speck_update:: - end -end - -function Speck_Sys:is_empty() - if self.data.size == 0 then - return true - end - return self.free_ids_size == self.data.size -end - -function Speck_Sys:draw() - local scale_curve = EASING_FUNCTIONS[self.scale_curve] - local data = self.data - local tex = get_tex(self.texture_path) - - for i = 1, data.size do - if not data.alive[i] then - goto next_speck_draw - end - - local anim = 1 - data.lifetime[i] / data.lifetime_max[i] - local scale = scale_curve(data.scale_start[i], data.scale_end[i], anim) - - local w, h = tex:getDimensions() - - local gradient = get_tex_data(self.gradient) - local sample_pos = math.floor((gradient:getWidth() - 1) * anim) - lg.setColor(gradient:getPixel(sample_pos, 0.5)) - - lg.draw( - tex, data.pos[i].x, data.pos[i].y, 0, scale, scale, w / 2, h / 2 - ) - lg.setColor(1, 1, 1, 1) - ::next_speck_draw:: - end -end - -register_comp("Speck_System", function(ent) - ent.speck_sys = Speck_Sys.new() -end) - -function speck_update_sys(ent, dt) - ent.speck_sys.x = ent.x - ent.speck_sys.y = ent.y - - ent.speck_sys:update(dt) -end - -function speck_draw_sys(ent) - ent.speck_sys:draw() -end - -function export_speck_sys(sys, filename) - local EXPORTED_ARGS = { - "spawn_shape", - "spawn_amount_min", - "spawn_amount_max", - "spawn_width", - "spawn_height", - "spawn_radius", - "scale_curve", - "scale_start_min", - "scale_start_max", - "scale_end_min", - "scale_end_max", - "forcex", - "forcey", - "initial_velx", - "initial_vely", - "spread", - "lifetime_min", - "lifetime_max", - "interval", - "texture_path", - "gradient", - "oneshot", - "bounce", - } - local exp = {} - - for i, arg in ipairs(EXPORTED_ARGS) do - exp[arg] = sys[arg] - end - export_to_source(exp, filename) -end - -function load_speck_sys(filename) - local sys = Speck_Sys.new() - local loaded = lf.load(filename)() - for key, data in pairs(loaded) do - sys[key] = data - end - return sys -end - -function new_speck_entity(x, y, filepath) - local ent = new_entity() - add_comp(ent, "Position", x, y) - add_comp(ent, "Speck_System") - ent.speck_sys = load_speck_sys(filepath) - ent.speck_sys.oneshot = true - - add_comp(ent, "Speck_Entity") -end - -register_comp("Speck_Entity", TAGCOMP) - -function speck_entity_system(ent, dt) - local sys = ent.speck_sys - if not sys.emitting and sys:is_empty() then - queue_entity_kill(ent) - end -end diff --git a/src/specks.lua b/src/specks.lua new file mode 100644 index 0000000..a703d04 --- /dev/null +++ b/src/specks.lua @@ -0,0 +1,283 @@ +Speck_Sys = {} +Speck_Sys.__index = Speck_Sys + +local SPAWN_FUNCTIONS = { + Rectangle = function (speck_sys) + local w = (speck_sys.spawn_width or 0) / 2 + local h = (speck_sys.spawn_height or 0) / 2 + + return randf_range(-w, w), randf_range(-h, h) + end, + + Circle = function (speck_sys) + local angle = randf_range(0, math.pi * 2) + local r = speck_sys.spawn_radius or 0 + r = r * lmath.random() + return r*math.cos(angle), r*math.sin(angle) + end, + + Point = function (speck_sys) + return 0, 0 + end +} + +function Speck_Sys.new() + local self = setmetatable({}, Speck_Sys) + + self.data = { + size = 0, + pos = {}, + vel = {}, + + alive = {}, + lifetime = {}, + lifetime_max = {}, + + scale_start = {}, + scale_end = {} + } + + self.free_ids = {} + self.free_ids_size = 0 + + self.bounce = false + self.gradient = "res/img/test_gradient.png" + + self.spawn_shape = "Point" + + self.spawn_amount_min = 1 + self.spawn_amount_max = 1 + + self.scale_curve = "Lerp" + self.scale_start_min = 0.8 + self.scale_start_max = 1.25 + + self.scale_end_min = 0 + self.scale_end_max = 0 + + self.forcex = 0 + self.forcey = 0 + + self.initial_velx = 0 + self.initial_vely = 0 + self.spread = 0 + + self.lifetime_min = 1 + self.lifetime_max = 5 + + self.interval = 0.1 + + self.texture_path = "res/img/speck.png" + + self.x = 0 + self.y = 0 + self.spawn_timer = self.interval + + self.oneshot = false + self.emitting = true + return self +end + +function Speck_Sys:check_bounce(i) + if not self.bounce then + return false + end + + local scn = get_current_scene() + assert(scn, "No scene set.") + + return has_tile( + scn.tilemap, + to_tile_coords(self.data.pos[i].x, self.data.pos[i].y)) +end + +function Speck_Sys:spawn_particles() + local data = self.data + local amt = lmath.random(self.spawn_amount_min, self.spawn_amount_max) + + local shape_func = SPAWN_FUNCTIONS[self.spawn_shape] + for i = 1, amt do + local id + if self.free_ids_size > 0 then + id = self.free_ids[self.free_ids_size] + self.free_ids_size = self.free_ids_size - 1 + else + self.data.size = self.data.size + 1 + id = self.data.size + end + + data.alive[id] = true + + local offx, offy = shape_func(self) + data.pos[id] = { x = self.x + offx, y = self.y + offy } + data.vel[id] = { x = self.initial_velx, y = self.initial_vely } + + local beta = self.spread * math.pi / 180 + beta = beta / 2 + beta = randf_range(-beta, beta) + + local rvx = (data.vel[id].x * math.cos(beta)) + - (data.vel[id].y * math.sin(beta)) + local rvy = (data.vel[id].x * math.sin(beta)) + + (data.vel[id].y * math.cos(beta)) + data.vel[id].x = rvx + data.vel[id].y = rvy + + data.lifetime[id] = randf_range( + self.lifetime_min, self.lifetime_max) + data.lifetime_max[id] = data.lifetime[id] + + data.scale_start[id] = randf_range( + self.scale_start_min, self.scale_start_max) + data.scale_end[id] = randf_range( + self.scale_end_min, self.scale_end_max) + end +end + +function Speck_Sys:update(dt) + local data = self.data + + -- particle spawning + self.spawn_timer = self.spawn_timer - dt + if self.spawn_timer <= 0 and self.emitting then + self.spawn_timer = self.interval + + self:spawn_particles() + if self.oneshot then + self.emitting = false + end + end + + -- particles processing + for i = 1, data.size do + if not data.alive[i] then + goto next_speck_update + end + + if data.lifetime[i] <= 0 then + data.alive[i] = false + + self.free_ids_size = self.free_ids_size + 1 + self.free_ids[self.free_ids_size] = i + goto next_speck_update + end + + data.vel[i].x = data.vel[i].x + self.forcex * dt + data.vel[i].y = data.vel[i].y + self.forcey * dt + + -- move and bounce + data.pos[i].x = data.pos[i].x + data.vel[i].x * dt + if self:check_bounce(i) then + data.vel[i].x = -data.vel[i].x + data.pos[i].x = data.pos[i].x + data.vel[i].x * dt + end + + data.pos[i].y = data.pos[i].y + data.vel[i].y * dt + if self:check_bounce(i) then + data.vel[i].y = -data.vel[i].y + data.pos[i].y = data.pos[i].y + data.vel[i].y * dt + end + + data.lifetime[i] = data.lifetime[i] - dt + + ::next_speck_update:: + end +end + +function Speck_Sys:is_empty() + if self.data.size == 0 then + return true + end + return self.free_ids_size == self.data.size +end + +function Speck_Sys:draw() + local scale_curve = EASING_FUNCTIONS[self.scale_curve] + local data = self.data + local tex = get_tex(self.texture_path) + + for i = 1, data.size do + if not data.alive[i] then + goto next_speck_draw + end + + local anim = 1 - data.lifetime[i] / data.lifetime_max[i] + local scale = scale_curve(data.scale_start[i], data.scale_end[i], anim) + + local w, h = tex:getDimensions() + + local gradient = get_tex_data(self.gradient) + local sample_pos = math.floor((gradient:getWidth() - 1) * anim) + lg.setColor(gradient:getPixel(sample_pos, 0.5)) + + lg.draw( + tex, data.pos[i].x, data.pos[i].y, 0, scale, scale, w / 2, h / 2 + ) + lg.setColor(1, 1, 1, 1) + ::next_speck_draw:: + end +end + + +function export_speck_sys(sys, filename) + local EXPORTED_ARGS = { + "spawn_shape", + "spawn_amount_min", + "spawn_amount_max", + "spawn_width", + "spawn_height", + "spawn_radius", + "scale_curve", + "scale_start_min", + "scale_start_max", + "scale_end_min", + "scale_end_max", + "forcex", + "forcey", + "initial_velx", + "initial_vely", + "spread", + "lifetime_min", + "lifetime_max", + "interval", + "texture_path", + "gradient", + "oneshot", + "bounce", + } + local exp = {} + + for i, arg in ipairs(EXPORTED_ARGS) do + exp[arg] = sys[arg] + end + export_to_source(exp, filename) +end + +local speck_bank = {} +function load_specks_from(path) + path = path or "res/speck" + local files = lf.getDirectoryItems(path) + + for _, file in ipairs(files) do + local filepath = path.."/"..file + + if lf.getInfo(filepath).type == "directory" then + load_specks_from(filepath) + else + if is_filetype(filepath, {"speck.lua"}) then + local data = lf.load(filepath)() + speck_bank[filepath] = data + end + end + end +end + +function load_speck_sys(filename) + local sys = Speck_Sys.new() + local loaded = speck_bank[filename] + for key, data in pairs(loaded) do + sys[key] = data + end + return sys +end + -- cgit v1.3-2-g0d8e