]> git.lizzy.rs Git - Crafter.git/blob - mods/experience/init.lua
Fix a bug with experience
[Crafter.git] / mods / experience / init.lua
1 minetest.hud_replace_builtin("health",{
2     hud_elem_type = "statbar",
3     position = {x = 0.5, y = 1},
4     text = "heart.png",
5     number = core.PLAYER_MAX_HP_DEFAULT,
6     direction = 0,
7     size = {x = 24, y = 24},
8     offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
9 })
10
11 local experience_bar_max = 36
12 minetest.register_on_joinplayer(function(player)
13     local meta = player:get_meta()
14     meta:set_float("experience_collection_buffer",0)
15     player:hud_add({
16         hud_elem_type = "statbar",
17         position = {x = 0.5, y = 1},
18         text = "heart_bg.png",
19         number = core.PLAYER_MAX_HP_DEFAULT,
20         direction = 0,
21         size = {x = 24, y = 24},
22         offset = {x = (-10 * 24) - 25, y = -(48 + 24 + 38)},
23     })
24     player:hud_add({
25         hud_elem_type = "statbar",  -- See HUD element types
26         -- Type of element, can be "image", "text", "statbar", or "inventory"
27
28         position = {x=0.5, y=1},
29         -- Left corner position of element
30
31         name = "experience",
32
33         --scale = {x = 2, y = 2},
34
35         text = "experience_bar_background.png",
36
37         number = experience_bar_max,
38
39         --item = 3,
40         -- Selected item in inventory. 0 for no item selected.
41
42         direction = 0,
43         -- Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
44
45         offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
46
47         size = { x=28, y=28 },
48         -- Size of element in pixels
49
50         z_index = 0,
51         -- Z index : lower z-index HUDs are displayed behind higher z-index HUDs
52     })
53     local hud_id = player:hud_add({
54         hud_elem_type = "statbar",  -- See HUD element types
55         -- Type of element, can be "image", "text", "statbar", or "inventory"
56
57         position = {x=0.5, y=1},
58         -- Left corner position of element
59
60         name = "experience",
61
62         --scale = {x = 2, y = 2},
63
64         text = "experience_bar.png",
65
66         number = meta:get_int("experience_bar_count"),
67
68         --item = 3,
69         -- Selected item in inventory. 0 for no item selected.
70
71         direction = 0,
72         -- Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
73
74         offset = {x = (-8 * 28) - 29, y = -(48 + 24 + 16)},
75
76         size = { x=28, y=28 },
77         -- Size of element in pixels
78
79         z_index = 0,
80         -- Z index : lower z-index HUDs are displayed behind higher z-index HUDs
81     })
82     
83     local meta = player:get_meta()
84     local level = meta:get_int("experience_level")                                
85     local hud_bg_id = player:hud_add({
86         hud_elem_type = "text",  -- See HUD element types
87         -- Type of element, can be "image", "text", "statbar", or "inventory"
88
89         position = {x=0.5, y=1},
90         -- Left corner position of element
91
92         name = "levelbg",
93
94         --scale = {x = 2, y = 2},
95
96         text = tostring(level),
97
98         number = 0x000000,--0xFFFFFF,
99
100         --item = 3,
101         -- Selected item in inventory. 0 for no item selected.
102
103         --direction = 0,
104         -- Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
105
106         offset = {x = 0, y = -(48 + 24 + 24)},
107
108         --size = { x=28, y=28 },
109         -- Size of element in pixels
110
111         z_index = 0,
112         -- Z index : lower z-index HUDs are displayed behind higher z-index HUDs
113     })                            
114     local hud_fg_id = player:hud_add({
115         hud_elem_type = "text",  -- See HUD element types
116         -- Type of element, can be "image", "text", "statbar", or "inventory"
117
118         position = {x=0.5, y=1},
119         -- Left corner position of element
120
121         name = "levelfg",
122
123         --scale = {x = 2, y = 2},
124
125         text = tostring(level),
126
127         number = 0xFFFFFF,
128
129         --item = 3,
130         -- Selected item in inventory. 0 for no item selected.
131
132         --direction = 0,
133         -- Direction: 0: left-right, 1: right-left, 2: top-bottom, 3: bottom-top
134
135         offset = {x = -1, y = -(48 + 24 + 25)},
136
137         --size = { x=28, y=28 },
138         -- Size of element in pixels
139
140         z_index = 0,
141         -- Z index : lower z-index HUDs are displayed behind higher z-index HUDs
142     }) 
143     meta:set_int("experience_bar", hud_id)
144     meta:set_int("experience_level_fg", hud_fg_id)                                
145     meta:set_int("experience_level_bg", hud_bg_id)                                                            
146 end)
147
148
149 function level_up_experience(player)
150     local meta = player:get_meta()
151     local level = meta:get_int("experience_level")
152     level = level + 1
153     meta:set_int("experience_level",level)
154     
155     local hud_fg_id = meta:get_int("experience_level_fg")
156     local hud_bg_id = meta:get_int("experience_level_bg")
157     
158     player:hud_change(hud_bg_id, "text", tostring(level))
159     player:hud_change(hud_fg_id, "text", tostring(level))
160 end
161
162 function add_experience(player,experience)
163     local meta = player:get_meta()
164     local hud_id = meta:get_int("experience_bar")
165     local hud = player:hud_get(hud_id)
166     local bar_count = hud.number
167     bar_count = bar_count + experience
168     if bar_count > experience_bar_max then
169         minetest.sound_play("level_up",{gain=0.2,to_player = player:get_player_name()})
170         bar_count = bar_count - experience_bar_max
171         level_up_experience(player)
172     else
173         minetest.sound_play("experience",{gain=0.1,to_player = player:get_player_name(),pitch=math.random(75,99)/100})
174     end
175     meta:set_int("experience_bar_count",bar_count)
176     player:hud_change(hud_id, number, bar_count)
177 end
178
179 --[[
180 local function test_experience()
181     for _, player in pairs(minetest.get_connected_players()) do
182         add_experience(player,math.random(1,3)*2)
183     end
184        
185     minetest.after(0.1, function()
186         test_experience()
187     end)
188 end
189 test_experience()                  
190 ]]--
191
192 --reset player level
193 minetest.register_on_dieplayer(function(player)
194     local meta = player:get_meta()
195     local amount_of_experience = (meta:get_int("experience_bar_count")/2) + (meta:get_int("experience_level") * 18)
196     --bar
197     meta:set_int("experience_bar_count",0)
198     local hud_id = meta:get_int("experience_bar")
199     player:hud_change(hud_id, number, 0)
200                               
201     --level number
202     local level = 0
203     meta:set_int("experience_level",level)
204     
205     local hud_fg_id = meta:get_int("experience_level_fg")
206     local hud_bg_id = meta:get_int("experience_level_bg")
207     
208     player:hud_change(hud_bg_id, "text", tostring(level))
209     player:hud_change(hud_fg_id, "text", tostring(level))
210                               
211     minetest.throw_experience(player:get_pos(), amount_of_experience)
212                               
213 end)
214
215
216 local time_to_live = tonumber(minetest.settings:get("item_entity_ttl")) or 300
217 local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81
218
219
220 minetest.register_entity("experience:orb", {
221         initial_properties = {
222                 hp_max = 1,
223                 physical = true,
224                 collide_with_objects = false,
225                 collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
226                 visual = "sprite",
227                 visual_size = {x = 0.4, y = 0.4},
228                 textures = {name="experience_orb.png", animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=2.0}},
229                 spritediv = {x = 1, y = 14},
230                 initial_sprite_basepos = {x = 0, y = 0},
231                 is_visible = true,
232                 pointable = false,
233         },
234         moving_state = true,
235         slippery_state = false,
236         physical_state = true,
237         -- Item expiry
238         age = 0,
239         -- Pushing item out of solid nodes
240         force_out = nil,
241         force_out_start = nil,
242         --Collection Variables
243         collection_timer = 2,
244         collection_timer_goal = collection.collection_time,
245         collection_height = 0.8,
246         collectable = false,
247         try_timer = 0,
248         collected = false,
249         delete_timer = 0,
250         radius = collection.magnet_radius,
251         time_to_live = time_to_live,
252
253         get_staticdata = function(self)
254                 return minetest.serialize({
255                         age = self.age,
256                         collection_timer = self.collection_timer,
257                         collectable = self.collectable,
258                         try_timer = self.try_timer,
259                         collected = self.collected,
260                         delete_timer = self.delete_timer,
261                         collector = self.collector,
262                 })
263         end,
264
265         on_activate = function(self, staticdata, dtime_s)
266                 if string.sub(staticdata, 1, string.len("return")) == "return" then
267                         local data = minetest.deserialize(staticdata)
268                         if data and type(data) == "table" then
269                                 self.age = (data.age or 0) + dtime_s
270                                 self.collection_timer = data.collection_timer
271                                 self.collectable = data.collectable
272                                 self.try_timer = data.try_timer
273                                 self.collected = data.collected
274                                 self.delete_timer = data.delete_timer
275                                 self.collector = data.collector
276                                 --print("restored timer: "..self.collection_timer)
277                         end
278                 else
279
280                         local x=math.random(-2,2)*math.random()
281                         local y=math.random(2,5)
282                         local z=math.random(-2,2)*math.random()
283                         self.object:setvelocity(vector.new(x,y,z))
284                      -- print(self.collection_timer)
285                 end
286                 self.object:set_armor_groups({immortal = 1})
287                 self.object:set_velocity({x = 0, y = 2, z = 0})
288                 self.object:set_acceleration({x = 0, y = -gravity, z = 0})
289         local size = math.random(20,36)/100
290         self.object:set_properties({
291                         visual_size = {x = size, y = size},
292                         glow = 14,
293                 })
294                 self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
295         end,
296
297         enable_physics = function(self)
298                 if not self.physical_state then
299                         self.physical_state = true
300                         self.object:set_properties({physical = true})
301                         self.object:set_velocity({x=0, y=0, z=0})
302                         self.object:set_acceleration({x=0, y=-gravity, z=0})
303                 end
304         end,
305
306         disable_physics = function(self)
307                 if self.physical_state then
308                         self.physical_state = false
309                         self.object:set_properties({physical = false})
310                         self.object:set_velocity({x=0, y=0, z=0})
311                         self.object:set_acceleration({x=0, y=0, z=0})
312                 end
313         end,
314         on_step = function(self, dtime)
315                 --if item set to be collected then only execute go to player
316                 if self.collected == true then
317                         if not self.collector then
318                                 self.collected = false
319                                 return
320                         end
321                         local collector = minetest.get_player_by_name(self.collector)
322                         if collector and collector:get_hp() > 0 then
323                                 self.object:setacceleration(vector.new(0,0,0))
324                                 self.disable_physics(self)
325                                 --get the variables
326                                 local pos = self.object:getpos()
327                                 local pos2 = collector:getpos()
328                                 
329                 local player_velocity = collector:get_player_velocity()
330                                             
331                                 pos2.y = pos2.y + self.collection_height
332                                                                 
333                                 local direction = vector.direction(pos,pos2)
334                                 local distance = vector.distance(pos2,pos)
335                 local multiplier = distance
336                 if multiplier < 1 then
337                     multiplier = 1
338                 end
339                                 local goal = vector.multiply(direction,multiplier)
340                 local currentvel = self.object:get_velocity()
341                 local acceleration
342                                 if distance > 1 then
343                     local multiplier = (self.radius*5) - distance
344                     local velocity = vector.multiply(direction,multiplier)
345                     local goal = velocity--vector.add(player_velocity,velocity)
346                     acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
347                 else
348                     acceleration = vector.new(goal.x,goal.y,goal.z)
349                 end
350                                 --acceleration = vector.multiply(acceleration, )
351                                             
352                 
353                                 
354                                 self.object:add_velocity(acceleration)
355                                 
356                                             
357                                             
358                     
359                 local meta = collector:get_meta()
360                 local experience_collection_buffer = meta:get_float("experience_collection_buffer")
361                                 if distance < 0.2 and experience_collection_buffer == 0 then
362                     meta:set_float("experience_collection_buffer",0.04)
363                     add_experience(collector,2)
364                                         self.object:remove()
365                                 end
366                                 
367                                 
368                                 --self.delete_timer = self.delete_timer + dtime
369                                 --this is where the item gets removed from world
370                                 --if self.delete_timer > 1 then
371                                 --      self.object:remove()
372                                 --end
373                                 return
374                         else
375                                 print(self.collector.." does not exist")
376                                 self.object:remove()
377                         end
378                 end
379                 
380                 --allow entity to be collected after timer
381                 if self.collectable == false and self.collection_timer >= self.collection_timer_goal then
382                         self.collectable = true
383                 elseif self.collectable == false then
384                         self.collection_timer = self.collection_timer + dtime
385                 end
386                                 
387                 self.age = self.age + dtime
388                 if self.time_to_live > 0 and self.age > self.time_to_live then
389                         self.object:remove()
390                         return
391                 end
392
393                 local pos = self.object:get_pos()
394                 local node
395                 if pos then
396                         node = minetest.get_node_or_nil({
397                                 x = pos.x,
398                                 y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
399                                 z = pos.z
400                         })
401                 else
402                         return
403                 end
404
405                 -- Remove nodes in 'ignore'
406                 if node and node.name == "ignore" then
407                         self.object:remove()
408                         return
409                 end
410
411                 local is_stuck = false
412                 local snode = minetest.get_node_or_nil(pos)
413                 if snode then
414                         local sdef = minetest.registered_nodes[snode.name] or {}
415                         is_stuck = (sdef.walkable == nil or sdef.walkable == true)
416                                 and (sdef.collision_box == nil or sdef.collision_box.type == "regular")
417                                 and (sdef.node_box == nil or sdef.node_box.type == "regular")
418                 end
419
420                 -- Push item out when stuck inside solid node
421                 if is_stuck then
422                         local shootdir
423                         local order = {
424                                 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
425                                 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
426                         }
427
428                         -- Check which one of the 4 sides is free
429                         for o = 1, #order do
430                                 local cnode = minetest.get_node(vector.add(pos, order[o])).name
431                                 local cdef = minetest.registered_nodes[cnode] or {}
432                                 if cnode ~= "ignore" and cdef.walkable == false then
433                                         shootdir = order[o]
434                                         break
435                                 end
436                         end
437                         -- If none of the 4 sides is free, check upwards
438                         if not shootdir then
439                                 shootdir = {x=0, y=1, z=0}
440                                 local cnode = minetest.get_node(vector.add(pos, shootdir)).name
441                                 if cnode == "ignore" then
442                                         shootdir = nil -- Do not push into ignore
443                                 end
444                         end
445
446                         if shootdir then
447                                 -- Set new item moving speed accordingly
448                                 local newv = vector.multiply(shootdir, 3)
449                                 self:disable_physics()
450                                 self.object:set_velocity(newv)
451
452                                 self.force_out = newv
453                                 self.force_out_start = vector.round(pos)
454                                 return
455                         end
456                 elseif self.force_out then
457                         -- This code runs after the entity got a push from the above code.
458                         -- It makes sure the entity is entirely outside the solid node
459                         local c = self.object:get_properties().collisionbox
460                         local s = self.force_out_start
461                         local f = self.force_out
462                         local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
463                                 (f.y > 0 and pos.y + c[2] > s.y + 0.5) or
464                                 (f.z > 0 and pos.z + c[3] > s.z + 0.5) or
465                                 (f.x < 0 and pos.x + c[4] < s.x - 0.5) or
466                                 (f.z < 0 and pos.z + c[6] < s.z - 0.5)
467                         if ok then
468                                 -- Item was successfully forced out
469                                 self.force_out = nil
470                                 self:enable_physics()
471                         end
472                 end
473
474                 if not self.physical_state then
475                         return -- Don't do anything
476                 end
477
478                 -- Slide on slippery nodes
479                 local vel = self.object:get_velocity()
480                 local def = node and minetest.registered_nodes[node.name]
481                 local is_moving = (def and not def.walkable) or
482                         vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
483                 local is_slippery = false
484
485                 if def and def.walkable then
486                         local slippery = minetest.get_item_group(node.name, "slippery")
487                         is_slippery = slippery ~= 0
488                         if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
489                                 -- Horizontal deceleration
490                                 local slip_factor = 4.0 / (slippery + 4)
491                                 self.object:set_acceleration({
492                                         x = -vel.x * slip_factor,
493                                         y = 0,
494                                         z = -vel.z * slip_factor
495                                 })
496                         elseif vel.y == 0 then
497                                 is_moving = false
498                         end
499                 end
500
501                 if self.moving_state == is_moving and self.slippery_state == is_slippery then
502                         -- Do not update anything until the moving state changes
503                         return
504                 end
505
506                 self.moving_state = is_moving
507                 self.slippery_state = is_slippery
508                 
509                 if is_moving then
510                         self.object:set_acceleration({x = 0, y = -gravity, z = 0})
511                 else
512                         self.object:set_acceleration({x = 0, y = 0, z = 0})
513                         self.object:set_velocity({x = 0, y = 0, z = 0})
514                 end
515         end,
516 })