Index: FlagHistory.h
===================================================================
--- FlagHistory.h	(revision 20911)
+++ FlagHistory.h	(working copy)
@@ -26,10 +26,10 @@
  public:
   void clear();
   void get(char message[]);
-  void add(FlagType* type);
-  std::vector<FlagType*> get(void) { return flagHistory;}
+  void add(const FlagType* type);
+  std::vector<const FlagType*> get(void) { return flagHistory;}
  private:
-  std::vector<FlagType*> flagHistory;
+  std::vector<const FlagType*> flagHistory;
 };
 
 #endif /* __FLAGHISTORY_H__ */
Index: FlagInfo.cxx
===================================================================
--- FlagInfo.cxx	(revision 20911)
+++ FlagInfo.cxx	(working copy)
@@ -16,31 +16,53 @@
 #pragma warning( 4:4786)
 #endif
 
-/* interface header */
+#include "common.h"
+
+// interface header
 #include "FlagInfo.h"
 
-/* system headers */
+// system headers
+#include <stdlib.h>
+#include <string.h>
 #include <iostream>
 
-// implementation-specific bzflag headers
+// common headers
 #include "BZDBCache.h"
 
-/* private */
+// bzfs headers
+#include "bzfs.h"
+#include "CmdLineOptions.h"
 
-/* protected */
+typedef std::map<const FlagType*, int> FlagCountMap;
 
-/* public */
 
-// flags list
-FlagInfo	      *FlagInfo::flagList      = NULL;
-std::vector<FlagType*> FlagInfo::allowedFlags;
-int		    FlagInfo::numExtraFlags = 0;
-int		    FlagInfo::numFlags      = 0;
-int		    FlagInfo::numFlagsInAir;
+// local prototypes
+static int  flagCountMapSum(FlagCountMap& fcm);
+static void flagCountMapClampMin(FlagCountMap& fcm, int min);
+static int  flagCountMapValue(const FlagType* ft, const FlagCountMap& fcm);
+static void addToFlagCountMap(const FlagType* ft, int count, FlagCountMap& fcm);
 
+
+static int numFlagsInAir = 0;
+
+
+/******************************************************************************/
+//
+// FlagInfo
+//
+
+
 FlagInfo::FlagInfo()
 {
+  player    = -1;
+  extra     = true;
+  random    = false;
+  srcZone   = NULL;
+  grabsLeft = 0;
+  shotCount = 0;
+
   // prep flag
+
   flag.type		  = Flags::Null;
   flag.status		  = FlagNoExist;
   flag.endurance	  = FlagNormal;
@@ -57,248 +79,1575 @@
   flag.flightTime	  = 0.0f;
   flag.flightEnd	  = 0.0f;
   flag.initialVelocity    = 0.0f;
-  player		  = -1;
-  grabs			  = 0;
-  required		  = false;
 }
 
-void FlagInfo::setSize(int _numFlags)
-{
-  // sanity check
-  if (_numFlags > 1000000) {
-    std::cerr << "WARNING: FlagInfo::setSize was given an insane flag count of " << _numFlags << std::endl;
-    std::cerr << "clamping to 1000000 flags just for kicks" << std::endl;
-    _numFlags = 100000;
-  }
 
-  numFlags = _numFlags;
-  delete[] flagList;
-  flagList = NULL;
-  if (numFlags)
-    flagList = new FlagInfo[numFlags];
-  for (int i = 0; i < numFlags; i++)
-    flagList[i].flagIndex = i;
-}
-
-void FlagInfo::setAllowed(std::vector<FlagType*> allowed)
+void FlagInfo::setExtraFlag()
 {
-  allowedFlags = allowed;
+  extra = true;
+  random = false;
+  flag.type = Flags::Null;
 }
 
-void FlagInfo::setExtra(int extra)
-{
-  numExtraFlags = extra;
-}
 
-int FlagInfo::lookupFirstTeamFlag(int teamindex)
+void FlagInfo::setRandomFlag()
 {
-  for (int i = 0; i < numFlags; i++) {
-    if (flagList[i].flag.type->flagTeam == teamindex)
-      return i;
-  }
-  return -1;
+  extra = false;
+  random = true;
+  flag.type = Flags::Null;
 }
 
-void FlagInfo::setRequiredFlag(FlagType *desc)
+
+void FlagInfo::setForcedFlag(const FlagType *desc)
 {
-  required = true;
-  flag.type = desc;
+  extra = false;
+  random = false;
+  flag.type = (FlagType*) desc; // FIXME - cast cheat
 }
 
+
 void FlagInfo::addFlag()
 {
+  if (flag.type == Flags::Null) {
+    // pick a random flag
+    flag.type = (FlagType*) FLAGMGR.pickRandomFlagType(); // FIXME - cast cheat
+    if (flag.type == Flags::Null) {
+      return; // no random flags available, bail
+    }
+  }
+
   const float flagAltitude = BZDB.eval(StateDatabase::BZDB_FLAGALTITUDE);
   const float gravity      = BZDBCache::gravity;
   const float maxGrabs     = BZDB.eval(StateDatabase::BZDB_MAXFLAGGRABS);
 
   // flag in now entering game
   numFlagsInAir++;
-  flag.status	  = FlagComing;
+  flag.status = FlagComing;
 
   // compute drop time
   const float flightTime = 2.0f * sqrtf(-2.0f * flagAltitude / gravity);
-  flag.flightTime	= 0.0f;
-  flag.flightEnd	 = flightTime;
+  flag.flightTime        = 0.0f;
+  flag.flightEnd         = flightTime;
   flag.initialVelocity   = -0.5f * gravity * flightTime;
-  dropDone	       = TimeKeeper::getCurrent();
-  dropDone	      += flightTime;
+  dropDone               = TimeKeeper::getCurrent();
+  dropDone              += flightTime;
 
-  if (flag.type == Flags::Null)
-    // pick a random flag
-    flag.type = allowedFlags[(int)(allowedFlags.size() * (float)bzfrand())];
 
   // decide how sticky the flag will be
-  if (flag.type->flagQuality == FlagBad)
+  if (flag.type->flagQuality == FlagBad) {
     flag.endurance = FlagSticky;
-  else
+  } else {
     flag.endurance = FlagUnstable;
+  }
 
   // how times will it stick around
-  if ((flag.endurance == FlagSticky) || (flag.type == Flags::Thief))
-    grabs = 1;
-  else
-    grabs = int(floor(maxGrabs * (float)bzfrand())) + 1;
+  if ((flag.endurance == FlagSticky) || (flag.type == Flags::Thief)) {
+    grabsLeft = 1;
+  } else {
+    grabsLeft = int(floor(maxGrabs * (float)bzfrand())) + 1;
+  }
 }
 
-void *FlagInfo::pack(void *buf, bool hide)
+
+void* FlagInfo::pack(void *buf, bool hide)
 {
-  if (FlagInfo::flagList[flagIndex].flag.type->flagTeam != ::NoTeam)
+  if ((player != -1) || (flag.type->flagTeam != ::NoTeam)) {
     hide = false;
-  if (FlagInfo::flagList[flagIndex].player != -1)
-    hide = false;
+  }
+
   buf = nboPackUShort(buf, flagIndex);
-  if (hide)
-    buf = FlagInfo::flagList[flagIndex].flag.fakePack(buf);
-  else
-    buf = FlagInfo::flagList[flagIndex].flag.pack(buf);
+  if (hide) {
+    buf = flag.fakePack(buf);
+  } else {
+    buf = flag.pack(buf);
+  }
+
   return buf;
 }
 
+
 void FlagInfo::dropFlag(float pos[3], float landingPos[3], bool vanish)
 {
   numFlagsInAir++;
-  flag.status	     = vanish ? FlagGoing : FlagInAir;
+  flag.status = vanish ? FlagGoing : FlagInAir;
 
   flag.landingPosition[0] = landingPos[0];
   flag.landingPosition[1] = landingPos[1];
   flag.landingPosition[2] = landingPos[2];
 
-  flag.position[0]       = landingPos[0];
-  flag.position[1]       = landingPos[1];
-  flag.position[2]       = landingPos[2];
+  flag.position[0] = landingPos[0];
+  flag.position[1] = landingPos[1];
+  flag.position[2] = landingPos[2];
   flag.launchPosition[0] = pos[0];
   flag.launchPosition[1] = pos[1];
   flag.launchPosition[2] = pos[2] + BZDBCache::tankHeight;
 
   // compute flight info -- flight time depends depends on start and end
   // altitudes and desired height above start altitude
-  const float gravity	= BZDBCache::gravity;
+  const float gravity        = BZDBCache::gravity;
   const float flagAltitude   = BZDB.eval(StateDatabase::BZDB_FLAGALTITUDE);
   const float thrownAltitude = (flag.type == Flags::Shield) ?
     BZDB.eval(StateDatabase::BZDB_SHIELDFLIGHT) * flagAltitude : flagAltitude;
   const float maxAltitude    = pos[2] + thrownAltitude;
-  const float upTime	 = sqrtf(-2.0f * thrownAltitude / gravity);
+  const float upTime         = sqrtf(-2.0f * thrownAltitude / gravity);
   const float downTime       = sqrtf(-2.0f * (maxAltitude - pos[2]) / gravity);
   const float flightTime     = upTime + downTime;
 
-  dropDone	     = TimeKeeper::getCurrent();
-  dropDone	    += flightTime;
+  dropDone = TimeKeeper::getCurrent();
+  dropDone += flightTime;
   flag.flightTime      = 0.0f;
   flag.flightEnd       = flightTime;
   flag.initialVelocity = -gravity * upTime;
 }
 
-void FlagInfo::resetFlag(float position[3], bool teamIsEmpty)
+
+void FlagInfo::resetFlag(const float position[3], bool teamIsEmpty)
 {
   // reset a flag's info
-  player      = -1;
+  player = -1;
+
   // if it's a random flag, reset flag id
-  if (flagIndex >= numFlags - numExtraFlags)
+  if (extra || random) {
     flag.type = Flags::Null;
+  }
 
   flag.position[0] = position[0];
   flag.position[1] = position[1];
   flag.position[2] = position[2];
 
   // required flags mustn't just disappear
-  if (required) {
-    if (flag.type->flagTeam == ::NoTeam)
+  if (extra || random) {
+    flag.status = FlagNoExist;
+  }
+  else {
+    if (flag.type->flagTeam == ::NoTeam) {
       // flag in now entering game
       addFlag();
-    else if (teamIsEmpty)
+    } else if (teamIsEmpty) {
       flag.status = FlagNoExist;
-    else
+    } else {
       flag.status = FlagOnGround;
-  } else {
-    flag.status = FlagNoExist;
+    }
   }
 }
 
+
 void FlagInfo::grab(int playerIndex)
 {
   flag.status = FlagOnTank;
   flag.owner  = playerIndex;
   player      = playerIndex;
-  numShots    = 0;
+  shotCount   = 0;
 }
 
-int FlagInfo::teamIndex() const
+
+bool FlagInfo::landing(const TimeKeeper &tm)
 {
-  return flag.type->flagTeam;
+  if (numFlagsInAir <= 0) {
+    return false;
+  }
+
+  bool land = false;
+
+  if ((dropDone - tm) <= 0.0f) {
+    if ((flag.status == FlagInAir) ||
+        (flag.status == FlagComing)) {
+      flag.status = FlagOnGround;
+      numFlagsInAir--;
+      land = true;
+    }
+    else if (flag.status == FlagGoing) {
+      flag.status = FlagNoExist;
+      numFlagsInAir--;
+      land = true;
+    }
+  }
+
+  return land;
 }
 
-int FlagInfo::getIndex() const
+
+void FlagInfo::getTextualInfo(char *message) const
 {
-  return flagIndex;
+  char status;
+  switch (flag.status) {
+    case FlagNoExist:  { status = 'N'; break; }
+    case FlagOnGround: { status = 'G'; break; }
+    case FlagOnTank:   { status = 'T'; break; }
+    case FlagInAir:    { status = 'a'; break; }
+    case FlagComing:   { status = 'c'; break; }
+    case FlagGoing:    { status = 'g'; break; }
+  }
+  
+  int flagLimit = -1;
+  FlagNumberMap::const_iterator it = clOptions->flagLimit.find(flag.type);
+  if (it != clOptions->flagLimit.end()) {
+    flagLimit = it->second;
+  }
+
+  sprintf(message, "#%-3d i:%-3s p:%-3d g:%-2d r:%c  e:%c  s:%c  z:%c  "
+		   "p:{%.1f, %.1f, %.1f}",
+	  flagIndex, flag.type->flagAbbv, player, grabsLeft,
+	  random ? 'Y' : 'N', extra ? 'Y' : 'N',
+	  status, (srcZone != NULL) ? 'Y' : 'N',
+	  flag.position[0], flag.position[1], flag.position[2]);
 }
 
-float FlagInfo::getNextDrop(TimeKeeper &tm)
+
+/******************************************************************************/
+//
+// FlagManager
+//
+
+FlagManager FlagManager::manager;
+
+
+// fake flag types
+const FlagType* FlagManager::allFlagType  = (const FlagType*)1;
+const FlagType* FlagManager::badFlagType  = (const FlagType*)2;
+const FlagType* FlagManager::goodFlagType = (const FlagType*)3;
+const FlagType* FlagManager::teamFlagType = (const FlagType*)4;
+
+
+FlagType* FlagManager::getTeamFlagType(int t)
 {
+  FlagType* teamFlags[5] = {
+    Flags::Null, // rogue
+    Flags::RedTeam,
+    Flags::GreenTeam,
+    Flags::BlueTeam,
+    Flags::PurpleTeam
+  };
+  if ((t >= RedTeam) && (t <= PurpleTeam)) {
+    return teamFlags[t];
+  }
+  return Flags::Null;
+}
+
+
+FlagManager::FlagManager()
+{
+  flagList = NULL;
+  clear();
+  forbidType(Flags::Null);
+  zoneIdentifyFlag = false;
+  minRandomDelay = 1.0f;
+  minRandomDelay = 5.0f;
+  nextRandomInsertion = TimeKeeper::getCurrent();
+  return;
+}
+
+
+FlagManager::~FlagManager()
+{
+  clear();
+  return;
+}
+
+
+void FlagManager::clear()
+{
+  forcedFlags.clear();
+  randomFlags.clear();
+  forbiddenFlags.clear();
+
+  delete[] flagList;
+  flagList= NULL;
+  fullCount = 0;
+  zoneCount = 0;
+  extraCount = 0;
+  forcedCount = 0;
+  randomCount = 0;
+
+  numFlagsInAir = 0; //FIXME?
+
+  return;
+}
+
+
+void FlagManager::clearRandomPool()
+{
+  randomFlags.clear();
+  return;
+}
+
+
+int FlagManager::getZoneTeamFlagCount(const FlagType* flagType,
+                                      const EntryZones& entryZones) const
+{
+  if (!isAllowedType(flagType)) {
+    return 0;
+  }
+  // tally zone team flags
+  int count = 0;
+  const ZoneList& zl = entryZones.getZoneList();
+  for (int z = 0; z < (int)zl.size(); z++) {
+    const CustomZone& zone = zl[z];
+    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
+    ZoneFlagMap::const_iterator zfmIt;
+    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
+      if (zfmIt->first == flagType) {
+	count += zfmIt->second;
+      }
+    }
+  }
+  return count;
+}
+
+
+int FlagManager::addZoneTeamFlags(int startIndex, const FlagType* flagType,
+                                  const EntryZones& entryZones)
+{
+  if (!isAllowedType(flagType)) {
+    return startIndex;
+  }
+  // add zone team flags
+  const ZoneList& zl = entryZones.getZoneList();
+  for (int z = 0; z < (int)zl.size(); z++) {
+    const CustomZone& zone = zl[z];
+    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
+    ZoneFlagMap::const_iterator zfmIt;
+    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
+      if (zfmIt->first == flagType) {
+	const int count = zfmIt->second;
+	for (int c = 0; c < count; c++) {
+	  FlagInfo& fi = flagList[startIndex];
+	  fi.setForcedFlag(flagType);
+	  fi.srcZone = &zone;
+	  startIndex++;
+	}
+      }
+    }
+  }
+  return startIndex;
+}
+
+
+void FlagManager::finalize()
+{
+  CmdLineOptions& options = *clOptions;
+  const EntryZones& entryZones = world-> getEntryZones();
+
+  // setup the forbidden flags
+  selectForbidden();  
+  removeForbidden(forcedFlags);
+  removeForbidden(randomFlags);
+
+  // make sure we have non-negative values
+  flagCountMapClampMin(forcedFlags, 0);
+  flagCountMapClampMin(randomFlags, 0);
+  if (extraCount < 0) {
+    extraCount = 0;
+  }
+  if (randomCount < 0) {
+    randomCount = 0;
+  }
+
+  // zone team flag counts
+  const int zoneTeamFlagCounts[5] = {
+    0, // rogue
+    getZoneTeamFlagCount(Flags::RedTeam, entryZones),
+    getZoneTeamFlagCount(Flags::GreenTeam, entryZones),
+    getZoneTeamFlagCount(Flags::BlueTeam, entryZones),
+    getZoneTeamFlagCount(Flags::PurpleTeam, entryZones)
+  };
+
+  // add a team flag for each active team
+  //   (if zone team flags are not being used)
+  if (options.gameStyle & TeamFlagGameStyle) {
+    for (int col = RedTeam; col <= PurpleTeam; col++) {
+      if ((options.maxTeam[col] > 0) && (zoneTeamFlagCounts[col] <= 0)) {
+        addToFlagCountMap(getTeamFlagType(col), 1, forcedFlags);
+      }
+    }
+  }
+
+  // make sure there are enough random flags in the pool
+  int randomPoolCount = flagCountMapSum(randomFlags);
+  if (randomPoolCount < randomCount) {
+    logDebugMessage(1, "WARNING: insufficient random flags in the pool (%i vs. %i)\n",
+           randomPoolCount, randomCount);
+// FIXME - Useless, better then a warning?
+//    const int missing = randomCount - randomPoolCount;
+//    addToFlagCountMap(Flags::Useless, missing, randomFlags);
+//    DEBUG1("WARNING: added %i useless random flags to satisfy the random count.\n",
+//           missing);
+  }
+
+  // zone flag count (including teams flags)
+  zoneCount = 0;
+  const ZoneList& zl = entryZones.getZoneList();
+  for (int z = 0; z < (int)zl.size(); z++) {
+    const CustomZone& zone = zl[z];
+    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
+    ZoneFlagMap::const_iterator zfmIt;
+    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
+      if (isAllowedType(zfmIt->first)) {
+	zoneCount += zfmIt->second;
+      }
+    }
+  }
+
+  // forced count (including team flags)  
+  forcedCount = flagCountMapSum(forcedFlags);
+
+  //
+  // make the array  
+  ///////////////////
+
+  fullCount = zoneCount + forcedCount + randomCount + extraCount;
+  flagList = new FlagInfo[fullCount];
+
+  // set the slot numbers
+  for (int i = 0; i < fullCount; i++) {
+    flagList[i].flagIndex = i;
+  }
+
+  // add team flags (ordered, zone and forced)
+  int f = 0;
+  if (options.gameStyle & TeamFlagGameStyle) {
+    for (int t = RedTeam; t <= PurpleTeam; t++) {
+      if (options.maxTeam[t] > 0) {
+        const FlagType* ft = getTeamFlagType(t);
+        f = addZoneTeamFlags(f, ft, entryZones);
+        const int count = flagCountMapValue(ft, forcedFlags);
+        for (int n = 0; n < count; n++) {
+          flagList[f].setForcedFlag(ft);
+          f++;
+        }
+      }
+    }
+  }
+
+  // super flags?
+  if (f < fullCount) {
+    // FIXME -- not quite true,
+    //          multiple team flags can be converted into superflags
+    options.gameStyle |= int(SuperFlagGameStyle);
+  }
+
+  // add normal zone flags
+  for (int z = 0; z < (int)zl.size(); z++) {
+    const CustomZone& zone = zl[z];
+    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
+    ZoneFlagMap::const_iterator zfmIt;
+    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
+      const FlagType* ft = zfmIt->first;
+      if ((ft->flagTeam == ::NoTeam) && // no team flags here
+	  (forbiddenFlags.find(ft) == forbiddenFlags.end())) {
+	const int count = zfmIt->second;
+	for (int c = 0; c < count; c++) {
+	  FlagInfo& fi = flagList[f];
+	  fi.setForcedFlag(ft);
+	  fi.srcZone = &zone;
+          f++;
+	}
+	if ((ft == Flags::Identify) && (count > 0)) {
+          zoneIdentifyFlag = true;
+        }
+      }
+    }
+  }
+
+  // add normal flags
+  FlagCountMap::const_iterator fcm_it;
+  for (fcm_it = forcedFlags.begin(); fcm_it != forcedFlags.end(); fcm_it++) {
+    const FlagType *ft = fcm_it->first;
+    if (ft->flagTeam == ::NoTeam) {
+      const int count = fcm_it->second;
+      for (int n = 0; n < count; n++) {
+        flagList[f].setForcedFlag(ft);
+        f++;
+      }
+    }
+  }
+
+  // add random flags
+  for (int rf = 0; rf < randomCount; rf++) {
+    flagList[f].setRandomFlag();
+    f++;
+  }
+
+  return;
+}
+
+
+void FlagManager::selectForbidden()
+{
+  CmdLineOptions& options = *clOptions;
+
+  // the obvious one
+  forbidType(Flags::Null);
+
+  // do we have any team flags?
+  bool hasTeam = false;
+  for (int p = RedTeam; p <= PurpleTeam; p++) {
+    if (options.maxTeam[p] > 1) {
+      hasTeam = true;
+      break;
+    }
+  }
+
+  // first disallow flags inconsistent with game style
+  if (options.gameStyle & JumpingGameStyle) {
+    forbidType(Flags::Jumping);
+  } else {
+    forbidType(Flags::NoJumping);
+  }
+  if (options.gameStyle & RicochetGameStyle) {
+    forbidType(Flags::Ricochet);
+  }
+  if (!options.useTeleporters && (options.worldFile == "")) {
+    forbidType(Flags::PhantomZone);
+  }
+  if (!hasTeam) {
+    forbidType(Flags::Genocide);
+    forbidType(Flags::Colorblindness);
+    forbidType(Flags::Masquerade);
+  }
+  if ((options.gameStyle & TeamFlagGameStyle) == 0) {
+    forbidType(Flags::RedTeam);
+    forbidType(Flags::GreenTeam);
+    forbidType(Flags::BlueTeam);
+    forbidType(Flags::PurpleTeam);
+  }
+  if (options.maxTeam[RedTeam] <= 0) {
+    forbidType(Flags::RedTeam);
+  }
+  if (options.maxTeam[GreenTeam] <= 0) {
+    forbidType(Flags::GreenTeam);
+  }
+  if (options.maxTeam[BlueTeam] <= 0) {
+    forbidType(Flags::BlueTeam);
+  }
+  if (options.maxTeam[PurpleTeam] <= 0) {
+    forbidType(Flags::PurpleTeam);
+  }
+
+  return;
+}
+
+
+void FlagManager::removeTeam(std::set<const FlagType*>& fs) const
+{
+  std::set<const FlagType*>::iterator it = fs.begin();
+  while (it != fs.end()) {
+    std::set<const FlagType*>::iterator tmp_it = it;
+    it++;
+    const FlagType* ft = *tmp_it;
+    if (ft->flagTeam != ::NoTeam) {
+      fs.erase(tmp_it);
+    }
+  }
+  return;
+}
+
+
+void FlagManager::removeForbidden(std::set<const FlagType*>& fs) const
+{
+  std::set<const FlagType*>::iterator it = fs.begin();
+  while (it != fs.end()) {
+    std::set<const FlagType*>::iterator tmp_it = it;
+    it++;
+    if (!isAllowedType(*tmp_it)) {
+      fs.erase(tmp_it);
+    }
+  }
+  return;
+}
+
+
+void FlagManager::removeForbidden(FlagCountMap& fcm) const
+{
+  FlagCountMap::iterator it = fcm.begin();
+  while (it != fcm.end()) {
+    FlagCountMap::iterator tmp_it = it;
+    it++;
+    if (!isAllowedType(tmp_it->first)) {
+      fcm.erase(tmp_it);
+    }
+  }
+  return;
+}
+
+
+const FlagType* FlagManager::getFlagTypeFromLabel(const char* label)
+{
+  if (strcasecmp(label, "all") == 0) {
+    return allFlagType;
+  }  
+  else if (strcasecmp(label, "bad") == 0) {
+    return badFlagType;
+  }  
+  else if (strcasecmp(label, "good") == 0) {
+    return goodFlagType;
+  }  
+  else if (strcasecmp(label, "team") == 0) {
+    return teamFlagType;
+  }  
+  else {
+    return Flag::getDescFromAbbreviation(label);
+  }
+}
+
+
+void FlagManager::getFlagSet(const FlagType* ft,
+                             std::set<const FlagType*>& fs) const
+{
+  fs.clear();
+
+  if (ft == allFlagType) {
+    const FlagTypeMap& ftm = FlagType::getFlagMap();
+    FlagTypeMap::const_iterator it;
+    for (it = ftm.begin(); it != ftm.end(); it++) {
+      const FlagType* ft_tmp = it->second;
+      if (ft_tmp->flagTeam == ::NoTeam) {
+        if (isAllowedType(ft_tmp)) {
+          fs.insert(ft_tmp);
+        }
+      }
+    }
+  }
+  else if (ft == teamFlagType) {
+    const FlagTypeMap& ftm = FlagType::getFlagMap();
+    FlagTypeMap::const_iterator it;
+    for (it = ftm.begin(); it != ftm.end(); it++) {
+      const FlagType* ft_tmp = it->second;
+      if (ft_tmp->flagTeam != ::NoTeam) {
+        if (isAllowedType(ft_tmp)) {
+          fs.insert(ft_tmp);
+        }
+      }
+    }
+  }
+  else if (ft == badFlagType) {
+    const FlagSet& badFlags = Flag::getBadFlags();
+    FlagSet::const_iterator it;
+    for (it = badFlags.begin(); it != badFlags.end(); it++) {
+      const FlagType* ft_tmp = *it;
+      if (ft_tmp->flagTeam == ::NoTeam) {
+        if (isAllowedType(ft_tmp)) {
+          fs.insert(ft_tmp);
+        }
+      }
+    }
+  }
+  else if (ft == goodFlagType) {
+    const FlagSet& goodFlags = Flag::getGoodFlags();
+    FlagSet::const_iterator it;
+    for (it = goodFlags.begin(); it != goodFlags.end(); it++) {
+      const FlagType* ft_tmp = *it;
+      if (ft_tmp->flagTeam == ::NoTeam) {
+        if (isAllowedType(ft_tmp)) {
+          fs.insert(ft_tmp);
+        }
+      }
+    }
+  }
+  else {
+    fs.insert(ft);
+  }
+
+  return;
+}
+
+
+void FlagManager::setExtraFlags(int count)
+{
+  if (count < 0) {
+    count = 0;
+  }
+  extraCount = count;
+  return;
+}
+
+
+void FlagManager::setRandomDelayTimes(float min, float max)
+{
+  if (min < 0.0f) { min = 0.0f; }
+  if (max < min) { max = min; }
+  minRandomDelay = min;
+  maxRandomDelay = max;
+  return;
+}
+
+
+void FlagManager::setRandomMaximum(int count)
+{
+  if (count < 0) {
+    count = 0;
+  }
+  randomCount = count;
+  return;
+}
+
+
+bool FlagManager::adjustForced(const char* type, int count)
+{
+  const FlagType* ft = getFlagTypeFromLabel(type);
+  if (ft == Flags::Null) {
+    return false;
+  }
+  std::set<const FlagType*> fs;
+  std::set<const FlagType*>::const_iterator it;
+  getFlagSet(ft, fs);
+  for (it = fs.begin(); it != fs.end(); it++) {
+    addToFlagCountMap(*it, count, forcedFlags);
+  }
+  return true;
+}
+
+
+bool FlagManager::adjustRandom(const char* type, int count)
+{
+  const FlagType* ft = getFlagTypeFromLabel(type);
+  if (ft == Flags::Null) {
+    return false;
+  }
+  std::set<const FlagType*> fs;
+  std::set<const FlagType*>::const_iterator it;
+  getFlagSet(ft, fs);
+  for (it = fs.begin(); it != fs.end(); it++) {
+    addToFlagCountMap(*it, count, randomFlags);
+  }
+  return true;
+}
+
+
+void FlagManager::forbidType(const FlagType* ft)
+{
+  forbiddenFlags.insert(ft);
+  return;
+}
+
+
+bool FlagManager::forbidType(const char* type)
+{
+  const FlagType* ft = getFlagTypeFromLabel(type);
+  if (ft == Flags::Null) {
+    return false;
+  }
+  std::set<const FlagType*> fs;
+  std::set<const FlagType*>::const_iterator it;
+  getFlagSet(ft, fs);
+
+// FIXME - let 'em do it
+//  removeTeam(fs); 
+//  if (fs.size() <= 0) {
+//    return false;
+//  }
+
+  for (it = fs.begin(); it != fs.end(); it++) {
+    forbidType(ft);
+  }
+
+  return true;
+}
+
+
+bool FlagManager::isAllowedType(const FlagType* ft) const
+{
+  return (forbiddenFlags.find(ft) == forbiddenFlags.end());
+}
+
+
+const FlagType* FlagManager::pickRandomFlagType() const
+{
+  // tally the random flags on the field
+  FlagCountMap currentRandom;
+  for (int i = 0; i < fullCount; i++) {
+    FlagInfo& fi = flagList[i];
+    if (fi.isRandom() && fi.exists()) {
+      addToFlagCountMap(fi.flag.type, 1, currentRandom);
+    }
+  }
+
+  // make a vector of missing random flags
+  std::vector<const FlagType*> missingFlags;
+  FlagCountMap::const_iterator it;
+  for (it = randomFlags.begin(); it != randomFlags.end(); it++) {
+    const FlagType* ft = it->first;
+    const int currentCount = flagCountMapValue(ft, currentRandom);
+    const int missingCount = it->second - currentCount;
+    for (int n = 0; n < missingCount; n++) {
+      missingFlags.push_back(ft);
+    }
+  }
+
+  // something is wrong
+  if (missingFlags.size() <= 0) {
+    return Flags::Null;
+  }
+
+  return missingFlags[rand() % missingFlags.size()];
+}
+
+
+void FlagManager::setNoFlagInAir()
+{
+  numFlagsInAir = 0;
+  return;
+}
+
+
+float FlagManager::getNextDrop(TimeKeeper &tm) const
+{
   // find timeout when next flag would hit ground
   float waitTime = 3.0f;
   if (numFlagsInAir > 0) {
-    for (int i = 0; i < numFlags; i++) {
-      FlagInfo &flag = flagList[i];
-      if (flag.flag.status != FlagNoExist &&
-	  flag.flag.status != FlagOnTank &&
-	  flag.flag.status != FlagOnGround &&
-	  flag.dropDone - tm < waitTime)
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo& flag = flagList[i];
+      if ((flag.flag.status != FlagNoExist) &&
+	  (flag.flag.status != FlagOnTank) &&
+	  (flag.flag.status != FlagOnGround) &&
+	  ((flag.dropDone - tm) < waitTime)) {
 	waitTime = float(flag.dropDone - tm);
+      }
     }
   }
   return waitTime;
 }
 
-bool FlagInfo::landing(const TimeKeeper &tm)
+
+void FlagManager::addRandomFlags(const TimeKeeper& tm)
 {
-  if (numFlagsInAir <= 0)
+  if (randomCount <= 0) {
+    return;
+  }
+
+  if (maxRandomDelay <= 0.0f) {
+    // add all of the unassigned random flags
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo &fi = flagList[i];
+      if (fi.isRandom() && (fi.flag.type == Flags::Null)) {
+        // flag in now entering game
+        fi.addFlag();
+        sendFlagUpdate(fi);
+      }
+    }
+    nextRandomInsertion = tm;
+  }
+  else if (nextRandomInsertion <= tm) {
+    // drop a random flag if available
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo &fi = flagList[i];
+      if (fi.isRandom() && (fi.flag.type == Flags::Null)) {
+        // flag in now entering game
+        fi.addFlag();
+        sendFlagUpdate(fi);
+        break;
+      }
+    }
+    // set the next insertion time
+    float r = (float)bzfrand();
+    nextRandomInsertion = tm;
+    nextRandomInsertion += minRandomDelay;
+    nextRandomInsertion += (r * (maxRandomDelay - minRandomDelay));
+  }
+
+  return;
+}
+
+
+int FlagManager::lookupFirstTeamFlag(int teamindex) const
+{
+  for (int i = 0; i < fullCount; i++) {
+    if (flagList[i].flag.type->flagTeam == teamindex) {
+      return i;
+    }
+  }
+  return -1;
+}
+
+
+bool FlagManager::identifyFlag() const
+{
+  if (zoneIdentifyFlag) {
+    return true;
+  }
+  if (forcedFlags.find(Flags::Identify) != forcedFlags.end()) {
+    return true;
+  }
+  if (randomCount > 0) {
+    if (randomFlags.find(Flags::Identify) != randomFlags.end()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+
+/******************************************************************************/
+//
+// FlagManager::userXXX() members
+//
+
+static int compareAdd(const void* a, const void* b)
+{
+  const FlagInfo& fia = **((const FlagInfo**)a);
+  const FlagInfo& fib = **((const FlagInfo**)b);
+  if (fia.isExtra() && !fib.isExtra()) { return -1; }
+  if (!fia.isExtra() && fib.isExtra()) { return +1; }
+  return 0;
+}
+
+
+static int compareRemoveForced(const void* a, const void* b)
+{
+  const FlagInfo& fia = **((const FlagInfo**)a);
+  const FlagInfo& fib = **((const FlagInfo**)b);
+  if (!fia.isExtra() && fib.isExtra()) { return -1; }
+  if (fia.isExtra() && !fib.isExtra()) { return +1; }
+  if (!fia.isRandom() && fib.isRandom()) { return -1; }
+  if (fia.isRandom() && !fib.isRandom()) { return +1; }
+  const bool zoneA = (fia.getSourceZone() != NULL);
+  const bool zoneB = (fib.getSourceZone() != NULL);
+  if (!zoneA && zoneB) { return -1; }
+  if (zoneA && !zoneB) { return +1; }
+  const bool playerA = (fia.getPlayer() >= 0);
+  const bool playerB = (fib.getPlayer() >= 0);
+  if (!playerA && playerB) { return -1; }
+  if (playerA && !playerB) { return +1; }
+  return 0;
+}
+
+
+bool FlagManager::userAdjustForced(const char* type, int count, int playerIndex)
+{
+  const FlagType* ft = getFlagTypeFromLabel(type);
+  if (ft == Flags::Null) {
+    char buffer[MessageLen];
+    snprintf(buffer, MessageLen, "Invalid flag type: %s", type);
+    sendMessage(ServerPlayer, playerIndex, buffer);
     return false;
+  }
+  if (count == 0) {
+    return true;
+  }
 
-  bool land = false;
-  if (flag.status == FlagInAir || flag.status == FlagComing) {
-    if (dropDone - tm <= 0.0f) {
-      flag.status = FlagOnGround;
-      numFlagsInAir--;
-      land	= true;
+  std::set<const FlagType*> fs;
+  std::set<const FlagType*>::const_iterator it;
+  getFlagSet(ft, fs);
+  removeForbidden(fs);
+  if (fs.size() <= 0) {
+    char buffer[MessageLen];
+    snprintf(buffer, MessageLen, "Forbidden flag type: %s", type);
+    sendMessage(ServerPlayer, playerIndex, buffer);
+    return false;
+  }
+
+  const int guessCount = count * (int)fs.size();
+  if (guessCount > extraCount) {
+    char buf[MessageLen];
+    snprintf(buf, MessageLen, "Not enough extra slots (%i vs. %i)",
+             extraCount, guessCount);
+    sendMessage(ServerPlayer, playerIndex, buf);
+    return false;
+  }
+
+  if ((*fs.begin())->flagTeam != ::NoTeam) {
+    adjustTeamFlags(fs, count);
+    return true;
+  }
+
+  // setup the sorted list  
+  FlagInfo** sortList = new FlagInfo*[fullCount];
+  for (int i = 0; i < fullCount; i++) {
+    sortList[i] = &flagList[i];
+  }
+
+  if (count > 0) {
+    // add the new flags, and reset them
+    qsort(sortList, fullCount, sizeof(FlagInfo*), compareAdd);
+    for (it = fs.begin(); it != fs.end(); it++) {
+      const FlagType* ft_tmp = *it;
+      for (int n = 0; n < count; n++) {
+        int i;
+        for (i = 0; i < fullCount; i++) {
+          FlagInfo& fi = *sortList[i];
+          if (fi.isExtra()) {
+            extraCount--;
+            forcedCount++;
+            sendDrop(fi);
+            fi.setForcedFlag(ft_tmp);
+            addToFlagCountMap(ft_tmp, 1, forcedFlags);
+            resetFlag(fi);
+            break;
+          }
+        }
+        if (i == fullCount) {
+          break;
+        }
+      }
     }
-  } else if (flag.status == FlagGoing) {
-    if (dropDone - tm <= 0.0f) {
-      flag.status = FlagNoExist;
-      numFlagsInAir--;
-      land	= true;
+  }
+  else {
+    // remove the old flags, send out messages
+    qsort(sortList, fullCount, sizeof(FlagInfo*), compareRemoveForced);
+    for (it = fs.begin(); it != fs.end(); it++) {
+      const FlagType* ft_tmp = *it;
+      for (int n = 0; n < -count; n++) {
+        int i;
+        for (i = 0; i < fullCount; i++) {
+          FlagInfo& fi = *sortList[i];
+          if ((fi.getSourceZone() == NULL) &&
+              !fi.isExtra() && !fi.isRandom() &&
+              (fi.flag.type == ft_tmp)) {
+            extraCount++;
+            forcedCount--;
+            sendDrop(fi);
+            fi.setExtraFlag();
+            addToFlagCountMap(ft_tmp, -1, forcedFlags);
+            resetFlag(fi);
+            break;
+          }
+        }
+        if (i == fullCount) {
+          break;
+        }
+      }
     }
   }
-  return land;
+
+  delete[] sortList;
+
+  if (fs.size() == 1) {  
+    sendMessage(ServerPlayer, playerIndex, "adjusted flag count");
+  } else {
+    sendMessage(ServerPlayer, playerIndex, "adjusted flag counts");
+  }
+
+  return true;
 }
 
-void FlagInfo::setNoFlagInAir()
+/*
+static int compareZoneFlags(const void* a, const void* b)
 {
-  numFlagsInAir = 0;
+  const FlagInfo& fia = *((const FlagInfo*)a);
+  const FlagInfo& fib = *((const FlagInfo*)b);
+  const bool zoneA = (fia.getSourceZone() != NULL);
+  const bool zoneB = (fib.getSourceZone() != NULL);
+  if (!zoneA && zoneB) { return -1; }
+  if (zoneA && !zoneB) { return +1; }
+  return 0;
 }
+*/
 
-void FlagInfo::getTextualInfo(char *message)
+
+void FlagManager::adjustTeamFlags(std::set<const FlagType*>& fs, int count)
 {
-  sprintf(message, "#%-3d i:%-3s p:%-3d r:%-2d g:%-2d s:%-2d "
-		   "p:{%.1f, %.1f, %.1f}",
-	  flagIndex, flag.type->flagAbbv, player,
-	  required ? 1 : 0, grabs, flag.status,
-	  flag.position[0], flag.position[1], flag.position[2]);
+  int i;
+  int zoneTeamFlagCounts[5] = {0, 0, 0, 0, 0};
+
+  // setup the new forcedFlag counts
+  std::set<const FlagType*>::const_iterator fs_it;
+  for (fs_it = fs.begin(); fs_it != fs.end(); fs_it++) {
+    const FlagType* ft = *fs_it;
+    addToFlagCountMap(ft, count, forcedFlags);
+  }
+  flagCountMapClampMin(forcedFlags, 0);
+
+  // adjust extraCount and forcedCount
+  const int newForcedCount = flagCountMapSum(forcedFlags);
+  extraCount -= (newForcedCount - forcedCount);
+  forcedCount = newForcedCount;
+
+  // remove all player flags
+  for (i = 0; i < fullCount; i++) {
+    sendDrop(flagList[i]);
+  }
+
+  //
+  // shiny new list
+  ///////////////////
+  FlagInfo* newList = new FlagInfo[fullCount];
+
+  // add the team flags
+  int f = 0;
+  for (int t = RedTeam; t <= PurpleTeam; t++) {
+    const FlagType* ft = getTeamFlagType(t);
+    // copy the team zone flags
+    for (i = 0; i < fullCount; i++) {
+      FlagInfo& fi = flagList[i];
+      if ((fi.srcZone != NULL) && (fi.flag.type == ft)) {
+        zoneTeamFlagCounts[t]++;
+        newList[f] = flagList[i];
+        f++;
+      }
+    }
+    // fill the team flags
+    const int teamCount = flagCountMapValue(ft, forcedFlags);
+    for (i = 0; i < teamCount; i++) {
+      newList[f].setForcedFlag(ft);
+      f++;
+    }
+  }
+
+  // copy the normal zone flags
+  for (i = 0; i < fullCount; i++) {
+    FlagInfo& fi = flagList[i];
+    if ((fi.srcZone != NULL) && (fi.getTeamIndex() == ::NoTeam)) {
+      newList[f] = flagList[i];
+      f++;
+    }
+  }
+
+  // add normal flags
+  FlagCountMap::const_iterator fcm_it;
+  for (fcm_it = forcedFlags.begin(); fcm_it != forcedFlags.end(); fcm_it++) {
+    const FlagType *ft = fcm_it->first;
+    if (ft->flagTeam == ::NoTeam) {
+      const int flagCount = fcm_it->second;
+      for (int n = 0; n < flagCount; n++) {
+        newList[f].setForcedFlag(ft);
+        f++;
+      }
+    }
+  }
+
+  // add random flags
+  for (int rf = 0; rf < randomCount; rf++) {
+    newList[f].setRandomFlag();
+    f++;
+  }
+
+  // set the slot numbers
+  for (i = 0; i < fullCount; i++) {
+    newList[i].flagIndex = i;
+  }
+
+  // out with the old, in with the new
+  delete[] flagList;
+  flagList = newList;
+
+  // reset 'em
+  for (i = 0; i < fullCount; i++) {
+    resetFlag(flagList[i]);
+  }
+
+  return;
 }
 
-bool FlagInfo::exist()
+
+static int compareRemoveRandom(const void* a, const void* b)
 {
-  return flag.status != FlagNoExist;
+  const FlagInfo& fia = **((const FlagInfo**)a);
+  const FlagInfo& fib = **((const FlagInfo**)b);
+  if (!fia.exists() && fib.exists()) { return -1; }
+  if (fia.exists() && !fib.exists()) { return +1; }
+  if (!fia.isExtra() && fib.isExtra()) { return -1; }
+  if (fia.isExtra() && !fib.isExtra()) { return +1; }
+  if (fia.isRandom() && !fib.isRandom()) { return -1; }
+  if (!fia.isRandom() && fib.isRandom()) { return +1; }
+  const bool zoneA = (fia.getSourceZone() != NULL);
+  const bool zoneB = (fib.getSourceZone() != NULL);
+  if (!zoneA && zoneB) { return -1; }
+  if (zoneA && !zoneB) { return +1; }
+  const bool playerA = (fia.getPlayer() >= 0);
+  const bool playerB = (fib.getPlayer() >= 0);
+  if (!playerA && playerB) { return -1; }
+  if (playerA && !playerB) { return +1; }
+  return 0;
 }
 
-FlagInfo *FlagInfo::get(int index)
+
+bool FlagManager::userAdjustRandom(const char* type, int count, int playerIndex)
 {
-  if (index < 0)
-    return NULL;
-  if (index >= numFlags)
-    return NULL;
-  return &flagList[index];
+  const FlagType* ft = getFlagTypeFromLabel(type);
+  if (ft == Flags::Null) {
+    char buffer[MessageLen];
+    snprintf(buffer, MessageLen, "Invalid flag type: %s", type);
+    sendMessage(ServerPlayer, playerIndex, buffer);
+    return false;
+  }
+
+  if (count == 0) {
+    return true;
+  }
+
+  std::set<const FlagType*> fs;
+  std::set<const FlagType*>::const_iterator it;
+  getFlagSet(ft, fs);
+  removeTeam(fs); // no random team flags
+  removeForbidden(fs);
+  if (fs.size() <= 0) {
+    char buffer[MessageLen];
+    snprintf(buffer, MessageLen, "Forbidden flag type: %s", type);
+    sendMessage(ServerPlayer, playerIndex, buffer);
+    return false;
+  }
+
+  for (it = fs.begin(); it != fs.end(); it++) {
+    addToFlagCountMap(*it, count, randomFlags);
+  }
+
+  flagCountMapClampMin(randomFlags, 0);
+
+  // tally the random flags on the field
+  FlagCountMap currentRandom;
+  for (int i = 0; i < fullCount; i++) {
+    FlagInfo& fi = flagList[i];
+    if (fi.isRandom() && fi.exists()) {
+      addToFlagCountMap(fi.flag.type, 1, currentRandom);
+    }
+  }
+
+  // make a map of excess random flags
+  FlagCountMap excessFlags;
+  FlagCountMap::const_iterator fcm_it;
+  for (fcm_it = currentRandom.begin(); fcm_it != currentRandom.end(); fcm_it++) {
+    const FlagType* ft_tmp = fcm_it->first;
+    const int currentCount = flagCountMapValue(ft_tmp, currentRandom);
+    const int maximumCount = flagCountMapValue(ft_tmp, randomFlags);
+    const int excess = currentCount - maximumCount;
+    if (excess > 0) {
+      excessFlags[ft_tmp] = excess;
+    }
+  }
+
+  if (excessFlags.size() <= 0) {
+    return true; // no excess flags
+  }
+
+  // setup the sorted list  
+  FlagInfo** sortList = new FlagInfo*[fullCount];
+  for (int j = 0; j < fullCount; j++) {
+    sortList[j] = &flagList[j];
+  }
+
+  // reset any excess flags, randomFlags has already been adjusted
+  qsort(sortList, fullCount, sizeof(FlagInfo*), compareRemoveRandom);
+  for (fcm_it = excessFlags.begin(); fcm_it != excessFlags.end(); fcm_it++) {
+    const FlagType* ft_tmp = fcm_it->first;
+    int excess = fcm_it->second;
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo& fi = *sortList[i];
+      if ((fi.getSourceZone() == NULL) &&
+          !fi.isExtra() && fi.isRandom() &&
+          (fi.flag.type == ft_tmp)) {
+        sendDrop(fi);
+        resetFlag(fi);
+        excess--;
+        if (excess <= 0) {
+          break;
+        }
+      }
+    }
+  }
+
+  delete[] sortList;
+
+  sendMessage(ServerPlayer, playerIndex, "adjusted flag pool count");
+
+  return true;
 }
 
+
+bool FlagManager::userSetRandomCount(int count, int playerIndex)
+{
+  if (count < 0) {
+    count = 0;
+  }
+  const int diff = (count - randomCount);
+  if (diff == 0) {
+    return true;
+  }
+
+  if (diff > extraCount) {
+    char buf[MessageLen];
+    snprintf(buf, MessageLen, "Not enough extra slots (%i vs. %i)",
+             extraCount, count);
+    sendMessage(ServerPlayer, playerIndex, buf);
+    return false;
+  }
+
+  // setup the sorted list  
+  FlagInfo** sortList = new FlagInfo*[fullCount];
+  for (int i = 0; i < fullCount; i++) {
+    sortList[i] = &flagList[i];
+  }
+
+  if (diff > 0) {
+    // convert extra slots into random slots
+    qsort(sortList, fullCount, sizeof(FlagInfo*), compareAdd);
+    int changed = 0;
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo& fi = *sortList[i];
+      if (fi.isExtra()) {
+        extraCount--;
+        randomCount++;
+        sendDrop(fi);
+        fi.setRandomFlag();
+        resetFlag(fi);
+        changed++;
+        if (changed >= diff) {
+          break;
+        }
+      }
+    }
+  }
+  else {
+    // convert random slots into extra slots
+    qsort(sortList, fullCount, sizeof(FlagInfo*), compareRemoveRandom);
+    int changed = 0;
+    for (int i = 0; i < fullCount; i++) {
+      FlagInfo& fi = *sortList[i];
+      if (fi.isRandom()) {
+        extraCount++;
+        randomCount--;
+        changed++;
+        sendDrop(fi);
+        fi.setExtraFlag();
+        resetFlag(fi);
+        if (changed >= -diff) {
+          break;
+        }
+      }
+    }
+  }
+
+  delete[] sortList;
+
+  sendMessage(ServerPlayer, playerIndex, "set random flag maximum");
+
+  return true;
+}
+
+
+/******************************************************************************/
+//
+// FlagManager::sendFlagInfo() and friends
+//
+
+typedef std::pair<const FlagType*, int> FlagCountPair;
+
+
+
+void FlagManager::sendFlagInfo(int playerIndex) const
+{
+  std::vector<std::string> text;
+  getFlagList(text);
+  for (int i = 0; i < (int)text.size(); i++) {
+    sendMessage(ServerPlayer, playerIndex, text[i].c_str());
+  }
+  return;
+}
+
+
+static bool compareFlagType(const FlagCountPair& a, const FlagCountPair& b)
+{
+  const FlagType* fta = a.first;
+  const FlagType* ftb = b.first;
+  const bool isGoodA = (fta->flagQuality == FlagGood);
+  const bool isGoodB = (ftb->flagQuality == FlagGood);
+  if (isGoodA && !isGoodB) {
+    return true;
+  }
+  if (!isGoodA && isGoodB) {
+    return false;
+  }
+  return (strcmp(fta->flagAbbv, ftb->flagAbbv) < 0);
+}
+
+
+static void appendFlagCounts(const FlagCountMap& fcm,
+                             std::vector<std::string>& text)
+{
+  const int maxColumn = 8;
+  char buf[MessageLen];
+  char* c = buf;
+
+  FlagCountMap::const_iterator fcm_it;
+  std::vector<FlagCountPair> sortList;
+  for (fcm_it = fcm.begin(); fcm_it != fcm.end(); fcm_it++) {
+    sortList.push_back(FlagCountPair(fcm_it->first, fcm_it->second));
+  }
+  std::sort(sortList.begin(), sortList.end(), compareFlagType);
+
+  int column = 0;
+  std::vector<FlagCountPair>::const_iterator sort_it = sortList.begin();
+  while (sort_it != sortList.end()) {
+    const FlagType* ft = sort_it->first;
+    const int count = sort_it->second;
+    sort_it++; // increment here to check for sortList.end() before looping
+    if (count > 0) {
+      column++;
+      const int maxSize = (MessageLen - (c - buf));
+      c += snprintf(c, maxSize, "  %2s/%-3i", ft->flagAbbv, count);
+    }
+    if ((sort_it == sortList.end()) || (column >= maxColumn)) {
+      text.push_back(buf);
+      c = buf;
+      column = 0;
+    }
+  }
+  return;
+}    
+
+
+static void appendTeamFlagCounts(const FlagCountMap& fcm,
+                                 std::vector<std::string>& text)
+{
+  char buf[MessageLen];
+  char* c = buf;
+  for (int t = RedTeam; t <= PurpleTeam; t++) {
+    const FlagType* ft = FLAGMGR.getTeamFlagType(t);
+    FlagCountMap::const_iterator fcm_it = fcm.find(ft);
+    int count = 0;
+    if (fcm_it != fcm.end()) {
+      count = fcm_it->second;
+    }
+    const int maxSize = (MessageLen - (c - buf));
+    c += snprintf(c, maxSize, "  %2s/%-3i", ft->flagAbbv, count);
+  }
+  text.push_back(buf);
+  return;
+}
+
+
+void FlagManager::getFlagList(std::vector<std::string>& text) const
+{
+  text.clear();
+
+  // tally the flags
+  int teamTally = 0;
+  int zoneTeamTally = 0;
+  int zoneTally = 0;
+  int forcedTally = 0;
+  int randomTally = 0;
+  FlagCountMap teamTallyList;
+  FlagCountMap zoneTeamTallyList;
+  FlagCountMap zoneTallyList;
+  FlagCountMap forcedTallyList;
+  FlagCountMap randomTallyList;
+
+  for (int i = 0; i < fullCount; i++) {
+    const FlagInfo& fi = flagList[i];
+    const FlagType* ft = fi.flag.type;
+    if (fi.isExtra()) {
+      continue;
+    }
+    else if (fi.srcZone != NULL) {
+      if (ft->flagTeam != ::NoTeam) {
+        zoneTeamTally++;
+        addToFlagCountMap(ft, 1, zoneTeamTallyList);
+      } else {
+        zoneTally++;
+        addToFlagCountMap(ft, 1, zoneTallyList);
+      }
+    }
+    else {
+      if (ft->flagTeam != ::NoTeam) {
+        teamTally++;
+        addToFlagCountMap(ft, 1, teamTallyList);
+      }
+      else {
+        if (fi.isRandom()) {
+          if (fi.flag.type != Flags::Null) {
+            randomTally++;
+            addToFlagCountMap(ft, 1, randomTallyList);
+          }
+        } else {
+          forcedTally++;
+          addToFlagCountMap(ft, 1, forcedTallyList);
+        }
+      }
+    }
+  }
+
+  // append the info
+  char buf[MessageLen];
+
+  snprintf(buf, MessageLen, "Team(%i)", teamTally);
+  text.push_back(buf);
+  appendTeamFlagCounts(teamTallyList, text);
+
+  snprintf(buf, MessageLen, "TeamZone(%i)", zoneTeamTally);
+  text.push_back(buf);
+  appendTeamFlagCounts(zoneTeamTallyList, text);
+
+  snprintf(buf, MessageLen, "Zone(%i)", zoneTally);
+  text.push_back(buf);
+  appendFlagCounts(zoneTallyList, text);
+
+  snprintf(buf, MessageLen, "Forced(%i)", forcedTally);
+  text.push_back(buf);
+  appendFlagCounts(forcedTallyList, text);
+
+  snprintf(buf, MessageLen, "Random(%i)", randomTally);
+  text.push_back(buf);
+  appendFlagCounts(randomTallyList, text);
+
+  snprintf(buf, MessageLen, "RandomPool(%i)", randomCount);
+  text.push_back(buf);
+  appendFlagCounts(randomFlags, text);
+
+  snprintf(buf, MessageLen, "Extra Flags(%i)", extraCount);
+  text.push_back(buf);
+
+  return;
+}
+
+
+/******************************************************************************/
+//
+// Utilities
+//
+
+static int flagCountMapSum(FlagCountMap& fcm)
+{
+  int tally = 0;
+  FlagCountMap::iterator it;
+  for (it = fcm.begin(); it != fcm.end(); it++) {
+    tally += it->second;
+  }
+  return tally;
+}
+
+
+static void flagCountMapClampMin(FlagCountMap& fcm, int min)
+{
+  FlagCountMap::iterator it = fcm.begin();
+  while (it != fcm.end()) {
+    FlagCountMap::iterator tmp_it = it;
+    it++;
+    if (tmp_it->second <= min) {
+      tmp_it->second = min;
+      if (min == 0) {
+        fcm.erase(tmp_it);
+      }
+    }
+  }
+  return;
+}
+
+
+static int flagCountMapValue(const FlagType* ft, const FlagCountMap& fcm)
+{
+  FlagCountMap::const_iterator it = fcm.find(ft);
+  if (it != fcm.end()) {
+    return it->second;
+  }
+  return 0;
+}
+
+
+static void addToFlagCountMap(const FlagType* ft, int count,
+                              FlagCountMap& fcm)
+{
+  FlagCountMap::iterator it = fcm.find(ft);
+  if (it != fcm.end()) {
+    it->second += count;
+    if (it->second == 0) {
+      fcm.erase(it);
+    }
+  } else {
+    fcm[ft] = count;
+  }
+  return;
+}                              
+
+
 // Local Variables: ***
 // mode:C++ ***
 // tab-width: 8 ***
Index: CmdLineOptions.cxx
===================================================================
--- CmdLineOptions.cxx	(revision 20911)
+++ CmdLineOptions.cxx	(working copy)
@@ -68,8 +68,13 @@
 "[-d] "
 "[-density <num>] "
 "[-disableBots] "
-"[+f {good|<id>}] "
-"[-f {bad|<id>}] "
+//FIXME - DEPRECATED "[+f {good|<id>}] "
+//FIXME - DEPRECATED "[-f {bad|<id>}] "
+"[-fe <count>]"
+"[-ff <good|bad|team|<id>>[/count]]"
+"[-fr <good|bad|<id>>[/count]]"
+"[-frmax <count>]"
+"[-frtimes <min> <max>]"
 "[-fb] "
 "[-filterCallsigns] "
 "[-filterChat] "
@@ -119,8 +124,8 @@
 "[-reportfile <filename>] "
 "[-reportpipe <filename>] "
 "[-requireudp] "
-"[+s <flag-count>] "
-"[-s <flag-count>] "
+//FIXME - DEPRECATED "[+s <flag-count>] "
+//FIXME - DEPRECATED "[-s <flag-count>] "
 "[-sa] "
 "[-sb] "
 "[-set <name> <value>] "
@@ -170,9 +175,14 @@
 "\t-d: increase debugging level\n"
 "\t-density: specify building density for random worlds (default=5)\n"
 "\t-disableBots: disallow clients from using autopilot or robots\n"
-"\t+f: always have flag <id> available\n"
-"\t-f: never randomly generate flag <id>\n"
+//FIXME - DEPRECATED "\t+f: always have flag <id> available\n"
+//FIXME - DEPRECATED "\t-f: never randomly generate flag <id>\n"
 "\t-fb: allow flags on box buildings\n"
+"\t-fe: add extra flag slots\n"
+"\t-ff: add/delete forced flags\n"
+"\t-fr: add/delete random flags\n"
+"\t-frmax: set the maximum number of random flags on the field\n"
+"\t-frtimes: set the minimum and maximum random flag insertion delay times\n"
 "\t-filterCallsigns: filter callsigns to disallow inappropriate user names\n"
 "\t-filterChat: filter chat messages\n"
 "\t-filterSimple: perform simple exact matches with the bad word list\n"
@@ -222,8 +232,8 @@
 "\t-reportfile <filename>: the file to store reports in\n"
 "\t-reportpipe <filename>: the program to pipe reports through\n"
 "\t-requireudp: require clients to use udp\n"
-"\t+s: always have <num> super flags (default=16)\n"
-"\t-s: allow up to <num> super flags (default=16)\n"
+//FIXME - DEPRECATED "\t+s: always have <num> super flags (default=16)\n"
+//FIXME - DEPRECATED "\t-s: allow up to <num> super flags (default=16)\n"
 "\t-sa: insert antidote superflags\n"
 "\t-sb: allow tanks to respawn on buildings\n"
 "\t-set <name> <value>: set a BZDB variable's value\n"
@@ -545,7 +555,6 @@
 
 
 static bool accLimitSet = false;
-static bool allFlagsOut = false;
 
 void parse(int argc, char **argv, CmdLineOptions &options, bool fromWorldFile)
 {
@@ -664,59 +673,75 @@
       }
       debugLevel += count;
       // std::cout << "Debug level is now " << debugLevel << "" << std::endl;
+    } else if (strcmp(argv[i], "-fe") == 0) {
+      // extra flags
+      checkArgc(1, i, argc, argv[i]);
+      FLAGMGR.setExtraFlags(atoi(argv[i]));
+    } else if (strcmp(argv[i], "-ff") == 0) {
+      // forced flags
+      checkArgc(1, i, argc, argv[i]);
+      int count = 1;
+      char* slash = strchr(argv[i], '/');
+      if (slash != NULL) {
+        slash[0] = '\0'; // terminate
+        count = atoi(slash + 1);
+      }
+      if (!FLAGMGR.adjustForced(argv[i], count)) {
+        std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
+	usage(argv[0]);
+      }
+    } else if (strcmp(argv[i], "-fr") == 0) {
+      // random flags
+      checkArgc(1, i, argc, argv[i]);
+      int count = 1;
+      char* slash = strchr(argv[i], '/');
+      if (slash != NULL) {
+        slash[0] = '\0'; // terminate
+        count = atoi(slash + 1);
+      }
+      if (!FLAGMGR.adjustRandom(argv[i], count)) {
+        std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
+	usage(argv[0]);
+      }
+    } else if (strcmp(argv[i], "-frmax") == 0) {
+      // random flag minimum
+      checkArgc(1, i, argc, argv[i]);
+      FLAGMGR.setRandomMaximum(atoi(argv[i]));
+    } else if (strcmp(argv[i], "-frtimes") == 0) {
+      // random flag minimum
+      checkArgc(2, i, argc, argv[i]);
+      const float min = atof(argv[i]);
+      i++;
+      const float max = atof(argv[i]);
+      FLAGMGR.setRandomDelayTimes(min, max);
+/*
     } else if (strcmp(argv[i], "-f") == 0) {
-      // disallow given flag
+      // FIXME: DEPRECATED switch
       checkArgc(1, i, argc, argv[i]);
-      if (strcmp(argv[i], "bad") == 0) {
-	FlagSet badFlags = Flag::getBadFlags();
-	for (FlagSet::iterator it = badFlags.begin(); it != badFlags.end(); it++)
-	  options.flagDisallowed[*it] = true;
-      } else if (strcmp(argv[i], "good") == 0) {
-	FlagSet goodFlags = Flag::getGoodFlags();
-	for (FlagSet::iterator it = goodFlags.begin(); it != goodFlags.end(); it++)
-	  options.flagDisallowed[*it] = true;
-      } else {
-	FlagType* fDesc = Flag::getDescFromAbbreviation(argv[i]);
-	if (fDesc == Flags::Null) {
-	  std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
-	  usage(argv[0]);
-	}
-	options.flagDisallowed[fDesc] = true;
+      if (!FLAGMGR.forbidType(argv[i])) {
+        std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
+	usage(argv[0]);
       }
+*/
+/*
     } else if (strcmp(argv[i], "+f") == 0) {
-      // add required flag
+      // FIXME: DEPRECATED switch
       checkArgc(1, i, argc, argv[i]);
+      int count = 1;
 
       char *repeatStr = strchr(argv[i], '{');
-      int rptCnt = 1;
       if (repeatStr != NULL) {
 	*(repeatStr++) = 0;
-	rptCnt = atoi(repeatStr);
-	if (rptCnt <= 0)
-	  rptCnt = 1;
+	count = atoi(repeatStr);
+	if (count <= 0) {
+	  count = 1;
+        }
       }
-      if (strcmp(argv[i], "good") == 0) {
-	FlagSet goodFlags = Flag::getGoodFlags();
-	for (FlagSet::iterator it = goodFlags.begin(); it != goodFlags.end(); it++)
-	  options.flagCount[*it] += rptCnt;
-      } else if (strcmp(argv[i], "bad") == 0) {
-	FlagSet badFlags = Flag::getBadFlags();
-	for (FlagSet::iterator it = badFlags.begin(); it != badFlags.end(); it++)
-	  options.flagCount[*it] += rptCnt;
-      } else if (strcmp(argv[i], "team") == 0) {
-	for (int t = RedTeam; t <= PurpleTeam; t++)
-	  options.numTeamFlags[t] += rptCnt;
-      } else {
-	FlagType *fDesc = Flag::getDescFromAbbreviation(argv[i]);
-	if (fDesc == Flags::Null) {
-	  std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
-	  usage(argv[0]);
-	} else if (fDesc->flagTeam != NoTeam) {
-	  options.numTeamFlags[fDesc->flagTeam] += rptCnt;
-	} else {
-	  options.flagCount[fDesc] += rptCnt;
-	}
+      if (!FLAGMGR.adjustForced(argv[i], count)) {
+        std::cerr << "invalid flag \"" << argv[i] << "\"" << std::endl;
+	usage(argv[0]);
       }
+*/
     } else if (strcmp(argv[i], "-fb") == 0) {
       // flags on buildings
       options.flagsOnBuildings = true;
@@ -990,17 +1015,28 @@
     } else if (strcmp(argv[i], "-requireudp") == 0) {
       std::cerr << "require UDP clients!" << std::endl;
       options.requireUDP = true;
-    } else if (strcmp(argv[i], "+s") == 0 || strcmp(argv[i], "-s") == 0) {
-      // with +s all flags are required to exist all the time
-      allFlagsOut = argv[i][0] == '+' ? true : false;
+/*
+    } else if ((strcmp(argv[i], "-s") == 0) ||
+               (strcmp(argv[i], "+s") == 0)) {
+      // FIXME: DEPRECATED switches
+      int count = 16;
+      bool allOut = (argv[i][0] == '+');
       // set number of random flags
-      if (i+1 < argc && isdigit(argv[i+1][0])) {
-	++i;
-	if ((options.numExtraFlags = atoi(argv[i])) == 0)
-	  options.numExtraFlags = 16;
-      } else {
-	options.numExtraFlags = 16;
+      if (((i + 1) < argc) && isdigit(argv[i+1][0])) {
+	i++;
+	int tmp = atoi(argv[i]);
+	if (tmp != 0) {
+	  count = tmp;
+        }
       }
+      const int flagTypes = FlagType::getFlagMap().size();
+      FLAGMGR.clearRandomPool();
+      FLAGMGR.adjustRandom("all", 2 + (count / flagTypes));
+      FLAGMGR.setRandomMaximum(count);
+      if (allOut) {
+        FLAGMGR.setRandomDelayTimes(0.0f, 0.0f);
+      }
+*/
     } else if (strcmp(argv[i], "-sa") == 0) {
       // insert antidote flags
       options.gameStyle |= int(AntidoteGameStyle);
@@ -1026,6 +1062,7 @@
       i++;
       value = argv[i];
       BZDB.set(name, value);
+      BZDB.addCallback(name, onGlobalChanged, (void*)NULL);
       logDebugMessage(1,"set variable: %s = %s\n", name, BZDB.get(name).c_str());
     } else if (strcmp(argv[i], "-sl") == 0) {
       // add required flag
@@ -1201,68 +1238,21 @@
 }
 
 
-static int getZoneTeamFlagCount(FlagType* flagType, EntryZones& entryZones,
-				 const std::set<FlagType*>& forbidden)
+void finalizeParsing(int /*argc*/, char **argv, CmdLineOptions &options)
 {
-  if (forbidden.find(flagType) != forbidden.end()) {
-    return 0;
-  }
-  // tally zone team flags
-  int count = 0;
-  const ZoneList& zl = entryZones.getZoneList();
-  for (int z = 0; z < (int)zl.size(); z++) {
-    const ZoneFlagMap& zfm = zl[z].getZoneFlagMap();
-    ZoneFlagMap::const_iterator zfmIt;
-    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
-      if (zfmIt->first == flagType) {
-	count += zfmIt->second;
-      }
-    }
-  }
-  return count;
-}
-
-
-static int addZoneTeamFlags(int startIndex,
-			    FlagType* flagType, EntryZones& entryZones,
-			    const std::set<FlagType*>& forbidden)
-{
-  if (forbidden.find(flagType) != forbidden.end()) {
-    return startIndex;
-  }
-  // add zone team flags
-  const ZoneList& zl = entryZones.getZoneList();
-  for (int z = 0; z < (int)zl.size(); z++) {
-    const ZoneFlagMap& zfm = zl[z].getZoneFlagMap();
-    ZoneFlagMap::const_iterator zfmIt;
-    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
-      if (zfmIt->first == flagType) {
-	const int count = zfmIt->second;
-	for (int c = 0; c < count; c++) {
-	  entryZones.addZoneFlag(z, startIndex);
-	  FlagInfo::get(startIndex++)->setRequiredFlag(flagType);
-	}
-      }
-    }
-  }
-  return startIndex;
-}
-
-
-void finalizeParsing(int /*argc*/, char **argv,
-		     CmdLineOptions &options, EntryZones& entryZones)
-{
   if (options.flagsOnBuildings && !(options.gameStyle & JumpingGameStyle)) {
     std::cerr << "flags on boxes requires jumping" << std::endl;
     usage(argv[0]);
   }
 
+  // sanitize the team counts for rabbit modes
   if (options.gameStyle & int(RabbitChaseGameStyle)) {
     for (int j = RedTeam; j <= PurpleTeam; j++) {
-      if (options.maxTeam[j] > 0
-	  && options.maxTeam[RogueTeam] != maxRealPlayers)
+      if ((options.maxTeam[j] > 0) &&
+          (options.maxTeam[RogueTeam] != maxRealPlayers)) {
 	std::cout << "only rogues are allowed in Rabbit Style; zeroing out "
 		  << Team::getName((TeamColor) j) << std::endl;
+      }
       options.maxTeam[j] = 0;
     }
   }
@@ -1278,218 +1268,6 @@
 	   "affecting any players, including those using \"mouse enhancements\".\n");
   }
 
-
-  // do we have any team flags?
-  bool hasTeam = false;
-  for (int p = RedTeam; p <= PurpleTeam; p++) {
-    if (options.maxTeam[p] > 1) {
-      hasTeam = true;
-      break;
-    }
-  }
-
-  std::set<FlagType*> forbidden;
-  forbidden.insert(Flags::Null);
-
-  // first disallow flags inconsistent with game style
-  if (options.gameStyle & JumpingGameStyle) {
-    forbidden.insert(Flags::Jumping);
-  } else {
-    forbidden.insert(Flags::NoJumping);
-  }
-  if (options.gameStyle & RicochetGameStyle) {
-    forbidden.insert(Flags::Ricochet);
-  }
-  if (!options.useTeleporters && (options.worldFile == "")) {
-    forbidden.insert(Flags::PhantomZone);
-  }
-  if (!hasTeam) {
-    forbidden.insert(Flags::Genocide);
-    forbidden.insert(Flags::Colorblindness);
-    forbidden.insert(Flags::Masquerade);
-  }
-  if ((options.gameStyle & TeamFlagGameStyle) == 0) {
-    forbidden.insert(Flags::RedTeam);
-    forbidden.insert(Flags::GreenTeam);
-    forbidden.insert(Flags::BlueTeam);
-    forbidden.insert(Flags::PurpleTeam);
-  }
-  if (options.maxTeam[RedTeam] <= 0) {
-    forbidden.insert(Flags::RedTeam);
-  }
-  if (options.maxTeam[GreenTeam] <= 0) {
-    forbidden.insert(Flags::GreenTeam);
-  }
-  if (options.maxTeam[BlueTeam] <= 0) {
-    forbidden.insert(Flags::BlueTeam);
-  }
-  if (options.maxTeam[PurpleTeam] <= 0) {
-    forbidden.insert(Flags::PurpleTeam);
-  }
-
-  // void the forbidden flags
-  std::set<FlagType*>::const_iterator sit;
-  for (sit = forbidden.begin(); sit != forbidden.end(); sit++) {
-    FlagType* ft = *sit;
-    options.flagCount[ft] = 0;
-    options.flagDisallowed[ft] = true;
-  }
-
-  // zone team flag counts
-  const int zoneTeamFlagCounts[5] = {
-    0, // rogue
-    getZoneTeamFlagCount(Flags::RedTeam, entryZones, forbidden),
-    getZoneTeamFlagCount(Flags::GreenTeam, entryZones, forbidden),
-    getZoneTeamFlagCount(Flags::BlueTeam, entryZones, forbidden),
-    getZoneTeamFlagCount(Flags::PurpleTeam, entryZones, forbidden)
-  };
-
-  // make sure there is at least one team flag for each active team
-  if (options.gameStyle & TeamFlagGameStyle) {
-    for (int col = RedTeam; col <= PurpleTeam; col++) {
-      if ((options.maxTeam[col] > 0) &&
-	  (options.numTeamFlags[col] <= 0) &&
-	  (zoneTeamFlagCounts[col] <= 0)) {
-	options.numTeamFlags[col] = 1;
-      }
-    }
-  }
-
-  // make table of allowed extra flags
-  if (options.numExtraFlags > 0) {
-    // now count how many aren't disallowed
-    for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
-	 it != FlagType::getFlagMap().end(); ++it) {
-      if (!options.flagDisallowed[it->second]) {
-	options.numAllowedFlags++;
-      }
-    }
-    // if none allowed then no extra flags either
-    if (options.numAllowedFlags == 0) {
-      options.numExtraFlags = 0;
-    } else {
-      // types of extra flags allowed
-      std::vector<FlagType*> allowedFlags;
-      allowedFlags.clear();
-      for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
-	   it != FlagType::getFlagMap().end(); ++it) {
-	FlagType *fDesc = it->second;
-	if ((fDesc == Flags::Null) || (fDesc->flagTeam != ::NoTeam))
-	  continue;
-	if (!options.flagDisallowed[it->second])
-	  allowedFlags.push_back(it->second);
-      }
-      // Loading allowedFlags vector
-      FlagInfo::setAllowed(allowedFlags);
-    }
-  }
-
-  const ZoneList& zl = entryZones.getZoneList();
-
-  // allocate space for extra flags
-  numFlags = options.numExtraFlags;
-  // allocate space for team flags
-  if (options.gameStyle & TeamFlagGameStyle) {
-    for (int col = RedTeam; col <= PurpleTeam; col++) {
-      if (options.maxTeam[col] > 0) {
-	numFlags += options.numTeamFlags[col];
-      }
-    }
-  }
-  // allocate space for normal flags
-  for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
-       it != FlagType::getFlagMap().end(); ++it) {
-    numFlags += options.flagCount[it->second];
-  }
-  // allocate space for zone flags (including teams flags)
-  for (int z = 0; z < (int)zl.size(); z++) {
-    const CustomZone& zone = zl[z];
-    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
-    ZoneFlagMap::const_iterator zfmIt;
-    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
-      if (forbidden.find(zfmIt->first) == forbidden.end()) {
-	numFlags += zfmIt->second;
-      }
-    }
-  }
-
-  FlagInfo::setSize(numFlags);
-
-  // add team flags (ordered)
-  int f = 0;
-  if (options.gameStyle & TeamFlagGameStyle) {
-    if (options.maxTeam[RedTeam] > 0) {
-      f = addZoneTeamFlags(f, Flags::RedTeam, entryZones, forbidden);
-      for (int n = 0; n < options.numTeamFlags[RedTeam]; n++) {
-	FlagInfo::get(f++)->setRequiredFlag(Flags::RedTeam);
-      }
-    }
-    if (options.maxTeam[GreenTeam] > 0) {
-      f = addZoneTeamFlags(f, Flags::GreenTeam, entryZones, forbidden);
-      for (int n = 0; n < options.numTeamFlags[GreenTeam]; n++) {
-	FlagInfo::get(f++)->setRequiredFlag(Flags::GreenTeam);
-      }
-    }
-    if (options.maxTeam[BlueTeam] > 0) {
-      f = addZoneTeamFlags(f, Flags::BlueTeam, entryZones, forbidden);
-      for (int n = 0; n < options.numTeamFlags[BlueTeam]; n++) {
-	FlagInfo::get(f++)->setRequiredFlag(Flags::BlueTeam);
-      }
-    }
-    if (options.maxTeam[PurpleTeam] > 0) {
-      f = addZoneTeamFlags(f, Flags::PurpleTeam, entryZones, forbidden);
-      for (int n = 0; n < options.numTeamFlags[PurpleTeam]; n++) {
-	FlagInfo::get(f++)->setRequiredFlag(Flags::PurpleTeam);
-      }
-    }
-  }
-
-  // super flags?
-  if (f < numFlags) {
-    options.gameStyle |= int(SuperFlagGameStyle);
-  }
-
-  // add normal flags
-  for (FlagTypeMap::iterator it2 = FlagType::getFlagMap().begin();
-       it2 != FlagType::getFlagMap().end(); ++it2) {
-    FlagType *fDesc = it2->second;
-
-    if ((fDesc != Flags::Null) && (fDesc->flagTeam == NoTeam)) {
-      for (int j = 0; j < options.flagCount[fDesc]; j++) {
-	FlagInfo::get(f++)->setRequiredFlag(fDesc);
-      }
-    }
-  }
-  // add zone flags
-  for (int z = 0; z < (int)zl.size(); z++) {
-    const CustomZone& zone = zl[z];
-    const ZoneFlagMap& zfm = zone.getZoneFlagMap();
-    ZoneFlagMap::const_iterator zfmIt;
-    for (zfmIt = zfm.begin(); zfmIt != zfm.end(); zfmIt++) {
-      FlagType* ft = zfmIt->first;
-      if ((ft->flagTeam == ::NoTeam) && // no team flags here
-	  (forbidden.find(ft) == forbidden.end())) {
-	const int count = zfmIt->second;
-	for (int c = 0; c < count; c++) {
-	  entryZones.addZoneFlag(z, f);
-	  FlagInfo::get(f++)->setRequiredFlag(ft);
-	}
-      }
-    }
-  }
-  // add extra flags
-  for (; f < numFlags; f++) {
-    FlagInfo::get(f)->required = allFlagsOut;
-  }
-
-  // sum the sources of team flags
-  if (options.gameStyle & TeamFlagGameStyle) {
-    for (int col = RedTeam; col <= PurpleTeam; col++) {
-      options.numTeamFlags[col] += zoneTeamFlagCounts[col];
-    }
-  }
-
-
   // debugging
   logDebugMessage(1,"style: %x\n", options.gameStyle);
   if (options.gameStyle & int(TeamFlagGameStyle))
Index: GameKeeper.h
===================================================================
--- GameKeeper.h	(revision 20911)
+++ GameKeeper.h	(working copy)
@@ -93,7 +93,7 @@
     // When is the player's next GameTime?
     const TimeKeeper&	getNextGameTime() const;
     void		updateNextGameTime();
-
+    
     enum LSAState
       {
 	start,
Index: RandomSpawnPolicy.cxx
===================================================================
--- RandomSpawnPolicy.cxx	(revision 20911)
+++ RandomSpawnPolicy.cxx	(working copy)
@@ -48,15 +48,14 @@
   const PlayerInfo& pi = playerData->player;
   TeamColor t = pi.getTeam();
 
-  if (!BZDB.isTrue("freeCtfSpawns") &&
-      playerData->player.shouldRestartAtBase() &&
-      (t >= RedTeam) && (t <= PurpleTeam) &&
-      (bases.find(t) != bases.end())) {
+  if (!BZDB.isTrue("freeCtfSpawns")
+      && playerData->player.shouldRestartAtBase()
+      && (t >= RedTeam) && (t <= PurpleTeam)
+      && (bases.find(t) != bases.end())) {
 
     /* if the player needs to spawn on a base, select a random
      * position on one of their team's available bases.
      */
-
     TeamBases &teamBases = bases[t];
     const TeamBase &base = teamBases.getRandomBase((int)(bzfrand() * 100));
     base.getRandomPosition(pos[0], pos[1], pos[2]);
Index: EntryZones.cxx
===================================================================
--- EntryZones.cxx	(revision 20911)
+++ EntryZones.cxx	(working copy)
@@ -41,23 +41,6 @@
 }
 
 
-void EntryZones::addZoneFlag(int zone, int flagId)
-{
-  if (zone >= (int)zones.size()) {
-    printf ("Internal error: EntryZones::addZoneFlag() unknown zone\n");
-    exit(1);
-  }
-  const std::string& qualifier = CustomZone::getFlagIdQualifier(flagId);
-  QPairList &qPairList = qmap[qualifier];
-  if (qPairList.size() > 0) {
-    printf ("Internal error: EntryZones::addZoneFlag() duplicate\n");
-    exit(1);
-  }
-  qPairList.push_back(std::pair<int,float>(zone, 1.0f));
-  return;
-}
-
-
 void EntryZones::calculateQualifierLists()
 {
   // generate the qualifier lists
@@ -209,7 +192,7 @@
 
 
 void EntryZones::makeSplitLists (int zone,
-				 std::vector<FlagType*> &flags,
+				 std::vector<const FlagType*> &flags,
 				 std::vector<TeamColor> &teams,
 				 std::vector<TeamColor> &safety) const
 {
@@ -227,7 +210,7 @@
       if (zoneIndex == zone) {
 	const std::string& qual = mit->first;
 	int team;
-	FlagType *type;
+	const FlagType *type;
 	if ((type = CustomZone::getFlagTypeFromQualifier(qual)) != Flags::Null) {
 	  flags.push_back(type);
 	}
@@ -254,7 +237,7 @@
 
   for (unsigned int i = 0; i < zones.size(); i++) {
     const WorldFileLocation& z = (const WorldFileLocation) zones[i];
-    std::vector<FlagType*> flags;
+    std::vector<const FlagType*> flags;
     std::vector<TeamColor> teams;
     std::vector<TeamColor> safety;
     makeSplitLists (i, flags, teams, safety);
@@ -284,7 +267,7 @@
   fullSize += sizeof(uint32_t); // zone count
 
   for (unsigned int i = 0; i < zones.size(); i++) {
-    std::vector<FlagType*> flags;
+    std::vector<const FlagType*> flags;
     std::vector<TeamColor> teams;
     std::vector<TeamColor> safety;
     makeSplitLists (i, flags, teams, safety);
Index: WorldInfo.h
===================================================================
--- WorldInfo.h	(revision 20911)
+++ WorldInfo.h	(working copy)
@@ -109,11 +109,11 @@
   void finishWorld();
   int packDatabase();
 
-	bool isFinisihed() { return finished;}
+  bool isFinisihed() { return finished;}
 
 private:
 
-	bool finished;
+  bool finished;
 
   bool rectHitCirc(float dx, float dy, const float *p, float r) const;
   void loadCollisionManager();
Index: FlagHistory.cxx
===================================================================
--- FlagHistory.cxx	(revision 20911)
+++ FlagHistory.cxx	(working copy)
@@ -25,10 +25,10 @@
 
 void FlagHistory::get(char message[]) {
     char flag[MessageLen];
-    std::vector<FlagType*>::iterator fhIt = flagHistory.begin();
+    std::vector<const FlagType*>::iterator fhIt = flagHistory.begin();
 
     while (fhIt != flagHistory.end()) {
-      FlagType * fDesc = (FlagType*)(*fhIt);
+      const FlagType * fDesc = (const FlagType*)(*fhIt);
       if (fDesc->endurance == FlagNormal)
 	sprintf(flag, "(*%c) ", fDesc->flagName[0]);
       else
@@ -38,7 +38,7 @@
     }
 }
 
-void FlagHistory::add(FlagType* type) {
+void FlagHistory::add(const FlagType* type) {
   if (flagHistory.size() >= MAX_FLAG_HISTORY)
     flagHistory.erase(flagHistory.begin());
   flagHistory.push_back(type);
Index: bzfsAPI.cxx
===================================================================
--- bzfsAPI.cxx	(revision 20911)
+++ bzfsAPI.cxx	(working copy)
@@ -708,14 +708,14 @@
   playerRecord->rot = player->lastState.azimuth;
 
   int flagid = player->player.getFlag();
-  FlagInfo *flagInfo = FlagInfo::get(flagid);
+  FlagInfo *flagInfo = FLAGMGR.getFlag(flagid);
 
   std::string label;
-  if (flagInfo && flagInfo->flag.type)
-    label = flagInfo->flag.type->label();
+  if (flagInfo && flagInfo->getFlagType())
+    label = flagInfo->getFlagType()->label();
   playerRecord->currentFlag = label;
 
-  std::vector<FlagType*>	flagHistoryList = player->flagHistory.get();
+  std::vector<const FlagType*> flagHistoryList = player->flagHistory.get();
 
   playerRecord->flagHistory.clear();
   for ( unsigned int i = 0; i < flagHistoryList.size(); i ++)
@@ -861,11 +861,11 @@
   if (!player)
     return NULL;
 
-  FlagInfo* flagInfo = FlagInfo::get(player->player.getFlag());
+  FlagInfo* flagInfo = FLAGMGR.getFlag(player->player.getFlag());
   if (!flagInfo)
     return NULL;
 
-  return FlagInfo::get(player->player.getFlag())->flag.type->flagAbbv;
+  return FLAGMGR.getFlag(player->player.getFlag())->getFlagType()->flagAbbv;
 }
 
 BZF_API bool bz_isPlayerPaused( int playerID )
@@ -1624,13 +1624,13 @@
       // find unused and forced candidates
       FlagInfo* unused = NULL;
       FlagInfo* forced = NULL;
-      for (int i = 0; i < numFlags; i++)
+      for (int i = 0; i < FLAGMGR.size(); i++)
       {
-	FlagInfo* fi2 = FlagInfo::get(i);
-	if ((fi2 != NULL) && (fi2->flag.type == ft))
+	FlagInfo* fi2 = FLAGMGR.getFlag(i);
+	if ((fi2 != NULL) && (fi2->getFlagType() == ft))
 	{
 	  forced = fi2;
-	  if (fi2->player < 0)
+	  if (fi2->getPlayer() < 0)
 	  {
 	    unused = fi2;
 	    break;
@@ -1673,8 +1673,8 @@
     const int flagId = gkPlayer->player.getFlag();
     if (flagId >= 0)
     {
-      FlagInfo& currentFlag = *FlagInfo::get(flagId);
-      if (currentFlag.flag.type->flagTeam != NoTeam)
+      FlagInfo& currentFlag = *FLAGMGR.getFlag(flagId);
+      if (currentFlag.getTeamIndex() != NoTeam)
 	dropFlag(currentFlag, gkPlayer->lastState.pos);// drop team flags
       else
 	resetFlag(currentFlag);// reset non-team flags
@@ -1712,11 +1712,11 @@
 
 BZF_API void bz_resetFlags ( bool onlyUnused )
 {
-  for (int i = 0; i < numFlags; i++)
+  for (int i = 0; i < FLAGMGR.size(); i++)
   {
-    FlagInfo &flag = *FlagInfo::get(i);
+    FlagInfo &flag = *FLAGMGR.getFlag(i);
     // see if someone had grabbed flag,
-    const int playerIndex = flag.player;
+    const int playerIndex = flag.getPlayer();
     if (!onlyUnused || (playerIndex == -1))
     {
       if (playerIndex != -1)
@@ -1730,26 +1730,26 @@
 
 BZF_API unsigned int bz_getNumFlags( void )
 {
-  return numFlags;
+  return FLAGMGR.size();
 }
 
 BZF_API const bzApiString bz_getName( int flag )
 {
-  FlagInfo *pFlag = FlagInfo::get(flag);
+  FlagInfo *pFlag = FLAGMGR.getFlag(flag);
   if (!pFlag)
     return bzApiString("");
 
-  return bzApiString(pFlag->flag.type->flagAbbv);
+  return bzApiString(pFlag->getFlagType()->flagAbbv);
 }
 
 BZF_API bool bz_resetFlag ( int flag )
 {
-  FlagInfo *pFlag = FlagInfo::get(flag);
+  FlagInfo *pFlag = FLAGMGR.getFlag(flag);
   if(!pFlag)
     return false;
 
   // if somone has it, drop it
-  if (pFlag->player != -1)
+  if (pFlag->getPlayer() != -1)
     sendDrop(*pFlag);
 
   resetFlag(*pFlag);
@@ -1759,15 +1759,15 @@
 
 BZF_API bool bz_moveFlag ( int flag, float pos[3] )
 {
-  FlagInfo *pFlag = FlagInfo::get(flag);
+  FlagInfo *pFlag = FLAGMGR.getFlag(flag);
   if(!pFlag)
     return false;
 
   // if somone has it, drop it
-  if (pFlag->player != -1)
+  if (pFlag->getPlayer() != -1)
     sendDrop(*pFlag);
 
-  int teamIndex = pFlag->teamIndex();
+  int teamIndex = pFlag->getTeamIndex();
   bool teamIsEmpty = true;
   if (teamIndex != ::NoTeam)
     teamIsEmpty = (team[teamIndex].team.size == 0);
@@ -1780,22 +1780,22 @@
 
 BZF_API int bz_flagPlayer ( int flag )
 {
-  FlagInfo *pFlag = FlagInfo::get(flag);
+  FlagInfo *pFlag = FLAGMGR.getFlag(flag);
   if(!pFlag)
     return -1;
 
-  return pFlag->player;
+  return pFlag->getPlayer();
 }
 
 BZF_API bool bz_getFlagPosition ( int flag, float* pos )
 {
-  FlagInfo *pFlag = FlagInfo::get(flag);
+  FlagInfo *pFlag = FLAGMGR.getFlag(flag);
   if(!pFlag || !pos)
     return false;
 
-  if (pFlag->player != -1)
+  if (pFlag->getPlayer() != -1)
   {
-    GameKeeper::Player *player = GameKeeper::Player::getPlayerByIndex(pFlag->player);
+    GameKeeper::Player *player = GameKeeper::Player::getPlayerByIndex(pFlag->getPlayer());
 
     if (!player)
       return false;
@@ -1803,7 +1803,7 @@
     memcpy(pos,player->lastState.pos,sizeof(float)*3);
   }
   else
-    memcpy(pos,pFlag->flag.position,sizeof(float)*3);
+    memcpy(pos,pFlag->getFlag().position,sizeof(float)*3);
 
   return true;
 }
Index: commands.cxx
===================================================================
--- commands.cxx	(revision 20911)
+++ commands.cxx	(working copy)
@@ -1260,11 +1260,16 @@
 static void flagCommandHelp(int t)
 {
   sendMessage(ServerPlayer, t, "/flag up");
+  sendMessage(ServerPlayer, t, "/flag list");
   sendMessage(ServerPlayer, t, "/flag show");
   sendMessage(ServerPlayer, t, "/flag reset <all|unused|team|#flagId|FlagAbbv>");
   sendMessage(ServerPlayer, t, "/flag take <#slot|PlayerName|\"PlayerName\">");
   sendMessage(ServerPlayer, t,
 	      "/flag give <#slot|PlayerName|\"PlayerName\"> <#flagId|FlagAbbr> [force]");
+  sendMessage(ServerPlayer, t, "/flag ff <all|bad|good|team|FlagAbbv> [count]");
+  sendMessage(ServerPlayer, t, "/flag fr <all|bad|good|FlagAbbv> [count]");
+  sendMessage(ServerPlayer, t, "/flag frmax <count>");
+  sendMessage(ServerPlayer, t, "/flag frtimes <min> <max>");
   return;
 }
 
@@ -1304,22 +1309,25 @@
   while ((*msg != '\0') && isspace(*msg)) msg++; // eat whitespace
 
   if (strncasecmp(msg, "up", 2) == 0) {
-    for (int i = 0; i < numFlags; i++) {
-      FlagInfo &flag = *FlagInfo::get(i);
-      if (flag.flag.type->flagTeam == ::NoTeam) {
+    for (int i = 0; i < FLAGMGR.size(); i++) {
+      FlagInfo &flag = *FLAGMGR.getFlag(i);
+      if (flag.getTeamIndex() == ::NoTeam) {
 	sendDrop(flag);
-	flag.flag.status = FlagGoing;
-	if (!flag.required) {
-	  flag.flag.type = Flags::Null;
+	flag.setStatus(FlagGoing);
+	if (flag.isRandom()) {
+	  flag.setFlagType(Flags::Null);
 	}
 	sendFlagUpdate(flag);
       }
     }
   }
+  else if (strncasecmp(msg, "list", 4) == 0) {
+    FLAGMGR.sendFlagInfo(t);
+  }
   else if (strncasecmp(msg, "show", 4) == 0) {
-    for (int i = 0; i < numFlags; i++) {
+    for (int i = 0; i < FLAGMGR.size(); i++) {
       char showMessage[MessageLen];
-      FlagInfo::get(i)->getTextualInfo(showMessage);
+      FLAGMGR.getFlag(i)->getTextualInfo(showMessage);
       sendMessage(ServerPlayer, t, showMessage);
     }
   }
@@ -1342,10 +1350,10 @@
     }
     else if (strncasecmp(msg, "team", 4) == 0) {
       // team flags
-      for (int i = 0; i < numFlags; i++) {
-	FlagInfo* fi = FlagInfo::get(i);
-	if ((fi != NULL) && (fi->flag.type->flagTeam != NoTeam)) {
-	  const int playerIndex = fi->player;
+      for (int i = 0; i < FLAGMGR.size(); i++) {
+	FlagInfo* fi = FLAGMGR.getFlag(i);
+	if ((fi != NULL) && (fi->getTeamIndex() != NoTeam)) {
+	  const int playerIndex = fi->getPlayer();
 	  if (playerIndex != -1) {
 	    sendDrop(*fi);
 	  }
@@ -1358,9 +1366,9 @@
 	return true;
       }
       int fIndex = atoi(msg + 1);
-      FlagInfo* fi = FlagInfo::get(fIndex);
+      FlagInfo* fi = FLAGMGR.getFlag(fIndex);
       if (fi != NULL) {
-	const int playerIndex = fi->player;
+	const int playerIndex = fi->getPlayer();
 	if (playerIndex != -1) {
 	  sendDrop(*fi);
 	}
@@ -1372,10 +1380,10 @@
 	return true;
       }
       // by flag abbreviation
-      for (int i = 0; i < numFlags; i++) {
-	FlagInfo* fi = FlagInfo::get(i);
-	if ((fi != NULL) && (fi->flag.type == ft)) {
-	  const int playerIndex = fi->player;
+      for (int i = 0; i < FLAGMGR.size(); i++) {
+	FlagInfo* fi = FLAGMGR.getFlag(i);
+	if ((fi != NULL) && (fi->getFlagType() == ft)) {
+	  const int playerIndex = fi->getPlayer();
 	  if (playerIndex != -1) {
 	    sendDrop(*fi);
 	  }
@@ -1413,17 +1421,18 @@
       return true;
     }
 
-    FlagInfo* fi = FlagInfo::get(gkPlayer->player.getFlag());
+    FlagInfo* fi = FLAGMGR.getFlag(gkPlayer->player.getFlag());
     if (fi != NULL) {
-      sendDrop(*fi);
-      resetFlag(*fi);
+      // send the message before resetting the flag  (random flags)
       char buffer[MessageLen];
       snprintf(buffer, MessageLen, "%s took flag %s/%i from %s",
 	       playerData->player.getCallSign(),
-	       fi->flag.type->flagAbbv, fi->getIndex(),
+	       fi->getFlagType()->flagAbbv, fi->getIndex(),
 	       gkPlayer->player.getCallSign());
       sendMessage(ServerPlayer, t, buffer);
       sendMessage(ServerPlayer, AdminPlayers, buffer);
+      sendDrop(*fi);
+      resetFlag(*fi);
     } else {
       char buffer[MessageLen];
       snprintf(buffer, MessageLen,
@@ -1455,8 +1464,8 @@
 			  strncasecmp(argv[2].c_str(), "force", 5) == 0);
       if (argv[1][0] == '#') {
 	int fIndex = atoi(argv[1].c_str() + 1);
-	fi = FlagInfo::get(fIndex);
-	if ((fi != NULL) && ((fi->player >= 0) && !force)) {
+	fi = FLAGMGR.getFlag(fIndex);
+	if ((fi != NULL) && ((fi->getPlayer() >= 0) && !force)) {
 	  fi = NULL;
 	}
       }
@@ -1466,11 +1475,11 @@
 	  // find unused and forced candidates
 	  FlagInfo* unused = NULL;
 	  FlagInfo* forced = NULL;
-	  for (int i = 0; i < numFlags; i++) {
-	    FlagInfo* fi2 = FlagInfo::get(i);
-	    if ((fi2 != NULL) && (fi2->flag.type == ft)) {
+	  for (int i = 0; i < FLAGMGR.size(); i++) {
+	    FlagInfo* fi2 = FLAGMGR.getFlag(i);
+	    if ((fi2 != NULL) && (fi2->getFlagType() == ft)) {
 	      forced = fi2;
-	      if (fi2->player < 0) {
+	      if (fi2->getPlayer() < 0) {
 		unused = fi2;
 		break;
 	      }
@@ -1519,8 +1528,8 @@
       // deal with the player's current flag
       const int flagId = gkPlayer->player.getFlag();
       if (flagId >= 0) {
-	FlagInfo& currentFlag = *FlagInfo::get(flagId);
-	if (currentFlag.flag.type->flagTeam != NoTeam) {
+        FlagInfo& currentFlag = *FLAGMGR.getFlag(flagId);
+        if (currentFlag.getTeamIndex() != NoTeam) {
 	  // drop team flags
 	  dropFlag(currentFlag, gkPlayer->lastState.pos);
 	} else {
@@ -1530,11 +1539,12 @@
       }
 
       // deal with the flag's current player (for forced gives)
-      if (fi->player >= 0) {
-	GameKeeper::Player* fPlayer = GameKeeper::Player::getPlayerByIndex(fi->player);
+      if (fi->getPlayer() >= 0) {
+        GameKeeper::Player* fPlayer =
+          GameKeeper::Player::getPlayerByIndex(fi->getPlayer());
 	if (fPlayer) {
 	  void *bufStart = getDirectMessageBuffer();
-	  void *buf = nboPackUByte(bufStart, fi->player);
+          void *buf = nboPackUByte(bufStart, fi->getPlayer());
 	  buf = fi->pack(buf);
 	  broadcastMessage(MsgDropFlag, (char*)buf - (char*)bufStart, bufStart);
 	}
@@ -1555,12 +1565,72 @@
       char buffer[MessageLen];
       snprintf(buffer, MessageLen, "%s gave flag %s/%i to %s",
 	       playerData->player.getCallSign(),
-	       fi->flag.type->flagAbbv, fi->getIndex(),
+	       fi->getFlagType()->flagAbbv, fi->getIndex(),
 	       gkPlayer->player.getCallSign());
       sendMessage(ServerPlayer, t, buffer);
       sendMessage(ServerPlayer, AdminPlayers, buffer);
     }
   }
+  else if (strncasecmp(msg, "frtimes", 7) == 0) {
+    if (!checkFlagMaster(playerData)) {
+      return true;
+    }
+    msg += 7;
+    std::vector<std::string> argv = TextUtils::tokenize(msg, " \t", 0, true);
+    if (argv.size() < 2) {
+      flagCommandHelp(t);
+      return true;
+    }
+    const float min = atof(argv[0].c_str());
+    const float max = atof(argv[1].c_str());
+    FLAGMGR.setRandomDelayTimes(min, max);
+  }
+  else if (strncasecmp(msg, "frmax", 5) == 0) {
+    if (!checkFlagMaster(playerData)) {
+      return true;
+    }
+    msg += 5;
+    std::vector<std::string> argv = TextUtils::tokenize(msg, " \t", 0, true);
+    if (argv.size() < 1) {
+      flagCommandHelp(t);
+      return true;
+    }
+    FLAGMGR.userSetRandomCount(atoi(argv[0].c_str()), t);
+  }
+  else if (strncasecmp(msg, "fr", 2) == 0) {
+    if (!checkFlagMaster(playerData)) {
+      return true;
+    }
+    msg += 2;
+    while ((*msg != '\0') && isspace(*msg)) msg++; // eat whitespace
+    std::vector<std::string> argv = TextUtils::tokenize(msg, " \t", 0, true);
+    if (argv.size() < 1) {
+      flagCommandHelp(t);
+      return true;
+    }
+    int count = 1;
+    if (argv.size() > 1) {
+      count = atoi(argv[1].c_str());
+    }
+    FLAGMGR.userAdjustRandom(argv[0].c_str(), count, t);
+  }
+  else if (strncasecmp(msg, "ff", 2) == 0) {
+    if (!checkFlagMaster(playerData)) {
+      return true;
+    }
+    msg += 2;
+    while ((*msg != '\0') && isspace(*msg)) msg++; // eat whitespace
+    std::vector<std::string> argv = TextUtils::tokenize(msg, " \t", 0, true);
+    if (argv.size() < 1) {
+      flagCommandHelp(t);
+      return true;
+    }
+    int count = 1;
+    if (argv.size() > 1) {
+      count = atoi(argv[1].c_str());
+    }
+    FLAGMGR.userAdjustForced(argv[0].c_str(), count, t);
+  }
   else {
     flagCommandHelp(t);
   }
Index: bzfs.cxx
===================================================================
--- bzfs.cxx	(revision 20911)
+++ bzfs.cxx	(working copy)
@@ -73,8 +73,6 @@
 // or dropped us for some reason.
 static const float ListServerReAddTime = 30.0f * 60.0f;
 
-static const float FlagHalfLife = 10.0f;
-
 // do NOT change
 static const int InvalidPlayer = -1;
 
@@ -94,8 +92,7 @@
 static int maxFileDescriptor;
 // team info
 TeamInfo team[NumTeams];
-// num flags in flag list
-int numFlags;
+// ready to shutdown server?
 bool done = false;
 // true if hit time/score limit
 bool gameOver = true;
@@ -149,8 +146,7 @@
 static RejoinList rejoinList;
 
 static TimeKeeper lastWorldParmChange;
-static bool       isIdentifyFlagIn = false;
-static bool       playerHadWorld   = false;
+static bool playerHadWorld = false;
 
 bool		  publiclyDisconected = false;
 
@@ -289,7 +285,7 @@
 //
 // global variable callback
 //
-static void onGlobalChanged(const std::string& name, void*)
+void onGlobalChanged(const std::string& name, void*)
 {
   // This Callback is removed in replay mode. As
   // well, the /set and /reset commands are blocked.
@@ -304,7 +300,6 @@
   broadcastMessage(MsgSetVar, (char*)buf - (char*)bufStart, bufStart);
 }
 
-
 static void sendUDPupdate(int playerIndex)
 {
   // confirm inbound UDP with a TCP message
@@ -342,10 +337,9 @@
 {
   void *buf, *bufStart = getDirectMessageBuffer();
   buf = nboPackUShort(bufStart,1);
-  bool hide
-    = (flag.flag.type->flagTeam == ::NoTeam)
-    && !isIdentifyFlagIn
-    && (flag.player == -1);
+  bool hide = (flag.getTeamIndex() == ::NoTeam)
+              && (flag.getPlayer() == -1)
+              && !FLAGMGR.identifyFlag();
   buf = flag.pack(buf, hide);
   broadcastMessage(MsgFlagUpdate, (char*)buf - (char*)bufStart, bufStart);
 }
@@ -419,9 +413,9 @@
   buf = nboPackUShort(bufStart,0); //placeholder
   int cnt = 0;
   int length = sizeof(uint16_t);
-  for (int flagIndex = 0; flagIndex < numFlags; flagIndex++) {
-    FlagInfo &flag = *FlagInfo::get(flagIndex);
-    if (flag.exist()) {
+  for (int flagIndex = 0; flagIndex < FLAGMGR.size(); flagIndex++) {
+    FlagInfo &flag = *FLAGMGR.getFlag(flagIndex);
+    if (flag.exists()) {
       if ((length + sizeof(uint16_t) + FlagPLen)
 	  > MaxPacketLen - 2*sizeof(uint16_t)) {
 	nboPackUShort(bufStart, cnt);
@@ -434,10 +428,9 @@
 	buf    = nboPackUShort(bufStart,0); //placeholder
       }
 
-      bool hide
-	= (flag.flag.type->flagTeam == ::NoTeam)
-	&& !isIdentifyFlagIn
-	&& (flag.player == -1);
+      bool hide = (flag.getTeamIndex() == ::NoTeam)
+	          && (flag.getPlayer() == -1)
+	          && !FLAGMGR.identifyFlag();
       buf = flag.pack(buf, hide);
       length += sizeof(uint16_t)+FlagPLen;
       cnt++;
@@ -958,12 +951,12 @@
 
     if (clOptions->gameStyle & TeamFlagGameStyle) {
       for (int i = RedTeam; i <= PurpleTeam; i++) {
-	if ((clOptions->maxTeam[i] > 0) && bases.find(i) == bases.end()) {
-	  std::cerr << "base was not defined for "
-		    << Team::getName((TeamColor)i)
-		    << std::endl;
-	  return false;
-	}
+        if ((clOptions->maxTeam[i] > 0) && bases.find(i) == bases.end()) {
+          std::cerr << "base was not defined for "
+                    << Team::getName((TeamColor)i)
+                    << std::endl;
+          return false;
+        }
       }
     }
   } else {
@@ -1046,9 +1039,9 @@
     team[i].team.won = 0;
     team[i].team.lost = 0;
   }
-  FlagInfo::setNoFlagInAir();
-  for (i = 0; i < numFlags; i++) {
-    resetFlag(*FlagInfo::get(i));
+  FLAGMGR.setNoFlagInAir();
+  for (i = 0; i < FLAGMGR.size(); i++) {
+    resetFlag(*FLAGMGR.getFlag(i));
   }
 
   return true;
@@ -1917,14 +1910,14 @@
   }
 
   // if first player on team add team's flag
-  if (team[teamIndex].team.size == 1
+  if ((team[teamIndex].team.size == 1)
       && Team::isColorTeam((TeamColor)teamIndex)) {
     if (clOptions->gameStyle & int(TeamFlagGameStyle)) {
-      int flagid = FlagInfo::lookupFirstTeamFlag(teamIndex);
-      if (flagid >= 0 && !FlagInfo::get(flagid)->exist()) {
-	// reset those flags
-	for (int n = 0; n < clOptions->numTeamFlags[teamIndex]; n++)
-	  resetFlag(*FlagInfo::get(n + flagid));
+      for (int n = 0; n < FLAGMGR.size(); n++) {
+        FlagInfo& fi = *FLAGMGR.getFlag(n);
+        if (fi.getTeamIndex() == teamIndex) {
+          resetFlag(fi);
+        }
       }
     }
   }
@@ -2024,7 +2017,7 @@
   // reposition flag (middle of the map might be a bad idea)
   float flagPos[3] = {0.0f, 0.0f, 0.0f};
 
-  int teamIndex = flag.teamIndex();
+  int teamIndex = flag.getTeamIndex();
   if ((teamIndex >= ::RedTeam) &&  (teamIndex <= ::PurpleTeam)
       && (bases.find(teamIndex) != bases.end())) {
     if (!world->getFlagSpawnPoint(&flag, flagPos)) {
@@ -2065,14 +2058,15 @@
   }
 
   bool teamIsEmpty = true;
-  if (teamIndex != ::NoTeam)
+  if (teamIndex != ::NoTeam) {
     teamIsEmpty = (team[teamIndex].team.size == 0);
+  }
 
   bz_FlagResetEventData eventData;
   memcpy(eventData.pos,flagPos,sizeof(float)*3);
   eventData.teamIsEmpty = teamIsEmpty;
   eventData.flagID = flag.getIndex();
-  eventData.flagType = flag.flag.type->label().c_str();
+  eventData.flagType = flag.getFlagType()->label().c_str();
 
   // reset a flag's info
   flag.resetFlag(eventData.pos, teamIsEmpty);
@@ -2084,14 +2078,14 @@
 void sendDrop(FlagInfo &flag)
 {
   // see if someone had grabbed flag.  tell 'em to drop it.
-  const int playerIndex = flag.player;
+  const int playerIndex = flag.getPlayer();
 
   GameKeeper::Player *playerData
     = GameKeeper::Player::getPlayerByIndex(playerIndex);
   if (!playerData)
     return;
 
-  flag.player      = -1;
+  flag.setPlayer(-1);
   playerData->player.resetFlag();
 
   void *bufStart = getDirectMessageBuffer();
@@ -2110,7 +2104,7 @@
   // if flag was flying then it flies no more
   flag.landing(TimeKeeper::getSunExplodeTime());
 
-  flag.flag.status = FlagNoExist;
+  flag.setStatus(FlagNoExist);
 
   // reset flag status
   resetFlag(flag);
@@ -2119,11 +2113,14 @@
 // try to get over a bug where extraneous flag are attached to a tank
 // not really found why, but this should fix
 // Should be called when we sure that tank does not hold any
-static void dropAssignedFlag(int playerIndex) {
-  for (int flagIndex = 0; flagIndex < numFlags; flagIndex++) {
-    FlagInfo &flag = *FlagInfo::get(flagIndex);
-    if (flag.flag.status == FlagOnTank && flag.flag.owner == playerIndex)
-      resetFlag(flag);
+static void dropAssignedFlag(int playerIndex)
+{
+  for (int flagIndex = 0; flagIndex < FLAGMGR.size(); flagIndex++) {
+    FlagInfo &fi = *FLAGMGR.getFlag(flagIndex);
+    const Flag& flag = fi.getFlag();
+    if ((flag.status == FlagOnTank) && (flag.owner == playerIndex)) {
+      resetFlag(fi);
+    }
   }
 } // dropAssignedFlag
 
@@ -2226,13 +2223,12 @@
   if (flagid < 0)
     return;
 
-  FlagInfo &flag = *FlagInfo::get(flagid);
   // do not simply zap team flag
-  Flag &carriedflag = flag.flag;
-  if (carriedflag.type->flagTeam != ::NoTeam) {
+  FlagInfo &fi = *FLAGMGR.getFlag(flagid);
+  if (fi.getTeamIndex() != ::NoTeam) {
     dropPlayerFlag(*playerData, playerData->lastState.pos);
   } else {
-    zapFlag(flag);
+    zapFlag(fi);
   }
 }
 
@@ -2312,29 +2308,29 @@
     broadcastMessage(MsgRemovePlayer, (char*)buf-(char*)bufStart, bufStart);
 
     // decrease team size
-    int teamNum = int(playerData->player.getTeam());
-    --team[teamNum].team.size;
+    int teamIndex = int(playerData->player.getTeam());
+    --team[teamIndex].team.size;
 
-    // if last active player on team then remove team's flag if no one
-    // is carrying it
-    if (Team::isColorTeam((TeamColor)teamNum)
-	&& team[teamNum].team.size == 0 &&
-	(clOptions->gameStyle & int(TeamFlagGameStyle))) {
-      int flagid = FlagInfo::lookupFirstTeamFlag(teamNum);
-      if (flagid >= 0) {
-	GameKeeper::Player *otherData;
-	for (int n = 0; n < clOptions->numTeamFlags[teamNum]; n++) {
-	  FlagInfo &flag = *FlagInfo::get(flagid+n);
-	  otherData
-	    = GameKeeper::Player::getPlayerByIndex(flag.player);
-	  if (!otherData || otherData->player.isTeam((TeamColor)teamNum))
-	    zapFlag(flag);
-	}
+    // if last active player on team then, remove team's flags
+    // (unless someone is carrying them... ?)
+    if (Team::isColorTeam((TeamColor)teamIndex)
+	&& (team[teamIndex].team.size == 0)
+	&& (clOptions->gameStyle & int(TeamFlagGameStyle))) {
+      GameKeeper::Player* gkPlayer;
+      for (int n = 0; n < FLAGMGR.size(); n++) {
+        FlagInfo& fi = *FLAGMGR.getFlag(n);
+        if (fi.getTeamIndex() == teamIndex) {
+          gkPlayer = GameKeeper::Player::getPlayerByIndex(fi.getPlayer());
+          if ((gkPlayer == NULL)
+              || gkPlayer->player.isTeam((TeamColor)teamIndex)) {
+            zapFlag(fi);
+          }
+        }
       }
     }
 
     // send team update
-    sendTeamUpdate(-1, teamNum);
+    sendTeamUpdate(-1, teamIndex);
   }
 
   playerData->close();
@@ -2423,7 +2419,7 @@
   // An hack to fix a bug on the client
   buf = nboPackUShort (buf, PlayerSlot);
   buf = nboPackUShort (buf, clOptions->maxShots);
-  buf = nboPackUShort (buf, numFlags);
+  buf = nboPackUShort (buf, FLAGMGR.size());
   buf = nboPackFloat  (buf, clOptions->linearAcceleration);
   buf = nboPackFloat  (buf, clOptions->angularAcceleration);
   buf = nboPackUShort (buf, clOptions->shakeTimeout);
@@ -2538,7 +2534,7 @@
     return;
   }
 
-  bz_AllowSpawnData	spawnAllowData;
+  bz_AllowSpawnData spawnAllowData;
   spawnAllowData.playerID = playerIndex;
   spawnAllowData.team = convertTeam(playerData->player.getTeam());
 
@@ -2547,8 +2543,8 @@
     sendMessage(ServerPlayer, playerIndex, "This server may require identification before you can join.");
     sendMessage(ServerPlayer, playerIndex, "Please use /identify, or /register if you have not registerd your callsign or");
     sendMessage(ServerPlayer, playerIndex, "register on http://my.BZFlag.org/bb/ and use that callsign/password.");
-	spawnAllowData.allow = false;
-	}
+    spawnAllowData.allow = false;
+  }
 
   if (playerData->player.isBot()
       && BZDB.isTrue(StateDatabase::BZDB_DISABLEBOTS)) {
@@ -2837,7 +2833,7 @@
       playerData->player.isObserver() ||
       !playerData->player.isAlive() ||
       playerData->player.haveFlag() ||
-      flag.flag.status != FlagOnGround)
+      flag.getFlag().status != FlagOnGround)
     return;
 
   //last Pos might be lagged by TankSpeed so include in calculation
@@ -2845,7 +2841,7 @@
   const float tankSpeed = BZDBCache::tankSpeed;
   const float radius2 = (tankSpeed + tankRadius + BZDBCache::flagRadius) * (tankSpeed + tankRadius + BZDBCache::flagRadius);
   const float* tpos = playerData->lastState.pos;
-  const float* fpos = flag.flag.position;
+  const float* fpos = flag.getFlag().position;
   const float delta = (tpos[0] - fpos[0]) * (tpos[0] - fpos[0]) +
 		      (tpos[1] - fpos[1]) * (tpos[1] - fpos[1]);
 
@@ -2867,7 +2863,7 @@
 
   bz_FlagGrabbedEventData	data;
   data.flagID = flag.getIndex();
-  data.flagType = flag.flag.type->flagAbbv;
+  data.flagType = flag.getFlagType()->flagAbbv;
   memcpy(data.pos,fpos,sizeof(float)*3);
   data.playerID = playerIndex;
 
@@ -2875,7 +2871,7 @@
 
   broadcastMessage(MsgGrabFlag, (char*)buf-(char*)bufStart, bufStart);
 
-  playerData->flagHistory.add(flag.flag.type);
+  playerData->flagHistory.add(flag.getFlagType());
 }
 
 
@@ -2892,15 +2888,15 @@
   // player wants to drop flag.  we trust that the client won't tell
   // us to drop a sticky flag until the requirements are satisfied.
   const int flagIndex = drpFlag.getIndex();
-  if (drpFlag.flag.status != FlagOnTank) {
+  if (drpFlag.getFlag().status != FlagOnTank) {
     return;
   }
-  int flagTeam = drpFlag.flag.type->flagTeam;
+  int flagTeam = drpFlag.getTeamIndex();
   bool isTeamFlag = (flagTeam != ::NoTeam);
 
   // limited flags that have been fired should be disposed of
-  bool limited = clOptions->flagLimit[drpFlag.flag.type] != -1;
-  if (limited && drpFlag.numShots > 0) drpFlag.grabs = 0;
+  bool limited = clOptions->flagLimit[drpFlag.getFlagType()] != -1;
+  if (limited && (drpFlag.getShotCount() > 0)) { drpFlag.zeroGrabsLeft(); }
 
 
   const float waterLevel = world->getWaterLevel();
@@ -2918,9 +2914,9 @@
 
   if (isTeamFlag) {
     vanish = false;
-  } else if (--drpFlag.grabs <= 0) {
+  } else if (drpFlag.getGrabsLeft() <= 1) {
     vanish = true;
-    drpFlag.grabs = 0;
+    drpFlag.zeroGrabsLeft();
   } else if (!clOptions->flagsOnBuildings && (landing[2] > 0.0f)) {
     vanish = true;
   } else {
@@ -2953,7 +2949,7 @@
   if (isTeamFlag) {
     // if it is a team flag, check if there are any players left in
     // that team - if not, start the flag timeout
-    if (team[drpFlag.flag.type->flagTeam].team.size == 0) {
+    if (team[drpFlag.getTeamIndex()].team.size == 0) {
       team[flagIndex + 1].flagTimeout = TimeKeeper::getCurrent();
       team[flagIndex + 1].flagTimeout += (float)clOptions->teamFlagTimeout;
     }
@@ -2976,12 +2972,12 @@
   if (flagIndex < 0) {
     return;
   }
-  dropFlag(*FlagInfo::get(flagIndex), dropPos);
+  dropFlag(*FLAGMGR.getFlag(flagIndex), dropPos);
 
   bz_FlagDroppedEventData data;
   data.playerID = playerData.getIndex();
   data.flagID = flagIndex;
-  data.flagType = FlagInfo::get(flagIndex)->flag.type->flagAbbv;
+  data.flagType = FLAGMGR.getFlag(flagIndex)->getFlagType()->flagAbbv;
   memcpy(data.pos, dropPos, sizeof(float)*3);
 
   worldEventManager.callEvents(bz_eFlagDroppedEvent,&data);
@@ -3006,9 +3002,9 @@
   int flagIndex = playerData->player.getFlag();
   if (flagIndex < 0)
     return;
-  FlagInfo &flag = *FlagInfo::get(flagIndex);
+  FlagInfo &flag = *FLAGMGR.getFlag(flagIndex);
 
-  int teamIndex = flag.teamIndex();
+  int teamIndex = flag.getTeamIndex();
   if (teamIndex == ::NoTeam)
     return;
 
@@ -3136,20 +3132,21 @@
   }
 
   float shotSpeed = BZDB.eval(StateDatabase::BZDB_SHOTSPEED);
-  FlagInfo &fInfo = *FlagInfo::get(shooter.getFlag());
+  FlagInfo &fInfo = *FLAGMGR.getFlag(shooter.getFlag());
   // verify player flag
   if ((firingInfo.flagType != Flags::Null)
-      && (firingInfo.flagType != fInfo.flag.type)) {
+      && (firingInfo.flagType != fInfo.getFlagType())) {
     std::string fireFlag = "unknown";
     std::string holdFlag = "unknown";
     if (firingInfo.flagType) {
       fireFlag = firingInfo.flagType->flagAbbv;
     }
-    if (fInfo.flag.type) {
-      if (fInfo.flag.type == Flags::Null) {
+    const FlagType* ft = fInfo.getFlagType();
+    if (ft) {
+      if (ft == Flags::Null) {
 	holdFlag = "none";
       } else {
-	holdFlag = fInfo.flag.type->flagAbbv;
+	holdFlag = ft->flagAbbv;
       }
     }
 
@@ -3273,11 +3270,11 @@
   char message[MessageLen];
   if (shooter.haveFlag()){
 
-    fInfo.numShots++; // increase the # shots fired
+    fInfo.increaseShotCount(); // increase the # shots fired
 
-    int limit = clOptions->flagLimit[fInfo.flag.type];
+    int limit = clOptions->flagLimit[fInfo.getFlagType()];
     if (limit != -1){ // if there is a limit for players flag
-      int shotsLeft = limit -  fInfo.numShots;
+      int shotsLeft = limit - fInfo.getShotCount();
       if (shotsLeft > 0) { //still have some shots left
 	// give message each shot below 5, each 5th shot & at start
 	if (shotsLeft % 5 == 0 || shotsLeft <= 3 || shotsLeft == limit-1){
@@ -3295,7 +3292,7 @@
 	  for (int i = 0; i < 3; i ++){
 	    lastPos[i] = playerData->lastState.pos[i];
 	  }
-	  fInfo.grabs = 0; // recycle this flag now
+	  fInfo.zeroGrabsLeft(); // recycle this flag now
 	  dropPlayerFlag(*playerData, lastPos);
 	} else { // more shots fired than allowed
 	  // do nothing for now -- could return and not allow shot
@@ -3720,26 +3717,27 @@
       for (int i = 0; i < numClientFlags; i++) {
 	FlagType *fDesc;
 	buf = FlagType::unpack(buf, fDesc);
-	if (fDesc != Flags::Null)
+	if (fDesc != Flags::Null) {
 	  hasFlag[fDesc] = true;
+        }
       }
 
       /* Compare them to the flags this game might need, generating a list of missing flags */
       for (it = FlagType::getFlagMap().begin();
 	   it != FlagType::getFlagMap().end(); ++it) {
 	if (!hasFlag[it->second]) {
-	   if (clOptions->flagCount[it->second] > 0)
-	     missingFlags.insert(it->second);
-	   if ((clOptions->numExtraFlags > 0) && !clOptions->flagDisallowed[it->second])
-	     missingFlags.insert(it->second);
+	  if (FLAGMGR.isAllowedType(it->second)) {
+	    missingFlags.insert(it->second);
+          }
 	}
       }
 
       /* Pack a message with the list of missing flags */
       buf = bufStart = getDirectMessageBuffer();
       for (m_it = missingFlags.begin(); m_it != missingFlags.end(); ++m_it) {
-	if ((*m_it) != Flags::Null)
+	if ((*m_it) != Flags::Null) {
 	  buf = (*m_it)->pack(buf);
+        }
       }
       directMessage(t, MsgNegotiateFlags, (char*)buf-(char*)bufStart, bufStart);
       break;
@@ -3840,13 +3838,15 @@
       // data: flag index
       uint16_t flag;
 
-      if (invalidPlayerAction(playerData->player, t, "grab a flag"))
+      if (invalidPlayerAction(playerData->player, t, "grab a flag")) {
 	break;
+      }
 
       buf = nboUnpackUShort(buf, flag);
       // Sanity check
-      if (flag < numFlags)
-	grabFlag(t, *FlagInfo::get(flag));
+      if (flag < FLAGMGR.size()) {
+	grabFlag(t, *FLAGMGR.getFlag(flag));
+      }
       break;
     }
 
@@ -3892,8 +3892,8 @@
 
       int pFlag = playerData->player.getFlag();
       if (pFlag >= 0) {
-	FlagInfo &flag = *FlagInfo::get(pFlag);
-	if (flag.flag.type == Flags::Shield) {
+	FlagInfo &flag = *FLAGMGR.getFlag(pFlag);
+	if (flag.getFlagType() == Flags::Shield) {
 	  //sendMessage(ServerPlayer, AdminPlayers, "has Shield");
 	  playerData->player.endShotCredit--;
 	}
@@ -4021,22 +4021,22 @@
 
       int flagIndex = fromData->player.getFlag();
       if (to == ServerPlayer) {
-	if (flagIndex >= 0)
-	  zapFlag (*FlagInfo::get(flagIndex));
+	if (flagIndex >= 0) {
+	  zapFlag (*FLAGMGR.getFlag(flagIndex));
+        }
 	return;
       }
 
       // Sanity check
-      if (to >= curMaxPlayers)
-	return;
+      if ((to >= curMaxPlayers) || (flagIndex == -1)) {
+        return;
+      }
 
-      if (flagIndex == -1)
-	return;
-
       GameKeeper::Player *toData
 	= GameKeeper::Player::getPlayerByIndex(to);
-      if (!toData)
+      if (!toData) {
 	return;
+      }
 
       // verify that the player stealing HAS thief
       int toPlayersFlag = toData->player.getFlag();
@@ -4044,9 +4044,9 @@
       if (!toData->player.haveFlag())
 	cheater = true;
 
-      FlagInfo *toFlag = FlagInfo::get(toPlayersFlag);
+      FlagInfo *toFlag = FLAGMGR.getFlag(toPlayersFlag);
       // without flag or with a non thief flag? Then is probably cheating
-      if (!toFlag || toFlag->flag.type != Flags::Thief)
+      if (!toFlag || toFlag->getFlagType() != Flags::Thief)
 	cheater = true;
 
       // TODO, check thief radius here
@@ -4072,17 +4072,18 @@
 
       if (ftEventData.action != ftEventData.CancelSteal) {
 	int oFlagIndex = toData->player.getFlag();
-	if (oFlagIndex >= 0)
-	  zapFlag (*FlagInfo::get(oFlagIndex));
+	if (oFlagIndex >= 0) {
+	  zapFlag(*FLAGMGR.getFlag(oFlagIndex));
+        }
       }
 
       if (ftEventData.action == ftEventData.ContinueSteal) {
 	void *obufStart = getDirectMessageBuffer();
 	void *obuf = nboPackUByte(obufStart, from);
 	obuf = nboPackUByte(obuf, to);
-	FlagInfo &flag = *FlagInfo::get(flagIndex);
-	flag.flag.owner = to;
-	flag.player = to;
+        FlagInfo &flag = *FLAGMGR.getFlag(flagIndex);
+        flag.setOwner(to);
+        flag.setPlayer(to);
 	toData->player.resetFlag();
 	toData->player.setFlag(flagIndex);
 	fromData->player.resetFlag();
@@ -4301,14 +4302,14 @@
 	    // if tank is not alive cannot be sure it didn't just toss
 	    // (V)
 	    if (pFlag >= 0) {
-	      FlagInfo &flag = *FlagInfo::get(pFlag);
-	      if (flag.flag.type == Flags::Velocity)
+	      const FlagType* ft = FLAGMGR.getFlag(pFlag)->getFlagType();
+	      if (ft == Flags::Velocity)
 		maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_VELOCITYAD);
-	      else if (flag.flag.type == Flags::Thief)
+	      else if (ft == Flags::Thief)
 		maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_THIEFVELAD);
-	      else if (flag.flag.type == Flags::Agility)
+	      else if (ft == Flags::Agility)
 		maxPlanarSpeed *= BZDB.eval(StateDatabase::BZDB_AGILITYADVEL);
-	      else if ((flag.flag.type == Flags::Burrow) &&
+	      else if ((ft == Flags::Burrow) &&
 		(playerData->lastState.pos[2] == state.pos[2]) &&
 		(playerData->lastState.velocity[2] == state.velocity[2]) &&
 		(state.pos[2] <= BZDB.eval(StateDatabase::BZDB_BURROWDEPTH)))
@@ -4581,7 +4582,7 @@
       char message[MessageLen]
 	= "You were kicked because you were idle too long";
       sendMessage(ServerPlayer, p,  message);
-      removePlayer(p, "idling");
+      removePlayer(p, "idling", true);
       logDebugMessage(1,"Kicked player %s [%d] for idling (thresh = %f)\n",
 	     playerData.player.getCallSign(), p, clOptions->idlekickthresh);
       return;
@@ -4639,8 +4640,8 @@
 	return;
     }
     // if player is holding a flag, drop it
-    for (int j = 0; j < numFlags; j++) {
-      if (FlagInfo::get(j)->player == p) {
+    for (int j = 0; j < FLAGMGR.size(); j++) {
+      if (FLAGMGR.getFlag(j)->getPlayer() == p) {
 	dropPlayerFlag(playerData, playerData.lastState.pos);
 	// Should recheck if player is still available
 	if (!GameKeeper::Player::getPlayerByIndex(p))
@@ -4794,8 +4795,6 @@
   setvbuf(stderr, (char *)NULL, _IOLBF, 0);
 #endif
 
-  Record::init();
-
   // check time bomb
   if (timeBombBoom()) {
     std::cerr << "This release expired on " << timeBombString() << ".\n";
@@ -4810,6 +4809,9 @@
   }
 
   // initialize
+  Flags::init();
+  Record::init();
+
 #if defined(_WIN32)
   {
     static const int major = 2, minor = 2;
@@ -4838,7 +4840,6 @@
 
   bzfsrand(time(0));
 
-  Flags::init();
 
   clOptions = new CmdLineOptions();
 
@@ -4911,17 +4912,22 @@
 
   // make flags, check sanity, etc...
   // (do this after the world has been loaded)
-  finalizeParsing(argc, argv, *clOptions, world->getEntryZones());
-  {
-    FlagInfo::setNoFlagInAir();
-    for (int i = 0; i < numFlags; i++) {
-      resetFlag(*FlagInfo::get(i));
+  finalizeParsing(argc, argv, *clOptions);
+  
+  // setup the initial flag slots
+  FLAGMGR.finalize();
+  FLAGMGR.setNoFlagInAir();
+  for (int i = 0; i < FLAGMGR.size(); i++) {
+    resetFlag(*FLAGMGR.getFlag(i));
+  }
+  if (debugLevel >= 3) {
+    std::vector<std::string> strvec;
+    FLAGMGR.getFlagList(strvec);
+    for (int s = 0; s < (int)strvec.size(); s++) {
+      logDebugMessage(3, "Flags:  %s\n", strvec[s].c_str());
     }
   }
 
-  // loading extra flag number
-  FlagInfo::setExtra(clOptions->numExtraFlags);
-
   // loading lag announcement thresholds
   LagInfo::setAdminLagAnnounceThreshold(clOptions->adminlagannounce);
   LagInfo::setLagAnnounceThreshold(clOptions->lagannounce);
@@ -4947,8 +4953,7 @@
     Replay::init();
 
     // we don't send flags to a client that isn't expecting them
-    numFlags = 0;
-    clOptions->numExtraFlags = 0;
+    FLAGMGR.clear();
 
     // disable the BZDB callbacks
     for (unsigned int gi = 0; gi < numGlobalDBItems; ++gi) {
@@ -5082,9 +5087,6 @@
     world->getWorldWeapons().clear();
   }
 
-  TimeKeeper nextSuperFlagInsertion = TimeKeeper::getCurrent();
-  const float flagExp = -logf(0.5f) / FlagHalfLife;
-
   // load up the access permissions & stuff
   initGroups();
   if (passFile.size())
@@ -5092,16 +5094,6 @@
   if (userDatabaseFile.size())
     PlayerAccessInfo::readPermsFile(userDatabaseFile);
 
-  // See if an ID flag is in the game.
-  // If not, we could hide type info for all flags
-  if (clOptions->flagCount[Flags::Identify] > 0) {
-    isIdentifyFlagIn = true;
-  }
-  if ((clOptions->numExtraFlags > 0)
-      && !clOptions->flagDisallowed[Flags::Identify]) {
-    isIdentifyFlagIn = true;
-  }
-
   if (clOptions->startRecording) {
     Record::start(ServerPlayer);
   }
@@ -5170,12 +5162,12 @@
 
     // get time for next flag drop
     float dropTime;
-    while ((dropTime = FlagInfo::getNextDrop(tm)) <= 0.0f) {
+    while ((dropTime = FLAGMGR.getNextDrop(tm)) <= 0.0f) {
       // if any flags were in the air, see if they've landed
-      for (i = 0; i < numFlags; i++) {
-	FlagInfo &flag = *FlagInfo::get(i);
+      for (i = 0; i < FLAGMGR.size(); i++) {
+	FlagInfo &flag = *FLAGMGR.getFlag(i);
 	if (flag.landing(tm)) {
-	  if (flag.flag.status == FlagOnGround) {
+	  if (flag.getFlag().status == FlagOnGround) {
 	    sendFlagUpdate(flag);
 	  } else {
 	    resetFlag(flag);
@@ -5187,6 +5179,12 @@
       waitTime = dropTime;
     }
 
+// FIXME    
+//    const float nextFlagTime = FLAGMGR.getNextEventTime();
+//    if (nextFlagTime < waitTime) {
+//      waitTime = nextFlagTime;
+//    }
+
     // get time for next Player internal action
     GameKeeper::Player::updateLatency(waitTime);
 
@@ -5223,15 +5221,16 @@
     }
 
     // see if we are within the plug requested max wait time
-    if (waitTime > pluginMaxWait)
+    if (waitTime > pluginMaxWait) {
       waitTime = pluginMaxWait;
-
+    }
+    
     // don't wait (used by CURL and MsgEnter)
     if (dontWait) {
       waitTime = 0.0f;
       dontWait = false;
     }
-
+    
     /**************
      *  SELECT()  *
      **************/
@@ -5303,8 +5302,8 @@
 	      TeamColor vteam = player->player.getTeam();
 
 	      buf = nboPackUByte(bufStart, (uint8_t)curMaxPlayers);
-	      buf = nboPackUShort
-		(buf, uint16_t(FlagInfo::lookupFirstTeamFlag(vteam)));
+	      int firstFlag = FLAGMGR.lookupFirstTeamFlag(vteam);
+	      buf = nboPackUShort(buf, uint16_t(firstFlag));
 	      buf = nboPackUShort(buf, uint16_t(1 + (int(vteam) % 4)));
 	      directMessage(j, MsgCaptureFlag, (char*)buf - (char*)bufStart,
 			    bufStart);
@@ -5320,8 +5319,8 @@
 	  }
 
 	  // reset all flags
-	  for (int j = 0; j < numFlags; j++) {
-	    zapFlag(*FlagInfo::get(j));
+	  for (int j = 0; j < FLAGMGR.size(); j++) {
+	    zapFlag(*FLAGMGR.getFlag(j));
 	  }
 
 	  // quietly reset team scores in case of a capture during the countdown
@@ -5617,10 +5616,11 @@
 		  else if (action == "reset")
 		  {
 		logDebugMessage(1,"Poll flagreset taking action: resetting unused flags.\n");
-		for (int f = 0; f < numFlags; f++) {
-		  FlagInfo &flag = *FlagInfo::get(f);
-		  if (flag.player == -1)
+		for (int f = 0; f < FLAGMGR.size(); f++) {
+		  FlagInfo &flag = *FLAGMGR.getFlag(f);
+		  if (flag.getPlayer() == -1) {
 		    resetFlag(flag);
+                  }
 		}
 	      }
 	    } /* end if poll is successful */
@@ -5690,35 +5690,23 @@
     // check team flag timeouts
     if (clOptions->gameStyle & TeamFlagGameStyle) {
       for (i = RedTeam; i < CtfTeams; ++i) {
-	if (team[i].flagTimeout - tm < 0 && team[i].team.size == 0) {
-	  int flagid = FlagInfo::lookupFirstTeamFlag(i);
-	  if (flagid >= 0) {
-	    for (int n = 0; n < clOptions->numTeamFlags[i]; n++) {
-	      FlagInfo &flag = *FlagInfo::get(flagid + n);
-	      if (flag.exist() && flag.player == -1) {
-		logDebugMessage(1,"Flag timeout for team %d\n", i);
-		zapFlag(flag);
-	      }
-	    }
-	  }
-	}
+	if ((team[i].team.size == 0) && ((team[i].flagTimeout - tm) < 0.0f)) {
+          for (int n = 0; n < FLAGMGR.size(); n++) {
+            FlagInfo& fi = *FLAGMGR.getFlag(n);
+            if ((fi.getTeamIndex() == i)
+                && (fi.getPlayer() == -1)
+                && fi.exists()) {
+              logDebugMessage(1, "Flag timeout for team %d\n", i);
+              zapFlag(fi);
+            }
+          }
+        }
       }
     }
 
-    // maybe add a super flag (only if game isn't over)
-    if (!gameOver && clOptions->numExtraFlags > 0 && nextSuperFlagInsertion<=tm) {
-      // randomly choose next flag respawn time; halflife distribution
-      float r = float(bzfrand() + 0.01); // small offset, we do not want to wait forever
-      nextSuperFlagInsertion += -logf(r) / flagExp;
-      for (i = numFlags - clOptions->numExtraFlags; i < numFlags; i++) {
-	FlagInfo &flag = *FlagInfo::get(i);
-	if (flag.flag.type == Flags::Null) {
-	  // flag in now entering game
-	  flag.addFlag();
-	  sendFlagUpdate(flag);
-	  break;
-	}
-      }
+    // possibly add random flags
+    if (!gameOver) {
+      FLAGMGR.addRandomFlags(tm);
     }
 
     // occasionally add ourselves to the list again (in case we were
@@ -5811,7 +5799,7 @@
       if (NetHandler::anyUDPPending())
 	NetHandler::flushAllUDP();
     }
-
+    
     // Fire world weapons
     world->getWorldWeapons().fire();
 
@@ -5859,7 +5847,6 @@
 
   // free misc stuff
   delete clOptions; clOptions = NULL;
-  FlagInfo::setSize(0);
   delete world; world = NULL;
   delete[] worldDatabase; worldDatabase = NULL;
   delete votingarbiter; votingarbiter = NULL;
Index: EntryZones.h
===================================================================
--- EntryZones.h	(revision 20911)
+++ EntryZones.h	(working copy)
@@ -34,7 +34,6 @@
     EntryZones();
 
     void addZone( const CustomZone *zone );
-    void addZoneFlag(int zone, int flagId);
 
     void calculateQualifierLists();
 
@@ -56,7 +55,7 @@
     QualifierMap qmap;
 
     void makeSplitLists (int zone,
-			 std::vector<FlagType*> &flags,
+			 std::vector<const FlagType*> &flags,
 			 std::vector<TeamColor> &teams,
 			 std::vector<TeamColor> &safety) const;
 };
Index: GameKeeper.cxx
===================================================================
--- GameKeeper.cxx	(revision 20911)
+++ GameKeeper.cxx	(working copy)
@@ -146,6 +146,7 @@
   return;
 }
 
+
 void *GameKeeper::Player::packAdminInfo(void *buf)
 {
   buf = nboPackUByte(buf, netHandler->sizeOfIP());
Index: CustomZone.cxx
===================================================================
--- CustomZone.cxx	(revision 20911)
+++ CustomZone.cxx	(working copy)
@@ -63,7 +63,7 @@
 }
 
 
-void CustomZone::addZoneFlagCount(FlagType* flagType, int count)
+void CustomZone::addZoneFlagCount(const FlagType* flagType, int count)
 {
   ZoneFlagMap::iterator it = zoneFlagMap.find(flagType);
   if (it != zoneFlagMap.end()) {
@@ -89,7 +89,7 @@
       if (flag == "good") {
 	FlagSet &fs = Flag::getGoodFlags();
 	for (FlagSet::iterator it = fs.begin(); it != fs.end(); ++it) {
-	  FlagType *f = *it;
+	  const FlagType *f = *it;
 	  if (f->endurance != FlagNormal) { // Null and Team flags
 	    const std::string& qual = getFlagTypeQualifier(f);
 	    if (qual.size() > 0) {
@@ -101,7 +101,7 @@
       else if (flag == "bad") {
 	FlagSet &fs = Flag::getBadFlags();
 	for (FlagSet::iterator it = fs.begin(); it != fs.end(); ++it) {
-	  FlagType *f = *it;
+	  const FlagType *f = *it;
 	  if (f->endurance != FlagNormal) { // Null and Team flags
 	    const std::string& qual = getFlagTypeQualifier(f);
 	    if (qual.size() > 0) {
@@ -111,7 +111,7 @@
 	}
       }
       else {
-	FlagType* f = Flag::getDescFromAbbreviation(flag.c_str());
+	const FlagType* f = Flag::getDescFromAbbreviation(flag.c_str());
 	if (f == Flags::Null) {
 	  logDebugMessage(1,"WARNING: bad flag type: %s\n", flag.c_str());
 	  input.putback('\n');
@@ -150,7 +150,7 @@
     if (flag == "good") {
       FlagSet &fs = Flag::getGoodFlags();
       for (FlagSet::iterator it = fs.begin(); it != fs.end(); ++it) {
-	FlagType *f = *it;
+	const FlagType *f = *it;
 	if (f->endurance != FlagNormal) { // Null and Team flags
 	  addZoneFlagCount(f, count);
 	}
@@ -159,14 +159,14 @@
     else if (flag == "bad") {
       FlagSet &fs = Flag::getBadFlags();
       for (FlagSet::iterator it = fs.begin(); it != fs.end(); ++it) {
-	FlagType *f = *it;
+	const FlagType *f = *it;
 	if (f->endurance != FlagNormal) { // Null and Team flags
 	  addZoneFlagCount(f, count);
 	}
       }
     }
     else {
-      FlagType *f = Flag::getDescFromAbbreviation(flag.c_str());
+      const FlagType *f = Flag::getDescFromAbbreviation(flag.c_str());
       if (f != Flags::Null) {
 	addZoneFlagCount(f, count);
       }
@@ -219,6 +219,7 @@
   worldInfo->addZone( this );
 }
 
+
 void CustomZone::getRandomPoint(float *pt) const
 {
   float x = (float)((bzfrand() * (2.0f * size[0])) - size[0]);
@@ -256,6 +257,7 @@
   return qual;
 }
 
+
 static int checkIntQualifier(char prefix, const std::string& qual)
 {
   if (qual[0] == prefix) {
@@ -270,22 +272,9 @@
 }
 
 
-const std::string& CustomZone::getFlagIdQualifier(int flagId)
+const std::string& CustomZone::getFlagTypeQualifier(const FlagType* flagType)
 {
   static std::string qual;
-  qual = makeIntQualifier('#', flagId);
-  return qual;
-}
-
-int CustomZone::getFlagIdFromQualifier(const std::string& qual)
-{
-  return checkIntQualifier('#', qual);
-}
-
-
-const std::string& CustomZone::getFlagTypeQualifier(FlagType* flagType)
-{
-  static std::string qual;
   if (flagType != NULL) {
     qual = "f";
     qual += flagType->flagAbbv;
@@ -295,7 +284,7 @@
   return qual;
 }
 
-FlagType* CustomZone::getFlagTypeFromQualifier(const std::string& qual)
+const FlagType* CustomZone::getFlagTypeFromQualifier(const std::string& qual)
 {
   if (qual[0] == 'f') {
     return Flag::getDescFromAbbreviation(qual.c_str() + 1);
Index: bzfs.h
===================================================================
--- bzfs.h	(revision 20911)
+++ bzfs.h	(working copy)
@@ -114,6 +114,7 @@
 extern void       dropFlag(FlagInfo& flag, const float dropPos[3]);
 extern void       publicize();
 extern TeamColor  whoseBase(float x, float y, float z);
+extern void       onGlobalChanged(const std::string& name, void*);
 bool defineWorld ( void );
 bool saveWorldCache( const char* file = NULL );
 
@@ -128,7 +129,6 @@
 extern bool	    done;
 extern bool	    gameOver;
 extern TeamInfo	team[NumTeams];
-extern int	     numFlags;
 extern bool	    countdownActive;
 extern int	     countdownDelay;
 extern TimeKeeper      countdownPauseStart;
Index: CustomZone.h
===================================================================
--- CustomZone.h	(revision 20911)
+++ CustomZone.h	(working copy)
@@ -27,7 +27,7 @@
 
 
 typedef std::vector<std::string> QualifierList;
-typedef std::map<FlagType*, int> ZoneFlagMap; // type, count
+typedef std::map<const FlagType*, int> ZoneFlagMap; // type, count
 
 
 class CustomZone : public WorldFileLocation {
@@ -49,12 +49,9 @@
     float getDistToPoint (const float *pos) const;
 
   public:
-    static const std::string& getFlagIdQualifier(int flagId);
-    static int getFlagIdFromQualifier(const std::string&);
+    static const std::string& getFlagTypeQualifier(const FlagType* flagType);
+    static const FlagType* getFlagTypeFromQualifier(const std::string&);
 
-    static const std::string& getFlagTypeQualifier(FlagType* flagType);
-    static FlagType* getFlagTypeFromQualifier(const std::string&);
-
     static const std::string& getFlagSafetyQualifier(int team);
     static int getFlagSafetyFromQualifier(const std::string&);
 
@@ -62,7 +59,7 @@
     static int getPlayerTeamFromQualifier(const std::string&);
 
   private:
-    void addZoneFlagCount(FlagType* flagType, int count);
+    void addZoneFlagCount(const FlagType* flagType, int count);
 
   private:
     ZoneFlagMap zoneFlagMap;
Index: RecordReplay.cxx
===================================================================
--- RecordReplay.cxx	(revision 20911)
+++ RecordReplay.cxx	(working copy)
@@ -1590,9 +1590,9 @@
   int cnt = 0;
   int length = sizeof(u16);
 
-  for (flagIndex = 0; flagIndex < numFlags; flagIndex++) {
-    FlagInfo &flag = *FlagInfo::get(flagIndex);
-    if (flag.exist()) {
+  for (flagIndex = 0; flagIndex < FLAGMGR.size(); flagIndex++) {
+    FlagInfo &flag = *FLAGMGR.getFlag(flagIndex);
+    if (flag.exists()) {
       if ((length + sizeof(u16) + FlagPLen) > MaxPacketLen - 2*sizeof(u16)) {
 	// packet length overflow
 	nboPackUShort(bufStart, cnt);
@@ -2232,7 +2232,7 @@
 
 static bool replaceFlagTypes(ReplayHeader *h)
 {
-  bool replace = false;
+//FIXME  bool replace = false;
   void *buf = h->flags;
   FlagOptionMap headerFlag;
   FlagTypeMap::iterator it;
@@ -2250,6 +2250,9 @@
   // we're done with this
   delete[] h->flags;
 
+/* FIXME - with flexible flags, all must be active
+           FLAGMGR.clear() also clear forbiddenFlags
+
   // See if all of the flags required by the header are currently active
   for (it = FlagType::getFlagMap().begin();
        it != FlagType::getFlagMap().end(); ++it) {
@@ -2275,6 +2278,7 @@
     return true; // flag types were replaced
   }
 
+*/
   return false;  // flag types were not replaced
 }
 
@@ -2339,11 +2343,15 @@
   // Please see the MsgNegotiateFlags code in [bzfs.cxx]
   // to see what it is that we are trying to fake.
 
+  type = type; //FIXME  
+  /*FIXME
+
   if ((clOptions->flagCount[type] > 0) ||
       ((clOptions->numExtraFlags > 0) &&
        !clOptions->flagDisallowed[type])) {
     return true;
   }
+  */
   return false;
 }
 
Index: WorldInfo.cxx
===================================================================
--- WorldInfo.cxx	(revision 20911)
+++ WorldInfo.cxx	(working copy)
@@ -397,7 +397,7 @@
 bool WorldInfo::getFlagDropPoint(const FlagInfo* fi, const float* pos,
 				float* pt) const
 {
-  FlagType* flagType = fi->flag.type;
+  const FlagType* flagType = fi->getFlagType();
   const int team = (int)flagType->flagTeam;
   const bool teamFlag = (team != NoTeam);
 
@@ -407,31 +407,34 @@
     if (entryZones.getClosePoint(safetyQual, pos, pt)) {
       return true;
     }
-  } else {
-    const std::string& idQual =
-      CustomZone::getFlagIdQualifier(fi->getIndex());
-    if (entryZones.getClosePoint(idQual, pos, pt)) {
+  }
+  else {
+    const CustomZone* srcZone = fi->getSourceZone();
+    if (srcZone != NULL) {
+      srcZone->getRandomPoint(pt);
       return true;
     }
+
     const std::string& typeQual =
       CustomZone::getFlagTypeQualifier(flagType);
     if (entryZones.getClosePoint(typeQual, pos, pt)) {
       return true;
     }
   }
+  
   return false;
 }
 
 
 bool WorldInfo::getFlagSpawnPoint(const FlagInfo* fi, float* pt) const
 {
-  FlagType* flagType = fi->flag.type;
+  const FlagType* flagType = fi->getFlagType();
   const int team = (int)flagType->flagTeam;
   const bool teamFlag = (team != NoTeam);
 
-  const std::string& idQual =
-    CustomZone::getFlagIdQualifier(fi->getIndex());
-  if (entryZones.getRandomPoint(idQual, pt)) {
+  const CustomZone* srcZone = fi->getSourceZone();
+  if (srcZone != NULL) {
+    srcZone->getRandomPoint(pt);
     return true;
   }
 
@@ -442,6 +445,7 @@
       return true;
     }
   }
+
   return false;
 }
 
Index: FlagInfo.h
===================================================================
--- FlagInfo.h	(revision 20911)
+++ FlagInfo.h	(working copy)
@@ -18,64 +18,212 @@
 
 /* system headers */
 #include <stdlib.h>
+#include <map>
+#include <set>
+#include <vector>
+#include <string>
 
 /* bzflag common headers */
 #include "Flag.h"
 #include "TimeKeeper.h"
 
+
 /** FlagInfo describes a flag as it pertains to the world.
  */
+ 
+class CustomZone;
+class EntryZones;
+ 
+ 
 class FlagInfo {
-public:
 
-  FlagInfo();
+  friend class FlagManager;
 
-  void setRequiredFlag(FlagType *desc);
-  void addFlag();
-  void *pack(void *buf, bool hide = false);
-  void dropFlag(float pos[3], float landingPos[3], bool vanish);
-  void resetFlag(float position[3], bool teamIsEmpty);
-  void grab(int playerIndex);
-  int  teamIndex() const;
-  int  getIndex() const;
-  bool landing(const TimeKeeper &tm);
-  void getTextualInfo(char *message);
-  bool exist();
+  public:
+    FlagInfo();
 
-  static FlagInfo *get(int index);
-  static void setSize(int _numFlags);
-  static void setAllowed(std::vector<FlagType*> allowed);
-  static void setExtra(int extra);
-  static int  lookupFirstTeamFlag(int teamindex);
-  static float getNextDrop(TimeKeeper &tm);
-  static void  setNoFlagInAir();
+    void setExtraFlag();
+    void setRandomFlag();
+    void setForcedFlag(const FlagType *desc);
+    void addFlag();
+    void dropFlag(float pos[3], float landingPos[3], bool vanish);
+    void resetFlag(const float position[3], bool teamIsEmpty);
+    void grab(int playerIndex);
+    bool landing(const TimeKeeper &tm);
 
-    // flag info
+    void zeroGrabsLeft() { grabsLeft = 0; }
+    void decreaseGrabsLeft() { grabsLeft--; }
+    void increaseShotCount() { shotCount++; }
+    void setOwner(int playerIndex) { flag.owner = playerIndex; }
+    void setPlayer(int playerIndex) { player = playerIndex; }
+    void setStatus(FlagStatus status) { flag.status = status; }
+    void setFlagType(FlagType* type) { flag.type = type; }
+    
+    void* pack(void* buf, bool hide = false);
+
+    void getTextualInfo(char* message) const;
+    
+    inline bool exists() const { return (flag.status != FlagNoExist); }
+    inline bool isZone() const { return (srcZone != NULL); }
+    inline bool isExtra() const { return extra; }
+    inline bool isRandom() const { return random; }
+    inline int getIndex() const { return flagIndex; }
+    inline int getPlayer() const { return player; }
+    inline int getGrabsLeft() const { return grabsLeft; }
+    inline int getShotCount() const { return shotCount; }
+    inline int getTeamIndex() const { return flag.type->flagTeam; }
+    inline const Flag& getFlag() const { return flag; }
+    inline const FlagType* getFlagType() const { return flag.type; }
+    inline const CustomZone* getSourceZone() const { return srcZone; }
+
+  private:
     Flag flag;
-    // player index who has flag
+    int flagIndex;
     int player;
-    // how many grabs before removed
-    int grabs;
-    // true if flag must be in game
-    bool required;
-    // number of shots on this flag
-    int numShots;
+    int grabsLeft; // how many grabs before removed
+    int shotCount; // number of shots on this flag
+    bool extra;    // true if this is an extra slot
+    bool random;   // true if this flag is random
+    const CustomZone* srcZone;
+    TimeKeeper dropDone; // time flag will land
+};
 
-private:
-  int flagIndex;
-  static std::vector<FlagType*> allowedFlags;
-  static int		    numExtraFlags;
-  static int		    numFlags;
-  static int		    numFlagsInAir;
-  static FlagInfo	      *flagList;
 
-  // time flag will land
-  TimeKeeper		    dropDone;
+class FlagManager {
+  public:
+
+    inline int size() const { return fullCount; }
+
+    inline FlagInfo* getFlag(int index);
+
+    void clear();
+    void clearRandomPool();
+    
+    void finalize(); // build the flag list
+
+    void setRandomDelayTimes(float minSeconds, float maxSeconds);
+
+    void setExtraFlags(int count);
+    bool adjustForced(const char* type, int count);
+    bool adjustRandom(const char* type, int count);
+    void setRandomMaximum(int count);
+
+    bool userAdjustForced(const char* type, int count, int playerIndex);
+    bool userAdjustRandom(const char* type, int count, int playerIndex);
+    bool userSetRandomCount(int count, int playerIndex);
+
+    int getZoneCount() const { return zoneCount; }
+    int getExtraCount() const { return extraCount; }
+    int getForcedCount() const { return forcedCount; }
+    int getRandomCount() const { return randomCount; }
+
+    // for client-server flag negotiations
+    void forbidType(const FlagType* ft);
+    bool forbidType(const char* type);
+    bool isAllowedType(const FlagType* ft) const;
+    
+    float getNextDrop(TimeKeeper &tm) const;  // FIXME ?
+
+    void dropFlags();
+    void addRandomFlags(const TimeKeeper& tm);
+    float getNextTime();
+
+    const FlagType* pickRandomFlagType() const; // for FlagInfo
+
+// FIXME - move all the flag type functions here,
+//         dropping, zapping, popping, whatever...
+    void setNoFlagInAir();
+
+//    void resetFlags();
+//    void removeFlags();
+//    void removePlayerFlag(int playerIndex);
+
+//    void addFlag(FlagInfo& fi); 
+//    void zapFlag(FlagInfo& fi);
+//    void dropFlag(FlagInfo& fi, const float pos[3]);
+//    void resetFlag(FlagInfo& fi);
+//    void incShotCount(FlagInfo& fi);
+//    void giveFlag(FlagInfo& fi, int playerIndex);
+
+    void sendFlagInfo(int playerIndex) const;
+    void getFlagList(std::vector<std::string>& text) const;
+    
+    bool identifyFlag() const;
+    int lookupFirstTeamFlag(int teamIndex) const;
+    
+    
+    static FlagType* getTeamFlagType(int);
+
+  private:
+    void adjustTeamFlags(std::set<const FlagType*>& fs, int count);
+    
+    void selectForbidden(); // based on game style, etc...
+    void removeForbidden(std::set<const FlagType*>& fs) const;
+    void removeForbidden(std::map<const FlagType*, int>& fcm) const;
+    void removeTeam(std::set<const FlagType*>& fs) const;
+    
+    static const FlagType* getFlagTypeFromLabel(const char* label);
+    // returned set does not include any forbidden flags
+    // (ft may be one of the fake flag types,  ex: FlagInfo::badFlagType)
+    void getFlagSet(const FlagType* ft, std::set<const FlagType*>& fs) const;
+
+    int getZoneTeamFlagCount(const FlagType*, const EntryZones&) const;
+    int addZoneTeamFlags(int index, const FlagType*, const EntryZones&);
+    
+  private:
+    std::map<const FlagType*, int> randomPool;  // available random counts
+    std::map<const FlagType*, int> randomFlags; // active random flags
+    std::map<const FlagType*, int> forcedFlags; // forced to be active
+    std::set<const FlagType*> forbiddenFlags;   // never present
+
+    FlagInfo* flagList;
+    int fullCount;
+    int zoneCount;
+    int extraCount;
+    int forcedCount;
+    int randomCount;
+    
+    //int numFlagsInAir;
+    
+    bool zoneIdentifyFlag;
+    
+    float minRandomDelay;
+    float maxRandomDelay;
+    TimeKeeper nextRandomInsertion;
+
+    // fake flag types
+    static const FlagType* allFlagType;
+    static const FlagType* badFlagType;
+    static const FlagType* goodFlagType;
+    static const FlagType* teamFlagType;
+
+  private:
+    FlagManager();
+    ~FlagManager();
+    FlagManager(const FlagManager&);
+    FlagManager& operator=(const FlagManager&);
+
+  public:    
+    static FlagManager manager;
 };
-#else
-class FlagInfo;
-#endif
 
+
+#define FLAGMGR (FlagManager::manager)
+
+
+inline FlagInfo* FlagManager::getFlag(int index)
+{
+  if ((index < 0) || (index >= fullCount)) {
+    return NULL;
+  } else {
+    return &flagList[index];
+  }
+}
+
+
+#endif // __FLAGINFO_H__
+
+
 // Local Variables: ***
 // mode:C++ ***
 // tab-width: 8 ***
Index: CmdLineOptions.h
===================================================================
--- CmdLineOptions.h	(revision 20911)
+++ CmdLineOptions.h	(working copy)
@@ -44,8 +44,8 @@
   RandomRabbitSelection		// pick the new rabbit out of a hat
 };
 
-typedef std::map<FlagType*, int> FlagNumberMap;
-typedef std::map<FlagType*,bool> FlagOptionMap;
+typedef std::map<const FlagType*, int> FlagNumberMap;
+typedef std::map<const FlagType*, bool> FlagOptionMap;
 
 #define _DEFAULT_LIN_ACCEL 0.0f
 #define _DEFAULT_ANGLE_ACCELL 0.0f
@@ -56,46 +56,103 @@
 struct CmdLineOptions
 {
   CmdLineOptions()
-  : wksPort(ServerPort), gameStyle(PlainGameStyle),
-    rabbitSelection(ScoreRabbitSelection), msgTimer(0), spamWarnMax(5),
-    servermsg(""), advertisemsg(""), worldFile(""),
-    pingInterface(""), password(""),
-    listServerOverridden(false), publicizedTitle(""), publicizedAddress(""),
-    advertiseGroups("EVERYONE"),
-    suppressMasterBanList(false), masterBanListOverridden(false),
-    maxShots(1), maxTeamScore(0), maxPlayerScore(0),
-    numExtraFlags(0), teamKillerKickRatio(0),
-    numAllowedFlags(0), shakeWins(0), shakeTimeout(0),
-    teamFlagTimeout(30), maxlagwarn(10000), maxjitterwarn(10000), maxpacketlosswarn(10000),
-    adminlagannounce(-1.0), lagannounce(-1.0), lagwarnthresh(-1.0), jitterwarnthresh(-1.0),
-    idlekickthresh(-1.0), timeLimit(0.0f), timeElapsed(0.0f), addedTime(0.0f),
-    linearAcceleration(_DEFAULT_LIN_ACCEL), angularAcceleration(_DEFAULT_ANGLE_ACCELL), useGivenPort(false),
-    useFallbackPort(false), requireUDP(false), randomBoxes(false),
-    randomCTF(false), flagsOnBuildings(false), respawnOnBuildings(false),
-    oneGameOnly(false), timeManualStart(false), randomHeights(false),
-    useTeleporters(false), teamKillerDies(true), printScore(false),
-    publicizeServer(false), replayServer(false), startRecording(false),
-    timestampLog(false), timestampMicros(false),
-    filterFilename(""), filterCallsigns(false), filterChat(false), filterSimple(false),
-    banTime(300), voteTime(60), vetoTime(2), votesRequired(2),
-    votePercentage(50.1f), voteRepeatTime(300),
-    autoTeam(false), citySize(5), cacheURL(""), cacheOut(""), tkAnnounce(false), wallSides(4)
   {
-    int i;
-    for (FlagTypeMap::iterator it = FlagType::getFlagMap().begin();
-	 it != FlagType::getFlagMap().end(); ++it) {
-	flagCount[it->second] = 0;
-	flagLimit[it->second] = -1;
-	flagDisallowed[it->second] = false;
+    const FlagTypeMap& ftm = FlagType::getFlagMap();
+    FlagTypeMap::const_iterator it;
+    for (it = ftm.begin(); it != ftm.end(); it++) {
+      flagLimit[it->second] = -1;
     }
 
-    for (i = 0; i < NumTeams; i++) {
+    for (int i = 0; i < NumTeams; i++) {
       maxTeam[i] = MaxPlayers;
-      numTeamFlags[i] = 0;
     }
 
     listServerURL.push_back(DefaultListServerURL);
     masterBanListURL.push_back(DefaultMasterBanURL);
+
+    listServerOverridden = false;
+    suppressMasterBanList = false;
+    masterBanListOverridden = false;
+
+    wksPort		= ServerPort;
+    useGivenPort	= false;
+    useFallbackPort	= false;
+    requireUDP		= false;
+    pingInterface	= "";
+    
+    gameStyle		= PlainGameStyle;
+    rabbitSelection	= ScoreRabbitSelection;
+    
+    msgTimer		= 0;
+    spamWarnMax		= 5;
+   
+    servermsg		= "";
+    advertisemsg	= "";
+   
+    password		= "";
+    
+    publicizeServer	= false;
+    publicizedTitle	= "";
+    publicizedAddress	= "";
+    advertiseGroups	= "EVERYONE";
+   
+    maxShots		= 1;
+    maxTeamScore	= 0;
+    maxPlayerScore	= 0;
+
+    autoTeam		= false;
+    teamKillerDies	= true;
+    teamKillerKickRatio	= 0;
+   
+    numAllowedFlags	= 0;
+    shakeWins		= 0;
+    shakeTimeout	= 0;
+    teamFlagTimeout	= 30;
+
+    maxlagwarn		= 10000;
+    lagwarnthresh	= -1.0;
+    idlekickthresh	= -1.0;
+
+    timeLimit		= 0.0f;
+    timeElapsed		= 0.0f;
+   
+    worldFile		= "";
+    randomBoxes		= false;
+    randomCTF		= false;
+    randomHeights	= false;
+    useTeleporters	= false;
+    citySize		= 5;
+
+    respawnOnBuildings	= false;
+    flagsOnBuildings	= false;
+    
+    linearAcceleration	= _DEFAULT_LIN_ACCEL;
+    angularAcceleration	= _DEFAULT_ANGLE_ACCELL;
+   
+    oneGameOnly		= false;
+    timeManualStart	= false;
+
+    replayServer	= false;
+    startRecording	= false;
+   
+    printScore		= false;
+    timestampLog	= false;
+    timestampMicros	= false;
+   
+    filterFilename	= "";
+    filterCallsigns	= false;
+    filterChat		= false;
+    filterSimple	= false;
+   
+    banTime		= 300;
+    voteTime		= 60;
+    vetoTime		= 2;
+    votesRequired	= 2;
+    votePercentage	= 50.1f;
+    voteRepeatTime	= 300;
+
+    cacheURL		= "";
+    cacheOut		= "";
   }
 
   int			wksPort;
@@ -104,27 +161,26 @@
   int			msgTimer;
   int			spamWarnMax;
 
-  std::string   servermsg;
-  std::string   advertisemsg;
-  std::string   worldFile;
-  std::string   pingInterface;
-  std::string   password;
+  std::string   	servermsg;
+  std::string   	advertisemsg;
+  std::string   	worldFile;
+  std::string   	pingInterface;
+  std::string   	password;
 
-  bool listServerOverridden;
+  bool			listServerOverridden;
   std::vector<std::string>   listServerURL;
 
-  std::string	publicizedTitle;
-  std::string	publicizedAddress;
-  std::string	advertiseGroups;
+  std::string		publicizedTitle;
+  std::string		publicizedAddress;
+  std::string		advertiseGroups;
 
   bool			suppressMasterBanList;
   bool			masterBanListOverridden;
-  std::vector<std::string>		masterBanListURL;
+  std::vector<std::string>	masterBanListURL;
 
   uint16_t		maxShots;
   int			maxTeamScore;
   int			maxPlayerScore;
-  int			numExtraFlags;
   int			teamKillerKickRatio; // if players tk*100/wins > teamKillerKickRatio -> kicked
   int			numAllowedFlags;
   uint16_t		shakeWins;
@@ -167,9 +223,7 @@
   bool			countdownPaused;
 
   uint16_t		maxTeam[NumTeams];
-  FlagNumberMap		flagCount;
   FlagNumberMap		flagLimit; // # shots allowed / flag
-  FlagOptionMap		flagDisallowed;
 
   AccessControlList	acl;
   TextChunkManager	textChunker;
@@ -182,12 +236,12 @@
   WordFilter		filter;
 
   /* vote poll options */
-  unsigned short int banTime;
-  unsigned short int voteTime;
-  unsigned short int vetoTime;
-  unsigned short int votesRequired;
-  float votePercentage;
-  unsigned short int voteRepeatTime;
+  unsigned short int	banTime;
+  unsigned short int	voteTime;
+  unsigned short int	vetoTime;
+  unsigned short int	votesRequired;
+  float 		votePercentage;
+  unsigned short int	voteRepeatTime;
 
   std::string		reportFile;
   std::string		reportPipe;
@@ -199,33 +253,31 @@
 
   /* city options */
   int			citySize;
-  int			numTeamFlags[NumTeams];
 
-  std::string	   cacheURL;
-  std::string	   cacheOut;
+  std::string		cacheURL;
+  std::string		cacheOut;
 
   bool			tkAnnounce;
   int			wallSides;
 
   // plugins
-  typedef struct
-  {
-	  std::string plugin;
-	  std::string command;
-  }pluginDef;
+  typedef struct {
+    std::string plugin;
+    std::string command;
+  } pluginDef;
 
   std::vector<pluginDef>	pluginList;
 };
 
 
 void parse(int argc, char **argv, CmdLineOptions &options, bool fromWorldFile = false);
-void finalizeParsing(int argc, char **argv, CmdLineOptions &options, EntryZones& ez);
+void finalizeParsing(int argc, char **argv, CmdLineOptions &options);
 bool checkCommaList (const char *list, int maxlen);
 
-#else
-struct CmdLineOptions;
-#endif
 
+#endif // __CMDLINEOPTIONS_H__
+
+
 // Local Variables: ***
 // mode:C++ ***
 // tab-width: 8 ***
Index: SpawnPolicy.cxx
===================================================================
--- SpawnPolicy.cxx	(revision 20911)
+++ SpawnPolicy.cxx	(working copy)
@@ -210,8 +210,8 @@
       float  enemyAngle = playerData->lastState.azimuth;
       if (playerData->player.getFlag() >= 0) {
 	// check for dangerous flags
-	const FlagInfo *finfo = FlagInfo::get(playerData->player.getFlag());
-	const FlagType *ftype = finfo->flag.type;
+	const FlagInfo *finfo = FLAGMGR.getFlag(playerData->player.getFlag());
+	const FlagType *ftype = finfo->getFlagType();
 	// FIXME: any more?
 	if (ftype == Flags::Laser) {  // don't spawn in the line of sight of an L
 	  if (isFacing(enemyPos, enemyAngle, twentyDegrees)) { // he's looking within 20 degrees of spawn point
