-
Notifications
You must be signed in to change notification settings - Fork 12
/
init.lua
369 lines (342 loc) · 10.2 KB
/
init.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
local load_time_start = minetest.get_us_time()
-- Functions which can be overridden by mods
assert(item_drop == nil)
item_drop = {
-- This function is executed before picking up an item or making it fly to
-- the player. If it does not return true, the item is ignored.
-- It is also executed before collecting the item after it flew to
-- the player and did not reach him/her for magnet_time seconds.
can_pickup = function(entity, player)
if entity.item_drop_picked then
-- Ignore items where picking has already failed
return false
end
return true
end,
-- before_collect and after_collect are executed before and after an item
-- is collected by a player
before_collect = function(entity, pos, player)
end,
after_collect = function(entity, pos, player)
entity.item_drop_picked = true
end,
}
if minetest.settings:get_bool("item_drop.enable_item_pickup") ~= false and
minetest.settings:get_bool("enable_item_pickup") ~= false then
local pickup_gain = tonumber(
minetest.settings:get("item_drop.pickup_sound_gain")) or
tonumber(minetest.settings:get("item_pickup_gain")) or 0.2
local pickup_particle =
minetest.settings:get_bool("item_drop.pickup_particle") ~= false
local pickup_radius = tonumber(
minetest.settings:get("item_drop.pickup_radius")) or
tonumber(minetest.settings:get("item_pickup_radius")) or 0.75
local magnet_radius = tonumber(
minetest.settings:get("item_drop.magnet_radius")) or -1
local magnet_time = tonumber(
minetest.settings:get("item_drop.magnet_time")) or 5.0
local pickup_age = tonumber(
minetest.settings:get("item_drop.pickup_age")) or 0.5
local key_triggered = minetest.settings:get_bool(
"item_drop.enable_pickup_key") or
minetest.settings:get_bool("enable_item_pickup_key") ~= false
local key_invert = minetest.settings:get_bool(
"item_drop.pickup_keyinvert") or false
local keytype
if key_triggered then
keytype = minetest.settings:get("item_drop.pickup_keytype") or
minetest.settings:get("item_pickup_keytype") or "Use"
-- disable pickup age if picking is explicitly enabled by the player
if not key_invert then
pickup_age = math.min(pickup_age, 0)
end
end
local mouse_pickup = minetest.settings:get_bool(
"item_drop.mouse_pickup") ~= false
if not mouse_pickup then
minetest.registered_entities["__builtin:item"].pointable = false
end
local magnet_mode = magnet_radius > pickup_radius
local zero_velocity_mode = pickup_age == -1
if magnet_mode
and zero_velocity_mode then
error"zero velocity mode can't be used together with magnet mode"
end
-- tells whether an inventorycube should be shown as pickup_particle or not
-- for known drawtypes
local inventorycube_drawtypes = {
normal = true,
allfaces = true,
allfaces_optional = true,
glasslike = true,
glasslike_framed = true,
glasslike_framed_optional = true,
liquid = true,
flowingliquid = true,
}
-- adds the item to the inventory and removes the object
local function collect_item(ent, pos, player)
item_drop.before_collect(ent, pos, player)
minetest.sound_play("item_drop_pickup", {
pos = pos,
gain = pickup_gain,
})
if pickup_particle then
local item = minetest.registered_nodes[
ent.itemstring:gsub("(.*)%s.*$", "%1")]
local image = ""
if item and item.tiles and item.tiles[1] then
if inventorycube_drawtypes[item.drawtype] then
local tiles = item.tiles
local top = tiles[1]
if type(top) == "table" then
top = top.name
end
local left = tiles[3] or top
if type(left) == "table" then
left = left.name
end
local right = tiles[5] or left
if type(right) == "table" then
right = right.name
end
image = minetest.inventorycube(top, left, right)
else
image = item.inventory_image or item.tiles[1]
end
minetest.add_particle({
pos = {x = pos.x, y = pos.y + 1.5, z = pos.z},
velocity = {x = 0, y = 1, z = 0},
acceleration = {x = 0, y = -4, z = 0},
expirationtime = 0.2,
size = 3,--math.random() + 0.5,
vertical = false,
texture = image,
})
end
end
ent:on_punch(player)
item_drop.after_collect(ent, pos, player)
end
-- opt_get_ent gets the object's luaentity if it can be collected
local opt_get_ent
if zero_velocity_mode then
function opt_get_ent(object)
if object:is_player()
or not vector.equals(object:getvelocity(), {x=0, y=0, z=0}) then
return
end
local ent = object:get_luaentity()
if not ent
or ent.name ~= "__builtin:item"
or ent.itemstring == "" then
return
end
return ent
end
else
function opt_get_ent(object)
if object:is_player() then
return
end
local ent = object:get_luaentity()
if not ent
or ent.name ~= "__builtin:item"
or (ent.dropped_by and ent.age < pickup_age)
or ent.itemstring == "" then
return
end
return ent
end
end
local afterflight
if magnet_mode then
-- take item or reset velocity after flying a second
function afterflight(object, inv, player)
-- TODO: test what happens if player left the game
local ent = opt_get_ent(object)
if not ent then
return
end
local item = ItemStack(ent.itemstring)
if inv
and inv:room_for_item("main", item)
and can_pickup(ent, player) then
collect_item(ent, object:get_pos(), player)
else
-- the acceleration will be reset by the object's on_step
object:set_velocity({x=0,y=0,z=0})
ent.is_magnet_item = false
end
end
-- disable velocity and acceleration changes of items flying to players
minetest.after(0, function()
local ObjectRef
local blocked_methods = {"set_acceleration", "set_velocity",
"setacceleration", "setvelocity"}
local itemdef = minetest.registered_entities["__builtin:item"]
local old_on_step = itemdef.on_step
local function do_nothing() end
function itemdef.on_step(self, dtime)
if not self.is_magnet_item then
return old_on_step(self, dtime)
end
ObjectRef = ObjectRef or getmetatable(self.object)
local old_funcs = {}
for i = 1, #blocked_methods do
local method = blocked_methods[i]
old_funcs[method] = ObjectRef[method]
ObjectRef[method] = do_nothing
end
old_on_step(self, dtime)
for i = 1, #blocked_methods do
local method = blocked_methods[i]
ObjectRef[method] = old_funcs[method]
end
end
end)
end
-- set keytype to the key name if possible
if keytype == "Use" then
keytype = "aux1"
elseif keytype == "Sneak" then
keytype = "sneak"
elseif keytype == "LeftAndRight" then -- LeftAndRight combination
keytype = 0
elseif keytype == "SneakAndRMB" then -- SneakAndRMB combination
keytype = 1
end
-- tests if the player has the keys pressed to enable item picking
local function has_keys_pressed(player)
if not key_triggered then
return true
end
local control = player:get_player_control()
local keys_pressed
if keytype == 0 then -- LeftAndRight combination
keys_pressed = control.left and control.right
elseif keytype == 1 then -- SneakAndRMB combination
keys_pressed = control.sneak and control.RMB
else
keys_pressed = control[keytype]
end
return keys_pressed ~= key_invert
end
-- called for each player to possibly collect an item, returns true if so
local function pickupfunc(player)
if not has_keys_pressed(player)
or not minetest.get_player_privs(player:get_player_name()).interact
or player:get_hp() <= 0 then
return
end
local pos = player:getpos()
pos.y = pos.y+0.5
local inv
local objectlist = minetest.get_objects_inside_radius(pos,
magnet_mode and magnet_radius or pickup_radius)
for i = 1,#objectlist do
local object = objectlist[i]
local ent = opt_get_ent(object)
if ent
and item_drop.can_pickup(ent, player) then
if not inv then
inv = player:get_inventory()
if not inv then
minetest.log("error", "[item_drop] Couldn't " ..
"get inventory")
return
end
end
local item = ItemStack(ent.itemstring)
if inv:room_for_item("main", item) then
local flying_item
local pos2
if magnet_mode then
pos2 = object:getpos()
flying_item = vector.distance(pos, pos2) > pickup_radius
end
if not flying_item then
-- The item is near enough to pick it
collect_item(ent, pos, player)
-- Collect one item at a time to avoid the loud pop
return true
end
-- The item is not too far a way but near enough to be
-- magnetised, make it fly to the player
local vel = vector.multiply(vector.subtract(pos, pos2), 3)
vel.y = vel.y + 0.6
object:set_velocity(vel)
if not ent.is_magnet_item then
ent.object:set_acceleration({x=0, y=0, z=0})
ent.is_magnet_item = true
minetest.after(magnet_time, afterflight,
object, inv, player)
end
end
end
end
end
local function pickup_step()
local got_item
local players = minetest.get_connected_players()
for i = 1,#players do
got_item = got_item or pickupfunc(players[i])
end
-- lower step if takeable item(s) were found
local time
if got_item then
time = 0.02
else
time = 0.2
end
minetest.after(time, pickup_step)
end
minetest.after(3.0, pickup_step)
end
if minetest.settings:get_bool("item_drop.enable_item_drop") ~= false and
minetest.settings:get_bool("enable_item_drop") ~= false
and not minetest.settings:get_bool("creative_mode") then
function minetest.handle_node_drops(pos, drops)
for i = 1,#drops do
local item = drops[i]
local count, name
if type(item) == "string" then
count = 1
name = item
else
count = item:get_count()
name = item:get_name()
end
-- Sometimes nothing should be dropped
if name == ""
or not minetest.registered_nodes[name] then
count = 0
end
for _ = 1,count do
local obj = minetest.add_item(pos, name)
if not obj then
error("Couldn't spawn item " .. name .. ", drops: " .. dump(drops))
end
local vel = obj:getvelocity()
local x = math.random(-5, 4)
if x >= 0 then
x = x+1
end
vel.x = 1 / x
local z = math.random(-5, 4)
if z >= 0 then
z = z+1
end
vel.z = 1 / z
obj:set_velocity(vel)
end
end
end
end
local time = (minetest.get_us_time() - load_time_start) / 1000000
local msg = "[item_drop] loaded after ca. " .. time .. " seconds."
if time > 0.01 then
print(msg)
else
minetest.log("info", msg)
end