BraskyBuilds
New member
I'm trying to build my own world boss plugin where, based on a configuration, the plugin will automatically spawn in a "boss" at a specific location with a specific set of parameters (dino blueprint, levels, etc) but I keep getting half initialized actors that crash the client as soon as they get close. Can someone share with me the right way to spawn an actor at a specific location from the plugin?
Also I do mostly Java and TS development and am not very familiar with C++ so I've been relying on Claude Code, I'm sorry if this code is bad.
Also I do mostly Java and TS development and am not very familiar with C++ so I've been relying on Claude Code, I'm sorry if this code is bad.
C++:
void SpawnBoss(const BossConfig& config) {
// Check if boss already exists
auto& state = g_BossStates[config.Name];
if (state.DinoPtr && state.DinoPtr->IsValidLowLevel() && !state.DinoPtr->IsDead()) {
state.IsAlive = true;
return;
}
// Clear any stale reference
if (state.DinoPtr) {
state.DinoPtr = nullptr;
}
// Validate world instance
UWorld* world = AsaApi::GetApiUtils().GetWorld();
if (!world) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
SaveStates();
return;
}
// Select tier and set in state
int tierIndex = SelectTierIndex();
g_BossStates[config.Name].SelectedTierIndex = tierIndex;
if (tierIndex < 0 || tierIndex >= g_TierConfigs.size()) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
SaveStates();
return;
}
// Load dino class
std::wstring wClassName = AsaApi::Tools::Utf8Decode(config.ClassName);
FString fClassName(wClassName.c_str());
UClass* dinoClass = UVictoryCore::BPLoadClass(fClassName);
if (!dinoClass) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
SaveStates();
return;
}
// Setup spawn parameters for deferred spawning
FActorSpawnParameters spawnParams;
spawnParams.bNoFail = true;
spawnParams.bDeferConstruction = true;
spawnParams.SpawnCollisionHandlingOverride[0] =
static_cast<unsigned char>(ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn);
// Spawn the dino using deferred spawning
FVector spawnLocation = config.Location;
FRotator spawnRotation(0, 0, 0);
APrimalDinoCharacter* dino = static_cast<APrimalDinoCharacter*>(world->SpawnActor(
dinoClass,
&spawnLocation,
&spawnRotation,
&spawnParams
));
if (!dino) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
SaveStates();
return;
}
// Immediate validation
if (!dino->IsValidLowLevel()) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
SaveStates();
return;
}
// Configure dino before finishing spawn
dino->TargetingTeamField() = 50000; // Wild team
// Finish spawning - this initializes all components
FTransform spawnTransform;
spawnTransform.SetLocation(spawnLocation);
dino->FinishSpawning(&spawnTransform, false, nullptr, ESpawnActorScaleMethod::MultiplyWithRoot);
// Store reference immediately
state.DinoPtr = dino;
state.IsAlive = true;
// Capture config values for delayed configuration
std::string bossName = config.Name;
int tier = tierIndex;
int configLevel = config.Level;
float healthMult = config.HealthMultiplier;
float meleeMult = config.MeleeMultiplier;
float sizeMult = config.SizeMultiplier;
// Use a timer to configure the boss after components initialize
API::Timer::Get().DelayExecute([bossName, tier, configLevel, healthMult, meleeMult, sizeMult]() {
auto& state = g_BossStates[bossName];
// Validate dino reference
if (!state.DinoPtr || !state.DinoPtr->IsValidLowLevel()) {
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
state.DinoPtr = nullptr;
SaveStates();
return;
}
// Check if dino died immediately
if (state.DinoPtr->IsDead()) {
state.DinoPtr->Destroy(true, false);
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
state.DinoPtr = nullptr;
SaveStates();
return;
}
// Get status component
UPrimalCharacterStatusComponent* statusComp = state.DinoPtr->MyCharacterStatusComponentField();
if (!statusComp) {
state.DinoPtr->Destroy(true, false);
state.IsAlive = false;
state.CooldownEndTime = INT_MAX;
state.DinoPtr = nullptr;
SaveStates();
return;
}
// Set level
statusComp->BaseCharacterLevelField() = configLevel;
statusComp->ExtraCharacterLevelField() = 0;
// Apply health multiplier
if (healthMult != 1.0f) {
float* currentVals = statusComp->CurrentStatusValuesField()();
float* maxVals = statusComp->MaxStatusValuesField()();
maxVals[0] *= healthMult;
currentVals[0] *= healthMult;
}
// Apply size multiplier
if (sizeMult != 1.0f) {
USceneComponent* rootComp = state.DinoPtr->RootComponentField();
if (rootComp) {
FVector scale(sizeMult, sizeMult, sizeMult);
rootComp->SetWorldScale3D(&scale);
}
}
// Set custom name
if (tier >= 0 && tier < g_TierConfigs.size()) {
std::string fullName = bossName + " " + g_TierConfigs[tier].Name;
std::wstring wName = AsaApi::Tools::Utf8Decode(fullName);
FString fName(wName.c_str());
state.DinoPtr->TamedNameField() = fName;
// Broadcast spawn message
std::string spawnMsg = fullName + " has appeared!";
std::wstring wSpawnMsg = AsaApi::Tools::Utf8Decode(spawnMsg);
FString fSpawnMsg(wSpawnMsg.c_str());
AsaApi::GetApiUtils().SendServerMessageToAll(FLinearColor(1, 0.5, 0, 1), *fSpawnMsg);
}
SaveStates();
}, 5); // Wait 5 seconds for components to initialize
}