Balatro/engine/particles.lua
2024-02-27 23:47:25 +08:00

178 lines
6.4 KiB
Lua

---@class Particles: Moveable
Particles = Moveable:extend()
--Class Methods
function Particles:init(X, Y, W, H, config)
config = config or {}
Moveable.init(self,X, Y, W, H)
self.fill = config.fill
self.padding = config.padding or 0
if config.attach then
self:set_alignment{
major = config.attach,
type = 'cm',
bond = 'Strong'
}
table.insert(self.role.major.children,self)
self.parent = self.role.major
self.T.x = self.role.major.T.x + self.padding
self.T.y = self.role.major.T.y + self.padding
if self.fill then
self.T.w = self.role.major.T.w - self.padding
self.T.h = self.role.major.T.h - self.padding
end
end
self.states.hover.can = false
self.states.click.can = false
self.states.collide.can = false
self.states.drag.can = false
self.states.release_on.can = false
self.timer = config.timer or 0.5
self.timer_type = (self.created_on_pause and 'REAL') or config.timer_type or 'REAL'
self.last_real_time = G.TIMERS[self.timer_type] - self.timer
self.last_drawn = 0
self.lifespan = config.lifespan or 1
self.fade_alpha = 0
self.speed = config.speed or 1
self.max = config.max or 1000000000000000
self.pulse_max = math.min(20, config.pulse_max or 0)
self.pulsed = 0
self.vel_variation = config.vel_variation or 1
self.particles = {}
self.scale = config.scale or 1
self.colours = config.colours or {G.C.BACKGROUND.D}
if config.initialize then
for i = 1, 60 do
self.last_real_time = self.last_real_time - 15/60
self:update(15/60)
self:move(15/60)
end
end
if getmetatable(self) == Particles then
table.insert(G.I.MOVEABLE, self)
end
end
function Particles:update(dt)
if G.SETTINGS.paused and not self.created_on_pause then self.last_real_time = G.TIMERS[self.timer_type] ; return end
local added_this_frame = 0
while G.TIMERS[self.timer_type] > self.last_real_time + self.timer and (#self.particles < self.max or self.pulsed < self.pulse_max) and added_this_frame < 20 do
self.last_real_time = self.last_real_time + self.timer
local new_offset = {
x=self.fill and (0.5-math.random())*self.T.w or 0,
y=self.fill and (0.5-math.random())*self.T.h or 0
}
if self.fill and self.T.r < 0.1 and self.T.r > -0.1 then
local newer_offset = {
x = math.sin(self.T.r)*new_offset.y + math.cos(self.T.r)*new_offset.x,
y = math.sin(self.T.r)*new_offset.x + math.cos(self.T.r)*new_offset.y,
}
new_offset = newer_offset
end
table.insert(self.particles, {
draw = false,
dir = math.random()*2*math.pi,
facing = math.random()*2*math.pi,
size = math.random()*0.5+0.1,
age = 0,
velocity = self.speed*(self.vel_variation*math.random() + (1-self.vel_variation))*0.7,
r_vel = 0.2*(0.5 - math.random()),
e_prev = 0,
e_curr = 0,
scale = 0,
visible_scale = 0,
time = G.TIMERS[self.timer_type],
colour = pseudorandom_element(self.colours),
offset = new_offset
})
added_this_frame = added_this_frame + 1
if self.pulsed <= self.pulse_max then self.pulsed = self.pulsed + 1 end
end
end
function Particles:move(dt)
if G.SETTINGS.paused and not self.created_on_pause then return end
Moveable.move(self, dt)
if self.timer_type ~= 'REAL' then dt = dt*G.SPEEDFACTOR end
for i=#self.particles,1,-1 do
self.particles[i].draw = true
self.particles[i].e_vel = self.particles[i].e_vel or dt*self.scale
self.particles[i].e_prev = self.particles[i].e_curr
self.particles[i].age = self.particles[i].age + dt
self.particles[i].e_curr = math.min(2*math.min((self.particles[i].age/self.lifespan)*self.scale, self.scale*((self.lifespan - self.particles[i].age)/self.lifespan)), self.scale)
self.particles[i].e_vel = (self.particles[i].e_curr - self.particles[i].e_prev)*self.scale*dt + (1-self.scale*dt)*self.particles[i].e_vel
self.particles[i].scale = self.particles[i].scale + self.particles[i].e_vel
self.particles[i].scale = math.min(2*math.min((self.particles[i].age/self.lifespan)*self.scale, self.scale*((self.lifespan - self.particles[i].age)/self.lifespan)), self.scale)
if self.particles[i].scale < 0 then
table.remove(self.particles, i)
else
self.particles[i].offset.x = self.particles[i].offset.x + self.particles[i].velocity*math.sin(self.particles[i].dir)*dt
self.particles[i].offset.y = self.particles[i].offset.y + self.particles[i].velocity*math.cos(self.particles[i].dir)*dt
self.particles[i].facing = self.particles[i].facing + self.particles[i].r_vel*dt
self.particles[i].velocity = math.max(0, self.particles[i].velocity - self.particles[i].velocity*0.07*dt)
end
end
end
function Particles:fade(delay, to)
G.E_MANAGER:add_event(Event({
trigger = 'ease',
timer = self.timer_type,
blockable = false,
blocking = false,
ref_value = 'fade_alpha',
ref_table = self,
ease_to = to or 1,
delay = delay
}))
end
function Particles:draw(alpha)
alpha = alpha or 1
prep_draw(self, 1)
love.graphics.translate(self.T.w/2, self.T.h/2)
for k, v in pairs(self.particles) do
if v.draw then
love.graphics.push()
love.graphics.setColor(v.colour[1], v.colour[2], v.colour[3], v.colour[4]*alpha*(1-self.fade_alpha))
love.graphics.translate(v.offset.x, v.offset.y)
love.graphics.rotate(v.facing)
love.graphics.rectangle('fill', -v.scale/2, -v.scale/2, v.scale, v.scale) -- origin in the middle
love.graphics.pop()
end
end
love.graphics.pop()
add_to_drawhash(self)
self:draw_boundingrect()
end
function Particles:remove()
if self.role.major then
for k, v in pairs(self.role.major.children) do
if v == self and type(k) == 'number' then
table.remove(self.role.major.children, k)
end
end
end
remove_all(self.children)
Moveable.remove(self)
end