1 local minetest,math,vector,os = minetest,math,vector,os
2 local mod_storage = minetest.get_mod_storage()
5 -- loads data from mod storage
8 local load_data = function(player)
9 name = player:get_player_name()
11 temp_pool = pool[name]
12 if mod_storage:get_int(name.."xp_save") > 0 then
13 temp_pool.xp_level = mod_storage:get_int(name.."xp_level")
14 temp_pool.xp_bar = mod_storage:get_int(name.."xp_bar" )
16 temp_pool.last_time= os.clock()
18 temp_pool.xp_level = 0
21 temp_pool.last_time= os.clock()
25 -- saves data to be utilized on next login
28 local save_data = function(name)
29 if type(name) ~= "string" and name:is_player() then
30 name = name:get_player_name()
32 temp_pool = pool[name]
34 mod_storage:set_int(name.."xp_level",temp_pool.xp_level)
35 mod_storage:set_int(name.."xp_bar", temp_pool.xp_bar )
37 mod_storage:set_int(name.."xp_save",1)
42 -- saves specific users data for when they relog
43 minetest.register_on_leaveplayer(function(player)
47 -- is used for shutdowns to save all data
48 local save_all = function()
49 for name,_ in pairs(pool) do
54 -- save all data to mod storage on shutdown
55 minetest.register_on_shutdown(function()
60 minetest.hud_replace_builtin("health",{
61 hud_elem_type = "statbar",
62 position = {x = 0.5, y = 1},
64 number = core.PLAYER_MAX_HP_DEFAULT,
66 size = {x = 24, y = 24},
67 offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
72 minetest.register_on_joinplayer(function(player)
76 name = player:get_player_name()
77 temp_pool = pool[name]
79 hud_manager.add_hud(player,"heart_bar_bg",{
80 hud_elem_type = "statbar",
81 position = {x = 0.5, y = 1},
82 text = "heart_bg.png",
83 number = core.PLAYER_MAX_HP_DEFAULT,
85 size = {x = 24, y = 24},
86 offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
90 hud_manager.add_hud(player,"experience_bar_background",{
91 hud_elem_type = "statbar",
92 position = {x=0.5, y=1},
93 name = "experience bar background",
94 text = "experience_bar_background.png",
97 offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
98 size = { x=28, y=28 },
102 hud_manager.add_hud(player,"experience_bar",{
103 hud_elem_type = "statbar",
104 position = {x=0.5, y=1},
105 name = "experience bar",
106 text = "experience_bar.png",
107 number = temp_pool.xp_bar,
109 offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
110 size = { x=28, y=28 },
114 hud_manager.add_hud(player,"xp_level_bg",{
115 hud_elem_type = "text",
116 position = {x=0.5, y=1},
117 name = "xp_level_bg",
118 text = tostring(temp_pool.xp_level),
120 offset = {x = 0, y = -(48 + 24 + 24)},
123 hud_manager.add_hud(player,"xp_level_fg",{
124 hud_elem_type = "text",
125 position = {x=0.5, y=1},
126 name = "xp_level_fg",
127 text = tostring(temp_pool.xp_level),
129 offset = {x = -1, y = -(48 + 24 + 25)},
137 local function level_up_experience(player)
138 name = player:get_player_name()
139 temp_pool = pool[name]
141 temp_pool.xp_level = temp_pool.xp_level + 1
143 hud_manager.change_hud({
145 hud_name = "xp_level_fg",
147 data = tostring(temp_pool.xp_level)
149 hud_manager.change_hud({
151 hud_name = "xp_level_bg",
153 data = tostring(temp_pool.xp_level)
160 local function add_experience(player,experience)
161 name = player:get_player_name()
162 temp_pool = pool[name]
164 temp_pool.xp_bar = temp_pool.xp_bar + experience
166 if temp_pool.xp_bar > 36 then
167 if os.clock() - temp_pool.last_time > 0.04 then
168 minetest.sound_play("level_up",{gain=0.2,to_player = name})
169 temp_pool.last_time = os.clock()
171 temp_pool.xp_bar = temp_pool.xp_bar - 36
172 level_up_experience(player)
174 if os.clock() - temp_pool.last_time > 0.01 then
175 temp_pool.last_time = os.clock()
176 minetest.sound_play("experience",{gain=0.1,to_player = name,pitch=math.random(75,99)/100})
179 hud_manager.change_hud({
181 hud_name = "experience_bar",
183 data = temp_pool.xp_bar
191 minetest.register_on_dieplayer(function(player)
192 name = player:get_player_name()
193 temp_pool = pool[name]
194 xp_amount = temp_pool.xp_level
197 temp_pool.xp_level = 0
200 hud_manager.change_hud({
202 hud_name = "xp_level_fg",
204 data = tostring(temp_pool.xp_level)
206 hud_manager.change_hud({
208 hud_name = "xp_level_bg",
210 data = tostring(temp_pool.xp_level)
213 hud_manager.change_hud({
215 hud_name = "experience_bar",
217 data = temp_pool.xp_bar
220 minetest.throw_experience(player:get_pos(), xp_amount)
231 local player_velocity
246 minetest.register_entity("experience:orb", {
247 initial_properties = {
250 collide_with_objects = false,
251 collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
253 visual_size = {x = 0.4, y = 0.4},
254 textures = {name="experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
255 spritediv = {x = 1, y = 14},
256 initial_sprite_basepos = {x = 0, y = 0},
261 slippery_state = false,
262 physical_state = true,
265 -- Pushing item out of solid nodes
267 force_out_start = nil,
268 --Collection Variables
275 get_staticdata = function(self)
276 return minetest.serialize({
278 collectable = self.collectable,
279 try_timer = self.try_timer,
280 collected = self.collected,
281 delete_timer = self.delete_timer,
282 collector = self.collector,
286 on_activate = function(self, staticdata, dtime_s)
287 if string.sub(staticdata, 1, string.len("return")) == "return" then
288 data = minetest.deserialize(staticdata)
289 if data and type(data) == "table" then
290 self.age = (data.age or 0) + dtime_s
291 self.collectable = data.collectable
292 self.try_timer = data.try_timer
293 self.collected = data.collected
294 self.delete_timer = data.delete_timer
295 self.collector = data.collector
298 self.object:set_velocity(vector.new(
299 math.random(-2,2)*math.random(),
301 math.random(-2,2)*math.random()
304 self.object:set_armor_groups({immortal = 1})
305 self.object:set_velocity({x = 0, y = 2, z = 0})
306 self.object:set_acceleration({x = 0, y = -9.81, z = 0})
307 size = math.random(20,36)/100
308 self.object:set_properties({
309 visual_size = {x = size, y = size},
312 self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
315 enable_physics = function(self)
316 if not self.physical_state then
317 self.physical_state = true
318 self.object:set_properties({physical = true})
319 self.object:set_velocity({x=0, y=0, z=0})
320 self.object:set_acceleration({x=0, y=-9.81, z=0})
324 disable_physics = function(self)
325 if self.physical_state then
326 self.physical_state = false
327 self.object:set_properties({physical = false})
328 self.object:set_velocity({x=0, y=0, z=0})
329 self.object:set_acceleration({x=0, y=0, z=0})
332 on_step = function(self, dtime)
333 --if item set to be collected then only execute go to player
334 if self.collected == true then
335 if not self.collector then
336 self.collected = false
339 collector = minetest.get_player_by_name(self.collector)
340 if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 5 then
341 temp_pool = pool[self.collector]
343 self.object:set_acceleration(vector.new(0,0,0))
344 self.disable_physics(self)
346 pos = self.object:get_pos()
347 pos2 = collector:get_pos()
349 player_velocity = collector:get_player_velocity()
351 pos2.y = pos2.y + 0.8
353 direction = vector.direction(pos,pos2)
354 distance = vector.distance(pos2,pos)
355 multiplier = distance
356 if multiplier < 1 then
359 goal = vector.multiply(direction,multiplier)
360 currentvel = self.object:get_velocity()
363 multiplier = 20 - distance
364 velocity = vector.multiply(direction,multiplier)
366 acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
367 self.object:add_velocity(vector.add(acceleration,player_velocity))
368 elseif distance > 0.9 and temp_pool.buffer > 0 then
369 temp_pool.buffer = temp_pool.buffer - dtime
370 multiplier = 20 - distance
371 velocity = vector.multiply(direction,multiplier)
372 goal = vector.multiply(minetest.yaw_to_dir(minetest.dir_to_yaw(vector.direction(vector.new(pos.x,0,pos.z),vector.new(pos2.x,0,pos2.z)))+math.pi/2),10)
373 goal = vector.add(player_velocity,goal)
374 acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
375 self.object:add_velocity(acceleration)
377 if distance < 0.4 and temp_pool.buffer <= 0 then
378 temp_pool.buffer = 0.04
379 add_experience(collector,2)
385 self.enable_physics(self)
390 self.age = self.age + dtime
391 if self.age > 300 then
396 pos = self.object:get_pos()
399 node = minetest.get_node_or_nil({
408 -- Remove nodes in 'ignore'
409 if node and node.name == "ignore" then
414 if not self.physical_state then
415 return -- Don't do anything
418 -- Slide on slippery nodes
419 vel = self.object:get_velocity()
420 def = node and minetest.registered_nodes[node.name]
421 is_moving = (def and not def.walkable) or
422 vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
425 if def and def.walkable then
426 slippery = minetest.get_item_group(node.name, "slippery")
427 is_slippery = slippery ~= 0
428 if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
429 -- Horizontal deceleration
430 slip_factor = 4.0 / (slippery + 4)
431 self.object:set_acceleration({
432 x = -vel.x * slip_factor,
434 z = -vel.z * slip_factor
436 elseif vel.y == 0 then
441 if self.moving_state == is_moving and self.slippery_state == is_slippery then
442 -- Do not update anything until the moving state changes
446 self.moving_state = is_moving
447 self.slippery_state = is_slippery
450 self.object:set_acceleration({x = 0, y = -9.81, z = 0})
452 self.object:set_acceleration({x = 0, y = 0, z = 0})
453 self.object:set_velocity({x = 0, y = 0, z = 0})
459 minetest.register_chatcommand("xp", {
461 description = "Spawn x amount of a mob, used as /spawn 'mob' 10 or /spawn 'mob' for one",
462 privs = {server=true},
463 func = function(name)
464 local player = minetest.get_player_by_name(name)
465 local pos = player:get_pos()
467 minetest.throw_experience(pos, 1000)