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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
|
ch_base.open_mod(core.get_current_modname())
local worldpath = core.get_worldpath()
local datapath = worldpath.."/ch_playerdata"
local playerlist_path = worldpath.."/ch_data_players"
local storage = core.get_mod_storage()
local old_online_charinfo = {} -- uchovává online_charinfo[] po odpojení postavy
local players_list, players_set = {}, {} -- seznam/množina všech známých hráčských postav (pro offline_charinfo)
local lc_to_player_name = {} -- pro jména existujících postav lowercase => loginname
local current_format_version = 2
ch_data = {
online_charinfo = {},
offline_charinfo = {},
supported_lang_codes = {cs = true, sk = true},
-- Tato funkce může být přepsána. Rozhoduje, zda zadané jméno postavy je přijatelné.
is_acceptable_name = function(player_name) return true end,
initial_offline_charinfo = {
-- int (> 0) -- pro [ch_core/ap], udává aktuální úroveň postavy
ap_level = 1, -- musí být > 0
-- int (> 0) -- pro [ch_core/ap], udává verzi systému AP (pro upgrade)
ap_version = 1,
-- int (>= 0) -- pro [ch_core/ap], udává celkový počet bodů aktivity postavy
ap_xp = 0,
-- int {0, 1} -- 0 = nic, 1 = předměty házet do koše
discard_drops = 0,
-- string -- pro [ch_core/teleport], pozice uložená příkazem /domů
domov = "",
-- int (>= 0) -- pro [ch_core/chat] udává aktuální doslech
doslech = 50,
-- int {0, 1} -- pro [ch_core] 0 = normální velikost, 1 = rozšířený inventář
extended_inventory = 0,
-- string -- pole příznaků
flags = "",
-- string YYYY-MM-DD nebo "" -- datum, kdy byla hráči/ce naposledy vypsána oznámení po přihlášení do hry (YYYY-MM-DD)
last_ann_shown_date = "1970-01-01",
-- int (>= 0) -- v sekundách od 1. 1. 2000 UTC; 0 značí neplatnou hodnotu
last_login = 0,
-- int {0, 1} -- 0 = shýbat se při stisku Shift; 1 = neshýbat se
neshybat = 0,
-- int {0, 1} -- 0 = krásná obloha ano, 1 ne
no_ch_sky = 0,
-- float -- v sekundách
past_ap_playtime = 0.0,
-- float -- v sekundách
past_playtime = 0.0,
-- string -- výčet dodatečných práv naplánovaných pro registraci postavy
pending_registration_privs = "",
-- string -- typ naplánované registrace postavy
pending_registration_type = "",
-- int -- pro [ch_bank]
rezim_plateb = 0,
-- int {0, 1} -- 0 => zobrazit, 1 => skrýt
skryt_body = 0,
-- int {0, 1} -- 0 => zobrazovat (výchozí), 1 => skrýt
skryt_hlad = 0,
-- int {0, 1} -- 0 => zobrazovat (výchozí), 1 => skrýt
skryt_zbyv = 0,
-- string -- pro [ch_core/teleport], pozice uložená příkazem /stavím
stavba = "",
-- string -- nastavení filtru událostí
ui_event_filter = "",
-- int (>= 1) -- číslo verze uložených dat (umožňuje upgrade)
version = current_format_version,
-- int {1, 2, 3} -- volba cíle pro /začátek: 1 => Začátek, 2 => Masarykovo náměstí, 3 => Hlavní nádraží
zacatek_kam = 1,
},
initial_offline_playerinfo = {
-- int -- výše uloženého trestu (může být záporná)
trest = 0,
}
}
-- POZNÁMKA: protože příznaky z následujícího pole se mapují na znaky v řetězci 'flags', nesmí se mazat ani zakomentovat!
-- Je však možno je přejmenovat při zachování pozice.
local flag_ids = {
"discard_drops",
"extended_inventory",
"neshybat",
"no_ch_sky",
"skryt_body",
"skryt_hlad",
"skryt_zbyv",
}
local flag_name_to_id = {}
for i, flag in ipairs(flag_ids) do
local old_id = flag_name_to_id[flag]
if old_id ~= nil then
error("Flag '"..flag.."' has multiple IDs: "..old_id..", "..i.."!")
end
flag_name_to_id[flag] = i
end
ch_data.initial_offline_charinfo.flags = string.rep(" ", #flag_ids)
local function add_player(player_name, new_offline_charinfo, player_info)
if players_set[player_name] then
return false
end
assert(new_offline_charinfo)
local lcase = string.lower(player_name)
-- add player to players_list (persistently):
table.insert(players_list, player_name)
core.safe_file_write(playerlist_path, assert(core.serialize(players_list)))
-- add player to players_set and lc_to_player_name:
players_set[player_name] = true
lc_to_player_name[lcase] = player_name
-- add new offline_charinfo:
ch_data.offline_charinfo[player_name] = new_offline_charinfo
-- add new offline_charinfo[].player:
new_offline_charinfo.player = player_info or table.copy(ch_data.initial_offline_playerinfo)
if new_offline_charinfo.player.name == nil then
new_offline_charinfo.player.name = player_name
end
return true
end
local function delete_player(player_name)
-- check if the player exists:
if not players_set[player_name] then
return false
end
-- detach offline_playerinfo:
local offline_player_info = assert(ch_data.offline_charinfo[player_name].player)
local aliases = {}
for alias, offline_charinfo in pairs(ch_data.offline_charinfo) do
if alias ~= player_name and offline_charinfo.player.name == player_name then
offline_player_info = offline_charinfo.player
table.insert(aliases, alias)
end
end
if #aliases > 0 then
offline_player_info.name = aliases[1]
core.log("debug", "delete_player(): dotplayer of "..table.concat(aliases, ",").." corrected to "..player_info.name)
for _, alias in ipairs(aliases) do
ch_core.save_offline_playerinfo(alias)
end
end
-- remove player from players_list:
local lcase = string.lower(player_name)
for i, pname in ipairs(players_list) do
if pname == player_name then
table.remove(players_list, i)
break
end
end
-- save the player_list:
core.safe_file_write(playerlist_path, assert(core.serialize(players_list)))
-- remove player from players_set:
players_set[player_name] = nil
-- remove player from lc_to_player_name:
lc_to_player_name[lcase] = nil
-- remove player from offline_charinfo:
ch_data.offline_charinfo[player_name] = nil
return true
end
function ch_data.get_flag(charinfo, flag_name, default_result)
local id = flag_name_to_id[flag_name]
if id ~= nil then
local result = (charinfo.flags or ""):sub(id, id)
if result ~= "" then
return result
end
end
return default_result or " "
end
function ch_data.get_flags(charinfo)
local flags = charinfo.flags or ""
local result = {}
for id, name in ipairs(flag_ids) do
local value = flags:sub(id, id)
if value == "" then
value = " "
end
result[name] = value
end
return result
end
function ch_data.set_flag(charinfo, flag_name, value)
local id = flag_name_to_id[flag_name]
if id == nil then
return false
end
value = (tostring(value).." "):sub(1,1)
local flags = charinfo.flags or ""
if flags:len() < id then
flags = flags..string.rep(" ", id - flags:len() - 1)..value
else
flags = flags:sub(1, id - 1)..value..flags:sub(id + 1, -1)
end
charinfo.flags = flags
return true
end
function ch_data.correct_player_name_casing(name)
return lc_to_player_name[string.lower(name)]
end
function ch_data.get_joining_online_charinfo(player)
assert(core.is_player(player))
local player_name = player:get_player_name()
local result = ch_data.online_charinfo[player_name]
if result ~= nil then
return result
end
local player_info = core.get_player_information(player_name)
local now = core.get_us_time()
result = {
areas = {{
id = 0,
name = "Český hvozd",
type = 1,
}},
-- časová známka vytvoření online_charinfo (vstupu postavy do hry)
join_timestamp = now,
-- jazykový kód (obvykle "cs")
lang_code = player_info.lang_code or "",
-- úroveň osvětlení postavy
light_level = 0,
-- časová známka pro úroveň osvětlení postavy
light_level_timestamp = now,
-- verze protokolu
protocol_version = player_info.protocol_version or 0,
-- tabulka již zobrazených nápověd (bude deserializována níže)
navody = {},
-- co udělat těsně po připojení
news_role = "new_player",
-- přihlašovací jméno
player_name = player_name,
}
-- news_role:
--[[
5.5.x => formspec_version = 5, protocol_version = 40
5.6.x => formspec_version = 6, protocol_version = 41
5.7.x => formspec_version = 6, protocol_version = 42
5.8.0 => formspec_version = 7, protocol_version = 43
5.9.0 => formspec_version = ?, protocol_version = ?
5.10.0 => formspec_version = 8, protocol_version = 46
]]
if result.protocol_version < 42 then
result.news_role = "disconnect"
elseif not ch_data.supported_lang_codes[result.lang_code] and not core.check_player_privs(player, "server") then
result.news_role = "invalid_locale"
elseif core.check_player_privs(player, "ch_registered_player") then
result.news_role = "player"
elseif not ch_data.is_acceptable_name(player_name) then
result.news_role = "invalid_name"
else
result.news_role = "new_player"
end
ch_data.online_charinfo[player_name] = result
local prev_online_charinfo = old_online_charinfo[player_name]
if prev_online_charinfo ~= nil then
old_online_charinfo[player_name] = nil
if prev_online_charinfo.leave_timestamp ~= nil then
result.prev_leave_timestamp = prev_online_charinfo.leave_timestamp
end
end
core.log("action", "JOIN PLAYER(" .. player_name ..") at "..now.." with lang_code \""..result.lang_code..
"\", formspec_version = "..tostring(player_info.formspec_version)..", protocol_version = "..
result.protocol_version..", news_role = "..result.news_role..", ip_address = "..tostring(player_info.address))
-- deserializovat návody:
local meta = player:get_meta()
local s = meta:get_string("navody")
if s and s ~= "" then
result.navody = core.deserialize(s, true) or result.navody
end
if result.news_role ~= "invalid_name" then
ch_data.get_or_add_offline_charinfo(player_name)
end
--[[
TODO:
if core.is_creative_enabled(player_name) then
result.is_creative = true
if ch_core.set_immortal then
ch_core.set_immortal(player, true)
end
end
else
core.log("error", "Player object not available for "..player_name.." in get_joining_online_charinfo()!")
end
]]
return result
end
function ch_data.get_leaving_online_charinfo(player)
assert(core.is_player(player))
local player_name = player:get_player_name()
local result = ch_data.online_charinfo[player_name]
if result ~= nil then
result.leave_timestamp = core.get_us_time()
old_online_charinfo[player_name] = result
ch_data.online_charinfo[player_name] = nil
return result
else
return old_online_charinfo[player_name]
end
end
function ch_data.delete_offline_charinfo(player_name)
if ch_data.online_charinfo[player_name] ~= nil then
return false, "Postava je ve hře!"
end
if not delete_player(player_name) then
return false, "Mazání selhalo."
end
local success, errmsg = os.remove(worldpath.."/ch_playerdata/"..player_name)
if success then
return true, "Úspěšně smazáno."
else
return false, "Mazání souboru selhalo: "..(errmsg or "nil")
end
end
function ch_data.get_offline_charinfo(player_name)
local result = ch_data.offline_charinfo[player_name]
if result == nil then
error("Offline charinfo not found for player '"..player_name.."'!")
end
return result
end
function ch_data.get_or_add_offline_charinfo(player_name)
local result = ch_data.offline_charinfo[player_name]
if result == nil then
add_player(player_name, table.copy(ch_data.initial_offline_charinfo))
result = assert(ch_data.offline_charinfo[player_name])
core.log("action", "[ch_data] Offline charinfo initialized for "..player_name)
ch_data.save_offline_charinfo(player_name)
end
return result
end
local debug_flag = false
function ch_data.save_offline_charinfo(player_name, include_playerinfo)
if players_set[player_name] == nil then
return false
end
local data = ch_data.offline_charinfo[player_name]
if data == nil then
return false
end
core.safe_file_write(datapath.."/"..player_name, assert(core.serialize(data)))
if include_playerinfo and data.player.name ~= player_name then
local dotplayer_name = data.player.name
assert(ch_data.offline_charinfo[dotplayer_name])
assert(ch_data.offline_charinfo[dotplayer_name].player.name == dotplayer_name)
return ch_data.save_offline_charinfo(dotplayer_name)
end
return true
end
function ch_data.save_offline_playerinfo(player_name)
if players_set[player_name] == nil then
return false
end
local data = ch_data.offline_charinfo[player_name]
if data == nil then
return false
end
local dotplayer_name = assert(data.player.name)
assert(ch_data.offline_charinfo[dotplayer_name])
assert(ch_data.offline_charinfo[dotplayer_name].player.name == dotplayer_name)
return ch_data.save_offline_charinfo(dotplayer_name)
end
local function on_joinplayer(player, last_login)
ch_data.get_joining_online_charinfo(player)
end
local function on_leaveplayer(player)
ch_data.get_leaving_online_charinfo(player)
end
core.register_on_joinplayer(on_joinplayer)
core.register_on_leaveplayer(on_leaveplayer)
local function upgrade_offline_charinfo(player_name, data)
local old_version = data.version
if data.version <= 1 then
data.player.trest = data.trest or 0
data.trest = nil
end
data.version = current_format_version
core.log("info", "Offline_charinfo["..player_name.."] upgraded from version "..old_version.." to the current version "..data.version..".")
return true
end
-- Load and initialize:
core.mkdir(datapath)
local function initialize()
local f = io.open(playerlist_path)
if f then
local text = f:read("*a")
f:close()
if text ~= nil and text ~= "" then
local new_players = core.deserialize(text, true)
if type(new_players) == "table" then
core.log("action", "[ch_data] "..#new_players.." known players.")
players_list = new_players
players_set = {}
for _, player_name in ipairs(players_list) do
players_set[player_name] = true
end
end
end
end
for _, player_name in ipairs(players_list) do
f = io.open(datapath.."/"..player_name)
if f then
local text = f:read("*a")
f:close()
if text ~= nil and text ~= "" then
local data = core.deserialize(text, true)
if type(data) == "table" then
ch_data.offline_charinfo[player_name] = data
lc_to_player_name[string.lower(player_name)] = player_name
end
end
end
if ch_data.offline_charinfo[player_name] == nil then
core.log("error", "[ch_data] deserialization of offline_charinfo["..player_name.."] failed!")
end
end
for player_name, poc in pairs(ch_data.offline_charinfo) do
-- vivify/upgrade/correct offline_charinfo:
for key, value in pairs(ch_data.initial_offline_charinfo) do
if poc[key] == nil then
poc[key] = value
core.log("warning", "Missing offline_charinfo key "..player_name.."/"..key.." vivified.")
end
end
-- correct invalid past_playtime:
if poc.past_playtime < 0 then
core.log("warning", "Invalid past_playtime for "..player_name.." ("..poc.past_playtime..") corrected to zero!")
poc.past_playtime = 0 -- correction of invalid data
end
-- vivify .player table (including .player.name)
if poc.player == nil or poc.player.name == nil then
poc.player = {name = player_name}
end
end
-- link .player tables according to .player.name:
for player_name, poc in pairs(ch_data.offline_charinfo) do
local dotplayer_name = assert(poc.player.name)
if dotplayer_name ~= player_name then
if players_set[dotplayer_name] and ch_data.offline_charinfo[dotplayer_name] then
poc.player = assert(ch_data.offline_charinfo[dotplayer_name].player)
else
error("offline_charinfo["..player_name.."] looks for player data of '"..dotplayer_name.."', but it doesn't exist!")
end
end
end
-- upgrade old data:
for player_name, poc in pairs(ch_data.offline_charinfo) do
if poc.version < current_format_version then
upgrade_offline_charinfo(player_name, poc)
end
end
end
initialize()
initialize = nil
-- Obsluhy událostí:
-- ======================================================================================================================
local function on_joinplayer(player, last_login)
local player_name = player:get_player_name()
local online_charinfo = ch_data.get_joining_online_charinfo(player)
local offline_charinfo = ch_data.get_offline_charinfo(player_name)
online_charinfo.doslech = offline_charinfo.doslech
if offline_charinfo ~= nil then
offline_charinfo.last_login = os.time() - 946684800
ch_data.save_offline_charinfo(player_name)
-- lc_to_player_name[string.lower(player_name)] = player_name
end
return true
end
local function save_playtime(online_charinfo, offline_charinfo)
if offline_charinfo == nil then
return 0, 0, 0
end
local now = core.get_us_time()
local past_playtime = offline_charinfo.past_playtime or 0
local current_playtime = math.max(0, 1.0e-6 * (now - online_charinfo.join_timestamp))
local total_playtime = past_playtime + current_playtime
offline_charinfo.past_playtime = total_playtime
ch_data.save_offline_charinfo(online_charinfo.player_name)
online_charinfo.join_timestamp = nil
return past_playtime, current_playtime, total_playtime
end
local function on_leaveplayer(player, timedout)
local player_name = player:get_player_name()
local online_info = ch_data.get_leaving_online_charinfo(player)
if online_info.join_timestamp then
local past_playtime, current_playtime, total_playtime = save_playtime(online_info, ch_data.offline_charinfo[player_name])
print("PLAYER(" .. player_name .."): played seconds: " .. current_playtime .. " / " .. total_playtime)
end
end
local function on_shutdown()
for player_name, online_info in pairs(table.copy(ch_data.online_charinfo)) do
if online_info.join_timestamp then
local past_playtime, current_playtime, total_playtime
past_playtime, current_playtime, total_playtime = save_playtime(online_info, ch_data.offline_charinfo[player_name])
print("PLAYER(" .. player_name .."): played seconds: " .. current_playtime .. " / " .. total_playtime)
end
end
end
local function on_placenode(pos, newnode, placer, oldnode, itemstack, pointed_thing)
if core.is_player(placer) then
local player_name = placer:get_player_name()
local online_charinfo = ch_data.online_charinfo[player_name]
if online_charinfo ~= nil then
online_charinfo.last_placenode_ustime = core.get_us_time()
end
end
end
local function on_dignode(pos, oldnode, digger)
if core.is_player(digger) then
local player_name = digger:get_player_name()
local online_charinfo = ch_data.online_charinfo[player_name]
if online_charinfo ~= nil then
online_charinfo.last_dignode_ustime = core.get_us_time()
end
end
end
function ch_data.clear_help(player)
local player_name = player:get_player_name()
local online_charinfo = ch_data.online_charinfo[player_name]
if online_charinfo then
online_charinfo.navody = {}
player:get_meta():set_string("navody", core.serialize(online_charinfo.navody))
return true
else
return false
end
end
--[[
Otestuje, zda podle online_charinfo má dané postavě být zobrazený
v četu návod k položce daného názvu. Pokud ano, nastaví příznak, aby se
to znovu již nestalo, a vrátí definici daného předmětu,
z níž lze z položek description a _ch_help sestavit text k zobrazení.
]]
function ch_data.should_show_help(player, online_charinfo, item_name)
local def = core.registered_items[item_name]
if def and def._ch_help then
if def._ch_help_group then
item_name = def._ch_help_group
end
local navody = online_charinfo.navody
if not navody then
navody = {[item_name] = 1}
online_charinfo.navody = navody
player:get_meta():set_string("navody", core.serialize(navody))
return def.description ~= nil and def
end
if not navody[item_name] then
navody[item_name] = 1
player:get_meta():set_string("navody", core.serialize(navody))
return def.description ~= nil and def
end
end
return nil
end
function ch_data.nastavit_shybani(player_name, shybat)
local offline_charinfo = ch_data.offline_charinfo[player_name]
if offline_charinfo == nil then
core.log("error", "ch_data.nastavit_shybani(): Expected offline charinfo for player "..player_name.." not found!")
return false
end
local new_state
if shybat then
new_state = 0
else
new_state = 1
end
offline_charinfo.neshybat = new_state
ch_data.save_offline_charinfo(player_name)
return true
end
core.register_on_joinplayer(on_joinplayer)
core.register_on_leaveplayer(on_leaveplayer)
core.register_on_shutdown(on_shutdown)
core.register_on_dignode(on_dignode)
core.register_on_placenode(on_placenode)
local def = {
description = "Smaže údaje o tom, ke kterým předmětům již byly postavě zobrazeny nápovědy, takže budou znovu zobrazovány nápovědy ke všem předmětům.",
func = function(player_name, param)
if ch_data.clear_help(core.get_player_by_name(player_name)) then
return true, "Údaje smazány."
else
core.log("error", "/návodyznovu: vnitřní chyba serveru ("..player_name..")!")
return false, "Vnitřní chyba serveru"
end
end,
}
core.register_chatcommand("návodyznovu", def)
core.register_chatcommand("navodyznovu", def)
def = {
description = "Odstraní údaje o postavě uložené v systému ch_data. Postava nesmí být ve hře.",
privs = {server = true},
func = function(player_name, param)
local offline_charinfo = ch_data.offline_charinfo[param]
if not offline_charinfo then
return false, "Data o "..param.." nenalezena!"
end
if ch_data.delete_offline_charinfo(param) then
return true, "Data o "..param.." smazána."
else
return false, "Při odstraňování nastala chyba."
end
end,
}
core.register_chatcommand("delete_offline_charinfo", def)
def = {
description = "Trvale vypne či zapne shýbání postavy při držení Shiftu.",
params = "<ano|ne>",
func = function(player_name, param)
if param ~= "ano" and param ~= "ne" then
return false, "Chybná syntaxe."
end
if ch_data.nastavit_shybani(player_name, param == "ano") then
core.chat_send_player(player_name, "*** Shýbání postavy "..(param == "ne" and "vypnuto" or "zapnuto")..".")
return true
else
core.log("error", "/shybat: Expected offline charinfo for player "..player_name.." not found!")
return false, "Vnitřní chyba serveru: Data postavy nenalezena."
end
end,
}
core.register_chatcommand("shýbat", def)
core.register_chatcommand("shybat", def)
local function merge_playerinfos(player_name_a, player_name_b)
if player_name_a == player_name_b then
return false, "'"..player_name_a.."' a '"..player_name_b.."' reprezentují tutéž postavu!"
end
local oci_a = ch_data.offline_charinfo[player_name_a]
local oci_b = ch_data.offline_charinfo[player_name_b]
if oci_a == nil then
return false, "Postava '"..player_name_a.."' neexistuje!"
end
if oci_b == nil then
return false, "Postava '"..player_name_b.."' neexistuje!"
end
local dotplayer_a = assert(oci_a.player.name)
local dotplayer_b = assert(oci_b.player.name)
if dotplayer_a == dotplayer_b then
return false, "Postavy '"..player_name_a.."' a '"..player_name_b.."' již patří stejné/mu hráči/ce '"..dotplayer_a.."'."
end
local aliases = {dotplayer_b}
for alias, offline_charinfo in pairs(ch_data.offline_charinfo) do
if alias ~= dotplayer_b and offline_charinfo.player.name == dotplayer_b then
table.insert(aliases, alias)
end
end
ch_data.save_offline_charinfo(dotplayer_b)
for _, alias in ipairs(aliases) do
ch_data.offline_charinfo[alias].player = oci_a.player
ch_data.save_offline_charinfo(alias)
core.log("action", "[MERGE] Player info of .player "..dotplayer_a.." assigned to player "..alias..".")
end
return true, "Postavy "..table.concat(aliases, ",").." přiřazeny hráči/ce "..dotplayer_a.."."
end
local function set_main_player(player_name)
local oci_a = ch_data.offline_charinfo[player_name]
if oci_a == nil then
return false, "Postava '"..player_name.."' neexistuje!"
end
local old_main = assert(oci_a.player.name)
if old_main == player_name then
return false, "Postava '"..player_name.."' již je hlavní."
end
local aliases = {}
for alias, offline_charinfo in pairs(ch_data.offline_charinfo) do
if alias ~= old_main and offline_charinfo.player.name == old_main then
table.insert(aliases, alias)
end
end
oci_a.player.name = player_name
ch_data.save_offline_charinfo(old_main)
for _, alias in ipairs(aliases) do
ch_data.save_offline_charinfo(alias)
end
return true, "Postava "..player_name.." nastavena jako hlavní (původní hlavní postava: "..old_main..")."
end
function ch_data.get_player_characters(player_name)
local offline_charinfo = ch_data.offline_charinfo[player_name]
if offline_charinfo == nil then
return nil
end
local main_name = offline_charinfo.player.name
local result = {}
for name, oci in pairs(ch_data.offline_charinfo) do
if oci.player.name == main_name then
table.insert(result, name)
end
end
if #result > 1 then
table.sort(result, function(a, b) return (a == main_name and b ~= main_name) or a < b end) -- TODO: better sorting
end
return result, main_name
end
def = {
description = "Sloučí hráčská data odpovídající postavě B s hráčskými daty odpovídajícími postavě A",
params = "<Jmeno_postavy_hlavni_A> <Jmeno_postavy_vedlejsi_B>",
privs = {server = true},
func = function(admin_name, param)
local a, b = string.match(param, "^([^ ]+) +([^ ]+)$")
if a == nil or b == nil then
return false, "Chybné zadání!"
end
local result, message = merge_playerinfos(a, b)
return result, message
end,
}
core.register_chatcommand("připojit_postavu", def)
core.register_chatcommand("pripojit_postavu", def)
def = {
description = "Změní hlavní postavu hráče/ky",
params = "<Nova_hlavni_postava>",
privs = {server = true},
func = function(admin_name, param)
local result, message = set_main_player(param)
return result, message
end,
}
core.register_chatcommand("hlavní_postava", def)
core.register_chatcommand("hlavni_postava", def)
ch_base.close_mod(core.get_current_modname())
|