Aaron Suen 0178fb67f3 Sort discoveries chronologically, newest first
Note that if the exact wording of a hint changes, then those hints
may be pushed to the top of the list.  This is an internal
limitation and cannot be prevented without assigning some other
stable unique identifier to hints (a lot of work and probably not
worth it for now).

A tiny amount of timing jitter is applied to hint discover time
(<1ms) to ensure that hints are always in a stable order.

Hints are identified by a short hash of the hint text, so it is
also possible (but unlikely) to have collisions that cause
hints to sort earlier in order than they normally should.
2023-04-07 20:29:45 -04:00

132 lines
3.7 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ipairs, math, minetest, nodecore, pairs, string, table
= ipairs, math, minetest, nodecore, pairs, string, table
local math_floor, math_random, string_sub, table_insert, table_sort
= math.floor, math.random, string.sub, table.insert, table.sort
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local pcache = {}
local ordercache = {}
local strings = {
progress = "@1 discovered, @2 available, @3 future",
explore = "The discovery system only alerts you to the existence of"
.. " some basic game mechanics. More advanced content, such as"
.. " emergent systems and automation, you will have to"
.. " invent yourself!",
hint = "- @1",
done = "- DONE: @1",
future = "- FUTURE: @1"
}
for k, v in pairs(strings) do
nodecore.translate_inform(v)
strings[k] = function(...) return nodecore.translate(v, ...) end
end
local function sort_by_time(pname, pmeta, tbl, suff)
local ordering = ordercache[pname .. "|" .. suff]
local metakey = modname .. "_hintsort_" .. suff
if not ordering then
local raw = pmeta:get_string(metakey)
ordering = raw and raw ~= "" and minetest.deserialize(raw) or {}
ordercache[pname] = ordering
end
local keys = {}
local revkeys = {}
for _, s in ipairs(tbl) do
local k = string_sub(minetest.sha1(s), 1, 8)
keys[s] = k
revkeys[k] = s
end
local dirty
for _, v in ipairs(tbl) do
if not ordering[keys[v]] then
ordering[keys[v]] = nodecore.gametime - math_random() / 1000
dirty = true
end
end
local t = {}
for k in pairs(ordering) do t[#t + 1] = k end
for _, k in ipairs(t) do
if not revkeys[k] then
ordering[k] = nil
dirty = true
end
end
if dirty then pmeta:set_string(metakey, minetest.serialize(ordering)) end
table_sort(tbl, function(a, b) return ordering[keys[a]] > ordering[keys[b]] end)
end
local function gethint(player)
local pname = player:get_player_name()
local now = math_floor(minetest.get_us_time() / 1000000)
local cached = pcache[pname]
if cached and cached.time == now then return cached.found end
local found, done = nodecore.hint_state(pname)
local future
local pmeta = player:get_meta()
if minetest.get_player_privs(pname).debug then
local seen = {}
for _, v in pairs(found) do seen[v] = true end
for _, v in pairs(done) do seen[v] = true end
future = {}
for _, v in pairs(nodecore.hints) do
if not seen[v] then
future[#future + 1] = strings.future(v.text)
end
end
sort_by_time(pname, pmeta, future, "future")
end
for k, v in pairs(found) do found[k] = strings.hint(v.text) end
for k, v in pairs(done) do done[k] = strings.done(v.text) end
sort_by_time(pname, pmeta, found, "found")
sort_by_time(pname, pmeta, done, "done")
local prog = #found
local left = #(nodecore.hints) - prog - #done
table_insert(found, 1, "")
table_insert(found, 1, strings.progress(#done, prog, left))
found[#found + 1] = ""
found[#found + 1] = strings.explore()
found[#found + 1] = ""
for i = 1, #done do found[#found + 1] = done[i] end
if future then
found[#found + 1] = ""
for i = 1, #future do found[#found + 1] = future[i] end
end
pcache[pname] = {time = now, found = found}
return found
end
local function clearcache(_, pname)
pcache[pname] = nil
return true
end
local mytab = {
title = "Discovery",
visible = function(_, player)
return nodecore.interact(player)
and not nodecore.hints_disabled()
or false
end,
content = gethint,
on_discover = clearcache,
on_privchange = clearcache
}
nodecore.register_inventory_tab(mytab)
nodecore.register_on_discover(function(player)
return nodecore.inventory_notify(player, "discover")
end)