From 38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 Mon Sep 17 00:00:00 2001 From: Jacek Antonelli Date: Fri, 15 Aug 2008 23:44:46 -0500 Subject: Second Life viewer sources 1.13.2.12 --- linden/indra/newview/llviewerparcelmgr.cpp | 2580 ++++++++++++++++++++++++++++ 1 file changed, 2580 insertions(+) create mode 100644 linden/indra/newview/llviewerparcelmgr.cpp (limited to 'linden/indra/newview/llviewerparcelmgr.cpp') diff --git a/linden/indra/newview/llviewerparcelmgr.cpp b/linden/indra/newview/llviewerparcelmgr.cpp new file mode 100644 index 0000000..af910e8 --- /dev/null +++ b/linden/indra/newview/llviewerparcelmgr.cpp @@ -0,0 +1,2580 @@ +/** + * @file llviewerparcelmgr.cpp + * @brief Viewer-side representation of owned land + * + * Copyright (c) 2002-2007, Linden Research, Inc. + * + * 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://secondlife.com/developers/opensource/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://secondlife.com/developers/opensource/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. + */ + +#include "llviewerprecompiledheaders.h" + +#include "llviewerparcelmgr.h" + +// Library includes +#include "audioengine.h" +#include "indra_constants.h" +#include "llcachename.h" +#include "llgl.h" +#include "llmediaengine.h" +#include "llparcel.h" +#include "llsecondlifeurls.h" +#include "message.h" + +// Viewer includes +#include "llagent.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 "llfloatertools.h" +#include "llnotify.h" +#include "llresmgr.h" +#include "llstatusbar.h" +#include "llui.h" +#include "llviewerimagelist.h" +#include "llviewermenu.h" +#include "llviewerparceloverlay.h" +#include "llviewerregion.h" +//#include "llwebbrowserctrl.h" +#include "llworld.h" +#include "lloverlaybar.h" +#include "roles_constants.h" + +const F32 PARCEL_COLLISION_DRAW_SECS = 1.f; + +// Globals +LLViewerParcelMgr *gParcelMgr = NULL; + +U8* LLViewerParcelMgr::sPackedOverlay = NULL; + +LLUUID gCurrentMovieID = LLUUID::null; + +// Local functions +void optionally_start_music(const LLString& music_url); +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(); +void callback_god_force_owner(S32 option, void* user_data); + +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), + mSelectedMultipleOwners(FALSE), + mWholeParcelSelected(FALSE), + mWestSouth(), + mEastNorth(), + mSelectedDwell(0.f), + mAgentParcelSequenceID(-1), + mHoverWestSouth(), + mHoverEastNorth(), + mRenderCollision(FALSE), + mRenderSelection(TRUE), + mCollisionBanned(0), + mCollisionTimer(), + mMediaParcelId(0), + mMediaRegionId(0) +{ + mParcel = new LLParcel(); + 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); + + mBlockedImageID.set(gViewerArt.getString("noentrylines.tga")); + mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE); + + mPassImageID.set(gViewerArt.getString("noentrypasslines.tga")); + mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE); + + 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; + } +} + + +LLViewerParcelMgr::~LLViewerParcelMgr() +{ + delete mParcel; + mParcel = NULL; + + delete mAgentParcel; + mAgentParcel = NULL; + + delete mCollisionParcel; + mCollisionParcel = NULL; + + delete mHoverParcel; + mHoverParcel = NULL; + + delete[] mHighlightSegments; + mHighlightSegments = NULL; + + delete[] mCollisionSegments; + mCollisionSegments = NULL; + + // weird, this crashes if I use an array delete on it! + delete sPackedOverlay; + sPackedOverlay = NULL; + + delete[] mAgentParcelOverlay; + mAgentParcelOverlay = NULL; +} + + +void LLViewerParcelMgr::destroyGL() +{ + mBlockedImage = NULL; + mPassImage = NULL; +} + + +void LLViewerParcelMgr::restoreGL() +{ + mBlockedImage = gImageList.getImage(mBlockedImageID, TRUE, TRUE); + mPassImage = gImageList.getImage(mPassImageID, TRUE, TRUE); +} + + +void LLViewerParcelMgr::dump() +{ + llinfos << "Parcel Manager Dump" << llendl; + llinfos << "mSelected " << S32(mSelected) << llendl; + llinfos << "Selected parcel: " << llendl; + llinfos << mWestSouth << " to " << mEastNorth << llendl; + mParcel->dump(); + llinfos << "banning " << mParcel->mBanList.size() << llendl; + + access_map_const_iterator cit = mParcel->mBanList.begin(); + access_map_const_iterator end = mParcel->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() +{ + if (!gWorldp) return NULL; + + return gWorldp->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 (mSelectedMultipleOwners) + { + area = getClaimableArea(); + } + else + { + area = getSelectedArea(); + } + + if (mParcel->getForSale()) + { + price = mParcel->getSalePrice(); + for_sale = TRUE; + } + else + { + price = area * mParcel->getClaimPricePerMeter(); + for_sale = FALSE; + } + + rent = mParcel->getTotalRent(); + + dwell = mSelectedDwell; + } + + *area_out = area; + *claim_out = price; + *rent_out = rent; + *for_sale_out = for_sale; + *dwell_out = dwell; +} + +void LLViewerParcelMgr::getPrimInfo(S32 &sw_max, S32 &sw_total, S32 &max, S32 &total, S32 &owner, S32 &group, S32 &other, S32& selected, F32 &parcel_object_bonus, S32 &other_clean) +{ + if (mSelected && mParcel) + { + sw_max = mParcel->getSimWideMaxPrimCapacity(); + sw_total = mParcel->getSimWidePrimCount(); + max = llround(mParcel->getMaxPrimCapacity()*mParcel->getParcelPrimBonus()); + total = mParcel->getPrimCount(); + owner = mParcel->getOwnerPrimCount(); + group = mParcel->getGroupPrimCount(); + other = mParcel->getOtherPrimCount(); + selected = mParcel->getSelectedPrimCount(); + parcel_object_bonus = mParcel->getParcelPrimBonus(); + other_clean = mParcel->getCleanOtherTime(); + } +} + +BOOL LLViewerParcelMgr::getMultipleOwners() const +{ + return mSelectedMultipleOwners; +} + + +BOOL LLViewerParcelMgr::getWholeParcelSelected() const +{ + return mWholeParcelSelected; +} + + +S32 LLViewerParcelMgr::getClaimableArea() const +{ + const S32 UNIT_AREA = S32( PARCEL_GRID_STEP_METERS * PARCEL_GRID_STEP_METERS ); + return mSelectedPublicCount * UNIT_AREA; +} + +bool LLViewerParcelMgr::hasOthersSelected() const +{ + return mSelectedOtherCount != 0; +} + + +S32 LLViewerParcelMgr::getSelectedArea() const +{ + S32 rv = 0; + if(mSelected && mParcel && mWholeParcelSelected) + { + rv = mParcel->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. +void 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 + selectLand( southwest, northeast, TRUE ); +} + + +// Tries to select the parcel inside the rectangle +void LLViewerParcelMgr::selectParcelInRectangle() +{ + selectLand(mWestSouth, mEastNorth, TRUE); +} + + +void LLViewerParcelMgr::selectCollisionParcel() +{ + if (!gWorldp) + { + return; + } + + // 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 + mParcel->setName( mCollisionParcel->getName() ); + mParcel->setDesc( mCollisionParcel->getDesc() ); + mParcel->setPassPrice(mCollisionParcel->getPassPrice()); + mParcel->setPassHours(mCollisionParcel->getPassHours()); + + // clear the list of segments to prevent flashing + resetSegments(mHighlightSegments); + + mSelected = TRUE; + mWholeParcelSelected = TRUE; + notifyObservers(); + return; +} + + +// snap_selection = auto-select the hit parcel, if there is exactly one +void LLViewerParcelMgr::selectLand(const LLVector3d &corner1, const LLVector3d &corner2, + BOOL snap_selection) +{ + if (!gWorldp) + { + return; + } + + 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; + } + + // ...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; + } + + // 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 = gWorldp->getRegionFromPosGlobal(mWestSouth); + LLViewerRegion *region_other = gWorldp->getRegionFromPosGlobal( east_north_region_check ); + + if(!region) + { + // just in case they somehow selected no land. + mSelected = FALSE; + return; + } + + if (region != region_other) + { + LLNotifyBox::showXml("CantSelectLandFromMultipleRegions"); + mSelected = FALSE; + notifyObservers(); + return; + } + + // Build region global copies of corners + LLVector3 wsb_region = region->getPosRegionFromGlobal( mWestSouth ); + LLVector3 ent_region = region->getPosRegionFromGlobal( mEastNorth ); + + /* + // Check land to make sure all is either public, owned, or self + LLViewerParcelOverlay* overlay = region->getParcelOverlay(); + if (!overlay) + { + llerrs << "No overlay in LLViewerParcelMgr::selectLand" << llendl; + return; + } + + U8 start_ownership = overlay->ownership( wsb_region ); + BOOL identical = TRUE; + S32 x_steps = S32( getSelectionWidth() / PARCEL_GRID_STEP_METERS ); + S32 y_steps = S32( getSelectionHeight() / PARCEL_GRID_STEP_METERS ); + + for (S32 x = 0; x < x_steps && identical; x++ ) + { + for (S32 y = 0; y < y_steps && identical; y++ ) + { + // strange recomputation each time to avoid error accumulation + LLVector3 check = wsb_region; + check.mV[VX] += x * PARCEL_GRID_STEP_METERS; + check.mV[VY] += y * PARCEL_GRID_STEP_METERS; + + identical = (start_ownership == overlay->ownership(check)); + } + } + + if (!identical) + { + add_chat("Can't select mix of your own, other people's and public land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM); + add_chat("Try selecting a smaller piece of land.", FALSE, "", FALSE, CHAT_SOURCE_SYSTEM); + mSelected = FALSE; + notifyObservers(); + return; + } + */ + + // 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); + + mSelected = TRUE; + mWholeParcelSelected = snap_selection; + notifyObservers(); + return; +} + + +void LLViewerParcelMgr::deselectLand() +{ + if (mSelected) + { + mSelected = FALSE; + + // Invalidate the selected parcel + mParcel->setLocalID(-1); + mParcel->mAccessList.clear(); + mParcel->mBanList.clear(); + //mParcel->mRenterList.reset(); + + mSelectedDwell = 0.f; + + notifyObservers(); + } +} + + +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 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; +} + + +LLParcel *LLViewerParcelMgr::getSelectedParcel() const +{ + if (mSelected) + { + return mParcel; + } + else + { + return NULL; + } +} + + +LLParcel *LLViewerParcelMgr::getAgentParcel() const +{ + return mAgentParcel; +} + +// Return whether the agent can build on the land they are on +BOOL LLViewerParcelMgr::agentCanBuild() const +{ + if (mAgentParcel) + { + return gAgent.isGodlike() + || (mAgentParcel->getOwnerID() == gAgent.getID()) + || (mAgentParcel->getAllowModify()); + } + else + { + return gAgent.isGodlike(); + } +} + +BOOL LLViewerParcelMgr::agentCanTakeDamage() const +{ + return mAgentParcel->getAllowDamage(); +} + +BOOL LLViewerParcelMgr::agentCanFly() const +{ + return TRUE; +} + +F32 LLViewerParcelMgr::agentDrawDistance() const +{ + return 512.f; +} + +BOOL LLViewerParcelMgr::isOwnedAt(const LLVector3d& pos_global) const +{ + LLViewerRegion* region = gWorldp->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 = gWorldp->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 = gWorldp->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 = gWorldp->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 (gParcelMgr->getAgentParcel()->getSoundLocal()) + { + // Not in same parcel, and agent parcel only has local sound + return FALSE; + } + else if (gParcelMgr->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 = gWorldp->getRegionFromPosGlobal(pos_global); + if (region != gAgent.getRegion()) + { + // Can't be in the agent parcel if you're not in the same region. + return FALSE; + } + + LLVector3 pos_region = gAgent.getRegion()->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 = gWorldp->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) + { + LLViewerRegion* regionp = gAgent.getRegion(); + BOOL use_pass = mCollisionParcel->getParcelFlag(PF_USE_PASS_LIST); + renderCollisionSegments(mCollisionSegments, use_pass, regionp); + } +} + + +void LLViewerParcelMgr::sendParcelAccessListRequest(U32 flags) +{ + if (!mSelected) + { + return; + } + + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) return; + + LLMessageSystem *msg = gMessageSystem; + + + if (flags & AL_BAN) + { + mParcel->mBanList.clear(); + } + if (flags & AL_ACCESS) + { + mParcel->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", mParcel->getLocalID() ); + msg->sendReliable( region->getHost() ); +} + + +void LLViewerParcelMgr::sendParcelDwellRequest() +{ + if (!mSelected) + { + return; + } + + LLViewerRegion *region = gWorldp->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", mParcel->getLocalID()); + msg->addUUID("ParcelID", LLUUID::null); // filled in on simulator + msg->sendReliable( region->getHost() ); +} + + +void LLViewerParcelMgr::sendParcelGodForceOwner(const LLUUID& owner_id) +{ + if (!mSelected) + { + gViewerWindow->alertXml("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 = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + // TODO: Add a force owner version of this alert. + gViewerWindow->alertXml("CannotContentifyNoRegion"); + return; + } + + // BUG: Make work for cross-region selections + LLViewerRegion *region2 = gWorldp->getRegionFromPosGlobal( east_north_region_check ); + if (region != region2) + { + gViewerWindow->alertXml("CannotSetLandOwnerMultipleRegions"); + return; + } + + llinfos << "Region " << region->getOriginGlobal() << llendl; + + LLGodForceOwnerData* data = new LLGodForceOwnerData(owner_id, mParcel->getLocalID(), region->getHost()); + if(mParcel->getAuctionID()) + { + gViewerWindow->alertXml("ForceOwnerAuctionWarning", + callback_god_force_owner, + (void*)data); + } + else + { + callback_god_force_owner(0, (void*)data); + } +} + +void callback_god_force_owner(S32 option, void* user_data) +{ + LLGodForceOwnerData* data = (LLGodForceOwnerData*)user_data; + if(data && (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", data->mOwnerID); + msg->addS32( "LocalID", data->mLocalID); + msg->sendReliable(data->mHost); + } + delete data; +} + +void LLViewerParcelMgr::sendParcelGodForceToContent() +{ + if (!mSelected) + { + gViewerWindow->alertXml("CannotContentifyNothingSelected"); + return; + } + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + gViewerWindow->alertXml("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", mParcel->getLocalID()); + msg->sendReliable(region->getHost()); +} + +void LLViewerParcelMgr::sendParcelRelease() +{ + if (!mSelected) + { + gViewerWindow->alertXml("CannotReleaseLandNothingSelected"); + return; + } + + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + gViewerWindow->alertXml("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", mParcel->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; + + // 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 || !mParcel) + { + gViewerWindow->alertXml("CannotBuyLandNothingSelected"); + return NULL; + } + + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + gViewerWindow->alertXml("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 = gWorldp->getRegionFromPosGlobal( east_north_region_check ); + + if (region != region2) + { + gViewerWindow->alertXml("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(); + + if (!is_claim) + { + info->mParcelID = mParcel->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); + } + msg->sendReliable(info->mHost); +} + +void LLViewerParcelMgr::deleteParcelBuy(ParcelBuyInfo*& info) +{ + delete info; + info = NULL; +} + +void LLViewerParcelMgr::sendParcelDeed(const LLUUID& group_id) +{ + if (!mSelected || !mParcel) + { + gViewerWindow->alertXml("CannotDeedLandNothingSelected"); + return; + } + if(group_id.isNull()) + { + gViewerWindow->alertXml("CannotDeedLandNoGroup"); + return; + } + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) + { + gViewerWindow->alertXml("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", mParcel->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() != mParcel->getOwnerID()) + { + return; + } + + LLVector3d global_center(mWestSouth); + global_center += mEastNorth; + global_center *= 0.5f; + + LLViewerRegion* region; + region = gWorldp->getRegionFromPosGlobal(global_center); + + LLVector3 west_south_bottom_region = region->getPosRegionFromGlobal( mWestSouth ); + LLVector3 east_north_top_region = region->getPosRegionFromGlobal( mEastNorth ); + + LLString name("My Land"); + char buffer[MAX_STRING]; + 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); + sprintf(buffer, "%s in %s (%d, %d)", + name.c_str(), + region->getName().c_str(), + pos_x, pos_y); + name.assign(buffer); + + const char* desc = "Claimed land"; + create_landmark(name.c_str(), desc, global_center); +} +*/ + +const char* LLViewerParcelMgr::getAgentParcelName() const +{ + return mAgentParcel->getName(); +} + + +void LLViewerParcelMgr::sendParcelPropertiesUpdate(LLParcel* parcel, BOOL want_reply_to_update) +{ + if (!parcel) return; + + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) return; + + 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 flags = 0x0; + if (want_reply_to_update) flags |= 0x01; + msg->addU32("Flags", flags); + + parcel->packMessage(msg); + + msg->sendReliable( region->getHost() ); +} + + +void LLViewerParcelMgr::requestHoverParcelProperties(const LLVector3d& pos) +{ + LLViewerRegion *region = gWorldp->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 0" << llendl; + return; + } + + S32 parcels_per_edge = gParcelMgr->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 = gWorldp->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; + BOOL is_reserved = FALSE; + 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; + BOOL region_deny_transacted_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 + gParcelMgr->mRequestResult = PARCEL_RESULT_SUCCESS; + parcel = gParcelMgr->mParcel; + } + else if (sequence_id == HOVERED_PARCEL_SEQ_ID) + { + gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS; + parcel = gParcelMgr->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) + { + gParcelMgr->mHoverRequestResult = PARCEL_RESULT_SUCCESS; + parcel = gParcelMgr->mCollisionParcel; + } + else if (sequence_id == 0 || sequence_id > gParcelMgr->mAgentParcelSequenceID) + { + // new agent parcel + gParcelMgr->mAgentParcelSequenceID = sequence_id; + parcel = gParcelMgr->mAgentParcel; + } + else + { + llinfos << "out of order agent parcel sequence id " << sequence_id + << " last good " << gParcelMgr->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->getBOOLFast(_PREHASH_ParcelData, _PREHASH_ReservedNewbie, is_reserved); + 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 ); + msg->getBOOLFast(_PREHASH_ParcelData, _PREHASH_RegionDenyTransacted, region_deny_transacted_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->setReservedForNewbie(is_reserved); + 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->setRegionDenyIdentifiedOverride(region_deny_identified_override); + parcel->setRegionDenyTransactedOverride(region_deny_transacted_override); + parcel->unpackMessage(msg); + + if (parcel == gParcelMgr->mAgentParcel) + { + S32 bitmap_size = gParcelMgr->mParcelsPerEdge + * gParcelMgr->mParcelsPerEdge + / 8; + U8* bitmap = new U8[ bitmap_size ]; + msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size); + + gParcelMgr->writeAgentParcelFromBitmap(bitmap); + delete[] bitmap; + } + } + + // Handle updating selections, if necessary. + if (sequence_id == SELECTED_PARCEL_SEQ_ID) + { + // Update selected counts + gParcelMgr->mSelectedSelfCount = self_count; + gParcelMgr->mSelectedOtherCount = other_count; + gParcelMgr->mSelectedPublicCount = public_count; + + gParcelMgr->mSelectedMultipleOwners = + (request_result == PARCEL_RESULT_MULTIPLE); + + // Select the whole parcel + LLViewerRegion *region = gWorldp->getRegion( msg->getSender() ); + if (region) + { + if (!snap_selection) + { + // don't muck with the westsouth and eastnorth. + // just highlight it + LLVector3 west_south = region->getPosRegionFromGlobal(gParcelMgr->mWestSouth); + LLVector3 east_north = region->getPosRegionFromGlobal(gParcelMgr->mEastNorth); + + gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments); + gParcelMgr->writeHighlightSegments( + west_south.mV[VX], + west_south.mV[VY], + east_north.mV[VX], + east_north.mV[VY] ); + gParcelMgr->mWholeParcelSelected = FALSE; + } + else if (0 == local_id) + { + // this is public land, just highlight the selection + gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min ); + gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max ); + + gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments); + gParcelMgr->writeHighlightSegments( + aabb_min.mV[VX], + aabb_min.mV[VY], + aabb_max.mV[VX], + aabb_max.mV[VY] ); + gParcelMgr->mWholeParcelSelected = TRUE; + } + else + { + gParcelMgr->mWestSouth = region->getPosGlobalFromRegion( aabb_min ); + gParcelMgr->mEastNorth = region->getPosGlobalFromRegion( aabb_max ); + + // Owned land, highlight the boundaries + S32 bitmap_size = gParcelMgr->mParcelsPerEdge + * gParcelMgr->mParcelsPerEdge + / 8; + U8* bitmap = new U8[ bitmap_size ]; + msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size); + + gParcelMgr->resetSegments(gParcelMgr->mHighlightSegments); + gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mHighlightSegments ); + + delete bitmap; + bitmap = NULL; + + gParcelMgr->mWholeParcelSelected = TRUE; + } + + // Request access list information for this land + gParcelMgr->sendParcelAccessListRequest(AL_ACCESS | AL_BAN); + + // Request dwell for this land, if it's not public land. + gParcelMgr->mSelectedDwell = 0.f; + if (0 != local_id) + { + gParcelMgr->sendParcelDwellRequest(); + } + + gParcelMgr->mSelected = TRUE; + gParcelMgr->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 + gParcelMgr->mRenderCollision = TRUE; + gParcelMgr->mCollisionTimer.reset(); + + // Differentiate this parcel if we are banned from it. + if (sequence_id == COLLISION_BANNED_PARCEL_SEQ_ID) + { + gParcelMgr->mCollisionBanned = BA_BANNED; + } + else if (sequence_id == COLLISION_NOT_IN_GROUP_PARCEL_SEQ_ID) + { + gParcelMgr->mCollisionBanned = BA_NOT_IN_GROUP; + } + else + { + gParcelMgr->mCollisionBanned = BA_NOT_ON_LIST; + + } + + S32 bitmap_size = gParcelMgr->mParcelsPerEdge + * gParcelMgr->mParcelsPerEdge + / 8; + U8* bitmap = new U8[ bitmap_size ]; + msg->getBinaryDataFast(_PREHASH_ParcelData, _PREHASH_Bitmap, bitmap, bitmap_size); + + gParcelMgr->resetSegments(gParcelMgr->mCollisionSegments); + gParcelMgr->writeSegmentsFromBitmap( bitmap, gParcelMgr->mCollisionSegments ); + + delete bitmap; + bitmap = NULL; + + } + else if (sequence_id == HOVERED_PARCEL_SEQ_ID) + { + LLViewerRegion *region = gWorldp->getRegion( msg->getSender() ); + if (region) + { + gParcelMgr->mHoverWestSouth = region->getPosGlobalFromRegion( aabb_min ); + gParcelMgr->mHoverEastNorth = region->getPosGlobalFromRegion( aabb_max ); + } + else + { + gParcelMgr->mHoverWestSouth.clearVec(); + gParcelMgr->mHoverEastNorth.clearVec(); + } + } + else + { + // It's the agent parcel + BOOL new_parcel = parcel ? FALSE : TRUE; + if (parcel) + { + S32 parcelid = parcel->getLocalID(); + U64 regionid = gAgent.getRegion()->getHandle(); + if (parcelid != gParcelMgr->mMediaParcelId || regionid != gParcelMgr->mMediaRegionId) + { + gParcelMgr->mMediaParcelId = parcelid; + gParcelMgr->mMediaRegionId = regionid; + new_parcel = TRUE; + } + } + // look for music. + if (gAudiop) + { + if (parcel) + { + LLString music_url_raw = parcel->getMusicURL(); + + // Trim off whitespace from front and back + LLString music_url = music_url_raw; + LLString::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 char* 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(music_url); + } + } + else if (gAudiop->getInternetStreamURL()[0]) + { + llinfos << "Stopping parcel music" << llendl; + gAudiop->startInternetStream(NULL); + } + } + } + else + { + // Public land has no music + gAudiop->stopInternetStream(); + } + }//if gAudiop + + // now check for video + if (LLMediaEngine::getInstance ()->isAvailable ()) + { + // we have a player + if (parcel) + { + // we're in a parcel + std::string mediaUrl = std::string ( parcel->getMediaURL () ); + LLString::trim(mediaUrl); + + // something changed + LLMediaEngine* me = LLMediaEngine::getInstance(); + if ( ( me->getUrl () != mediaUrl ) + || ( me->getImageUUID () != parcel->getMediaID () ) + || ( me->isAutoScaled () != parcel->getMediaAutoScale () ) ) + { + BOOL video_was_playing = FALSE; + LLMediaBase* renderer = me->getMediaRenderer(); + if (renderer && (renderer->isPlaying() || renderer->isLooping())) + { + video_was_playing = TRUE; + } + + stop_video(); + + if ( !mediaUrl.empty () ) + { + // Someone has "changed the channel", changing the URL of a video + // you were already watching. Do we want to automatically start playing? JC + if (!new_parcel + && gSavedSettings.getBOOL("AudioStreamingVideo") + && video_was_playing) + { + start_video(parcel); + } + else + { + // "Prepare" the media engine, but don't auto-play. JC + optionally_prepare_video(parcel); + } + } + } + } + else + { + stop_video(); + } + } + else + { + // no audio player, do a first use dialog if their is media here + if (parcel) + { + std::string mediaUrl = std::string ( parcel->getMediaURL () ); + if (!mediaUrl.empty ()) + { + if (gSavedSettings.getWarning("QuickTimeInstalled")) + { + gSavedSettings.setWarning("QuickTimeInstalled", FALSE); + + LLNotifyBox::showXml("NoQuickTime" ); + }; + } + } + } + + }; +} + +void optionally_start_music(const LLString& music_url) +{ + if (gSavedSettings.getWarning("FirstStreamingMusic")) + { + void* data = (void*)strdup(music_url.c_str()); + gViewerWindow->alertXml("ParcelCanPlayMusic", + callback_start_music, + (void*)data); + + } + else if (gSavedSettings.getBOOL("AudioStreamingMusic")) + { + // Make the user click the start button on the overlay bar. JC + // llinfos << "Starting parcel music " << music_url << 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->getMusicRemoteControl ()->getTransportState () == LLMediaRemoteCtrl::Play ) + { + if (gAudiop) + { + gAudiop->startInternetStream(music_url.c_str()); + } + }; + } +} + + +void callback_start_music(S32 option, void* data) +{ + const char* music_url = (const char*)data; + + if (0 == option) + { + gSavedSettings.setBOOL("AudioStreamingMusic", TRUE); + llinfos << "Starting first parcel music " << music_url << llendl; + if (gAudiop) + { + gAudiop->startInternetStream(music_url); + LLMediaRemoteCtrl* ctrl = gOverlayBar->getMusicRemoteControl(); + ctrl->setTransportState( LLMediaRemoteCtrl::Play, FALSE ); + } + } + else + { + gSavedSettings.setBOOL("AudioStreamingMusic", FALSE); + } + + gSavedSettings.setWarning("FirstStreamingMusic", FALSE); + + delete [] music_url; + music_url = NULL; +} + +void prepare_video(const LLParcel *parcel) +{ + std::string mediaUrl; + if (parcel->getParcelFlag(PF_URL_RAW_HTML)) + { + mediaUrl = std::string("data:"); + mediaUrl.append(parcel->getMediaURL()); + } + else + { + mediaUrl = std::string ( parcel->getMediaURL () ); + } + LLMediaEngine::getInstance ()->setUrl ( mediaUrl ); + LLMediaEngine::getInstance ()->setImageUUID ( parcel->getMediaID () ); + LLMediaEngine::getInstance ()->setAutoScaled ( parcel->getMediaAutoScale () ? TRUE : FALSE ); // (U8 instead of BOOL for future expansion) +} + +void start_video(const LLParcel *parcel) +{ + prepare_video(parcel); + std::string path( "" ); + LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, false, path); +} + +void stop_video() +{ + // set up remote control so stop is selected + LLMediaEngine::getInstance ()->stop (); + if (gOverlayBar) + { + gOverlayBar->refresh (); + } + + if (LLMediaEngine::getInstance ()->isLoaded()) + { + LLMediaEngine::getInstance ()->unload (); + + gImageList.updateMovieImage(LLUUID::null, FALSE); + gCurrentMovieID.setNull(); + } + + LLMediaEngine::getInstance ()->setUrl ( "" ); + LLMediaEngine::getInstance ()->setImageUUID ( LLUUID::null ); + +} + +void optionally_prepare_video(const LLParcel *parcelp) +{ + if (gSavedSettings.getWarning("FirstStreamingVideo")) + { + gViewerWindow->alertXml("ParcelCanPlayMedia", + callback_prepare_video, + (void*)parcelp); + } + else + { + llinfos << "Entering parcel " << parcelp->getLocalID() << " with video " << parcelp->getMediaURL() << llendl; + prepare_video(parcelp); + } +} + + +void callback_prepare_video(S32 option, void* data) +{ + const LLParcel *parcelp = (const LLParcel *)data; + + if (0 == option) + { + gSavedSettings.setBOOL("AudioStreamingVideo", TRUE); + llinfos << "Starting parcel video " << parcelp->getMediaURL() << " on parcel " << parcelp->getLocalID() << llendl; + gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", LLMediaEngine::process_parcel_media); + gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", LLMediaEngine::process_parcel_media_update ); + prepare_video(parcelp); + } + else + { + gMessageSystem->setHandlerFunc("ParcelMediaCommandMessage", null_message_callback); + gMessageSystem->setHandlerFunc ( "ParcelMediaUpdate", null_message_callback ); + gSavedSettings.setBOOL("AudioStreamingVideo", FALSE); + } + + gSavedSettings.setWarning("FirstStreamingVideo", FALSE); +} + +// 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 = gParcelMgr->mParcel; + 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) ); + }*/ + + gParcelMgr->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 == gParcelMgr->mParcel->getLocalID()) + { + gParcelMgr->mSelectedDwell = dwell; + gParcelMgr->notifyObservers(); + } +} + + +void LLViewerParcelMgr::sendParcelAccessListUpdate(U32 which) +{ + + LLUUID transactionUUID; + transactionUUID.generate(); + + if (!mSelected) + { + return; + } + + LLViewerRegion *region = gWorldp->getRegionFromPosGlobal( mWestSouth ); + if (!region) return; + + LLMessageSystem* msg = gMessageSystem; + + LLParcel* parcel = mParcel; + 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() ); + } + } +} + + +void LLViewerParcelMgr::deedLandToGroup() +{ + char group_name[MAX_STRING]; + gCacheName->getGroupName(mParcel->getGroupID(), group_name); + LLString::format_map_t args; + args["[AREA]"] = llformat("%d", mParcel->getArea()); + args["[GROUP_NAME]"] = group_name; + if(mParcel->getContributeWithDeed()) + { + char first_name[DB_FIRST_NAME_BUF_SIZE]; + first_name[0] = '\0'; + char last_name[DB_FIRST_NAME_BUF_SIZE]; + last_name[0] = '\0'; + gCacheName->getName(mParcel->getOwnerID(), first_name, last_name); + args["[FIRST_NAME]"] = first_name; + args["[LAST_NAME]"] = last_name; + gViewerWindow->alertXml("DeedLandToGroupWithContribution",args, deedAlertCB, NULL); + } + else + { + gViewerWindow->alertXml("DeedLandToGroup",args, deedAlertCB, NULL); + } +} + +// static +void LLViewerParcelMgr::deedAlertCB(S32 option, void*) +{ + if (option == 0) + { + LLParcel* parcel = gParcelMgr->getSelectedParcel(); + LLUUID group_id; + if(parcel) + { + group_id = parcel->getGroupID(); + } + gParcelMgr->sendParcelDeed(group_id); + } +} + + +void LLViewerParcelMgr::startReleaseLand() +{ + if (!mSelected) + { + gViewerWindow->alertXml("CannotReleaseLandNothingSelected"); + return; + } + + if (mRequestResult == PARCEL_RESULT_NO_DATA) + { + gViewerWindow->alertXml("CannotReleaseLandWatingForServer"); + return; + } + + if (mRequestResult == PARCEL_RESULT_MULTIPLE) + { + gViewerWindow->alertXml("CannotReleaseLandSelected"); + return; + } + + if (!isParcelOwnedByAgent(mParcel, GP_LAND_RELEASE) + && !(gAgent.canManageEstate())) + { + gViewerWindow->alertXml("CannotReleaseLandDontOwn"); + return; + } + + LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0; + LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center); + if (!region) + { + gViewerWindow->alertXml("CannotReleaseLandRegionNotFound"); + return; + } +/* + if ((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL) + && !gAgent.isGodlike()) + { + LLStringBase::format_map_t args; + args["[REGION]"] = region->getName(); + gViewerWindow->alertXml("CannotReleaseLandNoTransfer", args); + return; + } +*/ + + if (!mWholeParcelSelected) + { + gViewerWindow->alertXml("CannotReleaseLandPartialSelection"); + return; + } + + // Compute claim price + LLStringBase::format_map_t args; + args["[AREA]"] = llformat("%d",mParcel->getArea()); + gViewerWindow->alertXml("ReleaseLandWarning", args, + releaseAlertCB, this); +} + +bool LLViewerParcelMgr::canAgentBuyParcel(LLParcel* parcel, bool forGroup) const +{ + if (!parcel) + { + return false; + } + + if (mSelected && parcel == mParcel) + { + 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 + } + + 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 isAvailable + = parcel->getReservedForNewbie() + ? (!forGroup && gStatusBar->getSquareMetersCommitted() == 0) + : true; + // *TODO: should be based on never_owned_land, see SL-10728 + + bool isAuthorized + = (authorizeBuyer.isNull() || (gAgent.getID() == authorizeBuyer)); + + return isForSale && !isOwner && isAuthorized && isAvailable && isEmpowered; +} + + +void LLViewerParcelMgr::startBuyLand(BOOL is_for_group) +{ + LLFloaterBuyLand::buyLand(getSelectionRegion(), mParcel, is_for_group == TRUE); +} + +void LLViewerParcelMgr::startSellLand() +{ + LLFloaterSellLand::sellLand(getSelectionRegion(), mParcel); +} + +void LLViewerParcelMgr::startDivideLand() +{ + if (!mSelected) + { + gViewerWindow->alertXml("CannotDivideLandNothingSelected"); + return; + } + + if (mWholeParcelSelected) + { + gViewerWindow->alertXml("CannotDivideLandPartialSelection"); + return; + } + + gViewerWindow->alertXml("LandDivideWarning", + callbackDivideLand, + this); +} + +// static +void LLViewerParcelMgr::callbackDivideLand(S32 option, void* data) +{ + LLViewerParcelMgr* self = (LLViewerParcelMgr*)data; + + LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0; + LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center); + if (!region) + { + gViewerWindow->alertXml("CannotDivideLandNoRegion"); + return; + } + + if (0 == option) + { + LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth); + LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth); + + 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()); + } +} + + +void LLViewerParcelMgr::startJoinLand() +{ + if (!mSelected) + { + gViewerWindow->alertXml("CannotJoinLandNothingSelected"); + return; + } + + if (mWholeParcelSelected) + { + gViewerWindow->alertXml("CannotJoinLandEntireParcelSelected"); + return; + } + + if (!mSelectedMultipleOwners) + { + gViewerWindow->alertXml("CannotJoinLandSelection"); + return; + } + + gViewerWindow->alertXml("JoinLandWarning", + callbackJoinLand, + this); +} + +// static +void LLViewerParcelMgr::callbackJoinLand(S32 option, void* data) +{ + LLViewerParcelMgr* self = (LLViewerParcelMgr*)data; + + LLVector3d parcel_center = (self->mWestSouth + self->mEastNorth) / 2.0; + LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center); + if (!region) + { + gViewerWindow->alertXml("CannotJoinLandNoRegion"); + return; + } + + if (0 == option) + { + LLVector3 west_south = region->getPosRegionFromGlobal(self->mWestSouth); + LLVector3 east_north = region->getPosRegionFromGlobal(self->mEastNorth); + + 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()); + } +} + + +void LLViewerParcelMgr::startDeedLandToGroup() +{ + if (!mSelected || !mParcel) + { + gViewerWindow->alertXml("CannotDeedLandNothingSelected"); + return; + } + + if (mRequestResult == PARCEL_RESULT_NO_DATA) + { + gViewerWindow->alertXml("CannotDeedLandWaitingForServer"); + return; + } + + if (mRequestResult == PARCEL_RESULT_MULTIPLE) + { + gViewerWindow->alertXml("CannotDeedLandMultipleSelected"); + return; + } + + LLVector3d parcel_center = (mWestSouth + mEastNorth) / 2.0; + LLViewerRegion* region = gWorldp->getRegionFromPosGlobal(parcel_center); + if (!region) + { + gViewerWindow->alertXml("CannotDeedLandNoRegion"); + return; + } + + /* + if(!gAgent.isGodlike()) + { + if((region->getRegionFlags() & REGION_FLAGS_BLOCK_LAND_RESELL) + && (mParcel->getOwnerID() != region->getOwner())) + { + LLStringBase::format_map_t args; + args["[REGION]"] = region->getName(); + gViewerWindow->alertXml("CannotDeedLandNoTransfer", args); + return; + } + } + */ + + deedLandToGroup(); +} +void LLViewerParcelMgr::reclaimParcel() +{ + LLParcel* parcel = gParcelMgr->getSelectedParcel(); + LLViewerRegion* regionp = gParcelMgr->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 +void LLViewerParcelMgr::releaseAlertCB(S32 option, void *) +{ + if (option == 0) + { + // Send the release message, not a force + gParcelMgr->sendParcelRelease(); + } +} + +void LLViewerParcelMgr::buyPass() +{ + LLParcel* parcel = getSelectedParcel(); + 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] ); +} + -- cgit v1.1