· 6 years ago · Mar 26, 2019, 07:14 PM
1-- Towns 1.2.0.0
2-- © 2012-2019 Tsd
3-- Rest in peace Tsd
4
5conf = {
6 nick = "Города", -- play room nick
7 menu = ".:: Города", -- user menu name
8 file = "towns.txt", -- town list file
9 lang = "ru", -- used language
10 trig = { -- usage commands
11 main = "+towns",
12 work = "work",
13 mode = "mode",
14 list = "list",
15 best = "best",
16 help = "help"
17 },
18 clas = 2, -- administrator class
19 hint = 60, -- time before hint in seconds
20 exit = 120, -- time before stop in seconds
21 best = 50 -- number of best players
22}
23
24lang = {
25 en = { -- english
26 room = "Game was started in play room mode.",
27 chat = "Game was started in main chat mode.",
28 vers = "%s is available for play, it includes %d town names.",
29 mode = "The game is available in main chat mode for all users and play room mode for selected players.",
30 star = "To start the game write any town name in corresponding chat.",
31 quit = "To leave or enter the game write following command in chat: %s",
32 stop = "If nobody is playing after a hint, the game will be stopped after %d seconds.",
33 miss = "Game was stopped because nobody is playing, to start again write any town name in corresponding chat.",
34 lett = "Game was stopped because no more towns were found, to start again write any town name in corresponding chat.",
35 exit = "Player with score %d left the game: %s",
36 play = "Player with score %d entered the game: %s",
37 same = "%s: Given town was already named in this game.",
38 next = "%s: Town name accepted: %s, you score is %d, next town starts with: %s",
39 hint = "Hint for all: %s, next town starts with: %s",
40 best = "%d best players",
41 list = "Players list",
42 left = "Left players list",
43 none = "No players in list.",
44 clas = "You are not administrator.",
45 load = "Game was stopped by administrator.",
46 help = "Command list",
47 chan = "Change game mode",
48 leav = "Leave or enter game",
49 stat = "Player statistics",
50 work = "Turn game off and on"
51 },
52 ru = { -- russian
53 room = "Игра запущена в режиме игровой комнаты.",
54 chat = "Игра запущена в режиме общего чата.",
55 vers = "%s доÑтупна Ð´Ð»Ñ Ð¸Ð³Ñ€Ñ‹, она включает в ÑÐµÐ±Ñ %d названий городов.",
56 mode = "Игра доÑтупна в режиме обшего чата Ð´Ð»Ñ Ð²Ñех пользователей и режиме игровой комнаты Ð´Ð»Ñ Ð²Ñ‹Ð±Ð¾Ñ€Ð¾Ñ‡Ð½Ñ‹Ñ… пользователей.",
57 star = "Ð”Ð»Ñ Ð·Ð°Ð¿ÑƒÑка игры напишите в ÑоответÑтвующий чат название любого города.",
58 quit = "Ð”Ð»Ñ Ð²Ñ‹Ñ…Ð¾Ð´Ð° или входа в игру напишите Ñледующую команду в чат: %s",
59 stop = "ЕÑли никто не играет поÑле подÑказки, игра будет оÑтановлена через %d Ñекунд.",
60 miss = "Игра оÑтановлена поÑкольку никто не играет, Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка напишите в ÑоответÑтвующий чат название любого города.",
61 lett = "Игра оÑтановлена поÑкольку Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ñ Ð³Ð¾Ñ€Ð¾Ð´Ð¾Ð² закончилиÑÑŒ, Ð´Ð»Ñ Ð·Ð°Ð¿ÑƒÑка напишите в ÑоответÑтвующий чат название любого города.",
62 exit = "Игрок Ñ %d баллами покинул игру: %s",
63 play = "Игрок c %d баллами вошёл в игру: %s",
64 same = "%s: Данный город уже называли в Ñтой игре.",
65 next = "%s: Ðазвание города принÑто: %s, у Ð²Ð°Ñ Ñ‚ÐµÐ¿ÐµÑ€ÑŒ %d баллов, далее город на букву: %s",
66 hint = "ПодÑказка Ð´Ð»Ñ Ð²Ñех: %s, далее город на букву: %s",
67 best = "%d лучших игроков",
68 list = "СпиÑок игроков",
69 left = "СпиÑок вышедших игроков",
70 none = "Ðет игроков в ÑпиÑке.",
71 clas = "Ð’Ñ‹ не ÑвлÑетеÑÑŒ админиÑтратором.",
72 load = "Игра оÑтановлена админиÑтратором.",
73 help = "СпиÑок комманд",
74 chan = "Изменить режим игры",
75 leav = "Выход и вход в игру",
76 stat = "СтатиÑтика игроков",
77 work = "Отключить и включить игру"
78 }
79}
80
81sets = { -- dont touch this
82 load = true,
83 chat = true,
84 hint = false,
85 town = {},
86 play = {},
87 stat = {},
88 lett = "",
89 vers = "",
90 last = 0,
91 name = 0,
92 user = 0
93}
94
95function lower (data)
96 local back = ""
97
98 for pos = 1, # data do
99 local char = data:sub (pos, pos)
100 local byte = char:byte ()
101
102 if byte == 168 then
103 char = string.char (byte + 16)
104 elseif byte >= 192 and byte <= 223 then
105 char = string.char (byte + 32)
106 end
107
108 back = back .. char
109 end
110
111 return back:lower ()
112end
113
114function upper (data)
115 local back = ""
116
117 for pos = 1, # data do
118 local char = data:sub (pos, pos)
119 local byte = char:byte ()
120
121 if byte == 184 then
122 char = string.char (byte - 16)
123 elseif byte >= 224 and byte <= 255 then
124 char = string.char (byte - 32)
125 end
126
127 back = back .. char
128 end
129
130 return back:upper ()
131end
132
133function next (word)
134 local lett, keep = "", false
135
136 for pos = # word, 1, -1 do
137 lett = word:sub (pos, pos)
138
139 if sets.town [lett] then
140 for town, same in pairs (sets.town [lett]) do
141 if not same then
142 keep = true
143 break
144 end
145 end
146 end
147
148 if keep then
149 break
150 end
151 end
152
153 if not keep then
154 stop (lang [conf.lang].lett)
155 end
156
157 return lett
158end
159
160function stats (nick, priv)
161 local stat = {}
162
163 for user, scor in pairs (sets.stat) do
164 table.insert (stat, {
165 ["user"] = user,
166 ["scor"] = scor
167 })
168 end
169
170 if # stat == 0 then
171 if nick then
172 reply (lang [conf.lang].none, nick, priv)
173 end
174
175 return
176 end
177
178 table.sort (stat, function (one, two)
179 return one.scor > two.scor
180 end)
181
182 local list, best = "", 0
183
184 for _, data in pairs (stat) do
185 best = best + 1
186 list = list .. " " .. _tostring (best) .. ". " .. data.user .. " @ " .. _tostring (data.scor) .. "\r\n"
187
188 if best >= conf.best then
189 break
190 end
191 end
192
193 if nick then
194 reply (lang [conf.lang].best:format (best) .. ":\r\n\r\n" .. list, nick, priv)
195 else
196 chat (lang [conf.lang].best:format (best) .. ":\r\n\r\n" .. list)
197 end
198end
199
200function stop (data, quit)
201 chat (data)
202 stats ()
203
204 local rand = {}
205
206 for _, list in pairs (sets.town) do
207 for town, _ in pairs (list) do
208 table.insert (rand, town)
209 end
210 end
211
212 for pos = # rand, 1, -1 do
213 local new = math.random (pos)
214 rand [pos], rand [new] = rand [new], rand [pos]
215 end
216
217 sets.town = {}
218
219 for _, town in pairs (rand) do
220 local lett = town:sub (1, 1)
221
222 if not sets.town [lett] then
223 sets.town [lett] = {}
224 end
225
226 sets.town [lett][town] = false
227 end
228
229 sets.hint, sets.play, sets.lett, sets.user = false, {}, "", 0
230
231 if quit then
232 VH:UnRegBot (conf.nick)
233 sets.load = false
234 end
235end
236
237function chat (data, user)
238 local _, list = VH:GetNickList ()
239
240 for nick in list:sub (11):gmatch ("[^ %$]+") do
241 if not bot (nick) then
242 if sets.chat then -- chat mode
243 if not sets.play [nick] then
244 VH:SendToUser ("<" .. (user or conf.nick) .. "> " .. nmdc (data) .. "|", nick)
245 end
246
247 else -- room mode
248 if sets.play [nick] and nick ~= user then -- skip self
249 VH:SendToUser ("$To: " .. nick .. " From: " .. conf.nick .. " $<" .. (user or conf.nick) .. "> " .. nmdc (data) .. "|", nick)
250 end
251 end
252 end
253 end
254end
255
256function reply (data, nick, priv, user)
257 if priv then -- room mode
258 VH:SendToUser ("$To: " .. nick .. " From: " .. (user or conf.nick) .. " $<" .. (user or conf.nick) .. "> " .. nmdc (data) .. "|", nick)
259
260 else -- chat mode
261 VH:SendToUser ("<" .. (user or conf.nick) .. "> " .. nmdc (data) .. "|", nick)
262 end
263end
264
265function bot (nick)
266 if nick == VH.HubSec or nick == VH.OpChat then
267 return true
268 end
269
270 if VH.GetLuaBots then
271 local list = VH:GetLuaBots ()
272
273 if list then
274 for _, user in pairs (list) do
275 if nick == user ["sNick"] then
276 return true
277 end
278 end
279 end
280 end
281
282 return false
283end
284
285function class (nick)
286 local _, clas = VH:GetUserClass (nick)
287 return tonumber (clas or -2) or -2
288end
289
290function nmdc (data)
291 local back = data
292 back = back:gsub ("%$", "$")
293 back = back:gsub ("|", "|")
294 return back
295end
296
297function sql (data)
298 local back = data
299 back = back:gsub (string.char (92), string.char (92, 92))
300 back = back:gsub (string.char (34), string.char (92, 34))
301 back = back:gsub (string.char (39), string.char (92, 39))
302 return back
303end
304
305function _tostring (data)
306 if type (data) == "number" then
307 return string.format ("%d", data)
308 end
309
310 return tostring (data)
311end
312
313function Main (name)
314 local file = io.open (name, "r") -- load version
315
316 if file then
317 local line = file:read ("*line")
318 file:close ()
319
320 if line and # line > 0 then
321 sets.vers = line:sub (4)
322 sets.vers = sets.vers:gsub ("[\r\n]+", "")
323 end
324 end
325
326 local path = name:match ("^(.+/)[^/]+$") -- load towns
327 math.randomseed (os.time ())
328
329 if path then
330 path = path .. conf.file
331 local file = io.open (path, "r")
332
333 if file then
334 file:close ()
335 local list = {}
336
337 for town in io.lines (path) do
338 if # town >= 2 then
339 town = town:gsub ("[\r\n]+", "")
340 town = town:gsub (string.char (32), string.char (45))
341 table.insert (list, lower (town))
342 end
343 end
344
345 if # list > 0 then
346 for pos = # list, 1, -1 do
347 local new = math.random (pos)
348 list [pos], list [new] = list [new], list [pos]
349 end
350
351 for _, town in pairs (list) do
352 local lett = town:sub (1, 1)
353
354 if not sets.town [lett] then
355 sets.town [lett] = {}
356 end
357
358 sets.town [lett][town] = false
359 sets.name = sets.name + 1
360 end
361
362 else
363 -- todo: warn and disable
364 end
365 end
366 end
367
368 conf.nick = conf.nick:gsub (string.char (32), string.char (160)) -- create room
369 VH:RegBot (conf.nick, 3, sets.vers, string.char (2), "", 0) -- away
370
371 VH:SQLQuery ("create table if not exists `lua_towns_stat` (`nick` varchar(255) not null primary key, `score` bigint(20) unsigned not null default 0) engine = myisam default character set utf8 collate utf8_unicode_ci") -- load statistics
372 local _, rows = VH:SQLQuery ("select * from `lua_towns_stat`")
373
374 for row = 0, rows - 1 do
375 local _, nick, scor = VH:SQLFetch (row)
376 sets.stat [nick] = tonumber (scor)
377 end
378
379 chat (lang [conf.lang].vers:format (sets.vers, sets.name) .. "\r\n\r\n " .. lang [conf.lang].mode .. "\r\n " .. lang [conf.lang].star .. "\r\n " .. lang [conf.lang].quit:format (conf.trig.main) .. "\r\n " .. lang [conf.lang].stop:format (conf.exit) .. "\r\n")
380 return 1
381end
382
383function UnLoad (code)
384 if not sets.load then
385 return 1
386 end
387
388 sets.chat = true
389 sets.play = {}
390 chat (lang [conf.lang].load)
391 VH:UnRegBot (conf.nick)
392 return 1
393end
394
395function VH_OnUserLogin (nick, addr)
396 if VH.InUserSupports then
397 local _, flag = VH:InUserSupports (nick, "UserCommand")
398
399 if tonumber (flag) == 0 then
400 return
401 end
402 end
403
404 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].leav .. " $<%[mynick]> " .. conf.trig.main .. "||", nick)
405 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].list .. " $<%[mynick]> " .. conf.trig.main .. " " .. conf.trig.list .. "||", nick)
406 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].stat .. " $<%[mynick]> " .. conf.trig.main .. " " .. conf.trig.best .. "||", nick)
407 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].help .. " $<%[mynick]> " .. conf.trig.main .. " " .. conf.trig.help .. "||", nick)
408
409 if class (nick) >= conf.clas then
410 VH:SendToUser ("$UserCommand 0 3|", nick)
411 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].chan .. " $<%[mynick]> " .. conf.trig.main .. " " .. conf.trig.mode .. "||", nick)
412 VH:SendToUser ("$UserCommand 1 3 " .. nmdc (conf.menu) .. "\\" .. lang [conf.lang].work .. " $<%[mynick]> " .. conf.trig.main .. " " .. conf.trig.work .. "||", nick)
413 end
414
415 return 1
416end
417
418function VH_OnHubCommand (nick, data, oper, priv)
419 if data:sub (1, # conf.trig.main) ~= conf.trig.main then
420 return 1
421 end
422
423 local trig = data:sub (# conf.trig.main + 2)
424 local clas = class (nick)
425
426 if trig == conf.trig.work then -- work
427 if clas < conf.clas then
428 reply (lang [conf.lang].clas, nick, (priv == 1))
429 return 0
430 end
431
432 if sets.load then
433 stop (lang [conf.lang].load, true)
434
435 else
436 VH:RegBot (conf.nick, 3, sets.vers, string.char (2), "", 0) -- away
437 chat (lang [conf.lang].vers:format (sets.vers, sets.name) .. "\r\n\r\n " .. lang [conf.lang].mode .. "\r\n " .. lang [conf.lang].star .. "\r\n " .. lang [conf.lang].quit:format (conf.trig.main) .. "\r\n " .. lang [conf.lang].stop:format (conf.exit) .. "\r\n")
438 sets.load = true
439 end
440
441 return 0
442 end
443
444 if trig == conf.trig.mode then -- mode
445 if clas < conf.clas then
446 reply (lang [conf.lang].clas, nick, (priv == 1))
447 return 0
448 end
449
450 sets.play = {}
451
452 if sets.chat then -- room mode
453 chat (lang [conf.lang].room)
454 sets.chat = false
455
456 else -- chat mode
457 sets.chat = true
458 chat (lang [conf.lang].chat)
459 end
460
461 sets.user = 0
462 VH:EditBot (conf.nick, 3, sets.vers, string.char (sets.chat and 1 or 2), "", sets.user) -- away
463 return 0
464 end
465
466 if trig == conf.trig.list then -- list
467 local list = ""
468
469 for nick, _ in pairs (sets.play) do
470 list = list .. " " .. nick .. " @ " .. _tostring (sets.stat [nick] or 0) .. "\r\n"
471 end
472
473 if # list > 0 then
474 reply ((sets.chat and lang [conf.lang].left or lang [conf.lang].list) .. ":\r\n\r\n" .. list, nick, (priv == 1))
475 else
476 reply (lang [conf.lang].none, nick, (priv == 1))
477 end
478
479 return 0
480 end
481
482 if trig == conf.trig.best then -- best
483 stats (nick, (priv == 1))
484 return 0
485 end
486
487 if trig == conf.trig.help or # trig > 0 then -- help
488 local help = lang [conf.lang].help .. ":\r\n\r\n"
489 help = help .. " " .. conf.trig.main .. "\t\t- " .. lang [conf.lang].leav .. "\r\n"
490 help = help .. " " .. conf.trig.main .. " " .. conf.trig.list .. "\t- " .. lang [conf.lang].list .. "\r\n"
491 help = help .. " " .. conf.trig.main .. " " .. conf.trig.best .. "\t- " .. lang [conf.lang].stat .. "\r\n"
492 help = help .. " " .. conf.trig.main .. " " .. conf.trig.help .. "\t- " .. lang [conf.lang].help .. "\r\n"
493
494 if class (nick) >= conf.clas then
495 help = help .. "\r\n"
496 help = help .. " " .. conf.trig.main .. " " .. conf.trig.mode .. "\t- " .. lang [conf.lang].chan .. "\r\n"
497 help = help .. " " .. conf.trig.main .. " " .. conf.trig.work .. "\t- " .. lang [conf.lang].work .. "\r\n"
498 help = help .. "\r\n"
499 help = help .. " " .. (sets.chat and lang [conf.lang].chat or lang [conf.lang].room) .. "\r\n"
500 end
501
502 reply (help, nick, (priv == 1))
503 return 0
504 end
505
506 if not sets.load then
507 return 1
508 end
509
510 if sets.chat then -- chat mode
511 if sets.play [nick] then -- enter
512 sets.play [nick] = nil
513 chat (lang [conf.lang].play:format (sets.stat [nick] or 0, nick))
514
515 else -- leave
516 chat (lang [conf.lang].exit:format (sets.stat [nick] or 0, nick))
517 sets.play [nick] = true
518 end
519
520 else -- room mode
521 if sets.play [nick] then -- leave
522 chat (lang [conf.lang].exit:format (sets.stat [nick] or 0, nick))
523 sets.user = sets.user - 1
524 VH:EditBot (conf.nick, 3, sets.vers, string.char (1), "", sets.user)
525 sets.play [nick] = nil
526
527 else -- enter
528 sets.play [nick] = true
529 chat (lang [conf.lang].play:format (sets.stat [nick] or 0, nick))
530 sets.user = sets.user + 1
531 VH:EditBot (conf.nick, 3, sets.vers, string.char (1), "", sets.user)
532 end
533 end
534
535 return 0
536end
537
538function VH_OnParsedMsgChat (nick, data)
539 if not sets.load then
540 return 1
541 end
542
543 if not sets.chat then
544 sets.chat = true
545 reply (lang [conf.lang].room, nick)
546 sets.chat = false
547 return 0
548 end
549
550 local word = data:match ("^%s*(%S+)%s*$")
551
552 if not word or # word < 2 then
553 return 1
554 end
555
556 word = lower (word) -- todo: allow few mistakes
557 local lett = word:sub (1, 1)
558
559 if not sets.town [lett] or (# sets.lett > 0 and sets.lett ~= lett) then
560 return 1
561 end
562
563 for town, same in pairs (sets.town [lett]) do
564 if town == word then
565 if sets.play [nick] then
566 sets.play [nick] = nil
567 chat (lang [conf.lang].play:format (sets.stat [nick] or 0, nick))
568 end
569
570 chat (data, nick)
571
572 if same then
573 chat (lang [conf.lang].same:format (nick))
574
575 else
576 sets.stat [nick] = (sets.stat [nick] or 0) + 1
577 VH:SQLQuery ("insert into `lua_towns_stat` (`nick`, `score`) values ('" .. sql (nick) .. "', " .. _tostring (sets.stat [nick]) .. ") on duplicate key update `score` = " .. _tostring (sets.stat [nick]))
578 sets.town [lett][town] = true
579 sets.lett = next (town)
580
581 if # sets.lett > 0 then
582 chat (lang [conf.lang].next:format (nick, town, sets.stat [nick], upper (sets.lett)))
583 end
584 end
585
586 sets.last = os.time ()
587 sets.hint = false
588 return 0
589 end
590 end
591
592 return 1
593end
594
595function VH_OnParsedMsgPM (nick, data, user)
596 if user ~= conf.nick or not sets.load then
597 return 1
598 end
599
600 if VH_OnHubCommand (nick, data, 0, 1) == 0 then
601 return 0
602 end
603
604 if sets.chat then
605 sets.chat = false
606 reply (lang [conf.lang].chat, nick)
607 sets.chat = true
608 return 0
609 end
610
611 local word = data:match ("^%s*(%S+)%s*$")
612
613 if not word or # word < 2 then
614 if sets.play [nick] then
615 chat (data, nick)
616 end
617
618 return 0
619 end
620
621 word = lower (word) -- todo: allow few mistakes
622 local lett = word:sub (1, 1)
623
624 if not sets.town [lett] or (# sets.lett > 0 and sets.lett ~= lett) then
625 if sets.play [nick] then
626 chat (data, nick)
627 end
628
629 return 0
630 end
631
632 for town, same in pairs (sets.town [lett]) do
633 if town == word then
634 chat (data, nick)
635
636 if not sets.play [nick] then
637 sets.play [nick] = true
638 chat (lang [conf.lang].play:format (sets.stat [nick] or 0, nick))
639 sets.user = sets.user + 1
640 VH:EditBot (conf.nick, 3, sets.vers, string.char (1), "", sets.user)
641 end
642
643 if same then
644 chat (lang [conf.lang].same:format (nick))
645
646 else
647 sets.stat [nick] = (sets.stat [nick] or 0) + 1
648 VH:SQLQuery ("insert into `lua_towns_stat` (`nick`, `score`) values ('" .. sql (nick) .. "', " .. _tostring (sets.stat [nick]) .. ") on duplicate key update `score` = " .. _tostring (sets.stat [nick]))
649 sets.town [lett][town] = true
650 sets.lett = next (town)
651
652 if # sets.lett > 0 then
653 chat (lang [conf.lang].next:format (nick, town, sets.stat [nick], upper (sets.lett)))
654 end
655 end
656
657 sets.last = os.time ()
658 sets.hint = false
659 return 0
660 end
661 end
662
663 if sets.play [nick] then
664 chat (data, nick)
665 end
666
667 return 0
668end
669
670function VH_OnTimer (msec)
671 if not sets.load or # sets.lett == 0 then
672 return 1
673 end
674
675 local dif = os.difftime (os.time (), sets.last)
676
677 if not sets.hint and dif >= conf.hint then -- show hint
678 if sets.town [sets.lett] then
679 for town, same in pairs (sets.town [sets.lett]) do
680 if not same then
681 sets.town [sets.lett][town] = true
682 sets.lett = next (town)
683
684 if # sets.lett > 0 then
685 chat (lang [conf.lang].hint:format (town, upper (sets.lett)))
686 sets.hint = true
687 end
688
689 break
690 end
691 end
692
693 if # sets.lett == 0 then
694 return 1
695 end
696
697 else
698 stop (lang [conf.lang].lett)
699 end
700
701 return 1
702 end
703
704 if dif >= conf.exit then -- stop game
705 stop (lang [conf.lang].miss)
706 VH:EditBot (conf.nick, 3, sets.vers, string.char (2), "", 0) -- away
707 return 1
708 end
709
710 return 1
711end
712
713-- end of file