json = require "json" local common = require('common.common'); local difbar = require("components.diff_rectangle"); local spinnybg = require('components.background'); local msg = game.GetSkinSetting("MSG"); local normname = game.GetSkinSetting("username") local creww = game.GetSkinSetting("single_idol") local m_jacket = gfx.CreateSkinImage("multi/lobby/multi_jacket.png", 1); local m_base_panel = gfx.CreateSkinImage("multi/lobby/multi_base_panel.png", 1); local m_anim = gfx.CreateSkinImage("multi/lobby/panel_laser.png", 1); local m_panel = gfx.CreateSkinImage("multi/lobby/matching_panel.png", 1); local m_s_panel = gfx.CreateSkinImage("multi/lobby/song_panel.png", 1); local m_host_panel = gfx.CreateSkinImage("multi/lobby/user_panel.png", 1); local m_bpm_panel = gfx.CreateSkinImage("multi/lobby/lane_speed_panel.png", 1); local m_info_panel = gfx.CreateSkinImage("multi/lobby/button_panel.png", 1); local headerMatchingImage = gfx.CreateSkinImage("titlescreen/entry.png", 1); local m_4pb_panels = gfx.CreateSkinImage("multi/lobby/user_panel_2.png", 1); local ready_bt = gfx.CreateSkinImage("multi/lobby/READY.png", 1); local bg = gfx.CreateSkinImage("multi/lobby/bg.png", 1); local bg_graid1 = gfx.CreateSkinImage("multi/lobby/gradient_bottom.png", 1); local bg_graid2 = gfx.CreateSkinImage("multi/lobby/gradient_top.png", 1); local idolAnimation = gfx.LoadSkinAnimation('crew/anim/'..creww, 1 / 30, 0, true); local idolAnimTransitionScale = 0; local resX,resY = game.GetResolution() local mposx = 0; local mposy = 0; local hovered = nil; local buttonWidth = resX*(3/4); local buttonHeight = 75; local buttonBorder = 2; local portrait local jacket_size; local BAR_ALPHA = 191; local HEADER_HEIGHT = 100 local resx, resy = game.GetResolution() local desw = 1080 local desh = 1920 local scale; game.LoadSkinSample("click-02") game.LoadSkinSample("click-01") game.LoadSkinSample("menu_click") local loading = true; local rooms = {}; local lobby_users = {}; local selected_room = nil; local user_id = nil; local jacket = 0; local all_ready; local user_ready; local go; local hard_mode = false; local rotate_host = false; local start_game_soon = false; local host = nil; local owner = nil; local missing_song = false; local placeholderJacket = gfx.CreateSkinImage("song_select/loading.png", 0) local did_exit = false; local irHeartbeatRequested = false; local irText = '' local grades = { {["max"] = 6900000, ["image"] = gfx.CreateSkinImage("common/grades/D.png", 0)}, {["max"] = 7900000, ["image"] = gfx.CreateSkinImage("common/grades/C.png", 0)}, {["max"] = 8600000, ["image"] = gfx.CreateSkinImage("common/grades/B.png", 0)}, {["max"] = 8900000, ["image"] = gfx.CreateSkinImage("common/grades/A.png", 0)}, {["max"] = 9200000, ["image"] = gfx.CreateSkinImage("common/grades/A+.png", 0)}, {["max"] = 9400000, ["image"] = gfx.CreateSkinImage("common/grades/AA.png", 0)}, {["max"] = 9600000, ["image"] = gfx.CreateSkinImage("common/grades/AA+.png", 0)}, {["max"] = 9700000, ["image"] = gfx.CreateSkinImage("common/grades/AAA.png", 0)}, {["max"] = 9800000, ["image"] = gfx.CreateSkinImage("common/grades/AAA+.png", 0)}, {["max"] = 9900000, ["image"] = gfx.CreateSkinImage("common/grades/S.png", 0)} } local badges = { gfx.CreateSkinImage("badges/played.png", 0), gfx.CreateSkinImage("badges/clear.png", 0), gfx.CreateSkinImage("badges/hard-clear.png", 0), gfx.CreateSkinImage("badges/full-combo.png", 0), gfx.CreateSkinImage("badges/perfect.png", 0) } local user_name_key = game.GetSkinSetting('multi.user_name_key') if user_name_key == nil then user_name_key = 'nick' end local name = game.GetSkinSetting(user_name_key) if name == nil or name == '' then name = 'Guest' end local normal_font = game.GetSkinSetting('multi.normal_font') if normal_font == nil then normal_font = 'NotoSans-Regular.ttf' end local mono_font = game.GetSkinSetting('multi.mono_font') if mono_font == nil then mono_font = 'NovaMono.ttf' end local SERVER = game.GetSkinSetting("multi.server") local drawIdol = function(deltaTime) local idolAnimTickRes = gfx.TickAnimation(idolAnimation, deltaTime); if idolAnimTickRes == 1 then gfx.GlobalAlpha(idolAnimTransitionScale); idolAnimTransitionScale = idolAnimTransitionScale + 1 / 60; if (idolAnimTransitionScale > 1) then idolAnimTransitionScale = 1; end gfx.ImageRect(0, 0, resX,resY, idolAnimation, 1, 0); gfx.GlobalAlpha(1); end end songjacket = function() -- self explanatory if portrait then split = resX jacket_size = math.min(resX/4, resY/4); song_x_off = 0; else split = resX/2 jacket_size = math.min(resX/2, resY/2); song_x_off = 1/2*resX; end local jw , jh = gfx.ImageSize(m_jacket); gfx.BeginPath(); gfx.ImageRect(333, 1284, jw/1.18, jh/1.18, m_jacket,1,0); end m_own_info = function() local x = 0 local y = 1310 gfx.BeginPath(); gfx.FontSize(40) gfx.ImageRect(x, y, 343/1.18, 361/1.18,m_host_panel,1,0) gfx.Text(normname, x+20, y+78) gfx.FontSize(22) gfx.Text(irText, x+5, y+288); end m_base_part = function() -- just the images which slide up local jw , jh = gfx.ImageSize(m_base_panel); gfx.BeginPath(); gfx.ImageRect(1, 1250, jw/1.17, jh/1.18, m_base_panel,1,0); gfx.BeginPath(); gfx.ImageRect(1, 1250, jw/1.17, jh/1.18, m_anim,1,0); gfx.LoadSkinFont('Digital-Serial-Bold.ttf') gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_LEFT) gfx.FontSize(24) end m_part = function() -- room name part local jw , jh = gfx.ImageSize(m_panel); gfx.BeginPath(); gfx.ImageRect(429, 1142, jw/1.175, jh/1.18, m_panel,1,0); gfx.FontSize(32); gfx.LoadSkinFont('Digital-Serial-Bold.ttf') gfx.TextAlign(gfx.TEXT_ALIGN_LEFT, gfx.TEXT_ALIGN_MIDDLE) gfx.Text(selected_room.name, 575, 1179) end m_s_part = function () -- song info panel local jw , jh = gfx.ImageSize(m_s_panel); gfx.BeginPath(); gfx.ImageRect(283, 1187, jw/1.175, jh/1.18, m_s_panel,1,0); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_LEFT); gfx.FillColor(255,255,255) gfx.FontSize(32) if selected_song == nil then if host == user_id then gfx.Text("NO SONG", 535, 1236) gfx.Text("NO ARIST", 535, 1275) gfx.FontSize(24) gfx.Text("NO EFFECTOR", 746, 1378) gfx.Text("NO ILLUSTRATOR", 746, 1406) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(22) gfx.Text("BPM ?",780, 1307) else if missing_song then gfx.Text("MISSING SONG!!!!", 535, 1235) gfx.Text("MISSING ARIST!!!!", 535, 1275) gfx.FontSize(24) gfx.Text("MISSING EFFECTOR!!!!", 744, 1378) gfx.Text("MISSING ILLUSTRATOR!!!!", 744, 1406) else gfx.Text("HOST IS SELECTING SONG", 535, 1235) gfx.Text(" ", 535, 1275) gfx.FontSize(24) gfx.Text(" ", 746, 1378) gfx.Text(" ", 746, 1406) gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(22) gfx.Text("BPM ?",780, 1307) end end else if selected_song.min_bpm ~= selected_song.max_bpm then gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(22); gfx.Text("BPM",777, 1307) gfx.FontSize(26); gfx.Text(string.format("%.0f - %.0f", selected_song.min_bpm, selected_song.max_bpm), 780 + 77, 1307) else gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(22); gfx.Text("BPM",777, 1307) gfx.Text(string.format("%.0f", selected_song.min_bpm), 780 + 77, 1307) end gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_LEFT); gfx.FontSize(32); gfx.Text(selected_song.title, 535, 1236) gfx.Text(selected_song.artist, 535, 1275) gfx.FontSize(24) gfx.Text(selected_song.effector, 746, 1377) gfx.Text(selected_song.illustrator, 746, 1406) end end m_bpm_part = function () -- bpm and lane speed local jw , jh = gfx.ImageSize(m_bpm_panel); gfx.BeginPath(); gfx.ImageRect(0, 1692, jw/1.18, jh/1.18, m_bpm_panel,1,0); gfx.FontSize(32) if selected_song == nil then if host == user_id then gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(32) gfx.Text("BPM ?",76, 1788) gfx.Text("LANE-SPEED ?",76, 1832) else if missing_song then gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(32) gfx.Text("BPM ?",76, 1788) gfx.Text("LANE-SPEED ?",76, 1832) else gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.FontSize(32) gfx.Text("BPM ?",76, 1788) gfx.Text("LANE-SPEED ?",76, 1832) end end end if selected_song ~= nil then gfx.FillColor(255,255,255) gfx.FontSize(32); gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) if selected_song.min_bpm ~= selected_song.max_bpm then gfx.Text("BPM",76, 1788) gfx.Text(string.format("%.0f - %.0f", selected_song.min_bpm, selected_song.max_bpm), 76 + 75, 1788) gfx.Text("LANE-SPEED",76, 1832) gfx.Text(string.format("%.2f = %.0f", selected_song.hispeed, selected_song.speed_bpm * selected_song.hispeed), 76 + 175, 1832) else gfx.FontSize(32); gfx.Text("BPM",76, 1788) gfx.Text(string.format("%.0f", selected_song.min_bpm), 76 + 75, 1788) gfx.Text("LANE-SPEED",76, 1832) gfx.Text(string.format("%.2f = %.0f", selected_song.hispeed, selected_song.speed_bpm * selected_song.hispeed), 76 + 175, 1832) end end end m_info_part = function () -- the info panel local jw , jh = gfx.ImageSize(m_info_panel); gfx.BeginPath(); gfx.ImageRect(475, 1590, jw/1.18, jh/1.18, m_info_panel,1,0); local check = 770 draw_checkbox("Excessive", check - 200, 1625, toggle_hard, hard_mode, not start_game_soon) draw_checkbox("Mirror Mode",check - 15, 1625, toggle_mirror, mirror_mode, not start_game_soon) draw_checkbox("Rotate Host",check + 175, 1625, toggle_rotate, do_rotate, (owner == user_id or host == user_id) and not start_game_soon) for i, user in ipairs(lobby_users) do buttonY = 1775 local side_button_off = 0 if owner == user_id and user.id ~= user_id then draw_button("K",525+side_button_off, buttonY, 50, function() kick_user(user); end) side_button_off = 60; end if (owner == user_id or host == user_id) and user.id ~= host then draw_button("H",525+side_button_off, buttonY+85, 50, function() change_host(user); end) end end end user_setup = function () -- (semi new) user layering local distance = 350 for i, user in ipairs(lobby_users) do if i == 1 then draw_user(user, -distance, 1142-360/1.2, 420/1.2, 330/1.2, i,215,245.5) elseif i == 2 then draw_user(user, 16, 1142-360/1.2, 420/1.2, 330/1.2, i,215,245.5) elseif i == 3 then draw_user(user, 16+distance, 1142-360/1.2, 420/1.2, 330/1.2, i,215,245.5) elseif i == 4 then draw_user(user, 16+distance+distance, 1142-360/1.2, 420/1.2, 330/1.2, i,215,245.5) elseif i > 4 then draw_user(user, 16+distance+distance+distance+distance, 1142-360/1.2, 420/1.2, 330/1.2, i,215,245.5) end end end function drawHeader() gfx.BeginPath(); gfx.FillColor(0, 0, 0, BAR_ALPHA); gfx.Rect(0, 0, desw, HEADER_HEIGHT); gfx.Fill(); gfx.ClosePath() gfx.ImageRect(desw/2 - 200, HEADER_HEIGHT/2 - 20, 400, 40, headerMatchingImage, 1, 0) end mouse_clipped = function(x,y,w,h) return mposx > x and mposy > y and mposx < x+w and mposy < y+h; end; draw_room = function(name, x, y, selected, hoverindex) local buttonWidth = resX*(3/4); local rx = x - (buttonWidth / 2); local ty = y - (buttonHeight / 2); local roomButtonBorder = buttonBorder; gfx.BeginPath(); gfx.FillColor(0,128,255); if selected then gfx.FillColor(0,255,0); roomButtonBorder = 4; end if mouse_clipped(rx,ty, buttonWidth, buttonHeight) then hovered = hoverindex; gfx.FillColor(255,128,0); end gfx.Rect(rx - roomButtonBorder, ty - roomButtonBorder, buttonWidth + (roomButtonBorder * 2), buttonHeight + (roomButtonBorder * 2)); gfx.Fill(); gfx.BeginPath(); gfx.FillColor(40,40,40); gfx.Rect(rx, ty, buttonWidth, buttonHeight); gfx.Fill(); gfx.BeginPath(); gfx.FillColor(255,255,255); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.FontSize(40); gfx.Text(name, x, y); end; draw_button = function(name, x, y, buttonWidth, hoverindex) draw_button_color(name, x, y, buttonWidth, hoverindex, 40,40,40, 0,128,255) end draw_button_color = function(name, x, y, buttonWidth, hoverindex,r,g,b, olr,olg,olb) local rx = x - (buttonWidth / 2); local ty = y - (buttonHeight / 2); gfx.BeginPath(); gfx.FillColor(olr, olg, olb); if mouse_clipped(rx,ty, buttonWidth, buttonHeight) then hovered = hoverindex; gfx.FillColor(255,128,0); end gfx.Rect(rx - buttonBorder, ty - buttonBorder, buttonWidth + (buttonBorder * 2), buttonHeight + (buttonBorder * 2)); gfx.Fill(); gfx.BeginPath(); gfx.FillColor(r,g,b); gfx.Rect(rx, ty, buttonWidth, buttonHeight); gfx.Fill(); gfx.BeginPath(); gfx.FillColor(255,255,255); gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.FontSize(40); gfx.Text(name, x, y); end; draw_checkbox = function(text, x, y, hoverindex, current, can_click) gfx.BeginPath(); if can_click then gfx.FillColor(255,255,255); else gfx.FillColor(150,100,100); end gfx.TextAlign(gfx.TEXT_ALIGN_CENTER + gfx.TEXT_ALIGN_MIDDLE); gfx.FontSize(35); gfx.FillColor(201,0,0); gfx.Text(text, x, y) local xmin,ymin,xmax,ymax = gfx.TextBounds(x, y, text); local sx = xmin - 40; local sy = y - 15; if can_click and mouse_clipped(xmin-10, ymin, xmax-xmin, ymax-ymin) then hovered = hoverindex; end if current then -- Draw checkmark gfx.BeginPath(); gfx.FillColor(0, 236, 0); gfx.Text(text, x, y) gfx.Fill(); end end; --look into user changing -- IMPORTANT !!! draw_user = function(user, x, y , w, h, rank, breadx,bready) local name = user.name local showthing = false if user.id == user_id then name = name end if user.id == host then name = name elseif user.ready then showthing = true elseif not user.ready then showthing = false end gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_MIDDLE); gfx.FillColor(255,255,255) gfx.FontSize(42) gfx.FontSize(30) gfx.BeginPath(); gfx.ImageRect(x, y ,w, h,m_4pb_panels,1,0) gfx.Text(name, x+135, y+51) if showthing == true then local jw , jh = gfx.ImageSize(ready_bt); gfx.BeginPath(); gfx.ImageRect(x+breadx, y+bready, jw/1.18, jh/1.18, ready_bt,1,0); elseif showthing == false then local jw , jh = gfx.ImageSize(ready_bt); gfx.BeginPath(); gfx.ImageRect(x+breadx, y+bready, jw/1.18, jh/1.18, ready_bt,0,0); end end; function render_loading() if not loading then return end gfx.Save() gfx.ResetTransform() gfx.BeginPath() gfx.MoveTo(resX, resY) gfx.LineTo(resX - 350, resY) gfx.LineTo(resX - 300, resY - 50) gfx.LineTo(resX, resY - 50) gfx.ClosePath() gfx.FillColor(33,33,33) gfx.Fill() gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_RIGHT, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("LOADING...", resX - 20, resY - 3) gfx.Restore() end function render_info() if searchStatus then gfx.BeginPath() gfx.FillColor(255,255,255) gfx.FontSize(20); gfx.TextAlign(gfx.TEXT_ALIGN_LEFT + gfx.TEXT_ALIGN_TOP) gfx.Text(searchStatus, 3, 3) end end draw_diff_icon = function(diff, x, y, w, h) difbar.render(deltaTime, x, y, 1, diff.difficulty, diff.level); end local doffset = 0; local timer = 0; local possy = 1095; local possx = 150; draw_diffs = function(diffs, x, y, w, h, selectedDiff) local diffWidth = w/2 local diffHeight = w/2 local diffCount = #diffs gfx.Scissor(x+84 + possx,y + possy,w/2.451,h) for i = math.max(selectedDiff - 2, 1), math.max(selectedDiff - 1,1) do local diff = diffs[i] local xpos = x + ((w/2 - diffWidth/2) + (selectedDiff - i + doffset)*(-0.8*diffWidth)) if i ~= selectedDiff then draw_diff_icon(diff, xpos + possx, y + possy, diffWidth, diffHeight, false) end end --after selected for i = math.min(selectedDiff + 2, diffCount), selectedDiff + 1,-1 do local diff = diffs[i] local xpos = x + ((w/2 - diffWidth/2) + (selectedDiff - i + doffset)*(-0.8*diffWidth)) if i ~= selectedDiff then draw_diff_icon(diff, xpos + possx, y + possy, diffWidth, diffHeight, false) end end local diff = diffs[selectedDiff] local xpos = x + ((w/2 - diffWidth/2) + (doffset)*(-0.8*diffWidth)) draw_diff_icon(diff, xpos + possx, y + possy, diffWidth, diffHeight, true) gfx.ResetScissor() end set_diff = function(oldDiff, newDiff) game.PlaySample("click-02") doffset = doffset + oldDiff - newDiff end; local selected_room_index = 1; local ioffset = 0; function draw_rooms(y, h) if #rooms == 0 then return end local num_rooms_visible = math.floor(h / (buttonHeight + 10)) local first_half_rooms = math.floor(num_rooms_visible/2) local second_half_rooms = math.ceil(num_rooms_visible/2) - 1 local start_offset = math.max(selected_room_index - first_half_rooms, 1); local end_offset = math.min(selected_room_index + second_half_rooms + 2, #rooms); local start_index_offset = 1; -- If our selection is near the start or end we have to offset if selected_room_index <= first_half_rooms then start_index_offset = 0; end_offset = math.min(#rooms, num_rooms_visible + 1) end if selected_room_index >= #rooms - second_half_rooms then start_offset = math.max(1, #rooms - num_rooms_visible) end_offset = #rooms end for i = start_offset, end_offset do local room = rooms[i]; -- if selected room < halfvis then we start at 1 -- if sel > #rooms - halfvis then we start at -halfvis local offset_index = (start_offset + first_half_rooms) - i + start_index_offset local offsetY = (offset_index + ioffset) * (buttonHeight + 10); local ypos = y + (h/2) - offsetY; local status = room.current..'/'..room.max if room.ingame then status = status..' (IN MATCH)' end if room.password then status = status..'
' end draw_room(room.name .. ': '.. status, resX / 2, ypos, i == selected_room_index, function() join_room(room) end) end end change_selected_room = function(off) local new_index = selected_room_index + off; --selected_room_index = 2; if new_index < 1 or new_index > #rooms then return; end local h = resY - 290; local num_rooms_visible = math.floor(h / (buttonHeight + 10)) local first_half_rooms = math.floor(num_rooms_visible/2) local second_half_rooms = math.ceil(num_rooms_visible/2) - 1 if off > 0 and (selected_room_index < first_half_rooms or selected_room_index >= #rooms - second_half_rooms - 1) then elseif off < 0 and (selected_room_index <= first_half_rooms or selected_room_index >= #rooms - second_half_rooms) then else ioffset = ioffset - new_index + selected_room_index; end game.PlaySample("menu_click") selected_room_index = new_index; end local IR_HeartbeatResponse = function(res) if res.statusCode == IRData.States.Success then irText = res.body.serverName .. ' ' .. res.body.irVersion; else game.Log("Can't connect to IR!", game.LOGGER_WARNING) end end local IR_Handle = function() if not irHeartbeatRequested then IR.Heartbeat(IR_HeartbeatResponse) irHeartbeatRequested = true; end end function render_lobby(deltaTime) local jw , jh = gfx.ImageSize(bg); gfx.BeginPath(); gfx.ImageRect(0, 0, resX, resY, bg,1,0); drawIdol(deltaTime) gfx.BeginPath(); gfx.ImageRect(0, 0, resX, resY, bg_graid1,1,0); gfx.ImageRect(0, 0, resX, resY, bg_graid2,1,0); drawHeader() gfx.BeginPath(); m_base_part() m_own_info() user_setup() m_info_part() m_part() m_s_part() m_bpm_part() songjacket() if selected_song == nil then if jacket == 0 then jacket = placeholderJacket end else draw_diffs(selected_song.all_difficulties, split/2 + song_x_off - 150, 200, 300, 100, selected_song.diff_index+1) if selected_song.jacket == nil or selected_song.jacket == placeholderJacket then selected_song.jacket = gfx.LoadImageJob(selected_song.jacketPath, placeholderJacket) jacket = selected_song.jacket end end gfx.Save() gfx.BeginPath() gfx.Translate(481, 1303+jacket_size/2) gfx.ImageRect(-jacket_size/2,-jacket_size/2,jacket_size,jacket_size,jacket,1,0) if mouse_clipped(481-jacket_size/2,1423+-jacket_size/2,jacket_size,jacket_size) and host == user_id then hovered = function() missing_song = false mpScreen.SelectSong() end end gfx.Restore() end function render_room_list(deltaTime) spinnybg.draw(deltaTime); draw_rooms(175, resY - 290); -- Draw cover for rooms out of view gfx.BeginPath() gfx.FillColor(20, 20, 20) gfx.Rect(0, 0, resX, 145) gfx.Rect(0, resY-170, resX, 170) gfx.Fill() gfx.BeginPath() gfx.FillColor(60, 60, 60) gfx.Rect(0, 145, resX, 2) gfx.Rect(0, resY-170-2, resX, 2) gfx.Fill() gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("Multiplayer Rooms", resX/2, 100) if not loading then draw_button("Create new room", resX/2, resY-40-buttonHeight, resX*(3/4), new_room); end end passwordErrorOffset = 0; function render_password_screen(deltaTime) gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("Joining "..selected_room.name.."...", resX/2, resY/4) gfx.FillColor(50,50,50) gfx.BeginPath() gfx.Rect(0, resY/2-10, resX, 40) gfx.Fill(); gfx.FillColor(255,255,255) gfx.Text("Please enter room password:", resX/2, resY/2-40) gfx.Text(string.rep("*",#textInput.text), resX/2, resY/2+40) if passwordError then gfx.FillColor(255,50,50) gfx.FontSize(60 + math.floor(passwordErrorOffset*20)) gfx.Text("Invalid password", resX/2, resY/2+80) end draw_button("Join", resX/2, resY*3/4, resX/2, mpScreen.JoinWithPassword); end function render_new_room_password(delta_time) gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("Create New Room", resX/2, resY/4) gfx.FillColor(50,50,50) gfx.BeginPath() gfx.Rect(0, resY/2-10, resX, 40) gfx.Fill(); gfx.FillColor(255,255,255) gfx.Text("Enter room password:", resX/2, resY/2-40) gfx.Text(string.rep("*",#textInput.text), resX/2, resY/2+40) draw_button("Create Room", resX/2, resY*3/4, resX/2, mpScreen.NewRoomStep); end --here function render_new_room_name(deltaTime) gfx.BeginPath(); gfx.LoadSkinFont("segoeui.ttf") gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("Create New Room", resX/2, resY/4) gfx.Rect(0, resY/2-10, resX, 60) gfx.Text("Please enter room name:", resX/2, resY/2-40) gfx.Text(textInput.text, resX/2, resY/2+40) draw_button("Next", resX/2, resY*3/4, resX/2, mpScreen.NewRoomStep); end function render_set_username(deltaTime) gfx.FillColor(255,255,255) gfx.TextAlign(gfx.TEXT_ALIGN_CENTER, gfx.TEXT_ALIGN_BOTTOM) gfx.FontSize(70) gfx.Text("First things first...", resX/2, resY/4) gfx.FillColor(50,50,50) gfx.BeginPath() gfx.Rect(0, resY/2-10, resX, 60) gfx.Fill(); gfx.FillColor(255,255,255) gfx.Text("Enter a display name:", resX/2, resY/2-40) gfx.Text(textInput.text, resX/2, resY/2+40) draw_button("Join Multiplayer", resX/2, resY*3/4, resX/2, function() loading = true; mpScreen.SaveUsername() end); end render = function(deltaTime) resX,resY = game.GetResolution(); buttonWidth = resX*(3/4); mposx,mposy = game.GetMousePos(); portrait = resY > resX IR_Handle() common.stopMusic(); doffset = doffset * 0.9 ioffset = ioffset * 0.9 passwordErrorOffset = passwordErrorOffset * 0.9 timer = (timer + deltaTime) timer = timer % 2 hovered = nil; gfx.LoadSkinFont(normal_font); do_sounds(deltaTime); -- Room Listing View if screenState == "inRoom" then render_lobby(deltaTime); elseif screenState == "roomList" then render_room_list(deltaTime); elseif screenState == "passwordScreen" then render_password_screen(deltaTime); elseif screenState == "newRoomName" then render_new_room_name() elseif screenState == "newRoomPassword" then render_new_room_password() elseif screenState == "setUsername" then loading = false; render_set_username() end render_loading(); render_info(); end -- Ready up to play function ready_up() Tcp.SendLine(json.encode({topic="user.ready.toggle"})) end -- Toggle hard gauage function toggle_hard() Tcp.SendLine(json.encode({topic="user.hard.toggle"})) end -- Toggle hard gauage function toggle_mirror() Tcp.SendLine(json.encode({topic="user.mirror.toggle"})) end function new_room() host = user_id owner = user_id mpScreen.NewRoomStep() end -- Toggle host rotation function toggle_rotate() Tcp.SendLine(json.encode({topic="room.option.rotation.toggle"})) end -- Change lobby host function change_host(user) Tcp.SendLine(json.encode({topic="room.host.set", host=user.id})) end -- Kick user function kick_user(user) Tcp.SendLine(json.encode({topic="room.kick", id=user.id})) end -- Tell the server to start the game function start_game() selected_song.self_picked = false if (selected_song == nil) then return end if (start_game_soon) then return end Tcp.SendLine(json.encode({topic="room.game.start"})) end -- Join a given room function join_room(room) host = user_id selected_room = room; if room.password then mpScreen.JoinWithPassword(room.id) else mpScreen.JoinWithoutPassword(room.id) end end local que1 = 0 -- Handle button presses to advance the UI button_pressed = function(button) if button == game.BUTTON_FXL and game.BUTTON_FXR then if start_game_soon then return end if screenState == "roomList" then if #rooms == 0 then new_room() else -- TODO navigate room selection join_room(rooms[selected_room_index]) end elseif screenState == "inRoom" then if host == user_id then if selected_song and selected_song.self_picked then if all_ready then start_game() else missing_song = false mpScreen.SelectSong() end else missing_song = false mpScreen.SelectSong() end else ready_up() end end end if button == game.BUTTON_BTA then toggle_hard(); end if button == game.BUTTON_BTB then toggle_mirror(); end end -- Handle the escape key around the UI function key_pressed(key) if key == 27 then --escape pressed if screenState == "roomList" then did_exit = true; mpScreen.Exit(); return end -- Reset room data screenState = "roomList" -- have to update here selected_room = nil; rooms = {}; selected_song = nil selected_song_index = 1; jacket = 0; end end -- Handle mouse clicks in the UI mouse_pressed = function(button) if hovered then hovered() end return 0 end function init_tcp() Tcp.SetTopicHandler("server.info", function(data) loading = false user_id = data.userid end) -- Update the list of rooms as well as get user_id for the client Tcp.SetTopicHandler("server.rooms", function(data) rooms = {} for i, room in ipairs(data.rooms) do table.insert(rooms, room) end end) Tcp.SetTopicHandler("server.room.joined", function(data) selected_room = data.room end) local sound_time = 0; local sound_clip = nil; local sounds_left = 0; local sound_interval = 0; function repeat_sound(clip, times, interval) sound_clip = clip; sound_time = 0; sounds_left = times - 1; sound_interval = interval; game.PlaySample(clip) end function do_sounds(deltaTime) if sound_clip == nil then return end sound_time = sound_time + deltaTime; if sound_time > sound_interval then sound_time = sound_time - sound_interval; game.PlaySample(sound_clip); sounds_left = sounds_left - 1 if sounds_left <= 0 then sound_clip = nil end end end local last_song = nil -- Update the current lobby Tcp.SetTopicHandler("room.update", function(data) -- Update the users in the lobby lobby_users = {} local prev_all_ready = all_ready; all_ready = true for i, user in ipairs(data.users) do table.insert(lobby_users, user) if user.id == user_id then user_ready = user.ready end if not user.ready then all_ready = false end end if user_id == host and #data.users > 1 and all_ready and not prev_all_ready then repeat_sound("click-02", 3, .1) end if data.host == user_id and host ~= user_id then repeat_sound("click-02", 3, .1) end if data.song ~=nil and last_song ~=data.song then game.PlaySample("menu_click") last_song = data.song end host = data.host if data.owner then owner = data.owner else owner = host end hard_mode = data.hard_mode mirror_mode = data.mirror_mode do_rotate = data.do_rotate if data.start_soon and not start_game_soon then repeat_sound("click-01", 5, 1) end start_game_soon = data.start_soon end) end