-- 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