diff options
Diffstat (limited to 'ch_core/ap.lua')
-rw-r--r-- | ch_core/ap.lua | 628 |
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") |