2017-02-05 12:10:29 -08:00
local tournament_starting_time = tonumber ( minetest.setting_get ( " pvpplus.tournament_starting_time " ) ) or 60 -- seconds
local previous_player_transfer_distance
local tournament = {
engaging_players = false ,
2017-02-17 08:14:50 -08:00
engagement_position = nil ,
2017-02-05 12:10:29 -08:00
running_tournament = false ,
2017-02-17 08:14:50 -08:00
teleport_immediately = false ,
2017-02-05 12:10:29 -08:00
engaged_players = { } ,
2017-03-19 08:45:14 -07:00
previous_pvp_states = { } ,
2017-02-05 12:10:29 -08:00
players = { } ,
sent_damages = { } ,
received_damages = { } ,
2017-03-12 08:04:19 -07:00
kills = { } ,
2017-04-01 03:45:34 -07:00
sound_handles = { } ,
starting_infos = { starter = nil , open_time = nil , start_time = nil , initially_engaged_players = nil } ,
2017-02-05 12:10:29 -08:00
}
function pvpplus . engage_player ( player_name )
2017-03-26 08:03:08 -07:00
if not tournament.engaging_players then
return false , " There is no open tournament. "
end
2017-02-05 12:10:29 -08:00
tournament.engaged_players [ player_name ] = true
minetest.chat_send_player ( player_name , " You have been engaged for a PvP tournament! " )
2017-02-17 08:14:50 -08:00
if tournament.engagement_position and tournament.teleport_immediately then
local player_obj = minetest.get_player_by_name ( player_name )
player_obj : moveto ( tournament.engagement_position )
-- Send a chat message
minetest.chat_send_player ( player_name , " You've been teleported to the tournament position. " )
end
return true
end
function pvpplus . disengage_player ( player_name )
if not pvpplus.is_engaged ( player_name ) then
return false , " This player is not engaged "
end
tournament.engaged_players [ player_name ] = nil
return true
end
function pvpplus . is_engaged ( player_name )
if tournament.engaged_players [ player_name ] then
return true
else
return false
end
2017-02-05 12:10:29 -08:00
end
2017-03-26 08:03:08 -07:00
function pvpplus . is_engaging_players ( )
return tournament.engaging_players
end
2017-04-01 03:45:34 -07:00
function pvpplus . start_tournament ( )
2017-02-05 12:10:29 -08:00
if tournament.running_tournament then
2017-04-06 10:00:33 -07:00
return false , " There is already a running tournament. "
2017-02-05 12:10:29 -08:00
end
-- Stop engaging
tournament.engaging_players = false
local count = 0
for _ , _ in pairs ( tournament.engaged_players ) do
count = count + 1
end
2017-03-27 10:59:15 -07:00
if count <= 1 and not pvpplus.debug then
2017-04-01 03:45:34 -07:00
minetest.chat_send_all ( " There are not enough engaged players to start a tournament. " )
2017-02-19 08:25:48 -08:00
tournament.engaged_players = { }
tournament.engagement_position = nil
tournament.teleport_immediately = false
2017-02-05 12:10:29 -08:00
return false
end
2017-04-01 03:45:34 -07:00
local chat_message = " PVP TOURNAMENT BEGINS! Started by " .. ( tournament.starting_infos . starter or " [unknown player] " ) .. " \n Engaged players: "
tournament.starting_infos . initially_engaged_players = { }
2017-02-05 12:10:29 -08:00
for player , _ in pairs ( tournament.engaged_players ) do
2017-04-01 03:45:34 -07:00
tournament.starting_infos . initially_engaged_players [ player ] = true
-- Enable PvPs
2017-03-19 08:45:14 -07:00
tournament.previous_pvp_states [ player ] = pvpplus.is_pvp ( player )
2017-02-05 12:10:29 -08:00
pvpplus.pvp_enable ( player )
2017-02-17 08:14:50 -08:00
-- Move to the playing table
2017-02-05 12:10:29 -08:00
tournament.players [ player ] = true
tournament.sent_damages [ player ] = 0
tournament.received_damages [ player ] = 0
tournament.kills [ player ] = 0
2017-02-22 06:26:04 -08:00
chat_message = chat_message .. player .. " , "
2017-03-12 08:04:19 -07:00
-- Play the sounds
minetest.sound_play ( " pvpplus_tournament_start " , {
to_player = player ,
gain = 1.0 ,
} )
2017-03-12 08:11:00 -07:00
if minetest.setting_getbool ( " pvpplus.enable_sound_loop " ) ~= false then -- If it's true or nil (unset)
minetest.after ( 10 , function ( name )
tournament.sound_handles [ name ] = minetest.sound_play ( " pvpplus_tournament_loop " , {
to_player = name ,
gain = 1.0 ,
loop = true ,
} )
end , player )
end
2017-02-05 12:10:29 -08:00
end
2017-02-22 06:26:04 -08:00
chat_message = chat_message : sub ( 0 , - 3 ) -- Remove the last ', '
2017-02-05 12:10:29 -08:00
tournament.engaged_players = { }
2017-02-17 08:14:50 -08:00
tournament.engagement_position = nil
tournament.teleport_immediately = false
2017-02-05 12:10:29 -08:00
-- Set the player transfer distance
previous_player_transfer_distance = minetest.setting_get ( " player_transfer_distance " )
minetest.setting_set ( " player_transfer_distance " , 0 ) -- 0 = unlimited
2017-02-22 06:26:04 -08:00
-- Send the final chat message
minetest.chat_send_all ( chat_message )
2017-02-05 12:10:29 -08:00
-- Set the tournament flag
tournament.running_tournament = true
end
2017-04-01 03:45:34 -07:00
function pvpplus . start_global_tournament ( )
2017-02-05 12:10:29 -08:00
local players = minetest.get_connected_players ( )
-- Engage all connected players
for _ , player in ipairs ( players ) do
local player_name = player : get_player_name ( )
if minetest.get_player_privs ( player_name ) . interact then -- Well, don't engage players who don't have interact.
pvpplus.engage_player ( player : get_player_name ( ) )
end
end
-- Start the tournament
2017-04-08 04:21:43 -07:00
return pvpplus.start_tournament ( )
2017-02-05 12:10:29 -08:00
end
function pvpplus . stop_tournament ( )
if not pvpplus.is_running_tournament ( ) then
return false
end
minetest.chat_send_all ( " END OF TOURNAMENT! " )
-- Calculate rating
local rating = { }
for name , _ in pairs ( tournament.sent_damages ) do
table.insert ( rating , {
name = name ,
score = tournament.sent_damages [ name ] - tournament.received_damages [ name ] + tournament.kills [ name ] * 20
} )
2017-03-12 08:04:19 -07:00
if tournament.sound_handles [ name ] then
minetest.sound_stop ( tournament.sound_handles [ name ] )
end
2017-03-12 08:11:00 -07:00
minetest.sound_play ( " pvpplus_tournament_end " , {
to_player = name ,
gain = 1.0 ,
} )
2017-03-19 08:45:14 -07:00
-- Set PvP to the state it had before the tournament
2017-03-26 05:21:00 -07:00
if tournament.previous_pvp_states [ name ] then
pvpplus.pvp_set ( name , tournament.previous_pvp_states [ name ] )
end
2017-02-05 12:10:29 -08:00
end
2017-02-21 09:16:55 -08:00
table.sort ( rating , function ( a , b ) return a.score > b.score end )
2017-02-05 12:10:29 -08:00
-- Print it
2017-02-17 09:05:52 -08:00
-- It won't look good if the font used in the chat and the formspec textarea isn't monospace.
local str = " ***************************** TOURNAMENT RANKING **************************** \n " ..
" +--------------+-------+--------+---------------+-------------------+-------+ \n " ..
" | player | rank | score | sent damages | received damages | kills | \n " ..
" +--------------+-------+--------+---------------+-------------------+-------+ "
2017-02-05 12:10:29 -08:00
for i , v in ipairs ( rating ) do
local player = v.name
local rank = tostring ( i )
local score = tostring ( v.score )
local sent_damages = tostring ( tournament.sent_damages [ v.name ] )
local received_damages = tostring ( tournament.received_damages [ v.name ] )
local kills = tostring ( tournament.kills [ v.name ] )
2017-02-17 10:12:28 -08:00
local substr = " | "
2017-02-05 12:10:29 -08:00
local function cat_str ( value , len )
2017-02-17 10:12:28 -08:00
substr = substr .. value
2017-03-12 11:15:25 -07:00
if # substr > len then
substr = substr : sub ( 1 , len - 1 ) .. " + "
end
2017-02-17 10:12:28 -08:00
for n = # substr , len do
substr = substr .. " "
2017-02-05 12:10:29 -08:00
end
2017-02-17 10:12:28 -08:00
substr = substr .. " | "
2017-02-05 12:10:29 -08:00
end
2017-03-12 11:15:25 -07:00
cat_str ( player , 14 )
cat_str ( rank , 22 )
cat_str ( score , 31 )
cat_str ( sent_damages , 47 )
cat_str ( received_damages , 67 )
cat_str ( kills , 75 )
2017-02-17 10:12:28 -08:00
str = str .. " \n " .. substr
2017-02-17 09:05:52 -08:00
end
str = str .. " \n +--------------+-------+--------+---------------+-------------------+-------+ "
minetest.chat_send_all ( str )
2017-02-05 12:10:29 -08:00
2017-03-12 11:15:25 -07:00
local formspec = " size[10,8.5]textarea[0.5,0.5;9.5,8;ranking;Ranking; " .. str .. " ]button_exit[4,7.5;2,1;exit;Ok] "
2017-02-21 09:18:42 -08:00
for name , _ in pairs ( tournament.sent_damages ) do
2017-02-17 10:12:28 -08:00
minetest.show_formspec ( name , " pvpplus:ranking " , formspec )
2017-02-05 12:10:29 -08:00
end
-- Clean tables
tournament = {
engaging_players = false ,
running_tournament = false ,
engaged_players = { } ,
2017-03-19 08:45:14 -07:00
previous_pvp_states = { } ,
2017-02-05 12:10:29 -08:00
players = { } ,
sent_damages = { } ,
received_damages = { } ,
2017-03-12 08:04:19 -07:00
kills = { } ,
2017-04-01 03:45:34 -07:00
sound_handles = { } ,
starting_infos = { starter = nil , open_time = nil , start_time = nil , initially_engaged_players = nil } ,
2017-02-05 12:10:29 -08:00
}
-- Change the player transfer distance back
minetest.setting_set ( " player_transfer_distance " , previous_player_transfer_distance )
end
2017-02-17 08:14:50 -08:00
function pvpplus . allow_engaging ( starter_name , teleport )
2017-03-26 08:03:08 -07:00
if tournament.engaging_players then
return false , " There is already an open tournament. "
end
if tournament.is_running_tournament then
return false , " There is already a running tournament. "
end
2017-02-05 12:10:29 -08:00
tournament.engaging_players = true
minetest.chat_send_all ( starter_name .. " opened a tournament! Type /engage to engage yourself in the tournament! " )
2017-02-17 08:14:50 -08:00
if teleport then
tournament.engagement_position = minetest.get_player_by_name ( starter_name ) : get_pos ( )
end
2017-03-12 08:04:19 -07:00
minetest.sound_play ( " pvpplus_tournament_start " , {
gain = 1.0 ,
} )
2017-03-26 08:03:08 -07:00
return true
2017-02-17 08:14:50 -08:00
end
function pvpplus . teleport_engaged_players ( )
if not tournament.engagement_position then
return false , " No engagement position. "
end
for player , _ in pairs ( tournament.engaged_players ) do
-- Teleport player to the starter
if tournament.engagement_position then
local player_obj = minetest.get_player_by_name ( player )
player_obj : moveto ( tournament.engagement_position )
end
-- Send a chat message
minetest.chat_send_player ( player , " You've been teleported to the tournament position. " )
end
tournament.teleport_immediately = true
return true
2017-02-05 12:10:29 -08:00
end
function pvpplus . remove_from_tournament ( player_name )
-- Remove from players table
tournament.players [ player_name ] = nil
-- Send a chat message
if minetest.get_player_by_name ( player_name ) then
minetest.chat_send_player ( player_name , " You are no longer playing the tournament. " )
end
minetest.chat_send_all ( " Player " .. player_name .. " is no longer playing the tournament. " )
2017-03-26 05:21:00 -07:00
-- Change their PvP state
pvpplus.pvp_set ( player_name , tournament.previous_pvp_states [ player_name ] )
tournament.previous_pvp_states [ player_name ] = nil
2017-02-05 12:10:29 -08:00
-- Check if the tournament is ended
local count = 0
for _ , _ in pairs ( tournament.players ) do count = count + 1 end
if count <= 1 then -- 1 or less remaining players
pvpplus.stop_tournament ( )
end
end
function pvpplus . add_to_tournament ( player_name )
if not pvpplus.is_running_tournament ( ) or pvpplus.is_playing_tournament ( player_name ) then
return false
end
-- Add to tables
tournament.players [ player_name ] = true
tournament.sent_damages [ player_name ] = 0
tournament.received_damages [ player_name ] = 0
2017-02-13 09:46:58 -08:00
tournament.kills [ player_name ] = 0
2017-03-26 03:18:02 -07:00
tournament.previous_pvp_states [ player_name ] = pvpplus.is_pvp ( player_name )
-- Enable PvP
pvpplus.pvp_enable ( player_name )
2017-02-05 12:10:29 -08:00
-- Send a chat message
if minetest.get_player_by_name ( player_name ) then
minetest.chat_send_player ( player_name , " You joined the current tournament! " )
end
minetest.chat_send_all ( " Player " .. player_name .. " joined the current tournament! " )
end
function pvpplus . is_playing_tournament ( player_name )
if tournament.players [ player_name ] then
return true
else
return false
end
end
function pvpplus . is_running_tournament ( )
return tournament.running_tournament
end
function pvpplus . tournament_on_punchplayer ( player , hitter , damage )
local player_name = player : get_player_name ( )
local hitter_name = hitter : get_player_name ( )
if not ( pvpplus.is_running_tournament ( )
and pvpplus.is_playing_tournament ( player_name )
and pvpplus.is_playing_tournament ( hitter_name ) ) then
return false
end
tournament.received_damages [ player_name ] = tournament.received_damages [ player_name ] + damage
tournament.sent_damages [ hitter_name ] = tournament.sent_damages [ hitter_name ] + damage
if player : get_hp ( ) - damage <= 0 then -- Killed
tournament.kills [ hitter_name ] = tournament.kills [ hitter_name ] + 1
minetest.chat_send_player ( player_name , " You have been killed by " .. hitter_name )
-- Removing the player from the tournament is done by the on_dieplayer callback.
end
end
minetest.register_privilege ( " tournament_mod " , " PvP Tournament Moderator " )
minetest.register_chatcommand ( " start_global_tournament " , {
params = " " ,
description = " Start a PvP tournament engaging every connected players and starting immediately " ,
privs = { interact = true , tournament_mod = true } ,
func = function ( name , param )
2017-04-01 03:45:34 -07:00
-- Fill start infos
tournament.starting_infos . starter = name
tournament.starting_infos . open_time = nil
tournament.starting_infos . start_time = os.time ( )
2017-04-08 04:21:43 -07:00
return pvpplus.start_global_tournament ( )
2017-02-05 12:10:29 -08:00
end
} )
minetest.register_chatcommand ( " stop_tournament " , {
params = " " ,
description = " Stops the current PvP tournament " ,
privs = { interact = true , tournament_mod = true } ,
func = function ( name , param )
pvpplus.stop_tournament ( )
return true
end
} )
minetest.register_chatcommand ( " remove_from_tournament " , {
params = " <name> " ,
description = " Removes a player from a PvP tournament " ,
privs = { interact = true , tournament_mod = true } ,
func = function ( name , param )
if not minetest.get_player_by_name ( param ) then
return false , " Player does not exist. Please refer to usage: /help kick_from_tournament "
end
minetest.chat_send_player ( param , " You have been removed from the tournament by " .. name )
pvpplus.remove_from_tournament ( param )
end
} )
minetest.register_chatcommand ( " add_to_tournament " , {
params = " <name> " ,
description = " Adds a player to the current tournament " ,
privs = { interact = true , tournament_mod = true } ,
func = function ( name , param )
if not minetest.get_player_by_name ( param ) then
return false , " Player does not exist. Please refer to usage: /help kick_from_tournament "
end
if pvpplus.is_playing_tournament ( player ) then
return false , " Player is already playing a tournament. "
end
if not pvpplus.is_running_tournament ( ) then
return false , " There is no currently running tournament. "
end
minetest.chat_send_player ( param , " You have been added to the current tournament by " .. name )
pvpplus.add_to_tournament ( param )
end
} )
minetest.register_chatcommand ( " leave_tournament " , {
params = " " ,
description = " Leaves a PvP tournament " ,
privs = { interact = true } ,
func = function ( name , param )
if not pvpplus.is_playing_tournament ( name ) then
return false , " You are not playing a tournament. "
end
pvpplus.remove_from_tournament ( name )
end
} )
minetest.register_chatcommand ( " engage " , {
params = " " ,
description = " Engages for the next PvP tournament " ,
privs = { interact = true } ,
func = function ( name , param )
if pvpplus.is_playing_tournament ( name ) then
return false , " You are already playing a tournament. "
end
if not tournament.engaging_players then
return false , " There is no opened tournament. Type /tournament! "
end
pvpplus.engage_player ( name )
minetest.chat_send_all ( " Player " .. name .. " engaged himself/herself for the PvP tournament! " )
end
} )
minetest.register_chatcommand ( " tournament " , {
2017-04-08 04:23:54 -07:00
params = " [noteleport] [seconds] " ,
2017-02-17 08:14:50 -08:00
description = " Creates a new tournament, optionally teleporting players to your current position 10 seconds before the tournament starts. " ,
2017-02-05 12:10:29 -08:00
privs = { interact = true } ,
func = function ( name , param )
2017-03-11 06:57:57 -08:00
local params = param : split ( " " )
if # params > 2 then
return false , " Invalid usage. See /help tournament. "
end
local starting_time = tournament_starting_time
2017-04-08 04:23:54 -07:00
local teleport = true
2017-03-11 06:57:57 -08:00
if tonumber ( params [ 1 ] ) then
starting_time = tonumber ( params [ 1 ] )
2017-04-08 04:23:54 -07:00
elseif params [ 1 ] == " noteleport " then
teleport = false
2017-03-11 06:57:57 -08:00
if tonumber ( params [ 2 ] ) then
starting_time = tonumber ( params [ 2 ] )
elseif params [ 2 ] ~= " " and params [ 2 ] ~= nil then
return false , " Invalid usage. See /help tournament. "
end
2017-02-17 08:14:50 -08:00
end
2017-03-11 06:57:57 -08:00
if starting_time < 10 or starting_time > 600 then
return false , " Please set a starting time between 10s and 600s. "
end
2017-04-01 03:45:34 -07:00
-- Fill start infos
tournament.starting_infos . starter = name
tournament.starting_infos . open_time = os.time ( )
tournament.starting_infos . start_time = os.time ( ) + starting_time
2017-02-05 12:10:29 -08:00
-- Allow engaging
2017-03-26 08:03:08 -07:00
local e , m = pvpplus.allow_engaging ( name , teleport )
if e == false then
return false , m
end
2017-02-05 12:10:29 -08:00
2017-02-17 10:12:28 -08:00
-- Engage starter
pvpplus.engage_player ( name )
2017-02-05 12:10:29 -08:00
-- Chat messages
2017-03-11 06:57:57 -08:00
minetest.chat_send_all ( " The tournament will begin in " .. tostring ( starting_time ) .. " s. " )
minetest.after ( starting_time - 10 , function ( )
2017-02-05 12:10:29 -08:00
minetest.chat_send_all ( " The tournament will begin in 10s! Engage yourself by typing /engage! " )
2017-02-17 08:14:50 -08:00
pvpplus.teleport_engaged_players ( )
2017-02-05 12:10:29 -08:00
end )
2017-03-11 06:57:57 -08:00
minetest.after ( starting_time - 5 , function ( )
2017-02-05 12:10:29 -08:00
minetest.chat_send_all ( " The tournament will begin in 5s! " )
end )
for i = 1 , 4 do
2017-03-11 06:57:57 -08:00
minetest.after ( starting_time - i , function ( )
2017-02-05 12:10:29 -08:00
minetest.chat_send_all ( tostring ( i ) .. " ! " )
end )
end
-- Start tournament
2017-03-11 06:57:57 -08:00
minetest.after ( starting_time , function ( name )
2017-04-08 04:21:43 -07:00
local ok , e = pvpplus.start_tournament ( )
if ok == false and e then
minetest.chat_send_player ( name , e )
end
2017-02-05 12:10:29 -08:00
end , name )
end
} )
2017-04-01 03:45:34 -07:00
minetest.register_chatcommand ( " tournament_info " , {
params = " " ,
description = " Prints tournament informations " ,
privs = { } ,
func = function ( name , param )
if pvpplus.is_engaging_players ( ) then
local str = " There is an open tournament which is not yet started (you can engage for this tournament by typing /engage). \n " ..
" The tournament was open by: " .. tournament.starting_infos . starter ..
" \n The tournament will start in: " .. os.difftime ( tournament.starting_infos . start_time , os.time ( ) ) .. " seconds \n " ..
" The tournament is open since: " .. os.date ( " %c " , tournament.starting_infos . open_time ) ..
( tournament.engagement_position and " \n Engaged players will be teleported before the tournament starts. \n " or " \n Engaged players won't be teleported. \n " ) ..
" Currently engaged players are: "
for player , _ in pairs ( tournament.engaged_players ) do
str = str .. player .. " , "
end
str = str : sub ( 0 , - 3 )
return true , str
elseif pvpplus.is_running_tournament ( ) then
local str = " There is a currrently running tournament. \n " ..
" The tournament was open by: " .. tournament.starting_infos . starter ..
" \n The tournament is running since: " .. os.date ( " %c " , tournament.starting_infos . start_time ) ..
" \n Initially engaged players were: "
for player , _ in pairs ( tournament.starting_infos . initially_engaged_players ) do
str = str .. player .. " , "
end
str = str : sub ( 0 , - 3 )
str = str .. " \n Remaining players are: "
for player , _ in pairs ( tournament.players ) do
str = str .. player .. " , "
end
str = str : sub ( 0 , - 3 )
return true , str
else
return true , " There is no currently running tournament. You can start a new tournament by using /tournament (see /help tournament). "
end
end
} )
2017-02-05 12:10:29 -08:00
minetest.register_on_leaveplayer ( function ( player )
local player_name = player : get_player_name ( )
if pvpplus.is_playing_tournament ( player_name ) then
pvpplus.remove_from_tournament ( player_name )
end
2017-02-17 08:14:50 -08:00
if pvpplus.is_engaged ( player_name ) then
pvpplus.disengage_player ( player_name )
end
2017-02-05 12:10:29 -08:00
end )
minetest.register_on_dieplayer ( function ( player )
local player_name = player : get_player_name ( )
2017-03-27 10:59:15 -07:00
if not pvpplus.is_playing_tournament ( player_name ) then
return
end
pvpplus.remove_from_tournament ( player_name )
local pos = vector.round ( player : getpos ( ) )
if minetest.get_modpath ( " bones " ) and minetest.get_node ( pos ) . name == " bones:bones " then
local bones_inv = minetest.get_inventory ( { type = " node " , pos = pos } )
local player_inv = player : get_inventory ( )
if not player_inv : is_empty ( " main " ) then
minetest.chat_send_player ( player_name , " Cannot give you back your stuff because your inventory is not empty. Your stuff is still is your bones. " )
return
end
player_inv : set_list ( " main " , bones_inv : get_list ( " main " ) )
minetest.remove_node ( pos )
2017-02-05 12:10:29 -08:00
end
end )