-- player_chat_system - Player UI, core commands, and logic for Chat -- (component of Chat System) -- Template attachment: player (dynamic) -- Created by: Patrick Ferland -- Created on: 05/27/2008 -- Revision History -- 05/27/2008 P.Ferland - New script -- 05/30/2008 P.Ferland - Initial release -- 06/02/2008 P.Ferland - Cleanup and documentation -- 06/03/2008 P.Ferland - Repaired attachment/entry/detachment code to allow chat system deletion -- 06/10/2008 P.Ferland - Added /fontsize support -- 06/12/2008 P.Ferland - Made chat sort to front -- 06/20/2008 J.McNab - Added history on login -- 06/23/2008 P.Ferland - Added trigger reference counting for preprocessing -- 06/24/2008 P.Ferland - Corrected internal uses of message_pre to implement trigger refcount -- 06/25/2008 P.Ferland - Removed trigger refcount, moved command parsing to trigger to allow override behavior -- 06/27/2008 P.Ferland - Winstyle cleanup -- 07/01/2008 P.Ferland - Modified to use stylesheet winstyles -- 07/10/2008 P.Ferland - Fixed invalid reference error possible in chat unpause -- 07/16/2008 P.Ferland - Added channel aliases -- 07/17/2008 P.Ferland - Channel aliasing improvements: XW from selected channel, Suppress World, Togglable /channelname -- 07/22/2008 P.Ferland - Removed UiDelete that was throwing errors trying to delete a winstyle (That shouldn't be deleted anyway!) -- 07/24/2008 P.Ferland - Added UiCapability drag to chat window -- 07/28/2008 P.Ferland - Added persistent drag support -- 07/29/2008 P.Ferland - Updated pause button glow effect, replaced "-" prefix with system coloration -- 08/01/2008 P.Ferland - Added standardized UI for tooltips -- 08/06/2008 P.Ferland - Corrected "Nothing to say?" bug -- 08/08/2008 P.Ferland - Made xworld/Metaplace the default chat channel -- 08/29/2008 P.Ferland - Removed spurious debugs -- 09/12/2008 P.Ferland - Added safety check for place entry -- 09/15/2008 P.Ferland - Added new-style buttonfield support -- 09/25/2008 P.Ferland - Removed log entry when attach fires on a player who is not in a place -- 09/25/2008 P.Ferland - Added URL-to-link conversion (by Wayne "Crwth" Pearson) -- 10/07/2008 P.Ferland - Major overhaul of UI! New button bars, recent emotes, and menu system -- 10/08/2008 P.Ferland - Fixes to final message display. This script now handles buffering of formatted text -- in tags, and not the commands sending text -- 10/10/2008 P.Ferland - Assorted fixes from internal testing notes -- 10/14/2008 P.Ferland - Updated art and layout processing, and added flood control -- 10/15/2008 P.Ferland - Modified throw-at-background to drop out of throw mode instead of throw -- 10/16/2008 P.Ferland - Hacked throw into avatars instead of here. Dirty tight coupling. Sounds obscene. Because it is. -- 10/30/2008 P.Ferland - Added support for quickbutton toggling -- 11/06/2008 P.Ferland - Made "autochat" not auto-parse URLs, and added support for XW ignore -- Constants DEFAULT_SKIN = 'mpdefault' PRIVATE_HISTORY_MAX = 50 RESIZE_BUTTONS = { PLUS = '11302:467', MINUS = '11302:464', } MOOD_BAR_BUTTONS = { [1] = { sprite='11302:465', command = '/mood normal', tooltip = 'Set mood to Normal' }, [2] = { sprite='11302:484', command = '/mood happy', tooltip = 'Set mood to Happy' }, [3] = { sprite='11302:469', command = '/mood sad', tooltip = 'Set mood to Sad' }, [4] = { sprite='11302:477', command = '/mood angry', tooltip = 'Set mood to Angry' }, [5] = { sprite='11302:470', command = '/mood scared', tooltip = 'Set mood to Scared' }, } EMOTE_BAR_BUTTONS = { [1] = { sprite='11302:471', command = '/beginthrow snowball', tooltip = 'Throw a snowball' }, [2] = { sprite='11302:473', command = '/beginthrow tomato', tooltip = 'Throw a tomato' }, [3] = { sprite='11302:478', command = '/flagemote blah', tooltip = 'Emote "Blah"' }, [4] = { sprite='11302:482', command = '/flagemote exclaim', tooltip = 'Emote "Exclaim"' }, [5] = { sprite='11302:462', command = '/flagemote love', tooltip = 'Emote "Love"' }, [6] = { sprite='11302:466', command = '/flagemote peace', tooltip = 'Emote "Peace"' }, } ACTION_BAR_BUTTONS = { [1] = { sprite='11302:481', command = '/roll', tooltip = 'Roll a random number' }, [2] = { sprite='11302:474', command = '/wave', tooltip = 'Wave' }, [3] = { sprite='11302:480', command = '/dance', tooltip = 'Dance ' }, [4] = { sprite='11302:468', command = '/point', tooltip = 'Point' }, [5] = { sprite='11302:463', command = '/jump', tooltip = 'Jump' }, [6] = { sprite='11302:483', command = '/faint', tooltip = 'Faint' }, } EMOTE_ICONS = { ['blah'] = '11302:478', ['exclaim'] = '11302:482', ['love'] = '11302:462', ['peace'] = '11302:466', } BLANK_PNG = '11302:485' BAR_BUTTONS = { UNDERLAY = '11302:445', BN = '11302:485', BH = '11302:447', BP = '11302:446', W = 24, H = 24, P = 2, } QUICK_BUTTONS = { ['mood'] = '11302:465', ['emote'] = '11302:482', ['action'] = '11302:474', ['return'] = '11302:479', } THROW_SPEED = 70 THROWN = '11302:5' THROWN_SPRITES = { ['tomato'] = { pointer = '11302:473', sprite = '11302:475', splat = '11302:443' }, ['snowball'] = { pointer = '11302:471', sprite = '11302:476', splat = '11302:444' }, } THROWN_MAX_LIFETIME = 20000 SPLAT_LIFETIME = 2000 MAX_TRANSMIT_LENGTH = 900 -- Support for force-publish, where events sequence differently DEFAULT_TEXTFIELD_DATA = { winstyle = '11302:61', icon = { sprite = '11302:499', w = 15, h = 11, x = 3, y = 23 }, x = 10, y = 4, w = 388, h = 16, winheight = 25, win_y = 2, fc = { r = 64, g = 64, b = 64 }, bc = { r = 255, g = 255, b = 255, a = 0.0 }, visible = 1 } -- Script properties Define Properties() chat_system_skin = 'mpdefault' PersistProperty('chat_system_skin') chat_system_skin_size = 'normal' PersistProperty('chat_system_skin_size') -- Core UI elements chat_system_winstyle = '' chat_system_window = 0 chat_system_field = 0 chat_system_box = 0 chat_system_send_tooltip = 0 chat_system_pause = 0 chat_system_pause_buffer = {} chat_system_ignore = {} PersistProperty('chat_system_ignore') chat_system_mute = 0 PersistProperty('chat_system_mute') chat_system_font_size = 12 PersistProperty('chat_system_font_size') chat_system_current_message = '' chat_system_private_history = {} chat_system_channel_aliases = {} chat_system_private_key = '' chat_system_ui_offset = { x = 0, y = 0 } PersistProperty('chat_system_ui_offset') chat_system_mood = 'normal' chat_emote_throw_object = 'none' chat_system_emote_tooltips = {} chat_system_recent_emotes = { [1] = { 'emote', 2 }, -- tomato [2] = { 'emote', 6 }, -- peace [3] = { 'action', 2 }, -- wave } PersistProperty('chat_system_recent_emotes') chat_system_recent_tooltips = {} chat_emote_suppress_profile = 0 chat_system_last_button_press = 0 IncludeScript('11302:12') -- chat_utility IncludeScript('13197:21') -- ui_library end -- Commands Define Commands() MakeCommand('chat_system_player_chat', 'Player text-input chat', 'text:sentence') MakeCommand('chat_system_recent_button', 'Repeat a recent action', 'text:sentence') MakeCommand('chat_system_quick_button', 'Handle input from a quick-access button', 'bar:string', 'option:int') MakeCommand('chat_system_player_chat_button', 'Player text-input chat via send button', 'button_table:lua_table') MakeCommand('chat_system_player_size', 'Resize chat window', 'size:string') MakeCommand('chat_system_toggle_bar', 'Toggle visibility of a chat bar', 'bar:string', 'show:int') --MakeCommand('chat_system_throw_at_terrain', 'Throw at terrain if a player is missed', 'x:float', 'y:float', 'z:float') --MakeInput('chat_terrain', 'mouse-terrain', 'click', 'none', 'chat_system_throw_at_terrain') end --[[ Command chat_system_throw_at_terrain(x, y, z) if (self.chat_emote_throw_object ~= 'none') then local dx = self.x - x local dy = self.y - y local dz = self.z - z local distance = math.sqrt(dx * dx + dy * dy + dz * dz) local time = THROW_SPEED * distance local thrown = CreateObjectById(THROWN, self.x, self.y, self.z) thrown.name = self.chat_emote_throw_object thrown.lifetime = THROWN_MAX_LIFETIME thrown.spriteId = THROWN_SPRITES[self.chat_emote_throw_object].sprite SlideObject(thrown, x, y, z, time, 1) SendTo(self, 'chat_system_splat', time + 250, thrown.id, 0) end chat_system_reset_ui_state(self, 1) end --]] Command chat_system_toggle_bar(bar, show) local bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_' .. bar) if (bar_id == 0) then return end chat_system_toggle_bar(bar_id, show) chat_system_reset_ui_state(self) -- When showing a bar, hide all others! if (show == 1) then if (bar ~= 'mood') then bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_mood') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end end if (bar ~= 'emote') then bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_emote') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end end if (bar ~= 'action') then bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_action') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end end end end Command chat_system_player_size(size) local w = GetWorld() local cs = w.chat_system if (cs == nil) then return nil end if (cs.chat_system_skins_player_change ~= 1) then return nil end if ( (size == 'min') and (cs.chat_system_skins_minimized_enabled ~= 1) ) then return nil elseif ( (size == 'max') and (cs.chat_system_skins_maximized_enabled ~= 1) ) then return nil elseif ( (size == 'normal') and (cs.chat_system_skins_normal_enabled ~= 1) ) then return nil end SendTo(self, 'chat_system_player_chat', 0, '/chatskin ' .. self.chat_system_skin .. ' ' .. size) end -- Looks up and routes quick-button commands Command chat_system_quick_button(bar, option) chat_system_reset_ui_state(self, 1) -- flood control: Min 1 second between presses local now = os.time() % 1000000 -- mod time to prevent floating-point Lua number weirdness local diff = now - self.chat_system_last_button_press if (diff < -900000) then -- overflow! Assume all is good. diff = 1 end if (diff < 1) then self.chat_system_last_button_press = now return nil end self.chat_system_last_button_press = now local bar_table = EMOTE_BAR_BUTTONS if (bar == 'action') then bar_table = ACTION_BAR_BUTTONS elseif (bar == 'mood') then bar_table = MOOD_BAR_BUTTONS end local data = bar_table[option] if (data == nil) then return nil end local text = data.command if (text == nil) then return nil end SendTo(self, 'chat_system_player_chat', 0, text) if ( (bar == 'emote') or (bar == 'action') ) then chat_system_update_quick_buttons(self, data, bar, option) end end -- Used to route all chat commands; primarily from textfield, but can be called from other UI sources, too! Command chat_system_player_chat(text) SendTo(self, 'chat_system_player_chat', 0, text) end -- Used to route "recent hotkey" buttons Command chat_system_recent_button(text) chat_system_reset_ui_state(self, 1) -- flood control: Min 1 second between presses local now = os.time() % 1000000 -- mod time to prevent floating-point Lua number weirdness local diff = now - self.chat_system_last_button_press if (diff < -900000) then -- overflow! Assume all is good. diff = 1 end if (diff < 1) then self.chat_system_last_button_press = now return nil end self.chat_system_last_button_press = now SendTo(self, 'chat_system_player_chat', 0, text) end -- Handles buttonfield-style chat input Command chat_system_player_chat_button(button_table) chat_system_reset_ui_state(self, 1) local chat_text = button_table.chat_system_field if (chat_text == nil) then chat_text = '' end SendTo(self, 'chat_system_player_chat', 0, chat_text) end -- Triggers -- Handle use to throw, if throwing is active Trigger use(user) if (user.chat_emote_throw_object ~= 'none') then local distance = Distance(self, user) local time = THROW_SPEED * distance local thrown = CreateObjectById(THROWN, user.x, user.y, user.z) thrown.name = user.chat_emote_throw_object thrown.lifetime = THROWN_MAX_LIFETIME thrown.spriteId = THROWN_SPRITES[user.chat_emote_throw_object].sprite SlideObject(thrown, self.x + 0.2, self.y + 0.2, self.z + 0.2, time, 1) SendTo(self, 'chat_system_splat', time + 250, thrown.id, 1) user.chat_emote_throw_object = 'none' UiSetCursor(user, -1, 0, 0) return 1 -- block use end end Trigger chat_system_beginthrow(throwable) self.chat_emote_throw_object = throwable self.chat_emote_suppress_profile = 1 UiSetCursor(self, THROWN_SPRITES[throwable].pointer, 18, 18) end Trigger chat_system_abortthrow() chat_system_reset_ui_state(self, 1) end Trigger chat_system_splat(obj_id, thrown_at_me) local obj = GetObjectById(obj_id) if (obj == nil) then return nil end if (obj.type ~= 'thrown') then return nil end obj.spriteId = THROWN_SPRITES[obj.name].splat obj.lifetime = SPLAT_LIFETIME if ( (math.random(1, 100) > 90) and (thrown_at_me == 1) ) then SendTo(self, 'avatar_emote_die', 0) end end Trigger chat_system_flagemote(flag) local sprite = EMOTE_ICONS[flag] if (sprite == nil) then return nil end local ui_id = UiImage(0, 'flag_' .. flag, -12, -120, 24, 24, sprite) UiAttachObject(self, ui_id) UiVisible(ui_id, 1) UiVisible(ui_id, 0, 3000) SendTo(self, 'ui_clear', 3000, ui_id) end Trigger ui_clear(ui_id) UiDelete(ui_id) end Trigger ui_move(win_id, destination_x, destination_y) if (win_id == self.chat_system_window) then self.chat_system_ui_offset.x = destination_x self.chat_system_ui_offset.y = destination_y SaveToDb(self) end end -- Used to route all chat commands; primarily from textfield, but can be called from other UI sources, too! Trigger chat_system_player_chat(text) local sender = self local command = 'chat' local message = '' chat_system_set_callback(self, CHAT_STATUS_CODES.UNKNOWN, "Unknown Command") if ( (text == nil) or (text == '') ) then local user_channel = chat_system_channel_name('user', self.name) SendTo(self, 'chat_system_message_pre', 0, chat_system_format_system_message('Nothing to say?'), user_channel, self, 'help') SendTo(self, 'chat_system_message', 0, chat_system_format_system_message('Nothing to say?'), user_channel, self, 'help') SendTo(self, 'chat_system_message_finalize', 0, chat_system_format_system_message('Nothing to say?'), user_channel, self, 'help') UiText(self.chat_system_field, '') UiFocus(self, self.chat_system_field) return end if (string.sub(text, 1, 1) == '/') then local args = string.split(text, ' ') command = string.sub(args[1], 2) if (string.len(text) > (string.len(command) + 2) ) then message = string.sub(text, string.len(command) + 3) else message = '' end else message = text end local w = GetWorld() if (self.chat_system_current_channel == 'UNKNOWN') then -- Did the player enter the place properly? chat_system_do_enter(self) end if (command == 'chat') then-- only the "chat" command is implemented here; others are parsed... if (string.find(self.chat_system_current_channel, '/xworld/') == nil) then SendTo(w.chat_system, 'chat_msg_chat', 0, sender, message) else -- XW channels must be routed through gsay! local alias = '' local saved_alias = '' local channel_test = '' for alias, channel_test in pairs(self.chat_system_channel_aliases) do if (channel_test == self.chat_system_current_channel) then saved_alias = alias break end end SendTo(w.chat_system, 'chat_msg_gsay', 0, sender, saved_alias .. ' ' .. message, saved_alias) end else SendTo(w.chat_system, 'chat_system_parse_command', 0, sender, command, message) end if (self.chat_system_callback_data.status ~= CHAT_STATUS_CODES.OK) then if (w.chat_system.chat_system_slash_channel_shortcuts == 1) then -- Attempt to find a matching channel alias local found = 0 local alias = '' local aliased_channel = '' for alias, aliased_channel in pairs(self.chat_system_channel_aliases) do if (string.upper(alias) == string.upper(command)) then -- Match! -- Lock to channel self.chat_system_current_channel = aliased_channel -- Special case: Crossworld chat must be routed through /gsay if (string.find(aliased_channel, '/xworld/') ~= nil) then SendTo(w.chat_system, 'chat_msg_gsay', 0, sender, command .. ' ' .. message, command) else -- Any other chat can be transmitted to the in-world chat channel SendTo(w.chat_system, 'chat_msg_chat', 0, sender, message) end found = 1 break end end if (found == 0) then local channel = chat_system_channel_name('user', self.name) SendTo(self, 'chat_system_message_pre', 0, chat_system_format_system_message(self.chat_system_callback_data.status_text), channel, self, 'help') SendTo(self, 'chat_system_message', 0, chat_system_format_system_message(self.chat_system_callback_data.status_text), channel, self, 'help') SendTo(self, 'chat_system_message_finalize', 0, chat_system_format_system_message(self.chat_system_callback_data.status_text), channel, self, 'help') end end end if ( (self.chat_system_pause == 1) and (command ~= 'pause') ) then UiAddText(self.chat_system_box, chat_system_format_system_message('Your chat is paused. Results from any commands issued may not be seen until you unpause chat.')) end UiText(self.chat_system_field, '') UiFocus(self, self.chat_system_field) UiSort(self.chat_system_window, 'front') end -- Return focus to chat text field Trigger chat_system_focus() if (self.chat_system_field ~= 0) then UiFocus(self, self.chat_system_field) end end -- Destroy Chat UI Trigger chat_system_destroy_ui() chat_system_clear_ui(self) end -- Initialize Chat UI Trigger chat_system_initialize() chat_system_clear_ui(self) -- TODO: separate UI draw into a function or trigger of its own? local w = GetWorld() if (w.chat_system == nil) then return end local chat_skin_data = {} if (w.chat_system.chat_system_skins_player_change == 1) then -- Load player skin chat_skin_data = w.chat_system.chat_system_skins[self.chat_system_skin] else chat_skin_data = w.chat_system.chat_system_skins[w.chat_system.chat_system_skin] end if (chat_skin_data == nil) then -- ACK! Try to acquire default chat_skin_data = w.chat_system.chat_system_skins[DEFAULT_SKIN] end if (chat_skin_data == nil) then -- ABORT return end local chat_skin = {} if (w.chat_system.chat_system_skins_player_change == 1) then -- Load player skin chat_skin = chat_skin_data[self.chat_system_skin_size] else chat_skin = chat_skin_data[w.chat_system.chat_system_skin_size] end if (chat_skin == nil) then -- ABORT return end local ws = chat_skin.winstyle self.chat_system_winstyle = ws self.chat_system_window = UiWindow(0, 'chat_system_window', chat_skin.window.x + self.chat_system_ui_offset.x, chat_skin.window.y + self.chat_system_ui_offset.y, chat_skin.window.w, chat_skin.window.h, self.chat_system_winstyle) -- Force upgrade patch! if (chat_skin.textfield.win_y == nil) then chat_skin.textfield = table.deepcopy(DEFAULT_TEXTFIELD_DATA) end local chat_field_window = UiWindow(self.chat_system_window, 'chat_field_window', 0, chat_skin.window.h + chat_skin.textfield.win_y, chat_skin.textfield.x + chat_skin.textfield.w - chat_skin.go_button.w + 2 + chat_skin.go_button.w + 2, chat_skin.textfield.winheight, chat_skin.textfield.winstyle) local chat_field_icon = UiImage(chat_field_window, 'chat_field_icon', chat_skin.textfield.icon.x, chat_skin.textfield.icon.y, chat_skin.textfield.icon.w, chat_skin.textfield.icon.h, chat_skin.textfield.icon.sprite) self.chat_system_field = UiTextField(chat_field_window, 'chat_system_field', chat_skin.textfield.x, chat_skin.textfield.y, chat_skin.textfield.w - (chat_skin.go_button.w + 2), chat_skin.textfield.h, chat_skin.textfield.fc.r, chat_skin.textfield.fc.g, chat_skin.textfield.fc.b, chat_skin.textfield.bc.r, chat_skin.textfield.bc.g, chat_skin.textfield.bc.b, chat_skin.textfield.bc.a, 'chat_system_player_chat', '') local go_button = UiImageButton(chat_field_window, 'chat_system_go_button', chat_skin.textfield.x + chat_skin.textfield.w - chat_skin.go_button.w + 2, chat_skin.textfield.y - 2, chat_skin.go_button.w, chat_skin.go_button.h, chat_skin.go_button.bn, chat_skin.go_button.bh, chat_skin.go_button.bp, 'chat_system_player_chat_button') self.chat_system_box= UiTextBox(self.chat_system_window, 'chat_system_box', chat_skin.textbox.x, chat_skin.textbox.y, chat_skin.textbox.w, chat_skin.textbox.h, chat_skin.textbox.fc.r, chat_skin.textbox.fc.g, chat_skin.textbox.fc.b, chat_skin.textbox.bc.r, chat_skin.textbox.bc.g, chat_skin.textbox.bc.b, chat_skin.textbox.bc.a, 1, 1, 0) UiCapability(self.chat_system_window, 'drag') -- Only align chat window if it hasn't been moved... if ( (self.chat_system_ui_offset.x == 0) and (self.chat_system_ui_offset.y == 0) ) then UiAlign(self.chat_system_window, 0, 0 - chat_skin.textfield.winheight, 'bottom_left', 'scale_none') end UiAttachUser(self, self.chat_system_window) UiVisible(self.chat_system_window, 1) UiVisible(self.chat_system_field, chat_skin.textfield.visible) UiVisible(self.chat_system_box, chat_skin.textbox.visible) UiButtonField(go_button, self.chat_system_field) self.chat_system_send_tooltip = chat_system_toolbar_tooltip(self, 'Send chat', go_button) -- draw resize buttons if (w.chat_system.chat_system_skins_player_change == 1) then if (self.chat_system_skin_size == 'minimized') then if (w.chat_system.chat_system_skins_normal_enabled == 1) then UiImageButton(chat_field_window, 'chat_system_size_field', -9, -4, 9, 9, RESIZE_BUTTONS.PLUS, RESIZE_BUTTONS.PLUS, RESIZE_BUTTONS.PLUS, 'chat_system_player_size normal') end elseif (self.chat_system_skin_size == 'maximized') then if (w.chat_system.chat_system_skins_minimized_enabled == 1) then UiImageButton(chat_field_window, 'chat_system_size_field', -9, -4, 9, 9, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, 'chat_system_player_size min') end if (w.chat_system.chat_system_skins_normal_enabled == 1) then UiImageButton(self.chat_system_window, 'chat_system_size_box', -9, 0, 9, 9, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, 'chat_system_player_size normal') end else -- self.chat_system_skin_size = 'normal' if (w.chat_system.chat_system_skins_minimized_enabled == 1) then UiImageButton(chat_field_window, 'chat_system_size_field', -9, -4, 9, 9, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, RESIZE_BUTTONS.MINUS, 'chat_system_player_size min') end if (w.chat_system.chat_system_skins_maximized_enabled == 1) then UiImageButton(self.chat_system_window, 'chat_system_size_box', -9, 0, 9, 9, RESIZE_BUTTONS.PLUS, RESIZE_BUTTONS.PLUS, RESIZE_BUTTONS.PLUS, 'chat_system_player_size max') end end end -- Draw mood/emote/action buttons and most-recently-used quickbuttons if (w.chat_system.chat_system_display_quickbuttons == 1) then local button_bar_x = chat_skin.textfield.x + chat_skin.textfield.w - chat_skin.go_button.w + 2 + chat_skin.go_button.w + 4 local mood_button_underlay = UiImage(chat_field_window, 'mood_button_underlay', button_bar_x, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.UNDERLAY) local mood_button_img = UiImage(mood_button_underlay, 'mood_button_img', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, QUICK_BUTTONS['mood']) local mood_button = UiImageButton(mood_button_img, 'mood_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_toggle_bar mood 1') local tooltip = chat_system_toolbar_tooltip(self, 'Select a Mood', mood_button, 'above') table.insert(self.chat_system_emote_tooltips, tooltip) local emote_button_underlay = UiImage(chat_field_window, 'emote_button_underlay', button_bar_x + BAR_BUTTONS.W, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.UNDERLAY) local emote_button_img = UiImage(emote_button_underlay, 'emote_button_img', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, QUICK_BUTTONS['emote']) local emote_button = UiImageButton(emote_button_img, 'emote_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_toggle_bar emote 1') local tooltip = chat_system_toolbar_tooltip(self, 'Perform an Emote', emote_button, 'above') table.insert(self.chat_system_emote_tooltips, tooltip) local action_button_underlay = UiImage(chat_field_window, 'action_button_underlay', button_bar_x + 2 * BAR_BUTTONS.W, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.UNDERLAY) local action_button_img = UiImage(action_button_underlay, 'action_button_img', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, QUICK_BUTTONS['action']) local action_button = UiImageButton(action_button_img, 'action_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_toggle_bar action 1') local tooltip = chat_system_toolbar_tooltip(self, 'Perform an Action', action_button, 'above') table.insert(self.chat_system_emote_tooltips, tooltip) chat_system_draw_recent_buttons(self, chat_field_window, button_bar_x + 3 * BAR_BUTTONS.W) -- Draw (initially hidden) mood/emote/action menus local mood_bar = chat_system_button_bar(self, chat_field_window, 'mood', button_bar_x, 0, MOOD_BAR_BUTTONS) local emote_bar = chat_system_button_bar(self, chat_field_window, 'emote', button_bar_x + BAR_BUTTONS.W, 0, EMOTE_BAR_BUTTONS) local action_bar = chat_system_button_bar(self, chat_field_window, 'action', button_bar_x + 2 * BAR_BUTTONS.W, 0, ACTION_BAR_BUTTONS) end -- Repopulate the screen with private history local history = self.chat_system_private_history if (history ~= nil) then if (#history > 0) then local pre = '' local message = '' local i = 0 for i = 1, #history do UiAddText(self.chat_system_box, history[i]) end end end end -- System trigger: Fired when script is attached (by Chat System script management, dynamically!) Trigger attach() chat_system_do_enter(self) SendTo(self, 'chat_system_initialize', 0) local key = '' local i = 0 for i = 1, 6 do key = key .. math.random(5,95) end self.chat_system_private_key = key end -- System trigger: fired when user enters a place Trigger enter() chat_system_do_enter(self) -- Display the history for the place entered local w = GetWorld() if (w.chat_system ~= nil) then SendTo(w.chat_system, 'chat_msg_history', 50, self, w.chat_system.chat_system_login_history_length) end end -- System trigger: fired when user exits a place Trigger left() local place = GetPlace() local w = GetWorld() if (w == nil) then return end -- Unsubscribe from Local chat (Place and Spatial) if (place ~= nil) then SendTo(w.chat_system, 'chat_system_unsubscribe_channel', 0, self, chat_system_channel_name('place', place.name)) SendTo(w.chat_system, 'chat_system_unsubscribe_channel', 0, self, chat_system_channel_name('spatial', place.name)) end if ( (self.chat_system_current_channel == chat_system_channel_name('place', place.name)) or (self.chat_system_current_channel == chat_system_channel_name('spatial', place.name)) ) then self.chat_system_current_channel = 'UNKNOWN' end end -- Chat system trigger: Fired when a chat message is about to be sent Trigger chat_system_message_pre(message, channel, sender, command) -- Load the current message into the buffer, if it hasn't already been partially processed if (self.chat_system_current_message == '') then -- Last-chance raw message format self.chat_system_current_message = message end local w = GetWorld() local font_size = w.chat_system.chat_system_default_size if (w.chat_system.chat_system_player_size == 1) then font_size = self.chat_system_font_size end if (command ~= 'autochat') then -- autochat is preformatted, but otherwise, parse URLs self.chat_system_current_message = chat_system_urls(self.chat_system_current_message) end self.chat_system_current_message = '' .. self.chat_system_current_message .. '' end -- Chat System trigger: Fired when a chat message is sent Trigger chat_system_message(message, channel, sender, command) -- NOTE: Customization modules may process message for user display or entity interaction now SendTo(self, 'chat_system_message_done', 0, message, channel, sender, command) end -- Chat System trigger: Fired at end of chat system message sending process Trigger chat_system_message_finalize(message, channel, sender, command) if (sender.name ~= nil) then local super = 0 if (sender.type == 'player') then if (IsSuperuser(sender) == 1) then super = 1 end end if (super ~= 1) then -- never ignore superusers if (self.chat_system_ignore[string.lower(sender.name)] ~= nil) then -- Non-superuser on ignore list; clear message and call it a day self.chat_system_current_message = '' return nil elseif (command == 'gsay') then -- Detect and abort on a XW-ignored user -- (XW *) Username: local username_loc = string.find(message, ') ') + 2 local username_end = string.find(message, ':', username_loc) - 1 local username_lower = string.lower(string.sub(message, username_loc, username_end)) if (self.chat_system_ignore[username_lower] ~= nil) then self.chat_system_current_message = '' return nil end elseif (command == 'gtell') then -- Detect and abort on a XW-ignored user -- (XW, private) Username@metaplace.com/world: local username_loc = string.find(message, ') ') + 2 local username_end = string.find(message, '@', username_loc) - 1 local username_lower = string.lower(string.sub(message, username_loc, username_end)) if (self.chat_system_ignore[username_lower] ~= nil) then -- Non-superuser on ignore list; clear message and call it a day self.chat_system_current_message = '' return nil end end end end if (self.chat_system_current_message == '') then -- Last-chance raw message format self.chat_system_current_message = message end local w = GetWorld() local font_size = w.chat_system.chat_system_default_size if (w.chat_system.chat_system_player_size == 1) then font_size = self.chat_system_font_size end local font_pre = '' local font_post = '' if (self.chat_system_pause == 1) then local buffer_message = { m = self.chat_system_current_message, c = channel, s = sender, x = command } table.insert(self.chat_system_pause_buffer, buffer_message) else if (string.len(self.chat_system_current_message) < MAX_TRANSMIT_LENGTH) then UiAddText(self.chat_system_box, font_pre .. self.chat_system_current_message .. font_post) chat_system_store_history(self, font_pre .. self.chat_system_current_message .. font_post) else local tx_buffer = '' while ( string.len(self.chat_system_current_message) > 0 ) do -- Build a transmit buffer tx_buffer = string.sub(self.chat_system_current_message, 1, MAX_TRANSMIT_LENGTH) local br_found = 0 local beg_idx = 0 local end_idx = 0 local i = 0 while true do local b, e = string.find(tx_buffer, "<%s*[bB][rR]%s*/?>", i + 1) if b then i = b beg_idx = b end_idx = e br_found = 1 else break end end -- If we didn't find a break within the buffer, transmit what we have and accept the -- chance of a visual glitch... -- Otherwise, build a transmit buffer and update the current message master buffer if (br_found == 1) then tx_buffer = string.sub(self.chat_system_current_message, 1, beg_idx - 1) if (end_idx + 1 < string.len(self.chat_system_current_message)) then self.chat_system_current_message = string.sub(self.chat_system_current_message, end_idx + 1, string.len(self.chat_system_current_message)) else self.chat_system_current_message = '' end else self.chat_system_current_message = string.sub(self.chat_system_current_message, MAX_TRANSMIT_LENGTH + 1, string.len(self.chat_system_current_message)) end UiAddText(self.chat_system_box, font_pre .. tx_buffer .. font_post) chat_system_store_history(self, font_pre .. tx_buffer .. font_post) end end end -- Flush the current message buffer self.chat_system_current_message = '' end -- Marks a user's chat as paused Trigger chat_system_pause() self.chat_system_pause = 1 end -- Unpauses a user's chat and replays any buffered messages Trigger chat_system_unpause() self.chat_system_pause = 0 local message = '' for _, message in ipairs(self.chat_system_pause_buffer) do -- Dereference the sender, in case that object reference is no longer valid. -- If it's become invalid, have self impersonate the sender local message_sender = message.s if (message.s.id == nil) then message_sender = self end -- Skip straight to final transmit on these; the messages have already been preprocessed if they're in the buffer. SendTo(self, 'chat_system_message_finalize', 0, message.m, message.c, message_sender, message.x) end table.clear(self.chat_system_pause_buffer) end -- Adds a target to a user's ignore list Trigger chat_system_ignore(username) local channel = chat_system_channel_name('user', self.name) local message = chat_system_format_system_message('User "' .. username .. '" will be ignored. This is permanent! Use /unignore to hear the user again.') local lower_name = string.lower(username) self.chat_system_ignore[lower_name] = lower_name SendTo(self, 'chat_system_message_pre', 0, message, channel, self, 'ignore') SendTo(self, 'chat_system_message', 0, message, channel, self, 'ignore') SendTo(self, 'chat_system_message_finalize', 0, message, channel, self, 'ignore') SaveToDb(self) end -- Renders a user's ignore list Trigger chat_system_ignorelist() local channel = chat_system_channel_name('user', self.name) local message = '- You are ignoring the following users:
' local pre = '' local username = '' local plural = '' local count = 0 if (self.chat_system_ignore ~= {} ) then for _, username in pairs(self.chat_system_ignore) do count = count + 1 message = message .. pre .. username pre = ', ' end end if (count ~= 1) then plural = 's' end message = chat_system_format_system_message(message .. '
- ' .. count .. ' user' .. plural) SendTo(self, 'chat_system_message_pre', 0, message, channel, self, 'ignorelist') SendTo(self, 'chat_system_message', 0, message, channel, self, 'ignorelist') SendTo(self, 'chat_system_message_finalize', 0, message, channel, self, 'ignorelist') SaveToDb(self) end -- Removes a target from a user's ignore list Trigger chat_system_unignore(username) local channel = chat_system_channel_name('user', self.name) local message = chat_system_format_system_message('User "' .. username .. '" will not be ignored.') local lower_name = string.lower(username) self.chat_system_ignore[lower_name] = nil SendTo(self, 'chat_system_message_pre', 0, message, channel, self, 'unignore') SendTo(self, 'chat_system_message', 0, message, channel, self, 'unignore') SendTo(self, 'chat_system_message_finalize', 0, message, channel, self, 'unignore') SaveToDb(self) end -- Mutes a user (if not Superuser, cannot issue ANY chat commands) Trigger chat_system_mute() local super_user = 0 if (sender.type == 'player') then if (IsSuperuser(sender) == 1) then super_user = 1 end end if (super_user == 1) then return -- Superusers can't be muted! end local channel = chat_system_channel_name('user', self.name) local message = chat_system_format_system_message('You have been muted! You can PERMANENTLY not issue any chat commands.') self.chat_system_mute = 1 SendTo(self, 'chat_system_message_pre', 0, message, channel, self, 'mute') SendTo(self, 'chat_system_message', 0, message, channel, self, 'mute') SendTo(self, 'chat_system_message_finalize', 0, message, channel, self, 'mute') SaveToDb(self) end -- Unmutes a user (can issue chat commands) Trigger chat_system_unmute() local super_user = 0 if (sender.type == 'player') then if (IsSuperuser(sender) == 1) then super_user = 1 end end if (super_user == 1) then return -- Superusers can't be muted! end local channel = chat_system_channel_name('user', self.name) local message = chat_system_format_system_message('You have been unmuted! You can issue chat commands.') self.chat_system_mute = 0 SendTo(self, 'chat_system_message_pre', 0, message, channel, self, 'unmute') SendTo(self, 'chat_system_message', 0, message, channel, self, 'unmute') SendTo(self, 'chat_system_message_finalize', 0, message, channel, self, 'unmute') SaveToDb(self) end -- Channel subscription -- set up alias Trigger chat_system_subscribed(channel) alias = '' local silent = 0 -- Known channels with special names if (string.find(channel, '_spatial_') ~= nil) then alias = 'Spatial' elseif (string.find(channel, '#') ~= nil) then alias = 'Place' elseif (string.find(channel, '/user/') ~= nil) then alias = 'Private' elseif ( (string.find(channel, '#') == nil) and (string.find(channel, '/') == nil) ) then alias = 'World' silent = 1 -- suppress World channel as selectable -- TODO: Make this list dynamic! elseif (string.find(channel, '/group/') ~= nil) then alias = 'Group' elseif (string.find(channel, '/guild/') ~= nil) then alias = 'Guild' end -- Unknown channel; Use proper-cased entity name if (alias == '') then local channel_array = string.split(channel, '/') local alias_name = channel_array[#channel_array] if (string.len(alias_name) > 1) then alias = string.upper(string.sub(alias_name, 1, 1)) .. string.lower(string.sub(alias_name, 2, string.len(alias_name))) else alias = string.upper(alias_name) end end if (silent == 0) then self.chat_system_channel_aliases[alias] = channel end end -- Channel unsubscription -- remove alias Trigger chat_system_unsubscribed(channel) local silent = 0 alias = '' -- Known channels with special names if (string.find(channel, '_spatial_') ~= nil) then alias = 'Spatial' elseif (string.find(channel, '#') ~= nil) then alias = 'Place' elseif (string.find(channel, '/user/') ~= nil) then alias = 'Private' elseif ( (string.find(channel, '#') == nil) and (string.find(channel, '/') == nil) ) then alias = 'World' silent = 1 -- TODO: Make this list dynamic! elseif (string.find(channel, '/group/') ~= nil) then alias = 'Group' elseif (string.find(channel, '/guild/') ~= nil) then alias = 'Guild' end -- Unknown channel; Use proper-cased entity name if (alias == '') then local channel_array = string.split(channel, '/') local alias_name = channel_array[#channel_array] if (string.len(alias_name) > 1) then alias = string.upper(string.sub(alias_name, 1, 1)) .. string.lower(string.sub(alias_name, 2, string.len(alias_name))) else alias = string.upper(alias_name) end end self.chat_system_channel_aliases[alias] = nil end -- Local functions -- Safely delete Chat UI function chat_system_clear_ui(self) local tooltip = 0 for _, tooltip in ipairs(self.chat_system_recent_tooltips) do UiDelete(tooltip) end for _, tooltip in ipairs(self.chat_system_emote_tooltips) do UiDelete(tooltip) end if (self.chat_system_window ~= 0) then UiDelete(self.chat_system_window) self.chat_system_window = 0 end if (self.chat_system_send_tooltip ~= 0) then UiDelete(self.chat_system_send_tooltip) self.chat_system_send_tooltip = 0 end end -- Perform place-entry (or script attach) functions (channel subscription etc) function chat_system_do_enter(self) local place = GetPlace() if (place == nil) then return end local w = GetWorld() if (w == nil) then return end if (w.chat_system ~= nil) then -- Subscribe to World chat -- Will have no side effects if already subscribed SendTo(w.chat_system, 'chat_system_subscribe_channel', 0, self, chat_system_channel_name('world')) -- Subscribe to own User chat -- Will have no side effects if already subscribed SendTo(w.chat_system, 'chat_system_subscribe_channel', 0, self, chat_system_channel_name('user', self.name)) -- Subscribe to Local chat (Place and Spatial) -- Will have no side effects if already subscribed if (place ~= nil) then if (self.chat_system_current_channel == 'UNKNOWN') then --self.chat_system_current_channel = chat_system_channel_name('spatial', place.name) self.chat_system_current_channel = chat_system_channel_name('xworld', 'Metaplace') end SendTo(w.chat_system, 'chat_system_subscribe_channel', 0, self, chat_system_channel_name('place', place.name)) SendTo(w.chat_system, 'chat_system_subscribe_channel', 0, self, chat_system_channel_name('spatial', place.name)) if (self.chat_system_current_channel == 'UNKNOWN') then --self.chat_system_current_channel = chat_system_channel_name('spatial', place.name) self.chat_system_current_channel = chat_system_channel_name('xworld', 'Metaplace') end end end end function chat_system_button_bar(self, parent, name, x, y, buttons) local w = GetWorld() local cs = w.chat_system if (cs.chat_system_display_quickbuttons ~= 1) then return nil end local bar_tl_x = x local bar_tl_y = y - ((#buttons + 1) * (BAR_BUTTONS.H + BAR_BUTTONS.P)) local bar_w = BAR_BUTTONS.W local bar_h = (#buttons + 1) * (BAR_BUTTONS.H + BAR_BUTTONS.P) local chat_bar = UiElement(parent, 'chat_bar_' .. name, bar_tl_x, bar_tl_y) -- Make the bar invisible so we don't have to watch it draw... UiVisible(chat_bar, 0) local i = 0 local data = {} local cur_y = bar_h - (BAR_BUTTONS.H + BAR_BUTTONS.P) for i, data in ipairs(buttons) do local under = UiImage(chat_bar, name .. '_under_' .. i, 0, cur_y, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.UNDERLAY) local img = UiImage(under, name .. '_img_' .. i, 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, data.sprite) local btn = UiImageButton(img, name .. '_btn_' .. i, 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_quick_button "' .. name .. '" ' .. i) cur_y = cur_y - (BAR_BUTTONS.H + BAR_BUTTONS.P) local tooltip = chat_system_toolbar_tooltip(self, data.tooltip, btn, 'right') table.insert(self.chat_system_emote_tooltips, tooltip) end --local under = UiImage(chat_bar, name .. '_under_return', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.UNDERLAY) local img = UiImage(chat_bar, name .. '_img_return', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, QUICK_BUTTONS['return']) local btn = UiImageButton(img, name .. '_btn_return', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_toggle_bar ' .. name .. ' 0') local tooltip = chat_system_toolbar_tooltip(self, 'Close Menu', btn, 'above') table.insert(self.chat_system_emote_tooltips, tooltip) end function chat_system_toggle_bar(bar_id, show) UiVisible(bar_id, show) end -- Render a tooltip for a UI element function chat_system_toolbar_tooltip(self, text, item, direction) if (direction == nil) then direction = 'right' end local tooltip_id = ui_tooltip(item, text, 500, direction, self) return tooltip_id end -- Draw most-recent-action buttons function chat_system_draw_recent_buttons(self, parent, x) local w = GetWorld() local cs = w.chat_system if (cs.chat_system_display_quickbuttons ~= 1) then return nil end local bar_table = {} local bar = '' local option = 0 bar = self.chat_system_recent_emotes[1][1] option = self.chat_system_recent_emotes[1][2] bar_table = EMOTE_BAR_BUTTONS if (bar == 'action') then bar_table = ACTION_BAR_BUTTONS end local data = bar_table[option] if (data == nil) then return nil end local text = data.command if (text == nil) then return nil end local recent1_img = UiImage(parent, 'recent1_img', x, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, data.sprite) local recent1_button = UiImageButton(recent1_img, 'recent1_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_recent_button "' .. text .. '"') local tooltip = chat_system_toolbar_tooltip(self, data.tooltip, recent1_button, 'above') table.insert(self.chat_system_recent_tooltips, tooltip) bar = self.chat_system_recent_emotes[2][1] option = self.chat_system_recent_emotes[2][2] bar_table = EMOTE_BAR_BUTTONS if (bar == 'action') then bar_table = ACTION_BAR_BUTTONS end local data = bar_table[option] if (data == nil) then return nil end local text = data.command if (text == nil) then return nil end local recent2_img = UiImage(parent, 'recent2_img', x + BAR_BUTTONS.W, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, data.sprite) local recent2_button = UiImageButton(recent2_img, 'recent2_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_recent_button "' .. text .. '"') tooltip = chat_system_toolbar_tooltip(self, data.tooltip, recent2_button, 'above') table.insert(self.chat_system_recent_tooltips, tooltip) bar = self.chat_system_recent_emotes[3][1] option = self.chat_system_recent_emotes[3][2] bar_table = EMOTE_BAR_BUTTONS if (bar == 'action') then bar_table = ACTION_BAR_BUTTONS end local data = bar_table[option] if (data == nil) then return nil end local text = data.command if (text == nil) then return nil end local recent3_img = UiImage(parent, 'recent3_img', x + 2 * BAR_BUTTONS.W, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, data.sprite) local recent3_button = UiImageButton(recent3_img, 'recent3_button', 0, 0, BAR_BUTTONS.W, BAR_BUTTONS.H, BAR_BUTTONS.BN, BAR_BUTTONS.BH, BAR_BUTTONS.BP, 'chat_system_recent_button "' .. text .. '"') tooltip = chat_system_toolbar_tooltip(self, data.tooltip, recent3_button, 'above') table.insert(self.chat_system_recent_tooltips, tooltip) end function chat_system_update_quick_buttons(self, data, bar, option) local found = 0 local recent_data = {} for _, recent_data in ipairs(self.chat_system_recent_emotes) do local bar_table = EMOTE_BAR_BUTTONS if (recent_data[1] == 'action') then bar_table = ACTION_BAR_BUTTONS end local compare_data = bar_table[recent_data[2]] if (compare_data ~= nil) then if ( data.command == compare_data.command ) then found = 1 break end end end if (found == 1) then return nil end -- A new command was found; update! self.chat_system_recent_emotes[1] = table.deepcopy(self.chat_system_recent_emotes[2]) self.chat_system_recent_emotes[2] = table.deepcopy(self.chat_system_recent_emotes[3]) self.chat_system_recent_emotes[3] = {bar, option} local ui_id = 0 for _, ui_id in ipairs(self.chat_system_recent_tooltips) do UiDelete(ui_id) end local i = 0 for i = 1, 3 do recent_data = self.chat_system_recent_emotes[i] local bar_table = EMOTE_BAR_BUTTONS if (recent_data[1] == 'action') then bar_table = ACTION_BAR_BUTTONS end local button_data = bar_table[recent_data[2]] if (button_data ~= nil) then ui_id = UiFindWindow(self.chat_system_window, 'recent' .. i .. '_img') if (ui_id ~= 0) then UiArt(ui_id, button_data.sprite) local button_id = UiFindWindow(ui_id, 'recent' .. i .. '_button') UiCommand(button_id, 'chat_system_recent_button "' .. button_data.command .. '"') local tooltip = chat_system_toolbar_tooltip(self, button_data.tooltip, button_id, 'above') table.insert(self.chat_system_recent_tooltips, tooltip) end end end end function chat_system_reset_ui_state(self, reset_all) self.chat_emote_throw_object = 'none' self.chat_emote_suppress_profile = 0 UiSetCursor(self, -1, 0, 0) local bar_id = 0 if (reset_all == 1) then bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_mood') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_action') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end bar_id = UiFindWindow(self.chat_system_window, 'chat_bar_emote') if (bar_id ~= 0) then chat_system_toggle_bar(bar_id, 0) end end end function chat_system_store_history(self, entry) local history = self.chat_system_private_history if (history ~= nil) then while ( (#history >= PRIVATE_HISTORY_MAX) and (#history > 0) ) do table.remove(history, 1) end table.insert(history, entry) end end -- Convert URLs into links function chat_system_urls(text) return string.gsub(text,"(https?://[^%s'\"<]+)","%1") end