LOVELY_INTEGRITY = '435a9e8ef1b3d548ef4488606196332b7d61c0a33e2dd2225ca6c7f97e3ec32b' --Class UIBox = Moveable:extend() --The base level and container of a graph of 1 or more UIElements. These UIEs are\ --essentially a node based UI implementation. As the root node of the graph, this\ --node is the first called for any movement, updates, or changes to ensure that all child\ --nodes are updated and modified in the correct order.\\ --The UI_definitions file houses the majority of the definition tables needed for UIBox initialization. -- ---@param args {T: table, definition: table, config: table} --**T** A standard transform in game units describing the inital position and size of the object with x, y, w, h\ --ex - {x = 1, y = 5, w = 2, h = 2, r = 0} -- --**definition** A table containing a valid UIBox definition. These are mostly generated from UI_definitions -- --**config** A configuration table for the UIBox --ex - { align = 'cm', offset = {x = 1, y = 1}, parent_rect = A, attach_rect = B, can_collide = true } function UIBox:init(args) --First initialize the moveable Moveable.init(self,{args.T}) --Initialization of fields self.states.drag.can = false self.draw_layers = {} --if we need to explicitly change the draw order of the UIEs --The definition table that contains the schematic of this UIBox self.definition = args.definition if args.config then self.config = args.config args.config.major = args.config.major or args.config.parent or self self:set_alignment({ major = args.config.major, type = args.config.align or args.config.type or '', bond = args.config.bond or 'Strong', offset = args.config.offset or {x=0,y=0}, lr_clamp = args.config.lr_clamp }) self:set_role{ xy_bond = args.config.xy_bond, r_bond = args.config.r_bond, wh_bond = args.config.wh_bond or 'Weak', scale_bond = args.config.scale_bond or 'Weak' } self.states.collide.can = true if args.config.can_collide == nil then self.states.collide.can = true else self.states.collide.can = args.config.can_collide end self.parent = self.config.parent end --inherit the layered_parallax from the parent if there is any --self.layered_parallax = self.role.major and self.role.major.layered_parallax or self.layered_parallax --Initialization of the UIBox from the definition --First, set parent-child relationships to create the tree structure of the box self:set_parent_child(self.definition, nil) --Set the midpoint for any future alignments to use self.Mid = self.Mid or self.UIRoot --Calculate the correct and width/height and offset for each node self:calculate_xywh(self.UIRoot, self.T) --set the transform w/h to equal that of the calculated box self.T.w = self.UIRoot.T.w self.T.h = self.UIRoot.T.h --Then, calculate the correct width and height for each container self.UIRoot:set_wh() --Then, set all of the correct alignments for the ui elements\ self.UIRoot:set_alignments() self:align_to_major() self.VT.x, self.VT.y = self.T.x, self.T.y self.VT.w, self.VT.h = self.T.w, self.T.h if self.Mid ~= self and self.Mid.parent and false then self.VT.x = self.VT.x - self.Mid.role.offset.x + (self.Mid.parent.config.padding or 0) self.VT.y = self.VT.y - self.Mid.role.offset.y + (self.Mid.parent.config.padding or 0) end if self.alignment and self.alignment.lr_clamp then self:lr_clamp() end self.UIRoot:initialize_VT(true) if getmetatable(self) == UIBox then if args.config.instance_type then table.insert(G.I[args.config.instance_type], self) else table.insert(G.I.UIBOX, self) end end end function UIBox:get_UIE_by_ID(id, node) if not node then node = self.UIRoot end if node.config and node.config.id == id then return node end for k, v in pairs(node.children) do local res = self:get_UIE_by_ID(id, v) if res then return res elseif v.config.object and v.config.object.get_UIE_by_ID then res = v.config.object:get_UIE_by_ID(id, nil) if res then return res end end end return nil end function UIBox:calculate_xywh(node, _T, recalculate, _scale) node.ARGS.xywh_node_trans = node.ARGS.xywh_node_trans or {} local _nt = node.ARGS.xywh_node_trans local _ct = {} _ct.x, _ct.y, _ct.w, _ct.h = 0,0,0,0 local padding = node.config.padding or G.UIT.padding --current node does not contain anything if node.UIT == G.UIT.B or node.UIT == G.UIT.T or node.UIT == G.UIT.O then _nt.x, _nt.y, _nt.w, _nt.h = _T.x, _T.y, node.config.w or (node.config.object and node.config.object.T.w), node.config.h or (node.config.object and node.config.object.T.h) if node.UIT == G.UIT.T then node.config.text_drawable = nil local scale = node.config.scale or 1 if node.config.ref_table and node.config.ref_value then node.config.text = tostring(node.config.ref_table[node.config.ref_value]) if node.config.func and not recalculate then G.FUNCS[node.config.func](node) end end if not node.config.text then node.config.text = '[UI ERROR]' end node.config.lang = node.config.lang or G.LANG local tx = node.config.lang.font.FONT:getWidth(node.config.text)*node.config.lang.font.squish*scale*G.TILESCALE*node.config.lang.font.FONTSCALE local ty = node.config.lang.font.FONT:getHeight()*scale*G.TILESCALE*node.config.lang.font.FONTSCALE*node.config.lang.font.TEXT_HEIGHT_SCALE if node.config.vert then local thunk = tx; tx = ty; ty = thunk end _nt.x, _nt.y, _nt.w, _nt.h = _T.x, _T.y, tx/(G.TILESIZE*G.TILESCALE), ty/(G.TILESIZE*G.TILESCALE) node.content_dimensions = node.content_dimensions or {} node.content_dimensions.w = _T.w node.content_dimensions.h = _T.h node:set_values(_nt, recalculate) elseif node.UIT == G.UIT.B or node.UIT == G.UIT.O then node.content_dimensions = node.content_dimensions or {} node.content_dimensions.w = _nt.w node.content_dimensions.h = _nt.h node:set_values(_nt, recalculate) end return _nt.w, _nt.h else --For all other node containers, treat them explicitly like a column for i = 1, 2 do if i == 1 or (i == 2 and ((node.config.maxw and _ct.w > node.config.maxw) or (node.config.maxh and _ct.h > node.config.maxh))) then local fac = _scale or 1 if i == 2 then local restriction = node.config.maxw or node.config.maxh fac = fac*restriction/(node.config.maxw and _ct.w or _ct.h) end _nt.x, _nt.y, _nt.w, _nt.h = _T.x, _T.y, node.config.minw or 0, node.config.minh or 0 if node.UIT == G.UIT.ROOT then _nt.x, _nt.y, _nt.w, _nt.h = 0, 0, node.config.minw or 0, node.config.minh or 0 end _ct.x, _ct.y, _ct.w, _ct.h = _nt.x+padding, _nt.y+padding, 0, 0 local _tw, _th for k, v in ipairs(node.children) do if getmetatable(v) == UIElement then if v.config and v.config.scale then v.config.scale = v.config.scale*fac end _tw, _th = self:calculate_xywh(v, _ct, recalculate, fac) if _th and _tw then if Big then _th = to_big(_th):to_number() _tw = to_big(_tw):to_number() end if v.UIT == G.UIT.R then _ct.h = _ct.h + _th + padding _ct.y = _ct.y + _th + padding if _tw + padding > _ct.w then _ct.w = _tw + padding end if v.config and v.config.emboss then _ct.h = _ct.h + v.config.emboss _ct.y = _ct.y + v.config.emboss end else _ct.w = _ct.w + _tw + padding _ct.x = _ct.x + _tw + padding if _th + padding > _ct.h then _ct.h = _th + padding end if v.config and v.config.emboss then _ct.h = _ct.h + v.config.emboss end end end end end end end node.content_dimensions = node.content_dimensions or {} node.content_dimensions.w = _ct.w + padding node.content_dimensions.h = _ct.h + padding _nt.w = math.max(_ct.w + padding, _nt.w) _nt.h = math.max(_ct.h + padding, _nt.h)-- node:set_values(_nt, recalculate) return _nt.w, _nt.h end end function UIBox:remove_group(node, group) node = node or self.UIRoot for k, v in pairs(node.children) do if self:remove_group(v, group) then node.children[k] = nil end end if node.config and node.config.group and node.config.group == group then node:remove(); return true end if not node.parent or true then self:calculate_xywh(self.UIRoot, self.T, true); self.UIRoot:set_wh(); self.UIRoot:set_alignment() end--self:recalculate() end end function UIBox:get_group(node, group, ingroup) node = node or self.UIRoot ingroup = ingroup or {} for k, v in pairs(node.children) do self:get_group(v, group, ingroup) end if node.config and node.config.group and node.config.group == group then table.insert(ingroup, node); return ingroup end return ingroup end function UIBox:set_parent_child(node, parent) local UIE = UIElement(parent, self, node.n, node.config) --set the group of the element if parent and parent.config and parent.config.group then if UIE.config then UIE.config.group = parent.config.group else UIE.config = {group = parent.config.group} end end --set the button for the element if parent and parent.config and parent.config.button then if UIE.config then UIE.config.button_UIE = parent else UIE.config = {button_UIE = parent} end end if parent and parent.config and parent.config.button_UIE then if UIE.config then UIE.config.button_UIE = parent.config.button_UIE else UIE.config = {button = parent.config.button} end end if node.n and node.n == G.UIT.O and UIE.config.button then UIE.config.object.states.click.can = false end --current node is a container if (node.n and node.n == G.UIT.C or node.n == G.UIT.R or node.n == G.UIT.ROOT) and node.nodes then for k, v in pairs(node.nodes) do self:set_parent_child(v, UIE) end end if not parent then self.UIRoot = UIE self.UIRoot.parent = self else table.insert(parent.children, UIE) end if node.config and node.config.mid then self.Mid = UIE end end function UIBox:remove() if self == G.OVERLAY_MENU then G.REFRESH_ALERTS = true end self.UIRoot:remove() for k, v in pairs(G.I[self.config.instance_type or 'UIBOX']) do if v == self then table.remove(G.I[self.config.instance_type or 'UIBOX'], k) break; end end remove_all(self.children) Moveable.remove(self) end function UIBox:draw() if self.FRAME.DRAW >= G.FRAMES.DRAW and not G.OVERLAY_TUTORIAL then return end self.FRAME.DRAW = G.FRAMES.DRAW for k, v in pairs(self.children) do if k ~= 'h_popup' and k ~= 'alert' then v:draw() end end if self.states.visible then add_to_drawhash(self) self.UIRoot:draw_self() self.UIRoot:draw_children() for k, v in ipairs(self.draw_layers) do if v.draw_self then v:draw_self() else v:draw() end if v.draw_children then v:draw_children() end end end if self.children.alert then self.children.alert:draw() end self:draw_boundingrect() end function UIBox:recalculate() --Calculate the correct dimensions and width/height and offset for each node self:calculate_xywh(self.UIRoot, self.T, true) --Then, calculate the correct width and height for each container self.UIRoot:set_wh() --Then, set all of the correct alignments for the ui elements self.UIRoot:set_alignments() self.T.w = self.UIRoot.T.w self.T.h = self.UIRoot.T.h G.REFRESH_FRAME_MAJOR_CACHE = (G.REFRESH_FRAME_MAJOR_CACHE or 0) + 1 self.UIRoot:initialize_VT() G.REFRESH_FRAME_MAJOR_CACHE = (G.REFRESH_FRAME_MAJOR_CACHE > 1 and G.REFRESH_FRAME_MAJOR_CACHE - 1 or nil) end function UIBox:move(dt) Moveable.move(self, dt) Moveable.move(self.UIRoot, dt) end function UIBox:drag(offset) Moveable.drag(self,offset) Moveable.move(self.UIRoot, dt) end function UIBox:add_child(node, parent) self:set_parent_child(node, parent) self:recalculate() end function UIBox:set_container(container) self.UIRoot:set_container(container) Node.set_container(self, container) end function UIBox:print_topology(indent) local box_str = '| UIBox | - ID:'..self.ID..' w/h:'..self.T.w..'/'..self.T.h local indent = indent or 0 box_str = box_str..self.UIRoot:print_topology(indent) return box_str end --Class UIElement = Moveable:extend() --Class Methods function UIElement:init(parent, new_UIBox, new_UIT, config) self.parent = parent self.UIT = new_UIT self.UIBox = new_UIBox self.config = config or {} if self.config and self.config.object then self.config.object.parent = self end self.children = {} self.ARGS = self.ARGS or {} self.content_dimensions = {w=0, h=0} end function UIElement:set_values(_T, recalculate) if not recalculate or not self.T then Moveable.init(self,{T = _T}) self.states.click.can = false self.states.drag.can = false self.static_rotation = true else self.T.x = _T.x self.T.y = _T.y self.T.w = _T.w self.T.h = _T.h end if self.config.button_UIE then self.states.collide.can = true; self.states.hover.can = false; self.states.click.can = true end if self.config.button then self.states.collide.can = true; self.states.click.can = true end if self.config.on_demand_tooltip or self.config.tooltip or self.config.detailed_tooltip then self.states.collide.can = true end self:set_role{role_type = 'Minor', major = self.UIBox, offset = {x = _T.x, y = _T.y}, wh_bond = 'Weak', scale_bond = 'Weak'} if self.config.draw_layer then self.UIBox.draw_layers[self.config.draw_layer] = self end if self.config.collideable then self.states.collide.can = true end if self.config.can_collide ~= nil then self.states.collide.can = self.config.can_collide if self.config.object then self.config.object.states.collide.can = self.states.collide.can end end if self.UIT == G.UIT.O and not self.config.no_role then self.config.object:set_role(self.config.role or {role_type = 'Minor', major = self, xy_bond = 'Strong', wh_bond = 'Weak', scale_bond = 'Weak'}) end if self.config and self.config.ref_value and self.config.ref_table then self.config.prev_value = self.config.ref_table[self.config.ref_value] end if self.UIT == G.UIT.T then self.static_rotation = true end if self.config.juice then if self.UIT == G.UIT.ROOT then self:juice_up() end if self.UIT == G.UIT.T then self:juice_up() end if self.UIT == G.UIT.O then self.config.object:juice_up(0.5) end if self.UIT == G.UIT.B then self:juice_up() end if self.UIT == G.UIT.C then self:juice_up() end if self.UIT == G.UIT.R then self:juice_up() end self.config.juice = false end if not self.config.colour then if self.UIT == G.UIT.ROOT then self.config.colour = G.C.UI.BACKGROUND_DARK end if self.UIT == G.UIT.T then self.config.colour = G.C.UI.TEXT_LIGHT end if self.UIT == G.UIT.O then self.config.colour = G.C.WHITE end if self.UIT == G.UIT.B then self.config.colour = G.C.CLEAR end if self.UIT == G.UIT.C then self.config.colour = G.C.CLEAR end if self.UIT == G.UIT.R then self.config.colour = G.C.CLEAR end end if not self.config.outline_colour then if self.UIT == G.UIT.ROOT then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end if self.UIT == G.UIT.T then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end if self.UIT == G.UIT.O then self.config.colour = G.C.UI.OUTLINE_LIGHT end if self.UIT == G.UIT.B then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end if self.UIT == G.UIT.C then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end if self.UIT == G.UIT.R then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end end if self.config.focus_args and not self.config.focus_args.registered then if self.config.focus_args.button then G.CONTROLLER:add_to_registry(self.config.button_UIE or self, self.config.focus_args.button) end if self.config.focus_args.snap_to then G.CONTROLLER:snap_to{node = self} end if self.config.focus_args.funnel_to then local _par = self.parent while _par and _par:is(UIElement) do if _par.config.focus_args and _par.config.focus_args.funnel_from then _par.config.focus_args.funnel_from = self self.config.focus_args.funnel_to = _par break end _par = _par.parent end end self.config.focus_args.registered = true end if self.config.force_focus then self.states.collide.can = true end if self.config.button_delay and not self.config.button_delay_start then self.config.button_delay_start = G.TIMERS.REAL self.config.button_delay_end = G.TIMERS.REAL + self.config.button_delay self.config.button_delay_progress = 0 end self.layered_parallax = self.layered_parallax or {x=0, y=0} if self.config and self.config.func and (((self.config.button_UIE or self.config.button) and self.config.func ~= 'set_button_pip') or self.config.insta_func) then G.FUNCS[self.config.func](self) end end function UIElement:print_topology(indent) local UIT = '????' for k, v in pairs(G.UIT) do if v == self.UIT then UIT = ''..k end end local box_str = '\n'..(string.rep(" ", indent))..'| '..UIT..' | - ID:'..self.ID..' w/h:'..self.T.w..'/'..self.T.h if UIT == 'O' then box_str = box_str..' OBJ:'..( getmetatable(self.config.object) == CardArea and 'CardArea' or getmetatable(self.config.object) == Card and 'Card' or getmetatable(self.config.object) == UIBox and 'UIBox' or getmetatable(self.config.object) == Particles and 'Particles' or getmetatable(self.config.object) == DynaText and 'DynaText' or getmetatable(self.config.object) == Sprite and 'Sprite' or getmetatable(self.config.object) == AnimatedSprite and 'AnimatedSprite' or 'OTHER' ) elseif UIT == 'T' then box_str = box_str..' TEXT:'..(self.config.text or 'REF') end for k, v in ipairs(self.children) do if v.print_topology then box_str = box_str..v:print_topology(indent+1) end end return box_str end function UIElement:initialize_VT() self:move_with_major(0) self:calculate_parrallax() for _, v in pairs(self.children) do if v.initialize_VT then v:initialize_VT() end end self.VT.w, self.VT.h = self.T.w, self.T.h if self.UIT == G.UIT.T then self:update_text() end if self.config.object then if not self.config.no_role then self.config.object:hard_set_T(self.T.x, self.T.y, self.T.w, self.T.h) self.config.object:move_with_major(0) self.config.object.alignment.prev_type = '' self.config.object:align_to_major() end if self.config.object.recalculate then self.config.object:recalculate() end end end function UIElement:juice_up(amount, rot_amt) if self.UIT == G.UIT.O then if self.config.object then self.config.object:juice_up(amount, rot_amt) end else Moveable.juice_up(self, amount, rot_amt) end end function UIElement:can_drag() if self.states.drag.can then return self end return self.UIBox:can_drag() end function UIElement:draw() end function UIElement:draw_children(layer) if self.states.visible then for k, v in pairs(self.children) do if not v.config.draw_layer and k ~= 'h_popup' and k~= 'alert' then if v.draw_self and not v.config.draw_after then v:draw_self() else v:draw() end if v.draw_children then v:draw_children() end if v.draw_self and v.config.draw_after then v:draw_self() else v:draw() end end end end end function UIElement:set_wh() --Iterate through all children of this node local padding = (self.config and self.config.padding) or G.UIT.padding local _max_w, _max_h = 0,0 if next(self.children) == nil or self.config.no_fill then return self.T.w, self.T.h else for k, w in pairs(self.children) do if w.set_wh then local _cw, _ch = w:set_wh() if Big and G.STATE == G.STATES.MENU then _cw = to_big(_cw):to_number(); _ch = to_big(_ch):to_number() end if _cw and _ch then if _cw > _max_w then _max_w = _cw end if _ch > _max_h then _max_h = _ch end else _max_w = padding _max_h = padding end end end for k, w in pairs(self.children) do if w.UIT == G.UIT.R then w.T.w = _max_w end if w.UIT == G.UIT.C then w.T.h = _max_h end end end return self.T.w, self.T.h end function UIElement:align(x, y) self.role.offset.y = self.role.offset.y + y self.role.offset.x = self.role.offset.x + x for _, v in pairs(self.children) do if v.align then v:align(x, y) end end end function UIElement:set_alignments() --vertically centered is c = centered --horizontally centered is m = middle --top and left are default --bottom is b --right is r for k, v in pairs(self.children) do if self.config and self.config.align and v.align then local padding = self.config.padding or G.UIT.padding if string.find(self.config.align, "c") then if v.UIT == G.UIT.T or v.UIT == G.UIT.B or v.UIT == G.UIT.O then v:align(0,0.5*(self.T.h - 2*padding - v.T.h)) else v:align(0,0.5*(self.T.h - self.content_dimensions.h)) end end if string.find(self.config.align, "m") then v:align(0.5*(self.T.w - self.content_dimensions.w),0) end if string.find(self.config.align, "b") then v:align(0, self.T.h - self.content_dimensions.h) end if string.find(self.config.align, "r") then v:align((self.T.w - self.content_dimensions.w), 0) end end if v.set_alignments then v:set_alignments() end end end function UIElement:update_text() if self.config and self.config.text and not self.config.text_drawable then self.config.lang = self.config.lang or G.LANG self.config.text_drawable = love.graphics.newText(self.config.lang.font.FONT, {G.C.WHITE,self.config.text}) end if self.config.ref_table and self.config.ref_table[self.config.ref_value] ~= self.config.prev_value then self.config.text = tostring(self.config.ref_table[self.config.ref_value]) self.config.text_drawable:set(self.config.text) if not self.config.no_recalc and self.config.prev_value and string.len(self.config.prev_value) ~= string.len(self.config.text) then self.UIBox:recalculate() end self.config.prev_value = self.config.ref_table[self.config.ref_value] end end function UIElement:update_object() if self.config.ref_table and self.config.ref_value and self.config.ref_table[self.config.ref_value] ~= self.config.object then self.config.object = self.config.ref_table[self.config.ref_value] self.UIBox:recalculate() end if self.config.object then self.config.object.config.refresh_movement = true if self.config.object.states.hover.is and not self.states.hover.is then self:hover() self.states.hover.is = true end if not self.config.object.states.hover.is and self.states.hover.is then self:stop_hover() self.states.hover.is = false end end if self.config.object and self.config.object.ui_object_updated then self.config.object.ui_object_updated = nil self.config.object.parent = self self.config.object:set_role(self.config.role or {role_type = 'Minor', major = self}) self.config.object:move_with_major(0) if self.config.object.non_recalc then self.parent.content_dimensions.w = self.config.object.T.w self:align(self.parent.T.x - self.config.object.T.x, self.parent.T.y - self.config.object.T.y) self.parent:set_alignments() else self.UIBox:recalculate() end end end function UIElement:draw_self() if not self.states.visible then if self.config.force_focus then add_to_drawhash(self) end return end if self.config.force_focus or self.config.force_collision or self.config.button_UIE or self.config.button or self.states.collide.can then add_to_drawhash(self) end local button_active = true local parallax_dist = 1.5 local button_being_pressed = false if (self.config.button or self.config.button_UIE) then self.layered_parallax.x = ((self.parent and self.parent ~= self.UIBox and self.parent.layered_parallax.x or 0) + (self.config.shadow and 0.4*self.shadow_parrallax.x or 0)/G.TILESIZE) self.layered_parallax.y = ((self.parent and self.parent ~= self.UIBox and self.parent.layered_parallax.y or 0) + (self.config.shadow and 0.4*self.shadow_parrallax.y or 0)/G.TILESIZE) if self.config.button and ((self.last_clicked and self.last_clicked > G.TIMERS.REAL - 0.1) or ((self.config.button and (self.states.hover.is or self.states.drag.is)) and G.CONTROLLER.is_cursor_down)) then self.layered_parallax.x = self.layered_parallax.x - parallax_dist*self.shadow_parrallax.x/G.TILESIZE*(self.config.button_dist or 1) self.layered_parallax.y = self.layered_parallax.y - parallax_dist*self.shadow_parrallax.y/G.TILESIZE*(self.config.button_dist or 1) parallax_dist = 0 button_being_pressed = true end if self.config.button_UIE and not self.config.button_UIE.config.button then button_active = false end end if self.config.colour[4] > 0.01 then if self.UIT == G.UIT.T and self.config.scale then self.ARGS.text_parallax = self.ARGS.text_parallax or {} self.ARGS.text_parallax.sx = -self.shadow_parrallax.x*0.5/(self.config.scale*self.config.lang.font.FONTSCALE) self.ARGS.text_parallax.sy = -self.shadow_parrallax.y*0.5/(self.config.scale*self.config.lang.font.FONTSCALE) if (self.config.button_UIE and button_active) or (not self.config.button_UIE and self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On') then prep_draw(self, 0.97) if Big and G.STATE == G.STATES.MENU then self.config.scale = to_big(self.config.scale):to_number() end if self.config.vert then love.graphics.translate(0,self.VT.h); love.graphics.rotate(-math.pi/2) end if (self.config.shadow or (self.config.button_UIE and button_active)) and G.SETTINGS.GRAPHICS.shadows == 'On' then love.graphics.setColor(0, 0, 0, 0.3*self.config.colour[4]) love.graphics.draw( self.config.text_drawable, (self.config.lang.font.TEXT_OFFSET.x + (self.config.vert and -self.ARGS.text_parallax.sy or self.ARGS.text_parallax.sx))*(self.config.scale or 1)*self.config.lang.font.FONTSCALE/G.TILESIZE, (self.config.lang.font.TEXT_OFFSET.y + (self.config.vert and self.ARGS.text_parallax.sx or self.ARGS.text_parallax.sy))*(self.config.scale or 1)*self.config.lang.font.FONTSCALE/G.TILESIZE, 0, (self.config.scale)*self.config.lang.font.squish*self.config.lang.font.FONTSCALE/G.TILESIZE, (self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE ) end love.graphics.pop() end prep_draw(self, 1) if Big and G.STATE == G.STATES.MENU then self.config.scale = to_big(self.config.scale):to_number() end if self.config.vert then love.graphics.translate(0,self.VT.h); love.graphics.rotate(-math.pi/2) end if not button_active then love.graphics.setColor(G.C.UI.TEXT_INACTIVE) else love.graphics.setColor(self.config.colour) end love.graphics.draw( self.config.text_drawable, self.config.lang.font.TEXT_OFFSET.x*(self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE, self.config.lang.font.TEXT_OFFSET.y*(self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE, 0, (self.config.scale)*self.config.lang.font.squish*self.config.lang.font.FONTSCALE/G.TILESIZE, (self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE ) love.graphics.pop() elseif self.UIT == G.UIT.B or self.UIT == G.UIT.C or self.UIT == G.UIT.R or self.UIT == G.UIT.ROOT then prep_draw(self, 1) love.graphics.scale(1/(G.TILESIZE)) if self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On' then love.graphics.scale(0.98) if self.config.shadow_colour then love.graphics.setColor(self.config.shadow_colour) else love.graphics.setColor(0,0,0,0.3*self.config.colour[4]) end if self.config.r and self.VT.w > 0.01 then self:draw_pixellated_rect('shadow', parallax_dist) else love.graphics.rectangle('fill', -self.shadow_parrallax.x*parallax_dist, -self.shadow_parrallax.y*parallax_dist, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) end love.graphics.scale(1/0.98) end love.graphics.scale(button_being_pressed and 0.985 or 1) if self.config.emboss then love.graphics.setColor(darken(self.config.colour, self.states.hover.is and 0.5 or 0.3, true)) self:draw_pixellated_rect('emboss', parallax_dist, self.config.emboss) end local collided_button = self.config.button_UIE or self self.ARGS.button_colours = self.ARGS.button_colours or {} self.ARGS.button_colours[1] = self.config.button_delay and mix_colours(self.config.colour, G.C.L_BLACK, 0.5) or self.config.colour self.ARGS.button_colours[2] = (((collided_button.config.hover and collided_button.states.hover.is) or (collided_button.last_clicked and collided_button.last_clicked > G.TIMERS.REAL - 0.1)) and G.C.UI.HOVER or nil) for k, v in ipairs(self.ARGS.button_colours) do love.graphics.setColor(v) if self.config.r and self.VT.w > 0.01 then if self.config.button_delay then love.graphics.setColor(G.C.GREY) self:draw_pixellated_rect('fill', parallax_dist) love.graphics.setColor(v) self:draw_pixellated_rect('fill', parallax_dist, nil, self.config.button_delay_progress) elseif self.config.progress_bar then love.graphics.setColor(self.config.progress_bar.empty_col or G.C.GREY) self:draw_pixellated_rect('fill', parallax_dist) love.graphics.setColor(self.config.progress_bar.filled_col or G.C.BLUE) self:draw_pixellated_rect('fill', parallax_dist, nil, self.config.progress_bar.ref_table[self.config.progress_bar.ref_value]/self.config.progress_bar.max) else self:draw_pixellated_rect('fill', parallax_dist) end else love.graphics.rectangle('fill', 0,0, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) end end love.graphics.pop() elseif self.UIT == G.UIT.O and self.config.object then --Draw the outline for highlighted objext if self.config.focus_with_object and self.config.object.states.focus.is then self.object_focus_timer = self.object_focus_timer or G.TIMERS.REAL local lw = 50*math.max(0, self.object_focus_timer - G.TIMERS.REAL + 0.3)^2 prep_draw(self, 1) love.graphics.scale((1)/(G.TILESIZE)) love.graphics.setLineWidth(lw + 1.5) love.graphics.setColor(adjust_alpha(G.C.WHITE, 0.2*lw, true)) self:draw_pixellated_rect('fill', parallax_dist) love.graphics.setColor(self.config.colour[4] > 0 and mix_colours(G.C.WHITE, self.config.colour, 0.8) or G.C.WHITE) self:draw_pixellated_rect('line', parallax_dist) love.graphics.pop() else self.object_focus_timer = nil end self.config.object:draw() end end --Draw the outline of the object if self.config.outline and self.config.outline_colour[4] > 0.01 then if self.config.outline then prep_draw(self, 1) love.graphics.scale(1/(G.TILESIZE)) love.graphics.setLineWidth(self.config.outline) if self.config.line_emboss then love.graphics.setColor(darken(self.config.outline_colour, self.states.hover.is and 0.5 or 0.3, true)) self:draw_pixellated_rect('line_emboss', parallax_dist, self.config.line_emboss) end love.graphics.setColor(self.config.outline_colour) if self.config.r and self.VT.w > 0.01 then self:draw_pixellated_rect('line', parallax_dist) else love.graphics.rectangle('line', 0,0, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) end love.graphics.pop() end end --Draw the outline for highlighted buttons if self.states.focus.is then self.focus_timer = self.focus_timer or G.TIMERS.REAL local lw = 50*math.max(0, self.focus_timer - G.TIMERS.REAL + 0.3)^2 prep_draw(self, 1) love.graphics.scale((1)/(G.TILESIZE)) love.graphics.setLineWidth(lw + 1.5) love.graphics.setColor(adjust_alpha(G.C.WHITE, 0.2*lw, true)) self:draw_pixellated_rect('fill', parallax_dist) love.graphics.setColor(self.config.colour[4] > 0 and mix_colours(G.C.WHITE, self.config.colour, 0.8) or G.C.WHITE) self:draw_pixellated_rect('line', parallax_dist) love.graphics.pop() else self.focus_timer = nil end --Draw the 'chosen triangle' if self.config.chosen then prep_draw(self, 0.98) love.graphics.scale(1/(G.TILESIZE)) if self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On' then love.graphics.setColor(0,0,0,0.3*self.config.colour[4]) love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x - self.shadow_parrallax.x*parallax_dist*0.5, self.layered_parallax.y - self.shadow_parrallax.y*parallax_dist*0.5, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert')) end love.graphics.pop() prep_draw(self, 1) love.graphics.scale(1/(G.TILESIZE)) love.graphics.setColor(G.C.RED) love.graphics.setColor(self.config.colour) love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x, self.layered_parallax.y, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert')) love.graphics.pop() end self:draw_boundingrect() end function UIElement:draw_pixellated_rect(_type, _parallax, _emboss, _progress) if not self.pixellated_rect or #self.pixellated_rect[_type].vertices < 1 or _parallax ~= self.pixellated_rect.parallax or self.pixellated_rect.w ~= self.VT.w or self.pixellated_rect.h ~= self.VT.h or self.pixellated_rect.sw ~= self.shadow_parrallax.x or self.pixellated_rect.sh ~= self.shadow_parrallax.y or self.pixellated_rect.progress ~= (_progress or 1) then self.pixellated_rect = { w = self.VT.w, h = self.VT.h, sw = self.shadow_parrallax.x, sh = self.shadow_parrallax.y, progress = (_progress or 1), fill = {vertices = {}}, shadow = {vertices = {}}, line = {vertices = {}}, emboss = {vertices = {}}, line_emboss = {vertices = {}}, parallax = _parallax } local ext_up = self.config.ext_up and self.config.ext_up*G.TILESIZE or 0 local res = self.config.res or math.min(self.VT.w, self.VT.h + math.abs(ext_up)/G.TILESIZE) > 3.5 and 0.8 or math.min(self.VT.w, self.VT.h + math.abs(ext_up)/G.TILESIZE) > 0.3 and 0.6 or 0.15 local totw, toth, subw, subh = self.VT.w*G.TILESIZE, (self.VT.h + math.abs(ext_up)/G.TILESIZE)*G.TILESIZE, self.VT.w*G.TILESIZE-4*res, (self.VT.h + math.abs(ext_up)/G.TILESIZE)*G.TILESIZE-4*res local vertices = { subw/2, subh/2-ext_up, 0,4*res-ext_up, 1*res,4*res-ext_up, 1*res,2*res-ext_up, 2*res,2*res-ext_up, 2*res,1*res-ext_up, 4*res,1*res-ext_up, 4*res,0*res-ext_up, subw,0*res-ext_up, subw,1*res-ext_up, subw+2*res,1*res-ext_up, subw+2*res,2*res-ext_up, subw+3*res,2*res-ext_up, subw+3*res,4*res-ext_up, totw,4*res-ext_up, totw,subh-ext_up, subw+3*res, subh-ext_up, subw+3*res, subh+2*res-ext_up, subw+2*res, subh+2*res-ext_up, subw+2*res, subh+3*res-ext_up, subw, subh+3*res-ext_up, subw, toth-ext_up, 4*res, toth-ext_up, 4*res, subh+3*res-ext_up, 2*res, subh+3*res-ext_up, 2*res, subh+2*res-ext_up, 1*res, subh+2*res-ext_up, 1*res, subh-ext_up, 0, subh-ext_up, 0,4*res-ext_up, } for k, v in ipairs(vertices) do if k%2 == 1 and v > totw*self.pixellated_rect.progress then v = totw*self.pixellated_rect.progress end self.pixellated_rect.fill.vertices[k] = v if k > 4 then self.pixellated_rect.line.vertices[k-4] = v if _emboss then self.pixellated_rect.line_emboss.vertices[k-4] = v + (k%2 == 0 and -_emboss*self.shadow_parrallax.y or -0.7*_emboss*self.shadow_parrallax.x) end end if k%2 == 0 then self.pixellated_rect.shadow.vertices[k] = v -self.shadow_parrallax.y*_parallax if _emboss then self.pixellated_rect.emboss.vertices[k] = v + _emboss*G.TILESIZE end else self.pixellated_rect.shadow.vertices[k] = v -self.shadow_parrallax.x*_parallax if _emboss then self.pixellated_rect.emboss.vertices[k] = v end end end end love.graphics.polygon((_type == 'line' or _type == 'line_emboss') and 'line' or "fill", self.pixellated_rect[_type].vertices) end function UIElement:update(dt) G.ARGS.FUNC_TRACKER = G.ARGS.FUNC_TRACKER or {} if self.config.button_delay then self.config.button_temp = self.config.button or self.config.button_temp self.config.button = nil self.config.button_delay_progress = (G.TIMERS.REAL - self.config.button_delay_start)/self.config.button_delay if G.TIMERS.REAL >= self.config.button_delay_end then self.config.button_delay = nil end end if self.config.button_temp and not self.config.button_delay then self.config.button = self.config.button_temp end if self.button_clicked then self.button_clicked = nil end if self.config and self.config.func then G.ARGS.FUNC_TRACKER[self.config.func] = (G.ARGS.FUNC_TRACKER[self.config.func] or 0) + 1 G.FUNCS[self.config.func](self) end if self.UIT == G.UIT.T then self:update_text() end if self.UIT == G.UIT.O then self:update_object() end Node.update(self, dt) end function UIElement:collides_with_point(cursor_trans) if self.UIBox.states.collide.can then return Node.collides_with_point(self, cursor_trans) else return false end end function UIElement:click() if self.config.button and (not self.last_clicked or self.last_clicked + 0.1 < G.TIMERS.REAL) and self.states.visible and not self.under_overlay and not self.disable_button then if self.config.one_press then self.disable_button = true end self.last_clicked = G.TIMERS.REAL --Removes a layer from the overlay menu stack if self.config.id == 'overlay_menu_back_button' then G.CONTROLLER:mod_cursor_context_layer(-1) G.NO_MOD_CURSOR_STACK = true end if G.OVERLAY_TUTORIAL and G.OVERLAY_TUTORIAL.button_listen == self.config.button then G.FUNCS.tut_next() end G.FUNCS[self.config.button](self) G.NO_MOD_CURSOR_STACK = nil if self.config.choice then local chosen_temp = self.config.chosen local chosen_temp = self.config.chosen local choices = self.UIBox:get_group(nil, self.config.group) for k, v in pairs(choices) do if v.config and v.config.choice then v.config.chosen = false end end self.config.chosen = chosen_temp or true end play_sound('button', 1, 0.3) G.ROOM.jiggle = G.ROOM.jiggle + 0.5 self.button_clicked = true end if self.config.button_UIE then self.config.button_UIE:click() end end function UIElement:put_focused_cursor() if self.config.focus_args and self.config.focus_args.type == 'tab' then for k, v in pairs(self.children) do if v.children[1].config.chosen then return v.children[1]:put_focused_cursor() end end else return Node.put_focused_cursor(self) end end function UIElement:remove() if self.config and self.config.object then self.config.object:remove() self.config.object = nil end if self == G.CONTROLLER.text_input_hook then G.CONTROLLER.text_input_hook = nil end remove_all(self.children) Moveable.remove(self) end function UIElement:hover() if self.config and self.config.on_demand_tooltip then self.config.h_popup = create_popup_UIBox_tooltip(self.config.on_demand_tooltip) self.config.h_popup_config ={align=self.T.y > G.ROOM.T.h/2 and 'tm' or 'bm', offset = {x=0,y=self.T.y > G.ROOM.T.h/2 and -0.1 or 0.1}, parent = self} end if self.config.tooltip then self.config.h_popup = create_popup_UIBox_tooltip(self.config.tooltip) self.config.h_popup_config ={align="tm", offset = {x=0,y=-0.1}, parent = self} end if self.config.detailed_tooltip and G.CONTROLLER.HID.pointer then self.config.h_popup = create_UIBox_detailed_tooltip(self.config.detailed_tooltip) self.config.h_popup_config ={align="tm", offset = {x=0,y=-0.1}, parent = self} end Node.hover(self) end function UIElement:stop_hover() Node.stop_hover(self) if self.config and self.config.on_demand_tooltip then self.config.h_popup = nil end end function UIElement:release(other) if self.parent then self.parent:release(other) end end function is_UI_containter(node) if node.UIT ~= G.UIT.C and node.UIT ~= G.UIT.R and node.UIT ~= G.UIT.ROOT then return false end return true end