mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 12:41:55 +00:00 
			
		
		
		
	feat: add saving behaviors to the inventory (#1822)
* change behavior id to LWOOBJID Convert behavior ID to LWOOBJID length missed header fix sqlite field names sqlite brother * feat: add saving behaviors to the inventory consolidate copied code consolidate copied code Update ModelComponent.cpp remove ability to save loot behaviors
This commit is contained in:
		| @@ -7,7 +7,9 @@ | ||||
| #include "BehaviorStates.h" | ||||
| #include "ControlBehaviorMsgs.h" | ||||
| #include "tinyxml2.h" | ||||
| #include "InventoryComponent.h" | ||||
| #include "SimplePhysicsComponent.h" | ||||
| #include "eObjectBits.h" | ||||
|  | ||||
| #include "Database.h" | ||||
| #include "DluAssert.h" | ||||
| @@ -70,22 +72,27 @@ void ModelComponent::LoadBehaviors() { | ||||
| 		const auto behaviorId = GeneralUtils::TryParse<LWOOBJID>(behavior); | ||||
| 		if (!behaviorId.has_value() || behaviorId.value() == 0) continue; | ||||
|  | ||||
| 		LOG_DEBUG("Loading behavior %d", behaviorId.value()); | ||||
| 		auto& inserted = m_Behaviors.emplace_back(); | ||||
| 		inserted.SetBehaviorId(*behaviorId); | ||||
| 		// add behavior at the back | ||||
| 		LoadBehavior(behaviorId.value(), m_Behaviors.size(), false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| 		const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value()); | ||||
| void ModelComponent::LoadBehavior(const LWOOBJID behaviorID, const size_t index, const bool isIndexed) { | ||||
| 	LOG_DEBUG("Loading behavior %d", behaviorID); | ||||
| 	auto& inserted = *m_Behaviors.emplace(m_Behaviors.begin() + index, PropertyBehavior(isIndexed)); | ||||
| 	inserted.SetBehaviorId(behaviorID); | ||||
|  | ||||
| 		tinyxml2::XMLDocument behaviorXml; | ||||
| 		auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size()); | ||||
| 		LOG_DEBUG("Behavior %llu %d: %s", res, behaviorId.value(), behaviorStr.c_str()); | ||||
| 	const auto behaviorStr = Database::Get()->GetBehavior(behaviorID); | ||||
|  | ||||
| 		const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior"); | ||||
| 		if (!behaviorRoot) { | ||||
| 			LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value()); | ||||
| 			continue; | ||||
| 		} | ||||
| 	tinyxml2::XMLDocument behaviorXml; | ||||
| 	auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size()); | ||||
| 	LOG_DEBUG("Behavior %i %llu: %s", res, behaviorID, behaviorStr.c_str()); | ||||
|  | ||||
| 	const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior"); | ||||
| 	if (behaviorRoot) { | ||||
| 		inserted.Deserialize(*behaviorRoot); | ||||
| 	} else { | ||||
| 		LOG("Failed to load behavior %d due to missing behavior root", behaviorID); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -120,6 +127,7 @@ void ModelComponent::UpdatePendingBehaviorId(const LWOOBJID newId, const LWOOBJI | ||||
| 	for (auto& behavior : m_Behaviors) { | ||||
| 		if (behavior.GetBehaviorId() != oldId) continue; | ||||
| 		behavior.SetBehaviorId(newId); | ||||
| 		behavior.SetIsLoot(false); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -146,8 +154,21 @@ void ModelComponent::SendBehaviorBlocksToClient(const LWOOBJID behaviorToSend, A | ||||
| void ModelComponent::AddBehavior(AddMessage& msg) { | ||||
| 	// Can only have 1 of the loot behaviors | ||||
| 	for (auto& behavior : m_Behaviors) if (behavior.GetBehaviorId() == msg.GetBehaviorId()) return; | ||||
| 	m_Behaviors.insert(m_Behaviors.begin() + msg.GetBehaviorIndex(), PropertyBehavior()); | ||||
| 	m_Behaviors.at(msg.GetBehaviorIndex()).HandleMsg(msg); | ||||
|  | ||||
| 	// If we're loading a behavior from an ADD, it is from the database. | ||||
| 	// Mark it as not modified by default to prevent wasting persistentIDs. | ||||
| 	LoadBehavior(msg.GetBehaviorId(), msg.GetBehaviorIndex(), true); | ||||
| 	auto& insertedBehavior = m_Behaviors[msg.GetBehaviorIndex()]; | ||||
|  | ||||
| 	auto* const playerEntity = Game::entityManager->GetEntity(msg.GetOwningPlayerID()); | ||||
| 	if (playerEntity) { | ||||
| 		auto* inventoryComponent = playerEntity->GetComponent<InventoryComponent>(); | ||||
| 		if (inventoryComponent) { | ||||
| 			// Check if this behavior is able to be found via lot (if so, its a loot behavior). | ||||
| 			insertedBehavior.SetIsLoot(inventoryComponent->FindItemByLot(msg.GetBehaviorId(), eInventoryType::BEHAVIORS)); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	auto* const simplePhysComponent = m_Parent->GetComponent<SimplePhysicsComponent>(); | ||||
| 	if (simplePhysComponent) { | ||||
| 		simplePhysComponent->SetPhysicsMotionState(1); | ||||
| @@ -155,8 +176,41 @@ void ModelComponent::AddBehavior(AddMessage& msg) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) { | ||||
| std::string ModelComponent::SaveBehavior(const PropertyBehavior& behavior) const { | ||||
| 	tinyxml2::XMLDocument doc; | ||||
| 	auto* root = doc.NewElement("Behavior"); | ||||
| 	behavior.Serialize(*root); | ||||
| 	doc.InsertFirstChild(root); | ||||
|  | ||||
| 	tinyxml2::XMLPrinter printer(0, true, 0); | ||||
| 	doc.Print(&printer); | ||||
| 	return printer.CStr(); | ||||
| } | ||||
|  | ||||
| void ModelComponent::RemoveBehavior(MoveToInventoryMessage& msg, const bool keepItem) { | ||||
| 	if (msg.GetBehaviorIndex() >= m_Behaviors.size() || m_Behaviors.at(msg.GetBehaviorIndex()).GetBehaviorId() != msg.GetBehaviorId()) return; | ||||
| 	const auto behavior = m_Behaviors[msg.GetBehaviorIndex()]; | ||||
| 	if (keepItem) { | ||||
| 		auto* const playerEntity = Game::entityManager->GetEntity(msg.GetOwningPlayerID()); | ||||
| 		if (playerEntity) { | ||||
| 			auto* const inventoryComponent = playerEntity->GetComponent<InventoryComponent>(); | ||||
| 			if (inventoryComponent && !behavior.GetIsLoot()) { | ||||
| 				// config is owned by the item | ||||
| 				std::vector<LDFBaseData*> config; | ||||
| 				config.push_back(new LDFData<std::string>(u"userModelName", behavior.GetName())); | ||||
| 				inventoryComponent->AddItem(7965, 1, eLootSourceType::PROPERTY, eInventoryType::BEHAVIORS, config, LWOOBJID_EMPTY, true, false, msg.GetBehaviorId()); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// save the behavior before deleting it so players can re-add them | ||||
| 	IBehaviors::Info info{}; | ||||
| 	info.behaviorId = msg.GetBehaviorId(); | ||||
| 	info.behaviorInfo = SaveBehavior(behavior); | ||||
| 	info.characterId = msg.GetOwningPlayerID(); | ||||
|  | ||||
| 	Database::Get()->AddBehavior(info); | ||||
|  | ||||
| 	m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex()); | ||||
| 	// TODO move to the inventory | ||||
| 	if (m_Behaviors.empty()) { | ||||
| @@ -175,15 +229,7 @@ std::array<std::pair<LWOOBJID, std::string>, 5> ModelComponent::GetBehaviorsForS | ||||
| 		if (behavior.GetBehaviorId() == -1) continue; | ||||
| 		auto& [id, behaviorData] = toReturn[i]; | ||||
| 		id = behavior.GetBehaviorId(); | ||||
|  | ||||
| 		tinyxml2::XMLDocument doc; | ||||
| 		auto* root = doc.NewElement("Behavior"); | ||||
| 		behavior.Serialize(*root); | ||||
| 		doc.InsertFirstChild(root); | ||||
|  | ||||
| 		tinyxml2::XMLPrinter printer(0, true, 0); | ||||
| 		doc.Print(&printer); | ||||
| 		behaviorData = printer.CStr(); | ||||
| 		behaviorData = SaveBehavior(behavior); | ||||
| 	} | ||||
| 	return toReturn; | ||||
| } | ||||
|   | ||||
| @@ -66,15 +66,18 @@ public: | ||||
| 	 * | ||||
| 	 * @tparam Msg The message type to pass | ||||
| 	 * @param args the arguments of the message to be deserialized | ||||
| 	 *  | ||||
| 	 * @return returns true if a new behaviorID is needed. | ||||
| 	 */ | ||||
| 	template<typename Msg> | ||||
| 	void HandleControlBehaviorsMsg(const AMFArrayValue& args) { | ||||
| 	bool HandleControlBehaviorsMsg(const AMFArrayValue& args) { | ||||
| 		static_assert(std::is_base_of_v<BehaviorMessageBase, Msg>, "Msg must be a BehaviorMessageBase"); | ||||
| 		Msg msg{ args }; | ||||
| 		for (auto&& behavior : m_Behaviors) { | ||||
| 			if (behavior.GetBehaviorId() == msg.GetBehaviorId()) { | ||||
| 				behavior.CheckModifyState(msg); | ||||
| 				behavior.HandleMsg(msg); | ||||
| 				return; | ||||
| 				return msg.GetNeedsNewBehaviorID(); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -82,19 +85,21 @@ public: | ||||
| 		if (m_Behaviors.size() > 5) m_Behaviors.resize(5); | ||||
|  | ||||
| 		// Do not allow more than 5 to be added. The client UI will break if you do! | ||||
| 		if (m_Behaviors.size() == 5) return; | ||||
| 		if (m_Behaviors.size() == 5) return false; | ||||
|  | ||||
| 		auto newBehavior = m_Behaviors.insert(m_Behaviors.begin(), PropertyBehavior()); | ||||
| 		// Generally if we are inserting a new behavior, it is because the client is creating a new behavior. | ||||
| 		// However if we are testing behaviors the behavior will not exist on the initial pass, so we set the ID here to that of the msg. | ||||
| 		// This will either set the ID to -1 (no change in the current default) or set the ID to the ID of the behavior we are testing. | ||||
| 		newBehavior->SetBehaviorId(msg.GetBehaviorId()); | ||||
| 		newBehavior->CheckModifyState(msg); | ||||
| 		newBehavior->HandleMsg(msg); | ||||
| 		return msg.GetNeedsNewBehaviorID(); | ||||
| 	}; | ||||
|  | ||||
| 	void AddBehavior(AddMessage& msg); | ||||
|  | ||||
| 	void MoveToInventory(MoveToInventoryMessage& msg); | ||||
| 	void RemoveBehavior(MoveToInventoryMessage& msg, const bool keepItem); | ||||
|  | ||||
| 	// Updates the pending behavior ID to the new ID. | ||||
| 	void UpdatePendingBehaviorId(const LWOOBJID newId, const LWOOBJID oldId); | ||||
| @@ -141,6 +146,13 @@ public: | ||||
|  | ||||
| 	void OnChatMessageReceived(const std::string& sMessage); | ||||
| private: | ||||
|  | ||||
| 	// Loads a behavior from the database. | ||||
| 	void LoadBehavior(const LWOOBJID behaviorID, const size_t index, const bool isIndexed); | ||||
|  | ||||
| 	// Writes a behavior to a string so it can be saved. | ||||
| 	std::string SaveBehavior(const PropertyBehavior& behavior) const; | ||||
|  | ||||
| 	// Number of Actions that are awaiting an UnSmash to finish. | ||||
| 	uint32_t m_NumActiveUnSmash{}; | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "AddMessage.h" | ||||
|  | ||||
| AddMessage::AddMessage(const AMFArrayValue& arguments) : BehaviorMessageBase{ arguments } { | ||||
| AddMessage::AddMessage(const AMFArrayValue& arguments, const LWOOBJID _owningPlayerID) : m_OwningPlayerID{ _owningPlayerID }, BehaviorMessageBase { arguments } { | ||||
| 	const auto* const behaviorIndexValue = arguments.Get<double>("BehaviorIndex"); | ||||
| 	if (!behaviorIndexValue) return; | ||||
|  | ||||
|   | ||||
| @@ -9,11 +9,13 @@ | ||||
|  */ | ||||
| class AddMessage : public BehaviorMessageBase { | ||||
| public: | ||||
| 	AddMessage(const AMFArrayValue& arguments); | ||||
| 	AddMessage(const AMFArrayValue& arguments, const LWOOBJID _owningPlayerID); | ||||
| 	[[nodiscard]] uint32_t GetBehaviorIndex() const noexcept { return m_BehaviorIndex; }; | ||||
| 	[[nodiscard]] LWOOBJID GetOwningPlayerID() const noexcept { return m_OwningPlayerID; }; | ||||
|  | ||||
| private: | ||||
| 	uint32_t m_BehaviorIndex{ 0 }; | ||||
| 	LWOOBJID m_OwningPlayerID{}; | ||||
| }; | ||||
|  | ||||
| #endif  //!__ADDMESSAGE__H__ | ||||
|   | ||||
| @@ -19,11 +19,14 @@ public: | ||||
| 	BehaviorMessageBase(const AMFArrayValue& arguments) : m_BehaviorId{ GetBehaviorIdFromArgument(arguments) } {} | ||||
| 	[[nodiscard]] LWOOBJID GetBehaviorId() const noexcept { return m_BehaviorId; } | ||||
| 	[[nodiscard]] bool IsDefaultBehaviorId() const noexcept { return m_BehaviorId == DefaultBehaviorId; } | ||||
| 	[[nodiscard]] bool GetNeedsNewBehaviorID() const noexcept { return m_NeedsNewBehaviorID; } | ||||
| 	void SetNeedsNewBehaviorID(const bool val) noexcept { m_NeedsNewBehaviorID = val; } | ||||
|  | ||||
| protected: | ||||
| 	[[nodiscard]] LWOOBJID GetBehaviorIdFromArgument(const AMFArrayValue& arguments); | ||||
| 	[[nodiscard]] int32_t GetActionIndexFromArgument(const AMFArrayValue& arguments, const std::string_view keyName = "actionIndex") const; | ||||
| 	LWOOBJID m_BehaviorId{ DefaultBehaviorId }; | ||||
| 	bool m_NeedsNewBehaviorID{ false }; | ||||
| }; | ||||
|  | ||||
| #endif  //!__BEHAVIORMESSAGEBASE__H__ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| #include "MoveToInventoryMessage.h" | ||||
|  | ||||
| MoveToInventoryMessage::MoveToInventoryMessage(const AMFArrayValue& arguments) : BehaviorMessageBase{ arguments } { | ||||
| MoveToInventoryMessage::MoveToInventoryMessage(const AMFArrayValue& arguments, const LWOOBJID _owningPlayerID) : m_OwningPlayerID{ _owningPlayerID }, BehaviorMessageBase{ arguments } { | ||||
| 	const auto* const behaviorIndexValue = arguments.Get<double>("BehaviorIndex"); | ||||
| 	if (!behaviorIndexValue) return; | ||||
|  | ||||
|   | ||||
| @@ -10,11 +10,13 @@ class AMFArrayValue; | ||||
|  */ | ||||
| class MoveToInventoryMessage : public BehaviorMessageBase { | ||||
| public: | ||||
| 	MoveToInventoryMessage(const AMFArrayValue& arguments); | ||||
| 	MoveToInventoryMessage(const AMFArrayValue& arguments, const LWOOBJID owningPlayerID); | ||||
| 	[[nodiscard]] uint32_t GetBehaviorIndex() const noexcept { return m_BehaviorIndex; }; | ||||
| 	[[nodiscard]] LWOOBJID GetOwningPlayerID() const noexcept { return m_OwningPlayerID; }; | ||||
|  | ||||
| private: | ||||
| 	uint32_t m_BehaviorIndex; | ||||
| 	LWOOBJID m_OwningPlayerID{}; | ||||
| }; | ||||
|  | ||||
| #endif  //!__MOVETOINVENTORYMESSAGE__H__ | ||||
|   | ||||
| @@ -107,9 +107,12 @@ void ControlBehaviors::ProcessCommand(Entity* const modelEntity, const AMFArrayV | ||||
| 	if (!modelComponent) return; | ||||
|  | ||||
| 	ControlBehaviorContext context{ arguments, modelComponent, modelOwner }; | ||||
| 	bool needsNewBehaviorID = false; | ||||
|  | ||||
| 	if (command == "sendBehaviorListToClient") { | ||||
| 		SendBehaviorListToClient(context); | ||||
| 	} else if (command == "sendBehaviorBlocksToClient") { | ||||
| 		SendBehaviorBlocksToClient(context); | ||||
| 	} else if (command == "modelTypeChanged") { | ||||
| 		const auto* const modelType = arguments.Get<double>("ModelType"); | ||||
| 		if (!modelType) return; | ||||
| @@ -118,52 +121,54 @@ void ControlBehaviors::ProcessCommand(Entity* const modelEntity, const AMFArrayV | ||||
| 	} else if (command == "toggleExecutionUpdates") { | ||||
| 		// TODO | ||||
| 	} else if (command == "addStrip") { | ||||
| 		if (BehaviorMessageBase(context.arguments).IsDefaultBehaviorId()) RequestUpdatedID(context); | ||||
|  | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<AddStripMessage>(context.arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<AddStripMessage>(context.arguments); | ||||
| 	} else if (command == "removeStrip") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<RemoveStripMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<RemoveStripMessage>(arguments); | ||||
| 	} else if (command == "mergeStrips") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<MergeStripsMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<MergeStripsMessage>(arguments); | ||||
| 	} else if (command == "splitStrip") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<SplitStripMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<SplitStripMessage>(arguments); | ||||
| 	} else if (command == "updateStripUI") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<UpdateStripUiMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<UpdateStripUiMessage>(arguments); | ||||
| 	} else if (command == "addAction") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<AddActionMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<AddActionMessage>(arguments); | ||||
| 	} else if (command == "migrateActions") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<MigrateActionsMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<MigrateActionsMessage>(arguments); | ||||
| 	} else if (command == "rearrangeStrip") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<RearrangeStripMessage>(arguments); | ||||
| 	} else if (command == "add") { | ||||
| 		AddMessage msg{ context.arguments }; | ||||
| 		context.modelComponent->AddBehavior(msg); | ||||
| 		SendBehaviorListToClient(context); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<RearrangeStripMessage>(arguments); | ||||
| 	} else if (command == "removeActions") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<RemoveActionsMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<RemoveActionsMessage>(arguments); | ||||
| 	} else if (command == "updateAction") { | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<UpdateActionMessage>(arguments); | ||||
| 	} else if (command == "rename") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<RenameMessage>(arguments); | ||||
| 		needsNewBehaviorID = context.modelComponent->HandleControlBehaviorsMsg<RenameMessage>(arguments); | ||||
|  | ||||
| 		// Send the list back to the client so the name is updated. | ||||
| 		SendBehaviorListToClient(context); | ||||
| 	} else if (command == "sendBehaviorBlocksToClient") { | ||||
| 		SendBehaviorBlocksToClient(context); | ||||
| 	} else if (command == "moveToInventory") { | ||||
| 		MoveToInventoryMessage msg{ arguments }; | ||||
| 		context.modelComponent->MoveToInventory(msg); | ||||
| 	} else if (command == "add") { | ||||
| 		AddMessage msg{ context.arguments, context.modelOwner->GetObjectID() }; | ||||
| 		context.modelComponent->AddBehavior(msg); | ||||
| 		SendBehaviorListToClient(context); | ||||
| 	} else if (command == "moveToInventory" || command == "remove") { | ||||
| 		// both moveToInventory and remove use the same args | ||||
| 		const bool isRemove = command != "remove"; | ||||
| 		MoveToInventoryMessage msg{ arguments, modelOwner->GetObjectID() }; | ||||
| 		context.modelComponent->RemoveBehavior(msg, isRemove); | ||||
| 		auto* characterComponent = modelOwner->GetComponent<CharacterComponent>(); | ||||
| 		if (!characterComponent) return; | ||||
|  | ||||
| 		AMFArrayValue args; | ||||
| 		args.Insert("BehaviorID", std::to_string(msg.GetBehaviorId())); | ||||
| 		GameMessages::SendUIMessageServerToSingleClient(modelOwner, characterComponent->GetSystemAddress(), "BehaviorRemoved", args); | ||||
| 		if (!isRemove) { | ||||
| 			AMFArrayValue args; | ||||
| 			args.Insert("BehaviorID", std::to_string(msg.GetBehaviorId())); | ||||
| 			GameMessages::SendUIMessageServerToSingleClient(modelOwner, characterComponent->GetSystemAddress(), "BehaviorRemoved", args); | ||||
| 		} | ||||
|  | ||||
| 		SendBehaviorListToClient(context); | ||||
| 	} else if (command == "updateAction") { | ||||
| 		context.modelComponent->HandleControlBehaviorsMsg<UpdateActionMessage>(arguments); | ||||
| 	} else { | ||||
| 		LOG("Unknown behavior command (%s)", command.data()); | ||||
| 	} | ||||
|  | ||||
| 	if (needsNewBehaviorID) RequestUpdatedID(context); | ||||
| } | ||||
|  | ||||
| ControlBehaviors::ControlBehaviors() { | ||||
|   | ||||
| @@ -8,9 +8,12 @@ | ||||
|  | ||||
| #include <ranges> | ||||
|  | ||||
| PropertyBehavior::PropertyBehavior() { | ||||
| PropertyBehavior::PropertyBehavior(bool _isTemplated) { | ||||
| 	m_LastEditedState = BehaviorState::HOME_STATE; | ||||
| 	m_ActiveState = BehaviorState::HOME_STATE; | ||||
|  | ||||
| 	// Starts off as true so that only specific ways of adding behaviors allow a new id to be requested. | ||||
| 	isTemplated = _isTemplated; | ||||
| } | ||||
|  | ||||
| template<> | ||||
| @@ -81,13 +84,6 @@ void PropertyBehavior::HandleMsg(RenameMessage& msg) { | ||||
| 	m_Name = msg.GetName(); | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| void PropertyBehavior::HandleMsg(AddMessage& msg) { | ||||
| 	// TODO Parse the corresponding behavior xml file. | ||||
| 	m_BehaviorId = msg.GetBehaviorId(); | ||||
| 	isLoot = m_BehaviorId != 7965; | ||||
| }; | ||||
|  | ||||
| template<> | ||||
| void PropertyBehavior::HandleMsg(GameMessages::RequestUse& msg) { | ||||
| 	m_States[m_ActiveState].HandleMsg(msg); | ||||
| @@ -99,6 +95,12 @@ void PropertyBehavior::HandleMsg(GameMessages::ResetModelToDefaults& msg) { | ||||
| 	for (auto& state : m_States | std::views::values) state.HandleMsg(msg); | ||||
| } | ||||
|  | ||||
| void PropertyBehavior::CheckModifyState(BehaviorMessageBase& msg) { | ||||
| 	if (!isTemplated && m_BehaviorId != BehaviorMessageBase::DefaultBehaviorId) return; | ||||
| 	isTemplated = false; | ||||
| 	msg.SetNeedsNewBehaviorID(true); | ||||
| } | ||||
|  | ||||
| void PropertyBehavior::SendBehaviorListToClient(AMFArrayValue& args) const { | ||||
| 	args.Insert("id", std::to_string(m_BehaviorId)); | ||||
| 	args.Insert("name", m_Name); | ||||
| @@ -147,6 +149,9 @@ void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const { | ||||
| 	behavior.SetAttribute("isLocked", isLocked); | ||||
| 	behavior.SetAttribute("isLoot", isLoot); | ||||
|  | ||||
| 	// CUSTOM XML ATTRIBUTE | ||||
| 	behavior.SetAttribute("isTemplated", isTemplated); | ||||
|  | ||||
| 	for (const auto& [stateId, state] : m_States) { | ||||
| 		if (state.IsEmpty()) continue; | ||||
| 		auto* const stateElement = behavior.InsertNewChildElement("State"); | ||||
| @@ -161,6 +166,9 @@ void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) { | ||||
| 	behavior.QueryBoolAttribute("isLocked", &isLocked); | ||||
| 	behavior.QueryBoolAttribute("isLoot", &isLoot); | ||||
|  | ||||
| 	// CUSTOM XML ATTRIBUTE | ||||
| 	if (!isTemplated) behavior.QueryBoolAttribute("isTemplated", &isTemplated); | ||||
|  | ||||
| 	for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) { | ||||
| 		int32_t stateId = -1; | ||||
| 		stateElement->QueryIntAttribute("id", &stateId); | ||||
|   | ||||
| @@ -10,6 +10,7 @@ namespace tinyxml2 { | ||||
| enum class BehaviorState : uint32_t; | ||||
|  | ||||
| class AMFArrayValue; | ||||
| class BehaviorMessageBase; | ||||
| class ModelComponent; | ||||
|  | ||||
| /** | ||||
| @@ -17,7 +18,7 @@ class ModelComponent; | ||||
|  */ | ||||
| class PropertyBehavior { | ||||
| public: | ||||
| 	PropertyBehavior(); | ||||
| 	PropertyBehavior(bool _isTemplated = false); | ||||
|  | ||||
| 	template <typename Msg> | ||||
| 	void HandleMsg(Msg& msg); | ||||
| @@ -26,10 +27,16 @@ public: | ||||
| 	void VerifyLastEditedState(); | ||||
| 	void SendBehaviorListToClient(AMFArrayValue& args) const; | ||||
| 	void SendBehaviorBlocksToClient(AMFArrayValue& args) const; | ||||
| 	void CheckModifyState(BehaviorMessageBase& msg); | ||||
|  | ||||
| 	[[nodiscard]] LWOOBJID GetBehaviorId() const noexcept { return m_BehaviorId; } | ||||
| 	void SetBehaviorId(LWOOBJID id) noexcept { m_BehaviorId = id; } | ||||
|  | ||||
| 	bool GetIsLoot() const noexcept { return isLoot; } | ||||
| 	void SetIsLoot(const bool val) noexcept { isLoot = val; } | ||||
|  | ||||
| 	const std::string& GetName() const noexcept { return m_Name; } | ||||
|  | ||||
| 	void Serialize(tinyxml2::XMLElement& behavior) const; | ||||
| 	void Deserialize(const tinyxml2::XMLElement& behavior); | ||||
|  | ||||
| @@ -52,6 +59,9 @@ private: | ||||
| 	// Whether this behavior is custom or pre-fab. | ||||
| 	bool isLoot = false; | ||||
|  | ||||
| 	// Whether or not the behavior has been modified from its original state. | ||||
| 	bool isTemplated; | ||||
|  | ||||
| 	// The last state that was edited. This is used so when the client re-opens the behavior editor, it will open to the last edited state. | ||||
| 	// If the last edited state has no strips, it will open to the first state that has strips. | ||||
| 	BehaviorState m_LastEditedState; | ||||
|   | ||||
| @@ -213,7 +213,7 @@ TEST_F(GameMessageTests, ControlBehaviorAdd) { | ||||
| 	RakNet::BitStream inStream(reinterpret_cast<unsigned char*>(&data[0]), data.length(), true); | ||||
|  | ||||
| 	const auto arr = ReadArrayFromBitStream(inStream); | ||||
| 	AddMessage add(*arr); | ||||
| 	AddMessage add(*arr, LWOOBJID_EMPTY); | ||||
|  | ||||
| 	ASSERT_EQ(add.GetBehaviorId(), 10446); | ||||
| 	ASSERT_EQ(add.GetBehaviorIndex(), 0); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 David Markowitz
					David Markowitz