123 lines
3.9 KiB
Lua

local shown = {}
local settings = minetest.settings
local MSG_BUBBLE_LIFETIME = tonumber(settings:get("message_bubbles_lifetime")) or 10
local MAX_CHARS_PER_LINE = tonumber(settings:get("message_bubbles_char_limit")) or 40
local MAX_NUM_LINES = tonumber(settings:get("message_bubbles_max_num_lines")) or 1
local MSG_BUBBLE_PREFIX = settings:get("message_bubbles_prefix") or "Says: "
local cumulativeTime = 0
-- gsub requires escaping of custom characters otherwise the replacement can break
local function literal(str)
return str:gsub("[%(%)%.%%%+%-%*%?%[%^%$%]]", "%%%1")
end
local function removeEscape(str)
return str:gsub("%%", "")
end
local function trim(msg)
if not msg or not type(msg) == "string" then return "" end
if string.len(msg) <= MAX_CHARS_PER_LINE then return msg end
if MAX_NUM_LINES <= 1 then return string.sub(msg, 1, MAX_CHARS_PER_LINE)..".." end
-- else: we have multilines to split
local words = string.split(msg, " ", false)
local ret = ""
local currLine = 1
local wordIndex = 1
local line = ""
while currLine <= MAX_NUM_LINES do
local adjMaxChPerLine = MAX_CHARS_PER_LINE
if currLine == 1 then adjMaxChPerLine = MAX_CHARS_PER_LINE - string.len(MSG_BUBBLE_PREFIX) end
local space = ""
if string.len(line) > 0 then space = " " end
local tmp = line..space
if words[wordIndex] then
tmp = line..space..words[wordIndex]
else -- we used all the words, return early
return ret..line
end
if string.len(tmp) <= adjMaxChPerLine then
-- adding this word didn't overflow the line length
line = tmp
wordIndex = wordIndex + 1
else -- the word pushed the line length over the limit
if currLine == MAX_NUM_LINES then
-- last line, just cut last word and return early
line = string.sub(tmp, 1, adjMaxChPerLine)
return ret..line..".."
else
-- don't add the word, leave it to the next line (don't increment wordIndex)
ret = ret..line.."\n"
end
line = ""
currLine = currLine + 1
end
end
return ret
end
minetest.register_on_chat_message(function(name, origMessage)
if minetest.get_player_privs(name).shout then
local player = minetest.get_player_by_name(name)
if not player then return end
local msg = "\n"..MSG_BUBBLE_PREFIX..trim(origMessage)
msg = removeEscape(msg)
local nametag = player:get_nametag_attributes()
local nametagText = nametag.text
local hadToAddName = false
if shown[name] then
local currText = shown[name].text
if shown[name].wasEmpty then
shown[name].text = name..msg
shown[name].addedMsg = msg
else
shown[name].text = string.gsub(nametagText, literal(shown[name].addedMsg), msg)
shown[name].addedMsg = msg
end
shown[name].time = minetest.get_gametime()
else
local addedMsg = msg
if not nametagText or nametagText == "" then
msg = name..msg
hadToAddName = true
else
msg = nametagText..msg
end
shown[name] = {
text = msg,
addedMsg = addedMsg,
time = minetest.get_gametime(),
wasEmpty = hadToAddName,
}
end
nametag.text = shown[name].text
player:set_nametag_attributes(nametag)
end
end)
minetest.register_globalstep(function(dtime)
cumulativeTime = cumulativeTime + dtime
if cumulativeTime > 1 then
cumulativeTime = 0
local currTime = minetest.get_gametime()
for playerName, info in pairs(shown) do
if currTime - info.time > MSG_BUBBLE_LIFETIME then
local player = minetest.get_player_by_name(playerName)
if player then
local nametag = player:get_nametag_attributes()
if info.wasEmpty then
nametag.text = ""
else
nametag.text = string.gsub(nametag.text, literal(info.addedMsg), "")
end
player:set_nametag_attributes(nametag)
end
shown[playerName] = nil
end
end
end
end)