Spawning creatures without a player character

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.

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
}
 
Back
Top