initial commit, lordsawar source, slightly modified
[lordsawar] / src / city.cpp
diff --git a/src/city.cpp b/src/city.cpp
new file mode 100644 (file)
index 0000000..1738535
--- /dev/null
@@ -0,0 +1,535 @@
+//  Copyright (C) 2000, 2001, 2003 Michael Bartl
+//  Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006 Ulf Lorenz
+//  Copyright (C) 2002 Mark L. Amidon
+//  Copyright (C) 2005 Andrea Paternesi
+//  Copyright (C) 2006, 2007, 2008, 2009 Ben Asselstine
+//  Copyright (C) 2008 Ole Laursen
+//
+//  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 3 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 Library General Public License for more details.
+//
+//  You should have received a copy of the GNU General Public License
+//  along with this program; if not, write to the Free Software
+//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
+//  02110-1301, USA.
+
+#include <stdio.h>
+#include <algorithm>
+#include <stdlib.h>
+#include <sstream>
+#include "city.h"
+#include "path.h"
+#include "army.h"
+#include "armyprodbase.h"
+#include "hero.h"
+#include "stacklist.h"
+#include "stack.h"
+#include "playerlist.h"
+#include "armysetlist.h"
+#include "citylist.h"
+#include "GameMap.h"
+#include "vectoredunitlist.h"
+#include "vectoredunit.h"
+#include "action.h"
+
+std::string City::d_tag = "city";
+
+using namespace std;
+
+//#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
+#define debug(x)
+
+City::City(Vector<int> pos, guint32 width, string name, guint32 gold, 
+          guint32 numslots)
+    :Ownable((Player *)0), Location(pos, width), Renamable(name),
+    ProdSlotlist(numslots), d_gold(gold), d_defense_level(1), d_burnt(false), 
+    d_vectoring(false), d_vector(Vector<int>(-1,-1)), 
+    d_capital(false), d_capital_owner(0)
+
+{
+  // set the tiles to city type
+  for (unsigned int i = 0; i < getSize(); i++)
+    for (unsigned int j = 0; j < getSize(); j++)
+      {
+       Vector<int> pos = getPos() + Vector<int>(i, j);
+       GameMap::getInstance()->getTile(pos)->setBuilding(Maptile::CITY);
+      }
+}
+
+City::City(XML_Helper* helper, guint32 width)
+    :Ownable(helper), Location(helper, width), Renamable(helper),
+    ProdSlotlist(helper)
+{
+    //initialize the city
+
+    helper->getData(d_defense_level, "defense");
+    
+    helper->getData(d_gold, "gold");
+    helper->getData(d_burnt, "burnt");
+    helper->getData(d_capital, "capital");
+    if (d_capital)
+      {
+       guint32 ui;
+        helper->getData(ui, "capital_owner");
+        d_capital_owner = Playerlist::getInstance()->getPlayer(ui);
+      }
+
+
+    istringstream svect;
+    string s;
+
+    helper->getData(s, "vectoring");
+     svect.str(s);
+    svect >> d_vector.x;
+    svect >> d_vector.y;
+
+    if (d_vector.x!=-1 && d_vector.y!=-1) 
+        d_vectoring=true;
+    else 
+      d_vectoring=false;
+
+    //mark the positions on the map as being occupied by a city
+    for (unsigned int i = 0; i < d_size; i++)
+        for (unsigned int j = 0; j < d_size; j++)
+            GameMap::getInstance()->getTile(getPos().x+i, getPos().y+j)
+                                  ->setBuilding(Maptile::CITY);
+}
+
+City::City(const City& c)
+    :Ownable(c), Location(c), Renamable(c), ProdSlotlist(c),
+    d_gold(c.d_gold), d_defense_level(c.d_defense_level), d_burnt(c.d_burnt),
+    d_vectoring(c.d_vectoring),d_vector(c.d_vector), d_capital(c.d_capital), 
+    d_capital_owner(c.d_capital_owner)
+{
+}
+
+City::City(const City& c, Vector<int> pos)
+    :Ownable(c), Location(c, pos), Renamable(c), ProdSlotlist(c),
+    d_gold(c.d_gold), d_defense_level(c.d_defense_level), d_burnt(c.d_burnt),
+    d_vectoring(c.d_vectoring),d_vector(c.d_vector), d_capital(c.d_capital), 
+    d_capital_owner(c.d_capital_owner)
+{
+}
+
+City::~City()
+{
+}
+
+bool City::save(XML_Helper* helper) const
+{
+    bool retval = true;
+
+    stringstream svect;
+
+    svect << d_vector.x << " " << d_vector.y;
+
+    retval &= helper->openTag(City::d_tag);
+    retval &= helper->saveData("id", d_id);
+    retval &= helper->saveData("x", getPos().x);
+    retval &= helper->saveData("y", getPos().y);
+    retval &= helper->saveData("name", getName(false));
+    retval &= helper->saveData("owner", d_owner->getId());
+    retval &= helper->saveData("defense", d_defense_level);
+    retval &= helper->saveData("gold", d_gold);
+    retval &= helper->saveData("burnt", d_burnt);
+    retval &= helper->saveData("capital", d_capital);
+    if (d_capital)
+      retval &= helper->saveData("capital_owner", d_capital_owner->getId());
+    retval &= helper->saveData("vectoring", svect.str());
+
+    retval &= ProdSlotlist::save(helper);
+    retval &= helper->closeTag();
+    return retval;
+}
+
+void City::conquer(Player* newowner)
+{
+  Citylist::getInstance()->stopVectoringTo(this);
+
+  setOwner(newowner);
+
+    // remove vectoring info 
+    setVectoring(Vector<int>(-1,-1));
+
+    deFog(newowner);
+
+    VectoredUnitlist *vul = VectoredUnitlist::getInstance();
+    vul->removeVectoredUnitsGoingTo(this);
+    vul->removeVectoredUnitsComingFrom(this);
+}
+
+void City::produceStrongestProductionBase()
+{
+  debug("produceStrongestProductionBase()");
+
+  if (getNoOfProductionBases() == 0)
+    return;
+
+  if (!isFull(d_owner))
+    {
+      unsigned int max_strength = 0;
+      int strong_idx = -1;
+      for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
+       {
+         if ((*this)[i]->getArmyProdBase() == NULL)
+           continue;
+         if (getProductionBase(i)->getStrength() > max_strength)
+           {
+             strong_idx = i;
+             max_strength = getProductionBase(i)->getStrength();
+           }
+       }
+      if (strong_idx == -1)
+       return;
+
+      int savep = d_active_production_slot;
+      setActiveProductionSlot(strong_idx);
+      Vector<int> pos;
+      produceArmy(pos);
+      setActiveProductionSlot(savep);
+      return;
+    }
+}
+
+void City::produceScout()
+{
+  const Armysetlist* al = Armysetlist::getInstance();
+  guint32 set = d_owner->getArmyset();
+  ArmyProto *scout = al->getScout(set);
+  Army *a = new Army(*scout, d_owner);
+  GameMap::getInstance()->addArmy(this, a);
+
+}
+
+void City::produceWeakestProductionBase()
+{
+  debug("produceWeakestProductionBase()");
+
+  if (getNoOfProductionBases() == 0)
+    return;
+
+  if (!isFull(d_owner))
+    {
+      unsigned int min_strength = 100;
+      int weak_idx = -1;
+      for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
+       {
+         if ((*this)[i]->getArmyProdBase() == NULL)
+           continue;
+         if (getProductionBase(i)->getStrength() < min_strength)
+           {
+             weak_idx = i;
+             min_strength = getProductionBase(i)->getStrength();
+           }
+       }
+      if (weak_idx == -1)
+       return;
+
+      int savep = d_active_production_slot;
+      setActiveProductionSlot(weak_idx);
+      Vector<int> pos;
+      produceArmy(pos);
+      setActiveProductionSlot(savep);
+      return;
+    }
+}
+
+const Army *City::armyArrives(Vector<int> &pos)
+{
+  // vector the army to the new spot
+  if (d_vectoring)
+    {
+      VectoredUnitlist *vul = VectoredUnitlist::getInstance();
+      VectoredUnit *v = new VectoredUnit 
+       (getPos(), d_vector, 
+        (*this)[d_active_production_slot]->getArmyProdBase(),
+        MAX_TURNS_FOR_VECTORING, d_owner);
+      vul->push_back(v);
+      d_owner->cityChangeProduction(this, d_active_production_slot);
+      //we don't return an army when we've vectored it.
+      //it doesn't really exist until it lands at the destination.
+      return NULL;
+    }
+  else //or make it here
+    {
+      return produceArmy(pos);
+    }
+  return NULL;
+}
+
+void City::nextTurn()
+{
+  if (d_burnt)
+    return;
+
+  // check if an army should be produced
+  if (d_active_production_slot >= 0 && --d_duration == 0) 
+    {
+      if (d_owner->getGold() <= 0)
+       {
+         //dont make or vector the unit
+         //and also stop production
+         d_owner->cityChangeProduction(this, -1);
+         d_owner->vectorFromCity(this, Vector<int>(-1,-1));
+         return;
+       }
+
+      d_owner->cityProducesArmy(this);
+
+    }
+}
+
+void City::setVectoring(Vector<int> p) 
+{
+  d_vector = p;
+  d_vectoring = true;
+
+  if (p.x == -1 || p.y == -1)
+    {
+      d_vectoring=false;
+      d_vector.x = -1;
+      d_vector.y = -1;
+    }
+}
+
+Army *City::produceArmy(Vector<int> &pos)
+{
+  // add produced army to stack
+  if (d_active_production_slot == -1)
+    return NULL;
+
+  debug("produce_army()\n");
+
+  // do not produce an army if the player has no gold.
+  // unless it's the neutrals
+  if (d_owner != Playerlist::getInstance()->getNeutral() && 
+      d_owner->getGold() < 0) 
+    return NULL;
+
+  Army *a = new Army(*(getProductionBase(d_active_production_slot)), d_owner);
+  Stack *s = GameMap::getInstance()->addArmy(this, a);
+  pos = s->getPos();
+
+  if (d_owner == Playerlist::getInstance()->getNeutral()) 
+    {
+      //we're an active neutral city
+      //check to see if we've made 5 or not.
+      //stop producing if we've made 5 armies in our neutral city
+      if (countDefenders() >= MAX_ARMIES_PRODUCED_IN_NEUTRAL_CITY)
+       setActiveProductionSlot(-1);
+      else
+       setActiveProductionSlot(d_active_production_slot);
+    }
+  else // start producing next army of same type
+    setActiveProductionSlot(d_active_production_slot);
+  return a;
+}
+
+bool City::canAcceptMoreVectoring() const
+{
+  return canAcceptMoreVectoring(0);
+}
+
+bool City::canAcceptMoreVectoring(guint32 number_of_cities) const
+{
+  //here we presume that it's one unit per city
+  guint32 num = Citylist::getInstance()->countCitiesVectoringTo(this);
+  if (num + number_of_cities >= MAX_CITIES_VECTORED_TO_ONE_CITY)
+    return false;
+  return true;
+}
+
+bool City::changeVectorDestination(Vector<int> dest)
+{
+  VectoredUnitlist *vul = VectoredUnitlist::getInstance();
+  setVectoring(dest);
+  vul->changeDestination(this, dest);
+  return true;
+}
+
+std::vector<Stack *> City::getDefenders() const
+{
+  return getOwner()->getStacklist()->getDefendersInCity(this);
+}
+
+guint32 City::countDefenders() const
+{
+  std::vector<Stack*> defenders;
+  defenders = getDefenders();
+
+  guint32 armies = 0;
+  std::vector<Stack*>::iterator it = defenders.begin();
+  for (;it != defenders.end(); it++)
+    armies += (*it)->size();
+
+  return armies;
+}
+
+void City::randomlyImproveOrDegradeArmy(ArmyProdBase *army)
+{
+  if (rand() % 30 == 0) //random chance of improving strength
+      army->setStrength(army->getStrength() + 1);
+  if (rand() % 25 == 0) //random chance of improving turns
+    {
+      if (army->getProduction() > 1)
+       army->setProduction(army->getProduction() - 1);
+    }
+  if (rand() % 50 == 0) //random chance of degrading strength
+    {
+      if (army->getStrength() > 1)
+       army->setStrength(army->getStrength() - 1);
+    }
+  if (rand() % 45 == 0) //random chance of improving turns
+    {
+      if (army->getProduction() < 5)
+       army->setProduction(army->getProduction() + 1);
+    }
+}
+
+bool armyCompareStrength (const ArmyProdBase *lhs, const ArmyProdBase *rhs)
+{
+  guint32 lhs_strength = lhs->getStrength();
+  guint32 rhs_strength = rhs->getStrength();
+  return lhs_strength < rhs_strength; 
+}
+
+void City::sortProduction()
+{
+  //sort them by strength
+  if (getNoOfProductionBases() > 1)
+    {
+      std::list<ArmyProdBase*> productibles;
+      unsigned int j;
+      for (j = 0; j < getMaxNoOfProductionBases(); j++)
+       {
+         if ((*this)[j]->getArmyProdBase())
+           productibles.push_back((*this)[j]->getArmyProdBase());
+       }
+      productibles.sort(armyCompareStrength);
+      j = 0;
+      for (std::list<ArmyProdBase*>::iterator it = productibles.begin();
+          it != productibles.end(); it++, j++)
+               (*this)[j]->setArmyProdBase(*it);
+    }
+  return;
+}
+
+void City::setRandomArmytypes(bool produce_allies, int likely)
+{
+  //remove armies any that happen to be being produced
+  for (unsigned int i = 0; i < getMaxNoOfProductionBases(); i++)
+    removeProductionBase(i);
+
+  const Armysetlist* al = Armysetlist::getInstance();
+  guint32 set = d_owner->getArmyset();
+
+  int army_type;
+  int num = rand() % 10;
+  if (num < 7)
+    army_type = 1;
+  else if (num < 9 && likely == 0)
+    army_type = 0;
+  else
+    army_type = 1 + likely + (rand () % 11);
+  ArmyProto *template_army = al->getArmy(set, army_type);
+  if (!template_army || 
+      (template_army->getAwardable() == true && produce_allies == false) ||
+      template_army->isHero())
+    {
+      produceScout();
+      return;
+    }
+  ArmyProdBase *army = new ArmyProdBase (*template_army);
+  randomlyImproveOrDegradeArmy(army);
+  addProductionBase(0, army);
+
+  if (((rand() % 10) < 3 && !isCapital() && likely < 1) ||
+      template_army->getAwardable())
+    {
+      sortProduction();
+      return;
+    }
+
+  army_type += 1 + (rand() % (2 + (produce_allies ? 2 : 0)));
+  template_army = al->getArmy(set, army_type);
+  if (!template_army ||
+      (template_army->getAwardable() == true && produce_allies == false) ||
+      template_army->isHero())
+    {
+      sortProduction();
+      return;
+    }
+  army = new ArmyProdBase (*template_army);
+  randomlyImproveOrDegradeArmy(army);
+  addProductionBase(1, army);
+
+  if (((rand() % 10) < 4 && !isCapital() && likely < 2) ||
+      template_army->getAwardable())
+    {
+      sortProduction();
+      return;
+    }
+
+  if (army_type < 5)
+    army_type += 1 + (rand() % (7 + (produce_allies ? 2 : 0)));
+  else
+    army_type += 1 + (rand() % (2 + (produce_allies ? 2 : 0)));
+  template_army = al->getArmy(set, army_type);
+  if (!template_army ||
+      (template_army->getAwardable() == true && produce_allies == false) ||
+      template_army->isHero())
+    {
+      sortProduction();
+      return;
+    }
+  army = new ArmyProdBase (*template_army);
+  randomlyImproveOrDegradeArmy(army);
+  addProductionBase(2, army);
+
+  if (((rand() % 10) < 6 && !isCapital() && likely < 3) ||
+      template_army->getAwardable())
+    {
+      sortProduction();
+      return;
+    }
+
+  army_type += 1 + (rand() % (3 + (produce_allies ? 2 : 0)));
+  template_army = al->getArmy(set, army_type);
+  if (!template_army ||
+      (template_army->getAwardable() == true && produce_allies == false) ||
+      template_army->isHero())
+    {
+      sortProduction();
+      return;
+    }
+  army = new ArmyProdBase (*template_army);
+  randomlyImproveOrDegradeArmy(army);
+  addProductionBase(3, army);
+  sortProduction();
+}
+
+guint32 City::calculateDefenseLevel() const
+{
+  int num_production_bases = getNoOfProductionBases();
+  if (isBurnt()) 
+    return 0;
+  else if (num_production_bases <= 2 && 
+          getOwner() == Playerlist::getInstance()->getNeutral())
+    return 1;
+  else if (num_production_bases <= 2)
+    return 2;
+  else if (num_production_bases > 2 && 
+          getOwner() == Playerlist::getInstance()->getNeutral())
+    return 2;
+  else if (num_production_bases > 2)
+    return 3;
+  return 0;
+}
+// End of file