balatro-mods/lovely/dump/engine/text.lua
2025-01-19 15:01:49 +08:00

361 lines
16 KiB
Lua

LOVELY_INTEGRITY = '3f32eb39deebe597f05c1629610525603e46d33168f8992920aef2aab6c8d63a'
--Class
DynaText = Moveable:extend()
--Class Methods
function DynaText:init(config)
config = config or {}
self.config = config
self.shadow = config.shadow
self.scale = config.scale or 1
self.pop_in_rate = config.pop_in_rate or 3
self.bump_rate = config.bump_rate or 2.666
self.bump_amount = config.bump_amount or 1
self.font = config.font or G.LANG.font
if config.string and type(config.string) ~= 'table' then config.string = {config.string} end
self.string = (config.string and type(config.string) == 'table' and config.string[1]) or {'HELLO WORLD'}
self.text_offset = {
x = self.font.TEXT_OFFSET.x*self.scale + (self.config.x_offset or 0),
y = self.font.TEXT_OFFSET.y*self.scale + (self.config.y_offset or 0),
}
self.colours = config.colours or {G.C.RED}
self.created_time = G.TIMERS.REAL
self.silent = (config.silent)
self.start_pop_in = self.config.pop_in
config.W = 0
config.H = 0
self.strings = {}
self.focused_string = 1
self:update_text(true)
if self.config.maxw and self.config.W > self.config.maxw then
self.start_pop_in = self.config.pop_in
self.scale = self.scale*(self.config.maxw/self.config.W)
self:update_text(true)
end
if #self.strings > 1 then
self.pop_delay = self.config.pop_delay or 1.5
self:pop_out(4)
end
Moveable.init(self,config.X or 0, config.Y or 0, config.W, config.H)
self.T.r = self.config.text_rot or 0
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:set_role{
wh_bond = 'Weak',
scale_bond = 'Weak'
}
if getmetatable(self) == DynaText then
table.insert(G.I.MOVEABLE, self)
end
end
function DynaText:update(dt)
self:update_text()
self:align_letters()
end
function DynaText:update_text(first_pass)
self.config.W = 0
self.config.H = 0
self.scale = self.config.scale_function and self.config.scale_function() or self.scale
for k, v in ipairs(self.config.string) do
if (type(v) == 'table' and v.ref_table) or first_pass then
local part_a, part_b = 0,1000000
local new_string = v
local outer_colour = nil
local inner_colour = nil
local part_scale = 1
if type(v) == 'table' and (v.ref_table or v.string) then
new_string = (v.prefix or '')..format_ui_value(v.ref_table and v.ref_table[v.ref_value] or v.string)..(v.suffix or '')
part_a = #(v.prefix or '')
part_b = #new_string - #(v.suffix or '')
if v.scale then part_scale = v.scale end
if first_pass then
outer_colour = v.outer_colour or nil
inner_colour = v.colour or nil
end
v = new_string
end
self.strings[k] = self.strings[k] or {}
local old_string = self.strings[k].string
if old_string ~= new_string or first_pass then
if self.start_pop_in then self.reset_pop_in = true end
self.reset_pop_in = self.reset_pop_in or self.config.reset_pop_in
if not self.reset_pop_in then
self.config.pop_out = nil
self.config.pop_in = nil
else
self.config.pop_in = self.config.pop_in or 0
self.created_time = G.TIMERS.REAL
end
self.strings[k].string = v
local old_letters = self.strings[k].letters
local tempW = 0
local tempH = 0
local current_letter = 1
self.strings[k].letters = {}--EMPTY(self.strings[k].letters)
for _, c in utf8.chars(v) do
local old_letter = old_letters and old_letters[current_letter] or nil
local let_tab = {letter = love.graphics.newText(self.font.FONT, c), char = c, scale = old_letter and old_letter.scale or part_scale}
self.strings[k].letters[current_letter] = let_tab
local tx = self.font.FONT:getWidth(c)*self.scale*part_scale*G.TILESCALE*self.font.FONTSCALE + 2.7*(self.config.spacing or 0)*G.TILESCALE*self.font.FONTSCALE
local ty = self.font.FONT:getHeight(c)*self.scale*part_scale*G.TILESCALE*self.font.FONTSCALE*self.font.TEXT_HEIGHT_SCALE
let_tab.offset = old_letter and old_letter.offset or {x = 0, y = 0}
let_tab.dims = {x = tx/(self.font.FONTSCALE*G.TILESCALE), y = ty/(self.font.FONTSCALE*G.TILESCALE)}
let_tab.pop_in = first_pass and (old_letter and old_letter.pop_in or (self.config.pop_in and 0 or 1)) or 1
let_tab.prefix = current_letter <= part_a and outer_colour or nil
let_tab.suffix = current_letter > part_b and outer_colour or nil
let_tab.colour = inner_colour or nil
if k > 1 then let_tab.pop_in = 0 end
tempW = tempW + tx/(G.TILESIZE*G.TILESCALE)
tempH = math.max(ty/(G.TILESIZE*G.TILESCALE), tempH)
current_letter = current_letter + 1
end
self.strings[k].W = tempW
self.strings[k].H = tempH
end
end
if Big then
if type(self.strings[k].W) == 'table' then
self.strings[k].W = self.strings[k].W:to_number()
end
if type(self.strings[k].H) == 'table' then
self.strings[k].H = self.strings[k].H:to_number()
end
end
if self.strings[k].W > self.config.W then self.config.W = self.strings[k].W; self.strings[k].W_offset = 0 end
if self.strings[k].H > self.config.H then self.config.H = self.strings[k].H; self.strings[k].H_offset = 0 end
end
if self.T then
if (self.T.w ~= self.config.W or self.T.h ~= self.config.H) and (not first_pass or self.reset_pop_in) then
self.ui_object_updated = true
self.non_recalc = self.config.non_recalc
end
self.T.w = self.config.W
self.T.h = self.config.H
end
self.reset_pop_in = false
self.start_pop_in = false
for k, v in ipairs(self.strings) do
v.W_offset = 0.5*(self.config.W - v.W)
v.H_offset = 0.5*(self.config.H - v.H + (self.config.offset_y or 0))
end
end
function DynaText:pop_out(pop_out_timer)
self.config.pop_out = pop_out_timer or 1
self.pop_out_time = G.TIMERS.REAL + (self.pop_delay or 0)
end
function DynaText:pop_in(pop_in_timer)
self.reset_pop_in = true
self.config.pop_out = nil
self.config.pop_in = pop_in_timer or 0
self.created_time = G.TIMERS.REAL
for k, letter in ipairs(self.strings[self.focused_string].letters) do
if Big then
letter.dims.x = to_big(letter.dims.x):to_number()
letter.dims.y = to_big(letter.dims.y):to_number()
letter.offset.x = to_big(letter.offset.x):to_number()
letter.offset.y = to_big(letter.offset.y):to_number()
end
letter.pop_in = 0
end
self:update_text()
end
function DynaText:align_letters()
if self.pop_cycle then
self.focused_string = (self.config.random_element and math.random(1, #self.strings)) or self.focused_string == #self.strings and 1 or self.focused_string+1
self.pop_cycle = false
for k, letter in ipairs(self.strings[self.focused_string].letters) do
if Big then
letter.dims.x = to_big(letter.dims.x):to_number()
letter.dims.y = to_big(letter.dims.y):to_number()
letter.offset.x = to_big(letter.offset.x):to_number()
letter.offset.y = to_big(letter.offset.y):to_number()
end
letter.pop_in = 0
end
self.config.pop_in = 0.1
self.config.pop_out = nil
self.created_time = G.TIMERS.REAL
end
self.string = self.strings[self.focused_string].string
for k, letter in ipairs(self.strings[self.focused_string].letters) do
if Big then
letter.dims.x = to_big(letter.dims.x):to_number()
letter.dims.y = to_big(letter.dims.y):to_number()
letter.offset.x = to_big(letter.offset.x):to_number()
letter.offset.y = to_big(letter.offset.y):to_number()
end
if self.config.pop_out then
letter.pop_in = math.min(1, math.max((self.config.min_cycle_time or 1)-(G.TIMERS.REAL - self.pop_out_time)*self.config.pop_out/(self.config.min_cycle_time or 1), 0))
letter.pop_in = letter.pop_in*letter.pop_in
if k == #self.strings[self.focused_string].letters and letter.pop_in <= 0 and #self.strings > 1 then self.pop_cycle = true end
elseif self.config.pop_in then
local prev_pop_in = letter.pop_in
letter.pop_in = math.min(1, math.max((G.TIMERS.REAL - self.config.pop_in - self.created_time)*#self.string*self.pop_in_rate - k + 1, self.config.min_cycle_time == 0 and 1 or 0))
letter.pop_in = letter.pop_in*letter.pop_in
if prev_pop_in <=0 and letter.pop_in > 0 and not self.silent and
(#self.string < 10 or k%2 == 0) then
if self.T.x > G.ROOM.T.w+2 or
self.T.y > G.ROOM.T.h+2 or
self.T.x <-2 or
self.T.y <-2 then else
play_sound('paper1', 0.45+0.05*math.random()+(0.3/#self.string)*k + (self.config.pitch_shift or 0))
end
end
if k == #self.strings[self.focused_string].letters and letter.pop_in >= 1 then
if #self.strings > 1 then
self.pop_delay = (G.TIMERS.REAL - self.config.pop_in - self.created_time + (self.config.pop_delay or 1.5))
self:pop_out(4)
else
self.config.pop_in = nil
end
end
end
letter.r = 0
letter.scale = 1
if self.config.rotate then letter.r = (self.config.rotate == 2 and -1 or 1)*(0.2*(-#self.strings[self.focused_string].letters/2 - 0.5 + k)/(#self.strings[self.focused_string].letters)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+k)) end
if self.config.pulse then
letter.scale = letter.scale + (G.SETTINGS.reduced_motion and 0 or 1)*(1/self.config.pulse.width)*self.config.pulse.amount*(math.max(
math.min((self.config.pulse.start - G.TIMERS.REAL)*self.config.pulse.speed + k + self.config.pulse.width,
(G.TIMERS.REAL - self.config.pulse.start)*self.config.pulse.speed - k + self.config.pulse.width+ 2),
0))
letter.r = letter.r + (G.SETTINGS.reduced_motion and 0 or 1)*(letter.scale - 1)*(0.02*(-#self.strings[self.focused_string].letters/2 - 0.5 + k))
if self.config.pulse.start > G.TIMERS.REAL + 2*self.config.pulse.speed*#self.strings[self.focused_string].letters then
self.config.pulse = nil
end
end
if self.config.quiver then
letter.scale = letter.scale + (G.SETTINGS.reduced_motion and 0 or 1)*(0.1*self.config.quiver.amount)
letter.r = letter.r + (G.SETTINGS.reduced_motion and 0 or 1)*0.3*self.config.quiver.amount*(
math.sin(41.12342*G.TIMERS.REAL*self.config.quiver.speed + k*1223.2) +
math.cos(63.21231*G.TIMERS.REAL*self.config.quiver.speed + k*1112.2)*math.sin(36.1231*G.TIMERS.REAL*self.config.quiver.speed) +
math.cos(95.123*G.TIMERS.REAL*self.config.quiver.speed + k*1233.2) -
math.sin(30.133421*G.TIMERS.REAL*self.config.quiver.speed + k*123.2))
end
if self.config.float then letter.offset.y = (G.SETTINGS.reduced_motion and 0 or 1)*math.sqrt(self.scale)*(2+(self.font.FONTSCALE/G.TILESIZE)*2000*math.sin(2.666*G.TIMERS.REAL+200*k)) + 60*(letter.scale-1) end
if self.config.bump then letter.offset.y = (G.SETTINGS.reduced_motion and 0 or 1)*self.bump_amount*math.sqrt(self.scale)*7*math.max(0, (5+self.bump_rate)*math.sin(self.bump_rate*G.TIMERS.REAL+200*k) - 3 - self.bump_rate) end
end
end
function DynaText:set_quiver(amt)
self.config.quiver = {
speed = 0.5,
amount = amt or 0.7,
silent = false
}
end
function DynaText:pulse(amt)
self.config.pulse = {
speed = 40,
width = 2.5,
start = G.TIMERS.REAL,
amount = amt or 0.2,
silent = false
}
end
function DynaText:draw()
if Big then
self.scale = to_big(self.scale):to_number()
if self.shadow_parallax then self.shadow_parallax.x = to_big(self.shadow_parallax.x):to_number() end
end
if self.children.particle_effect then self.children.particle_effect:draw() end
if self.shadow then
prep_draw(self, 1)
love.graphics.translate(self.strings[self.focused_string].W_offset + self.text_offset.x*self.font.FONTSCALE/G.TILESIZE, self.strings[self.focused_string].H_offset + self.text_offset.y*self.font.FONTSCALE/G.TILESIZE)
if self.config.spacing then love.graphics.translate(self.config.spacing*self.font.FONTSCALE/G.TILESIZE, 0) end
if self.config.shadow_colour then
love.graphics.setColor(self.config.shadow_colour)
else
love.graphics.setColor(0, 0, 0, 0.3*self.colours[1][4])
end
for k, letter in ipairs(self.strings[self.focused_string].letters) do
if Big then
letter.dims.x = to_big(letter.dims.x):to_number()
letter.dims.y = to_big(letter.dims.y):to_number()
letter.offset.x = to_big(letter.offset.x):to_number()
letter.offset.y = to_big(letter.offset.y):to_number()
end
local real_pop_in = self.config.min_cycle_time == 0 and 1 or letter.pop_in
love.graphics.draw(
letter.letter,
0.5*(letter.dims.x - letter.offset.x)*self.font.FONTSCALE/G.TILESIZE -self.shadow_parrallax.x*self.scale/(G.TILESIZE),
0.5*(letter.dims.y)*self.font.FONTSCALE/G.TILESIZE -self.shadow_parrallax.y*self.scale/(G.TILESIZE),
letter.r or 0,
real_pop_in*self.scale*self.font.FONTSCALE/G.TILESIZE,
real_pop_in*self.scale*self.font.FONTSCALE/G.TILESIZE,
0.5*letter.dims.x/self.scale,
0.5*letter.dims.y/self.scale
)
love.graphics.translate(letter.dims.x*self.font.FONTSCALE/G.TILESIZE, 0)
end
love.graphics.pop()
end
prep_draw(self, 1)
love.graphics.translate(self.strings[self.focused_string].W_offset + self.text_offset.x*self.font.FONTSCALE/G.TILESIZE, self.strings[self.focused_string].H_offset + self.text_offset.y*self.font.FONTSCALE/G.TILESIZE)
if self.config.spacing then love.graphics.translate(self.config.spacing*self.font.FONTSCALE/G.TILESIZE, 0) end
self.ARGS.draw_shadow_norm = self.ARGS.draw_shadow_norm or {}
local _shadow_norm = self.ARGS.draw_shadow_norm
_shadow_norm.x, _shadow_norm.y =
self.shadow_parrallax.x/math.sqrt(self.shadow_parrallax.y*self.shadow_parrallax.y + self.shadow_parrallax.x*self.shadow_parrallax.x)*self.font.FONTSCALE/G.TILESIZE,
self.shadow_parrallax.y/math.sqrt(self.shadow_parrallax.y*self.shadow_parrallax.y + self.shadow_parrallax.x*self.shadow_parrallax.x)*self.font.FONTSCALE/G.TILESIZE
for k, letter in ipairs(self.strings[self.focused_string].letters) do
if Big then
letter.dims.x = to_big(letter.dims.x):to_number()
letter.dims.y = to_big(letter.dims.y):to_number()
letter.offset.x = to_big(letter.offset.x):to_number()
letter.offset.y = to_big(letter.offset.y):to_number()
end
local real_pop_in = self.config.min_cycle_time == 0 and 1 or letter.pop_in
love.graphics.setColor(letter.prefix or letter.suffix or letter.colour or self.colours[k%#self.colours + 1])
love.graphics.draw(
letter.letter,
0.5*(letter.dims.x - letter.offset.x)*self.font.FONTSCALE/G.TILESIZE + _shadow_norm.x,
0.5*(letter.dims.y - letter.offset.y)*self.font.FONTSCALE/G.TILESIZE + _shadow_norm.y,
letter.r or 0,
real_pop_in*letter.scale*self.scale*self.font.FONTSCALE/G.TILESIZE,
real_pop_in*letter.scale*self.scale*self.font.FONTSCALE/G.TILESIZE,
0.5*letter.dims.x/(self.scale),
0.5*letter.dims.y/(self.scale)
)
love.graphics.translate(letter.dims.x*self.font.FONTSCALE/G.TILESIZE, 0)
end
love.graphics.pop()
add_to_drawhash(self)
self:draw_boundingrect()
end