mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-10-31 12:41:55 +00:00 
			
		
		
		
	Add FlagComponent and msg handlers
This commit is contained in:
		| @@ -22,6 +22,7 @@ | ||||
| #include "eGameMasterLevel.h" | ||||
| #include "ePlayerFlag.h" | ||||
| #include "CDPlayerFlagsTable.h" | ||||
| #include "FlagComponent.h" | ||||
|  | ||||
| Character::Character(uint32_t id, User* parentUser) { | ||||
| 	//First load the name, etc: | ||||
| @@ -37,7 +38,7 @@ Character::~Character() { | ||||
| 	m_ParentUser = nullptr; | ||||
| } | ||||
|  | ||||
| void Character::UpdateInfoFromDatabase() { | ||||
| void Character::UpdateInfoFromDatabase(bool clearSessionFlags) { | ||||
| 	auto charInfo = Database::Get()->GetCharacterInfo(m_ID); | ||||
|  | ||||
| 	if (charInfo) { | ||||
| @@ -65,10 +66,17 @@ void Character::UpdateInfoFromDatabase() { | ||||
|  | ||||
| 	m_OurEntity = nullptr; | ||||
| 	m_BuildMode = false; | ||||
|  | ||||
| 	// This is not done through a game message because our current implementation does this at a point | ||||
| 	// in time where an Entity does not exist, so there is no FlagComponent to handle the msg. | ||||
| 	if (clearSessionFlags) { | ||||
| 		FlagComponent::ClearSessionFlags(m_Doc); | ||||
| 		WriteToDatabase(); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Character::UpdateFromDatabase() { | ||||
| 	UpdateInfoFromDatabase(); | ||||
| void Character::UpdateFromDatabase(bool clearSessionFlags) { | ||||
| 	UpdateInfoFromDatabase(clearSessionFlags); | ||||
| } | ||||
|  | ||||
| void Character::DoQuickXMLDataParse() { | ||||
| @@ -197,25 +205,6 @@ void Character::DoQuickXMLDataParse() { | ||||
| 		character->QueryAttribute("lzrz", &m_OriginalRotation.z); | ||||
| 		character->QueryAttribute("lzrw", &m_OriginalRotation.w); | ||||
| 	} | ||||
|  | ||||
| 	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag"); | ||||
| 	if (flags) { | ||||
| 		auto* currentChild = flags->FirstChildElement(); | ||||
| 		while (currentChild) { | ||||
| 			const auto* temp = currentChild->Attribute("v"); | ||||
| 			const auto* id = currentChild->Attribute("id"); | ||||
| 			if (temp && id) { | ||||
| 				uint32_t index = 0; | ||||
| 				uint64_t value = 0; | ||||
|  | ||||
| 				index = std::stoul(id); | ||||
| 				value = std::stoull(temp); | ||||
|  | ||||
| 				m_PlayerFlags.insert(std::make_pair(index, value)); | ||||
| 			} | ||||
| 			currentChild = currentChild->NextSiblingElement(); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Character::UnlockEmote(int emoteID) { | ||||
| @@ -276,25 +265,6 @@ void Character::SaveXMLToDatabase() { | ||||
| 		character->LinkEndChild(emotes); | ||||
| 	} | ||||
|  | ||||
| 	//Export our flags: | ||||
| 	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag"); | ||||
| 	if (!flags) { | ||||
| 		flags = m_Doc.NewElement("flag"); //Create a flags tag if we don't have one | ||||
| 		m_Doc.FirstChildElement("obj")->LinkEndChild(flags); //Link it to the obj tag so we can find next time | ||||
| 	} | ||||
|  | ||||
| 	flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes | ||||
| 	for (const auto& [index, flagBucket] : m_PlayerFlags) { | ||||
| 		auto* f = flags->InsertNewChildElement("f"); | ||||
| 		f->SetAttribute("id", index); | ||||
| 		f->SetAttribute("v", flagBucket); | ||||
| 	} | ||||
|  | ||||
| 	for (const auto& sessionFlag : m_SessionFlags) { | ||||
| 		auto* s = flags->InsertNewChildElement("s"); | ||||
| 		s->SetAttribute("si", sessionFlag); | ||||
| 	} | ||||
|  | ||||
| 	SaveXmlRespawnCheckpoints(); | ||||
|  | ||||
| 	m_OurEntity->UpdateXMLDoc(m_Doc); | ||||
| @@ -307,23 +277,6 @@ void Character::SaveXMLToDatabase() { | ||||
| 	LOG("%i:%s Saved character to Database in: %fs", this->GetID(), this->GetName().c_str(), elapsed.count()); | ||||
| } | ||||
|  | ||||
| void Character::SetIsNewLogin() { | ||||
| 	// If we dont have a flag element, then we cannot have a s element as a child of flag. | ||||
| 	auto* flags = m_Doc.FirstChildElement("obj")->FirstChildElement("flag"); | ||||
| 	if (!flags) return; | ||||
|  | ||||
| 	auto* currentChild = flags->FirstChildElement(); | ||||
| 	while (currentChild) { | ||||
| 		auto* nextChild = currentChild->NextSiblingElement(); | ||||
| 		if (currentChild->Attribute("si")) { | ||||
| 			LOG("Removed session flag (%s) from character %i:%s, saving character to database", currentChild->Attribute("si"), GetID(), GetName().c_str()); | ||||
| 			flags->DeleteChild(currentChild); | ||||
| 			WriteToDatabase(); | ||||
| 		} | ||||
| 		currentChild = nextChild; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Character::WriteToDatabase() { | ||||
| 	//Dump our xml into m_XMLData: | ||||
| 	tinyxml2::XMLPrinter printer(0, true, 0); | ||||
| @@ -333,90 +286,6 @@ void Character::WriteToDatabase() { | ||||
| 	Database::Get()->UpdateCharacterXml(m_ID, printer.CStr()); | ||||
| } | ||||
|  | ||||
| void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { | ||||
| 	// If the flag is already set, we don't have to recalculate it | ||||
| 	if (GetPlayerFlag(flagId) == value) return; | ||||
|  | ||||
| 	if (value) { | ||||
| 		// Update the mission component: | ||||
| 		auto* player = Game::entityManager->GetEntity(m_ObjectID); | ||||
|  | ||||
| 		if (player != nullptr) { | ||||
| 			auto* missionComponent = player->GetComponent<MissionComponent>(); | ||||
|  | ||||
| 			if (missionComponent != nullptr) { | ||||
| 				missionComponent->Progress(eMissionTaskType::PLAYER_FLAG, flagId); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId);	 | ||||
|  | ||||
| 	if (flagEntry && flagEntry->sessionOnly) { | ||||
| 		if (value) m_SessionFlags.insert(flagId); | ||||
| 		else m_SessionFlags.erase(flagId); | ||||
| 	} else { | ||||
| 		// Calculate the index first | ||||
| 		auto flagIndex = uint32_t(std::floor(flagId / 64)); | ||||
|  | ||||
| 		const auto shiftedValue = 1ULL << flagId % 64; | ||||
|  | ||||
| 		auto it = m_PlayerFlags.find(flagIndex); | ||||
|  | ||||
| 		// Check if flag index exists | ||||
| 		if (it != m_PlayerFlags.end()) { | ||||
| 			// Update the value | ||||
| 			if (value) { | ||||
| 				it->second |= shiftedValue; | ||||
| 			} else { | ||||
| 				it->second &= ~shiftedValue; | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (value) { | ||||
| 				// Otherwise, insert the value | ||||
| 				uint64_t flagValue = 0; | ||||
|  | ||||
| 				flagValue |= shiftedValue; | ||||
|  | ||||
| 				m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Notify the client that a flag has changed server-side | ||||
| 	GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress()); | ||||
| } | ||||
|  | ||||
| bool Character::GetPlayerFlag(const uint32_t flagId) const { | ||||
| 	using enum ePlayerFlag; | ||||
|  | ||||
| 	bool toReturn = false; //by def, return false. | ||||
|  | ||||
| 	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId); | ||||
| 	if (flagEntry && flagEntry->sessionOnly) { | ||||
| 		toReturn = m_SessionFlags.contains(flagId); | ||||
| 	} else { | ||||
| 		// Calculate the index first | ||||
| 		const auto flagIndex = uint32_t(std::floor(flagId / 64)); | ||||
|  | ||||
| 		const auto shiftedValue = 1ULL << flagId % 64; | ||||
|  | ||||
| 		auto it = m_PlayerFlags.find(flagIndex); | ||||
| 		if (it != m_PlayerFlags.end()) { | ||||
| 			// Don't set the data if we don't have to | ||||
| 			toReturn = (it->second & shiftedValue) != 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return toReturn; | ||||
| } | ||||
|  | ||||
| void Character::SetRetroactiveFlags() { | ||||
| 	// Retroactive check for if player has joined a faction to set their 'joined a faction' flag to true. | ||||
| 	if (GetPlayerFlag(ePlayerFlag::VENTURE_FACTION) || GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION) || GetPlayerFlag(ePlayerFlag::PARADOX_FACTION) || GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) { | ||||
| 		SetPlayerFlag(ePlayerFlag::JOINED_A_FACTION, true); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Character::SaveXmlRespawnCheckpoints() { | ||||
| 	//Export our respawn points: | ||||
| 	auto* points = m_Doc.FirstChildElement("obj")->FirstChildElement("res"); | ||||
| @@ -473,7 +342,11 @@ void Character::OnZoneLoad() { | ||||
| 	if (missionComponent != nullptr) { | ||||
| 		// Fix the monument race flag | ||||
| 		if (missionComponent->GetMissionState(319) >= eMissionState::READY_TO_COMPLETE) { | ||||
| 			SetPlayerFlag(ePlayerFlag::AG_FINISH_LINE_BUILT, true); | ||||
| 			GameMessages::SetFlag setFlag{}; | ||||
| 			setFlag.target = m_ObjectID; | ||||
| 			setFlag.iFlagId = ePlayerFlag::AG_FINISH_LINE_BUILT; | ||||
| 			setFlag.bFlag = true; | ||||
| 			SEND_ENTITY_MSG(setFlag); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -31,7 +31,7 @@ public: | ||||
| 	 */ | ||||
| 	void WriteToDatabase(); | ||||
| 	void SaveXMLToDatabase(); | ||||
| 	void UpdateFromDatabase(); | ||||
| 	void UpdateFromDatabase(bool clearSessionFlags = false); | ||||
|  | ||||
| 	void SaveXmlRespawnCheckpoints(); | ||||
| 	void LoadXmlRespawnCheckpoints(); | ||||
| @@ -40,15 +40,6 @@ public: | ||||
| 	const tinyxml2::XMLDocument& GetXMLDoc() const { return m_Doc; } | ||||
| 	void _setXmlDoc(tinyxml2::XMLDocument& doc) { doc.DeepCopy(&m_Doc); } | ||||
|  | ||||
| 	/** | ||||
| 	 * Out of abundance of safety and clarity of what this saves, this is its own function. | ||||
| 	 * | ||||
| 	 * Clears the s element from the flag element and saves the xml to the database.  Used to prevent the news | ||||
| 	 * feed from showing up on world transfers. | ||||
| 	 * | ||||
| 	 */ | ||||
| 	void SetIsNewLogin(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the database ID of the character | ||||
| 	 * @return the database ID of the character | ||||
| @@ -410,32 +401,11 @@ public: | ||||
| 	 */ | ||||
| 	void UnlockEmote(int emoteID); | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets a flag for the character, indicating certain parts of the game that have been interacted with. Not to be | ||||
| 	 * confused with the permissions | ||||
| 	 * @param flagId the ID of the flag to set | ||||
| 	 * @param value the value to set for the flag | ||||
| 	 */ | ||||
| 	void SetPlayerFlag(uint32_t flagId, bool value); | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the value for a certain character flag | ||||
| 	 * @param flagId the ID of the flag to get a value for | ||||
| 	 * @return the value of the flag given the ID (the default is false, obviously) | ||||
| 	 */ | ||||
| 	bool GetPlayerFlag(uint32_t flagId) const; | ||||
|  | ||||
| 	/** | ||||
| 	 * Notifies the character that they're now muted | ||||
| 	 */ | ||||
| 	void SendMuteNotice() const; | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets any flags that are meant to have been set that may not have been set due to them being | ||||
| 	 * missing in a previous patch. | ||||
| 	 */ | ||||
| 	void SetRetroactiveFlags(); | ||||
|  | ||||
| 	/** | ||||
| 	 * Get the equipped items for this character, only used for character creation | ||||
| 	 * @return the equipped items for this character on world load | ||||
| @@ -465,7 +435,7 @@ public: | ||||
| 	void _setXmlData(const std::string& xmlData) { m_XMLData = xmlData; } | ||||
|  | ||||
| private: | ||||
| 	void UpdateInfoFromDatabase(); | ||||
| 	void UpdateInfoFromDatabase(bool clearSessionFlags); | ||||
| 	/** | ||||
| 	 * The ID of this character. First 32 bits of the ObjectID. | ||||
| 	 */ | ||||
| @@ -620,17 +590,6 @@ private: | ||||
| 	 */ | ||||
| 	uint64_t m_LastLogin{}; | ||||
|  | ||||
| 	/** | ||||
| 	 * Flags only set for the duration of a session | ||||
| 	 *  | ||||
| 	 */ | ||||
| 	std::set<uint32_t> m_SessionFlags; | ||||
|  | ||||
| 	/** | ||||
| 	 * The gameplay flags this character has (not just true values) | ||||
| 	 */ | ||||
| 	std::unordered_map<uint32_t, uint64_t> m_PlayerFlags; | ||||
|  | ||||
| 	/** | ||||
| 	 * The character XML belonging to this character | ||||
| 	 */ | ||||
|   | ||||
| @@ -84,6 +84,7 @@ | ||||
| #include "GhostComponent.h" | ||||
| #include "AchievementVendorComponent.h" | ||||
| #include "VanityUtilities.h" | ||||
| #include "FlagComponent.h" | ||||
|  | ||||
| // Table includes | ||||
| #include "CDComponentsRegistryTable.h" | ||||
| @@ -482,6 +483,8 @@ void Entity::Initialize() { | ||||
| 		AddComponent<CharacterComponent>(m_Character, systemAddress)->LoadFromXml(m_Character->GetXMLDoc()); | ||||
|  | ||||
| 		AddComponent<GhostComponent>(); | ||||
|  | ||||
| 		AddComponent<FlagComponent>()->LoadFromXml(m_Character->GetXMLDoc()); | ||||
| 	} | ||||
|  | ||||
| 	if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::INVENTORY) > 0 || m_Character) { | ||||
|   | ||||
| @@ -604,3 +604,10 @@ void EntityManager::FireEventServerSide(Entity* origin, std::string args) { | ||||
| bool EntityManager::IsExcludedFromGhosting(LOT lot) { | ||||
| 	return std::find(m_GhostingExcludedLOTs.begin(), m_GhostingExcludedLOTs.end(), lot) != m_GhostingExcludedLOTs.end(); | ||||
| } | ||||
|  | ||||
| bool EntityManager::SendMsg(GameMessages::GameMsg& msg) { | ||||
| 	bool bRet = false; | ||||
| 	auto* entity = GetEntity(msg.target); | ||||
| 	if (entity) bRet = entity->HandleMsg(msg); | ||||
| 	return bRet; | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,13 @@ | ||||
|  | ||||
| #include "dCommonVars.h" | ||||
|  | ||||
| // Convenience macro to send a message to the entity manager | ||||
| #define SEND_ENTITY_MSG(msg) Game::entityManager->SendMsg(msg) | ||||
|  | ||||
| namespace GameMessages { | ||||
| 	struct GameMsg;	 | ||||
| }; | ||||
|  | ||||
| class Entity; | ||||
| class EntityInfo; | ||||
| class Player; | ||||
| @@ -72,6 +79,9 @@ public: | ||||
| 	const bool GetHardcoreDropinventoryOnDeath() { return m_HardcoreDropinventoryOnDeath; }; | ||||
| 	const uint32_t GetHardcoreUscoreEnemiesMultiplier() { return m_HardcoreUscoreEnemiesMultiplier; }; | ||||
|  | ||||
| 	// Sends a message to be handled by the receiving entity | ||||
| 	bool SendMsg(GameMessages::GameMsg& msg); | ||||
|  | ||||
| private: | ||||
| 	void SerializeEntities(); | ||||
| 	void KillEntities(); | ||||
|   | ||||
| @@ -197,6 +197,10 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { | ||||
| 			skillComponent->Reset(); | ||||
| 		} | ||||
|  | ||||
| 		GameMessages::ClearSessionFlags msg{}; | ||||
| 		msg.target = chars[i]->GetObjectID(); | ||||
| 		SEND_ENTITY_MSG(msg); | ||||
|  | ||||
| 		Game::entityManager->DestroyEntity(chars[i]->GetEntity()); | ||||
|  | ||||
| 		chars[i]->SaveXMLToDatabase(); | ||||
| @@ -210,8 +214,7 @@ void UserManager::RequestCharacterList(const SystemAddress& sysAddr) { | ||||
|  | ||||
| 	for (const auto& characterId : Database::Get()->GetAccountCharacterIds(u->GetAccountID())) { | ||||
| 		Character* character = new Character(characterId, u); | ||||
| 		character->UpdateFromDatabase(); | ||||
| 		character->SetIsNewLogin(); | ||||
| 		character->UpdateFromDatabase(true); | ||||
| 		chars.push_back(character); | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,7 @@ set(DGAME_DCOMPONENTS_SOURCES | ||||
| 	"ControllablePhysicsComponent.cpp" | ||||
| 	"DestroyableComponent.cpp" | ||||
| 	"DonationVendorComponent.cpp" | ||||
| 	"FlagComponent.cpp" | ||||
| 	"GhostComponent.cpp" | ||||
| 	"InventoryComponent.cpp" | ||||
| 	"ItemComponent.cpp" | ||||
|   | ||||
							
								
								
									
										199
									
								
								dGame/dComponents/FlagComponent.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								dGame/dComponents/FlagComponent.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,199 @@ | ||||
| #include "FlagComponent.h" | ||||
|  | ||||
| #include "CDPlayerFlagsTable.h" | ||||
|  | ||||
| #include "eMissionTaskType.h" | ||||
| #include "ePlayerFlag.h" | ||||
|  | ||||
| #include "MissionComponent.h" | ||||
|  | ||||
| FlagComponent::FlagComponent(Entity* parent) : Component(parent) { | ||||
| 	RegisterMsg(MessageType::Game::SET_FLAG, this, &FlagComponent::OnSetFlag); | ||||
| 	RegisterMsg(MessageType::Game::GET_FLAG, this, &FlagComponent::OnGetFlag); | ||||
| 	RegisterMsg(MessageType::Game::CLEAR_SESSION_FLAGS, this, &FlagComponent::OnClearSessionFlags); | ||||
| 	RegisterMsg(MessageType::Game::SET_RETROACTIVE_FLAGS, this, &FlagComponent::OnSetRetroactiveFlags); | ||||
| } | ||||
|  | ||||
| bool FlagComponent::OnSetFlag(GameMessages::GameMsg& msg) { | ||||
| 	auto& setFlag = static_cast<GameMessages::SetFlag&>(msg); | ||||
| 	LOG("Set %i", setFlag.iFlagId); | ||||
| 	SetPlayerFlag(setFlag.iFlagId, setFlag.bFlag); | ||||
|  | ||||
| 	// This is always set the first time a player loads into a world from character select | ||||
| 	// and is used to know when to refresh the players inventory items so they show up. | ||||
| 	if (setFlag.iFlagId == ePlayerFlag::IS_NEWS_SCREEN_VISIBLE && setFlag.bFlag) { | ||||
| 		m_Parent->SetVar<bool>(u"dlu_first_time_load", true); | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool FlagComponent::OnGetFlag(GameMessages::GameMsg& msg) { | ||||
| 	auto& getFlag = static_cast<GameMessages::GetFlag&>(msg); | ||||
| 	LOG("Get %i", getFlag.iFlagId); | ||||
|  | ||||
| 	getFlag.bFlag = GetPlayerFlag(getFlag.iFlagId); | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void FlagComponent::UpdateXml(tinyxml2::XMLDocument& doc) { | ||||
| 	if (!doc.FirstChildElement("obj")) return; | ||||
| 	auto& obj = *doc.FirstChildElement("obj"); | ||||
|  | ||||
| 	if (!obj.FirstChildElement("flag")) { | ||||
| 		obj.InsertNewChildElement("flag"); | ||||
| 	} | ||||
|  | ||||
| 	auto& flags = *obj.FirstChildElement("flag"); | ||||
|  | ||||
| 	flags.DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes | ||||
|  | ||||
| 	// Save our flags | ||||
| 	for (const auto& [index, flagBucket] : m_PlayerFlags) { | ||||
| 		auto& f = *flags.InsertNewChildElement("f"); | ||||
| 		f.SetAttribute("id", index); | ||||
| 		f.SetAttribute("v", flagBucket); | ||||
| 	} | ||||
|  | ||||
| 	// Save our session flags | ||||
| 	for (const auto& sessionFlag : m_SessionFlags) { | ||||
| 		auto& s = *flags.InsertNewChildElement("s"); | ||||
| 		LOG("Saving session flag %i", sessionFlag); | ||||
| 		s.SetAttribute("si", sessionFlag); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FlagComponent::LoadFromXml(const tinyxml2::XMLDocument& doc) { | ||||
| 	if (!doc.FirstChildElement("obj")) return; | ||||
| 	auto& obj = *doc.FirstChildElement("obj"); | ||||
|  | ||||
| 	if (!obj.FirstChildElement("flag")) return; | ||||
| 	auto& flags = *obj.FirstChildElement("flag"); | ||||
|  | ||||
| 	const auto* currentChild = flags.FirstChildElement("f"); | ||||
| 	while (currentChild) { | ||||
| 		const auto* temp = currentChild->Attribute("v"); | ||||
| 		const auto* id = currentChild->Attribute("id"); | ||||
| 		if (temp && id) { | ||||
| 			uint32_t index = 0; | ||||
| 			uint64_t value = 0; | ||||
|  | ||||
| 			index = std::stoul(id); | ||||
| 			value = std::stoull(temp); | ||||
|  | ||||
| 			m_PlayerFlags.insert(std::make_pair(index, value)); | ||||
| 		} | ||||
| 		currentChild = currentChild->NextSiblingElement("f"); | ||||
| 	} | ||||
|  | ||||
| 	// Now load our session flags | ||||
| 	currentChild = flags.FirstChildElement("s"); | ||||
| 	while (currentChild) { | ||||
| 		const auto* temp = currentChild->Attribute("si"); | ||||
| 		if (temp) { | ||||
| 			uint32_t sessionIndex = 0; | ||||
| 			sessionIndex = std::stoul(temp); | ||||
| 			m_SessionFlags.insert(sessionIndex); | ||||
| 		} | ||||
| 		currentChild = currentChild->NextSiblingElement("s"); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void FlagComponent::SetPlayerFlag(const uint32_t flagId, const bool value) { | ||||
| 	// If the flag is already set, we don't have to recalculate it | ||||
| 	if (GetPlayerFlag(flagId) == value) return; | ||||
|  | ||||
| 	if (value) { | ||||
| 		// Update the mission component: | ||||
| 		auto* missionComponent = m_Parent->GetComponent<MissionComponent>(); | ||||
|  | ||||
| 		if (missionComponent != nullptr) { | ||||
| 			missionComponent->Progress(eMissionTaskType::PLAYER_FLAG, flagId); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId); | ||||
|  | ||||
| 	if (flagEntry && flagEntry->sessionOnly) { | ||||
| 		if (value) m_SessionFlags.insert(flagId); | ||||
| 		else m_SessionFlags.erase(flagId); | ||||
| 	} else { | ||||
| 		// Calculate the index first | ||||
| 		auto flagIndex = uint32_t(std::floor(flagId / 64)); | ||||
|  | ||||
| 		const auto shiftedValue = 1ULL << flagId % 64; | ||||
|  | ||||
| 		auto it = m_PlayerFlags.find(flagIndex); | ||||
|  | ||||
| 		// Check if flag index exists | ||||
| 		if (it != m_PlayerFlags.end()) { | ||||
| 			// Update the value | ||||
| 			if (value) { | ||||
| 				it->second |= shiftedValue; | ||||
| 			} else { | ||||
| 				it->second &= ~shiftedValue; | ||||
| 			} | ||||
| 		} else { | ||||
| 			if (value) { | ||||
| 				// Otherwise, insert the value | ||||
| 				uint64_t flagValue = 0; | ||||
|  | ||||
| 				flagValue |= shiftedValue; | ||||
|  | ||||
| 				m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue)); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Notify the client that a flag has changed server-side | ||||
| 	GameMessages::SendNotifyClientFlagChange(m_Parent->GetObjectID(), flagId, value, m_Parent->GetSystemAddress()); | ||||
| } | ||||
|  | ||||
| bool FlagComponent::GetPlayerFlag(const uint32_t flagId) const { | ||||
| 	bool toReturn = false; //by def, return false. | ||||
|  | ||||
| 	const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId); | ||||
| 	if (flagEntry && flagEntry->sessionOnly) { | ||||
| 		toReturn = m_SessionFlags.contains(flagId); | ||||
| 	} else { | ||||
| 		// Calculate the index first | ||||
| 		const auto flagIndex = uint32_t(std::floor(flagId / 64)); | ||||
|  | ||||
| 		const auto shiftedValue = 1ULL << flagId % 64; | ||||
|  | ||||
| 		auto it = m_PlayerFlags.find(flagIndex); | ||||
| 		if (it != m_PlayerFlags.end()) { | ||||
| 			// Don't set the data if we don't have to | ||||
| 			toReturn = (it->second & shiftedValue) != 0; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return toReturn; | ||||
| } | ||||
|  | ||||
| bool FlagComponent::OnSetRetroactiveFlags(GameMessages::GameMsg& msg) { | ||||
| 	// Retroactive check for if player has joined a faction to set their 'joined a faction' flag to true. | ||||
| 	if (GetPlayerFlag(ePlayerFlag::VENTURE_FACTION) || | ||||
| 		GetPlayerFlag(ePlayerFlag::ASSEMBLY_FACTION) || | ||||
| 		GetPlayerFlag(ePlayerFlag::PARADOX_FACTION) || | ||||
| 		GetPlayerFlag(ePlayerFlag::SENTINEL_FACTION)) { | ||||
| 		SetPlayerFlag(ePlayerFlag::JOINED_A_FACTION, true); | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| void FlagComponent::ClearSessionFlags(tinyxml2::XMLDocument& doc) { | ||||
| 	if (!doc.FirstChildElement("obj")) return; | ||||
| 	auto& obj = *doc.FirstChildElement("obj"); | ||||
|  | ||||
| 	if (!obj.FirstChildElement("flag")) return; | ||||
| 	auto& flags = *obj.FirstChildElement("flag"); | ||||
|  | ||||
| 	auto* currentChild = flags.FirstChildElement(); | ||||
| 	while (currentChild) { | ||||
| 		auto* nextChild = currentChild->NextSiblingElement(); | ||||
| 		if (currentChild->Attribute("si")) { | ||||
| 			flags.DeleteChild(currentChild); | ||||
| 		} | ||||
| 		currentChild = nextChild; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										59
									
								
								dGame/dComponents/FlagComponent.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								dGame/dComponents/FlagComponent.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| #ifndef FLAGCOMPONENT_H | ||||
| #define FLAGCOMPONENT_H | ||||
|  | ||||
| #include <cstdint> | ||||
| #include <set> | ||||
| #include <unordered_map> | ||||
|  | ||||
| #include "Component.h" | ||||
| #include "eReplicaComponentType.h" | ||||
|  | ||||
| class FlagComponent final : public Component { | ||||
| public: | ||||
| 	static const inline eReplicaComponentType ComponentType = eReplicaComponentType::FLAG; | ||||
| 	FlagComponent(Entity* parent); | ||||
|  | ||||
| 	void UpdateXml(tinyxml2::XMLDocument& doc) override; | ||||
| 	void LoadFromXml(const tinyxml2::XMLDocument& doc) override; | ||||
|  | ||||
| 	// Used to clear the save data from a static context where you only have a doc (switching characters) | ||||
| 	static void ClearSessionFlags(tinyxml2::XMLDocument& doc); | ||||
| private: | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets a flag for the character, indicating certain parts of the game that have been interacted with. Not to be | ||||
| 	 * confused with the permissions | ||||
| 	 * @param flagId the ID of the flag to set | ||||
| 	 * @param value the value to set for the flag | ||||
| 	 */ | ||||
| 	bool OnSetFlag(GameMessages::GameMsg& msg); | ||||
| 	void SetPlayerFlag(const uint32_t flagId, const bool value); | ||||
|  | ||||
| 	/** | ||||
| 	 * Gets the value for a certain character flag | ||||
| 	 * @param flagId the ID of the flag to get a value for | ||||
| 	 * @return the value of the flag given the ID (the default is false, obviously) | ||||
| 	 */ | ||||
| 	bool OnGetFlag(GameMessages::GameMsg& msg); | ||||
| 	bool GetPlayerFlag(const uint32_t flagId) const; | ||||
|  | ||||
| 	bool OnClearSessionFlags(GameMessages::GameMsg& msg) { m_SessionFlags.clear(); return true; } | ||||
|  | ||||
| 	/** | ||||
| 	 * Sets any flags that are meant to have been set that may not have been set due to them being | ||||
| 	 * missing in a previous patch. | ||||
| 	 */ | ||||
| 	bool OnSetRetroactiveFlags(GameMessages::GameMsg& msg); | ||||
| 	/** | ||||
| 	 * Flags only set for the duration of a session | ||||
| 	 * | ||||
| 	 */ | ||||
| 	std::set<uint32_t> m_SessionFlags; | ||||
|  | ||||
| 	/** | ||||
| 	 * The gameplay flags this character has (not just true values) | ||||
| 	 */ | ||||
| 	std::unordered_map<uint32_t, uint64_t> m_PlayerFlags; | ||||
| }; | ||||
|  | ||||
| #endif  //!FLAGCOMPONENT_H | ||||
| @@ -557,7 +557,11 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { | ||||
|  | ||||
| 	// Triggers the catch a pet missions | ||||
| 	if (petFlags.find(m_Parent->GetLOT()) != petFlags.end()) { | ||||
| 		tamer->GetCharacter()->SetPlayerFlag(petFlags.at(m_Parent->GetLOT()), true); | ||||
| 		GameMessages::SetFlag setFlag{}; | ||||
| 		setFlag.target = tamer->GetObjectID(); | ||||
| 		setFlag.iFlagId = petFlags.at(m_Parent->GetLOT()); | ||||
| 		setFlag.bFlag = true; | ||||
| 		SEND_ENTITY_MSG(setFlag); | ||||
| 	} | ||||
|  | ||||
| 	auto* missionComponent = tamer->GetComponent<MissionComponent>(); | ||||
| @@ -847,7 +851,11 @@ void PetComponent::Activate(Item* item, bool registerPet, bool fromTaming) { | ||||
|  | ||||
| 	Game::entityManager->SerializeEntity(m_Parent); | ||||
|  | ||||
| 	owner->GetCharacter()->SetPlayerFlag(ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE, true); | ||||
| 	GameMessages::SetFlag setFlag{}; | ||||
| 	setFlag.target = owner->GetObjectID(); | ||||
| 	setFlag.iFlagId = ePlayerFlag::FIRST_MANUAL_PET_HIBERNATE; | ||||
| 	setFlag.bFlag = true; | ||||
| 	SEND_ENTITY_MSG(setFlag); | ||||
|  | ||||
| 	if (registerPet) { | ||||
| 		GameMessages::SendAddPetToPlayer(m_Owner, 0, GeneralUtils::UTF8ToUTF16(m_Name), m_DatabaseId, m_Parent->GetLOT(), owner->GetSystemAddress()); | ||||
|   | ||||
| @@ -481,14 +481,12 @@ void QuickBuildComponent::CompleteQuickBuild(Entity* const user) { | ||||
| 	} | ||||
|  | ||||
| 	// Set flag | ||||
| 	auto* character = user->GetCharacter(); | ||||
|  | ||||
| 	if (character != nullptr) { | ||||
| 		const auto flagNumber = m_Parent->GetVar<int32_t>(u"quickbuild_single_build_player_flag"); | ||||
|  | ||||
| 		if (flagNumber != 0) { | ||||
| 			character->SetPlayerFlag(flagNumber, true); | ||||
| 		} | ||||
| 	if (m_Parent->HasVar(u"quickbuild_single_build_player_flag")) { | ||||
| 		GameMessages::SetFlag setFlag{}; | ||||
| 		setFlag.target = user->GetObjectID(); | ||||
| 		setFlag.iFlagId = m_Parent->GetVar<int32_t>(u"quickbuild_single_build_player_flag"); | ||||
| 		setFlag.bFlag = true; | ||||
| 		SEND_ENTITY_MSG(setFlag); | ||||
| 	} | ||||
| 	RenderComponent::PlayAnimation(user, u"rebuild-celebrate", 1.09f); | ||||
| } | ||||
|   | ||||
| @@ -47,6 +47,7 @@ namespace { | ||||
| 	std::map<MessageType::Game, MessageCreator> g_MessageHandlers = { | ||||
| 		{ REQUEST_SERVER_OBJECT_INFO, []() { return std::make_unique<RequestServerObjectInfo>(); } }, | ||||
| 		{ SHOOTING_GALLERY_FIRE, []() { return std::make_unique<ShootingGalleryFire>(); } }, | ||||
| 		{ SET_FLAG, []() { return std::make_unique<SetFlag>(); } }, | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| @@ -123,11 +124,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	case MessageType::Game::SET_FLAG: { | ||||
| 		GameMessages::HandleSetFlag(inStream, entity); | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	case MessageType::Game::HAS_BEEN_COLLECTED: { | ||||
| 		GameMessages::HandleHasBeenCollected(inStream, entity); | ||||
| 		break; | ||||
| @@ -215,13 +211,17 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream& inStream, const System | ||||
| 		// After we've done our thing, tell the client they're ready | ||||
| 		GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr); | ||||
|  | ||||
| 		if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" | ||||
| 			|| !entity->GetCharacter() | ||||
| 			|| !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; | ||||
| 		entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { | ||||
| 			if (!entity) return; | ||||
| 			GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr); | ||||
| 			}); | ||||
| 		GameMessages::GetFlag getFlag{}; | ||||
| 		getFlag.target = entity->GetObjectID(); | ||||
| 		getFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS; | ||||
| 		SEND_ENTITY_MSG(getFlag); | ||||
|  | ||||
| 		if (Game::config->GetValue("allow_players_to_skip_cinematics") == "1" && getFlag.bFlag) { | ||||
| 			entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { | ||||
| 				if (!entity) return; | ||||
| 				GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr); | ||||
| 				}); | ||||
| 		} | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -5076,23 +5076,6 @@ void GameMessages::HandleModularBuildConvertModel(RakNet::BitStream& inStream, E | ||||
| 	item->SetCount(item->GetCount() - 1, false, false, true, eLootSourceType::QUICKBUILD); | ||||
| } | ||||
|  | ||||
| void GameMessages::HandleSetFlag(RakNet::BitStream& inStream, Entity* entity) { | ||||
| 	bool bFlag{}; | ||||
| 	int32_t iFlagID{}; | ||||
|  | ||||
| 	inStream.Read(bFlag); | ||||
| 	inStream.Read(iFlagID); | ||||
|  | ||||
| 	auto character = entity->GetCharacter(); | ||||
| 	if (character) character->SetPlayerFlag(iFlagID, bFlag); | ||||
|  | ||||
| 	// This is always set the first time a player loads into a world from character select | ||||
| 	// and is used to know when to refresh the players inventory items so they show up. | ||||
| 	if (iFlagID == ePlayerFlag::IS_NEWS_SCREEN_VISIBLE && bFlag) { | ||||
| 		entity->SetVar<bool>(u"dlu_first_time_load", true); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void GameMessages::HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity) { | ||||
| 	int missionID{}; | ||||
| 	LWOOBJID playerID{}; | ||||
| @@ -5157,13 +5140,17 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* en | ||||
| 		missionComponent->CompleteMission(missionID); | ||||
| 	} | ||||
|  | ||||
| 	if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" | ||||
| 		|| !player->GetCharacter() | ||||
| 		|| !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; | ||||
| 	player->AddCallbackTimer(0.5f, [player]() { | ||||
| 		if (!player) return; | ||||
| 		GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress()); | ||||
| 		}); | ||||
| 	GameMessages::GetFlag getFlag{}; | ||||
| 	getFlag.target = entity->GetObjectID(); | ||||
| 	getFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS; | ||||
| 	SEND_ENTITY_MSG(getFlag); | ||||
|  | ||||
| 	if (Game::config->GetValue("allow_players_to_skip_cinematics") == "1" && getFlag.bFlag) { | ||||
| 		player->AddCallbackTimer(0.5f, [player]() { | ||||
| 			if (!player) return; | ||||
| 			GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress()); | ||||
| 			}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void GameMessages::HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity) { | ||||
| @@ -6443,4 +6430,14 @@ namespace GameMessages { | ||||
| 		auto* handlingEntity = Game::entityManager->GetEntity(targetForReport); | ||||
| 		if (handlingEntity) handlingEntity->HandleMsg(*this); | ||||
| 	} | ||||
|  | ||||
| 	bool SetFlag::Deserialize(RakNet::BitStream& bitStream) { | ||||
| 		if (!bitStream.Read(bFlag)) return false; | ||||
| 		if (!bitStream.Read(iFlagId)) return false; | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	void SetFlag::Handle(Entity& entity, const SystemAddress& sysAddr) { | ||||
| 		entity.HandleMsg(*this); | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -634,7 +634,6 @@ namespace GameMessages { | ||||
| 	void HandleRequestUse(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); | ||||
| 	void HandlePlayEmote(RakNet::BitStream& inStream, Entity* entity); | ||||
| 	void HandleModularBuildConvertModel(RakNet::BitStream& inStream, Entity* entity, const SystemAddress& sysAddr); | ||||
| 	void HandleSetFlag(RakNet::BitStream& inStream, Entity* entity); | ||||
| 	void HandleRespondToMission(RakNet::BitStream& inStream, Entity* entity); | ||||
| 	void HandleMissionDialogOK(RakNet::BitStream& inStream, Entity* entity); | ||||
| 	void HandleRequestLinkedMission(RakNet::BitStream& inStream, Entity* entity); | ||||
| @@ -782,6 +781,30 @@ namespace GameMessages { | ||||
| 		bool Deserialize(RakNet::BitStream& bitStream) override; | ||||
| 		void Handle(Entity& entity, const SystemAddress& sysAddr) override; | ||||
| 	}; | ||||
|  | ||||
| 	struct SetFlag : public GameMsg { | ||||
| 		SetFlag() : GameMsg(MessageType::Game::SET_FLAG) {} | ||||
| 		bool Deserialize(RakNet::BitStream& bitStream) override; | ||||
| 		void Handle(Entity& entity, const SystemAddress& sysAddr) override; | ||||
|  | ||||
| 		uint32_t iFlagId{}; | ||||
| 		bool bFlag{}; | ||||
| 	}; | ||||
|  | ||||
| 	struct GetFlag : public GameMsg { | ||||
| 		GetFlag() : GameMsg(MessageType::Game::GET_FLAG) {} | ||||
|  | ||||
| 		uint32_t iFlagId{}; | ||||
| 		bool bFlag{}; | ||||
| 	}; | ||||
|  | ||||
| 	struct ClearSessionFlags : public GameMsg { | ||||
| 		ClearSessionFlags() : GameMsg(MessageType::Game::CLEAR_SESSION_FLAGS) {} | ||||
| 	}; | ||||
|  | ||||
| 	struct SetRetroactiveFlags : public GameMsg { | ||||
| 		SetRetroactiveFlags() : GameMsg(MessageType::Game::SET_RETROACTIVE_FLAGS) {} | ||||
| 	}; | ||||
| }; | ||||
|  | ||||
| #endif // GAMEMESSAGES_H | ||||
|   | ||||
| @@ -390,10 +390,15 @@ void Mission::Catchup() { | ||||
| 		} | ||||
|  | ||||
| 		if (type == eMissionTaskType::PLAYER_FLAG) { | ||||
| 			for (int32_t target : task->GetAllTargets()) { | ||||
| 				const auto flag = GetCharacter()->GetPlayerFlag(target); | ||||
| 			GameMessages::GetFlag getFlag{}; | ||||
| 			getFlag.target = entity->GetObjectID(); | ||||
|  | ||||
| 				if (!flag) { | ||||
| 			for (int32_t target : task->GetAllTargets()) { | ||||
| 				getFlag.iFlagId = target; | ||||
| 				getFlag.bFlag = false; | ||||
| 				SEND_ENTITY_MSG(getFlag); | ||||
|  | ||||
| 				if (!getFlag.bFlag) { | ||||
| 					continue; | ||||
| 				} | ||||
|  | ||||
|   | ||||
| @@ -146,17 +146,22 @@ bool Precondition::CheckValue(Entity* player, const uint32_t value, bool evaluat | ||||
| 		return missionComponent->GetMissionState(value) == eMissionState::AVAILABLE || missionComponent->GetMissionState(value) == eMissionState::COMPLETE_AVAILABLE; | ||||
| 	case PreconditionType::OnMission: | ||||
| 		if (missionComponent == nullptr) return false; | ||||
| 		return  missionComponent->GetMissionState(value) == eMissionState::ACTIVE ||  | ||||
| 				missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE || | ||||
| 				missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE || | ||||
| 				missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE; | ||||
| 		return  missionComponent->GetMissionState(value) == eMissionState::ACTIVE || | ||||
| 			missionComponent->GetMissionState(value) == eMissionState::COMPLETE_ACTIVE || | ||||
| 			missionComponent->GetMissionState(value) == eMissionState::READY_TO_COMPLETE || | ||||
| 			missionComponent->GetMissionState(value) == eMissionState::COMPLETE_READY_TO_COMPLETE; | ||||
| 	case PreconditionType::MissionComplete: | ||||
| 		if (missionComponent == nullptr) return false; | ||||
| 		return missionComponent->GetMissionState(value) >= eMissionState::COMPLETE; | ||||
| 	case PreconditionType::PetDeployed: | ||||
| 		return false; // TODO | ||||
| 	case PreconditionType::HasFlag: | ||||
| 		return character->GetPlayerFlag(value); | ||||
| 	case PreconditionType::HasFlag: { | ||||
| 		GameMessages::GetFlag getFlag{}; | ||||
| 		getFlag.target = player->GetObjectID(); | ||||
| 		getFlag.iFlagId = value; | ||||
| 		SEND_ENTITY_MSG(getFlag); | ||||
| 		return getFlag.bFlag; | ||||
| 	} | ||||
| 	case PreconditionType::WithinShape: | ||||
| 		return true; // Client checks this one | ||||
| 	case PreconditionType::InBuild: | ||||
|   | ||||
| @@ -108,11 +108,18 @@ namespace DEVGMCommands { | ||||
|  | ||||
| 	void ToggleSkipCinematics(Entity* entity, const SystemAddress& sysAddr, const std::string args) { | ||||
| 		if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" && entity->GetGMLevel() < eGameMasterLevel::DEVELOPER) return; | ||||
| 		auto* character = entity->GetCharacter(); | ||||
| 		if (!character) return; | ||||
| 		bool current = character->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS); | ||||
| 		character->SetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS, !current); | ||||
| 		if (!current) { | ||||
| 		GameMessages::GetFlag current{}; | ||||
| 		current.target = entity->GetObjectID(); | ||||
| 		current.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS; | ||||
| 		SEND_ENTITY_MSG(current); | ||||
|  | ||||
| 		GameMessages::SetFlag setFlag{}; | ||||
| 		setFlag.target = entity->GetObjectID(); | ||||
| 		setFlag.iFlagId = ePlayerFlag::DLU_SKIP_CINEMATICS; | ||||
| 		setFlag.bFlag = !current.bFlag; | ||||
| 		SEND_ENTITY_MSG(setFlag); | ||||
|  | ||||
| 		if (!current.bFlag) { | ||||
| 			GameMessages::SendSlashCommandFeedbackText(entity, u"You have elected to skip cinematics. Note that not all cinematics can be skipped, but most will be skipped now."); | ||||
| 		} else { | ||||
| 			GameMessages::SendSlashCommandFeedbackText(entity, u"Cinematics will no longer be skipped."); | ||||
| @@ -424,6 +431,9 @@ namespace DEVGMCommands { | ||||
|  | ||||
| 	void SetFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { | ||||
| 		const auto splitArgs = GeneralUtils::SplitString(args, ' '); | ||||
|  | ||||
| 		GameMessages::SetFlag setFlag{}; | ||||
| 		setFlag.target = entity->GetObjectID(); | ||||
| 		if (splitArgs.size() == 1) { | ||||
| 			const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(0)); | ||||
|  | ||||
| @@ -431,8 +441,9 @@ namespace DEVGMCommands { | ||||
| 				ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag id."); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			entity->GetCharacter()->SetPlayerFlag(flagId.value(), true); | ||||
| 			setFlag.iFlagId = flagId.value(); | ||||
| 			setFlag.bFlag = true; | ||||
| 			SEND_ENTITY_MSG(setFlag); | ||||
| 		} else if (splitArgs.size() >= 2) { | ||||
| 			const auto flagId = GeneralUtils::TryParse<int32_t>(splitArgs.at(1)); | ||||
| 			std::string onOffFlag = splitArgs.at(0); | ||||
| @@ -445,12 +456,17 @@ namespace DEVGMCommands { | ||||
| 				ChatPackets::SendSystemMessage(sysAddr, u"Invalid flag type."); | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			entity->GetCharacter()->SetPlayerFlag(flagId.value(), onOffFlag == "on"); | ||||
| 			setFlag.iFlagId = flagId.value(); | ||||
| 			setFlag.bFlag = onOffFlag == "on"; | ||||
| 			SEND_ENTITY_MSG(setFlag); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	void ClearFlag(Entity* entity, const SystemAddress& sysAddr, const std::string args) { | ||||
| 		GameMessages::SetFlag setFlag{}; | ||||
| 		setFlag.target = entity->GetObjectID(); | ||||
| 		setFlag.bFlag = false; | ||||
|  | ||||
| 		const auto splitArgs = GeneralUtils::SplitString(args, ' '); | ||||
| 		if (splitArgs.empty()) return; | ||||
|  | ||||
| @@ -461,7 +477,8 @@ namespace DEVGMCommands { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		entity->GetCharacter()->SetPlayerFlag(flagId.value(), false); | ||||
| 		setFlag.iFlagId = flagId.value(); | ||||
| 		SEND_ENTITY_MSG(setFlag); | ||||
| 	} | ||||
|  | ||||
| 	void PlayEffect(Entity* entity, const SystemAddress& sysAddr, const std::string args) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 David Markowitz
					David Markowitz