/* 
 * XEvil(TM) Copyright (C) 1994,2000 Steve Hardt and Michael Judge
 * http://www.xevil.com
 * satan@xevil.com
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program, the file "gpl.txt"; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA, or visit http://www.gnu.org.
 */

// game_style.cpp

#if X11
	#ifndef NO_PRAGMAS
	#pragma implementation "game_style.h"
	#endif
#endif

// Include Files
#include "stdafx.h"
extern "C" {
#include <string.h>
}

#if X11
#include <strstream.h>
#endif

#if WIN32
#include <strstrea.h>
#endif

#include <iomanip.h>
#include "utils.h"
#include "coord.h"
#include "world.h"
#include "locator.h"
#include "game_style.h"
#include "actual.h"
#include "sound_cmn.h"


#define SCENARIO_BONUS_TIME 650
#define SCENARIO_HIVE_ALIENS 13
#define SCENARIO_HIVE_EGGS 8
#define SCENARIO_FLAG_ENEMIES 15
#define SCENARIO_SEALS_ENEMIES 4
#define SCENARIO_ZIG_ZAG_ENEMIES 30 // 40 // 50
#define SCENARIO_POUND_DOGS 20
#define SCENARIO_JAPAN_TOWN_NINJAS 15
#define LEVEL_TIME 3000
#define HUMAN_LIVES 4
#define HUMAN_LIVES_SCENARIOS 7 //10
#define SCENARIO_SEALS_SEALS (SCENARIO_SEALS_TRAPDOORS * Trapdoor::SEALS_MAX)
#define SCENARIO_SEALS_NEED 15  // Used to be 20, make it easier.
#define SCENARIO_ANTI_SEALS_MAX_SLIPPAGE 3  // number of Seals that can get away
#define NORMAL_SCENARIOS_FREQUENCY 5 // Every fifth level is a SCENARIO.



////////////////////////// Specific Scenarios //////////////////////////////
enum ScenarioType {
  EXTERMINATE, // We don't use it right now.
  BONUS,
  HIVE,
  CAPTURE_THE_FLAG,
  SEALS,
  ANTI_SEALS,
  KILL_THE_FIRE_DEMON,
  KILL_THE_DRAGON,
  ZIG_ZAG,
  THE_POUND,
  JAPAN_TOWN,
  SCENARIO_NONE, // Must be last.
};



class Scenario {
public:
  Scenario();
  virtual ~Scenario();

  virtual ScenarioType get_scenario_type() = 0;
  /* NOTE: Only used so we don't choose the same scenario twice in a row. */

  virtual Boolean class_friends();
  /* EFFECTS: Are Enemies classFriends with each other. */
  /* DEFAULT: True */

  virtual void set_human_data(HumanP,WorldP,LocatorP);
  /* DEFAULT: do nothing */

  virtual Pos human_initial_pos(WorldP,LocatorP,const Size &s);
  /* DEFAULT: world.empty_rect(s) */

  virtual Boolean can_refill_game_objects();
  /* DEFAULT: True */

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP) = 0;

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &) = 0;
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum) = 0;
  /* EFFECTS: These two functions start a new level in a scenario.  setup_world() should
     just setup the map and size of the world.  new_level() is called after world.reset()
     and does the bulk of the work. */
  /* NOTE: Unlike GameStyle::new_level, level is constant. It has already been incremented. 
     Also, there is no kill_fntn as the physicals have already been cleaned. */


  virtual void new_level_set_timer(Timer &timer);
  /* DEFAULT: do nothing */

  virtual Boolean advance_level(); 
  /* DEFAULT: True */ 

  virtual Boolean award_bonus();
  /* DEFAULT: False */

  virtual void refill_enemies(Boolean enemiesRefill,WorldP,LocatorP,IPhysicalManagerP);
  /* DEFAULT: do nothing */

  virtual void filter_weapons_and_other_items(LocatorP,int &weaponsNum,
                                              const PhysicalContext *weapons[],
                                              int &oItemsNum,
                                              const PhysicalContext *oItems[]);
  /* DEFAULT: Do nothing. */

  virtual unsigned int get_soundtrack();
  /* DEFAULT: IDW_SOUNDTRACK */

  virtual SoundName get_midisoundtrack(){return SoundNames::SOUND_RANDOM;}
  /* DEFAULT: SOUND_RANDOM */
};



class Exterminate : public Scenario {
public:
  Exterminate();

  virtual ScenarioType get_scenario_type() {return EXTERMINATE;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);


private:
  int enemiesNum;
};



class Bonus : public Scenario {
public:
  Bonus();

  virtual ScenarioType get_scenario_type() {return BONUS;}

  virtual Boolean can_refill_game_objects();

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual void new_level_set_timer(Timer &timer);


private:
  Id frogs[SCENARIO_BONUS_FROGS]; 
  int frogsRemaining;
};



class Hive : public Scenario {
public:
  Hive();

  virtual ScenarioType get_scenario_type() {return HIVE;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual Boolean award_bonus();
  /* EFFECTS: Return True. */

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual void refill_enemies(Boolean enemiesRefill,WorldP,LocatorP,IPhysicalManagerP);

  static Boolean aliens_team(LocatorP,PhysicalP,PhysicalP);
  /* EFFECTS: Aliens and Huggers */

  virtual SoundName get_midisoundtrack(){return SoundNames::HIVE_SOUNDTRACK;}


private:
  Id xitId;
};



class CaptureTheFlag : public Scenario {
public:
  CaptureTheFlag();

  virtual ScenarioType get_scenario_type() {return CAPTURE_THE_FLAG;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual void refill_enemies(Boolean enemiesRefill,WorldP,LocatorP,IPhysicalManagerP);


private:
  Id flagIds[SCENARIO_FLAG_FLAGS];
  int flagsRemaining;
};



// Has methods common to Seals and AntiSeals.
class GenericSeals: public Scenario {
public:
  GenericSeals();

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual Boolean advance_level();  

  virtual void refill_enemies(Boolean enemiesRefill,WorldP,LocatorP,IPhysicalManagerP);

  virtual unsigned int get_soundtrack();

  virtual SoundName get_midisoundtrack(){return SoundNames::SEAL_SOUNDTRACK;}


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  void compute_out_safe_active_dead(LocatorP,int &lemOut,int &lemSafe,int &lemActive,int &lemDead);
  /* MODIFIES: lemOut, lemSafe, lemActive, lemDead */
  /* EFFECTS: Compute the current number of seals out, safe, active, and dead. */
  /* NOTE: Usually lemOut == lemSafe + lemActive + lemDead, but not always because of 
     one turn needed to get new Seals in the locator. */
  
  virtual Boolean (*get_team())(LocatorP,PhysicalP,PhysicalP) = 0;
  
  virtual void set_level_strings(int level,ostrstream &lStr,ostrstream &lTitleStr) = 0;
  /* EFFECTS: Called in new_level() to set the appropriate messages. */

  
  Id homeId;
  Id trapdoorIds[SCENARIO_SEALS_TRAPDOORS];
  int sealsOut,sealsSafe,sealsActive,sealsDead;
  Boolean dontAdvance;
};



class Seals : public GenericSeals {
public:
  Seals();

  virtual ScenarioType get_scenario_type() {return SEALS;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual Boolean award_bonus();
  /* EFFECTS: Return True. */


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  virtual Boolean (*get_team())(LocatorP,PhysicalP,PhysicalP);

  virtual void set_level_strings(int level,ostrstream &lStr,ostrstream &lTitleStr);


private:
  static Boolean seals_team(LocatorP,PhysicalP,PhysicalP);
  /* EFFECTS: A team such that any two seals are on the same team,
     any human/human-slave and seal is on the same team, but not
     two human/human-slaves on the same team. */
};



class AntiSeals : public GenericSeals {
public:
  AntiSeals();

  virtual ScenarioType get_scenario_type() {return ANTI_SEALS;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);


#ifndef PROTECTED_IS_PUBLIC
protected:
#endif
  virtual Boolean (*get_team())(LocatorP,PhysicalP,PhysicalP);

  virtual void set_level_strings(int level,ostrstream &lStr,ostrstream &lTitleStr);


private:
  static Boolean anti_seals_team(LocatorP,PhysicalP,PhysicalP);
};



class KillTheFireDemon : public Scenario {
public:
  KillTheFireDemon();

  virtual ScenarioType get_scenario_type() {return KILL_THE_FIRE_DEMON;}

  virtual SoundName get_midisoundtrack(){return SoundNames::FIRE_SOUNDTRACK;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual Boolean award_bonus();
  /* EFFECTS: Return True. */
};



class KillTheDragon : public Scenario {
public:
  KillTheDragon();

  virtual ScenarioType get_scenario_type() {return KILL_THE_DRAGON;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual SoundName get_midisoundtrack(){return SoundNames::FIRE_SOUNDTRACK;}

  virtual Boolean award_bonus();
  /* EFFECTS: Return True. */
};



class ZigZag : public Scenario {
public:
  ZigZag();

  virtual ScenarioType get_scenario_type() {return ZIG_ZAG;}

  virtual void set_human_data(HumanP,WorldP,LocatorP);

  virtual Pos human_initial_pos(WorldP,LocatorP,const Size &s);

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual void refill_enemies(Boolean enemiesRefill,WorldP,LocatorP,IPhysicalManagerP);

  virtual Boolean award_bonus();
  /* EFFECTS: Return True. */


private:
  Id xitId;
};



class ThePound : public Scenario {
public:
  ThePound();

  virtual ScenarioType get_scenario_type() {return THE_POUND;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);
};



class JapanTown : public Scenario {
public:
  JapanTown();

  virtual ScenarioType get_scenario_type() {return JAPAN_TOWN;}

  virtual int new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                              IPhysicalManagerP);

  virtual void setup_world(WorldP,LocatorP,const DifficultyLevel &);
  virtual void new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &dLevel,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int humansNum);

  virtual void filter_weapons_and_other_items(LocatorP,int &weaponsNum,
                                              const PhysicalContext *weapons[],
                                              int &oItemsNum,
                                              const PhysicalContext *oItems[]);

  static Boolean ninja_team(LocatorP,PhysicalP,PhysicalP);
  /* EFFECTS: All enemy Ninjas. */
  /* NOTE: Left public so that Game can reuse this team definition. */
};



GameStyle::GameStyle()
{}



GameStyle::~GameStyle()
{}



GameStyle *GameStyle::by_type(GameStyleType type) {
  GameStyle *ret = NULL;  

  switch (type) {
    case LEVELS:
      ret = new Normal();
      break;
    case LEVELS_ONLY:
      ret = new Levels();
      break;
    case KILL:
      ret = new KillKillKill();
      break;
    case DUEL:
      ret = new Duel();
      break;
    case EXTENDED:
      ret = new ExtendedDuel();
      break;
    case TRAINING:
      ret = new Training();
      break;
    case SCENARIOS:
      ret = new Scenarios();
      break;
  }      
  assert(ret);

  return ret;
}



Boolean GameStyle::uses_enemies_num(GameStyleType style) {
  return (style == KILL || 
					style == DUEL ||
					style == EXTENDED);
}



Boolean GameStyle::uses_enemies_refill(GameStyleType style) {
  // They happen to be the same now.
  return uses_enemies_num(style);
}



Boolean GameStyle::class_friends() {
  return True;
}  



int GameStyle::human_initial_lives() {
  return HUMAN_LIVES;
}



void GameStyle::set_human_data(HumanP,WorldP,LocatorP)
{}  



Pos GameStyle::human_initial_pos(WorldP world,LocatorP,const Size &size) {
  return world->empty_rect(size);
}



Boolean GameStyle::can_refill_game_objects() {
  return True;
}



Boolean GameStyle::need_difficulty(int) {
  return True;
}



int GameStyle::new_level_check(int,WorldP,LocatorP,
                                  int,Boolean &,ostrstream &,Timer &timer,
                                  IPhysicalManagerP) {  
  // A timer-based level.
  if (timer.ready()) {
    return 0;
  }
  
  // continue
  return -1;
}



Boolean GameStyle::game_over_check(int humansPlaying,int) {
  if (humansPlaying == HU_UNSPECIFIED) {
    return False;
  }

  return (humansPlaying == 0);  
}



Boolean GameStyle::advance_level() {
  return True;
}



Boolean GameStyle::award_bonus() {
  return False;
}



void GameStyle::new_level_set_timer(Timer &timer) {
  timer.set(LEVEL_TIME);
}



char *GameStyle::can_reset(int,int,Boolean) {
  // Ok to reset.
  return NULL;
}



void GameStyle::clean_physicals(Boolean doMinimum,WorldP world,
                                LocatorP locator,IPhysicalManagerP manager) {
  // Clean up all Physicals in the Game.  World must have been reset so 
  // that human players can be moved to the inside of the world.
  PhysicalIter pIter(*locator);
  PhysicalP p;


  // Make two passes, one just to get the followers.
  PtrList leaveAlone;  // Explicitly leave these Objects alone.
  while (p = pIter()) {
    int action = partition(p,doMinimum,world,locator);
    if (action == RELOCATE || action == LEAVE_ALONE) {
      // Don't worry about add_unique(), as of now, no object can be a
      // follower of more than one other object.
      //
      // Also, get all followers of followers.  Fix Dragon bug.
      get_followers_recursive(leaveAlone,p);
    }
  }


  // Now that we have the leaveAlone list, actually clean the objects.
  PhysicalIter pIter2(*locator);
  while (p = pIter2()) {
    int action = partition(p,doMinimum,world,locator);
    if (action == RELOCATE) {
      const Area &area = p->get_area();
      IntelP intel = p->get_intel();
      Size size = area.get_size();
      Pos posNew;
      if (intel && intel->is_human()) {
        // Let specific scenario decide.
        posNew = human_initial_pos(world,locator,size);
      }
      else {
        // If necessary, add machine_initial_pos().
        posNew = world->empty_rect(size);
      }
      ((MovingP)p)->relocate(posNew);
    }
    else if (action == KILL_OFF && !leaveAlone.contains(p)) {
      manager->kill_physical(p);
    }
    // else leave object alone
  }
}



void GameStyle::filter_weapons_and_other_items(LocatorP,int &,
                                               const PhysicalContext *[],
                                               int &,
                                               const PhysicalContext *[]) {
}



unsigned int GameStyle::get_soundtrack() {
  return SoundNames::SOUNDTRACK;
}



SoundName GameStyle::get_midisoundtrack() {
  return SoundNames::SOUND_RANDOM;
}



int GameStyle::partition(PhysicalP p,Boolean doMinimum,
                         WorldP world,LocatorP locator) {
  IntelP intel = p->get_intel();
  Boolean controlled = False;
  
  // Save Human controlled physicals.
  if (intel && intel->is_human()) {
    controlled = True;
  }


  // Save enemies if doMinimum is True.  Only used now for KillKillKill 
  // game style.
  if (doMinimum && intel && intel->is_enemy()) {
    controlled = True;
  }


  // Save Machines that are slaves of Humans.
  if (intel && !intel->is_human()) {
    IntelP master = 
      locator->lookup(((MachineP)intel)->get_master_intel_id());
    if (master && master->is_human()) {
      controlled = True;
    }
  }
  

  // Move human/slave to a new location inside the World.
  if (controlled) {
    assert(p->is_moving());
    const Area &area = p->get_area();
    Loc middle = area.middle_wsquare();
    if (!doMinimum || !world->inside(middle)) {
      // Relocate if doing everthing or if we need to keep it inside.
      return RELOCATE;
    }
    return LEAVE_ALONE;
  }
  else if (p->is_item()) {
    if (!doMinimum) {
      // The follower logic will take care of items held by Human players
      return KILL_OFF;
    }
    return LEAVE_ALONE;
  }
  // Kill everything else, ha, ha, ha.
  else {
    return KILL_OFF;
  }
}



void GameStyle::get_followers_recursive(PtrList& leaveAlone,PhysicalP p) {
  PtrList immediate;
  p->get_followers(immediate);
  leaveAlone.append(immediate);

  // Call self on all followers.
  for (int n = 0; n < immediate.length(); n++) {
    PhysicalP f = (PhysicalP)immediate.get(n);
    get_followers_recursive(leaveAlone,f);
  }
}



Normal::Normal() {
  playScenarios = False;
  levels = new Levels();
  scenarios = new Scenarios();
  assert(levels && scenarios);
}



Normal::~Normal() {
  delete levels;
  delete scenarios;
}



GameStyleP Normal::clone() {
  GameStyleP ret = new Normal();
  assert(ret);
  return ret;
}



void Normal::describe(ostrstream &str) {
  str << "Complete each level/scenario to proceed to the next one." << ends;
}



GameStyleType Normal::get_type() {
  // A bit of a hack
  return LEVELS;
}



int Normal::human_initial_lives() {
  return HUMAN_LIVES;
}



void Normal::set_human_data(HumanP h,WorldP w,LocatorP l) {
  GameStyleP delegate = get_delegate();
  delegate->set_human_data(h,w,l);
}  



Pos Normal::human_initial_pos(WorldP w,LocatorP l,const Size &s) {
  GameStyleP delegate = get_delegate();
  return delegate->human_initial_pos(w,l,s);
}



Boolean Normal::can_refill_game_objects() {
  GameStyleP delegate = get_delegate();
  return delegate->can_refill_game_objects();
}



char *Normal::can_reset(int humansNumNext,int,Boolean) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  
  if (humansNumNext < 1) {
    return Utils::strdup("NEED AT LEAST ONE HUMAN PLAYER");
  }
  return NULL;
}



void Normal::reset(WorldP w,LocatorP l,const DifficultyLevel &dLevel,int enemiesNumNext) {
  levels->reset(w,l,dLevel,enemiesNumNext);
  scenarios->reset(w,l,dLevel,enemiesNumNext);
}



int Normal::new_level_check(int enemiesPlaying,WorldP w,LocatorP l,
                             int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &timer,
                             IPhysicalManagerP manager) {
  GameStyleP delegate = get_delegate();
  return delegate->new_level_check(enemiesPlaying,w,l,level,lStrChanged,levelStr,timer,manager);
}



Boolean Normal::advance_level() {
  GameStyleP delegate = get_delegate();
  return delegate->advance_level();
}



Boolean Normal::award_bonus() {
  GameStyleP delegate = get_delegate();
  return delegate->award_bonus();
}



void Normal::new_level(int level,WorldP world,LocatorP locator,
                       const DifficultyLevel &dLevel,
                       ostrstream &lStr,ostrstream &lTitleStr,
                       IPhysicalManagerP manager,int humansNum) {
  // Choose which delegate to use.
  if (level % NORMAL_SCENARIOS_FREQUENCY == 0) {
    playScenarios = True;    
  }
  else {
    playScenarios = False;    
  }     

  GameStyleP delegate = get_delegate();
  delegate->new_level(level,world,locator,dLevel,lStr,lTitleStr,manager,humansNum);
}



void Normal::new_level_set_timer(Timer &timer) {
  GameStyleP delegate = get_delegate();
  delegate->new_level_set_timer(timer);
}



void Normal::refill_enemies(Boolean eRefill,WorldP w,LocatorP l,
                               IPhysicalManagerP manager) {
  GameStyleP delegate = get_delegate();
  delegate->refill_enemies(eRefill,w,l,manager);
}



unsigned int Normal::get_soundtrack() {
  GameStyleP delegate = get_delegate();
  return delegate->get_soundtrack();
}



SoundName Normal::get_midisoundtrack() {
  GameStyleP delegate = get_delegate();
  return delegate->get_midisoundtrack();
}



Levels::Levels() 
{}



GameStyleP Levels::clone() {
  GameStyleP ret = new Levels();
  assert(ret);
  return ret;
}



void Levels::describe(ostrstream &str) {
  str << "Human player(s) fights through increasing levels of "
	    << "difficulty." << "\n" 
	    << "To complete a level you must kill all enemy players." 
	    << ends;
}



GameStyleType Levels::get_type() {
  return LEVELS_ONLY;
}



char *Levels::can_reset(int humansNumNext,int,Boolean) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  if (humansNumNext < 1) {
    return Utils::strdup("LEVELS REQUIRES AT LEAST ONE HUMAN PLAYER");
  }
  return NULL;
}



void  Levels::reset(WorldP,LocatorP,const DifficultyLevel &dLevel,int) {
  // So first level will start out with enemiesInitial.
  enemiesNum = dLevel.enemiesInitial - dLevel.enemiesIncr;
}



int Levels::new_level_check(int enemiesPlaying,WorldP,LocatorP,
                              int,Boolean &,ostrstream &,Timer &,
                              IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    return 1;
  }
  else {
    return -1;
  }
}



void Levels::new_level(int level,WorldP world,LocatorP locator,
                       const DifficultyLevel &dLevel,
                       ostrstream &lStr,ostrstream &lTitleStr,
                       IPhysicalManagerP manager,int) {
  world->reset();  

//  clean_physicals(True,world,locator,manager);
  clean_physicals(False,world,locator,manager);


  lTitleStr << "[" << level << "] Kill All Machines" << ends;
  lStr << "Level: " << level << ends;

  enemiesNum += dLevel.enemiesIncr;
  enemiesNum = Utils::minimum(enemiesNum,dLevel.enemiesMax);
  for (int m = 0; m < enemiesNum; m++) {
    manager->create_enemy(NULL);
  }
}



void Levels::new_level_set_timer(Timer &)
{}



void Levels::refill_enemies(Boolean,WorldP,LocatorP,
                            IPhysicalManagerP) 
{
  // Never refill.
}



unsigned int Levels::get_soundtrack() {
  return SoundNames::SOUNDTRACK_LEVELS;
}



KillKillKill::KillKillKill() {
  justReset = False;
  enemiesNum = 0;
}



GameStyleP KillKillKill::clone() {
  GameStyleP ret = new KillKillKill();
  assert(ret);
  return ret;
}



void KillKillKill::describe(ostrstream &str) {
  str << "Every human and machine for him/her/itself." << ends;
}



GameStyleType KillKillKill::get_type() {
  return KILL;
}



Boolean KillKillKill::class_friends() {
  return False;
}  



char *KillKillKill::can_reset(int humansNumNext,int enemiesNumNext,Boolean) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  if (humansNumNext + enemiesNumNext < 2) {
    return Utils::strdup("YOU NEED AT LEAST 2 HUMAN OR MACHINE PLAYERS TO "
	                       "PLAY KILL, KILL, KILL");
  }
  return NULL;
}



Boolean KillKillKill::need_difficulty(int enemiesNumNext) {
  return (enemiesNumNext > 0);
}



void KillKillKill::reset(WorldP,LocatorP,const DifficultyLevel &,
                         int enemiesNumNext) {
  enemiesNum = enemiesNumNext;
  
  justReset = True;
}



Boolean KillKillKill::game_over_check(int humansPlaying,int enemiesPlaying) {
  if (humansPlaying == HU_UNSPECIFIED) {
    return False;
  }

  return (humansPlaying + enemiesPlaying <= 1); 
}



void KillKillKill::new_level(int level,WorldP world,LocatorP locator,
                             const DifficultyLevel &,
                             ostrstream &lStr,ostrstream &lTitleStr,
                             IPhysicalManagerP manager,int) {
  world->reset();

  // Don't wipe out the Enemies.
  clean_physicals(True,world,locator,manager);

  lTitleStr << "Level: " << level << ends;
  lStr << "Level: " << level << ends;

  // Only create enemies first time or if enemiesRefill is set.
  if (justReset) {
    justReset = False;
    for (int m = 0; m < enemiesNum; m++) {
      manager->create_enemy(NULL);
    }
  }
}



void KillKillKill::refill_enemies(Boolean enemiesRefill,WorldP,LocatorP locator,
                                  IPhysicalManagerP manager) {
  // Only refill enemies if enemiesRefill flag is set.
  if (!enemiesRefill) {
    return;
  }  

  int diff = enemiesNum - locator->enemies_alive();
  assert(diff >= 0);
  
  for (int n = 0; n < diff; n++) {
    manager->create_enemy(NULL);
  }
}



SoundName KillKillKill::get_midisoundtrack() {
  return SoundNames::KILL_SOUNDTRACK;
}



Duel::Duel() {
}



GameStyleP Duel::clone() {
  GameStyleP ret = new Duel();
  assert(ret);
  return ret;
}



void Duel::describe(ostrstream &str) {
  str << "Human vs. human battle to the death." 
	    << "\n" << "Each human has 3 lives." << ends;
}



GameStyleType Duel::get_type() {
  return DUEL;
}



Boolean Duel::need_difficulty(int enemiesNumNext) {
  return (enemiesNumNext > 0);
}



char *Duel::can_reset(int humansNumNext,int,Boolean cooperative) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  if (humansNumNext < 2) {
    return Utils::strdup("YOU NEED AT LEAST 2 HUMAN PLAYERS FOR A DUEL");
  }
  if (cooperative) {
    return Utils::strdup("CAN'T PLAY A DUEL WITH COOPERATIVE MODE ON");
  }
  return NULL;
}



void Duel::reset(WorldP,LocatorP,const DifficultyLevel &,
                 int enemiesNumNext) {
  enemiesNum = enemiesNumNext;
  
  justReset = True;
}



Boolean Duel::game_over_check(int humansPlaying,int) {
  if (humansPlaying == HU_UNSPECIFIED) {
    return False;
  }

  return (humansPlaying <= 1);
}



void Duel::new_level(int level,WorldP world,LocatorP locator,
                     const DifficultyLevel &,
                     ostrstream &lStr,ostrstream &lTitleStr,
                     IPhysicalManagerP manager,int) {
  world->reset();
  clean_physicals(False,world,locator,manager);

  lTitleStr << "Level: " << level << ends;
  lStr << "Level: " << level << ends;

  // Only create enemies first time.
  if (justReset) {
    justReset = False;
    for (int m = 0; m < enemiesNum; m++) {
      manager->create_enemy(NULL);
    }
  }
}



void Duel::refill_enemies(Boolean enemiesRefill,WorldP,LocatorP locator,
                          IPhysicalManagerP manager) {
  // Only refill enemies if enemiesRefill flag is set.
  if (!enemiesRefill) {
    return;
  }  

  int diff = enemiesNum - locator->enemies_alive();
  assert(diff >= 0);
  
  for (int n = 0; n < diff; n++) {
    manager->create_enemy(NULL);
  }
}



ExtendedDuel::ExtendedDuel() 
{}



GameStyleP ExtendedDuel::clone() {
  GameStyleP ret = new ExtendedDuel();
  assert(ret);
  return ret;
}



void ExtendedDuel::describe(ostrstream &str) {
  str << "Human vs. human battle to the death." 
	    << "\n" << "Unlimited lives." << ends;
}



GameStyleType ExtendedDuel::get_type() {
  return EXTENDED;
}



int ExtendedDuel::human_initial_lives() {
  return IT_INFINITE_LIVES;  
}




Boolean ExtendedDuel::need_difficulty(int enemiesNumNext) {
  return (enemiesNumNext > 0);
}



char *ExtendedDuel::can_reset(int humansNumNext,int,Boolean cooperative) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  if (humansNumNext < 2) {
    return Utils::strdup("YOU NEED AT LEAST 2 HUMAN PLAYERS FOR A DUEL");
  }
  if (cooperative) {
    return Utils::strdup("CAN'T PLAY A DUEL WITH COOPERATIVE MODE ON");
  }
  return NULL;
}



void ExtendedDuel::reset(WorldP,LocatorP,const DifficultyLevel &,
                         int enemiesNumNext) {
  enemiesNum = enemiesNumNext;  
  
  justReset = True;
}



Boolean ExtendedDuel::game_over_check(int humansPlaying,int) {
  if (humansPlaying == HU_UNSPECIFIED) {
    return False;
  }

  return (humansPlaying <= 1);
}



void ExtendedDuel::new_level(int level,WorldP world,LocatorP locator,
                             const DifficultyLevel &,
                             ostrstream &lStr,ostrstream &lTitleStr,
                             IPhysicalManagerP manager,int) {
  world->reset();
  clean_physicals(False,world,locator,manager);

  lTitleStr << "Level: " << level << ends;
  lStr << "Level: " << level << ends;

  // Only create enemies first time.
  if (justReset) {
    justReset = False;
    for (int m = 0; m < enemiesNum; m++) {
      manager->create_enemy(NULL);
    }
  }
}



void ExtendedDuel::refill_enemies(Boolean enemiesRefill,WorldP,LocatorP locator,
                                  IPhysicalManagerP manager) {
  // Only refill enemies if enemiesRefill flag is set.
  if (!enemiesRefill) {
    return;
  }  

  int diff = enemiesNum - locator->enemies_alive();
  assert(diff >= 0);
  
  for (int n = 0; n < diff; n++) {
    manager->create_enemy(NULL);
  }
}



Training::Training() 
{}



GameStyleP Training::clone() {
  GameStyleP ret = new Training();
  assert(ret);
  return ret;
}



void Training::describe(ostrstream &str) {
  str << "No enemies.  Useful for learning the controls." << ends;
}



GameStyleType Training::get_type() {
  return TRAINING;
}



Boolean Training::need_difficulty(int) {
  return False;
}



void Training::reset(WorldP,LocatorP,const DifficultyLevel &,int)
{}



void Training::new_level(int level,WorldP world,LocatorP locator,
                        const DifficultyLevel &,
                        ostrstream &lStr,ostrstream &lTitleStr,
                        IPhysicalManagerP manager,int) {
  world->reset();
  clean_physicals(False,world,locator,manager);

  lTitleStr << "Level: " << level << ends;
  lStr << "Level: " << level << ends;
}



void Training::refill_enemies(Boolean,WorldP,LocatorP,IPhysicalManagerP)
{}



Scenarios::Scenarios() {
  scenario = NULL;
}



Scenarios::~Scenarios() {
  delete scenario;
}



GameStyleP Scenarios::clone() {
  GameStyleP ret = new Scenarios();
  assert(ret);
  return ret;
}



void Scenarios::describe(ostrstream &str) {
  str << "A number of different scenarios." << "\n"
	    << "You must complete each scenario to continue on to the "
	    << "next one." << ends;
}



GameStyleType Scenarios::get_type() {
  return SCENARIOS;
}



Boolean Scenarios::class_friends() {
  assert(scenario);
  return scenario->class_friends();
}



int Scenarios::human_initial_lives() {
  return HUMAN_LIVES_SCENARIOS;
}



void Scenarios::set_human_data(HumanP h,WorldP w,LocatorP l) {
  assert(scenario);
  scenario->set_human_data(h,w,l);
}  



Pos Scenarios::human_initial_pos(WorldP w,LocatorP l,const Size &s) {
  // defer to specific scenario
  assert(scenario);
  return scenario->human_initial_pos(w,l,s);
}



Boolean Scenarios::can_refill_game_objects() {
  // defer to specific scenario
  assert(scenario);
  return scenario->can_refill_game_objects();
}



char *Scenarios::can_reset(int humansNumNext,int,Boolean) {
  if (humansNumNext == HU_UNSPECIFIED) {
    return NULL;
  }
  if (humansNumNext < 1) {
    return Utils::strdup("SCENARIOS REQUIRES AT LEAST ONE HUMAN PLAYER");
  }
  return NULL;
}



void Scenarios::reset(WorldP,LocatorP,const DifficultyLevel &,int) {
  delete scenario;
  scenario = NULL;
}



int Scenarios::new_level_check(int enemiesPlaying,WorldP w,LocatorP l,
                             int level,Boolean &lStrChanged,
                               ostrstream &levelStr,Timer &timer,
                             IPhysicalManagerP manager) {
  assert(scenario);
  return scenario->new_level_check(enemiesPlaying,w,l,level,
                                   lStrChanged,levelStr,timer,manager); 
}



Boolean Scenarios::advance_level() {
  if (scenario) {
    return scenario->advance_level();
  }
  else {
    // For first time.
    return True;
  }
}



Boolean Scenarios::award_bonus() {
  if (scenario) {
    return scenario->award_bonus();
  }
  else {
    // For first time.
    return False;
  }
}



void Scenarios::new_level(int level,WorldP world,LocatorP locator,
                       const DifficultyLevel &dLevel,
                       ostrstream &lStr,ostrstream &lTitleStr,
                       IPhysicalManagerP manager,int humansNum) {
  // Works even if scenario is NULL.
  if (!scenario || scenario->advance_level()) {
    // Sets new value for this->scenario.
    choose_scenario();
  }

  scenario->setup_world(world,locator,dLevel);

  world->reset();

  clean_physicals(False,world,locator,manager);

  scenario->new_level(level,world,locator,dLevel,
                      lStr,lTitleStr,manager,humansNum);
}



void Scenarios::new_level_set_timer(Timer &timer) {
  assert(scenario);
  scenario->new_level_set_timer(timer);
}



void Scenarios::refill_enemies(Boolean eRefill,WorldP w,LocatorP l,
                               IPhysicalManagerP manager) {
  assert(scenario);
  scenario->refill_enemies(eRefill,w,l,manager);
}



void Scenarios::set_override(char *val) {
//  override = Utils::strdup(val);  
  override = val;
}



void Scenarios::choose_scenario() {
  ScenarioType prevScenario = 
    scenario ? scenario->get_scenario_type() : SCENARIO_NONE;
  delete scenario;
  scenario = NULL;

  if (override) {
	  if (!(strcmp("exterminate",override))) {
      scenario = new Exterminate();
    }
	  else if (!(strcmp("bonus",override))) {
      scenario = new Bonus();
    }
	  else if (!(strcmp("hive",override))) {
      scenario = new Hive();
    }
	  else if (!(strcmp("flag",override))) {
      scenario = new CaptureTheFlag();
    }
	  else if (!(strcmp("baby-seals",override))) {
      scenario = new Seals();
    }
	  else if (!(strcmp("anti-baby-seals",override))) {
      scenario = new AntiSeals();
    }
	  else if (!(strcmp("fire-demon",override))) {
      scenario = new KillTheFireDemon();
    }
	  else if (!(strcmp("dragon",override))) {
      scenario = new KillTheDragon();
    }
	  else if (!(strcmp("zig-zag",override))) {
      scenario = new ZigZag();
    }
	  else if (!(strcmp("the-pound",override))) {
      scenario = new ThePound();
    }
	  else if (!(strcmp("japan-town",override))) {
      scenario = new JapanTown();
    }
  }

  // We chose one from the override, so we are done.
  if (scenario) {
    return;
  }   

  // Keep trying until we choose one that isn't the same as the last one.
  while (True) {
    switch (Utils::choose(10)) {
      case 0:
        scenario = new Bonus();
        break;
      case 1:
        scenario = new Hive();
        break;
      case 2:
        scenario = new CaptureTheFlag();
        break;
      case 3:
        scenario = new Seals();
        break;
      case 4:
        scenario = new AntiSeals();
        break;
      case 5:
        scenario = new KillTheFireDemon();
        break;
      case 6:
        scenario = new KillTheDragon();
        break;
      case 7:
        scenario = new ZigZag();
        break;
      case 8:
        scenario = new ThePound();
        break;
      case 9:
        scenario = new JapanTown();
        break;
      default:
        assert(0);
    }
    // Done, don't choose same scenario twice in a row.
    if (scenario->get_scenario_type() != prevScenario) {
      return;
    }
    // Try again.
    else {
      delete scenario;
      scenario = NULL;
    }
  }
}



void Scenarios::filter_weapons_and_other_items(LocatorP l,
                                               int &wNum,const PhysicalContext *w[],
                                               int &oINum,const PhysicalContext *oI[]) {
  assert(scenario);
  scenario->filter_weapons_and_other_items(l,wNum,w,oINum,oI);
}



unsigned int Scenarios::get_soundtrack() {
  // Needs to work before first new level.
  if (scenario) {
    return scenario->get_soundtrack();
  }
  else {
    return GameStyle::get_soundtrack();
  }
}



SoundName Scenarios::get_midisoundtrack() {
  // Needs to work before first new level.
  if (scenario) {
    return scenario->get_midisoundtrack();
  }
  else {
    return GameStyle::get_midisoundtrack();
  }
}



// All enemy dogs on same team.
static Boolean Scenarios_dog_team_member(PhysicalP p) {
  IntelP intel = p->get_intel();
  if (intel && intel->is_enemy() && p->get_class_id() == A_Dog) {
    return True;
  }
  return False;
}



Boolean Scenarios::dog_team(LocatorP,PhysicalP p1,PhysicalP p2) {
  return Scenarios_dog_team_member(p1) && Scenarios_dog_team_member(p2);
}



char *Scenarios::override = NULL;




////////////////////////// Specific Scenarios ////////////////////////////////////


Scenario::Scenario()
{}



Scenario::~Scenario()
{}



Boolean Scenario::class_friends() {
  return True;
}



void Scenario::set_human_data(HumanP,WorldP,LocatorP)
{}  



Pos Scenario::human_initial_pos(WorldP world,LocatorP,const Size &size) {
  return world->empty_rect(size);
}



Boolean Scenario::can_refill_game_objects() {
  return True;
}



void Scenario::new_level_set_timer(Timer &)
{}



Boolean Scenario::advance_level() {
  return True;
}



Boolean Scenario::award_bonus() {
  return False;
}



void Scenario::refill_enemies(Boolean,WorldP,LocatorP,IPhysicalManagerP)
{}



void Scenario::filter_weapons_and_other_items(LocatorP,int &,const PhysicalContext *[],
                                              int &,const PhysicalContext *[]) {
}



unsigned int Scenario::get_soundtrack() {
  return SoundNames::SOUNDTRACK;
}



Exterminate::Exterminate()
{}



int Exterminate::new_level_check(int enemiesPlaying,WorldP,LocatorP,
                                 int,Boolean &,ostrstream &,Timer &,
                                 IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    return 1;
  }
  return -1;  
}



void Exterminate::setup_world(WorldP,LocatorP,const DifficultyLevel &) {
  // Nothing to do.
}



void Exterminate::new_level(int level,WorldP,LocatorP,
                            const DifficultyLevel &dLevel,
                            ostrstream &lStr,ostrstream &lTitleStr,
                            IPhysicalManagerP manager,int) {
  lTitleStr << "[" << level << "] EXTERMINATE" << ends;
  lStr << "[" << level << "] EXTERMINATE\nKill all machines." << ends;

  // Doesn't increment like LEVELS game style
  enemiesNum = dLevel.enemiesIncr;

  for (int m = 0; m < enemiesNum; m++) {
    manager->create_enemy(NULL);
  }
}



Bonus::Bonus()
{
  frogsRemaining = 0;
}



Boolean Bonus::can_refill_game_objects() {
  return False;
}



int Bonus::new_level_check(int,WorldP,LocatorP locator,
                           int level,Boolean &lStrChanged,
                           ostrstream &levelStr,Timer &timer,
                           IPhysicalManagerP) {
  // Ran out of time.
  if (timer.ready()) {
    locator->message_enq(Utils::strdup("FAILED TO KILL ALL THE FROGS"));
    return 0;
  }

  // Count number of Frogs still alive.
  int remaining = 0;
  for (int n = 0; n < SCENARIO_BONUS_FROGS; n++) {
    PhysicalP p = locator->lookup(frogs[n]);
	  if (p && p->alive()) {
      remaining++;
    }
  }
	  
  // Update ui->
  if (remaining != frogsRemaining) {
    frogsRemaining = remaining;
    levelStr << "[" << level << "] BONUS LEVEL\nfrogs remaining: " 
	      << remaining << ends;
    lStrChanged = True;
    if (remaining > 0) {
	    ostrstream arenaStr;
	    arenaStr << remaining << " Frog" << 
	      (remaining > 1 ? "s" : "") << " Remaining" << ends;
	    locator->arena_message_enq(arenaStr.str());
    }

    // Will only be called once per level.
    if (frogsRemaining == 0) {
//      ostrstream ostr;
//      ostr << "**** You must feel proud of yourself for killing all "
//      << "those defenseless frogs. ****" << ends;
//      locator->message_enq(ostr.str());
      locator->arena_message_enq(Utils::strdup("Got em all"));
      return 1;
    }
  }

  return -1;
}	  



void Bonus::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(2,1);
  world->set_rooms_next(rooms);
}



void Bonus::new_level(int level,WorldP world,LocatorP locator,
                      const DifficultyLevel &,
                      ostrstream &lStr,ostrstream &lTitleStr,
                      IPhysicalManagerP manager,int) {
  // Create frogs
  for (int n = 0; n < SCENARIO_BONUS_FROGS; n++) {
	  PhysicalP p = manager->create_enemy(manager->enemy_physical(A_Frog));
	  frogs[n] = p->get_id();
  }

  // Create one weapon
  PhysicalP weap = NULL;
  switch(Utils::choose(5)) {
    case 0:
    weap = new FThrower(world,locator,world->empty_rect(FThrower::get_size_max()));
    break;
    case 1:
    weap = new Grenades(world,locator,world->empty_rect(Grenades::get_size_max()));
    break;
    case 2:
    weap = new Chainsaw(world,locator,world->empty_rect(Chainsaw::get_size_max()));
    break;
    case 3:
    weap = new MGun(world,locator,world->empty_rect(MGun::get_size_max()));
    break;
    case 4:
    weap = new Stars(world,locator,world->empty_rect(Pistol::get_size_max()));
    break;
  }
  assert(weap);
  locator->add(weap);

  frogsRemaining = SCENARIO_BONUS_FROGS;

  lTitleStr << "[" << level << "] Bonus Level: Kill " 
		  << (int)SCENARIO_BONUS_FROGS << " Frogs"  << ends;
  lStr << "[" << level << "] BONUS LEVEL\nfrogs remaining: " 
	     << (int)SCENARIO_BONUS_FROGS << ends;
}



void Bonus::new_level_set_timer(Timer &timer) {
  // Limited time to kill the frogs.
  timer.set(SCENARIO_BONUS_TIME);
}



Hive::Hive() {
}



int Hive::new_level_check(int,WorldP,LocatorP locator,
                          int,Boolean &,ostrstream &,Timer &,
                          IPhysicalManagerP) {
  PhysicalP xit = locator->lookup(xitId);
  assert(xit); // The Xit should never be destroyed.
  if (((TouchableP)xit)->wasTouched()) {
    return 0;
  }
  return -1;  
}



Boolean Hive::award_bonus() {
  return True;
}



void Hive::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(5,5);
  world->set_rooms_next(rooms);
}



void Hive::new_level(int level,WorldP world,LocatorP locator,
                     const DifficultyLevel &,
                     ostrstream &lStr,ostrstream &lTitleStr,
                     IPhysicalManagerP manager,int) {
	// Aliens are created in Game::clock.

  // So Aliens and Huggers are friends.
  locator->add_team(aliens_team,NULL);
	
  // Create the Xit.
  Pos pos = world->empty_accessible_rect(Xit::get_size_max());
  PhysicalP p = new Xit(world,locator,pos);
  assert(p);
  locator->add(p);
  xitId = p->get_id();
  
  // Create some eggs.
  IntelOptions eggOptions;
  ITmask eggMask = manager->intel_options_for(eggOptions,A_Alien);
  
  for (int n = 0; n < SCENARIO_HIVE_EGGS; n++) {
    Pos pos = world->empty_rect(Egg::get_size_max());
    PhysicalP egg = new Egg(world,locator,pos,eggOptions,eggMask);
    assert(egg);
    locator->add(egg);
  }
  
  lTitleStr << "[" << level << "] HIVE" << ends;
  lStr << "[" << level << "] HIVE.\nFind the exit." << ends;
}



void Hive::refill_enemies(Boolean,WorldP,LocatorP locator,IPhysicalManagerP manager) {
  int diff = SCENARIO_HIVE_ALIENS - locator->enemies_alive();

  // diff might be less than zero if some RedHuggers have come into existence.
  //assert(diff >= 0);

  for (int n = 0; n < diff; n++) {
    manager->create_enemy(manager->enemy_physical(A_Alien));
  }
}



static Boolean Hive_aliens_team_member(PhysicalP p) {
  // Eggs are on the team, even though they don't have intelligence.
  if (p->get_class_id() == A_Egg) {
    return True;
  }

  // Creature in process of being hugged.
  if (p->is_creature() && ((CreatureP)p)->get_alien_immune()) {
    return True;
  }

  IntelP intel = p->get_intel();
  if (!intel) {
    return False;
  }

  if (intel->is_human()) {
    return False;
  }

  // Hugger or alien.
  if (p->get_class_id() == A_Alien || p->get_ability(AB_Hugger)) {
    return True;
  }

  return False;
}



Boolean Hive::aliens_team(LocatorP,PhysicalP p1,PhysicalP p2) {
  return Hive_aliens_team_member(p1) && Hive_aliens_team_member(p2);
}



CaptureTheFlag::CaptureTheFlag()
{}



int CaptureTheFlag::new_level_check(int,WorldP,LocatorP locator,
                                    int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &,
                                    IPhysicalManagerP manager) {
  int fRemaining = 0;
  for (int n = 0; n < SCENARIO_FLAG_FLAGS; n++) {
    PhysicalP p = locator->lookup(flagIds[n]);
    if (p) {
	    fRemaining++;
	    if (((TouchableP)p)->wasTouched()) {
        manager->kill_physical(p);
      }
    }
  }


  if (fRemaining != flagsRemaining) {
    flagsRemaining = fRemaining;
    levelStr <<  "[" <<  level <<  "] COLLECT " 
	     <<  (int)SCENARIO_FLAG_FLAGS 
	     << " FLAGS\nremaining: "<<  flagsRemaining <<   ends;
    lStrChanged = True;

    ostrstream arenaStr;
    if (fRemaining != 0) {
      arenaStr << flagsRemaining << " Flag" <<
	      (flagsRemaining > 1 ? "s" : "") << " Remaining" << ends;
    }
    else {
      arenaStr << "All Flags Collected" << ends;
    }
      
    locator->arena_message_enq(arenaStr.str());
  }

  if (fRemaining == 0) { // All flags are gone, so end level.
    return 1;
  }


  return -1;
}



void CaptureTheFlag::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  world->set_special_map_next(World::FLAG);
}



void CaptureTheFlag::new_level(int level,WorldP world,LocatorP locator,
                               const DifficultyLevel &,
                               ostrstream &lStr,ostrstream &lTitleStr,
                               IPhysicalManagerP,int) {
  for (int n = 0; n < SCENARIO_FLAG_FLAGS; n++) {
	  Pos pos = world->empty_accessible_rect(Flag::get_size_max());
	  PhysicalP p = new Flag(world,locator,pos);
	  assert(p);
	  locator->add(p);
	  flagIds[n] = p->get_id();
  }
  flagsRemaining = SCENARIO_FLAG_FLAGS;

  lTitleStr << "[" << level << "] Capture The Flag: " 
		  << (int)SCENARIO_FLAG_FLAGS << " Flags" << ends;
  lStr << "[" << level << "] COLLECT " << (int)SCENARIO_FLAG_FLAGS 
	     << " FLAGS.\nremaining: " << (int)SCENARIO_FLAG_FLAGS << ends;
}



void CaptureTheFlag::refill_enemies(Boolean,WorldP,LocatorP locator,IPhysicalManagerP manager) {
  int diff = SCENARIO_FLAG_ENEMIES - locator->enemies_alive();
  assert(diff >= 0);
  
  for (int n = 0; n < diff; n++) {
    manager->create_enemy(NULL);
  }  
}



GenericSeals::GenericSeals() {
  dontAdvance = False;
  sealsOut = sealsSafe = sealsActive = sealsDead = 0;
}



void GenericSeals::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  world->set_special_map_next(World::SEALS);
}



void GenericSeals::new_level(int level,WorldP world,LocatorP locator,
                         const DifficultyLevel &,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP,int) {
  // Add a team for each human in the game.
  // Don't use locator->humans_registered() because they may not have been
  // registered at this time.
  TeamOptions ops;
  ops.membersDontCollide = False;

  // Only need one team rule now.
  Boolean (*teamFntn)(LocatorP,PhysicalP,PhysicalP) = get_team();
  locator->add_team(teamFntn,&ops);


  // You have to save/kill the seals to go on.
  dontAdvance = True;

  // Know these areas are open because they are within the 
  // (OBJECT_COL_MAX,OBJECT_ROW_MAX) margain around the World edge.
  Size worldSize = world->get_size();
  Size homeSize = Home::get_size();
  Size trapdoorSize = Trapdoor::get_size();
  Pos homePos((worldSize.width - homeSize.width) / 2,
		    worldSize.height - WSQUARE_HEIGHT - homeSize.height);
  PhysicalP home = new Home(world,locator,homePos);
  assert(home);
  locator->add(home);
  homeId = home->get_id();


  // Create Trapdoor after Home because it needs homeId.
  Pos trapdoorPos(3 * WSQUARE_WIDTH,WSQUARE_HEIGHT);
  Pos trapdoorPos2(worldSize.width - 3 * WSQUARE_WIDTH - trapdoorSize.width,
                   WSQUARE_HEIGHT);

  PhysicalP trapdoor = new Trapdoor(world,locator,trapdoorPos,homeId);
  locator->add(trapdoor);
  trapdoorIds[0] = trapdoor->get_id();

  PhysicalP trapdoor2 = new Trapdoor(world,locator,trapdoorPos2,homeId);
  locator->add(trapdoor2);
  trapdoorIds[1] = trapdoor2->get_id();

  sealsOut = sealsSafe =  sealsActive = sealsDead = 0;

  // lStr and lTitleStr were in the wrong order, ok now.
  set_level_strings(level,lStr,lTitleStr); 
}



Boolean GenericSeals::advance_level() {
  return !dontAdvance;
}



void GenericSeals::refill_enemies(Boolean,WorldP,LocatorP locator,IPhysicalManagerP manager) {
  int diff = SCENARIO_SEALS_ENEMIES - locator->enemies_alive();
  assert(diff >= 0);
  
  // Change the num argument to enemy_physical if this list is changed.
  int list[] = {A_Ninja,A_Dog,A_Walker};  // No more Hero or Alien

  for (int n = 0; n < diff; n++) {
    manager->create_enemy(manager->enemy_physical(list,3));
  }
}



unsigned int GenericSeals::get_soundtrack() {
  return SoundNames::SOUNDTRACK_SEAL;
}



void GenericSeals::compute_out_safe_active_dead(LocatorP locator,int &lemOut,int &lemSafe,int &lemActive,int &lemDead) {  
  // Lookup home and trapdoors.
  HomeP home = (HomeP)locator->lookup(homeId);
  TrapdoorP trapdoor = (TrapdoorP)locator->lookup(trapdoorIds[0]);
  TrapdoorP trapdoor2 = (TrapdoorP)locator->lookup(trapdoorIds[1]);
  assert(home && trapdoor && trapdoor2);

  // Count seals out, active, and safe.
  // Care about SealIntels, not Seals.
  IntelId seals[SCENARIO_SEALS_SEALS]; 
  lemOut = 0;
  trapdoor->append_seals_out(lemOut,seals);
  trapdoor2->append_seals_out(lemOut,seals);
 
  lemSafe = home->get_seals_safe();
  lemActive = 0;
  lemDead = 0;
  for (int n = 0; n < lemOut; n++) {
    IntelP sealIntel = locator->lookup(seals[n]);	      
    if (sealIntel) {
      PhysicalP seal = locator->lookup(sealIntel->get_id());
	    if (seal) {
        if (seal->alive()) {
	        lemActive++;
        }
        else {
          lemDead++;
        }
      }
    }
    else {
      lemDead++;
    }
  }

  // Don't  count safe seals as dead.
  lemDead -= lemSafe;
  assert(lemDead >= 0);
  lemDead = Utils::maximum(lemDead,0); // keep playing anyway.

  // Can't use this because, for one turn, lemOut is incremented, but lemActive hasn't
  // caught up.
  // lemDead = lemOut - lemSafe - lemActive;
}



Seals::Seals() {
}



int Seals::new_level_check(int,WorldP,LocatorP locator,
                              int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &,
                              IPhysicalManagerP) {  
  int lemOut,lemSafe,lemActive,lemDead;
  compute_out_safe_active_dead(locator,lemOut,lemSafe,lemActive,lemDead);

  // unnecessary.
  sealsDead = lemDead;
      
  // Update the Ui if necessary.
  if (lemSafe != sealsSafe || lemOut != sealsOut ||
      lemActive != sealsActive) {

    // Only update arena string if sealsSafe has changed.
    if (lemSafe != sealsSafe && lemSafe != SCENARIO_SEALS_NEED) {
      ostrstream arenaStr;
      arenaStr << "Save " << (SCENARIO_SEALS_NEED - lemSafe)
               << " More Baby Seals" << ends;
      locator->arena_message_enq(arenaStr.str());
    }

    sealsSafe = lemSafe;
    sealsOut = lemOut;
    sealsActive = lemActive;
    levelStr << "[" << level << "] SAVE " 
            << (int)SCENARIO_SEALS_NEED << " BABY SEALS\n" 
            << "out: " << sealsOut << " safe: " << sealsSafe
            << " dead: " << lemDead
            << ends;
    lStrChanged = True;

    // New level if enough seals are safe or too many are dead.
    if (lemDead > (SCENARIO_SEALS_SEALS - SCENARIO_SEALS_NEED)
	      || lemSafe >= SCENARIO_SEALS_NEED) {
      if (lemSafe < SCENARIO_SEALS_NEED) {
	      ostrstream msg;
	      msg << "YOU FAILED TO SAVE " << (int)SCENARIO_SEALS_NEED
	          << " BABY SEALS. TRY THIS LEVEL AGAIN." << ends;
	      locator->arena_message_enq(msg.str());
      }
      else {
        locator->arena_message_enq(Utils::strdup("Good work.  Go to the next level."));

        // Success!! 
        dontAdvance = False;
      }	

      return 1;
    }
  }

  return -1;    
}



Boolean Seals::award_bonus() {
  return True;
}



Boolean (* Seals::get_team())(LocatorP,PhysicalP,PhysicalP) {
  return seals_team;
}


  
void Seals::set_level_strings(int level,ostrstream &lStr,ostrstream &lTitleStr) {
  lTitleStr << "[" << level << "] SAVE THE BABY SEALS" << ends;
  lStr << "[" << level << "] SAVE "<< 
	  (int)SCENARIO_SEALS_NEED << " BABY SEALS\n" 
	     << "out: 0 safe: 0 dead: 0" << ends;
}



/* Team consists of:
     Seal X Seal
     Seal X (Human | HumanSlave)
     (Human | HumanSlave) X Seal
   Does not have:
     (Human | HumanSlave) X (Human | HumanSlave)
 */
static Boolean Seals_team_check(LocatorP locator,PhysicalP p1,PhysicalP p2) {
  IntelP p1Intel = p1->get_intel();
  IntelP p2Intel = p2->get_intel();
  if (!p1Intel || !p2Intel) {
    return False;
  }

  // p is a Seal
  if (p1Intel->is_seal_intel()) {
    if (p2Intel->is_seal_intel()) {
      return True;
    }
    
    if (p2Intel->is_human()) {
      return True;
    }
    // p2Intel is a machine
    else {
      IntelId p2MasterId = ((MachineP)p2Intel)->get_master_intel_id();
      IntelP p2Master = locator->lookup(p2MasterId);
      if (p2Master && p2Master->is_human()) {
        return True;
      }    
    }
  }
  return False;
}



Boolean Seals::seals_team(LocatorP locator,
			  PhysicalP p1,PhysicalP p2) {
  if (Seals_team_check(locator,p1,p2)) {
    return True;
  }
  if (Seals_team_check(locator,p2,p1)) {
    return True;
  }
  return False;
}



AntiSeals::AntiSeals() {
}



int AntiSeals::new_level_check(int,WorldP,LocatorP locator,
                                  int level,Boolean &lStrChanged,ostrstream &levelStr,Timer &,
                                  IPhysicalManagerP) {  
  // level not over by default.
  int ret = -1;
  
  int lemOut,lemSafe,lemActive,lemDead;
  compute_out_safe_active_dead(locator,lemOut,lemSafe,lemActive,lemDead);


  // Failure
  if (lemSafe > SCENARIO_ANTI_SEALS_MAX_SLIPPAGE) {
	  ostrstream msg;
#if 0
    // Not enough space on screen, need two lines.
	  msg << "YOU LET TOO MANY OF THE FAT BASTARDS GET AWAY\n"
	      << "Try this level again" << ends;
#else
	  msg << "YOU LET TOO MANY OF THE FAT BASTARDS GET AWAY" << ends;
#endif
	  locator->arena_message_enq(msg.str());

    // start new level
    ret = 1;
  }
  // Failure message takes precedence over the rest.
  else {    
    if (lemSafe != sealsSafe) {
      assert(lemSafe > sealsSafe); // strictly increasing.
      locator->arena_message_enq(Utils::strdup("Careful. One just got away."));
    }
    if (lemDead != sealsDead || lemSafe != sealsSafe) {
      int lemToKill = SCENARIO_SEALS_SEALS - lemDead - lemSafe;

      // Success!!    
      if (lemToKill == 0) {
        locator->arena_message_enq(Utils::strdup("Satan approves of your Blood-Lust. Go to the next level."));

        // start new level
        dontAdvance = False;
        ret = 1;
      }
      // Update UI
      else if (lemSafe == sealsSafe) {  // o.w. the "one got away" message takes priority
        ostrstream arenaStr;
        arenaStr << "Kill "
          << lemToKill << " More Baby Seals" << ends;
        locator->arena_message_enq(arenaStr.str());

        levelStr << "[" << level << "] KILL " 
                << lemToKill << " BABY SEALS" << ends;
        lStrChanged = True;
      }
    }  // lemDead != sealsDead
  } // lemSafe check


  sealsOut = lemOut;
  sealsActive = lemActive;
  sealsDead = lemDead;
  sealsSafe = lemSafe;

  return ret;    
}



Boolean (*AntiSeals::get_team())(LocatorP,PhysicalP,PhysicalP) {
  return anti_seals_team;
}


  
void AntiSeals::set_level_strings(int level,ostrstream &lStr,ostrstream &lTitleStr) {
  lTitleStr << "[" << level << "] KILL THE BABY SEALS" << ends;
  lStr << "[" << level << "] KILL "<< 
	  (int)SCENARIO_SEALS_SEALS << " BABY SEALS" << ends;
}



static Boolean anti_seals_team_check(LocatorP locator,PhysicalP p) {
  IntelP pIntel = p->get_intel();
  if (!pIntel) {
    return False;
  }

  // p is a Seal
  if (pIntel->is_seal_intel()) {
    return True;
  }

  // p is a machine
  if (!pIntel->is_human()) {
    IntelP master = locator->lookup(((MachineP)pIntel)->get_master_intel_id());
    // Slaves of humans are not on the team.
    if (master && master->is_human()) {
      return False;
    }
    return True;
  }
  return False;
}



Boolean AntiSeals::anti_seals_team(LocatorP locator,
				   PhysicalP p1,PhysicalP p2) {
  if (anti_seals_team_check(locator,p1) && 
      anti_seals_team_check(locator,p2)) {
    return True;
  }
  return False;
}



KillTheFireDemon::KillTheFireDemon() {
}



int KillTheFireDemon::new_level_check(int enemiesPlaying,WorldP,LocatorP locator,
                                   int,Boolean &,ostrstream &,Timer &,
                                   IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    locator->arena_message_enq(Utils::strdup("Take That, You Mother-Fucking Spawn From Hell"));
    return 1;
  }
  return -1;    
}



void KillTheFireDemon::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(2,2);
  world->set_rooms_next(rooms);
}



void KillTheFireDemon::new_level(int level,WorldP,LocatorP,
                                 const DifficultyLevel &,
                                 ostrstream &lStr,ostrstream &lTitleStr,
                                 IPhysicalManagerP manager,int) {
  manager->create_enemy(manager->enemy_physical(A_FireDemon));

  lTitleStr << "[" << level << "] FIRE DEMON" << ends;
  lStr << "[" << level << "] Kill the Fire Demon." << ends;
}



Boolean KillTheFireDemon::award_bonus() {
  return True;
}



KillTheDragon::KillTheDragon(){
}



int KillTheDragon::new_level_check(int enemiesPlaying,WorldP,LocatorP locator,
                                   int,Boolean &,ostrstream &,Timer &,
                                   IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    locator->arena_message_enq(Utils::strdup("The Infernal Worm has been vanquished"));
    return 1;
  }
  return -1;    
}



void KillTheDragon::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(2,2);
  world->set_rooms_next(rooms);
}



void KillTheDragon::new_level(int level,WorldP w,LocatorP l,
                                 const DifficultyLevel &,
                                 ostrstream &lStr,ostrstream &lTitleStr,
                                 IPhysicalManagerP manager,int) {
  const PhysicalContext *cx = &Dragon::creatureContext.movingContext.physicalContext;
  Pos pos = w->empty_rect(cx->sizeMax);
  PtrList segments;
  Segmented::create_and_add_composite(segments,w,l,Dragon::SEGMENTS_NUM,pos,cx->create,cx->arg);

  for (int n = 0; n < segments.length(); n++) {
    PhysicalP p = (PhysicalP)segments.get(n);
    // Add intel, register enemy, etc.
    // Pass in False, since we already added the object to the Locator.
    manager->create_enemy(p,False);
  }

  lTitleStr << "[" << level << "] Dragon" << ends;
  lStr << "[" << level << "] Kill the Dragon." << ends;
}



Boolean KillTheDragon::award_bonus() {
  return True;
}



ZigZag::ZigZag(){
}



void ZigZag::set_human_data(HumanP human,WorldP world,LocatorP) {
  // Set depth for zig-zag scenario, so doesn't display it immediatly.
  Rooms worldRooms = world->get_rooms();
  human->set_data((void *)(worldRooms.downMax - 1));
}



Pos ZigZag::human_initial_pos(WorldP world,LocatorP,const Size &size) {
  // ZIG_ZAG, start humans in lower left.

  // Doesn't really need to be accessible, just haven't made the
  // RoomIndex version of World::empty_rect().
  Rooms worldRooms = world->get_rooms();
  RoomIndex lowerLeft(worldRooms.downMax - 1,0);
  // Player starts in lower-left-most room.
  return world->empty_accessible_rect(size,lowerLeft);
}



int ZigZag::new_level_check(int,WorldP world,LocatorP locator,
                            int,Boolean &,ostrstream &,Timer &,
                            IPhysicalManagerP) {
  PhysicalP xit = locator->lookup(xitId);
  assert(xit); // The Xit should never be destroyed.
  if (((TouchableP)xit)->wasTouched()) {
	  return 0;
  }

  // See if we need to inform human of change in depth.
  for (int n = 0; n < locator->humans_registered(); n++) {
    HumanP human = locator->get_human(n);
    if (human) {
      // previous depth
      int depthOld = (int)human->get_data();
      Id id = human->get_id();

      // Get physical for intelligence
      PhysicalP p = locator->lookup(id);
      if (p) {
        // Compute current depth
        const Area &area = p->get_area();
        Loc mSquare = area.middle_wsquare();
        Dim roomDim = world->get_room_dim();
        int depth = mSquare.r / roomDim.rowMax;

        if (depth != depthOld) {
	        // so doesn't get set next turn.
	        human->set_data((void *)depth);

          // Use 1-based counting for the User.
	        ostrstream str;
	        str << "Depth " << (depth + 1) << ends;
	        locator->arena_message_enq(str.str(),p);
        }
      }
  	}
  } // for

  return -1;
}



void ZigZag::setup_world(WorldP world,LocatorP ,const DifficultyLevel &) {
  world->set_special_map_next(World::ZIG_ZAG);
}



void ZigZag::new_level(int level,WorldP world,LocatorP locator,
                       const DifficultyLevel &,
                       ostrstream &lStr,ostrstream &lTitleStr,
                       IPhysicalManagerP,int) {
  Rooms worldRooms = world->get_rooms();
  RoomIndex upperRight(0,worldRooms.acrossMax - 1);

  // Create the Xit.
  Pos pos = world->empty_accessible_rect(Xit::get_size_max(),upperRight);
  PhysicalP p = new Xit(world,locator,pos);
  assert(p);
  locator->add(p);
  xitId = p->get_id();

  // 1-based counting of depth for the user.
  lTitleStr << "[" << level << "] ZIG-ZAG: Depth " 
		  << worldRooms.downMax << ends;
  lStr << "[" << level << "] ZIG-ZAG.\nFind the exit." << ends;
}



void ZigZag::refill_enemies(Boolean,WorldP,LocatorP locator,IPhysicalManagerP manager) {
  int diff = SCENARIO_ZIG_ZAG_ENEMIES - locator->enemies_alive();
  assert(diff >= 0);
  
  for (int n = 0; n < diff; n++) {
    manager->create_enemy(NULL);
  }
}



Boolean ZigZag::award_bonus() {
  return True;
}



ThePound::ThePound(){
}



int ThePound::new_level_check(int enemiesPlaying,WorldP,LocatorP locator,
                              int,Boolean &,ostrstream &,Timer &,
                              IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    locator->arena_message_enq(Utils::strdup("Killed the Mutts"));
    return 1;
  }
  return -1;    
}



void ThePound::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(2,3);
  world->set_rooms_next(rooms);
}



void ThePound::new_level(int level,WorldP,LocatorP locator,
                         const DifficultyLevel &,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int) {
  locator->add_team(Scenarios::dog_team,NULL);

  for (int n = 0; n < SCENARIO_POUND_DOGS; n++) {
    manager->create_enemy(manager->enemy_physical(A_Dog));
  }

  lTitleStr << "[" << level << "] The Pound" << ends;
  lStr << "[" << level << "] The Pound" << ends;
}



JapanTown::JapanTown()
{}



int JapanTown::new_level_check(int enemiesPlaying,WorldP,LocatorP locator,
                              int,Boolean &,ostrstream &,Timer &,
                              IPhysicalManagerP) {
  if (enemiesPlaying == 0) {
    locator->arena_message_enq(Utils::strdup("Damn Ninjas.  Worse than roaches."));
    return 1;
  }
  return -1;    
}



void JapanTown::setup_world(WorldP world,LocatorP,const DifficultyLevel &) {
  Rooms rooms(2,3);
  world->set_rooms_next(rooms);
}



void JapanTown::new_level(int level,WorldP,LocatorP locator,
                         const DifficultyLevel &,
                         ostrstream &lStr,ostrstream &lTitleStr,
                         IPhysicalManagerP manager,int) {
  locator->add_team(JapanTown::ninja_team,NULL);

  for (int n = 0; n < SCENARIO_JAPAN_TOWN_NINJAS; n++) {
    manager->create_enemy(manager->enemy_physical(A_Ninja));
  }

  lTitleStr << "[" << level << "] Japan-Town" << ends;
  lStr << "[" << level << "] Japan-Town" << ends;
}



void JapanTown::filter_weapons_and_other_items(LocatorP locator,
                                               int &weaponsNum,
                                               const PhysicalContext *weapons[],
                                               int &,
                                               const PhysicalContext *[]) {
  weapons[0] = locator->get_context(A_Stars);
  weapons[1] = locator->get_context(A_Lancer);
  weaponsNum = 2;
  assert(weapons[0] && weapons[1]);

  // Leave items alone.
}



static Boolean JapanTown_ninja_team_member(PhysicalP p) {
  IntelP intel = p->get_intel();
  if (intel && intel->is_enemy() && p->get_class_id() == A_Ninja) {
    return True;
  }
  return False;
}



Boolean JapanTown::ninja_team(LocatorP,PhysicalP p1,PhysicalP p2) {
  return JapanTown_ninja_team_member(p1) && JapanTown_ninja_team_member(p2);
}
