mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge remote-tracking branch 'upstream/develop' into cpp-map-editor
This commit is contained in:
commit
d5403d3eb9
@ -17,6 +17,7 @@
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/spells/ISpellMechanics.h"
|
||||
#include "../../lib/battle/BattleStateInfoForRetreat.h"
|
||||
#include "../../lib/CStack.h" // TODO: remove
|
||||
// Eventually only IBattleInfoCallback and battle::Unit should be used,
|
||||
// CUnitState should be private and CStack should be removed completely
|
||||
@ -728,13 +729,34 @@ void CBattleAI::print(const std::string &text) const
|
||||
|
||||
boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
|
||||
{
|
||||
if(cb->battleCanSurrender(playerID))
|
||||
BattleStateInfoForRetreat bs;
|
||||
|
||||
bs.canFlee = cb->battleCanFlee();
|
||||
bs.canSurrender = cb->battleCanSurrender(playerID);
|
||||
bs.ourSide = cb->battleGetMySide();
|
||||
bs.ourHero = cb->battleGetMyHero();
|
||||
bs.enemyHero = nullptr;
|
||||
|
||||
for(auto stack : cb->battleGetAllStacks(false))
|
||||
{
|
||||
if(stack->alive())
|
||||
{
|
||||
if(stack->side == bs.ourSide)
|
||||
bs.ourStacks.push_back(stack);
|
||||
else
|
||||
{
|
||||
bs.enemyStacks.push_back(stack);
|
||||
bs.enemyHero = cb->battleGetOwnerHero(stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(cb->battleCanFlee())
|
||||
|
||||
if(!bs.canFlee || !bs.canSurrender)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
return boost::none;
|
||||
|
||||
return cb->makeSurrenderRetreatDecision(bs);
|
||||
}
|
||||
|
||||
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include "../../lib/serializer/CTypeList.h"
|
||||
#include "../../lib/serializer/BinarySerializer.h"
|
||||
#include "../../lib/serializer/BinaryDeserializer.h"
|
||||
#include "../../lib/battle/BattleStateInfoForRetreat.h"
|
||||
|
||||
#include "AIGateway.h"
|
||||
#include "Goals/Goals.h"
|
||||
@ -488,6 +489,24 @@ void AIGateway::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositio
|
||||
NET_EVENT_HANDLER;
|
||||
}
|
||||
|
||||
boost::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(
|
||||
const BattleStateInfoForRetreat & battleState)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
|
||||
double fightRatio = battleState.getOurStrength() / (double)battleState.getEnemyStrength();
|
||||
|
||||
// if we have no towns - things are already bad, so retreat is not an option.
|
||||
if(cb->getTownsInfo().size() && fightRatio < 0.3 && battleState.canFlee)
|
||||
{
|
||||
return BattleAction::makeRetreat(battleState.ourSide);
|
||||
}
|
||||
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
|
||||
void AIGateway::init(std::shared_ptr<Environment> env, std::shared_ptr<CCallback> CB)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
@ -1017,8 +1036,10 @@ bool AIGateway::canRecruitAnyHero(const CGTownInstance * t) const
|
||||
//TODO: make gathering gold, building tavern or conquering town (?) possible subgoals
|
||||
if(!t)
|
||||
t = findTownWithTavern();
|
||||
if(!t)
|
||||
|
||||
if(!t || !townHasFreeTavern(t))
|
||||
return false;
|
||||
|
||||
if(cb->getResourceAmount(Res::GOLD) < GameConstants::HERO_GOLD_COST) //TODO: use ResourceManager
|
||||
return false;
|
||||
if(cb->getHeroesInfo().size() >= ALLOWED_ROAMING_HEROES)
|
||||
@ -1383,7 +1404,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
|
||||
const CGTownInstance * AIGateway::findTownWithTavern() const
|
||||
{
|
||||
for(const CGTownInstance * t : cb->getTownsInfo())
|
||||
if(t->hasBuilt(BuildingID::TAVERN) && !t->visitingHero)
|
||||
if(townHasFreeTavern(t))
|
||||
return t;
|
||||
|
||||
return nullptr;
|
||||
@ -1414,34 +1435,6 @@ void AIGateway::buildArmyIn(const CGTownInstance * t)
|
||||
moveCreaturesToHero(t);
|
||||
}
|
||||
|
||||
void AIGateway::recruitHero(const CGTownInstance * t, bool throwing)
|
||||
{
|
||||
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
|
||||
|
||||
auto heroes = cb->getAvailableHeroes(t);
|
||||
if(heroes.size())
|
||||
{
|
||||
auto hero = heroes[0];
|
||||
if(heroes.size() >= 2) //makes sense to recruit two heroes with starting amries in first week
|
||||
{
|
||||
if(heroes[1]->getTotalStrength() > hero->getTotalStrength())
|
||||
hero = heroes[1];
|
||||
}
|
||||
|
||||
cb->recruitHero(t, hero);
|
||||
nullkiller->heroManager->update();
|
||||
|
||||
if(t->visitingHero)
|
||||
moveHeroToTile(t->visitablePos(), t->visitingHero.get());
|
||||
|
||||
throw goalFulfilledException(sptr(Goals::RecruitHero(t)));
|
||||
}
|
||||
else if(throwing)
|
||||
{
|
||||
throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
|
||||
}
|
||||
}
|
||||
|
||||
void AIGateway::finish()
|
||||
{
|
||||
//we want to lock to avoid multiple threads from calling makingTurn->join() at same time
|
||||
|
@ -166,6 +166,7 @@ public:
|
||||
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
|
||||
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
|
||||
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
|
||||
boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
|
||||
|
||||
void battleStart(const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side) override;
|
||||
void battleEnd(const BattleResult * br) override;
|
||||
@ -176,7 +177,6 @@ public:
|
||||
void endTurn();
|
||||
|
||||
// TODO: all the routines like recruiting hero or building army should be removed from here and extracted to elementar goals or whatever
|
||||
void recruitHero(const CGTownInstance * t, bool throwing = false);
|
||||
void recruitCreatures(const CGDwelling * d, const CArmedInstance * recruiter);
|
||||
void pickBestCreatures(const CArmedInstance * army, const CArmedInstance * source); //called when we can't find a slot for new stack
|
||||
void pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * other = nullptr);
|
||||
|
@ -450,4 +450,14 @@ bool shouldVisit(const Nullkiller * ai, const CGHeroInstance * h, const CGObject
|
||||
return true;
|
||||
}
|
||||
|
||||
bool townHasFreeTavern(const CGTownInstance * town)
|
||||
{
|
||||
if(!town->hasBuilt(BuildingID::TAVERN)) return false;
|
||||
if(!town->visitingHero) return true;
|
||||
|
||||
bool canMoveVisitingHeroToGarnison = !town->getUpperArmy()->stacksCount();
|
||||
|
||||
return canMoveVisitingHeroToGarnison;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -238,6 +238,7 @@ bool isSafeToVisit(HeroPtr h, const CCreatureSet *, uint64_t dangerStrength);
|
||||
bool compareHeroStrength(HeroPtr h1, HeroPtr h2);
|
||||
bool compareArmyStrength(const CArmedInstance * a1, const CArmedInstance * a2);
|
||||
bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2);
|
||||
bool townHasFreeTavern(const CGTownInstance * town);
|
||||
|
||||
uint64_t timeElapsed(std::chrono::time_point<std::chrono::high_resolution_clock> start);
|
||||
|
||||
|
@ -34,10 +34,40 @@ Goals::TGoalVec RecruitHeroBehavior::decompose() const
|
||||
Goals::TGoalVec tasks;
|
||||
auto towns = cb->getTownsInfo();
|
||||
|
||||
auto ourHeroes = ai->nullkiller->heroManager->getHeroRoles();
|
||||
auto minScoreToHireMain = std::numeric_limits<float>::max();
|
||||
|
||||
for(auto hero : ourHeroes)
|
||||
{
|
||||
if(hero.second != HeroRole::MAIN)
|
||||
continue;
|
||||
|
||||
auto newScore = ai->nullkiller->heroManager->evaluateHero(hero.first.get());
|
||||
|
||||
if(minScoreToHireMain > newScore)
|
||||
{
|
||||
// weakest main hero score
|
||||
minScoreToHireMain = newScore;
|
||||
}
|
||||
}
|
||||
|
||||
for(auto town : towns)
|
||||
{
|
||||
if(!town->garrisonHero && !town->visitingHero && ai->canRecruitAnyHero(town))
|
||||
if(ai->canRecruitAnyHero(town))
|
||||
{
|
||||
auto availableHeroes = cb->getAvailableHeroes(town);
|
||||
|
||||
for(auto hero : availableHeroes)
|
||||
{
|
||||
auto score = ai->nullkiller->heroManager->evaluateHero(hero);
|
||||
|
||||
if(score > minScoreToHireMain)
|
||||
{
|
||||
tasks.push_back(Goals::sptr(Goals::RecruitHero(town, hero).setpriority(200)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(cb->getHeroesInfo().size() < cb->getTownsInfo().size() + 1
|
||||
|| (ai->nullkiller->getFreeResources()[Res::GOLD] > 10000
|
||||
&& ai->nullkiller->buildAnalyzer->getGoldPreasure() < MAX_GOLD_PEASURE))
|
||||
|
@ -35,16 +35,47 @@ void RecruitHero::accept(AIGateway * ai)
|
||||
|
||||
if(!t) t = ai->findTownWithTavern();
|
||||
|
||||
if(t)
|
||||
{
|
||||
ai->recruitHero(t, true);
|
||||
//TODO try to free way to blocked town
|
||||
//TODO: adventure map tavern or prison?
|
||||
}
|
||||
else
|
||||
if(!t)
|
||||
{
|
||||
throw cannotFulfillGoalException("No town to recruit hero!");
|
||||
}
|
||||
|
||||
logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
|
||||
|
||||
auto heroes = cb->getAvailableHeroes(t);
|
||||
|
||||
if(!heroes.size())
|
||||
{
|
||||
throw cannotFulfillGoalException("No available heroes in tavern in " + t->nodeName());
|
||||
}
|
||||
|
||||
auto heroToHire = heroes[0];
|
||||
|
||||
for(auto hero : heroes)
|
||||
{
|
||||
if(objid == hero->id.getNum())
|
||||
{
|
||||
heroToHire = hero;
|
||||
break;
|
||||
}
|
||||
|
||||
if(hero->getTotalStrength() > heroToHire->getTotalStrength())
|
||||
heroToHire = hero;
|
||||
}
|
||||
|
||||
if(t->visitingHero)
|
||||
{
|
||||
cb->swapGarrisonHero(t);
|
||||
}
|
||||
|
||||
if(t->visitingHero)
|
||||
throw cannotFulfillGoalException("Town " + t->nodeName() + " is occupied. Cannot recruit hero!");
|
||||
|
||||
cb->recruitHero(t, heroToHire);
|
||||
ai->nullkiller->heroManager->update();
|
||||
|
||||
if(t->visitingHero)
|
||||
ai->moveHeroToTile(t->visitablePos(), t->visitingHero.get());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -387,3 +387,9 @@ bool CBattleCallback::battleMakeTacticAction( BattleAction * action )
|
||||
sendRequest(&ma);
|
||||
return true;
|
||||
}
|
||||
|
||||
boost::optional<BattleAction> CBattleCallback::makeSurrenderRetreatDecision(
|
||||
const BattleStateInfoForRetreat & battleState)
|
||||
{
|
||||
return cl->playerint[getPlayerID().get()]->makeSurrenderRetreatDecision(battleState);
|
||||
}
|
@ -32,6 +32,7 @@ struct CPackForServer;
|
||||
class IBattleEventsReceiver;
|
||||
class IGameEventsReceiver;
|
||||
struct ArtifactLocation;
|
||||
class BattleStateInfoForRetreat;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
@ -48,6 +49,7 @@ public:
|
||||
//battle
|
||||
virtual int battleMakeAction(const BattleAction * action) = 0;//for casting spells by hero - DO NOT use it for moving active stack
|
||||
virtual bool battleMakeTacticAction(BattleAction * action) = 0; // performs tactic phase actions
|
||||
virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) = 0;
|
||||
};
|
||||
|
||||
class IGameActionCallback
|
||||
@ -104,6 +106,7 @@ public:
|
||||
CBattleCallback(boost::optional<PlayerColor> Player, CClient *C);
|
||||
int battleMakeAction(const BattleAction * action) override;//for casting spells by hero - DO NOT use it for moving active stack
|
||||
bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
|
||||
boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState) override;
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
scripting::Pool * getContextPool() const override;
|
||||
|
@ -12,6 +12,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/battle/BattleHex.cpp
|
||||
${MAIN_LIB_DIR}/battle/BattleInfo.cpp
|
||||
${MAIN_LIB_DIR}/battle/BattleProxy.cpp
|
||||
${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.cpp
|
||||
${MAIN_LIB_DIR}/battle/CBattleInfoCallback.cpp
|
||||
${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.cpp
|
||||
${MAIN_LIB_DIR}/battle/CCallbackBase.cpp
|
||||
@ -241,6 +242,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/battle/BattleAttackInfo.h
|
||||
${MAIN_LIB_DIR}/battle/BattleHex.h
|
||||
${MAIN_LIB_DIR}/battle/BattleInfo.h
|
||||
${MAIN_LIB_DIR}/battle/BattleStateInfoForRetreat.h
|
||||
${MAIN_LIB_DIR}/battle/BattleProxy.h
|
||||
${MAIN_LIB_DIR}/battle/CBattleInfoCallback.h
|
||||
${MAIN_LIB_DIR}/battle/CBattleInfoEssentials.h
|
||||
|
@ -58,7 +58,9 @@ class CLoadFile;
|
||||
class CSaveFile;
|
||||
class BinaryDeserializer;
|
||||
class BinarySerializer;
|
||||
class BattleStateInfo;
|
||||
struct ArtifactLocation;
|
||||
class BattleStateInfoForRetreat;
|
||||
|
||||
#if SCRIPTING_ENABLED
|
||||
namespace scripting
|
||||
@ -93,7 +95,7 @@ public:
|
||||
|
||||
//pskill is gained primary skill, interface has to choose one of given skills and call callback with selection id
|
||||
virtual void heroGotLevel(const CGHeroInstance *hero, PrimarySkill::PrimarySkill pskill, std::vector<SecondarySkill> &skills, QueryID queryID)=0;
|
||||
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
|
||||
virtual void commanderGotLevel (const CCommanderInstance * commander, std::vector<ui32> skills, QueryID queryID)=0;
|
||||
|
||||
// Show a dialog, player must take decision. If selection then he has to choose between one of given components,
|
||||
// if cancel he is allowed to not choose. After making choice, CCallback::selectionMade should be called
|
||||
@ -108,6 +110,11 @@ public:
|
||||
|
||||
virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
|
||||
|
||||
virtual boost::optional<BattleAction> makeSurrenderRetreatDecision(const BattleStateInfoForRetreat & battleState)
|
||||
{
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
virtual void saveGame(BinarySerializer & h, const int version) = 0;
|
||||
virtual void loadGame(BinaryDeserializer & h, const int version) = 0;
|
||||
};
|
||||
|
@ -105,6 +105,22 @@ BattleAction BattleAction::makeEndOFTacticPhase(ui8 side)
|
||||
return ba;
|
||||
}
|
||||
|
||||
BattleAction BattleAction::makeSurrender(ui8 side)
|
||||
{
|
||||
BattleAction ba;
|
||||
ba.side = side;
|
||||
ba.actionType = EActionType::SURRENDER;
|
||||
return ba;
|
||||
}
|
||||
|
||||
BattleAction BattleAction::makeRetreat(ui8 side)
|
||||
{
|
||||
BattleAction ba;
|
||||
ba.side = side;
|
||||
ba.actionType = EActionType::RETREAT;
|
||||
return ba;
|
||||
}
|
||||
|
||||
std::string BattleAction::toString() const
|
||||
{
|
||||
std::stringstream actionTypeStream;
|
||||
|
@ -40,6 +40,8 @@ public:
|
||||
static BattleAction makeCreatureSpellcast(const battle::Unit * stack, const battle::Target & target, SpellID spellID);
|
||||
static BattleAction makeMove(const battle::Unit * stack, BattleHex dest);
|
||||
static BattleAction makeEndOFTacticPhase(ui8 side);
|
||||
static BattleAction makeRetreat(ui8 side);
|
||||
static BattleAction makeSurrender(ui8 side);
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
|
52
lib/battle/BattleStateInfoForRetreat.cpp
Normal file
52
lib/battle/BattleStateInfoForRetreat.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* BattleStateInfoForRetreat.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "BattleStateInfoForRetreat.h"
|
||||
#include "Unit.h"
|
||||
#include "CBattleInfoCallback.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
BattleStateInfoForRetreat::BattleStateInfoForRetreat()
|
||||
: canFlee(false), canSurrender(false), isLastTurnBeforeDie(false), ourStacks(), enemyStacks(), ourHero(nullptr), enemyHero(nullptr), ourSide(-1)
|
||||
{
|
||||
}
|
||||
|
||||
uint64_t getFightingStrength(std::vector<const battle::Unit *> stacks, const CGHeroInstance * hero = nullptr)
|
||||
{
|
||||
uint64_t result = 0;
|
||||
|
||||
for(const battle::Unit * stack : stacks)
|
||||
{
|
||||
result += stack->creatureId().toCreature()->AIValue * stack->getCount();
|
||||
}
|
||||
|
||||
if(hero)
|
||||
{
|
||||
result = (uint64_t)(result * hero->getFightingStrength());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t BattleStateInfoForRetreat::getOurStrength() const
|
||||
{
|
||||
return getFightingStrength(ourStacks, ourHero);
|
||||
}
|
||||
|
||||
uint64_t BattleStateInfoForRetreat::getEnemyStrength() const
|
||||
{
|
||||
return getFightingStrength(enemyStacks, enemyHero);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
38
lib/battle/BattleStateInfoForRetreat.h
Normal file
38
lib/battle/BattleStateInfoForRetreat.h
Normal file
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* BattleStateInfoForRetreat.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
namespace battle
|
||||
{
|
||||
class Unit;
|
||||
}
|
||||
|
||||
class CGHeroInstance;
|
||||
|
||||
class DLL_LINKAGE BattleStateInfoForRetreat
|
||||
{
|
||||
public:
|
||||
bool canFlee;
|
||||
bool canSurrender;
|
||||
bool isLastTurnBeforeDie;
|
||||
ui8 ourSide;
|
||||
std::vector<const battle::Unit *> ourStacks;
|
||||
std::vector<const battle::Unit *> enemyStacks;
|
||||
const CGHeroInstance * ourHero;
|
||||
const CGHeroInstance * enemyHero;
|
||||
|
||||
BattleStateInfoForRetreat();
|
||||
uint64_t getOurStrength() const;
|
||||
uint64_t getEnemyStrength() const;
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -116,13 +116,15 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
|
||||
size_t totalNumber = static_cast<size_t>(parser.readNumber()); // first line contains number of objects to read and nothing else
|
||||
parser.endLine();
|
||||
|
||||
for (size_t i=0; i<totalNumber; i++)
|
||||
for (size_t i = 0; i < totalNumber; i++)
|
||||
{
|
||||
auto templ = new ObjectTemplate;
|
||||
templ->readTxt(parser);
|
||||
auto tmpl = new ObjectTemplate;
|
||||
|
||||
tmpl->readTxt(parser);
|
||||
parser.endLine();
|
||||
std::pair<si32, si32> key(templ->id.num, templ->subid);
|
||||
legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(templ)));
|
||||
|
||||
std::pair<si32, si32> key(tmpl->id.num, tmpl->subid);
|
||||
legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(tmpl)));
|
||||
}
|
||||
|
||||
std::vector<JsonNode> ret(dataSize);// create storage for 256 objects
|
||||
@ -560,10 +562,6 @@ SObjectSounds AObjectTypeHandler::getSounds() const
|
||||
|
||||
void AObjectTypeHandler::addTemplate(std::shared_ptr<const ObjectTemplate> templ)
|
||||
{
|
||||
//Otherwise the template remains constant
|
||||
auto ptr = const_cast<ObjectTemplate*>(templ.get());
|
||||
ptr->id = Obj(type);
|
||||
ptr->subid = subtype;
|
||||
templates.push_back(templ);
|
||||
}
|
||||
|
||||
|
@ -197,9 +197,6 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
{
|
||||
const TerrainTile &tile = cb->gameState()->map->getTile(visitablePos());
|
||||
|
||||
this->ID = Obj(ID);
|
||||
this->subID = 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);
|
||||
@ -212,11 +209,16 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
appearance = handler->getTemplates(tile.terType->id)[0];
|
||||
else
|
||||
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
|
||||
if (ID == Obj::HERO)
|
||||
|
||||
if(this->ID == Obj::PRISON && ID == Obj::HERO)
|
||||
{
|
||||
//adjust for the prison offset
|
||||
pos = visitablePos();
|
||||
}
|
||||
|
||||
this->ID = Obj(ID);
|
||||
this->subID = subID;
|
||||
|
||||
cb->gameState()->map->addBlockVisTiles(this);
|
||||
}
|
||||
|
||||
|
@ -33,19 +33,11 @@ protected:
|
||||
auto obj = new ObjectType();
|
||||
preInitObject(obj);
|
||||
|
||||
//Set custom template or leave null
|
||||
if (tmpl)
|
||||
{
|
||||
obj->appearance = tmpl;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto templates = getTemplates();
|
||||
if (templates.empty())
|
||||
{
|
||||
throw std::runtime_error("No handler for created object");
|
||||
}
|
||||
obj->appearance = templates.front(); //just any template for now, will be initialized later
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
@ -490,6 +490,7 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard
|
||||
|
||||
auto & instance = object.addInstance(*guard);
|
||||
instance.setPosition(guardPos - object.getPosition());
|
||||
instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -105,20 +105,26 @@ void Object::Instance::setPositionRaw(const int3 & position)
|
||||
dObject.pos = dPosition + dParent.getPosition();
|
||||
}
|
||||
|
||||
void Object::Instance::setTemplate(const TerrainId & terrain)
|
||||
void Object::Instance::setAnyTemplate()
|
||||
{
|
||||
if(dObject.appearance->id == Obj::NO_OBJ)
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates();
|
||||
if(templates.empty())
|
||||
throw rmgException(boost::to_string(boost::format("Did not find any graphics for object (%d,%d)") % dObject.ID % dObject.subID));
|
||||
|
||||
dObject.appearance = templates.front();
|
||||
dAccessibleAreaCache.clear();
|
||||
setPosition(getPosition(false));
|
||||
}
|
||||
|
||||
void Object::Instance::setTemplate(TerrainId terrain)
|
||||
{
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
|
||||
if (templates.empty())
|
||||
{
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain);
|
||||
auto terrainName = VLC->terrainTypeHandler->terrains()[terrain].name;
|
||||
if (templates.empty())
|
||||
{
|
||||
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") %
|
||||
dObject.ID % dObject.subID % terrainName));
|
||||
}
|
||||
|
||||
dObject.appearance = templates.front();
|
||||
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s") % dObject.ID % dObject.subID % terrainName));
|
||||
}
|
||||
dObject.appearance = templates.front();
|
||||
dAccessibleAreaCache.clear();
|
||||
setPosition(getPosition(false));
|
||||
}
|
||||
@ -283,6 +289,21 @@ void Object::Instance::finalize(RmgMap & map)
|
||||
if(!map.isOnMap(getPosition(true)))
|
||||
throw rmgException(boost::to_string(boost::format("Position of object %d at %s is outside the map") % dObject.id % getPosition(true).toString()));
|
||||
|
||||
//If no specific template was defined for this object, select any matching
|
||||
if (!dObject.appearance)
|
||||
{
|
||||
auto terrainType = map.map().getTile(getPosition(true)).terType;
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->id);
|
||||
if (templates.empty())
|
||||
{
|
||||
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") % dObject.ID % dObject.subID % getPosition(true).toString() % terrainType));
|
||||
}
|
||||
else
|
||||
{
|
||||
setTemplate(terrainType->id);
|
||||
}
|
||||
}
|
||||
|
||||
if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos()))
|
||||
throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString()));
|
||||
|
||||
@ -292,17 +313,6 @@ void Object::Instance::finalize(RmgMap & map)
|
||||
throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString()));
|
||||
}
|
||||
|
||||
if (dObject.appearance->id == Obj::NO_OBJ)
|
||||
{
|
||||
auto terrainType = map.map().getTile(getPosition(true)).terType;
|
||||
auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType->id);
|
||||
if (templates.empty())
|
||||
throw rmgException(boost::to_string(boost::format("Did not find graphics for object (%d,%d) at %s (terrain %d)") %
|
||||
dObject.ID % dObject.subID % getPosition(true).toString() % terrainType->name));
|
||||
|
||||
setTemplate(terrainType->id);
|
||||
}
|
||||
|
||||
for(auto & tile : getBlockedArea().getTilesVector())
|
||||
{
|
||||
map.setOccupied(tile, ETileType::ETileType::USED);
|
||||
|
@ -35,7 +35,8 @@ public:
|
||||
int3 getVisitablePosition() const;
|
||||
bool isVisitableFrom(const int3 & tile) const;
|
||||
const Area & getAccessibleArea() const;
|
||||
void setTemplate(const TerrainId & terrain); //cache invalidation
|
||||
void setTemplate(TerrainId terrain); //cache invalidation
|
||||
void setAnyTemplate(); //cache invalidation
|
||||
|
||||
int3 getPosition(bool isAbsolute = false) const;
|
||||
void setPosition(const int3 & position); //cache invalidation
|
||||
|
@ -391,56 +391,17 @@ public:
|
||||
else
|
||||
data.reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void load(std::shared_ptr<const T> &data) //version of the above for const ptr
|
||||
void load(std::shared_ptr<const T> & data)
|
||||
{
|
||||
typedef typename std::remove_const<T>::type NonConstT;
|
||||
NonConstT *internalPtr;
|
||||
load(internalPtr);
|
||||
std::shared_ptr<T> nonConstData;
|
||||
|
||||
void *internalPtrDerived = typeList.castToMostDerived(internalPtr);
|
||||
load(nonConstData);
|
||||
|
||||
if(internalPtr)
|
||||
{
|
||||
auto itr = loadedSharedPointers.find(internalPtrDerived);
|
||||
if(itr != loadedSharedPointers.end())
|
||||
{
|
||||
// This pointer is already loaded. The "data" needs to be pointed to it,
|
||||
// so their shared state is actually shared.
|
||||
try
|
||||
{
|
||||
auto actualType = typeList.getTypeInfo(internalPtr);
|
||||
auto typeWeNeedToReturn = typeList.getTypeInfo<T>();
|
||||
if(*actualType == *typeWeNeedToReturn)
|
||||
{
|
||||
// No casting needed, just unpack already stored shared_ptr and return it
|
||||
data = boost::any_cast<std::shared_ptr<const T>>(itr->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We need to perform series of casts
|
||||
auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn);
|
||||
data = boost::any_cast<std::shared_ptr<const T>>(ret);
|
||||
}
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
logGlobal->error(e.what());
|
||||
logGlobal->error("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME", itr->second.type().name(), typeid(std::shared_ptr<T>).name());
|
||||
//TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa)
|
||||
throw;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto hlp = std::shared_ptr<const T>(internalPtr);
|
||||
data = hlp; //possibly adds const
|
||||
loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
|
||||
}
|
||||
}
|
||||
else
|
||||
data.reset();
|
||||
data = nonConstData;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void load(std::unique_ptr<T> &data)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user