1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Heroes placed on water in map will be automatically given boat

This commit is contained in:
Ivan Savenko 2023-06-19 22:01:18 +03:00
parent 738ace99cb
commit 3913b8e98c
21 changed files with 77 additions and 52 deletions

View File

@ -9,6 +9,7 @@
"120px" : "TPCASCAS",
"130px" : "CRBKGCAS"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZCAS",
@ -148,7 +149,6 @@
"mageGuild" : 4,
"warMachine" : "ballista",
"moatAbility" : "castleMoat",
"boat" : "boatCastle",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -9,6 +9,7 @@
"120px" : "TPCASELE",
"130px" : "CRBKGELE"
},
"boat" : "boatNecropolis",
"puzzleMap" :
{
"prefix" : "PUZELE",
@ -153,7 +154,6 @@
"primaryResource" : "mercury",
"warMachine" : "ballista",
"moatAbility" : "castleMoat",
"boat" : "boatNecropolis",
"buildings" :
{

View File

@ -10,6 +10,7 @@
"120px" : "TPCASDUN",
"130px" : "CRBKGDUN"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZDUN",

View File

@ -9,6 +9,7 @@
"120px" : "TPCASFOR",
"130px" : "CRBKGFOR"
},
"boat" : "boatFortress",
"puzzleMap" :
{
"prefix" : "PUZFOR",
@ -148,7 +149,6 @@
"mageGuild" : 3,
"warMachine" : "firstAidTent",
"moatAbility" : "fortressMoat",
"boat" : "boatFortress",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -10,6 +10,7 @@
"120px" : "TPCASINF",
"130px" : "CRBKGINF"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZINF",

View File

@ -10,6 +10,7 @@
"120px" : "TPCASNEC",
"130px" : "CRBKGNEC"
},
"boat" : "boatNecropolis",
"puzzleMap" :
{
"prefix" : "PUZNEC",
@ -153,7 +154,6 @@
"mageGuild" : 5,
"warMachine" : "firstAidTent",
"moatAbility" : "necropolisMoat",
"boat" : "boatNecropolis",
// primaryResource not specified so town get both Wood and Ore for resource bonus
"buildings" :

View File

@ -9,6 +9,7 @@
"120px" : "TPCASRAM",
"130px" : "CRBKGRAM"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZRAM",

View File

@ -9,6 +9,7 @@
"120px" : "TPCASSTR",
"130px" : "CRBKGSTR"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZSTR",

View File

@ -9,6 +9,7 @@
"120px" : "TPCASTOW",
"130px" : "CRBKGTOW"
},
"boat" : "boatCastle",
"puzzleMap" :
{
"prefix" : "PUZTOW",

View File

@ -16,12 +16,14 @@ VCMI_LIB_NAMESPACE_BEGIN
class FactionID;
enum class EAlignment : uint8_t;
enum class EBoatId : int32_t;
class DLL_LINKAGE Faction : public EntityT<FactionID>, public INativeTerrainProvider
{
public:
virtual bool hasTown() const = 0;
virtual EAlignment getAlignment() const = 0;
virtual EBoatId getBoatType() const = 0;
};
VCMI_LIB_NAMESPACE_END

View File

@ -1250,6 +1250,31 @@ void CGameState::initHeroes()
map->allHeroes[hero->type->getIndex()] = hero;
}
// generate boats for all heroes on water
for(auto hero : map->heroesOnMap)
{
assert(map->isInTheMap(hero->visitablePos()));
const auto & tile = map->getTile(hero->visitablePos());
if (tile.terType->isWater())
{
auto handler = VLC->objtypeh->getHandlerFor(Obj::BOAT, hero->getBoatType().getNum());
CGBoat * boat = dynamic_cast<CGBoat*>(handler->create());
handler->configureObject(boat, gs->getRandomGenerator());
boat->ID = Obj::BOAT;
boat->subID = hero->getBoatType().getNum();
boat->pos = hero->pos;
boat->appearance = handler->getTemplates().front();
boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
map->objects.emplace_back(boat);
map->addBlockVisTiles(boat);
boat->hero = hero;
hero->boat = boat;
}
}
for(auto obj : map->objects) //prisons
{
if(obj && obj->ID == Obj::PRISON)
@ -2535,6 +2560,10 @@ void CGameState::buildBonusSystemTree()
}
// CStackInstance <-> CCreature, CStackInstance <-> CArmedInstance, CArtifactInstance <-> CArtifact
// are provided on initializing / deserializing
// NOTE: calling deserializationFix() might be more correct option, but might lead to side effects
for (auto hero : map->heroesOnMap)
hero->boatDeserializationFix();
}
void CGameState::deserializationFix()

View File

@ -174,6 +174,11 @@ EAlignment CFaction::getAlignment() const
return alignment;
}
EBoatId CFaction::getBoatType() const
{
return boatType.toEnum();
}
TerrainId CFaction::getNativeTerrain() const
{
return nativeTerrain;
@ -893,16 +898,6 @@ 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;
@ -1028,6 +1023,15 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
faction->creatureBg120 = source["creatureBackground"]["120px"].String();
faction->creatureBg130 = source["creatureBackground"]["130px"].String();
faction->boatType = EBoatId::NONE;
if (!source["boat"].isNull())
{
VLC->modh->identifiers.requestIdentifier("core:boat", source["boat"], [=](int32_t boatTypeID)
{
faction->boatType = BoatId(boatTypeID);
});
}
int alignment = vstd::find_pos(GameConstants::ALIGNMENT_NAMES, source["alignment"].String());
if (alignment == -1)
faction->alignment = EAlignment::NEUTRAL;
@ -1158,25 +1162,6 @@ 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

@ -204,6 +204,11 @@ public:
EAlignment alignment = EAlignment::NEUTRAL;
bool preferUndergroundPlacement = false;
/// Boat that will be used by town shipyard (if any)
/// and for placing heroes directly on boat (in map editor, water prisons & taverns)
BoatId boatType;
CTown * town = nullptr; //NOTE: can be null
std::string creatureBg120;
@ -226,6 +231,7 @@ public:
bool hasTown() const override;
TerrainId getNativeTerrain() const override;
EAlignment getAlignment() const override;
EBoatId getBoatType() const override;
void updateFrom(const JsonNode & data);
void serializeJson(JsonSerializeFormat & handler);
@ -236,6 +242,7 @@ public:
h & identifier;
h & index;
h & nativeTerrain;
h & boatType;
h & alignment;
h & town;
h & creatureBg120;
@ -282,8 +289,6 @@ public:
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;
@ -349,7 +354,6 @@ public:
h & mageLevel;
h & primaryRes;
h & warMachine;
h & shipyardBoat;
h & clientInfo;
h & moatAbility;
h & defaultTavernChance;

View File

@ -1282,7 +1282,7 @@ class BattleField : public BaseForID<BattleField, si32>
DLL_LINKAGE static BattleField fromString(const std::string & identifier);
};
enum class EBoatId
enum class EBoatId : int32_t
{
NONE = -1,
NECROPOLIS = 0,

View File

@ -323,7 +323,7 @@ void MetaString::jsonSerialize(JsonNode & dest) const
for (const auto & entry : localStrings )
{
JsonNode value;
value.Float() = static_cast<int>(entry.first) * 10000 + entry.second;
value.Integer() = static_cast<int>(entry.first) * 10000 + entry.second;
jsonLocalStrings.Vector().push_back(value);
}
@ -344,7 +344,7 @@ void MetaString::jsonSerialize(JsonNode & dest) const
for (const auto & entry : numbers )
{
JsonNode value;
value.Float() = entry;
value.Integer() = entry;
jsonNumbers.Vector().push_back(value);
}

View File

@ -452,9 +452,8 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
//Create a new boat for hero
NewObject no;
no.ID = Obj::BOAT;
no.subID = BoatId(EBoatId::CASTLE);
no.pos = CGBoat::translatePos(boatPos);
no.subID = getBoatType().getNum();
cb->sendAndApply(&no);
boatId = cb->getTopObj(boatPos)->id;
@ -953,8 +952,7 @@ si32 CGHeroInstance::getManaNewTurn() const
BoatId CGHeroInstance::getBoatType() const
{
// hero can only generate boat via "Summon Boat" spell which always create same boat as in Necropolis shipyard
return EBoatId::NECROPOLIS;
return BoatId(VLC->townh->getById(type->heroClass->faction)->getBoatType());
}
void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
@ -1105,6 +1103,13 @@ int CGHeroInstance::maxSpellLevel() const
void CGHeroInstance::deserializationFix()
{
artDeserializationFix(this);
boatDeserializationFix();
}
void CGHeroInstance::boatDeserializationFix()
{
if (boat)
attachTo(const_cast<CGBoat&>(*boat));
}
CBonusSystemNode * CGHeroInstance::whereShouldBeAttachedOnSiege(const bool isBattleOutsideTown) const

View File

@ -274,6 +274,7 @@ public:
void getCastDescription(const spells::Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const override;
void spendMana(ServerCallback * server, const int spellCost) const override;
void boatDeserializationFix();
void deserializationFix();
void initObj(CRandomGenerator & rand) override;

View File

@ -682,7 +682,7 @@ void CGTownInstance::clearArmy() const
BoatId CGTownInstance::getBoatType() const
{
return town->shipyardBoat;
return town->faction->boatType;
}
int CGTownInstance::getMarketEfficiency() const

View File

@ -1294,11 +1294,6 @@ CGBoat::CGBoat()
layer = EPathfindingLayer::EEPathfindingLayer::SAIL;
}
void CGBoat::initObj(CRandomGenerator & rand)
{
hero = nullptr;
}
int3 CGBoat::translatePos(const int3& pos, bool reverse /* = false */)
{
//pos - offset we want to place the boat at the map

View File

@ -359,7 +359,6 @@ public:
std::array<std::string, PlayerColor::PLAYER_LIMIT_I> flagAnimations;
CGBoat();
void initObj(CRandomGenerator & rand) override;
static int3 translatePos(const int3 &pos, bool reverse = false);
template <typename Handler> void serialize(Handler &h, const int version)

View File

@ -4428,7 +4428,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::CASTLE);
no.subID = nh->getBoatType().getNum();
no.pos = hr.tile + int3(1,0,0);
sendAndApply(&no);