2022-07-26 15:07:42 +02:00
|
|
|
/*
|
2014-06-05 20:26:50 +03:00
|
|
|
* CBank.cpp, part of VCMI engine
|
2014-06-05 19:52:14 +03:00
|
|
|
*
|
|
|
|
* 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"
|
2014-06-05 20:26:50 +03:00
|
|
|
#include "CBank.h"
|
2014-06-05 19:52:14 +03:00
|
|
|
|
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
|
|
|
#include <vcmi/spells/Spell.h>
|
|
|
|
#include <vcmi/spells/Service.h>
|
|
|
|
|
2024-07-20 14:55:17 +02:00
|
|
|
#include "../texts/CGeneralTextHandler.h"
|
2024-08-31 13:00:36 +02:00
|
|
|
#include "../IGameSettings.h"
|
2023-10-19 14:41:59 +02:00
|
|
|
#include "../CPlayerState.h"
|
2023-06-02 20:47:37 +02:00
|
|
|
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
2023-06-06 17:32:53 +02:00
|
|
|
#include "../mapObjectConstructors/CBankInstanceConstructor.h"
|
2023-10-23 12:59:15 +02:00
|
|
|
#include "../mapObjects/CGHeroInstance.h"
|
|
|
|
#include "../networkPacks/Component.h"
|
|
|
|
#include "../networkPacks/PacksForClient.h"
|
|
|
|
#include "../networkPacks/PacksForClientBattle.h"
|
2014-06-25 17:11:07 +03:00
|
|
|
#include "../IGameCallback.h"
|
2023-06-23 17:02:48 +02:00
|
|
|
#include "../gameState/CGameState.h"
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
2014-06-05 19:52:14 +03:00
|
|
|
///helpers
|
2023-01-01 20:54:05 +02:00
|
|
|
static std::string visitedTxt(const bool visited)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
int id = visited ? 352 : 353;
|
|
|
|
return VLC->generaltexth->allTexts[id];
|
|
|
|
}
|
|
|
|
|
2024-01-01 16:37:48 +02:00
|
|
|
CBank::CBank(IGameCallback *cb)
|
|
|
|
: CArmedInstance(cb)
|
|
|
|
{}
|
|
|
|
|
2023-02-12 22:39:17 +02:00
|
|
|
//must be instantiated in .cpp file for access to complete types of all member fields
|
|
|
|
CBank::~CBank() = default;
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2024-06-01 17:28:17 +02:00
|
|
|
void CBank::initObj(vstd::RNG & rand)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
daycounter = 0;
|
2014-06-22 13:39:40 +03:00
|
|
|
resetDuration = 0;
|
2023-10-24 16:11:25 +02:00
|
|
|
getObjectHandler()->configureObject(this, rand);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2023-06-21 15:49:44 +02:00
|
|
|
bool CBank::isCoastVisitable() const
|
|
|
|
{
|
|
|
|
return coastVisitable;
|
|
|
|
}
|
|
|
|
|
2014-06-24 20:39:36 +03:00
|
|
|
std::string CBank::getHoverText(PlayerColor player) const
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-10-19 14:41:59 +02:00
|
|
|
if (!wasVisited(player))
|
|
|
|
return getObjectName();
|
|
|
|
|
2024-04-17 01:25:58 +02:00
|
|
|
return getObjectName() + "\n" + visitedTxt(bankConfig == nullptr);
|
2023-10-19 14:41:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Component> CBank::getPopupComponents(PlayerColor player) const
|
|
|
|
{
|
|
|
|
if (!wasVisited(player))
|
|
|
|
return {};
|
|
|
|
|
2024-08-31 13:00:36 +02:00
|
|
|
if (!cb->getSettings().getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION))
|
2023-10-19 14:41:59 +02:00
|
|
|
return {};
|
|
|
|
|
2024-04-17 01:25:58 +02:00
|
|
|
if (bankConfig == nullptr)
|
2023-11-12 17:50:52 +02:00
|
|
|
return {};
|
|
|
|
|
2023-10-19 14:41:59 +02:00
|
|
|
std::map<CreatureID, int> guardsAmounts;
|
|
|
|
std::vector<Component> result;
|
|
|
|
|
|
|
|
for (auto const & slot : Slots())
|
|
|
|
if (slot.second)
|
|
|
|
guardsAmounts[slot.second->getCreatureID()] += slot.second->getCount();
|
|
|
|
|
|
|
|
for (auto const & guard : guardsAmounts)
|
|
|
|
{
|
2023-10-31 11:09:56 +02:00
|
|
|
Component comp(ComponentType::CREATURE, guard.first, guard.second);
|
2023-10-19 14:41:59 +02:00
|
|
|
result.push_back(comp);
|
|
|
|
}
|
|
|
|
return result;
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
|
2014-06-22 13:39:40 +03:00
|
|
|
void CBank::setConfig(const BankConfig & config)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-04-17 01:25:58 +02:00
|
|
|
bankConfig = std::make_unique<BankConfig>(config);
|
2023-08-07 18:13:02 +02:00
|
|
|
clearSlots(); // remove all stacks, if any
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2023-02-12 22:39:17 +02:00
|
|
|
for(const auto & stack : config.guards)
|
2023-01-02 18:00:51 +02:00
|
|
|
setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count);
|
2024-07-13 17:46:55 +02:00
|
|
|
|
|
|
|
daycounter = 1; //yes, 1 since "today" daycounter won't be incremented
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2023-11-06 18:27:16 +02:00
|
|
|
void CBank::setPropertyDer (ObjProperty what, ObjPropertyID identifier)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
|
|
|
switch (what)
|
|
|
|
{
|
|
|
|
case ObjProperty::BANK_DAYCOUNTER: //daycounter
|
2023-11-06 18:27:16 +02:00
|
|
|
daycounter+= identifier.getNum();
|
2014-06-05 19:52:14 +03:00
|
|
|
break;
|
2014-06-22 13:39:40 +03:00
|
|
|
case ObjProperty::BANK_CLEAR:
|
2024-04-17 01:25:58 +02:00
|
|
|
bankConfig.reset();
|
2014-06-05 19:52:14 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-01 17:28:17 +02:00
|
|
|
void CBank::newTurn(vstd::RNG & rand) const
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-04-17 01:25:58 +02:00
|
|
|
if (bankConfig == nullptr)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2014-06-22 13:39:40 +03:00
|
|
|
if (resetDuration != 0)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2014-06-22 13:39:40 +03:00
|
|
|
if (daycounter >= resetDuration)
|
2024-07-13 17:46:55 +02:00
|
|
|
{
|
|
|
|
auto handler = std::dynamic_pointer_cast<CBankInstanceConstructor>(getObjectHandler());
|
|
|
|
auto config = handler->generateConfiguration(cb, rand, ID);
|
|
|
|
cb->setBankObjectConfiguration(id, config);
|
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
else
|
2023-11-06 18:27:16 +02:00
|
|
|
cb->setObjPropertyValue(id, ObjProperty::BANK_DAYCOUNTER, 1); //daycounter++
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2014-06-05 19:52:14 +03:00
|
|
|
bool CBank::wasVisited (PlayerColor player) const
|
|
|
|
{
|
2023-10-19 14:41:59 +02:00
|
|
|
return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id));
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
|
2017-11-15 22:16:44 +02:00
|
|
|
void CBank::onHeroVisit(const CGHeroInstance * h) const
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-08-30 17:24:13 +02:00
|
|
|
ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id);
|
2024-10-04 20:59:51 +02:00
|
|
|
cb->sendAndApply(cov);
|
2023-10-19 14:41:59 +02:00
|
|
|
|
2017-11-15 22:16:44 +02:00
|
|
|
BlockingDialog bd(true, false);
|
|
|
|
bd.player = h->getOwner();
|
2024-08-30 18:24:58 +02:00
|
|
|
bd.text.appendLocalString(EMetaText::ADVOB_TXT, 32);
|
2023-10-19 14:41:59 +02:00
|
|
|
bd.components = getPopupComponents(h->getOwner());
|
2024-10-26 13:39:06 +02:00
|
|
|
bd.text.replaceTextID(getObjectHandler()->getNameTextID());
|
2024-09-04 17:14:56 +02:00
|
|
|
cb->showBlockingDialog(this, &bd);
|
2017-11-15 22:16:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void CBank::doVisit(const CGHeroInstance * hero) const
|
|
|
|
{
|
|
|
|
InfoWindow iw;
|
2023-03-07 03:09:19 +02:00
|
|
|
iw.type = EInfoWindowMode::AUTO;
|
2017-11-15 22:16:44 +02:00
|
|
|
iw.player = hero->getOwner();
|
|
|
|
MetaString loot;
|
|
|
|
|
2024-08-30 18:24:58 +02:00
|
|
|
if (!bankConfig)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-08-30 18:24:58 +02:00
|
|
|
iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty
|
2024-10-26 13:39:06 +02:00
|
|
|
iw.text.replaceTextID(getObjectHandler()->getNameTextID());
|
2017-11-15 22:16:44 +02:00
|
|
|
cb->showInfoDialog(&iw);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
|
|
|
|
2014-06-22 13:39:40 +03:00
|
|
|
//grant resources
|
2024-04-17 01:25:58 +02:00
|
|
|
if (bankConfig)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-10-31 11:09:56 +02:00
|
|
|
for (GameResID it : GameResID::ALL_RESOURCES())
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-04-17 01:25:58 +02:00
|
|
|
if (bankConfig->resources[it] != 0)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-04-17 01:25:58 +02:00
|
|
|
iw.components.emplace_back(ComponentType::RESOURCE, it, bankConfig->resources[it]);
|
2023-06-18 11:18:25 +02:00
|
|
|
loot.appendRawString("%d %s");
|
2024-04-17 01:25:58 +02:00
|
|
|
loot.replaceNumber(bankConfig->resources[it]);
|
2023-11-08 16:13:08 +02:00
|
|
|
loot.replaceName(it);
|
2024-04-17 01:25:58 +02:00
|
|
|
cb->giveResource(hero->getOwner(), it, bankConfig->resources[it]);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
//grant artifacts
|
2024-04-17 01:25:58 +02:00
|
|
|
for (auto & elem : bankConfig->artifacts)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-10-31 11:09:56 +02:00
|
|
|
iw.components.emplace_back(ComponentType::ARTIFACT, elem);
|
2023-06-18 11:18:25 +02:00
|
|
|
loot.appendRawString("%s");
|
2023-11-02 22:01:49 +02:00
|
|
|
loot.replaceName(elem);
|
2024-09-04 19:39:13 +02:00
|
|
|
cb->giveHeroNewArtifact(hero, elem, ArtifactPosition::FIRST_AVAILABLE);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
//display loot
|
|
|
|
if (!iw.components.empty())
|
|
|
|
{
|
2024-08-30 18:24:58 +02:00
|
|
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 34);
|
|
|
|
const auto * strongest = boost::range::max_element(bankConfig->guards, [](const CStackBasicDescriptor & a, const CStackBasicDescriptor & b)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-08-30 18:24:58 +02:00
|
|
|
return a.type->getFightValue() < b.type->getFightValue();
|
|
|
|
})->type;
|
|
|
|
|
|
|
|
iw.text.replaceNamePlural(strongest->getId());
|
|
|
|
iw.text.replaceRawString(loot.buildList());
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2014-06-05 19:52:14 +03:00
|
|
|
cb->showInfoDialog(&iw);
|
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2016-10-09 12:41:35 +02:00
|
|
|
loot.clear();
|
|
|
|
iw.components.clear();
|
|
|
|
iw.text.clear();
|
|
|
|
|
2024-04-17 01:25:58 +02:00
|
|
|
if (!bankConfig->spells.empty())
|
2014-06-22 13:39:40 +03:00
|
|
|
{
|
|
|
|
std::set<SpellID> spells;
|
|
|
|
|
|
|
|
bool noWisdom = false;
|
2024-08-30 18:24:58 +02:00
|
|
|
|
2024-04-17 01:25:58 +02:00
|
|
|
for(const SpellID & spellId : bankConfig->spells)
|
2014-06-22 13:39:40 +03:00
|
|
|
{
|
2023-11-04 17:04:53 +02:00
|
|
|
const auto * spell = spellId.toEntity(VLC);
|
2023-11-02 22:01:49 +02:00
|
|
|
iw.text.appendName(spellId);
|
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
|
|
|
if(spell->getLevel() <= hero->maxSpellLevel())
|
2014-06-22 13:39:40 +03:00
|
|
|
{
|
2016-10-09 12:41:35 +02:00
|
|
|
if(hero->canLearnSpell(spell))
|
|
|
|
{
|
|
|
|
spells.insert(spellId);
|
2023-10-31 11:09:56 +02:00
|
|
|
iw.components.emplace_back(ComponentType::SPELL, spellId);
|
2016-10-09 12:41:35 +02:00
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
noWisdom = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!hero->getArt(ArtifactPosition::SPELLBOOK))
|
2023-06-18 11:18:25 +02:00
|
|
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 109); //no spellbook
|
2017-11-15 22:16:44 +02:00
|
|
|
else if(noWisdom)
|
2023-06-18 11:18:25 +02:00
|
|
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 108); //no expert Wisdom
|
2016-10-09 12:41:35 +02:00
|
|
|
|
|
|
|
if(!iw.components.empty() || !iw.text.toString().empty())
|
|
|
|
cb->showInfoDialog(&iw);
|
|
|
|
|
|
|
|
if(!spells.empty())
|
2017-11-15 22:16:44 +02:00
|
|
|
cb->changeSpells(hero, true, spells);
|
2014-06-22 13:39:40 +03:00
|
|
|
}
|
2016-10-09 12:41:35 +02:00
|
|
|
|
2014-06-05 19:52:14 +03:00
|
|
|
iw.components.clear();
|
|
|
|
iw.text.clear();
|
|
|
|
|
|
|
|
//grant creatures
|
|
|
|
CCreatureSet ourArmy;
|
2024-04-17 01:25:58 +02:00
|
|
|
for(const auto & slot : bankConfig->creatures)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-04-05 02:26:29 +02:00
|
|
|
ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->getId()), slot.type->getId(), slot.count);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
2014-06-22 13:39:40 +03:00
|
|
|
|
2023-02-12 22:39:17 +02:00
|
|
|
for(const auto & elem : ourArmy.Slots())
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2023-10-31 11:09:56 +02:00
|
|
|
iw.components.emplace_back(ComponentType::CREATURE, elem.second->getId(), elem.second->getCount());
|
2023-06-18 11:18:25 +02:00
|
|
|
loot.appendRawString("%s");
|
2023-11-02 22:01:49 +02:00
|
|
|
loot.replaceName(*elem.second);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
|
2015-12-24 20:30:57 +02:00
|
|
|
if(ourArmy.stacksCount())
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2015-12-24 20:30:57 +02:00
|
|
|
if(ourArmy.stacksCount() == 1 && ourArmy.Slots().begin()->second->count == 1)
|
2023-06-18 11:18:25 +02:00
|
|
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185);
|
2014-06-05 19:52:14 +03:00
|
|
|
else
|
2023-06-18 11:18:25 +02:00
|
|
|
iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186);
|
2014-06-05 19:52:14 +03:00
|
|
|
|
2023-06-18 11:18:25 +02:00
|
|
|
iw.text.replaceRawString(loot.buildList());
|
|
|
|
iw.text.replaceRawString(hero->getNameTranslated());
|
2014-06-05 19:52:14 +03:00
|
|
|
cb->showInfoDialog(&iw);
|
|
|
|
cb->giveCreatures(this, hero, ourArmy, false);
|
|
|
|
}
|
2023-11-06 18:27:16 +02:00
|
|
|
cb->setObjPropertyValue(id, ObjProperty::BANK_CLEAR); //bc = nullptr
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-22 13:39:40 +03:00
|
|
|
void CBank::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-08-11 22:22:35 +02:00
|
|
|
if (result.winner == BattleSide::ATTACKER)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2014-06-22 13:39:40 +03:00
|
|
|
doVisit(hero);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-08-09 00:28:28 +02:00
|
|
|
void CBank::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2014-06-22 13:39:40 +03:00
|
|
|
if (answer)
|
2014-06-05 19:52:14 +03:00
|
|
|
{
|
2024-04-17 01:25:58 +02:00
|
|
|
if (bankConfig) // not looted bank
|
2024-08-31 23:04:32 +02:00
|
|
|
cb->startBattle(hero, this);
|
2014-06-05 19:52:14 +03:00
|
|
|
else
|
2014-06-22 13:39:40 +03:00
|
|
|
doVisit(hero);
|
2014-06-05 19:52:14 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-26 15:07:42 +02:00
|
|
|
|
|
|
|
VCMI_LIB_NAMESPACE_END
|