ArenaSurfaceMaterials.cs
using Sandbox;
using System;
using System.Collections.Generic;

namespace PaintballBaddies;

/// <summary>Local arena finish. Workshop resources are restored when the arena unloads.</summary>
public sealed class ArenaSurfaceMaterials
{
    public int FloorSurfaces { get; private set; }
    public int WallSurfaces { get; private set; }
    public int WorkshopMaterials { get; private set; }
    private readonly List<Action> restore = new();

    public void Apply( Scene scene )
    {
        if(ArenaSelection.Current(scene)==ArenaSelection.Restaurant)return;
        if ( ArenaMap.DisplayTitle( scene ) == "Marine Base" )
        {
            ApplyMarine();
            return;
        }

        // Dry concrete stays rough; independent paint decals supply the wet highlights.
        var floor = Material.Load("materials/industrial_arena/floor.vmat");
        var wall = Material.Load("materials/industrial_arena/wall.vmat");
        var brick = Material.Load( "materials/industrial_arena/brick.vmat" );
        var painted = MultiplayerSession.Find(scene)?.IsOverdrive==true ? brick : Material.Load( "materials/industrial_arena/painted_brick.vmat" );
        foreach ( var r in scene.GetAllComponents<ModelRenderer>() )
        {
            if ( r.Model is null ) continue;
            var name = r.Model.Name;
            if ( name is "models/foundry/surfaces/floor.vmdl" or "models/foundry/service_yard/floor.vmdl" )
            {
                Override( r, floor );
                FloorSurfaces++;
            }
            else if ( name == "models/foundry/surfaces/end_wall.vmdl" )
            {
                Override( r, wall );
                WallSurfaces++;
            }
            else if ( name == "models/foundry/painted_brick_wall/wall.vmdl" || name.StartsWith( "models/foundry/service_yard/", StringComparison.Ordinal ) )
            {
                bool changed = false;
                for ( int i = 0; i < r.Materials.Count; i++ )
                {
                    var original = r.Materials.GetOriginal( i );
                    var replacement = original?.Name switch
                    {
                        "materials/foundry/bricks060/bricks060.vmat" => brick,
                        "models/foundry/painted_brick_wall/lower_paint.vmat" => painted,
                        _ => null
                    };
                    if ( replacement is null ) continue;
                    int slot = i;
                    var previous = r.Materials.GetOverride( slot );
                    restore.Add( () => { if ( r.IsValid() ) r.Materials.SetOverride( slot, previous ); } );
                    r.Materials.SetOverride( slot, replacement );
                    changed = true;
                }
                if ( changed ) WallSurfaces++;
            }
        }
    }

    private void Override( ModelRenderer renderer, Material material )
    {
        var previous = renderer.MaterialOverride;
        var tint = renderer.Tint;
        restore.Add( () =>
        {
            if ( !renderer.IsValid() ) return;
            renderer.MaterialOverride = previous;
            renderer.Tint = tint;
        } );
        renderer.MaterialOverride = material;
        renderer.Tint = Color.White;
    }

    private void ApplyMarine()
    {
        // Replace broad concrete with a matched source compiled for the same
        // blendable shader. Keep vertex blends, lightmaps and trim UVs intact;
        // never inspect native world-chunk Model handles.
        var finish = Material.Load("materials/arena_finish/marine_concrete.vmat");
        var floor = Material.Load( "materials/concrete/floor/floor_tile_blend_01.vmat" );
        if ( floor.IsValid && floor.GetTexture( "g_tColorA" ) is not null )
        {
            CopyConcrete(floor,finish);
            SetVector( floor, "g_flTintColorA", new Vector4( .51f, .55f, .55f, 0 ) );
            SetVector( floor, "g_flTintColorB", new Vector4( .30f, .36f, .35f, 0 ) );
            FloorSurfaces++;
            WorkshopMaterials++;
        }
        var wall = Material.Load( "materials/concrete/wall/concrete_rough_a.vmat" );
        if ( wall.IsValid && wall.GetTexture( "g_tColorA" ) is not null )
        {
            CopyConcrete(wall,finish);
            SetVector( wall, "g_flTintColorA", new Vector4( .69f, .71f, .68f, 0 ) );
            SetVector( wall, "g_flTintColorB", new Vector4( .38f, .46f, .44f, 0 ) );
            WallSurfaces++;
            WorkshopMaterials++;
        }
        // Keep the cast-panel and damaged trim atlases intact: their seams,
        // bolts and chipped edges are authored into the workshop geometry.
        foreach ( var path in new[] { "materials/concrete/hotspot/concrete_cast_hs_01.vmat", "materials/trimsheets/concrete/concrete_trim_damaged_blend_01.vmat" } )
        {
            var material = Material.Load( path );
            if ( !material.IsValid || material.GetTexture( "g_tColorA" ) is null ) continue;
            SetVector( material, "g_flTintColorA", new Vector4( .69f, .72f, .72f, 0 ) );
            SetVector( material, "g_flTintColorB", new Vector4( .47f, .52f, .52f, 0 ) );
            WallSurfaces++;
            WorkshopMaterials++;
        }
        // Coordinate the original metal grid with the cooler concrete palette.
        var grid=Material.Load("materials/generic/floor_steel_grid_dirty_a.vmat");
        if(grid.IsValid && grid.GetTexture("g_tColor") is not null)
            SetVector(grid,"g_vColorTint",new Vector4(.72f,.76f,.77f,1));
    }

    private void CopyConcrete(Material target,Material source)
    {
        foreach(var key in new[]{"g_tColorA","g_tColorB","g_tNormalA","g_tNormalB","g_tRmaA","g_tRmaB"})
        {
            var texture=source.GetTexture(key);
            if(texture is not null)SetTexture(target,key,texture);
        }
    }

    private void SetTexture( Material material, string name, Texture value )
    {
        var previous = material.GetTexture( name );
        if ( previous is null ) return;
        restore.Add( () => { if ( material.IsValid ) material.Set( name, previous ); } );
        material.Set( name, value );
    }

    private void SetVector( Material material, string name, Vector4 value )
    {
        var previous = material.GetVector4( name );
        restore.Add( () => { if ( material.IsValid ) material.Set( name, previous ); } );
        material.Set( name, value );
    }

    public void Restore()
    {
        for ( int i = restore.Count - 1; i >= 0; i-- ) restore[i]();
        restore.Clear();
    }
}