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