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
|
ch_core.open_submod("stavby", {chat = true, events = true, lib = true})
--[[
Datová struktura záznamu o stavbě:
{
key = string, -- pozice stavby v normalizovaném tvaru (klíč do tabulky staveb)
pos = {x = int y = int, z = int} -- pozice stavby ve formě vektoru celých čísel
-- ostatní pole mohou být přímo upravována:
nazev = string, -- název stavby (nemusí být jedinečný)
druh = string, -- druh stavby (doplňuje název)
zalozil_a = string, -- postava, která stavby založila (přihlašovací tvar)
spravuje = string, -- postava, která stavbu spravuje (přihlašovací tvar)
urceni = string, -- jeden z řetězců z ch_core.urceni_staveb
stav = string, -- jeden z řetězců z ch_core.stavy_staveb
historie = {
[1...] = string, ...
} -- postupná historie změn
zamer = string, -- záměr stavby (text)
heslo = string || nil, -- do budoucna: vygenerované heslo u staveb nabízených k převzetí; kdo heslo zadá, dostane stavbu do správy
mapa = {x1, z1, [w, h]}, -- volitelné pole; definuje místo zobrazení na mapě (buď jen bod, nebo zónu)
}
]]
minetest.register_privilege("ch_stavby_admin", {
description = "Umožňuje měnit všechny vlastnosti registrovaných staveb.",
give_to_singleplayer = true,
give_to_admin = true,
})
local worldpath = minetest.get_worldpath()
ch_core.urceni_staveb = {
soukroma = "soukromá",
verejna = "veřejná",
chranena_oblast = "chráněná oblast",
}
ch_core.stavy_staveb = {
k_povoleni = "čeká na stavební povolení",
rozestaveno = "rozestavěná",
k_schvaleni = "k schválení",
hotovo = "hotová",
rekonstrukce = "v rekonstrukci",
opusteno = "opuštěná",
k_smazani = "ke smazání",
}
ch_core.urceni_staveb_r = table.key_value_swap(ch_core.urceni_staveb)
ch_core.stavy_staveb_r = table.key_value_swap(ch_core.stavy_staveb)
local function pos_to_key(pos)
pos = vector.round(pos)
return string.format("%d,%d,%d", pos.x, pos.y, pos.z)
end
local function key_to_pos(s)
local parts = s:split(",")
local result
if #parts == 3 then
result = vector.round(vector.new(assert(tonumber(parts[1])), assert(tonumber(parts[2])), assert(tonumber(parts[3]))))
end
return result
end
local function verify_record(record)
if record == nil then
return "record is nil!"
end
local s
if type(record.key) ~= "string" then return "invalid key type: "..type(record.key) end
local pos = key_to_pos(record.key)
if pos == nil then return "invalid key (cannot unhash): "..record.key end
s = type(record.pos)
if s ~= "table" then return "invalid pos type: "..s end
if pos.x ~= record.pos.x or pos.y ~= record.pos.y or pos.z ~= record.pos.z then
return "invalid or inconsistent record.pos!"
end
s = type(record.nazev)
if s ~= "string" and s ~= "number" then return "invalid nazev type: "..s end
if record.nazev == "" then return "empty nazev!" end
s = type(record.zalozil_a)
if s ~= "string" and s ~= "number" then return "invalid zalozil_a type: "..s end
s = type(record.spravuje)
if s ~= "string" and s ~= "number" then return "invalid spravuje type: "..s end
s = record.urceni
if s == nil or ch_core.urceni_staveb[s or ""] == nil then
return "invalid urceni: "..(s or "nil")
end
s = record.stav
if s == nil or ch_core.stavy_staveb[s or ""] == nil then
return "invalid stav: "..(s or "nil")
end
s = type(record.historie)
if s ~= "table" then return "invalid historie type: "..s end
for i, v in ipairs(record.historie) do
if type(v) ~= "string" then return "invalid historie["..i.."] type: "..type(v) end
end
s = type(record.zamer)
if s ~= "string" and s ~= "number" then return "invalid zamer type: "..s end
return nil
end
local function load_data()
local f = io.open(worldpath.."/ch_stavby.json")
local result
if f then
result = f:read("*a")
f:close()
if result ~= nil then
result = assert(minetest.parse_json(result))
end
end
if result ~= nil then
local count = 0
for key, record in pairs(result) do
local error_message = verify_record(record)
if error_message == nil and key ~= record.key then
error_message = "key mismatch! table key = <"..key..">, record key = <"..(record.key or "nil")..">"
end
if error_message ~= nil then
minetest.log("warning", "[ch_core/stavby] Verification error for stavba <"..key..">: "..error_message)
end
count = count + 1
end
minetest.log("info", "[ch_core/stavby] "..count.." records loaded.")
return result
end
minetest.log("warning", "[ch_core/stavby] No data was loaded!")
return {}
end
local data = load_data()
function ch_core.stavby_add(pos, urceni, stav, nazev, druh, player_name, zamer)
local key = assert(pos_to_key(pos))
if data[key] ~= nil then
return nil -- a duplicity!
end
if nazev == nil or nazev == "" then nazev = "Bez názvu" end
if druh == nil then druh = "" end
assert(player_name ~= nil)
local cas = ch_time.aktualni_cas()
local new_record = {
key = key,
pos = assert(key_to_pos(key)),
urceni = urceni,
nazev = nazev,
druh = druh,
zalozil_a = player_name,
spravuje = player_name,
stav = stav,
historie = {
string.format("%s založeno (správa: %s, stav: %s)",
cas:YYYY_MM_DD(), ch_core.prihlasovaci_na_zobrazovaci(player_name), assert(ch_core.stavy_staveb[stav]))
},
zamer = zamer or "",
}
local record_error = verify_record(new_record)
if record_error ~= nil then
error("stavby_add() internal error: "..record_error)
end
data[key] = new_record
return new_record
end
function ch_core.stavby_get(key_or_pos)
if type(key_or_pos) ~= "string" then
key_or_pos = pos_to_key(key_or_pos)
end
return data[key_or_pos]
end
function ch_core.stavby_get_all(filter, sorter, extra_argument)
local result = {}
if filter == nil then
filter = function() return true end
end
for _, record in pairs(data) do
if filter(record, extra_argument) then
table.insert(result, record)
end
end
if sorter ~= nil then
table.sort(result, function(a, b) return sorter(a, b, extra_argument) end)
end
return result
end
function ch_core.stavby_move(key_from, pos)
local record = data[key_from]
local key_to = pos_to_key(pos)
if record == nil then return nil end
if data[key_to] ~= nil then return false end
record.key = key_to
record.pos = key_to_pos(key_to)
data[key_from] = nil
data[key_to] = record
return true
end
function ch_core.stavby_remove(key_or_pos)
if type(key_or_pos) ~= "string" then
key_or_pos = pos_to_key(key_or_pos)
end
if data[key_or_pos] == nil then
return false
end
data[key_or_pos] = nil
return true
end
local stavby_html_transtable = {
["-"] = "m",
[","] = "x",
}
function ch_core.stavby_save()
local json, error_message = minetest.write_json(data)
if json == nil then
error("ch_core.stavby_save(): serialization error: "..tostring(error_message or "nil"))
end
minetest.safe_file_write(worldpath.."/ch_stavby.json", json)
minetest.log("info", "ch_core/stavby: "..#data.." records saved ("..#json.." bytes).")
local html = {}
for key, stavba in pairs(data) do
local html_key = "st_"..key:gsub("[-,]", stavby_html_transtable)
local nazev = stavba.nazev
if stavba.druh ~= "" then
nazev = stavba.nazev.." ("..stavba.druh..")"
end
nazev = ch_core.formspec_hypertext_escape(nazev)
local mapa = stavba.mapa
table.insert(html, "<div class=\""..stavba.stav.." "..stavba.urceni)
if mapa ~= nil and #mapa == 4 then
table.insert(html, " zona")
end
table.insert(html, "\" id=\""..html_key.."\" style=\"")
if mapa == nil then
table.insert(html, string.format("left:%dpx;top:%dpx;", stavba.pos.x, -stavba.pos.z))
elseif #mapa == 2 then
table.insert(html, string.format("left:%dpx;top:%dpx;", mapa[1], -mapa[2]))
else
table.insert(html, string.format("left:%dpx;top:%dpx;width:%dpx;height:%dpx;", mapa[1], -(mapa[2] + mapa[4]), mapa[3], mapa[4]))
end
table.insert(html, "\" title=\"")
if stavba.urceni == "soukroma" then
table.insert(html, "<soukromá stavba> ")
elseif stavba.urceni == "chranena_oblast" then
table.insert(html, "<rezervovaná oblast> ")
end
table.insert(html, nazev.." spravuje: "..ch_core.prihlasovaci_na_zobrazovaci(stavba.spravuje)..
" stav: "..assert(ch_core.stavy_staveb[stavba.stav]).."\">"..nazev.."</div>\n")
end
minetest.safe_file_write(worldpath.."/ch_stavby.html", table.concat(html))
end
local function stavba_na_mape(admin_name, param)
local params = string.split(param, " ")
if #params ~= 1 and #params ~= 3 and #params ~= 5 then
return false, "Neplatný počet parametrů!"
end
local stavba = data[params[1]]
if stavba == nil then
return false, "Stavba s klíčem <"..params[1].."> nenalezena!"
end
if #params == 1 then
stavba.mapa = nil
else
local x1, z1 = tonumber(params[2]), tonumber(params[3])
if x1 == nil or z1 == nil then
return false, "Neplatné zadání!"
end
if #params == 3 then
stavba.mapa = {x1, z1}
elseif #params == 5 then
local x2, z2 = tonumber(params[4]), tonumber(params[5])
if x2 == nil or z2 == nil then
return false, "Neplatné zadání!"
end
if x2 < x1 then x1, x2 = x2, x1 end
if z2 < z1 then z1, z2 = z2, z1 end
stavba.mapa = {x1, z1, x2 - x1 + 1, z2 - z1 + 1}
end
end
ch_core.stavby_save()
return true, "Nastaveno."
end
local def = {
params = "<pozice,stavby> [<x1> <z1> [<x2> <z2>]]",
description = "Pro správce/yni staveb: nastaví zobrazení stavby na mapě nebo toto nastavení zruší.",
privs = {ch_stavby_admin = true},
func = stavba_na_mape,
}
minetest.register_chatcommand("stavbanamapě", def)
minetest.register_chatcommand("stavbanamape", def)
ch_core.close_submod("stavby")
|