aboutsummaryrefslogtreecommitdiff
path: root/ch_core/ap.lua
diff options
context:
space:
mode:
Diffstat (limited to 'ch_core/ap.lua')
-rw-r--r--ch_core/ap.lua628
1 files changed, 628 insertions, 0 deletions
diff --git a/ch_core/ap.lua b/ch_core/ap.lua
new file mode 100644
index 0000000..148eb77
--- /dev/null
+++ b/ch_core/ap.lua
@@ -0,0 +1,628 @@
+ch_core.open_submod("ap", {chat = true, data = true, events = true, hud = true, lib = true})
+
+-- Systém „Activity Points“
+
+local ap_increase = 2
+local ap_decrease = 1
+local ap_min = 0
+local ap_max_default = 26
+local ap_max_book = 10
+local min = math.min
+local def
+
+ch_data.initial_offline_charinfo.ap_version = ch_core.verze_ap
+
+local levels = {
+ {base = 0, count = 500, next = 500},
+}
+
+local ifthenelse = assert(ch_core.ifthenelse)
+
+ch_core.register_event_type("level_up", {
+ description = "zvýšení úrovně",
+ access = "public",
+ chat_access = "public",
+ color = "#eeee00",
+})
+
+ch_core.register_event_type("level_down", {
+ description = "snížení úrovně",
+ access = "public",
+ chat_access = "public",
+})
+
+local function add_level(level, count)
+ local last_level_number = #levels
+ local last_level = levels[last_level_number]
+ levels[last_level_number + 1] = {
+ base = last_level.base + last_level.count,
+ count = count,
+ next = last_level.base + last_level.count + count
+ }
+ return last_level_number + 1
+end
+
+add_level(2, 1100)
+add_level(3, 2400)
+add_level(4, 4200)
+add_level(5, 6500)
+add_level(6, 9300)
+add_level(7, 12600)
+add_level(8, 16400)
+add_level(9, 20700)
+add_level(10, 25500)
+add_level(11, 30800)
+add_level(12, 36600)
+add_level(13, 42900)
+add_level(14, 49700)
+add_level(15, 57000)
+add_level(16, 64800)
+add_level(17, 73100)
+add_level(18, 81900)
+add_level(19, 91200)
+add_level(20, 101000)
+add_level(21, 111300)
+add_level(22, 122100)
+add_level(23, 133400)
+add_level(24, 145200)
+add_level(25, 157500)
+add_level(26, 170300)
+add_level(27, 183600)
+add_level(28, 197400)
+add_level(29, 211700)
+add_level(30, 226500)
+add_level(31, 241800)
+add_level(32, 257600)
+add_level(33, 273900)
+add_level(34, 290700)
+add_level(35, 308000)
+add_level(36, 325800)
+add_level(37, 344100)
+add_level(38, 362900)
+add_level(39, 382200)
+add_level(40, 402000)
+add_level(41, 422300)
+add_level(42, 443100)
+add_level(43, 464400)
+add_level(44, 486200)
+add_level(45, 508500)
+add_level(46, 531300)
+add_level(47, 554600)
+add_level(48, 578400)
+add_level(49, 602700)
+add_level(50, 627500)
+add_level(51, 652800)
+add_level(52, 678600)
+add_level(53, 704900)
+add_level(54, 731700)
+add_level(55, 759000)
+add_level(56, 786800)
+add_level(57, 815100)
+add_level(58, 843900)
+add_level(59, 873200)
+add_level(60, 903000)
+add_level(61, 933300)
+add_level(62, 964100)
+add_level(63, 995400)
+add_level(64, 1027200)
+add_level(65, 1059500)
+add_level(66, 1092300)
+add_level(67, 1125600)
+add_level(68, 1159400)
+add_level(69, 1193700)
+add_level(70, 1228500)
+add_level(71, 1263800)
+add_level(72, 1299600)
+add_level(73, 1335900)
+add_level(74, 1372700)
+add_level(75, 1410000)
+add_level(76, 1447800)
+add_level(77, 1486100)
+add_level(78, 1524900)
+add_level(79, 1564200)
+add_level(80, 1604000)
+add_level(81, 1644300)
+add_level(82, 1685100)
+add_level(83, 1726400)
+add_level(84, 1768200)
+add_level(85, 1810500)
+add_level(86, 1853300)
+add_level(87, 1896600)
+add_level(88, 1940400)
+add_level(89, 1984700)
+add_level(90, 2029500)
+add_level(91, 2074800)
+add_level(92, 2120600)
+add_level(93, 2166900)
+add_level(94, 2213700)
+add_level(95, 2261000)
+add_level(96, 2308800)
+add_level(97, 2357100)
+add_level(98, 2405900)
+add_level(99, 2455200)
+add_level(100, 2505000)
+add_level(101, 2555300)
+add_level(102, 2606100)
+add_level(103, 2657400)
+add_level(104, 2709200)
+add_level(105, 2761500)
+add_level(106, 2814300)
+add_level(107, 2867600)
+add_level(108, 2921400)
+add_level(109, 2975700)
+add_level(110, 3030500)
+add_level(111, 3085800)
+add_level(112, 3141600)
+add_level(113, 3197900)
+add_level(114, 3254700)
+add_level(115, 3312000)
+add_level(116, 3369800)
+add_level(117, 3428100)
+add_level(118, 3486900)
+add_level(119, 3546200)
+add_level(120, 3606000)
+add_level(121, 3666300)
+add_level(122, 3727100)
+add_level(123, 3788400)
+add_level(124, 3850200)
+add_level(125, 3912500)
+add_level(126, 3975300)
+add_level(127, 4038600)
+add_level(128, 4102400)
+
+local function adjust_coef(t, key, amount)
+ local ap_max = ap_max_default
+ if key == "book_coef" then
+ ap_max = ap_max_book
+ end
+ local value = t[key] + amount
+ if value > ap_max then
+ value = ap_max
+ elseif value < ap_min then
+ value = ap_min
+ end
+ t[key] = value
+ return value
+end
+
+function ch_core.ap_get_level(l)
+ if l < 0 then
+ return nil
+ else
+ return levels[min(l, #levels)]
+ end
+end
+
+function ch_core.ap_announce_craft(player_or_player_name, hash)
+ -- TODO: check for repeating hash
+ local player_name
+ if type(player_or_player_name) == "string" then
+ player_name = player_or_player_name
+ else
+ player_name = player_or_player_name and player_or_player_name:is_player() and player_or_player_name:get_player_name()
+ end
+ local online_charinfo = player_name and ch_data.online_charinfo[player_name]
+ if online_charinfo then
+ local ap = online_charinfo.ap
+ if ap then
+ ap.craft_gen = ap.craft_gen + 1
+ end
+ end
+end
+
+local update_xp_hud
+
+if minetest.get_modpath("hudbars") then
+ update_xp_hud = function(player, player_name, offline_charinfo)
+ local pinfo = ch_core.normalize_player(player)
+ local player_role = pinfo.role
+ local has_creative_priv = false
+ if pinfo.privs.creative then
+ local online_charinfo = ch_data.online_charinfo[pinfo.player_name]
+ if online_charinfo ~= nil then
+ -- a creative priv icon can only appear after a second after login
+ has_creative_priv = minetest.get_us_time() - online_charinfo.join_timestamp > 1000000
+ end
+ end
+ local role_icon = ch_core.player_role_to_image(player_role, has_creative_priv, 16)
+ local skryt_body = player_role == "new" or offline_charinfo.skryt_body == 1
+
+ if skryt_body then
+ hb.hide_hudbar(player, "ch_xp")
+ else
+ local level, xp, max_xp = offline_charinfo.ap_level, offline_charinfo.ap_xp
+ if level and xp then
+ max_xp = ch_core.ap_get_level(level).count
+ else
+ level, xp, max_xp = 1, 0, ch_core.ap_get_level(1).count
+ end
+ hb.unhide_hudbar(player, "ch_xp")
+ hb.change_hudbar(player, "ch_xp", math.min(xp, max_xp), max_xp, role_icon, nil, nil, level, nil)
+ end
+ end
+else
+ update_xp_hud = function(player, player_name, offline_charinfo)
+ return false
+ end
+end
+
+function ch_core.ap_init(player, online_charinfo, offline_charinfo)
+ local vector_zero = vector.zero()
+ local default_coef = 0
+ local player_name = online_charinfo.player_name
+
+ if offline_charinfo.ap_level == nil or offline_charinfo.ap_version ~= ch_core.verze_ap then
+ offline_charinfo.ap_level = 1
+ offline_charinfo.ap_xp = 0
+ offline_charinfo.ap_version = ch_core.verze_ap
+ ch_data.save_offline_charinfo(player_name)
+ end
+ local ap = {
+ -- aktuální údaje
+ dig_gen = 0,
+ dig_pos = vector_zero,
+ look_h = 0,
+ look_h_gen = 0,
+ look_v = 0,
+ look_v_gen = 0,
+ place_gen = 0,
+ place_pos = vector_zero,
+ pos = player:get_pos(),
+ pos_x_gen = 0,
+ pos_y_gen = 0,
+ pos_z_gen = 0,
+ velocity = player:get_velocity(),
+ velocity_x_gen = 0,
+ velocity_y_gen = 0,
+ velocity_z_gen = 0,
+ control = player:get_player_control_bits(),
+ control_gen = 0,
+ chat_mistni_gen = 0,
+ chat_celoserverovy_gen = 0,
+ chat_sepot_gen = 0,
+ chat_soukromy_gen = 0,
+ craft_gen = 0,
+ book_gen = 0,
+
+ -- koeficienty
+ book_coef = default_coef,
+ craft_coef = default_coef,
+ chat_coef = default_coef,
+ dig_coef = default_coef,
+ place_coef = default_coef,
+ walk_coef = default_coef,
+ }
+ online_charinfo.ap = table.copy(ap)
+ online_charinfo.ap1 = ap
+ online_charinfo.ap2 = ap
+ online_charinfo.ap3 = ap
+ online_charinfo.ap_modify_timestamp = minetest.get_us_time()
+
+ return true
+end
+
+function ch_core.ap_add(player_name, offline_charinfo, points_to_add, debug_coef_changes)
+ local player = minetest.get_player_by_name(player_name)
+
+ if points_to_add == nil or points_to_add == 0 then
+ if player ~= nil then
+ update_xp_hud(player, player_name, offline_charinfo)
+ end
+ return 0
+ end
+
+ local player_role = ch_core.get_player_role(player_name)
+ local old_level, old_xp = offline_charinfo.ap_level, offline_charinfo.ap_xp
+ local new_level = old_level
+ local level_def = ch_core.ap_get_level(old_level)
+ local new_xp = old_xp + points_to_add
+
+ if points_to_add > 0 then
+ -- vyhodnotit zvýšení úrovně
+ while new_xp >= level_def.count do
+ new_xp = new_xp - level_def.count
+ new_level = new_level + 1
+ level_def = ch_core.ap_get_level(new_level)
+ end
+ else
+ -- vyhodnotit snížení úrovně
+ while new_xp < 0 do
+ if new_level > 1 then
+ new_level = new_level - 1
+ level_def = ch_core.ap_get_level(new_level)
+ new_xp = new_xp + level_def.count
+ else
+ new_xp = 0
+ break
+ end
+ end
+ end
+
+ -- aktualizovat offline_charinfo
+ offline_charinfo.ap_xp = new_xp
+ if new_level ~= old_level then
+ offline_charinfo.ap_level = new_level
+ end
+ ch_data.save_offline_charinfo(player_name)
+
+ -- je-li postava online, aktualizovat HUD
+ if player ~= nil then
+ update_xp_hud(player, player_name, offline_charinfo)
+ end
+
+ if debug_coef_changes then
+ core.log("info", player_name..": level_op="..(new_level > old_level and ">" or new_level < old_level and "<" or "=")..", level="..old_level.."=>"..new_level..", xp="..old_xp.."=>"..new_xp..", coefs = "..table.concat(debug_coef_changes))
+ end
+
+ -- oznámit
+ if player_role ~= "new" then
+ if new_level > old_level then
+ -- ch_core.systemovy_kanal("", "Postava "..ch_core.prihlasovaci_na_zobrazovaci(player_name).." dosáhla úrovně "..new_level)
+ ch_core.add_event("level_up", "Postava {PLAYER} dosáhla úrovně "..new_level, player_name)
+ elseif new_level < old_level then
+ -- ch_core.systemovy_kanal("", "Postava "..ch_core.prihlasovaci_na_zobrazovaci(player_name).." klesla na úroveň "..new_level)
+ ch_core.add_event("level_down", "Postava {PLAYER} klesla na úroveň "..new_level, player_name)
+ end
+ end
+
+ -- je-li postava online, aktualizovat online_charinfo.ap_modify_timestamp
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo ~= nil then
+ online_charinfo.ap_modify_timestamp = minetest.get_us_time()
+ end
+
+ return new_xp, new_level
+end
+
+function ch_core.ap_update(player, online_charinfo, offline_charinfo)
+ local result, ap, ap1, ap2, ap3 = 0, online_charinfo.ap, online_charinfo.ap1, online_charinfo.ap2, online_charinfo.ap3
+
+ -- posunout struktury
+ local ap_new = table.copy(ap)
+ online_charinfo.ap3 = ap2
+ online_charinfo.ap2 = ap1
+ online_charinfo.ap1 = ap
+ online_charinfo.ap = ap_new
+
+ local control_diff_1 = ap.control_gen - ap1.control_gen
+ local control_diff_2 = ap1.control_gen - ap2.control_gen
+ local control_diff_3 = ap2.control_gen - ap3.control_gen
+
+ -- aktivity
+ local act_book = ap.book_gen > ap1.book_gen
+ local act_chat = ap.chat_mistni_gen > ap1.chat_mistni_gen or ap.chat_celoserverovy_gen > ap1.chat_celoserverovy_gen or ap.chat_sepot_gen > ap1.chat_sepot_gen or ap.chat_soukromy_gen > ap1.chat_soukromy_gen
+ local act_craft = ap.craft_gen > ap1.craft_gen
+ local act_dig = ap.dig_gen > ap1.dig_gen and (not ap.dig_pos or not ap1.dig_pos or not ap2.dig_pos or vector.angle(vector.subtract(ap2.dig_pos, ap1.dig_pos), vector.subtract(ap1.dig_pos, ap.dig_pos)) ~= 0)
+ local act_place = ap.place_gen - ap1.place_gen > 1
+ local act_walk =
+ (ap.look_h_gen - ap1.look_h_gen) + (ap.look_v_gen - ap1.look_v_gen) > 2 and
+ ap.velocity_x_gen - ap1.velocity_x_gen > 8 and
+ ap.velocity_z_gen - ap1.velocity_z_gen > 8 and
+ control_diff_1 ~= control_diff_2 and
+ control_diff_2 ~= control_diff_3 and
+ control_diff_3 ~= control_diff_1
+
+ local act_any = act_chat or act_craft or act_dig or act_place or act_walk
+ local debug_coef_changes = {}
+
+ for coef_key, active in pairs({book_coef = act_book, chat_coef = act_chat, craft_coef = act_craft, dig_coef = act_dig, place_coef = act_place, walk_coef = act_walk}) do
+ local adjustment
+ if not active then
+ adjustment = -ap_decrease
+ elseif coef_key ~= "chat_coef" then
+ adjustment = ap_increase
+ else
+ adjustment = 2 * ap_increase -- čet => dvojnásobný vzrůst
+ end
+ local old_coef = ap_new[coef_key]
+ local new_coef = adjust_coef(ap_new, coef_key, adjustment)
+ result = result + new_coef
+ if new_coef ~= old_coef then
+ table.insert(debug_coef_changes, coef_key..":"..old_coef.."=>"..new_coef..";")
+ end
+ end
+
+ if act_any then
+ offline_charinfo.past_ap_playtime = offline_charinfo.past_ap_playtime + ch_core.ap_interval
+ ch_data.save_offline_charinfo(online_charinfo.player_name)
+ end
+
+ if result == 0 then
+ if debug_coef_changes[1] then
+ minetest.log("action", online_charinfo.player_name..": no xp change, but :"..table.concat(debug_coef_changes))
+ end
+ return 0
+ end
+
+ ch_core.ap_add(online_charinfo.player_name, offline_charinfo, result, debug_coef_changes)
+ return result
+end
+
+local function on_dignode(pos, oldnode, digger)
+ if digger == nil then
+ return
+ end
+ local player_name = digger:is_player() and digger:get_player_name()
+ local online_charinfo = player_name and ch_data.online_charinfo[player_name]
+ if online_charinfo then -- nil if digged by digtron
+ local ap = online_charinfo.ap
+ if ap then
+ local old_pos = ap.dig_pos
+ if not old_pos or pos.x ~= old_pos.x or pos.y ~= old_pos.y or pos.z ~= old_pos.z then
+ ap.dig_gen = ap.dig_gen + 1
+ ap.dig_pos = pos
+ end
+ end
+ end
+end
+
+local function on_placenode(pos, newnode, placer, oldnode, itemstack, pointed_thing)
+ if placer == nil then
+ return
+ end
+ local player_name = placer:is_player() and placer:get_player_name()
+ if player_name == nil or player_name == false or player_name == "" then
+ return
+ end
+ local online_charinfo = ch_data.online_charinfo[player_name]
+ if online_charinfo then
+ local ap = online_charinfo.ap
+ if ap then
+ local old_pos = ap.place_pos
+ pos = vector.round(pos)
+ if not old_pos or pos.x ~= old_pos.x or pos.y ~= old_pos.y or pos.z ~= old_pos.z then
+ ap.place_gen = ap.place_gen + 1
+ ap.place_pos = pos
+ end
+ end
+ else
+ minetest.log("warning", "on_placenode called with an offline player "..player_name)
+ end
+end
+
+local function on_craft(itemstack, player, old_craft_grid, craft_inv)
+ if craft_inv:get_location().type == "player" then
+ local ap = ch_core.safe_get_3(ch_data.online_charinfo, assert(player:get_player_name()), "ap")
+ if ap then
+ ap.craft_gen = ap.craft_gen + 1
+ end
+ end
+end
+
+local function on_player_inventory_action(player, action, inventory, inventory_info)
+ local ap = ch_core.safe_get_3(ch_data.online_charinfo, assert(player:get_player_name()), "ap")
+ if ap then
+ ap.craft_gen = ap.craft_gen + 1
+ end
+end
+
+local function on_item_eat(hp_change, replace_with_item, itemstack, user, pointed_thing)
+ local ap = ch_core.safe_get_3(ch_data.online_charinfo, assert(user:get_player_name()), "ap")
+ if ap then
+ ap.craft_gen = ap.craft_gen + 1
+ end
+end
+
+local function on_item_pickup(itemstack, picker, pointed_thing, time_from_last_punch, ...)
+ local ap = ch_core.safe_get_3(ch_data.online_charinfo, assert(picker:get_player_name()), "ap")
+ if ap then
+ ap.craft_gen = ap.craft_gen + 1
+ end
+end
+
+minetest.register_on_craft(on_craft)
+minetest.register_on_dignode(on_dignode)
+minetest.register_on_placenode(on_placenode)
+minetest.register_on_player_inventory_action(on_player_inventory_action)
+minetest.register_on_item_eat(on_item_eat)
+minetest.register_on_item_pickup(on_item_pickup)
+
+local function body(admin_name, param)
+ local player_name, ap_to_add = param:match("^(%S+)%s(%S+)$")
+ if not player_name then
+ return false, "Chybné zadání!"
+ end
+ player_name = ch_core.jmeno_na_prihlasovaci(player_name)
+ local offline_charinfo = ch_data.offline_charinfo[player_name]
+ if not offline_charinfo then
+ return false, "Vnitřní chyba: nenalezeno offline_charinfo pro '"..player_name.."'!"
+ end
+ ap_to_add = tonumber(ap_to_add)
+ local old_level, old_xp = offline_charinfo.ap_level, offline_charinfo.ap_xp
+ ch_core.ap_add(player_name, offline_charinfo, ap_to_add)
+ local new_level, new_xp = offline_charinfo.ap_level, offline_charinfo.ap_xp
+ return true, "Změna: úroveň "..old_level.." => "..new_level..", body: "..old_xp.." => "..new_xp
+end
+
+def = {
+ params = "<Jméno_Postavy> <celé_číslo>",
+ privs = {server = true},
+ description = "Přidá či odebere příslušné postavě body.",
+ func = body,
+}
+minetest.register_chatcommand("body", def)
+
+--[[
+ Zobrazí či skryje ukazatel úrovní a bodů. Postava nemusí být zrovna
+ ve hře.
+]]
+function ch_core.showhide_ap_hud(player_name, show)
+ local offline_charinfo = ch_data.offline_charinfo[player_name]
+ if not offline_charinfo then
+ return false
+ end
+ local new_value = ifthenelse(show, 0, 1)
+ if offline_charinfo.skryt_body == new_value then
+ return nil
+ end
+ offline_charinfo.skryt_body = new_value
+ ch_data.save_offline_charinfo(player_name)
+ local player = minetest.get_player_by_name(player_name)
+ if player ~= nil then
+ update_xp_hud(player, player_name, offline_charinfo)
+ end
+ return true
+end
+
+-- HUD
+if minetest.get_modpath("hudbars") then
+ local hudbar_formatstring = "úr. @1: @2/@3"
+ local hudbar_formatstring_config = {
+ order = { "label", "value", "max_value" },
+ textdomain = "hudbars",
+ }
+ local hudbar_defaults = {
+ icon = "ch_core_empty.png", bgicon = nil, bar = "hudbars_bar_xp.png"
+ }
+ hb.register_hudbar("ch_xp", 0xFFFFFF, "*", hudbar_defaults, 0, 100, true, hudbar_formatstring, hudbar_formatstring_config)
+ minetest.register_on_joinplayer(function(player, last_login)
+ local pinfo = ch_core.normalize_player(player)
+ local offline_charinfo = ch_data.get_offline_charinfo(pinfo.player_name)
+ --[[
+ local level, xp, _max_xp = offline_charinfo.ap_level, offline_charinfo.ap_xp
+ if not (level and xp) then
+ max_xp = ch_core.ap_get_level(level).count
+ else
+ level, xp, _max_xp = 1, 0, ch_core.ap_get_level(1).count
+ end
+ ]]
+ local skryt_body = pinfo.role == "new" or offline_charinfo.skryt_body == 1
+ hb.init_hudbar(player, "ch_xp", 0, 10, skryt_body)
+ update_xp_hud(player, pinfo.player_name, offline_charinfo)
+ end)
+
+ def = {
+ description = "Skryje hráči/ce ukazatel úrovní a bodů, je-li zobrazen.",
+ func = function(player_name, param)
+ local result = ch_core.showhide_ap_hud(player_name, false)
+ if result == false then
+ return false, "Vnitřní chyba"
+ elseif result == nil then
+ return false, "Ukazatel úrovně a bodů je již skryt."
+ else
+ return true, "Ukazatel úrovně a bodů úspěšně skryt."
+ end
+ end,
+ }
+
+ minetest.register_chatcommand("skrýtbody", def)
+ minetest.register_chatcommand("skrytbody", def)
+
+ def = {
+ description = "Zobrazí hráči/ce ukazatel úrovní a bodů, je-li skryt.",
+ func = function(player_name, param)
+ local result = ch_core.showhide_ap_hud(player_name, true)
+ if result == false then
+ return false, "Vnitřní chyba"
+ elseif result == nil then
+ return false, "Ukazatel úrovně a bodů je již zobrazen."
+ else
+ return true, "Ukazatel úrovně a bodů úspěšně zobrazen."
+ end
+ end,
+ }
+
+ minetest.register_chatcommand("zobrazitbody", def)
+end
+
+ch_core.close_submod("ap")