diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 9d7e536f25..1e95e999b1 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -125,9 +125,11 @@ EntityPropertyFlags EntityItem::getEntityProperties(EncodeBitstreamParams& param requestedProperties += PROP_LAST_EDITED_BY; requestedProperties += PROP_CLONEABLE; - requestedProperties += PROP_CLONEABLE_LIFETIME; - requestedProperties += PROP_CLONEABLE_LIMIT; - requestedProperties += PROP_CLONEABLE_DYNAMIC; + requestedProperties += PROP_CLONE_LIFETIME; + requestedProperties += PROP_CLONE_LIMIT; + requestedProperties += PROP_CLONE_DYNAMIC; + requestedProperties += PROP_CLONE_AVATAR_ENTITY; + requestedProperties += PROP_CLONE_ORIGIN_ID; return requestedProperties; } @@ -294,9 +296,11 @@ OctreeElement::AppendState EntityItem::appendEntityData(OctreePacketData* packet APPEND_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, getLastEditedBy()); APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, getCloneableLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, getCloneableLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, getCloneableDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, getCloneLifetime()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, getCloneLimit()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, getCloneDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, getCloneAvatarEntity()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, getCloneOriginID()); appendSubclassData(packetData, params, entityTreeElementExtraEncodeData, requestedProperties, @@ -859,9 +863,11 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef READ_ENTITY_PROPERTY(PROP_LAST_EDITED_BY, QUuid, setLastEditedBy); READ_ENTITY_PROPERTY(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, float, setCloneableLimit); - READ_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic); + READ_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, float, setCloneLifetime); + READ_ENTITY_PROPERTY(PROP_CLONE_LIMIT, float, setCloneLimit); + READ_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); + READ_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + READ_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); bytesRead += readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, propertyFlags, overwriteLocalData, somethingChanged); @@ -1291,9 +1297,11 @@ EntityItemProperties EntityItem::getProperties(EntityPropertyFlags desiredProper COPY_ENTITY_PROPERTY_TO_PROPERTIES(lastEditedBy, getLastEditedBy); COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneable, getCloneable); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLifetime, getCloneableLifetime); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableLimit, getCloneableLimit); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneableDynamic, getCloneableDynamic); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLifetime, getCloneLifetime); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneLimit, getCloneLimit); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneDynamic, getCloneDynamic); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneAvatarEntity, getCloneAvatarEntity); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(cloneOriginID, getCloneOriginID); properties._defaultSettings = false; @@ -1403,9 +1411,11 @@ bool EntityItem::setProperties(const EntityItemProperties& properties) { SET_ENTITY_PROPERTY_FROM_PROPERTIES(lastEditedBy, setLastEditedBy); SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneable, setCloneable); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLifetime, setCloneableLifetime); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableLimit, setCloneableLimit); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneableDynamic, setCloneableDynamic); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLifetime, setCloneLifetime); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneLimit, setCloneLimit); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneDynamic, setCloneDynamic); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneAvatarEntity, setCloneAvatarEntity); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(cloneOriginID, setCloneOriginID); if (updateQueryAACube()) { somethingChanged = true; @@ -3015,45 +3025,73 @@ void EntityItem::setCloneable(bool value) { }); } -float EntityItem::getCloneableLifetime() const { +float EntityItem::getCloneLifetime() const { float result; withReadLock([&] { - result = _cloneableLifetime; + result = _cloneLifetime; }); return result; } -void EntityItem::setCloneableLifetime(float value) { +void EntityItem::setCloneLifetime(float value) { withWriteLock([&] { - _cloneableLifetime = value; + _cloneLifetime = value; }); } -float EntityItem::getCloneableLimit() const { +float EntityItem::getCloneLimit() const { float result; withReadLock([&] { - result = _cloneableLimit; + result = _cloneLimit; }); return result; } -void EntityItem::setCloneableLimit(float value) { +void EntityItem::setCloneLimit(float value) { withWriteLock([&] { - _cloneableLimit = value; + _cloneLimit = value; }); } -bool EntityItem::getCloneableDynamic() const { +bool EntityItem::getCloneDynamic() const { bool result; withReadLock([&] { - result = _cloneableDynamic; + result = _cloneDynamic; }); return result; } -void EntityItem::setCloneableDynamic(const bool value) { +void EntityItem::setCloneDynamic(const bool value) { withWriteLock([&] { - _cloneableDynamic = value; + _cloneDynamic = value; + }); +} + +bool EntityItem::getCloneAvatarEntity() const { + bool result; + withReadLock([&] { + result = _cloneAvatarEntity; + }); + return result; +} + +void EntityItem::setCloneAvatarEntity(const bool value) { + withWriteLock([&] { + _cloneAvatarEntity = value; + }); +} + +const QUuid EntityItem::getCloneOriginID() const { + QUuid result; + withReadLock([&] { + result = _cloneOriginID; + }); + return result; +} + +void EntityItem::setCloneOriginID(const QUuid& value) { + withWriteLock([&] { + _cloneOriginID = value; }); } @@ -3072,4 +3110,4 @@ bool EntityItem::removeCloneID(const QUuid& cloneID) { return true; } return false; -} \ No newline at end of file +} diff --git a/libraries/entities/src/EntityItem.h b/libraries/entities/src/EntityItem.h index 95cc5f96e1..f314cd1418 100644 --- a/libraries/entities/src/EntityItem.h +++ b/libraries/entities/src/EntityItem.h @@ -343,12 +343,16 @@ public: bool getCloneable() const; void setCloneable(bool value); - float getCloneableLifetime() const; - void setCloneableLifetime(float value); - float getCloneableLimit() const; - void setCloneableLimit(float value); - bool getCloneableDynamic() const; - void setCloneableDynamic(const bool value); + float getCloneLifetime() const; + void setCloneLifetime(float value); + float getCloneLimit() const; + void setCloneLimit(float value); + bool getCloneDynamic() const; + void setCloneDynamic(const bool value); + bool getCloneAvatarEntity() const; + void setCloneAvatarEntity(const bool value); + const QUuid getCloneOriginID() const; + void setCloneOriginID(const QUuid& value); // TODO: get rid of users of getRadius()... float getRadius() const; @@ -506,8 +510,6 @@ public: bool addCloneID(const QUuid& cloneID); bool removeCloneID(const QUuid& cloneID); const QList& getCloneIDs() const { return _cloneIDs; } - void setCloneParent(const QUuid& cloneParentID) { _cloneParentID = cloneParentID; } - const QUuid& getCloneParent() const { return _cloneParentID; } signals: void requestRenderUpdate(); @@ -664,12 +666,12 @@ protected: bool _cauterized { false }; // if true, don't draw because it would obscure 1st-person camera bool _cloneable; - float _cloneableLifetime; - float _cloneableLimit; - bool _cloneableDynamic; - + float _cloneLifetime; + float _cloneLimit; + bool _cloneDynamic; + bool _cloneAvatarEntity; + QUuid _cloneOriginID; QList _cloneIDs; - QUuid _cloneParentID; private: std::unordered_map _materials; diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 7c799a64e1..11ef5a4a84 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -437,9 +437,11 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_RELAY_PARENT_JOINTS, relayParentJoints); CHECK_PROPERTY_CHANGE(PROP_CLONEABLE, cloneable); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_LIMIT, cloneableLimit); - CHECK_PROPERTY_CHANGE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + CHECK_PROPERTY_CHANGE(PROP_CLONE_LIFETIME, cloneLifetime); + CHECK_PROPERTY_CHANGE(PROP_CLONE_LIMIT, cloneLimit); + CHECK_PROPERTY_CHANGE(PROP_CLONE_DYNAMIC, cloneDynamic); + CHECK_PROPERTY_CHANGE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); + CHECK_PROPERTY_CHANGE(PROP_CLONE_ORIGIN_ID, cloneOriginID); changedProperties += _animation.getChangedProperties(); changedProperties += _keyLight.getChangedProperties(); @@ -1436,9 +1438,11 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_OWNING_AVATAR_ID, owningAvatarID); // Gettable but not settable COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE, cloneable); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIFETIME, cloneableLifetime); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_LIMIT, cloneableLimit); - COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONEABLE_DYNAMIC, cloneableDynamic); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIFETIME, cloneLifetime); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_LIMIT, cloneLimit); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_DYNAMIC, cloneDynamic); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_AVATAR_ENTITY, cloneAvatarEntity); + COPY_PROPERTY_TO_QSCRIPTVALUE(PROP_CLONE_ORIGIN_ID, cloneOriginID); // Rendering info if (!skipDefaults && !strictSemantics) { @@ -1653,9 +1657,11 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE(dpi, uint16_t, setDPI); COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneable, bool, setCloneable); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLifetime, float, setCloneableLifetime); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableLimit, float, setCloneableLimit); - COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneableDynamic, bool, setCloneableDynamic); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLifetime, float, setCloneLifetime); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneLimit, float, setCloneLimit); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneDynamic, bool, setCloneDynamic); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneAvatarEntity, bool, setCloneAvatarEntity); + COPY_PROPERTY_FROM_QSCRIPTVALUE(cloneOriginID, QUuid, setCloneOriginID); _lastEdited = usecTimestampNow(); } @@ -2033,9 +2039,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_PROPERTY_TO_MAP(PROP_DPI, DPI, dpi, uint16_t); ADD_PROPERTY_TO_MAP(PROP_CLONEABLE, Cloneable, cloneable, bool); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float); - ADD_PROPERTY_TO_MAP(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float); + ADD_PROPERTY_TO_MAP(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float); + ADD_PROPERTY_TO_MAP(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool); + ADD_PROPERTY_TO_MAP(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid); // FIXME - these are not yet handled //ADD_PROPERTY_TO_MAP(PROP_CREATED, Created, created, quint64); @@ -2353,9 +2361,11 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy APPEND_ENTITY_PROPERTY(PROP_STATIC_CERTIFICATE_VERSION, properties.getStaticCertificateVersion()); APPEND_ENTITY_PROPERTY(PROP_CLONEABLE, properties.getCloneable()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIFETIME, properties.getCloneableLifetime()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_LIMIT, properties.getCloneableLimit()); - APPEND_ENTITY_PROPERTY(PROP_CLONEABLE_DYNAMIC, properties.getCloneableDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIFETIME, properties.getCloneLifetime()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_LIMIT, properties.getCloneLimit()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_DYNAMIC, properties.getCloneDynamic()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_AVATAR_ENTITY, properties.getCloneAvatarEntity()); + APPEND_ENTITY_PROPERTY(PROP_CLONE_ORIGIN_ID, properties.getCloneOriginID()); } if (propertyCount > 0) { @@ -2727,9 +2737,11 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STATIC_CERTIFICATE_VERSION, quint32, setStaticCertificateVersion); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE, bool, setCloneable); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIFETIME, float, setCloneableLifetime); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_LIMIT, float, setCloneableLimit); - READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONEABLE_DYNAMIC, bool, setCloneableDynamic); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIFETIME, float, setCloneLifetime); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_LIMIT, float, setCloneLimit); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_DYNAMIC, bool, setCloneDynamic); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_AVATAR_ENTITY, bool, setCloneAvatarEntity); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_CLONE_ORIGIN_ID, QUuid, setCloneOriginID); return valid; } @@ -2815,7 +2827,7 @@ bool EntityItemProperties::encodeCloneEntityMessage(const EntityItemID& entityID char* copyAt = buffer.data(); int outputLength = 0; - if (buffer.size() < (int)(sizeof(NUM_BYTES_RFC4122_UUID) * 2)) { + if (buffer.size() < (int)(NUM_BYTES_RFC4122_UUID * 2)) { qCDebug(entities) << "ERROR - encodeCloneEntityMessage() called with buffer that is too small!"; return false; } @@ -3021,9 +3033,11 @@ void EntityItemProperties::markAllChanged() { _relayParentJointsChanged = true; _cloneableChanged = true; - _cloneableLifetimeChanged = true; - _cloneableLimitChanged = true; - _cloneableDynamicChanged = true; + _cloneLifetimeChanged = true; + _cloneLimitChanged = true; + _cloneDynamicChanged = true; + _cloneAvatarEntityChanged = true; + _cloneOriginIDChanged = true; } // The minimum bounding box for the entity. @@ -3459,14 +3473,20 @@ QList EntityItemProperties::listChangedProperties() { if (cloneableChanged()) { out += "cloneable"; } - if (cloneableLifetimeChanged()) { - out += "cloneableLifetime"; + if (cloneLifetimeChanged()) { + out += "cloneLifetime"; } - if (cloneableLimitChanged()) { - out += "cloneableLimit"; + if (cloneLimitChanged()) { + out += "cloneLimit"; } - if (cloneableDynamicChanged()) { - out += "cloneableDynamic"; + if (cloneDynamicChanged()) { + out += "cloneDynamic"; + } + if (cloneAvatarEntityChanged()) { + out += "cloneAvatarEntity"; + } + if (cloneOriginIDChanged()) { + out += "cloneOriginID"; } @@ -3637,10 +3657,12 @@ bool EntityItemProperties::verifyStaticCertificateProperties() { void EntityItemProperties::convertToCloneProperties(const EntityItemID& entityIDToClone) { setName(getName() + "-clone-" + entityIDToClone.toString()); setLocked(false); - setLifetime(getCloneableLifetime()); - setDynamic(getCloneableDynamic()); + setLifetime(getCloneLifetime()); + setDynamic(getCloneDynamic()); + setClientOnly(getCloneAvatarEntity()); setCloneable(ENTITY_ITEM_CLONEABLE); - setCloneableLifetime(ENTITY_ITEM_CLONEABLE_LIFETIME); - setCloneableLimit(ENTITY_ITEM_CLONEABLE_LIMIT); - setCloneableDynamic(ENTITY_ITEM_CLONEABLE_DYNAMIC); -} \ No newline at end of file + setCloneLifetime(ENTITY_ITEM_CLONE_LIFETIME); + setCloneLimit(ENTITY_ITEM_CLONE_LIMIT); + setCloneDynamic(ENTITY_ITEM_CLONE_DYNAMIC); + setCloneAvatarEntity(ENTITY_ITEM_CLONE_AVATAR_ENTITY); +} diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 12d4bdf919..a34058c010 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -273,9 +273,11 @@ public: DEFINE_PROPERTY(PROP_RELAY_PARENT_JOINTS, RelayParentJoints, relayParentJoints, bool, ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS); DEFINE_PROPERTY(PROP_CLONEABLE, Cloneable, cloneable, bool, ENTITY_ITEM_CLONEABLE); - DEFINE_PROPERTY(PROP_CLONEABLE_LIFETIME, CloneableLifetime, cloneableLifetime, float, ENTITY_ITEM_CLONEABLE_LIFETIME); - DEFINE_PROPERTY(PROP_CLONEABLE_LIMIT, CloneableLimit, cloneableLimit, float, ENTITY_ITEM_CLONEABLE_LIMIT); - DEFINE_PROPERTY(PROP_CLONEABLE_DYNAMIC, CloneableDynamic, cloneableDynamic, bool, ENTITY_ITEM_CLONEABLE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_LIFETIME, CloneLifetime, cloneLifetime, float, ENTITY_ITEM_CLONE_LIFETIME); + DEFINE_PROPERTY(PROP_CLONE_LIMIT, CloneLimit, cloneLimit, float, ENTITY_ITEM_CLONE_LIMIT); + DEFINE_PROPERTY(PROP_CLONE_DYNAMIC, CloneDynamic, cloneDynamic, bool, ENTITY_ITEM_CLONE_DYNAMIC); + DEFINE_PROPERTY(PROP_CLONE_AVATAR_ENTITY, CloneAvatarEntity, cloneAvatarEntity, bool, ENTITY_ITEM_CLONE_AVATAR_ENTITY); + DEFINE_PROPERTY(PROP_CLONE_ORIGIN_ID, CloneOriginID, cloneOriginID, QUuid, ENTITY_ITEM_CLONE_ORIGIN_ID); static QString getComponentModeString(uint32_t mode); static QString getComponentModeAsString(uint32_t mode); diff --git a/libraries/entities/src/EntityItemPropertiesDefaults.h b/libraries/entities/src/EntityItemPropertiesDefaults.h index 6e46453bda..1a31bddebe 100644 --- a/libraries/entities/src/EntityItemPropertiesDefaults.h +++ b/libraries/entities/src/EntityItemPropertiesDefaults.h @@ -98,8 +98,10 @@ const QUuid ENTITY_ITEM_DEFAULT_LAST_EDITED_BY = QUuid(); const bool ENTITY_ITEM_DEFAULT_RELAY_PARENT_JOINTS = false; const bool ENTITY_ITEM_CLONEABLE = false; -const float ENTITY_ITEM_CLONEABLE_LIFETIME = 300.0f; -const int ENTITY_ITEM_CLONEABLE_LIMIT = 0; -const bool ENTITY_ITEM_CLONEABLE_DYNAMIC = false; +const float ENTITY_ITEM_CLONE_LIFETIME = 300.0f; +const int ENTITY_ITEM_CLONE_LIMIT = 0; +const bool ENTITY_ITEM_CLONE_DYNAMIC = false; +const bool ENTITY_ITEM_CLONE_AVATAR_ENTITY = false; +const QUuid ENTITY_ITEM_CLONE_ORIGIN_ID = QUuid(); #endif // hifi_EntityItemPropertiesDefaults_h diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index f698739e01..85ab28afdc 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -243,9 +243,11 @@ enum EntityPropertyList { PROP_MATERIAL_DATA, PROP_CLONEABLE, - PROP_CLONEABLE_LIFETIME, - PROP_CLONEABLE_LIMIT, - PROP_CLONEABLE_DYNAMIC, + PROP_CLONE_LIFETIME, + PROP_CLONE_LIMIT, + PROP_CLONE_DYNAMIC, + PROP_CLONE_AVATAR_ENTITY, + PROP_CLONE_ORIGIN_ID, //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index 71e7d1c7c0..f47a353ae8 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -333,7 +333,8 @@ QUuid EntityScriptingInterface::cloneEntity(QUuid entityIDToClone) { EntityItemID newEntityID; EntityItemProperties properties = getEntityProperties(entityIDToClone); properties.convertToCloneProperties(entityIDToClone); - if (addLocalEntityCopy(properties, newEntityID)) { + bool success = addLocalEntityCopy(properties, newEntityID); + if (success) { getEntityPacketSender()->queueCloneEntityMessage(entityIDToClone, newEntityID); return newEntityID; } else { diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index bcea89232e..647edaccc0 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -593,7 +593,7 @@ void EntityTree::deleteEntity(const EntityItemID& entityID, bool force, bool ign return; } - removeCloneIDFromCloneParent(entityID); + cleanupCloneIDs(entityID); unhookChildAvatar(entityID); emit deletingEntity(entityID); emit deletingEntityPointer(existingEntity.get()); @@ -627,14 +627,23 @@ void EntityTree::unhookChildAvatar(const EntityItemID entityID) { }); } -void EntityTree::removeCloneIDFromCloneParent(const EntityItemID& entityID) { +void EntityTree::cleanupCloneIDs(const EntityItemID& entityID) { EntityItemPointer entity = findEntityByEntityItemID(entityID); if (entity) { - const QUuid& cloneParentID = entity->getCloneParent(); - if (!cloneParentID.isNull()) { - EntityItemPointer cloneParent = findEntityByID(cloneParentID); - if (cloneParent) { - cloneParent->removeCloneID(entityID); + // remove clone ID from it's clone origin's clone ID list if clone origin exists + const QUuid cloneOriginID = entity->getCloneOriginID(); + if (!cloneOriginID.isNull()) { + EntityItemPointer cloneOrigin = findEntityByID(cloneOriginID); + if (cloneOrigin) { + cloneOrigin->removeCloneID(entityID); + } + } + // clear the clone origin ID on any clones that this entity had + const QList& cloneIDs = entity->getCloneIDs(); + foreach(const QUuid& cloneChildID, cloneIDs) { + EntityItemPointer cloneChild = findEntityByEntityItemID(cloneChildID); + if (cloneChild) { + cloneChild->setCloneOriginID(QUuid()); } } } @@ -668,7 +677,7 @@ void EntityTree::deleteEntities(QSet entityIDs, bool force, bool i } // tell our delete operator about this entityID - removeCloneIDFromCloneParent(entityID); + cleanupCloneIDs(entityID); unhookChildAvatar(entityID); theOperator.addEntityIDToDeleteList(entityID); emit deletingEntity(entityID); @@ -1601,7 +1610,7 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c bool failedAdd = !allowed; bool isCertified = !properties.getCertificateID().isEmpty(); bool isCloneable = properties.getCloneable(); - int cloneLimit = properties.getCloneableLimit(); + int cloneLimit = properties.getCloneLimit(); if (!allowed) { qCDebug(entities) << "Filtered entity add. ID:" << entityItemID; } else if (!isClone && !isCertified && !senderNode->getCanRez() && !senderNode->getCanRezTmp()) { @@ -1646,14 +1655,15 @@ int EntityTree::processEditPacketData(ReceivedMessage& message, const unsigned c } } + if (newEntity && isClone) { + entityToClone->addCloneID(newEntity->getEntityItemID()); + newEntity->setCloneOriginID(entityIDToClone); + } + if (newEntity) { newEntity->markAsChangedOnServer(); notifyNewlyCreatedEntity(*newEntity, senderNode); - if (isClone) { - entityToClone->addCloneID(newEntity->getEntityItemID()); - newEntity->setCloneParent(entityIDToClone); - } - + startLogging = usecTimestampNow(); if (wantEditLogging()) { qCDebug(entities) << "User [" << senderNode->getUUID() << "] added entity. ID:" diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h index 0d7cc54df7..aa6e382112 100644 --- a/libraries/entities/src/EntityTree.h +++ b/libraries/entities/src/EntityTree.h @@ -117,7 +117,7 @@ public: // check if the avatar is a child of this entity, If so set the avatar parentID to null void unhookChildAvatar(const EntityItemID entityID); - void removeCloneIDFromCloneParent(const EntityItemID& entityID); + void cleanupCloneIDs(const EntityItemID& entityID); void deleteEntity(const EntityItemID& entityID, bool force = false, bool ignoreWarnings = true); void deleteEntities(QSet entityIDs, bool force = false, bool ignoreWarnings = true); diff --git a/scripts/system/controllers/controllerModules/equipEntity.js b/scripts/system/controllers/controllerModules/equipEntity.js index 1fce772ec8..b6cad6841d 100644 --- a/scripts/system/controllers/controllerModules/equipEntity.js +++ b/scripts/system/controllers/controllerModules/equipEntity.js @@ -413,8 +413,7 @@ EquipHotspotBuddy.prototype.update = function(deltaTime, timestamp, controllerDa this.cloneHotspot = function(props, controllerData) { if (entityIsCloneable(props)) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(props, worldEntityProps); + var cloneID = cloneEntity(props); return cloneID; } diff --git a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js index a4e439fe2f..5e06ed762d 100644 --- a/scripts/system/controllers/controllerModules/nearActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearActionGrabEntity.js @@ -235,8 +235,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); // switch to grabbing var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneID = cloneEntity(targetProps); var cloneProps = Entities.getEntityProperties(cloneID); this.targetEntityID = cloneID; this.startNearGrabAction(controllerData, cloneProps); diff --git a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js index d454d20a02..cda0a683c7 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabEntity.js @@ -342,8 +342,7 @@ Script.include("/~/system/libraries/cloneEntityUtils.js"); var targetCloneable = entityIsCloneable(targetProps); if (targetCloneable) { - var worldEntityProps = controllerData.nearbyEntityProperties[this.hand]; - var cloneID = cloneEntity(targetProps, worldEntityProps); + var cloneID = cloneEntity(targetProps); var cloneProps = Entities.getEntityProperties(cloneID); this.grabbing = true; this.targetEntityID = cloneID; diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index c81e51d674..10e7b4b60e 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -1041,10 +1041,11 @@ function loaded() { elIgnoreIK.checked = true; elCloneable.checked = properties.cloneable; - elCloneableDynamic.checked = properties.cloneableDynamic; + elCloneableDynamic.checked = properties.cloneDynamic; + elCloneableAvatarEntity.checked = properties.cloneAvatarEntity; elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; - elCloneableLimit.value = properties.cloneableLimit; - elCloneableLifetime.value = properties.cloneableLifetime; + elCloneableLimit.value = properties.cloneLimit; + elCloneableLifetime.value = properties.cloneLifetime; var grabbablesSet = false; var parsedUserData = {}; @@ -1441,9 +1442,10 @@ function loaded() { }); elCloneable.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneable')); - elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneableDynamic')); - elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLifetime')); - elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneableLimit')); + elCloneableDynamic.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneDynamic')); + elCloneableAvatarEntity.addEventListener('change', createEmitCheckedPropertyUpdateFunction('cloneAvatarEntity')); + elCloneableLifetime.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLifetime')); + elCloneableLimit.addEventListener('change', createEmitNumberPropertyUpdateFunction('cloneLimit')); elWantsTrigger.addEventListener('change', function() { userDataChanger("grabbableKey", "wantsTrigger", elWantsTrigger, elUserData, false); diff --git a/scripts/system/libraries/cloneEntityUtils.js b/scripts/system/libraries/cloneEntityUtils.js index 358bc4a75f..5381d39116 100644 --- a/scripts/system/libraries/cloneEntityUtils.js +++ b/scripts/system/libraries/cloneEntityUtils.js @@ -41,12 +41,12 @@ entityIsCloneable = function(props) { propsAreCloneDynamic = function(props) { var cloneable = entityIsCloneable(props); if (cloneable) { - return props.cloneableDynamic; + return props.cloneDynamic; } return false; }; -cloneEntity = function(props, worldEntityProps) { +cloneEntity = function(props) { var entityToClone = props.id; var certificateID = Entities.getEntityProperties(entityToClone, ['certificateID']).certificateID; // ensure entity is cloneable and does not have a certificate ID, whereas cloneable limits