-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrChat_Internals.lua
More file actions
1220 lines (1053 loc) · 39.5 KB
/
rChat_Internals.lua
File metadata and controls
1220 lines (1053 loc) · 39.5 KB
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
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
--[[ ---------------------------------------
This file separates out functions that are used by rChat but that
are "testable" because they use few enough of the ZOS functions
that I don't have to reproduce the entirety of ESO in my offline
test environment.
--]] ---------------------------------------
rChat_Internals = {}
local SF = LibSFUtils
local L = GetString
local RC_SEGTYPE_BARE = 0
local RC_SEGTYPE_MENTION = 1
local RC_SEGTYPE_LINK = 2
local RC_SEGTYPE_PAD = 3
local RC_SEGTYPE_TEXTURE = 4
local RC_SEGTYPE_QUICKLINK = 5
-- Used for rChat LinkHandling
local RCHAT_LINK = "p"
local RCHAT_URL_CHAN = 97
local RCHAT_CHANNEL_NONE = 99
-- "decision" tables for various channel groups
-- if entry is not nil then it belongs to the group
-- if it is true then it has special colors in GetChannelColors()
local npc_channels = {
[CHAT_CHANNEL_MONSTER_SAY] = true,
[CHAT_CHANNEL_MONSTER_YELL] = true,
[CHAT_CHANNEL_MONSTER_WHISPER] = true,
[CHAT_CHANNEL_MONSTER_EMOTE] = true,
}
local guild_mem_channels = {
[CHAT_CHANNEL_GUILD_1] = true,
[CHAT_CHANNEL_GUILD_2] = true,
[CHAT_CHANNEL_GUILD_3] = true,
[CHAT_CHANNEL_GUILD_4] = true,
[CHAT_CHANNEL_GUILD_5] = true,
}
local guild_ofc_channels = {
[CHAT_CHANNEL_OFFICER_1] = true,
[CHAT_CHANNEL_OFFICER_2] = true,
[CHAT_CHANNEL_OFFICER_3] = true,
[CHAT_CHANNEL_OFFICER_4] = true,
[CHAT_CHANNEL_OFFICER_5] = true,
}
local lang_zone_channels = {
[CHAT_CHANNEL_ZONE_LANGUAGE_1] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_2] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_3] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_4] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_5] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_6] = true,
[CHAT_CHANNEL_ZONE_LANGUAGE_7] = true,
}
--Detect the QuickChat messages |s<number><number:optional><number:optional><number:optional>|s
-- (taken from pChat with permission)
local function isQuickLink(text, start)
local start, _ = string.find(text, "|s%d%d-|s", start)
return start
end
-- return true if chanCode is one of the two whisper channels
local function isWhisperChannel( chanCode )
return (chanCode == CHAT_CHANNEL_WHISPER or chanCode == CHAT_CHANNEL_WHISPER_SENT)
end
-- return true if chanCode is one of the NPC/Monster chat channels
local function isMonsterChannel( chanCode )
if npc_channels[chanCode] then
return true
end
return false
end
-- return true if the chanCode is one of the guild or guild officer channels
local function isGuildChannel( chanCode )
if guild_mem_channels[chanCode] then
return true
elseif guild_ofc_channels[chanCode] then
return true
end
return false
end
rChat_Internals.isGuildChannel = isGuildChannel -- make available
-- return true if chanCode is one of the language zone channels
local function isLanguageChannel( chanCode )
return lang_zone_channels[chanCode] ~= nil
end
-------------------------------------------------------
-- return the index of the guild associated with the channel
-- that was passed in. If the channel is not associated with a
-- guild, then return 0.
function rChat_Internals.GetGuildIndex(channel)
if not channel then return 0,false end
local index = 0
local isOfc = false
if channel >= CHAT_CHANNEL_GUILD_1 and channel <= CHAT_CHANNEL_GUILD_5 then
index = channel - CHAT_CHANNEL_GUILD_1 + 1
elseif channel >= CHAT_CHANNEL_OFFICER_1 and channel <= CHAT_CHANNEL_OFFICER_5 then
index = channel - CHAT_CHANNEL_OFFICER_1 + 1
isOfc = true
end
return index, isOfc
end
-- creates raw and display-ready timestamp strings for the current time
function rChat_Internals.formatTimestamp(entry, ndx)
local db = rChat.save
if not db.showTimestamp then return "","" end
-- Message is timestamp for now
-- Add RCHAT_HANDLER for display
local tsString = rChat.CreateTimestamp(db.timestampFormat)
local timestamp = rChat.LinkHandler_CreateLink( ndx, entry.channel, tsString ) .. " "
-- Timestamp color
local display = timestamp
local raw = string.format("[%s] ", tsString)
return display, raw
end
-- create guild/officer tag
function rChat_Internals.formatTag(entry, ndx)
local db = rChat.save
-- Guild tag
local guild_number, isOfc = rChat_Internals.GetGuildIndex(entry.channel)
if guild_number == 0 then return nil end
local guild_name = SF.SafeGetGuildName(guild_number)
local tag
if not isOfc then
tag = db.guildTags[guild_name]
else
tag = db.officertag[guild_name]
end
if not tag or tag == "" then
tag = guild_name
end
if db.showGuildNumbers then
tag = guild_number .. "-" .. tag
end
local link_tag
if tag then
link_tag = ZO_LinkHandler_CreateLink(tag, nil, CHANNEL_LINK_TYPE, entry.channel)
tag = "["..tag.."] "
link_tag = link_tag .. " "
end
return link_tag, tag
end
-- overall message format
-- [timestamp] from separator text
--
function rChat_Internals.reduceString(entry, ndx, lvl)
local rawS = {} -- A list of strings to turn into a reduced message
if not lvl then lvl = 0 end
rawS[#rawS+1] = "*" -- I'm a reduced message
-- timestamp
if lvl == 0 then
-- try to keep the copy link (without color)
rawS[#rawS+1] = entry.displayT.timestamp
elseif lvl == 1 then
rawS[#rawS+1] = entry.rawT.timestamp
elseif lvl == 2 then
-- absolute minimum - no timestamp
end
-- message header
if entry.original.fromDisplayName then
local name = zo_strformat(SI_UNIT_NAME, entry.original.fromDisplayName)
if lvl == 0 then
-- minimal from with linking
rawS[#rawS+1] = "|H0:display:"..name.."|h"..name.."|h"
else
-- no linking
rawS[#rawS+1] = name
end
end
-- colon and optional CR
if lvl == 0 then
rawS[#rawS+1] = entry.rawT.separator
else
rawS[#rawS+1] = ": "
end
-- message body
-- text might be a string or it might be a table of segments
-- (whose first value is a string)
-- no links!
local text = entry.rawT.text
if text then
if type(text) == "string" then
rawS[#rawS+1] = text
elseif type(text) == "table" then
for _,v in ipairs(text) do
rawS[#rawS+1] = v[1]
end
end
end
text = table.concat(rawS)
if lvl == 2 then
text = string.sub(text,1,350)
end
return text
end
-- overall message format
-- [timestamp] [guildtag|partytag|langtag|whisptag] from [zonetag] separator linktext
--
function rChat_Internals.produceRawString(entry, ndx, rawT)
if not rawT then return "" end
local rawS = {} -- A list of strings to turn into a raw message
-- timestamp
if not rawT.timestamp then return "" end -- timestamps are required!
rawS[#rawS+1] = rawT.timestamp
-- message header
-- optional party, guild, language, whisper tag
if rawT.partytag then
rawS[#rawS+1] = rawT.partytag
elseif rawT.tag then -- note: this is the guild tag
rawS[#rawS+1] = rawT.tag
elseif rawT.languagetag then
rawS[#rawS+1] = rawT.languagetag
elseif rawS.whisper then
rawS[#rawS+1] = rawT.whisper
end
if rawT.from then
rawS[#rawS+1] = rawT.from
end
if rawT.zonetag then
rawS[#rawS+1] = rawT.zonetag
end
-- colon and optional CR
rawS[#rawS+1] = rawT.separator
-- message body
-- text might be a string or it might be a table of segments
-- (whose first value is a string)
local text = rawT.text
if text then
if type(text) == "string" then
rawS[#rawS+1] = text
elseif type(text) == "table" then
for _,v in ipairs(text) do
rawS[#rawS+1] = v[1]
end
end
end
return table.concat(rawS)
end
-- overall message format
-- [timestamp] lcol[guildtag|partytag|langtag|whisptag] from [zonetag] |r separator rcol linktext |r
--
-- Similar message format as used by produceRawString, but this function will
-- produce a message string containing colors, links, etc.
-- The colorT table provides the various colors that are applicable to THIS message.
function rChat_Internals.produceDisplayString(entry, ndx, displayT, colorT)
if not displayT then return "" end
local db = rChat.save
-- get colors for current message - (see initColorTable() )
local tcol, lcol, rcol, mcol = colorT.timecol, colorT.lcol, colorT.rcol, colorT.mentioncol
-- message header
local displayS = {} -- A list of strings to turn into a display message
-- timestamp and lcol
if db.showTimestamp and db.showTimestamp == true then
if displayT.timestamp and tcol then
displayS[#displayS+1] = tcol .. displayT.timestamp .. "|r" .. lcol
elseif displayT.timestamp then
displayS[#displayS+1] = lcol .. displayT.timestamp
end
else
displayS[#displayS+1] = lcol
end
-- optional party, guild, language, whisper tag
if displayT.partytag then
displayS[#displayS+1] = displayT.partyTag
elseif displayT.tag then
displayS[#displayS+1] = displayT.tag -- note: this is the guild tag
elseif displayT.languagetag then
displayS[#displayS+1] = displayT.languagetag
elseif displayT.whisper then
displayS[#displayS+1] = displayT.whisper
end
if displayT.from then
displayS[#displayS+1] = displayT.from
end
if displayT.zonetag then
displayS[#displayS+1] = displayT.zonetag
end
-- colon and optional CR
displayS[#displayS+1] = "|r" -- end of lcol
displayS[#displayS+1] = displayT.separator
-- message body
local text = displayT.text
if text then
if type(text) == "string" then
--displayS[#displayS+1] = rcol .. text .. "|r"
displayS[#displayS+1] = text
elseif type(text) == "table" then
for _,v in ipairs(text) do
if v[2] == RC_SEGTYPE_MENTION and mcol then
displayS[#displayS+1] = v[1]
--displayS[#displayS+1] = mcol .. v[1] .. "|r"
else
displayS[#displayS+1] = v[1]
--displayS[#displayS+1] = rcol .. v[1] .. "|r"
end
end
end
end
return table.concat(displayS)
end
-- overall copy from format
-- [guildtag|partytag|langtag|whisptag] from [zonetag]
--
-- npc and emote format never has []s
-- otherwise [] around player is controllable
function rChat_Internals.produceCopyFrom(entry, ndx, rawT)
if not rawT then return "" end
local rawS = {}
-- message header (only)
-- optional party, guild, language, whisper tag
if rawT.partytag then
rawS[#rawS+1] = rawT.partyTag
elseif rawT.tag then
rawS[#rawS+1] = rawT.tag
elseif rawT.languageTag then
rawS[#rawS+1] = rawT.languageTag
end
if rawT.from then
rawS[#rawS+1] = rawT.from
end
if rawT.zonetag then
rawS[#rawS+1] = rawT.zonetag
end
-- colon and optional CR
rawS[#rawS+1] = rawT.separator
return table.concat(rawS)
end
-- creates formatted zonetag/partytag for message prefix
-- returns linked and raw zone and party tags (which will be nil if nozonetags is true)
-- returns nil if not applicable
function rChat_Internals.formatZoneTag(entry, ndx)
local db = rChat.save
if db.nozonetags == true then
return nil,nil,nil,nil
end
local channel = entry.channel
-- Init zonetag to keep the channel tag
local zonetag
local partytag
if channel == CHAT_CHANNEL_PARTY then partytag = L(RCHAT_ZONETAGPARTY)
elseif channel == CHAT_CHANNEL_SAY then zonetag = L(RCHAT_ZONETAGSAY)
elseif channel == CHAT_CHANNEL_YELL then zonetag = L(RCHAT_ZONETAGYELL)
elseif channel == CHAT_CHANNEL_ZONE then zonetag = L(RCHAT_ZONETAGZONE)
end
local link_zonetag
if zonetag then
link_zonetag = " " .. rChat.LinkHandler_CreateLink( ndx, entry.channel, zonetag )
zonetag = " " .. zonetag
end
local link_partytag
if partytag then -- yes, I'm different
link_partytag = rChat.LinkHandler_CreateLink( ndx, entry.channel, partytag ).." "
partytag = partytag .. " "
end
return link_zonetag, zonetag, link_partytag, partytag
end
-- return language tag (both "raw" and "display" version currently identical)
-- returns nil if not applicable
function rChat_Internals.formatLanguageTag(entry, ndx)
local channel = entry.channel
if not isLanguageChannel(channel) then return nil, nil end
local lang = nil
if channel == CHAT_CHANNEL_ZONE_LANGUAGE_1 then lang = "[EN] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_2 then lang = "[FR] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_3 then lang = "[DE] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_4 then lang = "[JP] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_5 then lang = "[JP] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_6 then lang = "[ES] "
elseif channel == CHAT_CHANNEL_ZONE_LANGUAGE_7 then lang = "[ZH-Hans] "
end
return lang, lang
end
-- required in some form
function rChat_Internals.formatSeparator(entry, ndx)
local db = rChat.save
local sep
if db.carriageReturn then
sep = ":\n"
else
sep = ": "
end
return sep, sep
end
-- returns nil if not applicable
function rChat_Internals.formatWhisperTag(entry, ndx)
local channel = entry.channel
if not isWhisperChannel(channel) then return nil, nil end
local tag
if channel == CHAT_CHANNEL_WHISPER then
tag = ""
end
if channel == CHAT_CHANNEL_WHISPER_SENT then
tag = "-> "
end
return tag, tag
end
-- returns r,g,b colors that were set for the specified channel
-- from is optional - who the message is from - used in the PARTY channel (only)
-- to determine if sender is the group leader (who might have a special color)
function rChat_Internals.GetChannelColors(channel, from)
local db = rChat.save
-- Substract XX to a color (darker)
local function FirstEsoColor(r, g, b)
-- Scale is from 0-100 so divide per 300 will maximise difference at 0.33 (*2)
r = math.max(r - (db.diffforESOcolors / 300 ),0)
g = math.max(g - (db.diffforESOcolors / 300 ),0)
b = math.max(b - (db.diffforESOcolors / 300 ),0)
return r,g,b
end
-- Add XX to a color (brighter)
local function SecondEsoColor(r, g, b)
r = math.min(r + (db.diffforESOcolors / 300 ),1)
g = math.min(g + (db.diffforESOcolors / 300 ),1)
b = math.min(b + (db.diffforESOcolors / 300 ),1)
return r,g,b
end
if db.useESOcolors then
-- ESO actual color, return r,g,b
local rESO, gESO, bESO
-- Handle the same-colour options.
if db.allNPCSameColour and npc_channels[channel] then
rESO, gESO, bESO = ZO_ChatSystem_GetCategoryColorFromChannel(CHAT_CHANNEL_MONSTER_SAY)
elseif db.allGuildsSameColour and guild_mem_channels[channel] then
rESO, gESO, bESO = ZO_ChatSystem_GetCategoryColorFromChannel(CHAT_CHANNEL_GUILD_1)
elseif db.allGuildsSameColour and guild_ofc_channels[channel] then
rESO, gESO, bESO = ZO_ChatSystem_GetCategoryColorFromChannel(CHAT_CHANNEL_OFFICER_1)
elseif db.allZonesSameColour and lang_zone_channels[channel] then
rESO, gESO, bESO = ZO_ChatSystem_GetCategoryColorFromChannel(CHAT_CHANNEL_ZONE_LANGUAGE_1)
elseif channel == CHAT_CHANNEL_PARTY and from and db.groupLeader
and zo_strformat(SI_UNIT_NAME, from) == GetUnitName(GetGroupLeaderUnitTag()) then
rESO, gESO, bESO = SF.ConvertHexToRGBA(db.colours["groupleader"])
else
rESO, gESO, bESO = ZO_ChatSystem_GetCategoryColorFromChannel(channel)
end
-- Set right colour to left colour - cause ESO colors are rewritten; if onecolor, no rewriting
lcol = SF.ConvertRGBToHex(FirstEsoColor(rESO,gESO,bESO))
rcol = SF.ConvertRGBToHex(SecondEsoColor(rESO,gESO,bESO))
else
-- rChat Colors
-- Handle the same-colour options.
if db.allNPCSameColour and npc_channels[channel] then
lcol, rcol = rChat.getColors(CHAT_CHANNEL_MONSTER_SAY)
elseif db.allGuildsSameColour and guild_mem_channels[channel] then
lcol, rcol = rChat.getColors(CHAT_CHANNEL_GUILD_1)
elseif db.allGuildsSameColour and guild_ofc_channels[channel] then
lcol, rcol = rChat.getColors(CHAT_CHANNEL_OFFICER_1)
elseif db.allZonesSameColour and lang_zone_channels[channel] then
lcol, rcol = rChat.getColors(CHAT_CHANNEL_ZONE)
elseif channel == CHAT_CHANNEL_PARTY and from and db.groupLeader and zo_strformat(SI_UNIT_NAME, from) == GetUnitName(GetGroupLeaderUnitTag()) then
lcol = db.colours["groupleader"]
rcol = db.colours["groupleader1"]
else
lcol, rcol = rChat.getColors(channel)
end
end
-- Set right colour to left colour
if db.oneColour then
rcol = lcol
end
return lcol, rcol
end
-- get a color (hex) pair from the chat colors table (indexed by channel id)
function rChat.getColors(channelId)
local db = rChat.save
if not db.newcolors then return nil,nil end
local colorsEntry = db.newcolors[channelId]
if not colorsEntry then
colorsEntry = db.newcolors[1]
end
return colorsEntry[1], colorsEntry[2] -- left, right
end
-- create a table of the colors to be used in a particular message
-- lcol and rcol are required to be provided.
-- timecol is always provided, but might just be set to lcol
-- mentioncol is only provided if it is both available
-- and enabled, otherwise it will be nil
function rChat_Internals.initColorTable(channel, from)
local db = rChat.save
local colorT = {}
-- base colors for this message
colorT.lcol, colorT.rcol = rChat_Internals.GetChannelColors(channel, from)
if db.colours and db.colours.timestamp then
colorT.timecol = db.colours.timestamp
end
if db.timestampcolorislcol then
colorT.timecol = colorT.lcol
end
-- mentioncol if mentions are enabled
if db.mention.mentionEnabled and db.mention.colorEnabled then
colorT.mentioncol = db.mention.color
end
return colorT
end
-- Create a string in the closest format to the requested one.
-- We must know at least atname or toonname (since we have to have
-- one of these to have successfully looked up the nickname).
--
-- (Note that knowing the nickname will override the requested
-- format to use the nickname instead.)
--
-- (For instance: If a format requires toon name and we don't know it,
-- we have to degrade to a format that we know all of the parts of.)
--
-- Will return nil for bad formatId
function rChat_Internals.UseNameFormat(atname, toonname, nickname, formatId)
-- determine the usable format (if the desired one won't work)
if nickname then
formatId = 0 -- fake id for use "nickname"
elseif formatId == 1 then -- "@UserID"
if not atname then formatId = 2 end
elseif formatId == 2 then -- 2 = "Character Name"
if not toonname then formatId = 1 end
elseif formatId == 3 then -- "Character Name@UserID"
-- both ifs cannot be true
if not toonname then formatId = 1 end
if not atname then formatId = 2 end
elseif formatId == 4 then -- "Character Name(@UserID)
-- both ifs cannot be true
if not toonname then formatId = 1 end
if not atname then formatId = 2 end
elseif formatId == 5 then -- "Character Name/@UserID
-- both ifs cannot be true
if not toonname then formatId = 1 end
if not atname then formatId = 2 end
end
local name
-- create the display name (nicknames override format when available)
if formatId == 0 then name = nickname -- "Nickname" -- (unoffical format)
elseif formatId == 1 then name = atname -- "@UserID"
elseif formatId == 2 then name = toonname -- "Character Name"
elseif formatId == 3 then name = toonname .. atname -- "Character Name@UserID"
elseif formatId == 4 then name = toonname .. "(" .. atname .. ")" -- "Character Name(@UserID)
elseif formatId == 5 then name = toonname .. "/" .. atname -- "Character/Name@UserID"
end
return name
end
-- Add link handler to character/at name
-- linkType is either DISPLAY_NAME_LINK_TYPE or CHARACTER_LINK_TYPE or nil
-- if linkType is nil:
-- if anchor is an @name, use DISPLAY_NAME_LINK_TYPE,
-- else use CHARACTER_LINK_TYPE
--
-- disablebrackets = true, false, or nil
-- if nil then obey db.disablebrackets,
-- otherwise ignore db.disablebrackets in favor of this param
function rChat_Internals.GetNameLink(anchor, display, disablebrackets, linkType)
local db = rChat.save
if not linkType then
if IsDecoratedDisplayName(anchor) then
linkType = DISPLAY_NAME_LINK_TYPE
else
linkType = CHARACTER_LINK_TYPE
end
end
local link
if disablebrackets == true then
link = ZO_LinkHandler_CreateLinkWithoutBrackets(display, nil, linkType, anchor)
elseif db.disableBrackets and disablebrackets == nil then
link = ZO_LinkHandler_CreateLinkWithoutBrackets(display, nil, linkType, anchor)
else
link = ZO_LinkHandler_CreateLink(display, nil, linkType, anchor)
display = "[" .. display .. "]"
end
return link, display
end
-- Tries to get toon name from guild roster using @name
-- if not found then return nil
function rChat_Internals.GetGuildToon(guildIndex, atname)
if guildIndex == 0 then return nil end
local guildName, guildId = SF.SafeGetGuildName(guildIndex)
if not guildId then return nil end
local hastoon, rawtoonname = GetGuildMemberCharacterInfo(guildId,
GetGuildMemberIndexFromDisplayName(guildId, atname))
if hastoon then
return rawtoonname
end
return nil
end
-- Format "From" name
function rChat_Internals.formatName(channel, from, isCS, fromDisplayName)
local db = rChat.save
-- if we don't have a from, there is nothing to do here!
if not from then return nil, nil end
-- ------------------------
-- "From" can be Character or UserID (if char not available)
-- (or NPC name) depending on which channel we are
local atname = fromDisplayName
local toonname = from
if IsDecoratedDisplayName(from) == true then
toonname = nil
-- Messages from @Someone (guild / whisps)
if isGuildChannel(channel) == true then
local guildIndex = rChat_Internals.GetGuildIndex(channel)
-- guild msg, look up in roster
toonname = rChat_Internals.GetGuildToon(guildIndex, from) -- still might be nil
-- else is a whisper, can't get toon name -- is nil
end
end
if toonname then
-- strip gender marker
toonname = zo_strformat(SI_UNIT_NAME, toonname)
end
-- after this, at least one of atname or toonname is non-nil
-- look for nickname
local nick
if atname and db.nicknames[atname] then -- @UserID Nicknamed
nick = db.nicknames[atname]
elseif toonname and db.nicknames[toonname] then -- Char Nicknamed
nick = db.nicknames[toonname]
end
-- nick might still be nil
local new_from = from
local displayed = from -- raw
-- No brackets / UserID for emotes
local overrideBrackets
if channel == CHAT_CHANNEL_EMOTE then
overrideBrackets = true
end
if isMonsterChannel(channel) then
-- no changes for NPCs
new_from, displayed = toonname, toonname
elseif channel == CHAT_CHANNEL_PARTY then
local anchor = atname or toonname
--overrideBrackets = true -- requested by saenic
local displaynm = rChat_Internals.UseNameFormat(atname, toonname, nick, db.groupNames)
new_from, displayed = rChat_Internals.GetNameLink(atname, displaynm, overrideBrackets)
elseif isWhisperChannel(channel) then
local anchor = atname
overrideBrackets = true
new_from, displayed = rChat_Internals.GetNameLink(atname, atname, overrideBrackets)
elseif isGuildChannel(channel) then
local anchor = atname or toonname
local displaynm
local guildIndex = rChat_Internals.GetGuildIndex(channel)
local guildName, guildId = SF.SafeGetGuildName(guildIndex)
if db.formatguild[guildName] then
displaynm = rChat_Internals.UseNameFormat(atname, toonname, nick, db.formatguild[guildName])
else
displaynm = rChat_Internals.UseNameFormat(atname, toonname, nick, db.geoChannelsFormat)
end
new_from, displayed = rChat_Internals.GetNameLink(anchor, displaynm, overrideBrackets)
else -- zones / say / tell.
local anchor = atname or toonname
local displaynm = rChat_Internals.UseNameFormat(atname, toonname, nick, db.geoChannelsFormat)
new_from, displayed = rChat_Internals.GetNameLink(anchor, displaynm, overrideBrackets)
end
if isCS then -- ZOS icon
new_from = "|t16:16:EsoUI/Art/ChatWindow/csIcon.dds|t" .. new_from
displayed = "|t16:16:EsoUI/Art/ChatWindow/csIcon.dds|t" .. displayed
end
return new_from, displayed
end
-- --------------------------------------------
-- text formatting functions
-- Add a rChat handler for URL's
local function AddURLHandling(text, lineNum)
local rData = rChat.data
-- handle complex URLs and do
for pos, url, prot, subd, tld, colon, port, slash, path in text:gmatch("()(([%w_.~!*:@&+$/?%%#-]-)(%w[-.%w]*%.)(%w+)(:?)(%d*)(/?)([%w_.~!*:@&+$/?%#=-]*))") do
if rData.protocols[prot:lower()] == (1 - #slash) * #path
and (colon == "" or port ~= "" and port + 0 < 65536)
and (rData.tlds[tld:lower()] or tld:find("^%d+$") and subd:find("^%d+%.%d+%.%d+%.$")
and math.max(tld, subd:match("^(%d+)%.(%d+)%.(%d+)%.$")) < 256)
and not subd:find("%W%W") and url
then
local urlHandled = string.format("|H1:%s:%s:%s|h%s|h", RCHAT_LINK, lineNum, RCHAT_URL_CHAN, url)
url = url:gsub("([?+-])", "%%%1")
text, count = text:gsub(url, urlHandled)
end
end
return text
end
--[[
----------------------------------------------------------------------------------
In the text parsing functions, we build and manipulate a "text segment array",
which holds a segInfo table for each entry describing the text segment.
segInfo = {
text,
segment_type,
0 = bare, RC_SEGTYPE_BARE
1 = mention, RC_SEGTYPE_MENTION
2 = link, RC_SEGTYPE_LINK
3 = pad, RC_SEGTYPE_PAD
4 = texture, RC_SEGTYPE_TEXTURE
mention_col, (will be nil if not a RC_SEGTYPE_MENTION)
raw, (uncolored, link display text)
}
--]]
local function addSegment(tbl, ftype, text)
local entry = {text, ftype or RC_SEGTYPE_BARE}
table.insert(tbl,entry)
return entry
end
-- lookup segment in a segment table
-- returns displayText, segType, [mentionColor], [rawText]
local function getSegment(tbl,key)
local entry = tbl[key]
if not entry then return nil end
return entry[1], entry[2], entry[3], entry[4]
end
-- returns displayText, segType, [mentionColor], [rawText]
local function getSegInfo(seg)
if not seg then return nil end
if type(seg) ~= "table" then return tostring(seg) end
return seg[1], seg[2], seg[3], seg[4]
end
--[[
----------------------------------------------------------------------------------
In the text parsing functions, we also build and manipulate a "text fragment array",
which holds a fragInfo table for each entry describing the text fragments.
fragInfo = {
start = index to the start of the substring
stop = index to the end of the substring
text = substring text,
type = fragment_type,
0 = bare, RC_SEGTYPE_BARE
1 = mention, RC_SEGTYPE_MENTION
2 = link, RC_SEGTYPE_LINK
3 = pad, RC_SEGTYPE_PAD
4 = texture, RC_SEGTYPE_TEXTURE
5 = qlink, RC_SEGTYPE_QUICKLINK
raw = rawText, (if nil then raw is the same as text)
}
--]]
-- a fragment table is not the same as a segment table! (yet)
local function addFragment(tbl, start, stop, ftype, msgtext)
ftype =ftype or RC_SEGTYPE_BARE
if start > stop then return {} end
local entry = {
start=start,
stop=stop,
type=ftype,
text = string.sub(msgtext,start,stop)
}
table.insert(tbl,entry)
return entry
end
-- convert a fragment into a segment
local function frag2seg(frag)
if not frag then return end
local segentry = {
frag.text,
frag.type,
}
return segentry
end
-- Find out if segment is the specified type
-- returns true or false
local function isSegType(entry, stype)
if not entry or not entry[2] then return false end
if entry[2] == stype then
return true
end
return false
end
-- not currently used (or tested)
-- get a table of start and end positions for the link
-- handlers in the text string (returns fragment table)
function rChat_Internals.getPad(textstr)
if not textstr or type(textstr) ~= "string" then return {} end
local db = rChat.save
local linkpattern = "|u.-:.-:.-:(.-)|u"
local rslts={}
local last = 1
local start,fin, rt = string.find(textstr, linkpattern)
if not start then
addFragment(rslts,1,#textstr,RC_SEGTYPE_BARE, textstr)
return rslts
end
local ent
while( start ) do
if last ~= start then
addFragment(rslts, last, start-1, RC_SEGTYPE_BARE, textstr)
last = start
end
ent = addFragment(rslts, start, fin, RC_SEGTYPE_PAD, textstr)
ent.raw = rt
last = fin+1
start,fin = string.find(textstr,linkpattern, last)
end
if last < #textstr then
addFragment(rslts, last, #textstr, RC_SEGTYPE_BARE, textstr)
end
return rslts
end
-- not currently used (or tested)
-- get a table of start and end positions for the textures
-- in the text string (returns fragment table)
function rChat_Internals.getTexture(textstr)
if not textstr or type(textstr) ~= "string" then return {} end
local db = rChat.save
local linkpattern = "(|t.-|t)"
local rslts={}
local last = 1
local start,fin,t = string.find(textstr, linkpattern)
if not start then
addFragment(rslts, 1, #textstr, RC_SEGTYPE_BARE, textstr)
return rslts
end
local ent
while( start ) do
if last ~= start then
addFragment(rslts, last, start-1, RC_SEGTYPE_BARE, textstr)
last = start
end
ent = addFragment(rslts, start, fin, RC_SEGTYPE_TEXTURE, textstr)
ent.raw = ""
last = fin+1
start, fin, t = string.find(textstr, linkpattern, last)
end
if last < #text then
addFragment(rslts, last, #text, RC_SEGTYPE_BARE, textstr)
end
return rslts
end
-- QuickChat messages |s<number><number:optional><number:optional><number:optional>|s
local function getQuickLinks(rslts, text, starttxt, endtxt)
if rslts == nil then rslts = {} end
local quicklinkpattern = "|s(%d%d-)|s"
local last = 1
starttxt = starttxt or 1
endtxt = endtxt or #text
local segtxt = string.sub(text, starttxt, endtxt)
local start,fin, b, d, t = string.find(segtxt, quicklinkpattern, last)
if not start then
--addFragment(rslts, starttxt, #segtxt, RC_SEGTYPE_BARE, segtxt)
return rslts, starttxt, #segtxt
end
local ent
while( start ) do
if last ~= start then
addFragment(rslts, last, start-1, RC_SEGTYPE_BARE, segtxt)
last = start
end
ent = addFragment(rslts, start, fin, RC_SEGTYPE_QUICKLINK, segtxt)
ent.raw = t
last = fin+1
start, fin, b, d, t = string.find(segtxt,quicklinkpattern, last)
end
if last < #segtxt then
addFragment(rslts, last, #segtxt, RC_SEGTYPE_BARE, segtxt)
end
return rslts, start, endtxt
end
-- get a table of start and end positions for the link
-- handlers in the text string (returns fragment table)
local function getLinks(text)
if not text or type(text) ~= "string" then return {} end
local db = rChat.save
local linkpattern = "|H(.-):(.-)|h(.-)|h"
local rslts={}
local last = 1
local start,fin, b, dr, t = string.find(text, linkpattern, last)
if not start then
-- no links in text
rslts, start1 = getQuickLinks(rslts,text,last)
if next(rslts) == nil then
-- no quick links either
addFragment(rslts, last, #text-last+1, RC_SEGTYPE_BARE, text)
end
return rslts
end
local ent
-- got here so found a link
while( start ) do
if last ~= start then