Files
SingularityViewer/indra/newview/llviewerparcelmgr.cpp
Shyotl 863ab7fa6b Updated LLViewerParcelMgr.
Added LLViewerParcelMgr::setTeleportFinishedCallback, setTeleportFailedCallback. Not hooked into anything yet, however.
agentCanBuild, agentCanFly, etc, renamed.
2011-08-19 02:15:22 -05:00

2585 lines
72 KiB
C++

/**
* @file llviewerparcelmgr.cpp
* @brief Viewer-side representation of owned land
*
* $LicenseInfo:firstyear=2002&license=viewergpl$
*
* Copyright (c) 2002-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "llviewerparcelmgr.h"
// Library includes
#include "llaudioengine.h"
#include "indra_constants.h"
#include "llcachename.h"
#include "llgl.h"
#include "llparcel.h"
#include "llsecondlifeurls.h"
#include "message.h"
// Viewer includes
#include "llagent.h"
#include "llagentaccess.h"
#include "llfloatergroupinfo.h"
#include "llviewerwindow.h"
#include "llviewercontrol.h"
#include "llfirstuse.h"
#include "llfloaterbuyland.h"
#include "llfloatergroups.h"
//#include "llfloaterhtml.h"
#include "llfloatersellland.h"
#include "llfloaterteleporthistory.h"
#include "llfloatertools.h"
#include "llnotify.h"
#include "llparcelselection.h"
#include "llresmgr.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "llstatusbar.h"
#include "llui.h"
#include "llviewertexturelist.h"
#include "llviewermenu.h"
#include "llviewerparcelmedia.h"
#include "llviewerparceloverlay.h"
#include "llviewerregion.h"
#include "llworld.h"
#include "lloverlaybar.h"
#include "roles_constants.h"
#include "llweb.h"
const F32 PARCEL_COLLISION_DRAW_SECS = 1.f;
// Globals
U8* LLViewerParcelMgr::sPackedOverlay = NULL;
LLUUID gCurrentMovieID = LLUUID::null;
LLPointer<LLViewerTexture> sBlockedImage;
LLPointer<LLViewerTexture> sPassImage;
// Local functions
void optionally_start_music(LLParcel* parcel);
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();
bool callback_god_force_owner(const LLSD&, const LLSD&);
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),
mRequestResult(0),
mWestSouth(),
mEastNorth(),
mSelectedDwell(0.f),
mAgentParcelSequenceID(-1),
mHoverRequestResult(0),
mHoverWestSouth(),
mHoverEastNorth(),
mRenderCollision(FALSE),
mRenderSelection(TRUE),
mCollisionBanned(0),
mCollisionTimer(),
mMediaParcelId(0),
mMediaRegionId(0)
{
mCurrentParcel = new LLParcel();
mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
mFloatingParcelSelection = new LLParcelSelection(mCurrentParcel);
mAgentParcel = new LLParcel();
mHoverParcel = new LLParcel();
mCollisionParcel = new LLParcel();
mParcelsPerEdge = S32( REGION_WIDTH_METERS / PARCEL_GRID_STEP_METERS );
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("noentrylines.j2c");
mPassImage = LLViewerTextureManager::getFetchedTextureFromFile("noentrypasslines.j2c");
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;
}
mTeleportInProgress = TRUE; // the initial parcel update is treated like teleport
}
LLViewerParcelMgr::~LLViewerParcelMgr()
{
mCurrentParcelSelection->setParcel(NULL);
mCurrentParcelSelection = NULL;
mFloatingParcelSelection->setParcel(NULL);
mFloatingParcelSelection = NULL;
delete mCurrentParcel;
mCurrentParcel = NULL;
delete mAgentParcel;
mAgentParcel = NULL;
delete mCollisionParcel;
mCollisionParcel = NULL;
delete mHoverParcel;
mHoverParcel = NULL;
delete[] mHighlightSegments;
mHighlightSegments = NULL;
delete[] mCollisionSegments;
mCollisionSegments = NULL;
delete[] sPackedOverlay;
sPackedOverlay = NULL;
delete[] mAgentParcelOverlay;
mAgentParcelOverlay = NULL;
sBlockedImage = NULL;
sPassImage = NULL;
}
void LLViewerParcelMgr::dump()
{
llinfos << "Parcel Manager Dump" << llendl;
llinfos << "mSelected " << S32(mSelected) << llendl;
llinfos << "Selected parcel: " << llendl;
llinfos << mWestSouth << " to " << mEastNorth << llendl;
mCurrentParcel->dump();
llinfos << "banning " << mCurrentParcel->mBanList.size() << llendl;
access_map_const_iterator cit = mCurrentParcel->mBanList.begin();
access_map_const_iterator end = mCurrentParcel->mBanList.end();
for ( ; cit != end; ++cit)
{
llinfos << "ban id " << (*cit).first << llendl;
}
llinfos << "Hover parcel:" << llendl;
mHoverParcel->dump();
llinfos << "Agent parcel:" << llendl;
mAgentParcel->dump();
}
LLViewerRegion* LLViewerParcelMgr::getSelectionRegion()
{
return LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
}
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;
F32 dwell = 0.f;
if (mSelected)
{
if (mCurrentParcelSelection->mSelectedMultipleOwners)
{
area = mCurrentParcelSelection->getClaimableArea();
}
else
{
area = getSelectedArea();
}
if (mCurrentParcel->getForSale())
{
price = mCurrentParcel->getSalePrice();
for_sale = TRUE;
}
else
{
price = area * mCurrentParcel->getClaimPricePerMeter();
for_sale = FALSE;
}
rent = mCurrentParcel->getTotalRent();
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)
{
rv = mCurrentParcel->getArea();
}
else if(mSelected)
{
F64 width = mEastNorth.mdV[VX] - mWestSouth.mdV[VX];
F64 height = mEastNorth.mdV[VY] - mWestSouth.mdV[VY];
F32 area = (F32)(width * height);
rv = llround(area);
}
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;
S32 min_x = llround( west / PARCEL_GRID_STEP_METERS );
S32 max_x = llround( east / PARCEL_GRID_STEP_METERS );
S32 min_y = llround( south / PARCEL_GRID_STEP_METERS );
S32 max_y = llround( north / PARCEL_GRID_STEP_METERS );
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)
{
LLVector3d southwest = pos_global;
LLVector3d northeast = pos_global;
southwest -= LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
southwest.mdV[VX] = llround( southwest.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
southwest.mdV[VY] = llround( southwest.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
northeast += LLVector3d( PARCEL_GRID_STEP_METERS/2, PARCEL_GRID_STEP_METERS/2, 0 );
northeast.mdV[VX] = llround( northeast.mdV[VX], (F64)PARCEL_GRID_STEP_METERS );
northeast.mdV[VY] = llround( northeast.mdV[VY], (F64)PARCEL_GRID_STEP_METERS );
// Snap to parcel
return selectLand( southwest, northeast, TRUE );
}
// Tries to select the parcel inside the rectangle
LLParcelSelectionHandle LLViewerParcelMgr::selectParcelInRectangle()
{
return selectLand(mWestSouth, mEastNorth, TRUE);
}
void LLViewerParcelMgr::selectCollisionParcel()
{
// BUG: Claim to be in the agent's region
mWestSouth = gAgent.getRegion()->getOriginGlobal();
mEastNorth = mWestSouth;
mEastNorth += LLVector3d(PARCEL_GRID_STEP_METERS, PARCEL_GRID_STEP_METERS, 0.0);
// 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());
// clear the list of segments to prevent flashing
resetSegments(mHighlightSegments);
mFloatingParcelSelection->setParcel(mCurrentParcel);
mCurrentParcelSelection->setParcel(NULL);
mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
mSelected = TRUE;
mCurrentParcelSelection->mWholeParcelSelected = TRUE;
notifyObservers();
return;
}
// snap_selection = auto-select the hit parcel, if there is exactly one
LLParcelSelectionHandle LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2,
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();
return NULL;
}
// ...y isn't more than one meter away
F32 delta_y = getSelectionHeight();
if (delta_y * delta_y <= 1.f * 1.f)
{
mSelected = FALSE;
notifyObservers();
return NULL;
}
// 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 );
if(!region)
{
// just in case they somehow selected no land.
mSelected = FALSE;
return NULL;
}
if (region != region_other)
{
LLNotifications::instance().add("CantSelectLandFromMultipleRegions");
mSelected = FALSE;
notifyObservers();
return NULL;
}
// 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;
// clear the list of segments to prevent flashing
resetSegments(mHighlightSegments);
mFloatingParcelSelection->setParcel(mCurrentParcel);
mCurrentParcelSelection->setParcel(NULL);
mCurrentParcelSelection = new LLParcelSelection(mCurrentParcel);
mSelected = TRUE;
mCurrentParcelSelection->mWholeParcelSelected = snap_selection;
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();
}
}
void LLViewerParcelMgr::deselectLand()
{
if (mSelected)
{
mSelected = FALSE;
// Invalidate the selected parcel
mCurrentParcel->setLocalID(-1);
mCurrentParcel->mAccessList.clear();
mCurrentParcel->mBanList.clear();
//mCurrentParcel->mRenterList.reset();
mSelectedDwell = 0.f;
// 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
}
}
void LLViewerParcelMgr::addObserver(LLParcelObserver* observer)
{
mObservers.put(observer);
}
void LLViewerParcelMgr::removeObserver(LLParcelObserver* observer)
{
mObservers.removeObj(observer);
}
// 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()
{
LLDynamicArray<LLParcelObserver*> observers;
S32 count = mObservers.count();
S32 i;
for(i = 0; i < count; ++i)
{
observers.put(mObservers.get(i));
}
for(i = 0; i < count; ++i)
{
observers.get(i)->changed();
}
}
//
// ACCESSORS
//
BOOL LLViewerParcelMgr::selectionEmpty() const
{
return !mSelected;
}
LLParcelSelectionHandle LLViewerParcelMgr::getParcelSelection() const
{
return mCurrentParcelSelection;
}
LLParcelSelectionHandle LLViewerParcelMgr::getFloatingParcelSelection() const
{
return mFloatingParcelSelection;
}
LLParcel *LLViewerParcelMgr::getAgentParcel() const
{
return mAgentParcel;
}
// Return whether the agent can build on the land they are on
bool LLViewerParcelMgr::allowAgentBuild() const
{
if (mAgentParcel)
{
return (gAgent.isGodlike() ||
(mAgentParcel->allowModifyBy(gAgent.getID(), gAgent.getGroupID())) ||
(isParcelOwnedByAgent(mAgentParcel, GP_LAND_ALLOW_CREATE)));
}
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
{
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
{
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
{
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->getRegionFlags() & REGION_FLAGS_SKIP_SCRIPTS)
&& !(region->getRegionFlags() & REGION_FLAGS_ESTATE_SKIP_SCRIPTS)
&& parcel
&& parcel->getAllowOtherScripts();
}
bool LLViewerParcelMgr::allowAgentDamage(const LLViewerRegion* region, const LLParcel* parcel) const
{
return (region && region->getAllowDamage())
|| (parcel && parcel->getAllowDamage());
}
BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const
{
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos_global );
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 );
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 );
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 );
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())
{
// Not in same parcel, and agent parcel only has local sound
return FALSE;
}
else if (LLViewerParcelMgr::getInstance()->isSoundLocal(pos_global))
{
// 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);
LLViewerRegion* agent_region = gAgent.getRegion();
if (!region || !agent_region)
return FALSE;
if (region != agent_region)
{
// Can't be in the agent parcel if you're not in the same region.
return FALSE;
}
LLVector3 pos_region = agent_region->getPosRegionFromGlobal(pos_global);
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)
{
// Rendering is done in agent-coordinates, so need to supply
// an appropriate offset to the render code.
LLViewerRegion* regionp = LLWorld::getInstance()->getRegionFromPosGlobal(mWestSouth);
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"))
{
LLViewerRegion* regionp = gAgent.getRegion();
if (regionp)
{
BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST);
renderCollisionSegments(mCollisionSegments, use_pass, regionp);
}
}
}
void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags)
{
if (!mSelected)
{
return;
}
LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region) return;
LLMessageSystem *msg = gMessageSystem;
if (flags & AL_BAN)
{
mCurrentParcel->mBanList.clear();
}
if (flags & AL_ACCESS)
{
mCurrentParcel->mAccessList.clear();
}
// 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() );
msg->sendReliable( region->getHost() );
}
void LLViewerParcelMgr::sendParcelDwellRequest()
{
if (!mSelected)
{
return;
}
LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
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());
msg->addUUID("ParcelID", LLUUID::null); // filled in on simulator
msg->sendReliable( region->getHost() );
}
void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id)
{
if (!mSelected)
{
LLNotifications::instance().add("CannotSetLandOwnerNothingSelected");
return;
}
llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
// 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 );
if (!region)
{
// TODO: Add a force owner version of this alert.
LLNotifications::instance().add("CannotContentifyNoRegion");
return;
}
// BUG: Make work for cross-region selections
LLViewerRegion *region2 = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );
if (region != region2)
{
LLNotifications::instance().add("CannotSetLandOwnerMultipleRegions");
return;
}
llinfos << "Region " << region->getOriginGlobal() << llendl;
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(callback_god_force_owner);
if(mCurrentParcel->getAuctionID())
{
LLNotifications::instance().add(params);
}
else
{
LLNotifications::instance().forceResponse(params, 0);
}
}
bool callback_god_force_owner(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
if(0 == option)
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelGodForceOwner");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("Data");
msg->addUUID("OwnerID", notification["payload"]["owner_id"].asUUID());
msg->addS32( "LocalID", notification["payload"]["parcel_local_id"].asInteger());
msg->sendReliable(LLHost(notification["payload"]["region_host"].asString()));
}
return false;
}
void LLViewerParcelMgr::sendParcelGodForceToContent()
{
if (!mSelected)
{
LLNotifications::instance().add("CannotContentifyNothingSelected");
return;
}
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region)
{
LLNotifications::instance().add("CannotContentifyNoRegion");
return;
}
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelGodMarkAsContent");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("ParcelData");
msg->addS32("LocalID", mCurrentParcel->getLocalID());
msg->sendReliable(region->getHost());
}
void LLViewerParcelMgr::sendParcelRelease()
{
if (!mSelected)
{
LLNotifications::instance().add("CannotReleaseLandNothingSelected");
return;
}
LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region)
{
LLNotifications::instance().add("CannotReleaseLandNoRegion");
return;
}
//U32 flags = PR_NONE;
//if (god_force) flags |= PR_GOD_FORCE;
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelRelease");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID() );
msg->addUUID("SessionID", gAgent.getSessionID() );
msg->nextBlock("Data");
msg->addS32("LocalID", mCurrentParcel->getLocalID() );
//msg->addU32("Flags", flags);
msg->sendReliable( region->getHost() );
// Blitz selection, since the parcel might be non-rectangular, and
// we won't have appropriate parcel information.
deselectLand();
}
class LLViewerParcelMgr::ParcelBuyInfo
{
public:
LLUUID mAgent;
LLUUID mSession;
LLUUID mGroup;
BOOL mIsGroupOwned;
BOOL mRemoveContribution;
BOOL mIsClaim;
LLHost mHost;
// for parcel buys
S32 mParcelID;
S32 mPrice;
S32 mArea;
// for land claims
F32 mWest;
F32 mSouth;
F32 mEast;
F32 mNorth;
};
LLViewerParcelMgr::ParcelBuyInfo* LLViewerParcelMgr::setupParcelBuy(
const LLUUID& agent_id,
const LLUUID& session_id,
const LLUUID& group_id,
BOOL is_group_owned,
BOOL is_claim,
BOOL remove_contribution)
{
if (!mSelected || !mCurrentParcel)
{
LLNotifications::instance().add("CannotBuyLandNothingSelected");
return NULL;
}
LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region)
{
LLNotifications::instance().add("CannotBuyLandNoRegion");
return NULL;
}
if (is_claim)
{
llinfos << "Claiming " << mWestSouth << " to " << mEastNorth << llendl;
llinfos << "Region " << region->getOriginGlobal() << llendl;
// 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 *region2 = LLWorld::getInstance()->getRegionFromPosGlobal( east_north_region_check );
if (region != region2)
{
LLNotifications::instance().add("CantBuyLandAcrossMultipleRegions");
return NULL;
}
}
ParcelBuyInfo* info = new ParcelBuyInfo;
info->mAgent = agent_id;
info->mSession = session_id;
info->mGroup = group_id;
info->mIsGroupOwned = is_group_owned;
info->mIsClaim = is_claim;
info->mRemoveContribution = remove_contribution;
info->mHost = region->getHost();
info->mPrice = mCurrentParcel->getSalePrice();
info->mArea = mCurrentParcel->getArea();
if (!is_claim)
{
info->mParcelID = mCurrentParcel->getLocalID();
}
else
{
// BUG: Make work for cross-region selections
LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
info->mWest = west_south_bottom_region.mV[VX];
info->mSouth = west_south_bottom_region.mV[VY];
info->mEast = east_north_top_region.mV[VX];
info->mNorth = east_north_top_region.mV[VY];
}
return info;
}
void LLViewerParcelMgr::sendParcelBuy(ParcelBuyInfo* info)
{
// send the message
LLMessageSystem* msg = gMessageSystem;
msg->newMessage(info->mIsClaim ? "ParcelClaim" : "ParcelBuy");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", info->mAgent);
msg->addUUID("SessionID", info->mSession);
msg->nextBlock("Data");
msg->addUUID("GroupID", info->mGroup);
msg->addBOOL("IsGroupOwned", info->mIsGroupOwned);
if (!info->mIsClaim)
{
msg->addBOOL("RemoveContribution", info->mRemoveContribution);
msg->addS32("LocalID", info->mParcelID);
}
msg->addBOOL("Final", TRUE); // don't allow escrow buys
if (info->mIsClaim)
{
msg->nextBlock("ParcelData");
msg->addF32("West", info->mWest);
msg->addF32("South", info->mSouth);
msg->addF32("East", info->mEast);
msg->addF32("North", info->mNorth);
}
else // ParcelBuy
{
msg->nextBlock("ParcelData");
msg->addS32("Price",info->mPrice);
msg->addS32("Area",info->mArea);
}
msg->sendReliable(info->mHost);
}
void LLViewerParcelMgr::deleteParcelBuy(ParcelBuyInfo* *info)
{
// Must be here because ParcelBuyInfo is local to this .cpp file
delete *info;
*info = NULL;
}
void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id)
{
if (!mSelected || !mCurrentParcel)
{
LLNotifications::instance().add("CannotDeedLandNothingSelected");
return;
}
if(group_id.isNull())
{
LLNotifications::instance().add("CannotDeedLandNoGroup");
return;
}
LLViewerRegion *region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region)
{
LLNotifications::instance().add("CannotDeedLandNoRegion");
return;
}
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelDeedToGroup");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID() );
msg->addUUID("SessionID", gAgent.getSessionID() );
msg->nextBlock("Data");
msg->addUUID("GroupID", group_id );
msg->addS32("LocalID", mCurrentParcel->getLocalID() );
//msg->addU32("JoinNeighbors", join);
msg->sendReliable( region->getHost() );
}
/*
// *NOTE: We cannot easily make landmarks at global positions because
// global positions probably refer to a sim/local combination which
// can move over time. We could implement this by looking up the
// region global x,y, but it's easier to take it out for now.
void LLViewerParcelMgr::makeLandmarkAtSelection()
{
// Don't create for parcels you don't own
if (gAgent.getID() != mCurrentParcel->getOwnerID())
{
return;
}
LLVector3d global_center(mWestSouth);
global_center += mEastNorth;
global_center *= 0.5f;
LLViewerRegion* region;
region = LLWorld::getInstance()->getRegionFromPosGlobal(global_center);
LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth );
LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth );
std::string name("My Land");
std::string buffer;
S32 pos_x = (S32)floor((west_south_bottom_region.mV[VX] + east_north_top_region.mV[VX]) / 2.0f);
S32 pos_y = (S32)floor((west_south_bottom_region.mV[VY] + east_north_top_region.mV[VY]) / 2.0f);
buffer = llformat("%s in %s (%d, %d)",
name.c_str(),
region->getName().c_str(),
pos_x, pos_y);
name.assign(buffer);
create_landmark(name, "Claimed land", global_center);
}
*/
const std::string& LLViewerParcelMgr::getAgentParcelName() const
{
return mAgentParcel->getName();
}
void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, bool use_agent_region)
{
if(!parcel) return;
LLViewerRegion *region = use_agent_region ? gAgent.getRegion() : LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region) return;
//llinfos << "found region: " << region->getName() << llendl;
LLSD body;
std::string url = region->getCapability("ParcelPropertiesUpdate");
if (!url.empty())
{
// request new properties update from simulator
U32 message_flags = 0x01;
body["flags"] = ll_sd_from_U32(message_flags);
parcel->packMessage(body);
llinfos << "Sending parcel properties update via capability to: "
<< url << llendl;
LLHTTPClient::post(url, body, new LLHTTPClient::Responder());
}
else
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_ParcelPropertiesUpdate);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_ParcelData);
msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
U32 message_flags = 0x01;
msg->addU32("Flags", message_flags);
parcel->packMessage(msg);
msg->sendReliable( region->getHost() );
}
}
void LLViewerParcelMgr::setHoverParcel(const LLVector3d& pos)
{
static U32 last_west, last_south;
// only request parcel info when tooltip is shown
if (!gSavedSettings.getBOOL("ShowLandHoverTip"))
{
return;
}
// only request parcel info if position has changed outside of the
// last parcel grid step
U32 west_parcel_step = (U32) floor( pos.mdV[VX] / PARCEL_GRID_STEP_METERS );
U32 south_parcel_step = (U32) floor( pos.mdV[VY] / PARCEL_GRID_STEP_METERS );
if ((west_parcel_step == last_west) && (south_parcel_step == last_south))
{
return;
}
else
{
last_west = west_parcel_step;
last_south = south_parcel_step;
}
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( pos );
if (!region)
{
return;
}
// Send a rectangle around the point.
// This means the parcel sent back is at least a rectangle around the point,
// which is more efficient for public land. Fewer requests are sent. JC
LLVector3 wsb_region = region->getPosRegionFromGlobal( pos );
F32 west = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VX] / PARCEL_GRID_STEP_METERS );
F32 south = PARCEL_GRID_STEP_METERS * floor( wsb_region.mV[VY] / PARCEL_GRID_STEP_METERS );
F32 east = west + PARCEL_GRID_STEP_METERS;
F32 north = south + PARCEL_GRID_STEP_METERS;
// 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, HOVERED_PARCEL_SEQ_ID );
msg->addF32Fast(_PREHASH_West, west );
msg->addF32Fast(_PREHASH_South, south );
msg->addF32Fast(_PREHASH_East, east );
msg->addF32Fast(_PREHASH_North, north );
msg->addBOOL("SnapSelection", FALSE );
msg->sendReliable( region->getHost() );
mHoverRequestResult = PARCEL_RESULT_NO_DATA;
}
// static
void LLViewerParcelMgr::processParcelOverlay(LLMessageSystem *msg, void **user)
{
if (gNoRender)
{
return;
}
// Extract the packed overlay information
S32 packed_overlay_size = msg->getSizeFast(_PREHASH_ParcelData, _PREHASH_Data);
if (packed_overlay_size <= 0)
{
llwarns << "Overlay size " << packed_overlay_size << llendl;
return;
}
S32 parcels_per_edge = LLViewerParcelMgr::getInstance()->mParcelsPerEdge;
S32 expected_size = parcels_per_edge * parcels_per_edge / PARCEL_OVERLAY_CHUNKS;
if (packed_overlay_size != expected_size)
{
llwarns << "Got parcel overlay size " << packed_overlay_size
<< " expecting " << expected_size << llendl;
return;
}
S32 sequence_id;
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id);
msg->getBinaryDataFast(
_PREHASH_ParcelData,
_PREHASH_Data,
sPackedOverlay,
expected_size);
LLHost host = msg->getSender();
LLViewerRegion *region = LLWorld::getInstance()->getRegion(host);
if (region)
{
region->mParcelOverlay->uncompressLandOverlay( sequence_id, sPackedOverlay );
}
}
// static
void LLViewerParcelMgr::processParcelProperties(LLMessageSystem *msg, void **user)
{
S32 request_result;
S32 sequence_id;
BOOL snap_selection = FALSE;
S32 self_count = 0;
S32 other_count = 0;
S32 public_count = 0;
S32 local_id;
LLUUID owner_id;
BOOL is_group_owned;
U32 auction_id = 0;
S32 claim_price_per_meter = 0;
S32 rent_price_per_meter = 0;
S32 claim_date = 0;
LLVector3 aabb_min;
LLVector3 aabb_max;
S32 area = 0;
S32 sw_max_prims = 0;
S32 sw_total_prims = 0;
//LLUUID buyer_id;
U8 status = 0;
S32 max_prims = 0;
S32 total_prims = 0;
S32 owner_prims = 0;
S32 group_prims = 0;
S32 other_prims = 0;
S32 selected_prims = 0;
F32 parcel_prim_bonus = 1.f;
BOOL region_push_override = false;
BOOL region_deny_anonymous_override = false;
BOOL region_deny_identified_override = false; // Deprecated
BOOL region_deny_transacted_override = false; // Deprecated
BOOL region_deny_age_unverified_override = false;
S32 other_clean_time = 0;
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_RequestResult, request_result );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SequenceID, sequence_id );
if (request_result == PARCEL_RESULT_NO_DATA)
{
// no valid parcel data
llinfos << "no valid parcel data" << llendl;
return;
}
// Decide where the data will go.
LLParcel* parcel = NULL;
if (sequence_id == SELECTED_PARCEL_SEQ_ID)
{
// ...selected parcels report this sequence id
LLViewerParcelMgr::getInstance()->mRequestResult = PARCEL_RESULT_SUCCESS;
parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
}
else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
{
LLViewerParcelMgr::getInstance()->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
parcel = LLViewerParcelMgr::getInstance()->mHoverParcel;
}
else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
{
LLViewerParcelMgr::getInstance()->mHoverRequestResult = PARCEL_RESULT_SUCCESS;
parcel = LLViewerParcelMgr::getInstance()->mCollisionParcel;
}
else if (sequence_id == 0 || sequence_id > LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID)
{
// new agent parcel
LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID = sequence_id;
parcel = LLViewerParcelMgr::getInstance()->mAgentParcel;
}
else
{
llinfos << "out of order agent parcel sequence id " << sequence_id
<< " last good " << LLViewerParcelMgr::getInstance()->mAgentParcelSequenceID
<< llendl;
return;
}
msg->getBOOL("ParcelData", "SnapSelection", snap_selection);
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelfCount, self_count);
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherCount, other_count);
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_PublicCount, public_count);
msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_LocalID, local_id );
msg->getUUIDFast(_PREHASH_ParcelData, _PREHASH_OwnerID, owner_id);
msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_IsGroupOwned, is_group_owned);
msg->getU32Fast(_PREHASH_ParcelData, _PREHASH_AuctionID, auction_id);
msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimDate, claim_date);
msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_ClaimPrice, claim_price_per_meter);
msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_RentPrice, rent_price_per_meter);
msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMin, aabb_min);
msg->getVector3Fast(_PREHASH_ParcelData, _PREHASH_AABBMax, aabb_max);
msg->getS32Fast( _PREHASH_ParcelData, _PREHASH_Area, area );
//msg->getUUIDFast( _PREHASH_ParcelData, _PREHASH_BuyerID, buyer_id);
msg->getU8("ParcelData", "Status", status);
msg->getS32("ParcelData", "SimWideMaxPrims", sw_max_prims );
msg->getS32("ParcelData", "SimWideTotalPrims", sw_total_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_MaxPrims, max_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_TotalPrims, total_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OwnerPrims, owner_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_GroupPrims, group_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_OtherPrims, other_prims );
msg->getS32Fast(_PREHASH_ParcelData, _PREHASH_SelectedPrims, selected_prims );
msg->getF32Fast(_PREHASH_ParcelData, _PREHASH_ParcelPrimBonus, parcel_prim_bonus );
msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionPushOverride, region_push_override );
msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyAnonymous, region_deny_anonymous_override );
msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyIdentified, region_deny_identified_override ); // Deprecated
msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyTransacted, region_deny_transacted_override ); // Deprecated
if (msg->getNumberOfBlocksFast(_PREHASH_AgeVerificationBlock))
{
// this block was added later and may not be on older sims, so we have to test its existence first
msg->getBOOLFast(_PREHASH_AgeVerificationBlock, _PREHASH_RegionDenyAgeUnverified, region_deny_age_unverified_override );
}
msg->getS32("ParcelData", "OtherCleanTime", other_clean_time );
// Actually extract the data.
if (parcel)
{
parcel->init(owner_id,
FALSE, FALSE, FALSE,
claim_date, claim_price_per_meter, rent_price_per_meter,
area, other_prims, parcel_prim_bonus, is_group_owned);
parcel->setLocalID(local_id);
parcel->setAABBMin(aabb_min);
parcel->setAABBMax(aabb_max);
parcel->setAuctionID(auction_id);
parcel->setOwnershipStatus((LLParcel::EOwnershipStatus)status);
parcel->setSimWideMaxPrimCapacity(sw_max_prims);
parcel->setSimWidePrimCount(sw_total_prims);
parcel->setMaxPrimCapacity(max_prims);
parcel->setOwnerPrimCount(owner_prims);
parcel->setGroupPrimCount(group_prims);
parcel->setOtherPrimCount(other_prims);
parcel->setSelectedPrimCount(selected_prims);
parcel->setParcelPrimBonus(parcel_prim_bonus);
parcel->setCleanOtherTime(other_clean_time);
parcel->setRegionPushOverride(region_push_override);
parcel->setRegionDenyAnonymousOverride(region_deny_anonymous_override);
parcel->setRegionDenyAgeUnverifiedOverride(region_deny_age_unverified_override);
parcel->unpackMessage(msg);
if (parcel == LLViewerParcelMgr::getInstance()->mAgentParcel)
{
S32 bitmap_size = LLViewerParcelMgr::getInstance()->mParcelsPerEdge
* LLViewerParcelMgr::getInstance()->mParcelsPerEdge
/ 8;
U8* bitmap = new U8[ bitmap_size ];
msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
LLViewerParcelMgr::getInstance()->writeAgentParcelFromBitmap(bitmap);
delete[] bitmap;
// Let interesting parties know about agent parcel change.
LLViewerParcelMgr* instance = LLViewerParcelMgr::getInstance();
instance->mAgentParcelChangedSignal();
if (instance->mTeleportInProgress)
{
instance->mTeleportInProgress = FALSE;
instance->mTeleportFinishedSignal(gAgent.getPositionGlobal());
}
}
}
// Add any pending entry to the TP history now that we got the *new* parcel name.
LLFloaterTeleportHistory::getInstance()->addEntry(LLViewerParcelMgr::getInstance()->getAgentParcelName());
// Handle updating selections, if necessary.
if (sequence_id == SELECTED_PARCEL_SEQ_ID)
{
// Update selected counts
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mSelectedSelfCount = self_count;
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mSelectedOtherCount = other_count;
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mSelectedPublicCount = public_count;
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mSelectedMultipleOwners =
(request_result == PARCEL_RESULT_MULTIPLE);
// Select the whole parcel
LLViewerRegion* region = LLWorld::getInstance()->getRegion( msg->getSender() );
if (region)
{
if (!snap_selection)
{
// don't muck with the westsouth and eastnorth.
// just highlight it
LLVector3 west_south = region->getPosRegionFromGlobal(LLViewerParcelMgr::getInstance()->mWestSouth);
LLVector3 east_north = region->getPosRegionFromGlobal(LLViewerParcelMgr::getInstance()->mEastNorth);
LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mHighlightSegments);
LLViewerParcelMgr::getInstance()->writeHighlightSegments(
west_south.mV[VX],
west_south.mV[VY],
east_north.mV[VX],
east_north.mV[VY] );
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mWholeParcelSelected = FALSE;
}
else if (0 == local_id)
{
// this is public land, just highlight the selection
LLViewerParcelMgr::getInstance()->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
LLViewerParcelMgr::getInstance()->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mHighlightSegments);
LLViewerParcelMgr::getInstance()->writeHighlightSegments(
aabb_min.mV[VX],
aabb_min.mV[VY],
aabb_max.mV[VX],
aabb_max.mV[VY] );
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mWholeParcelSelected = TRUE;
}
else
{
LLViewerParcelMgr::getInstance()->mWestSouth = region->getPosGlobalFromRegion( aabb_min );
LLViewerParcelMgr::getInstance()->mEastNorth = region->getPosGlobalFromRegion( aabb_max );
// Owned land, highlight the boundaries
S32 bitmap_size = LLViewerParcelMgr::getInstance()->mParcelsPerEdge
* LLViewerParcelMgr::getInstance()->mParcelsPerEdge
/ 8;
U8* bitmap = new U8[ bitmap_size ];
msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mHighlightSegments);
LLViewerParcelMgr::getInstance()->writeSegmentsFromBitmap( bitmap, LLViewerParcelMgr::getInstance()->mHighlightSegments );
delete[] bitmap;
bitmap = NULL;
LLViewerParcelMgr::getInstance()->mCurrentParcelSelection->mWholeParcelSelected = TRUE;
}
// Request access list information for this land
LLViewerParcelMgr::getInstance()->sendParcelAccessListRequest(AL_ACCESS | AL_BAN);
// Request the media url filter list for this land
LLViewerParcelMgr::getInstance()->requestParcelMediaURLFilter();
// Request dwell for this land, if it's not public land.
LLViewerParcelMgr::getInstance()->mSelectedDwell = 0.f;
if (0 != local_id)
{
LLViewerParcelMgr::getInstance()->sendParcelDwellRequest();
}
LLViewerParcelMgr::getInstance()->mSelected = TRUE;
LLViewerParcelMgr::getInstance()->notifyObservers();
}
}
else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID ||
sequence_id == COLLISION_NOT_ON_LIST_PARCEL_SEQ_ID ||
sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
{
// We're about to collide with this parcel
LLViewerParcelMgr::getInstance()->mRenderCollision = TRUE;
LLViewerParcelMgr::getInstance()->mCollisionTimer.reset();
// Differentiate this parcel if we are banned from it.
if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID)
{
LLViewerParcelMgr::getInstance()->mCollisionBanned = BA_BANNED;
}
else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID)
{
LLViewerParcelMgr::getInstance()->mCollisionBanned = BA_NOT_IN_GROUP;
}
else
{
LLViewerParcelMgr::getInstance()->mCollisionBanned = BA_NOT_ON_LIST;
}
S32 bitmap_size = LLViewerParcelMgr::getInstance()->mParcelsPerEdge
* LLViewerParcelMgr::getInstance()->mParcelsPerEdge
/ 8;
U8* bitmap = new U8[ bitmap_size ];
msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size);
LLViewerParcelMgr::getInstance()->resetSegments(LLViewerParcelMgr::getInstance()->mCollisionSegments);
LLViewerParcelMgr::getInstance()->writeSegmentsFromBitmap( bitmap, LLViewerParcelMgr::getInstance()->mCollisionSegments );
delete[] bitmap;
bitmap = NULL;
}
else if (sequence_id == HOVERED_PARCEL_SEQ_ID)
{
LLViewerRegion *region = LLWorld::getInstance()->getRegion( msg->getSender() );
if (region)
{
LLViewerParcelMgr::getInstance()->mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min );
LLViewerParcelMgr::getInstance()->mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max );
}
else
{
LLViewerParcelMgr::getInstance()->mHoverWestSouth.clearVec();
LLViewerParcelMgr::getInstance()->mHoverEastNorth.clearVec();
}
}
else
{
// look for music.
if (gAudiop)
{
if (parcel)
{
std::string music_url_raw = parcel->getMusicURL();
// Trim off whitespace from front and back
std::string music_url = music_url_raw;
LLStringUtil::trim(music_url);
// On entering a new parcel, stop the last stream if the
// new parcel has a different music url. (Empty URL counts
// as different.)
const std::string& stream_url = gAudiop->getInternetStreamURL();
if (music_url.empty() || music_url != stream_url)
{
// URL is different from one currently playing.
gAudiop->stopInternetStream();
// If there is a new music URL and it's valid, play it.
if (music_url.size() > 12)
{
if (music_url.substr(0,7) == "http://")
{
optionally_start_music(parcel);
}
else
{
llinfos << "Stopping parcel music (invalid audio stream URL)" << llendl;
// clears the URL
gAudiop->startInternetStream(LLStringUtil::null);
}
}
else if (!gAudiop->getInternetStreamURL().empty())
{
llinfos << "Stopping parcel music (parcel stream URL is empty)" << llendl;
gAudiop->startInternetStream(LLStringUtil::null);
}
}
}
else
{
// Public land has no music
gAudiop->stopInternetStream();
}
}//if gAudiop
// now check for video
LLViewerParcelMedia::update( parcel );
};
}
void optionally_start_music(LLParcel* parcel)
{
if (gSavedSettings.getBOOL("AudioStreamingMusic"))
{
// Make the user click the start button on the overlay bar. JC
// llinfos << "Starting parcel music " << parcel->getMusicURL() << llendl;
// now only play music when you enter a new parcel if the control is in PLAY state
// changed as part of SL-4878
if (gOverlayBar && gOverlayBar->musicPlaying())
{
LLViewerParcelMedia::playStreamingMusic(parcel);
}
}
}
// static
void LLViewerParcelMgr::processParcelAccessListReply(LLMessageSystem *msg, void **user)
{
LLUUID agent_id;
S32 sequence_id = 0;
U32 message_flags = 0x0;
S32 parcel_id = -1;
msg->getUUIDFast(_PREHASH_Data, _PREHASH_AgentID, agent_id);
msg->getS32Fast( _PREHASH_Data, _PREHASH_SequenceID, sequence_id ); //ignored
msg->getU32Fast( _PREHASH_Data, _PREHASH_Flags, message_flags);
msg->getS32Fast( _PREHASH_Data, _PREHASH_LocalID, parcel_id);
LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
if (!parcel) return;
if (parcel_id != parcel->getLocalID())
{
llwarns << "processParcelAccessListReply for parcel " << parcel_id
<< " which isn't the selected parcel " << parcel->getLocalID()<< llendl;
return;
}
if (message_flags & AL_ACCESS)
{
parcel->unpackAccessEntries(msg, &(parcel->mAccessList) );
}
else if (message_flags & AL_BAN)
{
parcel->unpackAccessEntries(msg, &(parcel->mBanList) );
}
/*else if (message_flags & AL_RENTER)
{
parcel->unpackAccessEntries(msg, &(parcel->mRenterList) );
}*/
LLViewerParcelMgr::getInstance()->notifyObservers();
}
// static
void LLViewerParcelMgr::processParcelDwellReply(LLMessageSystem* msg, void**)
{
LLUUID agent_id;
msg->getUUID("AgentData", "AgentID", agent_id);
S32 local_id;
msg->getS32("Data", "LocalID", local_id);
LLUUID parcel_id;
msg->getUUID("Data", "ParcelID", parcel_id);
F32 dwell;
msg->getF32("Data", "Dwell", dwell);
if (local_id == LLViewerParcelMgr::getInstance()->mCurrentParcel->getLocalID())
{
LLViewerParcelMgr::getInstance()->mSelectedDwell = dwell;
LLViewerParcelMgr::getInstance()->notifyObservers();
}
}
void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which)
{
LLUUID transactionUUID;
transactionUUID.generate();
if (!mSelected)
{
return;
}
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region) return;
LLMessageSystem* msg = gMessageSystem;
LLParcel* parcel = mCurrentParcel;
if (!parcel) return;
if (which & AL_ACCESS)
{
S32 count = parcel->mAccessList.size();
S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
S32 sequence_id = 1;
BOOL start_message = TRUE;
BOOL initial = TRUE;
access_map_const_iterator cit = parcel->mAccessList.begin();
access_map_const_iterator end = parcel->mAccessList.end();
while ( (cit != end) || initial )
{
if (start_message)
{
msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
msg->nextBlockFast(_PREHASH_Data);
msg->addU32Fast(_PREHASH_Flags, AL_ACCESS);
msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
msg->addS32Fast(_PREHASH_Sections, num_sections);
start_message = FALSE;
if (initial && (cit == end))
{
// pack an empty block if there will be no data
msg->nextBlockFast(_PREHASH_List);
msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
msg->addS32Fast(_PREHASH_Time, 0 );
msg->addU32Fast(_PREHASH_Flags, 0 );
}
initial = FALSE;
sequence_id++;
}
while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
{
const LLAccessEntry& entry = (*cit).second;
msg->nextBlockFast(_PREHASH_List);
msg->addUUIDFast(_PREHASH_ID, entry.mID );
msg->addS32Fast(_PREHASH_Time, entry.mTime );
msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
++cit;
}
start_message = TRUE;
msg->sendReliable( region->getHost() );
}
}
if (which & AL_BAN)
{
S32 count = parcel->mBanList.size();
S32 num_sections = (S32) ceil(count/PARCEL_MAX_ENTRIES_PER_PACKET);
S32 sequence_id = 1;
BOOL start_message = TRUE;
BOOL initial = TRUE;
access_map_const_iterator cit = parcel->mBanList.begin();
access_map_const_iterator end = parcel->mBanList.end();
while ( (cit != end) || initial )
{
if (start_message)
{
msg->newMessageFast(_PREHASH_ParcelAccessListUpdate);
msg->nextBlockFast(_PREHASH_AgentData);
msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID() );
msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID() );
msg->nextBlockFast(_PREHASH_Data);
msg->addU32Fast(_PREHASH_Flags, AL_BAN);
msg->addS32(_PREHASH_LocalID, parcel->getLocalID() );
msg->addUUIDFast(_PREHASH_TransactionID, transactionUUID);
msg->addS32Fast(_PREHASH_SequenceID, sequence_id);
msg->addS32Fast(_PREHASH_Sections, num_sections);
start_message = FALSE;
if (initial && (cit == end))
{
// pack an empty block if there will be no data
msg->nextBlockFast(_PREHASH_List);
msg->addUUIDFast(_PREHASH_ID, LLUUID::null );
msg->addS32Fast(_PREHASH_Time, 0 );
msg->addU32Fast(_PREHASH_Flags, 0 );
}
initial = FALSE;
sequence_id++;
}
while ( (cit != end) && (msg->getCurrentSendTotal() < MTUBYTES))
{
const LLAccessEntry& entry = (*cit).second;
msg->nextBlockFast(_PREHASH_List);
msg->addUUIDFast(_PREHASH_ID, entry.mID );
msg->addS32Fast(_PREHASH_Time, entry.mTime );
msg->addU32Fast(_PREHASH_Flags, entry.mFlags );
++cit;
}
start_message = TRUE;
msg->sendReliable( region->getHost() );
}
}
}
class LLParcelMediaURLFilterResponder : public LLHTTPClient::Responder
{
virtual void result(const LLSD& content)
{
LLViewerParcelMgr::getInstance()->receiveParcelMediaURLFilter(content);
}
};
void LLViewerParcelMgr::requestParcelMediaURLFilter()
{
if (!mSelected)
{
return;
}
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal( mWestSouth );
if (!region)
{
return;
}
LLParcel* parcel = mCurrentParcel;
if (!parcel)
{
llwarns << "no parcel" << llendl;
return;
}
LLSD body;
body["local-id"] = parcel->getLocalID();
body["list"] = parcel->getMediaURLFilterList();
std::string url = region->getCapability("ParcelMediaURLFilterList");
if (!url.empty())
{
LLHTTPClient::post(url, body, new LLParcelMediaURLFilterResponder);
}
else
{
llwarns << "can't get ParcelMediaURLFilterList cap" << llendl;
}
}
void LLViewerParcelMgr::receiveParcelMediaURLFilter(const LLSD &content)
{
if (content.has("list"))
{
LLParcel* parcel = LLViewerParcelMgr::getInstance()->mCurrentParcel;
if (!parcel) return;
if (content["local-id"].asInteger() == parcel->getLocalID())
{
parcel->setMediaURLFilterList(content["list"]);
LLViewerParcelMgr::getInstance()->notifyObservers();
}
}
}
void LLViewerParcelMgr::deedLandToGroup()
{
std::string group_name;
gCacheName->getGroupName(mCurrentParcel->getGroupID(), group_name);
LLSD args;
args["AREA"] = llformat("%d", mCurrentParcel->getArea());
args["GROUP_NAME"] = group_name;
if(mCurrentParcel->getContributeWithDeed())
{
std::string full_name;
gCacheName->getFullName(mCurrentParcel->getOwnerID(),full_name);
args["NAME"] = full_name;
LLNotifications::instance().add("DeedLandToGroupWithContribution",args, LLSD(), deedAlertCB);
}
else
{
LLNotifications::instance().add("DeedLandToGroup",args, LLSD(), deedAlertCB);
}
}
// static
bool LLViewerParcelMgr::deedAlertCB(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
if (option == 0)
{
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
LLUUID group_id;
if(parcel)
{
group_id = parcel->getGroupID();
}
LLViewerParcelMgr::getInstance()->sendParcelDeed(group_id);
}
return false;
}
void LLViewerParcelMgr::startReleaseLand()
{
if (!mSelected)
{
LLNotifications::instance().add("CannotReleaseLandNothingSelected");
return;
}
if (mRequestResult == PARCEL_RESULT_NO_DATA)
{
LLNotifications::instance().add("CannotReleaseLandWatingForServer");
return;
}
if (mRequestResult == PARCEL_RESULT_MULTIPLE)
{
LLNotifications::instance().add("CannotReleaseLandSelected");
return;
}
if (!isParcelOwnedByAgent(mCurrentParcel, GP_LAND_RELEASE)
&& !(gAgent.canManageEstate()))
{
LLNotifications::instance().add("CannotReleaseLandDontOwn");
return;
}
LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
if (!region)
{
LLNotifications::instance().add("CannotReleaseLandRegionNotFound");
return;
}
/*
if ((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
&& !gAgent.isGodlike())
{
LLSD args;
args["REGION"] = region->getName();
LLNotifications::instance().add("CannotReleaseLandNoTransfer", args);
return;
}
*/
if (!mCurrentParcelSelection->mWholeParcelSelected)
{
LLNotifications::instance().add("CannotReleaseLandPartialSelection");
return;
}
// Compute claim price
LLSD args;
args["AREA"] = llformat("%d",mCurrentParcel->getArea());
LLNotifications::instance().add("ReleaseLandWarning", args, LLSD(), releaseAlertCB);
}
bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const
{
if (!parcel)
{
return false;
}
if (mSelected && parcel == mCurrentParcel)
{
if (mRequestResult == PARCEL_RESULT_NO_DATA)
{
return false;
}
}
const LLUUID& parcelOwner = parcel->getOwnerID();
const LLUUID& authorizeBuyer = parcel->getAuthorizedBuyerID();
if (parcel->isPublic())
{
return true; // change this if want to make it gods only
}
LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
if (regionp)
{
U8 sim_access = regionp->getSimAccess();
const LLAgentAccess& agent_access = gAgent.getAgentAccess();
// if the region is PG, we're happy already, so do nothing
// but if we're set to avoid either mature or adult, get us outta here
if ((sim_access == SIM_ACCESS_MATURE) &&
!agent_access.canAccessMature())
{
return false;
}
else if ((sim_access == SIM_ACCESS_ADULT) &&
!agent_access.canAccessAdult())
{
return false;
}
}
bool isForSale = parcel->getForSale()
&& ((parcel->getSalePrice() > 0) || (authorizeBuyer.notNull()));
bool isEmpowered
= forGroup ? gAgent.hasPowerInActiveGroup(GP_LAND_DEED) == TRUE : true;
bool isOwner
= parcelOwner == (forGroup ? gAgent.getGroupID() : gAgent.getID());
bool isAuthorized
= (authorizeBuyer.isNull()
|| (gAgent.getID() == authorizeBuyer)
|| (gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_DEED)
&& gAgent.hasPowerInGroup(authorizeBuyer,GP_LAND_SET_SALE_INFO)));
return isForSale && !isOwner && isAuthorized && isEmpowered;
}
void LLViewerParcelMgr::startBuyLand(BOOL is_for_group)
{
LLFloaterBuyLand::buyLand(getSelectionRegion(), mCurrentParcelSelection, is_for_group == TRUE);
}
void LLViewerParcelMgr::startSellLand()
{
LLFloaterSellLand::sellLand(getSelectionRegion(), mCurrentParcelSelection);
}
void LLViewerParcelMgr::startDivideLand()
{
if (!mSelected)
{
LLNotifications::instance().add("CannotDivideLandNothingSelected");
return;
}
if (mCurrentParcelSelection->mWholeParcelSelected)
{
LLNotifications::instance().add("CannotDivideLandPartialSelection");
return;
}
LLSD payload;
payload["west_south_border"] = ll_sd_from_vector3d(mWestSouth);
payload["east_north_border"] = ll_sd_from_vector3d(mEastNorth);
LLNotifications::instance().add("LandDivideWarning", LLSD(), payload, callbackDivideLand);
}
// static
bool LLViewerParcelMgr::callbackDivideLand(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
LLVector3d west_south_d = ll_vector3d_from_sd(notification["payload"]["west_south_border"]);
LLVector3d east_north_d = ll_vector3d_from_sd(notification["payload"]["east_north_border"]);
LLVector3d parcel_center = (west_south_d + east_north_d) / 2.0;
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
if (!region)
{
LLNotifications::instance().add("CannotDivideLandNoRegion");
return false;
}
if (0 == option)
{
LLVector3 west_south = region->getPosRegionFromGlobal(west_south_d);
LLVector3 east_north = region->getPosRegionFromGlobal(east_north_d);
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelDivide");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("ParcelData");
msg->addF32("West", west_south.mV[VX]);
msg->addF32("South", west_south.mV[VY]);
msg->addF32("East", east_north.mV[VX]);
msg->addF32("North", east_north.mV[VY]);
msg->sendReliable(region->getHost());
}
return false;
}
void LLViewerParcelMgr::startJoinLand()
{
if (!mSelected)
{
LLNotifications::instance().add("CannotJoinLandNothingSelected");
return;
}
if (mCurrentParcelSelection->mWholeParcelSelected)
{
LLNotifications::instance().add("CannotJoinLandEntireParcelSelected");
return;
}
if (!mCurrentParcelSelection->mSelectedMultipleOwners)
{
LLNotifications::instance().add("CannotJoinLandSelection");
return;
}
LLSD payload;
payload["west_south_border"] = ll_sd_from_vector3d(mWestSouth);
payload["east_north_border"] = ll_sd_from_vector3d(mEastNorth);
LLNotifications::instance().add("JoinLandWarning", LLSD(), payload, callbackJoinLand);
}
// static
bool LLViewerParcelMgr::callbackJoinLand(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
LLVector3d west_south_d = ll_vector3d_from_sd(notification["payload"]["west_south_border"]);
LLVector3d east_north_d = ll_vector3d_from_sd(notification["payload"]["east_north_border"]);
LLVector3d parcel_center = (west_south_d + east_north_d) / 2.0;
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
if (!region)
{
LLNotifications::instance().add("CannotJoinLandNoRegion");
return false;
}
if (0 == option)
{
LLVector3 west_south = region->getPosRegionFromGlobal(west_south_d);
LLVector3 east_north = region->getPosRegionFromGlobal(east_north_d);
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelJoin");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("ParcelData");
msg->addF32("West", west_south.mV[VX]);
msg->addF32("South", west_south.mV[VY]);
msg->addF32("East", east_north.mV[VX]);
msg->addF32("North", east_north.mV[VY]);
msg->sendReliable(region->getHost());
}
return false;
}
void LLViewerParcelMgr::startDeedLandToGroup()
{
if (!mSelected || !mCurrentParcel)
{
LLNotifications::instance().add("CannotDeedLandNothingSelected");
return;
}
if (mRequestResult == PARCEL_RESULT_NO_DATA)
{
LLNotifications::instance().add("CannotDeedLandWaitingForServer");
return;
}
if (mRequestResult == PARCEL_RESULT_MULTIPLE)
{
LLNotifications::instance().add("CannotDeedLandMultipleSelected");
return;
}
LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0;
LLViewerRegion* region = LLWorld::getInstance()->getRegionFromPosGlobal(parcel_center);
if (!region)
{
LLNotifications::instance().add("CannotDeedLandNoRegion");
return;
}
/*
if(!gAgent.isGodlike())
{
if((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL)
&& (mCurrentParcel->getOwnerID() != region->getOwner()))
{
LLSD args;
args["REGION"] = region->getName();
LLNotifications::instance().add("CannotDeedLandNoTransfer", args);
return;
}
}
*/
deedLandToGroup();
}
void LLViewerParcelMgr::reclaimParcel()
{
LLParcel* parcel = LLViewerParcelMgr::getInstance()->getParcelSelection()->getParcel();
LLViewerRegion* regionp = LLViewerParcelMgr::getInstance()->getSelectionRegion();
if(parcel && parcel->getOwnerID().notNull()
&& (parcel->getOwnerID() != gAgent.getID())
&& regionp && (regionp->getOwner() == gAgent.getID()))
{
LLMessageSystem* msg = gMessageSystem;
msg->newMessage("ParcelReclaim");
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlock("Data");
msg->addS32("LocalID", parcel->getLocalID());
msg->sendReliable(regionp->getHost());
}
}
// static
bool LLViewerParcelMgr::releaseAlertCB(const LLSD& notification, const LLSD& response)
{
S32 option = LLNotification::getSelectedOption(notification, response);
if (option == 0)
{
// Send the release message, not a force
LLViewerParcelMgr::getInstance()->sendParcelRelease();
}
return false;
}
void LLViewerParcelMgr::buyPass()
{
LLParcel* parcel = getParcelSelection()->getParcel();
if (!parcel) return;
LLViewerRegion* region = getSelectionRegion();
if (!region) return;
LLMessageSystem* msg = gMessageSystem;
msg->newMessageFast(_PREHASH_ParcelBuyPass);
msg->nextBlock("AgentData");
msg->addUUID("AgentID", gAgent.getID());
msg->addUUID("SessionID", gAgent.getSessionID());
msg->nextBlockFast(_PREHASH_ParcelData);
msg->addS32Fast(_PREHASH_LocalID, parcel->getLocalID() );
msg->sendReliable( region->getHost() );
}
//Tells whether we are allowed to buy a pass or not
BOOL LLViewerParcelMgr::isCollisionBanned()
{
if ((mCollisionBanned == BA_ALLOWED) || (mCollisionBanned == BA_NOT_ON_LIST) || (mCollisionBanned == BA_NOT_IN_GROUP))
return FALSE;
else
return TRUE;
}
// This implementation should mirror LLSimParcelMgr::isParcelOwnedBy
// static
BOOL LLViewerParcelMgr::isParcelOwnedByAgent(const LLParcel* parcelp, U64 group_proxy_power)
{
if (!parcelp)
{
return FALSE;
}
// Gods can always assume ownership.
if (gAgent.isGodlike())
{
return TRUE;
}
// The owner of a parcel automatically gets all powersr.
if (parcelp->getOwnerID() == gAgent.getID())
{
return TRUE;
}
// Only gods can assume 'ownership' of public land.
if (parcelp->isPublic())
{
return FALSE;
}
// Return whether or not the agent has group_proxy_power powers in the
// parcel's group.
return gAgent.hasPowerInGroup(parcelp->getOwnerID(), group_proxy_power);
}
// This implementation should mirror llSimParcelMgr::isParcelModifiableBy
// static
BOOL LLViewerParcelMgr::isParcelModifiableByAgent(const LLParcel* parcelp, U64 group_proxy_power)
{
// If the agent can assume ownership, it is probably modifiable.
BOOL rv = FALSE;
if (parcelp)
{
// *NOTE: This should only work for leased parcels, but group owned
// parcels cannot be OS_LEASED yet. Phoenix 2003-12-15.
rv = isParcelOwnedByAgent(parcelp, group_proxy_power);
// ... except for the case that the parcel is not OS_LEASED for agent-owned parcels.
if( (gAgent.getID() == parcelp->getOwnerID())
&& !gAgent.isGodlike()
&& (parcelp->getOwnershipStatus() != LLParcel::OS_LEASED) )
{
rv = FALSE;
}
}
return rv;
}
void sanitize_corners(const LLVector3d &corner1,
const LLVector3d &corner2,
LLVector3d &west_south_bottom,
LLVector3d &east_north_top)
{
west_south_bottom.mdV[VX] = llmin( corner1.mdV[VX], corner2.mdV[VX] );
west_south_bottom.mdV[VY] = llmin( corner1.mdV[VY], corner2.mdV[VY] );
west_south_bottom.mdV[VZ] = llmin( corner1.mdV[VZ], corner2.mdV[VZ] );
east_north_top.mdV[VX] = llmax( corner1.mdV[VX], corner2.mdV[VX] );
east_north_top.mdV[VY] = llmax( corner1.mdV[VY], corner2.mdV[VY] );
east_north_top.mdV[VZ] = llmax( corner1.mdV[VZ], corner2.mdV[VZ] );
}
void LLViewerParcelMgr::cleanupGlobals()
{
LLParcelSelection::sNullSelection = NULL;
}
LLViewerTexture* LLViewerParcelMgr::getBlockedImage() const
{
return sBlockedImage;
}
LLViewerTexture* LLViewerParcelMgr::getPassImage() const
{
return sPassImage;
}
boost::signals2::connection LLViewerParcelMgr::addAgentParcelChangedCallback(parcel_changed_callback_t cb)
{
return mAgentParcelChangedSignal.connect(cb);
}
/*
* Set finish teleport callback. You can use it to observe all teleport events.
* NOTE:
* After local( in one region) teleports we
* cannot rely on gAgent.getPositionGlobal(),
* so the new position gets passed explicitly.
* Use args of this callback to get global position of avatar after teleport event.
*/
boost::signals2::connection LLViewerParcelMgr::setTeleportFinishedCallback(teleport_finished_callback_t cb)
{
return mTeleportFinishedSignal.connect(cb);
}
boost::signals2::connection LLViewerParcelMgr::setTeleportFailedCallback(parcel_changed_callback_t cb)
{
return mTeleportFailedSignal.connect(cb);
}
/* Ok, we're notified that teleport has been finished.
* We should now propagate the notification via mTeleportFinishedSignal
* to all interested parties.
*/
void LLViewerParcelMgr::onTeleportFinished(bool local, const LLVector3d& new_pos)
{
// Treat only teleports within the same parcel as local (EXT-3139).
if (local && LLViewerParcelMgr::getInstance()->inAgentParcel(new_pos))
{
// Local teleport. We already have the agent parcel data.
// Emit the signal immediately.
getInstance()->mTeleportFinishedSignal(new_pos);
}
else
{
// Non-local teleport (inter-region or between different parcels of the same region).
// The agent parcel data has not been updated yet.
// Let's wait for the update and then emit the signal.
mTeleportInProgress = TRUE;
}
}
void LLViewerParcelMgr::onTeleportFailed()
{
mTeleportFailedSignal();
}