Proper Rocket Holding

Sanity checks on Prop and LUP launchpads to not open if no valid rocket
Add serialization for sending item configs
so that rockets show for other players
This commit is contained in:
Aaron Kimbre 2022-05-08 19:57:36 -05:00
parent 24745c2e7a
commit ec207838d4
10 changed files with 156 additions and 132 deletions

View File

@ -13,6 +13,7 @@
#include "PossessorComponent.h"
#include "VehiclePhysicsComponent.h"
#include "GameMessages.h"
#include "Item.h"
CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) {
m_Character = character;
@ -448,6 +449,56 @@ void CharacterComponent::SetLastRocketConfig(std::u16string config) {
m_LastRocketConfig = config;
}
Item* CharacterComponent::GetRocket(Entity* player) {
Item* rocket = nullptr;
auto* inventoryComponent = player->GetComponent<InventoryComponent>();
if (!inventoryComponent) return rocket;
// Select the rocket
if (!rocket){
rocket = inventoryComponent->FindItemById(GetLastRocketItemID());
}
if (!rocket) {
rocket = inventoryComponent->FindItemByLot(6416);
}
if (!rocket) {
Game::logger->Log("CharacterComponent", "Unable to find rocket to equip!\n");
return rocket;
}
return rocket;
}
Item* CharacterComponent::RocketEquip(Entity* player) {
Item* rocket = GetRocket(player);
if (!rocket) return rocket;
// build and define the rocket config
for (LDFBaseData* data : rocket->GetConfig()) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr = data->GetValueAsString() + ";";
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
}
}
// Store the last used rocket item's ID
SetLastRocketItemID(rocket->GetId());
// carry the rocket
rocket->Equip(true);
return rocket;
}
void CharacterComponent::RocketUnEquip(Entity* player) {
Item* rocket = GetRocket(player);
if (!rocket) return;
// We don't want to carry it anymore
rocket->UnEquip();
}
void CharacterComponent::TrackMissionCompletion(bool isAchievement) {
UpdatePlayerStatistic(MissionsCompleted);

View File

@ -5,6 +5,7 @@
#include "RakNetTypes.h"
#include "Character.h"
#include "Component.h"
#include "Item.h"
#include <string>
#include "CDMissionsTable.h"
#include "tinyxml2.h"
@ -74,6 +75,26 @@ public:
*/
void SetLastRocketConfig(std::u16string config);
/**
* Find a player's rocket
* @param player the entity that triggered the event
* @return rocket
*/
Item* GetRocket(Entity* player);
/**
* Equip a player's rocket
* @param player the entity that triggered the event
* @return rocket
*/
Item* RocketEquip(Entity* player);
/**
* Find a player's rocket and unequip it
* @param player the entity that triggered the event
*/
void RocketUnEquip(Entity* player);
/**
* Gets the current level of the entity
* @return the current level of the entity

View File

@ -1,4 +1,4 @@
#include "InventoryComponent.h"
#include "InventoryComponent.h"
#include <sstream>
@ -794,10 +794,29 @@ void InventoryComponent::Serialize(RakNet::BitStream* outBitStream, const bool b
outBitStream->Write(item.slot != 0);
if (item.slot != 0) outBitStream->Write<uint16_t>(item.slot);
outBitStream->Write0();
outBitStream->Write0(); //TODO: This is supposed to be true and write the assemblyPartLOTs when they're present.
bool flag = !item.config.empty();
outBitStream->Write(flag);
if (flag) {
RakNet::BitStream ldfStream;
ldfStream.Write<int32_t>(item.config.size()); // Key count
for (LDFBaseData* data : item.config) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr = data->GetValueAsString() + ";";
GeneralUtils::ReplaceInString(newRocketStr, "+", ";");
LDFData<std::u16string>* ldf_data = new LDFData<std::u16string>(u"assemblyPartLOTs", GeneralUtils::ASCIIToUTF16(newRocketStr));
ldf_data->WriteToPacket(&ldfStream);
delete ldf_data;
} else {
data->WriteToPacket(&ldfStream);
}
}
outBitStream->Write(ldfStream.GetNumberOfBytesUsed() + 1);
outBitStream->Write<uint8_t>(0); // Don't compress
outBitStream->Write(ldfStream);
}
outBitStream->Write1();
}
@ -1043,7 +1062,7 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks)
GenerateProxies(item);
UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot() });
UpdateSlot(item->GetInfo().equipLocation, { item->GetId(), item->GetLot(), item->GetCount(), item->GetSlot(), item->GetConfig() });
ApplyBuff(item);

View File

@ -1,4 +1,4 @@
#include "PropertyEntranceComponent.h"
#include "PropertyEntranceComponent.h"
#include <CDPropertyEntranceComponentTable.h>
@ -8,6 +8,7 @@
#include "PropertyManagementComponent.h"
#include "PropertySelectQueryProperty.h"
#include "RocketLaunchpadControlComponent.h"
#include "CharacterComponent.h"
#include "UserManager.h"
#include "dLogger.h"
@ -22,20 +23,25 @@ PropertyEntranceComponent::PropertyEntranceComponent(uint32_t componentID, Entit
this->m_PropertyName = entry.propertyName;
}
void PropertyEntranceComponent::OnUse(Entity* entity)
{
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
void PropertyEntranceComponent::OnUse(Entity* entity) {
auto* characterComponent = entity->GetComponent<CharacterComponent>();
if (!characterComponent) return;
AMFArrayValue args;
auto* rocket = entity->GetComponent<CharacterComponent>()->RocketEquip(entity);
if (!rocket) return;
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), entity->GetSystemAddress());
AMFArrayValue args;
auto* state = new AMFStringValue();
state->SetStringValue("property_menu");
args.InsertValue("state", state);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
GameMessages::SendUIMessageServerToSingleClient(entity, entity->GetSystemAddress(), "pushGameState", &args);
delete state;
delete state;
}
void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index, bool returnToZone, const SystemAddress& sysAddr)
@ -75,7 +81,7 @@ void PropertyEntranceComponent::OnEnterProperty(Entity* entity, uint32_t index,
launcher->SetSelectedCloneId(entity->GetObjectID(), cloneId);
launcher->Launch(entity, LWOOBJID_EMPTY, launcher->GetTargetZone(), cloneId);
launcher->Launch(entity, launcher->GetTargetZone(), cloneId);
}
PropertySelectQueryProperty PropertyEntranceComponent::SetPropertyValues(PropertySelectQueryProperty property, LWOCLONEID cloneId, std::string ownerName, std::string propertyName, std::string propertyDescription, float reputation, bool isBFF, bool isFriend, bool isModeratorApproved, bool isAlt, bool isOwned, uint32_t privacyOption, uint32_t timeLastUpdated, float performanceCost) {

View File

@ -1,8 +1,8 @@
#include "RocketLaunchLupComponent.h"
#include "CDClientDatabase.h"
#include "RocketLaunchpadControlComponent.h"
#include "InventoryComponent.h"
#include "CharacterComponent.h"
#include "Item.h"
RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(parent) {
m_Parent = parent;
@ -21,20 +21,11 @@ RocketLaunchLupComponent::RocketLaunchLupComponent(Entity* parent) : Component(p
RocketLaunchLupComponent::~RocketLaunchLupComponent() {}
void RocketLaunchLupComponent::OnUse(Entity* originator) {
// the LUP world menu is just the property menu, the client knows how to handle it
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress());
// get the rocket to "equip" it so we are holding it
// taken from the RocketLaunchControlComponent
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
auto* characterComponent = originator->GetComponent<CharacterComponent>();
if (!inventoryComponent || !characterComponent) return;
Item* rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID());
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) return;
rocket->Equip(true);
// the LUP world menu is just the property menu, the client knows how to handle it
GameMessages::SendPropertyEntranceBegin(m_Parent->GetObjectID(), m_Parent->GetSystemAddress());
}
void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index) {
@ -45,5 +36,5 @@ void RocketLaunchLupComponent::OnSelectWorld(Entity* originator, uint32_t index)
if (!rocketLaunchpadControlComponent) return;
rocketLaunchpadControlComponent->Launch(originator, LWOOBJID_EMPTY, m_LUPWorlds[index], 0);
rocketLaunchpadControlComponent->Launch(originator, m_LUPWorlds[index], 0);
}

View File

@ -13,6 +13,7 @@
#include "ChatPackets.h"
#include "MissionComponent.h"
#include "PropertyEntranceComponent.h"
#include "RocketLaunchLupComponent.h"
#include "dServer.h"
#include "dMessageIdentifiers.h"
#include "PacketUtils.h"
@ -40,19 +41,7 @@ RocketLaunchpadControlComponent::~RocketLaunchpadControlComponent() {
delete m_AltPrecondition;
}
void RocketLaunchpadControlComponent::RocketEquip(Entity* entity, LWOOBJID rocketID) {
if (m_PlayersInRadius.find(entity->GetObjectID()) != m_PlayersInRadius.end()) {
Launch(entity, rocketID);
//Go ahead and save the player
//This causes a double-save, but it should prevent players from not being saved
//before the next world server starts loading their data.
if (entity->GetCharacter())
entity->GetCharacter()->SaveXMLToDatabase();
}
}
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID optionalRocketID, LWOMAPID mapId, LWOCLONEID cloneId) {
void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOMAPID mapId, LWOCLONEID cloneId) {
auto zone = mapId == LWOMAPID_INVALID ? m_TargetZone : mapId;
if (zone == 0)
@ -60,53 +49,22 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
return;
}
TellMasterToPrepZone(zone);
// This also gets triggered by a proximity monitor + item equip, I will set that up when havok is ready
auto* inventoryComponent = originator->GetComponent<InventoryComponent>();
auto* characterComponent = originator->GetComponent<CharacterComponent>();
auto* character = originator->GetCharacter();
if (inventoryComponent == nullptr || characterComponent == nullptr || character == nullptr) {
if (!characterComponent || !character) return;
auto* rocket = characterComponent->GetRocket(originator);
if (!rocket) {
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket!\n");
return;
}
// Select the rocket
Item* rocket = nullptr;
if (optionalRocketID != LWOOBJID_EMPTY)
{
rocket = inventoryComponent->FindItemById(optionalRocketID);
}
if (rocket == nullptr)
{
rocket = inventoryComponent->FindItemById(characterComponent->GetLastRocketItemID());
}
if (rocket == nullptr)
{
rocket = inventoryComponent->FindItemByLot(6416);
}
if (rocket == nullptr)
{
Game::logger->Log("RocketLaunchpadControlComponent", "Unable to find rocket (%llu)!\n", optionalRocketID);
return;
}
if (rocket->GetConfig().empty()) // Sanity check
{
rocket->SetCount(0, false, false);
return;
}
// we have the ability to launch, so now we prep the zone
TellMasterToPrepZone(zone);
// Achievement unlocked: "All zones unlocked"
if (!m_AltLandingScene.empty() && m_AltPrecondition->Check(originator)) {
character->SetTargetScene(m_AltLandingScene);
}
@ -114,27 +72,6 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
character->SetTargetScene(m_TargetScene);
}
if (characterComponent) {
for (LDFBaseData* data : rocket->GetConfig()) {
if (data->GetKey() == u"assemblyPartLOTs") {
std::string newRocketStr;
for (char character : data->GetValueAsString()) {
if (character == '+') {
newRocketStr.push_back(';');
}
else {
newRocketStr.push_back(character);
}
}
newRocketStr.push_back(';');
characterComponent->SetLastRocketConfig(GeneralUtils::ASCIIToUTF16(newRocketStr));
}
}
}
// Store the last used rocket item's ID
characterComponent->SetLastRocketItemID(rocket->GetId());
characterComponent->UpdatePlayerStatistic(RocketsUsed);
character->SaveXMLToDatabase();
@ -143,23 +80,31 @@ void RocketLaunchpadControlComponent::Launch(Entity* originator, LWOOBJID option
GameMessages::SendFireEventClientSide(m_Parent->GetObjectID(), originator->GetSystemAddress(), u"RocketEquipped", rocket->GetId(), cloneId, -1, originator->GetObjectID());
rocket->Equip(true);
GameMessages::SendChangeObjectWorldState(rocket->GetId(), WORLDSTATE_ATTACHED, UNASSIGNED_SYSTEM_ADDRESS);
EntityManager::Instance()->SerializeEntity(originator);
}
void RocketLaunchpadControlComponent::OnUse(Entity* originator) {
// If we are have the property or the LUP component, we don't want to immediately launch
// instead we let their OnUse handlers do their things
// which components of an Object have their OnUse called when using them
// so we don't need to call it here
auto* propertyEntrance = m_Parent->GetComponent<PropertyEntranceComponent>();
if (propertyEntrance != nullptr)
{
propertyEntrance->OnUse(originator);
if (propertyEntrance) {
return;
}
auto* rocketLaunchLUP = m_Parent->GetComponent<RocketLaunchLupComponent>();
if (rocketLaunchLUP) {
return;
}
// No rocket no launch
auto* rocket = originator->GetComponent<CharacterComponent>()->RocketEquip(originator);
if (!rocket) {
return;
}
Launch(originator);
}

View File

@ -22,21 +22,13 @@ public:
RocketLaunchpadControlComponent(Entity* parent, int rocketId);
~RocketLaunchpadControlComponent() override;
/**
* Launches the passed entity using the passed rocket and saves their data
* @param entity the entity to launch
* @param rocketID the ID of the rocket to use
*/
void RocketEquip(Entity* entity, LWOOBJID rocketID);
/**
* Launches some entity to another world
* @param originator the entity to launch
* @param optionalRocketID the ID of the rocket to launch
* @param mapId the world to go to
* @param cloneId the clone ID (for properties)
*/
void Launch(Entity* originator, LWOOBJID optionalRocketID = LWOOBJID_EMPTY, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
void Launch(Entity* originator, LWOMAPID mapId = LWOMAPID_INVALID, LWOCLONEID cloneId = LWOCLONEID_INVALID);
/**
* Handles an OnUse event from some entity, preparing it for launch to some other world

View File

@ -5329,21 +5329,11 @@ void GameMessages::HandleEquipItem(RakNet::BitStream* inStream, Entity* entity)
Item* item = inv->FindItemById(objectID);
if (!item) return;
/*if (item->GetLot() == 6416) { // if it's a rocket
std::vector<Entity*> rocketPads = EntityManager::Instance()->GetEntitiesByComponent(COMPONENT_TYPE_ROCKET_LAUNCH);
for (Entity* rocketPad : rocketPads) {
RocketLaunchpadControlComponent* rocketComp = static_cast<RocketLaunchpadControlComponent*>(rocketPad->GetComponent(COMPONENT_TYPE_ROCKET_LAUNCH));
if (rocketComp) {
rocketComp->RocketEquip(entity, objectID);
}
}
}
else*/ {
item->Equip();
EntityManager::Instance()->SerializeEntity(entity);
}
}
void GameMessages::HandleUnequipItem(RakNet::BitStream* inStream, Entity* entity) {
bool immediate;

View File

@ -1,6 +1,7 @@
#pragma once
#pragma once
#include "dCommonVars.h"
#include "LDFFormat.h"
/**
* An item that's equipped, generally as a smaller return type than the regular Item class
@ -26,4 +27,9 @@ struct EquippedItem
* The slot this item is stored in
*/
uint32_t slot = 0;
/**
* The configuration of the item with any extra data
*/
std::vector<LDFBaseData*> config = {};
};

View File

@ -969,13 +969,16 @@ void HandlePacket(Packet* packet) {
{
GameMessages::SendPlayerReachedRespawnCheckpoint(player, respawnPoint, NiQuaternion::IDENTITY);
}
EntityManager::Instance()->ConstructAllEntities(packet->systemAddress);
player->GetComponent<CharacterComponent>()->SetLastRocketConfig(u"");
EntityManager::Instance()->ConstructAllEntities(packet->systemAddress);
auto* characterComponent = player->GetComponent<CharacterComponent>();
if (characterComponent) {
player->GetComponent<CharacterComponent>()->RocketUnEquip(player);
}
c->SetRetroactiveFlags();
player->RetroactiveVaultSize();
player->GetCharacter()->SetTargetScene("");