summaryrefslogtreecommitdiff
path: root/indra/newview/llviewerparcelmgr.cpp
diff options
context:
space:
mode:
authorJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
committerJames Cook <james@lindenlab.com>2007-01-02 08:33:20 +0000
commit420b91db29485df39fd6e724e782c449158811cb (patch)
treeb471a94563af914d3ed3edd3e856d21cb1b69945 /indra/newview/llviewerparcelmgr.cpp
Print done when done.
Diffstat (limited to 'indra/newview/llviewerparcelmgr.cpp')
-rw-r--r--indra/newview/llviewerparcelmgr.cpp2568
1 files changed, 2568 insertions, 0 deletions
diff --git a/indra/newview/llviewerparcelmgr.cpp b/indra/newview/llviewerparcelmgr.cpp
new file mode 100644
index 0000000000..2b3c364454
--- /dev/null
+++ b/indra/newview/llviewerparcelmgr.cpp
@@ -0,0 +1,2568 @@
+/**
+ * @file llviewerparcelmgr.cpp
+ * @brief Viewer-side representation of owned land
+ *
+ * Copyright (c) 2002-$CurrentYear$, Linden Research, Inc.
+ * $License$
+ */
+
+#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<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;
+}
+
+
+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);
+ bool web_url = (parcel->getParcelFlag(PF_URL_WEB_PAGE) || parcel->getParcelFlag(PF_URL_RAW_HTML));
+ std::string path( "" );
+ #if LL_MOZILLA_ENABLED
+ if (web_url)
+ {
+ path = get_mozilla_path();
+ }
+ #endif
+ LLMediaEngine::getInstance ()->convertImageAndLoadUrl ( true, web_url, 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" << 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<char>::format_map_t args;
+ args["[REGION]"] = region->getName();
+ gViewerWindow->alertXml("CannotReleaseLandNoTransfer", args);
+ return;
+ }
+*/
+
+ if (!mWholeParcelSelected)
+ {
+ gViewerWindow->alertXml("CannotReleaseLandPartialSelection");
+ return;
+ }
+
+ // Compute claim price
+ LLStringBase<char>::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;
+ //FIXME: 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<char>::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))
+ 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)
+ {
+ // *FIX: 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] );
+}
+