Merge pull request 'Rework XP API' (#1888) from EliasFleckenstein03/MineClone2:xp into master
Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/1888
This commit is contained in:
commit
88e084cbca
|
@ -290,10 +290,10 @@ function minetest.handle_node_drops(pos, drops, digger)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if digger and mcl_experience.throw_experience and not silk_touch_drop then
|
if digger and mcl_experience.throw_xp and not silk_touch_drop then
|
||||||
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
|
local experience_amount = minetest.get_item_group(dug_node.name,"xp")
|
||||||
if experience_amount > 0 then
|
if experience_amount > 0 then
|
||||||
mcl_experience.throw_experience(pos, experience_amount)
|
mcl_experience.throw_xp(pos, experience_amount)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -122,7 +122,7 @@ mobs.death_logic = function(self, dtime)
|
||||||
if self.death_animation_timer >= 1.25 then
|
if self.death_animation_timer >= 1.25 then
|
||||||
item_drop(self,false,1)
|
item_drop(self,false,1)
|
||||||
mobs.death_effect(self)
|
mobs.death_effect(self)
|
||||||
mcl_experience.throw_experience(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
|
mcl_experience.throw_xp(self.object:get_pos(), math_random(self.xp_min, self.xp_max))
|
||||||
if self.on_die then
|
if self.on_die then
|
||||||
self.on_die(self, self.object:get_pos())
|
self.on_die(self, self.object:get_pos())
|
||||||
end
|
end
|
||||||
|
|
|
@ -103,7 +103,7 @@ mobs:register_mob("mobs_mc:enderdragon", {
|
||||||
mcl_portals.spawn_gateway_portal()
|
mcl_portals.spawn_gateway_portal()
|
||||||
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
|
mcl_structures.call_struct(self._portal_pos, "end_exit_portal_open")
|
||||||
if self._initial then
|
if self._initial then
|
||||||
mcl_experience.throw_experience(pos, 11500) -- 500 + 11500 = 12000
|
mcl_experience.throw_xp(pos, 11500) -- 500 + 11500 = 12000
|
||||||
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
|
minetest.set_node(vector.add(self._portal_pos, vector.new(3, 5, 3)), {name = mobs_mc.items.dragon_egg})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
63
mods/HUD/mcl_experience/bottle.lua
Normal file
63
mods/HUD/mcl_experience/bottle.lua
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_experience:bottle",{
|
||||||
|
textures = {"mcl_experience_bottle.png"},
|
||||||
|
hp_max = 1,
|
||||||
|
visual_size = {x = 0.35, y = 0.35},
|
||||||
|
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
|
||||||
|
pointable = false,
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
local pos = self.object:get_pos()
|
||||||
|
local node = minetest.get_node(pos)
|
||||||
|
local n = node.name
|
||||||
|
if n ~= "air" and n ~= "mcl_portals:portal" and n ~= "mcl_portals:portal_end" and minetest.get_item_group(n, "liquid") == 0 then
|
||||||
|
minetest.sound_play("mcl_potions_breaking_glass", {pos = pos, max_hear_distance = 16, gain = 1})
|
||||||
|
mcl_experience.throw_xp(pos, math.random(3, 11))
|
||||||
|
minetest.add_particlespawner({
|
||||||
|
amount = 50,
|
||||||
|
time = 0.1,
|
||||||
|
minpos = vector.add(pos, vector.new(-0.1, 0.5, -0.1)),
|
||||||
|
maxpos = vector.add(pos, vector.new( 0.1, 0.6, 0.1)),
|
||||||
|
minvel = vector.new(-2, 0, -2),
|
||||||
|
maxvel = vector.new( 2, 2, 2),
|
||||||
|
minacc = vector.new(0, 0, 0),
|
||||||
|
maxacc = vector.new(0, 0, 0),
|
||||||
|
minexptime = 0.5,
|
||||||
|
maxexptime = 1.25,
|
||||||
|
minsize = 1,
|
||||||
|
maxsize = 2,
|
||||||
|
collisiondetection = true,
|
||||||
|
vertical = false,
|
||||||
|
texture = "mcl_particles_effect.png^[colorize:blue:127",
|
||||||
|
})
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
|
||||||
|
local function throw_xp_bottle(pos, dir, velocity)
|
||||||
|
minetest.sound_play("mcl_throwing_throw", {pos = pos, gain = 0.4, max_hear_distance = 16}, true)
|
||||||
|
local obj = minetest.add_entity(pos, "mcl_experience:bottle")
|
||||||
|
obj:set_velocity(vector.multiply(dir, velocity))
|
||||||
|
local acceleration = vector.multiply(dir, -3)
|
||||||
|
acceleration.y = -9.81
|
||||||
|
obj:set_acceleration(acceleration)
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_craftitem("mcl_experience:bottle", {
|
||||||
|
description = "Bottle o' Enchanting",
|
||||||
|
inventory_image = "mcl_experience_bottle.png",
|
||||||
|
wield_image = "mcl_experience_bottle.png",
|
||||||
|
stack_max = 64,
|
||||||
|
on_use = function(itemstack, placer, pointed_thing)
|
||||||
|
throw_xp_bottle(vector.add(placer:get_pos(), vector.new(0, 1.5, 0)), placer:get_look_dir(), 10)
|
||||||
|
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||||||
|
itemstack:take_item()
|
||||||
|
end
|
||||||
|
return itemstack
|
||||||
|
end,
|
||||||
|
_on_dispense = function(_, pos, _, _, dir)
|
||||||
|
throw_xp_bottle(vector.add(pos, vector.multiply(dir, 0.51)), dir, 10)
|
||||||
|
end
|
||||||
|
})
|
||||||
|
|
39
mods/HUD/mcl_experience/command.lua
Normal file
39
mods/HUD/mcl_experience/command.lua
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
local S = minetest.get_translator(minetest.get_current_modname())
|
||||||
|
|
||||||
|
minetest.register_chatcommand("xp", {
|
||||||
|
params = S("[[<player>] <xp>]"),
|
||||||
|
description = S("Gives a player some XP"),
|
||||||
|
privs = {server=true},
|
||||||
|
func = function(name, params)
|
||||||
|
local player, xp = nil, 1000
|
||||||
|
local P, i = {}, 0
|
||||||
|
for str in string.gmatch(params, "([^ ]+)") do
|
||||||
|
i = i + 1
|
||||||
|
P[i] = str
|
||||||
|
end
|
||||||
|
if i > 2 then
|
||||||
|
return false, S("Error: Too many parameters!")
|
||||||
|
end
|
||||||
|
if i > 0 then
|
||||||
|
xp = tonumber(P[i])
|
||||||
|
end
|
||||||
|
if i < 2 then
|
||||||
|
player = minetest.get_player_by_name(name)
|
||||||
|
end
|
||||||
|
if i == 2 then
|
||||||
|
player = minetest.get_player_by_name(P[1])
|
||||||
|
end
|
||||||
|
|
||||||
|
if not xp then
|
||||||
|
return false, S("Error: Incorrect value of XP")
|
||||||
|
end
|
||||||
|
|
||||||
|
if not player then
|
||||||
|
return false, S("Error: Player not found")
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_experience.add_xp(player, xp)
|
||||||
|
|
||||||
|
return true, S("Added @1 XP to @2, total: @3, experience level: @4", tostring(xp), player:get_player_name(), tostring(mcl_experience.get_xp(player)), tostring(mcl_experience.get_level(player)))
|
||||||
|
end,
|
||||||
|
})
|
|
@ -1,203 +1,25 @@
|
||||||
local S = minetest.get_translator(minetest.get_current_modname())
|
mcl_experience = {
|
||||||
|
on_add_xp = {},
|
||||||
mcl_experience = {}
|
|
||||||
|
|
||||||
local vector = vector
|
|
||||||
local math = math
|
|
||||||
local string = string
|
|
||||||
|
|
||||||
local pool = {}
|
|
||||||
local registered_nodes
|
|
||||||
local max_xp = 2^31-1
|
|
||||||
local max_orb_age = 300 -- seconds
|
|
||||||
|
|
||||||
local gravity = {x = 0, y = -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), z = 0}
|
|
||||||
local size_min, size_max = 20, 59 -- percents
|
|
||||||
local delta_size = size_max - size_min
|
|
||||||
local size_to_xp = {
|
|
||||||
{-32768, 2}, -- 1
|
|
||||||
{ 3, 6}, -- 2
|
|
||||||
{ 7, 16}, -- 3
|
|
||||||
{ 17, 36}, -- 4
|
|
||||||
{ 37, 72}, -- 5
|
|
||||||
{ 73, 148}, -- 6
|
|
||||||
{ 149, 306}, -- 7
|
|
||||||
{ 307, 616}, -- 8
|
|
||||||
{ 617, 1236}, -- 9
|
|
||||||
{ 1237, 2476}, --10
|
|
||||||
{ 2477, 32767} --11
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local function xp_to_size(xp)
|
local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
local i, l = 1, #size_to_xp
|
|
||||||
while (xp > size_to_xp[i][1]) and (i < l) do
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
return ((i-1) / (l-1) * delta_size + size_min)/100
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_on_mods_loaded(function()
|
dofile(modpath .. "/command.lua")
|
||||||
registered_nodes = minetest.registered_nodes
|
dofile(modpath .. "/orb.lua")
|
||||||
end)
|
dofile(modpath .. "/bottle.lua")
|
||||||
|
|
||||||
local function load_data(player)
|
-- local storage
|
||||||
local name = player:get_player_name()
|
|
||||||
pool[name] = {}
|
|
||||||
local temp_pool = pool[name]
|
|
||||||
local meta = player:get_meta()
|
|
||||||
temp_pool.xp = meta:get_int("xp") or 0
|
|
||||||
temp_pool.level = mcl_experience.xp_to_level(temp_pool.xp)
|
|
||||||
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
|
|
||||||
temp_pool.last_time= minetest.get_us_time()/1000000
|
|
||||||
end
|
|
||||||
|
|
||||||
-- saves data to be utilized on next login
|
local hud_bars = {}
|
||||||
local function save_data(player)
|
local hud_levels = {}
|
||||||
local name = player:get_player_name()
|
local caches = {}
|
||||||
local temp_pool = pool[name]
|
|
||||||
local meta = player:get_meta()
|
|
||||||
meta:set_int("xp", temp_pool.xp)
|
|
||||||
pool[name] = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local player_huds = {} -- the list of players hud lists (3d array)
|
-- helpers
|
||||||
hud_manager = {} -- hud manager class
|
|
||||||
|
|
||||||
-- terminate the player's list on leave
|
local function xp_to_level(xp)
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
player_huds[name] = nil
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- create instance of new hud
|
|
||||||
function hud_manager.add_hud(player,hud_name,def)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
if minetest.is_creative_enabled(name) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local local_hud = player:hud_add({
|
|
||||||
hud_elem_type = def.hud_elem_type,
|
|
||||||
position = def.position,
|
|
||||||
text = def.text,
|
|
||||||
text2 = def.text2,
|
|
||||||
number = def.number,
|
|
||||||
item = def.item,
|
|
||||||
direction = def.direction,
|
|
||||||
size = def.size,
|
|
||||||
offset = def.offset,
|
|
||||||
z_index = def.z_index,
|
|
||||||
alignment = def.alignment,
|
|
||||||
scale = def.scale,
|
|
||||||
})
|
|
||||||
-- create new 3d array here
|
|
||||||
-- depends.txt is not needed
|
|
||||||
-- with it here
|
|
||||||
if not player_huds[name] then
|
|
||||||
player_huds[name] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
player_huds[name][hud_name] = local_hud
|
|
||||||
end
|
|
||||||
|
|
||||||
-- delete instance of hud
|
|
||||||
function hud_manager.remove_hud(player,hud_name)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
if player_huds[name] and player_huds[name][hud_name] then
|
|
||||||
player:hud_remove(player_huds[name][hud_name])
|
|
||||||
player_huds[name][hud_name] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- change element of hud
|
|
||||||
function hud_manager.change_hud(data)
|
|
||||||
local name = data.player:get_player_name()
|
|
||||||
if player_huds[name] and player_huds[name][data.hud_name] then
|
|
||||||
data.player:hud_change(player_huds[name][data.hud_name], data.element, data.data)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- gets if hud exists
|
|
||||||
function hud_manager.hud_exists(player,hud_name)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
if player_huds[name] and player_huds[name][hud_name] then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
-- saves specific users data for when they relog
|
|
||||||
minetest.register_on_leaveplayer(function(player)
|
|
||||||
save_data(player)
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- is used for shutdowns to save all data
|
|
||||||
local function save_all()
|
|
||||||
for name,_ in pairs(pool) do
|
|
||||||
local player = minetest.get_player_by_name(name)
|
|
||||||
if player then
|
|
||||||
save_data(player)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- save all data to mod storage on shutdown
|
|
||||||
minetest.register_on_shutdown(function()
|
|
||||||
save_all()
|
|
||||||
end)
|
|
||||||
|
|
||||||
|
|
||||||
function mcl_experience.get_player_xp_level(player)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
return pool[name].level
|
|
||||||
end
|
|
||||||
|
|
||||||
function mcl_experience.set_player_xp_level(player,level)
|
|
||||||
local name = player:get_player_name()
|
|
||||||
if level == pool[name].level then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
pool[name].level = level
|
|
||||||
pool[name].xp, pool[name].bar_step, pool[name].xp_next_level = mcl_experience.bar_to_xp(pool[name].bar, level)
|
|
||||||
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(level)})
|
|
||||||
-- we may don't update the bar
|
|
||||||
end
|
|
||||||
|
|
||||||
local name
|
|
||||||
local temp_pool
|
|
||||||
minetest.register_on_joinplayer(function(player)
|
|
||||||
|
|
||||||
load_data(player)
|
|
||||||
|
|
||||||
name = player:get_player_name()
|
|
||||||
temp_pool = pool[name]
|
|
||||||
|
|
||||||
hud_manager.add_hud(player,"experience_bar",
|
|
||||||
{
|
|
||||||
hud_elem_type = "image",
|
|
||||||
name = "experience bar",
|
|
||||||
text = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",
|
|
||||||
position = {x=0.5, y=1},
|
|
||||||
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
|
|
||||||
scale = {x = 2.8, y = 3.0},
|
|
||||||
alignment = { x = 1, y = 1 },
|
|
||||||
z_index = 11,
|
|
||||||
})
|
|
||||||
|
|
||||||
hud_manager.add_hud(player,"xp_level",
|
|
||||||
{
|
|
||||||
hud_elem_type = "text", position = {x=0.5, y=1},
|
|
||||||
name = "xp_level", text = tostring(temp_pool.level),
|
|
||||||
number = 0x80FF20,
|
|
||||||
offset = {x = 0, y = -(48 + 24 + 24)},
|
|
||||||
z_index = 12,
|
|
||||||
})
|
|
||||||
end)
|
|
||||||
|
|
||||||
function mcl_experience.xp_to_level(xp)
|
|
||||||
local xp = xp or 0
|
local xp = xp or 0
|
||||||
local a, b, c, D
|
local a, b, c, D
|
||||||
|
|
||||||
if xp > 1507 then
|
if xp > 1507 then
|
||||||
a, b, c = 4.5, -162.5, 2220 - xp
|
a, b, c = 4.5, -162.5, 2220 - xp
|
||||||
elseif xp > 352 then
|
elseif xp > 352 then
|
||||||
|
@ -205,437 +27,201 @@ function mcl_experience.xp_to_level(xp)
|
||||||
else
|
else
|
||||||
a, b, c = 1, 6, -xp
|
a, b, c = 1, 6, -xp
|
||||||
end
|
end
|
||||||
|
|
||||||
D = b * b - 4 * a * c
|
D = b * b - 4 * a * c
|
||||||
|
|
||||||
if D == 0 then
|
if D == 0 then
|
||||||
return math.floor(-b / 2 / a)
|
return math.floor(-b / 2 / a)
|
||||||
elseif D > 0 then
|
elseif D > 0 then
|
||||||
local v1, v2 = -b / 2 / a, math.sqrt(D) / 2 / a
|
local v1, v2 = -b / 2 / a, math.sqrt(D) / 2 / a
|
||||||
return math.floor((math.max(v1-v2, v1+v2)))
|
return math.floor(math.max(v1 - v2, v1 + v2))
|
||||||
end
|
end
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_experience.level_to_xp(level)
|
local function level_to_xp(level)
|
||||||
if (level >= 1 and level <= 16) then
|
if level >= 1 and level <= 16 then
|
||||||
return math.floor(math.pow(level, 2) + 6 * level)
|
return math.floor(math.pow(level, 2) + 6 * level)
|
||||||
elseif (level >= 17 and level <= 31) then
|
elseif level >= 17 and level <= 31 then
|
||||||
return math.floor(2.5 * math.pow(level, 2) - 40.5 * level + 360)
|
return math.floor(2.5 * math.pow(level, 2) - 40.5 * level + 360)
|
||||||
elseif level >= 32 then
|
elseif level >= 32 then
|
||||||
return math.floor(4.5 * math.pow(level, 2) - 162.5 * level + 2220);
|
return math.floor(4.5 * math.pow(level, 2) - 162.5 * level + 2220)
|
||||||
end
|
end
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_experience.xp_to_bar(xp, level)
|
local function calculate_bounds(level)
|
||||||
local level = level or mcl_experience.xp_to_level(xp)
|
return level_to_xp(level), level_to_xp(level + 1)
|
||||||
local xp_this_level = mcl_experience.level_to_xp(level)
|
|
||||||
local xp_next_level = mcl_experience.level_to_xp(level+1)
|
|
||||||
local bar_step = 36 / (xp_next_level-xp_this_level)
|
|
||||||
local bar = (xp-xp_this_level) * bar_step
|
|
||||||
return bar, bar_step, xp_next_level
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_experience.bar_to_xp(bar, level)
|
local function xp_to_bar(xp, level)
|
||||||
local xp_this_level = mcl_experience.level_to_xp(level)
|
local xp_min, xp_max = calculate_bounds(level)
|
||||||
local xp_next_level = mcl_experience.level_to_xp(level+1)
|
|
||||||
local bar_step = 36 / (xp_next_level-xp_this_level)
|
return (xp - xp_min) / (xp_max - xp_min)
|
||||||
local xp = xp_this_level + math.floor(bar/36*(xp_next_level-xp_this_level))
|
|
||||||
return xp, bar_step, xp_next_level
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function mcl_experience.add_experience(player, experience)
|
local function bar_to_xp(bar, level)
|
||||||
|
local xp_min, xp_max = calculate_bounds(level)
|
||||||
|
|
||||||
|
return xp_min + bar * (xp_max - xp_min)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_time()
|
||||||
|
return minetest.get_us_time() / 1000000
|
||||||
|
end
|
||||||
|
|
||||||
|
-- api
|
||||||
|
|
||||||
|
function mcl_experience.get_level(player)
|
||||||
|
return caches[player].level
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_experience.set_level(player, level)
|
||||||
|
local cache = caches[player]
|
||||||
|
|
||||||
|
if level ~= cache.level then
|
||||||
|
mcl_experience.set_xp(player, math.floor(bar_to_xp(xp_to_bar(mcl_experience.get_xp(player), cache.level), level)))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_experience.get_xp(player)
|
||||||
|
return player:get_meta():get_int("xp")
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_experience.set_xp(player, xp)
|
||||||
|
player:get_meta():set_int("xp", xp)
|
||||||
|
|
||||||
|
mcl_experience.update(player)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_experience.add_xp(player, xp)
|
||||||
|
for _, cb in ipairs(mcl_experience.on_add_xp) do
|
||||||
|
xp = cb.func(player, xp) or xp
|
||||||
|
|
||||||
|
if xp == 0 then
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local cache = caches[player]
|
||||||
|
local old_level = cache.level
|
||||||
|
|
||||||
|
mcl_experience.set_xp(player, mcl_experience.get_xp(player) + xp)
|
||||||
|
|
||||||
|
local current_time = get_time()
|
||||||
|
|
||||||
|
if current_time - cache.last_time > 0.01 then
|
||||||
local name = player:get_player_name()
|
local name = player:get_player_name()
|
||||||
local temp_pool = pool[name]
|
|
||||||
|
|
||||||
local inv = player:get_inventory()
|
if old_level == cache.level then
|
||||||
local candidates = {
|
minetest.sound_play("mcl_experience", {
|
||||||
{list = "main", index = player:get_wield_index()},
|
to_player = name,
|
||||||
{list = "armor", index = 2},
|
gain = 0.1,
|
||||||
{list = "armor", index = 3},
|
pitch = math.random(75, 99) / 100,
|
||||||
{list = "armor", index = 4},
|
|
||||||
{list = "armor", index = 5},
|
|
||||||
}
|
|
||||||
local final_candidates = {}
|
|
||||||
for _, can in ipairs(candidates) do
|
|
||||||
local stack = inv:get_stack(can.list, can.index)
|
|
||||||
local wear = stack:get_wear()
|
|
||||||
if mcl_enchanting.has_enchantment(stack, "mending") and wear > 0 then
|
|
||||||
can.stack = stack
|
|
||||||
can.wear = wear
|
|
||||||
table.insert(final_candidates, can)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if #final_candidates > 0 then
|
|
||||||
local can = final_candidates[math.random(#final_candidates)]
|
|
||||||
local stack, list, index, wear = can.stack, can.list, can.index, can.wear
|
|
||||||
local uses = mcl_util.calculate_durability(stack)
|
|
||||||
local multiplier = 2 * 65535 / uses
|
|
||||||
local repair = experience * multiplier
|
|
||||||
local new_wear = wear - repair
|
|
||||||
if new_wear < 0 then
|
|
||||||
experience = math.floor(-new_wear / multiplier + 0.5)
|
|
||||||
new_wear = 0
|
|
||||||
else
|
|
||||||
experience = 0
|
|
||||||
end
|
|
||||||
stack:set_wear(math.floor(new_wear))
|
|
||||||
inv:set_stack(list, index, stack)
|
|
||||||
end
|
|
||||||
|
|
||||||
local old_bar, old_xp, old_level = temp_pool.bar, temp_pool.xp, temp_pool.level
|
|
||||||
temp_pool.xp = math.min(math.max(temp_pool.xp + experience, 0), max_xp)
|
|
||||||
|
|
||||||
if (temp_pool.xp < temp_pool.xp_next_level) and (temp_pool.xp >= old_xp) then
|
|
||||||
temp_pool.bar = temp_pool.bar + temp_pool.bar_step * experience
|
|
||||||
else
|
|
||||||
temp_pool.level = mcl_experience.xp_to_level(temp_pool.xp)
|
|
||||||
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_bar ~= temp_pool.bar then
|
|
||||||
hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",})
|
|
||||||
end
|
|
||||||
|
|
||||||
if experience > 0 and minetest.get_us_time()/1000000 - temp_pool.last_time > 0.01 then
|
|
||||||
if old_level ~= temp_pool.level then
|
|
||||||
minetest.sound_play("level_up",{gain=0.2,to_player = name})
|
|
||||||
temp_pool.last_time = minetest.get_us_time()/1000000 + 0.2
|
|
||||||
else
|
|
||||||
minetest.sound_play("experience",{gain=0.1,to_player = name,pitch=math.random(75,99)/100})
|
|
||||||
temp_pool.last_time = minetest.get_us_time()/1000000
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if old_level ~= temp_pool.level then
|
|
||||||
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(temp_pool.level)})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--reset player level
|
|
||||||
local name
|
|
||||||
local temp_pool
|
|
||||||
local xp_amount
|
|
||||||
minetest.register_on_dieplayer(function(player)
|
|
||||||
if minetest.settings:get_bool("mcl_keepInventory", false) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
name = player:get_player_name()
|
|
||||||
temp_pool = pool[name]
|
|
||||||
xp_amount = temp_pool.xp
|
|
||||||
|
|
||||||
temp_pool.xp = 0
|
|
||||||
temp_pool.level = 0
|
|
||||||
temp_pool.bar, temp_pool.bar_step, temp_pool.xp_next_level = mcl_experience.xp_to_bar(temp_pool.xp, temp_pool.level)
|
|
||||||
|
|
||||||
hud_manager.change_hud({player = player, hud_name = "xp_level", element = "text", data = tostring(temp_pool.level)})
|
|
||||||
hud_manager.change_hud({player = player, hud_name = "experience_bar", element = "text", data = "experience_bar_background.png^[lowpart:" .. math.floor(temp_pool.bar / 36 * 100) .. ":experience_bar.png^[transformR270",})
|
|
||||||
|
|
||||||
mcl_experience.throw_experience(player:get_pos(), xp_amount)
|
|
||||||
end)
|
|
||||||
|
|
||||||
local collector, pos, pos2
|
|
||||||
local direction, distance, player_velocity, goal
|
|
||||||
local currentvel, acceleration, multiplier, velocity
|
|
||||||
local node, vel, def
|
|
||||||
local is_moving, is_slippery, slippery, slip_factor
|
|
||||||
local size
|
|
||||||
local function xp_step(self, dtime)
|
|
||||||
--if item set to be collected then only execute go to player
|
|
||||||
if self.collected == true then
|
|
||||||
if not self.collector then
|
|
||||||
self.collected = false
|
|
||||||
return
|
|
||||||
end
|
|
||||||
collector = minetest.get_player_by_name(self.collector)
|
|
||||||
if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then
|
|
||||||
self.object:set_acceleration(vector.new(0,0,0))
|
|
||||||
self.disable_physics(self)
|
|
||||||
--get the variables
|
|
||||||
pos = self.object:get_pos()
|
|
||||||
pos2 = collector:get_pos()
|
|
||||||
|
|
||||||
player_velocity = collector:get_velocity() or collector:get_player_velocity()
|
|
||||||
|
|
||||||
pos2.y = pos2.y + 0.8
|
|
||||||
|
|
||||||
direction = vector.direction(pos,pos2)
|
|
||||||
distance = vector.distance(pos2,pos)
|
|
||||||
multiplier = distance
|
|
||||||
if multiplier < 1 then
|
|
||||||
multiplier = 1
|
|
||||||
end
|
|
||||||
goal = vector.multiply(direction,multiplier)
|
|
||||||
currentvel = self.object:get_velocity()
|
|
||||||
|
|
||||||
if distance > 1 then
|
|
||||||
multiplier = 20 - distance
|
|
||||||
velocity = vector.multiply(direction,multiplier)
|
|
||||||
goal = velocity
|
|
||||||
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
|
|
||||||
self.object:add_velocity(vector.add(acceleration,player_velocity))
|
|
||||||
elseif distance < 0.8 then
|
|
||||||
mcl_experience.add_experience(collector, self._xp)
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
return
|
|
||||||
else
|
|
||||||
self.collector = nil
|
|
||||||
self.enable_physics(self)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
self.age = self.age + dtime
|
|
||||||
if self.age > max_orb_age then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
pos = self.object:get_pos()
|
|
||||||
|
|
||||||
if pos then
|
|
||||||
node = minetest.get_node_or_nil({
|
|
||||||
x = pos.x,
|
|
||||||
y = pos.y -0.25,
|
|
||||||
z = pos.z
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
cache.last_time = current_time
|
||||||
else
|
else
|
||||||
return
|
minetest.sound_play("mcl_experience_level_up", {
|
||||||
end
|
to_player = name,
|
||||||
|
gain = 0.2,
|
||||||
-- Remove nodes in 'ignore'
|
|
||||||
if node and node.name == "ignore" then
|
|
||||||
self.object:remove()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if not self.physical_state then
|
|
||||||
return -- Don't do anything
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Slide on slippery nodes
|
|
||||||
vel = self.object:get_velocity()
|
|
||||||
def = node and registered_nodes[node.name]
|
|
||||||
is_moving = (def and not def.walkable) or
|
|
||||||
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
|
|
||||||
is_slippery = false
|
|
||||||
|
|
||||||
if def and def.walkable then
|
|
||||||
slippery = minetest.get_item_group(node.name, "slippery")
|
|
||||||
is_slippery = slippery ~= 0
|
|
||||||
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
|
|
||||||
-- Horizontal deceleration
|
|
||||||
slip_factor = 4.0 / (slippery + 4)
|
|
||||||
self.object:set_acceleration({
|
|
||||||
x = -vel.x * slip_factor,
|
|
||||||
y = 0,
|
|
||||||
z = -vel.z * slip_factor
|
|
||||||
})
|
})
|
||||||
elseif vel.y == 0 then
|
|
||||||
is_moving = false
|
cache.last_time = current_time + 0.2
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if self.moving_state == is_moving and self.slippery_state == is_slippery then
|
function mcl_experience.throw_xp(pos, total_xp)
|
||||||
-- Do not update anything until the moving state changes
|
local i, j = 0, 0
|
||||||
return
|
|
||||||
|
while i < total_xp and j < 100 do
|
||||||
|
local xp = math.min(math.random(1, math.min(32767, total_xp - math.floor(i / 2))), total_xp - i)
|
||||||
|
local obj = minetest.add_entity(pos, "mcl_experience:orb", tostring(xp))
|
||||||
|
|
||||||
|
if not obj then
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
self.moving_state = is_moving
|
obj:set_velocity(vector.new(
|
||||||
self.slippery_state = is_slippery
|
|
||||||
|
|
||||||
if is_moving then
|
|
||||||
self.object:set_acceleration(gravity)
|
|
||||||
else
|
|
||||||
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
|
||||||
self.object:set_velocity({x = 0, y = 0, z = 0})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
minetest.register_entity("mcl_experience:orb", {
|
|
||||||
initial_properties = {
|
|
||||||
hp_max = 1,
|
|
||||||
physical = true,
|
|
||||||
collide_with_objects = false,
|
|
||||||
collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
|
|
||||||
visual = "sprite",
|
|
||||||
visual_size = {x = 0.4, y = 0.4},
|
|
||||||
textures = {name="experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
|
|
||||||
spritediv = {x = 1, y = 14},
|
|
||||||
initial_sprite_basepos = {x = 0, y = 0},
|
|
||||||
is_visible = true,
|
|
||||||
pointable = false,
|
|
||||||
static_save = false,
|
|
||||||
},
|
|
||||||
moving_state = true,
|
|
||||||
slippery_state = false,
|
|
||||||
physical_state = true,
|
|
||||||
-- Item expiry
|
|
||||||
age = 0,
|
|
||||||
-- Pushing item out of solid nodes
|
|
||||||
force_out = nil,
|
|
||||||
force_out_start = nil,
|
|
||||||
--Collection Variables
|
|
||||||
collectable = false,
|
|
||||||
try_timer = 0,
|
|
||||||
collected = false,
|
|
||||||
delete_timer = 0,
|
|
||||||
radius = 4,
|
|
||||||
|
|
||||||
|
|
||||||
on_activate = function(self, staticdata, dtime_s)
|
|
||||||
self.object:set_velocity(vector.new(
|
|
||||||
math.random(-2, 2) * math.random(),
|
math.random(-2, 2) * math.random(),
|
||||||
math.random( 2, 5),
|
math.random( 2, 5),
|
||||||
math.random(-2, 2) * math.random()
|
math.random(-2, 2) * math.random()
|
||||||
))
|
))
|
||||||
self.object:set_armor_groups({immortal = 1})
|
|
||||||
self.object:set_velocity({x = 0, y = 2, z = 0})
|
|
||||||
self.object:set_acceleration(gravity)
|
|
||||||
local xp = tonumber(staticdata)
|
|
||||||
self._xp = xp
|
|
||||||
size = xp_to_size(xp)
|
|
||||||
self.object:set_properties({
|
|
||||||
visual_size = {x = size, y = size},
|
|
||||||
glow = 14,
|
|
||||||
})
|
|
||||||
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
|
|
||||||
end,
|
|
||||||
|
|
||||||
enable_physics = function(self)
|
|
||||||
if not self.physical_state then
|
|
||||||
self.physical_state = true
|
|
||||||
self.object:set_properties({physical = true})
|
|
||||||
self.object:set_velocity({x=0, y=0, z=0})
|
|
||||||
self.object:set_acceleration(gravity)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
disable_physics = function(self)
|
|
||||||
if self.physical_state then
|
|
||||||
self.physical_state = false
|
|
||||||
self.object:set_properties({physical = false})
|
|
||||||
self.object:set_velocity({x=0, y=0, z=0})
|
|
||||||
self.object:set_acceleration({x=0, y=0, z=0})
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
on_step = function(self, dtime)
|
|
||||||
xp_step(self, dtime)
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
minetest.register_chatcommand("xp", {
|
|
||||||
params = S("[[<player>] <xp>]"),
|
|
||||||
description = S("Gives a player some XP"),
|
|
||||||
privs = {server=true},
|
|
||||||
func = function(name, params)
|
|
||||||
local player, xp = nil, 1000
|
|
||||||
local P, i = {}, 0
|
|
||||||
for str in string.gmatch(params, "([^ ]+)") do
|
|
||||||
i = i + 1
|
|
||||||
P[i] = str
|
|
||||||
end
|
|
||||||
if i > 2 then
|
|
||||||
return false, S("Error: Too many parameters!")
|
|
||||||
end
|
|
||||||
if i > 0 then
|
|
||||||
xp = tonumber(P[i])
|
|
||||||
end
|
|
||||||
if i < 2 then
|
|
||||||
player = minetest.get_player_by_name(name)
|
|
||||||
end
|
|
||||||
if i == 2 then
|
|
||||||
player = minetest.get_player_by_name(P[1])
|
|
||||||
end
|
|
||||||
if not xp then
|
|
||||||
return false, S("Error: Incorrect value of XP")
|
|
||||||
end
|
|
||||||
if not player then
|
|
||||||
return false, S("Error: Player not found")
|
|
||||||
end
|
|
||||||
mcl_experience.add_experience(player, xp)
|
|
||||||
local playername = player:get_player_name()
|
|
||||||
minetest.chat_send_player(name, S("Added @1 XP to @2, total: @3, experience level: @4", tostring(xp), playername, tostring(pool[playername].xp), tostring(pool[playername].level)))
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
function mcl_experience.throw_experience(pos, amount)
|
|
||||||
local i, j = 0, 0
|
|
||||||
local obj, xp
|
|
||||||
while i < amount and j < 100 do
|
|
||||||
xp = math.min(math.random(1, math.min(32767, amount-math.floor(i/2))), amount-i)
|
|
||||||
obj = minetest.add_entity(pos, "mcl_experience:orb", tostring(xp))
|
|
||||||
if not obj then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
obj:set_velocity({
|
|
||||||
x=math.random(-2,2)*math.random(),
|
|
||||||
y=math.random(2,5),
|
|
||||||
z=math.random(-2,2)*math.random()
|
|
||||||
})
|
|
||||||
i = i + xp
|
i = i + xp
|
||||||
j = j + 1
|
j = j + 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_entity("mcl_experience:bottle",{
|
function mcl_experience.update(player)
|
||||||
textures = {"mcl_experience_bottle.png"},
|
local xp = mcl_experience.get_xp(player)
|
||||||
hp_max = 1,
|
local cache = caches[player]
|
||||||
visual_size = {x = 0.35, y = 0.35},
|
|
||||||
collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
|
|
||||||
pointable = false,
|
|
||||||
on_step = function(self, dtime)
|
|
||||||
local pos = self.object:get_pos()
|
|
||||||
local node = minetest.get_node(pos)
|
|
||||||
local n = node.name
|
|
||||||
if n ~= "air" and n ~= "mcl_portals:portal" and n ~= "mcl_portals:portal_end" and minetest.get_item_group(n, "liquid") == 0 then
|
|
||||||
minetest.sound_play("mcl_potions_breaking_glass", {pos = pos, max_hear_distance = 16, gain = 1})
|
|
||||||
mcl_experience.throw_experience(pos, math.random(3, 11))
|
|
||||||
minetest.add_particlespawner({
|
|
||||||
amount = 50,
|
|
||||||
time = 0.1,
|
|
||||||
minpos = vector.add(pos, vector.new(-0.1, 0.5, -0.1)),
|
|
||||||
maxpos = vector.add(pos, vector.new( 0.1, 0.6, 0.1)),
|
|
||||||
minvel = vector.new(-2, 0, -2),
|
|
||||||
maxvel = vector.new( 2, 2, 2),
|
|
||||||
minacc = vector.new(0, 0, 0),
|
|
||||||
maxacc = vector.new(0, 0, 0),
|
|
||||||
minexptime = 0.5,
|
|
||||||
maxexptime = 1.25,
|
|
||||||
minsize = 1,
|
|
||||||
maxsize = 2,
|
|
||||||
collisiondetection = true,
|
|
||||||
vertical = false,
|
|
||||||
texture = "mcl_particles_effect.png^[colorize:blue:127",
|
|
||||||
})
|
|
||||||
self.object:remove()
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
})
|
|
||||||
|
|
||||||
local function throw_xp_bottle(pos, dir, velocity)
|
cache.level = xp_to_level(xp)
|
||||||
minetest.sound_play("mcl_throwing_throw", {pos = pos, gain = 0.4, max_hear_distance = 16}, true)
|
|
||||||
local obj = minetest.add_entity(pos, "mcl_experience:bottle")
|
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
obj:set_velocity(vector.multiply(dir, velocity))
|
player:hud_change(hud_bars[player], "text", "mcl_experience_bar_background.png^[lowpart:"
|
||||||
local acceleration = vector.multiply(dir, -3)
|
.. math.floor(math.floor(xp_to_bar(xp, cache.level) * 18) / 18 * 100)
|
||||||
acceleration.y = -9.81
|
.. ":mcl_experience_bar.png^[transformR270"
|
||||||
obj:set_acceleration(acceleration)
|
)
|
||||||
|
|
||||||
|
if cache.level == 0 then
|
||||||
|
player:hud_change(hud_levels[player], "text", "")
|
||||||
|
else
|
||||||
|
player:hud_change(hud_levels[player], "text", tostring(cache.level))
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
minetest.register_craftitem("mcl_experience:bottle", {
|
function mcl_experience.register_on_add_xp(func, priority)
|
||||||
description = "Bottle o' Enchanting",
|
table.insert(mcl_experience.on_add_xp, {func = func, priority = priority or 0})
|
||||||
inventory_image = "mcl_experience_bottle.png",
|
|
||||||
wield_image = "mcl_experience_bottle.png",
|
|
||||||
stack_max = 64,
|
|
||||||
on_use = function(itemstack, placer, pointed_thing)
|
|
||||||
throw_xp_bottle(vector.add(placer:get_pos(), vector.new(0, 1.5, 0)), placer:get_look_dir(), 10)
|
|
||||||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
|
||||||
itemstack:take_item()
|
|
||||||
end
|
|
||||||
return itemstack
|
|
||||||
end,
|
|
||||||
_on_dispense = function(_, pos, _, _, dir)
|
|
||||||
throw_xp_bottle(vector.add(pos, vector.multiply(dir, 0.51)), dir, 10)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- callbacks
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
caches[player] = {
|
||||||
|
last_time = get_time(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
|
hud_bars[player] = player:hud_add({
|
||||||
|
hud_elem_type = "image",
|
||||||
|
position = {x = 0.5, y = 1},
|
||||||
|
offset = {x = (-9 * 28) - 3, y = -(48 + 24 + 16 - 5)},
|
||||||
|
scale = {x = 2.8, y = 3.0},
|
||||||
|
alignment = {x = 1, y = 1},
|
||||||
|
z_index = 11,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
hud_levels[player] = player:hud_add({
|
||||||
|
hud_elem_type = "text",
|
||||||
|
position = {x = 0.5, y = 1},
|
||||||
|
number = 0x80FF20,
|
||||||
|
offset = {x = 0, y = -(48 + 24 + 24)},
|
||||||
|
z_index = 12,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_experience.update(player)
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
hud_bars[player] = nil
|
||||||
|
hud_levels[player] = nil
|
||||||
|
caches[player] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_dieplayer(function(player)
|
||||||
|
if not minetest.settings:get_bool("mcl_keepInventory", false) then
|
||||||
|
mcl_experience.throw_xp(player:get_pos(), mcl_experience.get_xp(player))
|
||||||
|
mcl_experience.set_xp(player, 0)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_mods_loaded(function()
|
||||||
|
table.sort(mcl_experience.on_add_xp, function(a, b) return a.priority < b.priority end)
|
||||||
|
end)
|
||||||
|
|
220
mods/HUD/mcl_experience/orb.lua
Normal file
220
mods/HUD/mcl_experience/orb.lua
Normal file
|
@ -0,0 +1,220 @@
|
||||||
|
local size_min, size_max = 20, 59
|
||||||
|
local delta_size = size_max - size_min
|
||||||
|
|
||||||
|
local size_to_xp = {
|
||||||
|
{-32768, 2}, -- 1
|
||||||
|
{ 3, 6}, -- 2
|
||||||
|
{ 7, 16}, -- 3
|
||||||
|
{ 17, 36}, -- 4
|
||||||
|
{ 37, 72}, -- 5
|
||||||
|
{ 73, 148}, -- 6
|
||||||
|
{ 149, 306}, -- 7
|
||||||
|
{ 307, 616}, -- 8
|
||||||
|
{ 617, 1236}, -- 9
|
||||||
|
{ 1237, 2476}, -- 10
|
||||||
|
{ 2477, 32767} -- 11
|
||||||
|
}
|
||||||
|
|
||||||
|
local function xp_to_size(xp)
|
||||||
|
local i, l = 1, #size_to_xp
|
||||||
|
|
||||||
|
while xp > size_to_xp[i][1] and i < l do
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return ((i - 1) / (l - 1) * delta_size + size_min) / 100
|
||||||
|
end
|
||||||
|
|
||||||
|
local max_orb_age = 300 -- seconds
|
||||||
|
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
|
||||||
|
|
||||||
|
local collector, pos, pos2
|
||||||
|
local direction, distance, player_velocity, goal
|
||||||
|
local currentvel, acceleration, multiplier, velocity
|
||||||
|
local node, vel, def
|
||||||
|
local is_moving, is_slippery, slippery, slip_factor
|
||||||
|
local size
|
||||||
|
local function xp_step(self, dtime)
|
||||||
|
--if item set to be collected then only execute go to player
|
||||||
|
if self.collected == true then
|
||||||
|
if not self.collector then
|
||||||
|
self.collected = false
|
||||||
|
return
|
||||||
|
end
|
||||||
|
collector = minetest.get_player_by_name(self.collector)
|
||||||
|
if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then
|
||||||
|
self.object:set_acceleration(vector.new(0,0,0))
|
||||||
|
self.disable_physics(self)
|
||||||
|
--get the variables
|
||||||
|
pos = self.object:get_pos()
|
||||||
|
pos2 = collector:get_pos()
|
||||||
|
|
||||||
|
player_velocity = collector:get_velocity() or collector:get_player_velocity()
|
||||||
|
|
||||||
|
pos2.y = pos2.y + 0.8
|
||||||
|
|
||||||
|
direction = vector.direction(pos,pos2)
|
||||||
|
distance = vector.distance(pos2,pos)
|
||||||
|
multiplier = distance
|
||||||
|
if multiplier < 1 then
|
||||||
|
multiplier = 1
|
||||||
|
end
|
||||||
|
goal = vector.multiply(direction,multiplier)
|
||||||
|
currentvel = self.object:get_velocity()
|
||||||
|
|
||||||
|
if distance > 1 then
|
||||||
|
multiplier = 20 - distance
|
||||||
|
velocity = vector.multiply(direction,multiplier)
|
||||||
|
goal = velocity
|
||||||
|
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
|
||||||
|
self.object:add_velocity(vector.add(acceleration,player_velocity))
|
||||||
|
elseif distance < 0.8 then
|
||||||
|
mcl_experience.add_xp(collector, self._xp)
|
||||||
|
self.object:remove()
|
||||||
|
end
|
||||||
|
return
|
||||||
|
else
|
||||||
|
self.collector = nil
|
||||||
|
self.enable_physics(self)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
self.age = self.age + dtime
|
||||||
|
if self.age > max_orb_age then
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
pos = self.object:get_pos()
|
||||||
|
|
||||||
|
if pos then
|
||||||
|
node = minetest.get_node_or_nil({
|
||||||
|
x = pos.x,
|
||||||
|
y = pos.y -0.25,
|
||||||
|
z = pos.z
|
||||||
|
})
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Remove nodes in 'ignore'
|
||||||
|
if node and node.name == "ignore" then
|
||||||
|
self.object:remove()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if not self.physical_state then
|
||||||
|
return -- Don't do anything
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Slide on slippery nodes
|
||||||
|
vel = self.object:get_velocity()
|
||||||
|
def = node and minetest.registered_nodes[node.name]
|
||||||
|
is_moving = (def and not def.walkable) or
|
||||||
|
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
|
||||||
|
is_slippery = false
|
||||||
|
|
||||||
|
if def and def.walkable then
|
||||||
|
slippery = minetest.get_item_group(node.name, "slippery")
|
||||||
|
is_slippery = slippery ~= 0
|
||||||
|
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
|
||||||
|
-- Horizontal deceleration
|
||||||
|
slip_factor = 4.0 / (slippery + 4)
|
||||||
|
self.object:set_acceleration({
|
||||||
|
x = -vel.x * slip_factor,
|
||||||
|
y = 0,
|
||||||
|
z = -vel.z * slip_factor
|
||||||
|
})
|
||||||
|
elseif vel.y == 0 then
|
||||||
|
is_moving = false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.moving_state == is_moving and self.slippery_state == is_slippery then
|
||||||
|
-- Do not update anything until the moving state changes
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
self.moving_state = is_moving
|
||||||
|
self.slippery_state = is_slippery
|
||||||
|
|
||||||
|
if is_moving then
|
||||||
|
self.object:set_acceleration(gravity)
|
||||||
|
else
|
||||||
|
self.object:set_acceleration({x = 0, y = 0, z = 0})
|
||||||
|
self.object:set_velocity({x = 0, y = 0, z = 0})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
minetest.register_entity("mcl_experience:orb", {
|
||||||
|
initial_properties = {
|
||||||
|
hp_max = 1,
|
||||||
|
physical = true,
|
||||||
|
collide_with_objects = false,
|
||||||
|
collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
|
||||||
|
visual = "sprite",
|
||||||
|
visual_size = {x = 0.4, y = 0.4},
|
||||||
|
textures = {name="mcl_experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
|
||||||
|
spritediv = {x = 1, y = 14},
|
||||||
|
initial_sprite_basepos = {x = 0, y = 0},
|
||||||
|
is_visible = true,
|
||||||
|
pointable = false,
|
||||||
|
static_save = false,
|
||||||
|
},
|
||||||
|
moving_state = true,
|
||||||
|
slippery_state = false,
|
||||||
|
physical_state = true,
|
||||||
|
-- Item expiry
|
||||||
|
age = 0,
|
||||||
|
-- Pushing item out of solid nodes
|
||||||
|
force_out = nil,
|
||||||
|
force_out_start = nil,
|
||||||
|
--Collection Variables
|
||||||
|
collectable = false,
|
||||||
|
try_timer = 0,
|
||||||
|
collected = false,
|
||||||
|
delete_timer = 0,
|
||||||
|
radius = 4,
|
||||||
|
|
||||||
|
|
||||||
|
on_activate = function(self, staticdata, dtime_s)
|
||||||
|
self.object:set_velocity(vector.new(
|
||||||
|
math.random(-2,2)*math.random(),
|
||||||
|
math.random(2,5),
|
||||||
|
math.random(-2,2)*math.random()
|
||||||
|
))
|
||||||
|
self.object:set_armor_groups({immortal = 1})
|
||||||
|
self.object:set_velocity({x = 0, y = 2, z = 0})
|
||||||
|
self.object:set_acceleration(gravity)
|
||||||
|
local xp = tonumber(staticdata)
|
||||||
|
self._xp = xp
|
||||||
|
size = xp_to_size(xp)
|
||||||
|
self.object:set_properties({
|
||||||
|
visual_size = {x = size, y = size},
|
||||||
|
glow = 14,
|
||||||
|
})
|
||||||
|
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
|
||||||
|
end,
|
||||||
|
|
||||||
|
enable_physics = function(self)
|
||||||
|
if not self.physical_state then
|
||||||
|
self.physical_state = true
|
||||||
|
self.object:set_properties({physical = true})
|
||||||
|
self.object:set_velocity({x=0, y=0, z=0})
|
||||||
|
self.object:set_acceleration(gravity)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
disable_physics = function(self)
|
||||||
|
if self.physical_state then
|
||||||
|
self.physical_state = false
|
||||||
|
self.object:set_properties({physical = false})
|
||||||
|
self.object:set_velocity({x=0, y=0, z=0})
|
||||||
|
self.object:set_acceleration({x=0, y=0, z=0})
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
on_step = function(self, dtime)
|
||||||
|
xp_step(self, dtime)
|
||||||
|
end,
|
||||||
|
})
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 891 B After Width: | Height: | Size: 891 B |
|
@ -379,6 +379,49 @@ mcl_enchanting.enchantments.mending = {
|
||||||
inv_tool_tab = true,
|
inv_tool_tab = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mcl_experience.register_on_add_xp(function(player, xp)
|
||||||
|
local inv = player:get_inventory()
|
||||||
|
|
||||||
|
local candidates = {
|
||||||
|
{list = "main", index = player:get_wield_index()},
|
||||||
|
{list = "armor", index = 2},
|
||||||
|
{list = "armor", index = 3},
|
||||||
|
{list = "armor", index = 4},
|
||||||
|
{list = "armor", index = 5},
|
||||||
|
}
|
||||||
|
|
||||||
|
local final_candidates = {}
|
||||||
|
for _, can in ipairs(candidates) do
|
||||||
|
local stack = inv:get_stack(can.list, can.index)
|
||||||
|
local wear = stack:get_wear()
|
||||||
|
if mcl_enchanting.has_enchantment(stack, "mending") and wear > 0 then
|
||||||
|
can.stack = stack
|
||||||
|
can.wear = wear
|
||||||
|
table.insert(final_candidates, can)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if #final_candidates > 0 then
|
||||||
|
local can = final_candidates[math.random(#final_candidates)]
|
||||||
|
local stack, list, index, wear = can.stack, can.list, can.index, can.wear
|
||||||
|
local uses = mcl_util.calculate_durability(stack)
|
||||||
|
local multiplier = 2 * 65535 / uses
|
||||||
|
local repair = xp * multiplier
|
||||||
|
local new_wear = wear - repair
|
||||||
|
|
||||||
|
if new_wear < 0 then
|
||||||
|
xp = math.floor(-new_wear / multiplier + 0.5)
|
||||||
|
new_wear = 0
|
||||||
|
else
|
||||||
|
xp = 0
|
||||||
|
end
|
||||||
|
|
||||||
|
stack:set_wear(math.floor(new_wear))
|
||||||
|
inv:set_stack(list, index, stack)
|
||||||
|
end
|
||||||
|
|
||||||
|
return xp
|
||||||
|
end, 0)
|
||||||
|
|
||||||
mcl_enchanting.enchantments.multishot = {
|
mcl_enchanting.enchantments.multishot = {
|
||||||
name = S("Multishot"),
|
name = S("Multishot"),
|
||||||
|
|
|
@ -499,7 +499,7 @@ function mcl_enchanting.show_enchanting_formspec(player)
|
||||||
.. "real_coordinates[true]"
|
.. "real_coordinates[true]"
|
||||||
.. "image[3.15,0.6;7.6,4.1;mcl_enchanting_button_background.png]"
|
.. "image[3.15,0.6;7.6,4.1;mcl_enchanting_button_background.png]"
|
||||||
local itemstack = inv:get_stack("enchanting_item", 1)
|
local itemstack = inv:get_stack("enchanting_item", 1)
|
||||||
local player_levels = mcl_experience.get_player_xp_level(player)
|
local player_levels = mcl_experience.get_level(player)
|
||||||
local y = 0.65
|
local y = 0.65
|
||||||
local any_enchantment = false
|
local any_enchantment = false
|
||||||
local table_slots = mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
|
local table_slots = mcl_enchanting.get_table_slots(player, itemstack, num_bookshelves)
|
||||||
|
@ -549,11 +549,11 @@ function mcl_enchanting.handle_formspec_fields(player, formname, fields)
|
||||||
if not slot then
|
if not slot then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
local player_level = mcl_experience.get_player_xp_level(player)
|
local player_level = mcl_experience.get_level(player)
|
||||||
if player_level < slot.level_requirement then
|
if player_level < slot.level_requirement then
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
mcl_experience.set_player_xp_level(player, player_level - button_pressed)
|
mcl_experience.set_level(player, player_level - button_pressed)
|
||||||
inv:remove_item("enchanting_lapis", cost)
|
inv:remove_item("enchanting_lapis", cost)
|
||||||
mcl_enchanting.set_enchanted_itemstring(itemstack)
|
mcl_enchanting.set_enchanted_itemstring(itemstack)
|
||||||
mcl_enchanting.set_enchantments(itemstack, slot.enchantments)
|
mcl_enchanting.set_enchantments(itemstack, slot.enchantments)
|
||||||
|
|
|
@ -117,8 +117,8 @@ local fish = function(itemstack, player, pointed_thing)
|
||||||
else
|
else
|
||||||
minetest.add_item(pos, item)
|
minetest.add_item(pos, item)
|
||||||
end
|
end
|
||||||
if mcl_experience.throw_experience then
|
if mcl_experience.throw_xp then
|
||||||
mcl_experience.throw_experience(pos, math.random(1,6))
|
mcl_experience.throw_xp(pos, math.random(1,6))
|
||||||
end
|
end
|
||||||
|
|
||||||
if not minetest.is_creative_enabled(player:get_player_name()) then
|
if not minetest.is_creative_enabled(player:get_player_name()) then
|
||||||
|
|
|
@ -75,9 +75,9 @@ local function give_xp(pos, player)
|
||||||
local xp = meta:get_int("xp")
|
local xp = meta:get_int("xp")
|
||||||
if xp > 0 then
|
if xp > 0 then
|
||||||
if player then
|
if player then
|
||||||
mcl_experience.add_experience(player, xp)
|
mcl_experience.add_xp(player, xp)
|
||||||
else
|
else
|
||||||
mcl_experience.throw_experience(vector.add(pos, dir), xp)
|
mcl_experience.throw_xp(vector.add(pos, dir), xp)
|
||||||
end
|
end
|
||||||
meta:set_int("xp", 0)
|
meta:set_int("xp", 0)
|
||||||
end
|
end
|
||||||
|
|
|
@ -317,7 +317,7 @@ minetest.register_node("mcl_mobspawners:spawner", {
|
||||||
if obj then
|
if obj then
|
||||||
obj:remove()
|
obj:remove()
|
||||||
end
|
end
|
||||||
mcl_experience.throw_experience(pos, math.random(15, 43))
|
mcl_experience.throw_xp(pos, math.random(15, 43))
|
||||||
end,
|
end,
|
||||||
|
|
||||||
on_punch = function(pos)
|
on_punch = function(pos)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user