local gui = require("gui") local color = require("gui.core.color") local loader = require("loader") local theme = require("gui.core.theme") local fmt = require("fmt") local timer = require("utils") local multi, thread = require("multi"):init() local timesup = love.audio.newSource("assets/sounds/timesup.mp3", "static") local double = love.audio.newSource("assets/sounds/double.mp3", "static") local boardUpdater = gui:getProcessor():newProcessor("board-updater") boardUpdater.Start() local activePlayer local playerList = {} local playerStaticList = {} local scoreboard = {} -- Create a table to manage GUI elements local manage = {} -- Function to resize fonts for all managed elements local function resizeFonts() -- Use multi-threading to improve performance multi:newThread(function() -- Introduce a small delay to avoid too many iterations at once thread.skip(2) -- Iterate over each element in the 'manage' table for i = 1, #manage do local elem = manage[i] -- Check if the current element has a centerFont property if elem.centerFont then -- Adjust font size and re-center the text elem:fitFont(nil, nil, {scale = 2/3}) elem:centerFont() end end end) end gui.Events.OnResized(resizeFonts) local completed_questions = 0 local min_questions = 0 local dd_count = 0 local dd_enabled = false local applied_dd = false local board, question function gui:cleanup() for i = #self.children, 1, -1 do self.children[i]:destroy() end self.children = {} completed_questions = completed_questions + 1 end local function pickUniqueIndices(t, count) assert(#t >= count, "Not enough elements to pick " .. count .. " unique items") local indices = {} for i = 1, #t do indices[i] = i end -- partial Fisher-Yates shuffle for i = 1, count do local j = math.random(i, #indices) indices[i], indices[j] = indices[j], indices[i] end return unpack(indices, 1, count) end local function pickFiltered(items, count) -- Filter to only elements whose text contains "$" local filtered = {} for _, item in ipairs(items) do if item.text:find("%$") then filtered[#filtered + 1] = item end end -- Use pickUniqueIndices to select from filtered results local picks = { pickUniqueIndices(filtered, count) } local results = {} for _, idx in ipairs(picks) do results[#results + 1] = filtered[idx] end return unpack(results) end function applyDD() if not applied_dd and completed_questions > min_questions then local dd = 0 local dd_list = {} local dds = {pickFiltered(board.children, dd_count)} for i,v in pairs(dds) do print(i,v) v.isDouble = true end applied_dd = true end end local function buildBoard(frame, path) local data = loader:new(path) board, question, dailydouble = frame:newFrame(), frame:newFrame(), frame:newImageLabel("assets/images/double.jpg") index = data.index board:fullFrame() question:fullFrame() dailydouble:fullFrame() dailydouble.visible = false question.visible = false question.color = color.new("#060ce9") local tiers = index.settings.tiers or 5 local start = index.settings.start or 100 local inc = index.settings.increment or 100 if index.settings.dailyDouble and index.settings.dailyDouble.enabled then local dd = index.settings.dailyDouble dd_enabled = dd.enabled min_questions = dd.minQuestions dd_count = dd.count end for cat,v in pairs(index.categories) do local c if v.image then c = board:newImageLabel(v.image, 0, 0, 0, 0,(1/#index.categories)*(cat-1),0,1/#index.categories,1/(tiers+1)) else c = board:newTextLabel(v.displayName or v.name,0,0,0,0,(1/#index.categories)*(cat-1),0,1/#index.categories,1/(tiers+1)) c.align = gui.ALIGN_CENTER c.textColor = color.new("#ffffff") c.color = color.new("#060ce9") end c.UUID = multi.generate_uuid7() -- Each otpion gets a unique UUID img = c:newImageButton("assets/images/placeholder.jpg") img.visibility = 0 img:OnReleased(function(self) self.visible = false end) img:fullFrame() table.insert(manage,c) for tier = 1,tiers do local t = board:newTextButton("$" .. start + inc*(tier-1),0,0,0,0,(1/#index.categories)*(cat-1),1/(tiers+1)*tier,(1/#index.categories),1/(tiers+1)) t.textColor = color.new("#9b9024") t.align = gui.ALIGN_CENTER t.color = color.new("#060ce9") t.category = v.name t.index = tier t.price = start + inc*(tier-1) t:OnReleased(boardUpdater:newFunction(function(self) if self.text == "" or GetActivePlayer() == nil then return end if dd_enabled then applyDD() -- check and run DD if conditions meet end if index.categories[cat].questions == nil then fmt.Printf("Question not defined: File: %v Category: %v - %v\n",path,index.categories[cat].name,start + inc*(tier-1)) return end local q = index.categories[cat].questions[self.index] if q == nil then fmt.Printf("Question contains no data: File: %v Category: %v Tier: %v\n",path,index.categories[cat].name,start + inc*(tier-1)) return end self.textVisibility = 0 local template = LoadTemplate(q.template, path) question.visible = true local player = GetActivePlayer() local tm local stop fmt.Printf("--------------------\nQuestion: %v \nAnswer: %v\n--------------------\n",q["title"],q["answer"]) local mul = 1 boardUpdater:newThread(function() if self.isDouble then mul = 2 double:play() dailydouble.visible = true thread.hold(function() return not double:isPlaying() end) dailydouble.visible = false end if q["time-limit"] then tm = timer.startTimer({duration = q["time-limit"]}) tm.OnStop(function() -- Make sound? Subtract if daily double if stop then return end timesup:play() end) end local finished = false template.index(question, q, function(ans) if finished then return end player = GetActivePlayer() if tm then tm:Cleanup() end stop = true if ans then player:Add(self.price*mul) finished = true question.visible = false question:cleanup() elseif ans == false then player:Add(-self.price*mul) player = GetNextPlayer() else finished = true question.visible = false question:cleanup() end -- nil is a valid option where you weren't right or wrong, you just skipped self.text = "" end) boardUpdater:newThread("QuestionUpdater",function() while true do template.update(dt) thread.yield() if self.text == "" then return end end end) end) end)) table.insert(manage,t) end end resizeFonts() resizeFonts() end local qUpdater = gui:getProcessor():newProcessor("question-updater") qUpdater.Start() function LoadTemplate(name, path) if love.filesystem.getInfo("templates/" .. name .. ".lua") then data = love.filesystem.read("templates/" .. name .. ".lua") elseif love.filesystem.getInfo(path .."/templates/" .. name .. ".lua") then data = love.filesystem.read(path .."/templates/" .. name .. ".lua") end local template, err = loadstring(data) if err ~= nil then error(err) end local timer = function(wait, callback) multi:newAlarm(wait):OnRing(callback) end local env = { -- lua built-ins except io/os code execution pairs=pairs, print=print, tostring=tostring, tonumber=tonumber, type=type, assert=assert, ipairs=ipairs, table=table, math=math, string=string, next=next, pcall=pcall, select=select, xpcall=xpcall, utf8=utf8, clock = require("socket").gettime, ALIGN_CENTER = gui.ALIGN_CENTER, ALIGN_LEFT = gui.ALIGN_LEFT, ALIGN_RIGHT = gui.ALIGN_RIGHT, ALIGN_JUSTIFY = gui.ALIGN_JUSTIFY, -- Global vars color = color, error = multi.error, theme = theme, gui = gui, timer = timer, -- love stuff newSource = love.audio.newSource } env._G = env local isoTemplate = multi.isolateFunction(template, env) return isoTemplate() end return { buildBoard = buildBoard }