1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Quests will now be handled as object member instead of inheritance. Enabled quest objects for AI.

This commit is contained in:
DjWarmonger 2012-09-16 13:34:01 +00:00
parent 5118386cb4
commit 554a98dbd7
7 changed files with 134 additions and 102 deletions

View File

@ -3462,7 +3462,7 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj)
case Obj::SEER_HUT:
case Obj::QUEST_GUARD:
{
return false; //fixme: avoid crash
//return false; //fixme: avoid crash
BOOST_FOREACH (auto q, ai->myCb->getMyQuests())
{
if (q.obj == obj)

View File

@ -1281,7 +1281,8 @@ void CBoundedLabel::showAll(SDL_Surface * to)
for (int i = 0; i < lineCapacity; i++)
{
const std::string &line = lines[i];
if(!line.size()) continue;
if ( !(lines.size() && line.size())) //empty message or empty line
continue;
int x = pos.x;
if(alignment == CENTER)

View File

@ -4404,41 +4404,41 @@ void CQuest::getCompletionText (MetaString &iwText, std::vector<Component> &comp
}
void CGSeerHut::setObjToKill()
{
if (missionType == MISSION_KILL_CREATURE)
if (quest.missionType == CQuest::MISSION_KILL_CREATURE)
{
stackToKill = getCreatureToKill(false)->getStack(0); //FIXME: stacks tend to dissapear (desync?) on server :?
stackToKill.count = 0; //no count in info window
stackDirection = checkDirection();
quest.stackToKill = getCreatureToKill(false)->getStack(0); //FIXME: stacks tend to dissapear (desync?) on server :?
quest.stackToKill.count = 0; //no count in info window
quest.stackDirection = checkDirection();
}
else if (missionType == MISSION_KILL_HERO)
else if (quest.missionType == CQuest::MISSION_KILL_HERO)
{
heroName = getHeroToKill(false)->name;
heroPortrait = getHeroToKill(false)->portrait;
quest.heroName = getHeroToKill(false)->name;
quest.heroPortrait = getHeroToKill(false)->portrait;
}
}
void CGSeerHut::initObj()
{
seerName = VLC->generaltexth->seerNames[ran()%VLC->generaltexth->seerNames.size()];
textOption = ran()%3;
progress = 0;
if (missionType)
quest.textOption = ran()%3;
quest.progress = 0;
if (quest.missionType)
{
if (!isCustomFirst)
firstVisitText = VLC->generaltexth->quests[missionType-1][0][textOption];
if (!isCustomNext)
nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];
if (!isCustomComplete)
completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
if (!quest.isCustomFirst)
quest.firstVisitText = VLC->generaltexth->quests[quest.missionType-1][0][quest.textOption];
if (!quest.isCustomNext)
quest.nextVisitText = VLC->generaltexth->quests[quest.missionType-1][1][quest.textOption];
if (!quest.isCustomComplete)
quest.completedText = VLC->generaltexth->quests[quest.missionType-1][2][quest.textOption];
}
else
firstVisitText = VLC->generaltexth->seerEmpty[textOption];
quest.firstVisitText = VLC->generaltexth->seerEmpty[quest.textOption];
}
void CGSeerHut::getRolloverText (MetaString &text, bool onHover) const
{
CQuest::getRolloverText (text, onHover);//TODO: simplify?
quest.getRolloverText (text, onHover);//TODO: simplify?
if (!onHover)
text.addReplacement(seerName);
}
@ -4448,7 +4448,7 @@ const std::string & CGSeerHut::getHoverText() const
switch (ID)
{
case 83:
if (progress)
if (quest.progress)
{
hoverName = VLC->generaltexth->allTexts[347];
boost::algorithm::replace_first(hoverName,"%s", seerName);
@ -4462,7 +4462,7 @@ const std::string & CGSeerHut::getHoverText() const
default:
tlog5 << "unrecognized quest object\n";
}
if (progress & missionType) //rollover when the quest is active
if (quest.progress & quest.missionType) //rollover when the quest is active
{
MetaString ms;
getRolloverText (ms, true);
@ -4488,9 +4488,19 @@ void CQuest::addReplacements(MetaString &out, const std::string &base) const
}
}
bool IQuestObject::checkQuest(const CGHeroInstance* h) const
{
return quest.checkQuest(h);
}
void IQuestObject::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
{
quest.getVisitText (text,components, isCustom, FirstVisit, h);
}
void CGSeerHut::getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h) const
{
CQuest::getCompletionText (text, components, isCustom, h);
quest.getCompletionText (text, components, isCustom, h);
switch (rewardType)
{
case 1: components.push_back(Component (Component::EXPERIENCE, 0, rVal*(100+h->getSecSkillLevel(CGHeroInstance::LEARNING)*5)/100.0, 0));
@ -4521,16 +4531,16 @@ void CGSeerHut::setPropertyDer (ui8 what, ui32 val)
switch (what)
{
case 10:
progress = val;
quest.progress = val;
break;
case 11:
missionType = CQuest::MISSION_NONE;
quest.missionType = CQuest::MISSION_NONE;
break;
}
}
void CGSeerHut::newTurn() const
{
if (lastDay >= 0 && lastDay < cb->getDate(0)) //time is up
if (quest.lastDay >= 0 && quest.lastDay < cb->getDate(0)) //time is up
{
cb->setObjProperty (id, 11, 0);
cb->setObjProperty (id, 10, 0);
@ -4541,25 +4551,25 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
{
InfoWindow iw;
iw.player = h->getOwner();
if (missionType)
if (quest.missionType)
{
bool firstVisit = !progress;
bool firstVisit = !quest.progress;
bool failRequirements = !checkQuest(h);
bool isCustom=false;
if (firstVisit)
{
isCustom = isCustomFirst;
cb->setObjProperty (id, 10, IN_PROGRESS);
isCustom = quest.isCustomFirst;
cb->setObjProperty (id, 10, CQuest::IN_PROGRESS);
AddQuest aq;
aq.quest = QuestInfo (this, this, visitablePos());
aq.quest = QuestInfo (&quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply (&aq); //TODO: merge with setObjProperty?
}
else if (failRequirements)
{
isCustom = isCustomNext;
isCustom = quest.isCustomNext;
}
if (firstVisit || failRequirements)
@ -4582,7 +4592,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
}
else
{
iw.text << VLC->generaltexth->seerEmpty[textOption];
iw.text << VLC->generaltexth->seerEmpty[quest.textOption];
if (ID == 83)
iw.text.addReplacement(seerName);
cb->showInfoDialog(&iw);
@ -4623,27 +4633,27 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
{
if (accept)
{
switch (missionType)
switch (quest.missionType)
{
case CQuest::MISSION_ART:
for (std::vector<ui16>::const_iterator it = m5arts.begin(); it != m5arts.end(); ++it)
for (std::vector<ui16>::const_iterator it = quest.m5arts.begin(); it != quest.m5arts.end(); ++it)
{
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(*it, false)));
}
break;
case CQuest::MISSION_ARMY:
cb->takeCreatures(h->id, m6creatures);
cb->takeCreatures(h->id, quest.m6creatures);
break;
case CQuest::MISSION_RESOURCES:
for (int i = 0; i < 7; ++i)
{
cb->giveResource(h->getOwner(), i, -m7resources[i]);
cb->giveResource(h->getOwner(), i, -quest.m7resources[i]);
}
break;
default:
break;
}
cb->setObjProperty (id, 10, COMPLETE); //mission complete - for AI
cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete - for AI
cb->setObjProperty (id, 11, 0); //no more mission available - redundant?
completeQuest(h); //make sure to remove QuestQuard at the very end
}
@ -4706,7 +4716,7 @@ void CGSeerHut::completeQuest (const CGHeroInstance * h) const //reward
const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest.m13489val);
if(allowNull && !o)
return NULL;
assert(o && o->ID == GameConstants::HEROI_TYPE);
@ -4715,7 +4725,7 @@ const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(m13489val);
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest.m13489val);
if(allowNull && !o)
return NULL;
assert(o && o->ID == 54);
@ -4725,19 +4735,19 @@ const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
void CGQuestGuard::initObj()
{
blockVisit = true;
progress = 0;
textOption = ran()%3 + 3; //3-5
if (missionType)
quest.progress = 0;
quest.textOption = ran()%3 + 3; //3-5
if (quest.missionType)
{
if (!isCustomFirst)
firstVisitText = VLC->generaltexth->quests[missionType-1][0][textOption];
if (!isCustomNext)
nextVisitText = VLC->generaltexth->quests[missionType-1][1][textOption];
if (!isCustomComplete)
completedText = VLC->generaltexth->quests[missionType-1][2][textOption];
if (!quest.isCustomFirst)
quest.firstVisitText = VLC->generaltexth->quests[quest.missionType-1][0][quest.textOption];
if (!quest.isCustomNext)
quest.nextVisitText = VLC->generaltexth->quests[quest.missionType-1][1][quest.textOption];
if (!quest.isCustomComplete)
quest.completedText = VLC->generaltexth->quests[quest.missionType-1][2][quest.textOption];
}
else
firstVisitText = VLC->generaltexth->seerEmpty[textOption];
quest.firstVisitText = VLC->generaltexth->seerEmpty[quest.textOption];
}
void CGQuestGuard::completeQuest(const CGHeroInstance *h) const
{
@ -6361,7 +6371,7 @@ void CGBorderGuard::onHeroVisit( const CGHeroInstance * h ) const
cb->showInfoDialog (&iw);
AddQuest aq;
aq.quest = QuestInfo (this, this, visitablePos());
aq.quest = QuestInfo (&quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply (&aq);
//TODO: add this quest only once OR check for multiple instances later
@ -6384,7 +6394,7 @@ void CGBorderGate::onHeroVisit( const CGHeroInstance * h ) const //TODO: passabi
cb->showInfoDialog(&iw);
AddQuest aq;
aq.quest = QuestInfo (this, this, visitablePos());
aq.quest = QuestInfo (&quest, this, visitablePos());
aq.player = h->tempOwner;
cb->sendAndApply (&aq);
}

View File

@ -759,7 +759,21 @@ public:
}
};
class DLL_LINKAGE CGSeerHut : public CArmedInstance, public CQuest //army is used when giving reward
class DLL_LINKAGE IQuestObject
{
public:
CQuest quest;
virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = NULL) const;
virtual bool checkQuest (const CGHeroInstance * h) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & quest;
}
};
class DLL_LINKAGE CGSeerHut : public CArmedInstance, public IQuestObject //army is used when giving reward
{
public:
ui8 rewardType; //type of reward: 0 - no reward; 1 - experience; 2 - mana points; 3 - morale bonus; 4 - luck bonus; 5 - resources; 6 - main ability bonus (attak, defence etd.); 7 - secondary ability gain; 8 - artifact; 9 - spell; 10 - creature
@ -770,21 +784,21 @@ public:
void initObj();
const std::string & getHoverText() const;
void setPropertyDer (ui8 what, ui32 val);
int checkDirection() const; //calculates the region of map where monster is placed
void newTurn() const;
void onHeroVisit (const CGHeroInstance * h) const;
int checkDirection() const; //calculates the region of map where monster is placed
void setObjToKill(); //remember creatures / heroes to kill after they are initialized
const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
const CGCreature *getCreatureToKill(bool allowNull = false) const;
void getRolloverText (MetaString &text, bool onHover) const;
void getCompletionText(MetaString &text, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h = NULL) const;
void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
void completeQuest (const CGHeroInstance * h) const;
void setObjToKill(); //remember creatures / heroes to kill after they are initialized
const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
const CGCreature *getCreatureToKill(bool allowNull = false) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArmedInstance&>(*this) & static_cast<CQuest&>(*this);
h & static_cast<CArmedInstance&>(*this) & static_cast<IQuestObject&>(*this);
h & rewardType & rID & rVal & seerName;
}
};
@ -1067,7 +1081,7 @@ public:
}
};
class DLL_LINKAGE CGBorderGuard : public CGKeys, public CQuest
class DLL_LINKAGE CGBorderGuard : public CGKeys, public IQuestObject
{
public:
void initObj();
@ -1080,7 +1094,7 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CQuest&>(*this);
h & static_cast<IQuestObject&>(*this);
h & static_cast<CGObjectInstance&>(*this);
h & blockVisit;
}

View File

@ -38,6 +38,7 @@ void registerTypes1(Serializer &s)
s.template registerType<CGCreature>();
s.template registerType<CGSignBottle>();
s.template registerType<CQuest>();
s.template registerType<IQuestObject>();
s.template registerType<CGSeerHut>();
s.template registerType<CGQuestGuard>();
s.template registerType<CGWitchHut>();

View File

@ -567,18 +567,18 @@ int Mapa::loadSeerHut( const ui8 * bufor, int i, CGObjectInstance *& nobj )
int artID = bufor[i]; ++i;
if (artID != 255) //not none quest
{
hut->m5arts.push_back (artID);
hut->missionType = CQuest::MISSION_ART;
hut->quest.m5arts.push_back (artID);
hut->quest.missionType = CQuest::MISSION_ART;
}
else
{
hut->missionType = CQuest::MISSION_NONE; //no mission
hut->quest.missionType = CQuest::MISSION_NONE; //no mission
}
hut->lastDay = -1; //no timeout
hut->isCustomFirst = hut->isCustomNext = hut->isCustomComplete = false;
hut->quest.lastDay = -1; //no timeout
hut->quest.isCustomFirst = hut->quest.isCustomNext = hut->quest.isCustomComplete = false;
}
if (hut->missionType)
if (hut->quest.missionType)
{
ui8 rewardType = bufor[i]; ++i;
hut->rewardType = rewardType;
@ -1461,7 +1461,7 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
case 83: //seer's hut
{
i = loadSeerHut(bufor, i, nobj);
addQuest (dynamic_cast<CQuest *>(nobj));
addQuest (dynamic_cast<IQuestObject *>(nobj));
break;
}
case 113: //witch hut
@ -1722,7 +1722,7 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
CGQuestGuard *guard = new CGQuestGuard();
nobj = guard;
loadQuest(guard, bufor, i);
addQuest (dynamic_cast <CQuest *>(guard));
addQuest (dynamic_cast <IQuestObject *>(guard));
break;
}
case 28: //faerie ring
@ -1803,13 +1803,13 @@ void Mapa::readObjects( const ui8 * bufor, int &i)
case 9: //Border Guard
{
nobj = new CGBorderGuard();
addQuest (dynamic_cast<CQuest *>(nobj));
addQuest (dynamic_cast<IQuestObject *>(nobj));
break;
}
case 212: //Border Gate
{
nobj = new CGBorderGate();
addQuest (dynamic_cast<CQuest *>(nobj));
addQuest (dynamic_cast<IQuestObject *>(nobj));
break;
}
case 27: case 37: //Eye and Hut of Magi
@ -1945,20 +1945,20 @@ bool Mapa::isInTheMap(const int3 &pos) const
else return true;
}
void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
void Mapa::loadQuest(IQuestObject * guard, const ui8 * bufor, int & i)
{
guard->missionType = bufor[i]; ++i;
guard->quest.missionType = bufor[i]; ++i;
//int len1, len2, len3;
switch(guard->missionType)
switch(guard->quest.missionType)
{
case 0:
return;
case 2:
{
guard->m2stats.resize(4);
guard->quest.m2stats.resize(4);
for(int x=0; x<4; x++)
{
guard->m2stats[x] = bufor[i++];
guard->quest.m2stats[x] = bufor[i++];
}
}
break;
@ -1966,7 +1966,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
case 3:
case 4:
{
guard->m13489val = read_le_u32(bufor + i); i+=4;
guard->quest.m13489val = read_le_u32(bufor + i); i+=4;
break;
}
case 5:
@ -1975,7 +1975,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
for(int yy=0; yy<artNumber; ++yy)
{
int artid = read_le_u16(bufor + i); i+=2;
guard->m5arts.push_back(artid);
guard->quest.m5arts.push_back(artid);
allowedArtifact[artid] = false; //these are unavailable for random generation
}
break;
@ -1983,20 +1983,20 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
case 6:
{
int typeNumber = bufor[i]; ++i;
guard->m6creatures.resize(typeNumber);
guard->quest.m6creatures.resize(typeNumber);
for(int hh=0; hh<typeNumber; ++hh)
{
guard->m6creatures[hh].type = VLC->creh->creatures[read_le_u16(bufor + i)]; i+=2;
guard->m6creatures[hh].count = read_le_u16(bufor + i); i+=2;
guard->quest.m6creatures[hh].type = VLC->creh->creatures[read_le_u16(bufor + i)]; i+=2;
guard->quest.m6creatures[hh].count = read_le_u16(bufor + i); i+=2;
}
break;
}
case 7:
{
guard->m7resources.resize(7);
guard->quest.m7resources.resize(7);
for(int x=0; x<7; x++)
{
guard->m7resources[x] = read_le_u32(bufor + i);
guard->quest.m7resources[x] = read_le_u32(bufor + i);
i+=4;
}
break;
@ -2004,7 +2004,7 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
case 8:
case 9:
{
guard->m13489val = bufor[i]; ++i;
guard->quest.m13489val = bufor[i]; ++i;
break;
}
}
@ -2013,18 +2013,18 @@ void Mapa::loadQuest(CQuest * guard, const ui8 * bufor, int & i)
int limit = read_le_u32(bufor + i); i+=4;
if(limit == ((int)0xffffffff))
{
guard->lastDay = -1;
guard->quest.lastDay = -1;
}
else
{
guard->lastDay = limit;
guard->quest.lastDay = limit;
}
guard->firstVisitText = readString(bufor,i);
guard->nextVisitText = readString(bufor,i);
guard->completedText = readString(bufor,i);
guard->isCustomFirst = guard->firstVisitText.size() > 0;
guard->isCustomNext = guard->nextVisitText.size() > 0;
guard->isCustomComplete = guard->completedText.size() > 0;
guard->quest.firstVisitText = readString(bufor,i);
guard->quest.nextVisitText = readString(bufor,i);
guard->quest.completedText = readString(bufor,i);
guard->quest.isCustomFirst = guard->quest.firstVisitText.size() > 0;
guard->quest.isCustomNext = guard->quest.nextVisitText.size() > 0;
guard->quest.isCustomComplete = guard->quest.completedText.size() > 0;
}
TerrainTile & Mapa::getTile( const int3 & tile )
@ -2070,10 +2070,10 @@ void Mapa::addNewArtifactInstance( CArtifactInstance *art )
artInstances.push_back(art);
}
void Mapa::addQuest (CQuest *quest)
void Mapa::addQuest (IQuestObject *obj)
{
quest->qid = quests.size();
quests.push_back(quest);
obj->quest.qid = quests.size();
quests.push_back(&obj->quest);
}
bool Mapa::loadArtifactToSlot(CGHeroInstance *h, int slot, const ui8 * bufor, int &i)

View File

@ -30,6 +30,7 @@ class CGCreature;
class CQuest;
class CGTownInstance;
class IModableArt;
class IQuestObject;
/// Struct which describes a single terrain tile
struct DLL_LINKAGE TerrainTile
@ -306,7 +307,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
std::vector< ConstTransitivePtr<CGHeroInstance> > heroes;
std::vector< ConstTransitivePtr<CGTownInstance> > towns;
std::vector< ConstTransitivePtr<CArtifactInstance> > artInstances; //stores all artifacts
std::vector< ConstTransitivePtr<CQuest> > quests;
std::vector< ConstTransitivePtr<CQuest> > quests; //FIXME: allow to serialize quests not related to objects
//std::vector< ConstTransitivePtr<CCommanderInstance> > commanders;
//bmap<ui16, ConstTransitivePtr<CGCreature> > monsters;
//bmap<ui16, ConstTransitivePtr<CGHeroInstance> > heroesToBeat;
@ -317,7 +318,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
void readEvents( const ui8 * bufor, int &i);
void readObjects( const ui8 * bufor, int &i);
void loadQuest( CQuest * guard, const ui8 * bufor, int & i);
void loadQuest( IQuestObject * guard, const ui8 * bufor, int & i);
void readDefInfo( const ui8 * bufor, int &i);
void readTerrain( const ui8 * bufor, int &i);
void readPredefinedHeroes( const ui8 * bufor, int &i);
@ -331,7 +332,7 @@ struct DLL_LINKAGE Mapa : public CMapHeader
CArtifactInstance *createArt(int aid, int spellID = -1);
void addNewArtifactInstance(CArtifactInstance *art);
void addQuest (CQuest *quest);
void addQuest (IQuestObject *quest);
void eraseArtifactInstance(CArtifactInstance *art);
@ -447,11 +448,16 @@ struct DLL_LINKAGE Mapa : public CMapHeader
for(ui32 i=0; i<objects.size(); i++)
{
if(!objects[i]) continue;
if(objects[i]->ID == GameConstants::HEROI_TYPE)
heroes.push_back(static_cast<CGHeroInstance*>(+objects[i]));
else if(objects[i]->ID == GameConstants::TOWNI_TYPE)
towns.push_back(static_cast<CGTownInstance*>(+objects[i]));
switch (objects[i]->ID)
{
case GameConstants::HEROI_TYPE:
heroes.push_back (static_cast<CGHeroInstance*>(+objects[i]));
break;
case GameConstants::TOWNI_TYPE:
towns.push_back (static_cast<CGTownInstance*>(+objects[i]));
break;
}
addBlockVisTiles(objects[i]); //recreate blockvis map
}
for(ui32 i=0; i<heroes.size(); i++) //if hero is visiting/garrisoned in town set appropriate pointers