-- profile_window - Prototype of player profile -- Template attachment: player (?) -- Created by: Patrick Ferland -- Created on: 05/13/2008 -- Revision History -- 05/13/2008 P.Ferland - New script -- 05/15/2008 P.Ferland - Fleshed out and first demo -- 06/10/2008 P.Ferland - Collected changes: -- World previews now properly wired into PHP Renderer -- Replaced Heroes with Favs (Favorite worlds) -- Added Metamail and Add Friend buttons -- Added tooltips for all left-panel buttons -- Exported as module miniprofile_test -- 06/12/2008 P.Ferland - Fixed incorrect presence detection logic -- 06/16/2008 P.Ferland - Fixed bad syntax on world listing service call -- 06/28/2008 P.Ferland - Converted winstyle property to string, added deletion -- 07/01/2008 P.Ferland - Multiple updates -- Changed fans, favorites, and worlds to use new Web API calls -- Added module dependency to 11746 mpwinstyles_core -- Converted to Stylesheet winstyles and removed deletion -- 07/11/2008 P.Ferland - Fixed badges to add tooltip and display fullsize -- 07/29/2008 P.Ferland - Added drag persistence and F&F beta cleanup -- Home now goes to user's profile -- Smarter badge URL parsing to reduce 404s -- Close button for World Preview in expected place -- Ignore/Unignore button added -- Goto World in current window -- 07/30/2008 P.Ferland - Fixed friend addition -- 08/01/2008 P.Ferland - Modified to use ui_library -- 08/04/2008 P.Ferland - Updated world listing to use newest API -- 09/12/2008 P.Ferland - Removed "default play image" -- 11/04/2008 P.Ferland - Updated maprenderer to use new service URL -- Listens for these triggers: -- no listen -- Broadcasts these triggers: -- no broadcast -- Constants WEB_SERVER = 'https://alpha.metaplace.com' PUBLIC_API = '/api/v0.1/public/' RENDERING_SERVICE = 'http://assets.metaplace.com/tools/schema/map/' DEFAULT_USER_IMAGE = 'https://assets.metaplace.com/worlds/metaplace/assets/images/default_user_medium.jpg' DEFAULT_WORLD_IMAGE = '13197:164' BLANK_PNG_ID = 25 MONTHS = { ['01'] = 'Jan', ['02'] = 'Feb', ['03'] = 'Mar', ['04'] = 'Apr', ['05'] = 'May', ['06'] = 'Jun', ['07'] = 'Jul', ['08'] = 'Aug', ['09'] = 'Sep', ['10'] = 'Oct', ['11'] = 'Nov', ['12'] = 'Dec' } -- Layout hints LAYOUT = { -- X/Y offset from originating object X_OFF = -260, Y_OFF = 10, -- X/Y offset with screen attachment X_SCR = 0, Y_SCR = 10, -- Width and height of profile window W = 250, H = 190, -- Divider DIV_X1 = 100, DIV_Y1 = 24, DIV_X2 = 100, DIV_Y2 = 187, -- Title (name) location TITLE_X = 8, TITLE_Y = 0, -- MP Status Indicator STATUS_X = 8, STATUS_Y = 30, STATUS_W = 8, STATUS_H = 8, ONLINE = 26, OFFLINE = 27, CURRENT_X = 16, CURRENT_Y = 22, CURRENT_W = 90, CURRENT_H = 22, -- Profile Image IMAGE_X = 11, IMAGE_Y = 44, IMAGE_W = 80, IMAGE_H = 80, -- Details text DETAIL_X = 10, DETAIL_Y = 123, -- History HBACK_X = 38, HHOME_X = 52, HFWD_X = 66, HISTORY_Y = 140, HISTORY_W = 12, HISTORY_H = 12, HISTORYBACK = { N = '13197:163', H = '13197:162', P = '13197:139' }, HISTORYHOME = { N = '13197:147', H = '13197:146', P = '13197:148' }, HISTORYNEXT = { N = '13197:159', H = '13197:158', P = '13197:160' }, -- Toolbar Commands TOOLBAR_Y = 140, TOOLBAR_W = 12, TOOLBAR_H = 12, TMAIL_X = 24, TADDFRIEND_X = 80, TOOLBARMAIL = { N = '13197:153', H = '13197:152', P = '13197:154' }, TOOLBARADDFRIEND = { N = '13197:144', H = '13197:143', P = '13197:145' }, TIGNORE_X = 10, TOOLBARIGNORE = { N = '13197:150', H = '13197:149', P = '13197:151', }, -- User options OPTIONS_X = 0, OPTIONS_Y = 154, OPTIONS_W = 36, OPTIONS_H = 48, OPTIONS_2_Y = 168, OPTIONS_BH = 12, OFRIENDS_W = 39, OFANS_W = 25, OHEROES_W = 37, OFRIENDS_X = 4, OFAVS_X = 68, OFAVS_W = 25, OFANS_X = 42, OBADGES_X = 8, OBADGES_W = 47, OWORLDS_X = 50, OWORLDS_W = 39, -- Close button CLOSE_X = 232, CLOSE_Y = 3, CLOSE_W = 12, CLOSE_H = 13, CB_N = 21, CB_H = 20, CB_P = 22, -- Detail pane PANE_X = 108, PANE_Y = 24 } FRIENDS = { TITLE_X = 2, TITLE_Y = 0, LOADING_X = 10, LOADING_Y = 30, LIST_X = 2, LIST_Y = 26, LIST_W = 130, LIST_H = 100, STATUS_X = 2, STATUS_Y = 138, STATUS_W = 8, STATUS_H = 8, THUMB_X = 14, THUMB_Y = 130, THUMB_W = 24, THUMB_H = 24, NAME_X = 40, NAME_Y = 127, VIEW_X = 38, VIEW_Y = 142, VIEW_W = 64, VIEW_H = 12 } BADGES = { LIST_H = 80, X = 2, Y = 111, W = 48, H = 48, DESC_X = 52, DESC_Y = 102, DESC_W = 85, DESC_H = 90 } WORLDS = { PREVIEW_X = 2, PREVIEW_Y = 4, PREVIEW_W = 130, PREVIEW_H = 130, PLAY_X = 18, PLAY_Y = 130, PLAY_W = 120, PLAY_H = 40, CLOSE_X = 4, CLOSE_Y = 140, CLOSE_W = 12, CLOSE_H = 12, CB_N = '13197:163', CB_H = '13197:162', CB_P = '13197:139' } DEF_STYLE = { TL = 19, TC = 18, TR = 11, ML = 16, MC = 15, MR = 17, BL = 13, BC = 12, BR = 14 } FRIEND_NOTE = { X = 0, Y = 0, W = 150, H = 120, R = 96, G = 96, B = 96, BR = 255, BG = 255, BB = 255, BA = 0.0 } -- Script properties Define Properties() -- UI handle for profile window profile_window = 0 -- Tooltip handles profile_tooltips = { tignore = 0, tmail = 0, hprev = 0, hhome = 0, hnext = 0, taddfriend = 0, status = 0, friends = 0, fans = 0, favs = 0, badges = 0, worlds = 0, badge = 0, worldback = 0, } -- "Pane" of profile profile_pane = 0 -- Profile browsing history profile_history = {} profile_location = 0 -- Prevention for fast clicking problems profile_trigger_history = {} profile_current_world = '' -- Drag Memory profile_drag_offset = { x = 0, y = 0 } PersistProperty('profile_drag_offset') --IncludeScript('11746:2') -- mpwinstyles_core IncludeScript('13197:21') -- ui_library end -- Commands Define Commands() MakeCommand('profile_close_window', 'Close profile window') MakeCommand('profile_friends', 'Look up friends list', 'user_name:string') MakeCommand('profile_friend_mini', 'Get a mini-profile (preview) of a friend', 'user_name:string') MakeCommand('profile_fans', 'Look up friends list', 'user_name:string') --MakeCommand('profile_heroes', 'Look up friends list', 'user_name:string') MakeCommand('profile_mail', 'Send Metamail', 'user_name:string') MakeCommand('profile_addfriend', 'Send request to add Friend', 'user_name:string') MakeCommand('profile_badges', 'Look up badges list', 'user_name:string') MakeCommand('profile_badge_mini', 'Get a preview of a badge', 'badge_data:string') MakeCommand('profile_world_mini', 'Get a preview of a world', 'world_clean_name:string') MakeCommand('profile_favs', 'Look up favorite worlds list', 'user_name:string') MakeCommand('profile_fav_users', 'Look up favorite users list', 'user_name:string') MakeCommand('profile_world_close_mini', 'Close preview of a world') MakeCommand('profile_toggle_ignore', 'Ignore or unignore user in chat', 'user_name:string') MakeCommand('profile_next', 'Go forward in profiles') MakeCommand('profile_prev', 'Go backward in profiles') MakeCommand('profile_home', 'Go to first profile (self)') MakeCommand('profile', 'Display arbitrary profile', 'user_name:string') MakeCommand('profile_nohistory', 'Display arbitrary profile (without adding to history)', 'user_name:string') end Command profile_close_window() profile_close_window(self) end Command profile_home() DoCommand(self, 'profile', self.name) end Command profile_next() if (self.profile_location < #self.profile_history) then self.profile_location = self.profile_location + 1 DoCommand(self, 'profile_nohistory', self.profile_history[self.profile_location]) end end Command profile_prev() if (self.profile_location > 1) then self.profile_location = self.profile_location - 1 DoCommand(self, 'profile_nohistory', self.profile_history[self.profile_location]) end end Command profile_friends(user_name) -- Request friends data local friends_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/friends/' .. user_name), method = "GET", trigger = "user_friends_response", body = "" } WebRequest(self, friends_request) -- Draw pane and title only while waiting for callback profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Friends')) local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, profile_italic_text('loading...')) UiColor(loading, 96, 96, 96, 1) end Command profile_toggle_ignore(user_name) local w = GetWorld() local cs = w.chat_system if (cs == nil) then return end local lower_name = string.lower(user_name) if (self.chat_system_ignore ~= nil) then if (self.chat_system_ignore[lower_name] == nil) then SendTo(cs, 'chat_msg_ignore', 0, self, lower_name, lower_name) else SendTo(cs, 'chat_msg_unignore', 0, self, lower_name, lower_name) end profile_format_ignore_button(self, user_name) end end Command profile_mail(user_name) local browse_tag = '[S_BROWSE]|' .. WEB_SERVER .. '/mymeta/messages/compose/' .. user_name OutputToUser(self, browse_tag) end Command profile_addfriend(user_name) -- Send friend request local addfriend_request = { url = urlescape(WEB_SERVER .. '/api/v0.1/public/open_friends/add_friend/QyLMPLb0DzX2IwMU/' .. self.name .. '/' .. user_name), method = "GET", trigger = "user_addfriend_response", body = "" } WebRequest(self, addfriend_request) end Command profile_badges(user_name) -- Request friends data local friends_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/mybadges/' .. user_name), method = "GET", trigger = "user_badges_response", body = "" } WebRequest(self, friends_request) -- Draw pane and title only while waiting for callback profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Badges')) local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, profile_italic_text('loading...')) UiColor(loading, 96, 96, 96, 1) end Command profile_favs(user_name) -- Request friends data local friends_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/favoriteworlds/' .. user_name), method = "GET", trigger = "world_favs_response", body = "" } WebRequest(self, friends_request) Debug(friends_request.url) -- Draw pane and title only while waiting for callback profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Favorite Worlds')) local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, profile_italic_text('loading...')) UiColor(loading, 96, 96, 96, 1) end Command profile_fav_users(user_name) -- Request friends data local friends_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/favoriteusers/' .. user_name), method = "GET", trigger = "user_favs_response", body = "" } WebRequest(self, friends_request) Debug(friends_request.url) -- Draw pane and title only while waiting for callback profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Favorite Users')) local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, profile_italic_text('loading...')) UiColor(loading, 96, 96, 96, 1) end Command profile_fans(user_name) -- Request friends data local friends_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/favoritedme/' .. user_name), method = "GET", trigger = "user_fans_response", body = "" } WebRequest(self, friends_request) -- Draw pane and title only while waiting for callback profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Fans')) local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, profile_italic_text('loading...')) UiColor(loading, 96, 96, 96, 1) end Command profile_friend_mini(user_name) -- Request presence data local presence_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/mypresence/' .. user_name), method = "GET", trigger = "friend_presence_response", body = "" } WebRequest(self, presence_request) -- Request profile data local profile_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/profile/' .. user_name), method = "GET", trigger = "friend_profile_response", body = "" } WebRequest(self, profile_request) end Command profile_badge_mini(badge_data) local badge_table = string.split(badge_data, '`', 2) local badge_url = badge_table[1] badge_url = string.gsub(badge_url, ' ', '%%20') if (string.find(badge_url, 'http') == nil) then badge_url = WEB_SERVER .. badge_url end local badge_desc = badge_table[2] local pane_badge_holder = UiFindWindow(self.profile_pane, 'pane_badge_holder') if (pane_badge_holder ~= 0) then UiDelete(pane_badge_holder) end pane_badge_holder = UiRect(self.profile_pane, 'pane_badge_holder', BADGES.X, BADGES.Y, BADGES.W, BADGES.H) UiColor(pane_badge_holder, 255, 255, 255, 1) local pane_badge = UiImageRef(pane_badge_holder, 'pane_badge', 0, 0, BADGES.W, BADGES.H, badge_url) UiVisible(pane_badge_holder, 1) local pane_desc = UiFindWindow(self.profile_pane, 'pane_desc') if (pane_desc == 0) then pane_desc = UiMultiLabel(self.profile_pane, 'pane_desc', BADGES.DESC_X, BADGES.DESC_Y, BADGES.DESC_W, BADGES.DESC_H, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) end UiText(pane_desc, badge_desc) end Command profile_world_mini(world_clean_name) local pane_preview = UiFindWindow(self.profile_pane, 'pane_preview') if (pane_preview ~= 0) then UiDelete(pane_preview) end pane_preview = UiRect(self.profile_pane, 'pane_preview', WORLDS.PREVIEW_X, WORLDS.PREVIEW_Y, WORLDS.PREVIEW_W, WORLDS.PREVIEW_H) UiColor(pane_preview, 0, 0, 0, 1) local pane_preview_img = UiImage(pane_preview, 'pane_preview_img', 0, 0, WORLDS.PREVIEW_W, WORLDS.PREVIEW_H, DEFAULT_WORLD_IMAGE) local pane_world_play= UiFindWindow(self.profile_pane, 'pane_world_play') if (pane_world_play == 0) then pane_world_play = UiMultiLabel(self.profile_pane, 'pane_world_play', WORLDS.PLAY_X, WORLDS.PLAY_Y, WORLDS.PLAY_W, WORLDS.PLAY_H, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) end UiText(pane_world_play, profile_link_text('Play ' .. world_clean_name, urlescape(WEB_SERVER .. '/' .. world_clean_name .. '/play'))) local pane_close_button = UiFindWindow(self.profile_pane, 'pane_close_button') if (pane_close_button == 0) then pane_close_button = UiImageButton(self.profile_pane, 'pane_close_button', WORLDS.CLOSE_X, WORLDS.CLOSE_Y, WORLDS.CLOSE_W, WORLDS.CLOSE_H, WORLDS.CB_N, WORLDS.CB_H, WORLDS.CB_P, 'profile_world_close_mini') end -- Send a web trigger to "pre-load" the world preview -- Request profile data local world_preview_request = { url = urlescape(RENDERING_SERVICE .. world_clean_name .. '.jpg?width=' .. WORLDS.PREVIEW_W .. '&height=' .. WORLDS.PREVIEW_H), method = "GET", trigger = "world_preview_response", body = "" } WebRequest(self, world_preview_request) self.profile_current_world = world_clean_name end Command profile_world_close_mini() Debug('worldback tip ' .. self.profile_tooltips.worldback) if (self.profile_tooltips.worldback ~= 0) then UiDelete(self.profile_tooltips.worldback) self.profile_tooltips.worldback = 0 end local pane_preview = UiFindWindow(self.profile_pane, 'pane_preview') if (pane_preview ~= 0) then UiDelete(pane_preview) end local pane_world_play = UiFindWindow(self.profile_pane, 'pane_world_play') if (pane_world_play ~= 0) then UiDelete(pane_world_play) end local pane_close_button = UiFindWindow(self.profile_pane, 'pane_close_button') if (pane_close_button ~= 0) then UiDelete(pane_close_button) end end Command profile(user_name) if (#self.profile_history > 0) then if (self.profile_history[#self.profile_history] ~= user_name) then table.insert(self.profile_history, user_name) self.profile_location = #self.profile_history end end profile_display_user_by_name(self, user_name) end Command profile_nohistory(user_name) profile_display_user_by_name(self, user_name) end -- Triggers -- System trigger fired when script is attached Trigger use(user) -- Coupled to chat system now. Update this once we do Use 2.0 if ( user.chat_emote_suppress_profile ~= 1 ) then SendTo(user, 'profile_view_user', 0, self) else user.chat_emote_suppress_profile = 0 end end -- Called when profile_view is selected for a user Trigger profile_view_user(user) table.clear(self.profile_history) table.insert(self.profile_history, user.name) self.profile_location = 1 profile_display_user_by_name(self, user.name) end Trigger world_preview_response(response, body) Debug('world resp') local pane_preview = UiFindWindow(self.profile_pane, 'pane_preview') if (pane_preview == 0) then return -- Could not find the world preview anymore! end -- Now that the preview has been loaded... local new_pane_preview = UiImageRef(self.profile_pane, 'pane_preview', WORLDS.PREVIEW_X, WORLDS.PREVIEW_Y, WORLDS.PREVIEW_W, WORLDS.PREVIEW_H, RENDERING_SERVICE .. self.profile_current_world .. '.jpg?width=' .. WORLDS.PREVIEW_W .. '&height=' .. WORLDS.PREVIEW_H) UiDelete(pane_preview) -- Redraw the now-closed button... local pane_close_button = UiFindWindow(self.profile_pane, 'pane_close_button') if (pane_close_buttton ~= 0) then UiDelete(pane_close_button) end pane_close_button = UiImageButton(self.profile_pane, 'pane_close_button', WORLDS.CLOSE_X, WORLDS.CLOSE_Y, WORLDS.CLOSE_W, WORLDS.CLOSE_H, WORLDS.CB_N, WORLDS.CB_H, WORLDS.CB_P, 'profile_world_close_mini') self.profile_tooltips.worldback = profile_tooltip(self, 'Back to Worlds list', pane_close_button) end Trigger user_profile_response(response, body) local profile_data = XmlParse(body) local registration_date = string.split(profile_data[1].attr.registerDate, " ")[1] --local user_url = profile_data[1].attr.url local reg_text = '' local link_text = '' --if ( (user_url ~= nil) and (user_url ~= '') ) then -- local link_text = profile_link_text('web', user_url) .. ' ' --end local reg_date_pieces = string.split(registration_date, "-") reg_text = 'Joined ' .. MONTHS[reg_date_pieces[2]] .. ' ' .. reg_date_pieces[1] local ui_detail_text = UiLabel(self.profile_window, 'profile_user_detail', LAYOUT.DETAIL_X, LAYOUT.DETAIL_Y, link_text .. reg_text) UiColor(ui_detail_text, 96, 96, 96, 1) local image_url = profile_data[1].attr.icon_medium if ( (image_url ~= nil) and (image_url ~= '') and (image_url ~= 'public') ) then -- Load image url into 'profile_image' element local player_image = UiFindWindow(self.profile_window, 'profile_image') UiSetUrl(player_image, image_url) end end Trigger user_friends_response(response, body) local friends_data = XmlParse(body) local friend = {} local friends_table = {} for _, friend in ipairs(friends_data) do --{ table.insert(friends_table, friend.attr.username) --} end table.sort(friends_table, function (a,b) return string.lower(a) < string.lower(b) end) -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Friends')) if (#friends_table > 0) then local friends_list = UiListBox(self.profile_pane, 'friends_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, FRIENDS.LIST_H, 'profile_friend_mini') for _, friend in ipairs(friends_table) do UiListItem(friends_list, friend, friend) end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No friends found. :(') UiColor(loading, 96, 96, 96, 1) end end Trigger user_favs_response(response, body) local friends_data = XmlParse(body) local friend = {} local friends_table = {} for _, friend in ipairs(friends_data) do --{ table.insert(friends_table, friend.attr.favorite) --} end table.sort(friends_table, function (a,b) return string.lower(a) < string.lower(b) end) -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Favorite Users')) if (#friends_table > 0) then local friends_list = UiListBox(self.profile_pane, 'friends_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, FRIENDS.LIST_H, 'profile_friend_mini') for _, friend in ipairs(friends_table) do UiListItem(friends_list, friend, friend) end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No favorite users. :(') UiColor(loading, 96, 96, 96, 1) end end Trigger user_addfriend_response(response, body) local friends_data = XmlParse(body) local friend_notify_win = ui_default_window(0, 'profile_friend_notification', FRIEND_NOTE.X, FRIEND_NOTE.Y, FRIEND_NOTE.W, FRIEND_NOTE.H) local friend_notification = UiMultiLabel(friend_notify_win, 'friend_notificaiton', 10, 30, FRIEND_NOTE.W - 20, FRIEND_NOTE.H - 40, FRIEND_NOTE.R, FRIEND_NOTE.G, FRIEND_NOTE.B, FRIEND_NOTE.BR, FRIEND_NOTE.BG, FRIEND_NOTE.BB, FRIEND_NOTE.BA, 0, 0, 2) UiText(friend_notification, '' .. friends_data[1] .. '') UiAlign(friend_notify_win, 0, 0, 'center', 'scale_none') UiAttachUser(self, friend_notify_win) UiVisible(friend_notify_win, 1) end Trigger profile_fade_friendrequest(window_id) UiVisible(window_id, 0, 1000) SendTo(self, 'profile_clear_friendrequest', 1000, window_id) end Trigger profile_fade_friendrequest(window_id) UiDelete(window_id) end Trigger user_badges_response(response, body) local badges_data = XmlParse(body) local badge= {} local badges_table = {} for _, badge in ipairs(badges_data) do --{ local badge_short = {name = badge.attr.badge_name, value = badge.attr.badge_url, desc=badge[1][1]} table.insert(badges_table, badge_short) --} end table.sort(badges_table, function (a,b) return string.lower(a.name) < string.lower(b.name) end) -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Badges')) if (#badges_table > 0) then local badges_list = UiListBox(self.profile_pane, 'badgges_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, BADGES.LIST_H, 'profile_badge_mini') for _, badge in ipairs(badges_table) do UiListItem(badges_list, badge.name, '"' .. badge.value .. '`' .. badge.desc .. '"') end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No badges found. :(') UiColor(loading, 96, 96, 96, 1) end end Trigger user_fans_response(response, body) local friends_data = XmlParse(body) local friend = {} local friends_table = {} for _, friend in ipairs(friends_data) do --{ table.insert(friends_table, friend.attr.username) --} end table.sort(friends_table, function (a,b) return string.lower(a) < string.lower(b) end) -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Fans')) if (#friends_table > 0) then local friends_list = UiListBox(self.profile_pane, 'friends_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, FRIENDS.LIST_H, 'profile_friend_mini') for _, friend in ipairs(friends_table) do UiListItem(friends_list, friend, friend) end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No fans found. :(') UiColor(loading, 96, 96, 96, 1) end end Trigger user_presence_response(response, body) local presence_data = XmlParse(body) local status_sprite = LAYOUT.OFFLINE local world_name = 'Offline' local world_clean_name = '' if (presence_data ~= nil) then local w = GetWorld() -- Search for match in this world; failing, take most recent world local data_table= {} local fallback_date = '1970-01-01 00:00:00' local fallback_name = 'Offline' local fallback_clean = '' for _, data_table in ipairs(presence_data) do if (data_table.attr.name == w.name) then -- Match! world_name = data_table.attr.name world_clean_name = data_table.attr.clean_name break elseif (data_table.attr.last_updated > fallback_date) then fallback_date = data_table.attr.last_updated fallback_name = data_table.attr.name fallback_clean = data_table.attr.clean_name end end if (world_name == 'Offline') then world_name = fallback_name world_clean_name = fallback_clean end end if (string.len(world_name) > 14) then world_name = string.sub(world_name, 1, 13) .. '...' end if (world_name ~= 'Offline') then status_sprite = LAYOUT.ONLINE end local status_icon = UiImage(self.profile_window, 'profile_status_icon', LAYOUT.STATUS_X, LAYOUT.STATUS_Y, LAYOUT.STATUS_W, LAYOUT.STATUS_H, status_sprite) local current_link = UiMultiLabel(self.profile_window, 'profile_current_link', LAYOUT.CURRENT_X, LAYOUT.CURRENT_Y, LAYOUT.CURRENT_W, LAYOUT.CURRENT_H, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) local current_link_text = world_name if (world_clean_name ~= '') then current_link_text = profile_link_text(world_name, urlescape(WEB_SERVER .. '/' .. world_clean_name)) end UiText(current_link, current_link_text) UiColor(current_link, 96, 96, 96, 1, 'text') if (status_sprite == LAYOUT.OFFLINE) then self.profile_tooltips.status = profile_tooltip(self, 'Offline', status_icon) else self.profile_tooltips.status = profile_tooltip(self, 'Online now!', status_icon) end end Trigger friend_profile_response(response, body) local profile_data = XmlParse(body) local registration_date = string.split(profile_data[1].attr.registerDate, " ")[1] local user_name = profile_data[1].attr.username if (string.len(user_name) > 20) then user_name = string.sub(user_name, 1, 20) end local pane_image= UiFindWindow(self.profile_pane, 'pane_image') if (pane_image== 0) then pane_image= UiImageRef(self.profile_pane, 'pane_image', FRIENDS.THUMB_X, FRIENDS.THUMB_Y, FRIENDS.THUMB_W, FRIENDS.THUMB_H, DEFAULT_USER_IMAGE) end local image_url = profile_data[1].attr.icon_thumb if ( (image_url ~= nil) and (image_url ~= '') and (image_url ~= '1') ) then UiSetUrl(pane_image, image_url) end local pane_name = UiFindWindow(self.profile_pane, 'pane_name') if (pane_name == 0) then pane_name = UiLabel(self.profile_pane, 'pane_name', FRIENDS.NAME_X, FRIENDS.NAME_Y, user_name) UiColor(pane_name, 96, 96, 96, 1) else UiText(pane_name, '' .. user_name .. '') end local view_friend = UiFindWindow(self.profile_pane, 'view_friend') if (view_friend ~= 0) then UiDelete(view_friend) end view_friend = flat_text_button_hack(self.profile_pane, 'view_friend', FRIENDS.VIEW_X, FRIENDS.VIEW_Y, FRIENDS.VIEW_W, FRIENDS.VIEW_H, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('view profile')), 'profile "' .. user_name .. '"') end Trigger friend_presence_response(response, body) local presence_data = XmlParse(body) local status_sprite = LAYOUT.OFFLINE if (#presence_data > 0) then status_sprite = LAYOUT.ONLINE end local pane_status = UiFindWindow(self.profile_pane, 'pane_status') if (pane_status ~= 0) then UiDelete(pane_status) end pane_status = UiImage(self.profile_pane, 'pane_status', FRIENDS.STATUS_X, FRIENDS.STATUS_Y, FRIENDS.STATUS_W, FRIENDS.STATUS_H, status_sprite) end Trigger user_worlds_response(response, body) local worlds_data = XmlParse(body) local world = {} local worlds_table = {} if (worlds_data ~= nil) then for _, world in ipairs(worlds_data) do --{ world = { name = world.attr.name, clean_name = world.attr.clean_name } table.insert(worlds_table, world) --} end table.sort(worlds_table, function (a,b) return string.lower(a.name) < string.lower(b.name) end) end -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Worlds')) if (#worlds_table > 0) then local worlds_list = UiListBox(self.profile_pane, 'worlds_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, FRIENDS.LIST_H, 'profile_world_mini') for _, world in ipairs(worlds_table) do UiListItem(worlds_list, world.name , world.clean_name) end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No worlds found. :(') UiColor(loading, 96, 96, 96, 1) end end Trigger world_favs_response(response, body) local worlds_data = XmlParse(body) local world = {} local worlds_table = {} for _, world in ipairs(worlds_data) do local new_world = {} new_world = { name = world.attr.favorite, clean_name = world.attr.clean_name } table.insert(worlds_table, new_world) end table.sort(worlds_table, function (a,b) return string.lower(a.name) < string.lower(b.name) end) -- Clear the pane. This may look redundant, but multiple callbacks could be competing here... profile_close_pane(self) self.profile_pane = UiElement(self.profile_window, 'profile_pane', LAYOUT.PANE_X, LAYOUT.PANE_Y) local title_label = UiMultiLabel(self.profile_pane, 'profile_title', FRIENDS.TITLE_X, FRIENDS.TITLE_Y, LAYOUT.W / 2, 24, 96, 96, 96, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text('Favorite Worlds')) if (#worlds_table > 0) then local worlds_list = UiListBox(self.profile_pane, 'worlds_list', FRIENDS.LIST_X, FRIENDS.LIST_Y, FRIENDS.LIST_W, FRIENDS.LIST_H, 'profile_world_mini') for _, world in ipairs(worlds_table) do UiListItem(worlds_list, world.name , world.clean_name) end else local loading = UiLabel(self.profile_pane, 'pane_loading', FRIENDS.LOADING_X, FRIENDS.LOADING_Y, 'No favorite worlds found :(') UiColor(loading, 96, 96, 96, 1) end end Trigger ui_move(win_id, destination_x, destination_y) if (win_id == self.profile_window) then self.profile_drag_offset.x = destination_x self.profile_drag_offset.y = destination_y SaveToDb(self) end end -- Local functions -- This function is used to draw a "flat-style" text button without hover or press effects function flat_text_button_hack(parent, name, x, y, w, h, border_r, border_g, border_b, bg_r, bg_g, bg_b, text_r, text_g, text_b, alpha, text, command) local button_base_border = UiRect(parent, name, x, y, w, h) UiColor(button_base_border, border_r, border_g, border_b, alpha) local button_base_bg = UiRect(button_base_border, name .. '_bg', 1, 1, w-2, h-2) UiColor(button_base_bg, bg_r, bg_g, bg_b, alpha) local button_label = UiLabel(button_base_bg, name .. '_text', 2, -4, text) UiColor(button_label, text_r, text_g, text_b, 1) local button_button = UiImageButton(button_base_bg, name .. '_button', 0, 0, w-2, h-2, BLANK_PNG_ID, BLANK_PNG_ID, BLANK_PNG_ID, command) UiVisible(button_base_border, 1) UiVisible(button_base_bg, 1) UiVisible(button_label, 1) UiVisible(button_button, 1) return button_base_border end -- Close all windows based on "object_locator" root function profile_close_window(self) profile_close_pane(self) if (self.profile_window ~= 0) then local profile_window = UiFindWindow(self.profile_window, 'profile_window') if (profile_window ~= 0) then UiDelete(profile_window) end self.profile_window = 0 end local key = '' local window = 0 for key, window in pairs(self.profile_tooltips) do if (window ~= 0) then UiDelete(window) self.profile_tooltips[key] = 0 end end end -- Close only profile detail pane function profile_close_pane(self) if (self.profile_pane ~= 0) then local profile_pane = UiFindWindow(self.profile_pane, 'profile_pane') if (profile_pane ~= 0) then UiDelete(profile_pane) end self.profile_pane = 0 end end -- Format a URL with link "text" function profile_link_text(text, url) return '' .. text .. '' end -- Format text as large and bold function profile_bold_large_text(text) return '' .. text .. '' end -- Format text as italics function profile_italic_text(text) return '' .. text .. '' end -- Format text as underline function profile_underline_text(text) return '' .. text .. '' end -- Format text as centered function profile_center_text(text) return '

' .. text .. '

' end function profile_display_user_by_name(self, user_name) profile_close_window(self) -- Load static components of data local base_x = 0 local base_y = 0 self.profile_window = ui_default_window(0, 'profile_window', base_x + self.profile_drag_offset.x, base_y + self.profile_drag_offset.y, LAYOUT.W, LAYOUT.H) UiVisible(self.profile_window, 0) UiAttachUser(self, self.profile_window) if ( (self.profile_drag_offset.x == 0) and (self.profile_drag_offset.y == 0) ) then UiAlign(self.profile_window, LAYOUT.X_SCR, LAYOUT.Y_SCR, 'top', 'scale_none') else UiAlign(self.profile_window, 0, 0, 'top_left', 'scale_none') end UiCapability(self.profile_window, 'drag') local title_label = UiMultiLabel(self.profile_window, 'profile_title', LAYOUT.TITLE_X, LAYOUT.TITLE_Y, LAYOUT.W / 2, 24, 0, 0, 0, 255, 255, 255, 0, 0, 0, 0) UiText(title_label, profile_bold_large_text(user_name)) local ui_user_image = UiImageRef(self.profile_window, 'profile_image', LAYOUT.IMAGE_X, LAYOUT.IMAGE_Y, LAYOUT.IMAGE_W, LAYOUT.IMAGE_H, DEFAULT_USER_IMAGE) local history_back = UiImageButton(self.profile_window, 'profile_history_back', LAYOUT.HBACK_X, LAYOUT.HISTORY_Y, LAYOUT.HISTORY_W, LAYOUT.HISTORY_W, LAYOUT.HISTORYBACK.N, LAYOUT.HISTORYBACK.H, LAYOUT.HISTORYBACK.P, 'profile_prev') local history_home = UiImageButton(self.profile_window, 'profile_history_home', LAYOUT.HHOME_X, LAYOUT.HISTORY_Y, LAYOUT.HISTORY_W, LAYOUT.HISTORY_W, LAYOUT.HISTORYHOME.N, LAYOUT.HISTORYHOME.H, LAYOUT.HISTORYHOME.P, 'profile_home') local history_next = UiImageButton(self.profile_window, 'profile_history_next', LAYOUT.HFWD_X, LAYOUT.HISTORY_Y, LAYOUT.HISTORY_W, LAYOUT.HISTORY_W, LAYOUT.HISTORYNEXT.N, LAYOUT.HISTORYNEXT.H, LAYOUT.HISTORYNEXT.P, 'profile_next') local toolbar_mail = UiImageButton(self.profile_window, 'profile_toolbar_mail', LAYOUT.TMAIL_X, LAYOUT.TOOLBAR_Y, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBARMAIL.N, LAYOUT.TOOLBARMAIL.H, LAYOUT.TOOLBARMAIL.P, 'profile_mail "' .. user_name .. '"') local toolbar_ignore = UiImageButton(self.profile_window, 'profile_toolbar_ignore', LAYOUT.TIGNORE_X, LAYOUT.TOOLBAR_Y, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBARIGNORE.N, LAYOUT.TOOLBARIGNORE.H, LAYOUT.TOOLBARIGNORE.P, 'profile_toggle_ignore "' .. user_name .. '"') profile_format_ignore_button(self, user_name) local toolbar_addfriend = UiImageButton(self.profile_window, 'profile_toolbar_addfriend', LAYOUT.TADDFRIEND_X, LAYOUT.TOOLBAR_Y, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBAR_W, LAYOUT.TOOLBARADDFRIEND.N, LAYOUT.TOOLBARADDFRIEND.H, LAYOUT.TOOLBARADDFRIEND.P, 'profile_addfriend "' .. user_name .. '"') local option_friends= flat_text_button_hack(self.profile_window, 'option_friends', LAYOUT.OFRIENDS_X, LAYOUT.OPTIONS_Y, LAYOUT.OFRIENDS_W, LAYOUT.OPTIONS_BH, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('friends')), 'profile_friends "' .. user_name .. '"') local option_fans= flat_text_button_hack(self.profile_window, 'option_fans', LAYOUT.OFANS_X, LAYOUT.OPTIONS_Y, LAYOUT.OFANS_W, LAYOUT.OPTIONS_BH, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('fans')), 'profile_fans "' .. user_name .. '"') local option_favs= flat_text_button_hack(self.profile_window, 'option_favs', LAYOUT.OFAVS_X, LAYOUT.OPTIONS_Y, LAYOUT.OFAVS_W, LAYOUT.OPTIONS_BH, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('favs')), 'profile_fav_users "' .. user_name .. '"') local option_badges= flat_text_button_hack(self.profile_window, 'option_fans', LAYOUT.OBADGES_X, LAYOUT.OPTIONS_2_Y, LAYOUT.OBADGES_W, LAYOUT.OPTIONS_BH, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('badges')), 'profile_badges "' .. user_name .. '"') local option_worlds= flat_text_button_hack(self.profile_window, 'option_heroes', LAYOUT.OWORLDS_X, LAYOUT.OPTIONS_2_Y, LAYOUT.OWORLDS_W, LAYOUT.OPTIONS_BH, 212, 212, 212, 212, 212, 212, 96, 96, 96, 0, profile_italic_text(profile_underline_text('worlds')), 'profile_favs "' .. user_name .. '"') local ui_divider = UiLine(self.profile_window, 'profile_divider_line', LAYOUT.DIV_X1, LAYOUT.DIV_Y1, LAYOUT.DIV_X2, LAYOUT.DIV_Y2) UiColor(ui_divider, 128, 128, 128, 1) UiVisible(self.profile_window, 1) -- Lay out tooltips self.profile_tooltips.tmail = profile_tooltip(self, 'Send MetaMail to ' .. user_name, toolbar_mail) -- 'Add ' .. user_name .. ' as friend' self.profile_tooltips.taddfriend = profile_tooltip(self, 'Send a Friend Request to ' .. user_name, toolbar_addfriend) self.profile_tooltips.hprev = profile_tooltip(self, 'View previous user', history_back) self.profile_tooltips.hhome = profile_tooltip(self, 'Home (my profile)', history_home) self.profile_tooltips.hnext = profile_tooltip(self, 'View next user', history_next) self.profile_tooltips.friends = profile_tooltip(self, 'View ' .. user_name .. "'s friends", option_friends) self.profile_tooltips.fans = profile_tooltip(self, 'View ' .. user_name .. "'s fans", option_fans) self.profile_tooltips.favs = profile_tooltip(self, 'View ' .. user_name .. "'s favorite users", option_favs) self.profile_tooltips.badges = profile_tooltip(self, 'View ' .. user_name .. "'s badges", option_badges) self.profile_tooltips.worlds = profile_tooltip(self, 'View ' .. user_name .. "'s favorite worlds", option_worlds) -- Request presence data local presence_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/mypresence/' .. user_name), method = "GET", trigger = "user_presence_response", body = "" } WebRequest(self, presence_request) -- Request profile data local profile_request = { url = urlescape(WEB_SERVER .. PUBLIC_API .. 'user/profile/' .. user_name), method = "GET", trigger = "user_profile_response", body = "" } WebRequest(self, profile_request) DoCommand(self, 'profile_friends', user_name) end function profile_format_ignore_button(self, user_name) local ignore_button = UiFindWindow(self.profile_window, 'profile_toolbar_ignore') if (ignore_button == 0) then return end local ignore_tooltip = 'Ignore ' .. user_name if (self.chat_system_ignore ~= nil) then if (self.chat_system_ignore[string.lower(user_name)] ~= nil) then UiTint(ignore_button, 160, 160, 160) ignore_tooltip = 'Unignore ' .. user_name else UiTint(ignore_button, 255, 255, 255) end end if (self.profile_tooltips.tignore ~= 0) then UiDelete(self.profile_tooltips.tignore) self.profile_tooltips.tignore = 0 end self.profile_tooltips.tignore= profile_tooltip(self, ignore_tooltip, ignore_button) end -- Care of Google, http://lua-users.org/lists/lua-l/2004-11/msg00171.html function urlescape(text) local escaped = string.gsub(text, "([^A-Za-z0-9_:/.,:!?&])", function(c) return string.format("%%%02X", string.byte(c)) end) return escaped end function profile_tooltip(self, text, item) local tooltip_id = ui_tooltip(item, text, 500, 'above', self) return tooltip_id end