Allow for shift+clicking inventory from bones
[oweals/minetest_game.git] / mods / bones / init.lua
1 -- Minetest 0.4 mod: bones
2 -- See README.txt for licensing and other information. 
3
4 bones = {}
5
6 local function is_owner(pos, name)
7         local owner = minetest.get_meta(pos):get_string("owner")
8         if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
9                 return true
10         end
11         return false
12 end
13
14 bones.bones_formspec =
15         "size[8,9]" ..
16         default.gui_bg ..
17         default.gui_bg_img ..
18         default.gui_slots ..
19         "list[current_name;main;0,0.3;8,4;]" ..
20         "list[current_player;main;0,4.85;8,1;]" ..
21         "list[current_player;main;0,6.08;8,3;8]" ..
22         "listring[current_name;main]" ..
23         "listring[current_player;main]" ..
24         default.get_hotbar_bg(0,4.85)
25
26 local share_bones_time = tonumber(minetest.setting_get("share_bones_time")) or 1200
27 local share_bones_time_early = tonumber(minetest.setting_get("share_bones_time_early")) or share_bones_time / 4
28
29 minetest.register_node("bones:bones", {
30         description = "Bones",
31         tiles = {
32                 "bones_top.png",
33                 "bones_bottom.png",
34                 "bones_side.png",
35                 "bones_side.png",
36                 "bones_rear.png",
37                 "bones_front.png"
38         },
39         paramtype2 = "facedir",
40         groups = {dig_immediate=2},
41         sounds = default.node_sound_dirt_defaults({
42                 footstep = {name="default_gravel_footstep", gain=0.5},
43                 dug = {name="default_gravel_footstep", gain=1.0},
44         }),
45         
46         can_dig = function(pos, player)
47                 local inv = minetest.get_meta(pos):get_inventory()
48                 local name = ""
49                 if player then
50                         name = player:get_player_name()
51                 end
52                 return is_owner(pos, name) and inv:is_empty("main")
53         end,
54         
55         allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
56                 if is_owner(pos, player:get_player_name()) then
57                         return count
58                 end
59                 return 0
60         end,
61         
62         allow_metadata_inventory_put = function(pos, listname, index, stack, player)
63                 return 0
64         end,
65         
66         allow_metadata_inventory_take = function(pos, listname, index, stack, player)
67                 if is_owner(pos, player:get_player_name()) then
68                         return stack:get_count()
69                 end
70                 return 0
71         end,
72         
73         on_metadata_inventory_take = function(pos, listname, index, stack, player)
74                 local meta = minetest.get_meta(pos)
75                 if meta:get_inventory():is_empty("main") then
76                         minetest.remove_node(pos)
77                 end
78         end,
79         
80         on_punch = function(pos, node, player)
81                 if(not is_owner(pos, player:get_player_name())) then
82                         return
83                 end
84                 
85                 if(minetest.get_meta(pos):get_string("infotext") == "") then
86                         return
87                 end
88                 
89                 local inv = minetest.get_meta(pos):get_inventory()
90                 local player_inv = player:get_inventory()
91                 local has_space = true
92                 
93                 for i=1,inv:get_size("main") do
94                         local stk = inv:get_stack("main", i)
95                         if player_inv:room_for_item("main", stk) then
96                                 inv:set_stack("main", i, nil)
97                                 player_inv:add_item("main", stk)
98                         else
99                                 has_space = false
100                                 break
101                         end
102                 end
103                 
104                 -- remove bones if player emptied them
105                 if has_space then
106                         if player_inv:room_for_item("main", {name = "bones:bones"}) then
107                                 player_inv:add_item("main", {name = "bones:bones"})
108                         else
109                                 minetest.add_item(pos,"bones:bones")
110                         end
111                         minetest.remove_node(pos)
112                 end
113         end,
114         
115         on_timer = function(pos, elapsed)
116                 local meta = minetest.get_meta(pos)
117                 local time = meta:get_int("time") + elapsed
118                 if time >= share_bones_time then
119                         meta:set_string("infotext", meta:get_string("owner").."'s old bones")
120                         meta:set_string("owner", "")
121                 else
122                         meta:set_int("time", time)
123                         return true
124                 end
125         end,
126 })
127
128 local function may_replace(pos, player)
129         local node_name = minetest.get_node(pos).name
130         local node_definition = minetest.registered_nodes[node_name]
131
132         -- if the node is unknown, we let the protection mod decide
133         -- this is consistent with when a player could dig or not dig it
134         -- unknown decoration would often be removed
135         -- while unknown building materials in use would usually be left
136         if not node_definition then
137                 -- only replace nodes that are not protected
138                 return not minetest.is_protected(pos, player:get_player_name())
139         end
140
141         -- allow replacing air and liquids
142         if node_name == "air" or node_definition.liquidtype ~= "none" then
143                 return true
144         end
145
146         -- don't replace filled chests and other nodes that don't allow it
147         local can_dig_func = node_definition.can_dig
148         if can_dig_func and not can_dig_func(pos, player) then
149                 return false
150         end
151
152         -- default to each nodes buildable_to; if a placed block would replace it, why shouldn't bones?
153         -- flowers being squished by bones are more realistical than a squished stone, too
154         -- exception are of course any protected buildable_to
155         return node_definition.buildable_to and not minetest.is_protected(pos, player:get_player_name())
156 end
157
158 minetest.register_on_dieplayer(function(player)
159         if minetest.setting_getbool("creative_mode") then
160                 return
161         end
162         
163         local player_inv = player:get_inventory()
164         if player_inv:is_empty("main") and
165                 player_inv:is_empty("craft") then
166                 return
167         end
168
169         local pos = player:getpos()
170         pos.x = math.floor(pos.x+0.5)
171         pos.y = math.floor(pos.y+0.5)
172         pos.z = math.floor(pos.z+0.5)
173         local param2 = minetest.dir_to_facedir(player:get_look_dir())
174         local player_name = player:get_player_name()
175         local player_inv = player:get_inventory()
176
177         if (not may_replace(pos, player)) then
178                 if (may_replace({x=pos.x, y=pos.y+1, z=pos.z}, player)) then
179                         -- drop one node above if there's space
180                         -- this should solve most cases of protection related deaths in which players dig straight down
181                         -- yet keeps the bones reachable
182                         pos.y = pos.y+1
183                 else
184                         -- drop items instead of delete
185                         for i=1,player_inv:get_size("main") do
186                                 minetest.add_item(pos, player_inv:get_stack("main", i))
187                         end
188                         for i=1,player_inv:get_size("craft") do
189                                 minetest.add_item(pos, player_inv:get_stack("craft", i))
190                         end
191                         -- empty lists main and craft
192                         player_inv:set_list("main", {})
193                         player_inv:set_list("craft", {})
194                         return
195                 end
196         end
197         
198         minetest.set_node(pos, {name="bones:bones", param2=param2})
199         
200         local meta = minetest.get_meta(pos)
201         local inv = meta:get_inventory()
202         inv:set_size("main", 8*4)
203         inv:set_list("main", player_inv:get_list("main"))
204         
205         for i=1,player_inv:get_size("craft") do
206                 local stack = player_inv:get_stack("craft", i)
207                 if inv:room_for_item("main", stack) then
208                         inv:add_item("main", stack)
209                 else
210                         --drop if no space left
211                         minetest.add_item(pos, stack)
212                 end
213         end
214         
215         player_inv:set_list("main", {})
216         player_inv:set_list("craft", {})
217         
218         meta:set_string("formspec", bones.bones_formspec)
219         meta:set_string("owner", player_name)
220         
221         if share_bones_time ~= 0 then
222                 meta:set_string("infotext", player_name.."'s fresh bones")
223
224                 if share_bones_time_early == 0 or not minetest.is_protected(pos, player_name) then
225                         meta:set_int("time", 0)
226                 else
227                         meta:set_int("time", (share_bones_time - share_bones_time_early))
228                 end
229
230                 minetest.get_node_timer(pos):start(10)
231         else
232                 meta:set_string("infotext", player_name.."'s bones")
233         end
234 end)