2 minetest,math,vector,os,pairs,type
4 minetest,math,vector,os,pairs,type
6 local mod_storage = minetest.get_mod_storage()
9 local get_node_or_nil = minetest.get_node_or_nil
10 local get_time = minetest.get_us_time
11 local get_player_by_name = minetest.get_player_by_name
12 local yaw_to_dir = minetest.yaw_to_dir
13 local dir_to_yaw = minetest.dir_to_yaw
14 local get_item_group = minetest.get_item_group
15 local serialize = minetest.serialize
16 local deserialize = minetest.deserialize
17 local play_sound = minetest.sound_play
18 local registered_nodes
19 minetest.register_on_mods_loaded(function()
20 registered_nodes = minetest.registered_nodes
24 local new_vec = vector.new
25 local vec_distance = vector.distance
26 local add_vec = vector.add
27 local multiply_vec = vector.multiply
28 local vec_direction = vector.direction
32 local random = math.random
36 local s_sub = string.sub
37 local s_len = string.len
40 -- loads data from mod storage
43 local load_data = function(player)
44 name = player:get_player_name()
46 temp_pool = pool[name]
47 if mod_storage:get_int(name.."xp_save") > 0 then
48 temp_pool.xp_level = mod_storage:get_int(name.."xp_level")
49 temp_pool.xp_bar = mod_storage:get_int(name.."xp_bar" )
51 temp_pool.last_time= get_time()/1000000
53 temp_pool.xp_level = 0
56 temp_pool.last_time= get_time()/1000000
60 -- saves data to be utilized on next login
63 local save_data = function(name)
64 if type(name) ~= "string" and name:is_player() then
65 name = name:get_player_name()
67 temp_pool = pool[name]
69 mod_storage:set_int(name.."xp_level",temp_pool.xp_level)
70 mod_storage:set_int(name.."xp_bar", temp_pool.xp_bar )
72 mod_storage:set_int(name.."xp_save",1)
77 -- saves specific users data for when they relog
78 minetest.register_on_leaveplayer(function(player)
82 -- is used for shutdowns to save all data
83 local save_all = function()
84 for name,_ in pairs(pool) do
89 -- save all data to mod storage on shutdown
90 minetest.register_on_shutdown(function()
96 function get_player_xp_level(player)
97 name = player:get_player_name()
98 return(pool[name].xp_level)
103 function set_player_xp_level(player,level)
104 name = player:get_player_name()
105 pool[name].xp_level = level
106 hud_manager.change_hud({
108 hud_name = "xp_level_fg",
110 data = tostring(level)
112 hud_manager.change_hud({
114 hud_name = "xp_level_bg",
116 data = tostring(level)
120 minetest.hud_replace_builtin("health",{
121 hud_elem_type = "statbar",
122 position = {x = 0.5, y = 1},
124 number = core.PLAYER_MAX_HP_DEFAULT,
126 size = {x = 24, y = 24},
127 offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
132 minetest.register_on_joinplayer(function(player)
136 name = player:get_player_name()
137 temp_pool = pool[name]
139 hud_manager.add_hud(player,"heart_bar_bg",{
140 hud_elem_type = "statbar",
141 position = {x = 0.5, y = 1},
142 text = "heart_bg.png",
143 number = core.PLAYER_MAX_HP_DEFAULT,
145 size = {x = 24, y = 24},
146 offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
150 hud_manager.add_hud(player,"experience_bar_background",{
151 hud_elem_type = "statbar",
152 position = {x=0.5, y=1},
153 name = "experience bar background",
154 text = "experience_bar_background.png",
157 offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
158 size = { x=28, y=28 },
162 hud_manager.add_hud(player,"experience_bar",{
163 hud_elem_type = "statbar",
164 position = {x=0.5, y=1},
165 name = "experience bar",
166 text = "experience_bar.png",
167 number = temp_pool.xp_bar,
169 offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
170 size = { x=28, y=28 },
174 hud_manager.add_hud(player,"xp_level_bg",{
175 hud_elem_type = "text",
176 position = {x=0.5, y=1},
177 name = "xp_level_bg",
178 text = tostring(temp_pool.xp_level),
180 offset = {x = 0, y = -(48 + 24 + 24)},
183 hud_manager.add_hud(player,"xp_level_fg",{
184 hud_elem_type = "text",
185 position = {x=0.5, y=1},
186 name = "xp_level_fg",
187 text = tostring(temp_pool.xp_level),
189 offset = {x = -1, y = -(48 + 24 + 25)},
197 local function level_up_experience(player)
198 name = player:get_player_name()
199 temp_pool = pool[name]
201 temp_pool.xp_level = temp_pool.xp_level + 1
203 hud_manager.change_hud({
205 hud_name = "xp_level_fg",
207 data = tostring(temp_pool.xp_level)
209 hud_manager.change_hud({
211 hud_name = "xp_level_bg",
213 data = tostring(temp_pool.xp_level)
220 local function add_experience(player,experience)
221 name = player:get_player_name()
222 temp_pool = pool[name]
224 temp_pool.xp_bar = temp_pool.xp_bar + experience
226 if temp_pool.xp_bar > 36 then
227 if get_time()/1000000 - temp_pool.last_time > 0.04 then
228 play_sound("level_up",{gain=0.2,to_player = name})
229 temp_pool.last_time = get_time()/1000000
231 temp_pool.xp_bar = temp_pool.xp_bar - 36
232 level_up_experience(player)
234 if get_time()/1000000 - temp_pool.last_time > 0.01 then
235 temp_pool.last_time = get_time()/1000000
236 play_sound("experience",{gain=0.1,to_player = name,pitch=random(75,99)/100})
239 hud_manager.change_hud({
241 hud_name = "experience_bar",
243 data = temp_pool.xp_bar
251 minetest.register_on_dieplayer(function(player)
252 name = player:get_player_name()
253 temp_pool = pool[name]
254 xp_amount = temp_pool.xp_level
257 temp_pool.xp_level = 0
260 hud_manager.change_hud({
262 hud_name = "xp_level_fg",
264 data = tostring(temp_pool.xp_level)
266 hud_manager.change_hud({
268 hud_name = "xp_level_bg",
270 data = tostring(temp_pool.xp_level)
273 hud_manager.change_hud({
275 hud_name = "experience_bar",
277 data = temp_pool.xp_bar
280 minetest.throw_experience(player:get_pos(), xp_amount)
291 local player_velocity
306 local function xp_step(self, dtime)
307 --if item set to be collected then only execute go to player
308 if self.collected == true then
309 if not self.collector then
310 self.collected = false
313 collector = get_player_by_name(self.collector)
314 if collector and collector:get_hp() > 0 and vec_distance(self.object:get_pos(),collector:get_pos()) < 5 then
315 temp_pool = pool[self.collector]
317 self.object:set_acceleration(new_vec(0,0,0))
318 self.disable_physics(self)
320 pos = self.object:get_pos()
321 pos2 = collector:get_pos()
323 player_velocity = collector:get_player_velocity()
325 pos2.y = pos2.y + 0.8
327 direction = vec_direction(pos,pos2)
328 distance = vec_distance(pos2,pos)
329 multiplier = distance
330 if multiplier < 1 then
333 goal = multiply_vec(direction,multiplier)
334 currentvel = self.object:get_velocity()
337 multiplier = 20 - distance
338 velocity = multiply_vec(direction,multiplier)
340 acceleration = new_vec(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
341 self.object:add_velocity(add_vec(acceleration,player_velocity))
342 elseif distance > 0.9 and temp_pool.buffer > 0 then
343 temp_pool.buffer = temp_pool.buffer - dtime
344 multiplier = 20 - distance
345 velocity = multiply_vec(direction,multiplier)
346 goal = multiply_vec(yaw_to_dir(dir_to_yaw(vec_direction(new_vec(pos.x,0,pos.z),new_vec(pos2.x,0,pos2.z)))+pi/2),10)
347 goal = add_vec(player_velocity,goal)
348 acceleration = new_vec(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
349 self.object:add_velocity(acceleration)
351 if distance < 0.4 and temp_pool.buffer <= 0 then
352 temp_pool.buffer = 0.04
353 add_experience(collector,2)
359 self.enable_physics(self)
364 self.age = self.age + dtime
365 if self.age > 300 then
370 pos = self.object:get_pos()
373 node = get_node_or_nil({
382 -- Remove nodes in 'ignore'
383 if node and node.name == "ignore" then
388 if not self.physical_state then
389 return -- Don't do anything
392 -- Slide on slippery nodes
393 vel = self.object:get_velocity()
394 def = node and registered_nodes[node.name]
395 is_moving = (def and not def.walkable) or
396 vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
399 if def and def.walkable then
400 slippery = get_item_group(node.name, "slippery")
401 is_slippery = slippery ~= 0
402 if is_slippery and (abs(vel.x) > 0.2 or abs(vel.z) > 0.2) then
403 -- Horizontal deceleration
404 slip_factor = 4.0 / (slippery + 4)
405 self.object:set_acceleration({
406 x = -vel.x * slip_factor,
408 z = -vel.z * slip_factor
410 elseif vel.y == 0 then
415 if self.moving_state == is_moving and self.slippery_state == is_slippery then
416 -- Do not update anything until the moving state changes
420 self.moving_state = is_moving
421 self.slippery_state = is_slippery
424 self.object:set_acceleration({x = 0, y = -9.81, z = 0})
426 self.object:set_acceleration({x = 0, y = 0, z = 0})
427 self.object:set_velocity({x = 0, y = 0, z = 0})
431 minetest.register_entity("experience:orb", {
432 initial_properties = {
435 collide_with_objects = false,
436 collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
438 visual_size = {x = 0.4, y = 0.4},
439 textures = {name="experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
440 spritediv = {x = 1, y = 14},
441 initial_sprite_basepos = {x = 0, y = 0},
446 slippery_state = false,
447 physical_state = true,
450 -- Pushing item out of solid nodes
452 force_out_start = nil,
453 --Collection Variables
460 get_staticdata = function(self)
463 collectable = self.collectable,
464 try_timer = self.try_timer,
465 collected = self.collected,
466 delete_timer = self.delete_timer,
467 collector = self.collector,
471 on_activate = function(self, staticdata, dtime_s)
472 if s_sub(staticdata, 1, s_len("return")) == "return" then
473 data = deserialize(staticdata)
474 if data and type(data) == "table" then
475 self.age = (data.age or 0) + dtime_s
476 self.collectable = data.collectable
477 self.try_timer = data.try_timer
478 self.collected = data.collected
479 self.delete_timer = data.delete_timer
480 self.collector = data.collector
483 self.object:set_velocity(new_vec(
484 random(-2,2)*random(),
486 random(-2,2)*random()
489 self.object:set_armor_groups({immortal = 1})
490 self.object:set_velocity({x = 0, y = 2, z = 0})
491 self.object:set_acceleration({x = 0, y = -9.81, z = 0})
492 size = random(20,36)/100
493 self.object:set_properties({
494 visual_size = {x = size, y = size},
497 self.object:set_sprite({x=1,y=random(1,14)}, 14, 0.05, false)
500 enable_physics = function(self)
501 if not self.physical_state then
502 self.physical_state = true
503 self.object:set_properties({physical = true})
504 self.object:set_velocity({x=0, y=0, z=0})
505 self.object:set_acceleration({x=0, y=-9.81, z=0})
509 disable_physics = function(self)
510 if self.physical_state then
511 self.physical_state = false
512 self.object:set_properties({physical = false})
513 self.object:set_velocity({x=0, y=0, z=0})
514 self.object:set_acceleration({x=0, y=0, z=0})
517 on_step = function(self, dtime)
523 minetest.register_chatcommand("xp", {
525 description = "Spawn x amount of a mob, used as /spawn 'mob' 10 or /spawn 'mob' for one",
526 privs = {server=true},
527 func = function(name)
528 local player = get_player_by_name(name)
529 local pos = player:get_pos()
531 minetest.throw_experience(pos, 1000)
535 minetest.register_chatcommand("levelup", {
537 description = "Spawn x amount of a mob, used as /spawn 'mob' 10 or /spawn 'mob' for one",
538 privs = {server=true},
539 func = function(name)
540 local player = get_player_by_name(name)
542 level_up_experience(player)