diff --git a/ai-anime/quiz.zip b/ai-anime/quiz.zip new file mode 100644 index 0000000..f6119e5 Binary files /dev/null and b/ai-anime/quiz.zip differ diff --git a/ai-anime/villains.yml b/ai-anime/villains.yml deleted file mode 100644 index 6e1e652..0000000 --- a/ai-anime/villains.yml +++ /dev/null @@ -1,33 +0,0 @@ -questions: - - title: "This Death Note villain is a world-famous detective who refuses to sit in a normal chair." - time-limit: 15 - answer: "L" - template: "whatis" - - - title: "In Fullmetal Alchemist: Brotherhood, Father created seven Homunculi. Which of these is NOT one of them?" - answer: 4 - choices: - - "Gluttony" - - "Envy" - - "Wrath" - - "Malice" - template: "multiplechoice" - - - title: "In Attack on Titan, this character orchestrated the Rumbling to trample the world outside Paradis Island." - time-limit: 20 - answer: "Eren Yeager" - template: "whatis" - - - title: "Which of these best describes Griffith's betrayal in Berserk, the event known as the Eclipse?" - answer: 1 - choices: - - "He sacrificed the Band of the Hawk to be reborn as the demon king Femto" - - "He defected to the Midland king and had Guts executed" - - "He destroyed Guts' sword to prevent him from following him" - - "He erased everyone's memories of the Band of the Hawk" - template: "multiplechoice" - - - title: "Dio Brando's Stand in JoJo's Bizarre Adventure Part 3 has the ability to stop time. This is its name." - time-limit: 20 - answer: "The World" - template: "whatis" diff --git a/anime/shounen.yml b/anime/shounen.yml index 5869446..c3e3809 100644 --- a/anime/shounen.yml +++ b/anime/shounen.yml @@ -20,7 +20,7 @@ questions: # expects a list equal to the tier - "9000" - "-4" - "wack" - template: "multiplechoice" # template to use + template: "multiplechoice" - title: "Who directed the groundbreaking film 'Akira',\nwhich helped introduce anime to Western audiences?" time-limit: 10 # If omitted there is no time limit answer: "Katsuhiro Otomo" @@ -32,4 +32,4 @@ questions: # expects a list equal to the tier start: 4 playFor: 3 template: "whatvideo" # template to use - display-answer: true # displays the correct answer before returning to the main screen \ No newline at end of file + display-answer: true # displays the correct answer before returning to the main screen diff --git a/font/gyparody.ttf b/assets/fonts/gyparody.ttf similarity index 100% rename from font/gyparody.ttf rename to assets/fonts/gyparody.ttf diff --git a/assets/images/CLOUD.png b/assets/images/CLOUD.png new file mode 100644 index 0000000..e1eded5 Binary files /dev/null and b/assets/images/CLOUD.png differ diff --git a/assets/images/background.jpg b/assets/images/background.jpg new file mode 100644 index 0000000..13ea8d2 Binary files /dev/null and b/assets/images/background.jpg differ diff --git a/assets/checked.png b/assets/images/checked.png similarity index 100% rename from assets/checked.png rename to assets/images/checked.png diff --git a/assets/double.jpg b/assets/images/double.jpg similarity index 100% rename from assets/double.jpg rename to assets/images/double.jpg diff --git a/assets/final-jeopardy-1639404433135-3347818171.jpeg b/assets/images/final-jeopardy-1639404433135-3347818171.jpeg similarity index 100% rename from assets/final-jeopardy-1639404433135-3347818171.jpeg rename to assets/images/final-jeopardy-1639404433135-3347818171.jpeg diff --git a/assets/placeholder.jpg b/assets/images/placeholder.jpg similarity index 100% rename from assets/placeholder.jpg rename to assets/images/placeholder.jpg diff --git a/assets/plus.png b/assets/images/plus.png similarity index 100% rename from assets/plus.png rename to assets/images/plus.png diff --git a/assets/speaker.png b/assets/images/speaker.png similarity index 100% rename from assets/speaker.png rename to assets/images/speaker.png diff --git a/assets/unchecked.png b/assets/images/unchecked.png similarity index 100% rename from assets/unchecked.png rename to assets/images/unchecked.png diff --git a/assets/double.mp3 b/assets/sounds/double.mp3 similarity index 100% rename from assets/double.mp3 rename to assets/sounds/double.mp3 diff --git a/assets/sounds/finaltheme.mp3 b/assets/sounds/finaltheme.mp3 new file mode 100644 index 0000000..3154925 Binary files /dev/null and b/assets/sounds/finaltheme.mp3 differ diff --git a/assets/timesup.mp3 b/assets/sounds/timesup.mp3 similarity index 100% rename from assets/timesup.mp3 rename to assets/sounds/timesup.mp3 diff --git a/board.lua b/board.lua index 7688626..e6286d0 100644 --- a/board.lua +++ b/board.lua @@ -6,12 +6,17 @@ local fmt = require("fmt") local timer = require("utils") local multi, thread = require("multi"):init() -local timesup = love.audio.newSource("assets/timesup.mp3", "static") -local double = love.audio.newSource("assets/double.mp3", "static") +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 = {} @@ -101,7 +106,7 @@ end local function buildBoard(frame, path) local data = loader:new(path) - board, question, dailydouble = frame:newFrame(), frame:newFrame(), frame:newImageLabel("assets/double.jpg") + board, question, dailydouble = frame:newFrame(), frame:newFrame(), frame:newImageLabel("assets/images/double.jpg") index = data.index board:fullFrame() question:fullFrame() @@ -132,7 +137,7 @@ local function buildBoard(frame, path) c.UUID = multi.generate_uuid7() -- Each otpion gets a unique UUID - img = c:newImageButton("assets/placeholder.jpg") + img = c:newImageButton("assets/images/placeholder.jpg") img.visibility = 0 img:OnReleased(function(self) diff --git a/gui/init.lua b/gui/init.lua index 092aa8a..bb91d87 100644 --- a/gui/init.lua +++ b/gui/init.lua @@ -16,7 +16,6 @@ local max, min, abs, rad, floor, ceil = math.max, math.min, math.abs, math.rad, local frame, image, text, box, video, button, anim = 0, 1, 2, 4, 8, 16, 32 local global_drag local object_focus = gui -local first_loop = false -- Types gui.TYPE_FRAME = frame @@ -324,7 +323,7 @@ end function gui:isDescendantOf(obj) local parent = self.parent - while parent ~= gui do + while parent ~= gui and parent ~= nil do if parent == obj then return true end parent = parent.parent end @@ -575,7 +574,7 @@ function gui:clone(opt) end function gui:isActive() - return self.active and not (self:isDescendantOf(gui.virtual)) + return self.active and not self:isDescendantOf(gui.virtual) end function gui:isOnScreen() @@ -934,7 +933,7 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual) end -- shader stuff - function c:setShader(shader) + function c:setShader(shader, env) if type(shader) == "string" then self.shader = love.graphics.newShader(shader) elseif type(shader) == "table" then @@ -948,6 +947,13 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual) else self.shader:send(i, self[i]) end + elseif env[i] then + if type(v) == "function" then + local data = v(env) + self.shader:send(i, data) + else + self.shader:send(i, env[i]) + end else error(i .. " is a required argument!\n\n".. shader.usage()) end @@ -969,10 +975,14 @@ function gui:newBase(typ, x, y, w, h, sx, sy, sw, sh, virtual) self.shader:send(name, ...) end end - - function c:enableShaderTime() + local st + function c:shaderTime(b) + if not b then + st:Unconnect() + end + if st then return end self.__shaderTime = 0 - mainupdater.OnLoop(function(_, _, dt) + st = mainupdater.OnLoop(function(_, _, dt) if not self.shader then return end self.__shaderTime = self.__shaderTime + dt if self.shader:hasUniform("time") then @@ -2311,24 +2321,18 @@ local draw_loop = drawer:newLoop(function(self, dt) draw_handler(child, nil, dt) end end - first_loop = true love.graphics.setColor(1, 1, 1, 1) end) draw_loop:setName("GUI Draw Handler") drawer:newThread("Draw Handler",function() while true do - thread.sleep(.01) + thread.sleep(.1) local children = gui.virtual:getAllChildren() for i = 1, #children do local child = children[i] - if child.effect then - child.effect(function() draw_handler(child, true, 0) end) - else - draw_handler(child, true, 0) - end + draw_handler(child, true, 0) end - first_loop = true end end) diff --git a/main.lua b/main.lua index 78be67b..897cf8d 100644 --- a/main.lua +++ b/main.lua @@ -1,8 +1,4 @@ local gui, color, theme, utils, board, yaml, loader, scoreUpdater -local activePlayer -local playerList = {} -local playerStaticList = {} -local scoreboard = {} multi, thread = require("multi"):init({priority=true}) multi.setClock(require("socket").gettime) -- When on linux os.clock doesn't reture actual seconds the program has elapsed for @@ -17,20 +13,10 @@ yaml = require("yaml") loader = require("loader") require("gui.addons") -scoreUpdater = gui:getProcessor():newProcessor("score-updater") -scoreUpdater.Start() - -gui.registerExtension({ - sayHello = function(self) - print("Extended Hello World") - end, - sayBye = function(self) - print("Extended Bye World") - end, -}) +title = require("menus.title") function love.load() - gui:cacheImage({"assets/checked.png","assets/unchecked.png","assets/speaker.png"}) + gui:cacheImage({"assets/images/checked.png","assets/images/unchecked.png","assets/images/speaker.png"}) gui:setAspectSize(1920, 1080) gui.aspect_ratio = true @@ -38,25 +24,10 @@ function love.load() bg:fullFrame() bg.color = color.new("#242f9b") - local btn = bg:newImageButton("rainbow.jpg", 100, 100, 600, 450) - btn.color = color.light_blue - btn.align = gui.ALIGN_CENTER + require("menus.menus").init(bg) - btn.direction = {1,0} + title:visible(true) - btn:enableShaderTime() - btn.OnEnter(function() - btn:setShader(gui.SHADERS.blur) - end) - - btn.OnExit(function() - btn:setShader() - end) - - -- btn:fitFont() - -- btn:centerFont() - - -- bg:setBlur(10) bg:OnUpdate(function(self, dt) if self.__blur then -- pulse the blur between 2 and 12 @@ -64,309 +35,6 @@ function love.load() self:setBlurRadius(7 + 5 * math.sin(t * 2)) end end) - - -- StartGame(bg) -end - -function GetActivePlayer() - if not activePlayer then return end - return activePlayer.link -end - -local function GetPlayerPos() - for i,v in pairs(playerStaticList) do - if v == GetActivePlayer() then - return i - end - end -end - -function GetNextPlayer() - local pos = GetPlayerPos() - - if pos >= #playerStaticList then - activePlayer = playerStaticList[1].Ref.Frame - else - activePlayer = playerStaticList[pos + 1].Ref.Frame - end - scoreboard:RenderPlayer(playerList) - return GetActivePlayer() -end - -function love.filedropped(file) - file:open("r") - local data = file:read() - print("Load file? " .. file:getFilename()) -end - -function ScoreBoard(frame, x, y, w, h, sx, sy, sw, sh) - -- Colors - local C_BG_PANEL = color.new("#1a1a2e") - local C_BG_HEADER = color.new("#16213e") - local C_ACCENT = color.new("#e94560") - local C_ROW_TOP = color.new("#1c2641") - local C_ROW_NORM = color.new("#121226") - local C_BORDER_TOP = color.new("#323c6e") - local C_BORDER_NRM = color.new("#1c1c37") - local C_BAR_EMPTY = color.new("#232341") - local C_TEXT_MUTED = color.new("#786e96") - local C_WHITE = color.new("#ffffff") - local C_GOLD = color.new("#ffd700") - local C_SILVER = color.new("#C0C0C0") - local C_BRONZE = color.new("#cd7f32") - - -- Config stuff - local LEADER_HEIGHT_SCALE = .06 - local HEIGHT_SCALE = .03 - local PLAYER_HEIGHT = .05 - -- - - local rankColors = { C_GOLD, C_SILVER, C_BRONZE } - - local leaderboard = frame:newFrame(x, y, w, h, sx, sy, sw, sh) - leaderboard.color = C_BORDER_NRM - - local headernum = leaderboard:newTextLabel("#",0,0,0,0,0,LEADER_HEIGHT_SCALE,1/6,HEIGHT_SCALE) - headernum.align = gui.ALIGN_CENTER - - local headerplayer = leaderboard:newTextLabel("PLAYER",0,0,0,0,1/6,LEADER_HEIGHT_SCALE,3/6,HEIGHT_SCALE) - headerplayer.align = gui.ALIGN_LEFT - - local headerscore = leaderboard:newTextLabel("SCORE",0,0,0,0,4/6,LEADER_HEIGHT_SCALE,2/6,HEIGHT_SCALE) - headerscore.align = gui.ALIGN_RIGHT - - local header = leaderboard:newTextLabel("LEADERBOARD",0,0,0,0,0,0,1,LEADER_HEIGHT_SCALE) - header.borderColor = C_ACCENT - header.textColor = C_ACCENT - header.color = C_BG_HEADER - header.align = gui.ALIGN_CENTER - - local BASE_HEIGHT = LEADER_HEIGHT_SCALE + HEIGHT_SCALE - - gui.apply({ - drawBorder = false, - color = color.new("#0f2a60"), - textColor = C_TEXT_MUTED, - }, headernum, headerplayer, headerscore) - - local updateList = {header, headernum, headerplayer, headerscore} - - local function ScoreResize() - scoreUpdater:newThread(function() - thread.skip(2) - - for _,object in pairs(updateList) do - object:fitFont(nil, nil, {scale = 5/6}) - object:centerFont() - end - - for _,object in pairs(playerList) do - if type(object) == "table" and object.Ref then - for i, player in pairs(object.Ref) do - if player:hasType(gui.TYPE_TEXT) then - player:fitFont(nil, nil, {scale = 5/6}) - player:centerFont() - end - end - end - end - end) - end - - function scoreboard:AddPlayer(name, score, icon) - local player = { - Name = name, - Score = score, - Icon = icon, - UUID = multi.generate_uuid7(), - Add = function(self, amt) - self.Score = tostring(tonumber(self.Score) + amt) - table.sort(playerList, function(a, b) - if a.Score == b.Score then - return a.Name < b.Name - end - return tonumber(a.Score) > tonumber(b.Score) - end) - scoreboard:RenderPlayer(playerList) - ScoreResize() - end, - } - table.insert(playerList, player) - table.insert(playerStaticList, player) - scoreboard:RenderPlayer(playerList) - ScoreResize() - return player - end - - local colors = {C_GOLD, C_SILVER, C_BRONZE} - local function MapColor(index) - if index <=3 and index > 0 then - return colors[index] - else - return C_WHITE - end - end - - local add_player = leaderboard:newFrame(5,-5,-10,0,0,1-PLAYER_HEIGHT,1,PLAYER_HEIGHT) - local edit_player = leaderboard:newFrame(5,-10,-10,0,0,1-2*PLAYER_HEIGHT,1,PLAYER_HEIGHT) - local remove_player = leaderboard:newTextButton("Remove Selected",5,-15,-10,0,0,1-3*PLAYER_HEIGHT,1,PLAYER_HEIGHT) - remove_player:setFont(20) - remove_player.align = gui.ALIGN_CENTER - local embededWatch = {remove_player} - scoreUpdater:newThread(function() - while true do - thread.sleep(.01) - for i,v in pairs(embededWatch) do - v:centerFont() - end - end - end) - - local function embedTextEdit(reference, default, but_text, callback) - reference.color = C_BORDER_NRM - local textbox = reference:newTextBox(default,0,0,0,0,.015,.1,.8,.8) - textbox.textColor = C_GOLD - textbox.blink = false - textbox.color = C_BORDER_TOP - textbox.textColor = C_WHITE - textbox:OnPressed(function() - textbox.text = "" - end) - - local button = reference:newTextButton(but_text,5,0,-10,0,.815,.1,.185,.8) - button.color = color.new("#7eae5b") - button:OnReleased(function() - callback(textbox) - end) - gui.apply({ - setFont = {20}, - align = gui.ALIGN_CENTER - },textbox,button,reference) - table.insert(embededWatch,textbox) - table.insert(embededWatch,button) - end - - remove_player.color = color.new("#a13a3a") - remove_player:OnReleased(function() - local player = GetActivePlayer() - uuid = player.UUID - for i = 1, #playerList do - if playerList[i].UUID == uuid then - table.remove(playerList,i) - break - end - end - for i = 1, #playerStaticList do - if playerStaticList[i].UUID == uuid then - table.remove(playerStaticList,i) - break - end - end - scoreboard:RenderPlayer(playerList) - player.Ref.Frame:destroy() - end) - - embedTextEdit(add_player, "Player Name", "Add", function(self) - scoreboard:AddPlayer(self.text, "0") - end) - - embedTextEdit(edit_player, "Modify Score", "Edit", function(self) - local player = GetActivePlayer() - if player then - player.Score = self.text - scoreboard:RenderPlayer(playerList) - end - end) - - function scoreboard:RenderPlayer(list) - for index, player in ipairs(list) do - if player.Ref then - player.Ref[1].text = player.Name or "" - player.Ref[2].text = player.Score or "" - player.Ref[3].text = tostring(index) - player.Ref.Frame:setDualDim(nil,5*index,nil,nil,nil,BASE_HEIGHT + (index-1) * PLAYER_HEIGHT) - player.Ref.Frame.link = player - - gui.apply({ - visibility = 0, - drawBorder = false, - textColor = MapColor(index) - }, unpack(player.Ref)) - - if activePlayer == nil then - activePlayer = player.Ref.Frame - end - - if player.Ref.Frame == activePlayer then - player.Ref.Frame.borderColor = C_BORDER_NRM - player.Ref.Frame.color = C_ROW_NORM - else - player.Ref.Frame.borderColor = C_BORDER_TOP - player.Ref.Frame.color = C_ROW_TOP - end - else - local playernum, playerName, playerIcon, playerScore, playerLine - local playerFrame = leaderboard:newFrame(5,5*index,-10,0,0,BASE_HEIGHT + (index-1) * PLAYER_HEIGHT,1,PLAYER_HEIGHT) - - playerFrame:OnReleased(function(self) - activePlayer = self - scoreboard:RenderPlayer(playerList) - end) - - playerFrame:respectHierarchy(false) - - playerFrame.borderColor = C_BORDER_TOP - playerFrame.color = C_ROW_TOP - - playernum = playerFrame:newTextLabel(index,0,0,0,0,.015,.1,1/8,.8) - playernum.align = gui.ALIGN_CENTER - - if player.Icon ~= nil then - playerIcon = playerFrame:newImageLabel(player.Icon,0,0,0,0,.16,.1,.1,.8) - playerIcon.square = "h" -- When working with scales squaring is trickier. (h/w) to switch on width or height - - playerName = playerFrame:newTextLabel(player.Name,0,0,0,0,.3,0,2/5,.8) - playerName.align = gui.ALIGN_LEFT - - playerLine = playerFrame:newFrame(0,0,0,0,.3,.8,.69,.07) - playerLine.color = C_GOLD - else - playerName = playerFrame:newTextLabel(player.Name,0,0,0,0,.16,0,7/13,.8) - playerName.align = gui.ALIGN_LEFT - - playerLine = playerFrame:newFrame(0,0,0,0,.16,.8,7/13 + 2/7,.07) - playerLine.color = C_GOLD - end - - playerScore = playerFrame:newTextLabel(player.Score,0,0,0,0,.71,0,2/7,.8) - playerScore.align = gui.ALIGN_CENTER - playerLine.drawBorder = false - - gui.apply({ - visibility = 0, - drawBorder = false, - textColor = MapColor(index) - },playernum, playerName, playerScore, playerIcon, playerLine) - - player.Ref = {playerName, playerScore, playernum, playerIcon, playerLine, Frame = playerFrame} - end - end - end - - gui.Events.OnResized(ScoreResize) - ScoreResize() - return scoreboard -end - -function LandingPage(bg) - -end - -function StartGame(bg) - local qframe = bg:newFrame(0, 0, 0, 0, .2, .05, .75, .9) - qframe.color = color.new("#060ee9") - local scoreboard = ScoreBoard(bg, 0, 0, 0, 0, .015, .05, .170, .9) - board.buildBoard(qframe, "anime") end function love.update(dt) diff --git a/menus/board.lua b/menus/board.lua new file mode 100644 index 0000000..bc28b33 --- /dev/null +++ b/menus/board.lua @@ -0,0 +1,592 @@ +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 menu = require("menus.menus") + +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 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 + +function Menu(frame, path) + local qframe = frame:newFrame(0, 0, 0, 0, .2, .05, .75, .9) + qframe.color = color.new("#060ee9") + local scoreboard = ScoreBoard(frame, 0, 0, 0, 0, .015, .05, .170, .9) + + local data = loader:new(path) + board, question, dailydouble = qframe:newFrame(), qframe:newFrame(), qframe: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() + + return background +end + +scoreUpdater = gui:getProcessor():newProcessor("score-updater") +scoreUpdater.Start() + +function GetActivePlayer() + if not activePlayer then return end + return activePlayer.link +end + +local function GetPlayerPos() + for i,v in pairs(playerStaticList) do + if v == GetActivePlayer() then + return i + end + end +end + +function GetNextPlayer() + local pos = GetPlayerPos() + + if pos >= #playerStaticList then + activePlayer = playerStaticList[1].Ref.Frame + else + activePlayer = playerStaticList[pos + 1].Ref.Frame + end + scoreboard:RenderPlayer(playerList) + return GetActivePlayer() +end + +function love.filedropped(file) + file:open("r") + local data = file:read() + print("Load file? " .. file:getFilename()) +end + +function ScoreBoard(frame, x, y, w, h, sx, sy, sw, sh) + -- Colors + local C_BG_PANEL = color.new("#1a1a2e") + local C_BG_HEADER = color.new("#16213e") + local C_ACCENT = color.new("#e94560") + local C_ROW_TOP = color.new("#1c2641") + local C_ROW_NORM = color.new("#121226") + local C_BORDER_TOP = color.new("#323c6e") + local C_BORDER_NRM = color.new("#1c1c37") + local C_BAR_EMPTY = color.new("#232341") + local C_TEXT_MUTED = color.new("#786e96") + local C_WHITE = color.new("#ffffff") + local C_GOLD = color.new("#ffd700") + local C_SILVER = color.new("#C0C0C0") + local C_BRONZE = color.new("#cd7f32") + + -- Config stuff + local LEADER_HEIGHT_SCALE = .06 + local HEIGHT_SCALE = .03 + local PLAYER_HEIGHT = .05 + -- + + local rankColors = { C_GOLD, C_SILVER, C_BRONZE } + + local leaderboard = frame:newFrame(x, y, w, h, sx, sy, sw, sh) + leaderboard.color = C_BORDER_NRM + + local headernum = leaderboard:newTextLabel("#",0,0,0,0,0,LEADER_HEIGHT_SCALE,1/6,HEIGHT_SCALE) + headernum.align = gui.ALIGN_CENTER + + local headerplayer = leaderboard:newTextLabel("PLAYER",0,0,0,0,1/6,LEADER_HEIGHT_SCALE,3/6,HEIGHT_SCALE) + headerplayer.align = gui.ALIGN_LEFT + + local headerscore = leaderboard:newTextLabel("SCORE",0,0,0,0,4/6,LEADER_HEIGHT_SCALE,2/6,HEIGHT_SCALE) + headerscore.align = gui.ALIGN_RIGHT + + local header = leaderboard:newTextLabel("LEADERBOARD",0,0,0,0,0,0,1,LEADER_HEIGHT_SCALE) + header.borderColor = C_ACCENT + header.textColor = C_ACCENT + header.color = C_BG_HEADER + header.align = gui.ALIGN_CENTER + + local BASE_HEIGHT = LEADER_HEIGHT_SCALE + HEIGHT_SCALE + + gui.apply({ + drawBorder = false, + color = color.new("#0f2a60"), + textColor = C_TEXT_MUTED, + }, headernum, headerplayer, headerscore) + + local updateList = {header, headernum, headerplayer, headerscore} + + local function ScoreResize() + scoreUpdater:newThread(function() + thread.skip(2) + + for _,object in pairs(updateList) do + object:fitFont(nil, nil, {scale = 5/6}) + object:centerFont() + end + + for _,object in pairs(playerList) do + if type(object) == "table" and object.Ref then + for i, player in pairs(object.Ref) do + if player:hasType(gui.TYPE_TEXT) then + player:fitFont(nil, nil, {scale = 5/6}) + player:centerFont() + end + end + end + end + end) + end + + function scoreboard:AddPlayer(name, score, icon) + local player = { + Name = name, + Score = score, + Icon = icon, + UUID = multi.generate_uuid7(), + Add = function(self, amt) + self.Score = tostring(tonumber(self.Score) + amt) + table.sort(playerList, function(a, b) + if a.Score == b.Score then + return a.Name < b.Name + end + return tonumber(a.Score) > tonumber(b.Score) + end) + scoreboard:RenderPlayer(playerList) + ScoreResize() + end, + } + table.insert(playerList, player) + table.insert(playerStaticList, player) + scoreboard:RenderPlayer(playerList) + ScoreResize() + return player + end + + local colors = {C_GOLD, C_SILVER, C_BRONZE} + local function MapColor(index) + if index <=3 and index > 0 then + return colors[index] + else + return C_WHITE + end + end + + local add_player = leaderboard:newFrame(5,-5,-10,0,0,1-PLAYER_HEIGHT,1,PLAYER_HEIGHT) + local edit_player = leaderboard:newFrame(5,-10,-10,0,0,1-2*PLAYER_HEIGHT,1,PLAYER_HEIGHT) + local remove_player = leaderboard:newTextButton("Remove Selected",5,-15,-10,0,0,1-3*PLAYER_HEIGHT,1,PLAYER_HEIGHT) + remove_player:setFont(20) + remove_player.align = gui.ALIGN_CENTER + local embededWatch = {remove_player} + scoreUpdater:newThread(function() + while true do + thread.sleep(.01) + for i,v in pairs(embededWatch) do + v:centerFont() + end + end + end) + + local function embedTextEdit(reference, default, but_text, callback) + reference.color = C_BORDER_NRM + local textbox = reference:newTextBox(default,0,0,0,0,.015,.1,.8,.8) + textbox.textColor = C_GOLD + textbox.blink = false + textbox.color = C_BORDER_TOP + textbox.textColor = C_WHITE + textbox:OnPressed(function() + textbox.text = "" + end) + + local button = reference:newTextButton(but_text,5,0,-10,0,.815,.1,.185,.8) + button.color = color.new("#7eae5b") + button:OnReleased(function() + callback(textbox) + end) + gui.apply({ + setFont = {20}, + align = gui.ALIGN_CENTER + },textbox,button,reference) + table.insert(embededWatch,textbox) + table.insert(embededWatch,button) + end + + remove_player.color = color.new("#a13a3a") + remove_player:OnReleased(function() + local player = GetActivePlayer() + uuid = player.UUID + for i = 1, #playerList do + if playerList[i].UUID == uuid then + table.remove(playerList,i) + break + end + end + for i = 1, #playerStaticList do + if playerStaticList[i].UUID == uuid then + table.remove(playerStaticList,i) + break + end + end + scoreboard:RenderPlayer(playerList) + player.Ref.Frame:destroy() + end) + + embedTextEdit(add_player, "Player Name", "Add", function(self) + scoreboard:AddPlayer(self.text, "0") + end) + + embedTextEdit(edit_player, "Modify Score", "Edit", function(self) + local player = GetActivePlayer() + if player then + player.Score = self.text + scoreboard:RenderPlayer(playerList) + end + end) + + function scoreboard:RenderPlayer(list) + for index, player in ipairs(list) do + if player.Ref then + player.Ref[1].text = player.Name or "" + player.Ref[2].text = player.Score or "" + player.Ref[3].text = tostring(index) + player.Ref.Frame:setDualDim(nil,5*index,nil,nil,nil,BASE_HEIGHT + (index-1) * PLAYER_HEIGHT) + player.Ref.Frame.link = player + + gui.apply({ + visibility = 0, + drawBorder = false, + textColor = MapColor(index) + }, unpack(player.Ref)) + + if activePlayer == nil then + activePlayer = player.Ref.Frame + end + + if player.Ref.Frame == activePlayer then + player.Ref.Frame.borderColor = C_BORDER_NRM + player.Ref.Frame.color = C_ROW_NORM + else + player.Ref.Frame.borderColor = C_BORDER_TOP + player.Ref.Frame.color = C_ROW_TOP + end + else + local playernum, playerName, playerIcon, playerScore, playerLine + local playerFrame = leaderboard:newFrame(5,5*index,-10,0,0,BASE_HEIGHT + (index-1) * PLAYER_HEIGHT,1,PLAYER_HEIGHT) + + playerFrame:OnReleased(function(self) + activePlayer = self + scoreboard:RenderPlayer(playerList) + end) + + playerFrame:respectHierarchy(false) + + playerFrame.borderColor = C_BORDER_TOP + playerFrame.color = C_ROW_TOP + + playernum = playerFrame:newTextLabel(index,0,0,0,0,.015,.1,1/8,.8) + playernum.align = gui.ALIGN_CENTER + + if player.Icon ~= nil then + playerIcon = playerFrame:newImageLabel(player.Icon,0,0,0,0,.16,.1,.1,.8) + playerIcon.square = "h" -- When working with scales squaring is trickier. (h/w) to switch on width or height + + playerName = playerFrame:newTextLabel(player.Name,0,0,0,0,.3,0,2/5,.8) + playerName.align = gui.ALIGN_LEFT + + playerLine = playerFrame:newFrame(0,0,0,0,.3,.8,.69,.07) + playerLine.color = C_GOLD + else + playerName = playerFrame:newTextLabel(player.Name,0,0,0,0,.16,0,7/13,.8) + playerName.align = gui.ALIGN_LEFT + + playerLine = playerFrame:newFrame(0,0,0,0,.16,.8,7/13 + 2/7,.07) + playerLine.color = C_GOLD + end + + playerScore = playerFrame:newTextLabel(player.Score,0,0,0,0,.71,0,2/7,.8) + playerScore.align = gui.ALIGN_CENTER + playerLine.drawBorder = false + + gui.apply({ + visibility = 0, + drawBorder = false, + textColor = MapColor(index) + },playernum, playerName, playerScore, playerIcon, playerLine) + + player.Ref = {playerName, playerScore, playernum, playerIcon, playerLine, Frame = playerFrame} + end + end + end + + gui.Events.OnResized(ScoreResize) + ScoreResize() + return scoreboard +end + +return menu.registerMenu("board", Menu) \ No newline at end of file diff --git a/menus/menus.lua b/menus/menus.lua new file mode 100644 index 0000000..1969dcf --- /dev/null +++ b/menus/menus.lua @@ -0,0 +1,54 @@ +local gui = require("gui") +local menu = {} +local menus = {} +local frame +local menuRef = {} +local mt = { + __index = menuRef +} + +function menu.registerMenu(name, menu, delete) + local c = {} + c.MenuFunc = menu + c.Delete = delete + setmetatable(c, mt) + menus[name] = c + return c +end + +function menu.getMenu(name) + return menus[name] or nil, "Menu ".. name .. " not found." +end + +function menu.init(f) + frame = f +end + +function menu.hideMenus() + for k,v in pairs(menus) do + if v.frame then + if v.Delete then + v.frame:destroy() + menus[k] = nil + else + v.frame.visible = false + v.frame.active = false + v.frame:setParent(gui.virtual) + end + end + end +end + +function menuRef:visible(value, ...) + menu.hideMenus() + if self.__menuConfigured and self.frame then + self.frame.visible = true + self.frame.active = true + self.frame:setParent(frame) + else + self.__menuConfigured = true + self.frame = self.MenuFunc(frame, ...) + end +end + +return menu \ No newline at end of file diff --git a/menus/settings.lua b/menus/settings.lua new file mode 100644 index 0000000..a9d2b74 --- /dev/null +++ b/menus/settings.lua @@ -0,0 +1,9 @@ +local gui = require("gui") +local color = require("gui.core.color") +local menu = require("menus.menus") +-- local proc = gui:getProcessor():newProcessor("settings") + +function Menu(frame) + local background = frame:newImageLabel("assets/images/background.jpg") + background:fullFrame() +end \ No newline at end of file diff --git a/menus/title.lua b/menus/title.lua new file mode 100644 index 0000000..26cc84b --- /dev/null +++ b/menus/title.lua @@ -0,0 +1,72 @@ +local gui = require("gui") +local color = require("gui.core.color") +local menu = require("menus.menus") +local proc = gui:getProcessor():newProcessor("main-title") + +local board = require("menus.board") + +function Menu(frame) + local background = frame:newImageLabel("assets/images/background.jpg") + background:fullFrame() + local title = frame:newTextLabel("Jeopardy",0,0,0,0,0,.1,1,.25) + title:setFont("assets/fonts/gyparody.ttf",300) + + title:setShader(gui.SHADERS.vignette,{ + intensity = .5, + smoothness = .8 + }) + + title.align = gui.ALIGN_CENTER + title.visibility = 0 + title.textColor = color.new("#b16d24") + title.drawBorder = false + + proc:newThread(function() + local function enabled() + return background.visible + end + while true do + thread.hold(enabled) + title:centerFont() + end + end) + + local items = 0 + local MenuOption = function(str) + local item = background:newImageLabel("assets/images/CLOUD.png",-150,-63+(items*130),300,125,.5,.6) + local text = item:newTextLabel(str) + text:fullFrame() + text.align = gui.ALIGN_CENTER + thread:newThread(function() + thread.skip(2) + text:fitFont(nil,nil,{scale = 1/2}) + text:centerFont() + text.visibility = 0 + text.textColor = color.new("#24269a") + end) + items = items + 1 + return item, text + end + local play = MenuOption("PLAY") + local settings = MenuOption("SETTINGS") + local quit = MenuOption("QUIT") + + gui.apply({ + OnEnter = function(self) + self:setShader(gui.SHADERS.glow) + end, + OnExit = function(self) + self:setShader() + end, + shaderTime = {true} + },play,settings,quit) + + play:OnReleased(function() + background.visible = false + menu.getMenu("board"):visible(true,"anime") + end) + + return background +end + +return menu.registerMenu("title", Menu) \ No newline at end of file diff --git a/multi b/multi index c864133..ee5e3bd 160000 --- a/multi +++ b/multi @@ -1 +1 @@ -Subproject commit c86413351c8e2fdd3b595d3df1ba0b9ac7d1f9b1 +Subproject commit ee5e3bde7f9ea255ebcacb52e8e5333b6d3ac62a diff --git a/templates/imageword.lua b/templates/imageword.lua index fcc04a6..31fe3ae 100644 --- a/templates/imageword.lua +++ b/templates/imageword.lua @@ -26,7 +26,7 @@ local function index(window, q, callback) imageHolder:setDualDim(0, 0, 0, 0, 0, 0, .4, 1) -- Plus sign: centered between images at 0.42 to 0.58 - plusLabel = frame:newImageLabel("assets/plus.png", 0, 0, 0, 0, .4125, .4, .175) + plusLabel = frame:newImageLabel("assets/images/plus.png", 0, 0, 0, 0, .4125, .4, .175) plusLabel.square = "w" plusLabel.align = ALIGN_CENTER plusLabel.textColor = color.white diff --git a/templates/multiplechoice.lua b/templates/multiplechoice.lua index 1b85cb8..427f540 100644 --- a/templates/multiplechoice.lua +++ b/templates/multiplechoice.lua @@ -29,17 +29,17 @@ local function index(window, q, callback) function choices:newChoice(choice, i) local c = choices:newFrame(noOf(.25,(i-1)/#q.choices,.5,1/#q.choices)) c.visibility = 0 - local box = c:newImageButton("assets/unchecked.png",noOf(.024*(#q.choices/4),.1,0,.8)) + local box = c:newImageButton("assets/images/unchecked.png",noOf(.024*(#q.choices/4),.1,0,.8)) box.checked = false box:OnReleased(function() if box.checked then - box:setImage("assets/unchecked.png") + box:setImage("assets/images/unchecked.png") else for _, other in pairs(allBoxes) do other.checked = false - other:setImage("assets/unchecked.png") + other:setImage("assets/images/unchecked.png") end - box:setImage("assets/checked.png") + box:setImage("assets/images/checked.png") selected = i end box.checked = not checked diff --git a/templates/whatsound.lua b/templates/whatsound.lua index 56d42c9..d69f7e6 100644 --- a/templates/whatsound.lua +++ b/templates/whatsound.lua @@ -21,7 +21,7 @@ local function index(window, q, callback) label.align = ALIGN_CENTER label.textColor = color.white label.color = color.new("#060ce9") - imageHolder = frame:newImageButton("assets/speaker.png",0,0,0,0,0,0,.3,.4) + imageHolder = frame:newImageButton("assets/images/speaker.png",0,0,0,0,0,0,.3,.4) imageHolder:setAspectSize(imageHolder.imageWidth,imageHolder.imageHeight) imageHolder:centerX(true) imageHolder:centerY(true)