2015-11-29 00:05:37 +00:00
|
|
|
ctf_stats = {}
|
|
|
|
|
2017-10-12 23:09:25 +00:00
|
|
|
local storage = minetest.get_mod_storage()
|
|
|
|
local data_to_persist = { "matches", "players" }
|
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
function ctf_stats.load_legacy()
|
2017-10-12 23:09:25 +00:00
|
|
|
local file = io.open(minetest.get_worldpath() .. "/ctf_stats.txt", "r")
|
2018-01-09 02:40:18 +00:00
|
|
|
if not file then
|
|
|
|
return false
|
|
|
|
end
|
2017-10-12 23:09:25 +00:00
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
local table = minetest.deserialize(file:read("*all"))
|
|
|
|
file:close()
|
|
|
|
if type(table) ~= "table" then
|
|
|
|
return false
|
|
|
|
end
|
2017-11-07 00:32:20 +00:00
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
ctf.log("ctf_stats", "Migrating stats...")
|
|
|
|
ctf_stats.matches = table.matches
|
|
|
|
ctf_stats.players = table.players
|
2017-11-07 00:32:20 +00:00
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
for name, player_stats in pairs(ctf_stats.players) do
|
|
|
|
if not player_stats.score or player_stats.score < 0 then
|
|
|
|
player_stats.score = 0
|
|
|
|
end
|
|
|
|
if player_stats.score > 300 then
|
|
|
|
player_stats.score = (player_stats.score - 300) / 30 + 300
|
|
|
|
end
|
|
|
|
if player_stats.score > 800 then
|
|
|
|
player_stats.score = 800
|
|
|
|
end
|
2017-11-07 00:32:20 +00:00
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
player_stats.wins = player_stats.wins or {}
|
|
|
|
if player_stats.blue_wins then
|
|
|
|
player_stats.wins.blue = player_stats.blue_wins
|
|
|
|
player_stats.blue_wins = nil
|
2017-10-12 23:09:25 +00:00
|
|
|
end
|
2018-01-09 02:40:18 +00:00
|
|
|
if player_stats.red_wins then
|
|
|
|
player_stats.wins.red = player_stats.red_wins
|
|
|
|
player_stats.red_wins = nil
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
2018-01-09 02:40:18 +00:00
|
|
|
player_stats.wins.blue = player_stats.wins.blue or 0
|
|
|
|
player_stats.wins.red = player_stats.wins.red or 0
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
ctf_stats.matches.wins = ctf_stats.matches.wins or {
|
|
|
|
red = ctf_stats.matches.red_wins or 0,
|
|
|
|
blue = ctf_stats.matches.blue_wins or 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
ctf.needs_save = true
|
2018-01-09 02:41:46 +00:00
|
|
|
|
|
|
|
os.remove(minetest.get_worldpath() .. "/ctf_stats.txt")
|
2018-01-09 02:40:18 +00:00
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
|
|
|
function ctf_stats.load()
|
|
|
|
if not ctf_stats.load_legacy() then
|
|
|
|
for _, key in pairs(data_to_persist) do
|
|
|
|
ctf_stats[key] = minetest.parse_json(storage:get_string(key))
|
2018-01-03 00:23:38 +00:00
|
|
|
end
|
2018-01-09 02:40:18 +00:00
|
|
|
ctf.needs_save = true
|
2018-01-03 00:23:38 +00:00
|
|
|
end
|
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
-- Make sure all tables are present
|
|
|
|
ctf_stats.players = ctf_stats.players or {}
|
2017-10-12 23:09:25 +00:00
|
|
|
ctf_stats.matches = ctf_stats.matches or {
|
|
|
|
wins = {
|
|
|
|
blue = 0,
|
2018-01-09 02:40:18 +00:00
|
|
|
red = 0,
|
2017-10-12 23:09:25 +00:00
|
|
|
},
|
2018-01-09 02:40:18 +00:00
|
|
|
skipped = 0,
|
2015-11-29 00:05:37 +00:00
|
|
|
}
|
2017-10-12 23:09:25 +00:00
|
|
|
ctf_stats.current = ctf_stats.current or {
|
2015-11-29 00:05:37 +00:00
|
|
|
red = {},
|
|
|
|
blue = {}
|
|
|
|
}
|
|
|
|
|
2018-04-18 20:49:47 +00:00
|
|
|
ctf_stats.start = os.time()
|
|
|
|
|
2018-01-09 02:40:18 +00:00
|
|
|
-- Strip players which have no score
|
|
|
|
for name, player_stats in pairs(ctf_stats.players) do
|
|
|
|
if not player_stats.score or player_stats.score <= 0 then
|
|
|
|
ctf_stats.players[name] = nil
|
|
|
|
ctf.needs_save = true
|
2018-04-19 00:01:19 +00:00
|
|
|
else
|
|
|
|
player_stats.bounty_kills = player_stats.bounty_kills or 0
|
2018-01-09 02:40:18 +00:00
|
|
|
end
|
|
|
|
end
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
ctf.register_on_save(function()
|
2017-10-12 23:09:25 +00:00
|
|
|
for _, key in pairs(data_to_persist) do
|
|
|
|
storage:set_string(key, minetest.write_json(ctf_stats[key]))
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
return nil
|
|
|
|
end)
|
|
|
|
|
2018-01-02 23:51:38 +00:00
|
|
|
function ctf_stats.player_or_nil(name)
|
|
|
|
return ctf_stats.players[name], ctf_stats.current.red[name] or ctf_stats.current.blue[name]
|
|
|
|
end
|
|
|
|
|
2017-10-12 23:09:25 +00:00
|
|
|
-- Returns a tuple: `player_stats`, `match_player_stats`
|
2015-11-29 00:05:37 +00:00
|
|
|
function ctf_stats.player(name)
|
2017-10-12 23:09:25 +00:00
|
|
|
local player_stats = ctf_stats.players[name]
|
|
|
|
if not player_stats then
|
|
|
|
player_stats = {
|
2015-12-03 23:57:28 +00:00
|
|
|
name = name,
|
2017-10-12 23:09:25 +00:00
|
|
|
wins = {
|
|
|
|
red = 0,
|
|
|
|
blue = 0,
|
|
|
|
},
|
2015-11-29 00:05:37 +00:00
|
|
|
kills = 0,
|
|
|
|
deaths = 0,
|
2015-12-03 23:57:28 +00:00
|
|
|
captures = 0,
|
2017-10-12 00:19:23 +00:00
|
|
|
attempts = 0,
|
2017-10-12 23:09:25 +00:00
|
|
|
score = 0,
|
2018-04-18 23:57:21 +00:00
|
|
|
bounty_kills = 0,
|
2015-11-29 00:05:37 +00:00
|
|
|
}
|
2017-10-12 23:09:25 +00:00
|
|
|
ctf_stats.players[name] = player_stats
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
|
|
|
|
2017-10-12 23:09:25 +00:00
|
|
|
local match_player_stats =
|
|
|
|
ctf_stats.current.red[name] or ctf_stats.current.blue[name]
|
2015-11-29 00:05:37 +00:00
|
|
|
|
2017-10-12 23:09:25 +00:00
|
|
|
return player_stats, match_player_stats
|
2015-11-29 00:05:37 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
ctf.register_on_join_team(function(name, tname)
|
2017-10-12 23:09:25 +00:00
|
|
|
ctf_stats.current[tname][name] = ctf_stats.current[tname][name] or {
|
2015-11-29 00:05:37 +00:00
|
|
|
kills = 0,
|
2017-10-12 23:09:25 +00:00
|
|
|
kills_since_death = 0,
|
2015-11-29 00:05:37 +00:00
|
|
|
deaths = 0,
|
2015-12-03 23:46:52 +00:00
|
|
|
attempts = 0,
|
2017-10-12 23:09:25 +00:00
|
|
|
captures = 0,
|
|
|
|
score = 0,
|
2018-04-18 22:00:15 +00:00
|
|
|
bounty_kills = 0,
|
2015-11-29 00:05:37 +00:00
|
|
|
}
|
|
|
|
end)
|
|
|
|
|
|
|
|
ctf_match.register_on_skip_map(function()
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-11-29 00:05:37 +00:00
|
|
|
ctf_stats.matches.skipped = ctf_stats.matches.skipped + 1
|
|
|
|
end)
|
|
|
|
|
2018-04-18 20:49:47 +00:00
|
|
|
local winner_team = "-"
|
|
|
|
local winner_player = "-"
|
|
|
|
|
2015-12-03 23:46:52 +00:00
|
|
|
ctf_flag.register_on_capture(function(name, flag)
|
2015-12-03 23:57:28 +00:00
|
|
|
local main, match = ctf_stats.player(name)
|
|
|
|
if main and match then
|
2017-10-12 23:09:25 +00:00
|
|
|
main.captures = main.captures + 1
|
2017-10-12 23:57:09 +00:00
|
|
|
main.score = main.score + 25
|
2015-12-03 23:57:28 +00:00
|
|
|
match.captures = match.captures + 1
|
2017-10-12 23:57:09 +00:00
|
|
|
match.score = match.score + 25
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-12-03 23:46:52 +00:00
|
|
|
end
|
2018-04-18 20:49:47 +00:00
|
|
|
winner_player = name
|
2015-12-03 23:46:52 +00:00
|
|
|
end)
|
|
|
|
|
2015-11-29 00:05:37 +00:00
|
|
|
ctf_match.register_on_winner(function(winner)
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2017-10-12 23:09:25 +00:00
|
|
|
ctf_stats.matches.wins[winner] = ctf_stats.matches.wins[winner] + 1
|
2018-04-18 20:49:47 +00:00
|
|
|
winner_team = winner
|
2015-11-29 00:05:37 +00:00
|
|
|
end)
|
|
|
|
|
|
|
|
ctf_match.register_on_new_match(function()
|
2018-04-18 20:49:47 +00:00
|
|
|
local fs = ctf_stats.get_formspec_match_summary(ctf_stats.current, winner_team, winner_player, os.time()-ctf_stats.start)
|
2015-12-03 23:46:52 +00:00
|
|
|
local players = minetest.get_connected_players()
|
|
|
|
for _, player in pairs(players) do
|
|
|
|
minetest.show_formspec(player:get_player_name(), "ctf_stats:eom", fs)
|
|
|
|
end
|
2015-11-29 00:05:37 +00:00
|
|
|
|
|
|
|
ctf_stats.current = {
|
|
|
|
red = {},
|
|
|
|
blue = {}
|
|
|
|
}
|
2018-04-18 20:49:47 +00:00
|
|
|
winner_team = "-"
|
|
|
|
winner_player = "-"
|
|
|
|
ctf_stats.start = os.time()
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-11-29 00:05:37 +00:00
|
|
|
end)
|
|
|
|
|
|
|
|
ctf_flag.register_on_pick_up(function(name, flag)
|
|
|
|
local main, match = ctf_stats.player(name)
|
2015-11-29 23:55:27 +00:00
|
|
|
if main and match then
|
2017-10-12 23:09:25 +00:00
|
|
|
main.attempts = main.attempts + 1
|
|
|
|
main.score = main.score + 5
|
2015-11-29 23:55:27 +00:00
|
|
|
match.attempts = match.attempts + 1
|
2017-10-12 23:57:09 +00:00
|
|
|
match.score = match.score + 10
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-11-29 23:55:27 +00:00
|
|
|
end
|
2015-11-29 00:05:37 +00:00
|
|
|
end)
|
|
|
|
|
|
|
|
ctf_flag.register_on_precapture(function(name, flag)
|
|
|
|
local tplayer = ctf.player(name)
|
2018-04-06 13:04:56 +00:00
|
|
|
local main, _ = ctf_stats.player(name)
|
2015-11-29 23:55:27 +00:00
|
|
|
if main then
|
2017-10-12 23:09:25 +00:00
|
|
|
main.wins[tplayer.team] = main.wins[tplayer.team] + 1
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-11-29 23:55:27 +00:00
|
|
|
end
|
2015-11-29 00:05:37 +00:00
|
|
|
return true
|
|
|
|
end)
|
|
|
|
|
2018-02-15 08:04:34 +00:00
|
|
|
-- good_weapons now includes all mese and diamond implements, and swords of steel and better
|
2017-10-12 23:09:25 +00:00
|
|
|
local good_weapons = {
|
|
|
|
"default:sword_steel",
|
2018-02-11 15:44:10 +00:00
|
|
|
"default:sword_bronze",
|
|
|
|
"default:sword_mese",
|
|
|
|
"default:sword_diamond",
|
2018-02-18 08:30:50 +00:00
|
|
|
"default:pick_mese",
|
|
|
|
"default:pick_diamond",
|
|
|
|
"default:axe_mese",
|
|
|
|
"default:axe_diamond",
|
|
|
|
"default:shovel_mese",
|
|
|
|
"default:shovel_diamond",
|
2017-10-12 23:09:25 +00:00
|
|
|
"shooter:grenade",
|
|
|
|
"shooter:shotgun",
|
|
|
|
"shooter:rifle",
|
|
|
|
"shooter:machine_gun",
|
|
|
|
}
|
2018-02-15 08:04:34 +00:00
|
|
|
|
2017-10-12 23:09:25 +00:00
|
|
|
local function invHasGoodWeapons(inv)
|
|
|
|
for _, weapon in pairs(good_weapons) do
|
|
|
|
if inv:contains_item("main", weapon) then
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
local function calculateKillReward(victim, killer)
|
|
|
|
local vmain, victim_match = ctf_stats.player(victim)
|
|
|
|
|
|
|
|
-- +5 for every kill they've made since last death in this match.
|
|
|
|
local reward = victim_match.kills_since_death * 5
|
2017-10-12 23:57:09 +00:00
|
|
|
ctf.log("ctf_stats", "Player " .. victim .. " has made " .. reward ..
|
|
|
|
" score worth of kills since last death")
|
2017-10-12 23:09:25 +00:00
|
|
|
|
2018-02-15 08:04:34 +00:00
|
|
|
-- 30 * K/D ratio, with variable based on player's score
|
2017-10-13 01:05:21 +00:00
|
|
|
local kdreward = 30 * vmain.kills / (vmain.deaths + 1)
|
|
|
|
local max = vmain.score / 6
|
2017-10-12 23:09:25 +00:00
|
|
|
if kdreward > max then
|
|
|
|
kdreward = max
|
|
|
|
end
|
2017-10-13 01:05:21 +00:00
|
|
|
if kdreward > 80 then
|
|
|
|
kdreward = 80
|
2017-10-12 23:09:25 +00:00
|
|
|
end
|
2017-10-12 23:57:09 +00:00
|
|
|
reward = reward + kdreward
|
2017-10-12 23:09:25 +00:00
|
|
|
|
2018-02-15 08:04:34 +00:00
|
|
|
-- Limited to 0 <= X <= 200
|
|
|
|
if reward > 200 then
|
|
|
|
reward = 200
|
2017-10-13 01:05:21 +00:00
|
|
|
elseif reward < 14 then
|
|
|
|
reward = 14
|
2017-10-12 23:09:25 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
-- Half if no good weapons
|
|
|
|
local inv = minetest.get_inventory({ type="player", name = victim })
|
|
|
|
if not invHasGoodWeapons(inv) then
|
|
|
|
ctf.log("ctf_stats", "Player " .. victim .. " has no good weapons")
|
|
|
|
reward = reward * 0.5
|
|
|
|
else
|
|
|
|
ctf.log("ctf_stats", "Player " .. victim .. " has good weapons")
|
|
|
|
end
|
|
|
|
|
|
|
|
return reward
|
|
|
|
end
|
|
|
|
|
2018-11-12 06:15:51 +00:00
|
|
|
ctf_stats.current_bounty = {}
|
|
|
|
|
|
|
|
local hud_timer = {}
|
|
|
|
local hud_main = {}
|
|
|
|
local hud_bounty = {}
|
|
|
|
local hud_duration = 2
|
|
|
|
|
|
|
|
-- Called on every player kill
|
|
|
|
-- Creates new HUD element or updates existing one
|
|
|
|
local function showKillPoints(killer, bounty_kill, points)
|
|
|
|
if not killer then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local name = killer:get_player_name()
|
|
|
|
local bounty = ctf_stats.current_bounty["score"]
|
|
|
|
|
|
|
|
-- If HUD element already exists, update
|
|
|
|
if hud_main[name] then
|
|
|
|
killer:hud_change(hud_main[name], "text", points)
|
|
|
|
|
|
|
|
if bounty_kill and hud_bounty[name] then
|
|
|
|
killer:hud_change(hud_bounty[name], "text", "+ " .. bounty)
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Else create a new one
|
|
|
|
else
|
|
|
|
hud_main[name] = killer:hud_add({
|
|
|
|
hud_elem_type = "text",
|
|
|
|
position = {x = 0.5, y = 1},
|
|
|
|
alignment = {x = 0, y = 0},
|
|
|
|
offset = {x = 0, y = -150},
|
|
|
|
text = points,
|
|
|
|
number = 0x00DD00
|
|
|
|
})
|
|
|
|
-- If victim was a bounty target, show bounty points too
|
|
|
|
if bounty_kill then
|
|
|
|
hud_bounty[name] = killer:hud_add({
|
|
|
|
hud_elem_type = "text",
|
|
|
|
position = {x = 0.5, y = 1},
|
|
|
|
alignment = {x = 0, y = 0},
|
|
|
|
offset = {x = 0, y = -120},
|
|
|
|
text = "+ " .. bounty,
|
|
|
|
number = 0xFF4444
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
-- Set duration for which the HUD element would be visible
|
|
|
|
hud_timer[name] = os.time() + hud_duration
|
|
|
|
end
|
|
|
|
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
|
|
for _, player in pairs(minetest.get_connected_players()) do
|
|
|
|
local name = player:get_player_name()
|
|
|
|
|
|
|
|
-- If no timer exists for player, return
|
|
|
|
if not hud_timer[name] then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
-- If HUD element's visibility duration expired, remove it
|
|
|
|
if hud_timer[name] <= os.time() then
|
|
|
|
player:hud_remove(hud_main[name])
|
|
|
|
hud_main[name] = nil
|
|
|
|
-- If bounty score is displayed, remove that too
|
|
|
|
if hud_bounty[name] then
|
|
|
|
player:hud_remove(hud_bounty[name])
|
|
|
|
hud_bounty[name] = nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2015-12-04 00:15:03 +00:00
|
|
|
ctf.register_on_killedplayer(function(victim, killer)
|
|
|
|
local main, match = ctf_stats.player(killer)
|
|
|
|
if main and match then
|
2017-10-12 23:09:25 +00:00
|
|
|
local reward = calculateKillReward(victim, killer)
|
2018-11-12 06:15:51 +00:00
|
|
|
local bounty_kill = victim:get_player_name() ==
|
|
|
|
ctf_stats.current_bounty["name"] and true or false
|
|
|
|
showKillPoints(killer, bounty_kill, reward)
|
2017-10-12 23:09:25 +00:00
|
|
|
main.kills = main.kills + 1
|
|
|
|
main.score = main.score + reward
|
2015-12-04 00:15:03 +00:00
|
|
|
match.kills = match.kills + 1
|
2017-10-12 23:09:25 +00:00
|
|
|
match.score = match.score + reward
|
|
|
|
match.kills_since_death = match.kills_since_death + 1
|
2015-12-04 00:15:03 +00:00
|
|
|
ctf.needs_save = true
|
|
|
|
end
|
|
|
|
end)
|
|
|
|
|
2015-11-29 00:05:37 +00:00
|
|
|
minetest.register_on_dieplayer(function(player)
|
|
|
|
local main, match = ctf_stats.player(player:get_player_name())
|
2015-11-29 23:55:27 +00:00
|
|
|
if main and match then
|
|
|
|
main.deaths = main.deaths + 1
|
|
|
|
match.deaths = match.deaths + 1
|
2017-10-12 23:09:25 +00:00
|
|
|
match.kills_since_death = 0
|
2015-12-03 23:57:28 +00:00
|
|
|
ctf.needs_save = true
|
2015-11-29 23:55:27 +00:00
|
|
|
end
|
2015-11-29 00:05:37 +00:00
|
|
|
end)
|
|
|
|
|
|
|
|
ctf_stats.load()
|
2015-12-03 23:46:52 +00:00
|
|
|
|
2018-11-12 06:15:51 +00:00
|
|
|
dofile(minetest.get_modpath("ctf_stats") .. "/gui.lua")
|