Part of the FootballWorldPanel UI/controller. Draws a pulsing material tint highlight over the 22 player meshes during a match tutorial, caching tinted material copies keyed by the original and restoring overrides afterward. Also exposes a small debug object.
using Sandbox;
using System;
using System.Collections.Generic;
public sealed partial class FootballWorldPanel
{
// Reuse the real player's meshes. Copies only modulate the material tint; the
// original palette materials stay immutable and no renderers are added.
readonly Dictionary<Material,Material> tutorialTintMaterials=new();
readonly ModelRenderer[] tutorialHighlightedParts=new ModelRenderer[13];
readonly Material[] tutorialOriginalMaterials=new Material[13];
int tutorialHighlightedCount;
int tutorialHighlightedPlayer=-1;
float tutorialPulse;
void RestoreTutorialPlayerHighlight()
{
for(int i=0;i<tutorialHighlightedCount;i++)
{
var part=tutorialHighlightedParts[i];
if(part?.GameObject?.IsValid==true)part.Materials.SetOverride(0,tutorialOriginalMaterials[i]);
tutorialHighlightedParts[i]=null;tutorialOriginalMaterials[i]=null;
}
tutorialHighlightedCount=0;tutorialHighlightedPlayer=-1;tutorialPulse=0;
}
void DrawTutorialPlayerHighlight()
{
if(Game?.MatchTutorialNeedsAction!=true || Game.CommandWheelOpen || CallInput?.Dragging==true)return;
int player=Game.MatchTutorialFocusPlayer;
if(player<0 || player>=22 || player>=actors.Count || Game.Match.Players[player].SentOff)return;
float wave=.5f+.5f*MathF.Cos(Time.Now*MathF.PI*2/1.35f);
tutorialPulse=wave*wave;tutorialHighlightedPlayer=player;
// Warm brightness pulses through the body and boots. Never disappear
// the model or flash the whole screen while waiting for an action.
var tint=new Vector4(1+1.65f*tutorialPulse,1+1.25f*tutorialPulse,1-.2f*tutorialPulse,1);
foreach(var part in actors[player])
{
if(tutorialHighlightedCount>=13)break;
if(!part.Enabled || part.Materials.Count==0)continue;
var original=part.Materials.GetOverride(0);
var source=original??part.Materials.GetOriginal(0);
if(source==null)continue;
if(!tutorialTintMaterials.TryGetValue(source,out var highlighted))
{
highlighted=source.CreateCopy("football-tutorial-pulse-"+tutorialTintMaterials.Count);
tutorialTintMaterials[source]=highlighted;
}
int slot=tutorialHighlightedCount++;
tutorialHighlightedParts[slot]=part;tutorialOriginalMaterials[slot]=original;
highlighted.Set("g_vColorTint",tint);
part.Materials.SetOverride(0,highlighted);
}
}
public object TutorialHighlightDebug()=>new{Player=tutorialHighlightedPlayer,Pulse=tutorialPulse,
Parts=tutorialHighlightedCount,CachedMaterials=tutorialTintMaterials.Count};
}