-- player_chat_bubbles - Core logic, properties, and data for the Chat system -- (component of Chat System) -- Template attachment: chat_system -- Created by: Patrick Ferland -- Created on: 06/09/2008 -- Revision History -- 06/09/2008 P.Ferland - Made bubbles player-draw instead of place-draw -- 06/10/2008 P.Ferland - Improved size adjustment algorithm. Now adjusts width, too! -- 06/12/2008 P.Ferland - No, really, width is adjusted now. (Fixed bug that forced min-width incorrectly) -- 06/27/2008 P.Ferland - Winstyle cleanup -- 07/01/2008 P.Ferland - Modified to use stylesheet winstyles -- 07/01/2008 P.Ferland - Added bubble tracking to push consecutive bubbles up per-user -- 07/11/2008 P.Ferland - Corrected error that prevented user profanity setting from taking priority over system -- 07/18/2008 P.Ferland - Added support for ignored player list -- 08/01/2008 P.Ferland - Added emote winstyle -- 08/07/2008 J.McNab - Modified bubble text area alpha -- 08/18/2008 P.Ferland - Made bubble abort when chat system is sender and improved name parsing to handle XW channels -- 08/29/2008 P.Ferland - Improved bubble sizing heuristic (upper and lower case) -- 09/24/2008 P.Ferland - More sizing heuristic adjustments (extended uppercase to "larger" letter set) -- 11/06/2008 P.Ferland - Made chat bubbles use TextBox for link clickiness, display on "autochat" command, and attach earlier -- Constants -- Character widths and fudge factor for wordwrap CHAR_WIDTH_SMALLER = 7.8 CHAR_WIDTH_BIGGER = 10.2 -- Fudge factor BASE_WIDTH = 20 -- Row-of-text height CHAR_HEIGHT = 12 -- Maximum bubble width MAX_WIDTH = 200 -- Minimum bubble width MIN_WIDTH = 75 LAYOUT = { spout = { w = 29, h = 25 }, chat = { ws = '11303:58', spout = 114 }, emote = { ws = '11303:62', spout = -1 }, think = { ws = '11303:58', spout = 115 } } MOOD_ICONS = { ['angry'] = '11302:477', ['happy'] = '11302:484', ['normal'] = '-1:-1', ['sad'] = '11302:469', ['scared'] = '11302:470', } -- Script properties Define Properties() chat_system_active_bubbles = {} IncludeScript('11302:12') -- chat_utility IncludeScript('11302:8') -- chat_profanity end -- Commands -- no commands -- Triggers -- Fade a bubble out Trigger chat_system_bubbles_fade(bubble_id) UiVisible(bubble_id, 0, 1000) SendTo(self, 'chat_system_bubbles_delete', 1000, bubble_id) end -- Delete a bubble (called post-fade) Trigger chat_system_bubbles_delete(bubble_id) UiDelete(bubble_id) self.chat_system_active_bubbles[bubble_id] = nil end -- Chat system core trigger Trigger chat_system_message(message, channel, sender, command) local w = GetWorld() local cs = w.chat_system if (cs.chat_system_bubbles_enabled == 1) then if (sender.name ~= nil) then if (sender.type == 'player') then if (IsSuperuser(sender) ~= 1) then if (self.chat_system_ignore[string.lower(sender.name)] ~= nil) then -- Non-superuser on ignore list; clear message and call it a day message = '' return end end end end local profanity = 0 if (cs.chat_system_profanity_filter_user_change == 1) then profanity = self.chat_system_profanity_filter else profanity = cs.chat_system_profanity_filter end local bubble_message = message if (profanity == 1) then bubble_message = chat_system_profanity_nice(message) end if ( (command == 'chat') or (command == 'gsay') or (command == 'autochat') ) then -- chat bubble -- Chat is unusual; we may want to remove the sender's name. local chat_message = bubble_message if (sender.name ~= nil) then local lower_message = string.lower(bubble_message) local name_index = string.find(bubble_message, sender.name .. ':') if ( name_index ~= nil ) then local start = string.find(bubble_message, ': ') + 1 while ( (string.sub(bubble_message, start, start) == ' ') and (start < string.len(bubble_message) ) ) do start = start + 1 end if (start <= string.len(bubble_message) ) then chat_message = string.sub(bubble_message, start, string.len(bubble_message)) else chat_message = '' end end end chat_system_bubbles_draw(self, sender, chat_message, 'chat') elseif (command == 'emote') then -- emote bubble chat_system_bubbles_draw(self, sender, bubble_message, 'emote') elseif (command == 'roll') then -- emote bubble chat_system_bubbles_draw(self, sender, bubble_message, 'emote') elseif (command == 'think') then -- think bubble chat_system_bubbles_draw(self, sender, bubble_message, 'think') end end SendTo(self, 'chat_system_message_done', 0, message, channel, sender, command) end -- Local functions -- Draw a bubble function chat_system_bubbles_draw(self, sender, message, command) local world = GetWorld() local cs = world.chat_system if (cs == nil) then return end if (sender.id == cs.id) then return end if (cs.chat_system_bubbles_enabled ~= 1) then return end local l = LAYOUT[command] if (l == nil) then return end local w, h = chat_system_bubbles_estimate_size(message) local x = 0 - math.floor(w / 2) local y = 0 - LAYOUT.spout.h - h - cs.chat_system_bubbles_offset local bubble_id = UiWindow(0, 'chat_bubble', x, y, w, h, LAYOUT[command].ws) UiAttachUserObject(self, sender, bubble_id) UiVisible(bubble_id, 1) local sx = math.floor(w / 2) local sy = h - 2 local spout = UiImage(bubble_id, 'chat_bubble_spout', sx, sy, LAYOUT.spout.w, LAYOUT.spout.h, l.spout) local tx = 7 local ty = 7 local tw = w - 14 local th = h - 14 local text = UiTextBox(bubble_id, 'chat_bubble_text', tx, ty, tw, th, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0) UiAddText(text, message) local mood = sender.chat_system_mood if (mood == nil) then mood = 'normal' end UiImage(bubble_id, 'chat_bubble_mood', -12, -12, 24, 24, MOOD_ICONS[mood]) local other_bubble_id = 0 local other_bubble = {} if (self.chat_system_active_bubbles ~= nil) then for other_bubble_id, other_bubble in pairs(self.chat_system_active_bubbles) do if (other_bubble[1] == sender.id) then local other_spout = UiFindWindow(other_bubble_id, 'chat_bubble_spout') UiVisible(other_spout, 0) UiSlide(other_bubble_id, 0, -1 * (5 + other_bubble[2]), 0, 0, 0) end end end self.chat_system_active_bubbles[bubble_id] = {sender.id, h} SendTo(self, 'chat_system_bubbles_fade', 5000, bubble_id) end -- Estimate the required size of a chat bubble function chat_system_bubbles_estimate_size(message) -- Strip out any HTML before calculating length message = string.gsub(message, "%b<>", "") -- Calculate size, making guesses about capital letters local biggercount = 0 local letter = 'a' for letter in string.gfind(message, "[wm%u]") do biggercount = biggercount + 1 end local smallercount = string.len(message) - biggercount local char_size = smallercount * CHAR_WIDTH_SMALLER + biggercount * CHAR_WIDTH_BIGGER + BASE_WIDTH local width = MIN_WIDTH if (char_size > MAX_WIDTH) then width = MAX_WIDTH elseif (char_size < MIN_WIDTH) then width = MIN_WIDTH else width = char_size end local char_height = CHAR_HEIGHT * (math.floor(char_size / MAX_WIDTH) + 1) + 25 return width, char_height end