Index: CommandsImplementation.cxx
===================================================================
--- CommandsImplementation.cxx	(revision 20925)
+++ CommandsImplementation.cxx	(working copy)
@@ -127,7 +127,13 @@
     bool operator() (const char *commandLine);
 };
 
+class ForceRadarCommand : LocalCommand {
+  public:
+    ForceRadarCommand();
+    bool operator() (const char *commandLine);
+};
 
+
 // class instantiations
 static CommandList	  commandList;
 static BindCommand	  bindCommand;
@@ -144,24 +150,26 @@
 static ReTextureCommand   reTextureCommand;
 static SaveMsgsCommand	  saveMsgsCommand;
 static SaveWorldCommand   saveWorldCommand;
+static ForceRadarCommand  forceRadarCommand;
 
 
 // class constructors
-BindCommand::BindCommand() :		LocalCommand("/bind") {}
-CommandList::CommandList() :		LocalCommand("/cmds") {}
-DiffCommand::DiffCommand() :		LocalCommand("/diff") {}
-DumpCommand::DumpCommand() :		LocalCommand("/dumpvars") {}
-HighlightCommand::HighlightCommand() :	LocalCommand("/highlight") {}
-LocalSetCommand::LocalSetCommand() :	LocalCommand("/localset") {}
-QuitCommand::QuitCommand() :		LocalCommand("/quit") {}
-ReTextureCommand::ReTextureCommand() :	LocalCommand("/retexture") {}
-RoamPosCommand::RoamPosCommand() :	LocalCommand("/roampos") {}
-SaveMsgsCommand::SaveMsgsCommand() :	LocalCommand("/savemsgs") {}
-SaveWorldCommand::SaveWorldCommand() :	LocalCommand("/saveworld") {}
-SetCommand::SetCommand() :		LocalCommand("/set") {}
-SilenceCommand::SilenceCommand() :	LocalCommand("/silence") {}
-UnsilenceCommand::UnsilenceCommand() :	LocalCommand("/unsilence") {}
-ClientQueryCommand::ClientQueryCommand() : LocalCommand("CLIENTQUERY") {}
+BindCommand::BindCommand()			: LocalCommand("/bind") {}
+CommandList::CommandList()			: LocalCommand("/cmds") {}
+DiffCommand::DiffCommand()			: LocalCommand("/diff") {}
+DumpCommand::DumpCommand()			: LocalCommand("/dumpvars") {}
+HighlightCommand::HighlightCommand()		: LocalCommand("/highlight") {}
+LocalSetCommand::LocalSetCommand()		: LocalCommand("/localset") {}
+QuitCommand::QuitCommand()			: LocalCommand("/quit") {}
+ReTextureCommand::ReTextureCommand()		: LocalCommand("/retexture") {}
+RoamPosCommand::RoamPosCommand()		: LocalCommand("/roampos") {}
+SaveMsgsCommand::SaveMsgsCommand()		: LocalCommand("/savemsgs") {}
+SaveWorldCommand::SaveWorldCommand()		: LocalCommand("/saveworld") {}
+ForceRadarCommand::ForceRadarCommand()		: LocalCommand("/forceradar") {}
+SetCommand::SetCommand()			: LocalCommand("/set") {}
+SilenceCommand::SilenceCommand()		: LocalCommand("/silence") {}
+UnsilenceCommand::UnsilenceCommand()		: LocalCommand("/unsilence") {}
+ClientQueryCommand::ClientQueryCommand()	: LocalCommand("CLIENTQUERY") {}
 
 
 // the meat of the matter
@@ -762,6 +770,28 @@
 }
 
 
+bool ForceRadarCommand::operator() (const char*)
+{
+  LocalPlayer* myTank = LocalPlayer::getMyTank();
+  if (myTank == NULL) {
+    return true;
+  }
+  if (myTank->getTeam() != ObserverTeam) {
+    return true; // FIXME -- add a message
+  }
+  float value;
+  if (BZDBCache::radarLimit == 0.0f) {
+    value = +BZDBCache::worldSize;
+  } else {
+    value = -BZDBCache::radarLimit;
+  }
+  BZDB.setPermission(StateDatabase::BZDB_RADARLIMIT, StateDatabase::User);
+  BZDB.setFloat(StateDatabase::BZDB_RADARLIMIT, value);
+  BZDB.setPermission(StateDatabase::BZDB_RADARLIMIT, StateDatabase::Server);
+  return true;
+}
+    
+
 // Local Variables: ***
 // mode:C++ ***
 // tab-width: 8 ***
Index: MainWindow.h
===================================================================
--- MainWindow.h	(revision 20925)
+++ MainWindow.h	(working copy)
@@ -67,6 +67,9 @@
 
     void		showWindow(bool = true);
     void		warpMouse();
+    void		warpMouse(int mx, int my);
+    void		warpMouseCenterX();
+    void		warpMouseCenterY();
     void		grabMouse();
     void		enableGrabMouse(bool on);
     bool		isGrabEnabled(void);
@@ -98,6 +101,8 @@
 
     static void		resizeCB(void*);
 
+    inline int		getYOffset() const;
+
   private:
     BzfWindow*		window;
     BzfJoystick*	joystick;
Index: BackgroundRenderer.cxx
===================================================================
--- BackgroundRenderer.cxx	(revision 20925)
+++ BackgroundRenderer.cxx	(working copy)
@@ -1217,6 +1217,14 @@
 }
 
 
+static void setupBlackFog(float fogColor[4])
+{
+  static const float black[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
+  glGetFloatv(GL_FOG_COLOR, fogColor);
+  glFogfv(GL_FOG_COLOR, black);
+}
+
+
 void BackgroundRenderer::drawGroundReceivers(SceneRenderer& renderer)
 {
   static const int receiverRings = 4;
@@ -1244,6 +1252,10 @@
 
   receiverGState.setState();
 
+  // setup black fog
+  float fogColor[4];
+  setupBlackFog(fogColor);
+
   glPushMatrix();
   int i, j;
   for (int k = 0; k < count; k++) {
@@ -1339,6 +1351,8 @@
     glTranslatef(-pos[0], -pos[1], 0.0f);
   }
   glPopMatrix();
+
+  glFogfv(GL_FOG_COLOR, fogColor);
 }
 
 
@@ -1384,6 +1398,10 @@
   advGState = builder.getState();
   advGState.setState();
 
+  // setup black fog
+  float fogColor[4];
+  setupBlackFog(fogColor);
+
   // lazy way to get texcoords
   if (useTexture) {
     const float repeat = BZDB.eval("groundHighResTexRepeat");
@@ -1512,6 +1530,8 @@
     glDisable(GL_TEXTURE_GEN_S);
     glDisable(GL_TEXTURE_GEN_T);
   }
+
+  glFogfv(GL_FOG_COLOR, fogColor);
 }
 
 
Index: playing.cxx
===================================================================
--- playing.cxx	(revision 20925)
+++ playing.cxx	(working copy)
@@ -171,6 +171,11 @@
 static float FarDeepPlane = FarDeepPlaneDefault;
 static float NearPlane = NearPlaneNormal;
 
+static bool leftButton   = false;
+static bool rightButton  = false;
+static bool middleButton = false;
+
+
 enum BlowedUpReason {
   GotKilledMsg,
   GotShot,
@@ -547,10 +552,11 @@
 static bool		motionFreeze = false;
 #endif
 
-static enum {None, Left, Right, Up, Down} keyboardMovement;
+static enum { None, Left, Right, Up, Down } keyboardMovement;
 static int shiftKeyStatus;
 
-static bool		doKeyCommon(const BzfKeyEvent& key, bool pressed)
+
+static bool doKeyCommon(const BzfKeyEvent& key, bool pressed)
 {
   keyboardMovement = None;
   shiftKeyStatus   = key.shift;
@@ -577,6 +583,7 @@
       return true;
     }
   }
+
   std::string cmdDrive = cmd;
   if (cmdDrive.empty()) {
     // Check for driving keys
@@ -594,7 +601,7 @@
     keyboardMovement = Down;
   }
 
-  if (myTank)
+  if (myTank) {
     switch (keyboardMovement) {
       case Left:
 	myTank->setKey(BzfKeyEvent::Left, pressed);
@@ -611,9 +618,12 @@
       case None:
 	break;
     }
+  }
+
   if (!cmd.empty()) {
-    if (cmd=="fire")
+    if (cmd == "fire") {
       fireButton = pressed;
+    }
     roamButton = pressed;
     if (keyboardMovement == None) {
       std::string result = CMDMGR.run(cmd);
@@ -753,7 +763,14 @@
   }
 }
 
+
 static void doKey(const BzfKeyEvent& key, bool pressed) {
+  switch (key.button) {
+    case BzfKeyEvent::LeftMouse:   { leftButton   = pressed; break; }
+    case BzfKeyEvent::RightMouse:  { rightButton  = pressed; break; }
+    case BzfKeyEvent::MiddleMouse: { middleButton = pressed; break; }
+  }
+    
   if (myTank) {
     const std::string cmd = KEYMGR.get(key, pressed);
     if (cmd == "jump") {
@@ -3254,9 +3271,11 @@
     msg = nboUnpackString(msg, value, valueLen);
     value[valueLen] = '\0';
 
-    BZDB.set(name, value);
-    BZDB.setPersistent(name, false);
-    BZDB.setPermission(name, StateDatabase::Locked);
+    if (strcmp(name, "fpsLimit") != 0) {
+      BZDB.set(name, value);
+      BZDB.setPersistent(name, false);
+      BZDB.setPermission(name, StateDatabase::Locked);
+    }
   }
   return msg;
 }
@@ -4862,6 +4881,7 @@
   }
 }
 
+
 static void checkDirtyControlPanel(ControlPanel *cp)
 {
   if (cp) {
@@ -4872,7 +4892,8 @@
   return;
 }
 
-static int		getZoomFactor()
+
+static int getZoomFactor()
 {
   if (!BZDB.isSet("displayZoom")) return 1;
   const int zoom = atoi(BZDB.get("displayZoom").c_str());
@@ -4882,6 +4903,53 @@
 }
 
 
+static void renderRoamMouse()
+{
+  if (!ROAM.isRoaming() ||
+      !myTank || (myTank->getTeam() != ObserverTeam) ||
+      !(leftButton || rightButton || middleButton)) {
+    return;
+  }
+
+  const int sx = mainWindow->getWidth();
+  const int sy = mainWindow->getHeight();
+  const int ox = mainWindow->getOriginX();
+  const int oy = mainWindow->getOriginY();
+  int mx, my;
+  mainWindow->getWindow()->getMouse(mx, my);
+  my = sy - my - 1; // flip the y axis
+  const int xc = ox + (sx / 2);
+  const int y2 = oy + (mainWindow->getViewHeight() / 2);
+  const int yc = (sy - y2 - 1); // flip the y axis
+
+  glPushAttrib(GL_ALL_ATTRIB_BITS);
+
+  glScissor(ox, oy, sx, sy);
+  glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity();
+  glOrtho(0.0, sx, 0.0, sy, -1.0, 1.0);
+  glMatrixMode(GL_MODELVIEW);  glPushMatrix(); glLoadIdentity();
+
+  glShadeModel(GL_SMOOTH);
+  glEnable(GL_BLEND);
+  glEnable(GL_LINE_SMOOTH);
+  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+  static const float color0[4] = { 0.0f, 0.0f, 0.0f, 0.1f };
+  static const float color1[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+
+  glLineWidth(1.49f);
+  glBegin(GL_LINES);  
+  glColor4fv(color0); glVertex2i(xc, yc);
+  glColor4fv(color1); glVertex2i(mx, my);
+  glEnd();
+
+  glMatrixMode(GL_PROJECTION); glPopMatrix();
+  glMatrixMode(GL_MODELVIEW);  glPopMatrix();
+
+  glPopAttrib();
+}
+
+
 static void drawUI()
 {
   // setup the triangle counts  (FIXME: hackish)
@@ -4918,6 +4986,9 @@
   // update the HUD (menus)
   renderDialog();
 
+  // render the drag-line
+  renderRoamMouse();
+
   return;
 }
 
@@ -5675,10 +5746,53 @@
 }
 
 
-static void		setupRoamingCamera(float dt)
+//============================================================================//
+
+enum MouseButtonBits {
+  leftMouseBit   = (1 << 0),
+  rightMouseBit  = (1 << 1),
+  middleMouseBit = (1 << 2)
+};
+
+enum MouseCtrlType {
+  NoCtrl,
+  ShiftX, // left/right
+  ShiftY, // backwards/forewards
+  ShiftZ, // up/down
+  SpinX,  // tilt (phi)
+  SpinY,  // -- not used --
+  SpinZ   // heading (theta)
+};
+
+struct MouseCtrlPair {
+  MouseCtrlType x;
+  MouseCtrlType y;
+};
+
+static const bool mouseCtrlMask = 0x7;
+
+static const MouseCtrlPair mouseCtrlMap[8] = {
+//  X       Y
+  { NoCtrl, NoCtrl }, // . . .
+  { SpinZ,  ShiftY }, // L . .
+  { SpinZ,  SpinX  }, // . R .
+  { ShiftX, ShiftY }, // L R .
+  { ShiftX, ShiftZ }, // . . M
+  { SpinZ,  ShiftZ }, // L . M
+  { SpinZ,  ShiftZ }, // . R M
+  { ShiftX, ShiftY }  // L R M
+};
+
+
+static void setupRoamingCamera(float dt)
 {
+
   static Roaming::RoamingCamera prevDeltaCamera;
   static bool inited = false;
+  static int prevMouseBits = 0;
+  int currMouseBits = (leftButton   ? leftMouseBit   : 0) |
+                      (rightButton  ? rightMouseBit  : 0) |
+                      (middleButton ? middleMouseBit : 0);
 
   if (!inited) {
     memset(&prevDeltaCamera, 0, sizeof(Roaming::RoamingCamera));
@@ -5690,25 +5804,71 @@
 
   // move roaming camera
   if (myTank) {
-    bool control = ((shiftKeyStatus & BzfKeyEvent::ControlKey) != 0);
-    bool alt     = ((shiftKeyStatus & BzfKeyEvent::AltKey) != 0);
-    bool shift   = ((shiftKeyStatus & BzfKeyEvent::ShiftKey) != 0);
-    if (display->hasGetKeyMode()) {
-      display->getModState (shift, control, alt);
+    int mx, my;
+    mainWindow->getMousePosition(mx, my);
+
+    const MouseCtrlPair currCtrl = mouseCtrlMap[currMouseBits];
+    const MouseCtrlPair prevCtrl = mouseCtrlMap[prevMouseBits];
+
+    if (currCtrl.x == prevCtrl.x) {
+      if (currCtrl.y != prevCtrl.y) {
+        mainWindow->warpMouseCenterY();
+        my = 0;
+      }
     }
-    if (!control && !shift) {
-      deltaCamera.pos[0] = (float)(4 * myTank->getSpeed()) * BZDBCache::tankSpeed;
+    else if (currCtrl.y == prevCtrl.y) {
+      mainWindow->warpMouseCenterX();
+      mx = 0;
     }
-    if (alt) {
-      deltaCamera.pos[1] = (float)(4 * myTank->getRotation()) * BZDBCache::tankSpeed;
-    } else {
-      deltaCamera.theta  = ROAM.getZoom() * (float)myTank->getRotation();
+    else {
+      mainWindow->warpMouse();
+      mx = my = 0;
     }
-    if (control) {
-      deltaCamera.phi    = -2.0f * ROAM.getZoom() / 3.0f * (float)myTank->getSpeed();
+    
+    if (currMouseBits != 0) {
+      // mouse control
+      const float spinMult  =  -100.0f;
+      const float shiftMult = -1000.0f;
+      const int wx = mainWindow->getWidth();
+      const int wy = mainWindow->getViewHeight();
+      const int ws = (wx < wy) ? wx : wy;
+      const float wf = 1.0f / (float(ws * ws) * 0.25f);
+      const float sx = float(mx * abs(mx)) * wf;
+      const float sy = float(my * abs(my)) * wf;
+      switch (currCtrl.x) {
+        case SpinZ:  { deltaCamera.theta  = spinMult  * sx; break; }
+        case ShiftX: { deltaCamera.pos[1] = shiftMult * sx; break; }
+        default: { break; }
+      }
+      switch (currCtrl.y) {
+        case SpinX:  { deltaCamera.phi    =  spinMult * sy; break; }
+        case ShiftY: { deltaCamera.pos[0] = shiftMult * sy; break; }
+        case ShiftZ: { deltaCamera.pos[2] = shiftMult * sy; break; }
+        default: { break; }
+      }
     }
-    if (shift) {
-      deltaCamera.pos[2] = (float)(-4 * myTank->getSpeed()) * BZDBCache::tankSpeed;
+    else {
+      // keyboard control
+      bool control = ((shiftKeyStatus & BzfKeyEvent::ControlKey) != 0);
+      bool alt     = ((shiftKeyStatus & BzfKeyEvent::AltKey) != 0);
+      bool shift   = ((shiftKeyStatus & BzfKeyEvent::ShiftKey) != 0);
+      if (display->hasGetKeyMode()) {
+        display->getModState (shift, control, alt);
+      }
+      if (!control && !shift) {
+        deltaCamera.pos[0] = (float)(4 * myTank->getSpeed()) * BZDBCache::tankSpeed;
+      }
+      if (alt) {
+        deltaCamera.pos[1] = (float)(4 * myTank->getRotation()) * BZDBCache::tankSpeed;
+      } else {
+        deltaCamera.theta  = ROAM.getZoom() * (float)myTank->getRotation();
+      }
+      if (control) {
+        deltaCamera.phi    = -2.0f * ROAM.getZoom() / 3.0f * (float)myTank->getSpeed();
+      }
+      if (shift) {
+        deltaCamera.pos[2] = (float)(-4 * myTank->getSpeed()) * BZDBCache::tankSpeed;
+      }
     }
   }
 
@@ -5723,8 +5883,8 @@
     deltaCamera.pos[0] = (at * deltaCamera.pos[0]) + (bt * prevDeltaCamera.pos[0]);
     deltaCamera.pos[1] = (at * deltaCamera.pos[1]) + (bt * prevDeltaCamera.pos[1]);
     deltaCamera.pos[2] = (at * deltaCamera.pos[2]) + (bt * prevDeltaCamera.pos[2]);
-    deltaCamera.theta  = (at * deltaCamera.theta) + (bt * prevDeltaCamera.theta);
-    deltaCamera.phi    = (at * deltaCamera.phi) + (bt * prevDeltaCamera.phi);
+    deltaCamera.theta  = (at * deltaCamera.theta)  + (bt * prevDeltaCamera.theta);
+    deltaCamera.phi    = (at * deltaCamera.phi)    + (bt * prevDeltaCamera.phi);
   }
 
   deltaCamera.zoom = roamDZoom;
@@ -5734,10 +5894,14 @@
   // copy the old delta values
   memcpy(&prevDeltaCamera, &deltaCamera, sizeof(Roaming::RoamingCamera));
 
+  prevMouseBits = currMouseBits;
+
   return;
 }
 
 
+//============================================================================//
+
 static void		prepareTheHUD()
 {
   // prep the HUD
Index: MainWindow.cxx
===================================================================
--- MainWindow.cxx	(revision 20925)
+++ MainWindow.cxx	(working copy)
@@ -76,20 +76,46 @@
   if (on) resize();
 }
 
+
+inline int MainWindow::getYOffset() const
+{
+  if (quadrant == FullWindow) {
+    return 0;
+  }
+  return ((trueHeight + 1) >> 1) - yOrigin;
+}
+
 void			MainWindow::warpMouse()
 {
   // move mouse to center of view window (zero motion box)
-  int y = viewHeight >> 1;
-  if (quadrant != FullWindow) y += ((trueHeight+1) >> 1) - yOrigin;
-  window->warpMouse((width >> 1) + xOrigin, y);
+  window->warpMouse((width >> 1) + xOrigin, (viewHeight >> 1) + getYOffset());
 }
 
+void			MainWindow::warpMouse(int mx, int my)
+{
+  window->warpMouse(mx, my);
+}
+
+void			MainWindow::warpMouseCenterX()
+{
+  int mx, my;
+  getMousePosition(mx, my);
+  window->warpMouse((width >> 1) + xOrigin, my + (viewHeight >> 1) + getYOffset());
+}
+
+void			MainWindow::warpMouseCenterY()
+{
+  int mx, my;
+  getMousePosition(mx, my);
+  const int y = (viewHeight >> 1) + getYOffset();
+  window->warpMouse(mx + (width >> 1) + xOrigin, y);
+}
+
 void			MainWindow::getMousePosition(int& mx, int& my) const
 {
   window->getMouse(mx, my);
   mx -= (width >> 1) + xOrigin;
-  my -= (viewHeight >> 1);
-  if (quadrant != FullWindow) my -= ((trueHeight+1) >> 1) - yOrigin;
+  my -= (viewHeight >> 1) + getYOffset();
 }
 
 void			MainWindow::grabMouse()
