lobby.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. local love = require("compat")
  2. local panel = require( "panel" )
  3. local lobby = {
  4. camMoveTime = 5,
  5. currentMapString = nil,
  6. ready = false,
  7. --locked = false,
  8. timers = {},
  9. countdown = nil,
  10. }
  11. lobby.colors = {
  12. { 0, 0, 0 },
  13. { 25, 25, 25 },
  14. { 255, 255, 255 },
  15. { 255, 0, 0 },
  16. { 0, 255, 0 },
  17. { 0, 0, 255 },
  18. { 255, 255, 0 },
  19. { 0, 255, 255 },
  20. { 255, 0, 255 },
  21. { 128, 0, 0 },
  22. { 0, 128, 0 },
  23. { 0, 0, 128 },
  24. { 255, 128, 0 },
  25. { 128, 255, 0 },
  26. { 128, 0, 255 },
  27. { 255, 0, 128 },
  28. { 0, 255, 128 },
  29. { 0, 128, 255 },
  30. { 255, 128, 128 },
  31. { 128, 255, 128 },
  32. { 128, 128, 255 },
  33. { 255, 128, 128 },
  34. { 128, 255, 128 },
  35. { 128, 128, 255 },
  36. { 0, 128, 128 },
  37. { 128, 0, 128 },
  38. { 128, 128, 0 },
  39. { 0, 64, 128 },
  40. { 0, 128, 64 },
  41. { 64, 0, 128 },
  42. { 128, 0, 64 },
  43. { 64, 128, 0 },
  44. { 128, 64, 0 },
  45. }
  46. local scr
  47. -- The first level in the list to display:
  48. local levelListStart = 1
  49. local levelNameList = nil
  50. local ERROR_MSG = nil
  51. local ERROR_TIMER = 0
  52. function lobby:init()
  53. scr = ui:newScreen( "lobby" )
  54. scr:addPanel( "topPanel",
  55. 0, 0,
  56. love.graphics.getWidth(), 35 )
  57. scr:addFunction( "topPanel", "close", 20, 0, "Leave", "q", lobby.close )
  58. scr:addFunction( "topPanel", "stats", 3*love.graphics.getWidth()/4-40, 0, "Toggle Statistics", "t",
  59. function() lobby:toggleStats() end )
  60. scr:addFunction( "topPanel", "ready", love.graphics.getWidth() - 100, 0, "Ready", "r",
  61. function() lobby:toggleReady() end )
  62. self.userPanel = panel:new( 0, 0, 300, 18 )
  63. end
  64. function lobby:show()
  65. print("Starting lobby")
  66. STATE = "Lobby"
  67. --self.currentMapString = nil
  68. --self.locked = false
  69. self.timers = {}
  70. self.countdown = nil
  71. if server then
  72. for k, u in pairs( server:getUsers() ) do
  73. server:setUserValue( u, "ready", false )
  74. server:setUserValue( u, "ingame", false )
  75. end
  76. if DEDICATED then
  77. --[[local t = Timer:new( 5, function() dedicated:chooseMap() end )
  78. table.insert( self.timers, t )
  79. dedicated:postMatchLock()]]
  80. dedicated:chooseMap()
  81. end
  82. updateAdvertisementInfo()
  83. end
  84. if client then
  85. ui:setActiveScreen( scr )
  86. self.ready = false
  87. -- In case I was a server before, remove the server settings:
  88. if levelNameList ~= nil then
  89. scr:removeFunction( "topPanel", "start" )
  90. scr:removeList( levelNameList.name )
  91. levelNameList = nil
  92. end
  93. chat:show()
  94. if map.loaded then
  95. map:zoomOut()
  96. end
  97. ERROR_TIMER = 0
  98. end
  99. -- If I'm the server, then let me choose the map:
  100. if server and client then
  101. self:createLevelList()
  102. levelListStart = 1
  103. scr:addFunction( "topPanel", "start", love.graphics.getWidth()/2 - 20, 0, "Start", "s",
  104. function() lobby:attemptGameStart() end )
  105. end
  106. end
  107. function lobby:update( dt )
  108. map:update( dt )
  109. if map.loaded then
  110. self.camMoveTime = self.camMoveTime + dt
  111. if self.camMoveTime > 4 then
  112. local x = math.random( map.Boundary.minX, map.Boundary.maxX )
  113. local y = math.random( map.Boundary.minY, map.Boundary.maxY )
  114. map:camSwingToPos( x, y, 2 )
  115. map:camZoom( 0.05 + math.random( 0, 1 )*0.05, 2)
  116. self.camMoveTime = self.camMoveTime - 4
  117. end
  118. end
  119. -- Continue any timers:
  120. for k, t in ipairs( self.timers ) do
  121. -- if the timer fires, delete it:
  122. if t:updateAndFire( dt ) then
  123. table.remove( self.timers, k )
  124. end
  125. end
  126. if self.countdown then
  127. self.countdown = self.countdown - dt
  128. if self.countdown < 5 and not self.sentCountdownTimes[math.ceil(self.countdown)] then
  129. if math.ceil(self.countdown) > 0 then
  130. server:send( CMD.SERVERCHAT, math.ceil(self.countdown))
  131. self.sentCountdownTimes[math.ceil(self.countdown)] = true
  132. end
  133. end
  134. end
  135. if ERROR_TIMER > 0 then
  136. ERROR_TIMER = ERROR_TIMER - dt
  137. end
  138. if client then
  139. stats:update( dt )
  140. end
  141. end
  142. function lobby:draw()
  143. map:draw()
  144. lobby:drawUserList()
  145. if ERROR_TIMER > 0 then
  146. ERROR_PANEL:draw()
  147. love.graphics.setColor( 255,128,0, 200 )
  148. love.graphics.printf( ERROR_MSG, 60, love.graphics.getHeight() - 65,
  149. love.graphics.getWidth() - 120, "center" )
  150. end
  151. if client then
  152. stats:draw()
  153. end
  154. end
  155. function lobby:drawUserList()
  156. -- Print list of users:
  157. love.graphics.setColor( 255,255,255, 255 )
  158. local users, num = network:getUsers()
  159. local x, y = 20, 60
  160. local i = 1
  161. if client and users then
  162. --love.graphics.setColor( 0, 0, 0, 128 )
  163. --love.graphics.rectangle( "fill", x - 5, y - 5, 300, num*20 + 5 )
  164. for k, u in pairs( users ) do
  165. self.userPanel:draw( x - 5, y - 3 )
  166. love.graphics.setColor( 255,255,255, 255 )
  167. love.graphics.printf( i .. ":", x, y, 20, "right" )
  168. love.graphics.printf( u.playerName, x + 25, y, 250, "left" )
  169. if u.customData.ready == true then
  170. love.graphics.setColor( 128, 255, 128, 255 )
  171. local dx = love.graphics.getFont():getWidth( u.playerName ) + 30
  172. love.graphics.print( "[Ready]", x + dx, y )
  173. end
  174. y = y + 20
  175. i = i + 1
  176. end
  177. end
  178. end
  179. function lobby:keypressed( key )
  180. end
  181. function lobby:mousepressed( button, x, y )
  182. end
  183. function lobby:close()
  184. network:closeConnection()
  185. end
  186. function lobby:createLevelList()
  187. local levelnames = love.filesystem.getDirectoryItems( "maps" )
  188. if levelNameList then
  189. scr:removeList( levelNameList.name )
  190. end
  191. local list = {}
  192. --[[if levelListStart > 1 then
  193. local entry = { txt="Up", event=function() lobby:moveLevelList( -1 ) end, key = "u" }
  194. table.insert( list, entry )
  195. end]]
  196. -- Display up to 8 items in the list:
  197. for i = 1, #levelnames do
  198. if levelnames[i]:match("(.*).stl") or levelnames[i]:match("(.*).STL") then
  199. local entry = {
  200. txt=levelnames[i],
  201. event=function() self:chooseMap( levelnames[i] ) end,
  202. }
  203. table.insert( list, entry )
  204. end
  205. end
  206. --[[if levelListStart + 7 < #levelnames then
  207. local entry = { txt="Down", event=function() lobby:moveLevelList( 1 ) end, key = "d" }
  208. table.insert( list, entry )
  209. end]]
  210. levelNameList = scr:newList( love.graphics.getWidth() - 300, 60, 160, list, 9 )
  211. end
  212. function lobby:chooseMap( levelname )
  213. -- SERVER ONLY!
  214. if not server then return end
  215. print("Loading map: " .. levelname )
  216. self.currentMapString = nil
  217. -- Mapstring is the map, in serialized form:
  218. local mapstring
  219. if love then
  220. mapstring = love.filesystem.read( "maps/" .. levelname )
  221. else
  222. local f = io.open( "maps/" .. levelname, "r")
  223. if f then
  224. mapstring = f:read("*all")
  225. f:close()
  226. end
  227. end
  228. map:setName( levelname )
  229. map:newFromString(mapstring)
  230. if map.loaded then
  231. -- Remember for later:
  232. self.currentMapString = mapstring
  233. self:sendMap()
  234. print("\t->loaded!" )
  235. updateAdvertisementInfo()
  236. end
  237. end
  238. function lobby:sendMap( user )
  239. -- SERVER ONLY!
  240. if not server then return end
  241. if self.currentMapString then
  242. -- Remove linebreaks and replace by pipe symbol for sending.
  243. --mapstring = self.currentMapString:gsub( "\n", "|" )
  244. if user then
  245. -- Send to single user?
  246. server:send( CMD.MAP, self.currentMapString, user )
  247. server:send( CMD.LAPS, tostring(LAPS), user )
  248. else
  249. -- Broadcast to all:
  250. server:send( CMD.MAP, self.currentMapString )
  251. server:send( CMD.LAPS, tostring(LAPS) )
  252. end
  253. end
  254. end
  255. function lobby:receiveMap( mapstring )
  256. -- CLIENT ONLY!
  257. if not client then return end
  258. if server then return end
  259. -- Re-add line breaks (fallback for earlier versions:)
  260. if not mapstring:find( "\n" ) then
  261. mapstring = mapstring:gsub( "|", "\n" )
  262. end
  263. map:newFromString( mapstring )
  264. end
  265. function lobby:receiveLaps( laps )
  266. laps = tonumber(laps)
  267. game.numberOfLaps = laps
  268. end
  269. function lobby:toggleReady()
  270. if client then
  271. local ready = client:getUserValue( "ready" )
  272. client:setUserValue( "ready", not ready )
  273. end
  274. end
  275. function lobby:toggleStats()
  276. if stats.display then
  277. stats:hide()
  278. else
  279. stats:show()
  280. end
  281. end
  282. function lobby:attemptGameStart()
  283. -- SERVER ONLY!
  284. if not server then return end
  285. if not map.loaded then
  286. if not DEDICATED then
  287. local commands = {}
  288. commands[1] = { txt = "Ok", key = "y" }
  289. scr:newMsgBox( "Cannot start:", "No valid map file loaded.", nil, nil, nil, commands)
  290. end
  291. return false
  292. end
  293. local allReady = true
  294. local usersReady = 0
  295. local users = network:getUsers()
  296. for k, u in pairs( users ) do
  297. if u.customData.ready then
  298. usersReady = usersReady + 1
  299. else
  300. allReady = false
  301. end
  302. end
  303. -- ON DEDICATED ONLY!
  304. if DEDICATED and not allReady then
  305. if usersReady == 0 then
  306. if self.countdown then
  307. self.countdown = nil
  308. server:send( CMD.SERVERCHAT, "Server halted countdown. No one is ready." )
  309. end
  310. else
  311. if self.countdown == nil then
  312. self.countdown = COUNTDOWN
  313. self.sentCountdownTimes = {}
  314. server:send( CMD.SERVERCHAT, "Round starts in " .. COUNTDOWN .. " seconds. Get ready to play! ('R' to join this round.)" )
  315. end
  316. end
  317. end
  318. -- If all clients are ready, then they must also all be synchronized.
  319. -- So we're ok to start.
  320. if allReady or (self.countdown and self.countdown <= 0 ) then
  321. lobby:startGame()
  322. return true
  323. elseif not DEDICATED then
  324. if usersReady >= 1 then
  325. local commands = {}
  326. commands[1] = { txt = "Yes", key = "y", event = function() lobby:startGame() end }
  327. commands[2] = { txt = "No, wait", key = "n" }
  328. scr:newMsgBox( "Are you sure you want to start?", "Some users aren't ready yet. They won't be part of the round if you start now.", nil, nil, nil, commands)
  329. else
  330. local commands = {}
  331. commands[1] = { txt = "Ok", key = "y" }
  332. scr:newMsgBox( "Cannot start:", "No one is ready yet.", nil, nil, nil, commands)
  333. end
  334. end
  335. return false
  336. end
  337. function lobby:startGame()
  338. --self.locked = true -- don't let any more users join!
  339. server:send( CMD.START_GAME )
  340. --lobby:kickAllWhoArentReady()
  341. self.countdown = nil
  342. for k, u in pairs( server:getUsers() ) do
  343. if u.customData.ready then
  344. server:setUserValue( u, "ingame", true ) -- everyone else is spectating!
  345. end
  346. end
  347. end
  348. function lobby:authorize( user, authorizationRequest )
  349. --[[if self.locked then
  350. return false, "Game already started."
  351. else
  352. return true
  353. end]]
  354. -- Let only users running the correct version authorize:
  355. if authorizationRequest == VERSION then
  356. return true
  357. else
  358. return false, "Version mismatch. You're running version " .. authorizationRequest ..
  359. ", server version is " .. VERSION .. "."
  360. end
  361. end
  362. function lobby:setUserColor( user )
  363. -- SERVER ONLY!
  364. if server then
  365. math.randomseed( os.time() )
  366. local col = lobby.colors[ math.random(#lobby.colors) ]
  367. server:setUserValue( user, "red", col[1] )
  368. server:setUserValue( user, "green", col[2] )
  369. server:setUserValue( user, "blue", col[3] )
  370. server:setUserValue( user, "body", math.random(NUM_CAR_IMAGES) )
  371. end
  372. end
  373. function lobby:errorMsg( msg )
  374. local commands = {}
  375. commands[1] = { txt = "Ok", key = "y" }
  376. scr:newMsgBox( "Error loading map:", msg, nil, nil, nil, commands)
  377. end
  378. function lobby:kickAllWhoArentReady()
  379. if server then
  380. local users = network:getUsers()
  381. for k, u in pairs( users ) do
  382. if not u.customData.ready then
  383. server:kickUser( u, "Game started, and you were not ready." )
  384. end
  385. end
  386. end
  387. end
  388. function lobby:newWarning( msg )
  389. ERROR_MSG = msg
  390. ERROR_TIMER = 10
  391. if DEDICATED then
  392. return
  393. end
  394. ERROR_PANEL = panel:new( 60, love.graphics.getHeight() - 70,
  395. love.graphics.getWidth() - 120, love.graphics.getFont():getHeight()*2 + 10 )
  396. end
  397. return lobby