1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Merge pull request #2209 from IvanSavenko/map_object_configuration

Support for json configuration of some map objects
This commit is contained in:
Ivan Savenko 2023-06-16 19:32:05 +03:00 committed by GitHub
commit 2e2938303a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
66 changed files with 1274 additions and 925 deletions

View File

@ -15,7 +15,7 @@
#include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
#include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
#include "../../../lib/mapObjectConstructors/CommonConstructors.h"
#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
namespace NKAI
{

View File

@ -13,7 +13,7 @@
#include "Nullkiller.h"
#include "../../../lib/mapObjectConstructors/AObjectTypeHandler.h"
#include "../../../lib/mapObjectConstructors/CObjectClassesHandler.h"
#include "../../../lib/mapObjectConstructors/CommonConstructors.h"
#include "../../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
#include "../../../lib/mapObjects/MapObjects.h"
#include "../../../lib/CCreatureHandler.h"
#include "../../../lib/CPathfinder.h"

View File

@ -23,7 +23,7 @@ using namespace Goals;
bool BuildBoat::operator==(const BuildBoat & other) const
{
return shipyard->o->id == other.shipyard->o->id;
return shipyard == other.shipyard;
}
//
//TSubgoal BuildBoat::decomposeSingle() const
@ -54,7 +54,7 @@ void BuildBoat::accept(AIGateway * ai)
throw cannotFulfillGoalException("Can not afford boat");
}
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
{
throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
}
@ -65,9 +65,8 @@ void BuildBoat::accept(AIGateway * ai)
}
logAi->trace(
"Building boat at shipyard %s located at %s, estimated boat position %s",
shipyard->o->getObjectName(),
shipyard->o->visitablePos().toString(),
"Building boat at shipyard located at %s, estimated boat position %s",
shipyard->getObject()->visitablePos().toString(),
shipyard->bestLocation().toString());
cb->buildBoat(shipyard);

View File

@ -32,9 +32,9 @@ namespace AIPathfinding
Goals::TSubgoal BuildBoatAction::decompose(const CGHeroInstance * hero) const
{
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
{
return Goals::sptr(Goals::CaptureObject(shipyard->o));
return Goals::sptr(Goals::CaptureObject(targetObject()));
}
return Goals::sptr(Goals::Invalid());
@ -44,7 +44,7 @@ namespace AIPathfinding
{
auto hero = source->actor->hero;
if(cb->getPlayerRelations(hero->tempOwner, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
if(cb->getPlayerRelations(hero->tempOwner, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
{
#if NKAI_TRACE_LEVEL > 1
logAi->trace("Can not build a boat. Shipyard is enemy.");
@ -70,7 +70,7 @@ namespace AIPathfinding
const CGObjectInstance * BuildBoatAction::targetObject() const
{
return shipyard->o;
return dynamic_cast<const CGObjectInstance*>(shipyard);
}
const ChainActor * BuildBoatAction::getActor(const ChainActor * sourceActor) const
@ -101,7 +101,7 @@ namespace AIPathfinding
std::string BuildBoatAction::toString() const
{
return "Build Boat at " + shipyard->o->getObjectName();
return "Build Boat at " + shipyard->getObject()->visitablePos().toString();
}
bool SummonBoatAction::canAct(const AIPathNode * source) const

View File

@ -15,7 +15,7 @@
#include "../../lib/mapObjectConstructors/AObjectTypeHandler.h"
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
#include "../../lib/mapObjectConstructors/CommonConstructors.h"
#include "../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
#include "../../lib/mapObjects/CBank.h"
#include "../../lib/mapObjects/CGDwelling.h"

View File

@ -22,14 +22,14 @@ using namespace Goals;
bool BuildBoat::operator==(const BuildBoat & other) const
{
return shipyard->o->id == other.shipyard->o->id;
return shipyard == other.shipyard;
}
TSubgoal BuildBoat::whatToDoToAchieve()
{
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
{
return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
return fh->chooseSolution(ai->ah->howToVisitObj(dynamic_cast<const CGObjectInstance*>(shipyard)));
}
if(shipyard->shipyardStatus() != IShipyard::GOOD)
@ -53,7 +53,7 @@ void BuildBoat::accept(VCAI * ai)
throw cannotFulfillGoalException("Can not afford boat");
}
if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
if(cb->getPlayerRelations(ai->playerID, shipyard->getObject()->getOwner()) == PlayerRelations::ENEMIES)
{
throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
}
@ -64,9 +64,8 @@ void BuildBoat::accept(VCAI * ai)
}
logAi->trace(
"Building boat at shipyard %s located at %s, estimated boat position %s",
shipyard->o->getObjectName(),
shipyard->o->visitablePos().toString(),
"Building boat at shipyard located at %s, estimated boat position %s",
shipyard->getObject()->visitablePos().toString(),
shipyard->bestLocation().toString());
cb->buildBoat(shipyard);
@ -81,5 +80,5 @@ std::string BuildBoat::name() const
std::string BuildBoat::completeMessage() const
{
return "Boat have been built at " + shipyard->o->visitablePos().toString();
return "Boat have been built at " + shipyard->getObject()->visitablePos().toString();
}

View File

@ -301,7 +301,7 @@ void CCallback::sendMessage(const std::string &mess, const CGObjectInstance * cu
void CCallback::buildBoat( const IShipyard *obj )
{
BuildBoat bb;
bb.objid = obj->o->id;
bb.objid = dynamic_cast<const CGObjectInstance*>(obj)->id;
sendRequest(&bb);
}

View File

@ -906,7 +906,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
case EOpenWindowMode::SHIPYARD_WINDOW:
{
const IShipyard *sy = IShipyard::castFrom(cl.getObj(ObjectInstanceID(pack.id1)));
callInterfaceIfPresent(cl, sy->o->tempOwner, &IGameEventsReceiver::showShipyardDialog, sy);
callInterfaceIfPresent(cl, sy->getObject()->getOwner(), &IGameEventsReceiver::showShipyardDialog, sy);
}
break;
case EOpenWindowMode::THIEVES_GUILD:

View File

@ -45,6 +45,9 @@
#include "../../CCallback.h"
#include "../lib/mapObjectConstructors/AObjectTypeHandler.h"
#include "../lib/mapObjectConstructors/CObjectClassesHandler.h"
#include "../lib/mapObjectConstructors/CommonConstructors.h"
#include "../lib/mapObjects/CGHeroInstance.h"
#include "../lib/mapObjects/CGMarket.h"
#include "../lib/ArtifactUtils.h"
@ -1093,11 +1096,20 @@ CShipyardWindow::CShipyardWindow(const TResources & cost, int state, BoatId boat
bgWater = std::make_shared<CPicture>("TPSHIPBK", 100, 69);
std::string boatFilenames[3] = {"AB01_", "AB02_", "AB03_"};
auto handler = CGI->objtypeh->getHandlerFor(Obj::BOAT, boatType);
Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2);
bgShip = std::make_shared<CAnimImage>(boatFilenames[boatType.getNum()], 0, 7, 120, 96, 0);
bgShip->center(waterCenter);
auto boatConstructor = std::dynamic_pointer_cast<const BoatInstanceConstructor>(handler);
assert(boatConstructor);
if (boatConstructor)
{
std::string boatFilename = boatConstructor->getBoatAnimationName();
Point waterCenter = Point(bgWater->pos.x+bgWater->pos.w/2, bgWater->pos.y+bgWater->pos.h/2);
bgShip = std::make_shared<CAnimImage>(boatFilename, 0, 7, 120, 96, 0);
bgShip->center(waterCenter);
}
// Create resource icons and costs.
std::string goldValue = std::to_string(cost[EGameResID::GOLD]);

View File

@ -66,9 +66,14 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/logging/CLogger.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/AObjectTypeHandler.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/CBankInstanceConstructor.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/DwellingInstanceConstructor.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/ShipyardInstanceConstructor.cpp
${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.cpp
${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp
${MAIN_LIB_DIR}/mapObjects/CBank.cpp
@ -369,11 +374,17 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/logging/CLogger.h
${MAIN_LIB_DIR}/mapObjectConstructors/AObjectTypeHandler.h
${MAIN_LIB_DIR}/mapObjectConstructors/CBankInstanceConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/CDefaultObjectTypeHandler.h
${MAIN_LIB_DIR}/mapObjectConstructors/CObjectClassesHandler.h
${MAIN_LIB_DIR}/mapObjectConstructors/CommonConstructors.h
${MAIN_LIB_DIR}/mapObjectConstructors/CRewardableConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/DwellingInstanceConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/HillFortInstanceConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/IObjectInfo.h
${MAIN_LIB_DIR}/mapObjectConstructors/RandomMapInfo.h
${MAIN_LIB_DIR}/mapObjectConstructors/ShipyardInstanceConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/ShrineInstanceConstructor.h
${MAIN_LIB_DIR}/mapObjectConstructors/SObjectSounds.h
${MAIN_LIB_DIR}/mapObjects/CArmedInstance.h

View File

@ -148,6 +148,7 @@
"mageGuild" : 4,
"warMachine" : "ballista",
"moatAbility" : "castleMoat",
"boat" : "boatCastle",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -153,6 +153,7 @@
"primaryResource" : "mercury",
"warMachine" : "ballista",
"moatAbility" : "castleMoat",
"boat" : "boatNecropolis",
"buildings" :
{

View File

@ -148,6 +148,7 @@
"mageGuild" : 3,
"warMachine" : "firstAidTent",
"moatAbility" : "fortressMoat",
"boat" : "boatFortress",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -153,6 +153,7 @@
"mageGuild" : 5,
"warMachine" : "firstAidTent",
"moatAbility" : "necropolisMoat",
"boat" : "boatNecropolis",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -310,6 +310,10 @@
"rmg" : {
"value" : 500,
"rarity" : 100
},
"visitText" : 127,
"spell" : {
"level" : 1
}
}
}
@ -330,6 +334,10 @@
"rmg" : {
"value" : 2000,
"rarity" : 100
},
"visitText" : 128,
"spell" : {
"level" : 2
}
}
}
@ -350,6 +358,10 @@
"rmg" : {
"value" : 3000,
"rarity" : 100
},
"visitText" : 129,
"spell" : {
"level" : 3
}
}
}
@ -476,7 +488,8 @@
"index" : 0,
"aiValue" : 1000,
"rmg" : {
}
},
"boat" : "boatCastle"
}
}
},
@ -640,19 +653,6 @@
},
"randomDwelling" : { "index" :216, "handler": "randomDwelling", "types" : { "object" : { "index" : 0} } },
"randomResource" : {
"index" :76,
"handler": "randomResource",
"types" : {
"object" : {
"index" : 0,
"templates" : {
"normal" : { "animation" : "AVTrndm0", "visitableFrom" : [ "+++", "+-+", "+++" ], "mask" : [ "VV", "VA"] }
}
}
}
},
"randomArtifact" : {
"index" :65,
"handler": "randomArtifact",
@ -830,7 +830,7 @@
/// Classes without dedicated object
"hillFort" : {
"index" :35,
"handler": "generic",
"handler": "hillFort",
"base" : {
"sounds" : {
"ambient" : ["LOOPSWAR"],
@ -845,7 +845,14 @@
"zoneLimit" : 1,
"value" : 7000,
"rarity" : 20
}
},
"upgradeCostFactor" : [
0, // level 1
25,
50,
75,
100 // level 5+
]
}
}
},
@ -861,7 +868,7 @@
}
}
},
"marketOfTime" : { // Unused/not implemented H3 object present on some maps RoE maps
"marketOfTime" : { // Unused/not implemented H3 object present on some RoE maps
"index" :50,
"handler": "generic"
},

View File

@ -53,7 +53,7 @@
"randomResource":
{
"index" :76,
"handler": "resource",
"handler": "randomResource",
"base" : {
"base" : {
"visitableFrom" : [ "+++", "+-+", "+++" ],
@ -69,9 +69,10 @@
},
"templates" :
{
"res" :
{
"animation" : "AVTrndm0.def"
"normal" : {
"animation" : "AVTrndm0",
"visitableFrom" : [ "+++", "+-+", "+++" ],
"mask" : [ "VV", "VA"]
}
}
}
@ -146,21 +147,19 @@
}
},
"types" : {
"evil" : // Necropolis
{
"boatNecropolis" : {
"index" : 0,
"actualAnimation" : "AB01_.def",
"overlayAnimation" : "ABM01_.def",
"flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"]
},
"good" : // Castle
{
"boatCastle" : {
"index" : 1,
"actualAnimation" : "AB02_.def",
"overlayAnimation" : "ABM02_.def",
"flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"]
},
"neutral" : { // Fortress
"boatFortress" : {
"index" : 2,
"actualAnimation" : "AB03_.def",
"overlayAnimation" : "ABM03_.def",

View File

@ -124,6 +124,10 @@
"type" : "string",
"description" : "Identifier of war machine produced by blacksmith in town"
},
"boat" : {
"type" : "string",
"description" : "Identifier of boat type that is produced by shipyard in town, if any"
},
"horde" : {
"type" : "array",
"maxItems" : 2,

View File

@ -15,6 +15,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class SpellID;
enum class ESpellSchool: int8_t;
namespace spells
{
@ -43,6 +44,7 @@ public:
virtual bool isSpecial() const = 0;
virtual bool isMagical() const = 0; //Should this spell considered as magical effect or as ability (like dendroid's bind)
virtual bool hasSchool(ESpellSchool school) const = 0;
virtual void forEachSchool(const SchoolCallback & cb) const = 0;
virtual const std::string & getCastSound() const = 0;
virtual int32_t getCost(const int32_t skillLevel) const = 0;

View File

@ -621,7 +621,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
auto dwellingIDs = VLC->objtypeh->knownSubObjects(primaryID);
for (si32 entry : dwellingIDs)
{
const auto * handler = dynamic_cast<const CDwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
const auto * handler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(primaryID, entry).get());
if (handler->producesCreature(VLC->creh->objects[cid]))
result = std::make_pair(primaryID, entry);
@ -2018,54 +2018,28 @@ UpgradeInfo CGameState::fillUpgradeInfo(const CStackInstance &stack) const
UpgradeInfo ret;
const CCreature *base = stack.type;
const CGHeroInstance * h = stack.armyObj->ID == Obj::HERO ? dynamic_cast<const CGHeroInstance *>(stack.armyObj) : nullptr;
const CGTownInstance *t = nullptr;
if(stack.armyObj->ID == Obj::TOWN)
t = dynamic_cast<const CGTownInstance *>(stack.armyObj);
else if(h)
{ //hero specialty
TConstBonusListPtr lista = h->getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, base->getId()));
for(const auto & it : *lista)
{
auto nid = CreatureID(it->additionalInfo[0]);
if (nid != base->getId()) //in very specific case the upgrade is available by default (?)
{
ret.newID.push_back(nid);
ret.cost.push_back(nid.toCreature()->getFullRecruitCost() - base->getFullRecruitCost());
}
}
t = h->visitedTown;
}
if(t)
if (stack.armyObj->ID == Obj::HERO)
{
for(const CGTownInstance::TCreaturesSet::value_type & dwelling : t->creatures)
auto hero = dynamic_cast<const CGHeroInstance *>(stack.armyObj);
hero->fillUpgradeInfo(ret, stack);
if (hero->visitedTown)
{
if (vstd::contains(dwelling.second, base->getId())) //Dwelling with our creature
{
for(const auto & upgrID : dwelling.second)
{
if(vstd::contains(base->upgrades, upgrID)) //possible upgrade
{
ret.newID.push_back(upgrID);
ret.cost.push_back(upgrID.toCreature()->getFullRecruitCost() - base->getFullRecruitCost());
}
}
}
hero->visitedTown->fillUpgradeInfo(ret, stack);
}
else
{
auto object = vstd::frontOrNull(getVisitableObjs(hero->visitablePos()));
auto upgradeSource = dynamic_cast<const ICreatureUpgrader*>(object);
if (object != hero && upgradeSource != nullptr)
upgradeSource->fillUpgradeInfo(ret, stack);
}
}
//hero is visiting Hill Fort
if(h && map->getTile(h->visitablePos()).visitableObjects.front()->ID == Obj::HILL_FORT)
if (stack.armyObj->ID == Obj::TOWN)
{
static const int costModifiers[] = {0, 25, 50, 75, 100}; //we get cheaper upgrades depending on level
const int costModifier = costModifiers[std::min<int>(std::max((int)base->getLevel() - 1, 0), std::size(costModifiers) - 1)];
for(const auto & nid : base->upgrades)
{
ret.newID.push_back(nid);
ret.cost.push_back((nid.toCreature()->getFullRecruitCost() - base->getFullRecruitCost()) * costModifier / 100);
}
auto town = dynamic_cast<const CGTownInstance *>(stack.armyObj);
town->fillUpgradeInfo(ret, stack);
}
if(!ret.newID.empty())

View File

@ -893,6 +893,16 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
warMachinesToLoad[town] = source["warMachine"];
town->shipyardBoat = EBoatId::NONE;
if (!source["boat"].isNull())
{
VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)
{
town->shipyardBoat = BoatId(boatTypeID);
});
}
town->mageLevel = static_cast<ui32>(source["mageGuild"].Float());
town->namesCount = 0;
@ -1148,6 +1158,25 @@ void CTownHandler::afterLoadFinalization()
initializeRequirements();
initializeOverridden();
initializeWarMachines();
for(auto & faction : objects)
{
if (!faction->town)
continue;
bool hasBoat = faction->town->shipyardBoat != EBoatId::NONE;
bool hasShipyard = faction->town->buildings.count(BuildingID::SHIPYARD);
if ( hasBoat && !hasShipyard )
logMod->warn("Town %s has boat but has no shipyard!", faction->getJsonKey());
if ( !hasBoat && hasShipyard )
{
logMod->warn("Town %s has shipyard but has no boat set!", faction->getJsonKey());
// Mod compatibility for 1.3
faction->town->shipyardBoat = EBoatId::CASTLE;
}
}
}
void CTownHandler::initializeRequirements()

View File

@ -281,6 +281,9 @@ public:
GameResID primaryRes;
ArtifactID warMachine;
SpellID moatAbility;
/// boat that will be built by town shipyard, if exists
BoatId shipyardBoat;
// default chance for hero of specific class to appear in tavern, if field "tavern" was not set
// resulting chance = sqrt(town.chance * heroClass.chance)
ui32 defaultTavernChance;
@ -346,6 +349,7 @@ public:
h & mageLevel;
h & primaryRes;
h & warMachine;
h & shipyardBoat;
h & clientInfo;
h & moatAbility;
h & defaultTavernChance;

View File

@ -1285,9 +1285,9 @@ class BattleField : public BaseForID<BattleField, si32>
enum class EBoatId
{
NONE = -1,
BOAT_EVIL = 0,
BOAT_GOOD,
BOAT_NEUTRAL
NECROPOLIS = 0,
CASTLE,
FORTRESS
};
using BoatId = Identifier<EBoatId>;

View File

@ -170,16 +170,19 @@ void CPrivilegedInfoCallback::pickAllowedArtsSet(std::vector<const CArtifact *>
out.push_back(VLC->arth->objects[VLC->arth->pickRandomArtifact(rand, CArtifact::ART_MAJOR)]);
}
void CPrivilegedInfoCallback::getAllowedSpells(std::vector<SpellID> & out, ui16 level)
void CPrivilegedInfoCallback::getAllowedSpells(std::vector<SpellID> & out, std::optional<ui16> level)
{
for (ui32 i = 0; i < gs->map->allowedSpell.size(); i++) //spellh size appears to be greater (?)
{
const spells::Spell * spell = SpellID(i).toSpell();
if(isAllowed(0, spell->getIndex()) && spell->getLevel() == level)
{
out.push_back(spell->getId());
}
if (!isAllowed(0, spell->getIndex()))
continue;
if (level.has_value() && spell->getLevel() != level)
continue;
out.push_back(spell->getId());
}
}

View File

@ -71,7 +71,7 @@ public:
//gives 3 treasures, 3 minors, 1 major -> used by Black Market and Artifact Merchant
void pickAllowedArtsSet(std::vector<const CArtifact *> & out, CRandomGenerator & rand) const;
void getAllowedSpells(std::vector<SpellID> &out, ui16 level);
void getAllowedSpells(std::vector<SpellID> &out, std::optional<ui16> level = std::nullopt);
template<typename Saver>
void saveCommonState(Saver &out) const; //stores GS and VLC

View File

@ -243,21 +243,36 @@ namespace JsonRandom
if (value.getType() == JsonNode::JsonType::DATA_STRING)
return SpellID(VLC->modh->identifiers.getIdentifier("spell", value).value());
vstd::erase_if(spells, [=](const SpellID & spell)
if (!value["level"].isNull())
{
return VLC->spellh->getById(spell)->getLevel() != si32(value["level"].Float());
});
int32_t spellLevel = value["level"].Float();
vstd::erase_if(spells, [=](const SpellID & spell)
{
return VLC->spellh->getById(spell)->getLevel() != spellLevel;
});
}
if (!value["school"].isNull())
{
int32_t schoolID = VLC->modh->identifiers.getIdentifier("spellSchool", value["school"]).value();
vstd::erase_if(spells, [=](const SpellID & spell)
{
return !VLC->spellh->getById(spell)->hasSchool(ESpellSchool(schoolID));
});
}
if (spells.empty())
{
logMod->warn("Failed to select suitable random spell!");
return SpellID::NONE;
}
return SpellID(*RandomGeneratorUtil::nextItem(spells, rng));
}
std::vector<SpellID> loadSpells(const JsonNode & value, CRandomGenerator & rng, const std::vector<SpellID> & spells)
{
// possible extensions: (taken from spell json config)
// "type": "adventure",//"adventure", "combat", "ability"
// "school": {"air":true, "earth":true, "fire":true, "water":true},
// "level": 1,
std::vector<SpellID> ret;
for (const JsonNode & entry : value.Vector())
{

View File

@ -26,6 +26,15 @@ class DLL_LINKAGE TerrainType : public EntityT<TerrainId>
TerrainId id;
ui8 passabilityType;
enum PassabilityType : ui8
{
LAND = 1,
WATER = 2,
SURFACE = 4,
SUBTERRANEAN = 8,
ROCK = 16
};
public:
int32_t getIndex() const override { return id.getNum(); }
int32_t getIconIndex() const override { return 0; }
@ -37,15 +46,6 @@ public:
std::string getNameTextID() const override;
std::string getNameTranslated() const override;
enum PassabilityType : ui8
{
LAND = 1,
WATER = 2,
SURFACE = 4,
SUBTERRANEAN = 8,
ROCK = 16
};
std::vector<BattleField> battleFields;
std::vector<TerrainId> prohibitTransitions;
ColorRGBA minimapBlocked;

View File

@ -11,6 +11,7 @@
#include "StdInc.h"
#include "AObjectTypeHandler.h"
#include "IObjectInfo.h"
#include "../CGeneralTextHandler.h"
#include "../VCMI_Lib.h"
#include "../mapObjects/CGObjectInstance.h"
@ -221,5 +222,9 @@ void AObjectTypeHandler::afterLoadFinalization()
{
}
std::unique_ptr<IObjectInfo> AObjectTypeHandler::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return nullptr;
}
VCMI_LIB_NAMESPACE_END

View File

@ -107,7 +107,7 @@ public:
virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0;
/// Returns object configuration, if available. Otherwise returns NULL
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const = 0;
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const;
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -0,0 +1,248 @@
/*
* CBankInstanceConstructor.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 "CBankInstanceConstructor.h"
#include "../JsonRandom.h"
#include "../CGeneralTextHandler.h"
#include "../IGameCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
bool CBankInstanceConstructor::hasNameTextID() const
{
return true;
}
void CBankInstanceConstructor::initTypeData(const JsonNode & input)
{
if (input.Struct().count("name") == 0)
logMod->warn("Bank %s missing name!", getJsonKey());
VLC->generaltexth->registerString(input.meta, getNameTextID(), input["name"].String());
levels = input["levels"].Vector();
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
}
BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const
{
BankConfig bc;
bc.chance = static_cast<ui32>(level["chance"].Float());
bc.guards = JsonRandom::loadCreatures(level["guards"], rng);
bc.upgradeChance = static_cast<ui32>(level["upgrade_chance"].Float());
bc.combatValue = static_cast<ui32>(level["combat_value"].Float());
std::vector<SpellID> spells;
IObjectInterface::cb->getAllowedSpells(spells);
bc.resources = ResourceSet(level["reward"]["resources"]);
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng);
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng);
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells);
bc.value = static_cast<ui32>(level["value"].Float());
return bc;
}
void CBankInstanceConstructor::randomizeObject(CBank * bank, CRandomGenerator & rng) const
{
bank->resetDuration = bankResetDuration;
si32 totalChance = 0;
for(const auto & node : levels)
totalChance += static_cast<si32>(node["chance"].Float());
assert(totalChance != 0);
si32 selectedChance = rng.nextInt(totalChance - 1);
int cumulativeChance = 0;
for(const auto & node : levels)
{
cumulativeChance += static_cast<int>(node["chance"].Float());
if(selectedChance < cumulativeChance)
{
bank->setConfig(generateConfig(node, rng));
break;
}
}
}
CBankInfo::CBankInfo(const JsonVector & Config) :
config(Config)
{
assert(!Config.empty());
}
static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount)
{
army.totalStrength += crea->getFightValue() * amount;
bool walker = true;
if(crea->hasBonusOfType(BonusType::SHOOTER))
{
army.shootersStrength += crea->getFightValue() * amount;
walker = false;
}
if(crea->hasBonusOfType(BonusType::FLYING))
{
army.flyersStrength += crea->getFightValue() * amount;
walker = false;
}
if(walker)
army.walkersStrength += crea->getFightValue() * amount;
}
IObjectInfo::CArmyStructure CBankInfo::minGuards() const
{
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
assert(!stack.allowedCreatures.empty());
auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
{
return a->getFightValue() < b->getFightValue();
});
addStackToArmy(army, *weakest, stack.minAmount);
}
armies.push_back(army);
}
return *boost::range::min_element(armies);
}
IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
{
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
assert(!stack.allowedCreatures.empty());
auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
{
return a->getFightValue() < b->getFightValue();
});
addStackToArmy(army, *strongest, stack.maxAmount);
}
armies.push_back(army);
}
return *boost::range::max_element(armies);
}
TPossibleGuards CBankInfo::getPossibleGuards() const
{
TPossibleGuards out;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["guards"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
IObjectInfo::CArmyStructure army;
for(auto stack : stacks)
{
army.totalStrength += stack.allowedCreatures.front()->getAIValue() * (stack.minAmount + stack.maxAmount) / 2;
//TODO: add fields for flyers, walkers etc...
}
ui8 chance = static_cast<ui8>(configEntry["chance"].Float());
out.push_back(std::make_pair(chance, army));
}
return out;
}
std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward() const
{
std::vector<PossibleReward<TResources>> result;
for(const JsonNode & configEntry : config)
{
const JsonNode & resourcesInfo = configEntry["reward"]["resources"];
if(!resourcesInfo.isNull())
{
result.emplace_back(configEntry["chance"].Integer(), TResources(resourcesInfo));
}
}
return result;
}
std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
{
std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
for(auto stack : stacks)
{
const auto * creature = stack.allowedCreatures.front();
aproximateReward.emplace_back(configEntry["chance"].Integer(), CStackBasicDescriptor(creature, (stack.minAmount + stack.maxAmount) / 2));
}
}
return aproximateReward;
}
bool CBankInfo::givesResources() const
{
for(const JsonNode & node : config)
if(!node["reward"]["resources"].isNull())
return true;
return false;
}
bool CBankInfo::givesArtifacts() const
{
for(const JsonNode & node : config)
if(!node["reward"]["artifacts"].isNull())
return true;
return false;
}
bool CBankInfo::givesCreatures() const
{
for(const JsonNode & node : config)
if(!node["reward"]["creatures"].isNull())
return true;
return false;
}
bool CBankInfo::givesSpells() const
{
for(const JsonNode & node : config)
if(!node["reward"]["spells"].isNull())
return true;
return false;
}
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,104 @@
/*
* CBankInstanceConstructor.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
#include "CDefaultObjectTypeHandler.h"
#include "IObjectInfo.h"
#include "../CCreatureSet.h"
#include "../ResourceSet.h"
#include "../mapObjects/CBank.h"
VCMI_LIB_NAMESPACE_BEGIN
struct BankConfig
{
ui32 value = 0; //overall value of given things
ui32 chance = 0; //chance for this level being chosen
ui32 upgradeChance = 0; //chance for creatures to be in upgraded versions
ui32 combatValue = 0; //how hard are guards of this level
std::vector<CStackBasicDescriptor> guards; //creature ID, amount
ResourceSet resources; //resources given in case of victory
std::vector<CStackBasicDescriptor> creatures; //creatures granted in case of victory (creature ID, amount)
std::vector<ArtifactID> artifacts; //artifacts given in case of victory
std::vector<SpellID> spells; // granted spell(s), for Pyramid
template <typename Handler> void serialize(Handler &h, const int version)
{
h & chance;
h & upgradeChance;
h & guards;
h & combatValue;
h & resources;
h & creatures;
h & artifacts;
h & value;
h & spells;
}
};
using TPossibleGuards = std::vector<std::pair<ui8, IObjectInfo::CArmyStructure>>;
template <typename T>
struct DLL_LINKAGE PossibleReward
{
int chance;
T data;
PossibleReward(int chance, const T & data) : chance(chance), data(data) {}
};
class DLL_LINKAGE CBankInfo : public IObjectInfo
{
const JsonVector & config;
public:
CBankInfo(const JsonVector & Config);
TPossibleGuards getPossibleGuards() const;
std::vector<PossibleReward<TResources>> getPossibleResourcesReward() const;
std::vector<PossibleReward<CStackBasicDescriptor>> getPossibleCreaturesReward() const;
// These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI
CArmyStructure minGuards() const override;
CArmyStructure maxGuards() const override;
bool givesResources() const override;
bool givesArtifacts() const override;
bool givesCreatures() const override;
bool givesSpells() const override;
};
class CBankInstanceConstructor : public CDefaultObjectTypeHandler<CBank>
{
BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const;
JsonVector levels;
protected:
void initTypeData(const JsonNode & input) override;
public:
// all banks of this type will be reset N days after clearing,
si32 bankResetDuration = 0;
void randomizeObject(CBank * object, CRandomGenerator & rng) const override;
bool hasNameTextID() const override;
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & levels;
h & bankResetDuration;
h & static_cast<CDefaultObjectTypeHandler<CBank>&>(*this);
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,53 @@
/*
* CDefaultObjectTypeHandler.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
#include "AObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
/// Class that is used as base for multiple object constructors
template<class ObjectType>
class CDefaultObjectTypeHandler : public AObjectTypeHandler
{
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const final
{
ObjectType * castedObject = dynamic_cast<ObjectType*>(object);
if(castedObject == nullptr)
throw std::runtime_error("Unexpected object type!");
randomizeObject(castedObject, rng);
}
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const final
{
ObjectType * result = createObject();
preInitObject(result);
if(tmpl)
result->appearance = tmpl;
initializeObject(result);
return result;
}
protected:
virtual void initializeObject(ObjectType * object) const {}
virtual void randomizeObject(ObjectType * object, CRandomGenerator & rng) const {}
virtual ObjectType * createObject() const
{
return new ObjectType();
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -21,11 +21,21 @@
#include "../JsonNode.h"
#include "../CSoundBase.h"
#include "../mapObjectConstructors/CBankInstanceConstructor.h"
#include "../mapObjectConstructors/CRewardableConstructor.h"
#include "../mapObjectConstructors/CommonConstructors.h"
#include "../mapObjects/CQuest.h"
#include "../mapObjectConstructors/DwellingInstanceConstructor.h"
#include "../mapObjectConstructors/HillFortInstanceConstructor.h"
#include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
#include "../mapObjectConstructors/ShrineInstanceConstructor.h"
#include "../mapObjects/CGPandoraBox.h"
#include "../mapObjects/CQuest.h"
#include "../mapObjects/ObjectTemplate.h"
#include "../mapObjects/CGMarket.h"
#include "../mapObjects/MiscObjects.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGTownInstance.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -37,12 +47,15 @@ CObjectClassesHandler::CObjectClassesHandler()
// list of all known handlers, hardcoded for now since the only way to add new objects is via C++ code
//Note: should be in sync with registerTypesMapObjectTypes function
SET_HANDLER_CLASS("configurable", CRewardableConstructor);
SET_HANDLER_CLASS("dwelling", CDwellingInstanceConstructor);
SET_HANDLER_CLASS("dwelling", DwellingInstanceConstructor);
SET_HANDLER_CLASS("hero", CHeroInstanceConstructor);
SET_HANDLER_CLASS("town", CTownInstanceConstructor);
SET_HANDLER_CLASS("bank", CBankInstanceConstructor);
SET_HANDLER_CLASS("boat", BoatInstanceConstructor);
SET_HANDLER_CLASS("market", MarketInstanceConstructor);
SET_HANDLER_CLASS("shrine", ShrineInstanceConstructor);
SET_HANDLER_CLASS("hillFort", HillFortInstanceConstructor);
SET_HANDLER_CLASS("shipyard", ShipyardInstanceConstructor);
SET_HANDLER_CLASS("static", CObstacleConstructor);
SET_HANDLER_CLASS("", CObstacleConstructor);
@ -76,8 +89,6 @@ CObjectClassesHandler::CObjectClassesHandler()
SET_HANDLER("resource", CGResource);
SET_HANDLER("scholar", CGScholar);
SET_HANDLER("seerHut", CGSeerHut);
SET_HANDLER("shipyard", CGShipyard);
SET_HANDLER("shrine", CGShrine);
SET_HANDLER("sign", CGSignBottle);
SET_HANDLER("siren", CGSirens);
SET_HANDLER("monolith", CGMonolith);

View File

@ -10,19 +10,23 @@
#include "StdInc.h"
#include "CommonConstructors.h"
#include "../CCreatureHandler.h"
#include "../CGeneralTextHandler.h"
#include "../CHeroHandler.h"
#include "../CModHandler.h"
#include "../CTownHandler.h"
#include "../IGameCallback.h"
#include "../JsonRandom.h"
#include "../StringConstants.h"
#include "../TerrainHandler.h"
#include "../mapObjects/CBank.h"
#include "../VCMI_Lib.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGMarket.h"
#include "../mapObjects/CGTownInstance.h"
#include "../mapObjects/MiscObjects.h"
#include "../mapObjects/ObjectTemplate.h"
#include "../mapping/CMapDefines.h"
#include "JsonRandom.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -69,15 +73,13 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std
return filters.count(templ->stringID) != 0 && filters.at(templ->stringID).test(buildTest);
}
CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
void CTownInstanceConstructor::initializeObject(CGTownInstance * obj) const
{
CGTownInstance * obj = createTyped(tmpl);
obj->town = faction->town;
obj->tempOwner = PlayerColor::NEUTRAL;
return obj;
}
void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
void CTownInstanceConstructor::randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const
{
auto templ = getOverride(CGObjectInstance::cb->getTile(object->pos)->terType->getId(), object);
if(templ)
@ -121,144 +123,16 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std
return false;
}
CGObjectInstance * CHeroInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
void CHeroInstanceConstructor::initializeObject(CGHeroInstance * obj) const
{
CGHeroInstance * obj = createTyped(tmpl);
obj->type = nullptr; //FIXME: set to valid value. somehow.
return obj;
}
void CHeroInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
void CHeroInstanceConstructor::randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const
{
}
bool CDwellingInstanceConstructor::hasNameTextID() const
{
return true;
}
void CDwellingInstanceConstructor::initTypeData(const JsonNode & input)
{
if (input.Struct().count("name") == 0)
logMod->warn("Dwelling %s missing name!", getJsonKey());
VLC->generaltexth->registerString( input.meta, getNameTextID(), input["name"].String());
const JsonVector & levels = input["creatures"].Vector();
const auto totalLevels = levels.size();
availableCreatures.resize(totalLevels);
for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++)
{
const JsonVector & creaturesOnLevel = levels[currentLevel].Vector();
const auto creaturesNumber = creaturesOnLevel.size();
availableCreatures[currentLevel].resize(creaturesNumber);
for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
{
VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
{
availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index];
});
}
assert(!availableCreatures[currentLevel].empty());
}
guards = input["guards"];
}
bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const
{
return false;
}
CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
{
CGDwelling * obj = createTyped(tmpl);
obj->creatures.resize(availableCreatures.size());
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
obj->creatures.back().second.push_back(cre->getId());
}
return obj;
}
void CDwellingInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator &rng) const
{
auto * dwelling = dynamic_cast<CGDwelling *>(object);
dwelling->creatures.clear();
dwelling->creatures.reserve(availableCreatures.size());
for(const auto & entry : availableCreatures)
{
dwelling->creatures.resize(dwelling->creatures.size() + 1);
for(const CCreature * cre : entry)
dwelling->creatures.back().second.push_back(cre->getId());
}
bool guarded = false; //TODO: serialize for sanity
if(guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch
{
if(guards.Bool())
{
guarded = true;
}
}
else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
{
for(auto & stack : JsonRandom::loadCreatures(guards, rng))
{
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count));
}
}
else //default condition - creatures are of level 5 or higher
{
for(auto creatureEntry : availableCreatures)
{
if(creatureEntry.at(0)->getLevel() >= 5)
{
guarded = true;
break;
}
}
}
if(guarded)
{
for(auto creatureEntry : availableCreatures)
{
const CCreature * crea = creatureEntry.at(0);
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(crea->getId(), crea->getGrowth() * 3));
}
}
}
bool CDwellingInstanceConstructor::producesCreature(const CCreature * crea) const
{
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
if(crea == cre)
return true;
}
return false;
}
std::vector<const CCreature *> CDwellingInstanceConstructor::getProducedCreatures() const
{
std::vector<const CCreature *> creatures; //no idea why it's 2D, to be honest
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
creatures.push_back(cre);
}
return creatures;
}
void BoatInstanceConstructor::initTypeData(const JsonNode & input)
{
layer = EPathfindingLayer::SAIL;
@ -274,9 +148,8 @@ void BoatInstanceConstructor::initTypeData(const JsonNode & input)
bonuses = JsonRandom::loadBonuses(input["bonuses"]);
}
CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
void BoatInstanceConstructor::initializeObject(CGBoat * boat) const
{
CGBoat * boat = createTyped(tmpl);
boat->layer = layer;
boat->actualAnimation = actualAnimation;
boat->overlayAnimation = overlayAnimation;
@ -285,13 +158,20 @@ CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptr<const ObjectT
boat->onboardVisitAllowed = onboardVisitAllowed;
for(auto & b : bonuses)
boat->addNewBonus(std::make_shared<Bonus>(b));
return boat;
}
void BoatInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
std::string BoatInstanceConstructor::getBoatAnimationName() const
{
return actualAnimation;
}
void BoatInstanceConstructor::afterLoadFinalization()
{
if (layer == EPathfindingLayer::SAIL)
{
if (getTemplates(TerrainId(ETerrainId::WATER)).empty())
logMod->warn("Boat of type %s has no templates suitable for water!", getJsonKey());
}
}
void MarketInstanceConstructor::initTypeData(const JsonNode & input)
@ -309,31 +189,25 @@ void MarketInstanceConstructor::initTypeData(const JsonNode & input)
speech = input["speech"].String();
}
CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
CGMarket * MarketInstanceConstructor::createObject() const
{
CGMarket * market = nullptr;
if(marketModes.size() == 1)
{
switch(*marketModes.begin())
{
case EMarketMode::ARTIFACT_RESOURCE:
case EMarketMode::RESOURCE_ARTIFACT:
market = new CGBlackMarket;
break;
return new CGBlackMarket;
case EMarketMode::RESOURCE_SKILL:
market = new CGUniversity;
break;
return new CGUniversity;
}
}
if(!market)
market = new CGMarket;
preInitObject(market);
return new CGMarket;
}
if(tmpl)
market->appearance = tmpl;
void MarketInstanceConstructor::initializeObject(CGMarket * market) const
{
market->marketModes = marketModes;
market->marketEfficiency = marketEfficiency;
@ -343,11 +217,9 @@ CGObjectInstance * MarketInstanceConstructor::create(std::shared_ptr<const Objec
if (!speech.empty())
market->speech = VLC->generaltexth->translate(speech);
return market;
}
void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
void MarketInstanceConstructor::randomizeObject(CGMarket * object, CRandomGenerator & rng) const
{
if(auto * university = dynamic_cast<CGUniversity *>(object))
{
@ -356,241 +228,4 @@ void MarketInstanceConstructor::configureObject(CGObjectInstance * object, CRand
}
}
bool CBankInstanceConstructor::hasNameTextID() const
{
return true;
}
void CBankInstanceConstructor::initTypeData(const JsonNode & input)
{
if (input.Struct().count("name") == 0)
logMod->warn("Bank %s missing name!", getJsonKey());
VLC->generaltexth->registerString(input.meta, getNameTextID(), input["name"].String());
levels = input["levels"].Vector();
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
}
CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return createTyped(tmpl);
}
BankConfig CBankInstanceConstructor::generateConfig(const JsonNode & level, CRandomGenerator & rng) const
{
BankConfig bc;
bc.chance = static_cast<ui32>(level["chance"].Float());
bc.guards = JsonRandom::loadCreatures(level["guards"], rng);
bc.upgradeChance = static_cast<ui32>(level["upgrade_chance"].Float());
bc.combatValue = static_cast<ui32>(level["combat_value"].Float());
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
bc.resources = ResourceSet(level["reward"]["resources"]);
bc.creatures = JsonRandom::loadCreatures(level["reward"]["creatures"], rng);
bc.artifacts = JsonRandom::loadArtifacts(level["reward"]["artifacts"], rng);
bc.spells = JsonRandom::loadSpells(level["reward"]["spells"], rng, spells);
bc.value = static_cast<ui32>(level["value"].Float());
return bc;
}
void CBankInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
{
auto * bank = dynamic_cast<CBank *>(object);
bank->resetDuration = bankResetDuration;
si32 totalChance = 0;
for(const auto & node : levels)
totalChance += static_cast<si32>(node["chance"].Float());
assert(totalChance != 0);
si32 selectedChance = rng.nextInt(totalChance - 1);
int cumulativeChance = 0;
for(const auto & node : levels)
{
cumulativeChance += static_cast<int>(node["chance"].Float());
if(selectedChance < cumulativeChance)
{
bank->setConfig(generateConfig(node, rng));
break;
}
}
}
CBankInfo::CBankInfo(const JsonVector & Config) :
config(Config)
{
assert(!Config.empty());
}
static void addStackToArmy(IObjectInfo::CArmyStructure & army, const CCreature * crea, si32 amount)
{
army.totalStrength += crea->getFightValue() * amount;
bool walker = true;
if(crea->hasBonusOfType(BonusType::SHOOTER))
{
army.shootersStrength += crea->getFightValue() * amount;
walker = false;
}
if(crea->hasBonusOfType(BonusType::FLYING))
{
army.flyersStrength += crea->getFightValue() * amount;
walker = false;
}
if(walker)
army.walkersStrength += crea->getFightValue() * amount;
}
IObjectInfo::CArmyStructure CBankInfo::minGuards() const
{
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
assert(!stack.allowedCreatures.empty());
auto weakest = boost::range::min_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
{
return a->getFightValue() < b->getFightValue();
});
addStackToArmy(army, *weakest, stack.minAmount);
}
armies.push_back(army);
}
return *boost::range::min_element(armies);
}
IObjectInfo::CArmyStructure CBankInfo::maxGuards() const
{
std::vector<IObjectInfo::CArmyStructure> armies;
for(auto configEntry : config)
{
auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]);
IObjectInfo::CArmyStructure army;
for(auto & stack : stacks)
{
assert(!stack.allowedCreatures.empty());
auto strongest = boost::range::max_element(stack.allowedCreatures, [](const CCreature * a, const CCreature * b)
{
return a->getFightValue() < b->getFightValue();
});
addStackToArmy(army, *strongest, stack.maxAmount);
}
armies.push_back(army);
}
return *boost::range::max_element(armies);
}
TPossibleGuards CBankInfo::getPossibleGuards() const
{
TPossibleGuards out;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["guards"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
IObjectInfo::CArmyStructure army;
for(auto stack : stacks)
{
army.totalStrength += stack.allowedCreatures.front()->getAIValue() * (stack.minAmount + stack.maxAmount) / 2;
//TODO: add fields for flyers, walkers etc...
}
ui8 chance = static_cast<ui8>(configEntry["chance"].Float());
out.push_back(std::make_pair(chance, army));
}
return out;
}
std::vector<PossibleReward<TResources>> CBankInfo::getPossibleResourcesReward() const
{
std::vector<PossibleReward<TResources>> result;
for(const JsonNode & configEntry : config)
{
const JsonNode & resourcesInfo = configEntry["reward"]["resources"];
if(!resourcesInfo.isNull())
{
result.emplace_back(configEntry["chance"].Integer(), TResources(resourcesInfo));
}
}
return result;
}
std::vector<PossibleReward<CStackBasicDescriptor>> CBankInfo::getPossibleCreaturesReward() const
{
std::vector<PossibleReward<CStackBasicDescriptor>> aproximateReward;
for(const JsonNode & configEntry : config)
{
const JsonNode & guardsInfo = configEntry["reward"]["creatures"];
auto stacks = JsonRandom::evaluateCreatures(guardsInfo);
for(auto stack : stacks)
{
const auto * creature = stack.allowedCreatures.front();
aproximateReward.emplace_back(configEntry["chance"].Integer(), CStackBasicDescriptor(creature, (stack.minAmount + stack.maxAmount) / 2));
}
}
return aproximateReward;
}
bool CBankInfo::givesResources() const
{
for(const JsonNode & node : config)
if(!node["reward"]["resources"].isNull())
return true;
return false;
}
bool CBankInfo::givesArtifacts() const
{
for(const JsonNode & node : config)
if(!node["reward"]["artifacts"].isNull())
return true;
return false;
}
bool CBankInfo::givesCreatures() const
{
for(const JsonNode & node : config)
if(!node["reward"]["creatures"].isNull())
return true;
return false;
}
bool CBankInfo::givesSpells() const
{
for(const JsonNode & node : config)
if(!node["reward"]["spells"].isNull())
return true;
return false;
}
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
}
VCMI_LIB_NAMESPACE_END

View File

@ -9,63 +9,24 @@
*/
#pragma once
#include "AObjectTypeHandler.h"
#include "../mapObjects/CGMarket.h"
#include "../mapObjects/MiscObjects.h"
#include "../mapObjects/CGHeroInstance.h"
#include "../mapObjects/CGTownInstance.h"
#include "../mapObjects/CBank.h"
#include "CDefaultObjectTypeHandler.h"
#include "../LogicalExpression.h"
#include "IObjectInfo.h"
#include "../mapObjects/MiscObjects.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGArtifact;
class CGObjectInstance;
class CGTownInstance;
class CGHeroInstance;
class CGDwelling;
class CGMarket;
class CHeroClass;
class CBank;
class CGBoat;
class CFaction;
class CStackBasicDescriptor;
/// Class that is used for objects that do not have dedicated handler
template<class ObjectType>
class CDefaultObjectTypeHandler : public AObjectTypeHandler
{
protected:
ObjectType * createTyped(std::shared_ptr<const ObjectTemplate> tmpl /* = nullptr */) const
{
auto obj = new ObjectType();
preInitObject(obj);
//Set custom template or leave null
if (tmpl)
{
obj->appearance = tmpl;
}
return obj;
}
public:
CDefaultObjectTypeHandler() {}
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override
{
return createTyped(tmpl);
}
virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override
{
}
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override
{
return nullptr;
}
};
class CObstacleConstructor : public CDefaultObjectTypeHandler<CGObjectInstance>
{
public:
@ -83,8 +44,8 @@ public:
CFaction * faction = nullptr;
std::map<std::string, LogicalExpression<BuildingID>> filters;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
void initializeObject(CGTownInstance * object) const override;
void randomizeObject(CGTownInstance * object, CRandomGenerator & rng) const override;
void afterLoadFinalization() override;
template <typename Handler> void serialize(Handler &h, const int version)
@ -107,8 +68,8 @@ public:
CHeroClass * heroClass = nullptr;
std::map<std::string, LogicalExpression<HeroTypeID>> filters;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
void initializeObject(CGHeroInstance * object) const override;
void randomizeObject(CGHeroInstance * object, CRandomGenerator & rng) const override;
void afterLoadFinalization() override;
template <typename Handler> void serialize(Handler &h, const int version)
@ -120,34 +81,7 @@ public:
}
};
class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling>
{
std::vector<std::vector<const CCreature *>> availableCreatures;
JsonNode guards;
protected:
bool objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const override;
void initTypeData(const JsonNode & input) override;
public:
bool hasNameTextID() const override;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
bool producesCreature(const CCreature * crea) const;
std::vector<const CCreature *> getProducedCreatures() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & availableCreatures;
h & guards;
h & static_cast<CDefaultObjectTypeHandler<CGDwelling>&>(*this);
}
};
class BoatInstanceConstructor : public CDefaultObjectTypeHandler<CGBoat>
class DLL_LINKAGE BoatInstanceConstructor : public CDefaultObjectTypeHandler<CGBoat>
{
protected:
void initTypeData(const JsonNode & config) override;
@ -162,8 +96,11 @@ protected:
std::array<std::string, PlayerColor::PLAYER_LIMIT_I> flagAnimations;
public:
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
void initializeObject(CGBoat * object) const override;
void afterLoadFinalization() override;
/// Returns boat preview animation, for use in Shipyards
std::string getBoatAnimationName() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
@ -178,90 +115,6 @@ public:
}
};
struct BankConfig
{
ui32 value = 0; //overall value of given things
ui32 chance = 0; //chance for this level being chosen
ui32 upgradeChance = 0; //chance for creatures to be in upgraded versions
ui32 combatValue = 0; //how hard are guards of this level
std::vector<CStackBasicDescriptor> guards; //creature ID, amount
ResourceSet resources; //resources given in case of victory
std::vector<CStackBasicDescriptor> creatures; //creatures granted in case of victory (creature ID, amount)
std::vector<ArtifactID> artifacts; //artifacts given in case of victory
std::vector<SpellID> spells; // granted spell(s), for Pyramid
template <typename Handler> void serialize(Handler &h, const int version)
{
h & chance;
h & upgradeChance;
h & guards;
h & combatValue;
h & resources;
h & creatures;
h & artifacts;
h & value;
h & spells;
}
};
using TPossibleGuards = std::vector<std::pair<ui8, IObjectInfo::CArmyStructure>>;
template <typename T>
struct DLL_LINKAGE PossibleReward
{
int chance;
T data;
PossibleReward(int chance, const T & data) : chance(chance), data(data) {}
};
class DLL_LINKAGE CBankInfo : public IObjectInfo
{
const JsonVector & config;
public:
CBankInfo(const JsonVector & Config);
TPossibleGuards getPossibleGuards() const;
std::vector<PossibleReward<TResources>> getPossibleResourcesReward() const;
std::vector<PossibleReward<CStackBasicDescriptor>> getPossibleCreaturesReward() const;
// These functions should try to evaluate minimal possible/max possible guards to give provide information on possible thread to AI
CArmyStructure minGuards() const override;
CArmyStructure maxGuards() const override;
bool givesResources() const override;
bool givesArtifacts() const override;
bool givesCreatures() const override;
bool givesSpells() const override;
};
class CBankInstanceConstructor : public CDefaultObjectTypeHandler<CBank>
{
BankConfig generateConfig(const JsonNode & conf, CRandomGenerator & rng) const;
JsonVector levels;
protected:
void initTypeData(const JsonNode & input) override;
public:
// all banks of this type will be reset N days after clearing,
si32 bankResetDuration = 0;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
bool hasNameTextID() const override;
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & levels;
h & bankResetDuration;
h & static_cast<CDefaultObjectTypeHandler<CBank>&>(*this);
}
};
class MarketInstanceConstructor : public CDefaultObjectTypeHandler<CGMarket>
{
protected:
@ -274,8 +127,9 @@ protected:
std::string title, speech;
public:
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
CGMarket * createObject() const override;
void initializeObject(CGMarket * object) const override;
void randomizeObject(CGMarket * object, CRandomGenerator & rng) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -0,0 +1,146 @@
/*
* DwellingInstanceConstructor.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 "DwellingInstanceConstructor.h"
#include "../CCreatureHandler.h"
#include "../CGeneralTextHandler.h"
#include "../CModHandler.h"
#include "../JsonRandom.h"
#include "../VCMI_Lib.h"
#include "../mapObjects/CGDwelling.h"
VCMI_LIB_NAMESPACE_BEGIN
bool DwellingInstanceConstructor::hasNameTextID() const
{
return true;
}
void DwellingInstanceConstructor::initTypeData(const JsonNode & input)
{
if (input.Struct().count("name") == 0)
logMod->warn("Dwelling %s missing name!", getJsonKey());
VLC->generaltexth->registerString( input.meta, getNameTextID(), input["name"].String());
const JsonVector & levels = input["creatures"].Vector();
const auto totalLevels = levels.size();
availableCreatures.resize(totalLevels);
for(auto currentLevel = 0; currentLevel < totalLevels; currentLevel++)
{
const JsonVector & creaturesOnLevel = levels[currentLevel].Vector();
const auto creaturesNumber = creaturesOnLevel.size();
availableCreatures[currentLevel].resize(creaturesNumber);
for(auto currentCreature = 0; currentCreature < creaturesNumber; currentCreature++)
{
VLC->modh->identifiers.requestIdentifier("creature", creaturesOnLevel[currentCreature], [=] (si32 index)
{
availableCreatures[currentLevel][currentCreature] = VLC->creh->objects[index];
});
}
assert(!availableCreatures[currentLevel].empty());
}
guards = input["guards"];
}
bool DwellingInstanceConstructor::objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const
{
return false;
}
void DwellingInstanceConstructor::initializeObject(CGDwelling * obj) const
{
obj->creatures.resize(availableCreatures.size());
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
obj->creatures.back().second.push_back(cre->getId());
}
}
void DwellingInstanceConstructor::randomizeObject(CGDwelling * object, CRandomGenerator &rng) const
{
auto * dwelling = dynamic_cast<CGDwelling *>(object);
dwelling->creatures.clear();
dwelling->creatures.reserve(availableCreatures.size());
for(const auto & entry : availableCreatures)
{
dwelling->creatures.resize(dwelling->creatures.size() + 1);
for(const CCreature * cre : entry)
dwelling->creatures.back().second.push_back(cre->getId());
}
bool guarded = false; //TODO: serialize for sanity
if(guards.getType() == JsonNode::JsonType::DATA_BOOL) //simple switch
{
if(guards.Bool())
{
guarded = true;
}
}
else if(guards.getType() == JsonNode::JsonType::DATA_VECTOR) //custom guards (eg. Elemental Conflux)
{
for(auto & stack : JsonRandom::loadCreatures(guards, rng))
{
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(stack.type->getId(), stack.count));
}
}
else //default condition - creatures are of level 5 or higher
{
for(auto creatureEntry : availableCreatures)
{
if(creatureEntry.at(0)->getLevel() >= 5)
{
guarded = true;
break;
}
}
}
if(guarded)
{
for(auto creatureEntry : availableCreatures)
{
const CCreature * crea = creatureEntry.at(0);
dwelling->putStack(SlotID(dwelling->stacksCount()), new CStackInstance(crea->getId(), crea->getGrowth() * 3));
}
}
}
bool DwellingInstanceConstructor::producesCreature(const CCreature * crea) const
{
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
if(crea == cre)
return true;
}
return false;
}
std::vector<const CCreature *> DwellingInstanceConstructor::getProducedCreatures() const
{
std::vector<const CCreature *> creatures; //no idea why it's 2D, to be honest
for(const auto & entry : availableCreatures)
{
for(const CCreature * cre : entry)
creatures.push_back(cre);
}
return creatures;
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,45 @@
/*
* DwellingInstanceConstructor.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
#include "CDefaultObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGDwelling;
class DwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling>
{
std::vector<std::vector<const CCreature *>> availableCreatures;
JsonNode guards;
protected:
bool objectFilter(const CGObjectInstance * obj, std::shared_ptr<const ObjectTemplate> tmpl) const override;
void initTypeData(const JsonNode & input) override;
public:
bool hasNameTextID() const override;
void initializeObject(CGDwelling * object) const override;
void randomizeObject(CGDwelling * object, CRandomGenerator & rng) const override;
bool producesCreature(const CCreature * crea) const;
std::vector<const CCreature *> getProducedCreatures() const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & availableCreatures;
h & guards;
h & static_cast<CDefaultObjectTypeHandler<CGDwelling>&>(*this);
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,27 @@
/*
* HillFortInstanceConstructor.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 "HillFortInstanceConstructor.h"
#include "../mapObjects/MiscObjects.h"
VCMI_LIB_NAMESPACE_BEGIN
void HillFortInstanceConstructor::initTypeData(const JsonNode & config)
{
parameters = config;
}
void HillFortInstanceConstructor::initializeObject(HillFort * fort) const
{
fort->upgradeCostPercentage = parameters["upgradeCostFactor"].convertTo<std::vector<int>>();
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,34 @@
/*
* HillFortInstanceConstructor.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
#include "CDefaultObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class HillFort;
class HillFortInstanceConstructor final : public CDefaultObjectTypeHandler<HillFort>
{
JsonNode parameters;
protected:
void initTypeData(const JsonNode & config) override;
void initializeObject(HillFort * object) const override;
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<AObjectTypeHandler&>(*this);
h & parameters;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,28 @@
/*
* ShipyardInstanceConstructor.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 "ShipyardInstanceConstructor.h"
#include "../mapObjects/MiscObjects.h"
#include "../CModHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
void ShipyardInstanceConstructor::initTypeData(const JsonNode & config)
{
parameters = config;
}
void ShipyardInstanceConstructor::initializeObject(CGShipyard * shipyard) const
{
shipyard->createdBoat = BoatId(*VLC->modh->identifiers.getIdentifier("core:boat", parameters["boat"]));
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,34 @@
/*
* ShipyardInstanceConstructor.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
#include "CDefaultObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGShipyard;
class ShipyardInstanceConstructor final : public CDefaultObjectTypeHandler<CGShipyard>
{
JsonNode parameters;
protected:
void initTypeData(const JsonNode & config) override;
void initializeObject(CGShipyard * object) const override;
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<AObjectTypeHandler&>(*this);
h & parameters;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,42 @@
/*
* ShrineInstanceConstructor.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 "ShrineInstanceConstructor.h"
#include "../mapObjects/MiscObjects.h"
#include "../JsonRandom.h"
#include "../IGameCallback.h"
VCMI_LIB_NAMESPACE_BEGIN
void ShrineInstanceConstructor::initTypeData(const JsonNode & config)
{
parameters = config;
}
void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenerator & rng) const
{
auto visitTextParameter = parameters["visitText"];
if (visitTextParameter.isNumber())
shrine->visitText.addTxt(MetaString::ADVOB_TXT, static_cast<ui32>(visitTextParameter.Float()));
else
shrine->visitText << visitTextParameter.String();
if(shrine->spell == SpellID::NONE) // shrine has no predefined spell
{
std::vector<SpellID> possibilities;
shrine->cb->getAllowedSpells(possibilities);
shrine->spell =JsonRandom::loadSpell(parameters["spell"], rng, possibilities);
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -0,0 +1,34 @@
/*
* ShrineInstanceConstructor.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
#include "CDefaultObjectTypeHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGShrine;
class ShrineInstanceConstructor final : public CDefaultObjectTypeHandler<CGShrine>
{
JsonNode parameters;
protected:
void initTypeData(const JsonNode & config) override;
void randomizeObject(CGShrine * object, CRandomGenerator & rng) const override;
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<AObjectTypeHandler&>(*this);
h & parameters;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -18,7 +18,7 @@
#include "../CGeneralTextHandler.h"
#include "../CSoundBase.h"
#include "../mapObjectConstructors/CObjectClassesHandler.h"
#include "../mapObjectConstructors/CommonConstructors.h"
#include "../mapObjectConstructors/CBankInstanceConstructor.h"
#include "../IGameCallback.h"
#include "../CGameState.h"

View File

@ -225,7 +225,6 @@ int CGHeroInstance::maxMovePointsCached(bool onLand, const TurnInfo * ti) const
}
CGHeroInstance::CGHeroInstance():
IBoatGenerator(this),
tacticFormationEnabled(false),
inTownGarrison(false),
moveDir(4),
@ -453,7 +452,7 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
//Create a new boat for hero
NewObject no;
no.ID = Obj::BOAT;
no.subID = BoatId(EBoatId::BOAT_NEUTRAL);
no.subID = BoatId(EBoatId::CASTLE);
no.pos = CGBoat::translatePos(boatPos);
cb->sendAndApply(&no);
@ -954,13 +953,8 @@ si32 CGHeroInstance::getManaNewTurn() const
BoatId CGHeroInstance::getBoatType() const
{
switch (type->heroClass->getAlignment())
{
case EAlignment::EVIL: return EBoatId::BOAT_EVIL;
case EAlignment::GOOD: return EBoatId::BOAT_GOOD;
case EAlignment::NEUTRAL: return EBoatId::BOAT_NEUTRAL;
default: return EBoatId::NONE;
}
// hero can only generate boat via "Summon Boat" spell which always create same boat as in Necropolis shipyard
return EBoatId::NECROPOLIS;
}
void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
@ -973,6 +967,11 @@ void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
};
}
const IObjectInterface * CGHeroInstance::getObject() const
{
return this;
}
int32_t CGHeroInstance::getSpellCost(const spells::Spell * sp) const
{
return sp->getCost(getSpellSchoolLevel(sp));
@ -1715,4 +1714,18 @@ bool CGHeroInstance::isMissionCritical() const
return false;
}
void CGHeroInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
{
TConstBonusListPtr lista = getBonuses(Selector::typeSubtype(BonusType::SPECIAL_UPGRADE, stack.type->getId()));
for(const auto & it : *lista)
{
auto nid = CreatureID(it->additionalInfo[0]);
if (nid != stack.type->getId()) //in very specific case the upgrade is available by default (?)
{
info.newID.push_back(nid);
info.cost.push_back(nid.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost());
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -39,7 +39,7 @@ public:
};
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public spells::Caster, public AFactionMember, public ICreatureUpgrader
{
// We serialize heroes into JSON for crossover
friend class CCampaignState;
@ -133,6 +133,7 @@ public:
BoatId getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed
const IObjectInterface * getObject() const override;
//////////////////////////////////////////////////////////////////////////
@ -230,6 +231,8 @@ public:
void recreateSecondarySkillsBonuses();
void updateSkillBonus(const SecondarySkill & which, int val);
void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override;
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
/// If this hero perishes, the scenario is failed
bool isMissionCritical() const;

View File

@ -254,11 +254,6 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const
{
switch(ID)
{
case Obj::HILL_FORT:
{
openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum());
}
break;
case Obj::SANCTUARY:
{
//You enter the sanctuary and immediately feel as if a great weight has been lifted off your shoulders. You feel safe here.

View File

@ -224,8 +224,6 @@ bool CGTownInstance::hasCapitol() const
}
CGTownInstance::CGTownInstance():
IShipyard(this),
IMarket(),
town(nullptr),
builded(0),
destroyed(0),
@ -590,6 +588,19 @@ void CGTownInstance::getOutOffsets( std::vector<int3> &offsets ) const
offsets = {int3(-1,2,0), int3(-3,2,0)};
}
CGTownInstance::EGeneratorState CGTownInstance::shipyardStatus() const
{
if (!hasBuilt(BuildingID::SHIPYARD))
return EGeneratorState::UNKNOWN;
return IShipyard::shipyardStatus();
}
const IObjectInterface * CGTownInstance::getObject() const
{
return this;
}
void CGTownInstance::mergeGarrisonOnSiege() const
{
auto getWeakestStackSlot = [&](ui64 powerLimit)
@ -671,13 +682,7 @@ void CGTownInstance::clearArmy() const
BoatId CGTownInstance::getBoatType() const
{
switch (town->faction->alignment)
{
case EAlignment::EVIL : return EBoatId::BOAT_EVIL;
case EAlignment::GOOD : return EBoatId::BOAT_GOOD;
case EAlignment::NEUTRAL : return EBoatId::BOAT_NEUTRAL;
default: return EBoatId::NONE;
}
return town->shipyardBoat;
}
int CGTownInstance::getMarketEfficiency() const
@ -1242,5 +1247,22 @@ int GrowthInfo::totalGrowth() const
return ret;
}
void CGTownInstance::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
{
for(const CGTownInstance::TCreaturesSet::value_type & dwelling : creatures)
{
if (vstd::contains(dwelling.second, stack.type->getId())) //Dwelling with our creature
{
for(const auto & upgrID : dwelling.second)
{
if(vstd::contains(stack.type->upgrades, upgrID)) //possible upgrade
{
info.newID.push_back(upgrID);
info.cost.push_back(upgrID.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost());
}
}
}
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -42,10 +42,12 @@ struct DLL_LINKAGE GrowthInfo
int totalGrowth() const;
};
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
{
std::string name; // name of town
public:
using CGDwelling::getPosition;
enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
CTownAndVisitingHero townAndVis;
@ -71,7 +73,6 @@ public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGDwelling&>(*this);
h & static_cast<IShipyard&>(*this);
h & name;
h & builded;
h & destroyed;
@ -134,6 +135,8 @@ public:
int getSightRadius() const override; //returns sight distance
BoatId getBoatType() const override; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed. Parameter will be cleared
EGeneratorState shipyardStatus() const override;
const IObjectInterface * getObject() const override;
int getMarketEfficiency() const override; //=market count
bool allowsTrade(EMarketMode::EMarketMode mode) const override;
std::vector<int> availableItemsIds(EMarketMode::EMarketMode mode) const override;
@ -197,6 +200,8 @@ public:
void battleFinished(const CGHeroInstance * hero, const BattleResult & result) const override;
std::string getObjectName() const override;
void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override;
void afterAddToMap(CMap * map) override;
void afterRemoveFromMap(CMap * map) override;
static void reset();

View File

@ -92,10 +92,10 @@ 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 = getObject()->cb->getTile(getObject()->getPosition() + offset, false)) //tile is in the map
{
if(tile->terType->isWater() && (!tile->blocked || tile->blockingObjects.front()->ID == Obj::BOAT)) //and is water and is not blocked or is blocked by boat
return o->pos + offset;
return getObject()->getPosition() + offset;
}
}
return int3 (-1,-1,-1);
@ -107,24 +107,14 @@ IBoatGenerator::EGeneratorState IBoatGenerator::shipyardStatus() const
const TerrainTile *t = IObjectInterface::cb->getTile(tile);
if(!t)
return TILE_BLOCKED; //no available water
else if(t->blockingObjects.empty())
if(t->blockingObjects.empty())
return GOOD; //OK
else if(t->blockingObjects.front()->ID == Obj::BOAT)
if(t->blockingObjects.front()->ID == Obj::BOAT)
return BOAT_ALREADY_BUILT; //blocked with boat
else
return TILE_BLOCKED; //blocked
}
BoatId IBoatGenerator::getBoatType() const
{
//We make good ships by default
return EBoatId::BOAT_GOOD;
}
IBoatGenerator::IBoatGenerator(const CGObjectInstance *O)
: o(O)
{
return TILE_BLOCKED; //blocked
}
void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visitor) const
@ -144,7 +134,7 @@ void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visit
out.addTxt(MetaString::ADVOB_TXT, 189);
break;
case NO_WATER:
logGlobal->error("Shipyard without water! %s \t %d", o->pos.toString(), o->id.getNum());
logGlobal->error("Shipyard without water at tile %s! ", getObject()->getPosition().toString());
return;
}
}
@ -155,34 +145,9 @@ void IShipyard::getBoatCost(TResources & cost) const
cost[EGameResID::GOLD] = 1000;
}
IShipyard::IShipyard(const CGObjectInstance *O)
: IBoatGenerator(O)
{
}
IShipyard * IShipyard::castFrom( CGObjectInstance *obj )
{
if(!obj)
return nullptr;
if(obj->ID == Obj::TOWN)
{
return dynamic_cast<CGTownInstance *>(obj);
}
else if(obj->ID == Obj::SHIPYARD)
{
return dynamic_cast<CGShipyard *>(obj);
}
else
{
return nullptr;
}
}
const IShipyard * IShipyard::castFrom( const CGObjectInstance *obj )
{
return castFrom(const_cast<CGObjectInstance*>(obj));
return dynamic_cast<const IShipyard *>(obj);
}
VCMI_LIB_NAMESPACE_END

View File

@ -14,6 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN
struct BattleResult;
struct UpgradeInfo;
class CGObjectInstance;
class CRandomGenerator;
class IGameCallback;
@ -66,42 +67,36 @@ public:
}
};
class DLL_LINKAGE ICreatureUpgrader
{
public:
virtual void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const = 0;
virtual ~ICreatureUpgrader() = default;
};
class DLL_LINKAGE IBoatGenerator
{
public:
const CGObjectInstance *o;
IBoatGenerator(const CGObjectInstance *O);
virtual ~IBoatGenerator() = default;
virtual BoatId getBoatType() const; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
virtual void getOutOffsets(std::vector<int3> &offsets) const =0; //offsets to obj pos when we boat can be placed
virtual const IObjectInterface * getObject() const = 0;
virtual BoatId getBoatType() const = 0; //0 - evil (if a ship can be evil...?), 1 - good, 2 - neutral
virtual void getOutOffsets(std::vector<int3> & offsets) const = 0; //offsets to obj pos when we boat can be placed
int3 bestLocation() const; //returns location when the boat should be placed
enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER};
EGeneratorState shipyardStatus() const; //0 - can buid, 1 - there is already a boat at dest tile, 2 - dest tile is blocked, 3 - no water
enum EGeneratorState {GOOD, BOAT_ALREADY_BUILT, TILE_BLOCKED, NO_WATER, UNKNOWN};
virtual EGeneratorState shipyardStatus() const;
void getProblemText(MetaString &out, const CGHeroInstance *visitor = nullptr) const;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & o;
}
};
class DLL_LINKAGE IShipyard : public IBoatGenerator
{
public:
IShipyard(const CGObjectInstance *O);
virtual void getBoatCost(ResourceSet & cost) const;
static const IShipyard *castFrom(const CGObjectInstance *obj);
static IShipyard *castFrom(CGObjectInstance *obj);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<IBoatGenerator&>(*this);
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -26,6 +26,8 @@
#include "../CPlayerState.h"
#include "../GameSettings.h"
#include "../serializer/JsonSerializeFormat.h"
#include "../mapObjectConstructors/AObjectTypeHandler.h"
#include "../mapObjectConstructors/CObjectClassesHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -1530,7 +1532,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
InfoWindow iw;
iw.type = EInfoWindowMode::AUTO;
iw.player = h->getOwner();
iw.text.addTxt(MetaString::ADVOB_TXT,127 + ID - 88);
iw.text = visitText;
iw.text.addTxt(MetaString::SPELL_NAME,spell);
iw.text << ".";
@ -1542,7 +1544,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
{
iw.text.addTxt(MetaString::ADVOB_TXT,174);
}
else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && h->maxSpellLevel() < 3) //it's third level spell and hero doesn't have wisdom
else if(spell.toSpell()->getLevel() > h->maxSpellLevel()) //it's third level spell and hero doesn't have wisdom
{
iw.text.addTxt(MetaString::ADVOB_TXT,130);
}
@ -1560,20 +1562,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
void CGShrine::initObj(CRandomGenerator & rand)
{
if(spell == SpellID::NONE) //spell not set
{
int level = ID-87;
std::vector<SpellID> possibilities;
cb->getAllowedSpells (possibilities, level);
if(possibilities.empty())
{
logGlobal->error("Error: cannot init shrine, no allowed spells!");
return;
}
spell = *RandomGeneratorUtil::nextItem(possibilities, rand);
}
VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand);
}
std::string CGShrine::getHoverText(PlayerColor player) const
@ -1693,8 +1682,7 @@ void CGScholar::initObj(CRandomGenerator & rand)
break;
case SPELL:
std::vector<SpellID> possibilities;
for (int i = 1; i < 6; ++i)
cb->getAllowedSpells (possibilities, i);
cb->getAllowedSpells (possibilities);
bonusID = *RandomGeneratorUtil::nextItem(possibilities, rand);
break;
}
@ -1851,8 +1839,15 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
{
h->showInfoDialog(48);
}
}
CGBoat::CGBoat()
{
hero = nullptr;
direction = 4;
layer = EPathfindingLayer::EEPathfindingLayer::SAIL;
}
void CGBoat::initObj(CRandomGenerator & rand)
{
hero = nullptr;
@ -1927,12 +1922,6 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
}
}
cb->showInfoDialog(&iw);
}
CGShipyard::CGShipyard()
:IShipyard(this)
{
}
void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
@ -1947,6 +1936,11 @@ void CGShipyard::getOutOffsets( std::vector<int3> &offsets ) const
};
}
const IObjectInterface * CGShipyard::getObject() const
{
return this;
}
void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
{
if(!cb->gameState()->getPlayerRelations(tempOwner, h->tempOwner))
@ -1972,6 +1966,12 @@ void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler)
serializeJsonOwner(handler);
}
BoatId CGShipyard::getBoatType() const
{
// In H3, external shipyard will always create same boat as castle
return EBoatId::CASTLE;
}
void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
{
//if player has not bought map of this subtype yet and underground exist for stalagmite cartographer
@ -2176,4 +2176,26 @@ void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)
serializeJsonOwner(handler);
}
void HillFort::onHeroVisit(const CGHeroInstance * h) const
{
openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum());
}
void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const
{
int32_t level = stack.type->getLevel();
int32_t index = std::clamp<int32_t>(level - 1, 0, upgradeCostPercentage.size() - 1);
int costModifier = upgradeCostPercentage[index];
if (costModifier < 0)
return; // upgrade not allowed
for(const auto & nid : stack.type->upgrades)
{
info.newID.push_back(nid);
info.cost.push_back((nid.toCreature()->getFullRecruitCost() - stack.type->getFullRecruitCost()) * costModifier / 100);
}
}
VCMI_LIB_NAMESPACE_END

View File

@ -246,7 +246,9 @@ protected:
class DLL_LINKAGE CGShrine : public CTeamVisited
{
public:
MetaString visitText;
SpellID spell; //id of spell or NONE if random
void onHeroVisit(const CGHeroInstance * h) const override;
void initObj(CRandomGenerator & rand) override;
std::string getHoverText(PlayerColor player) const override;
@ -256,6 +258,7 @@ public:
{
h & static_cast<CTeamVisited&>(*this);;
h & spell;
h & visitText;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
@ -429,15 +432,10 @@ public:
std::string overlayAnimation; //waves animations
std::array<std::string, PlayerColor::PLAYER_LIMIT_I> flagAnimations;
CGBoat();
void initObj(CRandomGenerator & rand) override;
static int3 translatePos(const int3 &pos, bool reverse = false);
CGBoat()
{
hero = nullptr;
direction = 4;
layer = EPathfindingLayer::EEPathfindingLayer::SAIL;
}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGObjectInstance&>(*this);
@ -455,16 +453,23 @@ public:
class DLL_LINKAGE CGShipyard : public CGObjectInstance, public IShipyard
{
public:
void getOutOffsets(std::vector<int3> &offsets) const override; //offsets to obj pos when we boat can be placed
CGShipyard();
void onHeroVisit(const CGHeroInstance * h) const override;
friend class ShipyardInstanceConstructor;
template <typename Handler> void serialize(Handler &h, const int version)
BoatId createdBoat;
protected:
void getOutOffsets(std::vector<int3> & offsets) const override;
void onHeroVisit(const CGHeroInstance * h) const override;
const IObjectInterface * getObject() const override;
BoatId getBoatType() const override;
public:
template<typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<CGObjectInstance&>(*this);
h & static_cast<IShipyard&>(*this);
h & createdBoat;
}
protected:
void serializeJsonOptions(JsonSerializeFormat & handler) override;
};
@ -550,4 +555,22 @@ public:
}
};
class DLL_LINKAGE HillFort : public CGObjectInstance, public ICreatureUpgrader
{
friend class HillFortInstanceConstructor;
std::vector<int> upgradeCostPercentage;
protected:
void onHeroVisit(const CGHeroInstance * h) const override;
void fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const override;
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CGObjectInstance&>(*this);
h & upgradeCostPercentage;
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -164,7 +164,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
}
//assuming that object can be placed on other land terrains
anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
anyLandTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
id = Obj(boost::lexical_cast<int>(strings[5]));
subid = boost::lexical_cast<int>(strings[6]);
@ -230,7 +230,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader)
}
//assuming that object can be placed on other land terrains
anyTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
anyLandTerrain = allowedTerrains.size() >= 8 && !allowedTerrains.count(ETerrainId::WATER);
id = Obj(reader.readUInt32());
subid = reader.readUInt32();
@ -277,11 +277,11 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
allowedTerrains.insert(TerrainId(identifier));
});
}
anyTerrain = false;
anyLandTerrain = false;
}
else
{
anyTerrain = true;
anyLandTerrain = true;
}
auto charToTile = [&](const char & ch) -> ui8
@ -557,7 +557,7 @@ void ObjectTemplate::calculateVisitableOffset()
bool ObjectTemplate::canBePlacedAt(TerrainId terrainID) const
{
if (anyTerrain)
if (anyLandTerrain)
{
const auto & terrain = VLC->terrainTypeHandler->getById(terrainID);
return terrain->isLand() && terrain->isPassable();

View File

@ -36,7 +36,7 @@ class DLL_LINKAGE ObjectTemplate
std::set<TerrainId> allowedTerrains;
/// or, allow placing object on any terrain
bool anyTerrain;
bool anyLandTerrain;
void afterLoadFixup();
@ -109,7 +109,7 @@ public:
inline bool canBePlacedAtAnyTerrain() const
{
return anyTerrain;
return anyLandTerrain;
};
const std::set<TerrainId>& getAllowedTerrains() const
@ -159,6 +159,7 @@ public:
{
h & usedTiles;
h & allowedTerrains;
h & anyLandTerrain;
h & animationFile;
h & stringID;
h & id;

View File

@ -129,9 +129,10 @@ bool TerrainTile::isWater() const
}
CMap::CMap()
: checksum(0), grailPos(-1, -1, -1), grailRadius(0), terrain(nullptr),
guardingCreaturePositions(nullptr),
uidCounter(0)
: checksum(0)
, grailPos(-1, -1, -1)
, grailRadius(0)
, uidCounter(0)
{
allHeroes.resize(allowedHeroes.size());
allowedAbilities = VLC->skillh->getDefaultAllowed();
@ -142,22 +143,6 @@ CMap::CMap()
CMap::~CMap()
{
getEditManager()->getUndoManager().clearAll();
if(terrain)
{
for(int z = 0; z < levels(); z++)
{
for(int x = 0; x < width; x++)
{
delete[] terrain[z][x];
delete[] guardingCreaturePositions[z][x];
}
delete[] terrain[z];
delete[] guardingCreaturePositions[z];
}
delete [] terrain;
delete [] guardingCreaturePositions;
}
for(auto obj : objects)
obj.dellNull();
@ -572,19 +557,8 @@ void CMap::removeObject(CGObjectInstance * obj)
void CMap::initTerrain()
{
int level = levels();
terrain = new TerrainTile**[level];
guardingCreaturePositions = new int3**[level];
for(int z = 0; z < level; ++z)
{
terrain[z] = new TerrainTile*[width];
guardingCreaturePositions[z] = new int3*[width];
for(int x = 0; x < width; ++x)
{
terrain[z][x] = new TerrainTile[height];
guardingCreaturePositions[z][x] = new int3[height];
}
}
terrain.resize(boost::extents[levels()][width][height]);
guardingCreaturePositions.resize(boost::extents[levels()][width][height]);
}
CMapEditManager * CMap::getEditManager()

View File

@ -142,14 +142,14 @@ public:
std::map<si32, ObjectInstanceID> questIdentifierToId;
std::unique_ptr<CMapEditManager> editManager;
int3 ***guardingCreaturePositions;
boost::multi_array<int3, 3> guardingCreaturePositions;
std::map<std::string, ConstTransitivePtr<CGObjectInstance> > instanceNames;
private:
/// a 3-dimensional array of terrain tiles, access is as follows: x, y, level. where level=1 is underground
TerrainTile*** terrain;
boost::multi_array<TerrainTile, 3> terrain;
si32 uidCounter; //TODO: initialize when loading an old map
public:
@ -170,50 +170,8 @@ public:
h & questIdentifierToId;
//TODO: viccondetails
const int level = levels();
if(h.saving)
{
// Save terrain
for(int z = 0; z < level; ++z)
{
for(int x = 0; x < width; ++x)
{
for(int y = 0; y < height; ++y)
{
h & terrain[z][x][y];
h & guardingCreaturePositions[z][x][y];
}
}
}
}
else
{
// Load terrain
terrain = new TerrainTile**[level];
guardingCreaturePositions = new int3**[level];
for(int z = 0; z < level; ++z)
{
terrain[z] = new TerrainTile*[width];
guardingCreaturePositions[z] = new int3*[width];
for(int x = 0; x < width; ++x)
{
terrain[z][x] = new TerrainTile[height];
guardingCreaturePositions[z][x] = new int3[height];
}
}
for(int z = 0; z < level; ++z)
{
for(int x = 0; x < width; ++x)
{
for(int y = 0; y < height; ++y)
{
h & terrain[z][x][y];
h & guardingCreaturePositions[z][x][y];
}
}
}
}
h & terrain;
h & guardingCreaturePositions;
h & objects;
h & heroesOnMap;

View File

@ -1292,9 +1292,9 @@ CGObjectInstance * CMapLoaderH3M::readQuestGuard(const int3 & mapPosition)
return guard;
}
CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition)
CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
{
auto * object = new CGShipyard();
auto * object = readGeneric(mapPosition, objectTemplate);
setOwnerAndValidate(mapPosition, object, reader->readPlayer32());
return object;
}
@ -1448,7 +1448,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplat
return readQuestGuard(mapPosition);
case Obj::SHIPYARD:
return readShipyard(mapPosition);
return readShipyard(mapPosition, objectTemplate);
case Obj::HERO_PLACEHOLDER:
return readHeroPlaceholder(mapPosition);

View File

@ -180,7 +180,7 @@ private:
CGObjectInstance * readBorderGuard();
CGObjectInstance * readBorderGate(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
CGObjectInstance * readQuestGuard(const int3 & position);
CGObjectInstance * readShipyard(const int3 & mapPosition);
CGObjectInstance * readShipyard(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readLighthouse(const int3 & mapPosition);
CGObjectInstance * readGeneric(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
CGObjectInstance * readBank(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);

View File

@ -20,6 +20,11 @@
#include "../CModHandler.h" //needed?
#include "../mapObjectConstructors/CRewardableConstructor.h"
#include "../mapObjectConstructors/CommonConstructors.h"
#include "../mapObjectConstructors/CBankInstanceConstructor.h"
#include "../mapObjectConstructors/DwellingInstanceConstructor.h"
#include "../mapObjectConstructors/HillFortInstanceConstructor.h"
#include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
#include "../mapObjectConstructors/ShrineInstanceConstructor.h"
#include "../mapObjects/MapObjects.h"
#include "../mapObjects/CGTownBuilding.h"
#include "../mapObjects/ObjectTemplate.h"
@ -59,10 +64,11 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CGObjectInstance, CGBoat>();
s.template registerType<CGObjectInstance, CGMagi>();
s.template registerType<CGObjectInstance, CGSirens>();
s.template registerType<CGObjectInstance, CGShipyard>(); s.template registerType<IShipyard, CGShipyard>();
s.template registerType<CGObjectInstance, CGShipyard>();
s.template registerType<CGObjectInstance, CGDenOfthieves>();
s.template registerType<CGObjectInstance, CGLighthouse>();
s.template registerType<CGObjectInstance, CGTerrainPatch>();
s.template registerType<CGObjectInstance, HillFort>();
s.template registerType<CGObjectInstance, CGMarket>();
s.template registerType<CGMarket, CGBlackMarket>();
s.template registerType<CGMarket, CGUniversity>();
@ -71,9 +77,9 @@ void registerTypesMapObjects1(Serializer &s)
s.template registerType<CGObjectInstance, CArmedInstance>(); s.template registerType<CBonusSystemNode, CArmedInstance>(); s.template registerType<CCreatureSet, CArmedInstance>();
// Armed objects
s.template registerType<CArmedInstance, CGHeroInstance>(); s.template registerType<IBoatGenerator, CGHeroInstance>(); s.template registerType<CArtifactSet, CGHeroInstance>();
s.template registerType<CArmedInstance, CGHeroInstance>(); s.template registerType<CArtifactSet, CGHeroInstance>();
s.template registerType<CArmedInstance, CGDwelling>();
s.template registerType<CGDwelling, CGTownInstance>(); s.template registerType<IShipyard, CGTownInstance>();
s.template registerType<CGDwelling, CGTownInstance>();
s.template registerType<CArmedInstance, CGPandoraBox>();
s.template registerType<CGPandoraBox, CGEvent>();
s.template registerType<CArmedInstance, CGCreature>();
@ -92,11 +98,14 @@ void registerTypesMapObjectTypes(Serializer &s)
s.template registerType<AObjectTypeHandler, CRewardableConstructor>();
s.template registerType<AObjectTypeHandler, CHeroInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CTownInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CDwellingInstanceConstructor>();
s.template registerType<AObjectTypeHandler, DwellingInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CBankInstanceConstructor>();
s.template registerType<AObjectTypeHandler, BoatInstanceConstructor>();
s.template registerType<AObjectTypeHandler, MarketInstanceConstructor>();
s.template registerType<AObjectTypeHandler, CObstacleConstructor>();
s.template registerType<AObjectTypeHandler, ShrineInstanceConstructor>();
s.template registerType<AObjectTypeHandler, ShipyardInstanceConstructor>();
s.template registerType<AObjectTypeHandler, HillFortInstanceConstructor>();
#define REGISTER_GENERIC_HANDLER(TYPENAME) s.template registerType<AObjectTypeHandler, CDefaultObjectTypeHandler<TYPENAME> >()
@ -137,6 +146,7 @@ void registerTypesMapObjectTypes(Serializer &s)
REGISTER_GENERIC_HANDLER(CGTownInstance);
REGISTER_GENERIC_HANDLER(CGUniversity);
REGISTER_GENERIC_HANDLER(CGWitchHut);
REGISTER_GENERIC_HANDLER(HillFort);
#undef REGISTER_GENERIC_HANDLER

View File

@ -74,8 +74,7 @@ Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Conf
void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRandomGenerator & rng, Rewardable::Limiter & limiter, const JsonNode & source) const
{
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
IObjectInterface::cb->getAllowedSpells(spells);
limiter.dayOfWeek = JsonRandom::loadValue(source["dayOfWeek"], rng);
@ -124,8 +123,7 @@ void Rewardable::Info::configureReward(Rewardable::Configuration & object, CRand
reward.secondary = JsonRandom::loadSecondary(source["secondary"], rng);
std::vector<SpellID> spells;
for (size_t i=0; i<6; i++)
IObjectInterface::cb->getAllowedSpells(spells, static_cast<ui16>(i));
IObjectInterface::cb->getAllowedSpells(spells);
reward.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
reward.spells = JsonRandom::loadSpells(source["spells"], rng, spells);

View File

@ -22,7 +22,8 @@
#include "../../ArtifactUtils.h"
#include "../../mapObjectConstructors/AObjectTypeHandler.h"
#include "../../mapObjectConstructors/CObjectClassesHandler.h"
#include "../../mapObjectConstructors/CommonConstructors.h"
#include "../../mapObjectConstructors/DwellingInstanceConstructor.h"
#include "../../mapObjects/CGHeroInstance.h"
#include "../../mapObjects/CGPandoraBox.h"
#include "../../CCreatureHandler.h"
#include "../../spells/CSpellHandler.h" //for choosing random spells
@ -171,7 +172,7 @@ void TreasurePlacer::addAllPossibleObjects()
for(auto secondaryID : subObjects)
{
const auto * dwellingHandler = dynamic_cast<const CDwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(dwellingType, secondaryID).get());
const auto * dwellingHandler = dynamic_cast<const DwellingInstanceConstructor *>(VLC->objtypeh->getHandlerFor(dwellingType, secondaryID).get());
auto creatures = dwellingHandler->getProducedCreatures();
if(creatures.empty())
continue;

View File

@ -215,7 +215,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
{
NewObject no;
no.ID = Obj::BOAT;
no.subID = BoatId(EBoatId::BOAT_EVIL);
no.subID = BoatId(EBoatId::NECROPOLIS);
no.pos = CGBoat::translatePos(summonPos);
env->apply(&no);
}

View File

@ -127,6 +127,11 @@ int64_t CSpell::calculateDamage(const spells::Caster * caster) const
return caster->getSpellBonus(this, rawDamage, nullptr);
}
bool CSpell::hasSchool(ESpellSchool which) const
{
return school.count(which) && school.at(which);
}
bool CSpell::canBeCast(const CBattleInfoCallback * cb, spells::Mode mode, const spells::Caster * caster) const
{
//if caller do not interested in description just discard it and do not pollute even debug log

View File

@ -209,6 +209,8 @@ public:
int64_t calculateDamage(const spells::Caster * caster) const override;
bool hasSchool(ESpellSchool school) const override;
/**
* Calls cb for each school this spell belongs to
*

View File

@ -4421,7 +4421,7 @@ bool CGameHandler::hireHero(const CGObjectInstance *obj, ui8 hid, PlayerColor pl
//Create a new boat for hero
NewObject no;
no.ID = Obj::BOAT;
no.subID = BoatId(EBoatId::BOAT_NEUTRAL);
no.subID = BoatId(EBoatId::CASTLE);
no.pos = hr.tile + int3(1,0,0);
sendAndApply(&no);
@ -5627,12 +5627,6 @@ bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID)
complain("Cannot build boat in this shipyard!");
return false;
}
else if (obj->o->ID == Obj::TOWN
&& !static_cast<const CGTownInstance*>(obj)->hasBuilt(BuildingID::SHIPYARD))
{
complain("Cannot build boat in the town - no shipyard!");
return false;
}
TResources boatCost;
obj->getBoatCost(boatCost);

View File

@ -45,6 +45,7 @@ public:
MOCK_CONST_METHOD0(isOffensive, bool());
MOCK_CONST_METHOD0(isSpecial, bool());
MOCK_CONST_METHOD0(isMagical, bool());
MOCK_CONST_METHOD1(hasSchool, bool(ESpellSchool));
MOCK_CONST_METHOD1(forEachSchool, void(const SchoolCallback &));
MOCK_CONST_METHOD0(getCastSound, const std::string &());
MOCK_CONST_METHOD1(registerIcons, void(const IconRegistar &));