mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-03 14:52:11 +02:00
Merge pull request #2235 from IvanSavenko/hota_h3m_support
Hota h3m support
This commit is contained in:
commit
69dc95c3c8
@ -188,7 +188,7 @@ void AIGateway::showShipyardDialog(const IShipyard * obj)
|
||||
|
||||
void AIGateway::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
|
||||
{
|
||||
LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
|
||||
LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf.toString());
|
||||
NET_EVENT_HANDLER;
|
||||
logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.getStr(), player, player.getStr(), (victoryLossCheckResult.victory() ? "won" : "lost"));
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../lib/mapObjectConstructors/CBankInstanceConstructor.h"
|
||||
#include "../../lib/mapObjects/CBank.h"
|
||||
#include "../../lib/mapObjects/CGCreature.h"
|
||||
#include "../../lib/mapObjects/CGDwelling.h"
|
||||
|
||||
FuzzyHelper * fh;
|
||||
|
@ -202,7 +202,7 @@ void VCAI::showShipyardDialog(const IShipyard * obj)
|
||||
|
||||
void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
|
||||
{
|
||||
LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
|
||||
LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf.toString());
|
||||
NET_EVENT_HANDLER;
|
||||
logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.getStr(), player, player.getStr(), (victoryLossCheckResult.victory() ? "won" : "lost"));
|
||||
if(player == playerID)
|
||||
|
@ -173,7 +173,16 @@
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Setup...",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team Alignments",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Road Types",
|
||||
|
||||
|
||||
// Custom victory conditions for H3 campaigns and HotA maps
|
||||
"vcmi.map.victoryCondition.daysPassed.toOthers" : "The enemy has managed to survive till this day. Victory is theirs!",
|
||||
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Congratulations! You have managed to survive. Victory is yours!",
|
||||
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "The enemy has defeated all of the monsters plaguing this land and claims victory!",
|
||||
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Congratulations! You have defeated all of the monsters plaguing this land and can claim victory!",
|
||||
"vcmi.map.victoryCondition.collectArtifacts.message" : "Acquire Three Artifacts",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Congratulations! All your enemies have been defeated and you have Angelic Alliance! Victory is yours!",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.message" : "Defeat All Enemies and create Angelic Alliance",
|
||||
|
||||
// few strings from WoG used by vcmi
|
||||
"vcmi.stackExperience.description" : "» S t a c k E x p e r i e n c e D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
|
||||
"vcmi.stackExperience.rank.0" : "Basic",
|
||||
|
@ -156,7 +156,16 @@
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Налаштувати...",
|
||||
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Розподіл команд",
|
||||
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Види доріг",
|
||||
|
||||
|
||||
// Custom victory conditions for H3 campaigns and HotA maps
|
||||
"vcmi.map.victoryCondition.daysPassed.toOthers" : "Ворогу вдалося вижити до сьогоднішнього дня. Він переміг!",
|
||||
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Вітаємо! Вам вдалося залишитися в живих. Перемога за вами!",
|
||||
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "Ворог переміг усіх звірів, що заполонили цю землю, і святкує перемогу!",
|
||||
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Вітаємо! Ви перемогли усіх звірів, що заполонили цю землю, і можете святкувати перемогу!",
|
||||
"vcmi.map.victoryCondition.collectArtifacts.message" : "Здобути три артефакти",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Вітаємо! Усі ваші вороги переможені, і ви маєте Альянс Ангелів! Перемога ваша!",
|
||||
"vcmi.map.victoryCondition.angelicAlliance.message" : "Перемогти всіх ворогів і створити Альянс Ангелів",
|
||||
|
||||
"core.bonus.ADDITIONAL_ATTACK.name" : "Подвійний удар",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description" : "Атакує двічі",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name" : "Додаткові відплати",
|
||||
|
@ -270,14 +270,14 @@ void CPlayerInterface::acceptTurn()
|
||||
auto daysWithoutCastle = optDaysWithoutCastle.value();
|
||||
if (daysWithoutCastle < 6)
|
||||
{
|
||||
text.addTxt(MetaString::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
|
||||
text.addReplacement(MetaString::COLOR, playerColor.getNum());
|
||||
text.addReplacement(7 - daysWithoutCastle);
|
||||
text.appendLocalString(EMetaText::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
|
||||
text.replaceLocalString(EMetaText::COLOR, playerColor.getNum());
|
||||
text.replaceNumber(7 - daysWithoutCastle);
|
||||
}
|
||||
else if (daysWithoutCastle == 6)
|
||||
{
|
||||
text.addTxt(MetaString::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
|
||||
text.addReplacement(MetaString::COLOR, playerColor.getNum());
|
||||
text.appendLocalString(EMetaText::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
|
||||
text.replaceLocalString(EMetaText::COLOR, playerColor.getNum());
|
||||
}
|
||||
|
||||
showInfoDialogAndWait(components, text);
|
||||
@ -1048,8 +1048,7 @@ void CPlayerInterface::showInfoDialogAndWait(std::vector<Component> & components
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
|
||||
std::string str;
|
||||
text.toString(str);
|
||||
std::string str = text.toString();
|
||||
|
||||
showInfoDialog(EInfoWindowMode::MODAL, str, components, 0);
|
||||
waitWhileDialog();
|
||||
@ -1586,9 +1585,9 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
|
||||
{
|
||||
if (victoryLossCheckResult.loss() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
|
||||
{
|
||||
std::string str = victoryLossCheckResult.messageToSelf;
|
||||
boost::algorithm::replace_first(str, "%s", CGI->generaltexth->capColors[player.getNum()]);
|
||||
showInfoDialog(str, std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
|
||||
MetaString message = victoryLossCheckResult.messageToSelf;
|
||||
message.appendLocalString(EMetaText::COLOR, player.getNum());
|
||||
showInfoDialog(message.toString(), std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -600,8 +600,7 @@ void ApplyFirstClientNetPackVisitor::visitGiveHero(GiveHero & pack)
|
||||
|
||||
void ApplyClientNetPackVisitor::visitInfoWindow(InfoWindow & pack)
|
||||
{
|
||||
std::string str;
|
||||
pack.text.toString(str);
|
||||
std::string str = pack.text.toString();
|
||||
|
||||
if(!callInterfaceIfPresent(cl, pack.player, &CGameInterface::showInfoDialog, pack.type, str, pack.components,(soundBase::soundID)pack.soundID))
|
||||
logNetwork->warn("We received InfoWindow for not our player...");
|
||||
@ -643,8 +642,7 @@ void ApplyClientNetPackVisitor::visitCommanderLevelUp(CommanderLevelUp & pack)
|
||||
|
||||
void ApplyClientNetPackVisitor::visitBlockingDialog(BlockingDialog & pack)
|
||||
{
|
||||
std::string str;
|
||||
pack.text.toString(str);
|
||||
std::string str = pack.text.toString();
|
||||
|
||||
if(!callOnlyThatInterface(cl, pack.player, &CGameInterface::showBlockingDialog, str, pack.components, pack.queryID, (soundBase::soundID)pack.soundID, pack.selection(), pack.cancel()))
|
||||
logNetwork->warn("We received YesNoDialog for not our player...");
|
||||
|
@ -304,6 +304,9 @@ void EventDispatcher::dispatchMouseMoved(const Point & position)
|
||||
EventReceiversList miCopy = motioninterested;
|
||||
for(auto & elem : miCopy)
|
||||
{
|
||||
if (!vstd::contains(motioninterested, elem))
|
||||
continue;
|
||||
|
||||
if(elem->receiveEvent(position, AEventsReceiver::HOVER))
|
||||
{
|
||||
(elem)->mouseMoved(position);
|
||||
|
@ -210,9 +210,9 @@ void InfoCard::changeSelection()
|
||||
iconsMapSizes->setFrame(mapInfo->getMapSizeIconId());
|
||||
const CMapHeader * header = mapInfo->mapHeader.get();
|
||||
iconsVictoryCondition->setFrame(header->victoryIconIndex);
|
||||
labelVictoryConditionText->setText(header->victoryMessage);
|
||||
labelVictoryConditionText->setText(header->victoryMessage.toString());
|
||||
iconsLossCondition->setFrame(header->defeatIconIndex);
|
||||
labelLossConditionText->setText(header->defeatMessage);
|
||||
labelLossConditionText->setText(header->defeatMessage.toString());
|
||||
flagbox->recreate();
|
||||
labelDifficulty->setText(CGI->generaltexth->arraytxt[142 + mapInfo->mapHeader->difficulty]);
|
||||
iconDifficulty->setSelected(SEL->getCurrentDifficulty());
|
||||
|
@ -54,7 +54,7 @@ bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::share
|
||||
return (a->version < b->version);
|
||||
break;
|
||||
case _loscon: //by loss conditions
|
||||
return (a->defeatMessage < b->defeatMessage);
|
||||
return (a->defeatIconIndex < b->defeatIconIndex);
|
||||
break;
|
||||
case _playerAm: //by player amount
|
||||
int playerAmntB, humenPlayersB, playerAmntA, humenPlayersA;
|
||||
@ -89,7 +89,7 @@ bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::share
|
||||
return (a->width < b->width);
|
||||
break;
|
||||
case _viccon: //by victory conditions
|
||||
return (a->victoryMessage < b->victoryMessage);
|
||||
return (a->victoryIconIndex < b->victoryIconIndex);
|
||||
break;
|
||||
case _name: //by name
|
||||
return boost::ilexicographical_compare(a->name, b->name);
|
||||
|
@ -145,11 +145,13 @@ CDefFile::CDefFile(std::string Name):
|
||||
palette[i].a = SDL_ALPHA_OPAQUE;
|
||||
}
|
||||
|
||||
// first color seems to be used unconditionally as 100% transparency
|
||||
// these colors seems to be used unconditionally
|
||||
palette[0] = targetPalette[0];
|
||||
palette[1] = targetPalette[1];
|
||||
palette[4] = targetPalette[4];
|
||||
|
||||
// rest of special colors are used only if their RGB values are close to H3
|
||||
for (uint32_t i = 1; i < 8; ++i)
|
||||
for (uint32_t i = 0; i < 8; ++i)
|
||||
{
|
||||
if (colorsSimilar(sourcePalette[i], palette[i]))
|
||||
palette[i] = targetPalette[i];
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
#include "../../lib/mapObjects/CQuest.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -167,12 +167,12 @@ void CQuestLog::recreateLabelList()
|
||||
if (auto seersHut = dynamic_cast<const CGSeerHut *>(quests[i].obj))
|
||||
{
|
||||
MetaString toSeer;
|
||||
toSeer << VLC->generaltexth->allTexts[347];
|
||||
toSeer.addReplacement(seersHut->seerName);
|
||||
text.addReplacement(toSeer.toString());
|
||||
toSeer.appendRawString(VLC->generaltexth->allTexts[347]);
|
||||
toSeer.replaceRawString(seersHut->seerName);
|
||||
text.replaceRawString(toSeer.toString());
|
||||
}
|
||||
else
|
||||
text.addReplacement(quests[i].obj->getObjectName()); //get name of the object
|
||||
text.replaceRawString(quests[i].obj->getObjectName()); //get name of the object
|
||||
}
|
||||
auto label = std::make_shared<CQuestLabel>(Rect(13, 195, 149,31), FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, text.toString());
|
||||
label->disable();
|
||||
|
@ -77,6 +77,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
|
||||
${MAIN_LIB_DIR}/mapObjects/CArmedInstance.cpp
|
||||
${MAIN_LIB_DIR}/mapObjects/CBank.cpp
|
||||
${MAIN_LIB_DIR}/mapObjects/CGCreature.cpp
|
||||
${MAIN_LIB_DIR}/mapObjects/CGDwelling.cpp
|
||||
${MAIN_LIB_DIR}/mapObjects/CGHeroInstance.cpp
|
||||
${MAIN_LIB_DIR}/mapObjects/CGMarket.cpp
|
||||
@ -243,6 +244,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/LoadProgress.cpp
|
||||
${MAIN_LIB_DIR}/LogicalExpression.cpp
|
||||
${MAIN_LIB_DIR}/NetPacksLib.cpp
|
||||
${MAIN_LIB_DIR}/MetaString.cpp
|
||||
${MAIN_LIB_DIR}/ObstacleHandler.cpp
|
||||
${MAIN_LIB_DIR}/StartInfo.cpp
|
||||
${MAIN_LIB_DIR}/ResourceSet.cpp
|
||||
@ -395,6 +397,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
|
||||
${MAIN_LIB_DIR}/mapObjects/CArmedInstance.h
|
||||
${MAIN_LIB_DIR}/mapObjects/CBank.h
|
||||
${MAIN_LIB_DIR}/mapObjects/CGCreature.h
|
||||
${MAIN_LIB_DIR}/mapObjects/CGDwelling.h
|
||||
${MAIN_LIB_DIR}/mapObjects/CGHeroInstance.h
|
||||
${MAIN_LIB_DIR}/mapObjects/CGMarket.h
|
||||
@ -572,6 +575,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
|
||||
${MAIN_LIB_DIR}/Languages.h
|
||||
${MAIN_LIB_DIR}/LoadProgress.h
|
||||
${MAIN_LIB_DIR}/LogicalExpression.h
|
||||
${MAIN_LIB_DIR}/MetaString.h
|
||||
${MAIN_LIB_DIR}/NetPacksBase.h
|
||||
${MAIN_LIB_DIR}/NetPacks.h
|
||||
${MAIN_LIB_DIR}/NetPacksLobby.h
|
||||
|
@ -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" :
|
||||
|
@ -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" :
|
||||
{
|
||||
|
@ -10,6 +10,7 @@
|
||||
"120px" : "TPCASDUN",
|
||||
"130px" : "CRBKGDUN"
|
||||
},
|
||||
"boat" : "boatCastle",
|
||||
"puzzleMap" :
|
||||
{
|
||||
"prefix" : "PUZDUN",
|
||||
|
@ -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" :
|
||||
|
@ -10,6 +10,7 @@
|
||||
"120px" : "TPCASINF",
|
||||
"130px" : "CRBKGINF"
|
||||
},
|
||||
"boat" : "boatCastle",
|
||||
"puzzleMap" :
|
||||
{
|
||||
"prefix" : "PUZINF",
|
||||
|
@ -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" :
|
||||
|
@ -9,6 +9,7 @@
|
||||
"120px" : "TPCASRAM",
|
||||
"130px" : "CRBKGRAM"
|
||||
},
|
||||
"boat" : "boatCastle",
|
||||
"puzzleMap" :
|
||||
{
|
||||
"prefix" : "PUZRAM",
|
||||
|
@ -9,6 +9,7 @@
|
||||
"120px" : "TPCASSTR",
|
||||
"130px" : "CRBKGSTR"
|
||||
},
|
||||
"boat" : "boatCastle",
|
||||
"puzzleMap" :
|
||||
{
|
||||
"prefix" : "PUZSTR",
|
||||
|
@ -9,6 +9,7 @@
|
||||
"120px" : "TPCASTOW",
|
||||
"130px" : "CRBKGTOW"
|
||||
},
|
||||
"boat" : "boatCastle",
|
||||
"puzzleMap" :
|
||||
{
|
||||
"prefix" : "PUZTOW",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -144,6 +144,11 @@
|
||||
"description" : ".def file with animation of this creature on adventure map",
|
||||
"format" : "defFile"
|
||||
},
|
||||
"mapMask" : {
|
||||
"type" : "array",
|
||||
"items" : { "type" : "string" },
|
||||
"description" : "Object mask that describes on which tiles object is visible/blocked/activatable"
|
||||
},
|
||||
"iconLarge" : {
|
||||
"type" : "string",
|
||||
"description" : "Large icon for this creature, used for example in town screen",
|
||||
|
@ -32,7 +32,7 @@
|
||||
"description" : "Json format for defining new faction (aka towns) in VCMI",
|
||||
"required" : [ "name", "alignment", "nativeTerrain", "creatureBackground" ],
|
||||
"dependencies" : {
|
||||
"town" : [ "puzzleMap" ]
|
||||
"town" : [ "puzzleMap", "boat" ]
|
||||
},
|
||||
"additionalProperties" : false,
|
||||
"properties" : {
|
||||
@ -49,6 +49,11 @@
|
||||
"type" : "string",
|
||||
"description" : "Native terrain for creatures. Creatures fighting on native terrain receive several bonuses"
|
||||
},
|
||||
"boat" : {
|
||||
"type" : "string",
|
||||
"description" : "Identifier of boat type that is produced by shipyard in town, if any"
|
||||
},
|
||||
|
||||
"preferUndergroundPlacement" : {
|
||||
"type" : "boolean",
|
||||
"description" : "Random map generator places player/cpu-owned towns underground if true is specified and on the ground otherwise. Parameter is unused for maps without underground. False by default."
|
||||
@ -124,10 +129,6 @@
|
||||
"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,
|
||||
|
@ -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
|
||||
|
@ -13,7 +13,7 @@
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class PlayerColor;
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
class ServerCallback;
|
||||
class CGHeroInstance;
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
|
||||
namespace battle
|
||||
{
|
||||
|
@ -709,16 +709,9 @@ void CArtHandler::initAllowedArtifactsList(const std::vector<bool> &allowed)
|
||||
majors.clear();
|
||||
relics.clear();
|
||||
|
||||
for (ArtifactID i=ArtifactID::SPELLBOOK; i<ArtifactID::ART_SELECTION; i.advance(1))
|
||||
for (ArtifactID i=ArtifactID::SPELLBOOK; i < ArtifactID(static_cast<si32>(objects.size())); i.advance(1))
|
||||
{
|
||||
//check artifacts allowed on a map
|
||||
//TODO: This line will be different when custom map format is implemented
|
||||
if (allowed[i] && legalArtifact(i))
|
||||
allowedArtifacts.push_back(objects[i]);
|
||||
}
|
||||
for(ArtifactID i = ArtifactID::ART_SELECTION; i < ArtifactID(static_cast<si32>(objects.size())); i.advance(1)) //try to allow all artifacts added by mods
|
||||
{
|
||||
if (legalArtifact(ArtifactID(i)))
|
||||
if (allowed[i] && legalArtifact(ArtifactID(i)))
|
||||
allowedArtifacts.push_back(objects[i]);
|
||||
//keep im mind that artifact can be worn by more than one type of bearer
|
||||
}
|
||||
|
@ -625,17 +625,25 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
|
||||
registerObject(scope, type_name, extraName.String(), cre->getIndex());
|
||||
}
|
||||
|
||||
JsonNode advMapFile = node["graphics"]["map"];
|
||||
JsonNode advMapMask = node["graphics"]["mapMask"];
|
||||
|
||||
VLC->modh->identifiers.requestIdentifier(scope, "object", "monster", [=](si32 index)
|
||||
{
|
||||
JsonNode conf;
|
||||
conf.setMeta(scope);
|
||||
|
||||
VLC->objtypeh->loadSubObject(cre->identifier, conf, Obj::MONSTER, cre->getId().num);
|
||||
if (!cre->advMapDef.empty())
|
||||
if (!advMapFile.isNull())
|
||||
{
|
||||
JsonNode templ;
|
||||
templ["animation"].String() = cre->advMapDef;
|
||||
templ["animation"] = advMapFile;
|
||||
if (!advMapMask.isNull())
|
||||
templ["mask"] = advMapMask;
|
||||
templ.setMeta(scope);
|
||||
|
||||
// if creature has custom advMapFile, reset any potentially imported H3M templates and use provided file instead
|
||||
VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->getId().num)->clearTemplates();
|
||||
VLC->objtypeh->getHandlerFor(Obj::MONSTER, cre->getId().num)->addTemplate(templ);
|
||||
}
|
||||
|
||||
@ -871,7 +879,6 @@ void CCreatureHandler::loadJsonAnimation(CCreature * cre, const JsonNode & graph
|
||||
cre->animation.attackClimaxFrame = static_cast<int>(missile["attackClimaxFrame"].Float());
|
||||
cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double> >();
|
||||
|
||||
cre->advMapDef = graphics["map"].String();
|
||||
cre->smallIconName = graphics["iconSmall"].String();
|
||||
cre->largeIconName = graphics["iconLarge"].String();
|
||||
}
|
||||
|
@ -58,7 +58,6 @@ public:
|
||||
std::set<CreatureID> upgrades; // IDs of creatures to which this creature can be upgraded
|
||||
|
||||
std::string animDefName; // creature animation used during battles
|
||||
std::string advMapDef; //for new creatures only, image for adventure map
|
||||
|
||||
si32 iconIndex = -1; // index of icon in files like twcrport, used in tests now.
|
||||
/// names of files with appropriate icons. Used only during loading
|
||||
@ -230,7 +229,6 @@ public:
|
||||
h & ammMax;
|
||||
h & level;
|
||||
h & animDefName;
|
||||
h & advMapDef;
|
||||
h & iconIndex;
|
||||
h & smallIconName;
|
||||
h & largeIconName;
|
||||
|
@ -70,234 +70,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void MetaString::getLocalString(const std::pair<ui8, ui32> & txt, std::string & dst) const
|
||||
{
|
||||
int type = txt.first;
|
||||
int ser = txt.second;
|
||||
|
||||
if(type == ART_NAMES)
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getNameTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == ART_DESCR)
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getDescriptionTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if (type == ART_EVNTS)
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
dst = art->getEventTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == CRE_PL_NAMES)
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
dst = cre->getNamePluralTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == CRE_SING_NAMES)
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
dst = cre->getNameSingularTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == MINE_NAMES)
|
||||
{
|
||||
dst = VLC->generaltexth->translate("core.minename", ser);
|
||||
}
|
||||
else if(type == MINE_EVNTS)
|
||||
{
|
||||
dst = VLC->generaltexth->translate("core.mineevnt", ser);
|
||||
}
|
||||
else if(type == SPELL_NAME)
|
||||
{
|
||||
const auto * spell = SpellID(ser).toSpell(VLC->spells());
|
||||
if(spell)
|
||||
dst = spell->getNameTranslated();
|
||||
else
|
||||
dst = "#!#";
|
||||
}
|
||||
else if(type == OBJ_NAMES)
|
||||
{
|
||||
dst = VLC->objtypeh->getObjectName(ser, 0);
|
||||
}
|
||||
else if(type == SEC_SKILL_NAME)
|
||||
{
|
||||
dst = VLC->skillh->getByIndex(ser)->getNameTranslated();
|
||||
}
|
||||
else
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case GENERAL_TXT:
|
||||
dst = VLC->generaltexth->translate("core.genrltxt", ser);
|
||||
break;
|
||||
case RES_NAMES:
|
||||
dst = VLC->generaltexth->translate("core.restypes", ser);
|
||||
break;
|
||||
case ARRAY_TXT:
|
||||
dst = VLC->generaltexth->translate("core.arraytxt", ser);
|
||||
break;
|
||||
case CREGENS:
|
||||
dst = VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR1, ser);
|
||||
break;
|
||||
case CREGENS4:
|
||||
dst = VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR4, ser);
|
||||
break;
|
||||
case ADVOB_TXT:
|
||||
dst = VLC->generaltexth->translate("core.advevent", ser);
|
||||
break;
|
||||
case COLOR:
|
||||
dst = VLC->generaltexth->translate("vcmi.capitalColors", ser);
|
||||
break;
|
||||
case JK_TXT:
|
||||
dst = VLC->generaltexth->translate("core.jktext", ser);
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Failed string substitution because type is %d", type);
|
||||
dst = "#@#";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DLL_LINKAGE void MetaString::toString(std::string &dst) const
|
||||
{
|
||||
size_t exSt = 0;
|
||||
size_t loSt = 0;
|
||||
size_t nums = 0;
|
||||
dst.clear();
|
||||
|
||||
for(const auto & elem : message)
|
||||
{//TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER
|
||||
switch(elem)
|
||||
{
|
||||
case TEXACT_STRING:
|
||||
dst += exactStrings[exSt++];
|
||||
break;
|
||||
case TLOCAL_STRING:
|
||||
{
|
||||
std::string hlp;
|
||||
getLocalString(localStrings[loSt++], hlp);
|
||||
dst += hlp;
|
||||
}
|
||||
break;
|
||||
case TNUMBER:
|
||||
dst += std::to_string(numbers[nums++]);
|
||||
break;
|
||||
case TREPLACE_ESTRING:
|
||||
boost::replace_first(dst, "%s", exactStrings[exSt++]);
|
||||
break;
|
||||
case TREPLACE_LSTRING:
|
||||
{
|
||||
std::string hlp;
|
||||
getLocalString(localStrings[loSt++], hlp);
|
||||
boost::replace_first(dst, "%s", hlp);
|
||||
}
|
||||
break;
|
||||
case TREPLACE_NUMBER:
|
||||
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
|
||||
break;
|
||||
case TREPLACE_PLUSNUMBER:
|
||||
boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("MetaString processing error! Received message of type %d", int(elem));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::string MetaString::toString() const
|
||||
{
|
||||
std::string ret;
|
||||
toString(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::string MetaString::buildList () const
|
||||
///used to handle loot from creature bank
|
||||
{
|
||||
|
||||
size_t exSt = 0;
|
||||
size_t loSt = 0;
|
||||
size_t nums = 0;
|
||||
std::string lista;
|
||||
for (int i = 0; i < message.size(); ++i)
|
||||
{
|
||||
if (i > 0 && (message[i] == TEXACT_STRING || message[i] == TLOCAL_STRING))
|
||||
{
|
||||
if (exSt == exactStrings.size() - 1)
|
||||
lista += VLC->generaltexth->allTexts[141]; //" and "
|
||||
else
|
||||
lista += ", ";
|
||||
}
|
||||
switch (message[i])
|
||||
{
|
||||
case TEXACT_STRING:
|
||||
lista += exactStrings[exSt++];
|
||||
break;
|
||||
case TLOCAL_STRING:
|
||||
{
|
||||
std::string hlp;
|
||||
getLocalString (localStrings[loSt++], hlp);
|
||||
lista += hlp;
|
||||
}
|
||||
break;
|
||||
case TNUMBER:
|
||||
lista += std::to_string(numbers[nums++]);
|
||||
break;
|
||||
case TREPLACE_ESTRING:
|
||||
lista.replace (lista.find("%s"), 2, exactStrings[exSt++]);
|
||||
break;
|
||||
case TREPLACE_LSTRING:
|
||||
{
|
||||
std::string hlp;
|
||||
getLocalString (localStrings[loSt++], hlp);
|
||||
lista.replace (lista.find("%s"), 2, hlp);
|
||||
}
|
||||
break;
|
||||
case TREPLACE_NUMBER:
|
||||
lista.replace (lista.find("%d"), 2, std::to_string(numbers[nums++]));
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("MetaString processing error! Received message of type %d",int(message[i]));
|
||||
}
|
||||
|
||||
}
|
||||
return lista;
|
||||
}
|
||||
|
||||
void MetaString::addCreReplacement(const CreatureID & id, TQuantity count) //adds sing or plural name;
|
||||
{
|
||||
if (!count)
|
||||
addReplacement (CRE_PL_NAMES, id); //no creatures - just empty name (eg. defeat Angels)
|
||||
else if (count == 1)
|
||||
addReplacement (CRE_SING_NAMES, id);
|
||||
else
|
||||
addReplacement (CRE_PL_NAMES, id);
|
||||
}
|
||||
|
||||
void MetaString::addReplacement(const CStackBasicDescriptor & stack)
|
||||
{
|
||||
assert(stack.type); //valid type
|
||||
addCreReplacement(stack.type->getId(), stack.count);
|
||||
}
|
||||
|
||||
static CGObjectInstance * createObject(const Obj & id, int subid, const int3 & pos, const PlayerColor & owner)
|
||||
{
|
||||
CGObjectInstance * nobj;
|
||||
@ -651,7 +423,7 @@ void CGameState::randomizeObject(CGObjectInstance *cur)
|
||||
std::pair<Obj,int> ran = pickObject(cur);
|
||||
if(ran.first == Obj::NO_OBJ || ran.second<0) //this is not a random object, or we couldn't find anything
|
||||
{
|
||||
if(cur->ID==Obj::TOWN)
|
||||
if(cur->ID==Obj::TOWN || cur->ID==Obj::MONSTER)
|
||||
cur->setType(cur->ID, cur->subID); // update def, if necessary
|
||||
}
|
||||
else if(ran.first==Obj::HERO)//special code for hero
|
||||
@ -1480,6 +1252,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)
|
||||
@ -2265,10 +2062,10 @@ bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
|
||||
|
||||
EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(const PlayerColor & player) const
|
||||
{
|
||||
const std::string & messageWonSelf = VLC->generaltexth->allTexts[659];
|
||||
const std::string & messageWonOther = VLC->generaltexth->allTexts[5];
|
||||
const std::string & messageLostSelf = VLC->generaltexth->allTexts[7];
|
||||
const std::string & messageLostOther = VLC->generaltexth->allTexts[8];
|
||||
const MetaString messageWonSelf = MetaString::createFromTextID("core.genrltxt.659");
|
||||
const MetaString messageWonOther = MetaString::createFromTextID("core.genrltxt.5");
|
||||
const MetaString messageLostSelf = MetaString::createFromTextID("core.genrltxt.7");
|
||||
const MetaString messageLostOther = MetaString::createFromTextID("core.genrltxt.8");
|
||||
|
||||
auto evaluateEvent = [=](const EventCondition & condition)
|
||||
{
|
||||
@ -2765,6 +2562,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()
|
||||
|
@ -42,7 +42,7 @@ class CCreature;
|
||||
class CMap;
|
||||
struct StartInfo;
|
||||
struct SetObjectProperty;
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
struct CPack;
|
||||
class CSpell;
|
||||
struct TerrainTile;
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CCreatureSet.h"
|
||||
#include "MetaString.h"
|
||||
#include "int3.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
@ -101,12 +102,12 @@ struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy
|
||||
class DLL_LINKAGE EVictoryLossCheckResult
|
||||
{
|
||||
public:
|
||||
static EVictoryLossCheckResult victory(std::string toSelf, std::string toOthers)
|
||||
static EVictoryLossCheckResult victory(MetaString toSelf, MetaString toOthers)
|
||||
{
|
||||
return EVictoryLossCheckResult(VICTORY, toSelf, toOthers);
|
||||
}
|
||||
|
||||
static EVictoryLossCheckResult defeat(std::string toSelf, std::string toOthers)
|
||||
static EVictoryLossCheckResult defeat(MetaString toSelf, MetaString toOthers)
|
||||
{
|
||||
return EVictoryLossCheckResult(DEFEAT, toSelf, toOthers);
|
||||
}
|
||||
@ -140,8 +141,8 @@ public:
|
||||
return EVictoryLossCheckResult(-intValue, messageToOthers, messageToSelf);
|
||||
}
|
||||
|
||||
std::string messageToSelf;
|
||||
std::string messageToOthers;
|
||||
MetaString messageToSelf;
|
||||
MetaString messageToOthers;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -157,7 +158,7 @@ private:
|
||||
VICTORY= +1
|
||||
};
|
||||
|
||||
EVictoryLossCheckResult(si32 intValue, std::string toSelf, std::string toOthers):
|
||||
EVictoryLossCheckResult(si32 intValue, MetaString toSelf, MetaString toOthers):
|
||||
messageToSelf(toSelf),
|
||||
messageToOthers(toOthers),
|
||||
intValue(intValue)
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -75,7 +75,7 @@ namespace GameConstants
|
||||
|
||||
constexpr ui32 BASE_MOVEMENT_COST = 100; //default cost for non-diagonal movement
|
||||
|
||||
constexpr int HERO_PORTRAIT_SHIFT = 30;// 2 special frames + some extra portraits
|
||||
constexpr int HERO_PORTRAIT_SHIFT = 9;// 2 special frames + 7 extra portraits
|
||||
|
||||
constexpr std::array<int, 11> POSSIBLE_TURNTIME = {1, 2, 4, 6, 8, 10, 15, 20, 25, 30, 0};
|
||||
}
|
||||
@ -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,
|
||||
|
@ -20,7 +20,7 @@ struct SetMovePoints;
|
||||
struct GiveBonus;
|
||||
struct BlockingDialog;
|
||||
struct TeleportDialog;
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
struct StackLocation;
|
||||
struct ArtifactLocation;
|
||||
class CCreatureSet;
|
||||
|
@ -49,7 +49,7 @@ struct BattleTriggerEffect;
|
||||
struct CObstacleInstance;
|
||||
struct CPackForServer;
|
||||
class EVictoryLossCheckResult;
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
class ObstacleChanges;
|
||||
class UnitChanges;
|
||||
|
||||
|
388
lib/MetaString.cpp
Normal file
388
lib/MetaString.cpp
Normal file
@ -0,0 +1,388 @@
|
||||
/*
|
||||
* MetaString.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 "MetaString.h"
|
||||
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CCreatureSet.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "GameConstants.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
MetaString MetaString::createFromRawString(const std::string & value)
|
||||
{
|
||||
MetaString result;
|
||||
result.appendRawString(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
MetaString MetaString::createFromTextID(const std::string & value)
|
||||
{
|
||||
MetaString result;
|
||||
result.appendTextID(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
void MetaString::appendLocalString(EMetaText type, ui32 serial)
|
||||
{
|
||||
message.push_back(EMessage::APPEND_LOCAL_STRING);
|
||||
localStrings.emplace_back(type, serial);
|
||||
}
|
||||
|
||||
void MetaString::appendRawString(const std::string & value)
|
||||
{
|
||||
message.push_back(EMessage::APPEND_RAW_STRING);
|
||||
exactStrings.push_back(value);
|
||||
}
|
||||
|
||||
void MetaString::appendTextID(const std::string & value)
|
||||
{
|
||||
message.push_back(EMessage::APPEND_TEXTID_STRING);
|
||||
stringsTextID.push_back(value);
|
||||
}
|
||||
|
||||
void MetaString::appendNumber(int64_t value)
|
||||
{
|
||||
message.push_back(EMessage::APPEND_NUMBER);
|
||||
numbers.push_back(value);
|
||||
}
|
||||
|
||||
void MetaString::replaceLocalString(EMetaText type, ui32 serial)
|
||||
{
|
||||
message.push_back(EMessage::REPLACE_LOCAL_STRING);
|
||||
localStrings.emplace_back(type, serial);
|
||||
}
|
||||
|
||||
void MetaString::replaceRawString(const std::string &txt)
|
||||
{
|
||||
message.push_back(EMessage::REPLACE_RAW_STRING);
|
||||
exactStrings.push_back(txt);
|
||||
}
|
||||
|
||||
void MetaString::replaceTextID(const std::string & value)
|
||||
{
|
||||
message.push_back(EMessage::REPLACE_TEXTID_STRING);
|
||||
stringsTextID.push_back(value);
|
||||
}
|
||||
|
||||
void MetaString::replaceNumber(int64_t txt)
|
||||
{
|
||||
message.push_back(EMessage::REPLACE_NUMBER);
|
||||
numbers.push_back(txt);
|
||||
}
|
||||
|
||||
void MetaString::replacePositiveNumber(int64_t txt)
|
||||
{
|
||||
message.push_back(EMessage::REPLACE_POSITIVE_NUMBER);
|
||||
numbers.push_back(txt);
|
||||
}
|
||||
|
||||
void MetaString::clear()
|
||||
{
|
||||
exactStrings.clear();
|
||||
localStrings.clear();
|
||||
stringsTextID.clear();
|
||||
message.clear();
|
||||
numbers.clear();
|
||||
}
|
||||
|
||||
bool MetaString::empty() const
|
||||
{
|
||||
return message.empty();
|
||||
}
|
||||
|
||||
std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) const
|
||||
{
|
||||
EMetaText type = txt.first;
|
||||
int ser = txt.second;
|
||||
|
||||
switch(type)
|
||||
{
|
||||
case EMetaText::ART_NAMES:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
return art->getNameTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::ART_DESCR:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
return art->getDescriptionTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::ART_EVNTS:
|
||||
{
|
||||
const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
|
||||
if(art)
|
||||
return art->getEventTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::CRE_PL_NAMES:
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
return cre->getNamePluralTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::CRE_SING_NAMES:
|
||||
{
|
||||
const auto * cre = CreatureID(ser).toCreature(VLC->creatures());
|
||||
if(cre)
|
||||
return cre->getNameSingularTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::MINE_NAMES:
|
||||
{
|
||||
return VLC->generaltexth->translate("core.minename", ser);
|
||||
}
|
||||
case EMetaText::MINE_EVNTS:
|
||||
{
|
||||
return VLC->generaltexth->translate("core.mineevnt", ser);
|
||||
}
|
||||
case EMetaText::SPELL_NAME:
|
||||
{
|
||||
const auto * spell = SpellID(ser).toSpell(VLC->spells());
|
||||
if(spell)
|
||||
return spell->getNameTranslated();
|
||||
return "#!#";
|
||||
}
|
||||
case EMetaText::OBJ_NAMES:
|
||||
return VLC->objtypeh->getObjectName(ser, 0);
|
||||
case EMetaText::SEC_SKILL_NAME:
|
||||
return VLC->skillh->getByIndex(ser)->getNameTranslated();
|
||||
case EMetaText::GENERAL_TXT:
|
||||
return VLC->generaltexth->translate("core.genrltxt", ser);
|
||||
case EMetaText::RES_NAMES:
|
||||
return VLC->generaltexth->translate("core.restypes", ser);
|
||||
case EMetaText::ARRAY_TXT:
|
||||
return VLC->generaltexth->translate("core.arraytxt", ser);
|
||||
case EMetaText::CREGENS:
|
||||
return VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR1, ser);
|
||||
case EMetaText::CREGENS4:
|
||||
return VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR4, ser);
|
||||
case EMetaText::ADVOB_TXT:
|
||||
return VLC->generaltexth->translate("core.advevent", ser);
|
||||
case EMetaText::COLOR:
|
||||
return VLC->generaltexth->translate("vcmi.capitalColors", ser);
|
||||
case EMetaText::JK_TXT:
|
||||
return VLC->generaltexth->translate("core.jktext", ser);
|
||||
default:
|
||||
logGlobal->error("Failed string substitution because type is %d", static_cast<int>(type));
|
||||
return "#@#";
|
||||
}
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::string MetaString::toString() const
|
||||
{
|
||||
std::string dst;
|
||||
|
||||
size_t exSt = 0;
|
||||
size_t loSt = 0;
|
||||
size_t nums = 0;
|
||||
size_t textID = 0;
|
||||
dst.clear();
|
||||
|
||||
for(const auto & elem : message)
|
||||
{
|
||||
switch(elem)
|
||||
{
|
||||
case EMessage::APPEND_RAW_STRING:
|
||||
dst += exactStrings[exSt++];
|
||||
break;
|
||||
case EMessage::APPEND_LOCAL_STRING:
|
||||
dst += getLocalString(localStrings[loSt++]);
|
||||
break;
|
||||
case EMessage::APPEND_TEXTID_STRING:
|
||||
dst += VLC->generaltexth->translate(stringsTextID[textID++]);
|
||||
break;
|
||||
case EMessage::APPEND_NUMBER:
|
||||
dst += std::to_string(numbers[nums++]);
|
||||
break;
|
||||
case EMessage::REPLACE_RAW_STRING:
|
||||
boost::replace_first(dst, "%s", exactStrings[exSt++]);
|
||||
break;
|
||||
case EMessage::REPLACE_LOCAL_STRING:
|
||||
boost::replace_first(dst, "%s", getLocalString(localStrings[loSt++]));
|
||||
break;
|
||||
case EMessage::REPLACE_TEXTID_STRING:
|
||||
boost::replace_first(dst, "%s", VLC->generaltexth->translate(stringsTextID[textID++]));
|
||||
break;
|
||||
case EMessage::REPLACE_NUMBER:
|
||||
boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
|
||||
break;
|
||||
case EMessage::REPLACE_POSITIVE_NUMBER:
|
||||
boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::string MetaString::buildList() const
|
||||
{
|
||||
size_t exSt = 0;
|
||||
size_t loSt = 0;
|
||||
size_t nums = 0;
|
||||
size_t textID = 0;
|
||||
std::string lista;
|
||||
for(int i = 0; i < message.size(); ++i)
|
||||
{
|
||||
if(i > 0 && (message[i] == EMessage::APPEND_RAW_STRING || message[i] == EMessage::APPEND_LOCAL_STRING))
|
||||
{
|
||||
if(exSt == exactStrings.size() - 1)
|
||||
lista += VLC->generaltexth->allTexts[141]; //" and "
|
||||
else
|
||||
lista += ", ";
|
||||
}
|
||||
switch(message[i])
|
||||
{
|
||||
case EMessage::APPEND_RAW_STRING:
|
||||
lista += exactStrings[exSt++];
|
||||
break;
|
||||
case EMessage::APPEND_LOCAL_STRING:
|
||||
lista += getLocalString(localStrings[loSt++]);
|
||||
break;
|
||||
case EMessage::APPEND_TEXTID_STRING:
|
||||
lista += VLC->generaltexth->translate(stringsTextID[textID++]);
|
||||
break;
|
||||
case EMessage::APPEND_NUMBER:
|
||||
lista += std::to_string(numbers[nums++]);
|
||||
break;
|
||||
case EMessage::REPLACE_RAW_STRING:
|
||||
lista.replace(lista.find("%s"), 2, exactStrings[exSt++]);
|
||||
break;
|
||||
case EMessage::REPLACE_LOCAL_STRING:
|
||||
lista.replace(lista.find("%s"), 2, getLocalString(localStrings[loSt++]));
|
||||
break;
|
||||
case EMessage::REPLACE_TEXTID_STRING:
|
||||
lista.replace(lista.find("%s"), 2, VLC->generaltexth->translate(stringsTextID[textID++]));
|
||||
break;
|
||||
case EMessage::REPLACE_NUMBER:
|
||||
lista.replace(lista.find("%d"), 2, std::to_string(numbers[nums++]));
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("MetaString processing error! Received message of type %d", int(message[i]));
|
||||
}
|
||||
}
|
||||
return lista;
|
||||
}
|
||||
|
||||
void MetaString::replaceCreatureName(const CreatureID & id, TQuantity count) //adds sing or plural name;
|
||||
{
|
||||
if (count == 1)
|
||||
replaceLocalString (EMetaText::CRE_SING_NAMES, id);
|
||||
else
|
||||
replaceLocalString (EMetaText::CRE_PL_NAMES, id);
|
||||
}
|
||||
|
||||
void MetaString::replaceCreatureName(const CStackBasicDescriptor & stack)
|
||||
{
|
||||
assert(stack.type); //valid type
|
||||
replaceCreatureName(stack.type->getId(), stack.count);
|
||||
}
|
||||
|
||||
bool MetaString::operator == (const MetaString & other) const
|
||||
{
|
||||
return message == other.message && localStrings == other.localStrings && exactStrings == other.exactStrings && stringsTextID == other.stringsTextID && numbers == other.numbers;
|
||||
}
|
||||
|
||||
void MetaString::jsonSerialize(JsonNode & dest) const
|
||||
{
|
||||
JsonNode jsonMessage;
|
||||
JsonNode jsonLocalStrings;
|
||||
JsonNode jsonExactStrings;
|
||||
JsonNode jsonStringsTextID;
|
||||
JsonNode jsonNumbers;
|
||||
|
||||
for (const auto & entry : message )
|
||||
{
|
||||
JsonNode value;
|
||||
value.Float() = static_cast<int>(entry);
|
||||
jsonMessage.Vector().push_back(value);
|
||||
}
|
||||
|
||||
for (const auto & entry : localStrings )
|
||||
{
|
||||
JsonNode value;
|
||||
value.Integer() = static_cast<int>(entry.first) * 10000 + entry.second;
|
||||
jsonLocalStrings.Vector().push_back(value);
|
||||
}
|
||||
|
||||
for (const auto & entry : exactStrings )
|
||||
{
|
||||
JsonNode value;
|
||||
value.String() = entry;
|
||||
jsonExactStrings.Vector().push_back(value);
|
||||
}
|
||||
|
||||
for (const auto & entry : stringsTextID )
|
||||
{
|
||||
JsonNode value;
|
||||
value.String() = entry;
|
||||
jsonStringsTextID.Vector().push_back(value);
|
||||
}
|
||||
|
||||
for (const auto & entry : numbers )
|
||||
{
|
||||
JsonNode value;
|
||||
value.Integer() = entry;
|
||||
jsonNumbers.Vector().push_back(value);
|
||||
}
|
||||
|
||||
dest["message"] = jsonMessage;
|
||||
dest["localStrings"] = jsonLocalStrings;
|
||||
dest["exactStrings"] = jsonExactStrings;
|
||||
dest["stringsTextID"] = jsonStringsTextID;
|
||||
dest["numbers"] = jsonNumbers;
|
||||
}
|
||||
|
||||
void MetaString::jsonDeserialize(const JsonNode & source)
|
||||
{
|
||||
clear();
|
||||
|
||||
if (source.isString())
|
||||
{
|
||||
// compatibility with fields that were converted from string to MetaString
|
||||
if(boost::starts_with(source.String(), "core.") || boost::starts_with(source.String(), "vcmi."))
|
||||
appendTextID(source.String());
|
||||
else
|
||||
appendRawString(source.String());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto & entry : source["message"].Vector() )
|
||||
message.push_back(static_cast<EMessage>(entry.Integer()));
|
||||
|
||||
for (const auto & entry : source["localStrings"].Vector() )
|
||||
localStrings.push_back({ static_cast<EMetaText>(entry.Integer() / 10000), entry.Integer() % 10000 });
|
||||
|
||||
for (const auto & entry : source["exactStrings"].Vector() )
|
||||
exactStrings.push_back(entry.String());
|
||||
|
||||
for (const auto & entry : source["stringsTextID"].Vector() )
|
||||
stringsTextID.push_back(entry.String());
|
||||
|
||||
for (const auto & entry : source["numbers"].Vector() )
|
||||
numbers.push_back(entry.Integer());
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
127
lib/MetaString.h
Normal file
127
lib/MetaString.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* MetaString.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class JsonNode;
|
||||
class CreatureID;
|
||||
class CStackBasicDescriptor;
|
||||
using TQuantity = si32;
|
||||
|
||||
/// Strings classes that can be used as replacement in MetaString
|
||||
enum class EMetaText : uint8_t
|
||||
{
|
||||
GENERAL_TXT = 1,
|
||||
OBJ_NAMES,
|
||||
RES_NAMES,
|
||||
ART_NAMES,
|
||||
ARRAY_TXT,
|
||||
CRE_PL_NAMES,
|
||||
CREGENS,
|
||||
MINE_NAMES,
|
||||
MINE_EVNTS,
|
||||
ADVOB_TXT,
|
||||
ART_EVNTS,
|
||||
SPELL_NAME,
|
||||
SEC_SKILL_NAME,
|
||||
CRE_SING_NAMES,
|
||||
CREGENS4,
|
||||
COLOR,
|
||||
ART_DESCR,
|
||||
JK_TXT
|
||||
};
|
||||
|
||||
/// Class for string formatting tools that also support transfer over network with localization using language of local install
|
||||
/// Can be used to compose resulting text from multiple line segments and with placeholder replacement
|
||||
class DLL_LINKAGE MetaString
|
||||
{
|
||||
private:
|
||||
enum class EMessage : uint8_t
|
||||
{
|
||||
APPEND_RAW_STRING,
|
||||
APPEND_LOCAL_STRING,
|
||||
APPEND_TEXTID_STRING,
|
||||
APPEND_NUMBER,
|
||||
REPLACE_RAW_STRING,
|
||||
REPLACE_LOCAL_STRING,
|
||||
REPLACE_TEXTID_STRING,
|
||||
REPLACE_NUMBER,
|
||||
REPLACE_POSITIVE_NUMBER
|
||||
};
|
||||
|
||||
std::vector<EMessage> message;
|
||||
|
||||
std::vector<std::pair<EMetaText,ui32> > localStrings;
|
||||
std::vector<std::string> exactStrings;
|
||||
std::vector<std::string> stringsTextID;
|
||||
std::vector<int64_t> numbers;
|
||||
|
||||
std::string getLocalString(const std::pair<EMetaText, ui32> & txt) const;
|
||||
|
||||
public:
|
||||
/// Creates MetaString and appends provided raw string to it
|
||||
static MetaString createFromRawString(const std::string & value);
|
||||
/// Creates MetaString and appends provided text ID string to it
|
||||
static MetaString createFromTextID(const std::string & value);
|
||||
|
||||
/// Appends local string to resulting string
|
||||
void appendLocalString(EMetaText type, ui32 serial);
|
||||
/// Appends raw string, without translation to resulting string
|
||||
void appendRawString(const std::string & value);
|
||||
/// Appends text ID that will be translated in output
|
||||
void appendTextID(const std::string & value);
|
||||
/// Appends specified number to resulting string
|
||||
void appendNumber(int64_t value);
|
||||
|
||||
/// Replaces first '%s' placeholder in string with specified local string
|
||||
void replaceLocalString(EMetaText type, ui32 serial);
|
||||
/// Replaces first '%s' placeholder in string with specified fixed, untranslated string
|
||||
void replaceRawString(const std::string & txt);
|
||||
/// Repalces first '%s' placeholder with string ID that will be translated in output
|
||||
void replaceTextID(const std::string & value);
|
||||
/// Replaces first '%d' placeholder in string with specified number
|
||||
void replaceNumber(int64_t txt);
|
||||
/// Replaces first '%+d' placeholder in string with specified number using '+' sign as prefix
|
||||
void replacePositiveNumber(int64_t txt);
|
||||
|
||||
/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
|
||||
void replaceCreatureName(const CreatureID & id, TQuantity count);
|
||||
/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
|
||||
void replaceCreatureName(const CStackBasicDescriptor & stack);
|
||||
|
||||
/// erases any existing content in the string
|
||||
void clear();
|
||||
|
||||
///used to handle loot from creature bank
|
||||
std::string buildList() const;
|
||||
|
||||
/// Convert all stored values into a single, user-readable string
|
||||
std::string toString() const;
|
||||
|
||||
/// Returns true if current string is empty
|
||||
bool empty() const;
|
||||
|
||||
bool operator == (const MetaString & other) const;
|
||||
|
||||
void jsonSerialize(JsonNode & dest) const;
|
||||
void jsonDeserialize(const JsonNode & dest);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & exactStrings;
|
||||
h & localStrings;
|
||||
h & stringsTextID;
|
||||
h & message;
|
||||
h & numbers;
|
||||
}
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -19,6 +19,7 @@
|
||||
#include "CGameStateFwd.h"
|
||||
#include "mapping/CMapDefines.h"
|
||||
#include "battle/CObstacleInstance.h"
|
||||
#include "MetaString.h"
|
||||
|
||||
#include "spells/ViewSpellInt.h"
|
||||
|
||||
|
@ -115,86 +115,6 @@ protected:
|
||||
virtual void visitBasic(ICPackVisitor & cpackVisitor) override;
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE MetaString
|
||||
{
|
||||
private:
|
||||
enum EMessage {TEXACT_STRING, TLOCAL_STRING, TNUMBER, TREPLACE_ESTRING, TREPLACE_LSTRING, TREPLACE_NUMBER, TREPLACE_PLUSNUMBER};
|
||||
public:
|
||||
enum {GENERAL_TXT=1, OBJ_NAMES, RES_NAMES, ART_NAMES, ARRAY_TXT, CRE_PL_NAMES, CREGENS, MINE_NAMES,
|
||||
MINE_EVNTS, ADVOB_TXT, ART_EVNTS, SPELL_NAME, SEC_SKILL_NAME, CRE_SING_NAMES, CREGENS4, COLOR, ART_DESCR, JK_TXT};
|
||||
|
||||
std::vector<ui8> message; //vector of EMessage
|
||||
|
||||
std::vector<std::pair<ui8,ui32> > localStrings;
|
||||
std::vector<std::string> exactStrings;
|
||||
std::vector<int64_t> numbers;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & exactStrings;
|
||||
h & localStrings;
|
||||
h & message;
|
||||
h & numbers;
|
||||
}
|
||||
void addTxt(ui8 type, ui32 serial)
|
||||
{
|
||||
message.push_back(TLOCAL_STRING);
|
||||
localStrings.emplace_back(type, serial);
|
||||
}
|
||||
MetaString& operator<<(const std::pair<ui8,ui32> &txt)
|
||||
{
|
||||
message.push_back(TLOCAL_STRING);
|
||||
localStrings.push_back(txt);
|
||||
return *this;
|
||||
}
|
||||
MetaString& operator<<(const std::string &txt)
|
||||
{
|
||||
message.push_back(TEXACT_STRING);
|
||||
exactStrings.push_back(txt);
|
||||
return *this;
|
||||
}
|
||||
MetaString& operator<<(int64_t txt)
|
||||
{
|
||||
message.push_back(TNUMBER);
|
||||
numbers.push_back(txt);
|
||||
return *this;
|
||||
}
|
||||
void addReplacement(ui8 type, ui32 serial)
|
||||
{
|
||||
message.push_back(TREPLACE_LSTRING);
|
||||
localStrings.emplace_back(type, serial);
|
||||
}
|
||||
void addReplacement(const std::string &txt)
|
||||
{
|
||||
message.push_back(TREPLACE_ESTRING);
|
||||
exactStrings.push_back(txt);
|
||||
}
|
||||
void addReplacement(int64_t txt)
|
||||
{
|
||||
message.push_back(TREPLACE_NUMBER);
|
||||
numbers.push_back(txt);
|
||||
}
|
||||
void addReplacement2(int64_t txt)
|
||||
{
|
||||
message.push_back(TREPLACE_PLUSNUMBER);
|
||||
numbers.push_back(txt);
|
||||
}
|
||||
void addCreReplacement(const CreatureID & id, TQuantity count); //adds sing or plural name;
|
||||
void addReplacement(const CStackBasicDescriptor &stack); //adds sing or plural name;
|
||||
std::string buildList () const;
|
||||
void clear()
|
||||
{
|
||||
exactStrings.clear();
|
||||
localStrings.clear();
|
||||
message.clear();
|
||||
numbers.clear();
|
||||
}
|
||||
void toString(std::string &dst) const;
|
||||
std::string toString() const;
|
||||
void getLocalString(const std::pair<ui8, ui32> & txt, std::string & dst) const;
|
||||
|
||||
};
|
||||
|
||||
struct Component
|
||||
{
|
||||
enum class EComponentType : uint8_t
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include "StartInfo.h"
|
||||
#include "CPlayerState.h"
|
||||
#include "TerrainHandler.h"
|
||||
#include "mapObjects/CGCreature.h"
|
||||
#include "mapObjects/CGMarket.h"
|
||||
#include "mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -984,7 +985,7 @@ void GiveBonus::applyGs(CGameState *gs)
|
||||
|
||||
std::string &descr = b->description;
|
||||
|
||||
if(bdescr.message.empty() && (bonus.type == BonusType::LUCK || bonus.type == BonusType::MORALE))
|
||||
if(bdescr.empty() && (bonus.type == BonusType::LUCK || bonus.type == BonusType::MORALE))
|
||||
{
|
||||
if (bonus.source == BonusSource::OBJECT)
|
||||
{
|
||||
@ -997,12 +998,12 @@ void GiveBonus::applyGs(CGameState *gs)
|
||||
}
|
||||
else
|
||||
{
|
||||
bdescr.toString(descr);
|
||||
descr = bdescr.toString();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bdescr.toString(descr);
|
||||
descr = bdescr.toString();
|
||||
}
|
||||
// Some of(?) versions of H3 use %s here instead of %d. Try to replace both of them
|
||||
boost::replace_first(descr, "%d", std::to_string(std::abs(bonus.val)));
|
||||
|
@ -482,10 +482,10 @@ void CUnitState::getCasterName(MetaString & text) const
|
||||
|
||||
void CUnitState::getCastDescription(const spells::Spell * spell, const std::vector<const Unit *> & attacked, MetaString & text) const
|
||||
{
|
||||
text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 565);//The %s casts %s
|
||||
//todo: use text 566 for single creature
|
||||
getCasterName(text);
|
||||
text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
|
||||
text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
|
||||
}
|
||||
|
||||
int32_t CUnitState::manaLimit() const
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../MetaString.h"
|
||||
#include "../NetPacksBase.h"
|
||||
|
||||
#include "../serializer/JsonDeserializer.h"
|
||||
@ -172,7 +173,7 @@ BattleHex Unit::occupiedHex(BattleHex assumedPos, bool twoHex, ui8 side)
|
||||
}
|
||||
}
|
||||
|
||||
void Unit::addText(MetaString & text, ui8 type, int32_t serial, const boost::logic::tribool & plural) const
|
||||
void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural) const
|
||||
{
|
||||
if(boost::logic::indeterminate(plural))
|
||||
serial = VLC->generaltexth->pluralText(serial, getCount());
|
||||
@ -181,17 +182,17 @@ void Unit::addText(MetaString & text, ui8 type, int32_t serial, const boost::log
|
||||
else
|
||||
serial = VLC->generaltexth->pluralText(serial, 1);
|
||||
|
||||
text.addTxt(type, serial);
|
||||
text.appendLocalString(type, serial);
|
||||
}
|
||||
|
||||
void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
|
||||
{
|
||||
if(boost::logic::indeterminate(plural))
|
||||
text.addCreReplacement(creatureId(), getCount());
|
||||
text.replaceCreatureName(creatureId(), getCount());
|
||||
else if(plural)
|
||||
text.addReplacement(MetaString::CRE_PL_NAMES, creatureIndex());
|
||||
text.replaceLocalString(EMetaText::CRE_PL_NAMES, creatureIndex());
|
||||
else
|
||||
text.addReplacement(MetaString::CRE_SING_NAMES, creatureIndex());
|
||||
text.replaceLocalString(EMetaText::CRE_SING_NAMES, creatureIndex());
|
||||
}
|
||||
|
||||
std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
|
||||
@ -199,8 +200,8 @@ std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
|
||||
const int32_t textId = VLC->generaltexth->pluralText(baseTextId, getCount());
|
||||
|
||||
MetaString text;
|
||||
text.addTxt(MetaString::GENERAL_TXT, textId);
|
||||
text.addCreReplacement(creatureId(), getCount());
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, textId);
|
||||
text.replaceCreatureName(creatureId(), getCount());
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
@ -21,7 +21,8 @@
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct MetaString;
|
||||
enum class EMetaText : uint8_t;
|
||||
class MetaString;
|
||||
class JsonNode;
|
||||
class JsonSerializeFormat;
|
||||
|
||||
@ -122,7 +123,7 @@ public:
|
||||
static BattleHex occupiedHex(BattleHex assumedPos, bool twoHex, ui8 side);
|
||||
|
||||
///MetaStrings
|
||||
void addText(MetaString & text, ui8 type, int32_t serial, const boost::logic::tribool & plural = boost::logic::indeterminate) const;
|
||||
void addText(MetaString & text, EMetaText type, int32_t serial, const boost::logic::tribool & plural = boost::logic::indeterminate) const;
|
||||
void addNameReplacement(MetaString & text, const boost::logic::tribool & plural = boost::logic::indeterminate) const;
|
||||
std::string formatGeneralMessage(const int32_t baseTextId) const;
|
||||
|
||||
|
@ -148,6 +148,11 @@ SObjectSounds AObjectTypeHandler::getSounds() const
|
||||
return sounds;
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::clearTemplates()
|
||||
{
|
||||
templates.clear();
|
||||
}
|
||||
|
||||
void AObjectTypeHandler::addTemplate(const std::shared_ptr<const ObjectTemplate> & templ)
|
||||
{
|
||||
templates.push_back(templ);
|
||||
|
@ -70,6 +70,7 @@ public:
|
||||
|
||||
void addTemplate(const std::shared_ptr<const ObjectTemplate> & templ);
|
||||
void addTemplate(JsonNode config);
|
||||
void clearTemplates();
|
||||
|
||||
/// returns all templates matching parameters
|
||||
std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates() const;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "../mapObjectConstructors/HillFortInstanceConstructor.h"
|
||||
#include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
|
||||
#include "../mapObjectConstructors/ShrineInstanceConstructor.h"
|
||||
#include "../mapObjects/CGCreature.h"
|
||||
#include "../mapObjects/CGPandoraBox.h"
|
||||
#include "../mapObjects/CQuest.h"
|
||||
#include "../mapObjects/ObjectTemplate.h"
|
||||
|
@ -26,9 +26,9 @@ void ShrineInstanceConstructor::randomizeObject(CGShrine * shrine, CRandomGenera
|
||||
auto visitTextParameter = parameters["visitText"];
|
||||
|
||||
if (visitTextParameter.isNumber())
|
||||
shrine->visitText.addTxt(MetaString::ADVOB_TXT, static_cast<ui32>(visitTextParameter.Float()));
|
||||
shrine->visitText.appendLocalString(EMetaText::ADVOB_TXT, static_cast<ui32>(visitTextParameter.Float()));
|
||||
else
|
||||
shrine->visitText << visitTextParameter.String();
|
||||
shrine->visitText.appendRawString(visitTextParameter.String());
|
||||
|
||||
if(shrine->spell == SpellID::NONE) // shrine has no predefined spell
|
||||
{
|
||||
|
@ -123,9 +123,9 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const
|
||||
BlockingDialog bd(true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.soundID = soundBase::invalid; // Sound is handled in json files, else two sounds are played
|
||||
bd.text.addTxt(MetaString::ADVOB_TXT, banktext);
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, banktext);
|
||||
if (banktext == 32)
|
||||
bd.text.addReplacement(getObjectName());
|
||||
bd.text.replaceRawString(getObjectName());
|
||||
cb->showBlockingDialog(&bd);
|
||||
}
|
||||
|
||||
@ -179,15 +179,15 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
{
|
||||
case Obj::SHIPWRECK:
|
||||
textID = 123;
|
||||
gbonus.bdescr << VLC->generaltexth->arraytxt[99];
|
||||
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[99]);
|
||||
break;
|
||||
case Obj::DERELICT_SHIP:
|
||||
textID = 42;
|
||||
gbonus.bdescr << VLC->generaltexth->arraytxt[101];
|
||||
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[101]);
|
||||
break;
|
||||
case Obj::CRYPT:
|
||||
textID = 120;
|
||||
gbonus.bdescr << VLC->generaltexth->arraytxt[98];
|
||||
gbonus.bdescr.appendRawString(VLC->generaltexth->arraytxt[98]);
|
||||
break;
|
||||
}
|
||||
cb->giveHeroBonus(&gbonus);
|
||||
@ -208,12 +208,12 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
case Obj::CREATURE_BANK:
|
||||
case Obj::DRAGON_UTOPIA:
|
||||
default:
|
||||
iw.text << VLC->generaltexth->advobtxt[33];// This was X, now is completely empty
|
||||
iw.text.addReplacement(getObjectName());
|
||||
iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty
|
||||
iw.text.replaceRawString(getObjectName());
|
||||
}
|
||||
if(textID != -1)
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, textID);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID);
|
||||
}
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -227,9 +227,9 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
if (bc->resources[it] != 0)
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, it, bc->resources[it], 0);
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(iw.components.back().val);
|
||||
loot.addReplacement(MetaString::RES_NAMES, iw.components.back().subtype);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(iw.components.back().val);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, iw.components.back().subtype);
|
||||
cb->giveResource(hero->getOwner(), static_cast<EGameResID>(it), bc->resources[it]);
|
||||
}
|
||||
}
|
||||
@ -237,14 +237,14 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
for (auto & elem : bc->artifacts)
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
|
||||
loot << "%s";
|
||||
loot.addReplacement(MetaString::ART_NAMES, elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
cb->giveHeroNewArtifact(hero, VLC->arth->objects[elem], ArtifactPosition::FIRST_AVAILABLE);
|
||||
}
|
||||
//display loot
|
||||
if (!iw.components.empty())
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, textID);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID);
|
||||
if (textID == 34)
|
||||
{
|
||||
const auto * strongest = boost::range::max_element(bc->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b)
|
||||
@ -252,8 +252,8 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
return a.type->getFightValue() < b.type->getFightValue();
|
||||
})->type;
|
||||
|
||||
iw.text.addReplacement(MetaString::CRE_PL_NAMES, strongest->getId());
|
||||
iw.text.addReplacement(loot.buildList());
|
||||
iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, strongest->getId());
|
||||
iw.text.replaceRawString(loot.buildList());
|
||||
}
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -269,12 +269,12 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
bool noWisdom = false;
|
||||
if(textID == 106)
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, textID); //pyramid
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, textID); //pyramid
|
||||
}
|
||||
for(const SpellID & spellId : bc->spells)
|
||||
{
|
||||
const auto * spell = spellId.toSpell(VLC->spells());
|
||||
iw.text.addTxt(MetaString::SPELL_NAME, spellId);
|
||||
iw.text.appendLocalString(EMetaText::SPELL_NAME, spellId);
|
||||
if(spell->getLevel() <= hero->maxSpellLevel())
|
||||
{
|
||||
if(hero->canLearnSpell(spell))
|
||||
@ -288,9 +288,9 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
}
|
||||
|
||||
if (!hero->getArt(ArtifactPosition::SPELLBOOK))
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 109); //no spellbook
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 109); //no spellbook
|
||||
else if(noWisdom)
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 108); //no expert Wisdom
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 108); //no expert Wisdom
|
||||
|
||||
if(!iw.components.empty() || !iw.text.toString().empty())
|
||||
cb->showInfoDialog(&iw);
|
||||
@ -312,19 +312,19 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
for(const auto & elem : ourArmy.Slots())
|
||||
{
|
||||
iw.components.emplace_back(*elem.second);
|
||||
loot << "%s";
|
||||
loot.addReplacement(*elem.second);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(*elem.second);
|
||||
}
|
||||
|
||||
if(ourArmy.stacksCount())
|
||||
{
|
||||
if(ourArmy.stacksCount() == 1 && ourArmy.Slots().begin()->second->count == 1)
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 185);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185);
|
||||
else
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 186);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186);
|
||||
|
||||
iw.text.addReplacement(loot.buildList());
|
||||
iw.text.addReplacement(hero->getNameTranslated());
|
||||
iw.text.replaceRawString(loot.buildList());
|
||||
iw.text.replaceRawString(hero->getNameTranslated());
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->giveCreatures(this, hero, ourArmy, false);
|
||||
}
|
||||
|
574
lib/mapObjects/CGCreature.cpp
Normal file
574
lib/mapObjects/CGCreature.cpp
Normal file
@ -0,0 +1,574 @@
|
||||
/*
|
||||
* CGCreature.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 "CGCreature.h"
|
||||
|
||||
#include "../NetPacks.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CConfigHandler.h"
|
||||
#include "../GameSettings.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
std::string CGCreature::getHoverText(PlayerColor player) const
|
||||
{
|
||||
if(stacks.empty())
|
||||
{
|
||||
//should not happen...
|
||||
logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum());
|
||||
return "INVALID_STACK";
|
||||
}
|
||||
|
||||
std::string hoverName;
|
||||
MetaString ms;
|
||||
CCreature::CreatureQuantityId monsterQuantityId = stacks.begin()->second->getQuantityID();
|
||||
int quantityTextIndex = 172 + 3 * (int)monsterQuantityId;
|
||||
if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
|
||||
ms.appendRawString(CCreature::getQuantityRangeStringForId(monsterQuantityId));
|
||||
else
|
||||
ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex);
|
||||
ms.appendRawString(" ");
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
|
||||
hoverName = ms.toString();
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
|
||||
{
|
||||
std::string hoverName;
|
||||
if(hero->hasVisions(this, 0))
|
||||
{
|
||||
MetaString ms;
|
||||
ms.appendNumber(stacks.begin()->second->count);
|
||||
ms.appendRawString(" ");
|
||||
ms.appendLocalString(EMetaText::CRE_PL_NAMES,subID);
|
||||
|
||||
ms.appendRawString("\n");
|
||||
|
||||
int decision = takenAction(hero, true);
|
||||
|
||||
switch (decision)
|
||||
{
|
||||
case FIGHT:
|
||||
ms.appendLocalString(EMetaText::GENERAL_TXT,246);
|
||||
break;
|
||||
case FLEE:
|
||||
ms.appendLocalString(EMetaText::GENERAL_TXT,245);
|
||||
break;
|
||||
case JOIN_FOR_FREE:
|
||||
ms.appendLocalString(EMetaText::GENERAL_TXT,243);
|
||||
break;
|
||||
default: //decision = cost in gold
|
||||
ms.appendRawString(boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision));
|
||||
break;
|
||||
}
|
||||
|
||||
hoverName = ms.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
hoverName = getHoverText(hero->tempOwner);
|
||||
}
|
||||
|
||||
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
|
||||
|
||||
int choice;
|
||||
double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
|
||||
if (ratio < 0.1) choice = 0;
|
||||
else if (ratio < 0.25) choice = 1;
|
||||
else if (ratio < 0.6) choice = 2;
|
||||
else if (ratio < 0.9) choice = 3;
|
||||
else if (ratio < 1.1) choice = 4;
|
||||
else if (ratio < 1.3) choice = 5;
|
||||
else if (ratio < 1.8) choice = 6;
|
||||
else if (ratio < 2.5) choice = 7;
|
||||
else if (ratio < 4) choice = 8;
|
||||
else if (ratio < 8) choice = 9;
|
||||
else if (ratio < 20) choice = 10;
|
||||
else choice = 11;
|
||||
|
||||
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.levels." + std::to_string(choice));
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
int action = takenAction(h);
|
||||
switch( action ) //decide what we do...
|
||||
{
|
||||
case FIGHT:
|
||||
fight(h);
|
||||
break;
|
||||
case FLEE:
|
||||
{
|
||||
flee(h);
|
||||
break;
|
||||
}
|
||||
case JOIN_FOR_FREE: //join for free
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
default: //join for gold
|
||||
{
|
||||
assert(action > 0);
|
||||
|
||||
//ask if player agrees to pay gold
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
std::string tmp = VLC->generaltexth->advobtxt[90];
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
|
||||
boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
|
||||
ynd.text.appendRawString(tmp);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CGCreature::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
blockVisit = true;
|
||||
switch(character)
|
||||
{
|
||||
case 0:
|
||||
character = -4;
|
||||
break;
|
||||
case 1:
|
||||
character = rand.nextInt(1, 7);
|
||||
break;
|
||||
case 2:
|
||||
character = rand.nextInt(1, 10);
|
||||
break;
|
||||
case 3:
|
||||
character = rand.nextInt(4, 10);
|
||||
break;
|
||||
case 4:
|
||||
character = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
stacks[SlotID(0)]->setType(CreatureID(subID));
|
||||
TQuantity &amount = stacks[SlotID(0)]->count;
|
||||
CCreature &c = *VLC->creh->objects[subID];
|
||||
if(amount == 0)
|
||||
{
|
||||
amount = rand.nextInt(c.ammMin, c.ammMax);
|
||||
|
||||
if(amount == 0) //armies with 0 creatures are illegal
|
||||
{
|
||||
logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName());
|
||||
amount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
temppower = stacks[SlotID(0)]->count * static_cast<ui64>(1000);
|
||||
refusedJoining = false;
|
||||
}
|
||||
|
||||
void CGCreature::newTurn(CRandomGenerator & rand) const
|
||||
{//Works only for stacks of single type of size up to 2 millions
|
||||
if (!notGrowingTeam)
|
||||
{
|
||||
if (stacks.begin()->second->count < VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
|
||||
{
|
||||
ui32 power = static_cast<ui32>(temppower * (100 + VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
|
||||
}
|
||||
}
|
||||
if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
|
||||
}
|
||||
void CGCreature::setPropertyDer(ui8 what, ui32 val)
|
||||
{
|
||||
switch (what)
|
||||
{
|
||||
case ObjProperty::MONSTER_COUNT:
|
||||
stacks[SlotID(0)]->count = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_POWER:
|
||||
temppower = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_EXP:
|
||||
giveStackExp(val);
|
||||
break;
|
||||
case ObjProperty::MONSTER_RESTORE_TYPE:
|
||||
formation.basicType = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_REFUSED_JOIN:
|
||||
refusedJoining = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
{
|
||||
//calculate relative strength of hero and creatures armies
|
||||
double relStrength = static_cast<double>(h->getTotalStrength()) / getArmyStrength();
|
||||
|
||||
int powerFactor;
|
||||
if(relStrength >= 7)
|
||||
powerFactor = 11;
|
||||
|
||||
else if(relStrength >= 1)
|
||||
powerFactor = static_cast<int>(2 * (relStrength - 1));
|
||||
|
||||
else if(relStrength >= 0.5)
|
||||
powerFactor = -1;
|
||||
|
||||
else if(relStrength >= 0.333)
|
||||
powerFactor = -2;
|
||||
else
|
||||
powerFactor = -3;
|
||||
|
||||
std::set<CreatureID> myKindCres; //what creatures are the same kind as we
|
||||
const CCreature * myCreature = VLC->creh->objects[subID];
|
||||
myKindCres.insert(myCreature->getId()); //we
|
||||
myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
|
||||
|
||||
for(ConstTransitivePtr<CCreature> &crea : VLC->creh->objects)
|
||||
{
|
||||
if(vstd::contains(crea->upgrades, myCreature->getId())) //it's our base creatures
|
||||
myKindCres.insert(crea->getId());
|
||||
}
|
||||
|
||||
int count = 0; //how many creatures of similar kind has hero
|
||||
int totalCount = 0;
|
||||
|
||||
for(const auto & elem : h->Slots())
|
||||
{
|
||||
if(vstd::contains(myKindCres,elem.second->type->getId()))
|
||||
count += elem.second->count;
|
||||
totalCount += elem.second->count;
|
||||
}
|
||||
|
||||
int sympathy = 0; // 0 if hero have no similar creatures
|
||||
if(count)
|
||||
sympathy++; // 1 - if hero have at least 1 similar creature
|
||||
if(count*2 > totalCount)
|
||||
sympathy++; // 2 - hero have similar creatures more that 50%
|
||||
|
||||
int diplomacy = h->valOfBonuses(BonusType::WANDERING_CREATURES_JOIN_BONUS);
|
||||
int charisma = powerFactor + diplomacy + sympathy;
|
||||
|
||||
if(charisma < character)
|
||||
return FIGHT;
|
||||
|
||||
if (allowJoin)
|
||||
{
|
||||
if(diplomacy + sympathy + 1 >= character)
|
||||
return JOIN_FOR_FREE;
|
||||
|
||||
else if(diplomacy * 2 + sympathy + 1 >= character)
|
||||
return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
|
||||
}
|
||||
|
||||
//we are still here - creatures have not joined hero, flee or fight
|
||||
|
||||
if (charisma > character && !neverFlees)
|
||||
return FLEE;
|
||||
else
|
||||
return FIGHT;
|
||||
}
|
||||
|
||||
void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
|
||||
{
|
||||
if(refusedJoining)
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
|
||||
|
||||
if(pursue)
|
||||
{
|
||||
fight(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
cb->removeObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
|
||||
{
|
||||
if(!accept)
|
||||
{
|
||||
if(takenAction(h,false) == FLEE)
|
||||
{
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
|
||||
flee(h);
|
||||
}
|
||||
else //they fight
|
||||
{
|
||||
h->showInfoDialog(87, 0, EInfoWindowMode::MODAL);//Insulted by your refusal of their offer, the monsters attack!
|
||||
fight(h);
|
||||
}
|
||||
}
|
||||
else //accepted
|
||||
{
|
||||
if (cb->getResource(h->tempOwner, EGameResID::GOLD) < cost) //player don't have enough gold!
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT,29); //You don't have enough gold
|
||||
cb->showInfoDialog(&iw);
|
||||
|
||||
//act as if player refused
|
||||
joinDecision(h,cost,false);
|
||||
return;
|
||||
}
|
||||
|
||||
//take gold
|
||||
if(cost)
|
||||
cb->giveResource(h->tempOwner,EGameResID::GOLD,-cost);
|
||||
|
||||
giveReward(h);
|
||||
cb->tryJoiningArmy(this, h, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::fight( const CGHeroInstance *h ) const
|
||||
{
|
||||
//split stacks
|
||||
//TODO: multiple creature types in a stack?
|
||||
int basicType = stacks.begin()->second->type->getId();
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
|
||||
|
||||
int stacksCount = getNumberOfStacks(h);
|
||||
//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
|
||||
|
||||
int amount = getStackCount(SlotID(0));
|
||||
int m = amount / stacksCount;
|
||||
int b = stacksCount * (m + 1) - amount;
|
||||
int a = stacksCount - b;
|
||||
|
||||
SlotID sourceSlot = stacks.begin()->first;
|
||||
for (int slotID = 1; slotID < a; ++slotID)
|
||||
{
|
||||
int stackSize = m + 1;
|
||||
cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
|
||||
}
|
||||
for (int slotID = a; slotID < stacksCount; ++slotID)
|
||||
{
|
||||
int stackSize = m;
|
||||
if (slotID) //don't do this when a = 0 -> stack is single
|
||||
cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
|
||||
}
|
||||
if (stacksCount > 1)
|
||||
{
|
||||
if (containsUpgradedStack()) //upgrade
|
||||
{
|
||||
SlotID slotID = SlotID(static_cast<si32>(std::floor(static_cast<float>(stacks.size()) / 2.0f)));
|
||||
const auto & upgrades = getStack(slotID).type->upgrades;
|
||||
if(!upgrades.empty())
|
||||
{
|
||||
auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
|
||||
cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cb->startBattleI(h, this);
|
||||
|
||||
}
|
||||
|
||||
void CGCreature::flee( const CGHeroInstance * h ) const
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91);
|
||||
ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, subID);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
|
||||
void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
if(result.winner == 0)
|
||||
{
|
||||
giveReward(hero);
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else if(result.winner > 1) // draw
|
||||
{
|
||||
// guarded reward is lost forever on draw
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
//merge stacks into one
|
||||
TSlots::const_iterator i;
|
||||
CCreature * cre = VLC->creh->objects[formation.basicType];
|
||||
for(i = stacks.begin(); i != stacks.end(); i++)
|
||||
{
|
||||
if(cre->isMyUpgrade(i->second->type))
|
||||
{
|
||||
cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
|
||||
}
|
||||
}
|
||||
|
||||
//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
|
||||
if(!hasStackAtSlot(SlotID(0)))
|
||||
cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
|
||||
|
||||
while(stacks.size() > 1) //hopefully that's enough
|
||||
{
|
||||
// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
|
||||
i = stacks.end();
|
||||
i--;
|
||||
SlotID slot = getSlotFor(i->second->type);
|
||||
if(slot == i->first) //no reason to move stack to its own slot
|
||||
break;
|
||||
else
|
||||
cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
|
||||
}
|
||||
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
|
||||
{
|
||||
auto action = takenAction(hero);
|
||||
if(!refusedJoining && action >= JOIN_FOR_FREE) //higher means price
|
||||
joinDecision(hero, action, answer);
|
||||
else if(action != FIGHT)
|
||||
fleeDecision(hero, answer);
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
bool CGCreature::containsUpgradedStack() const
|
||||
{
|
||||
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=830557#focus
|
||||
|
||||
float a = 2992.911117f;
|
||||
float b = 14174.264968f;
|
||||
float c = 5325.181015f;
|
||||
float d = 32788.727920f;
|
||||
|
||||
int val = static_cast<int>(std::floor(a * pos.x + b * pos.y + c * pos.z + d));
|
||||
return ((val % 32768) % 100) < 50;
|
||||
}
|
||||
|
||||
int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
|
||||
{
|
||||
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266094#focus
|
||||
|
||||
double strengthRatio = static_cast<double>(hero->getArmyStrength()) / getArmyStrength();
|
||||
int split = 1;
|
||||
|
||||
if (strengthRatio < 0.5f)
|
||||
split = 7;
|
||||
else if (strengthRatio < 0.67f)
|
||||
split = 6;
|
||||
else if (strengthRatio < 1)
|
||||
split = 5;
|
||||
else if (strengthRatio < 1.5f)
|
||||
split = 4;
|
||||
else if (strengthRatio < 2)
|
||||
split = 3;
|
||||
else
|
||||
split = 2;
|
||||
|
||||
ui32 a = 1550811371u;
|
||||
ui32 b = 3359066809u;
|
||||
ui32 c = 1943276003u;
|
||||
ui32 d = 3174620878u;
|
||||
|
||||
ui32 R1 = a * static_cast<ui32>(pos.x) + b * static_cast<ui32>(pos.y) + c * static_cast<ui32>(pos.z) + d;
|
||||
ui32 R2 = (R1 >> 16) & 0x7fff;
|
||||
|
||||
int R4 = R2 % 100 + 1;
|
||||
|
||||
if (R4 <= 20)
|
||||
split -= 1;
|
||||
else if (R4 >= 80)
|
||||
split += 1;
|
||||
|
||||
vstd::amin(split, getStack(SlotID(0)).count); //can't divide into more stacks than creatures total
|
||||
vstd::amin(split, 7); //can't have more than 7 stacks
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
void CGCreature::giveReward(const CGHeroInstance * h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
|
||||
if(!resources.empty())
|
||||
{
|
||||
cb->giveResources(h->tempOwner, resources);
|
||||
for(int i = 0; i < resources.size(); i++)
|
||||
{
|
||||
if(resources[i] > 0)
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(gainedArtifact != ArtifactID::NONE)
|
||||
{
|
||||
cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0);
|
||||
}
|
||||
|
||||
if(!iw.components.empty())
|
||||
{
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 183); // % has found treasure
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
}
|
||||
|
||||
static const std::vector<std::string> CHARACTER_JSON =
|
||||
{
|
||||
"compliant", "friendly", "aggressive", "hostile", "savage"
|
||||
};
|
||||
|
||||
void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeEnum("character", character, CHARACTER_JSON);
|
||||
|
||||
if(handler.saving)
|
||||
{
|
||||
if(hasStackAtSlot(SlotID(0)))
|
||||
{
|
||||
si32 amount = getStack(SlotID(0)).count;
|
||||
handler.serializeInt("amount", amount, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
si32 amount = 0;
|
||||
handler.serializeInt("amount", amount);
|
||||
auto * hlp = new CStackInstance();
|
||||
hlp->count = amount;
|
||||
//type will be set during initialization
|
||||
putStack(SlotID(0), hlp);
|
||||
}
|
||||
|
||||
resources.serializeJson(handler, "rewardResources");
|
||||
|
||||
handler.serializeId("rewardArtifact", gainedArtifact, ArtifactID(ArtifactID::NONE));
|
||||
|
||||
handler.serializeBool("noGrowing", notGrowingTeam);
|
||||
handler.serializeBool("neverFlees", neverFlees);
|
||||
handler.serializeString("rewardMessage", message);
|
||||
}
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
91
lib/mapObjects/CGCreature.h
Normal file
91
lib/mapObjects/CGCreature.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* CGCreature.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 "CArmedInstance.h"
|
||||
#include "../ResourceSet.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
|
||||
{
|
||||
public:
|
||||
enum Action {
|
||||
FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
|
||||
};
|
||||
|
||||
enum Character {
|
||||
COMPLIANT = 0, FRIENDLY = 1, AGRESSIVE = 2, HOSTILE = 3, SAVAGE = 4
|
||||
};
|
||||
|
||||
ui32 identifier; //unique code for this monster (used in missions)
|
||||
si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
|
||||
std::string message; //message printed for attacking hero
|
||||
TResources resources; // resources given to hero that has won with monsters
|
||||
ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
|
||||
bool neverFlees; //if true, the troops will never flee
|
||||
bool notGrowingTeam; //if true, number of units won't grow
|
||||
ui64 temppower; //used to handle fractional stack growth for tiny stacks
|
||||
|
||||
bool refusedJoining;
|
||||
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
std::string getHoverText(PlayerColor player) const override;
|
||||
std::string getHoverText(const CGHeroInstance * hero) const override;
|
||||
void initObj(CRandomGenerator & rand) override;
|
||||
void newTurn(CRandomGenerator & rand) const override;
|
||||
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
|
||||
|
||||
//stack formation depends on position,
|
||||
bool containsUpgradedStack() const;
|
||||
int getNumberOfStacks(const CGHeroInstance *hero) const;
|
||||
|
||||
struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
|
||||
{
|
||||
si32 basicType;
|
||||
ui8 upgrade; //random seed used to determine number of stacks and is there's upgraded stack
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & basicType;
|
||||
h & upgrade;
|
||||
}
|
||||
} formation;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CArmedInstance&>(*this);
|
||||
h & identifier;
|
||||
h & character;
|
||||
h & message;
|
||||
h & resources;
|
||||
h & gainedArtifact;
|
||||
h & neverFlees;
|
||||
h & notGrowingTeam;
|
||||
h & temppower;
|
||||
h & refusedJoining;
|
||||
h & formation;
|
||||
}
|
||||
protected:
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
||||
private:
|
||||
void fight(const CGHeroInstance *h) const;
|
||||
void flee( const CGHeroInstance * h ) const;
|
||||
void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
|
||||
void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
|
||||
|
||||
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
|
||||
void giveReward(const CGHeroInstance * h) const;
|
||||
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
@ -167,8 +167,8 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted. Perhaps you should try next week.
|
||||
iw.text.addReplacement(MetaString::OBJ_NAMES, ID);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted. Perhaps you should try next week.
|
||||
iw.text.replaceLocalString(EMetaText::OBJ_NAMES, ID);
|
||||
cb->sendAndApply(&iw);
|
||||
return;
|
||||
}
|
||||
@ -182,13 +182,13 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
BlockingDialog bd(true,false);
|
||||
bd.player = h->tempOwner;
|
||||
bd.text.addTxt(MetaString::GENERAL_TXT, 421); //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
|
||||
bd.text.addReplacement(ID == Obj::CREATURE_GENERATOR1 ? MetaString::CREGENS : MetaString::CREGENS4, subID);
|
||||
bd.text.appendLocalString(EMetaText::GENERAL_TXT, 421); //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
|
||||
bd.text.replaceLocalString(ID == Obj::CREATURE_GENERATOR1 ? EMetaText::CREGENS : EMetaText::CREGENS4, subID);
|
||||
if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
|
||||
bd.text.addReplacement(CCreature::getQuantityRangeStringForId(Slots().begin()->second->getQuantityID()));
|
||||
bd.text.replaceRawString(CCreature::getQuantityRangeStringForId(Slots().begin()->second->getQuantityID()));
|
||||
else
|
||||
bd.text.addReplacement(MetaString::ARRAY_TXT, 173 + (int)Slots().begin()->second->getQuantityID()*3);
|
||||
bd.text.addReplacement(*Slots().begin()->second);
|
||||
bd.text.replaceLocalString(EMetaText::ARRAY_TXT, 173 + (int)Slots().begin()->second->getQuantityID()*3);
|
||||
bd.text.replaceCreatureName(*Slots().begin()->second);
|
||||
cb->showBlockingDialog(&bd);
|
||||
return;
|
||||
}
|
||||
@ -203,20 +203,20 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
|
||||
bd.player = h->tempOwner;
|
||||
if(ID == Obj::CREATURE_GENERATOR1 || ID == Obj::CREATURE_GENERATOR4)
|
||||
{
|
||||
bd.text.addTxt(MetaString::ADVOB_TXT, ID == Obj::CREATURE_GENERATOR1 ? 35 : 36); //{%s} Would you like to recruit %s? / {%s} Would you like to recruit %s, %s, %s, or %s?
|
||||
bd.text.addReplacement(ID == Obj::CREATURE_GENERATOR1 ? MetaString::CREGENS : MetaString::CREGENS4, subID);
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, ID == Obj::CREATURE_GENERATOR1 ? 35 : 36); //{%s} Would you like to recruit %s? / {%s} Would you like to recruit %s, %s, %s, or %s?
|
||||
bd.text.replaceLocalString(ID == Obj::CREATURE_GENERATOR1 ? EMetaText::CREGENS : EMetaText::CREGENS4, subID);
|
||||
for(const auto & elem : creatures)
|
||||
bd.text.addReplacement(MetaString::CRE_PL_NAMES, elem.second[0]);
|
||||
bd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, elem.second[0]);
|
||||
}
|
||||
else if(ID == Obj::REFUGEE_CAMP)
|
||||
{
|
||||
bd.text.addTxt(MetaString::ADVOB_TXT, 35); //{%s} Would you like to recruit %s?
|
||||
bd.text.addReplacement(MetaString::OBJ_NAMES, ID);
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, 35); //{%s} Would you like to recruit %s?
|
||||
bd.text.replaceLocalString(EMetaText::OBJ_NAMES, ID);
|
||||
for(const auto & elem : creatures)
|
||||
bd.text.addReplacement(MetaString::CRE_PL_NAMES, elem.second[0]);
|
||||
bd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, elem.second[0]);
|
||||
}
|
||||
else if(ID == Obj::WAR_MACHINE_FACTORY)
|
||||
bd.text.addTxt(MetaString::ADVOB_TXT, 157); //{War Machine Factory} Would you like to purchase War Machines?
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, 157); //{War Machine Factory} Would you like to purchase War Machines?
|
||||
else
|
||||
throw std::runtime_error("Illegal dwelling!");
|
||||
|
||||
@ -330,8 +330,8 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 425);//The %s would join your hero, but there aren't enough provisions to support them.
|
||||
iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 425);//The %s would join your hero, but there aren't enough provisions to support them.
|
||||
iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
else //give creatures
|
||||
@ -345,9 +345,9 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 423); //%d %s join your army.
|
||||
iw.text.addReplacement(count);
|
||||
iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 423); //%d %s join your army.
|
||||
iw.text.replaceNumber(count);
|
||||
iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
|
||||
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->sendAndApply(&sac);
|
||||
@ -358,8 +358,8 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 422); //There are no %s here to recruit.
|
||||
iw.text.addReplacement(MetaString::CRE_PL_NAMES, crid);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 422); //There are no %s here to recruit.
|
||||
iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
|
||||
iw.player = h->tempOwner;
|
||||
cb->sendAndApply(&iw);
|
||||
}
|
||||
|
@ -453,9 +453,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;
|
||||
@ -681,7 +680,7 @@ PlayerColor CGHeroInstance::getCasterOwner() const
|
||||
void CGHeroInstance::getCasterName(MetaString & text) const
|
||||
{
|
||||
//FIXME: use local name, MetaString need access to gamestate as hero name is part of map object
|
||||
text.addReplacement(getNameTranslated());
|
||||
text.replaceRawString(getNameTranslated());
|
||||
}
|
||||
|
||||
void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std::vector<const battle::Unit *> & attacked, MetaString & text) const
|
||||
@ -689,9 +688,9 @@ void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std::
|
||||
const bool singleTarget = attacked.size() == 1;
|
||||
const int textIndex = singleTarget ? 195 : 196;
|
||||
|
||||
text.addTxt(MetaString::GENERAL_TXT, textIndex);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, textIndex);
|
||||
getCasterName(text);
|
||||
text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
|
||||
text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
|
||||
if(singleTarget)
|
||||
attacked.at(0)->addNameReplacement(text, true);
|
||||
}
|
||||
@ -897,14 +896,14 @@ void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedSta
|
||||
|
||||
if (raisedStack.count > 1) // Practicing the dark arts of necromancy, ... (plural)
|
||||
{
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 145);
|
||||
iw.text.addReplacement(raisedStack.count);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 145);
|
||||
iw.text.replaceNumber(raisedStack.count);
|
||||
}
|
||||
else // Practicing the dark arts of necromancy, ... (singular)
|
||||
{
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 146);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 146);
|
||||
}
|
||||
iw.text.addReplacement(raisedStack);
|
||||
iw.text.replaceCreatureName(raisedStack);
|
||||
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -954,8 +953,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
|
||||
@ -1106,6 +1104,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
|
||||
|
@ -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;
|
||||
|
@ -105,7 +105,7 @@ std::set<int3> CGObjectInstance::getBlockedOffsets() const
|
||||
return appearance->getBlockedOffsets();
|
||||
}
|
||||
|
||||
void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
void CGObjectInstance::setType(si32 newID, si32 newSubID)
|
||||
{
|
||||
auto position = visitablePos();
|
||||
auto oldOffset = getVisitableOffset();
|
||||
@ -113,10 +113,10 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
|
||||
//recalculate blockvis tiles - new appearance might have different blockmap than before
|
||||
cb->gameState()->map->removeBlockVisTiles(this, true);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(ID, subID);
|
||||
auto handler = VLC->objtypeh->getHandlerFor(newID, newSubID);
|
||||
if(!handler)
|
||||
{
|
||||
logGlobal->error("Unknown object type %d:%d at %s", ID, subID, visitablePos().toString());
|
||||
logGlobal->error("Unknown object type %d:%d at %s", newID, newSubID, visitablePos().toString());
|
||||
return;
|
||||
}
|
||||
if(!handler->getTemplates(tile.terType->getId()).empty())
|
||||
@ -125,22 +125,26 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->getNameTranslated());
|
||||
logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", newID, newSubID, visitablePos().toString(), tile.terType->getNameTranslated());
|
||||
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
|
||||
}
|
||||
|
||||
if(this->ID == Obj::PRISON && ID == Obj::HERO)
|
||||
{
|
||||
auto newOffset = getVisitableOffset();
|
||||
// FIXME: potentially unused code - setType is NOT called when releasing hero from prison
|
||||
// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
|
||||
bool needToAdjustOffset = false;
|
||||
|
||||
// adjust position since hero and prison may have different visitable offset
|
||||
// FIXME: potentially unused code - setType is NOT called when releasing hero from prison
|
||||
// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
|
||||
needToAdjustOffset |= this->ID == Obj::PRISON && newID == Obj::HERO;
|
||||
needToAdjustOffset |= newID == Obj::MONSTER;
|
||||
|
||||
if(needToAdjustOffset)
|
||||
{
|
||||
// adjust position since object visitable offset might have changed
|
||||
auto newOffset = getVisitableOffset();
|
||||
pos = pos - oldOffset + newOffset;
|
||||
}
|
||||
|
||||
this->ID = Obj(ID);
|
||||
this->subID = subID;
|
||||
this->ID = Obj(newID);
|
||||
this->subID = newSubID;
|
||||
|
||||
cb->gameState()->map->addBlockVisTiles(this);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
BlockingDialog bd (true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.text.addTxt (MetaString::ADVOB_TXT, 14);
|
||||
bd.text.appendLocalString (EMetaText::ADVOB_TXT, 14);
|
||||
cb->showBlockingDialog (&bd);
|
||||
}
|
||||
|
||||
@ -79,8 +79,8 @@ void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const
|
||||
{
|
||||
TExpType expVal = h->calculateXp(gainedExp);
|
||||
//getText(iw,afterBattle,175,h); //wtf?
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 175); //%s learns something
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 175); //%s learns something
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
|
||||
if(expVal)
|
||||
iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(expVal), 0);
|
||||
@ -156,13 +156,13 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
|
||||
{
|
||||
if (spellsToGive.size() > 1)
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 188); //%s learns spells
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 188); //%s learns spells
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 184); //%s learns a spell
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 184); //%s learns a spell
|
||||
}
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
cb->changeSpells(h, true, spellsToGive);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -227,8 +227,8 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
|
||||
|
||||
iw.components.clear();
|
||||
// getText(iw,afterBattle,183,h);
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 183); //% has found treasure
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
for(const auto & elem : artifacts)
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
|
||||
@ -236,8 +236,8 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
|
||||
{
|
||||
cb->showInfoDialog(&iw);
|
||||
iw.components.clear();
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure - once more?
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 183); //% has found treasure - once more?
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
}
|
||||
}
|
||||
if(!iw.components.empty())
|
||||
@ -259,24 +259,24 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
|
||||
for(const auto & elem : creatures.Slots())
|
||||
{ //build list of joined creatures
|
||||
iw.components.emplace_back(*elem.second);
|
||||
loot << "%s";
|
||||
loot.addReplacement(*elem.second);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(*elem.second);
|
||||
}
|
||||
|
||||
if(creatures.stacksCount() == 1 && creatures.Slots().begin()->second->count == 1)
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 185);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185);
|
||||
else
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 186);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186);
|
||||
|
||||
iw.text.addReplacement(loot.buildList());
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.replaceRawString(loot.buildList());
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->giveCreatures(this, h, creatures, false);
|
||||
}
|
||||
if(!hasGuardians && !msg.empty())
|
||||
{
|
||||
iw.text << msg;
|
||||
iw.text.appendRawString(msg);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
}
|
||||
@ -285,12 +285,12 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int text, const C
|
||||
{
|
||||
if(afterBattle || message.empty())
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,text);//%s has lost treasure.
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,text);//%s has lost treasure.
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
afterBattle = true;
|
||||
}
|
||||
}
|
||||
@ -301,12 +301,12 @@ void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int nega
|
||||
iw.text.clear();
|
||||
if(afterBattle || message.empty())
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases
|
||||
iw.text.replaceRawString(h->getNameTranslated());
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
afterBattle = true;
|
||||
}
|
||||
}
|
||||
@ -461,9 +461,9 @@ void CGEvent::activated( const CGHeroInstance * h ) const
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
if(!message.empty())
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
else
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 16);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 16);
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->startBattleI(h, this);
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
mp.hid = heroID;
|
||||
cb->setMovePoints(&mp);
|
||||
|
||||
iw.text << VLC->generaltexth->allTexts[580];
|
||||
iw.text.appendRawString(VLC->generaltexth->allTexts[580]);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
break;
|
||||
@ -168,7 +168,7 @@ void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
cb->setManaPoints (heroID, 2 * h->manaLimit());
|
||||
//TODO: investigate line below
|
||||
//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
|
||||
iw.text << getVisitingBonusGreeting();
|
||||
iw.text.appendRawString(getVisitingBonusGreeting());
|
||||
cb->showInfoDialog(&iw);
|
||||
//extra visit penalty if hero alredy had double mana points (or even more?!)
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
@ -246,7 +246,7 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
|
||||
if(what != PrimarySkill::NONE)
|
||||
{
|
||||
iw.player = cb->getOwner(heroID);
|
||||
iw.text << getVisitingBonusGreeting();
|
||||
iw.text.appendRawString(getVisitingBonusGreeting());
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changePrimSkill (cb->getHero(heroID), what, val);
|
||||
town->addHeroToStructureVisitors(h, indexOnTV);
|
||||
@ -278,7 +278,7 @@ void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) con
|
||||
addToVisitors = true;
|
||||
|
||||
iw.player = cb->getOwner(h->id);
|
||||
iw.text << getCustomBonusGreeting(gb.bonus);
|
||||
iw.text.appendRawString(getCustomBonusGreeting(gb.bonus));
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
if(addToVisitors)
|
||||
|
@ -320,7 +320,7 @@ void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text << h->commander->getName();
|
||||
iw.text.appendRawString(h->commander->getName());
|
||||
iw.components.emplace_back(*h->commander);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@ -682,7 +682,7 @@ void CGTownInstance::clearArmy() const
|
||||
|
||||
BoatId CGTownInstance::getBoatType() const
|
||||
{
|
||||
return town->shipyardBoat;
|
||||
return town->faction->boatType;
|
||||
}
|
||||
|
||||
int CGTownInstance::getMarketEfficiency() const
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "../CSoundBase.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "MiscObjects.h"
|
||||
#include "CGCreature.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -187,19 +187,21 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
if(firstVisit)
|
||||
{
|
||||
isCustom = isCustomFirst;
|
||||
iwText << (text = firstVisitText);
|
||||
text = firstVisitText;
|
||||
iwText.appendRawString(text);
|
||||
}
|
||||
else if(failRequirements)
|
||||
{
|
||||
isCustom = isCustomNext;
|
||||
iwText << (text = nextVisitText);
|
||||
text = nextVisitText;
|
||||
iwText.appendRawString(text);
|
||||
}
|
||||
switch (missionType)
|
||||
{
|
||||
case MISSION_LEVEL:
|
||||
components.emplace_back(Component::EComponentType::EXPERIENCE, 0, m13489val, 0);
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(m13489val);
|
||||
iwText.replaceNumber(m13489val);
|
||||
break;
|
||||
case MISSION_PRIMARY_STAT:
|
||||
{
|
||||
@ -209,13 +211,13 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
if(m2stats[i])
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::PRIM_SKILL, i, m2stats[i], 0);
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m2stats[i]);
|
||||
loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
if (!isCustom)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
@ -227,7 +229,7 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
//FIXME: portrait may not match hero, if custom portrait was set in map editor
|
||||
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroh->objects[m13489val]->imageIndex, 0, 0);
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
iwText.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
break;
|
||||
case MISSION_KILL_CREATURE:
|
||||
{
|
||||
@ -244,11 +246,11 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
|
||||
loot << "%s";
|
||||
loot.addReplacement(MetaString::ART_NAMES, elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_ARMY:
|
||||
@ -257,11 +259,11 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
components.emplace_back(elem);
|
||||
loot << "%s";
|
||||
loot.addReplacement(elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_RESOURCES:
|
||||
@ -272,19 +274,19 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
|
||||
if(m7resources[i])
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::RESOURCE, i, m7resources[i], 0);
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m7resources[i]);
|
||||
loot.addReplacement(MetaString::RES_NAMES, i);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0);
|
||||
if(!isCustom)
|
||||
iwText.addReplacement(VLC->generaltexth->colors[m13489val]);
|
||||
iwText.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -295,17 +297,17 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
assert(missionType != MISSION_NONE);
|
||||
|
||||
if(onHover)
|
||||
ms << "\n\n";
|
||||
ms.appendRawString("\n\n");
|
||||
|
||||
std::string questName = missionName(missionType);
|
||||
std::string questState = missionState(onHover ? 3 : 4);
|
||||
|
||||
ms << VLC->generaltexth->translate("core.seerhut.quest", questName, questState,textOption);
|
||||
ms.appendRawString(VLC->generaltexth->translate("core.seerhut.quest", questName, questState,textOption));
|
||||
|
||||
switch(missionType)
|
||||
{
|
||||
case MISSION_LEVEL:
|
||||
ms.addReplacement(m13489val);
|
||||
ms.replaceNumber(m13489val);
|
||||
break;
|
||||
case MISSION_PRIMARY_STAT:
|
||||
{
|
||||
@ -314,29 +316,29 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
{
|
||||
if (m2stats[i])
|
||||
{
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m2stats[i]);
|
||||
loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
ms.addReplacement(loot.buildList());
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
ms.addReplacement(heroName);
|
||||
ms.replaceRawString(heroName);
|
||||
break;
|
||||
case MISSION_KILL_CREATURE:
|
||||
ms.addReplacement(stackToKill);
|
||||
ms.replaceCreatureName(stackToKill);
|
||||
break;
|
||||
case MISSION_ART:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
loot << "%s";
|
||||
loot.addReplacement(MetaString::ART_NAMES, elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
ms.addReplacement(loot.buildList());
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_ARMY:
|
||||
@ -344,10 +346,10 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
MetaString loot;
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
loot << "%s";
|
||||
loot.addReplacement(elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
ms.addReplacement(loot.buildList());
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_RESOURCES:
|
||||
@ -357,19 +359,19 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
{
|
||||
if (m7resources[i])
|
||||
{
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m7resources[i]);
|
||||
loot.addReplacement(MetaString::RES_NAMES, i);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
ms.addReplacement(loot.buildList());
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_HERO:
|
||||
ms.addReplacement(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
ms.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
ms.addReplacement(VLC->generaltexth->colors[m13489val]);
|
||||
ms.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -378,12 +380,12 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
|
||||
void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &components, bool isCustom, const CGHeroInstance * h) const
|
||||
{
|
||||
iwText << completedText;
|
||||
iwText.appendRawString(completedText);
|
||||
switch(missionType)
|
||||
{
|
||||
case CQuest::MISSION_LEVEL:
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(m13489val);
|
||||
iwText.replaceNumber(m13489val);
|
||||
break;
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
if (vstd::contains (completedText,'%')) //there's one case when there's nothing to replace
|
||||
@ -393,13 +395,13 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
|
||||
{
|
||||
if (m2stats[i])
|
||||
{
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m2stats[i]);
|
||||
loot.addReplacement(VLC->generaltexth->primarySkillNames[i]);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_ART:
|
||||
@ -407,11 +409,11 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
|
||||
MetaString loot;
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
loot << "%s";
|
||||
loot.addReplacement(MetaString::ART_NAMES, elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_ARMY:
|
||||
@ -419,11 +421,11 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
|
||||
MetaString loot;
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
loot << "%s";
|
||||
loot.addReplacement(elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
@ -433,13 +435,13 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
|
||||
{
|
||||
if (m7resources[i])
|
||||
{
|
||||
loot << "%d %s";
|
||||
loot.addReplacement(m7resources[i]);
|
||||
loot.addReplacement(MetaString::RES_NAMES, i);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(loot.buildList());
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
@ -449,11 +451,11 @@ void CQuest::getCompletionText(MetaString &iwText, std::vector<Component> &compo
|
||||
break;
|
||||
case MISSION_HERO:
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
iwText.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
if (!isCustomComplete)
|
||||
iwText.addReplacement(VLC->generaltexth->colors[m13489val]);
|
||||
iwText.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -594,7 +596,7 @@ void CGSeerHut::getRolloverText(MetaString &text, bool onHover) const
|
||||
{
|
||||
quest->getRolloverText (text, onHover);//TODO: simplify?
|
||||
if(!onHover)
|
||||
text.addReplacement(seerName);
|
||||
text.replaceRawString(seerName);
|
||||
}
|
||||
|
||||
std::string CGSeerHut::getHoverText(PlayerColor player) const
|
||||
@ -620,14 +622,14 @@ void CQuest::addReplacements(MetaString &out, const std::string &base) const
|
||||
switch(missionType)
|
||||
{
|
||||
case MISSION_KILL_CREATURE:
|
||||
out.addReplacement(stackToKill);
|
||||
out.replaceCreatureName(stackToKill);
|
||||
if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
|
||||
{
|
||||
out.addReplacement(VLC->generaltexth->arraytxt[147+stackDirection]);
|
||||
out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
out.addReplacement(heroName);
|
||||
out.replaceRawString(heroName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -747,9 +749,9 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text << VLC->generaltexth->seerEmpty[quest->completedOption];
|
||||
iw.text.appendRawString(VLC->generaltexth->seerEmpty[quest->completedOption]);
|
||||
if (ID == Obj::SEER_HUT)
|
||||
iw.text.addReplacement(seerName);
|
||||
iw.text.replaceRawString(seerName);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
}
|
||||
@ -1155,13 +1157,17 @@ void CGBorderGuard::initObj(CRandomGenerator & rand)
|
||||
|
||||
void CGBorderGuard::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
|
||||
{
|
||||
text << std::pair<ui8,ui32>(11,18);
|
||||
text.appendLocalString(EMetaText::ADVOB_TXT,18);
|
||||
}
|
||||
|
||||
void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const
|
||||
{
|
||||
if (!onHover)
|
||||
text << VLC->generaltexth->tentColors[subID] << " " << VLC->objtypeh->getObjectName(Obj::KEYMASTER, subID);
|
||||
{
|
||||
text.appendRawString(VLC->generaltexth->tentColors[subID]);
|
||||
text.appendRawString(" ");
|
||||
text.appendRawString(VLC->objtypeh->getObjectName(Obj::KEYMASTER, subID));
|
||||
}
|
||||
}
|
||||
|
||||
bool CGBorderGuard::checkQuest(const CGHeroInstance * h) const
|
||||
@ -1175,7 +1181,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
BlockingDialog bd (true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.text.addTxt (MetaString::ADVOB_TXT, 17);
|
||||
bd.text.appendLocalString (EMetaText::ADVOB_TXT, 17);
|
||||
cb->showBlockingDialog (&bd);
|
||||
}
|
||||
else
|
||||
|
@ -38,7 +38,7 @@ void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInf
|
||||
iw.soundID = soundID;
|
||||
iw.player = getOwner();
|
||||
iw.type = mode;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,txtID);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txtID);
|
||||
IObjectInterface::cb->sendAndApply(&iw);
|
||||
}
|
||||
|
||||
@ -122,16 +122,16 @@ void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visit
|
||||
switch(shipyardStatus())
|
||||
{
|
||||
case BOAT_ALREADY_BUILT:
|
||||
out.addTxt(MetaString::GENERAL_TXT, 51);
|
||||
out.appendLocalString(EMetaText::GENERAL_TXT, 51);
|
||||
break;
|
||||
case TILE_BLOCKED:
|
||||
if(visitor)
|
||||
{
|
||||
out.addTxt(MetaString::GENERAL_TXT, 134);
|
||||
out.addReplacement(visitor->getNameTranslated());
|
||||
out.appendLocalString(EMetaText::GENERAL_TXT, 134);
|
||||
out.replaceRawString(visitor->getNameTranslated());
|
||||
}
|
||||
else
|
||||
out.addTxt(MetaString::ADVOB_TXT, 189);
|
||||
out.appendLocalString(EMetaText::ADVOB_TXT, 189);
|
||||
break;
|
||||
case NO_WATER:
|
||||
logGlobal->error("Shipyard without water at tile %s! ", getObject()->getPosition().toString());
|
||||
|
@ -20,6 +20,7 @@ class CRandomGenerator;
|
||||
class IGameCallback;
|
||||
class ResourceSet;
|
||||
class int3;
|
||||
class MetaString;
|
||||
|
||||
class DLL_LINKAGE IObjectInterface
|
||||
{
|
||||
|
@ -15,16 +15,13 @@
|
||||
#include "../NetPacks.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "../CConfigHandler.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../CPlayerState.h"
|
||||
#include "../GameSettings.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -68,557 +65,6 @@ bool CTeamVisited::wasVisited(const TeamID & team) const
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string CGCreature::getHoverText(PlayerColor player) const
|
||||
{
|
||||
if(stacks.empty())
|
||||
{
|
||||
//should not happen...
|
||||
logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), subID, id.getNum());
|
||||
return "INVALID_STACK";
|
||||
}
|
||||
|
||||
std::string hoverName;
|
||||
MetaString ms;
|
||||
CCreature::CreatureQuantityId monsterQuantityId = stacks.begin()->second->getQuantityID();
|
||||
int quantityTextIndex = 172 + 3 * (int)monsterQuantityId;
|
||||
if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
|
||||
ms << CCreature::getQuantityRangeStringForId(monsterQuantityId);
|
||||
else
|
||||
ms.addTxt(MetaString::ARRAY_TXT, quantityTextIndex);
|
||||
ms << " " ;
|
||||
ms.addTxt(MetaString::CRE_PL_NAMES,subID);
|
||||
ms.toString(hoverName);
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
|
||||
{
|
||||
std::string hoverName;
|
||||
if(hero->hasVisions(this, 0))
|
||||
{
|
||||
MetaString ms;
|
||||
ms << stacks.begin()->second->count;
|
||||
ms << " " ;
|
||||
ms.addTxt(MetaString::CRE_PL_NAMES,subID);
|
||||
|
||||
ms << "\n";
|
||||
|
||||
int decision = takenAction(hero, true);
|
||||
|
||||
switch (decision)
|
||||
{
|
||||
case FIGHT:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,246);
|
||||
break;
|
||||
case FLEE:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,245);
|
||||
break;
|
||||
case JOIN_FOR_FREE:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,243);
|
||||
break;
|
||||
default: //decision = cost in gold
|
||||
ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
|
||||
break;
|
||||
}
|
||||
|
||||
ms.toString(hoverName);
|
||||
}
|
||||
else
|
||||
{
|
||||
hoverName = getHoverText(hero->tempOwner);
|
||||
}
|
||||
|
||||
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.title");
|
||||
|
||||
int choice;
|
||||
double ratio = (static_cast<double>(getArmyStrength()) / hero->getTotalStrength());
|
||||
if (ratio < 0.1) choice = 0;
|
||||
else if (ratio < 0.25) choice = 1;
|
||||
else if (ratio < 0.6) choice = 2;
|
||||
else if (ratio < 0.9) choice = 3;
|
||||
else if (ratio < 1.1) choice = 4;
|
||||
else if (ratio < 1.3) choice = 5;
|
||||
else if (ratio < 1.8) choice = 6;
|
||||
else if (ratio < 2.5) choice = 7;
|
||||
else if (ratio < 4) choice = 8;
|
||||
else if (ratio < 8) choice = 9;
|
||||
else if (ratio < 20) choice = 10;
|
||||
else choice = 11;
|
||||
|
||||
hoverName += VLC->generaltexth->translate("vcmi.adventureMap.monsterThreat.levels." + std::to_string(choice));
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
int action = takenAction(h);
|
||||
switch( action ) //decide what we do...
|
||||
{
|
||||
case FIGHT:
|
||||
fight(h);
|
||||
break;
|
||||
case FLEE:
|
||||
{
|
||||
flee(h);
|
||||
break;
|
||||
}
|
||||
case JOIN_FOR_FREE: //join for free
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.addTxt(MetaString::ADVOB_TXT, 86);
|
||||
ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
default: //join for gold
|
||||
{
|
||||
assert(action > 0);
|
||||
|
||||
//ask if player agrees to pay gold
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
std::string tmp = VLC->generaltexth->advobtxt[90];
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
|
||||
boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
|
||||
boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
|
||||
ynd.text << tmp;
|
||||
cb->showBlockingDialog(&ynd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CGCreature::initObj(CRandomGenerator & rand)
|
||||
{
|
||||
blockVisit = true;
|
||||
switch(character)
|
||||
{
|
||||
case 0:
|
||||
character = -4;
|
||||
break;
|
||||
case 1:
|
||||
character = rand.nextInt(1, 7);
|
||||
break;
|
||||
case 2:
|
||||
character = rand.nextInt(1, 10);
|
||||
break;
|
||||
case 3:
|
||||
character = rand.nextInt(4, 10);
|
||||
break;
|
||||
case 4:
|
||||
character = 10;
|
||||
break;
|
||||
}
|
||||
|
||||
stacks[SlotID(0)]->setType(CreatureID(subID));
|
||||
TQuantity &amount = stacks[SlotID(0)]->count;
|
||||
CCreature &c = *VLC->creh->objects[subID];
|
||||
if(amount == 0)
|
||||
{
|
||||
amount = rand.nextInt(c.ammMin, c.ammMax);
|
||||
|
||||
if(amount == 0) //armies with 0 creatures are illegal
|
||||
{
|
||||
logGlobal->warn("Stack %s cannot have 0 creatures. Check properties of %s", nodeName(), c.nodeName());
|
||||
amount = 1;
|
||||
}
|
||||
}
|
||||
|
||||
temppower = stacks[SlotID(0)]->count * static_cast<ui64>(1000);
|
||||
refusedJoining = false;
|
||||
}
|
||||
|
||||
void CGCreature::newTurn(CRandomGenerator & rand) const
|
||||
{//Works only for stacks of single type of size up to 2 millions
|
||||
if (!notGrowingTeam)
|
||||
{
|
||||
if (stacks.begin()->second->count < VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP) && cb->getDate(Date::DAY_OF_WEEK) == 1 && cb->getDate(Date::DAY) > 1)
|
||||
{
|
||||
ui32 power = static_cast<ui32>(temppower * (100 + VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_PERCENT)) / 100);
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_COUNT, std::min<uint32_t>(power / 1000, VLC->settings()->getInteger(EGameSettings::CREATURES_WEEKLY_GROWTH_CAP))); //set new amount
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, power); //increase temppower
|
||||
}
|
||||
}
|
||||
if (VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_EXP, VLC->settings()->getInteger(EGameSettings::CREATURES_DAILY_STACK_EXPERIENCE)); //for testing purpose
|
||||
}
|
||||
void CGCreature::setPropertyDer(ui8 what, ui32 val)
|
||||
{
|
||||
switch (what)
|
||||
{
|
||||
case ObjProperty::MONSTER_COUNT:
|
||||
stacks[SlotID(0)]->count = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_POWER:
|
||||
temppower = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_EXP:
|
||||
giveStackExp(val);
|
||||
break;
|
||||
case ObjProperty::MONSTER_RESTORE_TYPE:
|
||||
formation.basicType = val;
|
||||
break;
|
||||
case ObjProperty::MONSTER_REFUSED_JOIN:
|
||||
refusedJoining = val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
{
|
||||
//calculate relative strength of hero and creatures armies
|
||||
double relStrength = static_cast<double>(h->getTotalStrength()) / getArmyStrength();
|
||||
|
||||
int powerFactor;
|
||||
if(relStrength >= 7)
|
||||
powerFactor = 11;
|
||||
|
||||
else if(relStrength >= 1)
|
||||
powerFactor = static_cast<int>(2 * (relStrength - 1));
|
||||
|
||||
else if(relStrength >= 0.5)
|
||||
powerFactor = -1;
|
||||
|
||||
else if(relStrength >= 0.333)
|
||||
powerFactor = -2;
|
||||
else
|
||||
powerFactor = -3;
|
||||
|
||||
std::set<CreatureID> myKindCres; //what creatures are the same kind as we
|
||||
const CCreature * myCreature = VLC->creh->objects[subID];
|
||||
myKindCres.insert(myCreature->getId()); //we
|
||||
myKindCres.insert(myCreature->upgrades.begin(), myCreature->upgrades.end()); //our upgrades
|
||||
|
||||
for(ConstTransitivePtr<CCreature> &crea : VLC->creh->objects)
|
||||
{
|
||||
if(vstd::contains(crea->upgrades, myCreature->getId())) //it's our base creatures
|
||||
myKindCres.insert(crea->getId());
|
||||
}
|
||||
|
||||
int count = 0; //how many creatures of similar kind has hero
|
||||
int totalCount = 0;
|
||||
|
||||
for(const auto & elem : h->Slots())
|
||||
{
|
||||
if(vstd::contains(myKindCres,elem.second->type->getId()))
|
||||
count += elem.second->count;
|
||||
totalCount += elem.second->count;
|
||||
}
|
||||
|
||||
int sympathy = 0; // 0 if hero have no similar creatures
|
||||
if(count)
|
||||
sympathy++; // 1 - if hero have at least 1 similar creature
|
||||
if(count*2 > totalCount)
|
||||
sympathy++; // 2 - hero have similar creatures more that 50%
|
||||
|
||||
int diplomacy = h->valOfBonuses(BonusType::WANDERING_CREATURES_JOIN_BONUS);
|
||||
int charisma = powerFactor + diplomacy + sympathy;
|
||||
|
||||
if(charisma < character)
|
||||
return FIGHT;
|
||||
|
||||
if (allowJoin)
|
||||
{
|
||||
if(diplomacy + sympathy + 1 >= character)
|
||||
return JOIN_FOR_FREE;
|
||||
|
||||
else if(diplomacy * 2 + sympathy + 1 >= character)
|
||||
return VLC->creatures()->getByIndex(subID)->getRecruitCost(EGameResID::GOLD) * getStackCount(SlotID(0)); //join for gold
|
||||
}
|
||||
|
||||
//we are still here - creatures have not joined hero, flee or fight
|
||||
|
||||
if (charisma > character && !neverFlees)
|
||||
return FLEE;
|
||||
else
|
||||
return FIGHT;
|
||||
}
|
||||
|
||||
void CGCreature::fleeDecision(const CGHeroInstance *h, ui32 pursue) const
|
||||
{
|
||||
if(refusedJoining)
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, false);
|
||||
|
||||
if(pursue)
|
||||
{
|
||||
fight(h);
|
||||
}
|
||||
else
|
||||
{
|
||||
cb->removeObject(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const
|
||||
{
|
||||
if(!accept)
|
||||
{
|
||||
if(takenAction(h,false) == FLEE)
|
||||
{
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_REFUSED_JOIN, true);
|
||||
flee(h);
|
||||
}
|
||||
else //they fight
|
||||
{
|
||||
h->showInfoDialog(87, 0, EInfoWindowMode::MODAL);//Insulted by your refusal of their offer, the monsters attack!
|
||||
fight(h);
|
||||
}
|
||||
}
|
||||
else //accepted
|
||||
{
|
||||
if (cb->getResource(h->tempOwner, EGameResID::GOLD) < cost) //player don't have enough gold!
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text << std::pair<ui8,ui32>(1,29); //You don't have enough gold
|
||||
cb->showInfoDialog(&iw);
|
||||
|
||||
//act as if player refused
|
||||
joinDecision(h,cost,false);
|
||||
return;
|
||||
}
|
||||
|
||||
//take gold
|
||||
if(cost)
|
||||
cb->giveResource(h->tempOwner,EGameResID::GOLD,-cost);
|
||||
|
||||
giveReward(h);
|
||||
cb->tryJoiningArmy(this, h, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::fight( const CGHeroInstance *h ) const
|
||||
{
|
||||
//split stacks
|
||||
//TODO: multiple creature types in a stack?
|
||||
int basicType = stacks.begin()->second->type->getId();
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_RESTORE_TYPE, basicType); //store info about creature stack
|
||||
|
||||
int stacksCount = getNumberOfStacks(h);
|
||||
//source: http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266335#focus
|
||||
|
||||
int amount = getStackCount(SlotID(0));
|
||||
int m = amount / stacksCount;
|
||||
int b = stacksCount * (m + 1) - amount;
|
||||
int a = stacksCount - b;
|
||||
|
||||
SlotID sourceSlot = stacks.begin()->first;
|
||||
for (int slotID = 1; slotID < a; ++slotID)
|
||||
{
|
||||
int stackSize = m + 1;
|
||||
cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
|
||||
}
|
||||
for (int slotID = a; slotID < stacksCount; ++slotID)
|
||||
{
|
||||
int stackSize = m;
|
||||
if (slotID) //don't do this when a = 0 -> stack is single
|
||||
cb->moveStack(StackLocation(this, sourceSlot), StackLocation(this, SlotID(slotID)), stackSize);
|
||||
}
|
||||
if (stacksCount > 1)
|
||||
{
|
||||
if (containsUpgradedStack()) //upgrade
|
||||
{
|
||||
SlotID slotID = SlotID(static_cast<si32>(std::floor(static_cast<float>(stacks.size()) / 2.0f)));
|
||||
const auto & upgrades = getStack(slotID).type->upgrades;
|
||||
if(!upgrades.empty())
|
||||
{
|
||||
auto it = RandomGeneratorUtil::nextItem(upgrades, CRandomGenerator::getDefault());
|
||||
cb->changeStackType(StackLocation(this, slotID), VLC->creh->objects[*it]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cb->startBattleI(h, this);
|
||||
|
||||
}
|
||||
|
||||
void CGCreature::flee( const CGHeroInstance * h ) const
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.addTxt(MetaString::ADVOB_TXT,91);
|
||||
ynd.text.addReplacement(MetaString::CRE_PL_NAMES, subID);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
|
||||
void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
||||
{
|
||||
if(result.winner == 0)
|
||||
{
|
||||
giveReward(hero);
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else if(result.winner > 1) // draw
|
||||
{
|
||||
// guarded reward is lost forever on draw
|
||||
cb->removeObject(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
//merge stacks into one
|
||||
TSlots::const_iterator i;
|
||||
CCreature * cre = VLC->creh->objects[formation.basicType];
|
||||
for(i = stacks.begin(); i != stacks.end(); i++)
|
||||
{
|
||||
if(cre->isMyUpgrade(i->second->type))
|
||||
{
|
||||
cb->changeStackType(StackLocation(this, i->first), cre); //un-upgrade creatures
|
||||
}
|
||||
}
|
||||
|
||||
//first stack has to be at slot 0 -> if original one got killed, move there first remaining stack
|
||||
if(!hasStackAtSlot(SlotID(0)))
|
||||
cb->moveStack(StackLocation(this, stacks.begin()->first), StackLocation(this, SlotID(0)), stacks.begin()->second->count);
|
||||
|
||||
while(stacks.size() > 1) //hopefully that's enough
|
||||
{
|
||||
// TODO it's either overcomplicated (if we assume there'll be only one stack) or buggy (if we allow multiple stacks... but that'll also cause troubles elsewhere)
|
||||
i = stacks.end();
|
||||
i--;
|
||||
SlotID slot = getSlotFor(i->second->type);
|
||||
if(slot == i->first) //no reason to move stack to its own slot
|
||||
break;
|
||||
else
|
||||
cb->moveStack(StackLocation(this, i->first), StackLocation(this, slot), i->second->count);
|
||||
}
|
||||
|
||||
cb->setObjProperty(id, ObjProperty::MONSTER_POWER, stacks.begin()->second->count * 1000); //remember casualties
|
||||
}
|
||||
}
|
||||
|
||||
void CGCreature::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const
|
||||
{
|
||||
auto action = takenAction(hero);
|
||||
if(!refusedJoining && action >= JOIN_FOR_FREE) //higher means price
|
||||
joinDecision(hero, action, answer);
|
||||
else if(action != FIGHT)
|
||||
fleeDecision(hero, answer);
|
||||
else
|
||||
assert(0);
|
||||
}
|
||||
|
||||
bool CGCreature::containsUpgradedStack() const
|
||||
{
|
||||
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=830557#focus
|
||||
|
||||
float a = 2992.911117f;
|
||||
float b = 14174.264968f;
|
||||
float c = 5325.181015f;
|
||||
float d = 32788.727920f;
|
||||
|
||||
int val = static_cast<int>(std::floor(a * pos.x + b * pos.y + c * pos.z + d));
|
||||
return ((val % 32768) % 100) < 50;
|
||||
}
|
||||
|
||||
int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
|
||||
{
|
||||
//source http://heroescommunity.com/viewthread.php3?TID=27539&PID=1266094#focus
|
||||
|
||||
double strengthRatio = static_cast<double>(hero->getArmyStrength()) / getArmyStrength();
|
||||
int split = 1;
|
||||
|
||||
if (strengthRatio < 0.5f)
|
||||
split = 7;
|
||||
else if (strengthRatio < 0.67f)
|
||||
split = 6;
|
||||
else if (strengthRatio < 1)
|
||||
split = 5;
|
||||
else if (strengthRatio < 1.5f)
|
||||
split = 4;
|
||||
else if (strengthRatio < 2)
|
||||
split = 3;
|
||||
else
|
||||
split = 2;
|
||||
|
||||
ui32 a = 1550811371u;
|
||||
ui32 b = 3359066809u;
|
||||
ui32 c = 1943276003u;
|
||||
ui32 d = 3174620878u;
|
||||
|
||||
ui32 R1 = a * static_cast<ui32>(pos.x) + b * static_cast<ui32>(pos.y) + c * static_cast<ui32>(pos.z) + d;
|
||||
ui32 R2 = (R1 >> 16) & 0x7fff;
|
||||
|
||||
int R4 = R2 % 100 + 1;
|
||||
|
||||
if (R4 <= 20)
|
||||
split -= 1;
|
||||
else if (R4 >= 80)
|
||||
split += 1;
|
||||
|
||||
vstd::amin(split, getStack(SlotID(0)).count); //can't divide into more stacks than creatures total
|
||||
vstd::amin(split, 7); //can't have more than 7 stacks
|
||||
|
||||
return split;
|
||||
}
|
||||
|
||||
void CGCreature::giveReward(const CGHeroInstance * h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
|
||||
if(!resources.empty())
|
||||
{
|
||||
cb->giveResources(h->tempOwner, resources);
|
||||
for(int i = 0; i < resources.size(); i++)
|
||||
{
|
||||
if(resources[i] > 0)
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
if(gainedArtifact != ArtifactID::NONE)
|
||||
{
|
||||
cb->giveHeroNewArtifact(h, VLC->arth->objects[gainedArtifact], ArtifactPosition::FIRST_AVAILABLE);
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, gainedArtifact, 0, 0);
|
||||
}
|
||||
|
||||
if(!iw.components.empty())
|
||||
{
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 183); // % has found treasure
|
||||
iw.text.addReplacement(h->getNameTranslated());
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
}
|
||||
|
||||
static const std::vector<std::string> CHARACTER_JSON =
|
||||
{
|
||||
"compliant", "friendly", "aggressive", "hostile", "savage"
|
||||
};
|
||||
|
||||
void CGCreature::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeEnum("character", character, CHARACTER_JSON);
|
||||
|
||||
if(handler.saving)
|
||||
{
|
||||
if(hasStackAtSlot(SlotID(0)))
|
||||
{
|
||||
si32 amount = getStack(SlotID(0)).count;
|
||||
handler.serializeInt("amount", amount, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
si32 amount = 0;
|
||||
handler.serializeInt("amount", amount);
|
||||
auto * hlp = new CStackInstance();
|
||||
hlp->count = amount;
|
||||
//type will be set during initialization
|
||||
putStack(SlotID(0), hlp);
|
||||
}
|
||||
|
||||
resources.serializeJson(handler, "rewardResources");
|
||||
|
||||
handler.serializeId("rewardArtifact", gainedArtifact, ArtifactID(ArtifactID::NONE));
|
||||
|
||||
handler.serializeBool("noGrowing", notGrowingTeam);
|
||||
handler.serializeBool("neverFlees", neverFlees);
|
||||
handler.serializeString("rewardMessage", message);
|
||||
}
|
||||
|
||||
//CGMine
|
||||
void CGMine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
@ -636,7 +82,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->tempOwner;
|
||||
ynd.text.addTxt(MetaString::ADVOB_TXT, subID == 7 ? 84 : 187);
|
||||
ynd.text.appendLocalString(EMetaText::ADVOB_TXT, subID == 7 ? 84 : 187);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
return;
|
||||
}
|
||||
@ -710,7 +156,7 @@ void CGMine::flagMine(const PlayerColor & player) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.soundID = soundBase::FLAGMINE;
|
||||
iw.text.addTxt(MetaString::MINE_EVNTS, producedResource); //not use subID, abandoned mines uses default mine texts
|
||||
iw.text.appendLocalString(EMetaText::MINE_EVNTS, producedResource); //not use subID, abandoned mines uses default mine texts
|
||||
iw.player = player;
|
||||
iw.components.emplace_back(Component::EComponentType::RESOURCE, producedResource, producedQuantity, -1);
|
||||
cb->showInfoDialog(&iw);
|
||||
@ -822,7 +268,7 @@ void CGResource::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->getOwner();
|
||||
ynd.text << message;
|
||||
ynd.text.appendRawString(message);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
else
|
||||
@ -842,13 +288,13 @@ void CGResource::collectRes(const PlayerColor & player) const
|
||||
if(!message.empty())
|
||||
{
|
||||
sii.type = EInfoWindowMode::AUTO;
|
||||
sii.text << message;
|
||||
sii.text.appendRawString(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
sii.type = EInfoWindowMode::INFO;
|
||||
sii.text.addTxt(MetaString::ADVOB_TXT,113);
|
||||
sii.text.addReplacement(MetaString::RES_NAMES, subID);
|
||||
sii.text.appendLocalString(EMetaText::ADVOB_TXT,113);
|
||||
sii.text.replaceLocalString(EMetaText::RES_NAMES, subID);
|
||||
}
|
||||
sii.components.emplace_back(Component::EComponentType::RESOURCE,subID,amount,0);
|
||||
sii.soundID = soundBase::pickup01 + CRandomGenerator::getDefault().nextInt(6);
|
||||
@ -1189,7 +635,7 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 168);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 168);
|
||||
iw.components.emplace_back(CStackBasicDescriptor(h->getCreature(targetstack), -countToTake));
|
||||
cb->showInfoDialog(&iw);
|
||||
cb->changeStackCount(StackLocation(h, targetstack), -countToTake);
|
||||
@ -1281,9 +727,9 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
iw.components.emplace_back(Component::EComponentType::ARTIFACT, subID, 0, 0);
|
||||
if(message.length())
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
else
|
||||
iw.text.addTxt(MetaString::ART_EVNTS, subID);
|
||||
iw.text.appendLocalString(EMetaText::ART_EVNTS, subID);
|
||||
}
|
||||
break;
|
||||
case Obj::SPELL_SCROLL:
|
||||
@ -1291,11 +737,11 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
||||
int spellID = storedArtifact->getScrollSpellID();
|
||||
iw.components.emplace_back(Component::EComponentType::SPELL, spellID, 0, 0);
|
||||
if(message.length())
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,135);
|
||||
iw.text.addReplacement(MetaString::SPELL_NAME, spellID);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,135);
|
||||
iw.text.replaceLocalString(EMetaText::SPELL_NAME, spellID);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -1303,7 +749,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 2);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 2);
|
||||
}
|
||||
cb->showInfoDialog(&iw);
|
||||
pick(h);
|
||||
@ -1317,14 +763,14 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->getOwner();
|
||||
if(message.length())
|
||||
ynd.text << message;
|
||||
ynd.text.appendRawString(message);
|
||||
else
|
||||
{
|
||||
// TODO: Guard text is more complex in H3, see mantis issue 2325 for details
|
||||
ynd.text.addTxt(MetaString::GENERAL_TXT, 420);
|
||||
ynd.text.addReplacement("");
|
||||
ynd.text.addReplacement(getArmyDescription());
|
||||
ynd.text.addReplacement(MetaString::GENERAL_TXT, 43); // creatures
|
||||
ynd.text.appendLocalString(EMetaText::GENERAL_TXT, 420);
|
||||
ynd.text.replaceRawString("");
|
||||
ynd.text.replaceRawString(getArmyDescription());
|
||||
ynd.text.replaceLocalString(EMetaText::GENERAL_TXT, 43); // creatures
|
||||
}
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
@ -1335,7 +781,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
BlockingDialog ynd(true,false);
|
||||
ynd.player = h->getOwner();
|
||||
ynd.text << message;
|
||||
ynd.text.appendRawString(message);
|
||||
cb->showBlockingDialog(&ynd);
|
||||
}
|
||||
else
|
||||
@ -1432,8 +878,8 @@ void CGWitchHut::onHeroVisit( const CGHeroInstance * h ) const
|
||||
cb->changeSecSkill(h, SecondarySkill(ability), 1, true);
|
||||
}
|
||||
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,txt_id);
|
||||
iw.text.addReplacement(MetaString::SEC_SKILL_NAME, ability);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,txt_id);
|
||||
iw.text.replaceLocalString(EMetaText::SEC_SKILL_NAME, ability);
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
|
||||
@ -1494,7 +940,7 @@ void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
|
||||
case Obj::REDWOOD_OBSERVATORY:
|
||||
case Obj::PILLAR_OF_FIRE:
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,98 + (ID==Obj::PILLAR_OF_FIRE));
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,98 + (ID==Obj::PILLAR_OF_FIRE));
|
||||
|
||||
FoWChange fw;
|
||||
fw.player = h->tempOwner;
|
||||
@ -1505,7 +951,7 @@ void CGObservatory::onHeroVisit( const CGHeroInstance * h ) const
|
||||
}
|
||||
case Obj::COVER_OF_DARKNESS:
|
||||
{
|
||||
iw.text.addTxt (MetaString::ADVOB_TXT, 31);
|
||||
iw.text.appendLocalString (EMetaText::ADVOB_TXT, 31);
|
||||
for (auto & player : cb->gameState()->players)
|
||||
{
|
||||
if (cb->getPlayerStatus(player.first) == EPlayerStatus::INGAME &&
|
||||
@ -1533,20 +979,20 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->getOwner();
|
||||
iw.text = visitText;
|
||||
iw.text.addTxt(MetaString::SPELL_NAME,spell);
|
||||
iw.text << ".";
|
||||
iw.text.appendLocalString(EMetaText::SPELL_NAME,spell);
|
||||
iw.text.appendRawString(".");
|
||||
|
||||
if(!h->getArt(ArtifactPosition::SPELLBOOK))
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,131);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,131);
|
||||
}
|
||||
else if(h->spellbookContainsSpell(spell))//hero already knows the spell
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,174);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,174);
|
||||
}
|
||||
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);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,130);
|
||||
}
|
||||
else //give spell
|
||||
{
|
||||
@ -1609,7 +1055,7 @@ void CGSignBottle::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->getOwner();
|
||||
iw.text << message;
|
||||
iw.text.appendRawString(message);
|
||||
cb->showInfoDialog(&iw);
|
||||
|
||||
if(ID == Obj::OCEAN_BOTTLE)
|
||||
@ -1637,7 +1083,7 @@ void CGScholar::onHeroVisit( const CGHeroInstance * h ) const
|
||||
InfoWindow iw;
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.player = h->getOwner();
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,115);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,115);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
@ -1848,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
|
||||
@ -1886,7 +1327,7 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
|
||||
if(h->hasBonusFrom(BonusSource::OBJECT,ID)) //has already visited Sirens
|
||||
{
|
||||
iw.type = EInfoWindowMode::AUTO;
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,133);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,133);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1912,13 +1353,13 @@ void CGSirens::onHeroVisit( const CGHeroInstance * h ) const
|
||||
if(xp)
|
||||
{
|
||||
xp = h->calculateXp(static_cast<int>(xp));
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,132);
|
||||
iw.text.addReplacement(static_cast<int>(xp));
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,132);
|
||||
iw.text.replaceNumber(static_cast<int>(xp));
|
||||
cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, xp, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,134);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT,134);
|
||||
}
|
||||
}
|
||||
cb->showInfoDialog(&iw);
|
||||
@ -1998,7 +1439,7 @@ void CCartographer::onHeroVisit( const CGHeroInstance * h ) const
|
||||
assert(text);
|
||||
BlockingDialog bd (true, false);
|
||||
bd.player = h->getOwner();
|
||||
bd.text.addTxt (MetaString::ADVOB_TXT, text);
|
||||
bd.text.appendLocalString (EMetaText::ADVOB_TXT, text);
|
||||
cb->showBlockingDialog (&bd);
|
||||
}
|
||||
else //if he cannot afford
|
||||
@ -2061,7 +1502,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
|
||||
if(!wasVisited(team))
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 96);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 96);
|
||||
cb->sendAndApply(&iw);
|
||||
|
||||
// increment general visited obelisks counter
|
||||
@ -2077,7 +1518,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
|
||||
}
|
||||
else
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT, 97);
|
||||
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 97);
|
||||
cb->sendAndApply(&iw);
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "CArmedInstance.h"
|
||||
#include "../ResourceSet.h"
|
||||
#include "../MetaString.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -40,80 +40,6 @@ public:
|
||||
static constexpr int OBJPROP_VISITED = 10;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGCreature : public CArmedInstance //creatures on map
|
||||
{
|
||||
public:
|
||||
enum Action {
|
||||
FIGHT = -2, FLEE = -1, JOIN_FOR_FREE = 0 //values > 0 mean gold price
|
||||
};
|
||||
|
||||
enum Character {
|
||||
COMPLIANT = 0, FRIENDLY = 1, AGRESSIVE = 2, HOSTILE = 3, SAVAGE = 4
|
||||
};
|
||||
|
||||
ui32 identifier; //unique code for this monster (used in missions)
|
||||
si8 character; //character of this set of creatures (0 - the most friendly, 4 - the most hostile) => on init changed to -4 (compliant) ... 10 value (savage)
|
||||
std::string message; //message printed for attacking hero
|
||||
TResources resources; // resources given to hero that has won with monsters
|
||||
ArtifactID gainedArtifact; //ID of artifact gained to hero, -1 if none
|
||||
bool neverFlees; //if true, the troops will never flee
|
||||
bool notGrowingTeam; //if true, number of units won't grow
|
||||
ui64 temppower; //used to handle fractional stack growth for tiny stacks
|
||||
|
||||
bool refusedJoining;
|
||||
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
std::string getHoverText(PlayerColor player) const override;
|
||||
std::string getHoverText(const CGHeroInstance * hero) const override;
|
||||
void initObj(CRandomGenerator & rand) override;
|
||||
void newTurn(CRandomGenerator & rand) const override;
|
||||
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
|
||||
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
|
||||
|
||||
//stack formation depends on position,
|
||||
bool containsUpgradedStack() const;
|
||||
int getNumberOfStacks(const CGHeroInstance *hero) const;
|
||||
|
||||
struct DLL_LINKAGE formationInfo // info about merging stacks after battle back into one
|
||||
{
|
||||
si32 basicType;
|
||||
ui8 upgrade; //random seed used to determine number of stacks and is there's upgraded stack
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & basicType;
|
||||
h & upgrade;
|
||||
}
|
||||
} formation;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & static_cast<CArmedInstance&>(*this);
|
||||
h & identifier;
|
||||
h & character;
|
||||
h & message;
|
||||
h & resources;
|
||||
h & gainedArtifact;
|
||||
h & neverFlees;
|
||||
h & notGrowingTeam;
|
||||
h & temppower;
|
||||
h & refusedJoining;
|
||||
h & formation;
|
||||
}
|
||||
protected:
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
||||
private:
|
||||
void fight(const CGHeroInstance *h) const;
|
||||
void flee( const CGHeroInstance * h ) const;
|
||||
void fleeDecision(const CGHeroInstance *h, ui32 pursue) const;
|
||||
void joinDecision(const CGHeroInstance *h, int cost, ui32 accept) const;
|
||||
|
||||
int takenAction(const CGHeroInstance *h, bool allowJoin=true) const; //action on confrontation: -2 - fight, -1 - flee, >=0 - will join for given value of gold (may be 0)
|
||||
void giveReward(const CGHeroInstance * h) const;
|
||||
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CGSignBottle : public CGObjectInstance //signs and ocean bottles
|
||||
{
|
||||
public:
|
||||
@ -433,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)
|
||||
|
@ -398,17 +398,17 @@ void CMap::checkForObjectives()
|
||||
switch (cond.condition)
|
||||
{
|
||||
case EventCondition::HAVE_ARTIFACT:
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->objects[cond.objectType]->getNameTranslated());
|
||||
event.onFulfill.replaceTextID(VLC->artifacts()->getByIndex(cond.objectType)->getNameTextID());
|
||||
break;
|
||||
|
||||
case EventCondition::HAVE_CREATURES:
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->objects[cond.objectType]->getNameSingularTranslated());
|
||||
boost::algorithm::replace_first(event.onFulfill, "%d", std::to_string(cond.value));
|
||||
event.onFulfill.replaceTextID(VLC->creatures()->getByIndex(cond.objectType)->getNameSingularTextID());
|
||||
event.onFulfill.replaceNumber(cond.value);
|
||||
break;
|
||||
|
||||
case EventCondition::HAVE_RESOURCES:
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", VLC->generaltexth->restypes[cond.objectType]);
|
||||
boost::algorithm::replace_first(event.onFulfill, "%d", std::to_string(cond.value));
|
||||
event.onFulfill.replaceLocalString(EMetaText::RES_NAMES, cond.objectType);
|
||||
event.onFulfill.replaceNumber(cond.value);
|
||||
break;
|
||||
|
||||
case EventCondition::HAVE_BUILDING:
|
||||
@ -424,10 +424,10 @@ void CMap::checkForObjectives()
|
||||
{
|
||||
const auto * town = dynamic_cast<const CGTownInstance *>(cond.object);
|
||||
if (town)
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", town->getNameTranslated());
|
||||
event.onFulfill.replaceRawString(town->getNameTranslated());
|
||||
const auto * hero = dynamic_cast<const CGHeroInstance *>(cond.object);
|
||||
if (hero)
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", hero->getNameTranslated());
|
||||
event.onFulfill.replaceRawString(hero->getNameTranslated());
|
||||
}
|
||||
break;
|
||||
|
||||
@ -439,7 +439,7 @@ void CMap::checkForObjectives()
|
||||
{
|
||||
const auto * hero = dynamic_cast<const CGHeroInstance *>(cond.object);
|
||||
if (hero)
|
||||
boost::algorithm::replace_first(event.onFulfill, "%s", hero->getNameTranslated());
|
||||
event.onFulfill.replaceRawString(hero->getNameTranslated());
|
||||
}
|
||||
break;
|
||||
case EventCondition::TRANSPORT:
|
||||
@ -499,7 +499,6 @@ void CMap::removeQuestInstance(CQuest * quest)
|
||||
}
|
||||
|
||||
void CMap::setUniqueInstanceName(CGObjectInstance * obj)
|
||||
|
||||
{
|
||||
//this gives object unique name even if objects are removed later
|
||||
|
||||
|
@ -96,29 +96,29 @@ void CMapHeader::setupEvents()
|
||||
//Victory condition - defeat all
|
||||
TriggeredEvent standardVictory;
|
||||
standardVictory.effect.type = EventEffect::VICTORY;
|
||||
standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5];
|
||||
standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
|
||||
standardVictory.identifier = "standardVictory";
|
||||
standardVictory.description.clear(); // TODO: display in quest window
|
||||
standardVictory.onFulfill = VLC->generaltexth->allTexts[659];
|
||||
standardVictory.onFulfill.appendTextID("core.genrltxt.659");
|
||||
standardVictory.trigger = EventExpression(victoryCondition);
|
||||
|
||||
//Loss condition - 7 days without town
|
||||
TriggeredEvent standardDefeat;
|
||||
standardDefeat.effect.type = EventEffect::DEFEAT;
|
||||
standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8];
|
||||
standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
|
||||
standardDefeat.identifier = "standardDefeat";
|
||||
standardDefeat.description.clear(); // TODO: display in quest window
|
||||
standardDefeat.onFulfill = VLC->generaltexth->allTexts[7];
|
||||
standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
||||
standardDefeat.trigger = EventExpression(defeatCondition);
|
||||
|
||||
triggeredEvents.push_back(standardVictory);
|
||||
triggeredEvents.push_back(standardDefeat);
|
||||
|
||||
victoryIconIndex = 11;
|
||||
victoryMessage = VLC->generaltexth->victoryConditions[0];
|
||||
victoryMessage.appendTextID("core.vcdesc.0");
|
||||
|
||||
defeatIconIndex = 3;
|
||||
defeatMessage = VLC->generaltexth->lossCondtions[0];
|
||||
defeatMessage.appendTextID("core.lcdesc.0");
|
||||
}
|
||||
|
||||
CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72),
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "../CModVersion.h"
|
||||
#include "../LogicalExpression.h"
|
||||
#include "../int3.h"
|
||||
#include "../MetaString.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@ -159,7 +160,7 @@ struct DLL_LINKAGE EventEffect
|
||||
si8 type;
|
||||
|
||||
/// message that will be sent to other players
|
||||
std::string toOtherMessage;
|
||||
MetaString toOtherMessage;
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler & h, const int version)
|
||||
@ -178,10 +179,10 @@ struct DLL_LINKAGE TriggeredEvent
|
||||
std::string identifier;
|
||||
|
||||
/// string-description, for use in UI (capture town to win)
|
||||
std::string description;
|
||||
MetaString description;
|
||||
|
||||
/// Message that will be displayed when this event is triggered (You captured town. You won!)
|
||||
std::string onFulfill;
|
||||
MetaString onFulfill;
|
||||
|
||||
/// Effect of this event. TODO: refactor into something more flexible
|
||||
EventEffect effect;
|
||||
@ -229,8 +230,8 @@ public:
|
||||
/// maximum level for heroes. This is the default value.
|
||||
ui8 levelLimit;
|
||||
|
||||
std::string victoryMessage;
|
||||
std::string defeatMessage;
|
||||
MetaString victoryMessage;
|
||||
MetaString defeatMessage;
|
||||
ui16 victoryIconIndex;
|
||||
ui16 defeatIconIndex;
|
||||
|
||||
|
@ -135,21 +135,19 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint32_t hotaVersion)
|
||||
if(hotaVersion < 3)
|
||||
{
|
||||
result.artifactsCount = 163; // + HotA artifacts
|
||||
result.heroesCount = 177; // + Cove
|
||||
result.heroesPortraitsCount = 187; // + Cove
|
||||
result.heroesCount = 178; // + Cove
|
||||
result.heroesPortraitsCount = 185; // + Cove
|
||||
}
|
||||
if(hotaVersion == 3)
|
||||
{
|
||||
result.artifactsCount = 165; // + HotA artifacts
|
||||
result.heroesCount = 177; // + Cove + Giselle
|
||||
result.heroesCount = 179; // + Cove + Giselle
|
||||
result.heroesPortraitsCount = 188; // + Cove + Giselle
|
||||
}
|
||||
|
||||
assert((result.heroesCount + 7) / 8 == result.heroesBytes);
|
||||
assert((result.artifactsCount + 7) / 8 == result.artifactsBytes);
|
||||
|
||||
result.heroesCount = 179; // + Cove
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../mapObjects/CGCreature.h"
|
||||
#include "../mapObjects/MapObjects.h"
|
||||
#include "../mapObjects/ObjectTemplate.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
@ -133,13 +134,15 @@ void CMapLoaderH3M::readHeader()
|
||||
|
||||
if(hotaVersion > 0)
|
||||
{
|
||||
reader->skipZero(1);
|
||||
//TODO: HotA
|
||||
bool isMirrorMap = reader->readBool();
|
||||
bool isArenaMap = reader->readBool();
|
||||
|
||||
//TODO: HotA
|
||||
if (isMirrorMap)
|
||||
logGlobal->warn("Map '%s': Mirror maps are not yet supported!", mapName);
|
||||
|
||||
if (isArenaMap)
|
||||
{
|
||||
logGlobal->warn("Map '%s': Arena maps are not supported!", mapName);
|
||||
}
|
||||
}
|
||||
|
||||
if(hotaVersion > 1)
|
||||
@ -248,7 +251,7 @@ void CMapLoaderH3M::readPlayerInfo()
|
||||
|
||||
if(playerInfo.mainCustomHeroId != -1)
|
||||
{
|
||||
playerInfo.mainCustomHeroPortrait = reader->readHero().getNum();
|
||||
playerInfo.mainCustomHeroPortrait = reader->readHeroPortrait();
|
||||
playerInfo.mainCustomHeroName = readLocalizedString(TextIdentifier("header", "player", i, "mainHeroName"));
|
||||
}
|
||||
|
||||
@ -297,6 +300,8 @@ enum class ELossConditionType : uint8_t
|
||||
void CMapLoaderH3M::readVictoryLossConditions()
|
||||
{
|
||||
mapHeader->triggeredEvents.clear();
|
||||
mapHeader->victoryMessage.clear();
|
||||
mapHeader->defeatMessage.clear();
|
||||
|
||||
auto vicCondition = static_cast<EVictoryConditionType>(reader->readUInt8());
|
||||
|
||||
@ -306,18 +311,18 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
|
||||
TriggeredEvent standardVictory;
|
||||
standardVictory.effect.type = EventEffect::VICTORY;
|
||||
standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5];
|
||||
standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
|
||||
standardVictory.identifier = "standardVictory";
|
||||
standardVictory.description.clear(); // TODO: display in quest window
|
||||
standardVictory.onFulfill = VLC->generaltexth->allTexts[659];
|
||||
standardVictory.onFulfill.appendTextID("core.genrltxt.659");
|
||||
standardVictory.trigger = EventExpression(victoryCondition);
|
||||
|
||||
TriggeredEvent standardDefeat;
|
||||
standardDefeat.effect.type = EventEffect::DEFEAT;
|
||||
standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8];
|
||||
standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
|
||||
standardDefeat.identifier = "standardDefeat";
|
||||
standardDefeat.description.clear(); // TODO: display in quest window
|
||||
standardDefeat.onFulfill = VLC->generaltexth->allTexts[7];
|
||||
standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
||||
standardDefeat.trigger = EventExpression(defeatCondition);
|
||||
|
||||
// Specific victory conditions
|
||||
@ -326,7 +331,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
// create normal condition
|
||||
mapHeader->triggeredEvents.push_back(standardVictory);
|
||||
mapHeader->victoryIconIndex = 11;
|
||||
mapHeader->victoryMessage = VLC->generaltexth->victoryConditions[0];
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.0");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -336,7 +341,6 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
specialVictory.description.clear(); // TODO: display in quest window
|
||||
|
||||
mapHeader->victoryIconIndex = static_cast<ui16>(vicCondition);
|
||||
mapHeader->victoryMessage = VLC->generaltexth->victoryConditions[static_cast<size_t>(vicCondition) + 1];
|
||||
|
||||
bool allowNormalVictory = reader->readBool();
|
||||
bool appliesToAI = reader->readBool();
|
||||
@ -365,9 +369,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
EventCondition cond(EventCondition::HAVE_ARTIFACT);
|
||||
cond.objectType = reader->readArtifact();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[281];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[280];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.281");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.280");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.1");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::GATHERTROOP:
|
||||
@ -376,9 +382,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = reader->readCreature();
|
||||
cond.value = reader->readInt32();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[277];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[276];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.277");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.6");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.2");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::GATHERRESOURCE:
|
||||
@ -387,9 +395,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = reader->readUInt8();
|
||||
cond.value = reader->readInt32();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[279];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[278];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.278");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.3");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::BUILDCITY:
|
||||
@ -402,9 +412,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = BuildingID::FORT + reader->readUInt8();
|
||||
oper.expressions.emplace_back(cond);
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[283];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[282];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.282");
|
||||
specialVictory.trigger = EventExpression(oper);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.4");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::BUILDGRAIL:
|
||||
@ -415,9 +427,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
if(cond.position.z > 2)
|
||||
cond.position = int3(-1, -1, -1);
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[285];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[284];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.285");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.284");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.5");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::BEATHERO:
|
||||
@ -426,9 +440,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = Obj::HERO;
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[253];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[252];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.252");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.6");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::CAPTURECITY:
|
||||
@ -437,9 +453,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = Obj::TOWN;
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[250];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[249];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.249");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.7");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::BEATMONSTER:
|
||||
@ -448,9 +466,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = Obj::MONSTER;
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[287];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[286];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.287");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.286");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.8");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::TAKEDWELLINGS:
|
||||
@ -459,9 +479,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR1));
|
||||
oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR4));
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[289];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[288];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.289");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.288");
|
||||
specialVictory.trigger = EventExpression(oper);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.9");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::TAKEMINES:
|
||||
@ -469,9 +491,11 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
EventCondition cond(EventCondition::CONTROL);
|
||||
cond.objectType = Obj::MINE;
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[291];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[290];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.291");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.290");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.10");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::TRANSPORTITEM:
|
||||
@ -480,20 +504,37 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.objectType = reader->readUInt8();
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[293];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[292];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.292");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.11");
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::HOTA_ELIMINATE_ALL_MONSTERS:
|
||||
//TODO: HOTA
|
||||
logGlobal->warn("Map '%s': Victory condition 'Eliminate all monsters' is not implemented!", mapName);
|
||||
{
|
||||
EventCondition cond(EventCondition::DESTROY);
|
||||
cond.objectType = Obj::MONSTER;
|
||||
|
||||
specialVictory.effect.toOtherMessage.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toOthers");
|
||||
specialVictory.onFulfill.appendTextID("vcmi.map.victoryCondition.eliminateMonsters.toSelf");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.12");
|
||||
mapHeader->victoryIconIndex = 12;
|
||||
break;
|
||||
}
|
||||
case EVictoryConditionType::HOTA_SURVIVE_FOR_DAYS:
|
||||
{
|
||||
//TODO: HOTA
|
||||
uint32_t daysToSurvive = reader->readUInt32(); // Number of days
|
||||
logGlobal->warn("Map '%s': Victory condition 'Survive for %d days' is not implemented!", mapName, daysToSurvive);
|
||||
EventCondition cond(EventCondition::DAYS_PASSED);
|
||||
cond.value = reader->readUInt32();
|
||||
|
||||
specialVictory.effect.toOtherMessage.appendTextID("vcmi.map.victoryCondition.daysPassed.toOthers");
|
||||
specialVictory.onFulfill.appendTextID("vcmi.map.victoryCondition.daysPassed.toSelf");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.13");
|
||||
mapHeader->victoryIconIndex = 13;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -514,8 +555,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
// if normal victory allowed - add one more quest
|
||||
if(allowNormalVictory)
|
||||
{
|
||||
mapHeader->victoryMessage += " / ";
|
||||
mapHeader->victoryMessage += VLC->generaltexth->victoryConditions[0];
|
||||
mapHeader->victoryMessage.appendRawString(" / ");
|
||||
mapHeader->victoryMessage.appendTextID("core.vcdesc.0");
|
||||
mapHeader->triggeredEvents.push_back(standardVictory);
|
||||
}
|
||||
mapHeader->triggeredEvents.push_back(specialVictory);
|
||||
@ -526,18 +567,17 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
if(lossCond == ELossConditionType::LOSSSTANDARD)
|
||||
{
|
||||
mapHeader->defeatIconIndex = 3;
|
||||
mapHeader->defeatMessage = VLC->generaltexth->lossCondtions[0];
|
||||
mapHeader->defeatMessage.appendTextID("core.lcdesc.0");
|
||||
}
|
||||
else
|
||||
{
|
||||
TriggeredEvent specialDefeat;
|
||||
specialDefeat.effect.type = EventEffect::DEFEAT;
|
||||
specialDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[5];
|
||||
specialDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.5");
|
||||
specialDefeat.identifier = "specialDefeat";
|
||||
specialDefeat.description.clear(); // TODO: display in quest window
|
||||
|
||||
mapHeader->defeatIconIndex = static_cast<ui16>(lossCond);
|
||||
mapHeader->defeatMessage = VLC->generaltexth->lossCondtions[static_cast<size_t>(lossCond) + 1];
|
||||
|
||||
switch(lossCond)
|
||||
{
|
||||
@ -549,8 +589,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
noneOf.expressions.emplace_back(cond);
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[251];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.251");
|
||||
specialDefeat.trigger = EventExpression(noneOf);
|
||||
|
||||
mapHeader->defeatMessage.appendTextID("core.lcdesc.1");
|
||||
break;
|
||||
}
|
||||
case ELossConditionType::LOSSHERO:
|
||||
@ -561,8 +603,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
cond.position = reader->readInt3();
|
||||
|
||||
noneOf.expressions.emplace_back(cond);
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[253];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.253");
|
||||
specialDefeat.trigger = EventExpression(noneOf);
|
||||
|
||||
mapHeader->defeatMessage.appendTextID("core.lcdesc.2");
|
||||
break;
|
||||
}
|
||||
case ELossConditionType::TIMEEXPIRES:
|
||||
@ -570,8 +614,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
|
||||
EventCondition cond(EventCondition::DAYS_PASSED);
|
||||
cond.value = reader->readUInt16();
|
||||
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[254];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.254");
|
||||
specialDefeat.trigger = EventExpression(cond);
|
||||
|
||||
mapHeader->defeatMessage.appendTextID("core.lcdesc.3");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -640,7 +686,7 @@ void CMapLoaderH3M::readDisposedHeroes()
|
||||
for(int g = 0; g < disp; ++g)
|
||||
{
|
||||
map->disposedHeroes[g].heroId = reader->readHero().getNum();
|
||||
map->disposedHeroes[g].portrait = reader->readHero().getNum();
|
||||
map->disposedHeroes[g].portrait = reader->readHeroPortrait();
|
||||
map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId));
|
||||
map->disposedHeroes[g].players = reader->readUInt8();
|
||||
}
|
||||
@ -1299,24 +1345,6 @@ CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition, std::sh
|
||||
return object;
|
||||
}
|
||||
|
||||
CGObjectInstance * CMapLoaderH3M::readBorderGuard()
|
||||
{
|
||||
return new CGBorderGuard();
|
||||
}
|
||||
|
||||
CGObjectInstance * CMapLoaderH3M::readBorderGate(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate)
|
||||
{
|
||||
if(objectTemplate->subid < 1000)
|
||||
return new CGBorderGate();
|
||||
|
||||
//TODO: HotA - grave has same ID as border gate? WTF?
|
||||
if(objectTemplate->subid == 1001)
|
||||
return new CGObjectInstance();
|
||||
|
||||
logGlobal->warn("Map '%s: Quest gates at %s are not implemented!", mapName, mapPosition.toString());
|
||||
return readQuestGuard(mapPosition);
|
||||
}
|
||||
|
||||
CGObjectInstance * CMapLoaderH3M::readLighthouse(const int3 & mapPosition)
|
||||
{
|
||||
auto * object = new CGLighthouse();
|
||||
@ -1453,12 +1481,6 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr<const ObjectTemplat
|
||||
case Obj::HERO_PLACEHOLDER:
|
||||
return readHeroPlaceholder(mapPosition);
|
||||
|
||||
case Obj::BORDERGUARD:
|
||||
return readBorderGuard();
|
||||
|
||||
case Obj::BORDER_GATE:
|
||||
return readBorderGate(mapPosition, objectTemplate);
|
||||
|
||||
case Obj::PYRAMID:
|
||||
return readPyramid(mapPosition, objectTemplate);
|
||||
|
||||
@ -1601,7 +1623,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
||||
}
|
||||
|
||||
PlayerColor owner = reader->readPlayer();
|
||||
object->subID = reader->readUInt8();
|
||||
object->subID = reader->readHero().getNum();
|
||||
|
||||
//If hero of this type has been predefined, use that as a base.
|
||||
//Instance data will overwrite the predefined values where appropriate.
|
||||
@ -1617,7 +1639,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
||||
}
|
||||
|
||||
setOwnerAndValidate(mapPosition, object, owner);
|
||||
object->portrait = object->subID;
|
||||
object->portrait = CGHeroInstance::UNINITIALIZED_PORTRAIT;
|
||||
|
||||
for(auto & elem : map->disposedHeroes)
|
||||
{
|
||||
@ -1652,7 +1674,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
|
||||
|
||||
bool hasPortrait = reader->readBool();
|
||||
if(hasPortrait)
|
||||
object->portrait = reader->readHero().getNum();
|
||||
object->portrait = reader->readHeroPortrait();
|
||||
|
||||
bool hasSecSkills = reader->readBool();
|
||||
if(hasSecSkills)
|
||||
@ -1955,7 +1977,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
||||
|
||||
if(missionSubID == 0)
|
||||
{
|
||||
guard->quest->missionType = CQuest::MISSION_HOTA_HERO_CLASS;
|
||||
guard->quest->missionType = CQuest::MISSION_NONE; //TODO: CQuest::MISSION_HOTA_HERO_CLASS;
|
||||
std::set<HeroClassID> heroClasses;
|
||||
reader->readBitmaskHeroClassesSized(heroClasses, false);
|
||||
|
||||
@ -1964,7 +1986,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
||||
}
|
||||
if(missionSubID == 1)
|
||||
{
|
||||
guard->quest->missionType = CQuest::MISSION_HOTA_REACH_DATE;
|
||||
guard->quest->missionType = CQuest::MISSION_NONE; //TODO: CQuest::MISSION_HOTA_REACH_DATE;
|
||||
uint32_t daysPassed = reader->readUInt32();
|
||||
|
||||
logGlobal->warn("Map '%s': Quest at %s 'Wait till %d days passed' is not implemented!", mapName, position.toString(), daysPassed);
|
||||
|
@ -177,8 +177,6 @@ private:
|
||||
CGObjectInstance * readHeroPlaceholder(const int3 & position);
|
||||
CGObjectInstance * readGrail(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate);
|
||||
CGObjectInstance * readPyramid(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
|
||||
CGObjectInstance * readBorderGuard();
|
||||
CGObjectInstance * readBorderGate(const int3 & position, std::shared_ptr<const ObjectTemplate> objTempl);
|
||||
CGObjectInstance * readQuestGuard(const int3 & position);
|
||||
CGObjectInstance * readShipyard(const int3 & mapPosition, std::shared_ptr<const ObjectTemplate> objectTemplate);
|
||||
CGObjectInstance * readLighthouse(const int3 & mapPosition);
|
||||
|
@ -423,10 +423,10 @@ void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
|
||||
|
||||
handler.serializeLIC("allowedHeroes", &HeroTypeID::decode, &HeroTypeID::encode, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes);
|
||||
|
||||
handler.serializeString("victoryString", mapHeader->victoryMessage);
|
||||
// handler.serializeString("victoryString", mapHeader->victoryMessage);
|
||||
handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex);
|
||||
|
||||
handler.serializeString("defeatString", mapHeader->defeatMessage);
|
||||
// handler.serializeString("defeatString", mapHeader->defeatMessage);
|
||||
handler.serializeInt("defeatIconIndex", mapHeader->defeatIconIndex);
|
||||
}
|
||||
|
||||
@ -683,10 +683,10 @@ void CMapFormatJson::readTriggeredEvent(TriggeredEvent & event, const JsonNode &
|
||||
{
|
||||
using namespace TriggeredEventsDetail;
|
||||
|
||||
event.onFulfill = source["message"].String();
|
||||
event.description = source["description"].String();
|
||||
event.onFulfill.jsonDeserialize(source["message"]);
|
||||
event.description.jsonDeserialize(source["description"]);
|
||||
event.effect.type = vstd::find_pos(typeNames, source["effect"]["type"].String());
|
||||
event.effect.toOtherMessage = source["effect"]["messageToSend"].String();
|
||||
event.effect.toOtherMessage.jsonDeserialize(source["effect"]["messageToSend"]);
|
||||
event.trigger = EventExpression(source["condition"], JsonToCondition); // logical expression
|
||||
}
|
||||
|
||||
@ -705,15 +705,15 @@ void CMapFormatJson::writeTriggeredEvent(const TriggeredEvent & event, JsonNode
|
||||
using namespace TriggeredEventsDetail;
|
||||
|
||||
if(!event.onFulfill.empty())
|
||||
dest["message"].String() = event.onFulfill;
|
||||
event.onFulfill.jsonSerialize(dest["message"]);
|
||||
|
||||
if(!event.description.empty())
|
||||
dest["description"].String() = event.description;
|
||||
event.description.jsonSerialize(dest["description"]);
|
||||
|
||||
dest["effect"]["type"].String() = typeNames.at(static_cast<size_t>(event.effect.type));
|
||||
|
||||
if(!event.effect.toOtherMessage.empty())
|
||||
dest["effect"]["messageToSend"].String() = event.effect.toOtherMessage;
|
||||
event.description.jsonSerialize(dest["effect"]["messageToSend"]);
|
||||
|
||||
dest["condition"] = event.trigger.toJson(ConditionToJson);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@ -86,6 +87,15 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping)
|
||||
}
|
||||
}
|
||||
|
||||
for (auto entry : mapping["portraits"].Struct())
|
||||
{
|
||||
int32_t sourceID = entry.second.Integer();
|
||||
int32_t targetID = *VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "hero", entry.first);
|
||||
int32_t iconID = VLC->heroTypes()->getByIndex(targetID)->getIconIndex();
|
||||
|
||||
mappingHeroPortrait[sourceID] = iconID;
|
||||
}
|
||||
|
||||
loadMapping(mappingBuilding, mapping["buildingsCommon"], "building.core:random");
|
||||
loadMapping(mappingFaction, mapping["factions"], "faction");
|
||||
loadMapping(mappingCreature, mapping["creatures"], "creature");
|
||||
@ -111,6 +121,15 @@ void MapIdentifiersH3M::remapTemplate(ObjectTemplate & objectTemplate)
|
||||
objectTemplate.id = mappedType.ID;
|
||||
objectTemplate.subid = mappedType.subID;
|
||||
}
|
||||
|
||||
if (objectTemplate.id == Obj::TOWN || objectTemplate.id == Obj::RANDOM_DWELLING_FACTION)
|
||||
objectTemplate.subid = remap(FactionID(objectTemplate.subid));
|
||||
|
||||
if (objectTemplate.id == Obj::MONSTER)
|
||||
objectTemplate.subid = remap(CreatureID(objectTemplate.subid));
|
||||
|
||||
if (objectTemplate.id == Obj::ARTIFACT)
|
||||
objectTemplate.subid = remap(ArtifactID(objectTemplate.subid));
|
||||
}
|
||||
|
||||
BuildingID MapIdentifiersH3M::remapBuilding(std::optional<FactionID> owner, BuildingID input) const
|
||||
@ -149,6 +168,13 @@ HeroTypeID MapIdentifiersH3M::remap(HeroTypeID input) const
|
||||
return input;
|
||||
}
|
||||
|
||||
int32_t MapIdentifiersH3M::remapPortrrait(int32_t input) const
|
||||
{
|
||||
if (mappingHeroPortrait.count(input))
|
||||
return mappingHeroPortrait.at(input);
|
||||
return input;
|
||||
}
|
||||
|
||||
HeroClassID MapIdentifiersH3M::remap(HeroClassID input) const
|
||||
{
|
||||
if (mappingHeroClass.count(input))
|
||||
|
@ -37,6 +37,7 @@ class MapIdentifiersH3M
|
||||
std::map<FactionID, FactionID> mappingFaction;
|
||||
std::map<CreatureID, CreatureID> mappingCreature;
|
||||
std::map<HeroTypeID, HeroTypeID> mappingHeroType;
|
||||
std::map<int32_t, int32_t> mappingHeroPortrait;
|
||||
std::map<HeroClassID, HeroClassID> mappingHeroClass;
|
||||
std::map<TerrainId, TerrainId> mappingTerrain;
|
||||
std::map<ArtifactID, ArtifactID> mappingArtifact;
|
||||
@ -53,6 +54,7 @@ public:
|
||||
void remapTemplate(ObjectTemplate & objectTemplate);
|
||||
|
||||
BuildingID remapBuilding(std::optional<FactionID> owner, BuildingID input) const;
|
||||
int32_t remapPortrrait(int32_t input) const;
|
||||
FactionID remap(FactionID input) const;
|
||||
CreatureID remap(CreatureID input) const;
|
||||
HeroTypeID remap(HeroTypeID input) const;
|
||||
@ -60,6 +62,7 @@ public:
|
||||
TerrainId remap(TerrainId input) const;
|
||||
ArtifactID remap(ArtifactID input) const;
|
||||
SecondarySkill remap(SecondarySkill input) const;
|
||||
|
||||
};
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@ -96,8 +96,19 @@ HeroTypeID MapReaderH3M::readHero()
|
||||
if(result.getNum() == features.heroIdentifierInvalid)
|
||||
return HeroTypeID(-1);
|
||||
|
||||
assert(result.getNum() < features.heroesCount);
|
||||
return remapIdentifier(result);
|
||||
}
|
||||
|
||||
int32_t MapReaderH3M::readHeroPortrait()
|
||||
{
|
||||
HeroTypeID result(reader->readUInt8());
|
||||
|
||||
if(result.getNum() == features.heroIdentifierInvalid)
|
||||
return int32_t(-1);
|
||||
|
||||
assert(result.getNum() < features.heroesPortraitsCount);
|
||||
return remapIdentifier(result);;
|
||||
return remapper.remapPortrrait(result);
|
||||
}
|
||||
|
||||
CreatureID MapReaderH3M::readCreature()
|
||||
|
@ -35,6 +35,7 @@ public:
|
||||
ArtifactID readArtifact32();
|
||||
CreatureID readCreature();
|
||||
HeroTypeID readHero();
|
||||
int32_t readHeroPortrait();
|
||||
TerrainId readTerrain();
|
||||
RoadId readRoad();
|
||||
RiverId readRiver();
|
||||
|
@ -26,6 +26,7 @@
|
||||
#include "../mapObjectConstructors/ShipyardInstanceConstructor.h"
|
||||
#include "../mapObjectConstructors/ShrineInstanceConstructor.h"
|
||||
#include "../mapObjects/MapObjects.h"
|
||||
#include "../mapObjects/CGCreature.h"
|
||||
#include "../mapObjects/CGTownBuilding.h"
|
||||
#include "../mapObjects/ObjectTemplate.h"
|
||||
#include "../battle/CObstacleInstance.h"
|
||||
|
@ -11,8 +11,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Limiter.h"
|
||||
#include "Reward.h"
|
||||
#include "MetaString.h"
|
||||
#include "NetPacksBase.h"
|
||||
#include "Reward.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -34,9 +34,9 @@ namespace {
|
||||
{
|
||||
MetaString ret;
|
||||
if (value.isNumber())
|
||||
ret.addTxt(MetaString::ADVOB_TXT, static_cast<ui32>(value.Float()));
|
||||
ret.appendLocalString(EMetaText::ADVOB_TXT, static_cast<ui32>(value.Float()));
|
||||
else
|
||||
ret << value.String();
|
||||
ret.appendRawString(value.String());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -191,10 +191,10 @@ void Rewardable::Info::configureRewards(
|
||||
info.message = loadMessage(reward["message"]);
|
||||
|
||||
for (const auto & artifact : info.reward.artifacts )
|
||||
info.message.addReplacement(MetaString::ART_NAMES, artifact.getNum());
|
||||
info.message.replaceLocalString(EMetaText::ART_NAMES, artifact.getNum());
|
||||
|
||||
for (const auto & artifact : info.reward.spells )
|
||||
info.message.addReplacement(MetaString::SPELL_NAME, artifact.getNum());
|
||||
info.message.replaceLocalString(EMetaText::SPELL_NAME, artifact.getNum());
|
||||
|
||||
object.info.push_back(info);
|
||||
}
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "../../TerrainHandler.h"
|
||||
#include "../../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../mapObjects/CGCreature.h"
|
||||
#include "../../mapping/CMapEditManager.h"
|
||||
#include "../RmgObject.h"
|
||||
#include "ObjectManager.h"
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../../CCreatureHandler.h"
|
||||
#include "../../mapObjectConstructors/AObjectTypeHandler.h"
|
||||
#include "../../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
#include "../../mapObjects/CGCreature.h"
|
||||
#include "../../mapping/CMap.h"
|
||||
#include "../../mapping/CMapEditManager.h"
|
||||
#include "../Functions.h"
|
||||
|
@ -147,7 +147,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 333);//%s is already in boat
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 333);//%s is already in boat
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
@ -159,7 +159,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 334);//There is no place to put the boat.
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 334);//There is no place to put the boat.
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
@ -171,7 +171,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::OK;
|
||||
@ -208,7 +208,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 335); //There are no boats to summon.
|
||||
env->apply(&iw);
|
||||
}
|
||||
else //create boat
|
||||
@ -236,7 +236,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::OK;
|
||||
@ -313,7 +313,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
||||
parameters.caster->getCasterName(iw.text);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
@ -328,7 +328,7 @@ ESpellCastResult DimensionDoorMechanics::applyAdventureEffects(SpellCastEnvironm
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 70); //Dimension Door failed!
|
||||
env->apply(&iw);
|
||||
}
|
||||
else if(env->moveHero(ObjectInstanceID(parameters.caster->getCasterUnitId()), parameters.caster->getHeroCaster()->convertFromVisitablePos(parameters.pos), true))
|
||||
@ -376,7 +376,7 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 123);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 123);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
@ -461,7 +461,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 124);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
@ -472,7 +472,7 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 125);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 125);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
@ -517,14 +517,14 @@ ESpellCastResult TownPortalMechanics::beginCast(SpellCastEnvironment * env, cons
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->getCasterOwner();
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 124);
|
||||
iw.text.appendLocalString(EMetaText::GENERAL_TXT, 124);
|
||||
env->apply(&iw);
|
||||
return ESpellCastResult::CANCEL;
|
||||
}
|
||||
|
||||
request.player = parameters.caster->getCasterOwner();
|
||||
request.title.addTxt(MetaString::JK_TXT, 40);
|
||||
request.description.addTxt(MetaString::JK_TXT, 41);
|
||||
request.title.appendLocalString(EMetaText::JK_TXT, 40);
|
||||
request.description.appendLocalString(EMetaText::JK_TXT, 41);
|
||||
request.icon.id = Component::EComponentType::SPELL;
|
||||
request.icon.subtype = owner->id.toEnum();
|
||||
|
||||
|
@ -309,7 +309,7 @@ void BattleSpellMechanics::cast(ServerCallback * server, const Target & target)
|
||||
{
|
||||
MetaString line;
|
||||
caster->getCastDescription(owner, affectedUnits, line);
|
||||
if(!line.message.empty())
|
||||
if(!line.empty())
|
||||
castDescription.lines.push_back(line);
|
||||
}
|
||||
break;
|
||||
|
@ -13,7 +13,7 @@
|
||||
|
||||
#include <vcmi/spells/Spell.h>
|
||||
|
||||
#include "../NetPacksBase.h"
|
||||
#include "../MetaString.h"
|
||||
#include "../battle/Unit.h"
|
||||
#include "../bonuses/Bonus.h"
|
||||
|
||||
@ -33,7 +33,7 @@ BonusCaster::~BonusCaster() = default;
|
||||
void BonusCaster::getCasterName(MetaString & text) const
|
||||
{
|
||||
if(!bonus->description.empty())
|
||||
text.addReplacement(bonus->description);
|
||||
text.replaceRawString(bonus->description);
|
||||
else
|
||||
actualCaster->getCasterName(text);
|
||||
}
|
||||
@ -43,9 +43,9 @@ void BonusCaster::getCastDescription(const Spell * spell, const std::vector<cons
|
||||
const bool singleTarget = attacked.size() == 1;
|
||||
const int textIndex = singleTarget ? 195 : 196;
|
||||
|
||||
text.addTxt(MetaString::GENERAL_TXT, textIndex);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, textIndex);
|
||||
getCasterName(text);
|
||||
text.addReplacement(MetaString::SPELL_NAME, spell->getIndex());
|
||||
text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
|
||||
if(singleTarget)
|
||||
attacked.at(0)->addNameReplacement(text, true);
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ bool BaseMechanics::adaptGenericProblem(Problem & target) const
|
||||
{
|
||||
MetaString text;
|
||||
// %s recites the incantations but they seem to have no effect.
|
||||
text.addTxt(MetaString::GENERAL_TXT, 541);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 541);
|
||||
assert(caster);
|
||||
caster->getCasterName(text);
|
||||
|
||||
@ -489,14 +489,14 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem::ESpellCastProblem source, Pr
|
||||
if(b && b->val == 2 && b->source == BonusSource::ARTIFACT)
|
||||
{
|
||||
//The %s prevents %s from casting 3rd level or higher spells.
|
||||
text.addTxt(MetaString::GENERAL_TXT, 536);
|
||||
text.addReplacement(MetaString::ART_NAMES, b->sid);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 536);
|
||||
text.replaceLocalString(EMetaText::ART_NAMES, b->sid);
|
||||
caster->getCasterName(text);
|
||||
target.add(std::move(text), spells::Problem::NORMAL);
|
||||
}
|
||||
else if(b && b->source == BonusSource::TERRAIN_OVERLAY && VLC->battlefields()->getByIndex(b->sid)->identifier == "cursed_ground")
|
||||
{
|
||||
text.addTxt(MetaString::GENERAL_TXT, 537);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 537);
|
||||
target.add(std::move(text), spells::Problem::NORMAL);
|
||||
}
|
||||
else
|
||||
@ -510,14 +510,14 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem::ESpellCastProblem source, Pr
|
||||
case ESpellCastProblem::NO_APPROPRIATE_TARGET:
|
||||
{
|
||||
MetaString text;
|
||||
text.addTxt(MetaString::GENERAL_TXT, 185);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 185);
|
||||
target.add(std::move(text), spells::Problem::NORMAL);
|
||||
}
|
||||
break;
|
||||
case ESpellCastProblem::INVALID:
|
||||
{
|
||||
MetaString text;
|
||||
text.addReplacement("Internal error during check of spell cast.");
|
||||
text.appendRawString("Internal error during check of spell cast.");
|
||||
target.add(std::move(text), spells::Problem::CRITICAL);
|
||||
}
|
||||
break;
|
||||
|
@ -8,7 +8,6 @@
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "Problem.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
#include <vcmi/spells/Magic.h>
|
||||
|
||||
#include "../NetPacksBase.h"
|
||||
#include "../MetaString.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
|
@ -135,13 +135,13 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
MetaString line;
|
||||
if(kills > 1)
|
||||
{
|
||||
line.addTxt(MetaString::GENERAL_TXT, 119); //%d %s die under the terrible gaze of the %s.
|
||||
line.addReplacement(kills);
|
||||
line.appendLocalString(EMetaText::GENERAL_TXT, 119); //%d %s die under the terrible gaze of the %s.
|
||||
line.replaceNumber(kills);
|
||||
firstTarget->addNameReplacement(line, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
line.addTxt(MetaString::GENERAL_TXT, 118); //One %s dies under the terrible gaze of the %s.
|
||||
line.appendLocalString(EMetaText::GENERAL_TXT, 118); //One %s dies under the terrible gaze of the %s.
|
||||
firstTarget->addNameReplacement(line, false);
|
||||
}
|
||||
m->caster->getCasterName(line);
|
||||
@ -151,7 +151,7 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
{
|
||||
{
|
||||
MetaString line;
|
||||
firstTarget->addText(line, MetaString::GENERAL_TXT, -367, true);
|
||||
firstTarget->addText(line, EMetaText::GENERAL_TXT, -367, true);
|
||||
firstTarget->addNameReplacement(line, true);
|
||||
log.push_back(line);
|
||||
}
|
||||
@ -161,8 +161,8 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
//todo: handle newlines in metastring
|
||||
std::string text = VLC->generaltexth->allTexts[343]; //Does %d points of damage.
|
||||
boost::algorithm::trim(text);
|
||||
line << text;
|
||||
line.addReplacement(static_cast<int>(damage)); //no more text afterwards
|
||||
line.appendRawString(text);
|
||||
line.replaceNumber(static_cast<int>(damage)); //no more text afterwards
|
||||
log.push_back(line);
|
||||
}
|
||||
}
|
||||
@ -170,9 +170,9 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
{
|
||||
{
|
||||
MetaString line;
|
||||
line.addTxt(MetaString::GENERAL_TXT, 376); // Spell %s does %d damage
|
||||
line.addReplacement(MetaString::SPELL_NAME, m->getSpellIndex());
|
||||
line.addReplacement(static_cast<int>(damage));
|
||||
line.appendLocalString(EMetaText::GENERAL_TXT, 376); // Spell %s does %d damage
|
||||
line.replaceLocalString(EMetaText::SPELL_NAME, m->getSpellIndex());
|
||||
line.replaceNumber(static_cast<int>(damage));
|
||||
|
||||
log.push_back(line);
|
||||
}
|
||||
@ -183,19 +183,19 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
|
||||
|
||||
if(kills > 1)
|
||||
{
|
||||
line.addTxt(MetaString::GENERAL_TXT, 379); // %d %s perishes
|
||||
line.addReplacement(kills);
|
||||
line.appendLocalString(EMetaText::GENERAL_TXT, 379); // %d %s perishes
|
||||
line.replaceNumber(kills);
|
||||
|
||||
if(multiple)
|
||||
line.addReplacement(MetaString::GENERAL_TXT, 43); // creatures
|
||||
line.replaceLocalString(EMetaText::GENERAL_TXT, 43); // creatures
|
||||
else
|
||||
firstTarget->addNameReplacement(line, true);
|
||||
}
|
||||
else // single creature killed
|
||||
{
|
||||
line.addTxt(MetaString::GENERAL_TXT, 378); // one %s perishes
|
||||
line.appendLocalString(EMetaText::GENERAL_TXT, 378); // one %s perishes
|
||||
if(multiple)
|
||||
line.addReplacement(MetaString::GENERAL_TXT, 42); // creature
|
||||
line.replaceLocalString(EMetaText::GENERAL_TXT, 42); // creature
|
||||
else
|
||||
firstTarget->addNameReplacement(line, false);
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ void Dispel::apply(ServerCallback * server, const Mechanics * m, const EffectTar
|
||||
if(describe && positive && !negative && !neutral)
|
||||
{
|
||||
MetaString line;
|
||||
unit->addText(line, MetaString::GENERAL_TXT, -555, true);
|
||||
unit->addText(line, EMetaText::GENERAL_TXT, -555, true);
|
||||
unit->addNameReplacement(line, true);
|
||||
blm.lines.push_back(std::move(line));
|
||||
}
|
||||
|
@ -119,19 +119,19 @@ void Heal::prepareHealEffect(int64_t value, BattleUnitsChanged & pack, BattleLog
|
||||
// %d %s rise from the dead!
|
||||
// in the table first comes plural string, then the singular one
|
||||
MetaString resurrectText;
|
||||
state->addText(resurrectText, MetaString::GENERAL_TXT, 116, resurrectedCount == 1);
|
||||
state->addText(resurrectText, EMetaText::GENERAL_TXT, 116, resurrectedCount == 1);
|
||||
state->addNameReplacement(resurrectText);
|
||||
resurrectText.addReplacement(resurrectedCount);
|
||||
resurrectText.replaceNumber(resurrectedCount);
|
||||
logMessage.lines.push_back(std::move(resurrectText));
|
||||
}
|
||||
else if (unitHPgained > 0 && m->caster->getHeroCaster() == nullptr) //Show text about healed HP if healed by unit
|
||||
{
|
||||
MetaString healText;
|
||||
auto casterUnit = dynamic_cast<const battle::Unit*>(m->caster);
|
||||
healText.addTxt(MetaString::GENERAL_TXT, 414);
|
||||
healText.appendLocalString(EMetaText::GENERAL_TXT, 414);
|
||||
casterUnit->addNameReplacement(healText, false);
|
||||
state->addNameReplacement(healText, false);
|
||||
healText.addReplacement((int)unitHPgained);
|
||||
healText.replaceNumber((int)unitHPgained);
|
||||
logMessage.lines.push_back(std::move(healText));
|
||||
}
|
||||
|
||||
|
@ -259,8 +259,8 @@ bool Obstacle::isHexAvailable(const CBattleInfoCallback * cb, const BattleHex &
|
||||
bool Obstacle::noRoomToPlace(Problem & problem, const Mechanics * m)
|
||||
{
|
||||
MetaString text;
|
||||
text.addTxt(MetaString::GENERAL_TXT, 181);//No room to place %s here
|
||||
text.addReplacement(m->getSpellName());
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 181);//No room to place %s here
|
||||
text.replaceRawString(m->getSpellName());
|
||||
problem.add(std::move(text));
|
||||
return false;
|
||||
}
|
||||
|
@ -58,19 +58,19 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const
|
||||
const auto *elemental = otherSummoned.front();
|
||||
|
||||
MetaString text;
|
||||
text.addTxt(MetaString::GENERAL_TXT, 538);
|
||||
text.appendLocalString(EMetaText::GENERAL_TXT, 538);
|
||||
|
||||
const auto *caster = dynamic_cast<const CGHeroInstance *>(m->caster);
|
||||
if(caster)
|
||||
{
|
||||
text.addReplacement(caster->getNameTranslated());
|
||||
text.replaceRawString(caster->getNameTranslated());
|
||||
|
||||
text.addReplacement(MetaString::CRE_PL_NAMES, elemental->creatureIndex());
|
||||
text.replaceLocalString(EMetaText::CRE_PL_NAMES, elemental->creatureIndex());
|
||||
|
||||
if(caster->type->gender == EHeroGender::FEMALE)
|
||||
text.addReplacement(MetaString::GENERAL_TXT, 540);
|
||||
text.replaceLocalString(EMetaText::GENERAL_TXT, 540);
|
||||
else
|
||||
text.addReplacement(MetaString::GENERAL_TXT, 539);
|
||||
text.replaceLocalString(EMetaText::GENERAL_TXT, 539);
|
||||
|
||||
}
|
||||
problem.add(std::move(text), Problem::NORMAL);
|
||||
|
@ -30,7 +30,7 @@ static void describeEffect(std::vector<MetaString> & log, const Mechanics * m, c
|
||||
auto addLogLine = [&](const int32_t baseTextID, const boost::logic::tribool & plural)
|
||||
{
|
||||
MetaString line;
|
||||
target->addText(line, MetaString::GENERAL_TXT, baseTextID, plural);
|
||||
target->addText(line, EMetaText::GENERAL_TXT, baseTextID, plural);
|
||||
target->addNameReplacement(line, plural);
|
||||
log.push_back(std::move(line));
|
||||
};
|
||||
@ -78,10 +78,10 @@ static void describeEffect(std::vector<MetaString> & log, const Mechanics * m, c
|
||||
|
||||
//"The %s shrivel with age, and lose %d hit points."
|
||||
MetaString line;
|
||||
target->addText(line, MetaString::GENERAL_TXT, 551);
|
||||
target->addText(line, EMetaText::GENERAL_TXT, 551);
|
||||
target->addNameReplacement(line);
|
||||
|
||||
line.addReplacement(oldHealth - newHealth);
|
||||
line.replaceNumber(oldHealth - newHealth);
|
||||
log.push_back(std::move(line));
|
||||
return;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
struct Bonus;
|
||||
struct SetStackEffect;
|
||||
struct MetaString;
|
||||
class MetaString;
|
||||
|
||||
namespace spells
|
||||
{
|
||||
|
@ -539,7 +539,6 @@ bool Animation::loadFrame(size_t frame, size_t group)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// still here? image is missing
|
||||
|
||||
printError(frame, group, "LoadFrame");
|
||||
@ -547,7 +546,14 @@ bool Animation::loadFrame(size_t frame, size_t group)
|
||||
}
|
||||
else //load from separate file
|
||||
{
|
||||
images[group][frame] = getFromExtraDef(source[group][frame]["file"].String());;
|
||||
auto img = getFromExtraDef(source[group][frame]["file"].String());
|
||||
if(!img)
|
||||
{
|
||||
auto bitmap = BitmapHandler::loadBitmap(source[group][frame]["file"].String());
|
||||
img.reset(new QImage(bitmap));
|
||||
}
|
||||
|
||||
images[group][frame] = img;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -577,7 +583,6 @@ void Animation::init()
|
||||
source[defEntry.first].resize(defEntry.second);
|
||||
}
|
||||
|
||||
#if 0 //this code is not used but maybe requred if there will be configurable sprites
|
||||
ResourceID resID(std::string("SPRITES/") + name, EResType::TEXT);
|
||||
|
||||
//if(vstd::contains(graphics->imageLists, resID.getName()))
|
||||
@ -593,9 +598,47 @@ void Animation::init()
|
||||
|
||||
const JsonNode config((char*)textData.get(), stream->getSize());
|
||||
|
||||
//initFromJson(config);
|
||||
initFromJson(config);
|
||||
}
|
||||
}
|
||||
|
||||
void Animation::initFromJson(const JsonNode & config)
|
||||
{
|
||||
std::string basepath;
|
||||
basepath = config["basepath"].String();
|
||||
|
||||
JsonNode base(JsonNode::JsonType::DATA_STRUCT);
|
||||
base["margins"] = config["margins"];
|
||||
base["width"] = config["width"];
|
||||
base["height"] = config["height"];
|
||||
|
||||
for(const JsonNode & group : config["sequences"].Vector())
|
||||
{
|
||||
size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING)
|
||||
source[groupID].clear();
|
||||
|
||||
for(const JsonNode & frame : group["frames"].Vector())
|
||||
{
|
||||
JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
|
||||
JsonUtils::inherit(toAdd, base);
|
||||
toAdd["file"].String() = basepath + frame.String();
|
||||
source[groupID].push_back(toAdd);
|
||||
}
|
||||
}
|
||||
|
||||
for(const JsonNode & node : config["images"].Vector())
|
||||
{
|
||||
size_t group = node["group"].Integer();
|
||||
size_t frame = node["frame"].Integer();
|
||||
|
||||
if (source[group].size() <= frame)
|
||||
source[group].resize(frame+1);
|
||||
|
||||
JsonNode toAdd(JsonNode::JsonType::DATA_STRUCT);
|
||||
JsonUtils::inherit(toAdd, base);
|
||||
toAdd["file"].String() = basepath + node["file"].String();
|
||||
source[group][frame] = toAdd;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Animation::printError(size_t frame, size_t group, std::string type) const
|
||||
@ -808,4 +851,4 @@ void Animation::createFlippedGroup(const size_t sourceGroup, const size_t target
|
||||
auto image = getImage(frame, targetGroup);
|
||||
*image = image->transformed(QTransform::fromScale(1, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ private:
|
||||
bool unloadFrame(size_t frame, size_t group);
|
||||
|
||||
//initialize animation from file
|
||||
//void initFromJson(const JsonNode & input);
|
||||
void initFromJson(const JsonNode & input);
|
||||
void init();
|
||||
|
||||
//to get rid of copy-pasting error message :]
|
||||
|
@ -131,6 +131,7 @@ namespace BitmapHandler
|
||||
c = qRgb(qRed(c), qGreen(c), qBlue(c));
|
||||
image.setColorTable(colorTable);
|
||||
}
|
||||
return image;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <QStyledItemDelegate>
|
||||
#include "../lib/int3.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/mapObjects/CGCreature.h"
|
||||
#include "../lib/mapObjects/MapObjects.h"
|
||||
#include "../lib/ResourceSet.h"
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/MiscObjects.h"
|
||||
#include "../lib/mapObjects/CGCreature.h"
|
||||
#include "../lib/mapping/CMapService.h"
|
||||
#include "../lib/StringConstants.h"
|
||||
#include "inspector/townbulidingswidget.h" //to convert BuildingID to string
|
||||
@ -166,8 +166,8 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
|
||||
};
|
||||
|
||||
//victory & loss messages
|
||||
ui->victoryMessageEdit->setText(QString::fromStdString(controller.map()->victoryMessage));
|
||||
ui->defeatMessageEdit->setText(QString::fromStdString(controller.map()->defeatMessage));
|
||||
ui->victoryMessageEdit->setText(QString::fromStdString(controller.map()->victoryMessage.toString()));
|
||||
ui->defeatMessageEdit->setText(QString::fromStdString(controller.map()->defeatMessage.toString()));
|
||||
|
||||
//victory & loss conditions
|
||||
const std::array<std::string, 8> conditionStringsWin = {
|
||||
@ -550,8 +550,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
|
||||
//victory & loss messages
|
||||
|
||||
controller.map()->victoryMessage = ui->victoryMessageEdit->text().toStdString();
|
||||
controller.map()->defeatMessage = ui->defeatMessageEdit->text().toStdString();
|
||||
controller.map()->victoryMessage = MetaString::createFromRawString(ui->victoryMessageEdit->text().toStdString());
|
||||
controller.map()->defeatMessage = MetaString::createFromRawString(ui->defeatMessageEdit->text().toStdString());
|
||||
|
||||
//victory & loss conditions
|
||||
EventCondition victoryCondition(EventCondition::STANDARD_WIN);
|
||||
@ -561,19 +561,19 @@ void MapSettings::on_pushButton_clicked()
|
||||
//Victory condition - defeat all
|
||||
TriggeredEvent standardVictory;
|
||||
standardVictory.effect.type = EventEffect::VICTORY;
|
||||
standardVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[5];
|
||||
standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
|
||||
standardVictory.identifier = "standardVictory";
|
||||
standardVictory.description.clear(); // TODO: display in quest window
|
||||
standardVictory.onFulfill = VLC->generaltexth->allTexts[659];
|
||||
standardVictory.onFulfill.appendTextID("core.genrltxt.659");
|
||||
standardVictory.trigger = EventExpression(victoryCondition);
|
||||
|
||||
//Loss condition - 7 days without town
|
||||
TriggeredEvent standardDefeat;
|
||||
standardDefeat.effect.type = EventEffect::DEFEAT;
|
||||
standardDefeat.effect.toOtherMessage = VLC->generaltexth->allTexts[8];
|
||||
standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
|
||||
standardDefeat.identifier = "standardDefeat";
|
||||
standardDefeat.description.clear(); // TODO: display in quest window
|
||||
standardDefeat.onFulfill = VLC->generaltexth->allTexts[7];
|
||||
standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
||||
standardDefeat.trigger = EventExpression(defeatCondition);
|
||||
|
||||
controller.map()->triggeredEvents.clear();
|
||||
@ -583,7 +583,7 @@ void MapSettings::on_pushButton_clicked()
|
||||
{
|
||||
controller.map()->triggeredEvents.push_back(standardVictory);
|
||||
controller.map()->victoryIconIndex = 11;
|
||||
controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[0];
|
||||
controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -595,7 +595,7 @@ void MapSettings::on_pushButton_clicked()
|
||||
specialVictory.description.clear(); // TODO: display in quest window
|
||||
|
||||
controller.map()->victoryIconIndex = vicCondition;
|
||||
controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[size_t(vicCondition) + 1];
|
||||
controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[size_t(vicCondition) + 1]);
|
||||
|
||||
switch(vicCondition)
|
||||
{
|
||||
@ -603,8 +603,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
EventCondition cond(EventCondition::HAVE_ARTIFACT);
|
||||
assert(victoryTypeWidget);
|
||||
cond.objectType = victoryTypeWidget->currentData().toInt();
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[281];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[280];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.281");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.280");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -614,8 +614,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
assert(victoryTypeWidget);
|
||||
cond.objectType = victoryTypeWidget->currentData().toInt();
|
||||
cond.value = victoryValueWidget->text().toInt();
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[277];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[276];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.277");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.276");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -625,8 +625,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
assert(victoryTypeWidget);
|
||||
cond.objectType = victoryTypeWidget->currentData().toInt();
|
||||
cond.value = victoryValueWidget->text().toInt();
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[279];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[278];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.278");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -638,8 +638,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
int townIdx = victorySelectWidget->currentData().toInt();
|
||||
if(townIdx > -1)
|
||||
cond.position = controller.map()->objects[townIdx]->pos;
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[283];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[282];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.282");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -650,8 +650,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
cond.objectType = Obj::TOWN;
|
||||
int townIdx = victoryTypeWidget->currentData().toInt();
|
||||
cond.position = controller.map()->objects[townIdx]->pos;
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[250];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[249];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.249");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -662,8 +662,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
cond.objectType = Obj::HERO;
|
||||
int heroIdx = victoryTypeWidget->currentData().toInt();
|
||||
cond.position = controller.map()->objects[heroIdx]->pos;
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[253];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[252];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.252");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -675,8 +675,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
int townIdx = victorySelectWidget->currentData().toInt();
|
||||
if(townIdx > -1)
|
||||
cond.position = controller.map()->objects[townIdx]->pos;
|
||||
specialVictory.effect.toOtherMessage = VLC->generaltexth->allTexts[293];
|
||||
specialVictory.onFulfill = VLC->generaltexth->allTexts[292];
|
||||
specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
|
||||
specialVictory.onFulfill.appendTextID("core.genrltxt.292");
|
||||
specialVictory.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
@ -697,8 +697,8 @@ void MapSettings::on_pushButton_clicked()
|
||||
// if normal victory allowed - add one more quest
|
||||
if(ui->standardVictoryCheck->isChecked())
|
||||
{
|
||||
controller.map()->victoryMessage += " / ";
|
||||
controller.map()->victoryMessage += VLC->generaltexth->victoryConditions[0];
|
||||
controller.map()->victoryMessage.appendRawString(" / ");
|
||||
controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[0]);
|
||||
controller.map()->triggeredEvents.push_back(standardVictory);
|
||||
}
|
||||
controller.map()->triggeredEvents.push_back(specialVictory);
|
||||
@ -709,7 +709,7 @@ void MapSettings::on_pushButton_clicked()
|
||||
{
|
||||
controller.map()->triggeredEvents.push_back(standardDefeat);
|
||||
controller.map()->defeatIconIndex = 3;
|
||||
controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[0];
|
||||
controller.map()->defeatMessage.appendTextID("core.lcdesc.0");
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -721,7 +721,6 @@ void MapSettings::on_pushButton_clicked()
|
||||
specialDefeat.description.clear(); // TODO: display in quest window
|
||||
|
||||
controller.map()->defeatIconIndex = lossCondition;
|
||||
controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[size_t(lossCondition) + 1];
|
||||
|
||||
switch(lossCondition)
|
||||
{
|
||||
@ -733,8 +732,9 @@ void MapSettings::on_pushButton_clicked()
|
||||
int townIdx = loseTypeWidget->currentData().toInt();
|
||||
cond.position = controller.map()->objects[townIdx]->pos;
|
||||
noneOf.expressions.push_back(cond);
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[251];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.251");
|
||||
specialDefeat.trigger = EventExpression(noneOf);
|
||||
controller.map()->defeatMessage.appendTextID("core.lcdesc.1");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -746,8 +746,9 @@ void MapSettings::on_pushButton_clicked()
|
||||
int townIdx = loseTypeWidget->currentData().toInt();
|
||||
cond.position = controller.map()->objects[townIdx]->pos;
|
||||
noneOf.expressions.push_back(cond);
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[253];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.253");
|
||||
specialDefeat.trigger = EventExpression(noneOf);
|
||||
controller.map()->defeatMessage.appendTextID("core.lcdesc.2");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -755,8 +756,9 @@ void MapSettings::on_pushButton_clicked()
|
||||
EventCondition cond(EventCondition::DAYS_PASSED);
|
||||
assert(loseValueWidget);
|
||||
cond.value = expiredDate(loseValueWidget->text());
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[254];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.254");
|
||||
specialDefeat.trigger = EventExpression(cond);
|
||||
controller.map()->defeatMessage.appendTextID("core.lcdesc.3");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -764,7 +766,7 @@ void MapSettings::on_pushButton_clicked()
|
||||
EventCondition cond(EventCondition::DAYS_WITHOUT_TOWN);
|
||||
assert(loseValueWidget);
|
||||
cond.value = loseValueWidget->text().toInt();
|
||||
specialDefeat.onFulfill = VLC->generaltexth->allTexts[7];
|
||||
specialDefeat.onFulfill.appendTextID("core.genrltxt.7");
|
||||
specialDefeat.trigger = EventExpression(cond);
|
||||
break;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user