Skip to content
Snippets Groups Projects
llviewerparcelmgr.cpp 69.9 KiB
Newer Older
James Cook's avatar
James Cook committed
/** 
 * @file llviewerparcelmgr.cpp
 * @brief Viewer-side representation of owned land
 *
 * $LicenseInfo:firstyear=2002&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
James Cook's avatar
James Cook committed
 */

#include "llviewerprecompiledheaders.h"

#include "llviewerparcelmgr.h"

// Library includes
#include "llaudioengine.h"
James Cook's avatar
James Cook committed
#include "indra_constants.h"
#include "llcachename.h"
#include "llgl.h"
James Cook's avatar
James Cook committed
#include "llparcel.h"
#include "message.h"
James Cook's avatar
James Cook committed

// Viewer includes
#include "llagent.h"
James Cook's avatar
James Cook committed
#include "llviewerwindow.h"
#include "llviewercontrol.h"
James Cook's avatar
James Cook committed
#include "llfloaterbuyland.h"
#include "llfloatergroups.h"
#include "llpanelnearbymedia.h"
James Cook's avatar
James Cook committed
#include "llfloatersellland.h"
#include "llfloatertools.h"
Josh Bell's avatar
Josh Bell committed
#include "llparcelselection.h"
James Cook's avatar
James Cook committed
#include "llresmgr.h"
#include "llsdutil_math.h"
James Cook's avatar
James Cook committed
#include "llstatusbar.h"
#include "llui.h"
#include "llviewertexture.h"
#include "llviewertexturelist.h"
James Cook's avatar
James Cook committed
#include "llviewermenu.h"
Josh Bell's avatar
Josh Bell committed
#include "llviewerparcelmedia.h"
James Cook's avatar
James Cook committed
#include "llviewerparceloverlay.h"
#include "llviewerregion.h"
#include "llworld.h"
#include "roles_constants.h"
#include "llweb.h"
James Cook's avatar
James Cook committed

const F32 PARCEL_COLLISION_DRAW_SECS = 1.f;

James Cook's avatar
James Cook committed
// Globals

U8* LLViewerParcelMgr::sPackedOverlay = NULL;

LLUUID gCurrentMovieID = LLUUID::null;

LLPointer<LLViewerTexture> sBlockedImage;
LLPointer<LLViewerTexture> sPassImage;
James Cook's avatar
James Cook committed
// Local functions
void callback_start_music(S32 option, void* data);
void optionally_prepare_video(const LLParcel *parcelp);
void callback_prepare_video(S32 option, void* data);
void prepare_video(const LLParcel *parcelp);
void start_video(const LLParcel *parcelp);
void stop_video();
Kent Quirk's avatar
Kent Quirk committed
bool callback_god_force_owner(const LLSD&, const LLSD&);
James Cook's avatar
James Cook committed

struct LLGodForceOwnerData
{
	LLUUID mOwnerID;
	S32 mLocalID;
	LLHost mHost;

	LLGodForceOwnerData(
		const LLUUID& owner_id,
		S32 local_parcel_id,
		const LLHost& host) :
		mOwnerID(owner_id),
		mLocalID(local_parcel_id),
		mHost(host) {}
};

//
// Methods
//
LLViewerParcelMgr::LLViewerParcelMgr()
:	mSelected(FALSE),
Don Kjer's avatar
Don Kjer committed
	mRequestResult(0),
James Cook's avatar
James Cook committed
	mWestSouth(),
	mEastNorth(),
James Cook's avatar
James Cook committed
	mAgentParcelSequenceID(-1),
Don Kjer's avatar
Don Kjer committed
	mHoverRequestResult(0),
James Cook's avatar
James Cook committed
	mHoverWestSouth(),
	mHoverEastNorth(),
James Cook's avatar
James Cook committed
	mRenderCollision(FALSE),
	mRenderSelection(TRUE),
	mCollisionBanned(0),
Don Kjer's avatar
Don Kjer committed
	mCollisionTimer(),
	mMediaParcelId(0),
	mMediaRegionId(0)
James Cook's avatar
James Cook committed
{
	mCurrentParcel = new LLParcel();
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
	mFloatingParcelSelection = new LLParcelSelection(mCurrentParcel);

James Cook's avatar
James Cook committed
	mAgentParcel = new LLParcel();
	mHoverParcel = new LLParcel();
	mCollisionParcel = new LLParcel();

Cinder's avatar
Cinder committed
	mParcelsPerEdge = S32(8192.f / PARCEL_GRID_STEP_METERS); // 8192 is the maximum region size on Aurora
James Cook's avatar
James Cook committed
	mHighlightSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
	resetSegments(mHighlightSegments);

	mCollisionSegments = new U8[(mParcelsPerEdge+1)*(mParcelsPerEdge+1)];
	resetSegments(mCollisionSegments);

	// JC: Resolved a merge conflict here, eliminated
	// mBlockedImage->setAddressMode(LLTexUnit::TAM_WRAP);
	// because it is done in llviewertexturelist.cpp
	mBlockedImage = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryLines.png");
	mPassImage = LLViewerTextureManager::getFetchedTextureFromFile("world/NoEntryPassLines.png");
James Cook's avatar
James Cook committed

	S32 overlay_size = mParcelsPerEdge * mParcelsPerEdge / PARCEL_OVERLAY_CHUNKS;
	sPackedOverlay = new U8[overlay_size];

	mAgentParcelOverlay = new U8[mParcelsPerEdge * mParcelsPerEdge];
	S32 i;
	for (i = 0; i < mParcelsPerEdge * mParcelsPerEdge; i++)
	{
		mAgentParcelOverlay[i] = 0;
	}
Cinder's avatar
Cinder committed
	mParcelsPerEdge = S32(REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS);
	mTeleportInProgress = TRUE; // the initial parcel update is treated like teleport
Cinder's avatar
Cinder committed
void LLViewerParcelMgr::init(F32 region_size)
{
	mParcelsPerEdge = S32(region_size / PARCEL_GRID_STEP_METERS);
}
James Cook's avatar
James Cook committed

LLViewerParcelMgr::~LLViewerParcelMgr()
{
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = NULL;

	mFloatingParcelSelection->setParcel(NULL);
	mFloatingParcelSelection = NULL;

	delete mCurrentParcel;
	mCurrentParcel = NULL;
James Cook's avatar
James Cook committed

	delete mAgentParcel;
	mAgentParcel = NULL;

	delete mCollisionParcel;
	mCollisionParcel = NULL;

	delete mHoverParcel;
	mHoverParcel = NULL;

	delete[] mHighlightSegments;
	mHighlightSegments = NULL;

	delete[] mCollisionSegments;
	mCollisionSegments = NULL;

James Cook's avatar
James Cook committed
	sPackedOverlay = NULL;

	delete[] mAgentParcelOverlay;
	mAgentParcelOverlay = NULL;
Josh Bell's avatar
Josh Bell committed

	sBlockedImage = NULL;
	sPassImage = NULL;
James Cook's avatar
James Cook committed
}

void LLViewerParcelMgr::dump()
{
	LL_INFOS() << "Parcel Manager Dump" << LL_ENDL;
	LL_INFOS() << "mSelected " << S32(mSelected) << LL_ENDL;
	LL_INFOS() << "Selected parcel: " << LL_ENDL;
	LL_INFOS() << mWestSouth << " to " << mEastNorth << LL_ENDL;
	mCurrentParcel->dump();
	LL_INFOS() << "banning " << mCurrentParcel->mBanList.size() << LL_ENDL;
James Cook's avatar
James Cook committed
	
	LLAccessEntry::map::const_iterator cit = mCurrentParcel->mBanList.begin();
	LLAccessEntry::map::const_iterator end = mCurrentParcel->mBanList.end();
James Cook's avatar
James Cook committed
	for ( ; cit != end; ++cit)
	{
		LL_INFOS() << "ban id " << (*cit).first << LL_ENDL;
James Cook's avatar
James Cook committed
	}
	LL_INFOS() << "Hover parcel:" << LL_ENDL;
James Cook's avatar
James Cook committed
	mHoverParcel->dump();
	LL_INFOS() << "Agent parcel:" << LL_ENDL;
James Cook's avatar
James Cook committed
	mAgentParcel->dump();
}


LLViewerRegion* LLViewerParcelMgr::getSelectionRegion()
{
	return LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
James Cook's avatar
James Cook committed
}


void LLViewerParcelMgr::getDisplayInfo(S32* area_out, S32* claim_out,
									   S32* rent_out,
									   BOOL* for_sale_out,
									   F32* dwell_out)
{
	S32 area = 0;
	S32 price = 0;
	S32 rent = 0;
	BOOL for_sale = FALSE;
James Cook's avatar
James Cook committed

	if (mSelected)
	{
		if (mCurrentParcelSelection->mSelectedMultipleOwners)
James Cook's avatar
James Cook committed
		{
			area = mCurrentParcelSelection->getClaimableArea();
James Cook's avatar
James Cook committed
		}
		else
		{
			area = getSelectedArea();
		}

		if (mCurrentParcel->getForSale())
James Cook's avatar
James Cook committed
		{
			price = mCurrentParcel->getSalePrice();
James Cook's avatar
James Cook committed
			for_sale = TRUE;
		}
		else
		{
			price = area * mCurrentParcel->getClaimPricePerMeter();
James Cook's avatar
James Cook committed
			for_sale = FALSE;
		}

		rent = mCurrentParcel->getTotalRent();
James Cook's avatar
James Cook committed

		dwell = mSelectedDwell;
	}

	*area_out = area;
	*claim_out = price;
	*rent_out = rent;
	*for_sale_out = for_sale;
	*dwell_out = dwell;
}

S32 LLViewerParcelMgr::getSelectedArea() const
{
	S32 rv = 0;
	if(mSelected && mCurrentParcel && mCurrentParcelSelection->mWholeParcelSelected)
James Cook's avatar
James Cook committed
	{
		rv = mCurrentParcel->getArea();
James Cook's avatar
James Cook committed
	}
	else if(mSelected)
	{
		F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX];
		F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY];
		F32 area = (F32)(width * height);
Drake Arconis's avatar
Drake Arconis committed
		rv = ll_round(area);
James Cook's avatar
James Cook committed
	}
	return rv;
}

void LLViewerParcelMgr::resetSegments(U8* segments)
{
	S32 i;
	S32 count = (mParcelsPerEdge+1)*(mParcelsPerEdge+1);
	for (i = 0; i < count; i++)
	{
		segments[i] = 0x0;
	}
}


void LLViewerParcelMgr::writeHighlightSegments(F32 west, F32 south, F32 east,
											   F32 north)
{
	S32 x, y;
Drake Arconis's avatar
Drake Arconis committed
	S32 min_x = ll_round( west / PARCEL_GRID_STEP_METERS );
	S32 max_x = ll_round( east / PARCEL_GRID_STEP_METERS );
	S32 min_y = ll_round( south / PARCEL_GRID_STEP_METERS );
	S32 max_y = ll_round( north / PARCEL_GRID_STEP_METERS );
James Cook's avatar
James Cook committed

	const S32 STRIDE = mParcelsPerEdge+1;

	// south edge
	y = min_y;
	for (x = min_x; x < max_x; x++)
	{
		// exclusive OR means that writing to this segment twice
		// will turn it off
		mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
	}

	// west edge
	x = min_x;
	for (y = min_y; y < max_y; y++)
	{
		mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
	}

	// north edge - draw the south border on the y+1'th cell,
	// which given C-style arrays, is item foo[max_y]
	y = max_y;
	for (x = min_x; x < max_x; x++)
	{
		mHighlightSegments[x + y*STRIDE] ^= SOUTH_MASK;
	}

	// east edge - draw west border on x+1'th cell
	x = max_x;
	for (y = min_y; y < max_y; y++)
	{
		mHighlightSegments[x + y*STRIDE] ^= WEST_MASK;
	}
}


void LLViewerParcelMgr::writeSegmentsFromBitmap(U8* bitmap, U8* segments)
{
	S32 x;
	S32 y;
	const S32 IN_STRIDE = mParcelsPerEdge;
	const S32 OUT_STRIDE = mParcelsPerEdge+1;

	for (y = 0; y < IN_STRIDE; y++)
	{
		x = 0;
		while( x < IN_STRIDE )
		{
			U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];

			S32 bit;
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit) )
				{
					S32 out = x+y*OUT_STRIDE;

					// This and one above it
					segments[out]            ^= SOUTH_MASK;
					segments[out+OUT_STRIDE] ^= SOUTH_MASK;

					// This and one to the right
					segments[out]   ^= WEST_MASK;
					segments[out+1] ^= WEST_MASK;
				}
				x++;
			}
		}
	}
}


void LLViewerParcelMgr::writeAgentParcelFromBitmap(U8* bitmap)
{
	S32 x;
	S32 y;
	const S32 IN_STRIDE = mParcelsPerEdge;

	for (y = 0; y < IN_STRIDE; y++)
	{
		x = 0;
		while( x < IN_STRIDE )
		{
			U8 byte = bitmap[ (x + y*IN_STRIDE) / 8 ];

			S32 bit;
			for (bit = 0; bit < 8; bit++)
			{
				if (byte & (1 << bit) )
				{
					mAgentParcelOverlay[x+y*IN_STRIDE] = 1;
				}
				else
				{
					mAgentParcelOverlay[x+y*IN_STRIDE] = 0;
				}
				x++;
			}
		}
	}
}


// Given a point, find the PARCEL_GRID_STEP x PARCEL_GRID_STEP block
// containing it and select that.
LLParcelSelectionHandle LLViewerParcelMgr::selectParcelAt(const LLVector3d& pos_global)
James Cook's avatar
James Cook committed
{
	LLVector3d southwest = pos_global;
	LLVector3d northeast = pos_global;

	southwest -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
Drake Arconis's avatar
Drake Arconis committed
	southwest.mdV[VX] = ll_round( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	southwest.mdV[VY] = ll_round( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
James Cook's avatar
James Cook committed

	northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
Drake Arconis's avatar
Drake Arconis committed
	northeast.mdV[VX] = ll_round( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
	northeast.mdV[VY] = ll_round( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
James Cook's avatar
James Cook committed

	// Snap to parcel
	return selectLand( southwest, northeast, TRUE );
James Cook's avatar
James Cook committed
}


// Tries to select the parcel inside the rectangle
LLParcelSelectionHandle LLViewerParcelMgr::selectParcelInRectangle()
James Cook's avatar
James Cook committed
{
	return selectLand(mWestSouth, mEastNorth, TRUE);
James Cook's avatar
James Cook committed
}


void LLViewerParcelMgr::selectCollisionParcel()
{
	// BUG: Claim to be in the agent's region
Cinder's avatar
Cinder committed
	mWestSouth = getSelectionRegion()->getOriginGlobal();
James Cook's avatar
James Cook committed
	mEastNorth = mWestSouth;
Cinder's avatar
Cinder committed
	mEastNorth += LLVector3d(getSelectionRegion()->getWidth()/REGION_WIDTH_METERS * PARCEL_GRID_STEP_METERS, getSelectionRegion()->getWidth()/REGION_WIDTH_METERS * PARCEL_GRID_STEP_METERS, 0.0);
James Cook's avatar
James Cook committed

	// BUG: must be in the sim you are in
	LLMessageSystem *msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ParcelPropertiesRequestByID);
	msg->nextBlockFast(_PREHASH_AgentID);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
	msg->nextBlockFast(_PREHASH_ParcelData);
	msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
	msg->addS32Fast(_PREHASH_LocalID, mCollisionParcel->getLocalID() );
	gAgent.sendReliableMessage();

	mRequestResult = PARCEL_RESULT_NO_DATA;

	// Hack: Copy some data over temporarily
	mCurrentParcel->setName( mCollisionParcel->getName() );
	mCurrentParcel->setDesc( mCollisionParcel->getDesc() );
	mCurrentParcel->setPassPrice(mCollisionParcel->getPassPrice());
	mCurrentParcel->setPassHours(mCollisionParcel->getPassHours());
James Cook's avatar
James Cook committed

	// clear the list of segments to prevent flashing
	resetSegments(mHighlightSegments);

	mFloatingParcelSelection->setParcel(mCurrentParcel);
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

James Cook's avatar
James Cook committed
	mSelected = TRUE;
	mCurrentParcelSelection->mWholeParcelSelected = TRUE;
James Cook's avatar
James Cook committed
	notifyObservers();
	return;
}


// snap_selection = auto-select the hit parcel, if there is exactly one
LLParcelSelectionHandle LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
James Cook's avatar
James Cook committed
								   BOOL snap_selection)
{
	sanitize_corners( corner1, corner2, mWestSouth, mEastNorth );

	// ...x isn't more than one meter away
	F32 delta_x = getSelectionWidth();
	if (delta_x * delta_x <= 1.f * 1.f)
	{
		mSelected = FALSE;
		notifyObservers();
James Cook's avatar
James Cook committed
	}

	// ...y isn't more than one meter away
	F32 delta_y = getSelectionHeight();
	if (delta_y * delta_y <= 1.f * 1.f)
	{
		mSelected = FALSE;
		notifyObservers();
James Cook's avatar
James Cook committed
	}

	// Can't select across region boundary
	// We need to pull in the upper right corner by a little bit to allow
	// selection up to the x = 256 or y = 256 edge.
	LLVector3d east_north_region_check( mEastNorth );
	east_north_region_check.mdV[VX] -= 0.5;
	east_north_region_check.mdV[VY] -= 0.5;

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal(mWestSouth);
	LLViewerRegion *region_other = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );
James Cook's avatar
James Cook committed

	if(!region)
	{
		// just in case they somehow selected no land.
		mSelected = FALSE;
James Cook's avatar
James Cook committed
	}

	if (region != region_other)
	{
		LLNotificationsUtil::add("CantSelectLandFromMultipleRegions");
James Cook's avatar
James Cook committed
		mSelected = FALSE;
		notifyObservers();
James Cook's avatar
James Cook committed
	}

	// Build region global copies of corners
	LLVector3 wsb_region = region->getPosRegionFromGlobal( mWestSouth );
	LLVector3 ent_region = region->getPosRegionFromGlobal( mEastNorth );

	// Send request message
	LLMessageSystem *msg = gMessageSystem;
	msg->newMessageFast(_PREHASH_ParcelPropertiesRequest);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
	msg->nextBlockFast(_PREHASH_ParcelData);
	msg->addS32Fast(_PREHASH_SequenceID, SELECTED_PARCEL_SEQ_ID );
	msg->addF32Fast(_PREHASH_West,  wsb_region.mV[VX] );
	msg->addF32Fast(_PREHASH_South, wsb_region.mV[VY] );
	msg->addF32Fast(_PREHASH_East,  ent_region.mV[VX] );
	msg->addF32Fast(_PREHASH_North, ent_region.mV[VY] );
	msg->addBOOL("SnapSelection", snap_selection);
	msg->sendReliable( region->getHost() );

	mRequestResult = PARCEL_RESULT_NO_DATA;

	mFloatingParcelSelection->setParcel(mCurrentParcel);
	mCurrentParcelSelection->setParcel(NULL);
	mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

James Cook's avatar
James Cook committed
	mSelected = TRUE;
	mCurrentParcelSelection->mWholeParcelSelected = snap_selection;
James Cook's avatar
James Cook committed
	notifyObservers();
	return mCurrentParcelSelection;
void LLViewerParcelMgr::deselectUnused()
{
	// no more outstanding references to this selection, other than our own
	if (mCurrentParcelSelection->getNumRefs() == 1 && mFloatingParcelSelection->getNumRefs() == 1)
	{
		deselectLand();
	}
}
James Cook's avatar
James Cook committed

void LLViewerParcelMgr::deselectLand()
{
	if (mSelected)
	{
		mSelected = FALSE;

		// Invalidate the selected parcel
		mCurrentParcel->setLocalID(-1);
		mCurrentParcel->mAccessList.clear();
		mCurrentParcel->mBanList.clear();
		//mCurrentParcel->mRenterList.reset();
James Cook's avatar
James Cook committed

James Cook's avatar
James Cook committed

		// invalidate parcel selection so that existing users of this selection can clean up
		mCurrentParcelSelection->setParcel(NULL);
		mFloatingParcelSelection->setParcel(NULL);
		// create new parcel selection
		mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);

		notifyObservers(); // Notify observers *after* changing the parcel selection
James Cook's avatar
James Cook committed
	}
}


void LLViewerParcelMgr::addObserver(LLParcelObserver* observer)
{
James Cook's avatar
James Cook committed
}


void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
{
	vector_replace_with_last(mObservers, observer);
James Cook's avatar
James Cook committed
}


// Call this method when it's time to update everyone on a new state.
// Copy the list because an observer could respond by removing itself
// from the list.
void LLViewerParcelMgr::notifyObservers()
{
	std::vector<LLParcelObserver*> observers;
	S32 count = mObservers.size();
James Cook's avatar
James Cook committed
	S32 i;
	for(i = 0; i < count; ++i)
	{
James Cook's avatar
James Cook committed
	}
	for(i = 0; i < count; ++i)
	{
James Cook's avatar
James Cook committed
	}
}


//
// ACCESSORS
//
BOOL LLViewerParcelMgr::selectionEmpty() const
{
	return !mSelected;
}


LLParcelSelectionHandle LLViewerParcelMgr::getParcelSelection() const
James Cook's avatar
James Cook committed
{
	return mCurrentParcelSelection;
LLParcelSelectionHandle LLViewerParcelMgr::getFloatingParcelSelection() const
{
	return mFloatingParcelSelection;
}
James Cook's avatar
James Cook committed

LLParcel *LLViewerParcelMgr::getAgentParcel() const
{
	return mAgentParcel;
}

// Return whether the agent can build on the land they are on
bool LLViewerParcelMgr::allowAgentBuild() const
James Cook's avatar
James Cook committed
{
	if (mAgentParcel)
	{
Adam Moss's avatar
Adam Moss committed
		return (gAgent.isGodlike() ||
				(mAgentParcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID())) ||
				(isParcelOwnedByAgent(mAgentParcel, GP_LAND_ALLOW_CREATE)));
James Cook's avatar
James Cook committed
	}
	else
	{
		return gAgent.isGodlike();
	}
}

// Return whether anyone can build on the given parcel
bool LLViewerParcelMgr::allowAgentBuild(const LLParcel* parcel) const
{
	return parcel->getAllowModify();
}

bool LLViewerParcelMgr::allowAgentVoice() const
James Cook's avatar
James Cook committed
{
	return allowAgentVoice(gAgent.getRegion(), mAgentParcel);
}

bool LLViewerParcelMgr::allowAgentVoice(const LLViewerRegion* region, const LLParcel* parcel) const
{
	return region && region->isVoiceEnabled()
		&& parcel	&& parcel->getParcelFlagAllowVoice();
bool LLViewerParcelMgr::allowAgentFly(const LLViewerRegion* region, const LLParcel* parcel) const
James Cook's avatar
James Cook committed
{
	return region && !region->getBlockFly()
		&& parcel && parcel->getAllowFly();
// Can the agent be pushed around by LLPushObject?
bool LLViewerParcelMgr::allowAgentPush(const LLViewerRegion* region, const LLParcel* parcel) const
James Cook's avatar
James Cook committed
{
	return region && !region->getRestrictPushObject()
		&& parcel && !parcel->getRestrictPushObject();
bool LLViewerParcelMgr::allowAgentScripts(const LLViewerRegion* region, const LLParcel* parcel) const
{
	// *NOTE: This code does not take into account group-owned parcels
	// and the flag to allow group-owned scripted objects to run.
	// This mirrors the traditional menu bar parcel icon code, but is not
	// technically correct.
	return region
		&& !region->getRegionFlag(REGION_FLAGS_SKIP_SCRIPTS)
		&& !region->getRegionFlag(REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
		&& parcel
		&& parcel->getAllowOtherScripts();
bool LLViewerParcelMgr::allowAgentDamage(const LLViewerRegion* region, const LLParcel* parcel) const
		|| (parcel && parcel->getAllowDamage());
James Cook's avatar
James Cook committed
}

BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
James Cook's avatar
James Cook committed
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwned( pos_region );
}

BOOL LLViewerParcelMgr::isOwnedSelfAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
James Cook's avatar
James Cook committed
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwnedSelf( pos_region );
}

BOOL LLViewerParcelMgr::isOwnedOtherAt(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
James Cook's avatar
James Cook committed
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isOwnedOther( pos_region );
}

BOOL LLViewerParcelMgr::isSoundLocal(const LLVector3d& pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
James Cook's avatar
James Cook committed
	if (!region) return FALSE;

	LLViewerParcelOverlay* overlay = region->getParcelOverlay();
	if (!overlay) return FALSE;

	LLVector3 pos_region = region->getPosRegionFromGlobal( pos_global );

	return overlay->isSoundLocal( pos_region );
}

BOOL LLViewerParcelMgr::canHearSound(const LLVector3d &pos_global) const
{
	BOOL in_agent_parcel = inAgentParcel(pos_global);

	if (in_agent_parcel)
	{
		// In same parcel as the agent
		return TRUE;
	}
	else
	{
		if (LLViewerParcelMgr::getInstance()->getAgentParcel()->getSoundLocal())
James Cook's avatar
James Cook committed
		{
			// Not in same parcel, and agent parcel only has local sound
			return FALSE;
		}
		else if (LLViewerParcelMgr::getInstance()->isSoundLocal(pos_global))
James Cook's avatar
James Cook committed
		{
			// Not in same parcel, and target parcel only has local sound
			return FALSE;
		}
		else
		{
			// Not in same parcel, but neither are local sound
			return TRUE;
		}
	}
}


BOOL LLViewerParcelMgr::inAgentParcel(const LLVector3d &pos_global) const
{
	LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(pos_global);
Steven Bennetts's avatar
Steven Bennetts committed
	LLViewerRegion* agent_region = gAgent.getRegion();
	if (!region || !agent_region)
		return FALSE;

	if (region != agent_region)
James Cook's avatar
James Cook committed
	{
		// Can't be in the agent parcel if you're not in the same region.
		return FALSE;
	}

Steven Bennetts's avatar
Steven Bennetts committed
	LLVector3 pos_region = agent_region->getPosRegionFromGlobal(pos_global);
James Cook's avatar
James Cook committed
	S32 row =    S32(pos_region.mV[VY] / PARCEL_GRID_STEP_METERS);
	S32 column = S32(pos_region.mV[VX] / PARCEL_GRID_STEP_METERS);

	if (mAgentParcelOverlay[row*mParcelsPerEdge + column])
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}
}

// Returns NULL when there is no valid data.
LLParcel* LLViewerParcelMgr::getHoverParcel() const
{
	if (mHoverRequestResult == PARCEL_RESULT_SUCCESS)
	{
		return mHoverParcel;
	}
	else
	{
		return NULL;
	}
}

// Returns NULL when there is no valid data.
LLParcel* LLViewerParcelMgr::getCollisionParcel() const
{
	if (mRenderCollision)
	{
		return mCollisionParcel;
	}
	else
	{
		return NULL;
	}
}

//
// UTILITIES
//

void LLViewerParcelMgr::render()
{
	if (mSelected && mRenderSelection && gSavedSettings.getBOOL("RenderParcelSelection"))
James Cook's avatar
James Cook committed
	{
		// Rendering is done in agent-coordinates, so need to supply
		// an appropriate offset to the render code.
		LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mWestSouth);
James Cook's avatar
James Cook committed
		if (!regionp) return;

		renderHighlightSegments(mHighlightSegments, regionp);
	}
}


void LLViewerParcelMgr::renderParcelCollision()
{
	// check for expiration
	if (mCollisionTimer.getElapsedTimeF32() > PARCEL_COLLISION_DRAW_SECS)
	{
		mRenderCollision = FALSE;
	}

	if (mRenderCollision && gSavedSettings.getBOOL("ShowBanLines"))
James Cook's avatar
James Cook committed
	{
		LLViewerRegion* regionp = gAgent.getRegion();
		if (regionp)
		{
			BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST);
			renderCollisionSegments(mCollisionSegments, use_pass, regionp);
		}
James Cook's avatar
James Cook committed
	}
}


void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
{
	if (!mSelected)
	{
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
James Cook's avatar
James Cook committed
	if (!region) return;

	LLMessageSystem *msg = gMessageSystem;
James Cook's avatar
James Cook committed

	if (flags & AL_BAN) 
	{
		mCurrentParcel->mBanList.clear();
James Cook's avatar
James Cook committed
	}
	if (flags & AL_ACCESS) 
	{
		mCurrentParcel->mAccessList.clear();
James Cook's avatar
James Cook committed
	}		
	if (flags & AL_ALLOW_EXPERIENCE) 
	{
		mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_ALLOWED);
	}
	if (flags & AL_BLOCK_EXPERIENCE) 
	{
		mCurrentParcel->clearExperienceKeysByType(EXPERIENCE_KEY_TYPE_BLOCKED);
	}		
James Cook's avatar
James Cook committed

	// Only the headers differ
	msg->newMessageFast(_PREHASH_ParcelAccessListRequest);
	msg->nextBlockFast(_PREHASH_AgentData);
	msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
	msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
	msg->nextBlockFast(_PREHASH_Data);
	msg->addS32Fast(_PREHASH_SequenceID, 0);
	msg->addU32Fast(_PREHASH_Flags, flags);
	msg->addS32("LocalID", mCurrentParcel->getLocalID() );
James Cook's avatar
James Cook committed
	msg->sendReliable( region->getHost() );
}


void LLViewerParcelMgr::sendParcelDwellRequest()
{
	if (!mSelected)
	{
		return;
	}

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
James Cook's avatar
James Cook committed
	if (!region) return;

	LLMessageSystem *msg = gMessageSystem;

	// Only the headers differ
	msg->newMessage("ParcelDwellRequest");
	msg->nextBlock("AgentData");
	msg->addUUID("AgentID", gAgent.getID() );
	msg->addUUID("SessionID", gAgent.getSessionID());
	msg->nextBlock("Data");
	msg->addS32("LocalID", mCurrentParcel->getLocalID());
James Cook's avatar
James Cook committed
	msg->addUUID("ParcelID", LLUUID::null);	// filled in on simulator
	msg->sendReliable( region->getHost() );
}


void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id)
{
	if (!mSelected)
	{
		LLNotificationsUtil::add("CannotSetLandOwnerNothingSelected");
James Cook's avatar
James Cook committed
		return;
	}

	LL_INFOS() << "Claiming " << mWestSouth << " to " << mEastNorth << LL_ENDL;
James Cook's avatar
James Cook committed

	// BUG: Only works for the region containing mWestSouthBottom
	LLVector3d east_north_region_check( mEastNorth );
	east_north_region_check.mdV[VX] -= 0.5;
	east_north_region_check.mdV[VY] -= 0.5;

	LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
James Cook's avatar
James Cook committed
	if (!region)
	{
		// TODO: Add a force owner version of this alert.
		LLNotificationsUtil::add("CannotContentifyNoRegion");
James Cook's avatar
James Cook committed
		return;
	}

	// BUG: Make work for cross-region selections
	LLViewerRegion *region2 = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );
James Cook's avatar
James Cook committed
	if (region != region2)
	{
		LLNotificationsUtil::add("CannotSetLandOwnerMultipleRegions");
James Cook's avatar
James Cook committed
		return;
	}

	LL_INFOS() << "Region " << region->getOriginGlobal() << LL_ENDL;
James Cook's avatar
James Cook committed

Kent Quirk's avatar
Kent Quirk committed
	LLSD payload;
	payload["owner_id"] = owner_id;
	payload["parcel_local_id"] = mCurrentParcel->getLocalID();
	payload["region_host"] = region->getHost().getIPandPort();
	LLNotification::Params params("ForceOwnerAuctionWarning");
	params.payload(payload).functor.function(callback_god_force_owner);
	if(mCurrentParcel->getAuctionID())
James Cook's avatar
James Cook committed
	{
Kent Quirk's avatar
Kent Quirk committed
		LLNotifications::instance().add(params);
James Cook's avatar
James Cook committed
	}
	else