]> git.lizzy.rs Git - Crafter.git/blob - mods/itemhandling/init.lua
4fa5bc9799e694a95020d1b387a9a1dcef0871e4
[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 --handle node drops
11 function minetest.handle_node_drops(pos, drops, digger)
12         for _,item in ipairs(drops) do
13                 local count, name
14                 if type(item) == "string" then
15                         count = 1
16                         name = item
17                 else
18                         count = item:get_count()
19                         name = item:get_name()
20                 end
21                 for i=1,count do
22                         local obj = minetest.add_item(pos, name)
23                         if obj ~= nil then
24                                 local x=math.random(-2,2)*math.random()
25                                 local y=math.random(2,5)
26                                 local z=math.random(-2,2)*math.random()
27                                 obj:setvelocity({x=x, y=y, z=z})
28                         end
29                 end
30         end
31 end
32     
33
34 -- Minetest: builtin/item_entity.lua
35
36 function minetest.spawn_item(pos, item)
37         -- Take item in any format
38         local stack = ItemStack(item)
39         local obj = minetest.add_entity(pos, "__builtin:item")
40         -- Don't use obj if it couldn't be added to the map.
41         if obj then
42                 obj:get_luaentity():set_item(stack:to_string())
43         end
44         return obj
45 end
46
47 function minetest.throw_item(pos, item)
48         -- Take item in any format
49         local stack = ItemStack(item)
50         local obj = minetest.add_entity(pos, "__builtin:item")
51         -- Don't use obj if it couldn't be added to the map.
52         if obj then
53                 obj:get_luaentity():set_item(stack:to_string())
54                 local x=math.random(-2,2)*math.random()
55                 local y=math.random(2,5)
56                 local z=math.random(-2,2)*math.random()
57                 obj:setvelocity({x=x, y=y, z=z})
58         end
59         return obj
60 end
61
62 --override drops
63 function minetest.item_drop(itemstack, dropper, pos)
64         local dropper_is_player = dropper and dropper:is_player()
65         local p = table.copy(pos)
66         local cnt = itemstack:get_count()
67         if dropper_is_player then
68                 p.y = p.y + 1.2
69         end
70         local item = itemstack:take_item(cnt)
71         local obj = minetest.add_item(p, item)
72         if obj then
73                 if dropper_is_player then
74                         local dir = dropper:get_look_dir()
75                         dir.x = dir.x * 2.9
76                         dir.y = dir.y * 2.9 + 2
77                         dir.z = dir.z * 2.9
78                         obj:set_velocity(dir)
79                         obj:get_luaentity().dropped_by = dropper:get_player_name()
80                         obj:get_luaentity().collection_timer = 0
81                 end
82                 return itemstack
83         end
84         -- If we reach this, adding the object to the
85         -- environment failed
86 end
87
88 -- If item_entity_ttl is not set, enity will have default life time
89 -- Setting it to -1 disables the feature
90
91 local time_to_live = tonumber(minetest.settings:get("item_entity_ttl")) or 900
92 local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81
93
94
95 minetest.register_entity(":__builtin:item", {
96         initial_properties = {
97                 hp_max = 1,
98                 physical = true,
99                 collide_with_objects = false,
100                 collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
101                 visual = "wielditem",
102                 visual_size = {x = 0.4, y = 0.4},
103                 textures = {""},
104                 spritediv = {x = 1, y = 1},
105                 initial_sprite_basepos = {x = 0, y = 0},
106                 is_visible = false,
107                 pointable = false,
108         },
109
110         itemstring = "",
111         moving_state = true,
112         slippery_state = false,
113         physical_state = true,
114         -- Item expiry
115         age = 0,
116         -- Pushing item out of solid nodes
117         force_out = nil,
118         force_out_start = nil,
119         --Collection Variables
120         collection_timer = 2,
121         collection_timer_goal = collection.collection_time,
122         collection_height = collection.collection_height,
123         collectable = false,
124         try_timer = 0,
125         collected = false,
126         delete_timer = 0,
127         radius = collection.magnet_radius,
128
129         set_item = function(self, item)
130                 local stack = ItemStack(item or self.itemstring)
131                 self.itemstring = stack:to_string()
132                 if self.itemstring == "" then
133                         -- item not yet known
134                         return
135                 end
136
137                 -- Backwards compatibility: old clients use the texture
138                 -- to get the type of the item
139                 local itemname = stack:is_known() and stack:get_name() or "unknown"
140
141                 local max_count = stack:get_stack_max()
142                 local count = math.min(stack:get_count(), max_count)
143
144                 local size = 0.21
145                 local coll_height = size * 0.75
146                 local def = minetest.registered_nodes[itemname]
147                 local glow = def and def.light_source
148
149                 self.object:set_properties({
150                         is_visible = true,
151                         visual = "wielditem",
152                         textures = {itemname},
153                         visual_size = {x = size, y = size},
154                         collisionbox = {-size, -0.21, -size,
155                                 size, coll_height, size},
156                         selectionbox = {-size, -size, -size, size, size, size},
157                         automatic_rotate = math.pi * 0.5 * 0.2 / size,
158                         wield_item = self.itemstring,
159                         glow = glow,
160                 })
161
162         end,
163
164         get_staticdata = function(self)
165                 return minetest.serialize({
166                         itemstring = self.itemstring,
167                         age = self.age,
168                         dropped_by = self.dropped_by,
169                         collection_timer = self.collection_timer,
170                         collectable = self.collectable,
171                         try_timer = self.try_timer,
172                         collected = self.collected,
173                         delete_timer = self.delete_timer,
174                         collector = self.collector,
175                 })
176         end,
177
178         on_activate = function(self, staticdata, dtime_s)
179                 if string.sub(staticdata, 1, string.len("return")) == "return" then
180                         local data = minetest.deserialize(staticdata)
181                         if data and type(data) == "table" then
182                                 self.itemstring = data.itemstring
183                                 self.age = (data.age or 0) + dtime_s
184                                 self.dropped_by = data.dropped_by
185                                 
186                                 self.collection_timer = data.collection_timer
187                                 self.collectable = data.collectable
188                                 self.try_timer = data.try_timer
189                                 self.collected = data.collected
190                                 self.delete_timer = data.delete_timer
191                                 self.collector = data.collector
192                                 --print("restored timer: "..self.collection_timer)
193                         end
194                 else
195                         self.itemstring = staticdata
196                         
197                         local x=math.random(-2,2)*math.random()
198                         local y=math.random(2,5)
199                         local z=math.random(-2,2)*math.random()
200                         self.object:setvelocity(vector.new(x,y,z))
201                      -- print(self.collection_timer)
202                 end
203                 self.object:set_armor_groups({immortal = 1})
204                 self.object:set_velocity({x = 0, y = 2, z = 0})
205                 self.object:set_acceleration({x = 0, y = -gravity, z = 0})
206                 self:set_item()
207         end,
208
209         enable_physics = function(self)
210                 if not self.physical_state then
211                         self.physical_state = true
212                         self.object:set_properties({physical = true})
213                         self.object:set_velocity({x=0, y=0, z=0})
214                         self.object:set_acceleration({x=0, y=-gravity, z=0})
215                 end
216         end,
217
218         disable_physics = function(self)
219                 if self.physical_state then
220                         self.physical_state = false
221                         self.object:set_properties({physical = false})
222                         self.object:set_velocity({x=0, y=0, z=0})
223                         self.object:set_acceleration({x=0, y=0, z=0})
224                 end
225         end,
226         on_step = function(self, dtime)
227                 --if item set to be collected then only execute go to player
228                 if self.collected == true then
229                         if not self.collector then
230                                 self.collected = false
231                                 return
232                         end
233                         local collector = minetest.get_player_by_name(self.collector)
234                         if collector then
235                                 self.object:setacceleration(vector.new(0,0,0))
236                                 self.disable_physics(self)
237                                 --get the variables
238                                 local pos = self.object:getpos()
239                                 local pos2 = collector:getpos()
240                                 local player_velocity = collector:get_player_velocity()
241                                 pos2.y = pos2.y + self.collection_height
242                                                                 
243                                 local direction = vector.normalize(vector.subtract(pos2,pos))
244                                 local distance = vector.distance(pos2,pos)
245                                                                 
246                                 
247                                 --remove if too far away
248                                 if distance > self.radius then
249                                         distance = 0
250                                 end
251                                                                 
252                                 local multiplier = (self.radius*5) - distance
253                                 local velocity = vector.multiply(direction,multiplier)
254                                 
255                                 local velocity = vector.add(player_velocity,velocity)
256                                 
257                                 self.object:setvelocity(velocity)
258                                 
259                                 if distance < 0.2 then
260                                         self.object:remove()
261                                 end
262                                 
263                                 
264                                 --self.delete_timer = self.delete_timer + dtime
265                                 --this is where the item gets removed from world
266                                 --if self.delete_timer > 1 then
267                                 --      self.object:remove()
268                                 --end
269                                 return
270                         else
271                                 print(self.collector.." does not exist")
272                                 self.object:remove()
273                         end
274                 end
275                 
276                 --allow entity to be collected after timer
277                 if self.collectable == false and self.collection_timer >= self.collection_timer_goal then
278                         self.collectable = true
279                 elseif self.collectable == false then
280                         self.collection_timer = self.collection_timer + dtime
281                 end
282                                 
283                 self.age = self.age + dtime
284                 if time_to_live > 0 and self.age > time_to_live then
285                         self.itemstring = ""
286                         self.object:remove()
287                         return
288                 end
289
290                 local pos = self.object:get_pos()
291                 local node = minetest.get_node_or_nil({
292                         x = pos.x,
293                         y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
294                         z = pos.z
295                 })
296                 
297                 -- Delete in 'ignore' nodes
298                 if node and node.name == "ignore" then
299                         self.itemstring = ""
300                         self.object:remove()
301                         return
302                 end
303
304                 local is_stuck = false
305                 local snode = minetest.get_node_or_nil(pos)
306                 if snode then
307                         local sdef = minetest.registered_nodes[snode.name] or {}
308                         is_stuck = (sdef.walkable == nil or sdef.walkable == true)
309                                 and (sdef.collision_box == nil or sdef.collision_box.type == "regular")
310                                 and (sdef.node_box == nil or sdef.node_box.type == "regular")
311                 end
312
313                 -- Push item out when stuck inside solid node
314                 if is_stuck then
315                         local shootdir
316                         local order = {
317                                 {x=1, y=0, z=0}, {x=-1, y=0, z= 0},
318                                 {x=0, y=0, z=1}, {x= 0, y=0, z=-1},
319                         }
320
321                         -- Check which one of the 4 sides is free
322                         for o = 1, #order do
323                                 local cnode = minetest.get_node(vector.add(pos, order[o])).name
324                                 local cdef = minetest.registered_nodes[cnode] or {}
325                                 if cnode ~= "ignore" and cdef.walkable == false then
326                                         shootdir = order[o]
327                                         break
328                                 end
329                         end
330                         -- If none of the 4 sides is free, check upwards
331                         if not shootdir then
332                                 shootdir = {x=0, y=1, z=0}
333                                 local cnode = minetest.get_node(vector.add(pos, shootdir)).name
334                                 if cnode == "ignore" then
335                                         shootdir = nil -- Do not push into ignore
336                                 end
337                         end
338
339                         if shootdir then
340                                 -- Set new item moving speed accordingly
341                                 local newv = vector.multiply(shootdir, 3)
342                                 self:disable_physics()
343                                 self.object:set_velocity(newv)
344
345                                 self.force_out = newv
346                                 self.force_out_start = vector.round(pos)
347                                 return
348                         end
349                 elseif self.force_out then
350                         -- This code runs after the entity got a push from the above code.
351                         -- It makes sure the entity is entirely outside the solid node
352                         local c = self.object:get_properties().collisionbox
353                         local s = self.force_out_start
354                         local f = self.force_out
355                         local ok = (f.x > 0 and pos.x + c[1] > s.x + 0.5) or
356                                 (f.y > 0 and pos.y + c[2] > s.y + 0.5) or
357                                 (f.z > 0 and pos.z + c[3] > s.z + 0.5) or
358                                 (f.x < 0 and pos.x + c[4] < s.x - 0.5) or
359                                 (f.z < 0 and pos.z + c[6] < s.z - 0.5)
360                         if ok then
361                                 -- Item was successfully forced out
362                                 self.force_out = nil
363                                 self:enable_physics()
364                         end
365                 end
366
367                 if not self.physical_state then
368                         return -- Don't do anything
369                 end
370
371                 -- Slide on slippery nodes
372                 local vel = self.object:get_velocity()
373                 local def = node and minetest.registered_nodes[node.name]
374                 local is_moving = (def and not def.walkable) or
375                         vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
376                 local is_slippery = false
377
378                 if def and def.walkable then
379                         local slippery = minetest.get_item_group(node.name, "slippery")
380                         is_slippery = slippery ~= 0
381                         if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
382                                 -- Horizontal deceleration
383                                 local slip_factor = 4.0 / (slippery + 4)
384                                 self.object:set_acceleration({
385                                         x = -vel.x * slip_factor,
386                                         y = 0,
387                                         z = -vel.z * slip_factor
388                                 })
389                         elseif vel.y == 0 then
390                                 is_moving = false
391                                 --[[
392                                 local collisionbox = self.object:get_properties().collisionbox
393                                 local move_y = collisionbox[2]
394                                 if self.move_up == nil then
395                                         self.move_up = true
396                                 end
397                                 local addition = 0
398                                 if self.move_up == true then
399                                         move_y = move_y + (dtime/10)
400                                         addition = (dtime/8)
401                                         if move_y > -0.21 then
402                                                 self.move_up = false
403                                         end
404                                 elseif self.move_up == false then
405                                         move_y = move_y - (dtime/10)
406                                         if move_y < -0.5 then
407                                                 self.move_up = true
408                                         end
409                                 end
410                                 collisionbox[2] = move_y
411                                 self.object:set_properties({collisionbox=collisionbox,physical = true})
412                                 ]]--
413                         end
414                 end
415
416                 if self.moving_state == is_moving and self.slippery_state == is_slippery then
417                         -- Do not update anything until the moving state changes
418                         return
419                 end
420
421                 self.moving_state = is_moving
422                 self.slippery_state = is_slippery
423                 
424                 if is_moving then
425                         self.object:set_acceleration({x = 0, y = -gravity, z = 0})
426                 else
427                         self.object:set_acceleration({x = 0, y = 0, z = 0})
428                         self.object:set_velocity({x = 0, y = 0, z = 0})
429                 end
430         end,
431 })