initial commit, lordsawar source, slightly modified
[lordsawar] / src / QuestsManager.cpp
diff --git a/src/QuestsManager.cpp b/src/QuestsManager.cpp
new file mode 100644 (file)
index 0000000..3a7a4a9
--- /dev/null
@@ -0,0 +1,545 @@
+// Copyright (C) 2003, 2004, 2005 Ulf Lorenz
+// Copyright (C) 2004, 2005, 2006 Andrea Paternesi
+// Copyright (C) 2007, 2008, 2009 Ben Asselstine
+// Copyright (C) 2007, 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 <sigc++/functors/mem_fun.h>
+
+#include "QuestsManager.h"
+
+#include "Quest.h"
+#include "QKillHero.h"
+#include "QEnemyArmies.h"
+#include "QCitySack.h"
+#include "QCityRaze.h"
+#include "QCityOccupy.h"
+#include "QEnemyArmytype.h"
+#include "QPillageGold.h"
+#include "stacklist.h"
+#include "rewardlist.h"
+#include "army.h"
+#include "xmlhelper.h"
+#include "history.h"
+
+std::string QuestsManager::d_tag = "questlist";
+
+QuestsManager* QuestsManager::s_instance = NULL;
+
+using namespace std;
+
+//#define debug(x) {cerr<<__FILE__<<": "<<__LINE__<<": "<<x<<endl<<flush;}
+#define debug(x)
+
+
+QuestsManager* QuestsManager::getInstance()
+{
+    if (s_instance == 0)
+        s_instance = new QuestsManager();
+    return s_instance;
+}
+
+QuestsManager* QuestsManager::getInstance(XML_Helper* helper)
+{
+    if (s_instance)
+        deleteInstance();
+
+    s_instance = new QuestsManager(helper);
+    return s_instance;
+}
+
+void QuestsManager::deleteInstance()
+{
+    debug("QuestsManager: deleteInstance")
+    delete s_instance;
+    s_instance = 0;
+}
+
+QuestsManager::QuestsManager()
+{
+    sharedInit();
+}
+
+QuestsManager::QuestsManager(XML_Helper* helper)
+{
+    sharedInit();
+    debug("QuestsManager: registerTag!");
+    helper->registerTag(Quest::d_tag, sigc::mem_fun(this, &QuestsManager::load));
+}
+
+QuestsManager::~QuestsManager()
+{
+    std::map<guint32,Quest*>::iterator it;
+
+    for (it = d_quests.begin(); it != d_quests.end(); it++) 
+        delete (*it).second;
+    cleanup();
+}
+
+Quest* QuestsManager::createNewQuest(guint32 heroId, bool razing_possible)
+{
+    // don't let a hero have more than one quest
+    if (d_quests.count(heroId))
+        return NULL;
+
+    int which = 0;
+    while (!which)
+    {
+        which = 1 + rand() % 7;
+        // if this quest is not feasible - try again with another
+        // quest:
+        if ((*(d_questsFeasible[which-1]))(heroId) == 0)
+            which = 0;
+    }
+    // ok - this quest can be completed
+
+    Quest *quest = NULL;
+    switch (which)
+    {
+        case 1:
+            quest = new QuestKillHero(*this, heroId);
+            break;
+        case 2:
+            quest = new QuestEnemyArmies(*this, heroId);
+            break;
+        case 3:
+            quest = new QuestCitySack(*this, heroId);
+            break;
+        case 4:
+           if (razing_possible)
+             quest = new QuestCityRaze(*this, heroId);
+           else
+             quest = new QuestCitySack(*this, heroId);
+            break;
+        case 5:
+            quest = new QuestCityOccupy(*this, heroId);
+            break;
+        case 6:
+            quest = new QuestEnemyArmytype(*this, heroId);
+            break;
+        case 7:
+            quest = new QuestPillageGold(*this, heroId);
+            break;
+    }
+    
+    if (quest)
+    {
+        d_quests[heroId] = quest;
+    }
+    
+    return quest;
+}
+
+Quest* QuestsManager::createNewKillHeroQuest(guint32 heroId, guint32 targetHeroId)
+{
+  Quest *quest = new QuestKillHero(*this, heroId, targetHeroId);
+    
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewEnemyArmiesQuest(guint32 heroId, 
+                                               guint32 num_armies, 
+                                               guint32 victim_player_id)
+{
+  Quest *quest = new QuestEnemyArmies(*this, heroId, num_armies, 
+                                     victim_player_id);
+    
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewCitySackQuest(guint32 heroId, guint32 cityId)
+{
+  Quest *quest = new QuestCitySack(*this, heroId, cityId);
+
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewCityRazeQuest(guint32 heroId, guint32 cityId)
+{
+  Quest *quest = new QuestCityRaze(*this, heroId, cityId);
+
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewCityOccupyQuest(guint32 heroId, guint32 cityId)
+{
+  Quest *quest = new QuestCityOccupy(*this, heroId, cityId);
+
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewEnemyArmytypeQuest(guint32 heroId, 
+                                                 guint32 armyTypeId)
+{
+  Quest *quest = new QuestEnemyArmytype(*this, heroId, armyTypeId);
+
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+Quest* QuestsManager::createNewPillageGoldQuest(guint32 heroId, guint32 amount)
+{
+  Quest *quest = new QuestPillageGold(*this, heroId, amount);
+
+  d_quests[heroId] = quest;
+  
+  return quest;
+}
+
+void QuestsManager::questCompleted(guint32 heroId)
+{
+    Quest *quest = d_quests[heroId];
+    Player *p = quest->getHero()->getOwner();
+
+    p->heroCompletesQuest(quest->getHero());
+    Stack *stack = p->getStacklist()->getArmyStackById(heroId);
+
+    int num = rand() % 4;
+    if (num == 0)
+      {
+       int gold = Reward_Gold::getRandomGoldPieces();
+        Reward_Gold reward(gold);
+        p->giveReward(stack, &reward);
+        quest_completed.emit(quest, &reward);
+      }
+    else if (num == 1)
+      {
+        int num = (rand() % 8) + 1;
+        const ArmyProto *a = Reward_Allies::randomArmyAlly();
+        Reward_Allies reward(a, num);
+        p->giveReward(stack, &reward);
+        quest_completed.emit(quest, &reward);
+           
+       History_HeroFindsAllies* item = new History_HeroFindsAllies();
+       item->fillData(quest->getHero());
+       p->addHistory(item);
+      }
+    else if (num == 2)
+      {
+        Reward *itemReward = Rewardlist::getInstance()->popRandomItemReward();
+        if (itemReward)
+          {
+            p->giveReward(stack, itemReward);
+            quest_completed.emit(quest, itemReward);
+          }
+        else //no items left to give!
+          {
+           int gold = Reward_Gold::getRandomGoldPieces();
+            Reward_Gold reward(gold);
+            p->giveReward(stack, &reward);
+            quest_completed.emit(quest, &reward);
+          }
+      }
+    else if (num == 3)
+      {
+        Reward *ruinReward = Rewardlist::getInstance()->popRandomRuinReward();
+        if (ruinReward)
+          {
+            p->giveReward(stack, ruinReward);
+            quest_completed.emit(quest, ruinReward);
+          }
+        else //no ruins left to give!
+          {
+            int gold = Reward_Gold::getRandomGoldPieces();
+            Reward_Gold reward(gold);
+            p->giveReward(stack, &reward);
+            quest_completed.emit(quest, &reward);
+          }
+      }
+
+    //debug("deactivate quest");
+    //deactivateQuest(heroId);
+    d_quests.erase(heroId);
+    //debug("quest deactivated");
+}
+
+void QuestsManager::questExpired(guint32 heroId)
+{
+    Quest *quest = d_quests[heroId];
+
+    if (quest == 0)
+        return;
+    
+    //quest_expired.emit(quest);
+    
+    debug("deactivate quest");
+    deactivateQuest(heroId);
+    debug("quest deactivated");
+}
+
+std::vector<Quest*> QuestsManager::getPlayerQuests(const Player *player) const
+{
+  std::vector<Quest*> res;
+  // loop through the player's heroes 
+  // for every hero check any pending quests
+  const Stacklist* sl = player->getStacklist();
+  std::list<Hero*> heroes = sl->getHeroes();
+  for (std::list<Hero*>::iterator it = heroes.begin(); it != heroes.end(); it++)
+    {
+      guint32 heroId = (*it)->getId();
+
+      if (d_quests.count(heroId) > 0)
+        {
+          std::map<guint32, Quest*>::const_iterator qit = d_quests.find(heroId);
+          if (qit == d_quests.end())
+            continue;
+          Quest *q = (*qit).second;
+          if (q && q->isPendingDeletion() == true)
+            continue;
+          debug("heroId = " << heroId << " - has quest: " << q);
+          res.push_back(q);
+        }
+    }
+  return res;
+}
+        
+Quest* QuestsManager::getHeroQuest(guint32 hero_id) const
+{
+  std::map<guint32, Quest*>::const_iterator qit;
+  qit = d_quests.find(hero_id);
+  if (qit == d_quests.end())
+    return NULL;
+  Quest *q = (*qit).second;
+  if (q && q->isPendingDeletion() == true)
+    return NULL;
+  return (*qit).second;
+}
+
+bool QuestsManager::save(XML_Helper* helper) const
+{
+  debug("Saving quests\n");
+
+  bool retval = true;
+  retval &= helper->openTag(QuestsManager::d_tag);
+
+  for (std::map<guint32,Quest*>::const_iterator it = d_quests.begin(); 
+       it != d_quests.end(); it++) 
+    {
+      if ((*it).second == NULL)
+       continue;
+      retval &= ((*it).second)->save(helper);
+    }
+  for (std::list<Quest *>::const_iterator it = d_inactive_quests.begin();
+       it != d_inactive_quests.end(); it++)
+    retval &= (*it)->save(helper);
+
+  debug("Quests saved\n");
+  retval &= helper->closeTag();
+  return retval;
+}
+
+bool QuestsManager::load(string tag, XML_Helper* helper)
+{
+  debug("QuestsManager: load tag = " << tag);
+
+  if (tag == Quest::d_tag)
+    {
+      guint32  questType, hero;
+      std::string quest_type_str;
+      helper->getData(quest_type_str, "type");
+      questType = Quest::questTypeFromString(quest_type_str);
+      helper->getData(hero, "hero");
+
+      debug("quest load: type = " << questType << ", heroId = " << hero);
+
+      Quest *quest=0;
+      switch (static_cast<Quest::Type>(questType)) {
+      case Quest::KILLHERO:
+       quest = new QuestKillHero(*this, helper);
+       break;
+      case Quest::KILLARMIES:
+       quest = new QuestEnemyArmies(*this, helper);
+       break;
+      case Quest::CITYSACK:
+       quest = new QuestCitySack(*this, helper);
+       break;
+      case Quest::CITYRAZE:
+       quest = new QuestCityRaze(*this, helper);
+       break;
+      case Quest::CITYOCCUPY:
+       quest = new QuestCityOccupy(*this, helper);
+       break;
+      case Quest::KILLARMYTYPE:
+       quest = new QuestEnemyArmytype(*this, helper);
+       break;
+      case Quest::PILLAGEGOLD:
+       quest = new QuestPillageGold(*this, helper);
+       break;
+      }
+
+      debug("quest created: q = " << quest);
+      if (quest)
+       {
+         if (quest->isPendingDeletion())
+           d_inactive_quests.push_back(quest);
+         else
+           d_quests[hero] = quest;
+       }
+
+      return true;
+    }
+
+  return false;
+}
+
+void QuestsManager::sharedInit()
+{
+  debug("QuestsManager constructor")
+
+    // now prepare the vector of pointers to the
+    // functions (class static members) checking feasibility
+    // for every quest
+    d_questsFeasible.push_back(&(QuestKillHero::isFeasible));
+  d_questsFeasible.push_back(&(QuestEnemyArmies::isFeasible));
+  d_questsFeasible.push_back(&(QuestCitySack::isFeasible));
+  d_questsFeasible.push_back(&(QuestCityRaze::isFeasible));
+  d_questsFeasible.push_back(&(QuestCityOccupy::isFeasible));
+  d_questsFeasible.push_back(&(QuestEnemyArmytype::isFeasible));
+  d_questsFeasible.push_back(&(QuestPillageGold::isFeasible));
+}
+
+void QuestsManager::deactivateQuest(guint32 heroId)
+{
+  Quest *q = d_quests[heroId];
+  q->deactivate();
+  d_inactive_quests.push_back(q);
+  // delete it from hash of active quests
+  d_quests.erase(heroId);
+}
+
+void QuestsManager::cleanup(Player::Type type)
+{
+  debug("QuestsManager: cleanup!");
+
+  std::list<Quest *>::iterator it = d_inactive_quests.begin();
+  while(it != d_inactive_quests.end())
+    {
+      Quest *q =(*it);
+      it = d_inactive_quests.erase(it);
+      if (q)
+       delete q;
+    }
+}
+
+void QuestsManager::armyDied(Army *a, std::vector<guint32>& culprits)
+{
+  //tell all quests that an army died
+  //each quest takes care of what happens when an army dies
+  std::map<guint32,Quest*>::iterator it;
+  for (it = d_quests.begin(); it != d_quests.end(); it++) 
+    {
+      if ((*it).second == NULL)
+       continue;
+      if ((*it).second->isPendingDeletion() == true)
+       continue;
+      //was this hero a perpetrator?
+      bool heroIsCulprit = false;
+      for (unsigned int i = 0; i <culprits.size(); i++)
+       {
+         if (culprits[i] == (*it).second->getHeroId())
+           {
+             heroIsCulprit = true;
+             break;
+           }
+       }
+      (*it).second->armyDied(a, heroIsCulprit);
+    }
+
+  //is it a hero that has an outstanding quest?
+  //this is what deactivates a quest upon hero death
+  Quest *quest = d_quests[a->getId()];
+  if (quest && quest->isPendingDeletion() == false)
+    questExpired(a->getId());
+
+}
+
+void QuestsManager::cityAction(City *c, Stack *s, 
+                              CityDefeatedAction action, int gold)
+{
+  std::map<guint32,Quest*>::iterator it;
+  //did any of the heroes who have outstanding quests do this?
+  for (it = d_quests.begin(); it != d_quests.end(); it++) 
+    {
+      if ((*it).second == NULL)
+       continue;
+      if ((*it).second->isPendingDeletion() == true)
+       continue;
+      if (!s)
+       (*it).second->cityAction(c, action, false, gold);
+      else
+       {
+         for (Stack::iterator sit = s->begin(); sit != s->end(); sit++)
+           {
+             if ((*it).second == NULL) //fixme: how is this null sometimes?
+               break;
+             if ((*sit)->getId() == (*it).second->getHeroId())
+               (*it).second->cityAction(c, action, true, gold);
+             else
+               (*it).second->cityAction(c, action, false, gold);
+           }
+       }
+    }
+}
+
+void QuestsManager::cityRazed(City *c, Stack *s)
+{
+  cityAction(c, s, CITY_DEFEATED_RAZE, 0);
+  //did we raze a city we care about in another quest?
+}
+
+void QuestsManager::citySacked(City *c, Stack *s, int gold)
+{
+  cityAction(c, s, CITY_DEFEATED_SACK, gold);
+}
+
+void QuestsManager::cityPillaged(City *c, Stack *s, int gold)
+{
+  cityAction(c, s, CITY_DEFEATED_PILLAGE, gold);
+}
+
+void QuestsManager::cityOccupied(City *c, Stack *s)
+{
+  cityAction(c, s, CITY_DEFEATED_OCCUPY, 0);
+}
+
+void QuestsManager::nextTurn(Player *p)
+{
+  // go through our inactive list and remove quests belonging to us
+  for (std::list<Quest*>::iterator it = d_inactive_quests.begin();
+       it != d_inactive_quests.end(); it++)
+    {
+      if ((*it)->getOwner() == p)
+       {
+         Quest *q = *it;
+         quest_expired.emit(q);
+         it = d_inactive_quests.erase(it);
+         if (q)
+           delete q;
+       }
+    }
+}