mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-20 03:32:48 +00:00
feat(property): best friends of the owner can build on the property
property_bff_build (off by default, as live): while the owner is in build mode, their best friends can join it and place, move and pick up models, build brick by brick and edit behaviors, each from their own inventory. Only the owner starts build mode; a best friend still in it when the owner leaves keeps building until they leave it. The client lets only the property's owner edit, so each player now gets their own DownloadPropertyData, with their own id as the owner while they can build, sent again whenever that changes. SetBuildModeConfirmed and GetModelsOnProperty go to each player instead of everyone. The first builder makes the property private and pauses the models; the last one restores them. Saving no longer needs the owner in the world. A model taken off the property goes to whoever placed it; if they aren't here it stays placed. Privacy and the property's name stay owner only (neither was checked before). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -249,9 +249,9 @@ void BrickByBrick::Save(Entity& player, const LWOOBJID localId, const std::strin
|
||||
|
||||
auto* inventory = player.GetComponent<InventoryComponent>();
|
||||
auto* property = PropertyManagementComponent::Instance();
|
||||
// Brick by brick building happens on the player's own property; the build becomes models placed there
|
||||
if (!inventory || !property || property->GetOwnerId() != player.GetObjectID()) {
|
||||
LOG("Player %llu saved a brick by brick model but is not on their own property", player.GetObjectID());
|
||||
// Brick by brick building happens on a property the player can build on; the build becomes models placed there
|
||||
if (!inventory || !property || !property->CanBuild(player)) {
|
||||
LOG("Player %llu saved a brick by brick model but can't build on this property", player.GetObjectID());
|
||||
response.reasonCode = eBlueprintSaveResponseType::PlacementFailed;
|
||||
response.Send(player.GetSystemAddress());
|
||||
return;
|
||||
@@ -285,7 +285,7 @@ void BrickByBrick::Save(Entity& player, const LWOOBJID localId, const std::strin
|
||||
for (const auto& model : models) {
|
||||
LwoNameValue config;
|
||||
config.Insert<LWOOBJID>(u"blueprintid", model.blueprintId);
|
||||
property->SpawnModel(MODEL_OBJECT_LOT, model.modelId, model.center, QuatUtils::IDENTITY, config);
|
||||
property->SpawnModel(MODEL_OBJECT_LOT, model.modelId, model.center, QuatUtils::IDENTITY, config, player.GetObjectID());
|
||||
}
|
||||
property->Save();
|
||||
DashboardNotify::Changed("properties", property->GetId());
|
||||
@@ -315,8 +315,8 @@ void BrickByBrick::ReturnModel(Entity& player, const LWOOBJID itemId, const bool
|
||||
auto* item = inventory->FindItemById(itemId);
|
||||
if (item && item->GetInventory()->GetType() == eInventoryType::MODELS_IN_BBB) {
|
||||
auto* property = PropertyManagementComponent::Instance();
|
||||
if (hasWorldTransform && property && property->GetOwnerId() == player.GetObjectID()) {
|
||||
property->PlaceModelFromItem(*item, position, rotation);
|
||||
if (hasWorldTransform && property && property->CanBuild(player)) {
|
||||
property->PlaceModelFromItem(player, *item, position, rotation);
|
||||
property->SendModelsOnProperty();
|
||||
} else {
|
||||
MoveKeepingId(*inventory, item, eInventoryType::MODELS);
|
||||
|
||||
@@ -8,6 +8,7 @@ set(DGAME_DUTILITIES_SOURCES "BrickDatabase.cpp"
|
||||
"GUID.cpp"
|
||||
"LiveEvents.cpp"
|
||||
"Contraband.cpp"
|
||||
"PropertyBuilders.cpp"
|
||||
"PropertyRent.cpp"
|
||||
"PropertyReputation.cpp"
|
||||
"Loot.cpp"
|
||||
|
||||
28
dGame/dUtilities/PropertyBuilders.cpp
Normal file
28
dGame/dUtilities/PropertyBuilders.cpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#include "PropertyBuilders.h"
|
||||
|
||||
#include "BrickByBrick.h"
|
||||
#include "dConfig.h"
|
||||
#include "Game.h"
|
||||
|
||||
bool PropertyBuilders::BestFriendsBuild() {
|
||||
return Game::config && Game::config->GetValue("property_bff_build") == "1";
|
||||
}
|
||||
|
||||
bool PropertyBuilders::CanBuild(const Player& player, const LWOOBJID owner, const bool ownerBuilding, const bool bestFriendsBuild) {
|
||||
if (owner == LWOOBJID_EMPTY || player.id == LWOOBJID_EMPTY) return false;
|
||||
if (player.id == owner) return true;
|
||||
if (!bestFriendsBuild || !player.isBestFriend) return false;
|
||||
// Joining needs the owner building; staying doesn't
|
||||
return ownerBuilding || player.isBuilding;
|
||||
}
|
||||
|
||||
LWOOBJID PropertyBuilders::Placer(const LWOOBJID placedBy, const LWOOBJID owner) {
|
||||
return placedBy == LWOOBJID_EMPTY ? owner : placedBy;
|
||||
}
|
||||
|
||||
PropertyBuilders::eModelReturn PropertyBuilders::PlanModelReturn(const LWOOBJID picker, const LWOOBJID placer, const bool placerHere, const int32_t deleteReason) {
|
||||
if (picker == placer) return eModelReturn::PICKER;
|
||||
// Taking a model apart opens it from the picker's own inventory (BBBLoadItemRequest), where it wouldn't be
|
||||
if (static_cast<BrickByBrick::eDeleteReason>(deleteReason) == BrickByBrick::eDeleteReason::BREAKING_MODEL_APART) return eModelReturn::NOT_THEIRS;
|
||||
return placerHere ? eModelReturn::PLACER : eModelReturn::PLACER_AWAY;
|
||||
}
|
||||
44
dGame/dUtilities/PropertyBuilders.h
Normal file
44
dGame/dUtilities/PropertyBuilders.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#ifndef __PROPERTYBUILDERS__H__
|
||||
#define __PROPERTYBUILDERS__H__
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "dCommonVars.h"
|
||||
|
||||
/**
|
||||
* Who builds on a property (docs/PropertyBuilding.md): the owner, and with property_bff_build the owner's best friends
|
||||
* while the owner is building. The rules here have no game state (unit tested); PropertyManagementComponent applies
|
||||
* them.
|
||||
*/
|
||||
namespace PropertyBuilders {
|
||||
// worldconfig.ini's property_bff_build
|
||||
bool BestFriendsBuild();
|
||||
|
||||
// Where a player stands on the property
|
||||
struct Player {
|
||||
LWOOBJID id{};
|
||||
bool isBestFriend{}; // of the owner
|
||||
bool isBuilding{}; // in build mode on the property
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether a player can build: the owner always. A best friend of the owner (with property_bff_build) while the owner
|
||||
* is building, and after the owner stopped until they leave build mode themselves.
|
||||
*/
|
||||
bool CanBuild(const Player& player, LWOOBJID owner, bool ownerBuilding, bool bestFriendsBuild);
|
||||
|
||||
// Who placed a model, from its properties_contents.placed_by (0, NULL in the database: the owner)
|
||||
LWOOBJID Placer(LWOOBJID placedBy, LWOOBJID owner);
|
||||
|
||||
enum class eModelReturn {
|
||||
PICKER, // the player taking it off placed it: into their inventory
|
||||
PLACER, // someone else placed it and is in this world: into the placer's inventory
|
||||
PLACER_AWAY, // someone else placed it and isn't in this world: it stays on the property
|
||||
NOT_THEIRS, // someone else placed it and the picker wants to take it apart: it stays on the property
|
||||
};
|
||||
|
||||
// Where a model taken off the property goes (DeleteModelFromClient's reason, see BrickByBrick::eDeleteReason)
|
||||
eModelReturn PlanModelReturn(LWOOBJID picker, LWOOBJID placer, bool placerHere, int32_t deleteReason);
|
||||
}
|
||||
|
||||
#endif //!__PROPERTYBUILDERS__H__
|
||||
Reference in New Issue
Block a user