diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 14cc7b14d..bff6a0407 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -581,13 +581,13 @@ void MoraleLuckBox::set(const AFactionMember * node) else if(morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_MORALE)) { auto noMorale = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_MORALE)); - text += "\n" + noMorale->Description(); + text += "\n" + noMorale->Description(LOCPLINT->cb.get()); component.value = 0; } else if (!morale && node && node->getBonusBearer()->hasBonusOfType(BonusType::NO_LUCK)) { auto noLuck = node->getBonusBearer()->getBonus(Selector::type()(BonusType::NO_LUCK)); - text += "\n" + noLuck->Description(); + text += "\n" + noLuck->Description(LOCPLINT->cb.get()); component.value = 0; } else @@ -596,7 +596,7 @@ void MoraleLuckBox::set(const AFactionMember * node) for(auto & bonus : * modifierList) { if(bonus->val) { - const std::string& description = bonus->Description(); + const std::string& description = bonus->Description(LOCPLINT->cb.get()); //arraytxt already contains \n if (description.size() && description[0] != '\n') addInfo += '\n'; diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 0d754741f..136f7317b 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -583,28 +583,16 @@ void CKingdomInterface::generateMinesList(const std::vectorID == Obj::MINE || object->ID == Obj::ABANDONED_MINE) { const CGMine * mine = dynamic_cast(object); - assert(mine); minesCount[mine->producedResource]++; - totalIncome += mine->dailyIncome()[EGameResID::GOLD]; } } - //Heroes can produce gold as well - skill, specialty or arts - std::vector heroes = LOCPLINT->cb->getHeroesInfo(true); - auto * playerSettings = LOCPLINT->cb->getPlayerSettings(LOCPLINT->playerID); - for(auto & hero : heroes) - { - totalIncome += hero->dailyIncome()[EGameResID::GOLD]; - } - - //Add town income of all towns - std::vector towns = LOCPLINT->cb->getTownsInfo(true); - for(auto & town : towns) - { - totalIncome += town->dailyIncome()[EGameResID::GOLD]; - } + for(auto & mapObject : ownedObjects) + totalIncome += mapObject->asOwnable()->dailyIncome()[EGameResID::GOLD]; //if player has some modded boosts we want to show that as well + const auto * playerSettings = LOCPLINT->cb->getPlayerSettings(LOCPLINT->playerID); + const auto & towns = LOCPLINT->cb->getTownsInfo(true); totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD))) * playerSettings->handicap.percentIncome / 100; totalIncome += LOCPLINT->cb->getPlayerState(LOCPLINT->playerID)->valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(GameResID(EGameResID::GOLD))) * towns.size() * playerSettings->handicap.percentIncome / 100; diff --git a/config/gameConfig.json b/config/gameConfig.json index e83046e30..0f51e2ea7 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -53,6 +53,7 @@ "config/objects/creatureBanks.json", "config/objects/dwellings.json", "config/objects/generic.json", + "config/objects/lighthouse.json", "config/objects/magicSpring.json", "config/objects/magicWell.json", "config/objects/moddables.json", diff --git a/config/objects/generic.json b/config/objects/generic.json index 23240bbd5..764fb9983 100644 --- a/config/objects/generic.json +++ b/config/objects/generic.json @@ -270,23 +270,6 @@ } } }, - "lighthouse" : { - "index" :42, - "handler" : "lighthouse", - "base" : { - "sounds" : { - "visit" : ["LIGHTHOUSE"] - } - }, - "types" : { - "object" : { - "index" : 0, - "aiValue" : 500, - "rmg" : { - } - } - } - }, "obelisk" : { "index" :57, "handler" : "obelisk", diff --git a/config/objects/lighthouse.json b/config/objects/lighthouse.json new file mode 100644 index 000000000..cecabfc0b --- /dev/null +++ b/config/objects/lighthouse.json @@ -0,0 +1,29 @@ +{ + "lighthouse" : { + "index" :42, + "handler" : "flaggable", + "base" : { + "sounds" : { + "visit" : ["LIGHTHOUSE"] + } + }, + "types" : { + "lighthouse" : { + "compatibilityIdentifiers" : [ "object" ], + "index" : 0, + "aiValue" : 500, + "rmg" : { + }, + + "message" : "@core.advevent.69", + "bonuses" : { + "seaMovement" : { + "type" : "MOVEMENT", + "subtype" : "heroMovementSea", + "val" : 500 + } + } + } + } + } +} \ No newline at end of file diff --git a/docs/modders/Map_Object_Format.md b/docs/modders/Map_Object_Format.md index 22785ac55..5f6472e6c 100644 --- a/docs/modders/Map_Object_Format.md +++ b/docs/modders/Map_Object_Format.md @@ -49,6 +49,7 @@ These are object types that are available for modding and have configurable prop - `dwelling` - see [Dwelling](Map_Objects/Dwelling.md). Object that allows recruitments of units outside of towns - `market` - see [Market](Map_Objects/Market.md). Trading resources, artifacts, creatures and such - `boat` - see [Boat](Map_Objects/Boat.md). Object to move across different terrains, such as water +- `flaggable` - see [Flaggable](Map_Objects/Flaggable.md). Object that can be flagged by a player to provide [Bonus](Bonus_Format.md) or resources - `hillFort` - TODO: documentation. See config files in vcmi installation for reference - `shipyard` - TODO: documentation. See config files in vcmi installation for reference - `terrain` - Defines terrain overlays such as magic grounds. TODO: documentation. See config files in vcmi installation for reference @@ -60,7 +61,6 @@ These are types that don't have configurable properties, however it is possible - `generic` - Defines empty object type that provides no functionality. Note that unlike `static`, objects of this type are never used by RMG - `borderGate` - `borderGuard` -- `lighthouse` - `magi` - `mine` - `obelisk` diff --git a/docs/modders/Map_Objects/Flaggable.md b/docs/modders/Map_Objects/Flaggable.md new file mode 100644 index 000000000..f11bfca79 --- /dev/null +++ b/docs/modders/Map_Objects/Flaggable.md @@ -0,0 +1,39 @@ +# Flaggable objects + +Flaggable object are those that can be captured by a visiting hero. H3 examples are mines, dwellings, or lighthouse. + +Currently, it is possible to make flaggable objects that provide player with: +- Any [Bonus](Bonus_Format.md) supported by bonus system +- Daily resources income (wood, ore, gold, etc) + +## Format description + +```jsonc +{ + "baseObjectName" : { + "name" : "Object name", + "handler" : "flaggable", + "types" : { + "objectName" : { + + // Text for message that player will get on capturing this object with a hero + // Alternatively, it is possible to reuse existing string from H3 using form '@core.advevent.69' + "onVisit" : "{Object Name}\r\n\r\nText of messages that player will see on visit.", + + // List of bonuses that will be granted to player that owns this object + "bonuses" : { + "firstBonus" : { BONUS FORMAT }, + "secondBonus" : { BONUS FORMAT }, + }, + + // Resources that will be given to owner on every day + "dailyIncome" : { + "wood" : 2, + "ore" : 2, + "gold" : 1000 + } + } + } + } +} +``` diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0db2f9e50..ad817733a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -116,6 +116,7 @@ set(lib_MAIN_SRCS mapObjectConstructors/CommonConstructors.cpp mapObjectConstructors/CRewardableConstructor.cpp mapObjectConstructors/DwellingInstanceConstructor.cpp + mapObjectConstructors/FlaggableInstanceConstructor.cpp mapObjectConstructors/HillFortInstanceConstructor.cpp mapObjectConstructors/ShipyardInstanceConstructor.cpp @@ -132,6 +133,7 @@ set(lib_MAIN_SRCS mapObjects/CObjectHandler.cpp mapObjects/CQuest.cpp mapObjects/CRewardableObject.cpp + mapObjects/FlaggableMapObject.cpp mapObjects/IMarket.cpp mapObjects/IObjectInterface.cpp mapObjects/MiscObjects.cpp @@ -497,6 +499,7 @@ set(lib_MAIN_HEADERS mapObjectConstructors/CRewardableConstructor.h mapObjectConstructors/DwellingInstanceConstructor.h mapObjectConstructors/HillFortInstanceConstructor.h + mapObjectConstructors/FlaggableInstanceConstructor.h mapObjectConstructors/IObjectInfo.h mapObjectConstructors/RandomMapInfo.h mapObjectConstructors/ShipyardInstanceConstructor.h @@ -515,6 +518,7 @@ set(lib_MAIN_HEADERS mapObjects/CObjectHandler.h mapObjects/CQuest.h mapObjects/CRewardableObject.h + mapObjects/FlaggableMapObject.h mapObjects/IMarket.h mapObjects/IObjectInterface.h mapObjects/IOwnableObject.h diff --git a/lib/CSkillHandler.cpp b/lib/CSkillHandler.cpp index 87cad05bc..7c9e5fec6 100644 --- a/lib/CSkillHandler.cpp +++ b/lib/CSkillHandler.cpp @@ -122,7 +122,7 @@ CSkill::LevelInfo & CSkill::at(int level) DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info) { for(int i=0; i < info.effects.size(); i++) - out << (i ? "," : "") << info.effects[i]->Description(); + out << (i ? "," : "") << info.effects[i]->Description(nullptr); return out << "])"; } diff --git a/lib/battle/BattleInfo.cpp b/lib/battle/BattleInfo.cpp index 8a8eb633c..132358391 100644 --- a/lib/battle/BattleInfo.cpp +++ b/lib/battle/BattleInfo.cpp @@ -885,12 +885,12 @@ void BattleInfo::addOrUpdateUnitBonus(CStack * sta, const Bonus & value, bool fo if(forceAdd || !sta->hasBonus(Selector::source(BonusSource::SPELL_EFFECT, value.sid).And(Selector::typeSubtypeValueType(value.type, value.subtype, value.valType)))) { //no such effect or cumulative - add new - logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description()); + logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description(nullptr)); sta->addNewBonus(std::make_shared(value)); } else { - logBonus->trace("%s updated bonus: %s", sta->nodeName(), value.Description()); + logBonus->trace("%s updated bonus: %s", sta->nodeName(), value.Description(nullptr)); for(const auto & stackBonus : sta->getExportedBonusList()) //TODO: optimize { diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index a11e794f4..95d665d76 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -18,8 +18,11 @@ #include "../CCreatureHandler.h" #include "../CCreatureSet.h" #include "../CSkillHandler.h" +#include "../IGameCallback.h" #include "../TerrainHandler.h" #include "../VCMI_Lib.h" +#include "../mapObjects/CGObjectInstance.h" +#include "../mapObjectConstructors/CObjectClassesHandler.h" #include "../battle/BattleInfo.h" #include "../constants/StringConstants.h" #include "../entities/hero/CHero.h" @@ -87,7 +90,7 @@ JsonNode CAddInfo::toJsonNode() const return node; } } -std::string Bonus::Description(std::optional customValue) const +std::string Bonus::Description(const IGameInfoCallback * cb, std::optional customValue) const { MetaString descriptionHelper = description; auto valueToShow = customValue.value_or(val); @@ -112,6 +115,10 @@ std::string Bonus::Description(std::optional customValue) const case BonusSource::HERO_SPECIAL: descriptionHelper.appendTextID(sid.as().toEntity(VLC)->getNameTextID()); break; + case BonusSource::OBJECT_INSTANCE: + const auto * object = cb->getObj(sid.as()); + if (object) + descriptionHelper.appendTextID(VLC->objtypeh->getObjectName(object->ID, object->subID)); } } diff --git a/lib/bonuses/Bonus.h b/lib/bonuses/Bonus.h index 63b4d6b1d..caefbc46a 100644 --- a/lib/bonuses/Bonus.h +++ b/lib/bonuses/Bonus.h @@ -26,6 +26,7 @@ class IPropagator; class IUpdater; class BonusList; class CSelector; +class IGameInfoCallback; using BonusSubtypeID = VariantIdentifier; using BonusSourceID = VariantIdentifier; @@ -177,7 +178,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this, public Se val += Val; } - std::string Description(std::optional customValue = {}) const; + std::string Description(const IGameInfoCallback * cb, std::optional customValue = {}) const; JsonNode toJsonNode() const; std::shared_ptr addLimiter(const TLimiterPtr & Limiter); //returns this for convenient chain-calls diff --git a/lib/bonuses/CBonusSystemNode.cpp b/lib/bonuses/CBonusSystemNode.cpp index 7b944eefb..b5a2d582d 100644 --- a/lib/bonuses/CBonusSystemNode.cpp +++ b/lib/bonuses/CBonusSystemNode.cpp @@ -378,7 +378,7 @@ void CBonusSystemNode::propagateBonus(const std::shared_ptr & b, const CB ? source.getUpdatedBonus(b, b->propagationUpdater) : b; bonuses.push_back(propagated); - logBonus->trace("#$# %s #propagated to# %s", propagated->Description(), nodeName()); + logBonus->trace("#$# %s #propagated to# %s", propagated->Description(nullptr), nodeName()); } TNodes lchildren; @@ -392,9 +392,9 @@ void CBonusSystemNode::unpropagateBonus(const std::shared_ptr & b) if(b->propagator->shouldBeAttached(this)) { if (bonuses -= b) - logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(), nodeName()); + logBonus->trace("#$# %s #is no longer propagated to# %s", b->Description(nullptr), nodeName()); else - logBonus->warn("Attempt to remove #$# %s, which is not propagated to %s", b->Description(), nodeName()); + logBonus->warn("Attempt to remove #$# %s, which is not propagated to %s", b->Description(nullptr), nodeName()); bonuses.remove_if([b](const auto & bonus) { diff --git a/lib/mapObjectConstructors/CObjectClassesHandler.cpp b/lib/mapObjectConstructors/CObjectClassesHandler.cpp index 202372cf9..e8356d7ef 100644 --- a/lib/mapObjectConstructors/CObjectClassesHandler.cpp +++ b/lib/mapObjectConstructors/CObjectClassesHandler.cpp @@ -23,17 +23,21 @@ #include "../mapObjectConstructors/CRewardableConstructor.h" #include "../mapObjectConstructors/CommonConstructors.h" #include "../mapObjectConstructors/DwellingInstanceConstructor.h" +#include "../mapObjectConstructors/FlaggableInstanceConstructor.h" #include "../mapObjectConstructors/HillFortInstanceConstructor.h" #include "../mapObjectConstructors/ShipyardInstanceConstructor.h" + #include "../mapObjects/CGCreature.h" -#include "../mapObjects/CGPandoraBox.h" -#include "../mapObjects/CQuest.h" -#include "../mapObjects/ObjectTemplate.h" -#include "../mapObjects/CGMarket.h" -#include "../mapObjects/MiscObjects.h" #include "../mapObjects/CGHeroInstance.h" +#include "../mapObjects/CGMarket.h" +#include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/CGTownInstance.h" +#include "../mapObjects/CQuest.h" +#include "../mapObjects/FlaggableMapObject.h" +#include "../mapObjects/MiscObjects.h" +#include "../mapObjects/ObjectTemplate.h" #include "../mapObjects/ObstacleSetHandler.h" + #include "../modding/IdentifierStorage.h" #include "../modding/CModHandler.h" #include "../modding/ModScope.h" @@ -57,6 +61,7 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER_CLASS("town", CTownInstanceConstructor); SET_HANDLER_CLASS("bank", CBankInstanceConstructor); SET_HANDLER_CLASS("boat", BoatInstanceConstructor); + SET_HANDLER_CLASS("flaggable", FlaggableInstanceConstructor); SET_HANDLER_CLASS("market", MarketInstanceConstructor); SET_HANDLER_CLASS("hillFort", HillFortInstanceConstructor); SET_HANDLER_CLASS("shipyard", ShipyardInstanceConstructor); @@ -82,7 +87,6 @@ CObjectClassesHandler::CObjectClassesHandler() SET_HANDLER("garrison", CGGarrison); SET_HANDLER("heroPlaceholder", CGHeroPlaceholder); SET_HANDLER("keymaster", CGKeymasterTent); - SET_HANDLER("lighthouse", CGLighthouse); SET_HANDLER("magi", CGMagi); SET_HANDLER("mine", CGMine); SET_HANDLER("obelisk", CGObelisk); diff --git a/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp b/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp new file mode 100644 index 000000000..eeac5ee13 --- /dev/null +++ b/lib/mapObjectConstructors/FlaggableInstanceConstructor.cpp @@ -0,0 +1,60 @@ +/* +* FlaggableInstanceConstructor.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 "FlaggableInstanceConstructor.h" + +#include "../json/JsonBonus.h" +#include "../texts/CGeneralTextHandler.h" +#include "../VCMI_Lib.h" + +VCMI_LIB_NAMESPACE_BEGIN + +void FlaggableInstanceConstructor::initTypeData(const JsonNode & config) +{ + for (const auto & bonusJson : config["bonuses"].Struct()) + providedBonuses.push_back(JsonUtils::parseBonus(bonusJson.second)); + + if (!config["message"].isNull()) + { + std::string message = config["message"].String(); + if (!message.empty() && message.at(0) == '@') + { + visitMessageTextID = message.substr(1); + } + else + { + visitMessageTextID = TextIdentifier(getBaseTextID(), "onVisit").get(); + VLC->generaltexth->registerString( config.getModScope(), visitMessageTextID, config["message"]); + } + } + + dailyIncome = ResourceSet(config["dailyIncome"]); +} + +void FlaggableInstanceConstructor::initializeObject(FlaggableMapObject * flaggable) const +{ +} + +const std::string & FlaggableInstanceConstructor::getVisitMessageTextID() const +{ + return visitMessageTextID; +} + +const std::vector> & FlaggableInstanceConstructor::getProvidedBonuses() const +{ + return providedBonuses; +} + +const ResourceSet & FlaggableInstanceConstructor::getDailyIncome() const +{ + return dailyIncome; +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjectConstructors/FlaggableInstanceConstructor.h b/lib/mapObjectConstructors/FlaggableInstanceConstructor.h new file mode 100644 index 000000000..c0ad722fe --- /dev/null +++ b/lib/mapObjectConstructors/FlaggableInstanceConstructor.h @@ -0,0 +1,41 @@ +/* +* FlaggableInstanceConstructor.h, part of VCMI engine +* +* Authors: listed in file AUTHORS in main folder +* +* License: GNU General Public License v2.0 or later +* Full text of license available in license.txt file, in main folder +* +*/ +#pragma once + +#include "CDefaultObjectTypeHandler.h" + +#include "../ResourceSet.h" +#include "../bonuses/Bonus.h" +#include "../mapObjects/FlaggableMapObject.h" + +VCMI_LIB_NAMESPACE_BEGIN + +class FlaggableInstanceConstructor final : public CDefaultObjectTypeHandler +{ + /// List of bonuses that are provided by every map object of this type + std::vector> providedBonuses; + + /// ID of message to show on hero visit + std::string visitMessageTextID; + + /// Amount of resources granted by this object to owner every day + ResourceSet dailyIncome; + +protected: + void initTypeData(const JsonNode & config) override; + void initializeObject(FlaggableMapObject * object) const override; + +public: + const std::string & getVisitMessageTextID() const; + const std::vector> & getProvidedBonuses() const; + const ResourceSet & getDailyIncome() const; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 50d9b8b35..5b1a115ce 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -166,7 +166,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const const auto growth = b->val * (base + castleBonus) / 100; if (growth) { - ret.entries.emplace_back(growth, b->Description(growth)); + ret.entries.emplace_back(growth, b->Description(cb, growth)); } } @@ -174,7 +174,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const // Note: bonus uses 1-based levels (Pikeman is level 1), town list uses 0-based (Pikeman in 0-th creatures entry) TConstBonusListPtr bonuses = getBonuses(Selector::typeSubtype(BonusType::CREATURE_GROWTH, BonusCustomSubtype::creatureLevel(level+1))); for(const auto & b : *bonuses) - ret.entries.emplace_back(b->val, b->Description()); + ret.entries.emplace_back(b->val, b->Description(cb)); int dwellingBonus = 0; if(const PlayerState *p = cb->getPlayerState(tempOwner, false)) diff --git a/lib/mapObjects/FlaggableMapObject.cpp b/lib/mapObjects/FlaggableMapObject.cpp new file mode 100644 index 000000000..5b22650ae --- /dev/null +++ b/lib/mapObjects/FlaggableMapObject.cpp @@ -0,0 +1,105 @@ +/* + * FlaggableMapObject.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 "FlaggableMapObject.h" + +#include "../IGameCallback.h" +#include "CGHeroInstance.h" +#include "../networkPacks/PacksForClient.h" +#include "../mapObjectConstructors/FlaggableInstanceConstructor.h" + +VCMI_LIB_NAMESPACE_BEGIN + +const IOwnableObject * FlaggableMapObject::asOwnable() const +{ + return this; +} + +ResourceSet FlaggableMapObject::dailyIncome() const +{ + return getFlaggableHandler()->getDailyIncome(); +} + +std::vector FlaggableMapObject::providedCreatures() const +{ + return {}; +} + +void FlaggableMapObject::onHeroVisit( const CGHeroInstance * h ) const +{ + if (cb->getPlayerRelations(h->getOwner(), getOwner()) != PlayerRelations::ENEMIES) + return; // H3 behavior - revisiting owned Lighthouse is a no-op + + if (getOwner().isValidPlayer()) + takeBonusFrom(getOwner()); + + cb->setOwner(this, h->getOwner()); //not ours? flag it! + + InfoWindow iw; + iw.player = h->getOwner(); + iw.text.appendTextID(getFlaggableHandler()->getVisitMessageTextID()); + cb->showInfoDialog(&iw); + + giveBonusTo(h->getOwner()); +} + +void FlaggableMapObject::initObj(vstd::RNG & rand) +{ + if(getOwner().isValidPlayer()) + { + // FIXME: This is dirty hack + giveBonusTo(getOwner(), true); + } +} + +std::shared_ptr FlaggableMapObject::getFlaggableHandler() const +{ + return std::dynamic_pointer_cast(getObjectHandler()); +} + +void FlaggableMapObject::giveBonusTo(const PlayerColor & player, bool onInit) const +{ + for (auto const & bonus : getFlaggableHandler()->getProvidedBonuses()) + { + GiveBonus gb(GiveBonus::ETarget::PLAYER); + gb.id = player; + gb.bonus = *bonus; + + // FIXME: better place for this code? + gb.bonus.duration = BonusDuration::PERMANENT; + gb.bonus.source = BonusSource::OBJECT_INSTANCE; + gb.bonus.sid = BonusSourceID(id); + + // FIXME: This is really dirty hack + // Proper fix would be to make FlaggableMapObject into bonus system node + // Unfortunately this will cause saves breakage + if(onInit) + gb.applyGs(cb->gameState()); + else + cb->sendAndApply(gb); + } +} + +void FlaggableMapObject::takeBonusFrom(const PlayerColor & player) const +{ + RemoveBonus rb(GiveBonus::ETarget::PLAYER); + rb.whoID = player; + rb.source = BonusSource::OBJECT_INSTANCE; + rb.id = BonusSourceID(id); + cb->sendAndApply(rb); +} + +void FlaggableMapObject::serializeJsonOptions(JsonSerializeFormat& handler) +{ + serializeJsonOwner(handler); +} + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/FlaggableMapObject.h b/lib/mapObjects/FlaggableMapObject.h new file mode 100644 index 000000000..f3c86e0d2 --- /dev/null +++ b/lib/mapObjects/FlaggableMapObject.h @@ -0,0 +1,41 @@ +/* + * FlaggableMapObject.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 "CGObjectInstance.h" +#include "IOwnableObject.h" + +VCMI_LIB_NAMESPACE_BEGIN + +struct Bonus; +class FlaggableInstanceConstructor; + +class DLL_LINKAGE FlaggableMapObject : public CGObjectInstance, public IOwnableObject +{ + std::shared_ptr getFlaggableHandler() const; + + void giveBonusTo(const PlayerColor & player, bool onInit = false) const; + void takeBonusFrom(const PlayerColor & player) const; + +public: + using CGObjectInstance::CGObjectInstance; + + void onHeroVisit(const CGHeroInstance * h) const override; + void initObj(vstd::RNG & rand) override; + + const IOwnableObject * asOwnable() const final; + ResourceSet dailyIncome() const override; + std::vector providedCreatures() const override; + +protected: + void serializeJsonOptions(JsonSerializeFormat & handler) override; +}; + +VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index b8b95994d..0e2ce7e1d 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -1311,75 +1311,6 @@ void CGObelisk::setPropertyDer(ObjProperty what, ObjPropertyID identifier) } } -const IOwnableObject * CGLighthouse::asOwnable() const -{ - return this; -} - -ResourceSet CGLighthouse::dailyIncome() const -{ - return {}; -} - -std::vector CGLighthouse::providedCreatures() const -{ - return {}; -} - -void CGLighthouse::onHeroVisit( const CGHeroInstance * h ) const -{ - if(h->tempOwner != tempOwner) - { - PlayerColor oldOwner = tempOwner; - cb->setOwner(this,h->tempOwner); //not ours? flag it! - h->showInfoDialog(69); - giveBonusTo(h->tempOwner); - - if(oldOwner.isValidPlayer()) //remove bonus from old owner - { - RemoveBonus rb(GiveBonus::ETarget::PLAYER); - rb.whoID = oldOwner; - rb.source = BonusSource::OBJECT_INSTANCE; - rb.id = BonusSourceID(id); - cb->sendAndApply(rb); - } - } -} - -void CGLighthouse::initObj(vstd::RNG & rand) -{ - if(tempOwner.isValidPlayer()) - { - // FIXME: This is dirty hack - giveBonusTo(tempOwner, true); - } -} - -void CGLighthouse::giveBonusTo(const PlayerColor & player, bool onInit) const -{ - GiveBonus gb(GiveBonus::ETarget::PLAYER); - gb.bonus.type = BonusType::MOVEMENT; - gb.bonus.val = 500; - gb.id = player; - gb.bonus.duration = BonusDuration::PERMANENT; - gb.bonus.source = BonusSource::OBJECT_INSTANCE; - gb.bonus.sid = BonusSourceID(id); - gb.bonus.subtype = BonusCustomSubtype::heroMovementSea; - - // FIXME: This is really dirty hack - // Proper fix would be to make CGLighthouse into bonus system node - // Unfortunately this will cause saves breakage - if(onInit) - gb.applyGs(cb->gameState()); - else - cb->sendAndApply(gb); -} - -void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler) -{ - serializeJsonOwner(handler); -} - void HillFort::onHeroVisit(const CGHeroInstance * h) const { cb->showObjectWindow(this, EOpenWindowMode::HILL_FORT_WINDOW, h, false); diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index 862d52596..44b6c5aa3 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -413,28 +413,6 @@ protected: void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; }; -class DLL_LINKAGE CGLighthouse : public CGObjectInstance, public IOwnableObject -{ -public: - using CGObjectInstance::CGObjectInstance; - - void onHeroVisit(const CGHeroInstance * h) const override; - void initObj(vstd::RNG & rand) override; - - const IOwnableObject * asOwnable() const final; - ResourceSet dailyIncome() const override; - std::vector providedCreatures() const override; - - template void serialize(Handler &h) - { - h & static_cast(*this); - } - void giveBonusTo(const PlayerColor & player, bool onInit = false) const; - -protected: - void serializeJsonOptions(JsonSerializeFormat & handler) override; -}; - class DLL_LINKAGE CGTerrainPatch : public CGObjectInstance { public: diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 16e02db92..f2662b3e2 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1478,9 +1478,9 @@ CGObjectInstance * CMapLoaderH3M::readShipyard(const int3 & mapPosition, std::sh return object; } -CGObjectInstance * CMapLoaderH3M::readLighthouse(const int3 & mapPosition) +CGObjectInstance * CMapLoaderH3M::readLighthouse(const int3 & mapPosition, std::shared_ptr objectTemplate) { - auto * object = new CGLighthouse(map->cb); + auto * object = readGeneric(mapPosition, objectTemplate); setOwnerAndValidate(mapPosition, object, reader->readPlayer32()); return object; } @@ -1618,7 +1618,7 @@ CGObjectInstance * CMapLoaderH3M::readObject(std::shared_ptr objTempl); CGObjectInstance * readQuestGuard(const int3 & position); CGObjectInstance * readShipyard(const int3 & mapPosition, std::shared_ptr objectTemplate); - CGObjectInstance * readLighthouse(const int3 & mapPosition); + CGObjectInstance * readLighthouse(const int3 & mapPosition, std::shared_ptr objectTemplate); CGObjectInstance * readGeneric(const int3 & position, std::shared_ptr objectTemplate); CGObjectInstance * readBank(const int3 & position, std::shared_ptr objectTemplate); diff --git a/lib/serializer/RegisterTypes.h b/lib/serializer/RegisterTypes.h index 204d99432..073b68ae9 100644 --- a/lib/serializer/RegisterTypes.h +++ b/lib/serializer/RegisterTypes.h @@ -20,14 +20,17 @@ #include "../gameState/CGameState.h" #include "../gameState/CGameStateCampaign.h" #include "../gameState/TavernHeroesPool.h" + #include "../mapObjects/CGCreature.h" #include "../mapObjects/CGDwelling.h" #include "../mapObjects/CGMarket.h" #include "../mapObjects/CGPandoraBox.h" #include "../mapObjects/CGTownInstance.h" #include "../mapObjects/CQuest.h" +#include "../mapObjects/FlaggableMapObject.h" #include "../mapObjects/MiscObjects.h" #include "../mapObjects/TownBuildingInstance.h" + #include "../mapping/CMap.h" #include "../networkPacks/PacksForClient.h" #include "../networkPacks/PacksForClientBattle.h" @@ -73,7 +76,7 @@ void registerTypes(Serializer &s) s.template registerType(15); s.template registerType(16); s.template registerType(17); - s.template registerType(18); + s.template registerType(18); s.template registerType(19); s.template registerType(20); s.template registerType(21); diff --git a/mapeditor/inspector/inspector.cpp b/mapeditor/inspector/inspector.cpp index d68b8693f..259470ccb 100644 --- a/mapeditor/inspector/inspector.cpp +++ b/mapeditor/inspector/inspector.cpp @@ -60,7 +60,7 @@ Initializer::Initializer(CGObjectInstance * o, const PlayerColor & pl) : default INIT_OBJ_TYPE(CGHeroPlaceholder); INIT_OBJ_TYPE(CGHeroInstance); INIT_OBJ_TYPE(CGSignBottle); - INIT_OBJ_TYPE(CGLighthouse); + INIT_OBJ_TYPE(FlaggableMapObject); //INIT_OBJ_TYPE(CRewardableObject); //INIT_OBJ_TYPE(CGPandoraBox); //INIT_OBJ_TYPE(CGEvent); @@ -108,7 +108,7 @@ void Initializer::initialize(CGShipyard * o) o->tempOwner = defaultPlayer; } -void Initializer::initialize(CGLighthouse * o) +void Initializer::initialize(FlaggableMapObject * o) { if(!o) return; @@ -246,7 +246,7 @@ void Inspector::updateProperties(CGDwelling * o) } } -void Inspector::updateProperties(CGLighthouse * o) +void Inspector::updateProperties(FlaggableMapObject * o) { if(!o) return; @@ -494,7 +494,7 @@ void Inspector::updateProperties() UPDATE_OBJ_PROPERTIES(CGHeroPlaceholder); UPDATE_OBJ_PROPERTIES(CGHeroInstance); UPDATE_OBJ_PROPERTIES(CGSignBottle); - UPDATE_OBJ_PROPERTIES(CGLighthouse); + UPDATE_OBJ_PROPERTIES(FlaggableMapObject); UPDATE_OBJ_PROPERTIES(CRewardableObject); UPDATE_OBJ_PROPERTIES(CGPandoraBox); UPDATE_OBJ_PROPERTIES(CGEvent); @@ -542,7 +542,7 @@ void Inspector::setProperty(const QString & key, const QVariant & value) SET_PROPERTIES(CGHeroInstance); SET_PROPERTIES(CGShipyard); SET_PROPERTIES(CGSignBottle); - SET_PROPERTIES(CGLighthouse); + SET_PROPERTIES(FlaggableMapObject); SET_PROPERTIES(CRewardableObject); SET_PROPERTIES(CGPandoraBox); SET_PROPERTIES(CGEvent); @@ -555,7 +555,7 @@ void Inspector::setProperty(CArmedInstance * o, const QString & key, const QVari if(!o) return; } -void Inspector::setProperty(CGLighthouse * o, const QString & key, const QVariant & value) +void Inspector::setProperty(FlaggableMapObject * o, const QString & key, const QVariant & value) { if(!o) return; } diff --git a/mapeditor/inspector/inspector.h b/mapeditor/inspector/inspector.h index 50d631185..1f03d5601 100644 --- a/mapeditor/inspector/inspector.h +++ b/mapeditor/inspector/inspector.h @@ -17,6 +17,7 @@ #include "../lib/GameConstants.h" #include "../lib/mapObjects/CGCreature.h" #include "../lib/mapObjects/MapObjects.h" +#include "../lib/mapObjects/FlaggableMapObject.h" #include "../lib/mapObjects/CRewardableObject.h" #include "../lib/texts/CGeneralTextHandler.h" #include "../lib/ResourceSet.h" @@ -48,7 +49,7 @@ public: DECLARE_OBJ_TYPE(CGHeroInstance); DECLARE_OBJ_TYPE(CGCreature); DECLARE_OBJ_TYPE(CGSignBottle); - DECLARE_OBJ_TYPE(CGLighthouse); + DECLARE_OBJ_TYPE(FlaggableMapObject); //DECLARE_OBJ_TYPE(CRewardableObject); //DECLARE_OBJ_TYPE(CGEvent); //DECLARE_OBJ_TYPE(CGPandoraBox); @@ -78,7 +79,7 @@ protected: DECLARE_OBJ_PROPERTY_METHODS(CGHeroInstance); DECLARE_OBJ_PROPERTY_METHODS(CGCreature); DECLARE_OBJ_PROPERTY_METHODS(CGSignBottle); - DECLARE_OBJ_PROPERTY_METHODS(CGLighthouse); + DECLARE_OBJ_PROPERTY_METHODS(FlaggableMapObject); DECLARE_OBJ_PROPERTY_METHODS(CRewardableObject); DECLARE_OBJ_PROPERTY_METHODS(CGPandoraBox); DECLARE_OBJ_PROPERTY_METHODS(CGEvent); diff --git a/mapeditor/mapcontroller.cpp b/mapeditor/mapcontroller.cpp index fb4e18321..91f5134c7 100644 --- a/mapeditor/mapcontroller.cpp +++ b/mapeditor/mapcontroller.cpp @@ -121,7 +121,7 @@ void MapController::repairMap(CMap * map) const dynamic_cast(obj.get()) || dynamic_cast(obj.get()) || dynamic_cast(obj.get()) || - dynamic_cast(obj.get()) || + dynamic_cast(obj.get()) || dynamic_cast(obj.get())) obj->tempOwner = PlayerColor::NEUTRAL; }