Skip to content

Step 7: Evolotion System Guide

Can TATAR edited this page Feb 23, 2026 · 1 revision

URF Weapon Evolution System Guide

Overview

The Evolution System allows weapons to transform into more powerful versions when they reach max level and the player owns the required passive item. This is a core feature inspired by Vampire Survivors.


Features

Automatic Evolution - Weapons can auto-evolve when conditions are met
Manual Evolution - Players can trigger evolution via chest or button
Flexible Control - Blueprint events for full customization
Passive Item Tracking - Simple array-based passive management
Blueprint-Friendly - All functions exposed to Blueprints
Event System - Multiple events for UI and effects


How It Works

1. Evolution Conditions

A weapon can evolve when ALL of these conditions are met:

  1. ✅ Weapon is at max level
  2. ✅ Weapon has evolution config (bCanEvolve = true)
  3. ✅ Player owns the required passive item

2. Evolution Flow

Weapon reaches max level
    ↓
OnWeaponMaxLevel event fires
    ↓
Check if evolution is possible
    ↓
OnEvolutionAvailable event fires
    ↓
[If bAutoEvolveOnMaxLevel = true]
    ↓
Wait AutoEvolveDelay seconds
    ↓
Auto-evolve weapon
    ↓
OnWeaponEvolved event fires

[If bAutoEvolveOnMaxLevel = false]
    ↓
Wait for manual trigger (chest/button)
    ↓
Call EvolveWeapon()
    ↓
OnWeaponEvolved event fires

Setup Guide

Step 1: Configure Weapon Data Asset

In your weapon data asset Blueprint (e.g., DA_MagicWand):

Evolution Config:
  ✓ bCanEvolve = true
  ✓ RequiredPassiveItem = BP_EmptyTome (your passive ACTOR class, e.g., BP_PassiveEmptyTome)
  ✓ EvolvedWeapon = DA_HolyWand (your evolved weapon data asset)
  ✓ EvolutionDescription = "Magic Wand + Empty Tome = Holy Wand"

Important: RequiredPassiveItem should be an Actor class (e.g., BP_PassiveEmptyTome), not a component. Passive items are spawned as actors in the game world.

Step 2: Add Passive Items

Option A: Starting Passive Items (Auto-added on game start)

In your Player Blueprint's Weapon Component:

Starting Passive Items:
  [0] BP_PassiveEmptyTome
  [1] BP_PassiveSpinach

Owned Passive Items: (Read-Only - Auto-synced from Starting Passive Items)
  [0] BP_PassiveEmptyTome  ← Automatically added!
  [1] BP_PassiveSpinach    ← Automatically added!

How it works:

  1. Editor: Add passive to "Starting Passive Items" → Automatically appears in "Owned Passive Items"
  2. Game Start: Starting weapons equipped (level 1)
  3. Evolution check runs automatically for all weapons
  4. If weapon is max level + has passive → Evolution triggers!

Option B: Runtime Passive Items (Acquired during gameplay)

When player acquires a passive item:

// Blueprint:
WeaponComponent->AddPassiveItem(BP_EmptyTome);

// C++:
WeaponComponent->AddPassiveItem(TSoftClassPtr<AActor>(BP_EmptyTome::StaticClass()));

How it works:

  1. Player picks up passive item
  2. AddPassiveItem() called
  3. Added to "Owned Passive Items"
  4. Evolution check runs automatically for all weapons
  5. If any weapon is max level + has this passive → Evolution triggers!

Important:

  • Do NOT add passive items to "Starting Weapons" array - that's only for weapons!
  • "Owned Passive Items" is read-only in Editor - it auto-syncs from "Starting Passive Items"
  • Use AddPassiveItem() for runtime additions

Step 3: Choose Evolution Mode

Option A: Automatic Evolution (Simple)

// In Player Blueprint BeginPlay:
WeaponComponent->bAutoEvolveOnMaxLevel = true;
WeaponComponent->AutoEvolveDelay = 1.0f;  // 1 second delay for UI

Result: Weapon auto-evolves when max level + passive conditions are met.

Option B: Manual Evolution (Vampire Survivors Style)

// In Player Blueprint BeginPlay:
WeaponComponent->bAutoEvolveOnMaxLevel = false;

// In Chest Blueprint:
Event OnChestOpened()
{
    TArray<int32> EvolvableWeapons = WeaponComponent->GetEvolvableWeapons();
    
    if (EvolvableWeapons.Num() > 0)
    {
        // Show UI for player to select
        ShowEvolutionSelection(EvolvableWeapons);
        
        // When player selects:
        WeaponComponent->EvolveWeapon(SelectedSlotIndex);
    }
    else
    {
        // No evolutions available, give gold instead
        GiveGold(100);
    }
}

Result: Player manually triggers evolution via chest or button.


Blueprint Events

OnEvolutionAvailable

Fired when a weapon CAN evolve (max level + has passive).

Event OnEvolutionAvailable(SlotIndex, WeaponData)
{
    // Show UI notification
    ShowNotification("Evolution Ready: " + WeaponData->WeaponName);
    
    // Play sound
    PlaySound(EvolutionReadySound);
}

OnWeaponEvolved

Fired when a weapon successfully evolves.

Event OnWeaponEvolved(SlotIndex, OldWeapon, NewWeapon)
{
    // Show evolution animation
    PlayEvolutionAnimation(OldWeapon, NewWeapon);
    
    // Show UI popup
    ShowEvolutionPopup(NewWeapon->WeaponName);
    
    // Play sound/effect
    PlaySound(EvolutionCompleteSound);
    SpawnEffect(EvolutionEffect);
}

Blueprint Functions

Passive Item Management

// Add passive item (actor class)
TSoftClassPtr<AActor> PassiveClass = ...;
WeaponComponent->AddPassiveItem(PassiveClass);

// Remove passive item
WeaponComponent->RemovePassiveItem(PassiveClass);

// Check if player has passive
bool HasPassive = WeaponComponent->HasPassiveItem(PassiveClass);

// Get all owned passives
TArray<TSoftClassPtr<AActor>> Passives = WeaponComponent->GetOwnedPassiveItems();

Evolution Checks

// Check if specific weapon can evolve
bool CanEvolve = WeaponComponent->CanWeaponEvolve(SlotIndex);

// Get all weapons that can evolve
TArray<int32> EvolvableSlots = WeaponComponent->GetEvolvableWeapons();

// Get evolution config for a weapon
FURFWeaponEvolutionConfig Config = WeaponComponent->GetWeaponEvolutionConfig(SlotIndex);

Manual Evolution

// Manually trigger evolution
bool Success = WeaponComponent->EvolveWeapon(SlotIndex);

Banned Weapons

When a weapon evolves, the old weapon is automatically banned to prevent it from appearing in level-up cards:

// Check if a weapon is banned
bool IsBanned = WeaponComponent->IsWeaponBanned(WeaponData);

// Get all banned weapons
TArray<UURFWeaponDataAsset*> Banned = WeaponComponent->BannedWeapons;

// Example: Filter level-up card pool
TArray<UURFWeaponDataAsset*> GetAvailableWeapons()
{
    TArray<UURFWeaponDataAsset*> Available;
    
    for (UURFWeaponDataAsset* Weapon : AllWeapons)
    {
        // Skip banned weapons (evolved weapons)
        if (WeaponComponent->IsWeaponBanned(Weapon))
        {
            continue;
        }
        
        // Skip owned weapons
        if (WeaponComponent->HasWeapon(Weapon))
        {
            continue;
        }
        
        Available.Add(Weapon);
    }
    
    return Available;
}

How it works:

  1. Weapon evolves (Magic Wand → Holy Wand)
  2. Old weapon (Magic Wand) added to BannedWeapons
  3. Old weapon's persistent actors destroyed
  4. Level-up cards filter out banned weapons
  5. Magic Wand can't be picked again this run

Example: Complete Evolution Setup

1. Create Base Weapon (DA_MagicWand)

Weapon Name: Magic Wand
Max Level: 8
Base Damage: 15

Evolution Config:
  bCanEvolve: true
  RequiredPassiveItem: BP_PassiveEmptyTome (Actor class)
  EvolvedWeapon: DA_HolyWand
  EvolutionDescription: "Fires more projectiles with holy power"

2. Create Evolved Weapon (DA_HolyWand)

Weapon Name: Holy Wand
Max Level: 8
Base Damage: 30
Base Amount: 3  // Fires 3 projectiles instead of 1

Evolution Config:
  bCanEvolve: false  // Cannot evolve further

3. Create Passive Item (BP_EmptyTome)

// Simple actor component
UCLASS()
class UEmptyTomeComponent : public UActorComponent
{
    GENERATED_BODY()
    // No logic needed, just exists as a marker
};

4. Player Blueprint Setup

// BeginPlay:
WeaponComponent->bAutoEvolveOnMaxLevel = false;  // Manual evolution

// When player picks up Empty Tome:
Event OnPickupEmptyTome()
{
    WeaponComponent->AddPassiveItem(BP_EmptyTome);
    ShowNotification("Acquired: Empty Tome");
}

// Evolution Available Event:
Event OnEvolutionAvailable(SlotIndex, WeaponData)
{
    ShowNotification("Evolution Ready: " + WeaponData->WeaponName);
    PlaySound(EvolutionReadySound);
}

// Evolution Complete Event:
Event OnWeaponEvolved(SlotIndex, OldWeapon, NewWeapon)
{
    ShowEvolutionAnimation(OldWeapon, NewWeapon);
    PlaySound(EvolutionCompleteSound);
}

5. Chest Blueprint Setup

Event OnChestOpened()
{
    // Check for evolvable weapons
    TArray<int32> EvolvableWeapons = WeaponComponent->GetEvolvableWeapons();
    
    if (EvolvableWeapons.Num() > 0)
    {
        // Evolve first available weapon
        int32 SlotToEvolve = EvolvableWeapons[0];
        WeaponComponent->EvolveWeapon(SlotToEvolve);
    }
    else
    {
        // No evolutions, give gold
        CurrencyComponent->AddCurrency("Gold", 100);
    }
}

Common Patterns

Pattern 1: Auto-Evolution with Notification

// Player Blueprint:
WeaponComponent->bAutoEvolveOnMaxLevel = true;
WeaponComponent->AutoEvolveDelay = 2.0f;

Event OnEvolutionAvailable(SlotIndex, WeaponData)
{
    // Show 2-second notification before auto-evolve
    ShowNotification("Evolving: " + WeaponData->WeaponName, 2.0f);
}

Pattern 2: Chest-Based Evolution (Vampire Survivors)

// Player Blueprint:
WeaponComponent->bAutoEvolveOnMaxLevel = false;

// Chest Blueprint:
Event OnChestOpened()
{
    TArray<int32> EvolvableWeapons = WeaponComponent->GetEvolvableWeapons();
    
    if (EvolvableWeapons.Num() > 0)
    {
        // Show selection UI if multiple evolutions
        if (EvolvableWeapons.Num() > 1)
        {
            ShowEvolutionSelectionUI(EvolvableWeapons);
        }
        else
        {
            // Auto-evolve single weapon
            WeaponComponent->EvolveWeapon(EvolvableWeapons[0]);
        }
    }
}

Pattern 3: Button-Based Evolution

// Player Blueprint:
WeaponComponent->bAutoEvolveOnMaxLevel = false;

Event OnEvolutionAvailable(SlotIndex, WeaponData)
{
    // Show "Press E to Evolve" UI
    ShowEvolutionPrompt(SlotIndex, WeaponData);
}

Event OnInputAction_Evolve()
{
    TArray<int32> EvolvableWeapons = WeaponComponent->GetEvolvableWeapons();
    
    if (EvolvableWeapons.Num() > 0)
    {
        WeaponComponent->EvolveWeapon(EvolvableWeapons[0]);
    }
}

Configuration Properties

URFWeaponComponent Properties

Property Type Default Description
bAutoEvolveOnMaxLevel bool false Auto-evolve when conditions met
AutoEvolveDelay float 0.5 Delay before auto-evolution (seconds)
OwnedPassiveItems TArray Empty List of owned passive actor classes

FURFWeaponEvolutionConfig Properties

Property Type Description
bCanEvolve bool Whether weapon can evolve
RequiredPassiveItem TSoftClassPtr Required passive actor class
EvolvedWeapon TSoftObjectPtr Evolved weapon data asset
EvolutionDescription FString Description for UI

Tips & Best Practices

✅ DO:

  • Set bCanEvolve = false on evolved weapons (prevent double evolution)
  • Use descriptive evolution descriptions for UI
  • Test evolution with both auto and manual modes
  • Provide visual/audio feedback on evolution
  • Show "Evolution Ready" notifications to player

❌ DON'T:

  • Create evolution loops (A → B → A)
  • Forget to set RequiredPassiveItem (must be an Actor class, not component)
  • Forget to create the evolved weapon data asset
  • Make evolved weapons too weak (should feel powerful!)
  • Add passive items to "Starting Weapons" array (use "Starting Passive Items" instead!)

Troubleshooting

Weapon Not Evolving?

Check:

  1. ✅ Weapon is at max level? (CurrentLevel >= MaxLevel)
  2. bCanEvolve = true in weapon data asset?
  3. RequiredPassiveItem is set to an Actor class (not component)?
  4. EvolvedWeapon is set?
  5. ✅ Player owns the passive item? (HasPassiveItem())
  6. bAutoEvolveOnMaxLevel setting matches your intent?
  7. Passive item added via AddPassiveItem() or "Starting Passive Items"? (NOT "Starting Weapons"!)

Common Mistake: Passive Items in Starting Weapons

WRONG:

Starting Weapons:
  [0] DA_MagicWand
  [1] BP_PassiveEmptyTome  ← This is wrong!

CORRECT:

Starting Weapons:
  [0] DA_MagicWand

Starting Passive Items:
  [0] BP_PassiveEmptyTome  ← Use this array!

Evolution Event Not Firing?

Check:

  1. ✅ Event is implemented in Blueprint?
  2. ✅ Delegate is bound in Actor Blueprint?
  3. ✅ Enable debug logging: bEnableDebugLogging = true

Passive Item Not Working?

Check:

  1. ✅ Passive class matches exactly in data asset and code?
  2. AddPassiveItem() was called?
  3. ✅ Check owned passives: GetOwnedPassiveItems()

Debug Commands

Enable debug logging to see evolution system in action:

// In Player Blueprint:
WeaponComponent->bEnableDebugLogging = true;

Log Output:

URFWeaponComponent: Added passive item: BP_EmptyTome (Total: 1)
URFWeaponComponent: Weapon 'Magic Wand' can now evolve!
URFWeaponComponent: Auto-evolving weapon in 0.50 seconds...
URFWeaponComponent: Weapon evolved! Magic Wand -> Holy Wand (Slot 0)

Version History

  • v1.0 - Initial evolution system implementation
    • Automatic and manual evolution modes
    • Passive item tracking
    • Blueprint events and delegates
    • Full Vampire Survivors-style support

Community

Document Version: 1.0.0
Last Updated: 2026-02-07
Status: ✅ Production Ready

Clone this wiki locally