implement additional tower
This commit is contained in:
@@ -7,6 +7,7 @@ public class Enemy : MonoBehaviour
|
||||
private EnemyHealth enemyHealth;
|
||||
private float slowMultiplier = 1f;
|
||||
private float slowEndTime;
|
||||
private float stunEndTime;
|
||||
private Vector2 knockbackVelocity;
|
||||
private float knockbackEndTime;
|
||||
|
||||
@@ -26,6 +27,8 @@ public class Enemy : MonoBehaviour
|
||||
if (tower == null || enemyHealth == null) return;
|
||||
if (Time.time >= slowEndTime)
|
||||
slowMultiplier = 1f;
|
||||
if (Time.time < stunEndTime)
|
||||
return;
|
||||
|
||||
if (Time.time < knockbackEndTime && knockbackVelocity.sqrMagnitude > 0.01f)
|
||||
{
|
||||
@@ -52,6 +55,20 @@ public class Enemy : MonoBehaviour
|
||||
slowEndTime = Mathf.Max(slowEndTime, Time.time + duration);
|
||||
}
|
||||
|
||||
public void ApplyStun(float duration)
|
||||
{
|
||||
stunEndTime = Mathf.Max(stunEndTime, Time.time + duration);
|
||||
}
|
||||
|
||||
public void PullToward(Vector2 point, float distance)
|
||||
{
|
||||
Vector2 direction = (point - (Vector2)transform.position).normalized;
|
||||
if (direction.sqrMagnitude <= Mathf.Epsilon)
|
||||
return;
|
||||
|
||||
transform.position += (Vector3)(direction * distance);
|
||||
}
|
||||
|
||||
public void ApplyKnockback(Vector2 source, float distance)
|
||||
{
|
||||
Vector2 direction = ((Vector2)transform.position - source).normalized;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using TMPro;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
public class EnemyHealth : MonoBehaviour
|
||||
@@ -8,14 +9,18 @@ public class EnemyHealth : MonoBehaviour
|
||||
public EnemySpawner spawner;
|
||||
public bool isBoss;
|
||||
private int maxHealth = 1;
|
||||
private float ionizedDamageMultiplier = 1f;
|
||||
private float ionizedEndTime;
|
||||
private Coroutine residualCurrentRoutine;
|
||||
|
||||
public bool TakeDamage(int damage)
|
||||
{
|
||||
if (health <= 0)
|
||||
return false;
|
||||
|
||||
ShowDamageNumber(damage);
|
||||
health -= damage;
|
||||
int finalDamage = Mathf.Max(1, Mathf.RoundToInt(damage * GetDamageTakenMultiplier()));
|
||||
ShowDamageNumber(finalDamage);
|
||||
health -= finalDamage;
|
||||
if (isBoss && spawner != null)
|
||||
spawner.UpdateBossHealth(this, health, maxHealth);
|
||||
|
||||
@@ -52,6 +57,41 @@ public class EnemyHealth : MonoBehaviour
|
||||
return maxHealth <= 0 ? 0f : (float)health / maxHealth;
|
||||
}
|
||||
|
||||
public float GetDamageTakenMultiplier()
|
||||
{
|
||||
if (Time.time >= ionizedEndTime)
|
||||
ionizedDamageMultiplier = 1f;
|
||||
|
||||
return ionizedDamageMultiplier;
|
||||
}
|
||||
|
||||
public void ApplyIonized(float extraDamageMultiplier, float duration)
|
||||
{
|
||||
ionizedDamageMultiplier = Mathf.Max(ionizedDamageMultiplier, 1f + extraDamageMultiplier);
|
||||
ionizedEndTime = Mathf.Max(ionizedEndTime, Time.time + duration);
|
||||
}
|
||||
|
||||
public void ApplyResidualCurrent(int damagePerTick, float duration, float tickInterval)
|
||||
{
|
||||
if (residualCurrentRoutine != null)
|
||||
StopCoroutine(residualCurrentRoutine);
|
||||
|
||||
residualCurrentRoutine = StartCoroutine(ResidualCurrent(damagePerTick, duration, tickInterval));
|
||||
}
|
||||
|
||||
IEnumerator ResidualCurrent(int damagePerTick, float duration, float tickInterval)
|
||||
{
|
||||
float endTime = Time.time + duration;
|
||||
while (Time.time < endTime && health > 0)
|
||||
{
|
||||
yield return new WaitForSeconds(tickInterval);
|
||||
if (health > 0)
|
||||
TakeDamage(damagePerTick);
|
||||
}
|
||||
|
||||
residualCurrentRoutine = null;
|
||||
}
|
||||
|
||||
public void NotifySpawner()
|
||||
{
|
||||
if (spawner != null)
|
||||
|
||||
@@ -8,6 +8,7 @@ public class EnemySpawner : MonoBehaviour
|
||||
{
|
||||
const int WavesPerStage = 15;
|
||||
const string CurrentStageKey = "CurrentStage";
|
||||
const string TeslaTowerUnlockedKey = "TeslaTowerUnlocked";
|
||||
|
||||
public GameObject enemyPrefab;
|
||||
public Transform tower;
|
||||
@@ -223,9 +224,12 @@ public class EnemySpawner : MonoBehaviour
|
||||
isSpawning = false;
|
||||
HideBossHealth(activeBoss);
|
||||
int nextStage = currentStage + 1;
|
||||
bool unlockedTeslaTower = currentStage == 1 && PlayerPrefs.GetInt(TeslaTowerUnlockedKey, 0) == 0;
|
||||
PlayerPrefs.SetInt(CurrentStageKey, nextStage);
|
||||
if (unlockedTeslaTower)
|
||||
PlayerPrefs.SetInt(TeslaTowerUnlockedKey, 1);
|
||||
PlayerPrefs.Save();
|
||||
ShowWinScreen(nextStage);
|
||||
ShowWinScreen(nextStage, unlockedTeslaTower);
|
||||
}
|
||||
|
||||
public void UpdateBossHealth(EnemyHealth boss, int currentHealth, int maxHealth)
|
||||
@@ -262,7 +266,7 @@ public class EnemySpawner : MonoBehaviour
|
||||
bossHealthBar.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void ShowWinScreen(int nextStage)
|
||||
void ShowWinScreen(int nextStage, bool unlockedTeslaTower)
|
||||
{
|
||||
Time.timeScale = 0f;
|
||||
|
||||
@@ -287,10 +291,17 @@ public class EnemySpawner : MonoBehaviour
|
||||
PlaceOverlayRect(title.rectTransform, 0f, 76f, 420f, 70f);
|
||||
|
||||
TextMeshProUGUI subtitle = CreateOverlayText("Subtitle", rect, $"Stage {currentStage} complete. Stage {nextStage} unlocked.", 20f, FontStyles.Normal);
|
||||
PlaceOverlayRect(subtitle.rectTransform, 0f, 22f, 520f, 42f);
|
||||
PlaceOverlayRect(subtitle.rectTransform, 0f, unlockedTeslaTower ? 30f : 22f, 520f, 42f);
|
||||
|
||||
if (unlockedTeslaTower)
|
||||
{
|
||||
TextMeshProUGUI unlockText = CreateOverlayText("UnlockText", rect, "You unlocked: Tesla Tower", 22f, FontStyles.Bold);
|
||||
unlockText.color = new Color(0.52f, 0.86f, 1f, 1f);
|
||||
PlaceOverlayRect(unlockText.rectTransform, 0f, -14f, 520f, 42f);
|
||||
}
|
||||
|
||||
Button menuButton = CreateOverlayButton("MenuButton", rect, "MAIN MENU", ReturnToMenu);
|
||||
PlaceOverlayRect(menuButton.GetComponent<RectTransform>(), 0f, -58f, 220f, 54f);
|
||||
PlaceOverlayRect(menuButton.GetComponent<RectTransform>(), 0f, unlockedTeslaTower ? -86f : -58f, 220f, 54f);
|
||||
}
|
||||
|
||||
void ReturnToMenu()
|
||||
|
||||
+37
-10
@@ -7,7 +7,9 @@ public class MainMenu : MonoBehaviour
|
||||
{
|
||||
const string CurrentStageKey = "CurrentStage";
|
||||
const string SelectedTowerKey = "SelectedTower";
|
||||
const string TeslaTowerUnlockedKey = "TeslaTowerUnlocked";
|
||||
const string CannonTowerId = "cannon";
|
||||
const string TeslaTowerId = "tesla";
|
||||
|
||||
Canvas canvas;
|
||||
RectTransform contentRoot;
|
||||
@@ -21,6 +23,10 @@ public class MainMenu : MonoBehaviour
|
||||
PlayerPrefs.SetInt(CurrentStageKey, 1);
|
||||
if (!PlayerPrefs.HasKey(SelectedTowerKey))
|
||||
PlayerPrefs.SetString(SelectedTowerKey, CannonTowerId);
|
||||
if (PlayerPrefs.GetInt(CurrentStageKey, 1) > 1)
|
||||
PlayerPrefs.SetInt(TeslaTowerUnlockedKey, 1);
|
||||
if (!IsTowerUnlocked(PlayerPrefs.GetString(SelectedTowerKey, CannonTowerId)))
|
||||
PlayerPrefs.SetString(SelectedTowerKey, CannonTowerId);
|
||||
|
||||
BuildMenu();
|
||||
ShowHome();
|
||||
@@ -28,7 +34,8 @@ public class MainMenu : MonoBehaviour
|
||||
|
||||
public void StartGame()
|
||||
{
|
||||
PlayerPrefs.SetString(SelectedTowerKey, CannonTowerId);
|
||||
if (!IsTowerUnlocked(PlayerPrefs.GetString(SelectedTowerKey, CannonTowerId)))
|
||||
PlayerPrefs.SetString(SelectedTowerKey, CannonTowerId);
|
||||
PlayerPrefs.Save();
|
||||
SceneManager.LoadScene("SampleScene");
|
||||
}
|
||||
@@ -146,24 +153,44 @@ public class MainMenu : MonoBehaviour
|
||||
TextMeshProUGUI heading = CreateText("Heading", contentRoot, "TOWERS", 26f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
PlaceTop(heading.rectTransform, -34f, 36f);
|
||||
|
||||
RectTransform card = CreatePanel("CannonTowerCard", contentRoot, new Color(0.105f, 0.12f, 0.15f, 1f));
|
||||
PlaceCenter(card, 0f, -10f, 420f, 198f);
|
||||
CreateTowerCard("CannonTowerCard", "CANNON TOWER", "Explosive shells with splash and heavy upgrade scaling.", CannonTowerId, true, -216f);
|
||||
CreateTowerCard("TeslaTowerCard", "TESLA TOWER", "Rapid arcs with longer range and steadier damage.", TeslaTowerId, IsTowerUnlocked(TeslaTowerId), 216f);
|
||||
}
|
||||
|
||||
TextMeshProUGUI name = CreateText("Name", card, "CANNON TOWER", 24f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
void CreateTowerCard(string cardName, string towerName, string descriptionText, string towerId, bool unlocked, float x)
|
||||
{
|
||||
string selectedTower = PlayerPrefs.GetString(SelectedTowerKey, CannonTowerId);
|
||||
bool selected = selectedTower == towerId && unlocked;
|
||||
Color cardColor = unlocked ? new Color(0.105f, 0.12f, 0.15f, 1f) : new Color(0.078f, 0.084f, 0.096f, 1f);
|
||||
|
||||
RectTransform card = CreatePanel(cardName, contentRoot, cardColor);
|
||||
PlaceCenter(card, x, -10f, 196f, 198f);
|
||||
|
||||
TextMeshProUGUI name = CreateText("Name", card, towerName, 20f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
PlaceTop(name.rectTransform, -18f, 34f);
|
||||
name.color = unlocked ? new Color(0.91f, 0.94f, 0.98f, 1f) : new Color(0.58f, 0.62f, 0.68f, 1f);
|
||||
|
||||
TextMeshProUGUI description = CreateText("Description", card, "Single-target bullets with Fire Rate, Range and Damage upgrades.", 16f, FontStyles.Normal, TextAlignmentOptions.Center);
|
||||
PlaceCenter(description.rectTransform, 0f, 18f, 320f, 54f);
|
||||
TextMeshProUGUI description = CreateText("Description", card, unlocked ? descriptionText : "Locked until you beat stage 1.", 14f, FontStyles.Normal, TextAlignmentOptions.Center);
|
||||
PlaceCenter(description.rectTransform, 0f, 18f, 160f, 64f);
|
||||
description.color = unlocked ? new Color(0.78f, 0.83f, 0.9f, 1f) : new Color(0.5f, 0.54f, 0.6f, 1f);
|
||||
|
||||
Button equipButton = CreateButton("EquipButton", card, "EQUIPPED", () =>
|
||||
Button equipButton = CreateButton("EquipButton", card, selected ? "EQUIPPED" : unlocked ? "EQUIP" : "LOCKED", () =>
|
||||
{
|
||||
PlayerPrefs.SetString(SelectedTowerKey, CannonTowerId);
|
||||
if (!unlocked)
|
||||
return;
|
||||
|
||||
PlayerPrefs.SetString(SelectedTowerKey, towerId);
|
||||
PlayerPrefs.Save();
|
||||
ShowTowers();
|
||||
});
|
||||
RectTransform equipRect = equipButton.GetComponent<RectTransform>();
|
||||
PlaceCenter(equipRect, 0f, -54f, 170f, 42f);
|
||||
equipButton.interactable = false;
|
||||
PlaceCenter(equipRect, 0f, -54f, 144f, 42f);
|
||||
equipButton.interactable = unlocked && !selected;
|
||||
}
|
||||
|
||||
static bool IsTowerUnlocked(string towerId)
|
||||
{
|
||||
return towerId == CannonTowerId || (towerId == TeslaTowerId && PlayerPrefs.GetInt(TeslaTowerUnlockedKey, 0) == 1);
|
||||
}
|
||||
|
||||
void SetTabState(bool homeActive)
|
||||
|
||||
+315
-13
@@ -3,6 +3,9 @@ using UnityEngine;
|
||||
|
||||
public class Tower : MonoBehaviour
|
||||
{
|
||||
const string SelectedTowerKey = "SelectedTower";
|
||||
const string TeslaTowerId = "tesla";
|
||||
|
||||
public static Tower Instance;
|
||||
public GameObject bulletPrefab;
|
||||
public int damage = 1;
|
||||
@@ -33,13 +36,40 @@ public class Tower : MonoBehaviour
|
||||
public float volatileSecondaryExplosionMultiplier;
|
||||
public bool glassCannonDesign;
|
||||
public bool apocalypseRound;
|
||||
public bool isTeslaTower;
|
||||
public float teslaDamageMultiplier = 1f;
|
||||
public int teslaChainCount = 1;
|
||||
public float teslaChainFalloff = 0.72f;
|
||||
public float teslaChainRange = 3.25f;
|
||||
public float teslaForkChance;
|
||||
public bool teslaArcBounce;
|
||||
public int teslaSuperconductorStacks;
|
||||
public float teslaSlowMultiplier = 1f;
|
||||
public float teslaSlowDuration;
|
||||
public int teslaParalysisStacks;
|
||||
public float teslaIonizedBonus;
|
||||
public float teslaResidualDamageMultiplier;
|
||||
public int teslaEmpSurgeStacks;
|
||||
public int teslaCapacitorBankStacks;
|
||||
public float teslaOverflowDamage;
|
||||
public bool teslaEnergyOverflow;
|
||||
public float teslaFeedbackLoopMultiplier;
|
||||
public float teslaCriticalExplosionChance;
|
||||
public int teslaFieldStacks;
|
||||
public float teslaMagneticPull;
|
||||
public float teslaVoltageCollapseMultiplier;
|
||||
public int teslaMeltdownStacks;
|
||||
public bool teslaInfiniteArc;
|
||||
public bool teslaStormbringerProtocol;
|
||||
private float nextFireTime;
|
||||
private GameObject currentTarget;
|
||||
private int shotsFired;
|
||||
private float cooldownUntil;
|
||||
private bool cannonBaselineApplied;
|
||||
private bool towerBaselineApplied;
|
||||
private SpriteRenderer rangeCircle;
|
||||
private Camera mainCamera;
|
||||
private float teslaFieldNextTick;
|
||||
private int teslaCapacitorStacks;
|
||||
public int extraBullets = 0; // For "Shoot +1 bullet"
|
||||
public bool homingEnabled = false;
|
||||
public bool ricochetEnabled = false;
|
||||
@@ -47,12 +77,12 @@ public class Tower : MonoBehaviour
|
||||
|
||||
void Awake()
|
||||
{
|
||||
ApplyCannonBaseline();
|
||||
ApplySelectedTowerBaseline();
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
ApplyCannonBaseline();
|
||||
ApplySelectedTowerBaseline();
|
||||
EnsureRangeCircle();
|
||||
FitCameraToRange();
|
||||
}
|
||||
@@ -61,14 +91,15 @@ public class Tower : MonoBehaviour
|
||||
{
|
||||
UpdateRangeCircle();
|
||||
FitCameraToRange();
|
||||
TickTeslaField();
|
||||
|
||||
if (Time.time < cooldownUntil)
|
||||
return;
|
||||
|
||||
if (Time.time >= nextFireTime)
|
||||
{
|
||||
RetargetAndFire();
|
||||
nextFireTime = Time.time + fireRate;
|
||||
float cooldownMultiplier = RetargetAndFire();
|
||||
nextFireTime = Time.time + fireRate * cooldownMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,25 +166,91 @@ public class Tower : MonoBehaviour
|
||||
return Sprite.Create(texture, new Rect(0f, 0f, size, size), new Vector2(0.5f, 0.5f), size);
|
||||
}
|
||||
|
||||
void ApplyCannonBaseline()
|
||||
static void CreateTeslaArc(Vector2 start, Vector2 end)
|
||||
{
|
||||
if (cannonBaselineApplied)
|
||||
return;
|
||||
GameObject arc = new GameObject("TeslaArc", typeof(LineRenderer), typeof(ShockwaveEffect));
|
||||
LineRenderer line = arc.GetComponent<LineRenderer>();
|
||||
line.useWorldSpace = true;
|
||||
line.positionCount = 6;
|
||||
line.startWidth = 0.055f;
|
||||
line.endWidth = 0.018f;
|
||||
line.material = new Material(Shader.Find("Sprites/Default"));
|
||||
line.startColor = new Color(0.5f, 0.9f, 1f, 0.98f);
|
||||
line.endColor = new Color(0.95f, 1f, 1f, 0.2f);
|
||||
|
||||
fireRate *= 1.1f;
|
||||
cannonBaselineApplied = true;
|
||||
for (int i = 0; i < line.positionCount; i++)
|
||||
{
|
||||
float t = (float)i / (line.positionCount - 1);
|
||||
Vector2 point = Vector2.Lerp(start, end, t);
|
||||
if (i > 0 && i < line.positionCount - 1)
|
||||
point += Random.insideUnitCircle * 0.12f;
|
||||
line.SetPosition(i, point);
|
||||
}
|
||||
|
||||
arc.GetComponent<ShockwaveEffect>().Configure(0.12f);
|
||||
}
|
||||
|
||||
void RetargetAndFire()
|
||||
static void CreateTeslaPulseVisual(Vector2 center, float radius)
|
||||
{
|
||||
GameObject ring = new GameObject("TeslaPulse", typeof(LineRenderer), typeof(ShockwaveEffect));
|
||||
ring.transform.position = center;
|
||||
|
||||
LineRenderer line = ring.GetComponent<LineRenderer>();
|
||||
line.useWorldSpace = false;
|
||||
line.loop = true;
|
||||
line.positionCount = 56;
|
||||
line.startWidth = 0.03f;
|
||||
line.endWidth = 0.03f;
|
||||
line.material = new Material(Shader.Find("Sprites/Default"));
|
||||
line.startColor = new Color(0.42f, 0.88f, 1f, 0.78f);
|
||||
line.endColor = new Color(0.42f, 0.88f, 1f, 0.78f);
|
||||
|
||||
for (int i = 0; i < line.positionCount; i++)
|
||||
{
|
||||
float angle = (float)i / line.positionCount * Mathf.PI * 2f;
|
||||
line.SetPosition(i, new Vector3(Mathf.Cos(angle), Mathf.Sin(angle), 0f) * radius);
|
||||
}
|
||||
|
||||
ring.GetComponent<ShockwaveEffect>().Configure(0.25f);
|
||||
}
|
||||
|
||||
void ApplySelectedTowerBaseline()
|
||||
{
|
||||
if (towerBaselineApplied)
|
||||
return;
|
||||
|
||||
string selectedTower = PlayerPrefs.GetString(SelectedTowerKey, "cannon");
|
||||
if (selectedTower == TeslaTowerId)
|
||||
{
|
||||
isTeslaTower = true;
|
||||
damage = Mathf.Max(1, damage);
|
||||
fireRate *= 0.62f;
|
||||
attackRange *= 1.2f;
|
||||
splashRadius *= 0.45f;
|
||||
splashDamageMultiplier *= 0.2f;
|
||||
projectileSpeedMultiplier *= 1.35f;
|
||||
}
|
||||
else
|
||||
{
|
||||
fireRate *= 1.1f;
|
||||
}
|
||||
|
||||
towerBaselineApplied = true;
|
||||
}
|
||||
|
||||
float RetargetAndFire()
|
||||
{
|
||||
currentTarget = GetNearestEnemy();
|
||||
if (currentTarget != null)
|
||||
{
|
||||
FireAtTarget(currentTarget);
|
||||
return isTeslaTower ? FireTeslaAtTarget(currentTarget) : FireAtTarget(currentTarget);
|
||||
}
|
||||
|
||||
teslaCapacitorStacks = 0;
|
||||
return 1f;
|
||||
}
|
||||
|
||||
void FireAtTarget(GameObject target)
|
||||
float FireAtTarget(GameObject target)
|
||||
{
|
||||
shotsFired++;
|
||||
FireShell(target, BuildShellStats());
|
||||
@@ -163,6 +260,211 @@ public class Tower : MonoBehaviour
|
||||
|
||||
if (glassCannonDesign && shotsFired % 10 == 0)
|
||||
cooldownUntil = Time.time + 1.75f;
|
||||
|
||||
return 1f;
|
||||
}
|
||||
|
||||
float FireTeslaAtTarget(GameObject target)
|
||||
{
|
||||
shotsFired++;
|
||||
if (teslaCapacitorBankStacks > 0)
|
||||
teslaCapacitorStacks = Mathf.Min(teslaCapacitorStacks + 1, teslaCapacitorBankStacks * 6);
|
||||
|
||||
List<EnemyHealth> hitEnemies = new List<EnemyHealth>();
|
||||
EnemyHealth targetHealth = target.GetComponent<EnemyHealth>();
|
||||
if (targetHealth == null)
|
||||
return 1f;
|
||||
|
||||
int maxTargets = teslaInfiniteArc ? 64 : Mathf.Max(1, 1 + teslaChainCount);
|
||||
float damageMultiplier = teslaDamageMultiplier * (1f + teslaCapacitorStacks * 0.035f);
|
||||
if (teslaStormbringerProtocol)
|
||||
damageMultiplier *= 1f + Mathf.Min(0.75f, CountEnemiesInRange(transform.position, attackRange) * 0.025f);
|
||||
if (teslaOverflowDamage > 0f)
|
||||
{
|
||||
damageMultiplier += teslaOverflowDamage;
|
||||
teslaOverflowDamage = 0f;
|
||||
}
|
||||
|
||||
Vector2 chainSource = transform.position;
|
||||
EnemyHealth current = targetHealth;
|
||||
int chainIndex = 0;
|
||||
int totalHit = 0;
|
||||
while (current != null && chainIndex < maxTargets)
|
||||
{
|
||||
float falloff = Mathf.Pow(teslaChainFalloff, chainIndex);
|
||||
int dealtDamage = DamageTeslaTarget(current, chainSource, damageMultiplier * falloff, hitEnemies.Count == 0);
|
||||
totalHit++;
|
||||
hitEnemies.Add(current);
|
||||
chainSource = current.transform.position;
|
||||
|
||||
TryForkTesla(current, hitEnemies, damageMultiplier * falloff * 0.62f);
|
||||
|
||||
current = FindNextTeslaTarget(chainSource, hitEnemies);
|
||||
if (current == null && teslaArcBounce && hitEnemies.Count > 1)
|
||||
current = hitEnemies[Random.Range(0, hitEnemies.Count)];
|
||||
|
||||
chainIndex++;
|
||||
if (teslaInfiniteArc && damageMultiplier * Mathf.Pow(teslaChainFalloff, chainIndex) < 0.2f)
|
||||
break;
|
||||
}
|
||||
|
||||
if (totalHit == 1 && teslaSuperconductorStacks > 0)
|
||||
DamageTeslaTarget(targetHealth, transform.position, teslaSuperconductorStacks * 1.2f, false);
|
||||
|
||||
if (teslaEmpSurgeStacks > 0 && shotsFired % 20 == 0)
|
||||
TeslaPulse(transform.position, attackRange * 0.9f, 0.7f + teslaEmpSurgeStacks * 0.35f);
|
||||
|
||||
if (teslaFieldStacks > 0 && Time.time >= teslaFieldNextTick)
|
||||
{
|
||||
teslaFieldNextTick = Time.time + 0.55f;
|
||||
TeslaPulse(transform.position, 1.9f + teslaFieldStacks * 0.7f, 0.22f * teslaFieldStacks);
|
||||
}
|
||||
|
||||
if (teslaMeltdownStacks > 0 && Random.value < 0.06f * teslaMeltdownStacks)
|
||||
cooldownUntil = Time.time + 0.5f;
|
||||
|
||||
float feedback = Mathf.Clamp01(1f - totalHit * teslaFeedbackLoopMultiplier);
|
||||
return Mathf.Clamp(feedback, 0.42f, 1f);
|
||||
}
|
||||
|
||||
int DamageTeslaTarget(EnemyHealth enemyHealth, Vector2 source, float multiplier, bool primary)
|
||||
{
|
||||
if (enemyHealth == null)
|
||||
return 0;
|
||||
|
||||
int healthBefore = enemyHealth.GetCurrentHealth();
|
||||
int shockDamage = Mathf.Max(1, Mathf.RoundToInt(damage * multiplier));
|
||||
CreateTeslaArc(source, enemyHealth.transform.position);
|
||||
bool killed = enemyHealth.TakeDamage(shockDamage);
|
||||
ApplyTeslaControl(enemyHealth);
|
||||
NotifyTeslaHit();
|
||||
|
||||
if (teslaCriticalExplosionChance > 0f && Random.value < teslaCriticalExplosionChance)
|
||||
TeslaPulse(enemyHealth.transform.position, 1.1f, 0.55f);
|
||||
if (killed)
|
||||
{
|
||||
if (teslaEnergyOverflow)
|
||||
teslaOverflowDamage += Mathf.Clamp01((shockDamage - healthBefore) / 5f);
|
||||
if (teslaVoltageCollapseMultiplier > 0f)
|
||||
TeslaPulse(enemyHealth.transform.position, 1.3f, teslaVoltageCollapseMultiplier);
|
||||
}
|
||||
|
||||
return shockDamage;
|
||||
}
|
||||
|
||||
void TryForkTesla(EnemyHealth fromEnemy, List<EnemyHealth> hitEnemies, float multiplier)
|
||||
{
|
||||
if (teslaForkChance <= 0f || Random.value > teslaForkChance || fromEnemy == null)
|
||||
return;
|
||||
|
||||
EnemyHealth forkTarget = FindNextTeslaTarget(fromEnemy.transform.position, hitEnemies);
|
||||
if (forkTarget != null)
|
||||
DamageTeslaTarget(forkTarget, fromEnemy.transform.position, multiplier, false);
|
||||
}
|
||||
|
||||
void ApplyTeslaControl(EnemyHealth enemyHealth)
|
||||
{
|
||||
Enemy enemy = enemyHealth.GetComponent<Enemy>();
|
||||
if (enemy != null)
|
||||
{
|
||||
if (teslaSlowDuration > 0f)
|
||||
enemy.ApplySlow(teslaSlowMultiplier, teslaSlowDuration);
|
||||
if (teslaParalysisStacks > 0 && Random.value < 0.08f * teslaParalysisStacks)
|
||||
enemy.ApplyStun(0.28f + 0.12f * teslaParalysisStacks);
|
||||
if (teslaMagneticPull > 0f)
|
||||
PullEnemiesToward(enemyHealth.transform.position, teslaMagneticPull);
|
||||
}
|
||||
|
||||
if (teslaIonizedBonus > 0f)
|
||||
enemyHealth.ApplyIonized(teslaIonizedBonus, 2.5f);
|
||||
if (teslaResidualDamageMultiplier > 0f)
|
||||
enemyHealth.ApplyResidualCurrent(Mathf.Max(1, Mathf.RoundToInt(damage * teslaResidualDamageMultiplier)), 2.4f, 0.8f);
|
||||
}
|
||||
|
||||
EnemyHealth FindNextTeslaTarget(Vector2 source, List<EnemyHealth> ignored)
|
||||
{
|
||||
EnemyHealth best = null;
|
||||
float bestDistance = float.MaxValue;
|
||||
Collider2D[] colliders = Physics2D.OverlapCircleAll(source, teslaChainRange);
|
||||
foreach (Collider2D collider in colliders)
|
||||
{
|
||||
if (!collider.CompareTag("Enemy"))
|
||||
continue;
|
||||
|
||||
EnemyHealth enemyHealth = collider.GetComponent<EnemyHealth>();
|
||||
if (enemyHealth == null || ignored.Contains(enemyHealth))
|
||||
continue;
|
||||
|
||||
float distance = Vector2.Distance(source, collider.transform.position);
|
||||
if (distance < bestDistance)
|
||||
{
|
||||
best = enemyHealth;
|
||||
bestDistance = distance;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
void TeslaPulse(Vector2 center, float radius, float damageMultiplier)
|
||||
{
|
||||
CreateTeslaPulseVisual(center, radius);
|
||||
Collider2D[] colliders = Physics2D.OverlapCircleAll(center, radius);
|
||||
foreach (Collider2D collider in colliders)
|
||||
{
|
||||
if (!collider.CompareTag("Enemy"))
|
||||
continue;
|
||||
|
||||
EnemyHealth enemyHealth = collider.GetComponent<EnemyHealth>();
|
||||
if (enemyHealth == null)
|
||||
continue;
|
||||
|
||||
enemyHealth.TakeDamage(Mathf.Max(1, Mathf.RoundToInt(damage * teslaDamageMultiplier * damageMultiplier)));
|
||||
ApplyTeslaControl(enemyHealth);
|
||||
}
|
||||
}
|
||||
|
||||
void TickTeslaField()
|
||||
{
|
||||
if (!isTeslaTower || teslaFieldStacks <= 0 || Time.time < teslaFieldNextTick)
|
||||
return;
|
||||
|
||||
teslaFieldNextTick = Time.time + 0.55f;
|
||||
TeslaPulse(transform.position, 1.9f + teslaFieldStacks * 0.7f, 0.22f * teslaFieldStacks);
|
||||
}
|
||||
|
||||
void PullEnemiesToward(Vector2 center, float distance)
|
||||
{
|
||||
Collider2D[] colliders = Physics2D.OverlapCircleAll(center, 1.8f);
|
||||
foreach (Collider2D collider in colliders)
|
||||
{
|
||||
if (!collider.CompareTag("Enemy"))
|
||||
continue;
|
||||
|
||||
Enemy enemy = collider.GetComponent<Enemy>();
|
||||
if (enemy != null)
|
||||
enemy.PullToward(center, distance);
|
||||
}
|
||||
}
|
||||
|
||||
int CountEnemiesInRange(Vector2 center, float radius)
|
||||
{
|
||||
int count = 0;
|
||||
Collider2D[] colliders = Physics2D.OverlapCircleAll(center, radius);
|
||||
foreach (Collider2D collider in colliders)
|
||||
{
|
||||
if (collider.CompareTag("Enemy"))
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void NotifyTeslaHit()
|
||||
{
|
||||
UpgradeManager upgradeManager = FindAnyObjectByType<UpgradeManager>();
|
||||
if (upgradeManager != null)
|
||||
upgradeManager.EnemyHitByBullet();
|
||||
}
|
||||
|
||||
void FireShell(GameObject target, Bullet.ShellStats stats)
|
||||
|
||||
@@ -31,6 +31,31 @@ public class UpgradeCard : MonoBehaviour
|
||||
VolatileMunitions = 22,
|
||||
GlassCannonDesign = 23,
|
||||
ApocalypseRound = 24,
|
||||
HighVoltageCoils = 25,
|
||||
RapidCapacitors = 26,
|
||||
ConductiveReach = 27,
|
||||
ArcStability = 28,
|
||||
OverclockedWiring = 29,
|
||||
ExtendedArc = 30,
|
||||
ForkedLightning = 31,
|
||||
ArcBounce = 32,
|
||||
StormCurrent = 33,
|
||||
Superconductor = 34,
|
||||
StaticCharge = 35,
|
||||
ParalysisField = 36,
|
||||
IonizedArmor = 37,
|
||||
ResidualCurrent = 38,
|
||||
EmpSurge = 39,
|
||||
CapacitorBanks = 40,
|
||||
EnergyOverflow = 41,
|
||||
FeedbackLoop = 42,
|
||||
UnstablePlasma = 43,
|
||||
TeslaField = 44,
|
||||
MagneticStorm = 45,
|
||||
VoltageCollapse = 46,
|
||||
MeltdownCore = 47,
|
||||
InfiniteArc = 48,
|
||||
StormbringerProtocol = 49,
|
||||
}
|
||||
|
||||
|
||||
@@ -123,6 +148,31 @@ public class UpgradeCard : MonoBehaviour
|
||||
case UpgradeType.VolatileMunitions: return "VOLATILE MUNITIONS";
|
||||
case UpgradeType.GlassCannonDesign: return "GLASS CANNON DESIGN";
|
||||
case UpgradeType.ApocalypseRound: return "APOCALYPSE ROUND";
|
||||
case UpgradeType.HighVoltageCoils: return "HIGH VOLTAGE COILS";
|
||||
case UpgradeType.RapidCapacitors: return "RAPID CAPACITORS";
|
||||
case UpgradeType.ConductiveReach: return "CONDUCTIVE REACH";
|
||||
case UpgradeType.ArcStability: return "ARC STABILITY";
|
||||
case UpgradeType.OverclockedWiring: return "OVERCLOCKED WIRING";
|
||||
case UpgradeType.ExtendedArc: return "EXTENDED ARC";
|
||||
case UpgradeType.ForkedLightning: return "FORKED LIGHTNING";
|
||||
case UpgradeType.ArcBounce: return "ARC BOUNCE";
|
||||
case UpgradeType.StormCurrent: return "STORM CURRENT";
|
||||
case UpgradeType.Superconductor: return "SUPERCONDUCTOR";
|
||||
case UpgradeType.StaticCharge: return "STATIC CHARGE";
|
||||
case UpgradeType.ParalysisField: return "PARALYSIS FIELD";
|
||||
case UpgradeType.IonizedArmor: return "IONIZED ARMOR";
|
||||
case UpgradeType.ResidualCurrent: return "RESIDUAL CURRENT";
|
||||
case UpgradeType.EmpSurge: return "EMP SURGE";
|
||||
case UpgradeType.CapacitorBanks: return "CAPACITOR BANKS";
|
||||
case UpgradeType.EnergyOverflow: return "ENERGY OVERFLOW";
|
||||
case UpgradeType.FeedbackLoop: return "FEEDBACK LOOP";
|
||||
case UpgradeType.UnstablePlasma: return "UNSTABLE PLASMA";
|
||||
case UpgradeType.TeslaField: return "TESLA FIELD";
|
||||
case UpgradeType.MagneticStorm: return "MAGNETIC STORM";
|
||||
case UpgradeType.VoltageCollapse: return "VOLTAGE COLLAPSE";
|
||||
case UpgradeType.MeltdownCore: return "MELTDOWN CORE";
|
||||
case UpgradeType.InfiniteArc: return "INFINITE ARC";
|
||||
case UpgradeType.StormbringerProtocol: return "STORMBRINGER PROTOCOL";
|
||||
default: return type.ToString().ToUpperInvariant();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,9 @@ using UnityEngine.UI;
|
||||
|
||||
public class UpgradeManager : MonoBehaviour
|
||||
{
|
||||
const string SelectedTowerKey = "SelectedTower";
|
||||
const string TeslaTowerId = "tesla";
|
||||
|
||||
enum FreeUpgradeType
|
||||
{
|
||||
KillBonus,
|
||||
@@ -77,6 +80,35 @@ public class UpgradeManager : MonoBehaviour
|
||||
Card(UpgradeCard.UpgradeType.ApocalypseRound, "APOCALYPSE ROUND", "Every 20th shot fires an enormous extreme-damage shell.", "Legendary", 1, 14),
|
||||
};
|
||||
|
||||
static readonly CardDefinition[] TeslaCards =
|
||||
{
|
||||
Card(UpgradeCard.UpgradeType.HighVoltageCoils, "HIGH VOLTAGE COILS", "Lightning Damage +12%.", "Common", 8, 3),
|
||||
Card(UpgradeCard.UpgradeType.RapidCapacitors, "RAPID CAPACITORS", "Attack Speed +10%.", "Common", 8, 3),
|
||||
Card(UpgradeCard.UpgradeType.ConductiveReach, "CONDUCTIVE REACH", "Tower Range +15%.", "Common", 5, 3),
|
||||
Card(UpgradeCard.UpgradeType.ArcStability, "ARC STABILITY", "Chain damage falloff reduced.", "Uncommon", 5, 5),
|
||||
Card(UpgradeCard.UpgradeType.OverclockedWiring, "OVERCLOCKED WIRING", "Attack Speed +18%, Range -8%.", "Uncommon", 4, 5),
|
||||
Card(UpgradeCard.UpgradeType.ExtendedArc, "EXTENDED ARC", "Lightning chains to +1 additional enemy.", "Rare", 6, 7),
|
||||
Card(UpgradeCard.UpgradeType.ForkedLightning, "FORKED LIGHTNING", "Each chain has a 20% chance to split into another nearby enemy.", "Rare", 4, 7),
|
||||
Card(UpgradeCard.UpgradeType.ArcBounce, "ARC BOUNCE", "Chains can bounce back to previously hit enemies for reduced damage.", "Epic", 2, 10),
|
||||
Card(UpgradeCard.UpgradeType.StormCurrent, "STORM CURRENT", "Chain distance increased significantly.", "Uncommon", 4, 5),
|
||||
Card(UpgradeCard.UpgradeType.Superconductor, "SUPERCONDUCTOR", "If only one enemy is hit, damage massively increases.", "Epic", 3, 10),
|
||||
Card(UpgradeCard.UpgradeType.StaticCharge, "STATIC CHARGE", "Hit enemies are slowed by 10%.", "Common", 5, 3),
|
||||
Card(UpgradeCard.UpgradeType.ParalysisField, "PARALYSIS FIELD", "Repeated hits briefly stun enemies.", "Epic", 2, 10),
|
||||
Card(UpgradeCard.UpgradeType.IonizedArmor, "IONIZED ARMOR", "Shocked enemies take increased damage from all sources.", "Rare", 4, 7),
|
||||
Card(UpgradeCard.UpgradeType.ResidualCurrent, "RESIDUAL CURRENT", "Enemies continue taking electrical damage over time after being shocked.", "Rare", 5, 7),
|
||||
Card(UpgradeCard.UpgradeType.EmpSurge, "EMP SURGE", "Every 20 attacks emits a large pulse damaging all nearby enemies.", "Epic", 3, 10),
|
||||
Card(UpgradeCard.UpgradeType.CapacitorBanks, "CAPACITOR BANKS", "Consecutive attacks without interruption gain stacking damage.", "Rare", 4, 7),
|
||||
Card(UpgradeCard.UpgradeType.EnergyOverflow, "ENERGY OVERFLOW", "Excess damage from kills transfers into the next chain.", "Rare", 3, 7),
|
||||
Card(UpgradeCard.UpgradeType.FeedbackLoop, "FEEDBACK LOOP", "Each chained enemy slightly reduces reload time for the next shot.", "Rare", 5, 7),
|
||||
Card(UpgradeCard.UpgradeType.UnstablePlasma, "UNSTABLE PLASMA", "Critical strikes create mini lightning explosions.", "Epic", 3, 10),
|
||||
Card(UpgradeCard.UpgradeType.TeslaField, "TESLA FIELD", "Creates a passive electric field around the tower damaging nearby enemies.", "Epic", 3, 10),
|
||||
Card(UpgradeCard.UpgradeType.MagneticStorm, "MAGNETIC STORM", "Shocked enemies slightly pull nearby enemies toward them.", "Epic", 2, 10),
|
||||
Card(UpgradeCard.UpgradeType.VoltageCollapse, "VOLTAGE COLLAPSE", "Enemies killed by lightning explode in a small electrical burst.", "Rare", 4, 7),
|
||||
Card(UpgradeCard.UpgradeType.MeltdownCore, "MELTDOWN CORE", "Massive damage increase, but occasional self-stun after firing.", "Legendary", 2, 14),
|
||||
Card(UpgradeCard.UpgradeType.InfiniteArc, "INFINITE ARC", "Chains no longer have a maximum target count while damage keeps decaying.", "Legendary", 1, 14),
|
||||
Card(UpgradeCard.UpgradeType.StormbringerProtocol, "STORMBRINGER PROTOCOL", "During high enemy density, attack speed ramps up dramatically.", "Legendary", 1, 14),
|
||||
};
|
||||
|
||||
void Start()
|
||||
{
|
||||
InitializeUpgrades();
|
||||
@@ -142,7 +174,7 @@ public class UpgradeManager : MonoBehaviour
|
||||
|
||||
void InitializeUpgrades()
|
||||
{
|
||||
foreach (CardDefinition card in CannonCards)
|
||||
foreach (CardDefinition card in GetActiveCards())
|
||||
{
|
||||
if (!upgradeLevels.ContainsKey(card.type))
|
||||
upgradeLevels[card.type] = 0;
|
||||
@@ -154,7 +186,7 @@ public class UpgradeManager : MonoBehaviour
|
||||
List<UpgradeOffer> BuildOffers(bool guaranteeEpicOrHigher)
|
||||
{
|
||||
List<UpgradeOffer> paidOffers = new List<UpgradeOffer>();
|
||||
foreach (CardDefinition card in CannonCards)
|
||||
foreach (CardDefinition card in GetActiveCards())
|
||||
{
|
||||
int cost = upgradeCosts[card.type];
|
||||
if (bits < cost || IsAtMaxStacks(card.type))
|
||||
@@ -518,6 +550,86 @@ public class UpgradeManager : MonoBehaviour
|
||||
case UpgradeCard.UpgradeType.ApocalypseRound:
|
||||
tower.apocalypseRound = true;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.HighVoltageCoils:
|
||||
tower.teslaDamageMultiplier *= 1.12f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.RapidCapacitors:
|
||||
tower.fireRate *= 0.9f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ConductiveReach:
|
||||
tower.attackRange *= 1.15f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ArcStability:
|
||||
tower.teslaChainFalloff = Mathf.Min(0.94f, tower.teslaChainFalloff + 0.045f);
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.OverclockedWiring:
|
||||
tower.fireRate *= 0.82f;
|
||||
tower.attackRange *= 0.92f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ExtendedArc:
|
||||
tower.teslaChainCount++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ForkedLightning:
|
||||
tower.teslaForkChance += 0.2f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ArcBounce:
|
||||
tower.teslaArcBounce = true;
|
||||
tower.teslaChainFalloff = Mathf.Min(0.96f, tower.teslaChainFalloff + 0.04f);
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.StormCurrent:
|
||||
tower.teslaChainRange *= 1.28f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.Superconductor:
|
||||
tower.teslaSuperconductorStacks++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.StaticCharge:
|
||||
tower.teslaSlowMultiplier = Mathf.Min(tower.teslaSlowMultiplier, Mathf.Max(0.45f, 0.9f - 0.06f * upgradeLevels[type]));
|
||||
tower.teslaSlowDuration = Mathf.Max(tower.teslaSlowDuration, 1.5f);
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ParalysisField:
|
||||
tower.teslaParalysisStacks++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.IonizedArmor:
|
||||
tower.teslaIonizedBonus += 0.12f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.ResidualCurrent:
|
||||
tower.teslaResidualDamageMultiplier += 0.14f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.EmpSurge:
|
||||
tower.teslaEmpSurgeStacks++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.CapacitorBanks:
|
||||
tower.teslaCapacitorBankStacks++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.EnergyOverflow:
|
||||
tower.teslaEnergyOverflow = true;
|
||||
tower.teslaDamageMultiplier *= 1.06f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.FeedbackLoop:
|
||||
tower.teslaFeedbackLoopMultiplier += 0.025f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.UnstablePlasma:
|
||||
tower.teslaCriticalExplosionChance += 0.12f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.TeslaField:
|
||||
tower.teslaFieldStacks++;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.MagneticStorm:
|
||||
tower.teslaMagneticPull += 0.08f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.VoltageCollapse:
|
||||
tower.teslaVoltageCollapseMultiplier += 0.22f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.MeltdownCore:
|
||||
tower.teslaMeltdownStacks++;
|
||||
tower.teslaDamageMultiplier *= 1.55f;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.InfiniteArc:
|
||||
tower.teslaInfiniteArc = true;
|
||||
break;
|
||||
case UpgradeCard.UpgradeType.StormbringerProtocol:
|
||||
tower.teslaStormbringerProtocol = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,7 +752,7 @@ public class UpgradeManager : MonoBehaviour
|
||||
image.color = new Color(0f, 0f, 0f, 0.74f);
|
||||
image.raycastTarget = true;
|
||||
|
||||
string titleText = freeFallback ? "LOW BITS: CHOOSE SUPPLY BOOST" : "CHOOSE CANNON UPGRADE";
|
||||
string titleText = freeFallback ? "LOW BITS: CHOOSE SUPPLY BOOST" : IsTeslaTowerSelected() ? "CHOOSE TESLA UPGRADE" : "CHOOSE CANNON UPGRADE";
|
||||
TextMeshProUGUI title = CreateText("Title", rect, titleText, 28f, FontStyles.Bold, TextAlignmentOptions.Center);
|
||||
RectTransform titleRect = title.rectTransform;
|
||||
titleRect.anchorMin = new Vector2(0.5f, 0.5f);
|
||||
@@ -682,9 +794,25 @@ public class UpgradeManager : MonoBehaviour
|
||||
return card;
|
||||
}
|
||||
|
||||
foreach (CardDefinition card in TeslaCards)
|
||||
{
|
||||
if (card.type == type)
|
||||
return card;
|
||||
}
|
||||
|
||||
return CannonCards[0];
|
||||
}
|
||||
|
||||
static CardDefinition[] GetActiveCards()
|
||||
{
|
||||
return IsTeslaTowerSelected() ? TeslaCards : CannonCards;
|
||||
}
|
||||
|
||||
static bool IsTeslaTowerSelected()
|
||||
{
|
||||
return PlayerPrefs.GetString(SelectedTowerKey, "cannon") == TeslaTowerId;
|
||||
}
|
||||
|
||||
static RectTransform CreateRect(string name, Transform parent)
|
||||
{
|
||||
GameObject obj = new GameObject(name, typeof(RectTransform));
|
||||
|
||||
Reference in New Issue
Block a user