1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-08-13 19:54:17 +02:00

Implemented boat selection for town shipyards

This commit is contained in:
Ivan Savenko
2023-06-07 19:51:44 +03:00
parent 6aedb99117
commit 487f441f47
18 changed files with 101 additions and 43 deletions

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

@@ -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,7 +310,7 @@
"rmg" : {
"value" : 500,
"rarity" : 100
}
},
"visitText" : 127,
"spell" : {
"level" : 1

View File

@@ -146,21 +146,21 @@
}
},
"types" : {
"evil" : // Necropolis
"boatNecropolis" : // Necropolis
{
"index" : 0,
"actualAnimation" : "AB01_.def",
"overlayAnimation" : "ABM01_.def",
"flagAnimations" : ["ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"]
},
"good" : // Castle
"boatCastle" : // Castle
{
"index" : 1,
"actualAnimation" : "AB02_.def",
"overlayAnimation" : "ABM02_.def",
"flagAnimations" : ["ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"]
},
"neutral" : { // Fortress
"boatFortress" : { // Fortress
"index" : 2,
"actualAnimation" : "AB03_.def",
"overlayAnimation" : "ABM03_.def",

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

@@ -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

@@ -289,11 +289,25 @@ CGObjectInstance * BoatInstanceConstructor::create(std::shared_ptr<const ObjectT
return boat;
}
std::string BoatInstanceConstructor::getBoatAnimationName() const
{
return actualAnimation;
}
void BoatInstanceConstructor::configureObject(CGObjectInstance * object, CRandomGenerator & rng) const
{
}
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)
{
for(auto & element : input["modes"].Vector())

View File

@@ -110,7 +110,7 @@ public:
}
};
class BoatInstanceConstructor : public CDefaultObjectTypeHandler<CGBoat>
class DLL_LINKAGE BoatInstanceConstructor : public CDefaultObjectTypeHandler<CGBoat>
{
protected:
void initTypeData(const JsonNode & config) override;
@@ -127,6 +127,10 @@ protected:
public:
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) 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)
{

View File

@@ -953,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

View File

@@ -682,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

View File

@@ -1961,7 +1961,8 @@ void CGShipyard::serializeJsonOptions(JsonSerializeFormat& handler)
BoatId CGShipyard::getBoatType() const
{
return EBoatId::BOAT_GOOD;
// In H3, external shipyard will always create same boat as castle
return EBoatId::CASTLE;
}
void CCartographer::onHeroVisit( const CGHeroInstance * h ) const

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;