311 lines
8.3 KiB
Lua
311 lines
8.3 KiB
Lua
local modname = "yl_snowball"
|
|
local modpath = core.get_modpath(modname)
|
|
|
|
local Object = dofile(modpath .. DIR_DELIM .. "classic.lua")
|
|
|
|
local snowgame = {}
|
|
|
|
local get_player_participation = yl_snowball.get_player_participation
|
|
|
|
-- game will end after this time when no one is shooting/hitting
|
|
yl_snowball.GAME_TIMEOUT = 10
|
|
|
|
-- If people continue shooting without hitting anyone, game time
|
|
-- will be extended, but only up to this time limit
|
|
yl_snowball.PLAYER_SUCK_TIME = 30
|
|
|
|
-- Time in seconds after which non-moving person is considered AFK and
|
|
-- hits stop counting
|
|
yl_snowball.AFK_TIME = 45
|
|
|
|
|
|
local active_games = {}
|
|
yl_snowball.active_games = active_games
|
|
local games_by_player = yl_snowball.games_by_player or {}
|
|
yl_snowball.games_by_player = games_by_player
|
|
|
|
local f = string.format
|
|
|
|
|
|
local game_hud = futil.define_hud(
|
|
"yl_snowball:hud_",
|
|
{
|
|
period = 0.567, -- FIXME not needed if updated manually
|
|
enabled_by_default = false,
|
|
get_hud_def = function(player)
|
|
return yl_snowball.update_hud(player)
|
|
end,
|
|
}
|
|
)
|
|
|
|
|
|
core.register_on_leaveplayer(
|
|
function(player, _timed_out)
|
|
local player_name = player:get_player_name()
|
|
if player_name ~= "" then
|
|
local game = games_by_player[player_name]
|
|
if game then
|
|
game:leave(player_name)
|
|
end
|
|
games_by_player[player_name] = nil
|
|
end
|
|
end
|
|
)
|
|
|
|
|
|
yl_snowball.update_hud = function(player)
|
|
local player_name = player:get_player_name()
|
|
local game = games_by_player[player_name]
|
|
local text = ""
|
|
if game then
|
|
if game.player_num > 1 then
|
|
text = game:get_info_string(true)
|
|
else
|
|
text = ""
|
|
end
|
|
else
|
|
game_hud:set_enabled(player, false)
|
|
end
|
|
return {
|
|
type = "text",
|
|
text = text,
|
|
number = 0xFFFFFF,
|
|
direction = -1, -- right to left?
|
|
position = { x = 0.7, y = 0.5 },
|
|
alignment = { x = 1, y = -1 },
|
|
offset = { x = -10, y = -10 },
|
|
style = 1,
|
|
}
|
|
end
|
|
|
|
|
|
local GamePlayer = Object:extend()
|
|
function GamePlayer:new(player_name)
|
|
self.player_name = player_name
|
|
self.active = true
|
|
self.hits_taken = 0
|
|
self.hits_given = 0
|
|
self.shots = 0
|
|
|
|
game_hud:set_enabled(core.get_player_by_name(player_name), true)
|
|
end
|
|
|
|
|
|
function GamePlayer:on_hit_take()
|
|
self.hits_taken = self.hits_taken + 1
|
|
end
|
|
|
|
|
|
function GamePlayer:on_hit_give()
|
|
self.hits_given = self.hits_given + 1
|
|
|
|
-- this is a bit weird: since we don't start counting shots until
|
|
-- first one hits, we may have more hits than shots :D
|
|
self.shots = math.max(self.shots, self.hits_given)
|
|
end
|
|
|
|
|
|
function GamePlayer:on_shoot()
|
|
self.shots = self.shots + 1
|
|
end
|
|
|
|
function GamePlayer:quit()
|
|
--game_hud:set_enabled(core.get_player_by_name(self.player_name), false) -- this crashes XD
|
|
end
|
|
|
|
|
|
local Game = Object:extend()
|
|
|
|
|
|
function Game:new()
|
|
local current_time = core.get_gametime()
|
|
self.time_started = current_time
|
|
self.time_last_shot = current_time
|
|
self.time_last_hit = current_time
|
|
self.players = {}
|
|
self.player_num = 0
|
|
active_games[self] = true
|
|
return self
|
|
end
|
|
|
|
|
|
function Game:get_time_left()
|
|
local current_time = core.get_gametime()
|
|
local time_left
|
|
if (self.time_last_shot - self.time_last_hit) < yl_snowball.PLAYER_SUCK_TIME then
|
|
time_left = yl_snowball.GAME_TIMEOUT - (current_time - self.time_last_shot)
|
|
else
|
|
time_left = yl_snowball.GAME_TIMEOUT + yl_snowball.PLAYER_SUCK_TIME - (current_time - self.time_last_hit)
|
|
end
|
|
return time_left > 0 and time_left or 0
|
|
end
|
|
|
|
|
|
function Game:get_info_string(show_time)
|
|
local top = {}
|
|
for p_name, player in pairs(self.players) do
|
|
table.insert(top, {p_name, player})
|
|
end
|
|
table.sort(top, function (a,b) return a[2].hits_given > b[2].hits_given end)
|
|
local lines = {}
|
|
if show_time then
|
|
table.insert(lines, f("Game ends in: %ds", self:get_time_left()))
|
|
end
|
|
for _, info in ipairs(top) do
|
|
local p_name = info[1]
|
|
local player = info[2]
|
|
local accuracy = player.hits_given/player.shots * 100
|
|
table.insert(lines, f("%03d|%03d|%05.1f%%|%s",
|
|
player.hits_given,
|
|
player.hits_taken,
|
|
accuracy == accuracy and accuracy or 0,
|
|
p_name))
|
|
end
|
|
return table.concat(lines, "\n")
|
|
end
|
|
|
|
|
|
function Game:add_player(player_name, game_player)
|
|
if self.players[player_name] then
|
|
if game_player and game_player.active then
|
|
self.players[player_name] = game_player
|
|
games_by_player[player_name] = self
|
|
end
|
|
-- Do nothing here: player merged their old scores from the
|
|
-- game where they weren't active in
|
|
else
|
|
if (not game_player) or (not game_player.active) then
|
|
game_player = GamePlayer(player_name)
|
|
end
|
|
self.players[player_name] = game_player
|
|
games_by_player[player_name] = self
|
|
|
|
-- this is a new player, increase player count
|
|
self.player_num = self.player_num + 1
|
|
end
|
|
end
|
|
|
|
|
|
function Game:get_player(player_name)
|
|
return self.players[player_name]
|
|
end
|
|
|
|
|
|
function Game:leave(player_name)
|
|
game_hud:set_enabled(core.get_player_by_name(player_name), false)
|
|
self.players[player_name].active = false
|
|
games_by_player[player_name] = nil
|
|
end
|
|
|
|
|
|
function Game:merge(other_game)
|
|
for name, game_player in pairs(other_game.players) do
|
|
self:add_player(name, game_player)
|
|
end
|
|
self.time_started = math.min(self.time_started, other_game.time_started)
|
|
self.time_last_hit = math.max(self.time_last_hit, other_game.time_last_hit)
|
|
self.time_last_shot = math.max(self.time_last_shot, other_game.time_last_shot)
|
|
active_games[other_game] = nil
|
|
end
|
|
|
|
|
|
function Game:register_hit(shooter_name, target_name)
|
|
local s_player = self:get_player(shooter_name)
|
|
s_player:on_hit_give()
|
|
|
|
local t_player = self:get_player(target_name)
|
|
t_player:on_hit_take()
|
|
|
|
local current_time = core.get_gametime()
|
|
self.time_last_hit = current_time
|
|
end
|
|
|
|
|
|
function Game:register_shot(shooter_name)
|
|
local s_player = self:get_player(shooter_name)
|
|
s_player:on_shoot()
|
|
local current_time = core.get_gametime()
|
|
self.time_last_shot = current_time
|
|
end
|
|
|
|
|
|
function Game:stop()
|
|
-- TODO show only top + your own scores
|
|
local lasted = self.time_last_shot - self.time_started
|
|
local lines = {
|
|
f("* Snowball Game Ended (lasted %ds)", lasted),
|
|
self:get_info_string(),
|
|
}
|
|
|
|
for player_name, player in pairs(self.players) do
|
|
if games_by_player[player_name] then
|
|
if self.player_num > 1 then
|
|
core.chat_send_player(player_name, table.concat(lines, "\n"))
|
|
end
|
|
player:quit()
|
|
games_by_player[player_name] = nil
|
|
end
|
|
end
|
|
|
|
active_games[self] = nil
|
|
end
|
|
|
|
|
|
function snowgame.register_hit(shooter_name, target_name)
|
|
local s_game = games_by_player[shooter_name]
|
|
local t_game = games_by_player[target_name]
|
|
|
|
if not s_game then
|
|
-- somehow landed a hit before being in a game
|
|
-- probably because left and rejoined?
|
|
s_game = Game()
|
|
s_game:add_player(shooter_name)
|
|
s_game:register_shot(shooter_name) -- if was in the game, then shot was registered in on_use
|
|
end
|
|
|
|
if afk_api.is_afk(target_name, yl_snowball.AFK_TIME) then
|
|
-- don't count hits on afk people
|
|
return
|
|
end
|
|
|
|
if s_game and t_game then
|
|
if (s_game ~= t_game) then
|
|
-- if both playing, but different games, then merge
|
|
s_game:merge(t_game)
|
|
end
|
|
-- either same game, or was merged into one
|
|
s_game:register_hit(shooter_name, target_name)
|
|
end
|
|
|
|
end
|
|
|
|
|
|
function snowgame.register_shot(shooter_name)
|
|
local s_game = games_by_player[shooter_name]
|
|
if not s_game then
|
|
-- started shooting first time, started a game
|
|
s_game = Game()
|
|
s_game:add_player(shooter_name)
|
|
games_by_player[shooter_name] = s_game
|
|
end
|
|
s_game:register_shot(shooter_name)
|
|
end
|
|
|
|
|
|
futil.register_globalstep({
|
|
period = 1.31,
|
|
func = function(dtime)
|
|
local current_time = core.get_gametime()
|
|
for game,_ in pairs(active_games) do
|
|
if game:get_time_left() <= 0 then
|
|
game:stop()
|
|
end
|
|
end
|
|
end,
|
|
})
|
|
|
|
|
|
yl_snowball.snowgame = snowgame
|
|
|
|
return snowgame
|