mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-16 18:49:58 +00:00
db0e0d376e
Previously, reusing the console's object selection code made it so using the same Ptr twice in a row was akin to clicking on the same object twice in the console window, i.e. the object was deselected the second time around. Additionally, that same code was setting key focus to the hidden console window, preventing players from moving until they activated another window (such as their inventory or chat window).
981 lines
37 KiB
C++
981 lines
37 KiB
C++
#include "WorldEvent.hpp"
|
|
#include "Main.hpp"
|
|
#include "Networking.hpp"
|
|
#include "MechanicsHelper.hpp"
|
|
#include "LocalPlayer.hpp"
|
|
#include "DedicatedPlayer.hpp"
|
|
#include "PlayerList.hpp"
|
|
#include "CellController.hpp"
|
|
|
|
#include <components/openmw-mp/Log.hpp>
|
|
|
|
#include "../mwbase/world.hpp"
|
|
#include "../mwbase/environment.hpp"
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
|
#include "../mwbase/soundmanager.hpp"
|
|
#include "../mwbase/windowmanager.hpp"
|
|
|
|
#include "../mwmechanics/aifollow.hpp"
|
|
#include "../mwmechanics/spellcasting.hpp"
|
|
#include "../mwmechanics/summoning.hpp"
|
|
|
|
#include "../mwrender/animation.hpp"
|
|
|
|
#include "../mwworld/class.hpp"
|
|
#include "../mwworld/containerstore.hpp"
|
|
#include "../mwworld/esmstore.hpp"
|
|
#include "../mwworld/inventorystore.hpp"
|
|
#include "../mwworld/manualref.hpp"
|
|
|
|
using namespace mwmp;
|
|
using namespace std;
|
|
|
|
WorldEvent::WorldEvent()
|
|
{
|
|
|
|
}
|
|
|
|
WorldEvent::~WorldEvent()
|
|
{
|
|
|
|
}
|
|
|
|
Networking *WorldEvent::getNetworking()
|
|
{
|
|
return mwmp::Main::get().getNetworking();
|
|
}
|
|
|
|
void WorldEvent::reset()
|
|
{
|
|
cell.blank();
|
|
worldObjects.clear();
|
|
guid = mwmp::Main::get().getNetworking()->getLocalPlayer()->guid;
|
|
}
|
|
|
|
void WorldEvent::addObject(WorldObject worldObject)
|
|
{
|
|
worldObjects.push_back(worldObject);
|
|
}
|
|
|
|
void WorldEvent::editContainers(MWWorld::CellStore* cellStore)
|
|
{
|
|
WorldObject worldObject;
|
|
|
|
for (unsigned int i = 0; i < worldObjectCount; i++)
|
|
{
|
|
worldObject = worldObjects.at(i);
|
|
|
|
//LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
//LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
// ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
MWWorld::ContainerStore& containerStore = ptrFound.getClass().getContainerStore(ptrFound);
|
|
|
|
// If we are setting the entire contents, clear the current ones
|
|
if (action == BaseEvent::SET)
|
|
containerStore.clear();
|
|
|
|
MWWorld::Ptr ownerPtr = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
|
for (const auto &containerItem : worldObject.containerItems)
|
|
{
|
|
if (action == BaseEvent::ADD || action == BaseEvent::SET)
|
|
{
|
|
// Create a ManualRef to be able to set item charge
|
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), containerItem.refId, 1);
|
|
MWWorld::Ptr newPtr = ref.getPtr();
|
|
|
|
if (containerItem.count > 1)
|
|
newPtr.getRefData().setCount(containerItem.count);
|
|
|
|
if (containerItem.charge > -1)
|
|
newPtr.getCellRef().setCharge(containerItem.charge);
|
|
|
|
if (containerItem.enchantmentCharge > -1)
|
|
newPtr.getCellRef().setEnchantmentCharge(containerItem.enchantmentCharge);
|
|
|
|
containerStore.add(newPtr, containerItem.count, ownerPtr, true);
|
|
}
|
|
else if (action == BaseEvent::REMOVE)
|
|
{
|
|
// We have to find the right item ourselves because ContainerStore has no method
|
|
// accounting for charge
|
|
for (const auto ptr : containerStore)
|
|
{
|
|
if (Misc::StringUtils::ciEqual(ptr.getCellRef().getRefId(), containerItem.refId))
|
|
{
|
|
if (ptr.getRefData().getCount() == containerItem.count &&
|
|
ptr.getCellRef().getCharge() == containerItem.charge &&
|
|
ptr.getCellRef().getEnchantmentCharge() == containerItem.enchantmentCharge)
|
|
{
|
|
// Is this an actor's container? If so, unequip this item if it was equipped
|
|
if (ptrFound.getClass().isActor())
|
|
{
|
|
MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);
|
|
|
|
if (invStore.isEquipped(ptr))
|
|
invStore.unequipItemQuantity(ptr, ptrFound, containerItem.count);
|
|
}
|
|
|
|
containerStore.remove(ptr, containerItem.actionCount, ownerPtr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Was this a SET or ADD action on an actor's container, and are we the authority
|
|
// over the actor? If so, autoequip the actor
|
|
if ((action == BaseEvent::ADD || action == BaseEvent::SET) && ptrFound.getClass().isActor() &&
|
|
mwmp::Main::get().getCellController()->isLocalActor(ptrFound))
|
|
{
|
|
MWWorld::InventoryStore& invStore = ptrFound.getClass().getInventoryStore(ptrFound);
|
|
invStore.autoEquip(ptrFound);
|
|
}
|
|
|
|
// If we are in a container, and it happens to be this container, update its view
|
|
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))
|
|
{
|
|
CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;
|
|
|
|
if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex &&
|
|
currentContainer->mpNum == ptrFound.getCellRef().getMpNum())
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
|
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Container, ptrFound);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::placeObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, count: %i, charge: %i, enchantmentCharge: %i", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.mpNum, worldObject.count, worldObject.charge, worldObject.enchantmentCharge);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(0, worldObject.mpNum);
|
|
|
|
// Only create this object if it doesn't already exist
|
|
if (!ptrFound)
|
|
{
|
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), worldObject.refId, 1);
|
|
MWWorld::Ptr newPtr = ref.getPtr();
|
|
|
|
if (worldObject.count > 1)
|
|
newPtr.getRefData().setCount(worldObject.count);
|
|
|
|
if (worldObject.charge > -1)
|
|
newPtr.getCellRef().setCharge(worldObject.charge);
|
|
|
|
if (worldObject.enchantmentCharge > -1)
|
|
newPtr.getCellRef().setEnchantmentCharge(worldObject.enchantmentCharge);
|
|
|
|
newPtr.getCellRef().setGoldValue(worldObject.goldValue);
|
|
newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, worldObject.position);
|
|
|
|
// Because gold automatically gets replaced with a new object, make sure we set the mpNum at the end
|
|
newPtr.getCellRef().setMpNum(worldObject.mpNum);
|
|
}
|
|
else
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Object already existed!");
|
|
}
|
|
}
|
|
|
|
void WorldEvent::spawnObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(0, worldObject.mpNum);
|
|
|
|
// Only create this object if it doesn't already exist
|
|
if (!ptrFound)
|
|
{
|
|
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), worldObject.refId, 1);
|
|
MWWorld::Ptr newPtr = ref.getPtr();
|
|
|
|
newPtr.getCellRef().setMpNum(worldObject.mpNum);
|
|
|
|
newPtr = MWBase::Environment::get().getWorld()->placeObject(newPtr, cellStore, worldObject.position);
|
|
|
|
if (worldObject.hasMaster)
|
|
{
|
|
MWWorld::Ptr masterPtr;
|
|
|
|
if (worldObject.master.refId.empty())
|
|
masterPtr = MechanicsHelper::getPlayerPtr(worldObject.master);
|
|
else
|
|
masterPtr = cellStore->searchExact(worldObject.master.refNumIndex, worldObject.master.mpNum);
|
|
|
|
if (masterPtr)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Actor has master: %s", masterPtr.getCellRef().getRefId().c_str());
|
|
|
|
MWMechanics::AiFollow package(masterPtr.getCellRef().getRefId());
|
|
newPtr.getClass().getCreatureStats(newPtr).getAiSequence().stack(package, newPtr);
|
|
|
|
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(newPtr);
|
|
if (anim)
|
|
{
|
|
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
|
|
.search("VFX_Summon_Start");
|
|
if (fx)
|
|
anim->addEffect("meshes\\" + fx->mModel, -1, false);
|
|
}
|
|
|
|
int creatureActorId = newPtr.getClass().getCreatureStats(newPtr).getActorId();
|
|
|
|
MWMechanics::CreatureStats& masterCreatureStats = masterPtr.getClass().getCreatureStats(masterPtr);
|
|
masterCreatureStats.setSummonedCreatureActorId(worldObject.refId, creatureActorId);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Actor already existed!");
|
|
}
|
|
}
|
|
|
|
void WorldEvent::deleteObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
// If we are in a container, and it happens to be this object, exit it
|
|
if (MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Container))
|
|
{
|
|
CurrentContainer *currentContainer = &mwmp::Main::get().getLocalPlayer()->currentContainer;
|
|
|
|
if (currentContainer->refNumIndex == ptrFound.getCellRef().getRefNum().mIndex &&
|
|
currentContainer->mpNum == ptrFound.getCellRef().getMpNum())
|
|
{
|
|
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
|
|
MWBase::Environment::get().getWindowManager()->setDragDrop(false);
|
|
}
|
|
}
|
|
|
|
MWBase::Environment::get().getWorld()->deleteObject(ptrFound);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::lockObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
if (worldObject.lockLevel > 0)
|
|
ptrFound.getClass().lock(ptrFound, worldObject.lockLevel);
|
|
else
|
|
ptrFound.getClass().unlock(ptrFound);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::triggerTrapObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
if (!worldObject.isDisarmed)
|
|
{
|
|
MWMechanics::CastSpell cast(ptrFound, ptrFound);
|
|
cast.mHitPosition = worldObject.position.asVec3();
|
|
cast.cast(ptrFound.getCellRef().getTrap());
|
|
}
|
|
|
|
ptrFound.getCellRef().setTrap("");
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::scaleObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, scale: %f", worldObject.refId.c_str(), worldObject.refNumIndex,
|
|
worldObject.mpNum, worldObject.scale);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
MWBase::Environment::get().getWorld()->scaleObject(ptrFound, worldObject.scale);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::setObjectStates(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, state: %s", worldObject.refId.c_str(), worldObject.refNumIndex,
|
|
worldObject.mpNum, worldObject.objectState ? "true" : "false");
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
if (worldObject.objectState)
|
|
MWBase::Environment::get().getWorld()->enable(ptrFound);
|
|
else
|
|
MWBase::Environment::get().getWorld()->disable(ptrFound);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::moveObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
MWBase::Environment::get().getWorld()->moveObject(ptrFound, worldObject.position.pos[0], worldObject.position.pos[1],
|
|
worldObject.position.pos[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::rotateObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
MWBase::Environment::get().getWorld()->rotateObject(ptrFound,
|
|
worldObject.position.rot[0], worldObject.position.rot[1], worldObject.position.rot[2]);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::animateObjects(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
MWBase::MechanicsManager * mechanicsManager = MWBase::Environment::get().getMechanicsManager();
|
|
mechanicsManager->playAnimationGroup(ptrFound, worldObject.animGroup, worldObject.animMode,
|
|
std::numeric_limits<int>::max(), true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::activateDoors(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
ptrFound.getClass().setDoorState(ptrFound, worldObject.doorState);
|
|
MWBase::Environment::get().getWorld()->saveDoorState(ptrFound, worldObject.doorState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::runConsoleCommands(MWWorld::CellStore* cellStore)
|
|
{
|
|
MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();
|
|
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- console command: %s", consoleCommand.c_str());
|
|
|
|
if (worldObjects.empty())
|
|
{
|
|
windowManager->clearConsolePtr();
|
|
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- running with no object reference");
|
|
windowManager->executeCommandInConsole(consoleCommand);
|
|
}
|
|
else
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
windowManager->clearConsolePtr();
|
|
|
|
if (worldObject.isPlayer)
|
|
{
|
|
BasePlayer *player = 0;
|
|
|
|
if (worldObject.guid == Main::get().getLocalPlayer()->guid)
|
|
{
|
|
player = Main::get().getLocalPlayer();
|
|
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- running on local player");
|
|
windowManager->setConsolePtr(static_cast<LocalPlayer*>(player)->getPlayerPtr());
|
|
windowManager->executeCommandInConsole(consoleCommand);
|
|
}
|
|
else if (player != 0)
|
|
{
|
|
player = PlayerList::getPlayer(guid);
|
|
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- running on player %s", player->npc.mName.c_str());
|
|
windowManager->setConsolePtr(static_cast<DedicatedPlayer*>(player)->getPtr());
|
|
windowManager->executeCommandInConsole(consoleCommand);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- running on cellRef: %s, %i, %i", worldObject.refId.c_str(), worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
windowManager->setConsolePtr(ptrFound);
|
|
windowManager->executeCommandInConsole(consoleCommand);
|
|
}
|
|
}
|
|
}
|
|
|
|
windowManager->clearConsolePtr();
|
|
}
|
|
}
|
|
|
|
void WorldEvent::setLocalShorts(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, index: %i, shortVal: %i", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.mpNum, worldObject.index, worldObject.shortVal);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
ptrFound.getRefData().getLocals().mShorts.at(worldObject.index) = worldObject.shortVal;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::setLocalFloats(MWWorld::CellStore* cellStore)
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, %i, %i, index: %i, floatVal: %f", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.mpNum, worldObject.index, worldObject.floatVal);
|
|
|
|
MWWorld::Ptr ptrFound = cellStore->searchExact(worldObject.refNumIndex, worldObject.mpNum);
|
|
|
|
if (ptrFound)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
ptrFound.getRefData().getLocals().mFloats.at(worldObject.index) = worldObject.floatVal;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::setMemberShorts()
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, index: %i, shortVal: %i", worldObject.refId.c_str(),
|
|
worldObject.index, worldObject.shortVal);
|
|
|
|
// Mimic the way a Ptr is fetched in InterpreterContext for similar situations
|
|
MWWorld::Ptr ptrFound = MWBase::Environment::get().getWorld()->searchPtr(worldObject.refId, false);
|
|
|
|
if (!ptrFound.isEmpty())
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "-- Found %s, %i, %i", ptrFound.getCellRef().getRefId().c_str(),
|
|
ptrFound.getCellRef().getRefNum(), ptrFound.getCellRef().getMpNum());
|
|
|
|
std::string scriptId = ptrFound.getClass().getScript(ptrFound);
|
|
|
|
ptrFound.getRefData().setLocals(
|
|
*MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find(scriptId));
|
|
|
|
ptrFound.getRefData().getLocals().mShorts.at(worldObject.index) = worldObject.shortVal;;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WorldEvent::setGlobalShorts()
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- varName: %s, shortVal: %i", worldObject.varName.c_str(), worldObject.shortVal);
|
|
|
|
MWBase::Environment::get().getWorld()->setGlobalInt(worldObject.varName, worldObject.shortVal);
|
|
}
|
|
}
|
|
|
|
void WorldEvent::playMusic()
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- filename: %s", worldObject.filename.c_str());
|
|
|
|
MWBase::Environment::get().getSoundManager()->streamMusic(worldObject.filename);
|
|
}
|
|
}
|
|
|
|
void WorldEvent::playVideo()
|
|
{
|
|
for (const auto &worldObject : worldObjects)
|
|
{
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- filename: %s, allowSkipping: %s", worldObject.filename.c_str(),
|
|
worldObject.allowSkipping ? "true" : "false");
|
|
|
|
MWBase::Environment::get().getWindowManager()->playVideo(worldObject.filename, worldObject.allowSkipping);
|
|
}
|
|
}
|
|
|
|
void WorldEvent::addObjectPlace(const MWWorld::Ptr& ptr)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = 0;
|
|
worldObject.charge = ptr.getCellRef().getCharge();
|
|
worldObject.enchantmentCharge = ptr.getCellRef().getEnchantmentCharge();
|
|
|
|
// Make sure we send the RefData position instead of the CellRef one, because that's what
|
|
// we actually see on this client
|
|
worldObject.position = ptr.getRefData().getPosition();
|
|
|
|
// We have to get the count from the dropped object because it gets changed
|
|
// automatically for stacks of gold
|
|
worldObject.count = ptr.getRefData().getCount();
|
|
|
|
// Get the real count of gold in a stack
|
|
worldObject.goldValue = ptr.getCellRef().getGoldValue();
|
|
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectSpawn(const MWWorld::Ptr& ptr)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = 0;
|
|
worldObject.hasMaster = false;
|
|
|
|
// Make sure we send the RefData position instead of the CellRef one, because that's what
|
|
// we actually see on this client
|
|
worldObject.position = ptr.getRefData().getPosition();
|
|
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectSpawn(const MWWorld::Ptr& ptr, const MWWorld::Ptr& master)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = 0;
|
|
|
|
worldObject.hasMaster = true;
|
|
|
|
if (master == MWBase::Environment::get().getWorld()->getPlayerPtr())
|
|
{
|
|
worldObject.master.guid = mwmp::Main::get().getLocalPlayer()->guid;
|
|
worldObject.master.refId.clear();
|
|
}
|
|
else if (mwmp::PlayerList::isDedicatedPlayer(master))
|
|
{
|
|
worldObject.master.guid = mwmp::PlayerList::getPlayer(master)->guid;
|
|
worldObject.master.refId.clear();
|
|
}
|
|
else
|
|
{
|
|
MWWorld::CellRef *masterRef = &master.getCellRef();
|
|
|
|
worldObject.master.refId = masterRef->getRefId();
|
|
worldObject.master.refNumIndex = masterRef->getRefNum().mIndex;
|
|
worldObject.master.mpNum = masterRef->getMpNum();
|
|
}
|
|
|
|
// Make sure we send the RefData position instead of the CellRef one, because that's what
|
|
// we actually see on this client
|
|
worldObject.position = ptr.getRefData().getPosition();
|
|
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectDelete(const MWWorld::Ptr& ptr)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectLock(const MWWorld::Ptr& ptr, int lockLevel)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.lockLevel = lockLevel;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectTrap(const MWWorld::Ptr& ptr, const ESM::Position& pos, bool isDisarmed)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.isDisarmed = isDisarmed;
|
|
worldObject.position = pos;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectScale(const MWWorld::Ptr& ptr, float scale)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.scale = scale;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectState(const MWWorld::Ptr& ptr, bool objectState)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.objectState = objectState;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addObjectAnimPlay(const MWWorld::Ptr& ptr, std::string group, int mode)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.animGroup = group;
|
|
worldObject.animMode = mode;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addDoorState(const MWWorld::Ptr& ptr, int state)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.doorState = state;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addMusicPlay(std::string filename)
|
|
{
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.filename = filename;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addVideoPlay(std::string filename, bool allowSkipping)
|
|
{
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.filename = filename;
|
|
worldObject.allowSkipping = allowSkipping;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addScriptLocalShort(const MWWorld::Ptr& ptr, int index, int shortVal)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.index = index;
|
|
worldObject.shortVal = shortVal;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addScriptLocalFloat(const MWWorld::Ptr& ptr, int index, float floatVal)
|
|
{
|
|
cell = *ptr.getCell()->getCell();
|
|
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = ptr.getCellRef().getRefId();
|
|
worldObject.refNumIndex = ptr.getCellRef().getRefNum().mIndex;
|
|
worldObject.mpNum = ptr.getCellRef().getMpNum();
|
|
worldObject.index = index;
|
|
worldObject.floatVal = floatVal;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addScriptMemberShort(std::string refId, int index, int shortVal)
|
|
{
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = refId;
|
|
worldObject.index = index;
|
|
worldObject.shortVal = shortVal;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::addScriptGlobalShort(std::string varName, int shortVal)
|
|
{
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.varName = varName;
|
|
worldObject.shortVal = shortVal;
|
|
addObject(worldObject);
|
|
}
|
|
|
|
void WorldEvent::sendObjectPlace()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_OBJECT_PLACE about %s", cell.getDescription().c_str());
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, count: %i", worldObject.refId.c_str(), worldObject.count);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_PLACE)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_PLACE)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectSpawn()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_OBJECT_SPAWN about %s", cell.getDescription().c_str());
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s-%i", worldObject.refId.c_str(), worldObject.refNumIndex);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_SPAWN)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_SPAWN)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectDelete()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_DELETE)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_DELETE)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectLock()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_LOCK)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_LOCK)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectTrap()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_TRAP)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_TRAP)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectScale()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_SCALE)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_SCALE)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectState()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_STATE)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_STATE)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendObjectAnimPlay()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_ANIM_PLAY)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_OBJECT_ANIM_PLAY)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendDoorState()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_DOOR_STATE about %s", cell.getDescription().c_str());
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s-%i, state: %s", worldObject.refId.c_str(), worldObject.refNumIndex,
|
|
worldObject.doorState ? "true" : "false");
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_DOOR_STATE)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_DOOR_STATE)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendMusicPlay()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_MUSIC_PLAY)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_MUSIC_PLAY)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendVideoPlay()
|
|
{
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_VIDEO_PLAY)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_VIDEO_PLAY)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendScriptLocalShort()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_SCRIPT_LOCAL_SHORT about %s", cell.getDescription().c_str());
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s-%i, index: %i, shortVal: %i", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.index, worldObject.shortVal);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_LOCAL_SHORT)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_LOCAL_SHORT)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendScriptLocalFloat()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_SCRIPT_LOCAL_FLOAT about %s", cell.getDescription().c_str());
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s-%i, index: %i, floatVal: %f", worldObject.refId.c_str(),
|
|
worldObject.refNumIndex, worldObject.index, worldObject.floatVal);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_LOCAL_FLOAT)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_LOCAL_FLOAT)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendScriptMemberShort()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_SCRIPT_MEMBER_SHORT");
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- cellRef: %s, index: %i, shortVal: %i", worldObject.refId.c_str(),
|
|
worldObject.index, worldObject.shortVal);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_MEMBER_SHORT)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_MEMBER_SHORT)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendScriptGlobalShort()
|
|
{
|
|
LOG_MESSAGE_SIMPLE(Log::LOG_VERBOSE, "Sending ID_SCRIPT_GLOBAL_SHORT");
|
|
|
|
for (const auto &worldObject : worldObjects)
|
|
LOG_APPEND(Log::LOG_VERBOSE, "- varName: %s, shortVal: %i", worldObject.varName.c_str(), worldObject.shortVal);
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_SCRIPT_GLOBAL_SHORT)->Send();
|
|
}
|
|
|
|
void WorldEvent::sendContainers(MWWorld::CellStore* cellStore)
|
|
{
|
|
reset();
|
|
cell = *cellStore->getCell();
|
|
action = BaseEvent::SET;
|
|
|
|
MWWorld::CellRefList<ESM::Container> *containerList = cellStore->getContainers();
|
|
|
|
for (auto &container : containerList->mList)
|
|
{
|
|
mwmp::WorldObject worldObject;
|
|
worldObject.refId = container.mRef.getRefId();
|
|
worldObject.refNumIndex = container.mRef.getRefNum().mIndex;
|
|
worldObject.mpNum = container.mRef.getMpNum();
|
|
|
|
MWWorld::ContainerStore& containerStore = container.mClass->getContainerStore(MWWorld::Ptr(&container, 0));
|
|
|
|
for (const auto itemPtr : containerStore)
|
|
{
|
|
mwmp::ContainerItem containerItem;
|
|
containerItem.refId = itemPtr.getCellRef().getRefId();
|
|
containerItem.count = itemPtr.getRefData().getCount();
|
|
containerItem.charge = itemPtr.getCellRef().getCharge();
|
|
containerItem.enchantmentCharge = itemPtr.getCellRef().getEnchantmentCharge();
|
|
|
|
worldObject.containerItems.push_back(containerItem);
|
|
}
|
|
|
|
addObject(worldObject);
|
|
}
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->setEvent(this);
|
|
mwmp::Main::get().getNetworking()->getWorldPacket(ID_CONTAINER)->Send();
|
|
}
|