85 lines
4.3 KiB
C#
85 lines
4.3 KiB
C#
using System;
|
|
using UnityEngine;
|
|
|
|
namespace HotUpdate.Gameplay
|
|
{
|
|
/// <summary>Authored bounded VFX/audio pools; consumes resolved facts without touching battle state.</summary>
|
|
public sealed class EnemyCombatPresentation : MonoBehaviour
|
|
{
|
|
[Serializable] public sealed class EffectPool
|
|
{
|
|
public ParticleSystem[] instances;
|
|
[NonSerialized] public int next;
|
|
}
|
|
[SerializeField] EffectPool[] _effects;
|
|
[SerializeField] AudioClip[] _clips;
|
|
[SerializeField] AudioSource[] _voices;
|
|
[SerializeField, Range(0, 1)] float _mixBudget = .7f;
|
|
[SerializeField, Range(0, 1)] float _voiceVolume = .4f;
|
|
readonly float[] _lastSound = new float[8];
|
|
int _voice;
|
|
bool _paused;
|
|
float _speed = 1;
|
|
public int PlayedEffects { get; private set; }
|
|
public int PlayedSounds { get; private set; }
|
|
public int EffectCount => _effects?.Length ?? 0;
|
|
public AudioClip Clip(int index) => _clips[index];
|
|
public int VoiceCount => _voices?.Length ?? 0;
|
|
|
|
void OnEnable() { PrototypeClientPrefs.AudioPreferencesChanged += ApplyAudio; ResetPlayback(); }
|
|
void OnDisable() { PrototypeClientPrefs.AudioPreferencesChanged -= ApplyAudio; ResetPlayback(); }
|
|
void ApplyAudio() { if (!PrototypeClientPrefs.Sfx) foreach (var v in _voices) v.Stop(); }
|
|
|
|
public void ResetPlayback()
|
|
{
|
|
if (_effects != null) foreach (var pool in _effects)
|
|
foreach (var ps in pool.instances) ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
|
if (_voices != null) foreach (var v in _voices) v.Stop();
|
|
for (int i = 0; i < _lastSound.Length; i++) _lastSound[i] = -100;
|
|
_paused = false; _speed = 1; PlayedEffects = PlayedSounds = 0;
|
|
}
|
|
|
|
public void SetPlayback(bool paused, float speed)
|
|
{
|
|
if (_paused == paused && Mathf.Approximately(_speed, speed)) return;
|
|
bool speedChanged = !Mathf.Approximately(_speed, speed);
|
|
_speed = speed;
|
|
foreach (var pool in _effects) foreach (var ps in pool.instances)
|
|
{
|
|
if (speedChanged) foreach (var child in ps.GetComponentsInChildren<ParticleSystem>()) { var m = child.main; m.simulationSpeed = speed; }
|
|
if (paused && !_paused) ps.Pause(true);
|
|
else if (!paused && _paused && ps.IsAlive(true)) ps.Play(true);
|
|
}
|
|
if (paused != _paused) foreach (var v in _voices) { if (paused) v.Pause(); else v.UnPause(); }
|
|
_paused = paused;
|
|
}
|
|
|
|
public void Play(PrototypeBattle.CombatCue cue,bool sound=true)
|
|
{
|
|
if (_paused || _effects == null) return;
|
|
int index = cue.Kind == PrototypeBattle.CueKind.EnemyAttack ? Mathf.Clamp(cue.Type, 0, 4) : (cue.Type >= 2 ? 6 : 5);
|
|
Burst(index, cue.Kind == PrototypeBattle.CueKind.EnemyAttack ? cue.AttackPosition : cue.Position, cue.Direction,sound);
|
|
if (cue.Kind == PrototypeBattle.CueKind.EnemyAttack)
|
|
Burst(cue.Shield ? 7 : 5, cue.Position, -cue.Direction,sound);
|
|
}
|
|
|
|
void Burst(int index, Vector3 position, Vector3 direction,bool sound)
|
|
{
|
|
var pool = _effects[index];
|
|
var ps = pool.instances[pool.next++ % pool.instances.Length];
|
|
ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
|
|
ps.transform.position = transform.TransformPoint(position);
|
|
ps.transform.rotation = Quaternion.LookRotation(direction);
|
|
foreach (var child in ps.GetComponentsInChildren<ParticleSystem>()) { var m = child.main; m.simulationSpeed = _speed; }
|
|
ps.Play(true); PlayedEffects++;
|
|
// Mix budget and same-cue guard prevent a wave of hits becoming a wall of sound.
|
|
if (!sound || !PrototypeClientPrefs.Sfx || Time.unscaledTime - _lastSound[index] < .075f) return;
|
|
_lastSound[index] = Time.unscaledTime;
|
|
var voice = _voices[_voice++ % _voices.Length]; voice.Stop(); voice.clip = _clips[index]; voice.Play(); PlayedSounds++;
|
|
int active = 0; foreach (var v in _voices) if (v.isPlaying) active++;
|
|
float gain = Mathf.Min(_voiceVolume, _mixBudget / Mathf.Max(1, active));
|
|
foreach (var v in _voices) if (v.isPlaying) v.volume = gain;
|
|
}
|
|
}
|
|
}
|