]> git.lizzy.rs Git - Crafter.git/blob - mods/itemhandling/init.lua
Add in custom mob api explosion blink colors
[Crafter.git] / mods / itemhandling / init.lua
1 collection = {}
2 collection.collection_height = 0.5 -- the height of the collection based off the player's origin y height
3 collection.magnet_radius = 2 -- the radius of the item magnet
4 collection.allow_lower = false -- false = items below origin y of player will not be collected --true = player will collect all objects in radius --false = minecraft style --true = pilzadam style
5 collection.collection_time = 2.5 --the time which the item will be collected
6
7 local path = minetest.get_modpath("itemhandling")
8 dofile(path.."/magnet.lua")
9
10
11 local creative_mode = minetest.settings:get_bool("creative_mode")
12
13 --handle node drops
14 --survival
15 if not creative_mode then
16         function minetest.handle_node_drops(pos, drops, digger)
17                 local meta = digger:get_wielded_item():get_meta()
18                 local slippery =  meta:get_int("slippery")
19                 local careful = meta:get_int("careful")
20                 local fortune = meta:get_int("fortune") + 1
21                 local autorepair = meta:get_int("autorepair")
22                 local spiky = meta:get_int("spiky")
23                 if careful > 0 then
24                         drops = {minetest.get_node(pos).name}
25                 end
26                 for i = 1,fortune do
27                         for _,item in ipairs(drops) do
28                                 local count, name
29                                 if type(item) == "string" then
30                                         count = 1
31                                         name = item
32                                 else
33                                         count = item:get_count()
34                                         name = item:get_name()
35                                 end
36                                 for i=1,count do
37                                         local obj = minetest.add_item(pos, name)
38                                         if obj ~= nil then
39                                                 local x=math.random(-2,2)*math.random()
40                                                 local y=math.random(2,5)
41                                                 local z=math.random(-2,2)*math.random()
42                                                 obj:setvelocity({x=x, y=y, z=z})
43                                         end
44                                 end
45                         end
46                 local experience_amount = minetest.get_item_group(minetest.get_node(pos).name,"experience")
47                 if experience_amount > 0 then
48                     minetest.throw_experience(pos, experience_amount)
49                 end
50                 end
51                 --make the player drop their "slippery" item
52                 if slippery > 0 and math.random(0,1000) < slippery then
53                         minetest.item_drop(digger:get_wielded_item(), digger, digger:get_pos())
54                         digger:set_wielded_item("")
55                 end
56                 
57                 --auto repair the item
58                 if autorepair > 0 and math.random(0,1000) < autorepair then
59                         local itemstack = digger:get_wielded_item()
60                         itemstack:add_wear(autorepair*-100)
61                         digger:set_wielded_item(itemstack)
62                 end
63                 
64                 --hurt the player randomly
65                 if spiky > 0 and math.random(0,1000) < spiky then
66                         digger:set_hp(digger:get_hp()-spiky)
67                 end
68         end
69 --creative
70 else
71         function minetest.handle_node_drops(pos, drops, digger)
72         end
73     minetest.register_on_dignode(function(pos, oldnode, digger)
74                 if digger and digger:is_player() then
75                         local inv = digger:get_inventory()
76                         if inv and not inv:contains_item("main", oldnode) and inv:room_for_item("main", oldnode) then
77                                 inv:add_item("main", oldnode)
78                         end
79                 end
80         end)
81         minetest.register_on_placenode(function(pos, newnode, placer, oldnode, itemstack, pointed_thing)
82                 return(itemstack:get_name())
83         end)
84 end
85
86 -- Minetest: builtin/item_entity.lua
87
88 function minetest.spawn_item(pos, item)
89         -- Take item in any format
90         local stack = ItemStack(item)
91         local obj = minetest.add_entity(pos, "__builtin:item")
92         -- Don't use obj if it couldn't be added to the map.
93         if obj then
94                 obj:get_luaentity():set_item(stack:to_string())
95         end
96         return obj
97 end
98
99 function minetest.throw_item(pos, item)
100         -- Take item in any format
101         local stack = ItemStack(item)
102         local obj = minetest.add_entity(pos, "__builtin:item")
103         -- Don't use obj if it couldn't be added to the map.
104         if obj then
105                 obj:get_luaentity():set_item(stack:to_string())
106                 local x=math.random(-2,2)*math.random()
107                 local y=math.random(2,5)
108                 local z=math.random(-2,2)*math.random()
109                 obj:set_velocity({x=x, y=y, z=z})
110         end
111         return obj
112 end
113
114
115 function minetest.throw_experience(pos, amount)
116     for i = 1,amount do
117         local obj = minetest.add_entity(pos, "experience:orb")
118         -- Don't use obj if it couldn't be added to the map.
119         if obj then
120             local x=math.random(-2,2)*math.random()
121             local y=math.random(2,5)
122             local z=math.random(-2,2)*math.random()
123             obj:setvelocity({x=x, y=y, z=z})
124         end
125     end
126         --return obj
127 end
128
129 --override drops
130 function minetest.item_drop(itemstack, dropper, pos)
131         local dropper_is_player = dropper and dropper:is_player()
132         local p = table.copy(pos)
133         local sneak = dropper:get_player_control().sneak
134         local cnt
135         if dropper_is_player then
136                 p.y = p.y + 1.2
137                 if not sneak then
138                         cnt = itemstack:get_count()
139                 else
140                         cnt = 1
141                 end
142         else
143                 cnt = itemstack:get_count()
144         end
145         local item = itemstack:take_item(cnt)
146         local obj = minetest.add_item(p, item)
147         if obj then
148                 if dropper_is_player then
149                         local dir = dropper:get_look_dir()
150                         dir.x = dir.x * 2.9
151                         dir.y = dir.y * 2.9 + 2
152                         dir.z = dir.z * 2.9
153                         obj:set_velocity(dir)
154                         obj:get_luaentity().dropped_by = dropper:get_player_name()
155                         obj:get_luaentity().collection_timer = 0
156                 end
157                 return itemstack
158         end
159         -- If we reach this, adding the object to the
160         -- environment failed
161 end
162
163 -- If item_entity_ttl is not set, enity will have default life time
164 -- Setting it to -1 disables the feature
165
166 local time_to_live = tonumber(minetest.settings:get("item_entity_ttl")) or 300
167 local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81
168
169
170 minetest.register_entity(":__builtin:item", {
171         initial_properties = {
172                 hp_max = 1,
173                 physical = true,
174                 collide_with_objects = false,
175                 collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
176                 visual = "wielditem",
177                 visual_size = {x = 0.4, y = 0.4},
178                 textures = {""},
179                 spritediv = {x = 1, y = 1},
180                 initial_sprite_basepos = {x = 0, y = 0},
181                 is_visible = false,
182                 pointable = false,
183         },
184
185         itemstring = "",
186         moving_state = true,
187         slippery_state = false,
188         physical_state = true,
189         -- Item expiry
190         age = 0,
191         -- Pushing item out of solid nodes
192         force_out = nil,
193         force_out_start = nil,
194         --Collection Variables
195         collection_timer = 2,
196         collection_timer_goal = collection.collection_time,
197         collection_height = collection.collection_height,
198         collectable = false,
199         try_timer = 0,
200         collected = false,
201         delete_timer = 0,
202         radius = collection.magnet_radius,
203         time_to_live = time_to_live,
204
205         set_item = function(self, item)
206                 local stack = ItemStack(item or self.itemstring)
207                 self.itemstring = stack:to_string()
208                 if self.itemstring == "" then
209                         -- item not yet known
210                         return
211                 end
212
213                 -- Backwards compatibility: old clients use the texture
214                 -- to get the type of the item
215                 local itemname = stack:is_known() and stack:get_name() or "unknown"
216
217                 local max_count = stack:get_stack_max()
218                 local count = math.min(stack:get_count(), max_count)
219
220                 local size = 0.21
221                 local coll_height = size * 0.75
222                 local def = minetest.registered_nodes[itemname]
223                 local glow = def and def.light_source
224
225                 self.object:set_properties({
226                         is_visible = true,
227                         visual = "wielditem",
228                         textures = {itemname},
229                         visual_size = {x = size, y = size},
230                         collisionbox = {-size, -0.21, -size,
231                                 size, coll_height, size},
232                         selectionbox = {-size, -size, -size, size, size, size},
233                         automatic_rotate = math.pi * 0.5 * 0.2 / size,
234                         wield_item = self.itemstring,
235                         glow = glow,
236                 })
237
238         end,
239
240         get_staticdata = function(self)
241                 return minetest.serialize({
242                         itemstring = self.itemstring,
243                         age = self.age,
244                         dropped_by = self.dropped_by,
245                         collection_timer = self.collection_timer,
246                         collectable = self.collectable,
247                         try_timer = self.try_timer,
248                         collected = self.collected,
249                         delete_timer = self.delete_timer,
250                         collector = self.collector,
251                 })
252         end,
253
254         on_activate = function(self, staticdata, dtime_s)
255                 if string.sub(staticdata, 1, string.len("return")) == "return" then
256                         local data = minetest.deserialize(staticdata)
257                         if data and type(data) == "table" then
258                                 self.itemstring = data.itemstring
259                                 self.age = (data.age or 0) + dtime_s
260                                 self.dropped_by = data.dropped_by
261                                 
262                                 self.collection_timer = data.collection_timer
263                                 self.collectable = data.collectable
264                                 self.try_timer = data.try_timer
265                                 self.collected = data.collected
266                                 self.delete_timer = data.delete_timer
267                                 self.collector = data.collector
268                                 --print("restored timer: "..self.collection_timer)
269                         end
270                 else
271                         self.itemstring = staticdata
272                         
273                         local x=math.random(-2,2)*math.random()
274                         local y=math.random(2,5)
275                         local z=math.random(-2,2)*math.random()
276                         self.object:setvelocity(vector.new(x,y,z))
277                      -- print(self.collection_timer)
278                 end
279                 self.object:set_armor_groups({immortal = 1})
280                 self.object:set_velocity({x = 0, y = 2, z = 0})
281                 self.object:set_acceleration({x = 0, y = -gravity, z = 0})
282                 self:set_item()
283         end,
284
285         enable_physics = function(self)
286                 if not self.physical_state then
287                         self.physical_state = true
288                         self.object:set_properties({physical = true})
289                         self.object:set_velocity({x=0, y=0, z=0})
290                         self.object:set_acceleration({x=0, y=-gravity, z=0})
291                 end
292         end,
293
294         disable_physics = function(self)
295                 if self.physical_state then
296                         self.physical_state = false
297                         self.object:set_properties({physical = false})
298                         self.object:set_velocity({x=0, y=0, z=0})
299                         self.object:set_acceleration({x=0, y=0, z=0})
300                 end
301         end,
302         on_step = function(self, dtime)
303                 --if item set to be collected then only execute go to player
304                 if self.collected == true then
305                         if not self.collector then
306                                 self.collected = false
307                                 return
308                         end
309                         local collector = minetest.get_player_by_name(self.collector)
310                         if collector then
311                                 self.object:setacceleration(vector.new(0,0,0))
312                                 self.disable_physics(self)
313                                 --get the variables
314                                 local pos = self.object:getpos()
315                                 local pos2 = collector:getpos()
316                                 local player_velocity = collector:get_player_velocity()
317                                 pos2.y = pos2.y + self.collection_height
318                                                                 
319                                 local direction = vector.normalize(vector.subtract(pos2,pos))
320                                 local distance = vector.distance(pos2,pos)
321                                                                 
322                                 
323                                 --remove if too far away
324                                 if distance > self.radius then
325                                         distance = 0
326                                 end
327                                                                 
328                                 local multiplier = (self.radius*5) - distance
329                                 local velocity = vector.multiply(direction,multiplier)
330                                 
331                                 local velocity = vector.add(player_velocity,velocity)
332                                 
333                                 self.object:setvelocity(velocity)
334                                 
335                                 if distance < 0.2 then
336                                         self.object:remove()
337                                 end
338                                 
339                                 
340                                 --self.delete_timer = self.delete_timer + dtime
341                                 --this is where the item gets removed from world
342                                 --if self.delete_timer > 1 then
343                                 --      self.object:remove()
344                                 --end
345                                 return
346                         else
347                                 print(self.collector.." does not exist")
348                                 self.object:remove()
349                         end
350                 end
351                 
352                 --allow entity to be collected after timer
353                 if self.collectable == false and self.collection_timer >= self.collection_timer_goal then
354                         self.collectable = true
355                 elseif self.collectable == false then
356                         self.collection_timer = self.collection_timer + dtime
357                 end
358                                 
359                 self.age = self.age + dtime
360                 if self.time_to_live > 0 and self.age > self.time_to_live then
361                         self.itemstring = ""
362                         self.object:remove()
363                         return
364                 end
365
366                 local pos = self.object:get_pos()
367                 local node = minetest.get_node_or_nil({
368                         x = pos.x,
369                         y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
370                         z = pos.z
371                 })
372                 
373
374                 -- Remove nodes in 'ignore'
375                 if node and node.name == "ignore" then
376                         self.itemstring = ""
377                         self.object:remove()
378                         return
379                 end
380
381                 --burn inside fire nodes
382                 local node_inside = minetest.get_node_or_nil(pos)
383                 if node_inside and (node_inside.name == "fire:fire" or node_inside.name == "nether:lava" or node_inside.name == "nether:lavaflow" or node_inside.name == "main:lava" or node_inside.name == "main:lavaflow") then
384                         minetest.add_particlespawner({
385                                 amount = 6,
386                                 time = 0.001,
387                                 minpos = pos,
388                                 maxpos = pos,
389                                 minvel = vector.new(-1,0.5,-1),
390                                 maxvel = vector.new(1,1,1),
391                                 minacc = {x=0, y=1, z=0},
392                                 maxacc = {x=0, y=2, z=0},
393                                 minexptime = 1.1,
394                                 maxexptime = 1.5,
395                                 minsize = 1,
396                                 maxsize = 2,
397                                 collisiondetection = false,
398                                 vertical = false,
399                                 texture = "smoke.png",
400                         })
401                         minetest.sound_play("fire_extinguish", {pos=pos,gain=0.3,pitch=math.random(80,100)/100})
402                         self.itemstring = ""
403                         self.object:remove()
404                         return
405                 end
406
407
408                 local is_stuck = false
409                 local snode = minetest.get_node_or_nil(pos)
410                 if snode then
411                         local sdef = minetest.registered_nodes[snode.name] or {}
412                         is_stuck = (sdef.walkable == nil or sdef.walkable == true)
413                                 and (sdef.collision_box == nil or sdef.collision_box.type == "regular")
414                                 and (sdef.node_box == nil or sdef.node_box.type == "regular")
415                 end
416
417                 -- Push item out when stuck inside solid node
418                 if is_stuck then
419                         local shootdir
420                         local order = {
421                                 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
422                                 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
423                         }
424
425                         -- Check which one of the 4 sides is free
426                         for o = 1, #order do
427                                 local cnode = minetest.get_node(vector.add(pos, order[o])).name
428                                 local cdef = minetest.registered_nodes[cnode] or {}
429                                 if cnode ~= "ignore" and cdef.walkable == false then
430                                         shootdir = order[o]
431                                         break
432                                 end
433                         end
434                         -- If none of the 4 sides is free, check upwards
435                         if not shootdir then
436                                 shootdir = {x=0, y=1, z=0}
437                                 local cnode = minetest.get_node(vector.add(pos, shootdir)).name
438                                 if cnode == "ignore" then
439                                         shootdir = nil -- Do not push into ignore
440                                 end
441                         end
442
443                         if shootdir then
444                                 -- Set new item moving speed accordingly
445                                 local newv = vector.multiply(shootdir, 3)
446                                 self:disable_physics()
447                                 self.object:set_velocity(newv)
448
449                                 self.force_out = newv
450                                 self.force_out_start = vector.round(pos)
451                                 return
452                         end
453                 elseif self.force_out then
454                         -- This code runs after the entity got a push from the above code.
455                         -- It makes sure the entity is entirely outside the solid node
456                         local c = self.object:get_properties().collisionbox
457                         local s = self.force_out_start
458                         local f = self.force_out
459                         local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
460                                 (f.y > 0 and pos.y + c[2] > s.y + 0.5) or
461                                 (f.z > 0 and pos.z + c[3] > s.z + 0.5) or
462                                 (f.x < 0 and pos.x + c[4] < s.x - 0.5) or
463                                 (f.z < 0 and pos.z + c[6] < s.z - 0.5)
464                         if ok then
465                                 -- Item was successfully forced out
466                                 self.force_out = nil
467                                 self:enable_physics()
468                         end
469                 end
470
471                 if not self.physical_state then
472                         return -- Don't do anything
473                 end
474
475                 -- Slide on slippery nodes
476                 local vel = self.object:get_velocity()
477                 local def = node and minetest.registered_nodes[node.name]
478                 local is_moving = (def and not def.walkable) or
479                         vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
480                 local is_slippery = false
481
482                 if def and def.walkable then
483                         local slippery = minetest.get_item_group(node.name, "slippery")
484                         is_slippery = slippery ~= 0
485                         if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
486                                 -- Horizontal deceleration
487                                 local slip_factor = 4.0 / (slippery + 4)
488                                 self.object:set_acceleration({
489                                         x = -vel.x * slip_factor,
490                                         y = 0,
491                                         z = -vel.z * slip_factor
492                                 })
493                         elseif vel.y == 0 then
494                                 is_moving = false
495                                 --[[
496                                 local collisionbox = self.object:get_properties().collisionbox
497                                 local move_y = collisionbox[2]
498                                 if self.move_up == nil then
499                                         self.move_up = true
500                                 end
501                                 local addition = 0
502                                 if self.move_up == true then
503                                         move_y = move_y + (dtime/10)
504                                         addition = (dtime/8)
505                                         if move_y > -0.21 then
506                                                 self.move_up = false
507                                         end
508                                 elseif self.move_up == false then
509                                         move_y = move_y - (dtime/10)
510                                         if move_y < -0.5 then
511                                                 self.move_up = true
512                                         end
513                                 end
514                                 collisionbox[2] = move_y
515                                 self.object:set_properties({collisionbox=collisionbox,physical = true})
516                                 ]]--
517                         end
518                 end
519
520                 if self.moving_state == is_moving and self.slippery_state == is_slippery then
521                         -- Do not update anything until the moving state changes
522                         return
523                 end
524
525                 self.moving_state = is_moving
526                 self.slippery_state = is_slippery
527                 
528                 if is_moving then
529                         self.object:set_acceleration({x = 0, y = -gravity, z = 0})
530                 else
531                         self.object:set_acceleration({x = 0, y = 0, z = 0})
532                         self.object:set_velocity({x = 0, y = 0, z = 0})
533                 end
534         end,
535 })