Files
DarkflameServer/dGame/dComponents/Property/PropertyVendorComponent.cpp
Aaron Kimbrell baf8b24ad1 fix: a property is rented only with the level's required mission
LWOPropertyVendorComponent::onCheckUseRequirements (0x00c2e210) lets a
player use the property guard only when the level's
property_required_mission (LoadConfigData 0x00c2e550) is finished. The
server opened the vendor and rented the property on any request.

The guard is now not opened without the mission, and a rent request
without it is answered with PropertyRentalResponse code 9, which
onPropertyRentalResponse (0x00c56d70) shows as "you failed to rent" with
UGG_PROPRENT_Dont_have_achievement. A mission that was finished and is
being repeated counts, as in the client's list of finished missions. A
guard without the key asks for nothing.

The six property maps name missions 768, 872, 873, 874, 1293 and 1322.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
2026-11-10 15:28:18 -06:00

97 lines
3.4 KiB
C++

#include "PropertyVendorComponent.h"
#include "ObjectMessages.h"
#include "PropertyMessages.h"
#include "GameMessages.h"
#include "Character.h"
#include "EntityManager.h"
#include "dZoneManager.h"
#include "Game.h"
#include "Logger.h"
#include "PropertyManagementComponent.h"
#include "UserManager.h"
#include "Mission.h"
#include "MissionComponent.h"
PropertyVendorComponent::PropertyVendorComponent(Entity* parent, const int32_t componentID) : Component(parent, componentID) {
RegisterMsg(&PropertyVendorComponent::OnRequestUse);
}
uint32_t PropertyVendorComponent::GetRequiredMission() const {
return m_Parent->GetVarAs<uint32_t>(u"property_required_mission");
}
bool PropertyVendorComponent::MayRent(Entity& player) const {
const auto required = GetRequiredMission();
if (required == 0) return true;
const auto* const missions = player.GetComponent<MissionComponent>();
const auto* const mission = missions ? missions->GetMission(required) : nullptr;
// The client asks its list of finished missions: one that was completed and is repeated counts as well
return mission != nullptr && (mission->IsComplete() || mission->GetCompletions() > 0);
}
bool PropertyVendorComponent::OnRequestUse(GameMessages::RequestUseEvent& event) {
if (event.msg.IsMissionChoice()) return false;
auto* const originator = event.user;
// LWOPropertyVendorComponent::onCheckUseRequirements (0x00c2e210): no use without the level's mission
if (!MayRent(*originator)) return true;
if (PropertyManagementComponent::Instance() == nullptr) return true;
OnQueryPropertyData(originator, originator->GetSystemAddress());
if (PropertyManagementComponent::Instance()->GetOwnerId() == LWOOBJID_EMPTY) {
LOG("Property vendor opening!");
GameMessages::OpenPropertyVendor msg;
msg.target = m_Parent->GetObjectID();
msg.Send(originator->GetSystemAddress());
return true;
}
return true;
}
void PropertyVendorComponent::OnQueryPropertyData(Entity* originator, const SystemAddress& sysAddr) {
if (PropertyManagementComponent::Instance() == nullptr) return;
PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, sysAddr, m_Parent->GetObjectID());
}
void PropertyVendorComponent::OnBuyFromVendor(Entity* originator, const bool confirmed, const LOT lot, const uint32_t count) {
if (!MayRent(*originator)) {
LOG("Player %llu rents a property without mission %u", originator->GetObjectID(), GetRequiredMission());
GameMessages::PropertyRentalResponse refused;
refused.target = m_Parent->GetObjectID();
refused.code = RENTAL_MISSING_ACHIEVEMENT;
refused.Send(originator->GetSystemAddress());
return;
}
if (PropertyManagementComponent::Instance() == nullptr) return;
if (PropertyManagementComponent::Instance()->Claim(originator->GetObjectID()) == false) {
LOG("FAILED TO CLAIM PROPERTY. PLAYER ID IS %llu", originator->GetObjectID());
return;
}
GameMessages::PropertyRentalResponse msg;
msg.target = m_Parent->GetObjectID();
msg.cloneid = 0;
msg.code = 0;
msg.propertyID = 0;
msg.rentdue = 0;
msg.Send(originator->GetSystemAddress());
auto* controller = Game::zoneManager->GetZoneControlObject();
controller->OnFireEventServerSide(m_Parent, "propertyRented");
PropertyManagementComponent::Instance()->SetOwner(originator);
PropertyManagementComponent::Instance()->OnQueryPropertyData(originator, originator->GetSystemAddress());
LOG("Fired event; (%d) (%i) (%i)", confirmed, lot, count);
}