diff --git a/Assets/_Project/Audio/tesla_tower.ogg b/Assets/_Project/Audio/tesla_tower.ogg new file mode 100644 index 0000000..ca9d7f2 --- /dev/null +++ b/Assets/_Project/Audio/tesla_tower.ogg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e1dfa3589cac3f0e3e5fd6c4ca9f5547650894cd163597772cc62d8bfdac730b +size 38323 diff --git a/Assets/_Project/Audio/tesla_tower.ogg.meta b/Assets/_Project/Audio/tesla_tower.ogg.meta new file mode 100644 index 0000000..823ae51 --- /dev/null +++ b/Assets/_Project/Audio/tesla_tower.ogg.meta @@ -0,0 +1,23 @@ +fileFormatVersion: 2 +guid: a0b5d0b15de667604b825f12d39b05a8 +AudioImporter: + externalObjects: {} + serializedVersion: 8 + defaultSettings: + serializedVersion: 2 + loadType: 0 + sampleRateSetting: 0 + sampleRateOverride: 44100 + compressionFormat: 1 + quality: 1 + conversionMode: 0 + preloadAudioData: 0 + platformSettingOverrides: {} + forceToMono: 0 + normalize: 1 + loadInBackground: 0 + ambisonic: 0 + 3D: 1 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/_Project/Prefabs/Towers/Tower_TeslaCoil.prefab b/Assets/_Project/Prefabs/Towers/Tower_TeslaCoil.prefab index 55b8f98..959f827 100644 --- a/Assets/_Project/Prefabs/Towers/Tower_TeslaCoil.prefab +++ b/Assets/_Project/Prefabs/Towers/Tower_TeslaCoil.prefab @@ -99,6 +99,8 @@ GameObject: - component: {fileID: 805962841523123163} - component: {fileID: 8853488620519990682} - component: {fileID: 4524938955752569428} + - component: {fileID: 42814861218671054} + - component: {fileID: -2590971686077483651} m_Layer: 0 m_Name: Tower_TeslaCoil m_TagString: Untagged @@ -145,7 +147,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 1363936954 + GlobalObjectIdHash: 280718094 InScenePlacedSourceGlobalObjectIdHash: 0 DeferredDespawnTick: 0 Ownership: 1 @@ -222,6 +224,118 @@ MonoBehaviour: arcDuration: 0.12 segmentsPerArc: 6 jitter: 0.2 +--- !u!82 &42814861218671054 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6482414459531823157} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 0} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 0} + m_PlayOnAwake: 0 + m_Volume: 0.5 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!114 &-2590971686077483651 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6482414459531823157} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: db643146344a08a639133638e4746a33, type: 3} + m_Name: + m_EditorClassIdentifier: Assembly-CSharp::TD.Combat.TeslaTowerFireSound + fireClip: {fileID: 8300000, guid: a0b5d0b15de667604b825f12d39b05a8, type: 3} + minPitch: 0.92 + maxPitch: 1.07 --- !u!1 &7101790219931971190 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs new file mode 100644 index 0000000..69de453 --- /dev/null +++ b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs @@ -0,0 +1,56 @@ +// Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs +using UnityEngine; + +namespace TD.Combat +{ + /// + /// Local-only audio for the Tesla Coil tower. Subscribes to + /// and plays a one-shot fire sound + /// on every peer each time the coil discharges. + /// + /// + /// Pure client-side — nothing here is networked. Add additional + /// fields and calls here + /// as the Tesla Coil gains more audio (idle hum, charge-up, etc.). + /// + [RequireComponent(typeof(TowerCombat))] + [RequireComponent(typeof(AudioSource))] + public class TeslaTowerFireSound : MonoBehaviour + { + [Tooltip("Sound played each time the coil discharges.")] + [SerializeField] private AudioClip fireClip; + + [Tooltip("Pitch is randomized within this range each shot to avoid a mechanical loop.")] + [SerializeField] private float minPitch = 0.93f; + [SerializeField] private float maxPitch = 1.07f; + + private TowerCombat combat; + private AudioSource audioSource; + + private void Awake() + { + combat = GetComponent(); + audioSource = GetComponent(); + } + + private void OnEnable() + { + if (combat != null) combat.OnAreaFired += HandleAreaFired; + } + + private void OnDisable() + { + if (combat != null) combat.OnAreaFired -= HandleAreaFired; + } + + private void HandleAreaFired(Vector3[] positions) + { + if (fireClip != null) + { + audioSource.pitch = Random.Range(minPitch, maxPitch); + audioSource.clip = fireClip; + audioSource.Play(); + } + } + } +} diff --git a/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs.meta b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs.meta new file mode 100644 index 0000000..e944813 --- /dev/null +++ b/Assets/_Project/Scripts/Combat/TeslaTowerFireSound.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: db643146344a08a639133638e4746a33 \ No newline at end of file diff --git a/Assets/_Project/Scripts/Combat/TowerCombat.cs b/Assets/_Project/Scripts/Combat/TowerCombat.cs index 1c37234..657d5f8 100644 --- a/Assets/_Project/Scripts/Combat/TowerCombat.cs +++ b/Assets/_Project/Scripts/Combat/TowerCombat.cs @@ -55,7 +55,7 @@ namespace TD.Combat private EnemyHealth currentTarget; // Attack cooldown decrements each server frame. When ≤ 0 and a target - // is in range, the tower fires and the timer resets to 1 / FireRate. + // is in range, the tower fires and the timer resets to 1 / AttacksPerSecond. private float attackCooldown; // Cached on OnNetworkSpawn — avoids GetComponent every Update. @@ -129,7 +129,7 @@ namespace TD.Combat if (!IsServer) return; TowerDefinition def = towerInstance?.Definition; - if (def == null || def.Range <= 0f || def.FireRate <= 0f) return; + if (def == null || def.Range <= 0f || def.AttacksPerSecond <= 0f) return; // Only fire during active gameplay. var ms = MatchState.Instance; @@ -252,7 +252,7 @@ namespace TD.Combat attackCooldown -= Time.deltaTime; if (attackCooldown > 0f) return; - float effectiveFireRate = def.FireRate + float effectiveFireRate = def.AttacksPerSecond * (GetOwnerBuffManager()?.GetMultiplier(BuffStat.AttackSpeed) ?? 1f); attackCooldown = 1f / effectiveFireRate; Fire(def); diff --git a/Assets/_Project/Scripts/Towers/TowerDefinition.cs b/Assets/_Project/Scripts/Towers/TowerDefinition.cs index 24232ad..3ce1632 100644 --- a/Assets/_Project/Scripts/Towers/TowerDefinition.cs +++ b/Assets/_Project/Scripts/Towers/TowerDefinition.cs @@ -107,7 +107,7 @@ namespace TD.Towers public float Range; [Tooltip("Attacks per second.")] - public float FireRate; + public float AttacksPerSecond; [Header("Combat — Area")] [Tooltip("Radius of splash damage around the primary impact point. " + diff --git a/Assets/_Project/Scripts/UI/HUDController.cs b/Assets/_Project/Scripts/UI/HUDController.cs index 2dd0536..96f50ec 100644 --- a/Assets/_Project/Scripts/UI/HUDController.cs +++ b/Assets/_Project/Scripts/UI/HUDController.cs @@ -1290,7 +1290,7 @@ namespace TD.UI { if (def.Damage > 0) AddStatLine($"Damage: {def.Damage}"); if (def.Range > 0) AddStatLine($"Range: {def.Range:0.0}"); - if (def.FireRate > 0) AddStatLine($"Fire rate: {def.FireRate:0.0}/s"); + if (def.AttacksPerSecond > 0) AddStatLine($"Fire rate: {def.AttacksPerSecond:0.0}/s"); if (def.SlowFactor < 1f) AddStatLine($"Slow: {(1f - def.SlowFactor) * 100f:0}%"); } @@ -1427,8 +1427,8 @@ namespace TD.UI if (def.Damage > 0 || def.Range > 0) { ttStats.text = $"DMG: {def.Damage} · Range: {def.Range:0}"; - if (def.FireRate > 0) - ttStats.text += $"\nFire rate: {def.FireRate:0.0}/s"; + if (def.AttacksPerSecond > 0) + ttStats.text += $"\nFire rate: {def.AttacksPerSecond:0.0}/s"; if (def.SlowFactor < 1f) ttStats.text += $"\nSlow: {(1f - def.SlowFactor) * 100f:0}%"; }