mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
3304 lines
104 KiB
C++
3304 lines
104 KiB
C++
/* Copyright 2003-2005 ROBLOX Corporation, All Rights Reserved */
|
|
#include "stdafx.h"
|
|
|
|
#include "V8DataModel/PartInstance.h"
|
|
#include "V8DataModel/JointInstance.h"
|
|
#include "V8DataModel/BasicPartInstance.h"
|
|
#include "V8DataModel/TouchTransmitter.h"
|
|
#include "V8DataModel/Workspace.h"
|
|
#include "V8World/Primitive.h"
|
|
#include "V8World/Contact.h"
|
|
#include "V8World/Clump.h"
|
|
#include "V8World/Assembly.h"
|
|
#include "V8World/Mechanism.h"
|
|
#include "V8World/HumanoidStage.h"
|
|
#include "V8World/RigidJoint.h"
|
|
#include "V8World/MotorJoint.h"
|
|
#include "V8World/World.h"
|
|
#include "v8world/SpatialFilter.h"
|
|
#include "V8World/Tolerance.h"
|
|
#include "V8Kernel/Body.h"
|
|
#include "V8Kernel/Connector.h"
|
|
#include "V8Kernel/Kernel.h"
|
|
#include "Tool/Dragger.h"
|
|
#include "Tool/ToolsArrow.h"
|
|
#include "GfxBase/Adorn.h"
|
|
#include "AppDraw/Draw.h"
|
|
#include "AppDraw/DrawAdorn.h"
|
|
#include "AppDraw/HitTest.h"
|
|
#include "Util/Math.h"
|
|
#include "Util/Color.h"
|
|
#include "util/RobloxGoogleAnalytics.h"
|
|
#include "Network/NetworkOwner.h"
|
|
#include "G3D/CollisionDetection.h"
|
|
#include "v8datamodel/PartCookie.h"
|
|
|
|
#include "humanoid/Humanoid.h"
|
|
|
|
#include "GfxBase/GfxPart.h"
|
|
|
|
#include "Network/Players.h"
|
|
#include "Network/api.h"
|
|
#include "v8world/ContactManager.h"
|
|
#include "Network/NetworkOwner.h"
|
|
#include "script/ScriptContext.h"
|
|
#include "script/LuaInstanceBridge.h"
|
|
|
|
DYNAMIC_FASTFLAG(HumanoidFloorPVUpdateSignal)
|
|
DYNAMIC_FASTFLAGVARIABLE(SetUpdateTimeOnClumpChanged, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(SetNetworkOwnerFixAnchoring, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(SetNetworkOwnerFixAnchoring2, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(NetworkOwnershipRuleReplicates, false)
|
|
DYNAMIC_FASTFLAGVARIABLE(LocalScriptSpawnPartAlwaysSetOwner, false)
|
|
|
|
// Physical Material Properties
|
|
DYNAMIC_FASTFLAGVARIABLE(MaterialPropertiesEnabled, false)
|
|
SYNCHRONIZED_FASTFLAGVARIABLE(MaterialPropertiesNewIsDefault, false)
|
|
SYNCHRONIZED_FASTFLAGVARIABLE(NewPhysicalPropertiesForcedOnAll, false)
|
|
FASTFLAGVARIABLE (AnchoredSendPositionUpdate, false)
|
|
|
|
LOGGROUP(PartInstanceLifetime)
|
|
|
|
DYNAMIC_FASTFLAG(HumanoidCookieRecursive)
|
|
|
|
//Deprecate FormFactor
|
|
DYNAMIC_FASTFLAGVARIABLE(FormFactorDeprecated,false)
|
|
DYNAMIC_FASTFLAGVARIABLE(FixShapeChangeBug, false)
|
|
|
|
DYNAMIC_FASTFLAGVARIABLE(FixFallenPartsNotDeleted, false)
|
|
DYNAMIC_FASTFLAG(CleanUpInterpolationTimestamps)
|
|
|
|
namespace
|
|
{
|
|
bool computeNetworkOwnerIsSomeoneElseImpl(const RBX::SystemAddress& address, const RBX::SystemAddress& localAddress)
|
|
{
|
|
bool networkOwnerAssigned = ((address != RBX::Network::NetworkOwner::Unassigned()) && (address != RBX::Network::NetworkOwner::ServerUnassigned()));
|
|
bool ownerIsSomeoneElse = (address != localAddress);
|
|
|
|
return (networkOwnerAssigned && ownerIsSomeoneElse);
|
|
}
|
|
|
|
bool currentSecurityIdentityIsScript()
|
|
{
|
|
RBX::Security::Context securityContext = RBX::Security::Context::current();
|
|
|
|
if (securityContext.identity == RBX::Security::LocalGUI_ ||
|
|
securityContext.identity == RBX::Security::GameScript_ ||
|
|
securityContext.identity == RBX::Security::GameScriptInRobloxPlace_ ||
|
|
securityContext.identity == RBX::Security::RobloxGameScript_ ||
|
|
#if defined(RBX_STUDIO_BUILD)
|
|
securityContext.identity == RBX::Security::StudioPlugin ||
|
|
#endif
|
|
securityContext.identity == RBX::Security::CmdLine_)
|
|
{
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace RBX {
|
|
|
|
bool PartInstance::showContactPoints = false;
|
|
bool PartInstance::showJointCoordinates = false;
|
|
bool PartInstance::highlightSleepParts = false;
|
|
bool PartInstance::highlightAwakeParts = false;
|
|
bool PartInstance::showBodyTypes = false;
|
|
bool PartInstance::showAnchoredParts = false;
|
|
bool PartInstance::showPartCoordinateFrames = false;
|
|
bool PartInstance::showUnalignedParts = false;
|
|
bool PartInstance::showSpanningTree = false;
|
|
bool PartInstance::showMechanisms = false;
|
|
bool PartInstance::showAssemblies = false;
|
|
bool PartInstance::showReceiveAge = false;
|
|
bool PartInstance::disableInterpolation = false;
|
|
bool PartInstance::showInterpolationPath = false;
|
|
|
|
const char* const sPart = "BasePart";
|
|
|
|
// Declare some dictionary entries
|
|
static const RBX::Name& namePart = RBX::Name::declare(sPart);
|
|
static const RBX::Name& nameCFrame = RBX::Name::declare("CFrame");
|
|
static const RBX::Name& namePosition = RBX::Name::declare("Position");
|
|
|
|
using namespace Reflection;
|
|
|
|
///////////////////////////
|
|
// New stuff - coordinate frame, Ui Position, Velocity
|
|
// Old files used PVInstance::"CoordinateFrame", "Velocity" and "RotVel", but
|
|
// no files really had any legacy velocity data - the Velocity field here will pick up the Velocity of old PVInstances for parts
|
|
|
|
REFLECTION_BEGIN();
|
|
const PropDescriptor<PartInstance, CoordinateFrame> PartInstance::prop_CFrame("CFrame", category_Data, &PartInstance::getCoordinateFrame, &PartInstance::setCoordinateFrameRoot);
|
|
static const PropDescriptor<PartInstance, Vector3> prop_PositionUi("Position", category_Data, &PartInstance::getTranslationUi, &PartInstance::setTranslationUi, PropertyDescriptor::UI);
|
|
static const PropDescriptor<PartInstance, Vector3> prop_RotationUi("Rotation", category_Data, &PartInstance::getRotationUi, &PartInstance::setRotationUi, PropertyDescriptor::UI);
|
|
const PropDescriptor<PartInstance, Vector3> PartInstance::prop_Velocity("Velocity", category_Data, &PartInstance::getLinearVelocity, &PartInstance::setLinearVelocity);
|
|
const PropDescriptor<PartInstance, Vector3> PartInstance::prop_RotVelocity("RotVelocity", category_Data, &PartInstance::getRotationalVelocity, &PartInstance::setRotationalVelocityRoot);
|
|
static const PropDescriptor<PartInstance, float> prop_Density("SpecificGravity", category_Data, &PartInstance::getSpecificGravity, NULL, Reflection::PropertyDescriptor::UI);
|
|
REFLECTION_END();
|
|
|
|
//////////////////////////
|
|
|
|
const char* category_Part = "Part ";
|
|
|
|
namespace Reflection {
|
|
template<>
|
|
EnumDesc<NetworkOwnership>::EnumDesc()
|
|
:EnumDescriptor("NetworkOwnership")
|
|
{
|
|
addPair(NetworkOwnership_Auto, "Automatic");
|
|
addPair(NetworkOwnership_Manual, "Manual");
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<PartInstance::FormFactor>::EnumDesc()
|
|
:EnumDescriptor("FormFactor")
|
|
{
|
|
addPair(PartInstance::SYMETRIC, "Symmetric");
|
|
addPair(PartInstance::BRICK, "Brick");
|
|
addPair(PartInstance::PLATE, "Plate");
|
|
addPair(PartInstance::CUSTOM, "Custom");
|
|
addLegacyName("Block", PartInstance::BRICK);
|
|
}
|
|
|
|
template<>
|
|
EnumDesc<PartMaterial>::EnumDesc()
|
|
:EnumDescriptor("Material")
|
|
{
|
|
addPair(PLASTIC_MATERIAL, "Plastic");
|
|
addPair(WOOD_MATERIAL, "Wood");
|
|
addPair(SLATE_MATERIAL, "Slate");
|
|
addPair(CONCRETE_MATERIAL, "Concrete");
|
|
addPair(RUST_MATERIAL, "CorrodedMetal");
|
|
addPair(DIAMONDPLATE_MATERIAL, "DiamondPlate");
|
|
addPair(ALUMINUM_MATERIAL, "Foil");
|
|
addPair(GRASS_MATERIAL, "Grass");
|
|
addPair(ICE_MATERIAL, "Ice");
|
|
addPair(MARBLE_MATERIAL, "Marble");
|
|
addPair(GRANITE_MATERIAL, "Granite");
|
|
addPair(BRICK_MATERIAL, "Brick");
|
|
addPair(PEBBLE_MATERIAL, "Pebble");
|
|
addPair(SAND_MATERIAL, "Sand");
|
|
addPair(FABRIC_MATERIAL, "Fabric");
|
|
addPair(SMOOTH_PLASTIC_MATERIAL, "SmoothPlastic");
|
|
addPair(METAL_MATERIAL, "Metal");
|
|
addPair(WOODPLANKS_MATERIAL, "WoodPlanks");
|
|
addPair(COBBLESTONE_MATERIAL, "Cobblestone");
|
|
addPair(AIR_MATERIAL, "Air");
|
|
addPair(WATER_MATERIAL, "Water");
|
|
addPair(ROCK_MATERIAL, "Rock");
|
|
|
|
addPair(GLACIER_MATERIAL, "Glacier");
|
|
addPair(SNOW_MATERIAL, "Snow");
|
|
addPair(SANDSTONE_MATERIAL, "Sandstone");
|
|
addPair(MUD_MATERIAL, "Mud");
|
|
addPair(BASALT_MATERIAL, "Basalt");
|
|
addPair(GROUND_MATERIAL, "Ground");
|
|
addPair(CRACKED_LAVA_MATERIAL, "CrackedLava");
|
|
addPair(NEON_MATERIAL, "Neon");
|
|
|
|
addLegacyName("Corroded Metal", RUST_MATERIAL);
|
|
addLegacyName("Aluminum", ALUMINUM_MATERIAL);
|
|
}
|
|
|
|
template<>
|
|
PartMaterial& Variant::convert<PartMaterial>(void)
|
|
{
|
|
return genericConvert<PartMaterial>();
|
|
}
|
|
|
|
}//namespace Reflection
|
|
|
|
template<>
|
|
bool StringConverter<PartMaterial>::convertToValue(const std::string& text, PartMaterial& value)
|
|
{
|
|
return Reflection::EnumDesc<PartMaterial>::singleton().convertToValue(text.c_str(),value);
|
|
}
|
|
|
|
static BoundFuncDesc<PartInstance, void()> desc_unjoin(&PartInstance::destroyJoints, "BreakJoints", Security::None);
|
|
static BoundFuncDesc<PartInstance, void()> dep_breakJoints(&PartInstance::destroyJoints, "breakJoints", Security::None, Reflection::Descriptor::Attributes::deprecated(desc_unjoin));
|
|
static BoundFuncDesc<PartInstance, void()> desc_join(&PartInstance::join, "MakeJoints", Security::None);
|
|
static BoundFuncDesc<PartInstance, void()> desc_joinOld(&PartInstance::join, "makeJoints", Security::None);
|
|
static CustomBoundFuncDesc<PartInstance, float()> desc_getMass(&PartInstance::getMassScript, "GetMass", Security::None);
|
|
static CustomBoundFuncDesc<PartInstance, float()> desc_getMassOld(&PartInstance::getMassScript, "getMass", Security::None);
|
|
static BoundFuncDesc<PartInstance, bool()> desc_isGrounded(&PartInstance::isGrounded, "IsGrounded", Security::None);
|
|
static BoundFuncDesc<PartInstance, shared_ptr<const Instances>(bool)> desc_getConnectedParts(&PartInstance::getConnectedParts, "GetConnectedParts", "recursive", false, Security::None);
|
|
static BoundFuncDesc<PartInstance, shared_ptr<Instance>()> desc_getRootPart(&PartInstance::getRootPart, "GetRootPart", Security::None);
|
|
static BoundFuncDesc<PartInstance, CoordinateFrame()> desc_getRenderingCFrame(&PartInstance::calcRenderingCoordinateFrame, "GetRenderCFrame", Security::None);
|
|
|
|
// Network Ownership API
|
|
static BoundFuncDesc<PartInstance, void(shared_ptr<Instance>)> desc_setNetOwner(&PartInstance::setNetworkOwnerScript, "SetNetworkOwner", "playerInstance", shared_ptr<Instance>(), Security::None);
|
|
static BoundFuncDesc<PartInstance, shared_ptr<Instance>()> desc_getNetOwner(&PartInstance::getNetworkOwnerScript, "GetNetworkOwner", Security::None);
|
|
static BoundFuncDesc<PartInstance, void()> desc_setNetOwnershipAuto(&PartInstance::setNetworkOwnershipAuto, "SetNetworkOwnershipAuto", Security::None);
|
|
static BoundFuncDesc<PartInstance, bool()> desc_getNetOwnershipIsAuto(&PartInstance::getNetworkOwnershipAuto, "GetNetworkOwnershipAuto", Security::None);
|
|
static BoundFuncDesc<PartInstance, shared_ptr<const Reflection::Tuple>()> desc_canSetNetworkOwnershipScript(&PartInstance::canSetNetworkOwnershipScript, "CanSetNetworkOwnership", Security::None);
|
|
|
|
// shape, size, form factor - both UI and streaming
|
|
const PropDescriptor<PartInstance, RBX::Vector3> prop_Siz("siz", category_Part, NULL, &PartInstance::setPartSizeXml, PropertyDescriptor::LEGACY); // Used to prepare for TA14264
|
|
const PropDescriptor<PartInstance, RBX::Vector3> PartInstance::prop_Size("size", category_Part, &PartInstance::getPartSizeXml, &PartInstance::setPartSizeXml, PropertyDescriptor::STREAMING);
|
|
static const PropDescriptor<PartInstance, RBX::Vector3> prop_SizeUi("Size", category_Part, &PartInstance::getPartSizeUi, &PartInstance::setPartSizeUi, PropertyDescriptor::UI);
|
|
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_Elasticity("Elasticity", category_Part, &PartInstance::getElasticity, &PartInstance::setElasticity);
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_Friction("Friction", category_Part, &PartInstance::getFriction, &PartInstance::setFriction);
|
|
const PropDescriptor<PartInstance, PhysicalProperties> PartInstance::prop_CustomPhysicalProperties("CustomPhysicalProperties", category_Part, &PartInstance::getPhysicalProperties, &PartInstance::setPhysicalProperties);
|
|
|
|
// color, transparency, reflectance, anchored, canCollide, locked
|
|
const PropDescriptor<PartInstance, Color3> PartInstance::prop_Color("Color", category_Appearance, &PartInstance::getColor3, &PartInstance::setColor3, PropertyDescriptor::Functionality(PropertyDescriptor::UI));
|
|
const PropDescriptor<PartInstance, BrickColor> PartInstance::prop_BrickColor("BrickColor", category_Appearance, &PartInstance::getColor, &PartInstance::setColor);
|
|
const PropDescriptor<PartInstance, BrickColor> prop_BrickColorDep("brickColor", category_Appearance, &PartInstance::getColor, &PartInstance::setColor, PropertyDescriptor::Attributes::deprecated(PartInstance::prop_BrickColor));
|
|
const EnumPropDescriptor<PartInstance, PartMaterial> PartInstance::prop_renderMaterial("Material", category_Appearance, &PartInstance::getRenderMaterial, &PartInstance::setRenderMaterial);
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_Transparency("Transparency", category_Appearance, &PartInstance::getTransparencyXml, &PartInstance::setTransparency);
|
|
// DFFlagNetworkOwnershipRuleReplicates
|
|
// Temporary Hack that allows use to Replicate Network Ownerhip
|
|
static const PropDescriptor<PartInstance, bool> prop_networkOwnershipRuleBool("NetworkOwnershipRuleBool", category_Behavior, &PartInstance::getNetworkOwnershipManualReplicate, &PartInstance::setNetworkOwnershipManualReplicate, PropertyDescriptor::REPLICATE_ONLY);
|
|
// DFFlagNetworkOwnershipRuleReplicates -> Commented Out for now because cannot introduce new Enum safely
|
|
//static const EnumPropDescriptor<PartInstance, NetworkOwnership> prop_networkOwnershipRule("NetworkOwnershipRule", category_Behavior, &PartInstance::getNetworkOwnershipRule, &PartInstance::setNetworkOwnershipRule, PropertyDescriptor::REPLICATE_ONLY);
|
|
//Client side only LocalTransparencyModifier property
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_LocalTransparencyModifier("LocalTransparencyModifier",category_Data, &PartInstance::getLocalTransparencyModifier, &PartInstance::setLocalTransparencyModifier, PropertyDescriptor::HIDDEN_SCRIPTING);
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_Reflectance("Reflectance", category_Appearance, &PartInstance::getReflectance, &PartInstance::setReflectance);
|
|
const PropDescriptor<PartInstance, bool> PartInstance::prop_Locked("Locked", category_Behavior, &PartInstance::getPartLocked, &PartInstance::setPartLocked);
|
|
const PropDescriptor<PartInstance, bool> PartInstance::prop_Anchored("Anchored", category_Behavior, &PartInstance::getAnchored, &PartInstance::setAnchored);
|
|
const PropDescriptor<PartInstance, bool> PartInstance::prop_CanCollide("CanCollide", category_Behavior, &PartInstance::getCanCollide, &PartInstance::setCanCollide);
|
|
|
|
const PropDescriptor<PartInstance, Faces> prop_ResizeableFaces("ResizeableFaces", category_Behavior, &PartInstance::getResizeHandleMask, NULL, PropertyDescriptor::UI);
|
|
const PropDescriptor<PartInstance, int> prop_ResizeIncrement("ResizeIncrement", category_Behavior, &PartInstance::getResizeIncrement, NULL, PropertyDescriptor::UI);
|
|
const BoundFuncDesc<PartInstance, bool(NormalId, int)> desc_resize(&PartInstance::resize, "Resize", "normalId", "deltaAmount", Security::None);
|
|
const BoundFuncDesc<PartInstance, bool(NormalId, int)> desc_dep_resize(&PartInstance::resize, "resize", "normalId", "deltaAmount", Security::None, Descriptor::Attributes::deprecated(desc_resize));
|
|
|
|
const BoundFuncDesc<PartInstance, shared_ptr<const Instances>()> func_getTouchingParts(&PartInstance::getTouchingParts, "GetTouchingParts", Security::None);
|
|
|
|
|
|
// Used for network communication - streamed, not in the UI
|
|
// Nuked "v1" because the "" empty string is no longer legal
|
|
const PropDescriptor<PartInstance, RBX::SystemAddress> PartInstance::prop_NetworkOwner("NetworkOwnerV3", category_Data, &PartInstance::getNetworkOwner, &PartInstance::setNetworkOwner, PropertyDescriptor::REPLICATE_ONLY);
|
|
static RemoteEventDesc<PartInstance,
|
|
void(RBX::SystemAddress),
|
|
rbx::remote_signal<void(RBX::SystemAddress)>,
|
|
rbx::remote_signal<void(RBX::SystemAddress)>* (PartInstance::*)(bool)
|
|
> event_NetworkOwnerChanged(&PartInstance::getOrCreateNetworkOwnerChangedSignal, "NetworkOwnerChanged", "systemAddress", Security::LocalUser, RemoteEventCommon::REPLICATE_ONLY, RemoteEventCommon::CLIENT_SERVER);
|
|
const PropDescriptor<PartInstance, bool> PartInstance::prop_NetworkIsSleeping("NetworkIsSleeping", category_Data, &PartInstance::getNetworkIsSleeping, &PartInstance::setNetworkIsSleeping, PropertyDescriptor::REPLICATE_ONLY);
|
|
#ifdef RBX_TEST_BUILD
|
|
EventDesc<PartInstance, void(float, float)> event_OwnershipChange(&PartInstance::ownershipChangeSignal, "OwnershipChange", "binaryAddress", "port", Security::None);
|
|
#endif
|
|
|
|
// New way of reporting touches
|
|
EventDesc<PartInstance,
|
|
void(shared_ptr<Instance>),
|
|
rbx::signal<void(shared_ptr<Instance>)>,
|
|
rbx::signal<void(shared_ptr<Instance>)>* (PartInstance::*)(bool)> event_LocalSimulationTouched(&PartInstance::getOrCreateLocalSimulationTouchedSignal, "LocalSimulationTouched", "part");
|
|
|
|
EventDesc<PartInstance,
|
|
void(shared_ptr<Instance>),
|
|
PartInstance::TouchedSignal,
|
|
PartInstance::TouchedSignal* (PartInstance::*)(bool)> event_Touched(&PartInstance::getOrCreateTouchedSignal, "Touched", "otherPart");
|
|
|
|
EventDesc<PartInstance,
|
|
void(shared_ptr<Instance>),
|
|
PartInstance::TouchedSignal,
|
|
PartInstance::TouchedSignal* (PartInstance::*)(bool)> dep_Touched(&PartInstance::getOrCreateTouchedSignal, "touched", "otherPart", Reflection::Descriptor::Attributes::deprecated(event_Touched));
|
|
|
|
EventDesc<PartInstance,
|
|
void(shared_ptr<Instance>),
|
|
PartInstance::TouchedSignal,
|
|
PartInstance::TouchedSignal* (PartInstance::*)(bool)> event_TouchEnded(&PartInstance::getOrCreateTouchedEndedSignal, "TouchEnded", "otherPart");
|
|
|
|
EventDesc<PartInstance,
|
|
void(shared_ptr<Instance>),
|
|
rbx::signal<void(shared_ptr<Instance>)>,
|
|
rbx::signal<void(shared_ptr<Instance>)>* (PartInstance::*)(bool)> dep_StoppedTouching(&PartInstance::getOrCreateDeprecatedStoppedTouchingSignal, "StoppedTouching", "otherPart", Reflection::Descriptor::Attributes::deprecated(event_TouchEnded));
|
|
|
|
EventDesc<PartInstance,
|
|
void(),
|
|
rbx::signal<void()>,
|
|
rbx::signal<void()>* (PartInstance::*)(bool)> event_OutfitChanged(&PartInstance::getOrCreateOutfitChangedSignal, "OutfitChanged");
|
|
|
|
// Temporary property for handling concurrent drags
|
|
const PropDescriptor<PartInstance, bool> prop_Dragging("DraggingV1", category_Behavior, &PartInstance::getDragging, &PartInstance::setDragging, PropertyDescriptor::REPLICATE_ONLY);
|
|
const PropDescriptor<PartInstance, float> PartInstance::prop_ReceiveAge("ReceiveAge", category_Part, &PartInstance::getReceiveInterval, NULL, PropertyDescriptor::HIDDEN_SCRIPTING);
|
|
|
|
Extents PartInstance::computeExtentsWorld() const
|
|
{
|
|
return getConstPartPrimitive()->getExtentsWorld();
|
|
}
|
|
|
|
void PartInstance::addTouchTransmitter()
|
|
{
|
|
RBXASSERT(!onDemandRead() || onDemandRead()->touchTransmitter == NULL);
|
|
shared_ptr<TouchTransmitter> tt = Creatable<Instance>::create<TouchTransmitter>();
|
|
tt->setParent(this);
|
|
|
|
if (tt->getParent() == this) // sanity check
|
|
onDemandWrite()->touchTransmitter = tt.get();
|
|
}
|
|
|
|
void PartInstance::removeTouchTransmitter()
|
|
{
|
|
if (!onDemandRead() || onDemandRead()->touchTransmitter == NULL)
|
|
return;
|
|
onDemandWrite()->touchTransmitter->setParent(NULL);
|
|
onDemandWrite()->touchTransmitter = 0;
|
|
}
|
|
|
|
void PartInstance::incrementTouchedSlotCount()
|
|
{
|
|
RBX_CRASH_ASSERT(!onDemandRead() || onDemandRead()->touchedSlotCount >= 0);
|
|
if (++(onDemandWrite()->touchedSlotCount) == 1)
|
|
// TODO: This isn't thread-safe, as it might appear
|
|
addTouchTransmitter();
|
|
RBX_CRASH_ASSERT(onDemandRead()->touchedSlotCount >= 0);
|
|
}
|
|
|
|
void PartInstance::decrementTouchedSlotCount()
|
|
{
|
|
RBX_CRASH_ASSERT(!onDemandRead() || onDemandRead()->touchedSlotCount >= 0);
|
|
|
|
if (--(onDemandWrite()->touchedSlotCount) == 0)
|
|
// TODO: This isn't thread-safe, as it might appear
|
|
removeTouchTransmitter();
|
|
RBX_CRASH_ASSERT(onDemandRead()->touchedSlotCount >= 0);
|
|
}
|
|
|
|
void PartInstance::onChildRemoved(Instance* child)
|
|
{
|
|
Super::onChildRemoved(child);
|
|
if (onDemandRead() && child == onDemandRead()->touchTransmitter)
|
|
onDemandWrite()->touchTransmitter = NULL;
|
|
}
|
|
|
|
PartInstance::OnDemandPartInstance::OnDemandPartInstance(PartInstance* owner)
|
|
: touchTransmitter(NULL)
|
|
, isCurrentlyStreamRemovingPart(false)
|
|
{
|
|
touchedSlotCount = 0;
|
|
touchedSignal.part = owner;
|
|
touchEndedSignal.part = owner;
|
|
}
|
|
|
|
static float toSpecificGravity(PartMaterial material)
|
|
{
|
|
// http://www.simetric.co.uk/si_materials.htm
|
|
switch (material)
|
|
{
|
|
case LEGACY_MATERIAL:
|
|
case PLASTIC_MATERIAL:
|
|
case SMOOTH_PLASTIC_MATERIAL:
|
|
case NEON_MATERIAL:
|
|
return 0.7;
|
|
case WOOD_MATERIAL: return 0.350; // Pine
|
|
case WOODPLANKS_MATERIAL: return 0.350;
|
|
case MARBLE_MATERIAL: return 2.563;
|
|
case SLATE_MATERIAL: return 2.691;
|
|
case CONCRETE_MATERIAL: return 2.403;
|
|
case GRANITE_MATERIAL: return 2.691;
|
|
case BRICK_MATERIAL: return 1.922;
|
|
case PEBBLE_MATERIAL: return 2.403;
|
|
case COBBLESTONE_MATERIAL: return 2.691;
|
|
case RUST_MATERIAL: return 7.850;
|
|
case DIAMONDPLATE_MATERIAL: return 7.850;
|
|
case ALUMINUM_MATERIAL: return 7.700;
|
|
case METAL_MATERIAL: return 7.850;
|
|
case GRASS_MATERIAL: return 0.9;
|
|
case SAND_MATERIAL: return 1.602;
|
|
case FABRIC_MATERIAL: return 0.7;
|
|
case ICE_MATERIAL: return 0.919;
|
|
case AIR_MATERIAL: return 0;
|
|
case WATER_MATERIAL: return 1;
|
|
case ROCK_MATERIAL: return 2.691;
|
|
case GLACIER_MATERIAL: return 0.919;
|
|
case SNOW_MATERIAL: return 0.9;
|
|
case SANDSTONE_MATERIAL: return 2.691;
|
|
case MUD_MATERIAL: return 0.9;
|
|
case BASALT_MATERIAL: return 2.691;
|
|
case GROUND_MATERIAL: return 0.9;
|
|
case CRACKED_LAVA_MATERIAL: return 2.691;
|
|
default: RBXASSERT(false); return 1;
|
|
}
|
|
}
|
|
|
|
static const char* partName("Part");
|
|
|
|
PartInstance::PartInstance(const Vector3& initialSize)
|
|
: DescribedNonCreatable<PartInstance, PVInstance, sPart>(partName)
|
|
, raknetTime(0)
|
|
, primitive(new Primitive(Geometry::GEOMETRY_BLOCK))
|
|
, gfxPart(NULL)
|
|
, cookie(0)
|
|
, color(BrickColor::defaultColor())
|
|
, transparency(0.0f)
|
|
, reflectance(0.0f)
|
|
, localTransparencyModifier(0.0)
|
|
, locked(false)
|
|
, cachedNetworkOwnerIsSomeoneElse(false)
|
|
, formFactor(BRICK)
|
|
, legacyPartType(BLOCK_LEGACY_PART)
|
|
{
|
|
FASTLOG2(FLog::PartInstanceLifetime, "PartInstance created: %p, primitive: %p", this, getPartPrimitive());
|
|
|
|
primitive->setSurfaceType(NORM_Y, STUDS);
|
|
primitive->setSurfaceType(NORM_Y_NEG, INLET);
|
|
primitive->setSize(initialSize);
|
|
primitive->setFriction(defaultFriction());
|
|
primitive->setElasticity(defaultElasticity());
|
|
primitive->setSpecificGravity(toSpecificGravity(getRenderMaterial()));
|
|
primitive->setAnchoredProperty(false);
|
|
primitive->setPreventCollide(false);
|
|
primitive->setOwner(this);
|
|
|
|
onGuidChanged();
|
|
}
|
|
|
|
PartInstance::~PartInstance()
|
|
{
|
|
FASTLOG2(FLog::PartInstanceLifetime, "PartInstance destroyed: %p, primitive: %p", this, getPartPrimitive());
|
|
|
|
RBXASSERT(getPartPrimitive());
|
|
RBXASSERT(getPartPrimitive()->getClump()==NULL);
|
|
RBXASSERT(getPartPrimitive()->getWorld() == NULL);
|
|
}
|
|
|
|
OnDemandInstance* PartInstance::initOnDemand()
|
|
{
|
|
return new OnDemandPartInstance(this);
|
|
}
|
|
|
|
// reset on part creation and on setting of PV - only server side;
|
|
void PartInstance::resetNetworkOwnerTime(double extraTime)
|
|
{
|
|
networkOwnerTime = Time::now<Time::Fast>() + Time::Interval(extraTime); // three seconds takeover server side
|
|
}
|
|
|
|
bool PartInstance::networkOwnerTimeUp() const
|
|
{
|
|
return Time::now<Time::Fast>() > networkOwnerTime;
|
|
}
|
|
|
|
// Super Hack - any PartInstance named "Torso" tries to be the assembly root - somewhat consistent with the humanoid code
|
|
void PartInstance::setName(const std::string& value)
|
|
{
|
|
Super::setName(value);
|
|
|
|
FASTLOG1(FLog::PartInstanceLifetime, "PartInstance %p named", this);
|
|
FASTLOGS(FLog::PartInstanceLifetime, "Name: %s", value);
|
|
|
|
if (value == "HumanoidRootPart")
|
|
{
|
|
getPartPrimitive()->setSizeMultiplier(Primitive::ROOT_SIZE);
|
|
} else if (value == "Torso") {
|
|
getPartPrimitive()->setSizeMultiplier(Primitive::TORSO_SIZE);
|
|
} else {
|
|
getPartPrimitive()->setSizeMultiplier(Primitive::DEFAULT_SIZE);
|
|
}
|
|
}
|
|
|
|
|
|
void PartInstance::onGuidChanged()
|
|
{
|
|
RBXASSERT(!getPartPrimitive()->getWorld()); // not in workspace!
|
|
getPartPrimitive()->setGuid(this->getGuid());
|
|
}
|
|
|
|
|
|
// If size.x = 3.0,
|
|
// then 1/2 size = 1.5
|
|
// snapInPart = 1.5 - 1.0 = .5
|
|
CoordinateFrame PartInstance::worldSnapLocation() const
|
|
{
|
|
Vector3 snapInPart;
|
|
Vector3 halfSize = getConstPartPrimitive()->getSize() * 0.5f;
|
|
snapInPart.y = -halfSize.y; // i.e., at the bottom of the part
|
|
snapInPart.x = halfSize.x - Math::iFloor(halfSize.x); // i.e. Size
|
|
snapInPart.z = halfSize.z - Math::iFloor(halfSize.z);
|
|
|
|
CoordinateFrame localSnapLocation(snapInPart);
|
|
|
|
return getCoordinateFrame() * localSnapLocation;
|
|
}
|
|
|
|
|
|
bool PartInstance::aligned(bool useGlobalGridSetting) const
|
|
{
|
|
CoordinateFrame myCoord = worldSnapLocation();
|
|
|
|
CoordinateFrame myCoordSnapped;
|
|
|
|
if (!useGlobalGridSetting || AdvArrowToolBase::advGridMode == DRAG::ONE_STUD)
|
|
myCoordSnapped = Math::snapToGrid(myCoord, Dragger::dragSnap());
|
|
else
|
|
{
|
|
if (AdvArrowToolBase::advGridMode == DRAG::QUARTER_STUD) // Note "QUARTER_STUD" == 0.2 Stud
|
|
myCoordSnapped = Math::snapToGrid(myCoord, Vector3(0.2f, 0.1f, 0.2f)); // Vertical snapping relies on 0.1 increments
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
bool isAligned = Math::fuzzyEq(myCoord, myCoordSnapped);
|
|
#ifdef _DEBUG
|
|
if (isAligned) {
|
|
Vector3 extentPoint = getConstPartPrimitive()->getSize();
|
|
Vector3 pointInMe = myCoord.pointToWorldSpace(extentPoint);
|
|
Vector3 pointSnapped = myCoordSnapped.pointToWorldSpace(extentPoint);
|
|
float delta = (pointInMe - pointSnapped).magnitude();
|
|
RBXASSERT(delta < (Tolerance::maxOverlapOrGap() * 0.5));
|
|
}
|
|
#endif
|
|
return isAligned;
|
|
}
|
|
|
|
|
|
|
|
bool PartInstance::lockedInPlace() const // true if you could not normally "pull" this part out
|
|
{
|
|
bool weldTop = false;
|
|
bool weldBottom = false;
|
|
const Joint* j = getConstPartPrimitive()->getConstFirstJoint();
|
|
while (j) {
|
|
if (RigidJoint::isRigidJoint(j) || Joint::isMotorJoint(j))
|
|
{
|
|
NormalId norm = j->getNormalId(j->getPrimitiveId(getConstPartPrimitive()));
|
|
if (norm == NORM_Y) {
|
|
weldTop = true;
|
|
}
|
|
if (norm == NORM_Y_NEG) {
|
|
weldBottom = true;
|
|
}
|
|
}
|
|
j = getConstPartPrimitive()->getConstNextJoint(j);
|
|
}
|
|
return (weldTop && weldBottom);
|
|
}
|
|
|
|
// Determines whether the local simulation engine should report touches
|
|
bool PartInstance::reportTouches() const
|
|
{
|
|
bool hasTouchedSignalConnections =
|
|
onDemandRead() && (!onDemandRead()->touchedSignal.empty() || !onDemandRead()->localSimulationTouchedSignal.empty());
|
|
|
|
return (
|
|
hasTouchedSignalConnections
|
|
|| hasTouchTransmitter()
|
|
|| this->findConstFirstChildOfType<TouchTransmitter>()
|
|
);
|
|
}
|
|
|
|
|
|
void PartInstance::primitivesToParts(const G3D::Array<Primitive*>& primitives,
|
|
std::vector<shared_ptr<PartInstance> >& parts)
|
|
{
|
|
for (unsigned int i = 0; i < static_cast<unsigned int>(primitives.size()); ++i) {
|
|
PartInstance* part = PartInstance::fromPrimitive(primitives[i]);
|
|
parts.push_back(shared_from(part));
|
|
}
|
|
}
|
|
|
|
void PartInstance::findParts(Instance* instance, std::vector<weak_ptr<PartInstance> >& parts)
|
|
{
|
|
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance)) {
|
|
parts.push_back(shared_from(part));
|
|
}
|
|
|
|
for (size_t i = 0; i < instance->numChildren(); i++) {
|
|
findParts(instance->getChild(i), parts);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::nonNullInWorkspace(const shared_ptr<PartInstance>& part)
|
|
{
|
|
bool answer = (part.get() && part->getPartPrimitive()->getWorld());
|
|
RBXASSERT(!answer || part->getPartPrimitive()->inPipeline());
|
|
return answer;
|
|
}
|
|
|
|
bool PartInstance::partIsLegacyCustomPhysProperties(const PartInstance* part)
|
|
{
|
|
return (!part->getConstPartPrimitive()->getConstGeometry()->isTerrain() &&
|
|
!part->getPhysicalProperties().getCustomEnabled() &&
|
|
(!Math::fuzzyEq(part->getFriction(), PartInstance::defaultFriction(), Math::epsilonf()) ||
|
|
!Math::fuzzyEq(part->getElasticity(), PartInstance::defaultElasticity(), Math::epsilonf())));
|
|
}
|
|
|
|
|
|
void convertPartsToNewGANotify(DataModel* dataModel)
|
|
{
|
|
RobloxGoogleAnalytics::trackEvent(GA_CATEGORY_GAME, "PartInstance_ConvertToNewPhysicalProp",
|
|
boost::lexical_cast<std::string>(dataModel->getPlaceID()).c_str(), 0, false);
|
|
}
|
|
|
|
void PartInstance::convertToNewPhysicalPropRecursive(RBX::Instance* instance)
|
|
{
|
|
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance))
|
|
{
|
|
if (partIsLegacyCustomPhysProperties(part))
|
|
{
|
|
if (DataModel* dm = DataModel::get(part))
|
|
{
|
|
static boost::once_flag flag = BOOST_ONCE_INIT;
|
|
boost::call_once(flag, boost::bind(&convertPartsToNewGANotify, dm));
|
|
}
|
|
|
|
PhysicalProperties defaultProperty = MaterialProperties::getPrimitivePhysicalProperties(part->getPartPrimitive());
|
|
PhysicalProperties physicalProperty = PhysicalProperties(defaultProperty.getDensity(), part->getFriction(), part->getElasticity());
|
|
part->setPhysicalProperties(physicalProperty);
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < instance->numChildren(); i++) {
|
|
convertToNewPhysicalPropRecursive(instance->getChild(i));
|
|
}
|
|
}
|
|
|
|
bool PartInstance::instanceOrChildrenContainsNewCustomPhysics(RBX::Instance* instance)
|
|
{
|
|
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance))
|
|
{
|
|
if (!part->getPartPrimitive()->getGeometry()->isTerrain() &&
|
|
part->getPhysicalProperties().getCustomEnabled())
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (size_t i = 0; i < instance->numChildren(); i++)
|
|
{
|
|
if(instanceOrChildrenContainsNewCustomPhysics(instance->getChild(i)))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Vector3 PartInstance::xmlToUiSize(const Vector3& xmlSize) const
|
|
{
|
|
Vector3 uiSize = xmlSize;
|
|
|
|
if (!DFFlag::FormFactorDeprecated) {
|
|
switch (getFormFactor())
|
|
{
|
|
case BRICK:
|
|
uiSize.y = xmlSize.y / brickHeight();
|
|
break;
|
|
case PLATE:
|
|
uiSize.y = xmlSize.y / plateHeight(); // /= 0.4;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return uiSize;
|
|
}
|
|
|
|
Vector3 PartInstance::uiToXmlSize(const Vector3& uiSize) const
|
|
{
|
|
// >= minimium size (default 1.0) (no longer require int on all sides)
|
|
Vector3 gridUiSize, newRbxSize;
|
|
|
|
if (!DFFlag::FormFactorDeprecated) {
|
|
switch (getFormFactor())
|
|
{
|
|
case BRICK:
|
|
gridUiSize = Math::iRoundVector3(uiSize.max(getMinimumUiSize()));
|
|
newRbxSize = gridUiSize;
|
|
newRbxSize.y = gridUiSize.y * brickHeight();
|
|
break;
|
|
case PLATE:
|
|
gridUiSize = Math::iRoundVector3(uiSize.max(getMinimumUiSize()));
|
|
newRbxSize = gridUiSize;
|
|
newRbxSize.y = gridUiSize.y * plateHeight();
|
|
break;
|
|
case SYMETRIC:
|
|
newRbxSize = Math::iRoundVector3(uiSize.max(getMinimumUiSize()));
|
|
break;
|
|
case CUSTOM:
|
|
default:
|
|
newRbxSize = uiSize.max(getMinimumUiSizeCustom());
|
|
break;
|
|
}
|
|
} else {
|
|
newRbxSize = uiSize.max(getMinimumUiSizeCustom());
|
|
}
|
|
|
|
|
|
return newRbxSize;
|
|
}
|
|
|
|
|
|
void PartInstance::onCameraNear(float distance)
|
|
{
|
|
if (distance < cameraTransparentDistance()) {
|
|
setLocalTransparencyModifier(1.0);
|
|
}
|
|
else if (distance < cameraTranslucentDistance()) {
|
|
setLocalTransparencyModifier(0.5);
|
|
}
|
|
else {
|
|
setLocalTransparencyModifier(0.0);
|
|
}
|
|
}
|
|
|
|
void PartInstance::onClumpChanged()
|
|
{
|
|
if (getGfxPart())
|
|
getGfxPart()->onClumpChanged(this);
|
|
|
|
if (onDemandRead())
|
|
onDemandWrite()->clumpChangedSignal(shared_from(this));
|
|
|
|
if (DFFlag::SetUpdateTimeOnClumpChanged)
|
|
setLastUpdateTime(Time::nowFast());
|
|
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->clearHistory();
|
|
}
|
|
|
|
|
|
void PartInstance::onSleepingChanged(bool sleeping)
|
|
{
|
|
// Prevent this from being destroyed because of a side-effect of sleepingChangedSignal
|
|
// TODO: This may be removable if we have user slots, like scripts, run asynchronously.
|
|
shared_ptr<Instance> keep(shared_from(this));
|
|
|
|
RBXASSERT(sleeping == getSleeping());
|
|
if(onDemandRead())
|
|
onDemandWrite()->sleepingChangedSignal(sleeping);
|
|
|
|
if(getGfxPart())
|
|
getGfxPart()->onSleepingChanged(sleeping, this);
|
|
|
|
shouldRenderSetDirty();
|
|
}
|
|
|
|
void PartInstance::onBuoyancyChanged( bool value )
|
|
{
|
|
if (onDemandRead())
|
|
onDemandWrite()->buoyancyChangedSignal(value);
|
|
}
|
|
|
|
bool PartInstance::isInContinousMotion()
|
|
{
|
|
if (Mechanism* m = getPartPrimitive()->getMechanism())
|
|
{
|
|
// Use root of mechanism to determine motion
|
|
Primitive* mechRoot = m->getMechanismPrimitive();
|
|
PartInstance* mechRootPart = PartInstance::fromPrimitive(mechRoot);
|
|
|
|
return mechRootPart->renderingCoordinateFrame && mechRootPart->renderingCoordinateFrame->isBeingMovedByInterpolator();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool PartInstance::askSetParent(const Instance* instance) const
|
|
{
|
|
return Instance::fastDynamicCast<ModelInstance>(instance)!=NULL;
|
|
}
|
|
|
|
|
|
PartInstance* PartInstance::fromPrimitive(Primitive* p)
|
|
{
|
|
return const_cast<PartInstance*>(fromConstPrimitive(p));
|
|
}
|
|
|
|
|
|
const PartInstance* PartInstance::fromConstPrimitive(const Primitive* p)
|
|
{
|
|
return p
|
|
? rbx_static_cast<PartInstance*>(p->getOwner())
|
|
: NULL;
|
|
}
|
|
|
|
|
|
Clump* PartInstance::getClump()
|
|
{
|
|
Primitive* p = this->getPartPrimitive();
|
|
RBXASSERT(p);
|
|
return p ? p->getClump() : NULL;
|
|
}
|
|
|
|
const Clump* PartInstance::getConstClump() const
|
|
{
|
|
const Primitive* p = this->getConstPartPrimitive();
|
|
RBXASSERT(p);
|
|
return p ? p->getConstClump() : NULL;
|
|
}
|
|
|
|
|
|
PartInstance* PartInstance::fromAssembly(Assembly* a)
|
|
{
|
|
RBXASSERT(a);
|
|
Primitive* p = a->getAssemblyPrimitive();
|
|
return PartInstance::fromPrimitive(p);
|
|
}
|
|
|
|
const PartInstance* PartInstance::fromConstAssembly(const Assembly* a)
|
|
{
|
|
RBXASSERT(a);
|
|
const Primitive* p = a->getConstAssemblyPrimitive();
|
|
return PartInstance::fromConstPrimitive(p);
|
|
}
|
|
|
|
// destroy explicit (manual) joints and implicit (auto) joints
|
|
void PartInstance::destroyJoints()
|
|
{
|
|
if (World* world = Workspace::getWorldIfInWorkspace(this)) {
|
|
world->destroyAutoJoints(getPartPrimitive());
|
|
}
|
|
}
|
|
|
|
// only destroy auto joints
|
|
void PartInstance::destroyImplicitJoints()
|
|
{
|
|
if (World* world = Workspace::getWorldIfInWorkspace(this)) {
|
|
world->destroyAutoJoints(getPartPrimitive(), false);
|
|
}
|
|
}
|
|
|
|
void PartInstance::join()
|
|
{
|
|
if (World* world = Workspace::getWorldIfInWorkspace(this)) {
|
|
world->createAutoJoints(getPartPrimitive());
|
|
}
|
|
}
|
|
|
|
bool PartInstance::computeSurfacesNeedAdorn() const
|
|
{
|
|
for (int i = 0; i < 6; ++i) {
|
|
SurfaceType type = getSurfaceType(NormalId(i));
|
|
if (type == ROTATE
|
|
|| type == ROTATE_P
|
|
|| type == ROTATE_V)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Part PartInstance::getPart()
|
|
{
|
|
Vector6<SurfaceType> surf6;
|
|
for (int i = 0; i < 6; i++) {
|
|
surf6[i] = getSurfaceType(NormalId(i));
|
|
}
|
|
|
|
return Part(
|
|
getPartType(),
|
|
getConstPartPrimitive()->getSize(),
|
|
Color4(getColor3(), alpha()),
|
|
surf6,
|
|
calcRenderingCoordinateFrame());
|
|
}
|
|
|
|
void PartInstance::onServiceProvider(ServiceProvider* oldProvider, ServiceProvider* newProvider)
|
|
{
|
|
Super::onServiceProvider(oldProvider, newProvider);
|
|
}
|
|
|
|
void PartInstance::updatePrimitiveState()
|
|
{
|
|
Workspace* workspace = Workspace::getWorkspaceIfInWorkspace(this);
|
|
World* world = workspace ? workspace->getWorld() : NULL;
|
|
World* oldWorld = getPartPrimitive()->getWorld();
|
|
|
|
if (world != oldWorld)
|
|
{
|
|
RBXASSERT(oldWorld || (getPartPrimitive()->getClump()==NULL));
|
|
|
|
if (oldWorld)
|
|
{
|
|
Primitive* prim = getPartPrimitive();
|
|
// signal stop touching
|
|
for (int i = 0; i < prim->getNumContacts(); i++)
|
|
{
|
|
if (prim->getContact(i)->isInContact())
|
|
reportUntouch(shared_from(fromPrimitive(prim->getContactOther(i))));
|
|
}
|
|
|
|
setMovingManager(NULL);
|
|
oldWorld->removePrimitive(prim, getIsCurrentlyStreamRemovingPart());
|
|
}
|
|
|
|
if (world)
|
|
{
|
|
world->insertPrimitive(getPartPrimitive());
|
|
|
|
// The part is likely to be in non-sleeping state since CFrame setup marks the part as moving
|
|
// if the part is grounded it won't move; forcing the part to sleep eliminates useless awake-asleep
|
|
// transitions that would happen otherwise.
|
|
if (getPartPrimitive()->computeIsGrounded())
|
|
forceSleep();
|
|
|
|
setMovingManager(workspace);
|
|
|
|
const RBX::SystemAddress localAddress = RBX::Network::Players::findLocalSimulatorAddress(this);
|
|
|
|
// assign network ownership, this allows the part to be simulated asap
|
|
// this assignment is only temporary until it gets updated by server network owner job
|
|
if (getNetworkOwner() == RBX::Network::NetworkOwner::Unassigned())
|
|
{
|
|
if (RBX::Network::Player* player = RBX::Network::Players::findLocalPlayer(this))
|
|
{
|
|
CoordinateFrame temp;
|
|
// TODO WHEN REMOVING THIS FLAG:
|
|
// Must refactor function so that this Unassigned() logic is ran as a break-away function
|
|
if (DFFlag::LocalScriptSpawnPartAlwaysSetOwner)
|
|
{
|
|
getPartPrimitive()->setNetworkOwner(localAddress);
|
|
}
|
|
else if (player->hasCharacterHead(temp))
|
|
{
|
|
// TODO: check in region?
|
|
getPartPrimitive()->setNetworkOwner(localAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
getPartPrimitive()->setNetworkOwner(RBX::Network::NetworkOwner::ServerUnassigned());
|
|
}
|
|
}
|
|
|
|
cachedNetworkOwnerIsSomeoneElse = computeNetworkOwnerIsSomeoneElseImpl(getNetworkOwner(), localAddress);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Into / out of the World (not datamodel)
|
|
void PartInstance::onAncestorChanged(const AncestorChanged& event)
|
|
{
|
|
Super::onAncestorChanged(event);
|
|
|
|
// If there are any physics Joints connected to this PartInstance's primitive
|
|
// that are not a decendent of the PartInstance, process the JointInstance as if
|
|
// it were a child (special case for Joints since they can essentially have 2 parents,
|
|
// and are processed as such by the physics pipeline)
|
|
Primitive* prim = getPartPrimitive();
|
|
if (prim->getNumJoints() > 0 && prim->findWorld() != Workspace::getWorldIfInWorkspace(this))
|
|
{
|
|
std::vector<Joint*> processJoints;
|
|
for (int i = 0; i < prim->getNumJoints(); i++)
|
|
{
|
|
if (prim->getJoint(i)->getJointOwner())
|
|
processJoints.push_back(prim->getJoint(i));
|
|
}
|
|
for (unsigned int i = 0; i < processJoints.size(); i++)
|
|
{
|
|
JointInstance* jointInstance = static_cast<JointInstance*>(processJoints[i]->getJointOwner());
|
|
jointInstance->onCoParentChanged();
|
|
}
|
|
}
|
|
|
|
updatePrimitiveState();
|
|
updateHumanoidCookie();
|
|
}
|
|
|
|
|
|
bool PartInstance::shouldRender3dAdorn() const
|
|
{
|
|
return ((getTransparencyUi() < 1.0f) &&
|
|
(computeSurfacesNeedAdorn()
|
|
|| PartInstance::showContactPoints
|
|
|| PartInstance::showJointCoordinates
|
|
|| PartInstance::highlightSleepParts
|
|
|| PartInstance::highlightAwakeParts
|
|
|| PartInstance::showBodyTypes
|
|
|| PartInstance::showPartCoordinateFrames
|
|
|| PartInstance::showAnchoredParts
|
|
|| PartInstance::showUnalignedParts
|
|
|| PartInstance::showSpanningTree
|
|
|| Workspace::showEPhysicsOwners
|
|
|| Workspace::showEPhysicsRegions
|
|
|| PartInstance::showMechanisms
|
|
|| PartInstance::showAssemblies
|
|
|| PartInstance::showReceiveAge
|
|
|| PartInstance::showInterpolationPath));
|
|
}
|
|
|
|
|
|
void PartInstance::render3dAdorn(Adorn* adorn)
|
|
{
|
|
if(getTransparencyUi() >= 1.0f)
|
|
return;
|
|
|
|
if (getPartPrimitive()->getWorld() == NULL)
|
|
return;
|
|
|
|
if (computeSurfacesNeedAdorn()) {
|
|
Draw::partAdorn(
|
|
getPart(),
|
|
adorn,
|
|
Color3::gray()
|
|
);
|
|
}
|
|
|
|
if (PartInstance::highlightSleepParts && getPartPrimitive()->getAssembly())
|
|
{
|
|
Sim::AssemblyState sleepStatus = getPartPrimitive()->getAssembly()->getAssemblyState();
|
|
if ( (sleepStatus != Sim::ANCHORED)
|
|
&& (sleepStatus != Sim::AWAKE))
|
|
{
|
|
Color3 sleepColor;
|
|
switch (sleepStatus)
|
|
{
|
|
case Sim::RECURSIVE_WAKE_PENDING: sleepColor = Color3::purple(); break;
|
|
case Sim::WAKE_PENDING: sleepColor = Color3::purple(); break;
|
|
case Sim::SLEEPING_CHECKING: sleepColor = Color3::orange(); break;
|
|
case Sim::SLEEPING_DEEPLY: sleepColor = Color3::yellow(); break;
|
|
default: RBXASSERT(0); break;
|
|
}
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
sleepColor);
|
|
}
|
|
}
|
|
|
|
if (PartInstance::highlightAwakeParts && getPartPrimitive()->getAssembly())
|
|
{
|
|
Sim::AssemblyState sleepStatus = getPartPrimitive()->getAssembly()->getAssemblyState();
|
|
if ( (sleepStatus != Sim::ANCHORED)
|
|
&& (sleepStatus != Sim::SLEEPING_DEEPLY) )
|
|
{
|
|
Color3 wakeColor;
|
|
switch (sleepStatus)
|
|
{
|
|
case Sim::AWAKE: wakeColor = Color3::red(); break;
|
|
case Sim::SLEEPING_CHECKING: wakeColor = Color3::orange(); break;
|
|
case Sim::RECURSIVE_WAKE_PENDING: wakeColor = Color3::purple(); break;
|
|
case Sim::WAKE_PENDING: wakeColor = Color3::purple(); break;
|
|
default: RBXASSERT(0); break;
|
|
}
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
wakeColor);
|
|
}
|
|
}
|
|
|
|
if (PartInstance::showBodyTypes)
|
|
{
|
|
Body* body = getPartPrimitive()->getBody();
|
|
Workspace* workspace = Workspace::getWorkspaceIfInWorkspace(this);
|
|
World* world = workspace ? workspace->getWorld() : NULL;
|
|
|
|
if ( body && world )
|
|
{
|
|
SimBody* simBody = body->getRootSimBody();
|
|
if (simBody->isInKernel())
|
|
{
|
|
if ( simBody->isFreeFallBody() )
|
|
Draw::selectionBox( getPart(), adorn, Color3::green());
|
|
else if ( simBody->isRealTimeBody() )
|
|
Draw::selectionBox( getPart(), adorn, Color3::red());
|
|
else if ( simBody->isJointBody() )
|
|
Draw::selectionBox( getPart(), adorn, Color3::blue());
|
|
else {
|
|
RBXASSERT( simBody->isContactBody() );
|
|
if (simBody->isSymmetricContact())
|
|
{
|
|
if (simBody->isVerticalContact())
|
|
Draw::selectionBox( getPart(), adorn, Color3::brown());
|
|
else
|
|
Draw::selectionBox( getPart(), adorn, Color3::orange());
|
|
}
|
|
else
|
|
Draw::selectionBox( getPart(), adorn, Color3::yellow());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (PartInstance::showPartCoordinateFrames) {
|
|
renderCoordinateFrame(adorn);
|
|
}
|
|
|
|
if (PartInstance::showAnchoredParts && getAnchored()) {
|
|
DrawAdorn::partSurface(
|
|
getPart(),
|
|
Math::getClosestObjectNormalId(Vector3(0, -1, 0), getCoordinateFrame().rotation),
|
|
adorn,
|
|
G3D::Color4(Color3::gray(), alpha()));
|
|
}
|
|
|
|
if (PartInstance::showUnalignedParts && !aligned()) {
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
Color3::yellow());
|
|
}
|
|
|
|
if (PartInstance::showSpanningTree) {
|
|
if (getPartPrimitive()->getMechanism())
|
|
{
|
|
if (Mechanism::isMechanismRootPrimitive(getPartPrimitive()))
|
|
{
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame().translation);
|
|
Vector3 boxSize(Vector3::one() * 0.40);
|
|
bool primitiveAnchoredInEngine = Assembly::computeIsGroundingPrimitive(getPartPrimitive());
|
|
|
|
adorn->box( AABox(-boxSize, boxSize),
|
|
(primitiveAnchoredInEngine ? Color3::red() : Color3::orange())
|
|
);
|
|
}
|
|
else if (Mechanism::getRootMovingPrimitive(getPartPrimitive())==getPartPrimitive())
|
|
{
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame().translation);
|
|
Vector3 boxSize(Vector3::one() * 0.40);
|
|
adorn->box( AABox(-boxSize, boxSize),
|
|
(Color3::blue())
|
|
);
|
|
}
|
|
else if (Assembly::isAssemblyRootPrimitive(getPartPrimitive()))
|
|
{
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame().translation);
|
|
adorn->sphere( Sphere(Vector3::zero(), 0.4), Color3::yellow() * .8f );
|
|
}
|
|
else if (Clump::isClumpRootPrimitive(getPartPrimitive()))
|
|
{
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame().translation);
|
|
adorn->cylinderAlongX(0.125f, 0.25f, Color3::orange());
|
|
}
|
|
}
|
|
}
|
|
if (PartInstance::showContactPoints) {
|
|
for (int i = 0; i < getPartPrimitive()->getNumContacts(); ++i) {
|
|
Contact* c = getPartPrimitive()->getContact(i);
|
|
for (int j = 0; j < c->numConnectors(); ++j) {
|
|
Vector3 loc, normal;
|
|
float length;
|
|
ContactConnector* connector = c->getConnector(j);
|
|
if (!connector->isInKernel())
|
|
continue;
|
|
Color3 color = Color3::white();
|
|
if (connector->isSecondPass())
|
|
color = Color3::red();
|
|
else if (connector->isRealTime())
|
|
color = Color3::orange();
|
|
else if (connector->isJoint())
|
|
color = Color3::blue();
|
|
else if (connector->isContact())
|
|
{
|
|
color = Color3::yellow();
|
|
if (connector->isRestingContact())
|
|
color *= 0.5;
|
|
}
|
|
|
|
connector->getLengthNormalPosition(loc, normal, length);
|
|
if (length < 0.0f) {
|
|
adorn->setObjectToWorldMatrix(CoordinateFrame(loc));
|
|
adorn->ray( RbxRay::fromOriginAndDirection(Vector3::zero(), normal * length * -4.0f), color * .8f );
|
|
adorn->sphere( Sphere(Vector3::zero(), 0.5), color * .8f );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PartInstance::showJointCoordinates) {
|
|
|
|
// Helpful visualization function: shows the center of mass
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame().pointToWorldSpace(getPartPrimitive()->getGeometry()->getCofmOffset()));
|
|
adorn->sphere(Sphere(Vector3::zero(), 0.2), Color3::orange() * .8f );
|
|
|
|
adorn->setObjectToWorldMatrix(getCoordinateFrame());
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitX() * 2.0f ), Color3::red() * .5f );
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitY() * 2.0f ), Color3::green() * .5f );
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitZ() * 2.0f ), Color3::blue() * .5f );
|
|
|
|
for (int i = 0; i < getPartPrimitive()->getNumJoints(); ++i) {
|
|
Primitive* primitive = getPartPrimitive();
|
|
Joint* joint = primitive->getJoint(i);
|
|
if (!Joint::isSpringJoint(joint))
|
|
continue;
|
|
for (int j = 0; j < 2; ++j) {
|
|
adorn->setObjectToWorldMatrix(joint->getJointWorldCoord(j));
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitX() * 2.0f ), Color3::red() * .5f );
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitY() * 2.0f ), Color3::green() * .5f );
|
|
adorn->ray( RbxRay::fromOriginAndDirection( Vector3::zero(), Vector3::unitZ() * 2.0f ), Color3::blue() * .5f );
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Workspace::showEPhysicsOwners) {
|
|
if (this->getPartPrimitive()->getAssembly()) {
|
|
if (Primitive* movingRoot = Mechanism::getRootMovingPrimitive(this->getPartPrimitive())) {
|
|
const RBX::SystemAddress& systemAddress = PartInstance::fromPrimitive(movingRoot)->getNetworkOwner();
|
|
Color3 color = RBX::Network::NetworkOwner::colorFromAddress(systemAddress);
|
|
|
|
Assembly* a = movingRoot->getAssembly();
|
|
World* world = getPartPrimitive()->getWorld();
|
|
if (!world->getSpatialFilter()->addressMatch(a))
|
|
if (a->getFilterPhase() == Assembly::Sim_BufferZone)
|
|
color = Color::red();
|
|
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
color
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (PartInstance::showInterpolationPath)
|
|
{
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->renderPath(adorn);
|
|
}
|
|
|
|
if (PartInstance::showAssemblies) {
|
|
if (Assembly* a = this->getPartPrimitive()->getAssembly()) {
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
RBX::Color::colorFromPointer(a));
|
|
}
|
|
}
|
|
if (PartInstance::showMechanisms) {
|
|
if (Mechanism* m = this->getPartPrimitive()->getMechanism()) {
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
RBX::Color::colorFromPointer(m));
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::render3dSelect(Adorn* adorn, SelectState selectState)
|
|
{
|
|
Draw::selectionBox(
|
|
getPart(),
|
|
adorn,
|
|
selectState,
|
|
0.02f);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool PartInstance::hitTestImpl(const RBX::RbxRay& worldRay, Vector3& worldHitPoint)
|
|
{
|
|
RBX::RbxRay rayInPartCoords = getCoordinateFrame().toObjectSpace(worldRay);
|
|
|
|
Vector3 hitPointInPartCoords;
|
|
|
|
bool answer = HitTest::hitTest(getPart(), rayInPartCoords, hitPointInPartCoords, 1.0);
|
|
|
|
if (answer)
|
|
{
|
|
worldHitPoint = getCoordinateFrame().pointToWorldSpace(hitPointInPartCoords);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
// TODO: Change this - should occur on a surface by surface basis
|
|
|
|
void PartInstance::onSurfaceChanged(NormalId surfId)
|
|
{
|
|
shouldRenderSetDirty();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Sets and gets
|
|
|
|
// Server side - any pv change sets network owner to Server and resets the clock
|
|
void PartInstance::onPVChangedFromReflection()
|
|
{
|
|
if (getNetworkOwnershipRule() == NetworkOwnership_Manual)
|
|
{
|
|
// We want the user to be able to to set CFrame of the part
|
|
// withouth having to set Owner to Server
|
|
return;
|
|
}
|
|
if (Network::NetworkOwner::isClient(getNetworkOwner()))
|
|
{
|
|
if (Workspace::serverIsPresent(this))
|
|
{
|
|
if (Network::Players::getDistributedPhysicsEnabled())
|
|
{
|
|
// do this only if this part is not humanoid
|
|
if(ModelInstance* m = Instance::fastDynamicCast<ModelInstance>(this->getParent())) {
|
|
if (Network::Players::getPlayerFromCharacter(m))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
resetNetworkOwnerTime(3.0); // wait 3 seconds if PV set while already a client
|
|
setNetworkOwnerAndNotify(Network::NetworkOwner::Server());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<const Instances> PartInstance::getTouchingParts()
|
|
{
|
|
RBXASSERT(getPartPrimitive());
|
|
|
|
if (ContactManager* contactManager = Workspace::getContactManagerIfInWorkspace(this))
|
|
return contactManager->getPartCollisions(getPartPrimitive());
|
|
|
|
return shared_ptr<const Instances>();
|
|
}
|
|
|
|
void PartInstance::safeMove()
|
|
{
|
|
RBXASSERT(getPartPrimitive());
|
|
RBXASSERT_IF_VALIDATING(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
if (ContactManager* contactManager = Workspace::getContactManagerIfInWorkspace(this)) {
|
|
G3D::Array<Primitive*> temp;
|
|
temp.append(getPartPrimitive());
|
|
Dragger::safeMoveNoDrop(temp, Vector3::zero(), *contactManager);
|
|
}
|
|
}
|
|
|
|
void PartInstance::addInterpolationSample(const CoordinateFrame& value, const Velocity& velocity, const RemoteTime& timeStamp, Time now, float localTimeOffest, int numNodesAhead)
|
|
{
|
|
createRenderingCoordinateFrame();
|
|
renderingCoordinateFrame->setValue(this, value, velocity, timeStamp, now, localTimeOffest, numNodesAhead);
|
|
}
|
|
|
|
void PartInstance::setInterpolationDelay(float value)
|
|
{
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->setTargetDelay(value);
|
|
}
|
|
|
|
// All position changes from script go through this function
|
|
void PartInstance::setPhysics(const CoordinateFrame& value)
|
|
{
|
|
if (value != getPartPrimitive()->getCoordinateFrame())
|
|
{
|
|
RBXASSERT_IF_VALIDATING(Math::isOrthonormal(value.rotation));
|
|
|
|
getPartPrimitive()->setCoordinateFrame(value);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setPhysics(const PV& pv)
|
|
{
|
|
getPartPrimitive()->setVelocity(pv.velocity);
|
|
setPhysics(pv.position);
|
|
}
|
|
|
|
void PartInstance::setCoordinateFrame(const CoordinateFrame& value)
|
|
{
|
|
if (value != this->getCoordinateFrame())
|
|
{
|
|
CoordinateFrame orthoValue(value);
|
|
if (Math::hasNanOrInf(orthoValue)) {
|
|
// TODO: ASSERT This makes bad parts disappear silently.
|
|
// This is the desired behavior when reading a file,
|
|
// but if we try to set the CF from a script the desired
|
|
// behaviour would be to throw an exception.
|
|
// Put the part way down so that Workspace will delete it
|
|
orthoValue = CoordinateFrame(Vector3(0,-FLT_MAX,0));
|
|
}
|
|
else {
|
|
Math::orthonormalizeIfNecessary(orthoValue.rotation);
|
|
}
|
|
|
|
if (renderingCoordinateFrame)
|
|
{
|
|
renderingCoordinateFrame->clearHistory();
|
|
renderingCoordinateFrame->setRenderedFrame(value);
|
|
}
|
|
|
|
setPhysics(orthoValue);
|
|
onPVChangedFromReflection();
|
|
raisePropertyChanged(prop_CFrame);
|
|
raisePropertyChanged(prop_PositionUi);
|
|
raisePropertyChanged(prop_RotationUi);
|
|
|
|
if(onDemandRead())
|
|
onDemandWrite()->cframeChangedFromReflectionSignal();
|
|
}
|
|
}
|
|
|
|
CoordinateFrame PartInstance::computeRenderingCoordinateFrame(PartInstance* mechanismRootPart, const Time& t)
|
|
{
|
|
RBXASSERT(this->getConstPartPrimitive()->getConstMechanism() == mechanismRootPart->getConstPartPrimitive()->getConstMechanism());
|
|
RBXASSERT(mechanismRootPart->getConstPartPrimitive()->getConstMechanism()->getConstMechanismPrimitive() == mechanismRootPart->getConstPartPrimitive());
|
|
|
|
if (this == mechanismRootPart)
|
|
{
|
|
if (renderingCoordinateFrame)
|
|
{
|
|
World* world = getPartPrimitive()->getWorld();
|
|
|
|
if (world && world->getUiStepId() > renderingCoordinateFrame->getUiStepId())
|
|
{
|
|
renderingCoordinateFrame->setUiStepId(world->getUiStepId());
|
|
return renderingCoordinateFrame->computeValue(this, t);
|
|
}
|
|
else
|
|
{
|
|
return renderingCoordinateFrame->getLastComputedValue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return getCoordinateFrame();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CoordinateFrame meInRoot = mechanismRootPart->getCoordinateFrame().toObjectSpace(this->getCoordinateFrame());
|
|
|
|
return mechanismRootPart->computeRenderingCoordinateFrame(mechanismRootPart, t) * meInRoot;
|
|
}
|
|
}
|
|
|
|
CoordinateFrame PartInstance::calcRenderingCoordinateFrame()
|
|
{
|
|
if (DFFlag::CleanUpInterpolationTimestamps)
|
|
{
|
|
return calcRenderingCoordinateFrame(TaskScheduler::singleton().lastCyclcTimestamp);
|
|
}
|
|
|
|
return calcRenderingCoordinateFrame(Time::nowFast());
|
|
}
|
|
|
|
CoordinateFrame PartInstance::calcRenderingCoordinateFrame(const Time& t)
|
|
{
|
|
Mechanism* m = getPartPrimitive()->getMechanism();
|
|
bool networkOwnerIsSomeoneElse = computeNetworkOwnerIsSomeoneElse();
|
|
if (DFFlag::HumanoidFloorPVUpdateSignal && !PartInstance::disableInterpolation && m && (!networkOwnerIsSomeoneElse))
|
|
{
|
|
PartInstance* mechRootPart = PartInstance::fromPrimitive(m->getMechanismPrimitive());
|
|
Humanoid* humanoid = RBX::Humanoid::modelIsCharacter(mechRootPart->getParent());
|
|
if (humanoid && humanoid->getLastFloor())
|
|
{
|
|
const shared_ptr<PartInstance>& floorPart = humanoid->getLastFloor();
|
|
if (floorPart)
|
|
{
|
|
Vector3 localDeltaToChar = floorPart->getCoordinateFrame().pointToObjectSpace(getCoordinateFrame().translation);
|
|
return CoordinateFrame(getCoordinateFrame().rotation) + floorPart->calcRenderingCoordinateFrameImpl(t).pointToWorldSpace(localDeltaToChar);
|
|
}
|
|
}
|
|
}
|
|
return calcRenderingCoordinateFrameImpl(t);
|
|
}
|
|
|
|
CoordinateFrame PartInstance::calcRenderingCoordinateFrameImpl(const Time& t)
|
|
{
|
|
Mechanism* m = getPartPrimitive()->getMechanism();
|
|
|
|
if (!PartInstance::disableInterpolation && m)
|
|
{
|
|
Primitive* mechRoot = m->getMechanismPrimitive();
|
|
PartInstance* mechRootPart = PartInstance::fromPrimitive(mechRoot);
|
|
|
|
// See computeNetworkOwnerIsSomeoneElse(); we inline this here to avoid redundant computation of mech primitive
|
|
if (mechRootPart->cachedNetworkOwnerIsSomeoneElse)
|
|
{
|
|
return computeRenderingCoordinateFrame(mechRootPart, t);
|
|
}
|
|
}
|
|
|
|
const CoordinateFrame& cf = getCoordinateFrame();
|
|
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->setRenderedFrame(cf);
|
|
|
|
return cf;
|
|
}
|
|
|
|
bool PartInstance::computeNetworkOwnerIsSomeoneElse() const
|
|
{
|
|
if (const Mechanism* m = getConstPartPrimitive()->getConstMechanism()) {
|
|
// Use root of mechanism (not Assembly, as done previously) to determine network ownership
|
|
const Primitive* mechRoot = m->getConstMechanismPrimitive();
|
|
const PartInstance* mechRootPart = PartInstance::fromConstPrimitive(mechRoot);
|
|
|
|
return mechRootPart->cachedNetworkOwnerIsSomeoneElse;
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// A bunch of heuristic rules to categorize a part as projectile
|
|
bool PartInstance::isProjectile() const
|
|
{
|
|
if (getLinearVelocity().squaredMagnitude() < 25.0 * 25.0) // slow objects are not projectiles
|
|
return false;
|
|
|
|
const Primitive* p = getConstPartPrimitive();
|
|
|
|
const Primitive* clumpRoot = p->getRoot<Primitive>();
|
|
if (clumpRoot->numChildren() > 0) // clumps of more than one parts are not projectiles
|
|
return false;
|
|
RBXASSERT(p == clumpRoot);
|
|
const Clump* c = p->getConstClump();
|
|
const Clump* assemblyRoot = c->getRootClump();
|
|
if (assemblyRoot->numChildren() > 0) // Assemblies of more than one clumps are not projectiles
|
|
return false;
|
|
RBXASSERT(c == assemblyRoot);
|
|
const Assembly* a = p->getConstAssembly();
|
|
const Assembly* mechanismRoot = Mechanism::getConstMovingAssemblyRoot(a);
|
|
if (mechanismRoot->numChildren() > 0) // Mechanisms of more than one assemblies are not projectiles
|
|
return false;
|
|
RBXASSERT(a == mechanismRoot);
|
|
|
|
return true;
|
|
}
|
|
|
|
const CoordinateFrame& PartInstance::getCoordinateFrame() const
|
|
{
|
|
return getConstPartPrimitive()->getCoordinateFrame();
|
|
}
|
|
|
|
void PartInstance::setCoordinateFrameRoot(const CoordinateFrame& value)
|
|
{
|
|
Primitive* prim = getPartPrimitive();
|
|
PartInstance* part = this;
|
|
Mechanism* m = prim->getMechanism();
|
|
|
|
if (currentSecurityIdentityIsScript() && m != NULL)
|
|
{
|
|
prim = m->getMechanismPrimitive();
|
|
part = PartInstance::fromPrimitive(prim);
|
|
}
|
|
|
|
part->setCoordinateFrame(value);
|
|
}
|
|
|
|
void PartInstance::setTranslationUi(const Vector3& set)
|
|
{
|
|
CoordinateFrame current = getCoordinateFrame();
|
|
if (current.translation != set)
|
|
{
|
|
current.translation = set;
|
|
destroyJoints();
|
|
setCoordinateFrame(current);
|
|
safeMove();
|
|
join();
|
|
}
|
|
}
|
|
|
|
const Vector3& PartInstance::getTranslationUi() const
|
|
{
|
|
return getConstPartPrimitive()->getCoordinateFrame().translation; // getBody()->getPos();
|
|
}
|
|
|
|
/**
|
|
* Sets the rotation of the part using euler angles (in degrees).
|
|
*/
|
|
void PartInstance::setRotationUi(const Vector3& set)
|
|
{
|
|
CoordinateFrame current = getCoordinateFrame();
|
|
Matrix3 rotation = Matrix3::fromEulerAnglesXYZ(Math::degreesToRadians(set.x),Math::degreesToRadians(set.y),Math::degreesToRadians(set.z));
|
|
if ( rotation != current.rotation )
|
|
{
|
|
current.rotation = rotation;
|
|
destroyJoints();
|
|
setCoordinateFrame(current);
|
|
safeMove();
|
|
join();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Gets the rotation of the part using euler angles (in degrees).
|
|
*/
|
|
const Vector3 PartInstance::getRotationUi() const
|
|
{
|
|
CoordinateFrame current = getCoordinateFrame();
|
|
|
|
float x;
|
|
float y;
|
|
float z;
|
|
current.rotation.toEulerAnglesXYZ(x,y,z);
|
|
|
|
Vector3 v(
|
|
Math::radiansToDegrees(x),
|
|
Math::radiansToDegrees(y),
|
|
Math::radiansToDegrees(z) );
|
|
return v;
|
|
}
|
|
|
|
void PartInstance::setLinearVelocity(const Vector3& set)
|
|
{
|
|
|
|
Primitive* prim = getPartPrimitive();
|
|
PartInstance* part = this;
|
|
Mechanism* m = prim->getMechanism();
|
|
|
|
if (currentSecurityIdentityIsScript() && m != NULL)
|
|
{
|
|
prim = m->getMechanismPrimitive();
|
|
part = PartInstance::fromPrimitive(prim);
|
|
}
|
|
|
|
Velocity vel = prim->getBody()->getVelocity();
|
|
if (vel.linear != set) {
|
|
vel.linear = set;
|
|
prim->setVelocity(vel);
|
|
part->onPVChangedFromReflection();
|
|
part->raisePropertyChanged(prop_Velocity);
|
|
}
|
|
}
|
|
|
|
float PartInstance::getMass() const
|
|
{
|
|
return getConstPartPrimitive()->getConstBody()->getMass();
|
|
}
|
|
|
|
RBX::Vector3 PartInstance::getPrincipalMoment() const
|
|
{
|
|
return getConstPartPrimitive()->getConstBody()->getIBodyV3();
|
|
|
|
}
|
|
|
|
int PartInstance::getMassScript(lua_State* L)
|
|
{
|
|
if (DFFlag::MaterialPropertiesEnabled)
|
|
{
|
|
DataModel* dm = DataModel::get(&ScriptContext::getContext(L));
|
|
bool newMaterialPropertiesEnabled = dm->getWorkspace()->getUsingNewPhysicalProperties();
|
|
lua_pushnumber(L, getPartPrimitive()->getCalculateMass(newMaterialPropertiesEnabled));
|
|
return 1;
|
|
}
|
|
lua_pushnumber(L, getMass());
|
|
return 1;
|
|
}
|
|
|
|
const Velocity& PartInstance::getVelocity() const
|
|
{
|
|
return getConstPartPrimitive()->getPV().velocity; //getBody()->getVelocity();
|
|
}
|
|
|
|
const Vector3& PartInstance::getLinearVelocity() const
|
|
{
|
|
return getConstPartPrimitive()->getPV().velocity.linear; //getBody()->getVelocity().linear;
|
|
}
|
|
|
|
void PartInstance::setRotationalVelocityRoot(const Vector3& set)
|
|
{
|
|
Primitive* prim = getPartPrimitive();
|
|
PartInstance* part = this;
|
|
Mechanism* m = prim->getMechanism();
|
|
|
|
if (currentSecurityIdentityIsScript() && m != NULL)
|
|
{
|
|
prim = m->getMechanismPrimitive();
|
|
part = PartInstance::fromPrimitive(prim);
|
|
}
|
|
|
|
part->setRotationalVelocity(set);
|
|
}
|
|
|
|
void PartInstance::setRotationalVelocity(const Vector3& set)
|
|
{
|
|
Velocity vel = getPartPrimitive()->getBody()->getVelocity();
|
|
if (vel.rotational != set) {
|
|
vel.rotational = set;
|
|
getPartPrimitive()->setVelocity(vel);
|
|
onPVChangedFromReflection();
|
|
raisePropertyChanged(prop_RotVelocity);
|
|
}
|
|
}
|
|
|
|
const Vector3& PartInstance::getRotationalVelocity() const
|
|
{
|
|
return getConstPartPrimitive()->getPV().velocity.rotational; // getBody()->getVelocity().rotational;
|
|
}
|
|
|
|
|
|
const Vector3& PartInstance::getPartSizeXml() const
|
|
{
|
|
return getConstPartPrimitive()->getSize();
|
|
}
|
|
|
|
Vector3 PartInstance::getPartSizeUi() const
|
|
{
|
|
return getConstPartPrimitive()->getSize();
|
|
}
|
|
|
|
void PartInstance::setPartSizeXml(const Vector3& rbxSize)
|
|
{
|
|
if (DFFlag::FixShapeChangeBug)
|
|
{
|
|
// !hasThreeDimensionalSize = "requires all dimensions to be equal" (only spheres now)
|
|
// since this requirement might cause a size change we need a special case for it
|
|
if ((!hasThreeDimensionalSize() && rbxSize.xxx() != getPartSizeXml()))
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
getPartPrimitive()->setSize(rbxSize.xxx());
|
|
|
|
raisePropertyChanged(prop_Size);
|
|
|
|
refreshPartSizeUi();
|
|
}
|
|
else if (rbxSize != getPartSizeXml())
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
getPartPrimitive()->setSize(rbxSize);
|
|
|
|
raisePropertyChanged(prop_Size);
|
|
|
|
refreshPartSizeUi();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (rbxSize != getPartSizeXml())
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
Vector3 newRbxSize = hasThreeDimensionalSize() ? rbxSize : rbxSize.xxx();
|
|
|
|
// current smallest size is plate height (0.4) and grid x,z (1.0)
|
|
// TODO: ASSERT
|
|
//RBXASSERT(newRbxSize.max(Vector3(1.0, plateHeight(), 1.0)) == newRbxSize);
|
|
|
|
getPartPrimitive()->setSize(newRbxSize);
|
|
|
|
raisePropertyChanged(prop_Size);
|
|
|
|
refreshPartSizeUi();
|
|
}
|
|
}
|
|
}
|
|
void PartInstance::refreshPartSizeUi()
|
|
{
|
|
raisePropertyChanged(prop_SizeUi);
|
|
}
|
|
|
|
void PartInstance::setPartSizeUnjoined(const Vector3& uiSize)
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
Vector3 newRbxSize = this->uiToXmlSize(uiSize);
|
|
setPartSizeXml(newRbxSize);
|
|
safeMove(); // only moves if in-world
|
|
}
|
|
|
|
|
|
void PartInstance::setPartSizeUi(const Vector3& uiSize)
|
|
{
|
|
destroyJoints();
|
|
// Snap to nearest alignment:
|
|
Vector3 clampedSize = uiToXmlSize(xmlToUiSize(uiSize));
|
|
if(clampedSize != getPartSizeXml()){
|
|
setPartSizeXml(clampedSize );
|
|
}
|
|
else if(clampedSize != uiSize){
|
|
refreshPartSizeUi();
|
|
}
|
|
safeMove(); // only moves if in-world
|
|
join();
|
|
}
|
|
|
|
bool PartInstance::getDragging() const
|
|
{
|
|
return getConstPartPrimitive()->getDragging();
|
|
}
|
|
|
|
void PartInstance::setDragging(bool value)
|
|
{
|
|
if (value != getPartPrimitive()->getDragging()) {
|
|
getPartPrimitive()->setDragging( value );
|
|
raiseChanged(prop_Dragging);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::getNetworkIsSleeping() const
|
|
{
|
|
return getConstPartPrimitive()->getNetworkIsSleeping();
|
|
}
|
|
|
|
// Only set through replication, or in the filter stage
|
|
//
|
|
void PartInstance::setNetworkIsSleeping(bool value)
|
|
{
|
|
// if a change occurs, then onNetworkIsSleepingChanged will be called, which raises a prop_NetworkIsSleeping change
|
|
getPartPrimitive()->setNetworkIsSleeping(value, value?Time::nowFast():Time());
|
|
}
|
|
|
|
void PartInstance::onNetworkIsSleepingChanged(Time now)
|
|
{
|
|
// This call ensures that the rendering data has the latest coordinate frame for interpolation purposes.
|
|
// Without this, parts may go to sleep without updating the renderingCoordinateFrame, which will result
|
|
// in an interpolated coordinate frame at an intermediate, inappropriate location between proper end points
|
|
// (which often results in floating parts).
|
|
if (getNetworkIsSleeping())
|
|
{
|
|
RBXASSERT(!now.isZero()); // setNetworkIsSleeping passes 0 for speed when waking up.
|
|
if (DFFlag::CleanUpInterpolationTimestamps)
|
|
{
|
|
if (renderingCoordinateFrame && renderingCoordinateFrame->getLocalToRemoteTimeOffset() > 0.0)
|
|
addInterpolationSample(getCoordinateFrame(), getVelocity(), now - Time::Interval(renderingCoordinateFrame->getLocalToRemoteTimeOffset()), now, 0.0f);
|
|
}
|
|
else
|
|
{
|
|
createRenderingCoordinateFrame();
|
|
addInterpolationSample(getCoordinateFrame(), getVelocity(), now - Time::Interval(renderingCoordinateFrame->getLocalToRemoteTimeOffset()), now, 0.0f);
|
|
}
|
|
}
|
|
raiseChanged(prop_NetworkIsSleeping);
|
|
}
|
|
|
|
const RBX::SystemAddress PartInstance::getNetworkOwner() const
|
|
{
|
|
RBXASSERT(getConstPartPrimitive());
|
|
return getConstPartPrimitive()->getNetworkOwner();
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnerNotifyIfServer(const RBX::SystemAddress value, bool ownershipBecomingManual)
|
|
{
|
|
if (ownershipBecomingManual)
|
|
{
|
|
setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
}
|
|
|
|
if (RBX::Network::Players::serverIsPresent(this, false))
|
|
{
|
|
setNetworkOwnerAndNotify(value);
|
|
}
|
|
else
|
|
{
|
|
setNetworkOwner(value);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnershipRuleIfServer(NetworkOwnership value)
|
|
{
|
|
if (Network::Players::serverIsPresent(this, false))
|
|
{
|
|
setNetworkOwnershipRule(value);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setNetworkOwner(const RBX::SystemAddress value)
|
|
{
|
|
const RBX::SystemAddress currentOwner = getConstPartPrimitive()->getNetworkOwner();
|
|
if (value != currentOwner) {
|
|
RBXASSERT(value != Network::NetworkOwner::Unassigned());
|
|
|
|
const RBX::SystemAddress localAddress = RBX::Network::Players::findLocalSimulatorAddress(this);
|
|
|
|
if (value == localAddress)
|
|
{
|
|
// we will be new owner, no longer need interpolation
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->clearHistory();
|
|
}
|
|
|
|
cachedNetworkOwnerIsSomeoneElse = computeNetworkOwnerIsSomeoneElseImpl(value, localAddress);
|
|
|
|
if (Network::NetworkOwner::isClient(localAddress))
|
|
{
|
|
if (currentOwner == localAddress)
|
|
{
|
|
// if we switch from client to others, we change the recorded last cframe to the local value
|
|
// this is needed for cross packet compression in path-based movement
|
|
setLastCFrame(getCoordinateFrame());
|
|
setLastUpdateTime(Time::nowFast());
|
|
|
|
if (renderingCoordinateFrame)
|
|
renderingCoordinateFrame->clearHistory();
|
|
}
|
|
}
|
|
|
|
getPartPrimitive()->setNetworkOwner(value);
|
|
|
|
#ifdef RBX_TEST_BUILD
|
|
ownershipChangeSignal((float)value.getAddress(), (float)value.getPort());
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnerAndNotify(const RBX::SystemAddress value)
|
|
{
|
|
const RBX::SystemAddress oldOwner = getConstPartPrimitive()->getNetworkOwner();
|
|
setNetworkOwner(value);
|
|
|
|
notifyNetworkOwnerChanged(oldOwner);
|
|
}
|
|
|
|
void PartInstance::notifyNetworkOwnerChanged(const RBX::SystemAddress oldOwner)
|
|
{
|
|
const RBX::SystemAddress currentOwner = getConstPartPrimitive()->getNetworkOwner();
|
|
if (oldOwner != currentOwner)
|
|
{
|
|
/*
|
|
Who to notify:
|
|
old owner new owner
|
|
server->client x
|
|
client->client x x
|
|
client->server x x
|
|
c->s (triggered by client) x
|
|
*/
|
|
|
|
Reflection::EventArguments args(1);
|
|
|
|
if (Network::Players::backendProcessing(this))
|
|
{
|
|
if (oldOwner == Network::NetworkOwner::ServerUnassigned())
|
|
{
|
|
// if previous owner is ServerUnassigned, notify everyone owner is now assigned
|
|
raiseChanged(prop_NetworkOwner);
|
|
return;
|
|
}
|
|
|
|
if (!Network::NetworkOwner::isServer(oldOwner))
|
|
{
|
|
// notify old owner the part will be owned by someone else
|
|
args[0] = Network::NetworkOwner::AssignedOther();
|
|
raiseEventInvocation(event_NetworkOwnerChanged, args, &oldOwner);
|
|
}
|
|
|
|
if (!Network::NetworkOwner::isServer(currentOwner))
|
|
{
|
|
// notify new owner to take control
|
|
args[0] = currentOwner;
|
|
raiseEventInvocation(event_NetworkOwnerChanged, args, ¤tOwner);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// client should only be changing owner to server
|
|
RBXASSERT(currentOwner == Network::NetworkOwner::Server());
|
|
|
|
args[0] = currentOwner;
|
|
raiseEventInvocation(event_NetworkOwnerChanged, args, NULL);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
rbx::remote_signal<void(RBX::SystemAddress)>* PartInstance::getOrCreateNetworkOwnerChangedSignal(bool create)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
void PartInstance::processRemoteEvent(const EventDescriptor& descriptor, const EventArguments& args, const SystemAddress& source)
|
|
{
|
|
if (descriptor == event_NetworkOwnerChanged)
|
|
{
|
|
SystemAddress address = args[0].cast<SystemAddress>();
|
|
|
|
setNetworkOwner(address);
|
|
if (DFFlag::FixFallenPartsNotDeleted)
|
|
{
|
|
// networkOwnerTime does NOTHING on the Client
|
|
// When a part falls bellow "Kill Zone" they are pushed to server for ownership
|
|
// and then the server deletes them on the next frame.
|
|
// Without a networkOwnerTime(out) we may run NetworkOwnerJob which will return ownership to the Client.
|
|
resetNetworkOwnerTime(3.0f);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
Super::processRemoteEvent(descriptor, args, source);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::getCanCollide() const
|
|
{
|
|
return !getConstPartPrimitive()->getPreventCollide();
|
|
}
|
|
|
|
void PartInstance::setCanCollide(bool value)
|
|
{
|
|
bool preventCollide = !value;
|
|
if (preventCollide != getPartPrimitive()->getPreventCollide()) {
|
|
getPartPrimitive()->setPreventCollide( preventCollide );
|
|
raiseChanged(prop_CanCollide);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::getAnchored() const
|
|
{
|
|
return getConstPartPrimitive()->getAnchoredProperty();
|
|
}
|
|
|
|
void PartInstance::setAnchored(bool value)
|
|
{
|
|
if (value != getPartPrimitive()->getAnchoredProperty()) {
|
|
std::vector<Primitive*> futureMechRoots;
|
|
RootPrimitiveOwnershipData ownershipData;
|
|
|
|
if (value)
|
|
{
|
|
gatherResetOwnershipDataPreAnchor(futureMechRoots, ownershipData);
|
|
}
|
|
else if (DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
gatherResetOwnershipDataPreUnanchor(futureMechRoots);
|
|
}
|
|
|
|
getPartPrimitive()->setAnchoredProperty( value );
|
|
|
|
if (value)
|
|
{
|
|
updateOwnershipSettingsPostAnchor(futureMechRoots, ownershipData);
|
|
}
|
|
else if (DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
updateOwnershipSettingsPostUnAnchor(futureMechRoots);
|
|
}
|
|
else
|
|
{
|
|
updateResetOwnershipSettingsOnUnAnchor();
|
|
}
|
|
// Only need this to happen when setting to TRUE, because otherwise physics update take care of this.
|
|
if (FFlag::AnchoredSendPositionUpdate && value)
|
|
{
|
|
// Anchored will prevent position updates from syncing up between clients.
|
|
// This sends one final position update to make sure anchored parts stay anchored in same place on
|
|
// all clients.
|
|
const RBX::SystemAddress localAddress = RBX::Network::Players::findLocalSimulatorAddress(this);
|
|
if (this->getNetworkOwner() == localAddress)
|
|
{
|
|
raiseChanged(prop_CFrame);
|
|
}
|
|
}
|
|
raiseChanged(prop_Anchored);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::isGrounded()
|
|
{
|
|
return (primitive != NULL) && primitive->computeIsGrounded();
|
|
}
|
|
|
|
static void getConnectedPartsVisitor(Primitive *prim, shared_ptr<Instances> &inst)
|
|
{
|
|
if (prim != NULL && PartInstance::fromPrimitive(prim) != NULL)
|
|
{
|
|
inst->push_back(shared_from(PartInstance::fromPrimitive(prim)));
|
|
}
|
|
}
|
|
|
|
void PartInstance::getConnectedPartsRecursiveImpl(shared_ptr<Instances> &objs, boost::unordered_set<PartInstance*> &seen) const
|
|
{
|
|
const Workspace* ws = Workspace::findConstWorkspace(this);
|
|
const Instance* terrain = ws != NULL ? ws->getTerrain() : NULL;
|
|
for (int i = 0; i < primitive->getNumJoints(); ++i)
|
|
{
|
|
Primitive* otherNeighbor = primitive->getJointOther(i);
|
|
if (otherNeighbor != NULL &&
|
|
(Joint::isKinematicJoint(primitive->getJoint(i))))
|
|
{
|
|
PartInstance* otherPart = PartInstance::fromPrimitive(otherNeighbor);
|
|
if (otherPart != NULL && otherPart != terrain && seen.find(otherPart) == seen.end())
|
|
{
|
|
seen.insert(otherPart);
|
|
objs->push_back(shared_from(otherPart));
|
|
otherPart->getConnectedPartsRecursiveImpl(objs, seen);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::gatherResetOwnershipDataPreUnanchor(std::vector<Primitive*>& previousOwnershipRoots)
|
|
{
|
|
Primitive* partPrim = getPartPrimitive();
|
|
for (int i = 0; i < partPrim->getNumJoints(); ++i)
|
|
{
|
|
Primitive* otherNeighbor = partPrim->getJointOther(i);
|
|
if (otherNeighbor)
|
|
{
|
|
if (Joint::isRigidJoint(partPrim->getJoint(i)))
|
|
{
|
|
// Kinematic Joint neighbors will be
|
|
if (Assembly* assembly = otherNeighbor->getAssembly())
|
|
{
|
|
for (int i = 0; i < assembly->numChildren(); i++)
|
|
{
|
|
if (Primitive* rootChildAssemblyPrim = assembly->getTypedChild<Assembly>(i)->getAssemblyPrimitive()->getRootMovingPrimitive())
|
|
{
|
|
previousOwnershipRoots.push_back(rootChildAssemblyPrim);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!otherNeighbor->computeIsGrounded())
|
|
{
|
|
// Free Motion joints will be their own assembly, make sure to Add their Root.
|
|
if (Primitive* rootMovingPrim = otherNeighbor->getRootMovingPrimitive())
|
|
previousOwnershipRoots.push_back(rootMovingPrim);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::updateOwnershipSettingsPostUnAnchor(std::vector<Primitive*>& previousOwnershipRoots)
|
|
{
|
|
RBX::SystemAddress consistentAddress;
|
|
bool hasConsistentOwner;
|
|
bool hasConsistentManualOwnershipRule;
|
|
PartInstance::checkConsistentOwnerAndRuleResetRoots(consistentAddress, hasConsistentOwner, hasConsistentManualOwnershipRule, previousOwnershipRoots);
|
|
Primitive* currentRootMovingPrim = getPartPrimitive()->getRootMovingPrimitive();
|
|
if (hasConsistentOwner)
|
|
{
|
|
for (unsigned int i = 0; i < previousOwnershipRoots.size(); i++)
|
|
{
|
|
Primitive* rootPrim = previousOwnershipRoots[i]->getRootMovingPrimitive();
|
|
if (previousOwnershipRoots[i] == rootPrim)
|
|
{
|
|
PartInstance::fromPrimitive(rootPrim)->setNetworkOwnerNotifyIfServer(consistentAddress, hasConsistentManualOwnershipRule);
|
|
}
|
|
}
|
|
|
|
if (currentRootMovingPrim)
|
|
PartInstance::fromPrimitive(currentRootMovingPrim)->setNetworkOwnerNotifyIfServer(consistentAddress, hasConsistentManualOwnershipRule);
|
|
}
|
|
|
|
}
|
|
|
|
void PartInstance::gatherResetOwnershipDataPreAnchor(std::vector<Primitive*>& futureMechRoots, RootPrimitiveOwnershipData& ownershipData)
|
|
{
|
|
Primitive* partPrim = getPartPrimitive();
|
|
if (Primitive* rootMovingPrimitive = partPrim->getRootMovingPrimitive())
|
|
{
|
|
ownershipData.ownershipManual = rootMovingPrimitive->getNetworkOwnershipRuleInternal() == NetworkOwnership_Manual;
|
|
ownershipData.ownerAddress = rootMovingPrimitive->getNetworkOwner();
|
|
if (DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
// The anchoring Body is not the current root. Which means after
|
|
// anchoring the parent Primitive and the Child primitives may become roots.
|
|
// Collect all the Children
|
|
Assembly* assembly = partPrim->getAssembly();
|
|
for (int i = 0; i < assembly->numChildren(); i++)
|
|
{
|
|
if (Assembly* childAssembly = assembly->getTypedChild<Assembly>(i))
|
|
{
|
|
Primitive* assemblyRoot = childAssembly->getAssemblyPrimitive();
|
|
if (!assemblyRoot->computeIsGrounded())
|
|
futureMechRoots.push_back(assemblyRoot);
|
|
}
|
|
}
|
|
if (Assembly* parentAssembly = assembly->getTypedParent<Assembly>())
|
|
{
|
|
Primitive* assemblyRoot = parentAssembly->getAssemblyPrimitive();
|
|
if (!assemblyRoot->computeIsGrounded())
|
|
futureMechRoots.push_back(assemblyRoot);
|
|
}
|
|
}
|
|
else if (DFFlag::SetNetworkOwnerFixAnchoring)
|
|
{
|
|
for (int i = 0; i < partPrim->numChildren(); i++)
|
|
{
|
|
if (Primitive* primChild = partPrim->getTypedChild<Primitive>(i))
|
|
{
|
|
if (!primChild->getAnchoredProperty())
|
|
futureMechRoots.push_back(primChild);
|
|
}
|
|
}
|
|
// Add the parent
|
|
if (Primitive* primParent = partPrim->getTypedParent<Primitive>())
|
|
{
|
|
if (!primParent->getAnchoredProperty())
|
|
futureMechRoots.push_back(primParent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Assembly* rootAssembly = rootMovingPrimitive->getAssembly();
|
|
for (int i = 0; i < rootAssembly->numChildren(); i++)
|
|
{
|
|
if (Assembly* childAssembly = rootAssembly->getTypedChild<Assembly>(i))
|
|
{
|
|
futureMechRoots.push_back(childAssembly->getAssemblyPrimitive());
|
|
}
|
|
}
|
|
}
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
ownershipData.prim = rootMovingPrimitive;
|
|
else
|
|
PartInstance::fromPrimitive(rootMovingPrimitive)->setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
}
|
|
else
|
|
{
|
|
ownershipData.prim = NULL;
|
|
}
|
|
}
|
|
|
|
void PartInstance::updateOwnershipSettingsPostAnchor(std::vector<Primitive*>& futureMechRoots, RootPrimitiveOwnershipData& ownershipData)
|
|
{
|
|
if (DFFlag::NetworkOwnershipRuleReplicates && ownershipData.prim)
|
|
{
|
|
// Reset ownership of old Root afte Anchor
|
|
PartInstance::fromPrimitive(ownershipData.prim)->setNetworkOwnershipRuleIfServer(NetworkOwnership_Auto);
|
|
}
|
|
for (size_t i = 0; i < futureMechRoots.size(); i++)
|
|
{
|
|
if (DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
PartInstance* futureMechRootPart = NULL;
|
|
if (Primitive* rootMovingPrim = futureMechRoots[i]->getRootMovingPrimitive())
|
|
futureMechRootPart = PartInstance::fromPrimitive(rootMovingPrim);
|
|
|
|
if (futureMechRootPart)
|
|
{
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
{
|
|
futureMechRootPart->setNetworkOwnerNotifyIfServer(ownershipData.ownerAddress, ownershipData.ownershipManual);
|
|
}
|
|
else
|
|
{
|
|
futureMechRootPart->setNetworkOwner(ownershipData.ownerAddress);
|
|
if ((RBX::Network::Players::serverIsPresent(this) || DFFlag::NetworkOwnershipRuleReplicates)
|
|
&& ownershipData.ownershipManual)
|
|
{
|
|
futureMechRootPart->setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RBXASSERT(Mechanism::isMovingAssemblyRoot(futureMechRoots[i]->getAssembly()));
|
|
PartInstance* futureMechRootPart = PartInstance::fromPrimitive(futureMechRoots[i]);
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
{
|
|
futureMechRootPart->setNetworkOwnerNotifyIfServer(ownershipData.ownerAddress, ownershipData.ownershipManual);
|
|
}
|
|
else
|
|
{
|
|
futureMechRootPart->setNetworkOwner(ownershipData.ownerAddress);
|
|
if ((RBX::Network::Players::serverIsPresent(this) || DFFlag::NetworkOwnershipRuleReplicates)
|
|
&& ownershipData.ownershipManual)
|
|
{
|
|
futureMechRootPart->setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::updateResetOwnershipSettingsOnUnAnchor()
|
|
{
|
|
std::vector<Primitive*> ownershipRoots;
|
|
if (Assembly* currentAssembly = getPartPrimitive()->getAssembly())
|
|
{
|
|
if (!currentAssembly->computeIsGrounded())
|
|
{
|
|
if (DFFlag::SetNetworkOwnerFixAnchoring)
|
|
{
|
|
for (int i = 0; i < getPartPrimitive()->numChildren(); i++)
|
|
{
|
|
if (Primitive* unanchorPrimChild = getPartPrimitive()->getTypedChild<Primitive>(i))
|
|
{
|
|
if (!unanchorPrimChild->getAnchoredProperty())
|
|
ownershipRoots.push_back(unanchorPrimChild);
|
|
}
|
|
}
|
|
if (Primitive* unanchorParentPrim = getPartPrimitive()->getTypedParent<Primitive>())
|
|
{
|
|
if (!unanchorParentPrim->getAnchoredProperty())
|
|
ownershipRoots.push_back(unanchorParentPrim);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < currentAssembly->numChildren(); i++)
|
|
{
|
|
if (Primitive* rootChildAssemblyPrim = currentAssembly->getTypedChild<Assembly>(i)->getAssemblyPrimitive())
|
|
{
|
|
ownershipRoots.push_back(rootChildAssemblyPrim);
|
|
}
|
|
}
|
|
}
|
|
// Process for consistency
|
|
RBX::SystemAddress addressToPass;
|
|
bool hasConsistentAddress;
|
|
bool hasConsistentOwnership;
|
|
PartInstance::checkConsistentOwnerAndRuleResetRoots(addressToPass, hasConsistentAddress, hasConsistentOwnership, ownershipRoots);
|
|
|
|
if (hasConsistentAddress)
|
|
{
|
|
Primitive* rootMovingPrim = getPartPrimitive()->getRootMovingPrimitive();
|
|
PartInstance* rootMovingPrimPart = PartInstance::fromPrimitive(rootMovingPrim);
|
|
if (rootMovingPrim && !PartInstance::isPlayerCharacterPart(rootMovingPrimPart))
|
|
{
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
{
|
|
rootMovingPrimPart->setNetworkOwnerNotifyIfServer(addressToPass, hasConsistentOwnership);
|
|
}
|
|
else
|
|
{
|
|
rootMovingPrimPart->setNetworkOwner(addressToPass);
|
|
if ((RBX::Network::Players::serverIsPresent(this) || DFFlag::NetworkOwnershipRuleReplicates)
|
|
&& hasConsistentOwnership)
|
|
{
|
|
rootMovingPrimPart->setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
shared_ptr<const Instances> PartInstance::getConnectedParts(bool recursive)
|
|
{
|
|
const Workspace* ws = Workspace::findConstWorkspace(this);
|
|
const Instance* terrain = ws != NULL ? ws->getTerrain() : NULL;
|
|
shared_ptr<Instances> result(new Instances());
|
|
if (!recursive)
|
|
{
|
|
result->push_back(shared_from(this));
|
|
for (int i = 0; i < primitive->getNumJoints(); ++i)
|
|
{
|
|
Primitive* otherNeighbor = primitive->getJointOther(i);
|
|
if (otherNeighbor != NULL && PartInstance::fromPrimitive(otherNeighbor) != NULL &&
|
|
PartInstance::fromPrimitive(otherNeighbor) != terrain &&
|
|
(Joint::isKinematicJoint(primitive->getJoint(i))))
|
|
{
|
|
result->push_back(shared_from(PartInstance::fromPrimitive(otherNeighbor)));
|
|
}
|
|
}
|
|
} else { // recursive
|
|
if (primitive != NULL && primitive->getAssembly() != NULL)
|
|
{
|
|
if (!primitive->getAssembly()->computeIsGrounded())
|
|
{
|
|
primitive->getAssembly()->visitPrimitives(boost::bind(&getConnectedPartsVisitor, _1, result));
|
|
} else {
|
|
boost::unordered_set<PartInstance*> seen = boost::unordered_set<PartInstance*>();
|
|
// add ourself to the list of parts
|
|
result->push_back(shared_from(this));
|
|
seen.insert(this);
|
|
getConnectedPartsRecursiveImpl(result, seen);
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
shared_ptr<Instance> PartInstance::getRootPart()
|
|
{
|
|
PartInstance* part = NULL;
|
|
if (primitive != NULL && primitive->getAssembly() != NULL)
|
|
{
|
|
part = PartInstance::fromPrimitive(primitive->getAssembly()->getAssemblyPrimitive());
|
|
}
|
|
return shared_from(part);
|
|
}
|
|
|
|
void PartInstance::setPartLocked(bool value)
|
|
{
|
|
if (value != locked)
|
|
{
|
|
locked = value;
|
|
raisePropertyChanged(prop_Locked);
|
|
}
|
|
}
|
|
|
|
bool PartInstance::getLocked(Instance* instance)
|
|
{
|
|
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance)) {
|
|
return part->getPartLocked();
|
|
}
|
|
else {
|
|
for (size_t i = 0; i < instance->numChildren(); i++) {
|
|
if (getLocked(instance->getChild(i))) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void PartInstance::setLocked(Instance* instance, bool value)
|
|
{
|
|
if (PartInstance* part = Instance::fastDynamicCast<PartInstance>(instance)) {
|
|
part->setPartLocked(value);
|
|
}
|
|
else {
|
|
for (size_t i = 0; i < instance->numChildren(); i++) {
|
|
setLocked(instance->getChild(i), value);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::printNetAPIDisabledMessage()
|
|
{
|
|
StandardOut::singleton()->printf(MESSAGE_WARNING, "SetNetworkOwnership API Disabled");
|
|
}
|
|
|
|
PartMaterial PartInstance::getRenderMaterial() const
|
|
{
|
|
return getConstPartPrimitive()->getPartMaterial();
|
|
}
|
|
|
|
void PartInstance::setRenderMaterial(PartMaterial value)
|
|
{
|
|
if(value != getPartPrimitive()->getPartMaterial())
|
|
{
|
|
getPartPrimitive()->setSpecificGravity(toSpecificGravity(value));
|
|
// Has to be in this order for the correct SpecificGravity to be used
|
|
getPartPrimitive()->setPartMaterial(value);
|
|
raisePropertyChanged(prop_Density);
|
|
raisePropertyChanged(prop_renderMaterial);
|
|
}
|
|
}
|
|
|
|
const bool PartInstance::getNetworkOwnershipManualReplicate() const
|
|
{
|
|
return getNetworkOwnershipRule() == NetworkOwnership_Manual;
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnershipManualReplicate(bool value)
|
|
{
|
|
if (value != getNetworkOwnershipManualReplicate())
|
|
{
|
|
if (value)
|
|
{
|
|
setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
}
|
|
else
|
|
{
|
|
setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
}
|
|
}
|
|
}
|
|
|
|
const NetworkOwnership PartInstance::getNetworkOwnershipRule() const
|
|
{
|
|
return getConstPartPrimitive()->getNetworkOwnershipRuleInternal();
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnershipRule(NetworkOwnership value)
|
|
{
|
|
if (getNetworkOwnershipRule() != value)
|
|
{
|
|
getPartPrimitive()->setNetworkOwnershipRuleInternal(value);
|
|
if (DFFlag::NetworkOwnershipRuleReplicates && RBX::Network::Players::serverIsPresent(this, false))
|
|
{
|
|
raisePropertyChanged(prop_networkOwnershipRuleBool);
|
|
//DFFlagNetworkOwnershipRuleReplicates
|
|
//raisePropertyChanged(prop_networkOwnershipRule);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnershipAuto()
|
|
{
|
|
Primitive* rootPrim;
|
|
std::string statusMessage;
|
|
if (canSetNetworkOwnership(rootPrim, statusMessage))
|
|
{
|
|
PartInstance::fromPrimitive(rootPrim)->setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error(statusMessage.c_str());
|
|
}
|
|
}
|
|
|
|
bool PartInstance::getNetworkOwnershipAuto()
|
|
{
|
|
Primitive* rootPrim;
|
|
std::string statusMessage;
|
|
if (canSetNetworkOwnership(rootPrim, statusMessage))
|
|
{
|
|
return (rootPrim->getNetworkOwnershipRuleInternal() == NetworkOwnership_Auto);
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error(statusMessage.c_str());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PartInstance::setNetworkOwnerScript(shared_ptr<Instance> playerInstance)
|
|
{
|
|
Primitive* rootPrim;
|
|
std::string statusMessage;
|
|
if (canSetNetworkOwnership(rootPrim, statusMessage))
|
|
{
|
|
shared_ptr<Network::Player> player = Instance::fastSharedDynamicCast<Network::Player>(playerInstance);
|
|
if (!player && playerInstance)
|
|
{
|
|
throw std::runtime_error("SetNetworkOwner only takes player or 'nil' instance as an argument.");
|
|
}
|
|
PartInstance* rootPart = PartInstance::fromPrimitive(rootPrim);
|
|
if (Network::Players::backendProcessing(this, false) && Network::Players::frontendProcessing(this, false))
|
|
{
|
|
// Do nothing, PLAY SOLO
|
|
return;
|
|
}
|
|
if (DataModel* dm = DataModel::get(this))
|
|
{
|
|
static boost::once_flag flag = BOOST_ONCE_INIT;
|
|
boost::call_once(flag, boost::bind(&RobloxGoogleAnalytics::trackEvent, GA_CATEGORY_GAME, "PartInstance_SetNetworkOwnerScript",
|
|
boost::lexical_cast<std::string>(dm->getPlaceID()).c_str(), 0, false));
|
|
|
|
if (!player)
|
|
{
|
|
boost::call_once(flag, boost::bind(&RobloxGoogleAnalytics::trackEvent, GA_CATEGORY_GAME, "PartInstance_SetNetworkOwnerScript_TOSERVER",
|
|
boost::lexical_cast<std::string>(dm->getPlaceID()).c_str(), 0, false));
|
|
}
|
|
}
|
|
|
|
RBX::SystemAddress ownerAddress;
|
|
if (player)
|
|
{
|
|
ownerAddress = player->getRemoteAddressAsRbxAddress();
|
|
}
|
|
else
|
|
{
|
|
ownerAddress = RBX::Network::NetworkOwner::Server();
|
|
}
|
|
|
|
if (rootPart)
|
|
{
|
|
rootPart->setNetworkOwnershipRule(NetworkOwnership_Manual);
|
|
rootPart->setNetworkOwnerAndNotify(ownerAddress);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error(statusMessage.c_str());
|
|
}
|
|
}
|
|
|
|
|
|
shared_ptr<Instance> PartInstance::getNetworkOwnerScript()
|
|
{
|
|
Primitive* rootPrim;
|
|
std::string statusMessage;
|
|
if (canSetNetworkOwnership(rootPrim, statusMessage))
|
|
{
|
|
if(Network::Players::serverIsPresent(this))
|
|
{
|
|
if (DataModel* dm = DataModel::get(this))
|
|
{
|
|
static boost::once_flag flag = BOOST_ONCE_INIT;
|
|
boost::call_once(flag, boost::bind(&RobloxGoogleAnalytics::trackEvent, GA_CATEGORY_GAME, "PartInstance_GetNetworkOwnerScript",
|
|
boost::lexical_cast<std::string>(dm->getPlaceID()).c_str(), 0, false));
|
|
}
|
|
|
|
RBX::SystemAddress networkOwner = rootPrim->getNetworkOwner();
|
|
shared_ptr<Instance> player = Network::Players::findPlayerWithAddress(networkOwner, this);
|
|
return player;
|
|
|
|
}
|
|
else if (Network::Players::backendProcessing(this, false) && Network::Players::frontendProcessing(this, false))
|
|
{
|
|
// we're in PlaySolo. Do nothing.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw std::runtime_error(statusMessage.c_str());
|
|
}
|
|
return shared_ptr<Instance>();
|
|
}
|
|
|
|
bool PartInstance::canSetNetworkOwnership(Primitive*& rootPrimitive, std::string& statusMessage)
|
|
{
|
|
rootPrimitive = NULL;
|
|
statusMessage.clear();
|
|
|
|
if(!Workspace::getWorkspaceIfInWorkspace(this))
|
|
{
|
|
statusMessage = std::string("Can only call Network Ownership API on a part that is descendent of Workspace");
|
|
return false;
|
|
}
|
|
|
|
if(Network::Players::clientIsPresent(this))
|
|
{
|
|
statusMessage = std::string("Network Ownership API can only be called from the Server.");
|
|
return false;
|
|
}
|
|
|
|
if (getPartPrimitive()->getGeometry()->isTerrain())
|
|
{
|
|
statusMessage = std::string("Network Ownership API cannot be used on Terrain");
|
|
return false;
|
|
}
|
|
|
|
rootPrimitive = getPartPrimitive()->getRootMovingPrimitive();
|
|
if (!rootPrimitive)
|
|
{
|
|
statusMessage = std::string("Network Ownership API cannot be called on Anchored parts or parts welded to Anchored parts.");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
shared_ptr<const Reflection::Tuple> PartInstance::canSetNetworkOwnershipScript()
|
|
{
|
|
shared_ptr<Reflection::Tuple> args = rbx::make_shared<Reflection::Tuple>();
|
|
|
|
Primitive* placeholderPointer;
|
|
std::string statusMessage;
|
|
bool canSet = canSetNetworkOwnership(placeholderPointer, statusMessage);
|
|
args->values.push_back(canSet);
|
|
if (!canSet)
|
|
{
|
|
args->values.push_back(statusMessage);
|
|
}
|
|
|
|
|
|
return args;
|
|
}
|
|
|
|
bool PartInstance::isPlayerCharacterPart(PartInstance* part)
|
|
{
|
|
// This is how we check during NetworkOwnerJob
|
|
if (part)
|
|
{
|
|
if(ModelInstance* m = Instance::fastDynamicCast<ModelInstance>(part->getParent()))
|
|
{
|
|
if (Humanoid::modelIsCharacter(m) && Network::Players::getPlayerFromCharacter(m))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PartInstance::checkConsistentOwnerAndRuleResetRoots(RBX::SystemAddress& consistentAddress, bool& hasConsistentOwner, bool& hasConsistentManualOwnershipRule, std::vector<Primitive*>& primRoots)
|
|
{
|
|
bool ownerInitialized = false;
|
|
consistentAddress = Network::NetworkOwner::Unassigned();
|
|
hasConsistentOwner = true;
|
|
hasConsistentManualOwnershipRule = true;
|
|
for (size_t i = 0; i < primRoots.size(); i++)
|
|
{
|
|
PartInstance* primRootPart = PartInstance::fromPrimitive(primRoots[i]);
|
|
if (PartInstance::isPlayerCharacterPart(primRootPart))
|
|
{
|
|
// We discard Players from this process, since they should not be affected
|
|
// We still want to set the ownership of the PlayerCharacter to auto
|
|
primRootPart->setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
continue;
|
|
}
|
|
if (!ownerInitialized)
|
|
{
|
|
consistentAddress = primRoots[i]->getNetworkOwner();
|
|
ownerInitialized = true;
|
|
}
|
|
if (primRoots[i]->getNetworkOwner() != consistentAddress)
|
|
{
|
|
hasConsistentOwner = false;
|
|
}
|
|
if (primRoots[i]->getNetworkOwnershipRuleInternal() != NetworkOwnership_Manual)
|
|
{
|
|
hasConsistentManualOwnershipRule = false;
|
|
}
|
|
|
|
if (!DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
primRootPart->setNetworkOwnershipRuleIfServer(NetworkOwnership_Auto);
|
|
else
|
|
primRootPart->setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
}
|
|
}
|
|
|
|
if (DFFlag::SetNetworkOwnerFixAnchoring2)
|
|
{
|
|
// NetworkOwnershipRule should only ever be set to Auto from the server
|
|
// Allowing the Client to decide ownership is Auto can lead to weird race conditions
|
|
// Imagine you have a configuration like this:
|
|
// Where A and B are joined by a Hinge, B and C are also joined by a hinge
|
|
// [A]-[B]-[C] Our replication system Queues properties in the order the first change happened.
|
|
// This causes the server to send the final state of this assembly to the Client
|
|
// but because it arrives first, when the anchor event arrives, the ownership is reset to Auto.
|
|
|
|
// If primRoots is a redundant set of objects, we want to setAuto
|
|
// after we figure out consistency in ownership, so that we don't alter the result.
|
|
for (size_t i = 0; i < primRoots.size(); i++)
|
|
{
|
|
if (DFFlag::NetworkOwnershipRuleReplicates)
|
|
PartInstance::fromPrimitive(primRoots[i])->setNetworkOwnershipRuleIfServer(NetworkOwnership_Auto);
|
|
else
|
|
PartInstance::fromPrimitive(primRoots[i])->setNetworkOwnershipRule(NetworkOwnership_Auto);
|
|
}
|
|
}
|
|
|
|
if (!ownerInitialized)
|
|
{
|
|
// If the Network Address was never assigned, that means we were
|
|
// creating joints in a Player character, so we need to not run logic.
|
|
hasConsistentOwner = false;
|
|
hasConsistentManualOwnershipRule = false;
|
|
}
|
|
}
|
|
|
|
void PartInstance::setTransparency(float value)
|
|
{
|
|
if (value != transparency) {
|
|
transparency = value;
|
|
raisePropertyChanged(prop_Transparency);
|
|
shouldRenderSetDirty();
|
|
}
|
|
}
|
|
|
|
void PartInstance::setLocalTransparencyModifier(float value)
|
|
{
|
|
if (value != localTransparencyModifier) {
|
|
localTransparencyModifier = value;
|
|
raisePropertyChanged(prop_LocalTransparencyModifier);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setReflectance(float value)
|
|
{
|
|
if (value != reflectance)
|
|
{
|
|
reflectance = value;
|
|
raisePropertyChanged(prop_Reflectance);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setColor(BrickColor value)
|
|
{
|
|
if (value != color)
|
|
{
|
|
color = value;
|
|
raisePropertyChanged(prop_BrickColor);
|
|
raisePropertyChanged(prop_Color);
|
|
}
|
|
}
|
|
|
|
void PartInstance::setFriction(float friction)
|
|
{
|
|
float newVal = G3D::clamp(friction, 0.0f, 2.0f);
|
|
if (newVal != getPartPrimitive()->getFriction()) {
|
|
getPartPrimitive()->setFriction(newVal);
|
|
raisePropertyChanged(prop_Friction);
|
|
}
|
|
}
|
|
|
|
float PartInstance::getFriction() const
|
|
{
|
|
return getConstPartPrimitive()->getFriction();
|
|
}
|
|
|
|
void PartInstance::setPhysicalProperties(const PhysicalProperties& prop)
|
|
{
|
|
if (prop != getPartPrimitive()->getPhysicalProperties())
|
|
{
|
|
getPartPrimitive()->setPhysicalProperties(prop);
|
|
raisePropertyChanged(prop_CustomPhysicalProperties);
|
|
}
|
|
}
|
|
|
|
const PhysicalProperties& PartInstance::getPhysicalProperties() const
|
|
{
|
|
return getConstPartPrimitive()->getPhysicalProperties();
|
|
}
|
|
|
|
void PartInstance::setElasticity(float elasticity)
|
|
{
|
|
float newVal = G3D::clamp(elasticity, 0.0f, 1.0f);
|
|
if (newVal != getPartPrimitive()->getElasticity()) {
|
|
getPartPrimitive()->setElasticity(newVal);
|
|
raisePropertyChanged(prop_Elasticity);
|
|
}
|
|
}
|
|
|
|
float PartInstance::getElasticity() const
|
|
{
|
|
return getConstPartPrimitive()->getElasticity();
|
|
}
|
|
|
|
float PartInstance::getSpecificGravity() const
|
|
{
|
|
return getConstPartPrimitive()->getSpecificGravity();
|
|
}
|
|
|
|
bool PartInstance::resize(NormalId localNormalId, int amount)
|
|
{
|
|
destroyJoints();
|
|
bool result = resizeImpl(localNormalId, amount);
|
|
join();
|
|
return result;
|
|
}
|
|
|
|
bool PartInstance::resizeFloat(NormalId localNormalId, float amount, bool checkIntersection)
|
|
{
|
|
destroyJoints();
|
|
bool result = advResizeImpl(localNormalId, amount, checkIntersection);
|
|
join();
|
|
return result;
|
|
}
|
|
|
|
bool PartInstance::resizeImpl(NormalId localNormalId, int amount)
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
if (amount == 0) {
|
|
return false;
|
|
}
|
|
else {
|
|
Vector3 originalSizeXml = getPartSizeXml();
|
|
CoordinateFrame originalPosition = getCoordinateFrame();
|
|
|
|
Vector3 deltaSizeUi;
|
|
float posNeg = (localNormalId < NORM_X_NEG) ? 1.0f : -1.0f;
|
|
|
|
if (hasThreeDimensionalSize()) {
|
|
deltaSizeUi[localNormalId % 3] = (float)amount;
|
|
}
|
|
else {
|
|
if (!DFFlag::FormFactorDeprecated)
|
|
{
|
|
RBXASSERT(getFormFactor() == PartInstance::SYMETRIC);
|
|
}
|
|
deltaSizeUi = amount * Vector3(1,1,1);
|
|
}
|
|
|
|
Vector3 newSizeUi = xmlToUiSize(originalSizeXml) + deltaSizeUi;
|
|
Vector3 newSizeXml = uiToXmlSize(newSizeUi); // will snap on setting uiSize;
|
|
Vector3 deltaSizeXml = newSizeXml - originalSizeXml;
|
|
Vector3 deltaSizeWorld = originalPosition.rotation * deltaSizeXml;
|
|
Vector3 newTranslation = originalPosition.translation + (0.5f * deltaSizeWorld * posNeg);
|
|
CoordinateFrame newCoord(originalPosition.rotation, newTranslation);
|
|
|
|
setCoordinateFrame(newCoord);
|
|
setPartSizeXml(newSizeXml);
|
|
|
|
if (ContactManager* contactManager = Workspace::getContactManagerIfInWorkspace(this)) {
|
|
if (Dragger::intersectingWorldOrOthers( *this,
|
|
*contactManager,
|
|
Tolerance::maxOverlapAllowedForResize(),
|
|
Dragger::maxDragDepth())) {
|
|
|
|
setCoordinateFrame(originalPosition);
|
|
setPartSizeXml(originalSizeXml);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
bool PartInstance::advResizeImpl(NormalId localNormalId, float amount, bool checkIntersection)
|
|
{
|
|
RBXASSERT_VERY_FAST(!getPartPrimitive()->hasAutoJoints());
|
|
|
|
// Blow out if the requested change is less than the min resize increment.
|
|
if ( fabs(amount) < getMinimumResizeIncrement() ) {
|
|
return false;
|
|
}
|
|
else {
|
|
// Set the form factor to CUSTOM if resizing a part off the 1 stud grid (or 1.2 or 0.4 stud grid in the y-direction)
|
|
if(!DFFlag::FormFactorDeprecated && getFormFactor() != PartInstance::CUSTOM)
|
|
{
|
|
float fractPart, intPart, resizeIncrement;
|
|
switch(getFormFactor())
|
|
{
|
|
case PartInstance::BRICK:
|
|
resizeIncrement = (localNormalId == NORM_Y || localNormalId == NORM_Y_NEG) ? 1.2f : 1.0f;
|
|
break;
|
|
case PartInstance::PLATE:
|
|
resizeIncrement = (localNormalId == NORM_Y || localNormalId == NORM_Y_NEG) ? 0.4f : 1.0f;
|
|
break;
|
|
case PartInstance::SYMETRIC:
|
|
default:
|
|
resizeIncrement = 1.0f;
|
|
break;
|
|
}
|
|
|
|
fractPart = modff (fabs(amount)/resizeIncrement, &intPart);
|
|
if(fractPart > 0.0001f)
|
|
{
|
|
BasicPartInstance* aBPI = this->fastDynamicCast<BasicPartInstance>();
|
|
if(aBPI)
|
|
{
|
|
//aBPI->setFormFactorUi(PartInstance::CUSTOM);
|
|
}
|
|
}
|
|
}
|
|
|
|
Vector3 originalSizeXml = getPartSizeXml();
|
|
CoordinateFrame originalPosition = getCoordinateFrame();
|
|
|
|
Vector3 deltaSizeUi;
|
|
float posNeg = (localNormalId < NORM_X_NEG) ? 1.0f : -1.0f;
|
|
|
|
if (hasThreeDimensionalSize()) {
|
|
deltaSizeUi[localNormalId % 3] = (float)amount;
|
|
}
|
|
else {
|
|
if (!DFFlag::FormFactorDeprecated)
|
|
{
|
|
RBXASSERT(getFormFactor() == PartInstance::SYMETRIC);
|
|
}
|
|
deltaSizeUi = amount * Vector3(1,1,1);
|
|
}
|
|
|
|
Vector3 newSizeUi = originalSizeXml + deltaSizeUi;
|
|
Vector3 newSizeXml;
|
|
newSizeXml.x = newSizeUi.x < getMinimumXOrZDimension() ? getMinimumXOrZDimension() : newSizeUi.x;
|
|
newSizeXml.y = newSizeUi.y < getMinimumYDimension() ? getMinimumYDimension() : newSizeUi.y;
|
|
newSizeXml.z = newSizeUi.z < getMinimumXOrZDimension() ? getMinimumXOrZDimension() : newSizeUi.z;
|
|
Vector3 deltaSizeXml = newSizeXml - originalSizeXml;
|
|
Vector3 deltaSizeWorld = originalPosition.rotation * deltaSizeXml;
|
|
Vector3 newTranslation = originalPosition.translation + (0.5f * deltaSizeWorld * posNeg);
|
|
CoordinateFrame newCoord(originalPosition.rotation, newTranslation);
|
|
|
|
setCoordinateFrame(newCoord);
|
|
setPartSizeXml(newSizeXml);
|
|
|
|
if (checkIntersection)
|
|
{
|
|
if (ContactManager* contactManager = Workspace::getContactManagerIfInWorkspace(this)) {
|
|
if (Dragger::intersectingWorldOrOthers( *this,
|
|
*contactManager,
|
|
Tolerance::maxOverlapAllowedForResize(),
|
|
Dragger::maxDragDepth())) {
|
|
|
|
setCoordinateFrame(originalPosition);
|
|
setPartSizeXml(originalSizeXml);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
float PartInstance::getMinimumYDimension(void) const
|
|
{
|
|
if (DFFlag::FormFactorDeprecated) {
|
|
return 0.2f;
|
|
} else {
|
|
switch (getFormFactor())
|
|
{
|
|
case BRICK:
|
|
return 1.2f;
|
|
case PLATE:
|
|
return 0.4f;
|
|
case CUSTOM:
|
|
return 0.2f;
|
|
default:
|
|
return 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
float PartInstance::getMinimumXOrZDimension(void) const
|
|
{
|
|
if (DFFlag::FormFactorDeprecated) {
|
|
return 0.2f;
|
|
} else {
|
|
switch (getFormFactor())
|
|
{
|
|
case CUSTOM:
|
|
return 0.2f;
|
|
default:
|
|
return 1.0f;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// This is in Grid Location units - parts have their corner at 0,0,0
|
|
Surface PartInstance::getSurface(const RBX::RbxRay& gridRay, int& surfaceId)
|
|
{
|
|
const CoordinateFrame& currentCoord = getCoordinateFrame();
|
|
|
|
RBX::RbxRay localRay = currentCoord.toObjectSpace(gridRay);
|
|
|
|
Extents extents = getPartPrimitive()->getExtentsLocal();
|
|
Vector3 hitPoint;
|
|
|
|
G3D::CollisionDetection::collisionLocationForMovingPointFixedAABox(
|
|
localRay.origin(),
|
|
localRay.direction(),
|
|
G3D::AABox(extents.min(), extents.max()),
|
|
hitPoint);
|
|
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (hitPoint[i] == extents.max()[i]) {
|
|
surfaceId = i;
|
|
return Surface(this, (NormalId)surfaceId);
|
|
}
|
|
if (hitPoint[i] == extents.min()[i]) {
|
|
surfaceId = 3+i;
|
|
return Surface(this, (NormalId)surfaceId);
|
|
}
|
|
}
|
|
RBXASSERT_IF_VALIDATING(0);
|
|
return Surface(NULL, NORM_X);
|
|
}
|
|
|
|
SurfaceType PartInstance::getSurfaceType(NormalId surfId) const
|
|
{
|
|
return getConstPartPrimitive()->getSurfaceType(surfId);
|
|
}
|
|
|
|
void PartInstance::setSurfaceType(NormalId surfId, SurfaceType type)
|
|
{
|
|
if (type != getPartPrimitive()->getSurfaceType(surfId))
|
|
{
|
|
getPartPrimitive()->setSurfaceType(surfId, type);
|
|
onSurfaceChanged(surfId);
|
|
raiseSurfacePropertyChanged(Surface::getSurfaceTypeStatic(surfId));
|
|
}
|
|
}
|
|
|
|
LegacyController::InputType PartInstance::getInput(NormalId surfId) const
|
|
{
|
|
return getConstPartPrimitive()->getConstSurfaceData(surfId).inputType;
|
|
}
|
|
|
|
void PartInstance::setSurfaceInput(NormalId surfId, LegacyController::InputType inputType)
|
|
{
|
|
RBXASSERT(getPartPrimitive());
|
|
SurfaceData current = getPartPrimitive()->getSurfaceData(surfId);
|
|
if (inputType != current.inputType)
|
|
{
|
|
current.inputType = inputType;
|
|
getPartPrimitive()->setSurfaceData(surfId, current);
|
|
onSurfaceChanged(surfId);
|
|
raiseSurfacePropertyChanged(Surface::getSurfaceInputStatic(surfId));
|
|
}
|
|
}
|
|
|
|
float PartInstance::getParamA(NormalId surfId) const
|
|
{
|
|
RBXASSERT(getConstPartPrimitive());
|
|
return getConstPartPrimitive()->getConstSurfaceData(surfId).paramA;
|
|
}
|
|
|
|
float PartInstance::getParamB(NormalId surfId) const
|
|
{
|
|
RBXASSERT(getConstPartPrimitive());
|
|
return getConstPartPrimitive()->getConstSurfaceData(surfId).paramB;
|
|
}
|
|
|
|
void PartInstance::setParamA(NormalId surfId, float value)
|
|
{
|
|
SurfaceData current = getPartPrimitive()->getSurfaceData(surfId);
|
|
if (current.paramA != value)
|
|
{
|
|
current.paramA = value;
|
|
getPartPrimitive()->setSurfaceData(surfId, current);
|
|
onSurfaceChanged(surfId);
|
|
raiseSurfacePropertyChanged(Surface::getParamAStatic(surfId));
|
|
}
|
|
}
|
|
|
|
void PartInstance::setParamB(NormalId surfId, float value)
|
|
{
|
|
SurfaceData current = getPartPrimitive()->getSurfaceData(surfId);
|
|
if (current.paramB != value)
|
|
{
|
|
current.paramB = value;
|
|
getPartPrimitive()->setSurfaceData(surfId, current);
|
|
onSurfaceChanged(surfId);
|
|
raiseSurfacePropertyChanged(Surface::getParamBStatic(surfId));
|
|
}
|
|
}
|
|
|
|
bool PartInstance::containedByFrustum(const RBX::Frustum& frustum) const
|
|
{
|
|
return frustum.containsAABB(getConstPartPrimitive()->getExtentsLocal(), getCoordinateFrame());
|
|
}
|
|
|
|
bool PartInstance::intersectFrustum(const RBX::Frustum& frustum) const
|
|
{
|
|
return frustum.intersectsAABB(getConstPartPrimitive()->getExtentsLocal(), getCoordinateFrame());
|
|
}
|
|
|
|
bool PartInstance::isStandardPart() const
|
|
{
|
|
// Inlets on bottom, all sides flat, anything on top
|
|
return ( (getSurfaceType(NORM_Y_NEG) == INLET)
|
|
&& (getSurfaceType(NORM_Z_NEG) == NO_SURFACE)
|
|
&& (getSurfaceType(NORM_Z) == NO_SURFACE)
|
|
&& (getSurfaceType(NORM_X_NEG) == NO_SURFACE)
|
|
&& (getSurfaceType(NORM_X) == NO_SURFACE) );
|
|
}
|
|
|
|
namespace Reflection
|
|
{
|
|
template<>
|
|
const Type& Type::getSingleton<CoordinateFrame>()
|
|
{
|
|
static TType<CoordinateFrame> type("CoordinateFrame");
|
|
return type;
|
|
}
|
|
}
|
|
|
|
static float getComponent(const XmlElement* element, const XmlTag& name)
|
|
{
|
|
float result = 0.0f;
|
|
if(const XmlElement* valueElement = element->findFirstChildByTag(name)){
|
|
bool gotComponent = valueElement->getValue(result);
|
|
RBXASSERT(gotComponent);
|
|
return result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
namespace Reflection {
|
|
template<>
|
|
void RBX::TypedPropertyDescriptor<CoordinateFrame>::readValue(DescribedBase* instance, const XmlElement* element, IReferenceBinder& binder) const
|
|
{
|
|
if (!element->isXsiNil())
|
|
{
|
|
CoordinateFrame c;
|
|
c.translation.x = getComponent(element, tag_X);
|
|
c.translation.y = getComponent(element, tag_Y);
|
|
c.translation.z = getComponent(element, tag_Z);
|
|
c.rotation[0][0] = getComponent(element, tag_R00);
|
|
c.rotation[0][1] = getComponent(element, tag_R01);
|
|
c.rotation[0][2] = getComponent(element, tag_R02);
|
|
c.rotation[1][0] = getComponent(element, tag_R10);
|
|
c.rotation[1][1] = getComponent(element, tag_R11);
|
|
c.rotation[1][2] = getComponent(element, tag_R12);
|
|
c.rotation[2][0] = getComponent(element, tag_R20);
|
|
c.rotation[2][1] = getComponent(element, tag_R21);
|
|
c.rotation[2][2] = getComponent(element, tag_R22);
|
|
setValue(instance, c);
|
|
}
|
|
}
|
|
|
|
template<>
|
|
void RBX::TypedPropertyDescriptor<CoordinateFrame>::writeValue(const DescribedBase* instance, XmlElement* element) const
|
|
{
|
|
// Write the data out in accordance with ROBLOX Schema
|
|
G3D::CoordinateFrame c = getValue(instance);
|
|
element->addChild(new XmlElement(tag_X, c.translation.x));
|
|
element->addChild(new XmlElement(tag_Y, c.translation.y));
|
|
element->addChild(new XmlElement(tag_Z, c.translation.z));
|
|
element->addChild(new XmlElement(tag_R00, c.rotation[0][0]));
|
|
element->addChild(new XmlElement(tag_R01, c.rotation[0][1]));
|
|
element->addChild(new XmlElement(tag_R02, c.rotation[0][2]));
|
|
element->addChild(new XmlElement(tag_R10, c.rotation[1][0]));
|
|
element->addChild(new XmlElement(tag_R11, c.rotation[1][1]));
|
|
element->addChild(new XmlElement(tag_R12, c.rotation[1][2]));
|
|
element->addChild(new XmlElement(tag_R20, c.rotation[2][0]));
|
|
element->addChild(new XmlElement(tag_R21, c.rotation[2][1]));
|
|
element->addChild(new XmlElement(tag_R22, c.rotation[2][2]));
|
|
}
|
|
|
|
template<>
|
|
int TypedPropertyDescriptor<CoordinateFrame>::getDataSize(const DescribedBase* instance) const
|
|
{
|
|
return sizeof(CoordinateFrame);
|
|
}
|
|
|
|
template<>
|
|
bool RBX::TypedPropertyDescriptor<CoordinateFrame>::hasStringValue() const {
|
|
return false;
|
|
}
|
|
|
|
template<>
|
|
std::string RBX::TypedPropertyDescriptor<CoordinateFrame>::getStringValue(const DescribedBase* instance) const{
|
|
return Super::getStringValue(instance);
|
|
}
|
|
|
|
template<>
|
|
bool RBX::TypedPropertyDescriptor<CoordinateFrame>::setStringValue(DescribedBase* instance, const std::string& text) const {
|
|
return Super::setStringValue(instance, text);
|
|
}
|
|
}//namespace Reflection
|
|
|
|
void PartInstance::fireOutfitChanged()
|
|
{
|
|
OutfitChangedSignalData data;
|
|
combinedSignal(OUTFIT_CHANGED, &data);
|
|
if(onDemandRead())
|
|
onDemandWrite()->outfitChangedSignal();
|
|
}
|
|
|
|
void PartInstance::reportTouch(const shared_ptr<PartInstance>& other)
|
|
{
|
|
if (hasTouchTransmitter() && onDemandWrite()->touchTransmitter->checkTouch(other))
|
|
onDemandWrite()->touchedSignal(other);
|
|
}
|
|
void PartInstance::reportUntouch(const shared_ptr<PartInstance>& other)
|
|
{
|
|
if (hasTouchTransmitter() && onDemandWrite()->touchTransmitter->checkUntouch(other))
|
|
onDemandWrite()->touchEndedSignal(other);
|
|
}
|
|
|
|
void PartInstance::setIsCurrentlyStreamRemovingPart() {
|
|
onDemandWrite()->isCurrentlyStreamRemovingPart = true;
|
|
}
|
|
|
|
bool PartInstance::getIsCurrentlyStreamRemovingPart() const {
|
|
return onDemandRead() && onDemandRead()->isCurrentlyStreamRemovingPart;
|
|
}
|
|
|
|
Humanoid* PartInstance::findAncestorModelWithHumanoid()
|
|
{
|
|
Instance* current = getParent();
|
|
|
|
while (current)
|
|
{
|
|
if (Humanoid* humanoid = Humanoid::modelIsCharacter(current))
|
|
return humanoid;
|
|
|
|
current = current->getParent();
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void PartInstance::updateHumanoidCookie()
|
|
{
|
|
if (DFFlag::HumanoidCookieRecursive)
|
|
{
|
|
if (findAncestorModelWithHumanoid())
|
|
cookie |= PartCookie::IS_HUMANOID_PART;
|
|
else
|
|
cookie &= ~PartCookie::IS_HUMANOID_PART;
|
|
}
|
|
else
|
|
{
|
|
Instance* parent = getParent();
|
|
|
|
// Only treat parts as humanoid parts if they're not directly under workspace
|
|
if (parent && Humanoid::modelIsCharacter(parent) && !Instance::isA<Workspace>(parent))
|
|
{
|
|
cookie |= PartCookie::IS_HUMANOID_PART;
|
|
}
|
|
else
|
|
{
|
|
cookie &= ~PartCookie::IS_HUMANOID_PART;
|
|
}
|
|
}
|
|
}
|
|
|
|
void PartInstance::humanoidChanged()
|
|
{
|
|
updateHumanoidCookie();
|
|
Super::humanoidChanged();
|
|
}
|
|
|
|
float PartInstance::getReceiveInterval() const
|
|
{
|
|
return renderingCoordinateFrame ? renderingCoordinateFrame->getSampleInterval() : 0.f;
|
|
}
|
|
|
|
void PartInstance::createRenderingCoordinateFrame()
|
|
{
|
|
if (!renderingCoordinateFrame)
|
|
{
|
|
renderingCoordinateFrame.reset(new PathInterpolatedCFrame());
|
|
renderingCoordinateFrame->setRenderedFrame(getCoordinateFrame());
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|