mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge branch 'develop' into FileInfo
This commit is contained in:
commit
c3e8c8bc1c
@ -275,7 +275,7 @@ TSubgoal Win::whatToDoToAchieve()
|
||||
// 0.85 -> radius now 2 tiles
|
||||
// 0.95 -> 1 tile radius, position is fully known
|
||||
// AFAIK H3 AI does something like this
|
||||
int3 grailPos = cb->getGrailPos(ratio);
|
||||
int3 grailPos = cb->getGrailPos(&ratio);
|
||||
if(ratio > 0.99)
|
||||
{
|
||||
return sptr (Goals::DigAtTile(grailPos));
|
||||
@ -435,10 +435,18 @@ TSubgoal VisitHero::whatToDoToAchieve()
|
||||
|
||||
bool VisitHero::fulfillsMe (TSubgoal goal)
|
||||
{
|
||||
if (goal->goalType == Goals::VISIT_TILE && cb->getObj(ObjectInstanceID(objid))->visitablePos() == goal->tile)
|
||||
return true;
|
||||
else
|
||||
if (goal->goalType != Goals::VISIT_TILE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto obj = cb->getObj(ObjectInstanceID(objid));
|
||||
if (!obj)
|
||||
{
|
||||
logAi->errorStream() << boost::format("Hero %s: VisitHero::fulfillsMe at %s: object %d not found")
|
||||
% hero.name % goal->tile % objid;
|
||||
return false;
|
||||
}
|
||||
return obj->visitablePos() == goal->tile;
|
||||
}
|
||||
|
||||
TSubgoal GetArtOfType::whatToDoToAchieve()
|
||||
|
@ -86,7 +86,6 @@
|
||||
<Unit filename="Goals.cpp" />
|
||||
<Unit filename="Goals.h" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option compile="1" />
|
||||
<Option weight="0" />
|
||||
</Unit>
|
||||
<Unit filename="VCAI.cpp" />
|
||||
|
@ -1987,7 +1987,6 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
if(teleportChannelProbingList.size())
|
||||
doChannelProbing();
|
||||
}
|
||||
ret = !i;
|
||||
}
|
||||
if (h)
|
||||
{
|
||||
@ -2002,6 +2001,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
completeGoal (sptr(Goals::VisitTile(dst).sethero(h))); //we stepped on some tile, anyway
|
||||
completeGoal (sptr(Goals::ClearWayTo(dst).sethero(h)));
|
||||
|
||||
ret = (dst == h->visitablePos());
|
||||
|
||||
if(!ret) //reserve object we are heading towards
|
||||
{
|
||||
auto obj = vstd::frontOrNull(cb->getVisitableObjs(dst));
|
||||
@ -2020,7 +2021,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
|
||||
}
|
||||
void VCAI::tryRealize(Goals::Explore & g)
|
||||
{
|
||||
throw cannotFulfillGoalException("EXPLORE is not a elementar goal!");
|
||||
throw cannotFulfillGoalException("EXPLORE is not an elementar goal!");
|
||||
}
|
||||
|
||||
void VCAI::tryRealize(Goals::RecruitHero & g)
|
||||
|
@ -8,6 +8,8 @@ GENERAL:
|
||||
- Angel Wings
|
||||
- Boots of Levitation
|
||||
* Implemented rumors in tavern window
|
||||
* New cheat code:
|
||||
- vcmiglaurung - gives 5000 crystal dragons into each slot
|
||||
|
||||
ADVETURE AI:
|
||||
* Fixed AI trying to go through underground rock
|
||||
|
@ -441,6 +441,11 @@ int main(int argc, char** argv)
|
||||
CCS->musich->setVolume(settings["general"]["music"].Float());
|
||||
logGlobal->infoStream()<<"Initializing screen and sound handling: "<<pomtime.getDiff();
|
||||
|
||||
#ifdef __APPLE__
|
||||
// Ctrl+click should be treated as a right click on Mac OS X
|
||||
SDL_SetHint(SDL_HINT_MAC_CTRL_CLICK_EMULATE_RIGHT_CLICK, "1");
|
||||
#endif
|
||||
|
||||
#ifndef VCMI_NO_THREADED_LOAD
|
||||
//we can properly play intro only in the main thread, so we have to move loading to the separate thread
|
||||
boost::thread loading(init);
|
||||
|
@ -1335,7 +1335,7 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
|
||||
if(showingDialog->get() || !dialogs.empty())
|
||||
return;
|
||||
|
||||
duringMovement = true;
|
||||
setMovementStatus(true);
|
||||
|
||||
if (adventureInt && adventureInt->isHeroSleeping(h))
|
||||
{
|
||||
@ -1347,8 +1347,6 @@ void CPlayerInterface::moveHero( const CGHeroInstance *h, CGPath path )
|
||||
}
|
||||
|
||||
boost::thread moveHeroTask(std::bind(&CPlayerInterface::doMoveHero,this,h,path));
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool CPlayerInterface::shiftPressed() const
|
||||
@ -1559,6 +1557,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
waitWhileDialog();
|
||||
CCS->curh->hide();
|
||||
adventureInt->centerOn (pos);
|
||||
if(focusTime)
|
||||
{
|
||||
@ -1569,6 +1568,7 @@ void CPlayerInterface::centerView (int3 pos, int focusTime)
|
||||
SDL_Delay(focusTime);
|
||||
}
|
||||
}
|
||||
CCS->curh->show();
|
||||
}
|
||||
|
||||
void CPlayerInterface::objectRemoved( const CGObjectInstance *obj )
|
||||
@ -2170,7 +2170,7 @@ void CPlayerInterface::showPuzzleMap()
|
||||
|
||||
//TODO: interface should not know the real position of Grail...
|
||||
double ratio = 0;
|
||||
int3 grailPos = cb->getGrailPos(ratio);
|
||||
int3 grailPos = cb->getGrailPos(&ratio);
|
||||
|
||||
GH.pushInt(new CPuzzleWindow(grailPos, ratio));
|
||||
}
|
||||
@ -2510,9 +2510,20 @@ void CPlayerInterface::stacksRebalanced(const StackLocation &src, const StackLoc
|
||||
garrisonsChanged(objects);
|
||||
}
|
||||
|
||||
void CPlayerInterface::askToAssembleArtifact(const ArtifactLocation &al)
|
||||
{
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(al.relatedObj());
|
||||
if(hero)
|
||||
{
|
||||
CArtPlace::askToAssemble(hero->getArt(al.slot), al.slot, hero);
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayerInterface::artifactPut(const ArtifactLocation &al)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
adventureInt->infoBar.showSelection();
|
||||
askToAssembleArtifact(al);
|
||||
}
|
||||
|
||||
void CPlayerInterface::artifactRemoved(const ArtifactLocation &al)
|
||||
@ -2537,6 +2548,7 @@ void CPlayerInterface::artifactMoved(const ArtifactLocation &src, const Artifact
|
||||
if(artWin)
|
||||
artWin->artifactMoved(src, dst);
|
||||
}
|
||||
askToAssembleArtifact(dst);
|
||||
}
|
||||
|
||||
void CPlayerInterface::artifactAssembled(const ArtifactLocation &al)
|
||||
@ -2632,6 +2644,19 @@ bool CPlayerInterface::capturedAllEvents()
|
||||
return false;
|
||||
}
|
||||
|
||||
void CPlayerInterface::setMovementStatus(bool value)
|
||||
{
|
||||
duringMovement = value;
|
||||
if(value)
|
||||
{
|
||||
CCS->curh->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
CCS->curh->show();
|
||||
}
|
||||
}
|
||||
|
||||
void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
{
|
||||
int i = 1;
|
||||
@ -2776,7 +2801,7 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
|
||||
adventureInt->updateNextHero(h);
|
||||
}
|
||||
|
||||
duringMovement = false;
|
||||
setMovementStatus(false);
|
||||
}
|
||||
|
||||
void CPlayerInterface::showWorldViewEx(const std::vector<ObjectPosInfo>& objectPositions)
|
||||
|
@ -295,6 +295,8 @@ private:
|
||||
bool ignoreEvents;
|
||||
|
||||
void doMoveHero(const CGHeroInstance *h, CGPath path);
|
||||
void setMovementStatus(bool value);
|
||||
void askToAssembleArtifact(const ArtifactLocation &al);
|
||||
};
|
||||
|
||||
extern CPlayerInterface * LOCPLINT;
|
||||
|
@ -221,7 +221,9 @@ void CClient::endGame( bool closeConnection /*= true*/ )
|
||||
boost::unique_lock<boost::recursive_mutex> un(*LOCPLINT->pim);
|
||||
logNetwork->infoStream() << "Ending current game!";
|
||||
if(GH.topInt())
|
||||
{
|
||||
GH.topInt()->deactivate();
|
||||
}
|
||||
GH.listInt.clear();
|
||||
GH.objsToBlit.clear();
|
||||
GH.statusbar = nullptr;
|
||||
@ -238,8 +240,8 @@ void CClient::endGame( bool closeConnection /*= true*/ )
|
||||
battleints.clear();
|
||||
callbacks.clear();
|
||||
battleCallbacks.clear();
|
||||
CGObelisk::reset();
|
||||
logNetwork->infoStream() << "Deleted playerInts.";
|
||||
|
||||
logNetwork->infoStream() << "Client stopped.";
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,6 @@
|
||||
<Unit filename="NetPacksClient.cpp" />
|
||||
<Unit filename="SDLMain.h" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option compile="1" />
|
||||
<Option weight="0" />
|
||||
</Unit>
|
||||
<Unit filename="VCMI_client.rc">
|
||||
|
@ -1933,7 +1933,7 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= nullptr*/)
|
||||
waitForAnims();
|
||||
|
||||
TStacks stacksOfMine = tacticianInterface->cb->battleGetStacks(CBattleCallback::ONLY_MINE);
|
||||
stacksOfMine.erase(std::remove_if(stacksOfMine.begin(), stacksOfMine.end(), &immobile), stacksOfMine.end());
|
||||
vstd::erase_if(stacksOfMine, &immobile);
|
||||
auto it = vstd::find(stacksOfMine, current);
|
||||
if(it != stacksOfMine.end() && ++it != stacksOfMine.end())
|
||||
stackActivated(*it);
|
||||
|
@ -214,6 +214,32 @@ void CArtPlace::clickLeft(tribool down, bool previousState)
|
||||
}
|
||||
}
|
||||
|
||||
bool CArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
|
||||
const CGHeroInstance *hero)
|
||||
{
|
||||
std::vector<const CArtifact *> assemblyPossibilities = art->assemblyPossibilities(hero);
|
||||
|
||||
// If the artifact can be assembled, display dialog.
|
||||
for(const CArtifact *combination : assemblyPossibilities)
|
||||
{
|
||||
LOCPLINT->showArtifactAssemblyDialog(
|
||||
art->artType->id,
|
||||
combination->id,
|
||||
true,
|
||||
std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id),
|
||||
0);
|
||||
|
||||
if(assemblyPossibilities.size() > 2)
|
||||
{
|
||||
logGlobal->warnStream() << boost::format(
|
||||
"More than one possibility of assembling on %s... taking only first")
|
||||
% art->artType->Name();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CArtPlace::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(down && ourArt && !locked && text.size() && !picked) //if there is no description or it's a lock, do nothing ;]
|
||||
@ -225,20 +251,8 @@ void CArtPlace::clickRight(tribool down, bool previousState)
|
||||
std::vector<const CArtifact *> assemblyPossibilities = ourArt->assemblyPossibilities(ourOwner->curHero);
|
||||
|
||||
// If the artifact can be assembled, display dialog.
|
||||
for(const CArtifact *combination : assemblyPossibilities)
|
||||
if (askToAssemble(ourArt, slotID, ourOwner->curHero))
|
||||
{
|
||||
LOCPLINT->showArtifactAssemblyDialog(
|
||||
ourArt->artType->id,
|
||||
combination->id,
|
||||
true,
|
||||
std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), ourOwner->curHero, slotID, true, combination->id),
|
||||
0);
|
||||
|
||||
if(assemblyPossibilities.size() > 2)
|
||||
{
|
||||
logGlobal->warnStream() << "More than one possibility of assembling... taking only first";
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
@ -372,31 +386,19 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
|
||||
image->disable();
|
||||
text = std::string();
|
||||
hoverText = CGI->generaltexth->allTexts[507];
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
image->enable();
|
||||
image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex);
|
||||
|
||||
std::string artDesc = ourArt->artType->Description();
|
||||
if (vstd::contains (artDesc, '{'))
|
||||
text = artDesc;
|
||||
else
|
||||
text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style
|
||||
text = art->getEffectiveDescription(ourOwner->curHero);
|
||||
|
||||
if(art->artType->id == ArtifactID::SPELL_SCROLL)
|
||||
{
|
||||
// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
|
||||
// so we want to replace text in [...] with a spell name
|
||||
// however other language versions don't have name placeholder at all, so we have to be careful
|
||||
int spellID = art->getGivenSpellID();
|
||||
size_t nameStart = text.find_first_of('[');
|
||||
size_t nameEnd = text.find_first_of(']', nameStart);
|
||||
if(spellID >= 0)
|
||||
{
|
||||
if(nameStart != std::string::npos && nameEnd != std::string::npos)
|
||||
text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
|
||||
|
||||
//add spell component info (used to provide a pic in r-click popup)
|
||||
baseType = CComponent::spell;
|
||||
type = spellID;
|
||||
@ -409,33 +411,11 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
|
||||
type = art->artType->id;
|
||||
bonusValue = 0;
|
||||
}
|
||||
if (art->artType->constituents) //display info about components of combined artifact
|
||||
{
|
||||
//TODO
|
||||
}
|
||||
else if (art->artType->constituentOf.size()) //display info about set
|
||||
{
|
||||
std::string artList;
|
||||
auto combinedArt = art->artType->constituentOf[0];
|
||||
text += "\n\n";
|
||||
text += "{" + combinedArt->Name() + "}";
|
||||
int wornArtifacts = 0;
|
||||
for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
|
||||
{
|
||||
artList += "\n" + a->Name();
|
||||
if (ourOwner->curHero->hasArt(a->id, true))
|
||||
wornArtifacts++;
|
||||
}
|
||||
text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " +
|
||||
boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
|
||||
//TODO: fancy colors and fonts for this text
|
||||
}
|
||||
|
||||
if (locked) // Locks should appear as empty.
|
||||
hoverText = CGI->generaltexth->allTexts[507];
|
||||
else
|
||||
hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name());
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHero::SCommonPart::reset()
|
||||
@ -587,8 +567,8 @@ void CArtifactsOfHero::setSlotData(CArtPlace* artPlace, ArtifactPosition slotID)
|
||||
|
||||
if(const ArtSlotInfo *asi = curHero->getSlot(slotID))
|
||||
{
|
||||
artPlace->setArtifact(asi->artifact);
|
||||
artPlace->lockSlot(asi->locked);
|
||||
artPlace->setArtifact(asi->artifact);
|
||||
}
|
||||
else
|
||||
artPlace->setArtifact(nullptr);
|
||||
|
@ -74,6 +74,8 @@ public:
|
||||
|
||||
void setMeAsDest(bool backpackAsVoid = true);
|
||||
void setArtifact(const CArtifactInstance *art);
|
||||
static bool askToAssemble(const CArtifactInstance *art, ArtifactPosition slot,
|
||||
const CGHeroInstance *hero);
|
||||
};
|
||||
|
||||
/// Contains artifacts of hero. Distincts which artifacts are worn or backpacked
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "../CMessage.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/CArtifactHolder.h"
|
||||
#include "../windows/CAdvmapInterface.h"
|
||||
|
||||
#include "../../lib/CArtHandler.h"
|
||||
@ -144,14 +145,26 @@ size_t CComponent::getIndex()
|
||||
|
||||
std::string CComponent::getDescription()
|
||||
{
|
||||
switch (compType)
|
||||
switch(compType)
|
||||
{
|
||||
case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
|
||||
: CGI->generaltexth->allTexts[149]; //mana
|
||||
case secskill: return CGI->generaltexth->skillInfoTexts[subtype][val-1];
|
||||
case resource: return CGI->generaltexth->allTexts[242];
|
||||
case creature: return "";
|
||||
case artifact: return CGI->arth->artifacts[subtype]->Description();
|
||||
case artifact:
|
||||
{
|
||||
std::unique_ptr<CArtifactInstance> art;
|
||||
if (subtype != ArtifactID::SPELL_SCROLL)
|
||||
{
|
||||
art.reset(CArtifactInstance::createNewArtifactInstance(subtype));
|
||||
}
|
||||
else
|
||||
{
|
||||
art.reset(CArtifactInstance::createScroll(static_cast<SpellID>(val)));
|
||||
}
|
||||
return art->getEffectiveDescription();
|
||||
}
|
||||
case experience: return CGI->generaltexth->allTexts[241];
|
||||
case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description;
|
||||
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
|
||||
@ -166,7 +179,7 @@ std::string CComponent::getDescription()
|
||||
|
||||
std::string CComponent::getSubtitle()
|
||||
{
|
||||
if (!perDay)
|
||||
if(!perDay)
|
||||
return getSubtitleInternal();
|
||||
|
||||
std::string ret = CGI->generaltexth->allTexts[3];
|
||||
@ -186,19 +199,31 @@ std::string CComponent::getSubtitleInternal()
|
||||
case artifact: return CGI->arth->artifacts[subtype]->Name();
|
||||
case experience:
|
||||
{
|
||||
if (subtype == 1) //+1 level - tree of knowledge
|
||||
if(subtype == 1) //+1 level - tree of knowledge
|
||||
{
|
||||
std::string level = CGI->generaltexth->allTexts[442];
|
||||
boost::replace_first(level, "1", boost::lexical_cast<std::string>(val));
|
||||
return level;
|
||||
}
|
||||
else
|
||||
{
|
||||
return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
|
||||
}
|
||||
}
|
||||
case spell: return CGI->spellh->objects[subtype]->name;
|
||||
case morale: return "";
|
||||
case luck: return "";
|
||||
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
|
||||
case building:
|
||||
{
|
||||
auto building = CGI->townh->factions[subtype]->town->buildings[BuildingID(val)];
|
||||
if(!building)
|
||||
{
|
||||
logGlobal->errorStream() << boost::format("Town of faction %s has no building #%d")
|
||||
% CGI->townh->factions[subtype]->town->faction->name % val;
|
||||
return (boost::format("Missing building #%d") % val).str();
|
||||
}
|
||||
return building->Name();
|
||||
}
|
||||
case hero: return "";
|
||||
case flag: return CGI->generaltexth->capColors[subtype];
|
||||
}
|
||||
|
@ -73,11 +73,11 @@ void CGarrisonSlot::hover (bool on)
|
||||
{
|
||||
if(upg == EGarrisonType::UP)
|
||||
{
|
||||
temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
|
||||
temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
|
||||
}
|
||||
else if(owner->armedObjs[0] && (owner->armedObjs[0]->ID == Obj::TOWN || owner->armedObjs[0]->ID == Obj::HERO))
|
||||
{
|
||||
temp = CGI->generaltexth->tcommands[12]; //Select %s (in garrison)
|
||||
temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -758,14 +758,15 @@ void CAdvMapInt::updateSleepWake(const CGHeroInstance *h)
|
||||
|
||||
void CAdvMapInt::updateMoveHero(const CGHeroInstance *h, tribool hasPath)
|
||||
{
|
||||
//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
|
||||
if (hasPath == boost::indeterminate)
|
||||
hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
|
||||
if (!h)
|
||||
if(!h)
|
||||
{
|
||||
moveHero->block(true);
|
||||
return;
|
||||
}
|
||||
//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
|
||||
if(boost::logic::indeterminate(hasPath))
|
||||
hasPath = LOCPLINT->paths[h].nodes.size() ? true : false;
|
||||
|
||||
moveHero->block(!hasPath || (h->movement == 0));
|
||||
}
|
||||
|
||||
@ -1420,12 +1421,12 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
|
||||
bool canSelect = topBlocking && topBlocking->ID == Obj::HERO && topBlocking->tempOwner == LOCPLINT->playerID;
|
||||
canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
|
||||
|
||||
if (selection->ID != Obj::HERO) //hero is not selected (presumably town)
|
||||
if(selection->ID != Obj::HERO) //hero is not selected (presumably town)
|
||||
{
|
||||
assert(!terrain.currentPath); //path can be active only when hero is selected
|
||||
if(selection == topBlocking) //selected town clicked
|
||||
LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
|
||||
else if ( canSelect )
|
||||
else if(canSelect)
|
||||
select(static_cast<const CArmedInstance*>(topBlocking), false);
|
||||
return;
|
||||
}
|
||||
@ -1444,22 +1445,26 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
|
||||
}
|
||||
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
|
||||
{
|
||||
if (terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
|
||||
if(terrain.currentPath && terrain.currentPath->endPos() == mapPos)//we'll be moving
|
||||
{
|
||||
if (CGI->mh->canStartHeroMovement())
|
||||
LOCPLINT->moveHero(currentHero,*terrain.currentPath);
|
||||
if(CGI->mh->canStartHeroMovement())
|
||||
LOCPLINT->moveHero(currentHero, *terrain.currentPath);
|
||||
return;
|
||||
}
|
||||
else/* if(mp.z == currentHero->pos.z)*/ //remove old path and find a new one if we clicked on the map level on which hero is present
|
||||
else //remove old path and find a new one if we clicked on accessible tile
|
||||
{
|
||||
CGPath &path = LOCPLINT->paths[currentHero];
|
||||
CGPath newpath;
|
||||
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(newpath, mapPos); //try getting path, erase if failed
|
||||
if(gotPath && newpath.nodes.size())
|
||||
path = newpath;
|
||||
|
||||
if(path.nodes.size())
|
||||
terrain.currentPath = &path;
|
||||
bool gotPath = LOCPLINT->cb->getPathsInfo(currentHero)->getPath(path, mapPos); //try getting path, erase if failed
|
||||
updateMoveHero(currentHero);
|
||||
if (!gotPath)
|
||||
LOCPLINT->eraseCurrentPathOf(currentHero);
|
||||
else
|
||||
return;
|
||||
LOCPLINT->eraseCurrentPathOf(currentHero);
|
||||
|
||||
updateMoveHero(currentHero);
|
||||
}
|
||||
}
|
||||
} //end of hero is selected "case"
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/NetPacksBase.h"
|
||||
#include "../mapHandler.h"
|
||||
|
||||
/*
|
||||
* CHeroWindow.cpp, part of VCMI engine
|
||||
@ -275,6 +276,9 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
|
||||
if(!LOCPLINT->cb->howManyTowns() && LOCPLINT->cb->howManyHeroes() == 1)
|
||||
noDismiss = true;
|
||||
|
||||
if(curHero->isMissionCritical())
|
||||
noDismiss = true;
|
||||
|
||||
dismissButton->block(!!curHero->visitedTown || noDismiss);
|
||||
|
||||
if(curHero->getSecSkillLevel(SecondarySkill::TACTICS) == 0)
|
||||
|
@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
|
||||
case ARTIFACT_TYPE:
|
||||
case ARTIFACT_PLACEHOLDER:
|
||||
if(id >= 0)
|
||||
adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down);
|
||||
adventureInt->handleRightClick(hlp->getEffectiveDescription(), down);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,8 @@
|
||||
"ALL_CREATURES_GET_DOUBLE_MONTHS" : false,
|
||||
"NEGATIVE_LUCK" : false,
|
||||
"MAX_HEROES_AVAILABLE_PER_PLAYER" : 16,
|
||||
"MAX_HEROES_ON_MAP_PER_PLAYER" : 8
|
||||
"MAX_HEROES_ON_MAP_PER_PLAYER" : 8,
|
||||
"WINNING_HERO_WITH_NO_TROOPS_RETREATS": true
|
||||
|
||||
},
|
||||
"modules":
|
||||
|
@ -738,9 +738,14 @@ std::string CArtifactInstance::nodeName() const
|
||||
}
|
||||
|
||||
CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s)
|
||||
{
|
||||
return createScroll(s->id);
|
||||
}
|
||||
|
||||
CArtifactInstance *CArtifactInstance::createScroll(SpellID sid)
|
||||
{
|
||||
auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]);
|
||||
auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id);
|
||||
auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid);
|
||||
ret->addNewBonus(b);
|
||||
return ret;
|
||||
}
|
||||
@ -752,6 +757,48 @@ void CArtifactInstance::init()
|
||||
setNodeType(ARTIFACT_INSTANCE);
|
||||
}
|
||||
|
||||
std::string CArtifactInstance::getEffectiveDescription(
|
||||
const CGHeroInstance *hero) const
|
||||
{
|
||||
std::string text = artType->Description();
|
||||
if (!vstd::contains(text, '{'))
|
||||
text = '{' + artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style
|
||||
|
||||
if(artType->id == ArtifactID::SPELL_SCROLL)
|
||||
{
|
||||
// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
|
||||
// so we want to replace text in [...] with a spell name
|
||||
// however other language versions don't have name placeholder at all, so we have to be careful
|
||||
int spellID = getGivenSpellID();
|
||||
size_t nameStart = text.find_first_of('[');
|
||||
size_t nameEnd = text.find_first_of(']', nameStart);
|
||||
if(spellID >= 0)
|
||||
{
|
||||
if(nameStart != std::string::npos && nameEnd != std::string::npos)
|
||||
text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name);
|
||||
}
|
||||
}
|
||||
else if (hero && artType->constituentOf.size()) //display info about set
|
||||
{
|
||||
std::string artList;
|
||||
auto combinedArt = artType->constituentOf[0];
|
||||
text += "\n\n";
|
||||
text += "{" + combinedArt->Name() + "}";
|
||||
int wornArtifacts = 0;
|
||||
for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
|
||||
{
|
||||
artList += "\n" + a->Name();
|
||||
if (hero->hasArt(a->id, true))
|
||||
wornArtifacts++;
|
||||
}
|
||||
text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " +
|
||||
boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList;
|
||||
//TODO: fancy colors and fonts for this text
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const
|
||||
{
|
||||
for(auto slot : artType->possibleSlots.at(h->bearerType()))
|
||||
@ -1152,9 +1199,42 @@ const CArtifactInstance * CArtifactSet::getArtByInstanceId( ArtifactInstanceID a
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/) const
|
||||
bool CArtifactSet::hasArt(ui32 aid, bool onlyWorn /*= false*/,
|
||||
bool searchBackpackAssemblies /*= false*/) const
|
||||
{
|
||||
return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST;
|
||||
return getArtPos(aid, onlyWorn) != ArtifactPosition::PRE_FIRST ||
|
||||
(searchBackpackAssemblies && getHiddenArt(aid));
|
||||
}
|
||||
|
||||
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *>
|
||||
CArtifactSet::searchForConstituent(int aid) const
|
||||
{
|
||||
for(auto & slot : artifactsInBackpack)
|
||||
{
|
||||
auto art = slot.artifact;
|
||||
if(art->canBeDisassembled())
|
||||
{
|
||||
auto ass = static_cast<CCombinedArtifactInstance *>(art.get());
|
||||
for(auto& ci : ass->constituentsInfo)
|
||||
{
|
||||
if(ci.art->artType->id == aid)
|
||||
{
|
||||
return {ass, ci.art};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {nullptr, nullptr};
|
||||
}
|
||||
|
||||
const CArtifactInstance *CArtifactSet::getHiddenArt(int aid) const
|
||||
{
|
||||
return searchForConstituent(aid).second;
|
||||
}
|
||||
|
||||
const CCombinedArtifactInstance *CArtifactSet::getAssemblyByConstituent(int aid) const
|
||||
{
|
||||
return searchForConstituent(aid).first;
|
||||
}
|
||||
|
||||
const ArtSlotInfo * CArtifactSet::getSlot(ArtifactPosition pos) const
|
||||
|
@ -119,6 +119,7 @@ public:
|
||||
void deserializationFix();
|
||||
void setType(CArtifact *Art);
|
||||
|
||||
std::string getEffectiveDescription(const CGHeroInstance *hero = nullptr) const;
|
||||
ArtifactPosition firstAvailableSlot(const CArtifactSet *h) const;
|
||||
ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const;
|
||||
SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none
|
||||
@ -128,7 +129,9 @@ public:
|
||||
virtual bool canBeDisassembled() const;
|
||||
virtual void putAt(ArtifactLocation al);
|
||||
virtual void removeFrom(ArtifactLocation al);
|
||||
virtual bool isPart(const CArtifactInstance *supposedPart) const; //checks if this a part of this artifact: artifact instance is a part of itself, additionally truth is returned for consituents of combined arts
|
||||
/// Checks if this a part of this artifact: artifact instance is a part
|
||||
/// of itself, additionally truth is returned for constituents of combined arts
|
||||
virtual bool isPart(const CArtifactInstance *supposedPart) const;
|
||||
|
||||
std::vector<const CArtifact *> assemblyPossibilities(const CArtifactSet *h) const;
|
||||
void move(ArtifactLocation src, ArtifactLocation dst);
|
||||
@ -141,6 +144,7 @@ public:
|
||||
}
|
||||
|
||||
static CArtifactInstance *createScroll(const CSpell *s);
|
||||
static CArtifactInstance *createScroll(SpellID sid);
|
||||
static CArtifactInstance *createNewArtifactInstance(CArtifact *Art);
|
||||
static CArtifactInstance *createNewArtifactInstance(int aid);
|
||||
};
|
||||
@ -172,7 +176,7 @@ public:
|
||||
|
||||
void createConstituents();
|
||||
void addAsConstituent(CArtifactInstance *art, ArtifactPosition slot);
|
||||
CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replcaed with us (combined art), not lock
|
||||
CArtifactInstance *figureMainConstituent(const ArtifactLocation al); //main constituent is replaced with us (combined art), not lock
|
||||
|
||||
CCombinedArtifactInstance();
|
||||
|
||||
@ -265,10 +269,8 @@ struct DLL_LINKAGE ArtSlotInfo
|
||||
ConstTransitivePtr<CArtifactInstance> artifact;
|
||||
ui8 locked; //if locked, then artifact points to the combined artifact
|
||||
|
||||
ArtSlotInfo()
|
||||
{
|
||||
locked = false;
|
||||
}
|
||||
ArtSlotInfo() : locked(false) {}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & artifact & locked;
|
||||
@ -288,10 +290,16 @@ public:
|
||||
const ArtSlotInfo *getSlot(ArtifactPosition pos) const;
|
||||
const CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true) const; //nullptr - no artifact
|
||||
CArtifactInstance* getArt(ArtifactPosition pos, bool excludeLocked = true); //nullptr - no artifact
|
||||
ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const; //looks for equipped artifact with given ID and returns its slot ID or -1 if none(if more than one such artifact lower ID is returned)
|
||||
/// Looks for equipped artifact with given ID and returns its slot ID or -1 if none
|
||||
/// (if more than one such artifact lower ID is returned)
|
||||
ArtifactPosition getArtPos(int aid, bool onlyWorn = true) const;
|
||||
ArtifactPosition getArtPos(const CArtifactInstance *art) const;
|
||||
const CArtifactInstance *getArtByInstanceId(ArtifactInstanceID artInstId) const;
|
||||
bool hasArt(ui32 aid, bool onlyWorn = false) const; //checks if hero possess artifact of given id (either in backack or worn)
|
||||
/// Search for constituents of assemblies in backpack which do not have an ArtifactPosition
|
||||
const CArtifactInstance *getHiddenArt(int aid) const;
|
||||
const CCombinedArtifactInstance *getAssemblyByConstituent(int aid) const;
|
||||
/// Checks if hero possess artifact of given id (either in backack or worn)
|
||||
bool hasArt(ui32 aid, bool onlyWorn = false, bool searchBackpackAssemblies = false) const;
|
||||
bool isPositionFree(ArtifactPosition pos, bool onlyLockCheck = false) const;
|
||||
si32 getArtTypeId(ArtifactPosition pos) const;
|
||||
|
||||
@ -304,4 +312,7 @@ public:
|
||||
}
|
||||
|
||||
void artDeserializationFix(CBonusSystemNode *node);
|
||||
|
||||
protected:
|
||||
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *> searchForConstituent(int aid) const;
|
||||
};
|
||||
|
@ -66,6 +66,10 @@ const PlayerState * CGameInfoCallback::getPlayer(PlayerColor color, bool verbose
|
||||
{
|
||||
//funtion written from scratch since it's accessed A LOT by AI
|
||||
|
||||
if(!color.isValidPlayer())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
auto player = gs->players.find(color);
|
||||
if (player != gs->players.end())
|
||||
{
|
||||
@ -723,15 +727,16 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
|
||||
return -1;
|
||||
}
|
||||
|
||||
int3 CPlayerSpecificInfoCallback::getGrailPos( double &outKnownRatio )
|
||||
int3 CPlayerSpecificInfoCallback::getGrailPos( double *outKnownRatio )
|
||||
{
|
||||
if (!player || CGObelisk::obeliskCount == 0)
|
||||
{
|
||||
outKnownRatio = 0.0;
|
||||
*outKnownRatio = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
outKnownRatio = static_cast<double>(CGObelisk::visited[gs->getPlayerTeam(*player)->id]) / CGObelisk::obeliskCount;
|
||||
*outKnownRatio = static_cast<double>(CGObelisk::visited[gs->getPlayerTeam(*player)->id])
|
||||
/ CGObelisk::obeliskCount;
|
||||
}
|
||||
return gs->map->grailPos;
|
||||
}
|
||||
@ -964,4 +969,3 @@ void IGameEventRealizer::setObjProperty(ObjectInstanceID objid, int prop, si64 v
|
||||
sob.val = static_cast<ui32>(val);
|
||||
commitPackage(&sob);
|
||||
}
|
||||
|
||||
|
@ -125,7 +125,7 @@ class DLL_LINKAGE CPlayerSpecificInfoCallback : public CGameInfoCallback
|
||||
public:
|
||||
int howManyTowns() const;
|
||||
int howManyHeroes(bool includeGarrisoned = true) const;
|
||||
int3 getGrailPos(double &outKnownRatio);
|
||||
int3 getGrailPos(double *outKnownRatio);
|
||||
boost::optional<PlayerColor> getMyColor() const;
|
||||
|
||||
std::vector <const CGTownInstance *> getTownsInfo(bool onlyOur = true) const; //true -> only owned; false -> all visible
|
||||
@ -154,4 +154,3 @@ public:
|
||||
|
||||
virtual void showInfoDialog(const std::string &msg, PlayerColor player);
|
||||
};
|
||||
|
||||
|
@ -1219,8 +1219,8 @@ CGameState::CrossoverHeroesList CGameState::getCrossoverHeroesFromPreviousScenar
|
||||
// remove heroes which didn't reached the end of the scenario, but were available at the start
|
||||
for(auto hero : lostCrossoverHeroes)
|
||||
{
|
||||
crossoverHeroes.heroesFromAnyPreviousScenarios.erase(range::remove_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
|
||||
CGObjectInstanceBySubIdFinder(hero)), crossoverHeroes.heroesFromAnyPreviousScenarios.end());
|
||||
vstd::erase_if(crossoverHeroes.heroesFromAnyPreviousScenarios,
|
||||
CGObjectInstanceBySubIdFinder(hero));
|
||||
}
|
||||
|
||||
// now add heroes which completed the scenario
|
||||
@ -1665,8 +1665,9 @@ void CGameState::initStartingBonus()
|
||||
int res = VLC->townh->factions[scenarioOps->playerInfos[elem.first].castle]->town->primaryRes;
|
||||
if(res == Res::WOOD_AND_ORE)
|
||||
{
|
||||
elem.second.resources[Res::WOOD] += rand.nextInt(5, 10);
|
||||
elem.second.resources[Res::ORE] += rand.nextInt(5, 10);
|
||||
int amount = rand.nextInt(5, 10);
|
||||
elem.second.resources[Res::WOOD] += amount;
|
||||
elem.second.resources[Res::ORE] += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -2267,7 +2268,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
||||
|
||||
for (const TriggeredEvent & event : map->triggeredEvents)
|
||||
{
|
||||
if ((event.trigger.test(evaluateEvent)))
|
||||
if (event.trigger.test(evaluateEvent))
|
||||
{
|
||||
if (event.effect.type == EventEffect::VICTORY)
|
||||
return EVictoryLossCheckResult::victory(event.onFulfill, event.effect.toOtherMessage);
|
||||
@ -2284,7 +2285,7 @@ EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(PlayerColor player) c
|
||||
return EVictoryLossCheckResult();
|
||||
}
|
||||
|
||||
bool CGameState::checkForVictory( PlayerColor player, const EventCondition & condition ) const
|
||||
bool CGameState::checkForVictory(PlayerColor player, const EventCondition & condition) const
|
||||
{
|
||||
const PlayerState *p = CGameInfoCallback::getPlayer(player);
|
||||
switch (condition.condition)
|
||||
|
@ -551,21 +551,42 @@ CModHandler::CModHandler()
|
||||
|
||||
void CModHandler::loadConfigFromFile (std::string name)
|
||||
{
|
||||
std::string paths;
|
||||
for(auto& p : CResourceHandler::get()->getResourceNames(ResourceID("config/" + name)))
|
||||
{
|
||||
paths += p.string() + ", ";
|
||||
}
|
||||
paths = paths.substr(0, paths.size() - 2);
|
||||
logGlobal->debugStream() << "Loading hardcoded features settings from [" << paths << "], result:";
|
||||
settings.data = JsonUtils::assembleFromFiles("config/" + name);
|
||||
const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"];
|
||||
settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Float();
|
||||
logGlobal->debugStream() << "\tMAX_HEROES_AVAILABLE_PER_PLAYER\t" << settings.MAX_HEROES_AVAILABLE_PER_PLAYER;
|
||||
settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Float();
|
||||
logGlobal->debugStream() << "\tMAX_HEROES_ON_MAP_PER_PLAYER\t" << settings.MAX_HEROES_ON_MAP_PER_PLAYER;
|
||||
settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Float();
|
||||
logGlobal->debugStream() << "\tCREEP_SIZE\t" << settings.CREEP_SIZE;
|
||||
settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Float();
|
||||
logGlobal->debugStream() << "\tWEEKLY_GROWTH\t" << settings.WEEKLY_GROWTH;
|
||||
settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Float();
|
||||
logGlobal->debugStream() << "\tNEUTRAL_STACK_EXP\t" << settings.NEUTRAL_STACK_EXP;
|
||||
settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Float();
|
||||
logGlobal->debugStream() << "\tMAX_BUILDING_PER_TURN\t" << settings.MAX_BUILDING_PER_TURN;
|
||||
settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool();
|
||||
logGlobal->debugStream() << "\tDWELLINGS_ACCUMULATE_CREATURES\t" << settings.DWELLINGS_ACCUMULATE_CREATURES;
|
||||
settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool();
|
||||
logGlobal->debugStream() << "\tALL_CREATURES_GET_DOUBLE_MONTHS\t" << settings.ALL_CREATURES_GET_DOUBLE_MONTHS;
|
||||
settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool();
|
||||
logGlobal->debugStream() << "\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t" << settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS;
|
||||
const JsonNode & gameModules = settings.data["modules"];
|
||||
modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool();
|
||||
logGlobal->debugStream() << "\tSTACK_EXP\t" << modules.STACK_EXP;
|
||||
modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool();
|
||||
logGlobal->debugStream() << "\tSTACK_ARTIFACT\t" << modules.STACK_ARTIFACT;
|
||||
modules.COMMANDERS = gameModules["COMMANDERS"].Bool();
|
||||
logGlobal->debugStream() << "\tCOMMANDERS\t" << modules.COMMANDERS;
|
||||
modules.MITHRIL = gameModules["MITHRIL"].Bool();
|
||||
logGlobal->debugStream() << "\tMITHRIL\t" << modules.MITHRIL;
|
||||
}
|
||||
|
||||
// currentList is passed by value to get current list of depending mods
|
||||
|
@ -259,11 +259,21 @@ public:
|
||||
bool ALL_CREATURES_GET_DOUBLE_MONTHS;
|
||||
int MAX_HEROES_AVAILABLE_PER_PLAYER;
|
||||
int MAX_HEROES_ON_MAP_PER_PLAYER;
|
||||
bool WINNING_HERO_WITH_NO_TROOPS_RETREATS;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & data & CREEP_SIZE & WEEKLY_GROWTH & NEUTRAL_STACK_EXP & MAX_BUILDING_PER_TURN;
|
||||
h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS & MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER;
|
||||
h & DWELLINGS_ACCUMULATE_CREATURES & ALL_CREATURES_GET_DOUBLE_MONTHS &
|
||||
MAX_HEROES_AVAILABLE_PER_PLAYER & MAX_HEROES_ON_MAP_PER_PLAYER;
|
||||
if(version >= 756)
|
||||
{
|
||||
h & WINNING_HERO_WITH_NO_TROOPS_RETREATS;
|
||||
}
|
||||
else if(!h.saving)
|
||||
{
|
||||
WINNING_HERO_WITH_NO_TROOPS_RETREATS = true;
|
||||
}
|
||||
}
|
||||
} settings;
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "mapping/CCampaignHandler.h" //for CCampaignState
|
||||
#include "rmg/CMapGenerator.h" // for CMapGenOptions
|
||||
|
||||
const ui32 version = 755;
|
||||
const ui32 version = 757;
|
||||
const ui32 minSupportedVersion = 753;
|
||||
|
||||
class CISer;
|
||||
|
@ -56,6 +56,15 @@ JsonNode::JsonNode(ResourceID && fileURI):
|
||||
*this = parser.parse(fileURI.getName());
|
||||
}
|
||||
|
||||
JsonNode::JsonNode(const ResourceID & fileURI):
|
||||
type(DATA_NULL)
|
||||
{
|
||||
auto file = CResourceHandler::get()->load(fileURI)->readAll();
|
||||
|
||||
JsonParser parser(reinterpret_cast<char*>(file.first.get()), file.second);
|
||||
*this = parser.parse(fileURI.getName());
|
||||
}
|
||||
|
||||
JsonNode::JsonNode(ResourceID && fileURI, bool &isValidSyntax):
|
||||
type(DATA_NULL)
|
||||
{
|
||||
|
@ -55,6 +55,7 @@ public:
|
||||
explicit JsonNode(const char * data, size_t datasize);
|
||||
//Create tree from JSON file
|
||||
explicit JsonNode(ResourceID && fileURI);
|
||||
explicit JsonNode(const ResourceID & fileURI);
|
||||
explicit JsonNode(ResourceID && fileURI, bool & isValidSyntax);
|
||||
//Copy c-tor
|
||||
JsonNode(const JsonNode ©);
|
||||
|
@ -362,6 +362,10 @@ DLL_LINKAGE void RemoveObject::applyGs( CGameState *gs )
|
||||
p->heroes -= h;
|
||||
h->detachFrom(h->whereShouldBeAttached(gs));
|
||||
h->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero
|
||||
vstd::erase_if(h->artifactsInBackpack, [](const ArtSlotInfo& asi)
|
||||
{
|
||||
return asi.artifact->artType->id == ArtifactID::GRAIL;
|
||||
});
|
||||
|
||||
if(h->visitedTown)
|
||||
{
|
||||
@ -590,7 +594,11 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
|
||||
|
||||
h->setOwner(player);
|
||||
h->pos = tile;
|
||||
bool fresh = !h->isInitialized();
|
||||
if(fresh)
|
||||
{ // this is a fresh hero who hasn't appeared yet
|
||||
h->movement = h->maxMovePoints(true);
|
||||
}
|
||||
|
||||
gs->hpool.heroesPool.erase(hid);
|
||||
if(h->id == ObjectInstanceID())
|
||||
@ -604,7 +612,10 @@ DLL_LINKAGE void HeroRecruited::applyGs( CGameState *gs )
|
||||
gs->map->heroesOnMap.push_back(h);
|
||||
p->heroes.push_back(h);
|
||||
h->attachTo(p);
|
||||
if(fresh)
|
||||
{
|
||||
h->initObj();
|
||||
}
|
||||
gs->map->addBlockVisTiles(h);
|
||||
|
||||
if(t)
|
||||
@ -754,7 +765,7 @@ DLL_LINKAGE const CArtifactInstance *ArtifactLocation::getArt() const
|
||||
return s->artifact;
|
||||
else
|
||||
{
|
||||
logNetwork->warnStream() << "ArtifactLocation::getArt: That location is locked!";
|
||||
logNetwork->warnStream() << "ArtifactLocation::getArt: This location is locked!";
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@ -914,6 +925,32 @@ DLL_LINKAGE void PutArtifact::applyGs( CGameState *gs )
|
||||
|
||||
DLL_LINKAGE void EraseArtifact::applyGs( CGameState *gs )
|
||||
{
|
||||
auto slot = al.getSlot();
|
||||
if(slot->locked)
|
||||
{
|
||||
logGlobal->debugStream() << "Erasing locked artifact: " << slot->artifact->artType->Name();
|
||||
DisassembledArtifact dis;
|
||||
dis.al.artHolder = al.artHolder;
|
||||
auto aset = al.getHolderArtSet();
|
||||
bool found = false;
|
||||
for(auto& p : aset->artifactsWorn)
|
||||
{
|
||||
auto art = p.second.artifact;
|
||||
if(art->canBeDisassembled() && art->isPart(slot->artifact))
|
||||
{
|
||||
dis.al.slot = aset->getArtPos(art);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(found && "Failed to determine the assembly this locked artifact belongs to");
|
||||
logGlobal->debugStream() << "Found the corresponding assembly: " << dis.al.getSlot()->artifact->artType->Name();
|
||||
dis.applyGs(gs);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->debugStream() << "Erasing artifact " << slot->artifact->artType->Name();
|
||||
}
|
||||
al.removeArtifact();
|
||||
}
|
||||
|
||||
|
@ -201,7 +201,6 @@
|
||||
<Unit filename="ScopeGuard.h" />
|
||||
<Unit filename="StartInfo.h" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option compile="1" />
|
||||
<Option weight="0" />
|
||||
</Unit>
|
||||
<Unit filename="StringConstants.h" />
|
||||
|
@ -87,6 +87,20 @@ boost::optional<boost::filesystem::path> CFilesystemList::getResourceName(const
|
||||
return boost::optional<boost::filesystem::path>();
|
||||
}
|
||||
|
||||
std::set<boost::filesystem::path> CFilesystemList::getResourceNames(const ResourceID & resourceName) const
|
||||
{
|
||||
std::set<boost::filesystem::path> paths;
|
||||
for(auto& loader : getResourcesWithName(resourceName))
|
||||
{
|
||||
auto rn = loader->getResourceName(resourceName);
|
||||
if(rn)
|
||||
{
|
||||
paths.insert(*rn);
|
||||
}
|
||||
}
|
||||
return std::move(paths);
|
||||
}
|
||||
|
||||
std::unordered_set<ResourceID> CFilesystemList::getFilteredFiles(std::function<bool(const ResourceID &)> filter) const
|
||||
{
|
||||
std::unordered_set<ResourceID> ret;
|
||||
|
@ -58,15 +58,8 @@ class DLL_LINKAGE CFilesystemList : public ISimpleResourceLoader
|
||||
std::set<ISimpleResourceLoader *> writeableLoaders;
|
||||
|
||||
//FIXME: this is only compile fix, should be removed in the end
|
||||
CFilesystemList(CFilesystemList &)
|
||||
{
|
||||
//class is not copyable
|
||||
}
|
||||
CFilesystemList &operator=(CFilesystemList &)
|
||||
{
|
||||
//class is not copyable
|
||||
return *this;
|
||||
}
|
||||
CFilesystemList(CFilesystemList &) = delete;
|
||||
CFilesystemList &operator=(CFilesystemList &) = delete;
|
||||
|
||||
public:
|
||||
CFilesystemList();
|
||||
@ -77,6 +70,7 @@ public:
|
||||
bool existsResource(const ResourceID & resourceName) const override;
|
||||
std::string getMountPoint() const override;
|
||||
boost::optional<boost::filesystem::path> getResourceName(const ResourceID & resourceName) const override;
|
||||
std::set<boost::filesystem::path> getResourceNames(const ResourceID & resourceName) const override;
|
||||
std::unordered_set<ResourceID> getFilteredFiles(std::function<bool(const ResourceID &)> filter) const override;
|
||||
bool createResource(std::string filename, bool update = false) override;
|
||||
std::vector<const ISimpleResourceLoader *> getResourcesWithName(const ResourceID & resourceName) const override;
|
||||
|
@ -53,6 +53,22 @@ public:
|
||||
return boost::optional<boost::filesystem::path>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all full names of matching resources, e.g. names of files in filesystem.
|
||||
*
|
||||
* @return std::set with names.
|
||||
*/
|
||||
virtual std::set<boost::filesystem::path> getResourceNames(const ResourceID & resourceName) const
|
||||
{
|
||||
std::set<boost::filesystem::path> result;
|
||||
auto rn = getResourceName(resourceName);
|
||||
if(rn)
|
||||
{
|
||||
result.insert(*rn);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of files that matches filter function
|
||||
*
|
||||
|
@ -12,6 +12,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../CConsoleHandler.h"
|
||||
#include "../filesystem/FileStream.h"
|
||||
|
||||
class CLogger;
|
||||
struct LogRecord;
|
||||
@ -302,7 +303,7 @@ public:
|
||||
void write(const LogRecord & record) override;
|
||||
|
||||
private:
|
||||
boost::filesystem::ofstream file;
|
||||
FileStream file;
|
||||
CLogFormatter formatter;
|
||||
mutable boost::mutex mx;
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../BattleState.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "CGTownInstance.h"
|
||||
|
||||
///helpers
|
||||
@ -220,7 +221,9 @@ CGHeroInstance::CGHeroInstance()
|
||||
setNodeType(HERO);
|
||||
ID = Obj::HERO;
|
||||
tacticFormationEnabled = inTownGarrison = false;
|
||||
mana = movement = portrait = -1;
|
||||
mana = UNINITIALIZED_MANA;
|
||||
movement = UNINITIALIZED_MOVEMENT;
|
||||
portrait = UNINITIALIZED_PORTRAIT;
|
||||
isStanding = true;
|
||||
moveDir = 4;
|
||||
level = 1;
|
||||
@ -1453,3 +1456,26 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subty
|
||||
return (distance < visionsRange) && (target->pos.z == pos.z);
|
||||
}
|
||||
|
||||
bool CGHeroInstance::isMissionCritical() const
|
||||
{
|
||||
for(const TriggeredEvent & event : IObjectInterface::cb->getMapHeader()->triggeredEvents)
|
||||
{
|
||||
if(event.trigger.test([&](const EventCondition & condition)
|
||||
{
|
||||
if (condition.condition == EventCondition::CONTROL && condition.object)
|
||||
{
|
||||
auto hero = dynamic_cast<const CGHeroInstance*>(condition.object);
|
||||
return (hero != this);
|
||||
}
|
||||
else if(condition.condition == EventCondition::IS_HUMAN)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
class CHero;
|
||||
class CGBoat;
|
||||
class CGTownInstance;
|
||||
class CMap;
|
||||
struct TerrainTile;
|
||||
struct TurnInfo;
|
||||
|
||||
@ -64,6 +65,9 @@ public:
|
||||
ConstTransitivePtr<CCommanderInstance> commander;
|
||||
const CGBoat *boat; //set to CGBoat when sailing
|
||||
|
||||
static const ui32 UNINITIALIZED_PORTRAIT = -1;
|
||||
static const ui32 UNINITIALIZED_MANA = -1;
|
||||
static const ui32 UNINITIALIZED_MOVEMENT = -1;
|
||||
|
||||
//std::vector<const CArtifact*> artifacts; //hero's artifacts from bag
|
||||
//std::map<ui16, const CArtifact*> artifWorn; //map<position,artifact_id>; positions: 0 - head; 1 - shoulders; 2 - neck; 3 - right hand; 4 - left hand; 5 - torso; 6 - right ring; 7 - left ring; 8 - feet; 9 - misc1; 10 - misc2; 11 - misc3; 12 - misc4; 13 - mach1; 14 - mach2; 15 - mach3; 16 - mach4; 17 - spellbook; 18 - misc5
|
||||
@ -124,6 +128,11 @@ public:
|
||||
}
|
||||
} skillsInfo;
|
||||
|
||||
inline bool isInitialized() const
|
||||
{ // has this hero been on the map at least once?
|
||||
return movement != UNINITIALIZED_MOVEMENT && mana != UNINITIALIZED_MANA;
|
||||
}
|
||||
|
||||
//int3 getSightCenter() const; //"center" tile from which the sight distance is calculated
|
||||
int getSightRadious() const override; //sight distance (should be used if player-owned structure)
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@ -203,6 +212,8 @@ public:
|
||||
void updateSkill(SecondarySkill which, int val);
|
||||
|
||||
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
|
||||
/// If this hero perishes, the scenario is failed
|
||||
bool isMissionCritical() const;
|
||||
|
||||
CGHeroInstance();
|
||||
virtual ~CGHeroInstance();
|
||||
|
@ -295,10 +295,10 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int nega
|
||||
|
||||
void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
if(result.winner)
|
||||
return;
|
||||
|
||||
if(result.winner == 0)
|
||||
{
|
||||
giveContentsUpToExp(hero);
|
||||
}
|
||||
}
|
||||
|
||||
void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
|
||||
|
@ -169,7 +169,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
|
||||
{
|
||||
for(int h=0; h<getHeight(); ++h)
|
||||
{
|
||||
if (appearance.isBlockedAt(w, h))
|
||||
if(appearance.isBlockedAt(w, h))
|
||||
ret.insert(int3(pos.x - w, pos.y - h, pos.z));
|
||||
}
|
||||
}
|
||||
@ -191,7 +191,13 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
//recalculate blockvis tiles - new appearance might have different blockmap than before
|
||||
cb->gameState()->map->removeBlockVisTiles(this, true);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(ID, subID);
|
||||
if (!handler->getTemplates(tile.terType).empty())
|
||||
if(!handler)
|
||||
{
|
||||
logGlobal->errorStream() << boost::format(
|
||||
"Unknown object type %d:%d at %s") % ID % subID % visitablePos();
|
||||
return;
|
||||
}
|
||||
if(!handler->getTemplates(tile.terType).empty())
|
||||
appearance = handler->getTemplates(tile.terType)[0];
|
||||
else
|
||||
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
|
||||
@ -328,9 +334,9 @@ int3 IBoatGenerator::bestLocation() const
|
||||
|
||||
for (auto & offset : offsets)
|
||||
{
|
||||
if (const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map
|
||||
if(const TerrainTile *tile = IObjectInterface::cb->getTile(o->pos + offset, false)) //tile is in the map
|
||||
{
|
||||
if (tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat
|
||||
if(tile->terType == ETerrainType::WATER && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat
|
||||
return o->pos + offset;
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ bool CQuest::checkQuest (const CGHeroInstance * h) const
|
||||
case MISSION_ART:
|
||||
for (auto & elem : m5arts)
|
||||
{
|
||||
if (h->hasArt(elem))
|
||||
if (h->hasArt(elem, false, true))
|
||||
continue;
|
||||
return false; //if the artifact was not found
|
||||
}
|
||||
@ -416,6 +416,7 @@ void CGSeerHut::init()
|
||||
{
|
||||
seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, cb->gameState()->getRandomGenerator());
|
||||
quest->textOption = cb->gameState()->getRandomGenerator().nextInt(2);
|
||||
quest->completedOption = cb->gameState()->getRandomGenerator().nextInt(1, 5);
|
||||
}
|
||||
|
||||
void CGSeerHut::initObj()
|
||||
@ -435,7 +436,7 @@ void CGSeerHut::initObj()
|
||||
else
|
||||
{
|
||||
quest->progress = CQuest::COMPLETE;
|
||||
quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->textOption];
|
||||
quest->firstVisitText = VLC->generaltexth->seerEmpty[quest->completedOption];
|
||||
}
|
||||
}
|
||||
|
||||
@ -533,7 +534,7 @@ void CGSeerHut::newTurn() const
|
||||
{
|
||||
if (quest->lastDay >= 0 && quest->lastDay < cb->getDate()-1) //time is up
|
||||
{
|
||||
cb->setObjProperty (id, 10, CQuest::COMPLETE);
|
||||
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -550,7 +551,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
|
||||
if (firstVisit)
|
||||
{
|
||||
isCustom = quest->isCustomFirst;
|
||||
cb->setObjProperty (id, 10, CQuest::IN_PROGRESS);
|
||||
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::IN_PROGRESS);
|
||||
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (quest, this, visitablePos());
|
||||
@ -582,7 +583,7 @@ void CGSeerHut::onHeroVisit( const CGHeroInstance * h ) const
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text << VLC->generaltexth->seerEmpty[quest->textOption];
|
||||
iw.text << VLC->generaltexth->seerEmpty[quest->completedOption];
|
||||
if (ID == Obj::SEER_HUT)
|
||||
iw.text.addReplacement(seerName);
|
||||
cb->showInfoDialog(&iw);
|
||||
@ -630,6 +631,18 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
|
||||
case CQuest::MISSION_ART:
|
||||
for (auto & elem : quest->m5arts)
|
||||
{
|
||||
if(!h->hasArt(elem))
|
||||
{
|
||||
// first we need to disassemble this backpack artifact
|
||||
auto assembly = h->getAssemblyByConstituent(elem);
|
||||
assert(assembly);
|
||||
for(auto & ci : assembly->constituentsInfo)
|
||||
{
|
||||
cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::PRE_FIRST);
|
||||
}
|
||||
// remove the assembly
|
||||
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly)));
|
||||
}
|
||||
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false)));
|
||||
}
|
||||
break;
|
||||
@ -645,7 +658,7 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cb->setObjProperty (id, 10, CQuest::COMPLETE); //mission complete
|
||||
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE); //mission complete
|
||||
completeQuest(h); //make sure to remove QuestGuard at the very end
|
||||
}
|
||||
}
|
||||
|
@ -37,8 +37,11 @@ public:
|
||||
std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
|
||||
std::vector<ui32> m7resources; //TODO: use resourceset?
|
||||
|
||||
//following field are used only for kill creature/hero missions, the original objects became inaccessible after their removal, so we need to store info needed for messages / hover text
|
||||
// following fields are used only for kill creature/hero missions, the original
|
||||
// objects became inaccessible after their removal, so we need to store info
|
||||
// needed for messages / hover text
|
||||
ui8 textOption;
|
||||
ui8 completedOption;
|
||||
CStackBasicDescriptor stackToKill;
|
||||
ui8 stackDirection;
|
||||
std::string heroName; //backup of hero name
|
||||
@ -66,7 +69,16 @@ public:
|
||||
{
|
||||
h & qid & missionType & progress & lastDay & m13489val & m2stats & m5arts & m6creatures & m7resources
|
||||
& textOption & stackToKill & stackDirection & heroName & heroPortrait
|
||||
& firstVisitText & nextVisitText & completedText & isCustomFirst & isCustomNext & isCustomComplete;
|
||||
& firstVisitText & nextVisitText & completedText & isCustomFirst
|
||||
& isCustomNext & isCustomComplete;
|
||||
if(version >= 757)
|
||||
{
|
||||
h & completedOption;
|
||||
}
|
||||
else if(!h.saving)
|
||||
{
|
||||
completedOption = 1;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -118,6 +130,8 @@ public:
|
||||
h & rewardType & rID & rVal & seerName;
|
||||
}
|
||||
protected:
|
||||
static constexpr int OBJPROP_VISITED = 10;
|
||||
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
};
|
||||
|
||||
|
@ -496,6 +496,7 @@ void CGPickable::initObj()
|
||||
info.resize(1);
|
||||
info[0].message.addTxt(MetaString::ADVOB_TXT, 51);
|
||||
info[0].reward.removeObject = true;
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
info.resize(1);
|
||||
@ -682,7 +683,6 @@ void CGBonusingObject::initObj()
|
||||
info[i].message.addTxt(MetaString::ADVOB_TXT, 62);
|
||||
soundID = soundBase::experience;
|
||||
}
|
||||
onVisited.addTxt(MetaString::ADVOB_TXT, 63);
|
||||
info.back().limiter.dayOfWeek = 7;
|
||||
configureBonus(info.back(), Bonus::MORALE, 1, 68); // on last day of week
|
||||
configureBonus(info.back(), Bonus::LUCK, 1, 68);
|
||||
|
@ -133,10 +133,10 @@ namespace JsonRandom
|
||||
if (value["type"].getType() == JsonNode::DATA_STRING)
|
||||
return SpellID(VLC->modh->identifiers.getIdentifier("spell", value["type"]).get());
|
||||
|
||||
spells.erase(std::remove_if(spells.begin(), spells.end(), [=](SpellID spell)
|
||||
vstd::erase_if(spells, [=](SpellID spell)
|
||||
{
|
||||
return VLC->spellh->objects[spell]->level != si32(value["level"].Float());
|
||||
}), spells.end());
|
||||
});
|
||||
|
||||
return SpellID(*RandomGeneratorUtil::nextItem(spells, rng));
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
#include "../CPlayerState.h"
|
||||
|
||||
std::map <si32, std::vector<ObjectInstanceID> > CGMagi::eyelist;
|
||||
ui8 CGObelisk::obeliskCount; //how many obelisks are on map
|
||||
ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map
|
||||
std::map<TeamID, ui8> CGObelisk::visited; //map: team_id => how many obelisks has been visited
|
||||
|
||||
///helpers
|
||||
@ -60,7 +60,7 @@ static std::string & visitedTxt(const bool visited)
|
||||
|
||||
void CPlayersVisited::setPropertyDer( ui8 what, ui32 val )
|
||||
{
|
||||
if(what == 10)
|
||||
if(what == CPlayersVisited::OBJPROP_VISITED)
|
||||
players.insert(PlayerColor(val));
|
||||
}
|
||||
|
||||
@ -392,6 +392,7 @@ void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) co
|
||||
if(cost)
|
||||
cb->giveResource(h->tempOwner,Res::GOLD,-cost);
|
||||
|
||||
giveReward(h);
|
||||
cb->tryJoiningArmy(this, h, true, true);
|
||||
}
|
||||
}
|
||||
@ -452,9 +453,14 @@ void CGCreature::flee( const CGHeroInstance * h ) const
|
||||
|
||||
void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
|
||||
if(result.winner==0)
|
||||
if(result.winner == 0)
|
||||
{
|
||||
giveReward(hero);
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else if(result.winner > 1) // draw
|
||||
{
|
||||
// guarded reward is lost forever on draw
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else
|
||||
@ -462,11 +468,11 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
|
||||
//merge stacks into one
|
||||
TSlots::const_iterator i;
|
||||
CCreature * cre = VLC->creh->creatures[formation.basicType];
|
||||
for (i = stacks.begin(); i != stacks.end(); i++)
|
||||
for(i = stacks.begin(); i != stacks.end(); i++)
|
||||
{
|
||||
if (cre->isMyUpgrade(i->second->type))
|
||||
if(cre->isMyUpgrade(i->second->type))
|
||||
{
|
||||
cb->changeStackType (StackLocation(this, i->first), cre); //un-upgrade creatures
|
||||
cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
|
||||
}
|
||||
}
|
||||
|
||||
@ -474,16 +480,16 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
|
||||
if(!hasStackAtSlot(SlotID(0)))
|
||||
cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
|
||||
|
||||
while (stacks.size() > 1) //hopefully that's enough
|
||||
while(stacks.size() > 1) //hopefully that's enough
|
||||
{
|
||||
// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
|
||||
i = stacks.end();
|
||||
i--;
|
||||
SlotID slot = getSlotFor(i->second->type);
|
||||
if (slot == i->first) //no reason to move stack to its own slot
|
||||
if(slot == i->first) //no reason to move stack to its own slot
|
||||
break;
|
||||
else
|
||||
cb->moveStack (StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
|
||||
cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
|
||||
}
|
||||
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
|
||||
@ -557,6 +563,35 @@ int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
|
||||
return split;
|
||||
}
|
||||
|
||||
void CGCreature::giveReward(const CGHeroInstance * h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
|
||||
if(resources.size())
|
||||
{
|
||||
cb->giveResources(h->tempOwner, resources);
|
||||
for(int i = 0; i < resources.size(); i++)
|
||||
{
|
||||
if(resources[i] > 0)
|
||||
iw.components.push_back(Component(Component::RESOURCE, i, resources[i], 0));
|
||||
}
|
||||
}
|
||||
|
||||
if(gainedArtifact != ArtifactID::NONE)
|
||||
{
|
||||
cb->giveHeroNewArtifact(h, VLC->arth->artifacts[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
|
||||
iw.components.push_back(Component(Component::ARTIFACT, gainedArtifact, 0, 0));
|
||||
}
|
||||
|
||||
if(iw.components.size())
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
|
||||
iw.text.addReplacement(h->name);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
}
|
||||
|
||||
void CGMine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
int relations = cb->gameState()->getPlayerRelations(h->tempOwner, tempOwner);
|
||||
@ -1279,7 +1314,7 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
|
||||
iw.soundID = soundBase::gazebo;
|
||||
iw.player = h->getOwner();
|
||||
if(!wasVisited(h->tempOwner))
|
||||
cb->setObjProperty(id, 10, h->tempOwner.getNum());
|
||||
cb->setObjProperty(id, CGWitchHut::OBJPROP_VISITED, h->tempOwner.getNum());
|
||||
ui32 txt_id;
|
||||
if(h->getSecSkillLevel(SecondarySkill(ability))) //you already know this skill
|
||||
{
|
||||
@ -1389,7 +1424,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
}
|
||||
|
||||
if(!wasVisited(h->tempOwner))
|
||||
cb->setObjProperty(id, 10, h->tempOwner.getNum());
|
||||
cb->setObjProperty(id, CGShrine::OBJPROP_VISITED, h->tempOwner.getNum());
|
||||
|
||||
InfoWindow iw;
|
||||
iw.soundID = soundBase::temple;
|
||||
@ -1636,6 +1671,7 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
|
||||
cb->sendAndApply(&cv);
|
||||
}
|
||||
cv.pos = h->getPosition(false);
|
||||
cv.focusTime = 0;
|
||||
cb->sendAndApply(&cv);
|
||||
}
|
||||
}
|
||||
@ -1790,7 +1826,7 @@ void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answ
|
||||
//water = 0; land = 1; underground = 2;
|
||||
cb->getAllTiles (fw.tiles, hero->tempOwner, subID - 1, !subID + 1); //reveal appropriate tiles
|
||||
cb->sendAndApply (&fw);
|
||||
cb->setObjProperty (id, 10, hero->tempOwner.getNum());
|
||||
cb->setObjProperty (id, CCartographer::OBJPROP_VISITED, hero->tempOwner.getNum());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1812,11 +1848,16 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 96);
|
||||
cb->sendAndApply(&iw);
|
||||
|
||||
cb->setObjProperty(id, 20, h->tempOwner.getNum()); //increment general visited obelisks counter
|
||||
// increment general visited obelisks counter
|
||||
cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum());
|
||||
|
||||
openWindow(OpenWindow::PUZZLE_MAP, h->tempOwner.getNum());
|
||||
|
||||
cb->setObjProperty(id, 10, h->tempOwner.getNum()); //mark that particular obelisk as visited
|
||||
// mark that particular obelisk as visited for all players in the team
|
||||
for (auto & color : ts->players)
|
||||
{
|
||||
cb->setObjProperty(id, CGObelisk::OBJPROP_VISITED, color.getNum());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1831,6 +1872,12 @@ void CGObelisk::initObj()
|
||||
obeliskCount++;
|
||||
}
|
||||
|
||||
void CGObelisk::reset()
|
||||
{
|
||||
obeliskCount = 0;
|
||||
visited.clear();
|
||||
}
|
||||
|
||||
std::string CGObelisk::getHoverText(PlayerColor player) const
|
||||
{
|
||||
return getObjectName() + " " + visitedTxt(wasVisited(player));
|
||||
@ -1838,19 +1885,25 @@ std::string CGObelisk::getHoverText(PlayerColor player) const
|
||||
|
||||
void CGObelisk::setPropertyDer( ui8 what, ui32 val )
|
||||
{
|
||||
CPlayersVisited::setPropertyDer(what, val);
|
||||
switch(what)
|
||||
{
|
||||
case 20:
|
||||
assert(val < PlayerColor::PLAYER_LIMIT_I);
|
||||
visited[TeamID(val)]++;
|
||||
|
||||
if(visited[TeamID(val)] > obeliskCount)
|
||||
case CGObelisk::OBJPROP_INC:
|
||||
{
|
||||
logGlobal->errorStream() << "Error: Visited " << visited[TeamID(val)] << "\t\t" << obeliskCount;
|
||||
assert(val < PlayerColor::PLAYER_LIMIT_I);
|
||||
auto progress = ++visited[TeamID(val)];
|
||||
logGlobal->debugStream() << boost::format("Player %d: obelisk progress %d / %d")
|
||||
% val % static_cast<int>(progress) % static_cast<int>(obeliskCount);
|
||||
|
||||
if(progress > obeliskCount)
|
||||
{
|
||||
logGlobal->errorStream() << "Error: Visited " << progress << "\t\t" << obeliskCount;
|
||||
assert(0);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CPlayersVisited::setPropertyDer(what, val);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,8 @@ public:
|
||||
h & static_cast<CGObjectInstance&>(*this);
|
||||
h & players;
|
||||
}
|
||||
|
||||
static constexpr int OBJPROP_VISITED = 10;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
|
||||
@ -90,6 +92,7 @@ private:
|
||||
void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
|
||||
|
||||
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
|
||||
void giveReward(const CGHeroInstance * h) const;
|
||||
|
||||
};
|
||||
|
||||
@ -450,12 +453,14 @@ class DLL_LINKAGE CGDenOfthieves : public CGObjectInstance
|
||||
class DLL_LINKAGE CGObelisk : public CPlayersVisited
|
||||
{
|
||||
public:
|
||||
static constexpr int OBJPROP_INC = 20;
|
||||
static ui8 obeliskCount; //how many obelisks are on map
|
||||
static std::map<TeamID, ui8> visited; //map: team_id => how many obelisks has been visited
|
||||
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void initObj() override;
|
||||
std::string getHoverText(PlayerColor player) const override;
|
||||
static void reset();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -20,6 +20,8 @@ void CMapGenerator::foreach_neighbour(const int3 &pos, std::function<void(int3&
|
||||
for(const int3 &dir : int3::getDirs())
|
||||
{
|
||||
int3 n = pos + dir;
|
||||
/*important notice: perform any translation before this function is called,
|
||||
so the actual map position is checked*/
|
||||
if(map->isInTheMap(n))
|
||||
foo(n);
|
||||
}
|
||||
|
@ -1177,17 +1177,16 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
|
||||
|
||||
int3 closestTile = int3(-1,-1,-1);
|
||||
float minDistance = 1e10;
|
||||
|
||||
for (auto visitablePos : info.visitableFromBottomPositions) //objects that are not visitable from top must be accessible from bottom or side
|
||||
{
|
||||
int3 closestFreeTile = findClosestTile(freePaths, visitablePos);
|
||||
if (closestFreeTile.dist2d(visitablePos) < minDistance)
|
||||
{
|
||||
closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map (?)
|
||||
closestTile = visitablePos + int3 (0, 1, 0); //start below object (y+1), possibly even outside the map, to not make path up through it
|
||||
minDistance = closestFreeTile.dist2d(visitablePos);
|
||||
}
|
||||
}
|
||||
if (!closestTile.valid())
|
||||
{
|
||||
for (auto visitablePos : info.visitableFromTopPositions) //all objects are accessible from any direction
|
||||
{
|
||||
int3 closestFreeTile = findClosestTile(freePaths, visitablePos);
|
||||
@ -1197,7 +1196,6 @@ bool CRmgTemplateZone::createTreasurePile(CMapGenerator* gen, int3 &pos, float m
|
||||
minDistance = closestFreeTile.dist2d(visitablePos);
|
||||
}
|
||||
}
|
||||
}
|
||||
assert (closestTile.valid());
|
||||
|
||||
for (auto tile : info.occupiedPositions)
|
||||
|
@ -124,6 +124,11 @@ static inline double distance(int3 a, int3 b)
|
||||
}
|
||||
static void giveExp(BattleResult &r)
|
||||
{
|
||||
if(r.winner > 1)
|
||||
{
|
||||
// draw
|
||||
return;
|
||||
}
|
||||
r.exp[0] = 0;
|
||||
r.exp[1] = 0;
|
||||
for(auto i = r.casualties[!r.winner].begin(); i!=r.casualties[!r.winner].end(); i++)
|
||||
@ -471,7 +476,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
|
||||
const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject;
|
||||
const BattleResult::EResult result = battleResult.get()->result;
|
||||
|
||||
auto findBattleQuery = [this] () -> std::shared_ptr<CBattleQuery>
|
||||
auto findBattleQuery = [this]() -> std::shared_ptr<CBattleQuery>
|
||||
{
|
||||
for(auto &q : queries.allQueries())
|
||||
{
|
||||
@ -526,66 +531,70 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
|
||||
}
|
||||
|
||||
|
||||
std::vector<ui32> arts; //display them in window
|
||||
std::vector<const CArtifactInstance *> arts; //display them in window
|
||||
|
||||
if (result == BattleResult::NORMAL && finishingBattle->winnerHero)
|
||||
if(result == BattleResult::NORMAL && finishingBattle->winnerHero)
|
||||
{
|
||||
if (finishingBattle->loserHero)
|
||||
auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma)
|
||||
{
|
||||
auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; //TODO: wrap it into a function, somehow (boost::variant -_-)
|
||||
arts.push_back(art);
|
||||
ma->dst = ArtifactLocation(finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
|
||||
sendAndApply(ma);
|
||||
};
|
||||
if(finishingBattle->loserHero)
|
||||
{
|
||||
//TODO: wrap it into a function, somehow (boost::variant -_-)
|
||||
auto artifactsWorn = finishingBattle->loserHero->artifactsWorn;
|
||||
for (auto artSlot : artifactsWorn)
|
||||
{
|
||||
MoveArtifact ma;
|
||||
ma.src = ArtifactLocation (finishingBattle->loserHero, artSlot.first);
|
||||
ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first);
|
||||
const CArtifactInstance * art = ma.src.getArt();
|
||||
if (art && !art->artType->isBig() && art->artType->id != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook)
|
||||
if(art && !art->artType->isBig() &&
|
||||
art->artType->id != ArtifactID::SPELLBOOK)
|
||||
// don't move war machines or locked arts (spellbook)
|
||||
{
|
||||
arts.push_back (art->artType->id);
|
||||
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
|
||||
sendAndApply(&ma);
|
||||
sendMoveArtifact(art, &ma);
|
||||
}
|
||||
}
|
||||
while (!finishingBattle->loserHero->artifactsInBackpack.empty())
|
||||
while(!finishingBattle->loserHero->artifactsInBackpack.empty())
|
||||
{
|
||||
//we assume that no big artifacts can be found
|
||||
MoveArtifact ma;
|
||||
ma.src = ArtifactLocation (finishingBattle->loserHero,
|
||||
ma.src = ArtifactLocation(finishingBattle->loserHero,
|
||||
ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
|
||||
const CArtifactInstance * art = ma.src.getArt();
|
||||
arts.push_back (art->artType->id);
|
||||
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
|
||||
sendAndApply(&ma);
|
||||
if(art->artType->id != ArtifactID::GRAIL) //grail may not be won
|
||||
{
|
||||
sendMoveArtifact(art, &ma);
|
||||
}
|
||||
if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero?
|
||||
}
|
||||
if(finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero?
|
||||
{
|
||||
artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn;
|
||||
for (auto artSlot : artifactsWorn)
|
||||
for(auto artSlot : artifactsWorn)
|
||||
{
|
||||
MoveArtifact ma;
|
||||
ma.src = ArtifactLocation (finishingBattle->loserHero->commander.get(), artSlot.first);
|
||||
ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first);
|
||||
const CArtifactInstance * art = ma.src.getArt();
|
||||
if (art && !art->artType->isBig())
|
||||
{
|
||||
arts.push_back (art->artType->id);
|
||||
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
|
||||
sendAndApply(&ma);
|
||||
sendMoveArtifact(art, &ma);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
|
||||
for(auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks)
|
||||
{
|
||||
auto artifactsWorn = armySlot.second->artifactsWorn;
|
||||
for (auto artSlot : artifactsWorn)
|
||||
{
|
||||
MoveArtifact ma;
|
||||
ma.src = ArtifactLocation (armySlot.second, artSlot.first);
|
||||
ma.src = ArtifactLocation(armySlot.second, artSlot.first);
|
||||
const CArtifactInstance * art = ma.src.getArt();
|
||||
if (art && !art->artType->isBig())
|
||||
{
|
||||
arts.push_back (art->artType->id);
|
||||
ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero));
|
||||
sendAndApply(&ma);
|
||||
sendMoveArtifact(art, &ma);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -593,23 +602,25 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
|
||||
|
||||
sendAndApply(battleResult.data); //after this point casualties objects are destroyed
|
||||
|
||||
if (arts.size()) //display loot
|
||||
if(arts.size()) //display loot
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = finishingBattle->winnerHero->tempOwner;
|
||||
|
||||
iw.text.addTxt (MetaString::GENERAL_TXT, 30); //You have captured enemy artifact
|
||||
|
||||
for (auto id : arts) //TODO; separate function to display loot for various ojects?
|
||||
for(auto art : arts) //TODO; separate function to display loot for various ojects?
|
||||
{
|
||||
iw.components.push_back (Component (Component::ARTIFACT, id, 0, 0));
|
||||
iw.components.push_back(Component(
|
||||
Component::ARTIFACT, art->artType->id,
|
||||
art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0));
|
||||
if(iw.components.size() >= 14)
|
||||
{
|
||||
sendAndApply(&iw);
|
||||
iw.components.clear();
|
||||
}
|
||||
}
|
||||
if (iw.components.size())
|
||||
if(iw.components.size())
|
||||
{
|
||||
sendAndApply(&iw);
|
||||
}
|
||||
@ -712,7 +723,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
|
||||
|
||||
setBattle(nullptr);
|
||||
|
||||
if(visitObjectAfterVictory && result.winner==0)
|
||||
if(visitObjectAfterVictory && result.winner==0 && !finishingBattle->winnerHero->stacks.empty())
|
||||
{
|
||||
logGlobal->traceStream() << "post-victory visit";
|
||||
visitObjectOnTile(*getTile(finishingBattle->winnerHero->getPosition()), finishingBattle->winnerHero);
|
||||
@ -734,13 +745,34 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
|
||||
sah.army[0].setCreature(SlotID(0), finishingBattle->loserHero->type->initialArmy.at(0).creature, 1);
|
||||
}
|
||||
|
||||
if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(1))
|
||||
if(const CGHeroInstance *another = getPlayer(finishingBattle->loser)->availableHeroes.at(0))
|
||||
sah.hid[1] = another->subID;
|
||||
else
|
||||
sah.hid[1] = -1;
|
||||
|
||||
sendAndApply(&sah);
|
||||
}
|
||||
if(result.winner != 2 && finishingBattle->winnerHero && finishingBattle->winnerHero->stacks.empty())
|
||||
{
|
||||
RemoveObject ro(finishingBattle->winnerHero->id);
|
||||
sendAndApply(&ro);
|
||||
|
||||
if (VLC->modh->settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS)
|
||||
{
|
||||
SetAvailableHeroes sah;
|
||||
sah.player = finishingBattle->victor;
|
||||
sah.hid[0] = finishingBattle->winnerHero->subID;
|
||||
sah.army[0].clear();
|
||||
sah.army[0].setCreature(SlotID(0), finishingBattle->winnerHero->type->initialArmy.at(0).creature, 1);
|
||||
|
||||
if(const CGHeroInstance *another = getPlayer(finishingBattle->victor)->availableHeroes.at(0))
|
||||
sah.hid[1] = another->subID;
|
||||
else
|
||||
sah.hid[1] = -1;
|
||||
|
||||
sendAndApply(&sah);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CStack *def, int distance, int targetHex)
|
||||
@ -926,7 +958,10 @@ void CGameHandler::handleConnection(std::set<PlayerColor> players, CConnection &
|
||||
{
|
||||
const bool result = apply->applyOnGH(this,&c,pack, player);
|
||||
if(!result)
|
||||
complain("Got false in applying... that request must have been fishy!");
|
||||
{
|
||||
complain((boost::format("Got false in applying %s... that request must have been fishy!")
|
||||
% typeid(*pack).name()).str());
|
||||
}
|
||||
logGlobal->traceStream() << "Message successfully applied (result=" << result << ")!";
|
||||
sendPackageResponse(true);
|
||||
}
|
||||
@ -2113,7 +2148,6 @@ void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroI
|
||||
|
||||
void CGameHandler::removeArtifact(const ArtifactLocation &al)
|
||||
{
|
||||
assert(al.getArt());
|
||||
EraseArtifact ea;
|
||||
ea.al = al;
|
||||
sendAndApply(&ea);
|
||||
@ -3038,7 +3072,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
|
||||
sendAndApply(&da);
|
||||
}
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CGameHandler::buyArtifact( ObjectInstanceID hid, ArtifactID aid )
|
||||
@ -3321,21 +3355,25 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
|
||||
if((p->resources.at(Res::GOLD) < GameConstants::HERO_GOLD_COST && complain("Not enough gold for buying hero!"))
|
||||
|| ((!t) && (getHeroCount(player, false) >= VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER && complain("Cannot hire hero, too many wandering heroes already!")))
|
||||
|| ((t) && (getHeroCount(player, true) >= VLC->modh->settings.MAX_HEROES_AVAILABLE_PER_PLAYER && complain("Cannot hire hero, too many heroes garrizoned and wandering already!"))) )
|
||||
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(t) //tavern in town
|
||||
{
|
||||
if( (!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
|
||||
if((!t->hasBuilt(BuildingID::TAVERN) && complain("No tavern!"))
|
||||
|| (t->visitingHero && complain("There is visiting hero - no place!")))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if(obj->ID == Obj::TAVERN)
|
||||
{
|
||||
if(getTile(obj->visitablePos())->visitableObjects.back() != obj && complain("Tavern entry must be unoccupied!"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const CGHeroInstance *nh = p->availableHeroes.at(hid);
|
||||
if (!nh)
|
||||
@ -3351,13 +3389,14 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
|
||||
hr.tile = obj->visitablePos() + nh->getVisitableOffset();
|
||||
sendAndApply(&hr);
|
||||
|
||||
|
||||
std::map<ui32, ConstTransitivePtr<CGHeroInstance> > pool = gs->unusedHeroesFromPool();
|
||||
|
||||
const CGHeroInstance *theOtherHero = p->availableHeroes.at(!hid);
|
||||
const CGHeroInstance *newHero = nullptr;
|
||||
if (theOtherHero) //on XXL maps all heroes can be imprisoned :(
|
||||
{
|
||||
newHero = gs->hpool.pickHeroFor(false, player, getNativeTown(player), pool, gs->getRandomGenerator(), theOtherHero->type->heroClass);
|
||||
}
|
||||
|
||||
SetAvailableHeroes sah;
|
||||
sah.player = player;
|
||||
@ -3369,7 +3408,9 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
|
||||
sah.army[hid].setCreature(SlotID(0), newHero->type->initialArmy[0].creature, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
sah.hid[hid] = -1;
|
||||
}
|
||||
|
||||
sah.hid[!hid] = theOtherHero ? theOtherHero->subID : -1;
|
||||
sendAndApply(&sah);
|
||||
@ -4026,6 +4067,16 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
|
||||
if(!hero->hasStackAtSlot(SlotID(i)))
|
||||
insertNewStack(StackLocation(hero, SlotID(i)), blackKnight, 10);
|
||||
}
|
||||
else if(message == "vcmiglaurung") //gives 5000 crystal dragons into each slot
|
||||
{
|
||||
CGHeroInstance *hero = gs->getHero(currObj);
|
||||
const CCreature *crystalDragon = VLC->creh->creatures.at(133);
|
||||
if(!hero) return;
|
||||
|
||||
for(int i = 0; i < GameConstants::ARMY_SIZE; i++)
|
||||
if(!hero->hasStackAtSlot(SlotID(i)))
|
||||
insertNewStack(StackLocation(hero, SlotID(i)), crystalDragon, 5000);
|
||||
}
|
||||
else if(message == "vcminoldor") //all war machines
|
||||
{
|
||||
CGHeroInstance *hero = gs->getHero(currObj);
|
||||
@ -5651,16 +5702,18 @@ void CGameHandler::giveHeroNewArtifact(const CGHeroInstance *h, const CArtifact
|
||||
|
||||
void CGameHandler::setBattleResult(BattleResult::EResult resultType, int victoriusSide)
|
||||
{
|
||||
if(battleResult.get())
|
||||
boost::unique_lock<boost::mutex> guard(battleResult.mx);
|
||||
if(battleResult.data)
|
||||
{
|
||||
complain("There is already set result?");
|
||||
complain((boost::format("The battle result has been already set (to %d, asked to %d)")
|
||||
% battleResult.data->result % resultType).str());
|
||||
return;
|
||||
}
|
||||
auto br = new BattleResult;
|
||||
br->result = resultType;
|
||||
br->winner = victoriusSide; //surrendering side loses
|
||||
gs->curB->calculateCasualties(br->casualties);
|
||||
battleResult.set(br);
|
||||
battleResult.data = br;
|
||||
}
|
||||
|
||||
void CGameHandler::commitPackage( CPackForClient *pack )
|
||||
|
@ -321,7 +321,8 @@ CBlockingDialogQuery::CBlockingDialogQuery(const BlockingDialog &bd)
|
||||
|
||||
void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const
|
||||
{
|
||||
auto obj = dynamic_ptr_cast<const CGTeleport>(objectVisit.visitedObject);
|
||||
// do not change to dynamic_ptr_cast - SIGSEGV!
|
||||
auto obj = dynamic_cast<const CGTeleport*>(objectVisit.visitedObject);
|
||||
obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits);
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,6 @@
|
||||
<Unit filename="CVCMIServer.h" />
|
||||
<Unit filename="NetPacksServer.cpp" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option compile="1" />
|
||||
<Option weight="0" />
|
||||
</Unit>
|
||||
<Extensions>
|
||||
|
@ -38,6 +38,5 @@
|
||||
<Project filename="scripting/erm/ERM.cbp">
|
||||
<Depends filename="lib/VCMI_lib.cbp" />
|
||||
</Project>
|
||||
<Project filename="launcher/VCMI_launcher.cbp" />
|
||||
</Workspace>
|
||||
</CodeBlocks_workspace_file>
|
||||
|
Loading…
Reference in New Issue
Block a user