Skip to content

en Basic PRT System

HoCha113 edited this page Mar 29, 2026 · 9 revisions

中文版本

What is PRT?

PRT is an abbreviation for 'Particle', used to distinguish it from other particle systems.

Compared to the vanilla Dust system, the PRT system provided by InnoVault offers greater customization, allowing developers to implement more complex particle effects more efficiently.

PRT system features:

  • Multiple draw blend modes (Alpha blend, Additive blend, Non-premultiplied blend)
  • Shader support (attachable ArmorShaderData)
  • History position/rotation caches (for trail effects)
  • Lifetime management (automatic lifecycle tracking)
  • Global hooks (modify all particle behavior via GlobalPRT)
  • Local particle collections (manage particles in non-global contexts via PRTGroup)

BasePRT

BasePRT is the base class for PRT entities. Inherit from it to create various special particle effects.

The implementation and development process of PRT entities is similar to that of projectiles.

Each PRT entity is an independent instance with its own field space — they do not interfere with each other.

Key Properties

Property/Field Type Default Description
Texture string "" Texture path, supports auto-loading same-name .png
TexValue Texture2D Gets the loaded texture (read-only)
ID int Globally unique ID
active bool false Whether this particle is active
Time int 0 Frames alive (auto-incremented)
Lifetime int -1 Maximum lifetime in frames, -1 means infinite
LifetimeCompletion float Lifetime ratio 0~1 (read-only)
Position Vector2 World position
Velocity Vector2 Movement velocity
Origin Vector2 Draw origin
Color Color Color
Rotation float 0 Rotation angle (radians)
Scale float 1 Scale multiplier
Opacity float 0 Opacity/fade value
ai float[3] [0,0,0] AI data for custom behavior
Frame Rectangle default Generic frame index rectangle
PRTDrawMode PRTDrawModeEnum AlphaBlend Draw blend mode
PRTLayersMode PRTLayersModeEnum InWorld Update and draw layer
shader ArmorShaderData null Attached shader
ShouldKillWhenOffScreen bool true Auto-destroy when off screen
InGame_World_MaxCount int 4000 Max count of this particle type in the world
oldPositions Vector2[] null Historical position cache (requires manual initialization)
oldRotations float[] null Historical rotation cache (requires manual initialization)

Quick Example

Below is a simple particle example from InnoVaultExample

internal class ExamplePRT : BasePRT
{
    // The Texture property doesn't need to be overridden — BasePRT has an auto-loading mechanism
    // that automatically loads a same-name .png in the same directory.
    // So, let's prepare a .png file called "ExamplePRT".

    public override void SetProperty() {
        // Set draw blend mode to additive blending (glow effect)
        PRTDrawMode = PRTDrawModeEnum.AdditiveBlend;
        Lifetime = Main.rand.Next(220, 360); // 220 to 360 tick lifetime
    }

    public override void AI() {
        // A simple AI that randomly changes direction
        if (--ai[0] <= 0) {
            Velocity = Velocity.RotatedByRandom(0.3f);
            ai[0] = Main.rand.Next(10);
        }
        Rotation += Velocity.X * 0.02f;

        Color.A = (byte)(1 - LifetimeCompletion * 255f);
        if (LifetimeCompletion > 0.8f) {
            Color *= 0.9f;
        }
    }
    
    public override bool PreDraw(SpriteBatch spriteBatch) => true;
}

Lifecycle Callbacks

Method When Called Description
SetProperty() Called once when particle is spawned Initialize instance data, similar to ModProjectile.SetDefaults
AI() Every frame update Logic update, controls particle behavior. Position has already been updated by Velocity at this point
ShouldUpdatePosition() Before AI each frame Return false to disable automatic position updates
PreDraw(SpriteBatch) Before drawing Return true for default draw, return false for custom draw
PostDraw(SpriteBatch) After default drawing Runs regardless of what PreDraw returns
DrawInUI(SpriteBatch) Manual call Not called automatically, used for manual drawing in UI scenarios
Kill() Manual call Sets active to false, removed on next update

Draw Blend Modes (PRTDrawModeEnum)

Mode Description
AlphaBlend Alpha blending (default), for normal particles
NonPremultiplied Non-premultiplied blending
AdditiveBlend Additive blending, for glow effects

Each frame the system dynamically assigns particles to the corresponding draw batch based on PRTDrawMode, so it can be changed at runtime in real-time.

If a particle has a shader set, it will be additionally assigned to the shader draw batch.

Update and Draw Layers (PRTLayersModeEnum)

Mode Description
InWorld Update and draw in the world (default)
NoDraw Update logic only, no drawing
None No automatic update or draw, only loaded into the list

History Caches (Trail Effects)

Used to implement particle trails and similar effects. Must be initialized in SetProperty():

public override void SetProperty() {
    // Initialize 15 position and rotation cache points
    InitializeCaches(15);
}

public override void AI() {
    // Update caches every frame
    UpdatePositionCache(oldPositions.Length);
    UpdateRotationCache(oldRotations.Length);
}

Available methods:

  • InitializePositionCache(int length) — Initialize position cache
  • InitializeRotationCache(int length) — Initialize rotation cache
  • InitializeCaches(int length) — Initialize both position and rotation caches simultaneously
  • UpdatePositionCache(int length) — Update position cache (shift forward one slot, record current position at the end)
  • UpdateRotationCache(int length) — Update rotation cache

PRTLoader

This class is responsible for most of the logical updates in the PRT system and is the core of the PRT system.

Global Limits

  • Global particle total limit: InGame_World_MaxPRTCount = 20000
  • Each particle type can set an independent limit by overriding InGame_World_MaxCount (default 4000)

Spawning Particles

Use the PRTLoader.NewParticle static method to spawn particles into the world:

// Generic method (recommended)
PRTLoader.NewParticle<ExamplePRT>(
    player.Center,                                    // Spawn position
    Main.rand.NextFloat(MathHelper.TwoPi).ToRotationVector2() * 6, // Velocity
    Color.White,                                      // Color
    Main.rand.NextFloat(1, 2)                         // Scale
);

// Via ID method
PRTLoader.NewParticle(
    PRTLoader.GetParticleID<ExamplePRT>(), // Get particle ID
    player.Center,                          // Position
    velocity,                               // Velocity
    Color.White,                            // Color
    1f                                      // Scale
);

Common Static Methods

Method Description
NewParticle<T>(pos, vel, color, scale) Spawn particle via generic type
NewParticle(id, pos, vel, color, scale) Spawn particle via ID
GetParticleID<T>() Get the internal ID of a particle type
GetPRTInstance<T>() Get a new instance of the specified particle type
GetPRTInstance(int id) Get a new particle instance via ID

Note: The particle system is client-side only. On the server (Main.dedServ), NewParticle returns null directly and does not spawn any particles.


PRTGroup (Local Particle Collection)

PRTGroup manages a set of local particles, independent from the global particle system. Suitable for scenarios where you need separate control over a batch of particles (e.g., particles attached to a specific NPC/projectile).

public class MyProjectile : ModProjectile
{
    private PRTGroup _particles = new();

    public override void AI() {
        // Spawn particles into the local collection
        _particles.NewParticle<ExamplePRT>(
            Projectile.Center,
            Vector2.UnitY * -2f,
            Color.Cyan
        );
        // Manual update
        _particles.Update();
    }

    public override bool PreDraw(ref Color lightColor) {
        // Manual draw
        _particles.Draw(Main.spriteBatch);
        return true;
    }
}

PRTGroup API

Method Description
NewParticle<T>(center, velocity, color, scale) Create and add a particle via generic type
NewParticle(center, velocity, type, color, scale) Create and add a particle via ID
Add(BasePRT particle) Directly add a particle instance
Update() Update all particles in the collection
Draw(SpriteBatch) Draw all particles in the world
DrawInUI(SpriteBatch) Draw all particles in UI
Clear() Clear all particles
Any() / Any(int id) Check if any particles exist / if particles of a specific ID exist

PRTGroup implements IEnumerable<BasePRT>, allowing direct iteration with foreach.


GlobalPRT (Global Particle Hooks)

Inheriting GlobalPRT allows you to modify the behavior of all particles, similar to the GlobalItem / GlobalNPC concepts in tModLoader.

public class MyGlobalPRT : GlobalPRT
{
    // When a particle is spawned into the world
    public override void OnSpawn(BasePRT prt) { }

    // Before all particles update, return false to prevent all particle updates
    public override bool PreUpdatePRTAll() => true;
    
    // After all particles update
    public override void PostUpdatePRTAll() { }

    // Before a single particle draws, return false to prevent that particle from drawing
    public override bool PreDrawPRT(SpriteBatch spriteBatch, BasePRT prt) => true;

    // After a single particle draws
    public override void PostDrawPRT(SpriteBatch spriteBatch, BasePRT prt) { }
}

Finding Particle Instances via VaultContent

// Find a registered PRT instance from your mod
BasePRT prt = Mod.FindPRT<ExamplePRT>();
BasePRT prt2 = Mod.FindPRT("ExamplePRT");

Navigation

Previous Next
Basic VaultLoaden Basic TP Entity

Clone this wiki locally