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}%";
}