forked from GWRon/Dig
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbase.gfx.gui.chat.bmx
409 lines (310 loc) · 12.8 KB
/
base.gfx.gui.chat.bmx
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
SuperStrict
Import "base.gfx.gui.bmx"
Import "base.gfx.gui.panel.bmx"
Import "base.gfx.gui.input.bmx"
Import "base.gfx.gui.list.base.bmx"
Global CHAT_CHANNEL_NONE:Int = 0
Global CHAT_CHANNEL_DEBUG:Int = 1
Global CHAT_CHANNEL_SYSTEM:Int = 2
Global CHAT_CHANNEL_PRIVATE:Int = 4
'normal chat channels
Global CHAT_CHANNEL_LOBBY:Int = 8
Global CHAT_CHANNEL_INGAME:Int = 16
Global CHAT_CHANNEL_OTHER:Int = 32
Global CHAT_CHANNEL_GLOBAL:Int = 56 'includes LOBBY, INGAME, OTHER
Global CHAT_COMMAND_NONE:Int = 0
Global CHAT_COMMAND_WHISPER:Int = 1
Global CHAT_COMMAND_SYSTEM:Int = 2
Type TGUIChat Extends TGUIPanel
Field _defaultTextColor:TColor = TColor.Create(0,0,0)
Field _defaultHideEntryTime:Int = Null
'bitmask of channels the chat listens to
Field _channels:Int = 0
Field guiList:TGUIListBase = Null
Field guiInput:TGUIInput = Null
'is the input is inside the chatbox or absolute
Field guiInputPositionRelative:Int = 0
Field guiInputHistory:TList = CreateList()
Field keepInputActive:Int = True
'time when again allowed to send
Global antiSpamTimer:Long = 0
Global antiSpamTime:Int = 100
Method Create:TGUIChat(pos:TVec2D, dimension:TVec2D, limitState:String = "")
Super.Create(pos, dimension, limitState)
guiList = New TGUIListBase.Create(New TVec2D.Init(0,0), New TVec2D.Init(GetContentScreenWidth(),GetContentScreenHeight()), limitState)
guiList.setOption(GUI_OBJECT_ACCEPTS_DROP, False)
guiList.SetAutoSortItems(False)
guiList.SetAcceptDrop("")
guiList.setParent(Self)
guiList.SetAutoScroll(True)
guiList.SetBackground(Null)
guiInput = New TGUIInput.Create(New TVec2D.Init(0, dimension.y),New TVec2D.Init(dimension.x,-1), "", 32, limitState)
guiInput.setParent(Self)
'resize base and move child elements
resize(dimension.GetX(), dimension.GetY())
'by default all chats want to list private messages and system announcements
setListenToChannel(CHAT_CHANNEL_PRIVATE, True)
setListenToChannel(CHAT_CHANNEL_SYSTEM, True)
setListenToChannel(CHAT_CHANNEL_GLOBAL, True)
'register events
'- observe text changes in our input field
EventManager.registerListenerFunction( "guiobject.onChange", Self.onInputChange, Self.guiInput )
'- observe wishes to add a new chat entry - listen to all sources
EventManager.registerListenerMethod( "chat.onAddEntry", Self, "onAddEntry" )
GUIManager.Add( Self )
Return Self
End Method
'only hides the box with the messages
Method HideChat:Int()
guiList.Hide()
End Method
Method ShowChat:Int()
guiList.Show()
End Method
'returns boolean whether chat listens to a channel
Method isListeningToChannel:Int(channel:Int)
Return Self._channels & channel
End Method
Method setListenToChannel(channel:Int, enable:Int=True)
If enable
Self._channels :| channel
Else
Self._channels :& ~channel
EndIf
End Method
Method SetDefaultHideEntryTime(milliseconds:Int=Null)
Self._defaultHideEntryTime = milliseconds
End Method
Method SetDefaultTextColor(color:TColor)
Self._defaultTextColor = color
End Method
'implement in custom chats
Method GetSenderID:Int()
return 0
End Method
Function onInputChange:Int( triggerEvent:TEventBase )
Local guiInput:TGUIInput = TGUIInput(triggerEvent.getSender())
If guiInput = Null Then Return False
Local guiChat:TGUIChat = TGUIChat(guiInput._parent)
If guiChat = Null Then Return False
'skip empty text
If guiInput.value.Trim() = "" Then Return False
'emit event : chats should get a new line
'- step A) is to get what channels we want to announce to
Local sendToChannels:Int = guiChat.getChannelsFromText(guiInput.value)
'- step B) is emitting the event "for all"
' (the listeners have to handle if they want or ignore the line
EventManager.triggerEvent( TEventSimple.Create( "chat.onAddEntry", New TData.AddNumber("senderID", guiChat.GetSenderID()).AddNumber("channels", sendToChannels).AddString("text",guiInput.value) , guiChat ) )
'avoid getting the enter-key registered multiple times
'which leads to "flickering"
KEYMANAGER.blockKey(KEY_ENTER, 250) 'block for 100ms
'trigger antiSpam
guiChat.antiSpamTimer = Time.GetAppTimeGone() + guiChat.antiSpamTime
If guiChat.guiInputHistory.last() <> guiInput.value
guiChat.guiInputHistory.AddLast(guiInput.value)
'limit history to 50 entries
While guiChat.guiInputHistory.Count() > 50
guichat.guiInputHistory.RemoveFirst()
Wend
EndIf
'reset input field
guiInput.SetValue("")
End Function
Method onAddEntry:Int( triggerEvent:TEventBase )
Local guiChat:TGUIChat = TGUIChat(triggerEvent.getReceiver())
'if event has a specific receiver and this is not this chat
If triggerEvent.getReceiver() and guiChat <> Self Then Return False
'DO NOT WRITE COMMANDS !
If GetCommandFromText(triggerEvent.GetData().GetString("text")) = CHAT_COMMAND_SYSTEM
Return False
EndIf
'here we could add code to exlude certain other chat channels
Local sendToChannels:Int = triggerEvent.getData().getInt("channels", 0)
If Self.isListeningToChannel(sendToChannels)
Self.AddEntryFromData( triggerEvent.getData() )
Else
Print "onAddEntry - unknown channel, not interested"
EndIf
End Method
Function getChannelsFromText:Int(text:String)
Local sendToChannels:Int = 0 'by default send to no channel
Select GetCommandFromText(text)
Case CHAT_COMMAND_WHISPER
sendToChannels :| CHAT_CHANNEL_PRIVATE
Case CHAT_COMMAND_SYSTEM
sendToChannels :| CHAT_CHANNEL_SYSTEM
Default
sendToChannels :| CHAT_CHANNEL_GLOBAL
End Select
Return SendToChannels
End Function
Function GetPayloadFromText:String(text:String)
text = text.Trim()
If Left( text,1 ) <> "/" Then Return ""
Return Right(text, text.length - Instr(text, " "))
End Function
Function GetCommandStringFromText:String(text:String)
text = text.Trim()
If Left( text,1 ) <> "/" Then Return ""
Return Mid(text, 2, Instr(text, " ") - 2 )
End Function
Function GetCommandFromText:Int(text:String)
Select GetCommandStringFromText(text).ToLower()
Case "fluestern", "whisper", "w"
Return CHAT_COMMAND_WHISPER
Case "dev", "sys"
Return CHAT_COMMAND_SYSTEM
Default
Return CHAT_COMMAND_NONE
End Select
End Function
Method AddEntry(entry:TGUIListItem)
guiList.AddItem(entry)
End Method
Method AddEntryFromData( data:TData )
Local text:String = data.getString("text", "")
Local textColor:TColor = TColor( data.get("textColor") )
Local senderName:String = data.getString("senderName", "guest")
Local senderColor:TColor= TColor( data.get("senderColor") )
If Not textColor Then textColor = Self._defaultTextColor
If Not senderColor Then senderColor = Self._defaultTextColor
'finally add to the chat box
Local entry:TGUIChatEntry = New TGUIChatEntry.CreateSimple(text, textColor, senderName, senderColor, Null )
'if the default is "null" then no hiding will take place
entry.SetShowtime( _defaultHideEntryTime )
AddEntry( entry )
End Method
Method SetPadding:Int(top:Float, left:Float, bottom:Float, right:Float)
GetPadding().setTLBR(top,Left,bottom,Right)
resize()
End Method
'override resize and add minSize-support
Method Resize(w:Float=Null,h:Float=Null)
Super.Resize(w,h)
'background covers whole area, so resize it
If guiBackground Then guiBackground.resize(rect.getW(), rect.getH())
Local subtractInputHeight:Float = 0.0
'move and resize input field to the bottom
If guiInput And Not guiInput.hasOption(GUI_OBJECT_POSITIONABSOLUTE)
guiInput.resize(GetContentScreenWidth(),Null)
guiInput.rect.position.setXY(0, GetContentScreenHeight() - guiInput.GetScreenHeight())
subtractInputHeight = guiInput.GetScreenHeight()
EndIf
'move and resize the listbox (subtract input if needed)
If guiList
guiList.resize(GetContentScreenWidth(), GetContentScreenHeight() - subtractInputHeight)
EndIf
End Method
'override default update-method
Method Update:Int()
Super.Update()
'show items again if somone hovers over the list (-> reset timer)
If guiList._mouseOverArea
For Local entry:TGuiObject = EachIn guiList.entries
entry.show()
Next
EndIf
End Method
End Type
Type TGUIChatEntry Extends TGUIListItem
Field paddingBottom:Int = 5
Method CreateSimple:TGUIChatEntry(text:String, textColor:TColor, senderName:String, senderColor:TColor, lifetime:Int=Null)
Create(Null,Null, text)
SetLifetime(lifeTime)
SetShowtime(lifeTime)
SetSender(senderName, senderColor)
SetValue(text)
SetValueColor(textColor)
GetDimension()
Return Self
End Method
Method Create:TGUIChatEntry(pos:TVec2D=Null, dimension:TVec2D=Null, value:String="")
'no "super.Create..." as we do not need events and dragable and...
Super.CreateBase(pos, dimension, "")
SetValue(value)
SetLifetime( 1000 )
SetShowtime( 1000 )
'now we know the actual content and resize properly
Resize(GetDimension().GetX(), GetDimension().GetY())
GUIManager.add(Self)
Return Self
End Method
Method GetDimension:TVec2D()
local startX:int = self.GetScreenX()
local startY:int = self.GetScreenY()
Local move:TVec2D = New TVec2D.Init(0,0)
If Data.getString("senderName",Null)
Local senderColor:TColor = TColor(Data.get("senderColor"))
If Not senderColor Then senderColor = TColor.Create(0,0,0)
move = GetBitmapFontManager().baseFontBold.drawStyled(Data.getString("senderName")+":", startX, startY, senderColor, 2, 0)
'move the x so we get space between name and text
'move the y point 1 pixel as bold fonts are "higher"
move.SetXY( move.x + 5, 1)
EndIf
'available width is parentsDimension minus startingpoint
Local parentPanel:TGUIScrollablePanel = TGUIScrollablePanel(GetParent("tguiscrollablepanel"))
Local maxWidth:Int
If parentPanel
maxWidth = parentPanel.GetContentScreenWidth()
Else
maxWidth = GetParent().GetContentScreenWidth()
EndIf
if maxWidth <> -1 then maxWidth :- rect.GetX()
if maxWidth = -1 then maxWidth = GetScreenWidth()
if maxWidth <> -1 then maxWidth :+ 10
' maxWidth=295
Local maxHeight:Int = 2000 'more than 2000 pixel is a really long text
Local dimension:TVec2D = GetBitmapFontManager().baseFont.drawBlock(GetValue(), startX + move.x, startY + move.y, maxWidth - move.X, maxHeight, Null, Null, 2, 0)
'add padding
dimension.addXY(0, paddingBottom)
'print GetValue()+" " + dimension.y +" move.y="+move.Y+" maxWidth="+maxWidth+" move.X="+move.X
'set current size and refresh scroll limits of list
'but only if something changed (eg. first time or content changed)
If rect.getW() <> dimension.getX() Or rect.getH() <> dimension.getY()
'resize item
Resize(dimension.getX(), dimension.getY())
'recalculate item positions and scroll limits
'-> without multi-line entries would be not completely visible
local list:TGUIListBase = TGUIListBase(self.getParent("tguilistbase"))
if list then list.RecalculateElements()
EndIf
Return dimension
End Method
Method SetSender:Int(senderName:String=Null, senderColor:TColor=Null)
If senderName Then Self.Data.AddString("senderName", senderName)
If senderColor Then Self.Data.Add("senderColor", senderColor)
End Method
Method GetParentWidth:Float(parentClassName:String="toplevelparent")
If Not Self._parent Then Return Self.rect.getW()
Return Self.getParent(parentClassName).rect.getW()
End Method
Method GetParentHeight:Float(parentClassName:String="toplevelparent")
If Not Self._parent Then Return Self.rect.getH()
Return Self.getParent(parentClassName).rect.getH()
End Method
Method DrawContent()
'available width is parentsDimension minus startingpoint
Local parentPanel:TGUIScrollablePanel = TGUIScrollablePanel(Self.getParent("tguiscrollablepanel"))
local screenX:int = GetScreenX()
local screenY:int = GetScreenY()
if not parentPanel then throw "GUIChatEntry - no parentpanel"
if GetParent().GetScreenY() > screenY + rect.GetH() then return
if GetParent().GetScreenY() + GetParent().GetScreenHeight() < screenY then return
Local maxWidth:Int = parentPanel.getContentScreenWidth() - Self.rect.getX()
'local maxWidth:int = self.getParentWidth("tguiscrollablepanel")-self.rect.getX()
Local maxHeight:Int = 2000 'more than 2000 pixel is a really long text
Local move:TVec2D = New TVec2D.Init(0,0)
local oldCol:TColor = new TColor.Get()
If Self.showtime <> Null Then SetAlpha oldCol.a * Float(Self.showtime - Time.GetTimeGone())/500.0
If Self.Data.getString("senderName",Null)
Local senderColor:TColor = TColor(Self.Data.get("senderColor"))
If Not senderColor Then senderColor = TColor.Create(0,0,0)
move = GetBitmapFontManager().baseFontBold.drawStyled(Self.Data.getString("senderName", "")+":", screenX, screenY, senderColor, 2, 1)
'move the x so we get space between name and text
'move the y point 1 pixel as bold fonts are "higher"
move.SetXY( move.x + 5, 1)
EndIf
GetBitmapFontManager().baseFont.drawBlock(GetValue(), screenX + move.x, screenY + move.y, maxWidth - move.X, maxHeight, Null, valueColor, 2, 1, 0.5)
oldCol.SetRGBA()
End Method
End Type