-
Posts
730 -
Joined
-
Last visited
-
Days Won
2
Everything posted by koragg
-
Fixed 'dxscoreboard_client.lua' -- Double-Click nickname in scoreboard to spectate the selected player local nickName = false function scoreboardClick() for key, column in ipairs(scoreboardColumns) do if column.name == "name" then nickName = getPlayerNametagText(source) end end end addEventHandler("onClientPlayerScoreboardClick", root, scoreboardClick) function scoreboardDoubleClick() for key, column in ipairs(scoreboardColumns) do if column.name == "name" then local playerName = nickName if playerName then executeCommandHandler("spectate", playerName) outputChatBox("#FFFFFFSpectating: "..playerName, 255, 255, 255, true) end end end end function startDrawing() addEventHandler("onClientDoubleClick", root, scoreboardDoubleClick) scoreboardShowing = true end bindKey("tab", "down", startDrawing) function stopDrawing() removeEventHandler("onClientDoubleClick", root, scoreboardDoubleClick) showCursor(false) scoreboardShowing = false end bindKey("tab", "up", stopDrawing)
-
So my idea's to make it possible for players to double click a player's nickname in the scoreboard and spectate him. That would avoid a lot of arrows spam left/right to find the player who you want to spectate, also it's faster than typing /spectate nick. But I'm not sure how to actually get nickname from the row I've double clicked so that it would use that into the /spectate command. The following code is in the dxscoreboard_client.lua file. I wanna use the 'specNick' variable to save the player's name on which I've double clicked but not really sure how nor where to do it. function scoreboardDoubleClick(button) local sx, sy = guiGetScreenSize() local rowsCount = math.floor(sy / 20) - 2 if scoreboardShowing and button == "left" then local rCount = math.min(scoreboardGetSize(), rowsCount - 1) local x, y = sx / 2 - 320, sy / 2 - rCount * 20 / 2 local cx, cy = getCursorPosition() cx = cx * sx cy = cy * sy if x <= cx and cx <= x + 640 and y <= cy and cy <= y + rCount * 20 then cy = cy - y cy = math.floor(cy / 20) + 1 if isElement(specNick) then outputChatBox("Spectating "..specNick..".") executeCommandHandler("spectate", specNick) else outputChatBox("Player not found.") end end end end function startDrawing() addEventHandler("onClientDoubleClick", root, scoreboardDoubleClick) scoreboardShowing = true end bindKey("tab", "down", startDrawing) function stopDrawing() removeEventHandler("onClientDoubleClick", root, scoreboardDoubleClick) showCursor(false) scoreboardShowing = false end bindKey("tab", "up", stopDrawing)
-
Fixed it Thanks for help. I had trouble saving nick onPlayerNickChange so I just save it to account whenever a new map starts. Also I think I made country update as well (if player changed his location and joined my server, it'll display his new country in tops list even without him setting a better time). Can't really test country but I think it should work. If anyone's interested in how I did it, full file below: (I'm not using MTA's default race_toptimes though) -- maptimes_server.lua SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- SMaptimes:create() -- -- Create a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- SMaptimes:postCreate() --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" end --------------------------------------------------------------------------- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- SMaptimes:makeDatabaseTableName() --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- SMaptimes:timeMsToTimeText() --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- SMaptimes:load() --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- SMaptimes:save() --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- SMaptimes:sort() -- -- Not quick --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- SMaptimes:checkIsSorted() -- -- Debug --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = getAccountData(getAccount(self.dbTable.rows[i].playerName), "currentPlayerName"), dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = getAccountData(getAccount(self.dbTable.rows[i].playerName), "country") or "", } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:addPlayer() --------------------------------------------------------------------------- function SMaptimes:addPlayer( player ) table.insert( self.dbTable.rows, { playerName = getAccountName(getPlayerAccount(player)), playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = getAccountName(getPlayerAccount(player)) for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end return false end --------------------------------------------------------------------------- -- SMaptimes:getPositionForTime() -- -- Always succeeds --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it row.playerName = getAccountName(getPlayerAccount(player)) -- Refresh the name row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- SMaptimes:deletefirst() -- -- Remove the best time from this map --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end --------------------------------------------------------------------------- -- toptimeImprovement() -- -- Display personal best time of player --------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..") #F9CC8E[-"..SMaptimes:timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) --------------------------------------------------------------------------- -- Save the player's current nickname and current country into his account --------------------------------------------------------------------------- function onPlayerLogin() local account = getPlayerAccount(source) local countryid = exports["admin"]:getPlayerCountry(source) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(source) setAccountData(account, "currentPlayerName", playername) setAccountData(account, "country", countryid) end end addEventHandler("onPlayerLogin", root, onPlayerLogin) addEvent("onMapStarting", true) function onMapStarting() for i, player in ipairs(getElementsByType("player")) do local account = getPlayerAccount(player) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(player) setAccountData(account, "currentPlayerName", playername) end end end addEventHandler("onMapStarting", root, onMapStarting)
-
The only thing I see is that there's no event handler so it doesn't know when to execute the function. I'd advise you to make it client side and put onClientRender as the event handler. Then when the time comes it'll display the chatbox. If it still doesn't work I don't know as your code looks ok.
-
Thanks for the replies guys (I kinda forgot about this topic xD). I am playing at Race server(s) with 20-60 players. I found out that if I disable all of the (amazing) visual effects and displays I can record in FHD and play at 60-70fps all the time (even with 30+ players). I've heard that MTA uses just one core which is :~ af Guess I'll keep playing on vanilla MTA look (no custom cool, amazing windows and displays). I got used to it so it won't be a problem. Thanks for the info
-
Yea I did use those but turns out it's better if I don't save the nick anywhere and just get it when it's needed. (that way it's up to date)
-
So I made some tweaks today: - now account name gets recorded to the database - the player name is shown in the toptime list (not the account name) - this^ prevents double tops as each player has one account, if they change nickname they would not make another toptime with their new name But I still don't know how to make the nickname update each time the player associated with the account which is registered as a toptime changes his nickname :\ -- maptimes_server.lua SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- SMaptimes:create() -- -- Create a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- SMaptimes:postCreate() --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra', 'currentNick' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" row.currentNick = tostring(row.currentNick) or "currentNick" end --------------------------------------------------------------------------- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- SMaptimes:makeDatabaseTableName() --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- SMaptimes:timeMsToTimeText() --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- SMaptimes:load() --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- SMaptimes:save() --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- SMaptimes:sort() -- -- Not quick --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- SMaptimes:checkIsSorted() -- -- Debug --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = self.dbTable.rows[i].currentNick, dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = self.dbTable.rows[i].extra, } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:addPlayer() --------------------------------------------------------------------------- function SMaptimes:addPlayer( player ) table.insert( self.dbTable.rows, { playerName = getAccountName(getPlayerAccount(player)), playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", currentNick = getPlayerNameWithColor(player) } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) return #self.dbTable.rows end --------------------------------------------------------------------------- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = getAccountName(getPlayerAccount(player)) for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end return false end --------------------------------------------------------------------------- -- SMaptimes:getPositionForTime() -- -- Always succeeds --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it row.playerName = getAccountName(getPlayerAccount(player)) -- Refresh the name row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" row.currentNick = getPlayerNameWithColor(player) -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- SMaptimes:deletefirst() -- -- Remove the best time from this map --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end --------------------------------------------------------------------------- -- toptimeImprovement() -- -- Display personal best time of player --------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..SMaptimes:timeMsToTimeText(newTime)..") #F9CC8E[-"..SMaptimes:timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) PS: I removed the saving of nick to account as it was not needed actually.
-
Not sure how to make it update the name. I did as you said and it recorded account name, but then what? Isn't that what I did in my first attempt? I used 'getAccountData(account, "currentPlayerName")' instead of 'getPlayerName(player)'.
-
Hi guys, I tried to edit my race_toptimes resource so that it would always show the current nickname of the player who has done the toptime. For that I save the current player's nickname as account data onPlayerLogin and then replace "getPlayerName(player)" with that account data everywhere but it still shows the nickname with which the player has done the toptime, not his current one. For example, I make a toptime with my nick "[SiK]Megas" (color coded) and then change my name to "tester45" or something. The next time the same map starts it should show "tester45" as top1, not my old nick (in this case "[SiK]Megas"). I don't know what I'm doing wrong. Any help would be appreciated. Here's the 'maptimes_server.lua' file: -- -- maptimes_server.lua -- SMaptimes = {} SMaptimes.__index = SMaptimes SMaptimes.instances = {} --------------------------------------------------------------------------- -- -- SMaptimes:create() -- -- Create a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:create( raceModeName, mapName, statsKey ) local id = #SMaptimes.instances + 1 SMaptimes.instances[id] = setmetatable( { id = id, raceModeName = raceModeName, mapName = mapName, statsKey = statsKey, dbTable = nil, }, self ) SMaptimes.instances[id]:postCreate() return SMaptimes.instances[id] end --------------------------------------------------------------------------- -- -- SMaptimes:destroy() -- -- Destroy a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:destroy() self.dbTable:destroy() SMaptimes.instances[self.id] = nil self.id = 0 end --------------------------------------------------------------------------- -- -- SMaptimes:postCreate() -- -- -- --------------------------------------------------------------------------- function SMaptimes:postCreate() local tableName = self:makeDatabaseTableName( self.raceModeName, self.mapName ) local columns = { 'playerName', 'playerSerial', 'timeMs', 'timeText', 'dateRecorded', 'extra' } local columnTypes = { 'TEXT', 'TEXT', 'REAL', 'TEXT', 'TEXT' } self.dbTable = SDatabaseTable:create( tableName, columns, columnTypes ) end --------------------------------------------------------------------------- -- -- SMaptimes:validateDbTableRow() -- -- Make sure each cell in the row contains a valid value -- --------------------------------------------------------------------------- function SMaptimes:validateDbTableRow( index ) local row = self.dbTable.rows[index] row.playerName = tostring(row.playerName) or "playerName" row.playerSerial = tostring(row.playerSerial) or "playerSerial" row.timeMs = tonumber(row.timeMs) or 0 row.timeText = tostring(row.timeText) or "00:00:000" row.dateRecorded = tostring(row.dateRecorded) or "1900-00-00 00:00:00" row.extra = tostring(row.extra) or "" end --------------------------------------------------------------------------- -- -- SMaptimes:flush() -- -- Destroy a SMaptimes instance -- --------------------------------------------------------------------------- function SMaptimes:flush() outputDebug( 'TOPTIMES', 'SMaptimes:flush()') self:save() end --------------------------------------------------------------------------- -- -- SMaptimes:makeDatabaseTableName() -- -- -- --------------------------------------------------------------------------- function SMaptimes:makeDatabaseTableName( raceModeName, mapName ) return 'race maptimes ' .. raceModeName .. ' ' .. mapName end --------------------------------------------------------------------------- -- -- SMaptimes:timeMsToTimeText() -- -- -- --------------------------------------------------------------------------- function SMaptimes:timeMsToTimeText( timeMs ) local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end --------------------------------------------------------------------------- -- -- SMaptimes:load() -- -- -- --------------------------------------------------------------------------- function SMaptimes:load() self.dbTable:load() -- Make sure each cell in the table contains a valid value - saves lots of checks later for i,row in ipairs(self.dbTable.rows) do self:validateDbTableRow( i ) end self:sort() end --------------------------------------------------------------------------- -- -- SMaptimes:save() -- -- -- --------------------------------------------------------------------------- function SMaptimes:save() self.dbTable:save() end --------------------------------------------------------------------------- -- -- SMaptimes:sort() -- -- Not quick -- --------------------------------------------------------------------------- function SMaptimes:sort() self:checkIsSorted('Presort') table.sort(self.dbTable.rows, function(a, b) return a.timeMs < b.timeMs or ( a.timeMs == b.timeMs and a.dateRecorded < b.dateRecorded ) end ) self:checkIsSorted('Postsort') end --------------------------------------------------------------------------- -- -- SMaptimes:checkIsSorted() -- -- Debug -- --------------------------------------------------------------------------- function SMaptimes:checkIsSorted(msg) for i=2,#self.dbTable.rows do local prevTime = self.dbTable.rows[i-1].timeMs local time = self.dbTable.rows[i].timeMs if prevTime > time then outputWarning( 'Maptimes sort error: ' .. msg .. ' timeMs order error at ' .. i ) end if prevTime == time then prevDate = self.dbTable.rows[i-1].dateRecorded date = self.dbTable.rows[i].dateRecorded if prevDate > date then outputWarning( 'Maptimes sort error: ' .. msg .. ' dateRecorded order error at ' .. i ) end end end end --------------------------------------------------------------------------- -- -- SMaptimes:getToptimes() -- -- Return a table of the top 'n' toptimes -- --------------------------------------------------------------------------- function SMaptimes:getToptimes( howMany ) if _DEBUG_CHECK then self:checkIsSorted('getToptimes') end local result = {} for i=1,howMany do if i <= #self.dbTable.rows then result[i] = { timeText = self.dbTable.rows[i].timeText, playerName = self.dbTable.rows[i].playerName, dateRecorded = self.dbTable.rows[i].dateRecorded, playerCountry = self.dbTable.rows[i].extra, } else result[i] = { timeText = '', playerName = '-- Empty --', dateRecorded = '', playerCountry = '', } end end return result end --------------------------------------------------------------------------- -- -- SMaptimes:getValidEntryCount() -- -- Return a count of the number of toptimes -- --------------------------------------------------------------------------- function SMaptimes:getValidEntryCount() return #self.dbTable.rows end --------------------------------------------------------------------------- -- -- SMaptimes:addPlayer() -- -- -- --------------------------------------------------------------------------- function SMaptimes:addPlayer(player) local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") table.insert( self.dbTable.rows, { playerName = currentPlayerName, playerSerial = getPlayerSerial(player), timeMs = 0, timeText = '00:00:000', dateRecorded = '1900-00-00 00:00:00', extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "", } ) -- Make sure new row has valid values self:validateDbTableRow( #self.dbTable.rows ) end return #self.dbTable.rows end --------------------------------------------------------------------------- -- -- SMaptimes:getIndexForPlayer() -- -- Can return false if player has no entry -- --------------------------------------------------------------------------- function SMaptimes:getIndexForPlayer( player ) if isGuestAccount (getPlayerAccount(player)) then return end local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") if self.statsKey == 'serial' then -- Find player by serial local serial = getPlayerSerial(player) for i,row in ipairs(self.dbTable.rows) do if serial == row.playerSerial then return i end end else -- Find player by name local name = currentPlayerName for i,row in ipairs(self.dbTable.rows) do if name == row.playerName then return i end end end end return false end --------------------------------------------------------------------------- -- -- SMaptimes:getPositionForTime() -- -- Always succeeds -- --------------------------------------------------------------------------- function SMaptimes:getPositionForTime( time, dateRecorded ) for i,row in ipairs(self.dbTable.rows) do if time < row.timeMs then return i end if time == row.timeMs and dateRecorded < row.dateRecorded then return i end end return #self.dbTable.rows + 1 end --------------------------------------------------------------------------- -- -- SMaptimes:getTimeForPlayer() -- -- Can return false if player has no entry -- --------------------------------------------------------------------------- function SMaptimes:getTimeForPlayer( player ) local i = self:getIndexForPlayer( player ) if not i then return false end return self.dbTable.rows[i].timeMs end --------------------------------------------------------------------------- -- -- SMaptimes:setTimeForPlayer() -- -- Update the time for this player -- --------------------------------------------------------------------------- function SMaptimes:setTimeForPlayer( player, time, dateRecorded ) if isGuestAccount (getPlayerAccount(player)) then outputChatBox("You need to register and login in order to do toptimes!", player, 255,153,0) return end -- Find current entry for player local oldIndex = self:getIndexForPlayer( player ) if not oldIndex then -- No entry yet, so add it to the end oldIndex = self:addPlayer( player ) if oldIndex ~= self:getIndexForPlayer( player ) then outputError( "oldIndex ~= self:getIndexForPlayer( player )" ) end end -- Copy it out and then remove it from the table local row = self.dbTable.rows[oldIndex] table.remove( self.dbTable.rows, oldIndex ) -- Update it local account = getPlayerAccount(player) if account and not isGuestAccount(account) then local currentPlayerName = getAccountData(account, "currentPlayerName") row.playerName = currentPlayerName row.timeMs = time row.timeText = self:timeMsToTimeText(time) row.dateRecorded = dateRecorded row.extra = call(getResourceFromName("admin"),"getPlayerCountry",player) or "" -- Put it back in at the correct position to maintain sort order local newIndex = self:getPositionForTime( row.timeMs, row.dateRecorded ) table.insert( self.dbTable.rows, newIndex, row ) end if _DEBUG_CHECK then self:checkIsSorted('setTimeForPlayer') end end --------------------------------------------------------------------------- -- -- SMaptimes:deletefirst() -- -- Remove the best time from this map -- --------------------------------------------------------------------------- function SMaptimes:deletetime(place) place = tonumber(place) or 1 -- Although the list should be sorted already, make sure self:sort() -- Remove the first row if #self.dbTable.rows >= place then -- Copy it out and then remove it from the table local row = self.dbTable.rows[place] table.remove( self.dbTable.rows, place ) return row end return false end ------------------------------------------------------------------------------------------------------------------------- addEvent('onPlayerToptimeImprovement') function toptimeImprovement(newPos, newTime, oldPos, oldTime, displayTopCount, getValidEntryCount ) if not oldPos then outputChatBox ( "#96CC9EYou got a new personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9E#F3ECDC#"..newPos.." #F9CB8D("..timeMsToTimeText(newTime)..")" , source, 255, 165, 0, true) else outputChatBox ( "#96CC9EYou improved your personal time on this map:", source, 255, 165, 0, true) outputChatBox ( "#96CC9EFrom #F3ECDC#"..oldPos.." #F9CB8D("..timeMsToTimeText(oldTime)..") #96CC9Eto #F3ECDC#"..newPos.." #F9CB8D("..timeMsToTimeText(newTime)..") #F9CC8E[-"..timeMsToTimeText(oldTime - newTime).."]", source, 255, 165, 0, true) end end addEventHandler("onPlayerToptimeImprovement", root, toptimeImprovement) ------------------------------------------------------------------------------------------------------------------------- function timeMsToTimeText(timeMs) if timeMs then local minutes = math.floor( timeMs / 60000 ) timeMs = timeMs - minutes * 60000; local seconds = math.floor( timeMs / 1000 ) local ms = timeMs - seconds * 1000; return string.format( '%02d:%02d:%03d', minutes, seconds, ms ); end end ------------------------------------------------------------------------------------------------------------------------- function onPlayerLogin() local account = getPlayerAccount(source) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(source) setAccountData(account, "currentPlayerName", playername) end end addEventHandler("onPlayerLogin", root, onPlayerLogin) ------------------------------------------------------------------------------------------------------------------------- --[[addEvent("onMapStarting", true) function onMapStarting() for i, player in ipairs(getElementsByType('player')) do local account = getPlayerAccount(player) if(account and isGuestAccount(account)==false) then local playername = getPlayerNameWithColor(player) setAccountData(account, "currentPlayerName", playername) end end end addEventHandler("onMapStarting", root, onMapStarting)--]] And I want to make it work only with MTA's accounts system, no SQL or anything like that. Thanks to anyone that helps!
-
Yea I forgot Rockstar aren't mod/dev friendly
-
This isn't really a scripting question but maybe it is, idk. Are GTA SA single player races (from missions) in the same format as MTA maps? If so, can someone tell where I can find the files as I want to make all SP races into MTA maps (as of now only 1 or 2 are made). Or the only way to do that is to open a video and put cps/spawns manually in the editor?
-
To save it to the player's account: https://wiki.multitheftauto.com/wiki/SetAccountData To get it from the player's account: https://wiki.multitheftauto.com/wiki/GetAccountData To update cash window if it's client side: https://wiki.multitheftauto.com/wiki/SetElementData https://wiki.multitheftauto.com/wiki/GetElementData
-
Would my CPU be any problem regarding that aspect? It's an AMD Phenom II x4 965 3.4 GHZ 4-Core from 2009 or 2010 (model).
-
Hey guys, currently I got an AMD Radeon HD 6900 from 2010 but I wanna upgrade sometime. So I watched some reviews on youtube but I still wanted to ask here in case anyone has the card. Can I play MTA with constant 100 FPS and at same time record 1080p videos with bandicam (or shadowplay) ? The 1050 Ti is cheap and I think it's really good but I'd mostly be using it for MTA as I want to record my gameplay and also race in 100 fps or close to it. Please share your thoughts about it
-
I don't wanna know, I wanna just edit small stuff for my server That's why I hate compiled stuff.
-
Yea which earns the resource 0/5 rating even if it's working and all that. Why do they release it when it's compiled ...
-
You can use https://wiki.multitheftauto.com/wiki/GetElementPosition to get current player's position every frame with onClientRender and compare that to the door's position (again use same way to get door positron). For the comparing you'll need a function called getDistanceBetweenPoints3D. Good luck There are examples in wiki to help you. And if the player is in the specified interval, allow the player to use the command which opens the door.
-
The wiki batch converter works just fine (didn't try yours yet). Thanks a bunch mate
-
Here it is: https://www.dropbox.com/s/r7l1ppexwbwd7m1/zole-SimpleWay.zip?dl=0 There are a lot of others too though.
-
Thanks. But once converted it gave me only this line 50 times or so <vehicle model="522" posX="2137.0805660" posY="1369.4199220" posZ="10.3322530" rotX="0.0000000" rotY="0.0000000" rotZ="270.0000000" color="-1,-1" /> Where are spawnpoints and other stuff?
-
That doesn't work for maps, just server resources. Tried it now but nothing appeared in console.
-
I've got a lot of maps made a very long time ago, like very long ago. Old format: meta.xml <meta> <info type="map" gamemodes="race" name="Simple Way" author="zole" /> <race src="Simple way.map" /> </meta> .map <map mod="race" version="1.0"> <meta/> <options> <respawn>timelimit</respawn> <respawntime>3</respawntime> <duration>600</duration> </options> <spawnpoint name="spawnpoint"> <position>2137.080566 1369.419922 10.332253</position> <rotation>270</rotation> <vehicle>522</vehicle> </spawnpoint> <spawnpoint name="spawnpoint (1)"> <position>2136.681641 1370.788208 10.332253</position> <rotation>270</rotation> <vehicle>522</vehicle> </spawnpoint> ... New format: meta.xml <meta> <info gamemodes="race" type="map" name="Abberley" author="MSD" version="1.0.2" description="Race"></info> <map src="Abberley.map" dimension="0"></map> <settings> <setting name="#skins" value='[ "cj" ]'></setting> <setting name="#maxplayers" value="[ 32 ]"></setting> <setting name="#gamespeed" value="[ 1.050000 ]"></setting> <setting name="#ghostmode" value='[ "false" ]'></setting> <setting name="#time" value="20:0"></setting> <setting name="#vehicleweapons" value='[ "false" ]'></setting> <setting name="#minplayers" value="[ 0 ]"></setting> <setting name="#weather" value="[ 13 ]"></setting> <setting name="#gravity" value="[ 0.008000 ]"></setting> <setting name="#waveheight" value="[ 0 ]"></setting> <setting name="#respawntime" value="[ 5 ]"></setting> <setting name="#locked_time" value="[ false ]"></setting> <setting name="#duration" value="[ 1800 ]"></setting> <setting name="#respawn" value='[ "timelimit" ]'></setting> </settings> </meta> .map <map edf:definitions="race,editor_main"> <object id="object (ggbrig_04_sfw) (1)" doublesided="false" model="9696" interior="0" dimension="0" background="" border="" foreground="" framesFaded="0" framesToFade="0" height="15" posX="6741.1650390625" posY="-1783.2001953125" posZ="97.465599060059" rotX="0" rotY="0" rotZ="0" state="hidden" text="XYZ rotation" width="65" x="0" y="0"></object> <object id="object (ggbrig_01_sfw) (1)" doublesided="false" model="9694" interior="0" dimension="0" posX="6740.9462890625" posY="-1892.37890625" posZ="101.89939880371" rotX="0" rotY="0" rotZ="177.99450683594"></object> <object id="object (ggbrig_02_sfw) (1)" doublesided="false" model="9685" interior="0" dimension="0" posX="6741.169921875" posY="-1957.484375" posZ="122.78949737549" rotX="0" rotY="0" rotZ="0"></object> <object id="object (ggbrig_01_sfw) (2)" doublesided="false" model="9694" interior="0" dimension="0" posX="6740.8896484375" posY="-1892.3837890625" posZ="101.8675994873" rotX="0" rotY="0" rotZ="0"></object> <object id="object (ggbrig_01_sfw) (3)" doublesided="false" model="9694" interior="0" dimension="0" background="" border="" foreground="" framesFaded="0" framesToFade="0" height="15" posX="6740.9462890625" posY="-2025.82421875" posZ="101.89939880371" rotX="0" rotY="0" rotZ="179.99450683594" state="hidden" text="MTA dimension" width="85" x="0" y="0"></object> <object id="object (ggbrig_01_sfw) (4)" doublesided="false" model="9694" interior="0" dimension="0" posX="6740.8896484375" posY="-2025.8211669922" posZ="101.8675994873" rotX="0" rotY="0" rotZ="0"></object> <object id="object (vgsSairportland18) (1)" doublesided="false" model="8355" interior="0" dimension="0" posX="6718.94140625" posY="-2079.9411621094" posZ="64.584602355957" rotX="0" rotY="0" rotZ="338"></object> <object id="object (vgsSairportland18) (2)" doublesided="false" model="8355" interior="0" dimension="0" posX="6694.1528320313" posY="-2186.5822753906" posZ="64.581596374512" rotX="0" rotY="0" rotZ="359.99987792969"></object> <object id="object (vgsSairportland18) (3)" doublesided="false" model="8355" interior="0" dimension="0" posX="6743.3134765625" posY="-2275.4157714844" posZ="64.556602478027" rotX="0" rotY="0" rotZ="57.994506835938"></object> <object id="object (vgsSairportland18) (5)" doublesided="false" model="8355" interior="0" dimension="0" posX="6842.8330078125" posY="-2337.6157226563" posZ="64.531600952148" rotX="0" rotY="0" rotZ="57.991333007813"></object> ... How can I convert maps from the Old format to the New format? Some scripts like my distance to next checkpoint and the race_progressbar don't work for maps in the Old format. Also I need to make some Old format maps have ghostmode off but can't unless I somehow convert them to the New format first. I just hope it's possible
-
function JetPack(thePlayer) if doesPedHaveJetPack(thePlayer) then -- Check if the player already has a jetpack. removePedJetPack(thePlayer) -- Remove the jetpack if he has one. return -- And stop the function here. end -- Otherwise, give him a jetpack if he is an admin. local accName = getAccountName(getPlayerAccount(thePlayer)) -- Get his account name. if isObjectInACLGroup("user."..accName, aclGetGroup("Admin")) then -- Check if he's admin. if not doesPedHaveJetPack(thePlayer) then -- If the player doesn't have a jetpack, give it. givePedJetPack(thePlayer) -- Give him a jetpack. end end end addCommandHandler("jp", JetPack) -- Bind the key here for every player as soon as he joins your server. function bindKeys() bindKey(source, "j", "down", JetPack) addEventHandler("onPlayerJoin", root, bindKeys)