1SpeakTypesSettings = {
2 none = {},
3 say = { speakType = MessageModes.Say, color = '#FFFF00' },
4 whisper = { speakType = MessageModes.Whisper, color = '#FFFF00' },
5 yell = { speakType = MessageModes.Yell, color = '#FFFF00' },
6 broadcast = { speakType = MessageModes.GamemasterBroadcast, color = '#F55E5E' },
7 private = { speakType = MessageModes.PrivateTo, color = '#5FF7F7', private = true },
8 privateRed = { speakType = MessageModes.GamemasterTo, color = '#F55E5E', private = true },
9 privatePlayerToPlayer = { speakType = MessageModes.PrivateTo, color = '#9F9DFD', private = true },
10 privatePlayerToNpc = { speakType = MessageModes.NpcTo, color = '#9F9DFD', private = true, npcChat = true },
11 privateNpcToPlayer = { speakType = MessageModes.NpcFrom, color = '#5FF7F7', private = true, npcChat = true },
12 channelYellow = { speakType = MessageModes.Channel, color = '#FFFF00' },
13 channelWhite = { speakType = MessageModes.ChannelManagement, color = '#FFFFFF' },
14 channelRed = { speakType = MessageModes.GamemasterChannel, color = '#F55E5E' },
15 channelOrange = { speakType = MessageModes.ChannelHighlight, color = '#F6A731' },
16 monsterSay = { speakType = MessageModes.MonsterSay, color = '#FE6500', hideInConsole = true},
17 monsterYell = { speakType = MessageModes.MonsterYell, color = '#FE6500', hideInConsole = true},
18 rvrAnswerFrom = { speakType = MessageModes.RVRAnswer, color = '#FE6500' },
19 rvrAnswerTo = { speakType = MessageModes.RVRAnswer, color = '#FE6500' },
20 rvrContinue = { speakType = MessageModes.RVRContinue, color = '#FFFF00' },
21}
22
23SpeakTypes = {
24 [MessageModes.Say] = SpeakTypesSettings.say,
25 [MessageModes.Whisper] = SpeakTypesSettings.whisper,
26 [MessageModes.Yell] = SpeakTypesSettings.yell,
27 [MessageModes.GamemasterBroadcast] = SpeakTypesSettings.broadcast,
28 [MessageModes.PrivateFrom] = SpeakTypesSettings.private,
29 [MessageModes.GamemasterPrivateFrom] = SpeakTypesSettings.privateRed,
30 [MessageModes.NpcTo] = SpeakTypesSettings.privatePlayerToNpc,
31 [MessageModes.NpcFrom] = SpeakTypesSettings.privateNpcToPlayer,
32 [MessageModes.Channel] = SpeakTypesSettings.channelYellow,
33 [MessageModes.ChannelManagement] = SpeakTypesSettings.channelWhite,
34 [MessageModes.GamemasterChannel] = SpeakTypesSettings.channelRed,
35 [MessageModes.ChannelHighlight] = SpeakTypesSettings.channelOrange,
36 [MessageModes.MonsterSay] = SpeakTypesSettings.monsterSay,
37 [MessageModes.MonsterYell] = SpeakTypesSettings.monsterYell,
38 [MessageModes.RVRChannel] = SpeakTypesSettings.channelWhite,
39 [MessageModes.RVRContinue] = SpeakTypesSettings.rvrContinue,
40 [MessageModes.RVRAnswer] = SpeakTypesSettings.rvrAnswerFrom,
41 [MessageModes.NpcFromStartBlock] = SpeakTypesSettings.privateNpcToPlayer,
42
43 -- ignored types
44 [MessageModes.Spell] = SpeakTypesSettings.none,
45 [MessageModes.BarkLow] = SpeakTypesSettings.none,
46 [MessageModes.BarkLoud] = SpeakTypesSettings.none,
47}
48
49SayModes = {
50 [1] = { speakTypeDesc = 'whisper', icon = '/images/game/console/whisper' },
51 [2] = { speakTypeDesc = 'say', icon = '/images/game/console/say' },
52 [3] = { speakTypeDesc = 'yell', icon = '/images/game/console/yell' }
53}
54
55ChannelEventFormats = {
56 [ChannelEvent.Join] = '%s joined the channel.',
57 [ChannelEvent.Leave] = '%s left the channel.',
58 [ChannelEvent.Invite] = '%s has been invited to the channel.',
59 [ChannelEvent.Exclude] = '%s has been removed from the channel.',
60}
61
62MAX_HISTORY = 500
63MAX_LINES = 100
64HELP_CHANNEL = 9
65
66chatView = false
67consolePanel = nil
68consoleContentPanel = nil
69consoleTabBar = nil
70consoleTextEdit = nil
71consoleToggleChat = nil
72channels = nil
73channelsWindow = nil
74communicationWindow = nil
75ownPrivateName = nil
76messageHistory = {}
77currentMessageIndex = 0
78ignoreNpcMessages = false
79defaultTab = nil
80serverTab = nil
81violationsChannelId = nil
82violationWindow = nil
83violationReportTab = nil
84talkLock = true
85ignoredChannels = {}
86filters = {}
87
88floatingMode = false
89
90local communicationSettings = {
91 useIgnoreList = true,
92 useWhiteList = true,
93 privateMessages = false,
94 yelling = false,
95 allowVIPs = false,
96 ignoredPlayers = {},
97 whitelistedPlayers = {}
98}
99
100function init()
101 connect(g_game, {
102 onTalk = onTalk,
103 onChannelList = onChannelList,
104 onOpenChannel = onOpenChannel,
105 onCloseChannel = onCloseChannel,
106 onChannelEvent = onChannelEvent,
107 onOpenPrivateChannel = onOpenPrivateChannel,
108 onOpenOwnPrivateChannel = onOpenOwnPrivateChannel,
109 onRuleViolationChannel = onRuleViolationChannel,
110 onRuleViolationRemove = onRuleViolationRemove,
111 onRuleViolationCancel = onRuleViolationCancel,
112 onRuleViolationLock = onRuleViolationLock,
113 onGameStart = online,
114 onGameEnd = offline,
115 })
116
117 consolePanel = g_ui.loadUI('console', modules.game_interface.getBottomPanel())
118 consolePanel:focus()
119 consoleTextEdit = consolePanel:getChildById('consoleTextEdit')
120 consoleContentPanel = consolePanel:getChildById('consoleContentPanel')
121 consoleTabBar = consolePanel:getChildById('consoleTabBar')
122 consoleTabBar:setContentWidget(consoleContentPanel)
123 channels = {}
124
125 consolePanel.onDragEnter = onDragEnter
126 consolePanel.onDragLeave = onDragLeave
127 consolePanel.onDragMove = onDragMove
128 consoleTabBar.onDragEnter = onDragEnter
129 consoleTabBar.onDragLeave = onDragLeave
130 consoleTabBar.onDragMove = onDragMove
131
132 consolePanel.onKeyPress = function(self, keyCode, keyboardModifiers)
133 if not (keyboardModifiers == KeyboardCtrlModifier and keyCode == KeyC) then return false end
134
135 local tab = consoleTabBar:getCurrentTab()
136 if not tab then return false end
137
138 local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
139 if not selection then return false end
140
141 g_window.setClipboardText(selection)
142 return true
143 end
144
145 g_keyboard.bindKeyPress('Shift+Up', function() navigateMessageHistory(1) end, consolePanel)
146 g_keyboard.bindKeyPress('Shift+Down', function() navigateMessageHistory(-1) end, consolePanel)
147 g_keyboard.bindKeyPress('Tab', function() consoleTabBar:selectNextTab() end, consolePanel)
148 g_keyboard.bindKeyPress('Shift+Tab', function() consoleTabBar:selectPrevTab() end, consolePanel)
149 g_keyboard.bindKeyDown('Enter', sendCurrentMessage, consolePanel)
150 g_keyboard.bindKeyPress('Ctrl+A', function() consoleTextEdit:clearText() end, consolePanel)
151
152 -- apply buttom functions after loaded
153 consoleTabBar:setNavigation(consolePanel:getChildById('prevChannelButton'), consolePanel:getChildById('nextChannelButton'))
154 consoleTabBar.onTabChange = onTabChange
155
156 -- tibia like hotkeys
157 local gameRootPanel = modules.game_interface.getRootPanel()
158 g_keyboard.bindKeyDown('Ctrl+O', g_game.requestChannels, gameRootPanel)
159 g_keyboard.bindKeyDown('Ctrl+E', removeCurrentTab, gameRootPanel)
160 g_keyboard.bindKeyDown('Ctrl+H', openHelp, gameRootPanel)
161
162 consoleToggleChat = consolePanel:getChildById('toggleChat')
163 load()
164
165 if g_game.isOnline() then
166 online()
167 end
168
169end
170
171function onViewChat()
172 return chatView
173end
174function clearSelection(consoleBuffer)
175 for _,label in pairs(consoleBuffer:getChildren()) do
176 label:clearSelection()
177 end
178 consoleBuffer.selectionText = nil
179 consoleBuffer.selection = nil
180end
181
182function selectAll(consoleBuffer)
183 clearSelection(consoleBuffer)
184 if consoleBuffer:getChildCount() > 0 then
185 local text = {}
186 for _,label in pairs(consoleBuffer:getChildren()) do
187 label:selectAll()
188 table.insert(text, label:getSelection())
189 end
190 consoleBuffer.selectionText = table.concat(text, '\n')
191 consoleBuffer.selection = { first = consoleBuffer:getChildIndex(consoleBuffer:getFirstChild()), last = consoleBuffer:getChildIndex(consoleBuffer:getLastChild()) }
192 end
193end
194
195function toggleChat()
196 if consoleToggleChat:isChecked() then
197 disableChat()
198 else
199 enableChat()
200 consolePanel:focus()
201 consolePanel:raise()
202 modules.game_alert.onHideDisplayMsg()
203 end
204end
205
206function chatCheck()
207 return consoleToggleChat:isChecked()
208end
209function sendCurrentMessage()
210 local message = consoleTextEdit:getText()
211 if #message == 0 then
212 chatView = false
213 return
214 end
215 if not isChatEnabled() then return end
216
217 if talkLock then
218 if message:match('!') then
219 consoleTextEdit:clearText()
220 return
221 end
222 end
223 -- send message
224 consoleTextEdit:clearText()
225 sendMessage(message)
226 chatView = true
227end
228
229function onEndNPChat()
230 local npcTab = consoleTabBar:getTab('NPCs')
231 if npcTab then
232 consoleTabBar:removeTab(npcTab)
233 npcTab = nil
234 end
235end
236function enableChat(temporarily)
237 if modules.game_alert.onGetCinematic() then
238 return
239 end
240 if g_app.isMobile() then return end
241 if consoleToggleChat:isChecked() then
242 return consoleToggleChat:setChecked(false)
243 end
244 if not temporarily then
245 modules.client_options.setOption("wsadWalking", false)
246 end
247
248 local npcTab = consoleTabBar:getTab('NPCs')
249 if npcTab then
250 consoleTabBar:removeTab(npcTab)
251 npcTab = nil
252 end
253 consoleTextEdit:setVisible(true)
254 consoleTextEdit:setText("")
255 consoleTextEdit:raise()
256 consoleTextEdit:getFocusedChild()
257 consoleTextEdit:focus()
258 consoleToggleChat:raise()
259 consoleToggleChat:focus()
260 consoleToggleChat:getFocusedChild()
261 consolePanel:focus()
262 consolePanel:raise()
263
264 local gameRootPanel = modules.game_interface.getRootPanel()
265 g_keyboard.unbindKeyDown("Enter", gameRootPanel)
266
267 if temporarily then
268 local quickFunc = function()
269 if not g_game.isOnline() then return end
270 if chatView == false then
271 g_keyboard.unbindKeyDown("Enter", gameRootPanel)
272 g_keyboard.unbindKeyDown("Escape", gameRootPanel)
273 consoleTextEdit:raise()
274 consoleTextEdit:getFocusedChild()
275 consoleTextEdit:focus()
276 consolePanel:focus()
277 consolePanel:raise()
278 consoleToggleChat:raise()
279 consoleToggleChat:focus()
280 consoleToggleChat:getFocusedChild()
281 disableChat(temporarily)
282 end
283 end
284 g_keyboard.bindKeyDown("Enter", quickFunc, gameRootPanel)
285 g_keyboard.bindKeyDown("Escape", quickFunc, gameRootPanel)
286 end
287
288 modules.game_walking.disableWSAD()
289
290 -- Habilitar Chat
291 local gameInterface = modules.game_interface
292 local sayModeButton = consolePanel:getChildById('sayModeButton')
293 gameInterface.getSplitter():addAnchor(AnchorLeft, 'gameMapPanel', AnchorLeft)
294 gameInterface.getSplitter():addAnchor(AnchorRight, 'gameMapPanel', AnchorRight)
295 gameInterface.getSplitter():setMarginBottom(250)
296 consolePanel:setMarginBottom(100)
297 consolePanel:focus()
298 gameInterface.getSplitter():setMarginRight(g_window.getHeight() - 100)
299 sayModeButton:setVisible(true)
300
301 consoleTextEdit:setVisible(true)
302 consoleTextEdit:getFocusedChild()
303 consoleTextEdit:raise()
304 consoleTextEdit:focus()
305
306 consoleToggleChat:setTooltip(tr("Disable chat mode, allow to walk using ASDW"))
307 consoleToggleChat:getFocusedChild()
308 chatView = true
309end
310
311function disableChat(temporarily)
312 if g_app.isMobile() then return end
313 if not consoleToggleChat:isChecked() then
314 return consoleToggleChat:setChecked(true)
315 end
316 if not temporarily then
317 modules.client_options.setOption("wsadWalking", true)
318 end
319
320 local quickFunc = function()
321 if not g_game.isOnline() then return end
322 consoleTextEdit:raise()
323 consoleTextEdit:getFocusedChild()
324 consoleTextEdit:focus()
325 consolePanel:focus()
326 consolePanel:raise()
327 consoleToggleChat:raise()
328 consoleToggleChat:focus()
329 consoleToggleChat:getFocusedChild()
330 if consoleToggleChat:isChecked() then
331 consoleToggleChat:setChecked(false)
332 consoleToggleChat:focus()
333 end
334 enableChat(true)
335 switchMode(true)
336 end
337
338 local gameRootPanel = modules.game_interface.getRootPanel()
339 g_keyboard.bindKeyDown("Enter", quickFunc, gameRootPanel)
340
341 modules.game_walking.enableWSAD()
342
343 -- Ocultar Chat
344 local gameInterface = modules.game_interface
345 local sayModeButton = consolePanel:getChildById('sayModeButton')
346 gameInterface.getSplitter():setMarginBottom(0)
347 sayModeButton:setVisible(false)
348 consoleTextEdit:setVisible(false)
349 consoleTextEdit:getFocusedChild()
350 consoleToggleChat:getFocusedChild()
351 consoleToggleChat:raise()
352 consoleToggleChat:focus()
353 consolePanel:focus()
354
355 consoleToggleChat:setTooltip(tr("Enable chat mode"))
356 consoleTextEdit:raise()
357 consoleTextEdit:focus()
358 chatView = false
359end
360
361function isChatEnabled()
362 return consoleTextEdit:isVisible()
363end
364
365function terminate()
366 save()
367 disconnect(g_game, {
368 onTalk = onTalk,
369 onChannelList = onChannelList,
370 onOpenChannel = onOpenChannel,
371 onOpenPrivateChannel = onOpenPrivateChannel,
372 onOpenOwnPrivateChannel = onOpenPrivateChannel,
373 onCloseChannel = onCloseChannel,
374 onRuleViolationChannel = onRuleViolationChannel,
375 onRuleViolationRemove = onRuleViolationRemove,
376 onRuleViolationCancel = onRuleViolationCancel,
377 onRuleViolationLock = onRuleViolationLock,
378 onGameStart = online,
379 onGameEnd = offline,
380 onChannelEvent = onChannelEvent,
381 })
382
383 if g_game.isOnline() then clear() end
384
385 local gameRootPanel = modules.game_interface.getRootPanel()
386 g_keyboard.unbindKeyDown('Ctrl+O', gameRootPanel)
387 g_keyboard.unbindKeyDown('Ctrl+E', gameRootPanel)
388 g_keyboard.unbindKeyDown('Ctrl+H', gameRootPanel)
389
390 saveCommunicationSettings()
391
392 if channelsWindow then
393 channelsWindow:destroy()
394 end
395
396 if communicationWindow then
397 communicationWindow:destroy()
398 end
399
400 if violationWindow then
401 violationWindow:destroy()
402 end
403
404 consoleTabBar = nil
405 consoleContentPanel = nil
406 consoleToggleChat = nil
407 consoleTextEdit = nil
408
409 consolePanel:destroy()
410 consolePanel = nil
411 ownPrivateName = nil
412
413 Console = nil
414end
415
416function save()
417 local settings = {}
418 settings.messageHistory = messageHistory
419 g_settings.setNode('game_console', settings)
420end
421
422function load()
423 local settings = g_settings.getNode('game_console')
424 if settings then
425 messageHistory = settings.messageHistory or {}
426 end
427 loadCommunicationSettings()
428end
429
430function onTabChange(tabBar, tab)
431 if tab == defaultTab or tab == serverTab then
432 consolePanel:getChildById('closeChannelButton'):disable()
433 else
434 consolePanel:getChildById('closeChannelButton'):enable()
435 end
436end
437
438function clear()
439 -- save last open channels
440 local lastChannelsOpen = g_settings.getNode('lastChannelsOpen') or {}
441 local char = g_game.getCharacterName()
442 local savedChannels = {}
443 local set = false
444 for channelId, channelName in pairs(channels) do
445 if type(channelId) == 'number' then
446 savedChannels[channelName] = channelId
447 set = true
448 end
449 end
450 if set then
451 lastChannelsOpen[char] = savedChannels
452 else
453 lastChannelsOpen[char] = nil
454 end
455 g_settings.setNode('lastChannelsOpen', lastChannelsOpen)
456
457 -- close channels
458 for _, channelName in pairs(channels) do
459 local tab = consoleTabBar:getTab(channelName)
460 consoleTabBar:removeTab(tab)
461 end
462 channels = {}
463
464 consoleTabBar:removeTab(defaultTab)
465 defaultTab = nil
466 consoleTabBar:removeTab(serverTab)
467 serverTab = nil
468
469 local npcTab = consoleTabBar:getTab('NPCs')
470 if npcTab then
471 consoleTabBar:removeTab(npcTab)
472 npcTab = nil
473 end
474
475 if violationReportTab then
476 consoleTabBar:removeTab(violationReportTab)
477 violationReportTab = nil
478 end
479
480 consoleTextEdit:clearText()
481
482 if violationWindow then
483 violationWindow:destroy()
484 violationWindow = nil
485 end
486
487 if channelsWindow then
488 channelsWindow:destroy()
489 channelsWindow = nil
490 end
491end
492
493function switchMode(newView)
494 if newView then
495 consolePanel:setImageColor('#ffffff88')
496 else
497 consolePanel:setImageColor('white')
498 end
499 -- Fijamos el panel para que no sea arrastrable ni se pueda redimensionar
500 consolePanel:setDraggable(false)
501 consoleTabBar:setDraggable(false)
502 floatingMode = false
503end
504
505function onDragEnter(widget, pos)
506 return false
507end
508
509function onDragMove(widget, pos, moved)
510 return false
511end
512
513function onDragLeave(widget, pos)
514 return false
515end
516
517function clearChannel(consoleTabBar)
518 consoleTabBar:getCurrentTab().tabPanel:getChildById('consoleBuffer'):destroyChildren()
519end
520
521function setTextEditText(text)
522 consoleTextEdit:setText(text)
523 consoleTextEdit:setCursorPos(-1)
524end
525
526function openHelp()
527 local helpChannel = 9
528 if g_game.getClientVersion() <= 810 then
529 helpChannel = 8
530 end
531 g_game.joinChannel(helpChannel)
532end
533
534function openPlayerReportRuleViolationWindow()
535 if violationWindow or violationReportTab then return end
536 violationWindow = g_ui.loadUI('violationwindow', rootWidget)
537 violationWindow.onEscape = function()
538 violationWindow:destroy()
539 violationWindow = nil
540 end
541 violationWindow.onEnter = function()
542 local text = violationWindow:getChildById('text'):getText()
543 g_game.talkChannel(MessageModes.RVRChannel, 0, text)
544 violationReportTab = addTab(tr('Report Rule') .. '...', true)
545 addTabText(tr('Please wait patiently for a gamemaster to reply') .. '.', SpeakTypesSettings.privateRed, violationReportTab)
546 addTabText(applyMessagePrefixies(g_game.getCharacterName(), 0, text), SpeakTypesSettings.say, violationReportTab, g_game.getCharacterName())
547 violationReportTab.locked = true
548 violationWindow:destroy()
549 violationWindow = nil
550 end
551end
552
553function addTab(name, focus)
554 local tab = getTab(name)
555 if tab then -- is channel already open
556 if not focus then focus = true end
557 else
558 tab = consoleTabBar:addTab(name, nil, processChannelTabMenu)
559 end
560 if focus then
561 consoleTabBar:selectTab(tab)
562 end
563 return tab
564end
565
566function removeTab(tab)
567 if type(tab) == 'string' then
568 tab = consoleTabBar:getTab(tab)
569 end
570
571 if tab == defaultTab or tab == serverTab then
572 return
573 end
574
575 if tab == violationReportTab then
576 g_game.cancelRuleViolation()
577 violationReportTab = nil
578 elseif tab.violationChatName then
579 g_game.closeRuleViolation(tab.violationChatName)
580 elseif tab.channelId then
581 -- notificate the server that we are leaving the channel
582 for k, v in pairs(channels) do
583 if (k == tab.channelId) then channels[k] = nil end
584 end
585 g_game.leaveChannel(tab.channelId)
586 elseif tab:getText() == "NPCs" then
587 g_game.closeNpcChannel()
588 end
589
590 if getCurrentTab() == tab then
591 consoleTabBar:selectTab(defaultTab)
592 end
593
594 consoleTabBar:removeTab(tab)
595end
596
597function removeCurrentTab()
598 removeTab(consoleTabBar:getCurrentTab())
599end
600
601function getTab(name)
602 return consoleTabBar:getTab(name)
603end
604
605function getChannelTab(channelId)
606 local channel = channels[channelId]
607 if channel then
608 return getTab(channel)
609 end
610 return nil
611end
612
613function getRuleViolationsTab()
614 if violationsChannelId then
615 return getChannelTab(violationsChannelId)
616 end
617 return nil
618end
619
620function getCurrentTab()
621 return consoleTabBar:getCurrentTab()
622end
623
624function addChannel(name, id)
625 channels[id] = name
626 local focus = not table.find(ignoredChannels, id)
627 local tab = addTab(name, focus)
628 tab.channelId = id
629 return tab
630end
631
632function addPrivateChannel(receiver)
633 channels[receiver] = receiver
634 return addTab(receiver, true)
635end
636
637function addPrivateText(text, speaktype, name, isPrivateCommand, creatureName)
638 local focus = false
639 if speaktype.npcChat then
640 name = 'NPCs'
641 focus = true
642 end
643
644 local privateTab = getTab(name)
645 if privateTab == nil then
646 if (modules.client_options.getOption('showPrivateMessagesInConsole') and not focus) or (isPrivateCommand and not privateTab) then
647 privateTab = defaultTab
648 else
649 privateTab = addTab(name, focus)
650 channels[name] = name
651 end
652 privateTab.npcChat = speaktype.npcChat
653 elseif focus then
654 consoleTabBar:selectTab(privateTab)
655 end
656 addTabText(text, speaktype, privateTab, creatureName)
657end
658
659function addText(text, speaktype, tabName, creatureName)
660 local tab = getTab(tabName)
661 if tab ~= nil then
662 addTabText(text, speaktype, tab, creatureName)
663 end
664end
665
666-- Contains letter width for font "verdana-11px-antialised" as console is based on it
667local letterWidth = { -- New line (10) and Space (32) have width 1 because they are printed and not replaced with spacer
668 [10] = 1, [32] = 1, [33] = 3, [34] = 6, [35] = 8, [36] = 7, [37] = 13, [38] = 9, [39] = 3, [40] = 5, [41] = 5, [42] = 6, [43] = 8, [44] = 4, [45] = 5, [46] = 3, [47] = 8,
669 [48] = 7, [49] = 6, [50] = 7, [51] = 7, [52] = 7, [53] = 7, [54] = 7, [55] = 7, [56] = 7, [57] = 7, [58] = 3, [59] = 4, [60] = 8, [61] = 8, [62] = 8, [63] = 6,
670 [64] = 10, [65] = 9, [66] = 7, [67] = 7, [68] = 8, [69] = 7, [70] = 7, [71] = 8, [72] = 8, [73] = 5, [74] = 5, [75] = 7, [76] = 7, [77] = 9, [78] = 8, [79] = 8,
671 [80] = 7, [81] = 8, [82] = 8, [83] = 7, [84] = 8, [85] = 8, [86] = 8, [87] = 12, [88] = 8, [89] = 8, [90] = 7, [91] = 5, [92] = 8, [93] = 5, [94] = 9, [95] = 8,
672 [96] = 5, [97] = 7, [98] = 7, [99] = 6, [100] = 7, [101] = 7, [102] = 5, [103] = 7, [104] = 7, [105] = 3, [106] = 4, [107] = 7, [108] = 3, [109] = 11, [110] = 7,
673 [111] = 7, [112] = 7, [113] = 7, [114] = 6, [115] = 6, [116] = 5, [117] = 7, [118] = 8, [119] = 10, [120] = 8, [121] = 8, [122] = 6, [123] = 7, [124] = 4, [125] = 7, [126] = 8,
674 [127] = 1, [128] = 7, [129] = 6, [130] = 3, [131] = 7, [132] = 6, [133] = 11, [134] = 7, [135] = 7, [136] = 7, [137] = 13, [138] = 7, [139] = 4, [140] = 11, [141] = 6, [142] = 6,
675 [143] = 6, [144] = 6, [145] = 4, [146] = 3, [147] = 7, [148] = 6, [149] = 6, [150] = 7, [151] = 10, [152] = 7, [153] = 10, [154] = 6, [155] = 5, [156] = 11, [157] = 6, [158] = 6,
676 [159] = 8, [160] = 4, [161] = 3, [162] = 7, [163] = 7, [164] = 7, [165] = 8, [166] = 4, [167] = 7, [168] = 6, [169] = 10, [170] = 6, [171] = 8, [172] = 8, [173] = 16, [174] = 10,
677 [175] = 8, [176] = 5, [177] = 8, [178] = 5, [179] = 5, [180] = 6, [181] = 7, [182] = 7, [183] = 3, [184] = 5, [185] = 6, [186] = 6, [187] = 8, [188] = 12, [189] = 12, [190] = 12,
678 [191] = 6, [192] = 9, [193] = 9, [194] = 9, [195] = 9, [196] = 9, [197] = 9, [198] = 11, [199] = 7, [200] = 7, [201] = 7, [202] = 7, [203] = 7, [204] = 5, [205] = 5, [206] = 6,
679 [207] = 5, [208] = 8, [209] = 8, [210] = 8, [211] = 8, [212] = 8, [213] = 8, [214] = 8, [215] = 8, [216] = 8, [217] = 8, [218] = 8, [219] = 8, [220] = 8, [221] = 8, [222] = 7,
680 [223] = 7, [224] = 7, [225] = 7, [226] = 7, [227] = 7, [228] = 7, [229] = 7, [230] = 11, [231] = 6, [232] = 7, [233] = 7, [234] = 7, [235] = 7, [236] = 3, [237] = 4, [238] = 4,
681 [239] = 4, [240] = 7, [241] = 7, [242] = 7, [243] = 7, [244] = 7, [245] = 7, [246] = 7, [247] = 9, [248] = 7, [249] = 7, [250] = 7, [251] = 7, [252] = 7, [253] = 8, [254] = 7, [255] = 8
682}
683
684-- Return information about start, end in the string and the highlighted words
685function getHighlightedText(text)
686 local tmpData = {}
687
688 repeat
689 local tmp = {string.find(text, "{([^}]+)}", tmpData[#tmpData-1])}
690 for _, v in pairs(tmp) do
691 table.insert(tmpData, v)
692 end
693 until not(string.find(text, "{([^}]+)}", tmpData[#tmpData-1]))
694
695 return tmpData
696end
697
698function getNewHighlightedText(text, color, highlightColor)
699 local tmpData = {}
700
701 for i, part in ipairs(text:split("{")) do
702 if i == 1 then
703 table.insert(tmpData, part)
704 table.insert(tmpData, color)
705 else
706 for j, part2 in ipairs(part:split("}")) do
707 if j == 1 then
708 table.insert(tmpData, part2)
709 table.insert(tmpData, highlightColor)
710 else
711 table.insert(tmpData, part2)
712 table.insert(tmpData, color)
713 end
714 end
715 end
716 end
717
718 return tmpData
719end
720
721function addTabText(text, speaktype, tab, creatureName)
722 if not tab or tab.locked or not text or #text == 0 then return end
723 if speaktype.color == "#f36500" or speaktype.color == "#9f9dfd" then
724 if g_app.isMobile() then
725 MOBILE_EXIT_MENU = modules.game_interface.onMobileWindow(24)
726 if MOBILE_EXIT_MENU:isVisible() then
727 else
728 modules.game_alert.onDisplayMsg(text, speaktype.color)
729 end
730 else
731 if chatCheck() == false then
732 else
733 modules.game_alert.onDisplayMsg(text, speaktype.color)
734 end
735 end
736 end
737 if modules.client_options.getOption('showTimestampsInConsole') then
738 text = os.date('%H:%M') .. ' ' .. text
739 end
740
741 local panel = consoleTabBar:getTabPanel(tab)
742 local consoleBuffer = panel:getChildById('consoleBuffer')
743
744 local label = nil
745 if consoleBuffer:getChildCount() > MAX_LINES then
746 label = consoleBuffer:getFirstChild()
747 consoleBuffer:moveChildToIndex(label, consoleBuffer:getChildCount())
748 end
749
750 if not label then
751 label = g_ui.createWidget('ConsoleLabel', consoleBuffer)
752 end
753 label:setId('consoleLabel' .. consoleBuffer:getChildCount())
754 label:setText(text)
755 label:setColor(speaktype.color)
756 consoleTabBar:blinkTab(tab)
757
758 if speaktype.npcChat and (g_game.getCharacterName() ~= creatureName or g_game.getCharacterName() == 'Account Manager') then
759 local highlightData = getNewHighlightedText(text, speaktype.color, "#1f9ffe")
760 if #highlightData > 2 then
761 label:setColoredText(highlightData)
762 end
763 end
764
765 label.name = creatureName
766 consoleBuffer.onMouseRelease = function(self, mousePos, mouseButton)
767 processMessageMenu(mousePos, mouseButton, nil, nil, nil, tab)
768 end
769 label.onMouseRelease = function(self, mousePos, mouseButton)
770 processMessageMenu(mousePos, mouseButton, creatureName, text, self, tab)
771 end
772 label.onMousePress = function(self, mousePos, button)
773 if button == MouseLeftButton then clearSelection(consoleBuffer) end
774 end
775 label.onDragEnter = function(self, mousePos)
776 clearSelection(consoleBuffer)
777 return true
778 end
779 label.onDragLeave = function(self, droppedWidget, mousePos)
780 local text = {}
781 for selectionChild = consoleBuffer.selection.first, consoleBuffer.selection.last do
782 local label = self:getParent():getChildByIndex(selectionChild)
783 table.insert(text, label:getSelection())
784 end
785 consoleBuffer.selectionText = table.concat(text, '\n')
786 return true
787 end
788 label.onDragMove = function(self, mousePos, mouseMoved)
789 local parent = self:getParent()
790 local parentRect = parent:getPaddingRect()
791 local selfIndex = parent:getChildIndex(self)
792 local child = parent:getChildByPos(mousePos)
793
794 -- find bonding children
795 if not child then
796 if mousePos.y < self:getY() then
797 for index = selfIndex - 1, 1, -1 do
798 local label = parent:getChildByIndex(index)
799 if label:getY() + label:getHeight() > parentRect.y then
800 if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == 1 then
801 child = label
802 break
803 end
804 else
805 child = parent:getChildByIndex(index + 1)
806 break
807 end
808 end
809 elseif mousePos.y > self:getY() + self:getHeight() then
810 for index = selfIndex + 1, parent:getChildCount(), 1 do
811 local label = parent:getChildByIndex(index)
812 if label:getY() < parentRect.y + parentRect.height then
813 if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == parent:getChildCount() then
814 child = label
815 break
816 end
817 else
818 child = parent:getChildByIndex(index - 1)
819 break
820 end
821 end
822 else
823 child = self
824 end
825 end
826
827 if not child then return false end
828
829 local childIndex = parent:getChildIndex(child)
830
831 -- remove old selection
832 clearSelection(consoleBuffer)
833
834 -- update self selection
835 local textBegin = self:getTextPos(self:getLastClickPosition())
836 local textPos = self:getTextPos(mousePos)
837 self:setSelection(textBegin, textPos)
838
839 consoleBuffer.selection = { first = math.min(selfIndex, childIndex), last = math.max(selfIndex, childIndex) }
840
841 -- update siblings selection
842 if child ~= self then
843 for selectionChild = consoleBuffer.selection.first + 1, consoleBuffer.selection.last - 1 do
844 parent:getChildByIndex(selectionChild):selectAll()
845 end
846
847 local textPos = child:getTextPos(mousePos)
848 if childIndex > selfIndex then
849 child:setSelection(0, textPos)
850 else
851 child:setSelection(string.len(child:getText()), textPos)
852 end
853 end
854
855 return true
856 end
857end
858
859function removeTabLabelByName(tab, name)
860 local panel = consoleTabBar:getTabPanel(tab)
861 local consoleBuffer = panel:getChildById('consoleBuffer')
862 for _,label in pairs(consoleBuffer:getChildren()) do
863 if label.name == name then
864 label:destroy()
865 end
866 end
867end
868
869function processChannelTabMenu(tab, mousePos, mouseButton)
870 local menu = g_ui.createWidget('PopupMenu')
871 menu:setGameMenu(true)
872
873 local worldName = g_game.getWorldName()
874 local characterName = g_game.getCharacterName()
875 channelName = tab:getText()
876 if tab ~= defaultTab and tab ~= serverTab then
877 menu:addOption(tr('Close'), function() removeTab(channelName) end)
878 --menu:addOption(tr('Show Server Messages'), function() --[[TODO]] end)
879 menu:addSeparator()
880 end
881
882 if consoleTabBar:getCurrentTab() == tab then
883 menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end)
884 menu:addOption(tr('Save Messages'), function()
885 local panel = consoleTabBar:getTabPanel(tab)
886 local consoleBuffer = panel:getChildById('consoleBuffer')
887 local lines = {}
888 for _,label in pairs(consoleBuffer:getChildren()) do
889 table.insert(lines, label:getText())
890 end
891
892 local filename = worldName .. ' - ' .. characterName .. ' - ' .. channelName .. '.txt'
893 local filepath = '/user_dir/' .. filename
894
895 -- extra information at the beginning
896 table.insert(lines, 1, os.date('\nChannel saved at %a %b %d %H:%M:%S %Y'))
897
898 if g_resources.fileExists(filepath) then
899 table.insert(lines, 1, protectedcall(g_resources.readFileContents, filepath) or '')
900 end
901
902 g_resources.writeFileContents(filepath, table.concat(lines, '\n'))
903 modules.game_textmessage.displayStatusMessage(tr('Channel appended to %s', filename))
904 end)
905 end
906
907 menu:display(mousePos)
908end
909
910function processMessageMenu(mousePos, mouseButton, creatureName, text, label, tab)
911 if mouseButton == MouseRightButton then
912 local menu = g_ui.createWidget('PopupMenu')
913 menu:setGameMenu(true)
914 if creatureName and #creatureName > 0 then
915 if creatureName ~= g_game.getCharacterName() then
916 menu:addOption(tr('Message to ' .. creatureName), function () g_game.openPrivateChannel(creatureName) end)
917 if not g_game.getLocalPlayer():hasVip(creatureName) then
918 menu:addOption(tr('Add to VIP list'), function () g_game.addVip(creatureName) end)
919 end
920 if modules.game_console.getOwnPrivateTab() then
921 menu:addSeparator()
922 menu:addOption(tr('Invite to private chat'), function() g_game.inviteToOwnChannel(creatureName) end)
923 menu:addOption(tr('Exclude from private chat'), function() g_game.excludeFromOwnChannel(creatureName) end)
924 end
925 if isIgnored(creatureName) then
926 menu:addOption(tr('Unignore') .. ' ' .. creatureName, function() removeIgnoredPlayer(creatureName) end)
927 else
928 menu:addOption(tr('Ignore') .. ' ' .. creatureName, function() addIgnoredPlayer(creatureName) end)
929 end
930 menu:addSeparator()
931 end
932 if modules.game_ruleviolation.hasWindowAccess() then
933 menu:addOption(tr('Rule Violation'), function() modules.game_ruleviolation.show(creatureName, text:match('.+%:%s(.+)')) end)
934 menu:addSeparator()
935 end
936
937 menu:addOption(tr('Copy name'), function () g_window.setClipboardText(creatureName) end)
938 end
939 local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText
940 if selection and #selection > 0 then
941 menu:addOption(tr('Copy'), function() g_window.setClipboardText(selection) end, '(Ctrl+C)')
942 end
943 if text then
944 menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end)
945 end
946 menu:addOption(tr('Select all'), function() selectAll(tab.tabPanel:getChildById('consoleBuffer')) end)
947 if tab.violations and creatureName then
948 menu:addSeparator()
949 menu:addOption(tr('Process') .. ' ' .. creatureName, function() processViolation(creatureName, text) end)
950 menu:addOption(tr('Remove') .. ' ' .. creatureName, function() g_game.closeRuleViolation(creatureName) end)
951 end
952 menu:display(mousePos)
953 end
954end
955
956function addFilter(filter)
957 table.insert(filters, filter)
958end
959
960function removeFilter(filter)
961 table.removevalue(filters, filter)
962end
963
964function sendMessage(message, tab)
965 local tab = tab or getCurrentTab()
966 if not tab then return end
967
968 for k,func in pairs(filters) do
969 if func(message) then
970 return true
971 end
972 end
973
974 -- when talking on server log, the message goes to default channel
975 local name = tab:getText()
976 if tab == serverTab or tab == getRuleViolationsTab() then
977 tab = defaultTab
978 name = defaultTab:getText()
979 end
980
981 -- handling chat commands
982 local channel = tab.channelId
983 local originalMessage = message
984 local chatCommandSayMode
985 local chatCommandPrivate
986 local chatCommandPrivateReady
987 local chatCommandMessage
988
989 -- player used yell command
990 chatCommandMessage = message:match("^%#[y|Y] (.*)")
991 if chatCommandMessage ~= nil then
992 chatCommandSayMode = 'yell'
993 channel = 0
994 message = chatCommandMessage
995 end
996
997 -- player used whisper
998 chatCommandMessage = message:match("^%#[w|W] (.*)")
999 if chatCommandMessage ~= nil then
1000 chatCommandSayMode = 'whisper'
1001 message = chatCommandMessage
1002 channel = 0
1003 end
1004
1005 -- player say
1006 chatCommandMessage = message:match("^%#[s|S] (.*)")
1007 if chatCommandMessage ~= nil then
1008 chatCommandSayMode = 'say'
1009 message = chatCommandMessage
1010 channel = 0
1011 end
1012
1013 -- player red talk on channel
1014 chatCommandMessage = message:match("^%#[c|C] (.*)")
1015 if chatCommandMessage ~= nil then
1016 chatCommandSayMode = 'channelRed'
1017 message = chatCommandMessage
1018 end
1019
1020 -- player broadcast
1021 chatCommandMessage = message:match("^%#[b|B] (.*)")
1022 if chatCommandMessage ~= nil then
1023 chatCommandSayMode = 'broadcast'
1024 message = chatCommandMessage
1025 channel = 0
1026 end
1027
1028 local findIni, findEnd, chatCommandInitial, chatCommandPrivate, chatCommandEnd, chatCommandMessage = message:find("([%*%@])(.+)([%*%@])(.*)")
1029 if findIni ~= nil and findIni == 1 then -- player used private chat command
1030 if chatCommandInitial == chatCommandEnd then
1031 chatCommandPrivateRepeat = false
1032 if chatCommandInitial == "*" then
1033 setTextEditText('*'.. chatCommandPrivate .. '* ')
1034 end
1035 message = chatCommandMessage:trim()
1036 chatCommandPrivateReady = true
1037 end
1038 end
1039
1040 message = message:gsub("^(%s*)(.*)","%2") -- remove space characters from message init
1041 if #message == 0 then return end
1042
1043 -- add new command to history
1044 currentMessageIndex = 0
1045 if #messageHistory == 0 or messageHistory[#messageHistory] ~= originalMessage then
1046 table.insert(messageHistory, originalMessage)
1047 if #messageHistory > MAX_HISTORY then
1048 table.remove(messageHistory, 1)
1049 end
1050 end
1051
1052 local speaktypedesc
1053 if (channel or tab == defaultTab) and not chatCommandPrivateReady then
1054 if tab == defaultTab then
1055 speaktypedesc = chatCommandSayMode or SayModes[consolePanel:getChildById('sayModeButton').sayMode].speakTypeDesc
1056 if speaktypedesc ~= 'say' then sayModeChange(2) end -- head back to say mode
1057 else
1058 speaktypedesc = chatCommandSayMode or 'channelYellow'
1059 end
1060
1061 g_game.talkChannel(SpeakTypesSettings[speaktypedesc].speakType, channel, message)
1062 return
1063 else
1064 local isPrivateCommand = false
1065 local priv = true
1066 local tabname = name
1067 local dontAdd = false
1068 if chatCommandPrivateReady then
1069 speaktypedesc = 'privatePlayerToPlayer'
1070 name = chatCommandPrivate
1071 isPrivateCommand = true
1072 elseif tab.npcChat then
1073 speaktypedesc = 'privatePlayerToNpc'
1074 elseif tab == violationReportTab then
1075 if violationReportTab.locked then
1076 modules.game_textmessage.displayFailureMessage('Wait for a gamemaster reply.')
1077 dontAdd = true
1078 else
1079 speaktypedesc = 'rvrContinue'
1080 tabname = tr('Report Rule') .. '...'
1081 end
1082 elseif tab.violationChatName then
1083 speaktypedesc = 'rvrAnswerTo'
1084 name = tab.violationChatName
1085 tabname = tab.violationChatName .. '\'...'
1086 else
1087 speaktypedesc = 'privatePlayerToPlayer'
1088 end
1089
1090
1091 local speaktype = SpeakTypesSettings[speaktypedesc]
1092 local player = g_game.getLocalPlayer()
1093 g_game.talkPrivate(speaktype.speakType, name, message)
1094 if not dontAdd then
1095 message = applyMessagePrefixies(g_game.getCharacterName(), player:getLevel(), message)
1096 addPrivateText(message, speaktype, tabname, isPrivateCommand, g_game.getCharacterName())
1097 end
1098 end
1099end
1100
1101function sayModeChange(sayMode)
1102 local buttom = consolePanel:getChildById('sayModeButton')
1103 if sayMode == nil then
1104 sayMode = buttom.sayMode + 1
1105 end
1106
1107 if sayMode > #SayModes then sayMode = 1 end
1108
1109 buttom:setIcon(SayModes[sayMode].icon)
1110 buttom.sayMode = sayMode
1111end
1112
1113function getOwnPrivateTab()
1114 if not ownPrivateName then return end
1115 return getTab(ownPrivateName)
1116end
1117
1118function setIgnoreNpcMessages(ignore)
1119 ignoreNpcMessages = ignore
1120end
1121
1122function navigateMessageHistory(step)
1123 if not isChatEnabled() then
1124 return
1125 end
1126
1127 local numCommands = #messageHistory
1128 if numCommands > 0 then
1129 currentMessageIndex = math.min(math.max(currentMessageIndex + step, 0), numCommands)
1130 if currentMessageIndex > 0 then
1131 local command = messageHistory[numCommands - currentMessageIndex + 1]
1132 setTextEditText(command)
1133 else
1134 consoleTextEdit:clearText()
1135 end
1136 end
1137 local player = g_game.getLocalPlayer()
1138 if player then
1139 player:lockWalk(200) -- lock walk for 200 ms to avoid walk during release of shift
1140 end
1141end
1142
1143function applyMessagePrefixies(name, level, message)
1144 if name and #name > 0 then
1145 if modules.client_options.getOption('showLevelsInConsole') and level > 0 then
1146 message = name .. ' [' .. level .. ']: ' .. message
1147 else
1148 message = name .. ': ' .. message
1149 end
1150 end
1151 return message
1152end
1153
1154function onTalk(name, level, mode, message, channelId, creaturePos)
1155 if mode == MessageModes.GamemasterBroadcast then
1156 modules.game_textmessage.displayBroadcastMessage(name .. ': ' .. message)
1157 return
1158 end
1159
1160 local isNpcMode = (mode == MessageModes.NpcFromStartBlock or mode == MessageModes.NpcFrom)
1161
1162 if ignoreNpcMessages and isNpcMode then return end
1163
1164 speaktype = SpeakTypes[mode]
1165
1166 if not speaktype then
1167 perror('unhandled onTalk message mode ' .. mode .. ': ' .. message)
1168 return
1169 end
1170
1171 local localPlayer = g_game.getLocalPlayer()
1172 if name ~= g_game.getCharacterName()
1173 and isUsingIgnoreList()
1174 and not(isUsingWhiteList()) or (isUsingWhiteList() and not(isWhitelisted(name)) and not(isAllowingVIPs() and localPlayer:hasVip(name))) then
1175
1176 if mode == MessageModes.Yell and isIgnoringYelling() then
1177 return
1178 elseif speaktype.private and isIgnoringPrivate() and not isNpcMode then
1179 return
1180 elseif isIgnored(name) then
1181 return
1182 end
1183 end
1184
1185 if mode == MessageModes.RVRChannel then
1186 channelId = violationsChannelId
1187 end
1188
1189 if (mode == MessageModes.Say or mode == MessageModes.Whisper or mode == MessageModes.Yell or
1190 mode == MessageModes.Spell or mode == MessageModes.MonsterSay or mode == MessageModes.MonsterYell or
1191 mode == MessageModes.NpcFrom or mode == MessageModes.BarkLow or mode == MessageModes.BarkLoud or
1192 mode == MessageModes.NpcFromStartBlock) and creaturePos then
1193 local staticText = StaticText.create()
1194 -- Remove curly braces from screen message
1195 local staticMessage = message
1196 if isNpcMode then
1197 local highlightData = getNewHighlightedText(staticMessage, speaktype.color, "#1f9ffe")
1198 if #highlightData > 2 then
1199 staticText:addColoredMessage(name, mode, highlightData)
1200 else
1201 staticText:addMessage(name, mode, staticMessage)
1202 end
1203 staticText:setColor(speaktype.color)
1204 else
1205 staticText:addMessage(name, mode, staticMessage)
1206 end
1207 g_map.addThing(staticText, creaturePos, -1)
1208 end
1209
1210 local defaultMessage = mode <= 3 and true or false
1211
1212 if speaktype == SpeakTypesSettings.none then return end
1213
1214 if speaktype.hideInConsole then return end
1215
1216 local composedMessage = applyMessagePrefixies(name, level, message)
1217
1218 if mode == MessageModes.RVRAnswer then
1219 violationReportTab.locked = false
1220 addTabText(composedMessage, speaktype, violationReportTab, name)
1221 elseif mode == MessageModes.RVRContinue then
1222 addText(composedMessage, speaktype, name .. '\'...', name)
1223 elseif speaktype.private then
1224 addPrivateText(composedMessage, speaktype, name, false, name)
1225 if modules.client_options.getOption('showPrivateMessagesOnScreen') and speaktype ~= SpeakTypesSettings.privateNpcToPlayer then
1226 modules.game_textmessage.displayPrivateMessage(name .. ':\n' .. message)
1227 end
1228 else
1229 local channel = tr('Default')
1230 if not defaultMessage then
1231 channel = channels[channelId]
1232 end
1233
1234 if channel then
1235 addText(composedMessage, speaktype, channel, name)
1236 else
1237 -- server sent a message on a channel that is not open
1238 pwarning('message in channel id ' .. channelId .. ' which is unknown, this is a server bug, relogin if you want to see messages in this channel')
1239 end
1240 end
1241end
1242
1243function onOpenChannel(channelId, channelName)
1244 addChannel(channelName, channelId)
1245end
1246
1247function onOpenPrivateChannel(receiver)
1248 addPrivateChannel(receiver)
1249end
1250
1251function onOpenOwnPrivateChannel(channelId, channelName)
1252 local privateTab = getTab(channelName)
1253 if privateTab == nil then
1254 addChannel(channelName, channelId)
1255 end
1256 ownPrivateName = channelName
1257end
1258
1259function onCloseChannel(channelId)
1260 local channel = channels[channelId]
1261 if channel then
1262 local tab = getTab(channel)
1263 if tab then
1264 consoleTabBar:removeTab(tab)
1265 end
1266 for k, v in pairs(channels) do
1267 if (k == tab.channelId) then channels[k] = nil end
1268 end
1269 end
1270end
1271
1272function processViolation(name, text)
1273 local tabname = name .. '\'...'
1274 local tab = addTab(tabname, true)
1275 channels[tabname] = tabname
1276 tab.violationChatName = name
1277 g_game.openRuleViolation(name)
1278 addTabText(text, SpeakTypesSettings.say, tab, name)
1279end
1280
1281function onRuleViolationChannel(channelId)
1282 violationsChannelId = channelId
1283 local tab = addChannel(tr('Rule Violations'), channelId)
1284 tab.violations = true
1285end
1286
1287function onRuleViolationRemove(name)
1288 local tab = getRuleViolationsTab()
1289 if not tab then return end
1290 removeTabLabelByName(tab, name)
1291end
1292
1293function onRuleViolationCancel(name)
1294 local tab = getTab(name .. '\'...')
1295 if not tab then return end
1296 addTabText(tr('%s has finished the request', name) .. '.', SpeakTypesSettings.privateRed, tab)
1297 tab.locked = true
1298end
1299
1300function onRuleViolationLock()
1301 if not violationReportTab then return end
1302 violationReportTab.locked = false
1303 addTabText(tr('Your request has been closed') .. '.', SpeakTypesSettings.privateRed, violationReportTab)
1304 violationReportTab.locked = true
1305end
1306
1307function doChannelListSubmit()
1308 local channelListPanel = channelsWindow:getChildById('channelList')
1309 local openPrivateChannelWith = channelsWindow:getChildById('openPrivateChannelWith'):getText()
1310 if openPrivateChannelWith ~= '' then
1311 if openPrivateChannelWith:lower() ~= g_game.getCharacterName():lower() then
1312 g_game.openPrivateChannel(openPrivateChannelWith)
1313 else
1314 modules.game_textmessage.displayFailureMessage('You cannot create a private chat channel with yourself.')
1315 end
1316 else
1317 local selectedChannelLabel = channelListPanel:getFocusedChild()
1318 if not selectedChannelLabel then return end
1319 if selectedChannelLabel.channelId == 0xFFFF then
1320 g_game.openOwnChannel()
1321 else
1322 g_game.leaveChannel(selectedChannelLabel.channelId)
1323 g_game.joinChannel(selectedChannelLabel.channelId)
1324 end
1325 end
1326
1327 channelsWindow:destroy()
1328end
1329
1330function onChannelList(channelList)
1331 if channelsWindow then channelsWindow:destroy() end
1332 channelsWindow = g_ui.displayUI('channelswindow')
1333 local channelListPanel = channelsWindow:getChildById('channelList')
1334 channelsWindow.onEnter = doChannelListSubmit
1335 channelsWindow.onDestroy = function() channelsWindow = nil end
1336 g_keyboard.bindKeyPress('Down', function() channelListPanel:focusNextChild(KeyboardFocusReason) end, channelsWindow)
1337 g_keyboard.bindKeyPress('Up', function() channelListPanel:focusPreviousChild(KeyboardFocusReason) end, channelsWindow)
1338
1339 for k,v in pairs(channelList) do
1340 local channelId = v[1]
1341 local channelName = v[2]
1342
1343 if #channelName > 0 then
1344 local label = g_ui.createWidget('ChannelListLabel', channelListPanel)
1345 label.channelId = channelId
1346 label:setText(channelName)
1347
1348 label:setPhantom(false)
1349 label.onDoubleClick = doChannelListSubmit
1350 end
1351 end
1352end
1353
1354function loadCommunicationSettings()
1355 communicationSettings.whitelistedPlayers = {}
1356 communicationSettings.ignoredPlayers = {}
1357
1358 local ignoreNode = g_settings.getNode('IgnorePlayers')
1359 if ignoreNode then
1360 for _, player in pairs(ignoreNode) do
1361 table.insert(communicationSettings.ignoredPlayers, player)
1362 end
1363 end
1364
1365 local whitelistNode = g_settings.getNode('WhitelistedPlayers')
1366 if whitelistNode then
1367 for _, player in pairs(whitelistNode) do
1368 table.insert(communicationSettings.whitelistedPlayers, player)
1369 end
1370 end
1371
1372 communicationSettings.useIgnoreList = g_settings.getBoolean('UseIgnoreList')
1373 communicationSettings.useWhiteList = g_settings.getBoolean('UseWhiteList')
1374 communicationSettings.privateMessages = g_settings.getBoolean('IgnorePrivateMessages')
1375 communicationSettings.yelling = g_settings.getBoolean('IgnoreYelling')
1376 communicationSettings.allowVIPs = g_settings.getBoolean('AllowVIPs')
1377end
1378
1379function saveCommunicationSettings()
1380 local tmpIgnoreList = {}
1381 local ignoredPlayers = getIgnoredPlayers()
1382 for i = 1, #ignoredPlayers do
1383 table.insert(tmpIgnoreList, ignoredPlayers[i])
1384 end
1385
1386 local tmpWhiteList = {}
1387 local whitelistedPlayers = getWhitelistedPlayers()
1388 for i = 1, #whitelistedPlayers do
1389 table.insert(tmpWhiteList, whitelistedPlayers[i])
1390 end
1391
1392 g_settings.set('UseIgnoreList', communicationSettings.useIgnoreList)
1393 g_settings.set('UseWhiteList', communicationSettings.useWhiteList)
1394 g_settings.set('IgnorePrivateMessages', communicationSettings.privateMessages)
1395 g_settings.set('IgnoreYelling', communicationSettings.yelling)
1396 g_settings.setNode('IgnorePlayers', tmpIgnoreList)
1397 g_settings.setNode('WhitelistedPlayers', tmpWhiteList)
1398end
1399
1400function getIgnoredPlayers()
1401 return communicationSettings.ignoredPlayers
1402end
1403
1404function getWhitelistedPlayers()
1405 return communicationSettings.whitelistedPlayers
1406end
1407
1408function isUsingIgnoreList()
1409 return communicationSettings.useIgnoreList
1410end
1411
1412function isUsingWhiteList()
1413 return communicationSettings.useWhiteList
1414end
1415function isIgnored(name)
1416 return table.find(communicationSettings.ignoredPlayers, name, true)
1417end
1418
1419function addIgnoredPlayer(name)
1420 if isIgnored(name) then return end
1421 table.insert(communicationSettings.ignoredPlayers, name)
1422 communicationSettings.useIgnoreList = true
1423end
1424
1425function removeIgnoredPlayer(name)
1426 table.removevalue(communicationSettings.ignoredPlayers, name)
1427end
1428
1429function isWhitelisted(name)
1430 return table.find(communicationSettings.whitelistedPlayers, name, true)
1431end
1432
1433function addWhitelistedPlayer(name)
1434 if isWhitelisted(name) then return end
1435 table.insert(communicationSettings.whitelistedPlayers, name)
1436end
1437
1438function removeWhitelistedPlayer(name)
1439 table.removevalue(communicationSettings.whitelistedPlayers, name)
1440end
1441
1442function isIgnoringPrivate()
1443 return communicationSettings.privateMessages
1444end
1445
1446function isIgnoringYelling()
1447 return communicationSettings.yelling
1448end
1449
1450function isAllowingVIPs()
1451 return communicationSettings.allowVIPs
1452end
1453
1454function onClickIgnoreButton()
1455 if communicationWindow then return end
1456 communicationWindow = g_ui.displayUI('communicationwindow')
1457 local ignoreListPanel = communicationWindow:getChildById('ignoreList')
1458 local whiteListPanel = communicationWindow:getChildById('whiteList')
1459 communicationWindow.onDestroy = function() communicationWindow = nil end
1460
1461 local useIgnoreListBox = communicationWindow:getChildById('checkboxUseIgnoreList')
1462 useIgnoreListBox:setChecked(communicationSettings.useIgnoreList)
1463 local useWhiteListBox = communicationWindow:getChildById('checkboxUseWhiteList')
1464 useWhiteListBox:setChecked(communicationSettings.useWhiteList)
1465
1466 local removeIgnoreButton = communicationWindow:getChildById('buttonIgnoreRemove')
1467 removeIgnoreButton:disable()
1468 ignoreListPanel.onChildFocusChange = function() removeIgnoreButton:enable() end
1469 removeIgnoreButton.onClick = function()
1470 local selection = ignoreListPanel:getFocusedChild()
1471 if selection then
1472 ignoreListPanel:removeChild(selection)
1473 selection:destroy()
1474 end
1475 removeIgnoreButton:disable()
1476 end
1477
1478 local removeWhitelistButton = communicationWindow:getChildById('buttonWhitelistRemove')
1479 removeWhitelistButton:disable()
1480 whiteListPanel.onChildFocusChange = function() removeWhitelistButton:enable() end
1481 removeWhitelistButton.onClick = function()
1482 local selection = whiteListPanel:getFocusedChild()
1483 if selection then
1484 whiteListPanel:removeChild(selection)
1485 selection:destroy()
1486 end
1487 removeWhitelistButton:disable()
1488 end
1489
1490 local newlyIgnoredPlayers = {}
1491 local addIgnoreName = communicationWindow:getChildById('ignoreNameEdit')
1492 local addIgnoreButton = communicationWindow:getChildById('buttonIgnoreAdd')
1493 local addIgnoreFunction = function()
1494 local newEntry = addIgnoreName:getText()
1495 if newEntry == '' then return end
1496 if table.find(getIgnoredPlayers(), newEntry) then return end
1497 if table.find(newlyIgnoredPlayers, newEntry) then return end
1498 local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
1499 label:setText(newEntry)
1500 table.insert(newlyIgnoredPlayers, newEntry)
1501 addIgnoreName:setText('')
1502 end
1503 addIgnoreButton.onClick = addIgnoreFunction
1504
1505 local newlyWhitelistedPlayers = {}
1506 local addWhitelistName = communicationWindow:getChildById('whitelistNameEdit')
1507 local addWhitelistButton = communicationWindow:getChildById('buttonWhitelistAdd')
1508 local addWhitelistFunction = function()
1509 local newEntry = addWhitelistName:getText()
1510 if newEntry == '' then return end
1511 if table.find(getWhitelistedPlayers(), newEntry) then return end
1512 if table.find(newlyWhitelistedPlayers, newEntry) then return end
1513 local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
1514 label:setText(newEntry)
1515 table.insert(newlyWhitelistedPlayers, newEntry)
1516 addWhitelistName:setText('')
1517 end
1518 addWhitelistButton.onClick = addWhitelistFunction
1519
1520 communicationWindow.onEnter = function()
1521 if addWhitelistName:isFocused() then
1522 addWhitelistFunction()
1523 elseif addIgnoreName:isFocused() then
1524 addIgnoreFunction()
1525 end
1526 end
1527
1528 local ignorePrivateMessageBox = communicationWindow:getChildById('checkboxIgnorePrivateMessages')
1529 ignorePrivateMessageBox:setChecked(communicationSettings.privateMessages)
1530 local ignoreYellingBox = communicationWindow:getChildById('checkboxIgnoreYelling')
1531 ignoreYellingBox:setChecked(communicationSettings.yelling)
1532 local allowVIPsBox = communicationWindow:getChildById('checkboxAllowVIPs')
1533 allowVIPsBox:setChecked(communicationSettings.allowVIPs)
1534
1535 local saveButton = communicationWindow:recursiveGetChildById('buttonSave')
1536 saveButton.onClick = function()
1537 communicationSettings.ignoredPlayers = {}
1538 for i = 1, ignoreListPanel:getChildCount() do
1539 addIgnoredPlayer(ignoreListPanel:getChildByIndex(i):getText())
1540 end
1541
1542 communicationSettings.whitelistedPlayers = {}
1543 for i = 1, whiteListPanel:getChildCount() do
1544 addWhitelistedPlayer(whiteListPanel:getChildByIndex(i):getText())
1545 end
1546
1547 communicationSettings.useIgnoreList = useIgnoreListBox:isChecked()
1548 communicationSettings.useWhiteList = useWhiteListBox:isChecked()
1549 communicationSettings.yelling = ignoreYellingBox:isChecked()
1550 communicationSettings.privateMessages = ignorePrivateMessageBox:isChecked()
1551 communicationSettings.allowVIPs = allowVIPsBox:isChecked()
1552 communicationWindow:destroy()
1553 end
1554
1555 local cancelButton = communicationWindow:recursiveGetChildById('buttonCancel')
1556 cancelButton.onClick = function()
1557 communicationWindow:destroy()
1558 end
1559
1560 local ignoredPlayers = getIgnoredPlayers()
1561 for i = 1, #ignoredPlayers do
1562 local label = g_ui.createWidget('IgnoreListLabel', ignoreListPanel)
1563 label:setText(ignoredPlayers[i])
1564 end
1565
1566 local whitelistedPlayers = getWhitelistedPlayers()
1567 for i = 1, #whitelistedPlayers do
1568 local label = g_ui.createWidget('WhiteListLabel', whiteListPanel)
1569 label:setText(whitelistedPlayers[i])
1570 end
1571end
1572
1573function online()
1574 defaultTab = addTab(tr('Default'), true)
1575 serverTab = addTab(tr('Server Log'), false)
1576
1577 disableChat(temporarily)
1578 if g_game.getClientVersion() >= 820 then
1579 local tab = addTab("NPCs", false)
1580 --tab.npcChat = true
1581 end
1582
1583 if g_game.getClientVersion() < 862 then
1584 local gameRootPanel = modules.game_interface.getRootPanel()
1585 g_keyboard.bindKeyDown('Ctrl+R', openPlayerReportRuleViolationWindow, gameRootPanel)
1586 end
1587 -- open last channels
1588 local lastChannelsOpen = g_settings.getNode('lastChannelsOpen')
1589 if lastChannelsOpen then
1590 local savedChannels = lastChannelsOpen[g_game.getCharacterName()]
1591 if savedChannels then
1592 for channelName, channelId in pairs(savedChannels) do
1593 channelId = tonumber(channelId)
1594 if channelId ~= -1 and channelId < 100 then
1595 if not table.find(channels, channelId) then
1596 g_game.joinChannel(channelId)
1597 table.insert(ignoredChannels, channelId)
1598 end
1599 end
1600 end
1601 end
1602 end
1603 scheduleEvent(function() consoleTabBar:selectTab(defaultTab) end, 500)
1604 scheduleEvent(function() ignoredChannels = {} end, 3000)
1605end
1606
1607function offline()
1608 if g_game.getClientVersion() < 862 then
1609 local gameRootPanel = modules.game_interface.getRootPanel()
1610 g_keyboard.unbindKeyDown('Ctrl+R', gameRootPanel)
1611 end
1612 clear()
1613end
1614
1615function onChannelEvent(channelId, name, type)
1616 local fmt = ChannelEventFormats[type]
1617 if not fmt then
1618 print(('Unknown channel event type (%d).'):format(type))
1619 return
1620 end
1621
1622 local channel = channels[channelId]
1623 if channel then
1624 local tab = getTab(channel)
1625 if tab then
1626 addTabText(fmt:format(name), SpeakTypesSettings.channelOrange, tab)
1627 end
1628 end
1629end
1630