2018-07-02 08:18:25 +00:00
local storage = minetest.get_mod_storage ( )
2018-07-10 19:44:26 +00:00
local prev_match_summary = storage : get_string ( " prev_match_summary " )
2018-07-02 08:18:25 +00:00
2018-11-12 21:31:05 +00:00
-- Formspec element that governs table columns and their attributes
local tablecolumns = {
" tablecolumns[color; " ,
" text; " ,
" text,width=20; " ,
" text,width=4; " ,
" text,width=4; " ,
" text,width=4; " ,
" text,width=6; " ,
" text,width=6; " ,
" text,width=6; " ,
" text,width=6] "
}
tablecolumns = table.concat ( tablecolumns , " " )
local function render_team_stats ( red , blue , stat , round )
2018-04-18 20:49:47 +00:00
local red_stat , blue_stat = red [ stat ] , blue [ stat ]
if round then
2018-11-12 21:31:05 +00:00
red_stat = math.floor ( red_stat * 10 ) / 10
blue_stat = math.floor ( blue_stat * 10 ) / 10
2018-04-18 20:49:47 +00:00
end
2018-11-12 21:31:05 +00:00
return red_stat + blue_stat .. " ( " ..
minetest.colorize ( red.color , tostring ( red_stat ) ) .. " - " ..
minetest.colorize ( blue.color , tostring ( blue_stat ) ) .. " ) "
2018-04-18 20:49:47 +00:00
end
function ctf_stats . get_formspec_match_summary ( stats , winner_team , winner_player , time )
2015-12-03 23:46:52 +00:00
local players = { }
2018-04-18 20:49:47 +00:00
local red = {
color = ctf.flag_colors . red : gsub ( " 0x " , " # " ) ,
kills = 0 ,
attempts = 0 ,
score = 0 ,
}
local blue = {
color = ctf.flag_colors . blue : gsub ( " 0x " , " # " ) ,
kills = 0 ,
attempts = 0 ,
score = 0 ,
}
2015-12-03 23:46:52 +00:00
for name , pstat in pairs ( stats.red ) do
pstat.name = name
pstat.color = ctf.flag_colors . red
table.insert ( players , pstat )
2018-04-18 20:49:47 +00:00
red.kills = red.kills + pstat.kills
red.attempts = red.attempts + pstat.attempts
red.score = red.score + pstat.score
2015-12-03 23:46:52 +00:00
end
for name , pstat in pairs ( stats.blue ) do
pstat.name = name
pstat.color = ctf.flag_colors . blue
table.insert ( players , pstat )
2018-04-18 20:49:47 +00:00
blue.kills = blue.kills + pstat.kills
blue.attempts = blue.attempts + pstat.attempts
blue.score = blue.score + pstat.score
end
2018-08-03 17:51:25 +00:00
local match_length = string.format ( " %02d:%02d:%02d " ,
2018-11-12 21:31:05 +00:00
math.floor ( time / 3600 ) , -- hours
math.floor ( ( time % 3600 ) / 60 ) , -- minutes
math.floor ( time % 60 ) ) -- seconds
2018-08-03 17:51:25 +00:00
2018-04-18 20:49:47 +00:00
local ret = ctf_stats.get_formspec ( " Match Summary " , players , 1 )
if stats [ winner_team ] then
local winner_color = ctf.flag_colors [ winner_team ] : gsub ( " 0x " , " # " )
ret = ret .. " item_image[0,0;1,1;ctf_flag:flag_top_ " .. winner_team .. " ] "
2018-11-12 21:31:05 +00:00
ret = ret .. " label[1,0; " .. minetest.colorize ( winner_color ,
" TEAM " .. winner_team : upper ( ) .. " WON! " ) .. " ] "
ret = ret .. " label[1,0.5;Flag captured by " ..
minetest.colorize ( winner_color , winner_player ) .. " ] "
2018-04-18 20:49:47 +00:00
else
ret = ret .. " label[1,0;NO WINNER] "
2015-12-03 23:46:52 +00:00
end
2018-04-18 20:49:47 +00:00
2018-08-03 17:51:25 +00:00
ret = ret .. " label[6.5,0;Kills] "
2018-11-12 21:31:05 +00:00
ret = ret .. " label[8,0; " .. render_team_stats ( red , blue , " kills " ) .. " ] "
2018-08-03 17:51:25 +00:00
ret = ret .. " label[6.5,0.5;Attempts] "
2018-11-12 21:31:05 +00:00
ret = ret .. " label[8,0.5; " .. render_team_stats ( red , blue , " attempts " ) .. " ] "
ret = ret .. " label[10.5,0;Duration] "
ret = ret .. " label[12,0; " .. match_length .. " ] "
ret = ret .. " label[10.5,0.5;Total score] "
ret = ret .. " label[12,0.5; " .. render_team_stats ( red , blue , " score " , true ) .. " ] "
ret = ret .. " label[2,7.75;Tip: type /rankings for league tables] "
2018-07-02 08:18:25 +00:00
-- Set prev_match_summary and write to mod_storage
prev_match_summary = ret
2018-07-10 19:44:26 +00:00
storage : set_string ( " prev_match_summary " , ret )
2018-07-02 08:18:25 +00:00
2015-12-03 23:46:52 +00:00
return ret
end
2018-08-03 11:38:34 +00:00
function ctf_stats . get_formspec ( title , players , header , hlt_name )
2015-12-03 23:46:52 +00:00
table.sort ( players , function ( one , two )
2015-12-04 11:14:22 +00:00
return one.score > two.score
2015-12-03 23:46:52 +00:00
end )
2018-11-12 21:31:05 +00:00
local ret = " size[14, " .. 7 + header .. " ] "
2018-04-18 22:02:55 +00:00
ret = ret .. default.gui_bg .. default.gui_bg_img
2018-08-03 11:38:34 +00:00
ret = ret .. " container[0, " .. header .. " ] "
2018-04-18 20:49:47 +00:00
2018-11-12 21:31:05 +00:00
ret = ret .. " vertlabel[0,1; " .. title .. " ] "
ret = ret .. tablecolumns
2015-12-03 23:46:52 +00:00
ret = ret .. " tableoptions[highlight=#00000000] "
2018-11-12 21:31:05 +00:00
ret = ret .. " table[0.5,0;13.25,6.1;scores; "
ret = ret .. " #ffffff,,Player,Kills,Deaths,K/D,Bounty Kills,Captures,Attempts,Score "
2015-12-03 23:46:52 +00:00
2018-08-03 11:38:34 +00:00
local player_in_top_50 = false
for i = 1 , math.min ( # players , 50 ) do
2015-12-04 11:14:22 +00:00
local pstat = players [ i ]
2018-08-03 11:38:34 +00:00
local color
if hlt_name and pstat.name == hlt_name then
color = " #ffff00 "
player_in_top_50 = true
else
color = pstat.color or " #ffffff "
end
2015-12-18 02:14:33 +00:00
local kd = pstat.kills
2018-08-03 11:38:34 +00:00
if pstat.deaths > 1 then
2015-12-18 02:14:33 +00:00
kd = kd / pstat.deaths
end
2015-12-03 23:46:52 +00:00
ret = ret ..
2018-08-03 11:38:34 +00:00
" , " .. string.gsub ( color , " 0x " , " # " ) ..
" , " .. i ..
" , " .. pstat.name ..
" , " .. pstat.kills ..
" , " .. pstat.deaths ..
" , " .. math.floor ( kd * 10 ) / 10 ..
" , " .. pstat.bounty_kills ..
" , " .. pstat.captures ..
" , " .. pstat.attempts ..
" , " .. math.floor ( pstat.score * 10 ) / 10
end
2018-11-12 21:31:05 +00:00
ret = ret .. " ;-1] "
2018-08-03 11:38:34 +00:00
2018-11-12 21:31:05 +00:00
-- If hlt_name not in top 50, add a separate table
-- This would result in the player's score displayed at the bottom
-- of the list but yet be visible without having to scroll
2018-08-03 11:38:34 +00:00
if hlt_name and not player_in_top_50 then
local hlt_player , hlt_rank , hlt_kd
for i = 1 , # players do
if players [ i ] . name == hlt_name then
hlt_player = players [ i ]
hlt_rank = i
break
end
2015-12-03 23:57:28 +00:00
end
2018-08-03 11:38:34 +00:00
2018-11-12 21:31:05 +00:00
if hlt_player then
hlt_kd = hlt_player.kills
if hlt_player.deaths > 1 then
hlt_kd = hlt_kd / hlt_player.deaths
end
ret = ret .. tablecolumns
ret = ret .. " tableoptions[highlight=#00000000] "
ret = ret .. " table[0.5,6.1;13.25,0.4;hlt_score; "
ret = ret .. " #ffff00 " ..
" , " .. hlt_rank ..
" , " .. hlt_player.name ..
" , " .. hlt_player.kills ..
" , " .. hlt_player.deaths ..
" , " .. math.floor ( hlt_kd * 10 ) / 10 ..
" , " .. hlt_player.bounty_kills ..
" , " .. hlt_player.captures ..
" , " .. hlt_player.attempts ..
" , " .. math.floor ( hlt_player.score * 10 ) / 10 .. " ;-1] "
2018-08-03 11:38:34 +00:00
end
2018-11-12 21:31:05 +00:00
-- else
-- ret = ret .. "box[0.5,6.1;13.25,0.4;#101010]"
-- Adds a box where the extra table should be, in order to make it
-- appear as an extension of the main table, but the color can't be
-- matched, and looks slightly brighter or slightly darker than the table
2015-12-03 23:46:52 +00:00
end
2018-11-12 21:31:05 +00:00
ret = ret .. " button_exit[10,6.5;3,1;close;Close] "
2018-04-18 20:49:47 +00:00
ret = ret .. " container_end[] "
2015-12-03 23:46:52 +00:00
return ret
end
2015-12-03 23:57:28 +00:00
2015-12-07 23:49:58 +00:00
function ctf_stats . get_html ( title , players )
2017-10-12 23:09:25 +00:00
table.sort ( players , function ( one , two )
return one.score > two.score
end )
2015-12-07 23:49:58 +00:00
local ret = " <h1> " .. title .. " </h1> "
ret = ret .. " <table> " ..
" <tr><th></th> " ..
2018-08-03 11:38:34 +00:00
" <th>Player</th> " ..
" <th>Kills</th> " ..
" <th>Deaths</th> " ..
2015-12-07 23:49:58 +00:00
" <th>K/D ratio</th> " ..
2018-08-03 11:38:34 +00:00
" <th>Bounty kills</th> " ..
" <th>Captures</th> " ..
" <th>Attempts</th> " ..
" <th>Score</th></tr> "
2015-12-07 23:49:58 +00:00
2018-08-03 11:38:34 +00:00
for i = 1 , math.min ( # players , 50 ) do
2015-12-07 23:49:58 +00:00
local pstat = players [ i ]
2015-12-18 02:14:33 +00:00
local kd = pstat.kills
2018-08-03 11:38:34 +00:00
if pstat.deaths > 1 then
2015-12-18 02:14:33 +00:00
kd = kd / pstat.deaths
end
2015-12-07 23:49:58 +00:00
ret = ret ..
" <tr><td> " .. i ..
" </td><td> " .. pstat.name ..
" </td><td> " .. pstat.kills ..
" </td><td> " .. pstat.deaths ..
2018-08-03 11:38:34 +00:00
" </td><td> " .. math.floor ( kd * 10 ) / 10 ..
" </td><td> " .. pstat.bounty_kills ..
2015-12-07 23:49:58 +00:00
" </td><td> " .. pstat.captures ..
" </td><td> " .. pstat.attempts ..
" </td><td> " .. math.floor ( pstat.score * 10 ) / 10 .. " </td></tr> "
end
ret = ret .. " </table> \n "
return ret
end
function ctf_stats . html_to_file ( filepath )
local players = { }
for name , pstat in pairs ( ctf_stats.players ) do
pstat.name = name
pstat.color = nil
table.insert ( players , pstat )
end
local html = ctf_stats.get_html ( " Player Rankings " , players )
local f = io.open ( filepath , " w " )
f : write ( " <!doctype html> \n " )
f : write ( " <html><head> \n " )
f : write ( " <meta charset= \" utf-8 \" > \n " )
f : write ( " <title>Player Rankings</title> \n " )
f : write ( " <link rel= \" stylesheet \" href= \" score_style.css \" > \n " )
f : write ( " </head><body> \n " )
f : write ( html )
f : write ( " </body></html> \n " )
f : close ( )
end
2015-12-03 23:57:28 +00:00
2018-01-26 23:51:36 +00:00
local function send_as_chat_result ( to , name )
2018-01-03 00:23:38 +00:00
local players = { }
for pname , pstat in pairs ( ctf_stats.players ) do
pstat.name = pname
pstat.color = nil
table.insert ( players , pstat )
end
table.sort ( players , function ( one , two )
return one.score > two.score
end )
local place = - 1
local me = nil
for i = 1 , # players do
local pstat = players [ i ]
if pstat.name == name then
me = pstat
place = i
break
end
end
if place < 1 then
place = # players + 1
end
2018-11-12 15:15:55 +00:00
local you_are_in = ( to == name ) and " You are in " or name .. " is in "
local result = you_are_in .. place .. " place. \n "
2018-01-03 00:23:38 +00:00
if me then
local kd = me.kills
2018-08-03 11:38:34 +00:00
if me.deaths > 1 then
2018-01-03 00:23:38 +00:00
kd = kd / me.deaths
end
2018-01-26 23:51:36 +00:00
result = result .. " Kills: " .. me.kills ..
2018-01-03 00:23:38 +00:00
" | Deaths: " .. me.deaths ..
2018-08-03 11:38:34 +00:00
" | K/D: " .. math.floor ( kd * 10 ) / 10 ..
2018-11-12 15:15:55 +00:00
" \n Bounty kills: " .. me.bounty_kills ..
2018-01-03 00:23:38 +00:00
" | Captures: " .. me.captures ..
" | Attempts: " .. me.attempts ..
2018-11-12 15:15:55 +00:00
" \n Score: " .. math.floor ( me.score )
2018-01-03 00:23:38 +00:00
end
2018-01-26 23:51:36 +00:00
return true , result
2018-01-03 00:23:38 +00:00
end
2018-11-12 15:17:17 +00:00
minetest.register_chatcommand ( " r " , {
description = " Display your rankings as a chat result. " ,
func = function ( name , param )
return send_as_chat_result ( name , name )
end
} )
2015-12-03 23:57:28 +00:00
minetest.register_chatcommand ( " rankings " , {
2018-10-17 21:25:40 +00:00
params = " [<name>] " ,
description = " Display rankings of yourself or another player. " ,
2015-12-18 02:14:33 +00:00
func = function ( name , param )
2018-08-03 11:38:34 +00:00
local target
if param ~= " " then
param = param : trim ( )
if ctf_stats.players [ param ] then
target = param
2018-01-03 00:23:38 +00:00
else
2018-08-03 11:38:34 +00:00
return false , " Can't find player ' " .. param .. " ' "
2015-12-18 02:14:33 +00:00
end
2018-08-03 11:38:34 +00:00
else
target = name
end
if not minetest.get_player_by_name ( name ) then
2018-11-12 15:15:55 +00:00
return send_as_chat_result ( name , target )
2015-12-18 02:14:33 +00:00
else
local players = { }
for pname , pstat in pairs ( ctf_stats.players ) do
pstat.name = pname
pstat.color = nil
table.insert ( players , pstat )
end
2018-08-03 11:38:34 +00:00
local fs = ctf_stats.get_formspec ( " Player Rankings " , players , 0 , target )
2015-12-18 02:14:33 +00:00
minetest.show_formspec ( name , " ctf_stats:rankings " , fs )
2015-12-03 23:57:28 +00:00
end
end
} )
2018-01-27 00:01:17 +00:00
local reset_y = { }
minetest.register_chatcommand ( " reset_rankings " , {
2018-10-17 21:25:40 +00:00
params = " [<name>] " ,
description = " Reset the rankings of yourself or another player " ,
2018-01-27 00:01:17 +00:00
func = function ( name , param )
param = param : trim ( )
if param ~= " " and not minetest.check_player_privs ( name , { ctf_admin = true } ) then
return false , " Missing privilege: ctf_admin "
end
local reset_name = param == " " and name or param
if reset_name == name and not reset_y [ name ] then
reset_y [ name ] = true
return true , " This will reset your stats and rankings completely. You will lose access to any special privileges such as the team chest or userlimit skip. This is irreversable. If you're sure, type /reset_rankings again to perform the reset "
end
reset_y [ name ] = nil
ctf_stats.players [ name ] = nil
ctf_stats.player ( reset_name )
2018-10-17 21:25:40 +00:00
return true , " Successfully reset the stats and ranking of " .. reset_name
end
} )
minetest.register_chatcommand ( " transfer_rankings " , {
params = " <src> <dest> " ,
description = " Transfer rankings of one player to another. " ,
privs = { ctf_admin = true } ,
func = function ( name , param )
if not param then
return false , " Invalid syntax. Provide source and destination player names. "
end
param = param : trim ( )
local src , dest = param : trim ( ) : match ( " ([%a%d_-]+) ([%a%d_-]+) " )
if not src or not dest then
return false , " Invalid usage, see /help transfer_rankings "
end
if not ctf_stats.players [ src ] then
return false , " Player ' " .. src .. " ' does not exist. "
end
if not ctf_stats.players [ dest ] then
return false , " Player ' " .. dest .. " ' does not exist. "
end
ctf_stats.players [ dest ] = ctf_stats.players [ src ]
ctf_stats.players [ src ] = nil
ctf.needs_save = true
return true , " Stats of ' " .. src .. " ' have been transferred to ' " .. dest .. " '. "
2018-01-27 00:01:17 +00:00
end
} )
2018-07-02 08:18:25 +00:00
minetest.register_chatcommand ( " summary " , {
2018-10-17 21:25:40 +00:00
description = " Display the scoreboard of the previous match. " ,
2018-07-02 08:18:25 +00:00
func = function ( name , param )
if not prev_match_summary then
return false , " Couldn't find the requested data. "
end
minetest.show_formspec ( name , " ctf_stats:prev_match_summary " , prev_match_summary )
end
} )