예제 #1
0
void LLIMMgr::noteMutedUsers(LLFloaterIMPanel* floater,
								  const std::vector<LLUUID>& ids)
{
	// Don't do this if we don't have a mute list.
	LLMuteList *ml = LLMuteList::getInstance();
	if( !ml )
	{
		return;
	}

	S32 count = ids.size();
	if(count > 0)
	{
		for(S32 i = 0; i < count; ++i)
		{
			if( ml->isMuted(ids.at(i)) )
			{
				LLUIString muted = LLTrans::getString("muted_message");

				floater->addHistoryLine(muted);
				break;
			}
		}
	}
}
void LLIMMgr::noteMutedUsers(LLFloaterIMPanel* floater,
								  const LLDynamicArray<LLUUID>& ids)
{
	// Don't do this if we don't have a mute list.
	LLMuteList *ml = LLMuteList::getInstance();
	if( !ml )
	{
		return;
	}

	S32 count = ids.count();
	if(count > 0)
	{
		for(S32 i = 0; i < count; ++i)
		{
			if( ml->isMuted(ids.get(i)) )
			{
				LLUIString muted = sMutedMessage;
				floater->addHistoryLine(muted);
				break;
			}
		}
	}
}
예제 #3
0
void LLNetMap::draw()
{
	// Ansariel: Synchronize netmap scale throughout instances
	if (mScale != sScale)
	{
		setScale(sScale);
	}

 	static LLFrameTimer map_timer;
	static LLUIColor map_avatar_color = LLUIColorTable::instance().getColor("MapAvatarColor", LLColor4::white);
	static LLUIColor map_avatar_friend_color = LLUIColorTable::instance().getColor("MapAvatarFriendColor", LLColor4::white);
	static LLUIColor map_avatar_linden_color = LLUIColorTable::instance().getColor("MapAvatarLindenColor", LLColor4::blue);
	static LLUIColor map_avatar_muted_color = LLUIColorTable::instance().getColor("MapAvatarMutedColor", LLColor4::grey3);
	static LLUIColor map_track_color = LLUIColorTable::instance().getColor("MapTrackColor", LLColor4::white);
	static LLUIColor map_track_disabled_color = LLUIColorTable::instance().getColor("MapTrackDisabledColor", LLColor4::white);
	static LLUIColor map_frustum_color = LLUIColorTable::instance().getColor("MapFrustumColor", LLColor4::white);
	static LLUIColor map_frustum_rotating_color = LLUIColorTable::instance().getColor("MapFrustumRotatingColor", LLColor4::white);
	static LLUIColor map_chat_ring_color = LLUIColorTable::instance().getColor("MapChatRingColor", LLColor4::yellow);
	static LLUIColor map_shout_ring_color = LLUIColorTable::instance().getColor("MapShoutRingColor", LLColor4::red);
	
	if (mObjectImagep.isNull())
	{
		createObjectImage();
	}

	static LLUICachedControl<bool> auto_center("MiniMapAutoCenter", true);
	if (auto_center)
	{
		mCurPan = lerp(mCurPan, mTargetPan, LLCriticalDamp::getInterpolant(0.1f));
	}

	// Prepare a scissor region
	F32 rotation = 0;

	gGL.pushMatrix();
	gGL.pushUIMatrix();
	
	LLVector3 offset = gGL.getUITranslation();
	LLVector3 scale = gGL.getUIScale();

	gGL.loadIdentity();
	gGL.loadUIIdentity();

	gGL.scalef(scale.mV[0], scale.mV[1], scale.mV[2]);
	gGL.translatef(offset.mV[0], offset.mV[1], offset.mV[2]);
	
	{
		LLLocalClipRect clip(getLocalRect());
		{
			gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

			gGL.matrixMode(LLRender::MM_MODELVIEW);

			// Draw background rectangle
			LLColor4 background_color = mBackgroundColor.get();
			gGL.color4fv( background_color.mV );
			gl_rect_2d(0, getRect().getHeight(), getRect().getWidth(), 0);
		}

		// region 0,0 is in the middle
		S32 center_sw_left = getRect().getWidth() / 2 + llfloor(mCurPan.mV[VX]);
		S32 center_sw_bottom = getRect().getHeight() / 2 + llfloor(mCurPan.mV[VY]);

		gGL.pushMatrix();

		gGL.translatef( (F32) center_sw_left, (F32) center_sw_bottom, 0.f);

		static LLUICachedControl<bool> rotate_map("MiniMapRotate", true);
		if( rotate_map )
		{
			// rotate subsequent draws to agent rotation
			rotation = atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] );
			gGL.rotatef( rotation * RAD_TO_DEG, 0.f, 0.f, 1.f);
		}

		// figure out where agent is
		S32 region_width = llround(LLWorld::getInstance()->getRegionWidthInMeters());

		for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
			 iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
		{
			LLViewerRegion* regionp = *iter;
			// Find x and y position relative to camera's center.
			LLVector3 origin_agent = regionp->getOriginAgent();
			LLVector3 rel_region_pos = origin_agent - gAgentCamera.getCameraPositionAgent();
			F32 relative_x = (rel_region_pos.mV[0] / region_width) * mScale;
			F32 relative_y = (rel_region_pos.mV[1] / region_width) * mScale;

			// background region rectangle
			F32 bottom =	relative_y;
			F32 left =		relative_x;
			F32 top =		bottom + mScale ;
			F32 right =		left + mScale ;

			if (regionp == gAgent.getRegion())
			{
				gGL.color4f(1.f, 1.f, 1.f, 1.f);
			}
			else
			{
				gGL.color4f(0.8f, 0.8f, 0.8f, 1.f);
			}

			if (!regionp->isAlive())
			{
				gGL.color4f(1.f, 0.5f, 0.5f, 1.f);
			}


			// Draw using texture.
			gGL.getTexUnit(0)->bind(regionp->getLand().getSTexture());
			gGL.begin(LLRender::QUADS);
				gGL.texCoord2f(0.f, 1.f);
				gGL.vertex2f(left, top);
				gGL.texCoord2f(0.f, 0.f);
				gGL.vertex2f(left, bottom);
				gGL.texCoord2f(1.f, 0.f);
				gGL.vertex2f(right, bottom);
				gGL.texCoord2f(1.f, 1.f);
				gGL.vertex2f(right, top);
			gGL.end();

			// Draw water
			gGL.setAlphaRejectSettings(LLRender::CF_GREATER, ABOVE_WATERLINE_ALPHA / 255.f);
			{
				if (regionp->getLand().getWaterTexture())
				{
					gGL.getTexUnit(0)->bind(regionp->getLand().getWaterTexture());
					gGL.begin(LLRender::QUADS);
						gGL.texCoord2f(0.f, 1.f);
						gGL.vertex2f(left, top);
						gGL.texCoord2f(0.f, 0.f);
						gGL.vertex2f(left, bottom);
						gGL.texCoord2f(1.f, 0.f);
						gGL.vertex2f(right, bottom);
						gGL.texCoord2f(1.f, 1.f);
						gGL.vertex2f(right, top);
					gGL.end();
				}
			}
			gGL.setAlphaRejectSettings(LLRender::CF_DEFAULT);
		}

		// Redraw object layer periodically
		if (mUpdateNow || (map_timer.getElapsedTimeF32() > 0.5f))
		{
			mUpdateNow = false;

			// Locate the centre of the object layer, accounting for panning
			LLVector3 new_center = globalPosToView(gAgentCamera.getCameraPositionGlobal());
			new_center.mV[VX] -= mCurPan.mV[VX];
			new_center.mV[VY] -= mCurPan.mV[VY];
			new_center.mV[VZ] = 0.f;
			mObjectImageCenterGlobal = viewPosToGlobal(llfloor(new_center.mV[VX]), llfloor(new_center.mV[VY]));

			// Create the base texture.
			U8 *default_texture = mObjectRawImagep->getData();
			memset( default_texture, 0, mObjectImagep->getWidth() * mObjectImagep->getHeight() * mObjectImagep->getComponents() );

			// Draw objects
			gObjectList.renderObjectsForMap(*this);

			mObjectImagep->setSubImage(mObjectRawImagep, 0, 0, mObjectImagep->getWidth(), mObjectImagep->getHeight());
			
			map_timer.reset();
		}

		LLVector3 map_center_agent = gAgent.getPosAgentFromGlobal(mObjectImageCenterGlobal);
		LLVector3 camera_position = gAgentCamera.getCameraPositionAgent();
		map_center_agent -= camera_position;
		map_center_agent.mV[VX] *= mScale/region_width;
		map_center_agent.mV[VY] *= mScale/region_width;

		gGL.getTexUnit(0)->bind(mObjectImagep);
		F32 image_half_width = 0.5f*mObjectMapPixels;
		F32 image_half_height = 0.5f*mObjectMapPixels;

		gGL.begin(LLRender::QUADS);
			gGL.texCoord2f(0.f, 1.f);
			gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, image_half_height + map_center_agent.mV[VY]);
			gGL.texCoord2f(0.f, 0.f);
			gGL.vertex2f(map_center_agent.mV[VX] - image_half_width, map_center_agent.mV[VY] - image_half_height);
			gGL.texCoord2f(1.f, 0.f);
			gGL.vertex2f(image_half_width + map_center_agent.mV[VX], map_center_agent.mV[VY] - image_half_height);
			gGL.texCoord2f(1.f, 1.f);
			gGL.vertex2f(image_half_width + map_center_agent.mV[VX], image_half_height + map_center_agent.mV[VY]);
		gGL.end();

		gGL.popMatrix();

		// Mouse pointer in local coordinates
		S32 local_mouse_x;
		S32 local_mouse_y;
		//localMouse(&local_mouse_x, &local_mouse_y);
		LLUI::getMousePositionLocal(this, &local_mouse_x, &local_mouse_y);
		mClosestAgentToCursor.setNull();
		F32 closest_dist_squared = F32_MAX; // value will be overridden in the loop
		F32 min_pick_dist_squared = (mDotRadius * MIN_PICK_SCALE) * (mDotRadius * MIN_PICK_SCALE);

		LLVector3 pos_map;
		uuid_vec_t avatar_ids;
		std::vector<LLVector3d> positions;
		bool unknown_relative_z;

		LLWorld::getInstance()->getAvatars(&avatar_ids, &positions, gAgentCamera.getCameraPositionGlobal());

		// Draw avatars
		for (U32 i = 0; i < avatar_ids.size(); i++)
		{
			pos_map = globalPosToView(positions[i]);
			LLUUID uuid = avatar_ids[i];

			bool show_as_friend = (LLAvatarTracker::instance().getBuddyInfo(uuid) != NULL);

			LLColor4 color = show_as_friend ? map_avatar_friend_color : map_avatar_color;

			// <FS:Ansariel> Check for unknown Z-offset => AVATAR_UNKNOWN_Z_OFFSET
			//unknown_relative_z = positions[i].mdV[VZ] == COARSEUPDATE_MAX_Z &&
			//		camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z;
			unknown_relative_z = false;
			if (positions[i].mdV[VZ] == AVATAR_UNKNOWN_Z_OFFSET)
			{
				if (camera_position.mV[VZ] >= COARSEUPDATE_MAX_Z)
				{
					// No exact data and cam >=1020 => we don't know if
					// other avatar is above or below us => unknown
					unknown_relative_z = true;
				}
				else
				{
					// No exact data but cam is below 1020 => other avatar
					// is definitely above us => bump Z-offset to F32_MAX
					// so we get the up chevron
					pos_map.mV[VZ] = F32_MAX;
				}
			}
			// </FS:Ansariel>

			// Colorize muted avatars and Lindens
			std::string fullName;
			LLMuteList* muteListInstance = LLMuteList::getInstance();

			if (muteListInstance->isMuted(uuid)) color = map_avatar_muted_color;
			else if (gCacheName->getFullName(uuid, fullName) && muteListInstance->isLinden(fullName)) color = map_avatar_linden_color;			

			// Mark Avatars with special colors - Ansariel
			if (LLNetMap::sAvatarMarksMap.find(uuid) != LLNetMap::sAvatarMarksMap.end())
			{
				color = LLNetMap::sAvatarMarksMap[uuid];
			}
					
			//color based on contact sets prefs
			if(LGGContactSets::getInstance()->hasFriendColorThatShouldShow(uuid,FALSE,FALSE,FALSE,TRUE))
			{
				color = LGGContactSets::getInstance()->getFriendColor(uuid);
			}

// [RLVa:KB] - Checked: 2010-04-19 (RLVa-1.2.0f) | Modified: RLVa-1.2.0f | FS-Specific
			LLWorldMapView::drawAvatar(
				pos_map.mV[VX], pos_map.mV[VY],
				((!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES)) ? color : map_avatar_color.get()),
				pos_map.mV[VZ], mDotRadius,
				unknown_relative_z);
// [/RLVa:KB]
//			LLWorldMapView::drawAvatar(
//				pos_map.mV[VX], pos_map.mV[VY], 
//				color, 
//				pos_map.mV[VZ], mDotRadius,
//				unknown_relative_z);

			if(uuid.notNull())
			{
				bool selected = false;
				uuid_vec_t::iterator sel_iter = gmSelected.begin();
				for (; sel_iter != gmSelected.end(); sel_iter++)
				{
					if(*sel_iter == uuid)
					{
						selected = true;
						break;
					}
				}
				if(selected)
				{
					if( (pos_map.mV[VX] < 0) ||
						(pos_map.mV[VY] < 0) ||
						(pos_map.mV[VX] >= getRect().getWidth()) ||
						(pos_map.mV[VY] >= getRect().getHeight()) )
					{
						S32 x = llround( pos_map.mV[VX] );
						S32 y = llround( pos_map.mV[VY] );
						LLWorldMapView::drawTrackingCircle( getRect(), x, y, color, 1, 10);
					} else
					{
						LLWorldMapView::drawTrackingDot(pos_map.mV[VX],pos_map.mV[VY],color,0.f);
					}
				}
			}

			F32	dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
											LLVector2(local_mouse_x,local_mouse_y));
			if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared)
			{
				closest_dist_squared = dist_to_cursor_squared;
				mClosestAgentToCursor = uuid;
				mClosestAgentPosition = positions[i];
			}
		}

		// Draw dot for autopilot target
		if (gAgent.getAutoPilot())
		{
			drawTracking( gAgent.getAutoPilotTargetGlobal(), map_track_color );
		}
		else
		{
			LLTracker::ETrackingStatus tracking_status = LLTracker::getTrackingStatus();
			if (  LLTracker::TRACKING_AVATAR == tracking_status )
			{
				drawTracking( LLAvatarTracker::instance().getGlobalPos(), map_track_color );
			} 
			else if ( LLTracker::TRACKING_LANDMARK == tracking_status 
					|| LLTracker::TRACKING_LOCATION == tracking_status )
			{
				drawTracking( LLTracker::getTrackedPositionGlobal(), map_track_color );
			}
		}

		// Draw dot for self avatar position
		LLVector3d pos_global = gAgent.getPositionGlobal();
		pos_map = globalPosToView(pos_global);
		S32 dot_width = llround(mDotRadius * 2.f);
		LLUIImagePtr you = LLWorldMapView::sAvatarYouLargeImage;
		if (you)
		{
			you->draw(llround(pos_map.mV[VX] - mDotRadius),
					  llround(pos_map.mV[VY] - mDotRadius),
					  dot_width,
					  dot_width);

			F32	dist_to_cursor_squared = dist_vec_squared(LLVector2(pos_map.mV[VX], pos_map.mV[VY]),
										  LLVector2(local_mouse_x,local_mouse_y));
			if(dist_to_cursor_squared < min_pick_dist_squared && dist_to_cursor_squared < closest_dist_squared)
			{
				mClosestAgentToCursor = gAgent.getID();
				mClosestAgentPosition = pos_global;
			}

			// Draw chat range ring(s)
			static LLUICachedControl<bool> chat_ring("MiniMapChatRing", true);
			if(chat_ring)
			{
				drawRing(LLWorld::getInstance()->getSayDistance(), pos_map, map_chat_ring_color);
				drawRing(LLWorld::getInstance()->getShoutDistance(), pos_map, map_shout_ring_color);
			}
		}

		// Draw frustum
		F32 meters_to_pixels = mScale/ LLWorld::getInstance()->getRegionWidthInMeters();

		F32 horiz_fov = LLViewerCamera::getInstance()->getView() * LLViewerCamera::getInstance()->getAspect();
		F32 far_clip_meters = LLViewerCamera::getInstance()->getFar();
		F32 far_clip_pixels = far_clip_meters * meters_to_pixels;

		F32 half_width_meters = far_clip_meters * tan( horiz_fov / 2 );
		F32 half_width_pixels = half_width_meters * meters_to_pixels;
		
		F32 ctr_x = (F32)center_sw_left;
		F32 ctr_y = (F32)center_sw_bottom;


		gGL.getTexUnit(0)->unbind(LLTexUnit::TT_TEXTURE);

		if( rotate_map )
		{
			gGL.color4fv((map_frustum_color()).mV);

			gGL.begin( LLRender::TRIANGLES  );
				gGL.vertex2f( ctr_x, ctr_y );
				gGL.vertex2f( ctr_x - half_width_pixels, ctr_y + far_clip_pixels );
				gGL.vertex2f( ctr_x + half_width_pixels, ctr_y + far_clip_pixels );
			gGL.end();
		}
		else
		{
			gGL.color4fv((map_frustum_rotating_color()).mV);
			
			// If we don't rotate the map, we have to rotate the frustum.
			gGL.pushMatrix();
				gGL.translatef( ctr_x, ctr_y, 0 );
				gGL.rotatef( atan2( LLViewerCamera::getInstance()->getAtAxis().mV[VX], LLViewerCamera::getInstance()->getAtAxis().mV[VY] ) * RAD_TO_DEG, 0.f, 0.f, -1.f);
				gGL.begin( LLRender::TRIANGLES  );
					gGL.vertex2f( 0, 0 );
					gGL.vertex2f( -half_width_pixels, far_clip_pixels );
					gGL.vertex2f(  half_width_pixels, far_clip_pixels );
				gGL.end();
			gGL.popMatrix();
		}
	}
	
	gGL.popMatrix();
	gGL.popUIMatrix();

	LLUICtrl::draw();
}
예제 #4
0
void FSRadar::updateRadarList()
{
	//Configuration
	LLWorld* world = LLWorld::getInstance();
	LLMuteList* mutelist = LLMuteList::getInstance();
	FSWSAssetBlacklist* blacklist = FSWSAssetBlacklist::getInstance();

	static const F32 chat_range_say = LFSimFeatureHandler::getInstance()->sayRange();
	static const F32 chat_range_shout = LFSimFeatureHandler::getInstance()->shoutRange();

	static const std::string str_chat_entering =			LLTrans::getString("entering_chat_range");
	static const std::string str_chat_leaving =				LLTrans::getString("leaving_chat_range");
	static const std::string str_draw_distance_entering =	LLTrans::getString("entering_draw_distance");
	static const std::string str_draw_distance_leaving =	LLTrans::getString("leaving_draw_distance");
	static const std::string str_region_entering =			LLTrans::getString("entering_region");
	static const std::string str_region_entering_distance =	LLTrans::getString("entering_region_distance");
	static const std::string str_region_leaving =			LLTrans::getString("leaving_region");
	static const std::string str_avatar_age_alert =			LLTrans::getString("avatar_age_alert");

	static LLCachedControl<bool> RadarReportChatRangeEnter(gSavedSettings, "RadarReportChatRangeEnter");
	static LLCachedControl<bool> RadarReportChatRangeLeave(gSavedSettings, "RadarReportChatRangeLeave");
	static LLCachedControl<bool> RadarReportDrawRangeEnter(gSavedSettings, "RadarReportDrawRangeEnter");
	static LLCachedControl<bool> RadarReportDrawRangeLeave(gSavedSettings, "RadarReportDrawRangeLeave");
	static LLCachedControl<bool> RadarReportSimRangeEnter(gSavedSettings, "RadarReportSimRangeEnter");
	static LLCachedControl<bool> RadarReportSimRangeLeave(gSavedSettings, "RadarReportSimRangeLeave");
	static LLCachedControl<bool> RadarEnterChannelAlert(gSavedSettings, "RadarEnterChannelAlert");
	static LLCachedControl<bool> RadarLeaveChannelAlert(gSavedSettings, "RadarLeaveChannelAlert");
	static LLCachedControl<bool> RadarAvatarAgeAlert(gSavedSettings, "RadarAvatarAgeAlert");
	static LLCachedControl<F32> nearMeRange(gSavedSettings, "NearMeRange");
	static LLCachedControl<bool> limitRange(gSavedSettings, "LimitRadarByRange");
	static LLCachedControl<F32> RenderFarClip(gSavedSettings, "RenderFarClip");
	static LLCachedControl<bool> sFSLegacyRadarFriendColoring(gSavedSettings, "FSLegacyRadarFriendColoring");
	static LLCachedControl<bool> sRadarColorNamesByDistance(gSavedSettings, "FSRadarColorNamesByDistance", false);
	static LLCachedControl<bool> RadarShowMutedAndDerendered(gSavedSettings, "FSRadarShowMutedAndDerendered");
	static LLCachedControl<bool> sFSRadarEnhanceByBridge(gSavedSettings, "FSRadarEnhanceByBridge");
	bool sUseLSLBridge = FSLSLBridge::instance().canUseBridge();

	F32 drawRadius(RenderFarClip);
	const LLVector3d& posSelf = gAgent.getPositionGlobal();
	LLViewerRegion* own_reg = gAgent.getRegion();
	LLUUID regionSelf;
	if (own_reg)
	{
		regionSelf = own_reg->getRegionID();
	}
	bool alertScripts = mRadarAlertRequest; // save the current value, so it doesn't get changed out from under us by another thread
	time_t now = time(NULL);

	//STEP 0: Clear model data
	mRadarEnterAlerts.clear();
	mRadarLeaveAlerts.clear();
	mRadarOffsetRequests.clear();
	mRadarEntriesData.clear();
	mAvatarStats.clear();
	
	//STEP 1: Update our basic data model: detect Avatars & Positions in our defined range
	std::vector<LLVector3d> positions;
	uuid_vec_t avatar_ids;
	if (limitRange)
	{
		world->getAvatars(&avatar_ids, &positions, gAgent.getPositionGlobal(), nearMeRange);
	}
	else
	{
		world->getAvatars(&avatar_ids, &positions);
	}

	// Determine lists of new added and removed avatars
	uuid_vec_t current_vec, added_vec, removed_vec;
	uuid_vec_t::iterator vec_it_end;
	entry_map_t::iterator em_it_end = mEntryList.end();
	current_vec.reserve(mEntryList.size());
	for (entry_map_t::iterator em_it = mEntryList.begin(); em_it != em_it_end; ++em_it)
	{
		current_vec.push_back(em_it->first);
	}
	LLCommonUtils::computeDifference(avatar_ids, current_vec, added_vec, removed_vec);

	// Remove old avatars from our list
	vec_it_end = removed_vec.end();
	for (uuid_vec_t::iterator it = removed_vec.begin(); it != vec_it_end; ++it)
	{
		LLUUID avid = *it;
		entry_map_t::iterator found = mEntryList.find(avid);
		if (found != mEntryList.end())
		{
			delete found->second;
			mEntryList.erase(found);
		}
	}

	// Add new avatars
	vec_it_end = added_vec.end();
	for (uuid_vec_t::iterator it = added_vec.begin(); it != vec_it_end; ++it)
	{
		LLUUID avid = *it;
		mEntryList[avid] = new FSRadarEntry(avid);
	}

	LLLocalSpeakerMgr::getInstance()->update(TRUE);
	
	//STEP 2: Transform detected model list data into more flexible multimap data structure;
	//TS: Count avatars in chat range and in the same region
	U32 inChatRange = 0;
	U32 inSameRegion = 0;
	std::vector<LLVector3d>::const_iterator
		pos_it = positions.begin(),
		pos_end = positions.end();	
	uuid_vec_t::const_iterator
		item_it = avatar_ids.begin(),
		item_end = avatar_ids.end();
	for (;pos_it != pos_end && item_it != item_end; ++pos_it, ++item_it)
	{
		//
		//2a. For each detected av, gather up all data we would want to display or use to drive alerts
		//
		
		LLUUID avId          = static_cast<LLUUID>(*item_it);
		LLVector3d avPos     = static_cast<LLVector3d>(*pos_it);

		if (avId == gAgentID)
		{
			continue;
		}
		
		// Skip modelling this avatar if its basic data is either inaccessible, or it's a dummy placeholder
		FSRadarEntry* ent = getEntry(avId);
		LLViewerRegion *reg	 = world->getRegionFromPosGlobal(avPos);
		if (!ent) // don't update this radar listing if data is inaccessible
		{
			continue;
		}

		// Try to get the avatar's viewer object - we will need it anyway later
		LLVOAvatar* avVo = (LLVOAvatar*)gObjectList.findObject(avId);

		static LLUICachedControl<bool> showdummyav("FSShowDummyAVsinRadar");
		if (!showdummyav)
		{
			if (avVo && avVo->mIsDummy)
			{
				continue;
			}
		}

		bool is_muted = mutelist->isMuted(avId);
		bool is_blacklisted = blacklist->isBlacklisted(avId, LLAssetType::AT_OBJECT);
		bool should_be_ignored = is_muted || is_blacklisted;
		ent->mIgnore = should_be_ignored;
		if (!RadarShowMutedAndDerendered && should_be_ignored)
		{
			continue;
		}

		LLUUID avRegion;
		if (reg)
		{
			avRegion = reg->getRegionID();
		}
		bool isInSameRegion = (avRegion == regionSelf);
		S32 seentime = (S32)difftime(now, ent->mFirstSeen);
		S32 hours = (S32)(seentime / 3600);
		S32 mins = (S32)((seentime - hours * 3600) / 60);
		S32 secs = (S32)((seentime - hours * 3600 - mins * 60));
		std::string avSeenStr = llformat("%d:%02d:%02d", hours, mins, secs);
		S32 avStatusFlags     = ent->mStatus;
		ERadarPaymentInfoFlag avFlag = FSRADAR_PAYMENT_INFO_NONE;
		if (avStatusFlags & AVATAR_TRANSACTED)
		{
			avFlag = FSRADAR_PAYMENT_INFO_USED;
		}
		else if (avStatusFlags & AVATAR_IDENTIFIED)
		{
			avFlag = FSRADAR_PAYMENT_INFO_FILLED;
		}
		S32 avAge = ent->mAge;
		std::string avName = ent->mName;
		U32 lastZOffsetTime  = ent->mLastZOffsetTime;
		F32 avZOffset = ent->mZOffset;
		if (avPos[VZ] == AVATAR_UNKNOWN_Z_OFFSET) // if our official z position is AVATAR_UNKNOWN_Z_OFFSET, we need a correction.
		{
			// set correction if we have it
			if (avZOffset > 0.1f)
			{
				avPos[VZ] = avZOffset;
			}
			
			//schedule offset requests, if needed
			if (sUseLSLBridge && sFSRadarEnhanceByBridge && (now > (mRadarLastBulkOffsetRequestTime + FSRADAR_COARSE_OFFSET_INTERVAL)) && (now > lastZOffsetTime + FSRADAR_COARSE_OFFSET_INTERVAL))
			{
				mRadarOffsetRequests.push_back(avId);
				ent->mLastZOffsetTime = now;
			}
		}	
		F32 avRange = (avPos[VZ] != AVATAR_UNKNOWN_Z_OFFSET ? dist_vec(avPos, posSelf) : AVATAR_UNKNOWN_RANGE);
		ent->mRange = avRange;
		ent->mGlobalPos = avPos;
		ent->mRegion = avRegion;
		
		//
		//2b. Process newly detected avatars
		//
		radarfields_map_t::iterator last_sweep_found_it = mLastRadarSweep.find(avId);
		if (last_sweep_found_it == mLastRadarSweep.end())
		{
			// chat alerts
			if (RadarReportChatRangeEnter && (avRange <= chat_range_say) && avRange > AVATAR_UNKNOWN_RANGE)
			{
				LLStringUtil::format_map_t args;
				args["DISTANCE"] = llformat("%3.2f", avRange);
				std::string message = formatString(str_chat_entering, args);
				make_ui_sound("UISndRadarChatEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
				LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
			}
			if (RadarReportDrawRangeEnter && (avRange <= drawRadius) && avRange > AVATAR_UNKNOWN_RANGE)
			{
				LLStringUtil::format_map_t args;
				args["DISTANCE"] = llformat("%3.2f", avRange);
				std::string message = formatString(str_draw_distance_entering, args);
				make_ui_sound("UISndRadarDrawEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
				LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
			}
			if (RadarReportSimRangeEnter && isInSameRegion)
			{
				make_ui_sound("UISndRadarSimEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
				if (avRange != AVATAR_UNKNOWN_RANGE) // Don't report an inaccurate range in localchat, if the true range is not known.
				{
					LLStringUtil::format_map_t args;
					args["DISTANCE"] = llformat("%3.2f", avRange);
					std::string message = formatString(str_region_entering_distance, args);
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
				}
				else
				{
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_region_entering));
				}
			}
			if (RadarEnterChannelAlert || (alertScripts))
			{
				// Autodetect Phoenix chat UUID compatibility. 
				// If Leave channel alerts are not set, restrict reports to same-sim only.
				if (!RadarLeaveChannelAlert)
				{
					if (isInSameRegion)
					{
						mRadarEnterAlerts.push_back(avId);
					}
				}
				else
				{
					mRadarEnterAlerts.push_back(avId);
				}
			}
		}
		
		//
		// 2c. Process previously detected avatars
		//
		else
		{
			RadarFields rf = last_sweep_found_it->second;
			if (RadarReportChatRangeEnter || RadarReportChatRangeLeave)
			{
				if (RadarReportChatRangeEnter && (avRange <= chat_range_say && avRange > AVATAR_UNKNOWN_RANGE) && (rf.lastDistance > chat_range_say || rf.lastDistance == AVATAR_UNKNOWN_RANGE))
				{
					LLStringUtil::format_map_t args;
					args["DISTANCE"] = llformat("%3.2f", avRange);
					std::string message = formatString(str_chat_entering, args);
					make_ui_sound("UISndRadarChatEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
				}
				else if (RadarReportChatRangeLeave && (avRange > chat_range_say || avRange == AVATAR_UNKNOWN_RANGE) && (rf.lastDistance <= chat_range_say && rf.lastDistance > AVATAR_UNKNOWN_RANGE))
				{
					make_ui_sound("UISndRadarChatLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_chat_leaving));
				}
			}
			if (RadarReportDrawRangeEnter || RadarReportDrawRangeLeave)
			{
				if (RadarReportDrawRangeEnter && (avRange <= drawRadius && avRange > AVATAR_UNKNOWN_RANGE) && (rf.lastDistance > drawRadius || rf.lastDistance == AVATAR_UNKNOWN_RANGE))
				{
					LLStringUtil::format_map_t args;
					args["DISTANCE"] = llformat("%3.2f", avRange);
					std::string message = formatString(str_draw_distance_entering, args);
					make_ui_sound("UISndRadarDrawEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
				}
				else if (RadarReportDrawRangeLeave && (avRange > drawRadius || avRange == AVATAR_UNKNOWN_RANGE) && (rf.lastDistance <= drawRadius && rf.lastDistance > AVATAR_UNKNOWN_RANGE))
				{
					make_ui_sound("UISndRadarDrawLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_draw_distance_leaving));
				}
			}
			if (RadarReportSimRangeEnter || RadarReportSimRangeLeave)
			{
				if (RadarReportSimRangeEnter && isInSameRegion && avRegion != rf.lastRegion && rf.lastRegion.notNull())
				{
					make_ui_sound("UISndRadarSimEnter"); // <FS:PP> FIRE-6069: Radar alerts sounds
					if (avRange != AVATAR_UNKNOWN_RANGE) // Don't report an inaccurate range in localchat, if the true range is not known.
					{
						LLStringUtil::format_map_t args;
						args["DISTANCE"] = llformat("%3.2f", avRange);
						std::string message = formatString(str_region_entering_distance, args);
						LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
					}
					else
					{
						LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_region_entering));
					}
				}
				else if (RadarReportSimRangeLeave && rf.lastRegion == regionSelf && !isInSameRegion && avRegion.notNull())
				{
					make_ui_sound("UISndRadarSimLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_region_leaving));
				}
			}
			//If we were manually asked to update an external source for all existing avatars, add them to the queue.
			if (alertScripts)
			{
				mRadarEnterAlerts.push_back(avId);
			}
		}
		
		//
		//2d. Prepare data for presentation view for this avatar
		//
		if (isInSameRegion)
		{
			inSameRegion++;
		}

		LLSD entry;
		LLSD entry_options;

		entry["id"] = avId;
		entry["name"] = avName;
		entry["in_region"] = isInSameRegion;
		entry["flags"] = avFlag;
		entry["seen"] = avSeenStr;
		entry["range"] = (avRange > AVATAR_UNKNOWN_RANGE ? llformat("%3.2f", avRange) : llformat(">%3.2f", drawRadius));
		entry["typing"] = (avVo && avVo->isTyping());
		entry["sitting"] = (avVo && (avVo->getParent() || avVo->isMotionActive(ANIM_AGENT_SIT_GROUND) || avVo->isMotionActive(ANIM_AGENT_SIT_GROUND_CONSTRAINED)));
		entry["has_notes"] = ent->hasNotes();

		if (!gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
		{
			entry["age"] = (avAge > -1 ? llformat("%d", avAge) : "");
			if (ent->hasAlertAge())
			{
				entry_options["age_color"] = LLUIColorTable::instance().getColor("AvatarListItemAgeAlert", LLColor4::red).get().getValue();

				if (RadarAvatarAgeAlert && !ent->hasAgeAlertPerformed())
				{
					make_ui_sound("UISndRadarAgeAlert");
					LLStringUtil::format_map_t args;
					args["AGE"] = llformat("%d", avAge);
					std::string message = formatString(str_avatar_age_alert, args);
					LLAvatarNameCache::get(avId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, message));
				}
				ent->mAgeAlertPerformed = true;
			}
		}
		else
		{
			entry["age"] = "---";
		}

		//AO: Set any range colors / styles
		LLUIColor range_color;
		if (avRange > AVATAR_UNKNOWN_RANGE)
		{
			if (avRange <= chat_range_say)
			{
				range_color = LLUIColorTable::instance().getColor("AvatarListItemChatRange", LLColor4::red);
				inChatRange++;
			}
			else if (avRange <= chat_range_shout)
			{
				range_color = LLUIColorTable::instance().getColor("AvatarListItemShoutRange", LLColor4::white);
			}
			else 
			{
				range_color = LLUIColorTable::instance().getColor("AvatarListItemBeyondShoutRange", LLColor4::white);
			}
		}
		else 
		{
			range_color = LLUIColorTable::instance().getColor("AvatarListItemBeyondShoutRange", LLColor4::white);
		}
		entry_options["range_color"] = range_color.get().getValue();

		// Check if avatar is in draw distance and a VOAvatar instance actually exists
		if (avRange <= drawRadius && avRange > AVATAR_UNKNOWN_RANGE && avVo)
		{
			entry_options["range_style"] = LLFontGL::BOLD;
		}
		else
		{
			entry_options["range_style"] = LLFontGL::NORMAL;
		}

		// Set friends colors / styles
		LLFontGL::StyleFlags nameCellStyle = LLFontGL::NORMAL;
		const LLRelationship* relation = LLAvatarTracker::instance().getBuddyInfo(avId);
		if (relation && !sFSLegacyRadarFriendColoring && !gRlvHandler.hasBehaviour(RLV_BHVR_SHOWNAMES))
		{
			nameCellStyle = (LLFontGL::StyleFlags)(nameCellStyle | LLFontGL::BOLD);
		}
		if (is_muted)
		{
			nameCellStyle = (LLFontGL::StyleFlags)(nameCellStyle | LLFontGL::ITALIC);
		}
		entry_options["name_style"] = nameCellStyle;

		LLUIColor name_color = LLUIColorTable::instance().getColor("AvatarListItemIconDefaultColor", LLColor4::white);
		name_color = LGGContactSets::getInstance()->colorize(avId, (sRadarColorNamesByDistance ? range_color : name_color), LGG_CS_RADAR);

		if (LGGContactSets::getInstance()->hasFriendColorThatShouldShow(avId, LGG_CS_RADAR))
		{
			name_color = LGGContactSets::getInstance()->getFriendColor(avId);
		}
		entry_options["name_color"] = name_color.get().getValue();

		// Voice power level indicator
		LLVoiceClient* voice_client = LLVoiceClient::getInstance();
		if (voice_client->voiceEnabled() && voice_client->isVoiceWorking())
		{
			LLSpeaker* speaker = LLLocalSpeakerMgr::getInstance()->findSpeaker(avId);
			if (speaker && speaker->isInVoiceChannel())
			{
				EVoicePowerLevel power_level = voice_client->getPowerLevel(avId);
			
				switch (power_level)
				{
					case VPL_PTT_Off:
						entry["voice_level_icon"] = "Radar_VoicePTT_Off";
						break;
					case VPL_PTT_On:
						entry["voice_level_icon"] = "Radar_VoicePTT_On";
						break;
					case VPL_Level1:
						entry["voice_level_icon"] = "Radar_VoicePTT_Lvl1";
						break;
					case VPL_Level2:
						entry["voice_level_icon"] = "Radar_VoicePTT_Lvl2";
						break;
					case VPL_Level3:
						entry["voice_level_icon"] = "Radar_VoicePTT_Lvl3";
						break;
					default:
						break;
				}
			}
		}

		// Save data for our listeners
		LLSD entry_data;
		entry_data["entry"] = entry;
		entry_data["options"] = entry_options;
		mRadarEntriesData.push_back(entry_data);
	} // End STEP 2, all model/presentation row processing complete.
	
	//
	//STEP 3, process any bulk actions that require the whole model to be known first
	//
	
	//
	//3a. dispatch requests for ZOffset updates, working around minimap's inaccurate height
	//
	if (mRadarOffsetRequests.size() > 0)
	{
		static const std::string prefix = "getZOffsets|";
		std::string msg = "";
		U32 updatesPerRequest = 0;
		while (mRadarOffsetRequests.size() > 0)
		{
			LLUUID avId = mRadarOffsetRequests.back();
			mRadarOffsetRequests.pop_back();
			msg = llformat("%s%s,", msg.c_str(), avId.asString().c_str());
			if (++updatesPerRequest > FSRADAR_MAX_OFFSET_REQUESTS)
			{
				msg = msg.substr(0, msg.size() - 1);
				FSLSLBridgeRequestResponder* responder = new FSLSLBridgeRequestRadarPosResponder();
				FSLSLBridge::instance().viewerToLSL(prefix + msg, responder);
				//LL_INFOS() << " OFFSET REQUEST SEGMENT"<< prefix << msg << LL_ENDL;
				msg = "";
				updatesPerRequest = 0;
			}
		}
		if (updatesPerRequest > 0)
		{
			msg = msg.substr(0, msg.size() - 1);
			FSLSLBridgeRequestResponder* responder = new FSLSLBridgeRequestRadarPosResponder();
			FSLSLBridge::instance().viewerToLSL(prefix + msg, responder);
			//LL_INFOS() << " OFFSET REQUEST FINAL " << prefix << msg << LL_ENDL;
		}
		
		// clear out the dispatch queue
		mRadarOffsetRequests.clear();
		mRadarLastBulkOffsetRequestTime = now;
	}
	
	//
	//3b: process alerts for avatars that where here last frame, but gone this frame (ie, they left)
	//    as well as dispatch all earlier detected alerts for crossing range thresholds.
	//
	
	radarfields_map_t::iterator rf_it_end = mLastRadarSweep.end();
	for (radarfields_map_t::iterator i = mLastRadarSweep.begin(); i != rf_it_end; ++i)
	{
		LLUUID prevId = i->first;
		RadarFields rf = i->second;
		if ((RadarShowMutedAndDerendered || !rf.lastIgnore) && mEntryList.find(prevId) == mEntryList.end())
		{
			if (RadarReportChatRangeLeave && (rf.lastDistance <= chat_range_say) && rf.lastDistance > AVATAR_UNKNOWN_RANGE)
			{
				make_ui_sound("UISndRadarChatLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
				LLAvatarNameCache::get(prevId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_chat_leaving));
			}
			if (RadarReportDrawRangeLeave && (rf.lastDistance <= drawRadius) && rf.lastDistance > AVATAR_UNKNOWN_RANGE)
			{
				make_ui_sound("UISndRadarDrawLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
				LLAvatarNameCache::get(prevId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_draw_distance_leaving));
			}
			if (RadarReportSimRangeLeave && (rf.lastRegion == regionSelf || rf.lastRegion.isNull()))
			{
				make_ui_sound("UISndRadarSimLeave"); // <FS:PP> FIRE-6069: Radar alerts sounds
				LLAvatarNameCache::get(prevId, boost::bind(&FSRadar::radarAlertMsg, this, _1, _2, str_region_leaving));
			}
				
			if (RadarLeaveChannelAlert)
			{
				mRadarLeaveAlerts.push_back(prevId);
			}
		}
	}

	static LLCachedControl<S32> RadarAlertChannel(gSavedSettings, "RadarAlertChannel");
	U32 num_entering = mRadarEnterAlerts.size();
	if (num_entering > 0)
	{
		mRadarFrameCount++;
		S32 chan(RadarAlertChannel);
		U32 num_this_pass = llmin(FSRADAR_MAX_AVATARS_PER_ALERT, num_entering);
		std::string msg = llformat("%d,%d", mRadarFrameCount, num_this_pass);
		U32 loop = 0;
		while (loop < num_entering)
		{
			for (S32 i = 0; i < num_this_pass; i++)
			{
				msg = llformat("%s,%s", msg.c_str(), mRadarEnterAlerts[loop + i].asString().c_str());
			}
			LLMessageSystem* msgs = gMessageSystem;
			msgs->newMessage("ScriptDialogReply");
			msgs->nextBlock("AgentData");
			msgs->addUUID("AgentID", gAgent.getID());
			msgs->addUUID("SessionID", gAgent.getSessionID());
			msgs->nextBlock("Data");
			msgs->addUUID("ObjectID", gAgent.getID());
			msgs->addS32("ChatChannel", chan);
			msgs->addS32("ButtonIndex", 1);
			msgs->addString("ButtonLabel", msg.c_str());
			gAgent.sendReliableMessage();
			loop += num_this_pass;
			num_this_pass = llmin(FSRADAR_MAX_AVATARS_PER_ALERT, num_entering - loop);
			msg = llformat("%d,%d", mRadarFrameCount, num_this_pass);
		}
	}
	U32 num_leaving  = mRadarLeaveAlerts.size();
	if (num_leaving > 0)
	{
		mRadarFrameCount++;
		S32 chan(RadarAlertChannel);
		U32 num_this_pass = llmin(FSRADAR_MAX_AVATARS_PER_ALERT, num_leaving);
		std::string msg = llformat("%d,-%d", mRadarFrameCount, llmin(FSRADAR_MAX_AVATARS_PER_ALERT, num_leaving));
		U32 loop = 0;
		while (loop < num_leaving)
		{
			for (S32 i = 0; i < num_this_pass; i++)
			{
				msg = llformat("%s,%s", msg.c_str(), mRadarLeaveAlerts[loop + i].asString().c_str());
			}
			LLMessageSystem* msgs = gMessageSystem;
			msgs->newMessage("ScriptDialogReply");
			msgs->nextBlock("AgentData");
			msgs->addUUID("AgentID", gAgent.getID());
			msgs->addUUID("SessionID", gAgent.getSessionID());
			msgs->nextBlock("Data");
			msgs->addUUID("ObjectID", gAgent.getID());
			msgs->addS32("ChatChannel", chan);
			msgs->addS32("ButtonIndex", 1);
			msgs->addString("ButtonLabel", msg.c_str());
			gAgent.sendReliableMessage();
			loop += num_this_pass;
			num_this_pass = llmin(FSRADAR_MAX_AVATARS_PER_ALERT, num_leaving - loop);
			msg = llformat("%d,-%d", mRadarFrameCount, num_this_pass);
		}
	}

	// reset any active alert requests
	if (alertScripts)
	{
		mRadarAlertRequest = false;
	}

	//
	//STEP 4: Cache our current model data, so we can compare it with the next fresh group of model data for fast change detection.
	//
	
	mLastRadarSweep.clear();
	em_it_end = mEntryList.end();
	for (entry_map_t::iterator em_it = mEntryList.begin(); em_it != em_it_end; ++em_it)
	{
		FSRadarEntry* ent = em_it->second;
		RadarFields rf;
		rf.lastDistance = ent->mRange;
		rf.lastIgnore = ent->mIgnore;
		rf.lastRegion = LLUUID::null;
		if (ent->mGlobalPos != LLVector3d(0.0f, 0.0f, 0.0f))
		{
			LLViewerRegion* lastRegion = world->getRegionFromPosGlobal(ent->mGlobalPos);
			if (lastRegion)
			{
				rf.lastRegion = lastRegion->getRegionID();
			}
		}
		
		mLastRadarSweep[ent->mID] = rf;
	}

	//
	//STEP 5: Final data updates and notification of subscribers
	//

	mAvatarStats["total"] = llformat("%d", mLastRadarSweep.size() - 1);
	mAvatarStats["region"] = llformat("%d", inSameRegion);
	mAvatarStats["chatrange"] = llformat("%d", inChatRange);

	checkTracking();

	// Inform our subscribers about updates
	if (!mUpdateSignal.empty())
	{
		mUpdateSignal(mRadarEntriesData, mAvatarStats);
	}
}