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