summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Assembly_CSharp/.vs/ProjectEvaluation/roguetower.metadata.v5.2bin184148 -> 199149 bytes
-rw-r--r--Assembly_CSharp/.vs/ProjectEvaluation/roguetower.projects.v5.2bin129960 -> 390446 bytes
-rw-r--r--Assembly_CSharp/.vs/RogueTower/DesignTimeBuild/.dtbcache.v2bin85755 -> 85755 bytes
-rw-r--r--Assembly_CSharp/.vs/RogueTower/FileContentIndex/53548182-5dd3-46a3-8253-f9dcdfb12a9f.vsidxbin113771 -> 0 bytes
-rw-r--r--Assembly_CSharp/.vs/RogueTower/FileContentIndex/543ecb80-83ee-4e8b-a04a-2776c4c9ef4f.vsidxbin102255 -> 0 bytes
-rw-r--r--Assembly_CSharp/.vs/RogueTower/v17/.suobin71680 -> 123904 bytes
-rw-r--r--Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.GeneratedMSBuildEditorConfig.editorconfig2
-rw-r--r--Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.assets.cachebin505 -> 505 bytes
-rw-r--r--Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.csproj.AssemblyReference.cachebin230696 -> 130745 bytes
-rw-r--r--Assembly_CSharp/obj/RogueTower.csproj.nuget.dgspec.json10
-rw-r--r--Assembly_CSharp/obj/project.assets.json6
-rw-r--r--Assembly_CSharp/obj/project.nuget.cache4
-rw-r--r--GameCode/.NETStandard,Version=v2.0.AssemblyAttributes.cs4
-rw-r--r--GameCode/AchievementManager.cs424
-rw-r--r--GameCode/AssemblyInfo.cs5
-rw-r--r--GameCode/AudioPoolSource.cs40
-rw-r--r--GameCode/BattleCry.cs84
-rw-r--r--GameCode/BiPlane.cs161
-rw-r--r--GameCode/BuildButtonUI.cs107
-rw-r--r--GameCode/BuildingGhost.cs13
-rw-r--r--GameCode/BuildingManager.cs163
-rw-r--r--GameCode/CameraController.cs96
-rw-r--r--GameCode/CardManager.cs177
-rw-r--r--GameCode/DOTUpgradeCard.cs109
-rw-r--r--GameCode/DamageNumber.cs138
-rw-r--r--GameCode/DamageTracker.cs101
-rw-r--r--GameCode/DevelopmentManager.cs23
-rw-r--r--GameCode/Dropper.cs126
-rw-r--r--GameCode/Encampment.cs26
-rw-r--r--GameCode/Enemy.cs669
-rw-r--r--GameCode/Explosion.cs27
-rw-r--r--GameCode/FlameThrower.cs68
-rw-r--r--GameCode/GameManager.cs476
-rw-r--r--GameCode/GoldRushCard.cs17
-rw-r--r--GameCode/Grave.cs3
-rw-r--r--GameCode/HauntedHouse.cs111
-rw-r--r--GameCode/HauntedHouseUpgrade.cs8
-rw-r--r--GameCode/HealthBar.cs157
-rw-r--r--GameCode/House.cs49
-rw-r--r--GameCode/IBuildable.cs8
-rw-r--r--GameCode/IDamageable.cs4
-rw-r--r--GameCode/IncomeGenerator.cs39
-rw-r--r--GameCode/IronVein.cs3
-rw-r--r--GameCode/KeyDisappear.cs61
-rw-r--r--GameCode/Landmine.cs67
-rw-r--r--GameCode/LevelLoader.cs116
-rw-r--r--GameCode/LightManager.cs62
-rw-r--r--GameCode/Lookout.cs97
-rw-r--r--GameCode/MainMenu.cs16
-rw-r--r--GameCode/ManaBank.cs68
-rw-r--r--GameCode/ManaCrystal.cs3
-rw-r--r--GameCode/ManaGenUpgradeCard.cs21
-rw-r--r--GameCode/ManaSiphon.cs92
-rw-r--r--GameCode/MaxHealthUpgradeCard.cs13
-rw-r--r--GameCode/Mine.cs81
-rw-r--r--GameCode/MonsterManager.cs43
-rw-r--r--GameCode/MonsterManualEntry.cs63
-rw-r--r--GameCode/MonsterUpgradeCard.cs57
-rw-r--r--GameCode/Morter.cs39
-rw-r--r--GameCode/MorterShell.cs81
-rw-r--r--GameCode/MusicManager.cs164
-rw-r--r--GameCode/Obelisk.cs99
-rw-r--r--GameCode/ObjectPool.cs58
-rw-r--r--GameCode/OptionsMenu.cs187
-rw-r--r--GameCode/ParticleBeam.cs19
-rw-r--r--GameCode/Pathfinder.cs91
-rw-r--r--GameCode/PauseMenu.cs140
-rw-r--r--GameCode/PlayMenu.cs27
-rw-r--r--GameCode/Projectile.cs141
-rw-r--r--GameCode/ProjectileFX.cs47
-rw-r--r--GameCode/RadarTower.cs71
-rw-r--r--GameCode/ResourceManager.cs225
-rw-r--r--GameCode/RogueTower.csproj260
-rw-r--r--GameCode/SFXManager.cs158
-rw-r--r--GameCode/Sawblade.cs87
-rw-r--r--GameCode/Shrine.cs11
-rw-r--r--GameCode/SimpleUI.cs51
-rw-r--r--GameCode/SlowRotate.cs12
-rw-r--r--GameCode/SnowFlake.cs23
-rw-r--r--GameCode/SocialMediaManager.cs9
-rw-r--r--GameCode/Sound.cs22
-rw-r--r--GameCode/SpawnManager.cs495
-rw-r--r--GameCode/SpawnableObject.cs54
-rw-r--r--GameCode/SteamManager.cs123
-rw-r--r--GameCode/TerrainTile.cs87
-rw-r--r--GameCode/TeslaCoil.cs45
-rw-r--r--GameCode/TileManager.cs236
-rw-r--r--GameCode/TileSpawnLocation.cs27
-rw-r--r--GameCode/Tower.cs509
-rw-r--r--GameCode/TowerFlyweight.cs279
-rw-r--r--GameCode/TowerManager.cs1386
-rw-r--r--GameCode/TowerType.cs18
-rw-r--r--GameCode/TowerUI.cs289
-rw-r--r--GameCode/TowerUnlockCard.cs16
-rw-r--r--GameCode/TowerUnlockManager.cs54
-rw-r--r--GameCode/TowerUpgradeCard.cs117
-rw-r--r--GameCode/TreasureChest.cs26
-rw-r--r--GameCode/TreasureUI.cs30
-rw-r--r--GameCode/UICameraController.cs67
-rw-r--r--GameCode/UIManager.cs47
-rw-r--r--GameCode/UniBonusUI.cs46
-rw-r--r--GameCode/University.cs150
-rw-r--r--GameCode/UniversityUI.cs135
-rw-r--r--GameCode/UniversityUpgrade.cs13
-rw-r--r--GameCode/UpgradeButton.cs167
-rw-r--r--GameCode/UpgradeCard.cs37
-rw-r--r--GameCode/UpgradeManager.cs120
-rw-r--r--GameCode/WalkAnimator.cs55
-rw-r--r--GameCode/Waypoint.cs62
109 files changed, 11019 insertions, 11 deletions
diff --git a/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.metadata.v5.2 b/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.metadata.v5.2
index f9a914b..b0cfd99 100644
--- a/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.metadata.v5.2
+++ b/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.metadata.v5.2
Binary files differ
diff --git a/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.projects.v5.2 b/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.projects.v5.2
index bc7c288..5fb5798 100644
--- a/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.projects.v5.2
+++ b/Assembly_CSharp/.vs/ProjectEvaluation/roguetower.projects.v5.2
Binary files differ
diff --git a/Assembly_CSharp/.vs/RogueTower/DesignTimeBuild/.dtbcache.v2 b/Assembly_CSharp/.vs/RogueTower/DesignTimeBuild/.dtbcache.v2
index 185c6d1..a9ca918 100644
--- a/Assembly_CSharp/.vs/RogueTower/DesignTimeBuild/.dtbcache.v2
+++ b/Assembly_CSharp/.vs/RogueTower/DesignTimeBuild/.dtbcache.v2
Binary files differ
diff --git a/Assembly_CSharp/.vs/RogueTower/FileContentIndex/53548182-5dd3-46a3-8253-f9dcdfb12a9f.vsidx b/Assembly_CSharp/.vs/RogueTower/FileContentIndex/53548182-5dd3-46a3-8253-f9dcdfb12a9f.vsidx
deleted file mode 100644
index 1b7038b..0000000
--- a/Assembly_CSharp/.vs/RogueTower/FileContentIndex/53548182-5dd3-46a3-8253-f9dcdfb12a9f.vsidx
+++ /dev/null
Binary files differ
diff --git a/Assembly_CSharp/.vs/RogueTower/FileContentIndex/543ecb80-83ee-4e8b-a04a-2776c4c9ef4f.vsidx b/Assembly_CSharp/.vs/RogueTower/FileContentIndex/543ecb80-83ee-4e8b-a04a-2776c4c9ef4f.vsidx
deleted file mode 100644
index 2f00599..0000000
--- a/Assembly_CSharp/.vs/RogueTower/FileContentIndex/543ecb80-83ee-4e8b-a04a-2776c4c9ef4f.vsidx
+++ /dev/null
Binary files differ
diff --git a/Assembly_CSharp/.vs/RogueTower/v17/.suo b/Assembly_CSharp/.vs/RogueTower/v17/.suo
index 69249e8..7693ec6 100644
--- a/Assembly_CSharp/.vs/RogueTower/v17/.suo
+++ b/Assembly_CSharp/.vs/RogueTower/v17/.suo
Binary files differ
diff --git a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.GeneratedMSBuildEditorConfig.editorconfig b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.GeneratedMSBuildEditorConfig.editorconfig
index a8e9be8..8aa5c36 100644
--- a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.GeneratedMSBuildEditorConfig.editorconfig
+++ b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.GeneratedMSBuildEditorConfig.editorconfig
@@ -1,3 +1,3 @@
is_global = true
build_property.RootNamespace = RogueTower
-build_property.ProjectDir = D:\Games\_PC\_Mono\Rogue Tower\Decompile\Assembly_CSharp\
+build_property.ProjectDir = D:\Games\PC\_Mono\Rogue Tower\Decompile\Assembly_CSharp\
diff --git a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.assets.cache b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.assets.cache
index b8ef08a..af085e0 100644
--- a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.assets.cache
+++ b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.assets.cache
Binary files differ
diff --git a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.csproj.AssemblyReference.cache b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.csproj.AssemblyReference.cache
index 4ac6ac6..2a05939 100644
--- a/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.csproj.AssemblyReference.cache
+++ b/Assembly_CSharp/obj/Debug/netstandard2.0/RogueTower.csproj.AssemblyReference.cache
Binary files differ
diff --git a/Assembly_CSharp/obj/RogueTower.csproj.nuget.dgspec.json b/Assembly_CSharp/obj/RogueTower.csproj.nuget.dgspec.json
index 5f2465c..31878da 100644
--- a/Assembly_CSharp/obj/RogueTower.csproj.nuget.dgspec.json
+++ b/Assembly_CSharp/obj/RogueTower.csproj.nuget.dgspec.json
@@ -1,17 +1,17 @@
{
"format": 1,
"restore": {
- "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj": {}
+ "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj": {}
},
"projects": {
- "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj": {
+ "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj": {
"version": "1.0.0",
"restore": {
- "projectUniqueName": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
+ "projectUniqueName": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
"projectName": "Assembly-CSharp",
- "projectPath": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
+ "projectPath": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
- "outputPath": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\obj\\",
+ "outputPath": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
diff --git a/Assembly_CSharp/obj/project.assets.json b/Assembly_CSharp/obj/project.assets.json
index 38d8907..926b7af 100644
--- a/Assembly_CSharp/obj/project.assets.json
+++ b/Assembly_CSharp/obj/project.assets.json
@@ -186,11 +186,11 @@
"project": {
"version": "1.0.0",
"restore": {
- "projectUniqueName": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
+ "projectUniqueName": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
"projectName": "Assembly-CSharp",
- "projectPath": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
+ "projectPath": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
"packagesPath": "C:\\Users\\Administrator\\.nuget\\packages\\",
- "outputPath": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\obj\\",
+ "outputPath": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
diff --git a/Assembly_CSharp/obj/project.nuget.cache b/Assembly_CSharp/obj/project.nuget.cache
index 3d5d096..c9729cd 100644
--- a/Assembly_CSharp/obj/project.nuget.cache
+++ b/Assembly_CSharp/obj/project.nuget.cache
@@ -1,8 +1,8 @@
{
"version": 2,
- "dgSpecHash": "TXdjMDGnAcXpY8OX7QNyMyANBH85Ib9TBuPLsvYCe4Y195KcyQG5bt2WeTPgYc/ukbn2xXIEKovN3smCGtxa1w==",
+ "dgSpecHash": "+X9xFnoe2YNyXySc25Xl3dPzed7x+O7/vrAZMkSQSivo71Bz95RqS6JTrVV8bJpnKDKLegjGlRbuCMi+oCMOog==",
"success": true,
- "projectFilePath": "D:\\Games\\_PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
+ "projectFilePath": "D:\\Games\\PC\\_Mono\\Rogue Tower\\Decompile\\Assembly_CSharp\\RogueTower.csproj",
"expectedPackageFiles": [
"C:\\Users\\Administrator\\.nuget\\packages\\microsoft.netcore.platforms\\1.1.0\\microsoft.netcore.platforms.1.1.0.nupkg.sha512",
"C:\\Users\\Administrator\\.nuget\\packages\\netstandard.library\\2.0.3\\netstandard.library.2.0.3.nupkg.sha512"
diff --git a/GameCode/.NETStandard,Version=v2.0.AssemblyAttributes.cs b/GameCode/.NETStandard,Version=v2.0.AssemblyAttributes.cs
new file mode 100644
index 0000000..8bf3a42
--- /dev/null
+++ b/GameCode/.NETStandard,Version=v2.0.AssemblyAttributes.cs
@@ -0,0 +1,4 @@
+// <autogenerated />
+using System;
+using System.Reflection;
+[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
diff --git a/GameCode/AchievementManager.cs b/GameCode/AchievementManager.cs
new file mode 100644
index 0000000..a189341
--- /dev/null
+++ b/GameCode/AchievementManager.cs
@@ -0,0 +1,424 @@
+using Steamworks;
+using UnityEngine;
+
+public class AchievementManager : MonoBehaviour
+{
+ public static AchievementManager instance;
+
+ private int goldSpentOnResearch;
+
+ private int discoveriesMade;
+
+ private int enemiesKilled;
+
+ [SerializeField]
+ private string[] towerUnlockStrings;
+
+ public bool shrineSpawned;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ if (SteamManager.Initialized)
+ {
+ SteamUserStats.GetStat("EnemiesKilled", out enemiesKilled);
+ SteamUserStats.GetStat("DiscoveriesMade", out discoveriesMade);
+ SteamUserStats.GetStat("GoldOnResearch", out goldSpentOnResearch);
+ }
+ }
+
+ public void OnSceneClose()
+ {
+ if (SteamManager.Initialized)
+ {
+ SteamUserStats.SetStat("EnemiesKilled", enemiesKilled);
+ SteamUserStats.SetStat("DiscoveriesMade", discoveriesMade);
+ SteamUserStats.SetStat("GoldOnResearch", goldSpentOnResearch);
+ SteamUserStats.StoreStats();
+ }
+ }
+
+ public void ResetSteamStats()
+ {
+ SteamUserStats.ResetAllStats(bAchievementsToo: true);
+ }
+
+ public void FundResearchAchievement(int amt)
+ {
+ if (goldSpentOnResearch < 100000 && goldSpentOnResearch + amt >= 100000)
+ {
+ UnlockAchievement("Funding100000");
+ }
+ else if (goldSpentOnResearch < 10000 && goldSpentOnResearch + amt >= 10000)
+ {
+ UnlockAchievement("Funding10000");
+ }
+ else if (goldSpentOnResearch < 1000 && goldSpentOnResearch + amt >= 1000)
+ {
+ UnlockAchievement("Funding1000");
+ }
+ goldSpentOnResearch += amt;
+ }
+
+ public void NewDiscoveriesAchievement()
+ {
+ discoveriesMade++;
+ if (discoveriesMade == 100)
+ {
+ UnlockAchievement("Discover100");
+ }
+ else if (discoveriesMade == 10)
+ {
+ UnlockAchievement("Discover10");
+ }
+ else if (discoveriesMade == 1)
+ {
+ UnlockAchievement("Discover1");
+ }
+ }
+
+ public void EnemyKilled()
+ {
+ enemiesKilled++;
+ if (enemiesKilled == 1000000)
+ {
+ UnlockAchievement("Kill1000000Enemies");
+ }
+ else if (enemiesKilled == 100000)
+ {
+ UnlockAchievement("Kill100000Enemies");
+ }
+ else if (enemiesKilled == 10000)
+ {
+ UnlockAchievement("Kill10000Enemies");
+ }
+ else if (enemiesKilled == 1000)
+ {
+ UnlockAchievement("Kill1000Enemies");
+ }
+ else if (enemiesKilled == 100)
+ {
+ UnlockAchievement("Kill100Enemies");
+ }
+ }
+
+ public void UnlockAchievement(string s)
+ {
+ if (SteamManager.Initialized)
+ {
+ Debug.Log("Unlocked Achievement: " + s);
+ SteamUserStats.SetAchievement(s);
+ SteamUserStats.StoreStats();
+ }
+ }
+
+ public void CheckTowerUnlocks()
+ {
+ int num = 0;
+ string[] array = towerUnlockStrings;
+ foreach (string key in array)
+ {
+ num += PlayerPrefs.GetInt(key, 0);
+ }
+ if (num == towerUnlockStrings.Length)
+ {
+ UnlockAchievement("UnlockAllTowers");
+ }
+ }
+
+ public void BeatLevel(int level)
+ {
+ int gameMode = GameManager.instance.gameMode;
+ if (level == 15)
+ {
+ switch (gameMode)
+ {
+ case 1:
+ UnlockAchievement("BeatLevel15Mode1");
+ break;
+ case 2:
+ UnlockAchievement("BeatLevel15Mode2");
+ break;
+ case 3:
+ UnlockAchievement("BeatLevel15Mode3");
+ break;
+ }
+ }
+ if (level == 25)
+ {
+ switch (gameMode)
+ {
+ case 1:
+ UnlockAchievement("BeatLevel25Mode1");
+ break;
+ case 2:
+ UnlockAchievement("BeatLevel25Mode2");
+ break;
+ case 3:
+ UnlockAchievement("BeatLevel25Mode3");
+ break;
+ }
+ }
+ if (level == 35)
+ {
+ switch (gameMode)
+ {
+ case 1:
+ UnlockAchievement("BeatLevel35Mode1");
+ break;
+ case 2:
+ UnlockAchievement("BeatLevel35Mode2");
+ break;
+ case 3:
+ UnlockAchievement("BeatLevel35Mode3");
+ break;
+ }
+ if (!shrineSpawned)
+ {
+ UnlockAchievement("NoShrines");
+ }
+ }
+ if (level == 45)
+ {
+ switch (gameMode)
+ {
+ case 1:
+ UnlockAchievement("BeatLevel45Mode1");
+ break;
+ case 2:
+ UnlockAchievement("BeatLevel45Mode2");
+ break;
+ case 3:
+ UnlockAchievement("BeatLevel45Mode3");
+ break;
+ }
+ }
+ }
+
+ public void TowerLevel(int level)
+ {
+ switch (level)
+ {
+ case 10:
+ UnlockAchievement("TowerLevel10");
+ break;
+ case 20:
+ UnlockAchievement("TowerLevel20");
+ break;
+ case 50:
+ UnlockAchievement("TowerLevel50");
+ break;
+ }
+ }
+
+ public void CheckTowerTypesVictory()
+ {
+ int gameMode = GameManager.instance.gameMode;
+ if (TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Count <= 1)
+ {
+ UnlockAchievement("OnlyBallista");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallista2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallista3");
+ }
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Crossbow))
+ {
+ UnlockAchievement("NoBallista");
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Morter) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Morter) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaMortar");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaMortar2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaMortar3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.TeslaCoil) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.TeslaCoil) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaTesla");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaTesla2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaTesla3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Frost) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Frost) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaFrost");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaFrost2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaFrost3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.FlameThrower) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.FlameThrower) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaFlameThrower");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaFlameThrower2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaFlameThrower3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.PoisonSprayer) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.PoisonSprayer) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaPoisonSprayer");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaPoisonSprayer2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaPoisonSprayer3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Shredder) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Shredder) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaShredder");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaShredder2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaShredder3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Encampment) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Encampment) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaEncampment");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaEncampment2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaEncampment3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Lookout) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Lookout) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaLookout");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaLookout2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaLookout3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Radar) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Radar) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaRadar");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaRadar2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaRadar3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.Obelisk) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.Obelisk) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaObelisk");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaObelisk2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaObelisk3");
+ }
+ }
+ if ((TowerManager.instance.towersUsed.Contains(TowerType.Crossbow) && TowerManager.instance.towersUsed.Contains(TowerType.ParticleCannon) && TowerManager.instance.towersUsed.Count <= 2) || (TowerManager.instance.towersUsed.Contains(TowerType.ParticleCannon) && TowerManager.instance.towersUsed.Count <= 1))
+ {
+ UnlockAchievement("OnlyBallistaParticleCannon");
+ if (gameMode >= 2)
+ {
+ UnlockAchievement("OnlyBallistaParticleCannon2");
+ }
+ if (gameMode >= 3)
+ {
+ UnlockAchievement("OnlyBallistaParticleCannon3");
+ }
+ }
+ }
+
+ public bool CheckAllTowers()
+ {
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Crossbow))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Morter))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.TeslaCoil))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Frost))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.FlameThrower))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.PoisonSprayer))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Shredder))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Encampment))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Lookout))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Radar))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.Obelisk))
+ {
+ return false;
+ }
+ if (!TowerManager.instance.towersUsed.Contains(TowerType.ParticleCannon))
+ {
+ return false;
+ }
+ UnlockAchievement("UsedAllTowers");
+ return true;
+ }
+}
diff --git a/GameCode/AssemblyInfo.cs b/GameCode/AssemblyInfo.cs
new file mode 100644
index 0000000..bbd7eef
--- /dev/null
+++ b/GameCode/AssemblyInfo.cs
@@ -0,0 +1,5 @@
+using System.Diagnostics;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+[assembly: AssemblyVersion("0.0.0.0")]
diff --git a/GameCode/AudioPoolSource.cs b/GameCode/AudioPoolSource.cs
new file mode 100644
index 0000000..883151d
--- /dev/null
+++ b/GameCode/AudioPoolSource.cs
@@ -0,0 +1,40 @@
+using UnityEngine;
+
+public class AudioPoolSource : MonoBehaviour
+{
+ [SerializeField]
+ private AudioSource audioS;
+
+ private float timer = -1f;
+
+ private bool active;
+
+ public void PlayClip(AudioClip clip, float volume, float pitchVariance)
+ {
+ audioS.Stop();
+ audioS.clip = clip;
+ audioS.pitch = 1f + Random.Range(0f - pitchVariance, pitchVariance);
+ audioS.volume = volume;
+ timer = clip.length + 0.1f;
+ active = true;
+ audioS.Play();
+ }
+
+ private void Update()
+ {
+ if (!active)
+ {
+ return;
+ }
+ timer -= Time.deltaTime;
+ if (timer <= 0f)
+ {
+ active = false;
+ if (base.transform.parent != null)
+ {
+ base.transform.parent = null;
+ }
+ SFXManager.instance.sources.Add(this);
+ }
+ }
+}
diff --git a/GameCode/BattleCry.cs b/GameCode/BattleCry.cs
new file mode 100644
index 0000000..f76a756
--- /dev/null
+++ b/GameCode/BattleCry.cs
@@ -0,0 +1,84 @@
+using UnityEngine;
+
+public class BattleCry : MonoBehaviour
+{
+ public enum BattleCryTrigger
+ {
+ Death,
+ Spawn,
+ NearEnd,
+ ArmorBreak,
+ ShieldBreak
+ }
+
+ [SerializeField]
+ private LayerMask enemyLayerMask;
+
+ [SerializeField]
+ private string battleCryText = "Battle Cry!";
+
+ [SerializeField]
+ private float cryRadius;
+
+ [SerializeField]
+ private BattleCryTrigger myTrigger;
+
+ [SerializeField]
+ private float fortifyTime;
+
+ [SerializeField]
+ private float hastePercentage;
+
+ [SerializeField]
+ private GameObject[] enemiesToSpawn;
+
+ private bool triggered;
+
+ public void CheckBattleCry(BattleCryTrigger source)
+ {
+ if (source == myTrigger && !triggered)
+ {
+ Cry();
+ }
+ }
+
+ private void Cry()
+ {
+ if (enemiesToSpawn.Length != 0)
+ {
+ Waypoint currentWaypoint = GetComponent<Pathfinder>().currentWaypoint;
+ GameObject[] array = enemiesToSpawn;
+ for (int i = 0; i < array.Length; i++)
+ {
+ Enemy component = Object.Instantiate(array[i], base.transform.position + new Vector3(Random.Range(-0.5f, 0.5f), 0f, Random.Range(-0.5f, 0.5f)), Quaternion.identity).GetComponent<Enemy>();
+ component.SetStats();
+ component.SetFirstSpawnPoint(currentWaypoint);
+ SpawnManager.instance.currentEnemies.Add(component);
+ }
+ }
+ DamageNumber component2 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component2.SetText(battleCryText, "Grey", 1f);
+ component2.SetHoldTime(2f);
+ if (myTrigger != 0)
+ {
+ component2.transform.parent = base.transform;
+ }
+ Collider[] array2 = Physics.OverlapSphere(base.transform.position, cryRadius, enemyLayerMask, QueryTriggerInteraction.Collide);
+ for (int i = 0; i < array2.Length; i++)
+ {
+ Enemy component3 = array2[i].GetComponent<Enemy>();
+ if (component3 != null)
+ {
+ if (fortifyTime > 0f)
+ {
+ component3.Fortify(fortifyTime);
+ }
+ if (hastePercentage > 0f)
+ {
+ component3.AddHaste(hastePercentage);
+ }
+ }
+ }
+ triggered = true;
+ }
+}
diff --git a/GameCode/BiPlane.cs b/GameCode/BiPlane.cs
new file mode 100644
index 0000000..bcf32e5
--- /dev/null
+++ b/GameCode/BiPlane.cs
@@ -0,0 +1,161 @@
+using UnityEngine;
+
+public class BiPlane : MonoBehaviour
+{
+ [SerializeField]
+ private TowerType towerType;
+
+ [SerializeField]
+ private float speed;
+
+ [SerializeField]
+ private float turnTime = 3f;
+
+ [SerializeField]
+ private float minDistance = 3f;
+
+ [SerializeField]
+ private float maxDistance = 12f;
+
+ [SerializeField]
+ private float attackDistance = 6f;
+
+ public GameObject target;
+
+ private int directionMultiplier = 1;
+
+ private Vector3 previousPos;
+
+ private bool flyingTowards = true;
+
+ private float desiredAltitude = 5f;
+
+ private float desiredTilt;
+
+ public int damage;
+
+ public int healthDamage;
+
+ public int armorDamage;
+
+ public int shieldDamage;
+
+ public float rps;
+
+ public float slowPercent;
+
+ public float bleedPercent;
+
+ public float burnPercent;
+
+ public float poisonPercent;
+
+ public float critChance;
+
+ public float stunChance;
+
+ private int ammo;
+
+ [SerializeField]
+ private int maxAmmo = 20;
+
+ private float timeOfLastShot;
+
+ [SerializeField]
+ private LayerMask bulletHitMask;
+
+ [SerializeField]
+ private GameObject projectile;
+
+ [SerializeField]
+ private float projectileSpeed;
+
+ [SerializeField]
+ private Transform muzzle;
+
+ [SerializeField]
+ private Transform artTransform;
+
+ private void Start()
+ {
+ ammo = maxAmmo;
+ }
+
+ private void Update()
+ {
+ FlightPath();
+ }
+
+ private void FixedUpdate()
+ {
+ }
+
+ private void FlightPath()
+ {
+ Vector3 vector;
+ if (target != null)
+ {
+ vector = target.transform.position;
+ }
+ else
+ {
+ vector = Vector3.zero;
+ vector.y = 5f;
+ }
+ if (Vector3.SqrMagnitude(vector - base.transform.position) <= minDistance * minDistance)
+ {
+ flyingTowards = false;
+ Reload();
+ }
+ else if (Vector3.SqrMagnitude(vector - base.transform.position) >= maxDistance * maxDistance)
+ {
+ flyingTowards = true;
+ }
+ float num = ((!flyingTowards) ? 0f : Vector2.SignedAngle(new Vector2(base.transform.forward.x, base.transform.forward.z), new Vector2(vector.x - base.transform.position.x, vector.z - base.transform.position.z)));
+ float num2;
+ if (target != null && flyingTowards && Vector3.SqrMagnitude(vector - base.transform.position) <= attackDistance * attackDistance)
+ {
+ desiredAltitude += (Mathf.Min(vector.y, 1f) - desiredAltitude) * Time.deltaTime * 1.5f;
+ num2 = base.transform.position.y - desiredAltitude;
+ if (Mathf.Abs(num) < 22.5f)
+ {
+ Fire();
+ }
+ }
+ else
+ {
+ desiredAltitude += (5f - desiredAltitude) * Time.deltaTime * 3f;
+ num2 = base.transform.position.y - desiredAltitude;
+ }
+ num = Mathf.Clamp(num * 6f, -90f, 90f);
+ num2 = Mathf.Clamp(num2 * 6f, -45f, 45f);
+ base.transform.Rotate(new Vector3(0f, (0f - num) * Time.deltaTime / turnTime, 0f));
+ base.transform.eulerAngles = new Vector3(num2, base.transform.eulerAngles.y, 0f);
+ base.transform.Translate(Vector3.forward * Time.deltaTime * speed);
+ desiredTilt += (num - desiredTilt) * Time.deltaTime;
+ artTransform.eulerAngles = new Vector3(base.transform.eulerAngles.x, base.transform.eulerAngles.y, desiredTilt);
+ }
+
+ private void Reload()
+ {
+ ammo = maxAmmo;
+ }
+
+ private void Fire()
+ {
+ if (ammo > 0 && timeOfLastShot + rps <= Time.time)
+ {
+ ammo--;
+ timeOfLastShot = Time.time;
+ LaunchProjectile();
+ }
+ }
+
+ private void LaunchProjectile()
+ {
+ Vector3 position = target.transform.position;
+ position += new Vector3(Random.Range(-0.33f, 0.33f) + Random.Range(-0.33f, 0.33f), Random.Range(-0.33f, 0.33f) + Random.Range(-0.33f, 0.33f), Random.Range(-0.33f, 0.33f) + Random.Range(-0.33f, 0.33f));
+ muzzle.LookAt(position);
+ Object.Instantiate(projectile, muzzle.position, muzzle.rotation).GetComponent<Projectile>().SetStats(towerType, target, projectileSpeed, damage, healthDamage, armorDamage, shieldDamage, slowPercent, bleedPercent, burnPercent, poisonPercent, critChance, stunChance);
+ }
+}
diff --git a/GameCode/BuildButtonUI.cs b/GameCode/BuildButtonUI.cs
new file mode 100644
index 0000000..86c7732
--- /dev/null
+++ b/GameCode/BuildButtonUI.cs
@@ -0,0 +1,107 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class BuildButtonUI : MonoBehaviour
+{
+ [SerializeField]
+ private GameObject tower;
+
+ [SerializeField]
+ private TowerFlyweight towerFlyweight;
+
+ [SerializeField]
+ private Text priceTag;
+
+ [SerializeField]
+ private Text modifiersText;
+
+ [SerializeField]
+ private TowerType myTowerType;
+
+ private void Start()
+ {
+ priceTag.text = towerFlyweight.currentPrice + "g";
+ UpdateModifiersText();
+ }
+
+ public void Build()
+ {
+ BuildingManager.instance.EnterBuildMode(tower, towerFlyweight.currentPrice, towerFlyweight.priceIncrease, myTowerType);
+ }
+
+ public void UpdatePriceText()
+ {
+ priceTag.text = towerFlyweight.currentPrice + "g";
+ }
+
+ public void UpdateModifiersText()
+ {
+ if (!(modifiersText != null))
+ {
+ return;
+ }
+ string text = "";
+ if (TowerManager.instance.GetBonusBaseDamage(myTowerType) > 0)
+ {
+ text = text + "\n+" + TowerManager.instance.GetBonusBaseDamage(myTowerType) + " Base Damage";
+ }
+ if (TowerManager.instance.GetBonusBaseDamage(myTowerType) < 0)
+ {
+ text = text + "\n" + TowerManager.instance.GetBonusBaseDamage(myTowerType) + " Base Damage";
+ }
+ if (TowerManager.instance.GetBonusHealthDamage(myTowerType) > 0)
+ {
+ text = text + "\n+" + TowerManager.instance.GetBonusHealthDamage(myTowerType) + " Health Damage";
+ }
+ if (TowerManager.instance.GetBonusArmorDamage(myTowerType) > 0)
+ {
+ text = text + "\n+" + TowerManager.instance.GetBonusArmorDamage(myTowerType) + " Armor Damage";
+ }
+ if (TowerManager.instance.GetBonusShieldDamage(myTowerType) > 0)
+ {
+ text = text + "\n+" + TowerManager.instance.GetBonusShieldDamage(myTowerType) + " Shield Damage";
+ }
+ if (TowerManager.instance.GetCritChance(myTowerType) + TowerManager.instance.GetCritChanceLevelMultiplier(myTowerType) > 0f)
+ {
+ text = text + "\n+(" + (int)(TowerManager.instance.GetCritChance(myTowerType) * 100f);
+ if (TowerManager.instance.GetCritChanceLevelMultiplier(myTowerType) > 1f)
+ {
+ text = text + " + " + (int)TowerManager.instance.GetCritChanceLevelMultiplier(myTowerType) + "xLvl";
+ }
+ else if (TowerManager.instance.GetCritChanceLevelMultiplier(myTowerType) > 0f)
+ {
+ text += " + level";
+ }
+ text += ")% Crit";
+ }
+ if (TowerManager.instance.GetStunChance(myTowerType) > 0f)
+ {
+ text = text + "\n+" + Mathf.FloorToInt(TowerManager.instance.GetStunChance(myTowerType) * 101f) + "% Freeze Chance";
+ }
+ if (TowerManager.instance.GetBonusRange(myTowerType) > 0f)
+ {
+ text = text + "\n+" + TowerManager.instance.GetBonusRange(myTowerType) + " Range";
+ }
+ if (TowerManager.instance.GetBonusBlast(myTowerType) > 0f)
+ {
+ text = text + "\n+" + (int)(TowerManager.instance.GetBonusBlast(myTowerType) * 100f) + "% Blast Radius";
+ }
+ if (TowerManager.instance.GetBonusSlow(myTowerType) > 0f)
+ {
+ text = text + "\n+" + (int)(TowerManager.instance.GetBonusSlow(myTowerType) * 100f) + "% Slow";
+ }
+ if (TowerManager.instance.GetBonusBleed(myTowerType) > 0f)
+ {
+ text = text + "\n+" + (int)(TowerManager.instance.GetBonusBleed(myTowerType) * 100f) + "% Bleed";
+ }
+ if (TowerManager.instance.GetBonusBurn(myTowerType) > 0f)
+ {
+ text = text + "\n+" + (int)(TowerManager.instance.GetBonusBurn(myTowerType) * 100f) + "% Burn";
+ }
+ if (TowerManager.instance.GetBonusPoison(myTowerType) > 0f)
+ {
+ text = text + "\n+" + (int)(TowerManager.instance.GetBonusPoison(myTowerType) * 100f) + "% Poison";
+ }
+ modifiersText.text = text;
+ }
+}
diff --git a/GameCode/BuildingGhost.cs b/GameCode/BuildingGhost.cs
new file mode 100644
index 0000000..ea1db9f
--- /dev/null
+++ b/GameCode/BuildingGhost.cs
@@ -0,0 +1,13 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class BuildingGhost : MonoBehaviour
+{
+ [SerializeField]
+ private Text text;
+
+ public void SetText(string newText)
+ {
+ text.text = newText;
+ }
+}
diff --git a/GameCode/BuildingManager.cs b/GameCode/BuildingManager.cs
new file mode 100644
index 0000000..4ace1ac
--- /dev/null
+++ b/GameCode/BuildingManager.cs
@@ -0,0 +1,163 @@
+using UnityEngine;
+
+public class BuildingManager : MonoBehaviour
+{
+ public static BuildingManager instance;
+
+ [SerializeField]
+ private GameObject buildingGhost;
+
+ private GameObject currentGhost;
+
+ [SerializeField]
+ private GameObject placementFXObject;
+
+ [SerializeField]
+ public GameObject levelUpFX;
+
+ [SerializeField]
+ private LayerMask buildableMask;
+
+ private bool buildSpotAvailable;
+
+ private GameObject thingToBuild;
+
+ private int buildingCost;
+
+ private int priceIncrease;
+
+ private TowerType tType;
+
+ public bool buildMode { get; private set; }
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ buildMode = false;
+ }
+
+ private void FixedUpdate()
+ {
+ if (buildMode)
+ {
+ buildSpotAvailable = SamplePoint();
+ }
+ }
+
+ private void Update()
+ {
+ if (!buildMode)
+ {
+ return;
+ }
+ if (Input.GetMouseButtonDown(0) && BuildingCheck())
+ {
+ ResourceManager.instance.Spend(buildingCost);
+ DamageTracker.instance.AddCost(tType, buildingCost);
+ Build();
+ if (!Input.GetKey(KeyCode.LeftShift))
+ {
+ ExitBuildMode();
+ }
+ else
+ {
+ buildingCost += priceIncrease;
+ }
+ }
+ if (Input.GetMouseButtonDown(1) || Input.GetKeyDown(KeyCode.Escape))
+ {
+ ExitBuildMode();
+ }
+ }
+
+ public void EnterBuildMode(GameObject objectToBuild, int cost, int _priceIncrease, TowerType type)
+ {
+ buildMode = true;
+ thingToBuild = objectToBuild;
+ buildingCost = cost;
+ priceIncrease = _priceIncrease;
+ tType = type;
+ }
+
+ private void ExitBuildMode()
+ {
+ HideGhost();
+ buildMode = false;
+ }
+
+ private void Build()
+ {
+ GameObject gameObject = Object.Instantiate(thingToBuild, currentGhost.transform.position, Quaternion.identity);
+ gameObject.GetComponent<IBuildable>()?.SetStats();
+ Object.Instantiate(placementFXObject, gameObject.transform.position + Vector3.up * 0.333f, Quaternion.identity);
+ buildSpotAvailable = SamplePoint();
+ }
+
+ private bool BuildingCheck()
+ {
+ if (!PauseMenu.instance.paused && buildSpotAvailable && ResourceManager.instance.CheckMoney(buildingCost))
+ {
+ return true;
+ }
+ if (!ResourceManager.instance.CheckMoney(buildingCost) && !PauseMenu.instance.paused && buildSpotAvailable)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, currentGhost.transform.position + Vector3.up, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("Not enough gold", "Grey", 1f);
+ component.SetHoldTime(0.25f);
+ }
+ return false;
+ }
+
+ private bool SamplePoint()
+ {
+ if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out var hitInfo, 2000f, buildableMask, QueryTriggerInteraction.Ignore))
+ {
+ if (hitInfo.collider.gameObject.layer != LayerMask.NameToLayer("Grass"))
+ {
+ HideGhost();
+ return false;
+ }
+ Vector3 vector = new Vector3(Mathf.Round(hitInfo.point.x), Mathf.Round(3f * hitInfo.point.y) / 3f, Mathf.Round(hitInfo.point.z));
+ if (Vector3.SqrMagnitude(hitInfo.point - vector) < 0.25f)
+ {
+ string text = "";
+ if (vector.y > 0.34f)
+ {
+ text = "+" + (Mathf.RoundToInt(vector.y * 3f) - 1);
+ }
+ DisplayGhost(vector, text);
+ return true;
+ }
+ HideGhost();
+ return false;
+ }
+ HideGhost();
+ return false;
+ }
+
+ private void DisplayGhost(Vector3 pos, string text)
+ {
+ if (currentGhost == null)
+ {
+ currentGhost = Object.Instantiate(buildingGhost, pos, Quaternion.identity);
+ }
+ else
+ {
+ currentGhost.SetActive(value: true);
+ currentGhost.transform.position = pos;
+ }
+ currentGhost.GetComponent<BuildingGhost>().SetText(text);
+ }
+
+ private void HideGhost()
+ {
+ if (currentGhost != null)
+ {
+ currentGhost.SetActive(value: false);
+ }
+ }
+}
diff --git a/GameCode/CameraController.cs b/GameCode/CameraController.cs
new file mode 100644
index 0000000..2b91184
--- /dev/null
+++ b/GameCode/CameraController.cs
@@ -0,0 +1,96 @@
+using UnityEngine;
+
+public class CameraController : MonoBehaviour
+{
+ public static CameraController instance;
+
+ [SerializeField]
+ private Vector3 velocity = Vector3.zero;
+
+ [SerializeField]
+ private float cameraSpeed = 10f;
+
+ [SerializeField]
+ private float cameraBaseZoom = 10f;
+
+ [SerializeField]
+ private GameObject cameraHolder;
+
+ [SerializeField]
+ private Transform audioListenerObject;
+
+ [SerializeField]
+ private LayerMask zeroMask;
+
+ private Vector3 clickMoveOrigin;
+
+ private Vector3 cameraOrigin;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ Camera.main.orthographicSize = cameraBaseZoom;
+ if (audioListenerObject != null)
+ {
+ audioListenerObject.position = new Vector3(audioListenerObject.position.x, cameraBaseZoom, audioListenerObject.position.z);
+ }
+ }
+
+ private void Update()
+ {
+ UpdateMovement();
+ UpdateZoom();
+ }
+
+ private void UpdateMovement()
+ {
+ if (Input.GetKeyDown(KeyCode.C))
+ {
+ base.transform.position = Vector3.zero;
+ }
+ if (Input.GetMouseButtonDown(1) && Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out var hitInfo, 2000f, zeroMask, QueryTriggerInteraction.Collide))
+ {
+ clickMoveOrigin = hitInfo.point;
+ cameraOrigin = base.transform.position;
+ }
+ if (Input.GetMouseButton(1))
+ {
+ if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out var hitInfo2, 2000f, zeroMask, QueryTriggerInteraction.Collide))
+ {
+ Vector3 vector = cameraOrigin - 2f * (hitInfo2.point - clickMoveOrigin);
+ base.transform.position = (base.transform.position + vector) / 2f;
+ }
+ return;
+ }
+ if (Input.GetKey(KeyCode.LeftShift) || Input.GetKey(KeyCode.RightShift))
+ {
+ velocity *= 1f - Time.deltaTime;
+ velocity += new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical")) * Time.deltaTime * 2f;
+ float num = Mathf.Clamp(Camera.main.orthographicSize / 10f, 1f, 5f);
+ base.transform.Translate(velocity * Time.deltaTime * cameraSpeed * num);
+ return;
+ }
+ velocity = Vector3.zero;
+ Vector3 vector2 = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
+ if (vector2.sqrMagnitude > 0.1f)
+ {
+ float num2 = Mathf.Clamp(Camera.main.orthographicSize / 10f, 1f, 5f);
+ base.transform.Translate(vector2.normalized * Time.deltaTime * cameraSpeed * num2);
+ }
+ }
+
+ private void UpdateZoom()
+ {
+ float num = Mathf.Clamp(Camera.main.orthographicSize - Input.mouseScrollDelta.y, 1f, 50f);
+ Camera.main.orthographicSize = num;
+ cameraHolder.transform.localPosition = new Vector3(0f, 5f + 2f * num, -2f * num - 5f);
+ if (audioListenerObject != null)
+ {
+ audioListenerObject.position = new Vector3(audioListenerObject.position.x, (num + 10f) / 2f, audioListenerObject.position.z);
+ }
+ }
+}
diff --git a/GameCode/CardManager.cs b/GameCode/CardManager.cs
new file mode 100644
index 0000000..08797d3
--- /dev/null
+++ b/GameCode/CardManager.cs
@@ -0,0 +1,177 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class CardManager : MonoBehaviour
+{
+ public static CardManager instance;
+
+ private List<int> queuedDraws = new List<int>();
+
+ [SerializeField]
+ private int baseCardDraw = 3;
+
+ [SerializeField]
+ private GameObject ui;
+
+ [SerializeField]
+ private List<UpgradeCard> availableCards = new List<UpgradeCard>();
+
+ [SerializeField]
+ private List<UpgradeCard> startCardsCheck = new List<UpgradeCard>();
+
+ private UpgradeCard[] cards = new UpgradeCard[6];
+
+ [SerializeField]
+ private Text[] titles = new Text[6];
+
+ [SerializeField]
+ private Image[] images = new Image[6];
+
+ [SerializeField]
+ private Text[] descriptions = new Text[6];
+
+ [SerializeField]
+ private GameObject[] cardHolders = new GameObject[6];
+
+ public bool drawingCards { get; private set; }
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ baseCardDraw = 3 + PlayerPrefs.GetInt("CardDraw1", 0) + PlayerPrefs.GetInt("CardDraw2", 0) + PlayerPrefs.GetInt("CardDraw3", 0);
+ foreach (UpgradeCard item in startCardsCheck)
+ {
+ if (item.unlocked)
+ {
+ availableCards.Add(item);
+ }
+ }
+ ArrangeCards(baseCardDraw);
+ }
+
+ public void DrawCards()
+ {
+ DrawCards(baseCardDraw);
+ }
+
+ public void DrawCards(int numb)
+ {
+ if (drawingCards)
+ {
+ Debug.Log("Queueing Draw call of " + numb);
+ queuedDraws.Add(numb);
+ return;
+ }
+ int num = numb;
+ if (availableCards.Count < num)
+ {
+ num = availableCards.Count;
+ if (num <= 0)
+ {
+ Debug.LogError("No cards to draw. (Only " + availableCards.Count + " available)");
+ SpawnManager.instance.ShowSpawnUIs(state: true);
+ return;
+ }
+ }
+ ArrangeCards(num);
+ SFXManager.instance.PlaySound(Sound.Cards, MusicManager.instance.transform.position, MusicManager.instance.transform);
+ SpawnManager.instance.ShowSpawnUIs(state: false);
+ ui.SetActive(value: true);
+ drawingCards = true;
+ int[] array = new int[num];
+ for (int i = 0; i < num; i++)
+ {
+ array[i] = -1;
+ }
+ for (int j = 0; j < num; j++)
+ {
+ int num2 = Random.Range(0, availableCards.Count);
+ int num3 = 0;
+ while (CheckArray(array, num2))
+ {
+ num2 = Random.Range(0, availableCards.Count);
+ num3++;
+ if (num3 > 100)
+ {
+ Debug.LogError("Possible infinite loop in card selection");
+ break;
+ }
+ }
+ array[j] = num2;
+ }
+ for (int k = 0; k < num; k++)
+ {
+ cards[k] = availableCards[array[k]];
+ }
+ DisplayCards(num);
+ }
+
+ private void ArrangeCards(int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ cardHolders[i].SetActive(value: true);
+ }
+ for (int j = count; j < 6; j++)
+ {
+ cardHolders[j].SetActive(value: false);
+ }
+ for (int k = 0; k < count; k++)
+ {
+ cardHolders[k].transform.localPosition = new Vector3(200 * k - 100 * count + 100, 0f, 0f);
+ }
+ }
+
+ private void DisplayCards(int count)
+ {
+ for (int i = 0; i < count; i++)
+ {
+ titles[i].text = cards[i].title;
+ images[i].sprite = cards[i].image;
+ descriptions[i].text = cards[i].description;
+ }
+ }
+
+ public void ActivateCard(int index)
+ {
+ cards[index].Upgrade();
+ foreach (UpgradeCard unlock in cards[index].unlocks)
+ {
+ if (unlock.unlocked)
+ {
+ availableCards.Add(unlock);
+ }
+ }
+ availableCards.Remove(cards[index]);
+ drawingCards = false;
+ if (queuedDraws.Count > 0)
+ {
+ int numb = queuedDraws[0];
+ queuedDraws.RemoveAt(0);
+ DrawCards(numb);
+ return;
+ }
+ if (!SpawnManager.instance.combat)
+ {
+ SpawnManager.instance.ShowSpawnUIs(state: true);
+ }
+ ui.SetActive(value: false);
+ }
+
+ private bool CheckArray(int[] array, int val)
+ {
+ for (int i = 0; i < array.Length; i++)
+ {
+ if (array[i] == val)
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/GameCode/DOTUpgradeCard.cs b/GameCode/DOTUpgradeCard.cs
new file mode 100644
index 0000000..e90b065
--- /dev/null
+++ b/GameCode/DOTUpgradeCard.cs
@@ -0,0 +1,109 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class DOTUpgradeCard : UpgradeCard
+{
+ [SerializeField]
+ private int bleedAmount;
+
+ [SerializeField]
+ private int burnAmount;
+
+ [SerializeField]
+ private int poisonAmount;
+
+ [SerializeField]
+ private GameObject bleedUI;
+
+ [SerializeField]
+ private GameObject burnUI;
+
+ [SerializeField]
+ private GameObject poisonUI;
+
+ [SerializeField]
+ private GameObject stunDmgUI;
+
+ [SerializeField]
+ private Text bleedText;
+
+ [SerializeField]
+ private Text burnText;
+
+ [SerializeField]
+ private Text poisonText;
+
+ [SerializeField]
+ private Text stunDmgText;
+
+ [SerializeField]
+ private int bonusDamageOnBleed;
+
+ [SerializeField]
+ private int bonusDamageOnBurn;
+
+ [SerializeField]
+ private int bonusDamageOnPoison;
+
+ [SerializeField]
+ private int bonusDamageOnStun;
+
+ [SerializeField]
+ private float poisonSlowPercent;
+
+ [SerializeField]
+ private float burnSpeedDamagePercentBonus;
+
+ [SerializeField]
+ private float bleedingCritChance;
+
+ [SerializeField]
+ private float bleedPop;
+
+ [SerializeField]
+ private float burnPop;
+
+ [SerializeField]
+ private float poisonPop;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ GameManager.instance.dotTick += new Vector3(bleedAmount, burnAmount, poisonAmount);
+ MonsterManager.instance.bonusDamageOnBleed += bonusDamageOnBleed;
+ MonsterManager.instance.bonusDamageOnBurn += bonusDamageOnBurn;
+ MonsterManager.instance.bonusDamageOnPoison += bonusDamageOnPoison;
+ MonsterManager.instance.bonusDamageOnStun += bonusDamageOnStun;
+ MonsterManager.instance.poisonSlowPercent += poisonSlowPercent;
+ MonsterManager.instance.burnSpeedDamagePercentBonus += burnSpeedDamagePercentBonus;
+ MonsterManager.instance.bleedingCritChance += bleedingCritChance;
+ MonsterManager.instance.bleedPop += bleedPop;
+ MonsterManager.instance.burnPop += burnPop;
+ MonsterManager.instance.poisonPop += poisonPop;
+ if (bleedAmount > 0 || bonusDamageOnBleed > 0)
+ {
+ bleedUI.SetActive(value: true);
+ bleedText.text = GameManager.instance.dotTick.x + " \n (+" + (1 + MonsterManager.instance.bonusDamageOnBleed) + ")";
+ }
+ if (burnAmount > 0 || bonusDamageOnBurn > 0 || burnSpeedDamagePercentBonus > 0f)
+ {
+ burnUI.SetActive(value: true);
+ string text = GameManager.instance.dotTick.y + " \n (+" + (1 + MonsterManager.instance.bonusDamageOnBurn) + ")";
+ if (MonsterManager.instance.burnSpeedDamagePercentBonus > 0f)
+ {
+ text = text + "\n" + Mathf.FloorToInt(0.01f + MonsterManager.instance.burnSpeedDamagePercentBonus) + "xSlow%";
+ }
+ burnText.text = text;
+ }
+ if (poisonAmount > 0 || bonusDamageOnPoison > 0)
+ {
+ poisonUI.SetActive(value: true);
+ poisonText.text = GameManager.instance.dotTick.z + " \n (+" + (1 + MonsterManager.instance.bonusDamageOnPoison) + ")";
+ }
+ if (bonusDamageOnStun > 0)
+ {
+ stunDmgUI.SetActive(value: true);
+ stunDmgText.text = "+" + MonsterManager.instance.bonusDamageOnStun;
+ }
+ }
+}
diff --git a/GameCode/DamageNumber.cs b/GameCode/DamageNumber.cs
new file mode 100644
index 0000000..4355328
--- /dev/null
+++ b/GameCode/DamageNumber.cs
@@ -0,0 +1,138 @@
+using System.Collections;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class DamageNumber : MonoBehaviour
+{
+ [SerializeField]
+ private Transform holder;
+
+ [SerializeField]
+ private Text text;
+
+ [SerializeField]
+ private int baseTextSize;
+
+ [SerializeField]
+ private float zoom = 0.25f;
+
+ private float hold = 0.5f;
+
+ [SerializeField]
+ private float spin = 0.5f;
+
+ [SerializeField]
+ private Color healthColor;
+
+ [SerializeField]
+ private Color armorColor;
+
+ [SerializeField]
+ private Color shieldColor;
+
+ [SerializeField]
+ private Color greyColor;
+
+ [SerializeField]
+ private Color greenColor;
+
+ public void Start()
+ {
+ StartCoroutine(Bloop());
+ }
+
+ public void SetNumber(int num, string color, float fontScale)
+ {
+ text.fontSize = (int)((float)baseTextSize * fontScale);
+ text.text = num.ToString();
+ switch (color)
+ {
+ case "Blue":
+ text.color = shieldColor;
+ break;
+ case "Yellow":
+ text.color = armorColor;
+ break;
+ case "Red":
+ text.color = healthColor;
+ break;
+ case "Grey":
+ text.color = greyColor;
+ break;
+ case "Green":
+ text.color = greenColor;
+ break;
+ }
+ }
+
+ public void SetText(string _text, string color, float fontScale)
+ {
+ text.fontSize = (int)((float)baseTextSize * fontScale);
+ text.text = _text;
+ switch (color)
+ {
+ case "Blue":
+ text.color = shieldColor;
+ break;
+ case "Yellow":
+ text.color = armorColor;
+ break;
+ case "Red":
+ text.color = healthColor;
+ break;
+ case "Grey":
+ text.color = greyColor;
+ break;
+ case "Green":
+ text.color = greenColor;
+ break;
+ }
+ }
+
+ public void SetHoldTime(float time)
+ {
+ hold = time;
+ }
+
+ private IEnumerator Bloop()
+ {
+ Vector3 scale = Vector3.zero;
+ holder.localScale = scale;
+ holder.localPosition = new Vector3(-1.5f, 2.12132f, 1.5f);
+ text.rectTransform.localRotation = Quaternion.identity;
+ float t2 = zoom;
+ while (t2 > 0f)
+ {
+ scale += Vector3.one * (1f / zoom) * Time.deltaTime;
+ holder.localScale = scale;
+ t2 -= Time.deltaTime;
+ yield return null;
+ }
+ scale = Vector3.one;
+ holder.localScale = scale;
+ t2 = hold;
+ Vector3 direction = new Vector3(Random.Range(-1f, 1f), Random.Range(0f, 1f), 0f);
+ while (t2 > 0f)
+ {
+ holder.localPosition += direction * Time.deltaTime;
+ t2 -= Time.deltaTime;
+ yield return null;
+ }
+ t2 = spin;
+ int r = 1;
+ if (Random.Range(1f, 100f) <= 50f)
+ {
+ r = -1;
+ }
+ while (t2 > 0f)
+ {
+ holder.localPosition += direction * Time.deltaTime;
+ scale -= Vector3.one * (1f / spin) * Time.deltaTime;
+ holder.localScale = scale;
+ text.rectTransform.eulerAngles += r * 180 * Vector3.forward * Time.deltaTime / spin;
+ t2 -= Time.deltaTime;
+ yield return null;
+ }
+ ObjectPool.instance.PoolDamageNumber(base.gameObject);
+ }
+}
diff --git a/GameCode/DamageTracker.cs b/GameCode/DamageTracker.cs
new file mode 100644
index 0000000..2d80e8f
--- /dev/null
+++ b/GameCode/DamageTracker.cs
@@ -0,0 +1,101 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class DamageTracker : MonoBehaviour
+{
+ public enum IncomeType
+ {
+ Monster,
+ House,
+ Haunted,
+ Bonus
+ }
+
+ public static DamageTracker instance;
+
+ public GameObject uiObject;
+
+ [SerializeField]
+ private Text healthDmg;
+
+ [SerializeField]
+ private Text armorDmg;
+
+ [SerializeField]
+ private Text shieldDmg;
+
+ [SerializeField]
+ private Text totalDmg;
+
+ [SerializeField]
+ private Text cost;
+
+ [SerializeField]
+ private Text ratio;
+
+ [SerializeField]
+ private Text incomeTotals;
+
+ [SerializeField]
+ private Text incomePercent;
+
+ private Vector3[] towerDamage = new Vector3[15];
+
+ private int[] towerCost = new int[15];
+
+ private int[] income = new int[4];
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ public void AddDamage(TowerType type, int healthDmg, int armorDmg, int shieldDmg)
+ {
+ towerDamage[(int)type].x += (float)healthDmg / 1000f;
+ towerDamage[(int)type].y += (float)armorDmg / 1000f;
+ towerDamage[(int)type].z += (float)shieldDmg / 1000f;
+ }
+
+ public void AddCost(TowerType type, int gold)
+ {
+ towerCost[(int)type] += gold;
+ }
+
+ public void AddIncome(IncomeType type, int gold)
+ {
+ income[(int)type] += gold;
+ }
+
+ public void DisplayIncomeTotals()
+ {
+ float num = 0f;
+ for (int i = 0; i < income.Length; i++)
+ {
+ num += (float)income[i];
+ }
+ num = Mathf.Max(num, 1f);
+ string text = "Gold Generated\n" + income[0] + "g\n" + income[1] + "g\n" + income[2] + "g\n" + income[3] + "g\n";
+ incomeTotals.text = text;
+ string text2 = "Income Share\n" + Mathf.FloorToInt((float)(100 * income[0]) / num) + "%\n" + Mathf.FloorToInt((float)(100 * income[1]) / num) + "%\n" + Mathf.FloorToInt((float)(100 * income[2]) / num) + "%\n" + Mathf.FloorToInt((float)(100 * income[3]) / num) + "%\n";
+ incomePercent.text = text2;
+ }
+
+ public void DisplayDamageTotals()
+ {
+ uiObject.SetActive(value: true);
+ DisplayIncomeTotals();
+ string text = "Health\n" + (int)towerDamage[0].x + "K\n" + (int)towerDamage[1].x + "K\n" + (int)towerDamage[2].x + "K\n" + (int)towerDamage[3].x + "K\n" + (int)towerDamage[5].x + "K\n" + (int)towerDamage[6].x + "K\n" + (int)towerDamage[12].x + "K\n" + (int)towerDamage[13].x + "K\n" + (int)towerDamage[14].x + "K\n" + (int)towerDamage[8].x + "K\n" + (int)towerDamage[4].x + "K\n" + (int)towerDamage[9].x + "K\n" + (int)towerDamage[10].x + "K\n";
+ healthDmg.text = text;
+ string text2 = "Armor\n" + (int)towerDamage[0].y + "K\n" + (int)towerDamage[1].y + "K\n" + (int)towerDamage[2].y + "K\n" + (int)towerDamage[3].y + "K\n" + (int)towerDamage[5].y + "K\n" + (int)towerDamage[6].y + "K\n" + (int)towerDamage[12].y + "K\n" + (int)towerDamage[13].y + "K\n" + (int)towerDamage[14].y + "K\n" + (int)towerDamage[8].y + "K\n" + (int)towerDamage[4].y + "K\n" + (int)towerDamage[9].y + "K\n" + (int)towerDamage[10].y + "K\n";
+ armorDmg.text = text2;
+ string text3 = "Shield\n" + (int)towerDamage[0].z + "K\n" + (int)towerDamage[1].z + "K\n" + (int)towerDamage[2].z + "K\n" + (int)towerDamage[3].z + "K\n" + (int)towerDamage[5].z + "K\n" + (int)towerDamage[6].z + "K\n" + (int)towerDamage[12].z + "K\n" + (int)towerDamage[13].z + "K\n" + (int)towerDamage[14].z + "K\n" + (int)towerDamage[8].z + "K\n" + (int)towerDamage[4].z + "K\n" + (int)towerDamage[9].z + "K\n" + (int)towerDamage[10].z + "K\n";
+ shieldDmg.text = text3;
+ string text4 = "Total\n" + (int)(towerDamage[0].x + towerDamage[0].y + towerDamage[0].z) + "K\n" + (int)(towerDamage[1].x + towerDamage[1].y + towerDamage[1].z) + "K\n" + (int)(towerDamage[2].x + towerDamage[2].y + towerDamage[2].z) + "K\n" + (int)(towerDamage[3].x + towerDamage[3].y + towerDamage[3].z) + "K\n" + (int)(towerDamage[5].x + towerDamage[5].y + towerDamage[5].z) + "K\n" + (int)(towerDamage[6].x + towerDamage[6].y + towerDamage[6].z) + "K\n" + (int)(towerDamage[12].x + towerDamage[12].y + towerDamage[12].z) + "K\n" + (int)(towerDamage[13].x + towerDamage[13].y + towerDamage[13].z) + "K\n" + (int)(towerDamage[14].x + towerDamage[14].y + towerDamage[14].z) + "K\n" + (int)(towerDamage[8].x + towerDamage[8].y + towerDamage[8].z) + "K\n" + (int)(towerDamage[4].x + towerDamage[4].y + towerDamage[4].z) + "K\n" + (int)(towerDamage[9].x + towerDamage[9].y + towerDamage[9].z) + "K\n" + (int)(towerDamage[10].x + towerDamage[10].y + towerDamage[10].z) + "K\n";
+ totalDmg.text = text4;
+ string text5 = "Cost\n" + towerCost[0] + "g\n" + towerCost[1] + "g\n" + towerCost[2] + "g\n" + towerCost[3] + "g\n" + towerCost[5] + "g\n" + towerCost[6] + "g\n" + towerCost[12] + "g\n" + towerCost[13] + "g\n" + towerCost[14] + "g\n" + towerCost[8] + "g\n" + towerCost[4] + "g\n" + towerCost[9] + "g\n";
+ cost.text = text5;
+ string text6 = "Dmg/g\n" + (1000f * ((towerDamage[0].x + towerDamage[0].y + towerDamage[0].z) / (float)Mathf.Max(towerCost[0], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[1].x + towerDamage[1].y + towerDamage[1].z) / (float)Mathf.Max(towerCost[1], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[2].x + towerDamage[2].y + towerDamage[2].z) / (float)Mathf.Max(towerCost[2], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[3].x + towerDamage[3].y + towerDamage[3].z) / (float)Mathf.Max(towerCost[3], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[5].x + towerDamage[5].y + towerDamage[5].z) / (float)Mathf.Max(towerCost[5], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[6].x + towerDamage[6].y + towerDamage[6].z) / (float)Mathf.Max(towerCost[6], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[12].x + towerDamage[12].y + towerDamage[12].z) / (float)Mathf.Max(towerCost[12], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[13].x + towerDamage[13].y + towerDamage[13].z) / (float)Mathf.Max(towerCost[13], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[14].x + towerDamage[14].y + towerDamage[14].z) / (float)Mathf.Max(towerCost[14], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[8].x + towerDamage[8].y + towerDamage[8].z) / (float)Mathf.Max(towerCost[8], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[4].x + towerDamage[4].y + towerDamage[4].z) / (float)Mathf.Max(towerCost[4], 1))).ToString("F2") + "\n" + (1000f * ((towerDamage[9].x + towerDamage[9].y + towerDamage[9].z) / (float)Mathf.Max(towerCost[9], 1))).ToString("F2") + "\n";
+ ratio.text = text6;
+ }
+}
diff --git a/GameCode/DevelopmentManager.cs b/GameCode/DevelopmentManager.cs
new file mode 100644
index 0000000..7caec25
--- /dev/null
+++ b/GameCode/DevelopmentManager.cs
@@ -0,0 +1,23 @@
+using UnityEngine;
+
+public class DevelopmentManager : MonoBehaviour
+{
+ [SerializeField]
+ private GameObject[] startingDevelopments;
+
+ private float developmentPercent;
+
+ private void Start()
+ {
+ developmentPercent = PlayerPrefs.GetInt("UnlockedCardCount", 0) + PlayerPrefs.GetInt("Development", 0);
+ developmentPercent *= 0.4f;
+ GameObject[] array = startingDevelopments;
+ foreach (GameObject obj in array)
+ {
+ if (Random.Range(0f, 100f) > developmentPercent)
+ {
+ Object.Destroy(obj);
+ }
+ }
+ }
+}
diff --git a/GameCode/Dropper.cs b/GameCode/Dropper.cs
new file mode 100644
index 0000000..0dc7a10
--- /dev/null
+++ b/GameCode/Dropper.cs
@@ -0,0 +1,126 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Dropper : Tower
+{
+ [SerializeField]
+ private int manaPerSecond;
+
+ [SerializeField]
+ private float dropHeight = 3f;
+
+ [SerializeField]
+ private LayerMask dropSetMask;
+
+ [SerializeField]
+ protected Vector2[] dropPoints;
+
+ [SerializeField]
+ private bool requireTargetToDrop;
+
+ private int currentLevel;
+
+ public float dropperRPMdisplay;
+
+ private float timeOfNextManaCheck;
+
+ private bool canFire = true;
+
+ protected override void Start()
+ {
+ base.Start();
+ SetDropPoints();
+ }
+
+ public override void SetStats()
+ {
+ base.SetStats();
+ rps = 60f / rpm * (10f / (10f + (float)dropPoints.Length));
+ dropperRPMdisplay = rpm * (10f + (float)dropPoints.Length) / 10f;
+ }
+
+ private void SetDropPoints()
+ {
+ List<Vector2> list = new List<Vector2>();
+ for (int i = -(int)base.range; (float)i <= base.range; i++)
+ {
+ for (int j = -(int)base.range; (float)j <= base.range; j++)
+ {
+ if (Physics.Raycast(new Vector3(base.transform.position.x + (float)i, dropHeight, base.transform.position.z + (float)j), Vector3.down, out var hitInfo, 1.5f * dropHeight, dropSetMask, QueryTriggerInteraction.Ignore) && hitInfo.transform.gameObject.layer == LayerMask.NameToLayer("Path"))
+ {
+ list.Add(new Vector2(i, j));
+ }
+ }
+ }
+ dropPoints = list.ToArray();
+ rps = 60f / rpm * (10f / (10f + (float)dropPoints.Length));
+ dropperRPMdisplay = rpm * (10f + (float)dropPoints.Length) / 10f;
+ }
+
+ protected override void Update()
+ {
+ if (SpawnManager.instance.level != currentLevel)
+ {
+ SetDropPoints();
+ currentLevel = SpawnManager.instance.level;
+ }
+ if (currentTarget != null)
+ {
+ if (turret != null)
+ {
+ AimTurret();
+ }
+ GainXP();
+ }
+ if (manaPerSecond > 0 && Time.time >= timeOfNextManaCheck && SpawnManager.instance.combat)
+ {
+ timeOfNextManaCheck = Time.time + 1f;
+ if (ResourceManager.instance.CheckMana(manaPerSecond))
+ {
+ ResourceManager.instance.SpendMana(manaPerSecond);
+ canFire = true;
+ }
+ else
+ {
+ canFire = false;
+ }
+ }
+ timeSinceLastShot += Time.deltaTime;
+ if (canFire && TargetingCheck() && timeSinceLastShot > rps && SpawnManager.instance.combat)
+ {
+ Fire();
+ timeSinceLastShot = 0f;
+ }
+ }
+
+ private bool TargetingCheck()
+ {
+ if (requireTargetToDrop)
+ {
+ if (currentTarget != null)
+ {
+ return true;
+ }
+ return false;
+ }
+ return true;
+ }
+
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * manaConsumptionRate);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ if (dropPoints.Length != 0)
+ {
+ Vector2 vector = dropPoints[Random.Range(0, dropPoints.Length)];
+ Object.Instantiate(projectile, new Vector3(vector.x, dropHeight, vector.y) + base.transform.position, Quaternion.identity).GetComponent<Projectile>().SetStats(towerType, null, projectileSpeed, base.damage, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ }
+ }
+}
diff --git a/GameCode/Encampment.cs b/GameCode/Encampment.cs
new file mode 100644
index 0000000..eaa8ab7
--- /dev/null
+++ b/GameCode/Encampment.cs
@@ -0,0 +1,26 @@
+using UnityEngine;
+
+public class Encampment : Dropper
+{
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * manaConsumptionRate);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ if (dropPoints.Length != 0)
+ {
+ Vector2 vector = dropPoints[Random.Range(0, dropPoints.Length)];
+ Vector3 endPosition = new Vector3(vector.x + Random.Range(-0.125f, 0.125f) + base.transform.position.x, 0f, vector.y + Random.Range(-0.125f, 0.125f) + base.transform.position.z);
+ GameObject obj = Object.Instantiate(projectile, base.transform.position, Quaternion.identity);
+ obj.GetComponent<Projectile>().SetStats(towerType, null, projectileSpeed, base.damage, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ obj.GetComponent<Landmine>().blastRadius = base.blastRadius;
+ obj.GetComponent<Landmine>().SetEndPosition(endPosition);
+ }
+ }
+}
diff --git a/GameCode/Enemy.cs b/GameCode/Enemy.cs
new file mode 100644
index 0000000..2e7ce7d
--- /dev/null
+++ b/GameCode/Enemy.cs
@@ -0,0 +1,669 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Enemy : MonoBehaviour, IDamageable
+{
+ [SerializeField]
+ private Renderer renderer;
+
+ [SerializeField]
+ private Pathfinder pathfinder;
+
+ [SerializeField]
+ private HealthBar healthBar;
+
+ [SerializeField]
+ private GameObject deathObjectSpawn;
+
+ [SerializeField]
+ private BattleCry[] battleCries;
+
+ [SerializeField]
+ private GameObject treasureObject;
+
+ [SerializeField]
+ private Vector2 spawnRange;
+
+ public int level = 1;
+
+ [SerializeField]
+ private int damageToTower = 1;
+
+ [SerializeField]
+ private int bonusGoldDrop;
+
+ [SerializeField]
+ private bool dropsGold = true;
+
+ [SerializeField]
+ private int hpScaleDegree = 1;
+
+ public int baseHealth;
+
+ public int baseArmor;
+
+ public int baseShield;
+
+ public float baseSpeed;
+
+ public int healthRegen;
+
+ public int armorRegen;
+
+ public int shieldRegen;
+
+ private float timeSinceRegen;
+
+ private float currentSpeed;
+
+ private float currentSlowPercentage;
+
+ private float hastePercentage;
+
+ private float freezeTime;
+
+ private float fortifiedTime;
+
+ private bool fortified;
+
+ private int bleed;
+
+ private int burn;
+
+ private int poison;
+
+ private bool bleeding;
+
+ private bool burning;
+
+ private bool poisoned;
+
+ private float timeSinceBleed;
+
+ private float timeSinceBurn;
+
+ private float timeSincePoison;
+
+ private float dotTick;
+
+ private HashSet<TowerType> typesOfTowers = new HashSet<TowerType>();
+
+ public Lookout mark;
+
+ private bool dead;
+
+ public int health { get; private set; }
+
+ public int armor { get; private set; }
+
+ public int shield { get; private set; }
+
+ private void Start()
+ {
+ CheckBattleCries(BattleCry.BattleCryTrigger.Spawn);
+ healthBar.UpdateFortified(fortifiedTime);
+ healthBar.UpdateHaste(hastePercentage);
+ if (bonusGoldDrop == 0)
+ {
+ bonusGoldDrop = ResourceManager.instance.enemyBonusGoldDrop;
+ }
+ else
+ {
+ bonusGoldDrop += ResourceManager.instance.enemyBonusGoldDrop;
+ }
+ }
+
+ public void SetStats()
+ {
+ health = baseHealth;
+ armor = baseArmor;
+ shield = baseShield;
+ currentSpeed = baseSpeed + MonsterManager.instance.speedBonus;
+ pathfinder.speed = currentSpeed;
+ healthBar.SetHealth(health + armor + shield, health, armor, shield, hpScaleDegree);
+ }
+
+ public void SetFirstSpawnPoint(Waypoint point)
+ {
+ pathfinder.currentWaypoint = point;
+ }
+
+ private void Update()
+ {
+ if (fortifiedTime > 0f)
+ {
+ FortifiedUpdate();
+ }
+ if (currentSlowPercentage > 0f || hastePercentage > 0f || freezeTime > 0f)
+ {
+ SpeedUpdate();
+ }
+ DOTUpdate();
+ RegenUpdate();
+ }
+
+ private void SpeedUpdate()
+ {
+ float num = freezeTime;
+ freezeTime = Mathf.Max(0f, freezeTime - Time.deltaTime);
+ currentSlowPercentage = Mathf.Max(0f, currentSlowPercentage - Time.deltaTime * 0.05f);
+ hastePercentage = Mathf.Max(0f, hastePercentage - Time.deltaTime * 0.05f);
+ if (freezeTime > 0f)
+ {
+ currentSpeed = 0f;
+ }
+ else
+ {
+ currentSpeed = (baseSpeed + MonsterManager.instance.speedBonus) * (1f - currentSlowPercentage + hastePercentage);
+ }
+ pathfinder.speed = currentSpeed;
+ if (freezeTime <= 0f && num > 0f && renderer != null)
+ {
+ renderer.material.color = Color.white;
+ }
+ healthBar.UpdateSlow(currentSlowPercentage);
+ healthBar.UpdateHaste(hastePercentage);
+ }
+
+ public void CheckBattleCries(BattleCry.BattleCryTrigger cryType)
+ {
+ if (battleCries.Length != 0)
+ {
+ BattleCry[] array = battleCries;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].CheckBattleCry(cryType);
+ }
+ }
+ }
+
+ private void AddSlow(float newPercentage)
+ {
+ currentSlowPercentage = Mathf.Clamp(currentSlowPercentage + newPercentage, 0f, 0.6f + MonsterManager.instance.slowCapModifier);
+ }
+
+ public void AddHaste(float amount)
+ {
+ hastePercentage = Mathf.Clamp(hastePercentage + amount, 0f, 0.6f + MonsterManager.instance.hasteCapModifier);
+ if (OptionsMenu.instance.showConditionText)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("HASTED", "Grey", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ }
+
+ public void Freeze()
+ {
+ freezeTime = 1f;
+ if (renderer != null)
+ {
+ renderer.material.color = Color.blue;
+ }
+ if (OptionsMenu.instance.showConditionText)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("FROZEN", "Grey", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ }
+
+ private void FortifiedUpdate()
+ {
+ fortifiedTime = Mathf.Max(fortifiedTime - Time.deltaTime, 0f);
+ if (fortifiedTime <= 0f)
+ {
+ fortified = false;
+ }
+ healthBar.UpdateFortified(fortifiedTime);
+ }
+
+ public void Fortify(float time)
+ {
+ fortifiedTime = Mathf.Clamp(fortifiedTime + time, 0f, 12f);
+ healthBar.UpdateFortified(fortifiedTime);
+ fortified = true;
+ if (OptionsMenu.instance.showConditionText)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("FORTIFIED", "Grey", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ }
+
+ private void RegenUpdate()
+ {
+ if (timeSinceRegen >= 1f)
+ {
+ if (healthRegen > 0 && health > 0)
+ {
+ HealHealth(healthRegen);
+ }
+ if (armorRegen > 0 && armor > 0)
+ {
+ HealArmor(armorRegen);
+ }
+ if (shieldRegen > 0 && shield > 0)
+ {
+ HealShield(shieldRegen);
+ }
+ timeSinceRegen = 0f;
+ }
+ else
+ {
+ timeSinceRegen += Time.deltaTime;
+ }
+ }
+
+ private void HealHealth(int amount)
+ {
+ if (health >= baseHealth || bleeding)
+ {
+ if (bleeding)
+ {
+ DamageTracker.instance.AddDamage(TowerType.DOT, amount, 0, 0);
+ }
+ return;
+ }
+ if (health + amount >= baseHealth)
+ {
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("+" + (baseHealth - health), "Green", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ health = baseHealth;
+ }
+ else
+ {
+ health += amount;
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component2 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component2.SetText("+" + amount, "Green", 1f);
+ component2.SetHoldTime(0.5f);
+ }
+ }
+ if (healthBar != null)
+ {
+ healthBar.UpdateHealth(health, armor, shield, currentSlowPercentage, bleeding, burning, poisoned, bleed, burn, poison);
+ }
+ }
+
+ private void HealArmor(int amount)
+ {
+ if (armor >= baseArmor || burning)
+ {
+ if (burning)
+ {
+ DamageTracker.instance.AddDamage(TowerType.DOT, 0, amount, 0);
+ }
+ return;
+ }
+ if (armor + amount >= baseArmor)
+ {
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("+" + (baseArmor - armor), "Yellow", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ armor = baseArmor;
+ }
+ else
+ {
+ armor += amount;
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component2 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component2.SetText("+" + amount, "Yellow", 1f);
+ component2.SetHoldTime(0.5f);
+ }
+ }
+ if (healthBar != null)
+ {
+ healthBar.UpdateHealth(health, armor, shield, currentSlowPercentage, bleeding, burning, poisoned, bleed, burn, poison);
+ }
+ }
+
+ private void HealShield(int amount)
+ {
+ if (shield >= baseShield || poisoned)
+ {
+ if (poisoned)
+ {
+ DamageTracker.instance.AddDamage(TowerType.DOT, 0, 0, amount);
+ }
+ return;
+ }
+ if (shield + amount >= baseShield)
+ {
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("+" + (baseShield - shield), "Blue", 1f);
+ component.SetHoldTime(0.5f);
+ }
+ shield = baseShield;
+ }
+ else
+ {
+ shield += amount;
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component2 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component2.SetText("+" + amount, "Blue", 1f);
+ component2.SetHoldTime(0.5f);
+ }
+ }
+ if (healthBar != null)
+ {
+ healthBar.UpdateHealth(health, armor, shield, currentSlowPercentage, bleeding, burning, poisoned, bleed, burn, poison);
+ }
+ }
+
+ private void DOTUpdate()
+ {
+ if (!dead)
+ {
+ BleedCheck();
+ BurnCheck();
+ PoisonCheck();
+ if (poisoned && timeSincePoison <= 0f && dotTick <= 0f)
+ {
+ int num = poison;
+ poison = Mathf.Max(poison - (int)GameManager.instance.dotTick.z, 0);
+ num -= poison;
+ int num2 = Mathf.Max(num / 2, 1);
+ TakeDamage(TowerType.DOT, -999, num2, num2, num, 0f, 0f, 0f, 0f, 0f, 0f);
+ timeSincePoison = 1f;
+ dotTick = 0.1f;
+ }
+ if (burning && timeSinceBurn <= 0f && dotTick <= 0f)
+ {
+ int num3 = burn;
+ burn = Mathf.Max(burn - (int)(GameManager.instance.dotTick.y * Mathf.Max(1f + MonsterManager.instance.burnSpeedDamagePercentBonus * currentSlowPercentage, 1f)), 0);
+ num3 -= burn;
+ int num4 = num3 / 2;
+ TakeDamage(TowerType.DOT, -999, num4, num3, num4, 0f, 0f, 0f, 0f, 0f, 0f);
+ timeSinceBurn = 1f;
+ dotTick = 0.1f;
+ }
+ if (bleeding && timeSinceBleed <= 0f && dotTick <= 0f)
+ {
+ int num5 = bleed;
+ bleed = Mathf.Max(bleed - (int)GameManager.instance.dotTick.x, 0);
+ num5 -= bleed;
+ int num6 = num5 / 2;
+ TakeDamage(TowerType.DOT, -999, num5, num6, num6, 0f, 0f, 0f, 0f, 0f, 0f);
+ timeSinceBleed = 1f;
+ dotTick = 0.1f;
+ }
+ timeSinceBleed -= Time.deltaTime;
+ timeSinceBurn -= Time.deltaTime;
+ timeSincePoison -= Time.deltaTime;
+ dotTick -= Time.deltaTime;
+ healthBar.UpdateBleed(bleeding, bleed);
+ healthBar.UpdateBurn(burning, burn);
+ healthBar.UpdatePoison(poisoned, poison);
+ }
+ }
+
+ private int FortifiedCheck()
+ {
+ if (fortified)
+ {
+ return 5;
+ }
+ return 0;
+ }
+
+ private int BleedCheck()
+ {
+ if (bleed > 0)
+ {
+ bleeding = true;
+ return 1 + MonsterManager.instance.bonusDamageOnBleed;
+ }
+ bleeding = false;
+ return 0;
+ }
+
+ private int BurnCheck()
+ {
+ if (burn > 0)
+ {
+ burning = true;
+ return 1 + MonsterManager.instance.bonusDamageOnBurn;
+ }
+ burning = false;
+ return 0;
+ }
+
+ private int PoisonCheck()
+ {
+ if (poison > 0)
+ {
+ poisoned = true;
+ return 1 + MonsterManager.instance.bonusDamageOnPoison;
+ }
+ poisoned = false;
+ return 0;
+ }
+
+ private int FreezeDmgCheck()
+ {
+ if (freezeTime > 0f)
+ {
+ return MonsterManager.instance.bonusDamageOnStun;
+ }
+ return 0;
+ }
+
+ public float CurrentHealth()
+ {
+ return (float)(health + armor + shield) / (float)(baseHealth + baseArmor + baseShield);
+ }
+
+ public void TakeDamage(TowerType whoHitMe, int _baseDmg, int healthDmg, int armorDmg, int shieldDmg, float slowPercentage, float bleedPercentage, float burnPercentage, float poisonPercentage, float critChance, float stunChance)
+ {
+ if (dead)
+ {
+ Debug.Log("Dead enemy taking damage");
+ return;
+ }
+ typesOfTowers.Add(whoHitMe);
+ if (Random.Range(0f, 1f) < stunChance)
+ {
+ Freeze();
+ }
+ float num = critChance;
+ if (_baseDmg > -1 && BleedCheck() > 0)
+ {
+ num += MonsterManager.instance.bleedingCritChance;
+ if (mark != null)
+ {
+ num += mark.critChance;
+ }
+ }
+ int num2 = _baseDmg;
+ string text = "";
+ float num3 = 1f;
+ if (Random.Range(0f, 1f) < Mathf.Min(num - 1f, 0.5f))
+ {
+ num2 *= 4;
+ text = "!!!";
+ num3 = 2.5f;
+ SFXManager.instance.PlaySound(Sound.CritBig, base.transform.position);
+ }
+ else if (Random.Range(0f, 1f) < Mathf.Min(num - 0.5f, 0.5f))
+ {
+ num2 *= 3;
+ text = "!!";
+ num3 = 2f;
+ SFXManager.instance.PlaySound(Sound.CritBig, base.transform.position);
+ }
+ else if (Random.Range(0f, 1f) < Mathf.Min(num, 0.5f))
+ {
+ num2 *= 2;
+ text = "!";
+ num3 = 1.5f;
+ SFXManager.instance.PlaySound(Sound.CritBig, base.transform.position);
+ }
+ int num4 = 0;
+ int num5 = 0;
+ string color;
+ if (shield > 0)
+ {
+ num4 = Mathf.Max(num2 - FortifiedCheck() + (int)MarkCheck().x + FreezeDmgCheck(), 1) * (shieldDmg + PoisonCheck() + (int)MarkCheck().w);
+ if (num4 > shield)
+ {
+ num5 = (int)((float)(num4 - shield) / (float)shieldDmg);
+ }
+ shield -= num4;
+ color = "Blue";
+ DamageTracker.instance.AddDamage(whoHitMe, 0, 0, num4);
+ shield = Mathf.Max(shield, 0);
+ if (shield == 0)
+ {
+ CheckBattleCries(BattleCry.BattleCryTrigger.ShieldBreak);
+ }
+ }
+ else if (armor > 0)
+ {
+ num4 = Mathf.Max(num2 - FortifiedCheck() + (int)MarkCheck().x + FreezeDmgCheck(), 1) * (armorDmg + BurnCheck() + (int)MarkCheck().z);
+ if (num4 > armor)
+ {
+ num5 = (int)((float)(num4 - armor) / (float)armorDmg);
+ }
+ armor -= num4;
+ color = "Yellow";
+ DamageTracker.instance.AddDamage(whoHitMe, 0, num4, 0);
+ armor = Mathf.Max(armor, 0);
+ if (armor == 0)
+ {
+ CheckBattleCries(BattleCry.BattleCryTrigger.ArmorBreak);
+ }
+ }
+ else
+ {
+ num4 = Mathf.Max(num2 - FortifiedCheck() + (int)MarkCheck().x + FreezeDmgCheck(), 1) * (healthDmg + BleedCheck() + (int)MarkCheck().y);
+ if (num4 >= health)
+ {
+ DamageTracker.instance.AddDamage(whoHitMe, health, 0, 0);
+ }
+ else
+ {
+ DamageTracker.instance.AddDamage(whoHitMe, num4, 0, 0);
+ }
+ health -= num4;
+ color = "Red";
+ }
+ if (OptionsMenu.instance.showDamageNumbers)
+ {
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText(num4 + text, color, num3);
+ component.SetHoldTime(0.25f);
+ }
+ if (health <= 0)
+ {
+ Die();
+ return;
+ }
+ bleed += (int)((float)num4 * bleedPercentage);
+ burn += (int)((float)num4 * burnPercentage);
+ poison += (int)((float)num4 * poisonPercentage);
+ if (slowPercentage > 0f)
+ {
+ AddSlow((float)num4 * slowPercentage / 100f);
+ }
+ if (poisonPercentage > 0f)
+ {
+ AddSlow(MonsterManager.instance.poisonSlowPercent * poisonPercentage * (float)num4 / 100f);
+ }
+ if (num3 > 1f && _baseDmg > -1 && MonsterManager.instance.bleedPop > 0f && !dead)
+ {
+ int num6 = (int)((float)bleed * MonsterManager.instance.bleedPop);
+ bleed -= num6;
+ TakeDamage(TowerType.DOT, -999, num6, num6 / 2, num6 / 2, 0f, 0f, 0f, 0f, 0f, 0f);
+ }
+ if (num3 > 1f && _baseDmg > -1 && MonsterManager.instance.burnPop > 0f && !dead)
+ {
+ int num7 = (int)((float)burn * MonsterManager.instance.burnPop);
+ burn -= num7;
+ TakeDamage(TowerType.DOT, -999, num7 / 2, num7, num7 / 2, 0f, 0f, 0f, 0f, 0f, 0f);
+ }
+ if (num3 > 1f && _baseDmg > -1 && MonsterManager.instance.poisonPop > 0f && !dead)
+ {
+ int num8 = (int)((float)poison * MonsterManager.instance.poisonPop);
+ poison -= num8;
+ TakeDamage(TowerType.DOT, -999, num8 / 2, num8 / 2, num8, 0f, 0f, 0f, 0f, 0f, 0f);
+ }
+ if (healthBar != null)
+ {
+ healthBar.UpdateHealth(health, armor, shield, currentSlowPercentage, bleeding, burning, poisoned, bleed, burn, poison);
+ }
+ if (num5 > 0 && !dead)
+ {
+ TakeDamage(whoHitMe, num5, healthDmg, armorDmg, shieldDmg, slowPercentage, bleedPercentage, burnPercentage, poisonPercentage, 0f, 0f);
+ }
+ }
+
+ private Vector4 MarkCheck()
+ {
+ Vector4 zero = Vector4.zero;
+ if (mark != null)
+ {
+ zero.x = mark.damage;
+ zero.y = mark.healthDamage;
+ zero.z = mark.armorDamage;
+ zero.w = mark.shieldDamage;
+ }
+ return zero;
+ }
+
+ public Vector3 GetFuturePosition(float t)
+ {
+ float distance = currentSpeed * t;
+ return pathfinder.GetFuturePosition(distance);
+ }
+
+ public void AtEnd()
+ {
+ GameManager.instance.TakeDamage(damageToTower + MonsterManager.instance.extraTowerDamage);
+ SpawnManager.instance.currentEnemies.Remove(this);
+ Object.Destroy(base.gameObject);
+ }
+
+ private void Die()
+ {
+ if (!dead)
+ {
+ dead = true;
+ if (deathObjectSpawn != null)
+ {
+ Object.Instantiate(deathObjectSpawn, base.transform.position, Quaternion.identity);
+ }
+ if (treasureObject != null && spawnRange.y > 0f)
+ {
+ int numberOfDrops = Random.Range((int)spawnRange.x, (int)spawnRange.y + GameManager.instance.gameMode);
+ Object.Instantiate(treasureObject, base.transform.position, Quaternion.identity).GetComponent<TreasureChest>().numberOfDrops = numberOfDrops;
+ }
+ CheckBattleCries(BattleCry.BattleCryTrigger.Death);
+ typesOfTowers.Remove(TowerType.DOT);
+ if (dropsGold)
+ {
+ int num = level + typesOfTowers.Count + bonusGoldDrop + MonsterManager.instance.extraGoldDrop;
+ ResourceManager.instance.AddMoney(num);
+ ResourceManager.instance.AddManaPercentMax(MonsterManager.instance.manaDropOnDeath);
+ DamageTracker.instance.AddIncome(DamageTracker.IncomeType.Monster, num - MonsterManager.instance.extraGoldDrop);
+ DamageTracker.instance.AddIncome(DamageTracker.IncomeType.Bonus, MonsterManager.instance.extraGoldDrop);
+ }
+ SpawnManager.instance.currentEnemies.Remove(this);
+ SFXManager.instance.PlaySound(Sound.CoinLong, base.transform.position);
+ AchievementManager.instance.EnemyKilled();
+ Object.Destroy(base.gameObject);
+ }
+ }
+}
diff --git a/GameCode/Explosion.cs b/GameCode/Explosion.cs
new file mode 100644
index 0000000..a01aa17
--- /dev/null
+++ b/GameCode/Explosion.cs
@@ -0,0 +1,27 @@
+using UnityEngine;
+
+public class Explosion : MonoBehaviour
+{
+ [SerializeField]
+ private float duration = 2f;
+
+ [SerializeField]
+ private Sound sound;
+
+ private void Start()
+ {
+ if (sound != 0)
+ {
+ SFXManager.instance.PlaySound(sound, base.transform.position);
+ }
+ }
+
+ private void FixedUpdate()
+ {
+ duration -= Time.fixedDeltaTime;
+ if (duration <= 0f)
+ {
+ Object.Destroy(base.gameObject);
+ }
+ }
+}
diff --git a/GameCode/FlameThrower.cs b/GameCode/FlameThrower.cs
new file mode 100644
index 0000000..d415d88
--- /dev/null
+++ b/GameCode/FlameThrower.cs
@@ -0,0 +1,68 @@
+using UnityEngine;
+
+public class FlameThrower : Tower
+{
+ [SerializeField]
+ private ParticleSystem flames;
+
+ public bool flaming;
+
+ private bool hasMana = true;
+
+ [SerializeField]
+ private AudioSource audioS;
+
+ private bool soundPlaying;
+
+ protected override void Update()
+ {
+ if (hasMana && currentTarget != null && !flaming)
+ {
+ flames.Play();
+ PlaySound(onOff: true);
+ flaming = true;
+ }
+ else if (!hasMana || (currentTarget == null && flaming))
+ {
+ flames.Stop();
+ PlaySound(onOff: false);
+ flaming = false;
+ }
+ base.Update();
+ }
+
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * manaConsumptionRate);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ hasMana = false;
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ hasMana = true;
+ }
+ RaycastHit[] array = Physics.SphereCastAll(muzzle.position, base.range / 6f, muzzle.transform.forward, base.range * 1.5f, enemyLayerMask, QueryTriggerInteraction.Collide);
+ foreach (RaycastHit raycastHit in array)
+ {
+ raycastHit.collider.GetComponent<IDamageable>()?.TakeDamage(towerType, base.damage, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ }
+ }
+
+ private void PlaySound(bool onOff)
+ {
+ if (onOff && !soundPlaying)
+ {
+ audioS.volume = OptionsMenu.instance.masterVolume * OptionsMenu.instance.sfxVolume;
+ audioS.Play();
+ soundPlaying = true;
+ }
+ else if (!onOff && soundPlaying)
+ {
+ audioS.Stop();
+ soundPlaying = false;
+ }
+ }
+}
diff --git a/GameCode/GameManager.cs b/GameCode/GameManager.cs
new file mode 100644
index 0000000..bfa59e1
--- /dev/null
+++ b/GameCode/GameManager.cs
@@ -0,0 +1,476 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class GameManager : MonoBehaviour
+{
+ [SerializeField]
+ private TileManager tileManager;
+
+ [SerializeField]
+ private SpawnManager spawnManager;
+
+ public int gameMode = 3;
+
+ [SerializeField]
+ private TerrainTile singleStartTile;
+
+ [SerializeField]
+ private Waypoint[] singleStartWaypoints;
+
+ [SerializeField]
+ private TerrainTile doubleStartTile;
+
+ [SerializeField]
+ private Waypoint[] doubleStartWaypoints;
+
+ [SerializeField]
+ private TerrainTile tripleStartTile;
+
+ [SerializeField]
+ private Waypoint[] tripleStartWaypoints;
+
+ public bool gameOver;
+
+ public int health;
+
+ public int maxHealth;
+
+ private float healthLoss;
+
+ [SerializeField]
+ private Text healthText;
+
+ [SerializeField]
+ private Image maskBar;
+
+ [SerializeField]
+ private Image healthBar;
+
+ [SerializeField]
+ private Image healthLossBar;
+
+ [SerializeField]
+ private GameObject gameOverMenu;
+
+ [SerializeField]
+ private Text levelsCompletedText;
+
+ [SerializeField]
+ private Text xpGainText;
+
+ [SerializeField]
+ private Text newRecordText;
+
+ [SerializeField]
+ private GameObject victoryScreen;
+
+ [SerializeField]
+ private Text vicLevelsText;
+
+ [SerializeField]
+ private Text vicLevelXpText;
+
+ [SerializeField]
+ private Text vicXpBonusText;
+
+ [SerializeField]
+ private Text vicNewRecordText;
+
+ public Vector3 dotTick = 24f * Vector3.one;
+
+ public int hauntedHouseEfficiency = 1;
+
+ public int universityBonus;
+
+ public static GameManager instance;
+
+ private void Awake()
+ {
+ instance = this;
+ gameMode = PlayerPrefs.GetInt("GameMode", 1);
+ if (gameMode == 1)
+ {
+ singleStartTile.transform.Rotate(0f, 90 * Random.Range(-1, 3), 0f);
+ singleStartTile.SetCardinalDirections();
+ tileManager.startTile = singleStartTile;
+ spawnManager.initialSpawns = singleStartWaypoints;
+ doubleStartTile.gameObject.SetActive(value: false);
+ tripleStartTile.gameObject.SetActive(value: false);
+ }
+ else if (gameMode == 2)
+ {
+ doubleStartTile.transform.Rotate(0f, 90 * Random.Range(-1, 3), 0f);
+ doubleStartTile.SetCardinalDirections();
+ tileManager.startTile = doubleStartTile;
+ spawnManager.initialSpawns = doubleStartWaypoints;
+ singleStartTile.gameObject.SetActive(value: false);
+ tripleStartTile.gameObject.SetActive(value: false);
+ }
+ else if (gameMode == 3)
+ {
+ tripleStartTile.transform.Rotate(0f, 90 * Random.Range(-1, 3), 0f);
+ tripleStartTile.SetCardinalDirections();
+ tileManager.startTile = tripleStartTile;
+ spawnManager.initialSpawns = tripleStartWaypoints;
+ doubleStartTile.gameObject.SetActive(value: false);
+ singleStartTile.gameObject.SetActive(value: false);
+ }
+
+ }
+
+ private void Start()
+ {
+ maxHealth = 100 + 10 * (PlayerPrefs.GetInt("TowerHealth1", 0) + PlayerPrefs.GetInt("TowerHealth2", 0) + PlayerPrefs.GetInt("TowerHealth3", 0) + PlayerPrefs.GetInt("TowerHealth4", 0) + PlayerPrefs.GetInt("TowerHealth5", 0) + PlayerPrefs.GetInt("TowerHealth6", 0) + PlayerPrefs.GetInt("TowerHealth7", 0) + PlayerPrefs.GetInt("TowerHealth8", 0) + PlayerPrefs.GetInt("TowerHealth9", 0) + PlayerPrefs.GetInt("TowerHealth10", 0));
+ health = maxHealth;
+ healthLoss = maxHealth;
+ SetHealthBar();
+ int num = 0;
+ num = PlayerPrefs.GetInt("BalistaPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Crossbow, num);
+ }
+ num = PlayerPrefs.GetInt("BalistaPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Crossbow, num);
+ }
+ num = PlayerPrefs.GetInt("BalistaPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Crossbow, num);
+ }
+ num = PlayerPrefs.GetInt("MortarPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Morter, num);
+ }
+ num = PlayerPrefs.GetInt("MortarPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Morter, num);
+ }
+ num = PlayerPrefs.GetInt("MortarPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Morter, num);
+ }
+ num = PlayerPrefs.GetInt("TeslaPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.TeslaCoil, num);
+ }
+ num = PlayerPrefs.GetInt("TeslaPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.TeslaCoil, num);
+ }
+ num = PlayerPrefs.GetInt("TeslaPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.TeslaCoil, num);
+ }
+ num = PlayerPrefs.GetInt("FrostPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Frost, num);
+ }
+ num = PlayerPrefs.GetInt("FrostPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Frost, num);
+ }
+ num = PlayerPrefs.GetInt("FrostPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Frost, num);
+ }
+ num = PlayerPrefs.GetInt("FlamePHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.FlameThrower, num);
+ }
+ num = PlayerPrefs.GetInt("FlamePArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.FlameThrower, num);
+ }
+ num = PlayerPrefs.GetInt("FlamePShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.FlameThrower, num);
+ }
+ num = PlayerPrefs.GetInt("PoisonPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.PoisonSprayer, num);
+ }
+ num = PlayerPrefs.GetInt("PoisonPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.PoisonSprayer, num);
+ }
+ num = PlayerPrefs.GetInt("PoisonPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.PoisonSprayer, num);
+ }
+ num = PlayerPrefs.GetInt("ShredderPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Shredder, num);
+ }
+ num = PlayerPrefs.GetInt("ShredderPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Shredder, num);
+ }
+ num = PlayerPrefs.GetInt("ShredderPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Shredder, num);
+ }
+ num = PlayerPrefs.GetInt("EncampmentPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Encampment, num);
+ }
+ num = PlayerPrefs.GetInt("EncampmentPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Encampment, num);
+ }
+ num = PlayerPrefs.GetInt("EncampmentPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Encampment, num);
+ }
+ num = PlayerPrefs.GetInt("LookoutPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Lookout, num);
+ }
+ num = PlayerPrefs.GetInt("LookoutPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Lookout, num);
+ }
+ num = PlayerPrefs.GetInt("LookoutPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Lookout, num);
+ }
+ num = PlayerPrefs.GetInt("RadarPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Radar, num);
+ }
+ num = PlayerPrefs.GetInt("RadarPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Radar, num);
+ }
+ num = PlayerPrefs.GetInt("RadarPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Radar, num);
+ }
+ num = PlayerPrefs.GetInt("ObeliskPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Obelisk, num);
+ }
+ num = PlayerPrefs.GetInt("ObeliskPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Obelisk, num);
+ }
+ num = PlayerPrefs.GetInt("ObeliskPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Obelisk, num);
+ }
+ num = PlayerPrefs.GetInt("ParticleCannonPHealth", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(TowerType.ParticleCannon, num);
+ }
+ num = PlayerPrefs.GetInt("ParticleCannonPArmor", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(TowerType.ParticleCannon, num);
+ }
+ num = PlayerPrefs.GetInt("ParticleCannonPShield", 0);
+ if (num > 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(TowerType.ParticleCannon, num);
+ }
+ }
+
+ private void Update()
+ {
+ if (healthLoss > (float)health)
+ {
+ healthLoss = Mathf.Clamp(healthLoss - 4f * Time.deltaTime, health, healthLoss);
+ healthLossBar.rectTransform.sizeDelta = new Vector2(healthLoss / 10f, 0.25f);
+ }
+ }
+
+ public void IncreaseTowerHealth(int amt)
+ {
+ maxHealth += amt * 10;
+ health += amt * 10;
+ healthLoss = health;
+ SetHealthBar();
+ if (maxHealth / 10 >= 50)
+ {
+ AchievementManager.instance.UnlockAchievement("TowerHealth50");
+ }
+ else if (maxHealth / 10 >= 40)
+ {
+ AchievementManager.instance.UnlockAchievement("TowerHealth40");
+ }
+ else if (maxHealth / 10 >= 30)
+ {
+ AchievementManager.instance.UnlockAchievement("TowerHealth30");
+ }
+ }
+
+ private void SetHealthBar()
+ {
+ maskBar.rectTransform.localScale = new Vector3(2000 / maxHealth, 50f, 10f);
+ maskBar.rectTransform.sizeDelta = new Vector2((float)maxHealth / 10f, 0.25f);
+ healthLossBar.rectTransform.sizeDelta = new Vector2((float)health / 10f, 0.25f);
+ UpdateHealthText();
+ }
+
+ private void UpdateHealthText()
+ {
+ healthText.text = "Tower: " + health / 10 + "/" + maxHealth / 10;
+ healthBar.rectTransform.sizeDelta = new Vector2((float)health / 10f, 0.25f);
+ }
+
+ public void TakeDamage(int damage)
+ {
+ health = Mathf.Max(health - 10 * damage, 0);
+ UpdateHealthText();
+ if (health <= 0 && !gameOver)
+ {
+ gameOver = true;
+ GameOver();
+ }
+ }
+
+ public void RepairTower(int heal)
+ {
+ health = Mathf.Clamp(health + heal, 0, maxHealth);
+ if (healthLoss < (float)health)
+ {
+ healthLoss = health;
+ }
+ UpdateHealthText();
+ }
+
+ public void GameOverQuit()
+ {
+ if (!SpawnManager.instance.combat)
+ {
+ SpawnManager.instance.level++;
+ }
+ if (Time.timeScale != 1f)
+ {
+ Time.timeScale = 1f;
+ }
+ gameOver = true;
+ GameOver();
+ }
+
+ public int NaturalSum(int value)
+ {
+ return value * (value + 1) / 2;
+ }
+
+ private void GameOver()
+ {
+ gameOverMenu.SetActive(value: true);
+ DamageTracker.instance.DisplayDamageTotals();
+ int num = NaturalSum(SpawnManager.instance.level - 1) * gameMode;
+ int num2 = 0;
+ int @int = PlayerPrefs.GetInt("Record" + gameMode, 0);
+ if (SpawnManager.instance.level - 1 - @int > 0)
+ {
+ num2 = (NaturalSum(SpawnManager.instance.level - 1) - NaturalSum(@int)) * 3;
+ PlayerPrefs.SetInt("Record" + gameMode, SpawnManager.instance.level - 1);
+ string text = "";
+ if (gameMode == 1)
+ {
+ text = "single";
+ }
+ else if (gameMode == 2)
+ {
+ text = "double";
+ }
+ else if (gameMode == 3)
+ {
+ text = "triple";
+ }
+ newRecordText.text = "New " + text + " defense record!\n +" + num2 + " bonus xp";
+ }
+ else
+ {
+ newRecordText.text = "";
+ }
+ int int2 = PlayerPrefs.GetInt("XP", 0);
+ PlayerPrefs.SetInt("XP", int2 + num2);
+ levelsCompletedText.text = "Defended " + (SpawnManager.instance.level - 1) + " levels";
+ xpGainText.text = "+" + num + " xp";
+ }
+
+ public void Victory()
+ {
+ AchievementManager.instance.CheckTowerTypesVictory();
+ victoryScreen.SetActive(value: true);
+ DamageTracker.instance.DisplayDamageTotals();
+ int num = NaturalSum(SpawnManager.instance.level - 1) * gameMode;
+ int num2 = SpawnManager.instance.lastLevel * gameMode * 10;
+ int num3 = 0;
+ int @int = PlayerPrefs.GetInt("Record" + gameMode, 0);
+ if (SpawnManager.instance.lastLevel - @int > 0)
+ {
+ num3 = (NaturalSum(SpawnManager.instance.level - 1) - NaturalSum(@int)) * 3;
+ PlayerPrefs.SetInt("Record" + gameMode, SpawnManager.instance.lastLevel);
+ string text = "";
+ if (gameMode == 1)
+ {
+ text = "single";
+ }
+ else if (gameMode == 2)
+ {
+ text = "double";
+ }
+ else if (gameMode == 3)
+ {
+ text = "triple";
+ }
+ vicNewRecordText.text = "New " + text + " defense record!\n +" + num3 + " bonus xp";
+ }
+ else
+ {
+ vicNewRecordText.text = "";
+ }
+ int int2 = PlayerPrefs.GetInt("XP", 0);
+ PlayerPrefs.SetInt("XP", int2 + num3 + num2);
+ vicLevelsText.text = "Defended all " + SpawnManager.instance.lastLevel + " levels";
+ vicLevelXpText.text = "+" + num + " xp";
+ vicXpBonusText.text = "+" + num2 + " xp";
+ }
+
+ public void LoadScene(string sceneName)
+ {
+ if (AchievementManager.instance != null)
+ {
+ AchievementManager.instance.OnSceneClose();
+ }
+ LevelLoader.instance.LoadLevel(sceneName);
+ }
+}
diff --git a/GameCode/GoldRushCard.cs b/GameCode/GoldRushCard.cs
new file mode 100644
index 0000000..79663ce
--- /dev/null
+++ b/GameCode/GoldRushCard.cs
@@ -0,0 +1,17 @@
+using UnityEngine;
+
+public class GoldRushCard : UpgradeCard
+{
+ [SerializeField]
+ private int goldGain;
+
+ [SerializeField]
+ private float speedBonus;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ MonsterManager.instance.speedBonus += speedBonus;
+ ResourceManager.instance.AddMoney(goldGain);
+ }
+}
diff --git a/GameCode/Grave.cs b/GameCode/Grave.cs
new file mode 100644
index 0000000..45abddc
--- /dev/null
+++ b/GameCode/Grave.cs
@@ -0,0 +1,3 @@
+public class Grave : SpawnableObject
+{
+}
diff --git a/GameCode/HauntedHouse.cs b/GameCode/HauntedHouse.cs
new file mode 100644
index 0000000..9a23eda
--- /dev/null
+++ b/GameCode/HauntedHouse.cs
@@ -0,0 +1,111 @@
+using UnityEngine;
+
+public class HauntedHouse : IncomeGenerator, IBuildable
+{
+ [SerializeField]
+ private LayerMask graveLayerMask;
+
+ [SerializeField]
+ private GameObject UIObject;
+
+ [SerializeField]
+ private int goldBackOnDemolish;
+
+ private bool isGathering;
+
+ private int graveCount;
+
+ private int manaUsed;
+
+ private float timer;
+
+ private SimpleUI myUI;
+
+ protected override void Start()
+ {
+ base.Start();
+ DetectGraves();
+ }
+
+ private void Update()
+ {
+ if (myUI != null && isGathering)
+ {
+ string text = "Mana used: " + manaUsed;
+ text = text + "\nNearby graves: x" + graveCount;
+ text = text + "\nTax efficiency: x" + GameManager.instance.hauntedHouseEfficiency;
+ text = text + "\nDeath tax due: " + Mathf.Max((int)Mathf.Sqrt(manaUsed * GameManager.instance.hauntedHouseEfficiency * graveCount), 1) + "g";
+ text = text + "\nNet tax collected: " + base.netGold + "g.";
+ myUI.SetDiscriptionText(text);
+ }
+ if (!isGathering || !SpawnManager.instance.combat)
+ {
+ return;
+ }
+ if (timer <= 0f)
+ {
+ if (ResourceManager.instance.CheckMana(1))
+ {
+ ResourceManager.instance.SpendMana(1);
+ manaUsed++;
+ timer = 1f;
+ }
+ }
+ else
+ {
+ timer -= Time.deltaTime;
+ }
+ }
+
+ public override void GenerateIncome()
+ {
+ incomePerRound = Mathf.Max((int)Mathf.Sqrt(manaUsed * GameManager.instance.hauntedHouseEfficiency * graveCount), 1);
+ base.GenerateIncome();
+ manaUsed = 0;
+ incomePerRound = 1;
+ }
+
+ public void SetStats()
+ {
+ }
+
+ private void DetectGraves()
+ {
+ if (Physics.Raycast(base.transform.position + new Vector3(1f, 1f, 0f), -base.transform.up, out var hitInfo, 1f, graveLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckGrave(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(-1f, 1f, 0f), -base.transform.up, out hitInfo, 1f, graveLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckGrave(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(0f, 1f, 1f), -base.transform.up, out hitInfo, 1f, graveLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckGrave(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(0f, 1f, -1f), -base.transform.up, out hitInfo, 1f, graveLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckGrave(hitInfo);
+ }
+ }
+
+ private void CheckGrave(RaycastHit hit)
+ {
+ if (hit.collider.GetComponent<Grave>() != null && (double)Mathf.Abs(hit.collider.transform.position.y - base.transform.position.y) <= 0.001)
+ {
+ graveCount++;
+ isGathering = true;
+ }
+ }
+
+ public void SpawnUI()
+ {
+ myUI = Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>();
+ myUI.SetDemolishable(base.gameObject, goldBackOnDemolish);
+ }
+
+ public void Demolish()
+ {
+ RemoveIncomeGeneration();
+ }
+}
diff --git a/GameCode/HauntedHouseUpgrade.cs b/GameCode/HauntedHouseUpgrade.cs
new file mode 100644
index 0000000..47d4c8a
--- /dev/null
+++ b/GameCode/HauntedHouseUpgrade.cs
@@ -0,0 +1,8 @@
+public class HauntedHouseUpgrade : UpgradeCard
+{
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ GameManager.instance.hauntedHouseEfficiency++;
+ }
+}
diff --git a/GameCode/HealthBar.cs b/GameCode/HealthBar.cs
new file mode 100644
index 0000000..558df63
--- /dev/null
+++ b/GameCode/HealthBar.cs
@@ -0,0 +1,157 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HealthBar : MonoBehaviour
+{
+ [SerializeField]
+ private Image maskBar;
+
+ [SerializeField]
+ private Image healthBar;
+
+ [SerializeField]
+ private Image armorBar;
+
+ [SerializeField]
+ private Image shieldBar;
+
+ [SerializeField]
+ private Image dmgBar;
+
+ [SerializeField]
+ private Image slowImageLeft;
+
+ [SerializeField]
+ private Image slowImageRight;
+
+ [SerializeField]
+ private GameObject BleedImage;
+
+ [SerializeField]
+ private GameObject BurnImage;
+
+ [SerializeField]
+ private GameObject PoisonImage;
+
+ [SerializeField]
+ private Text bleedText;
+
+ [SerializeField]
+ private Text burnText;
+
+ [SerializeField]
+ private Text poisonText;
+
+ [SerializeField]
+ private Image fortImageLeft;
+
+ [SerializeField]
+ private Image fortImageRight;
+
+ [SerializeField]
+ private Image hasteImageLeft;
+
+ [SerializeField]
+ private Image hasteImageRight;
+
+ private float maxHp;
+
+ private float health;
+
+ private float armor;
+
+ private float shield;
+
+ private float dmg;
+
+ private float barScaler = 100f;
+
+ private void Update()
+ {
+ if (dmg > health + armor + shield)
+ {
+ dmg = Mathf.Clamp(dmg - 4f * Time.deltaTime, health + armor + shield, dmg);
+ dmgBar.rectTransform.sizeDelta = new Vector2(dmg, 0.25f);
+ }
+ }
+
+ public void SetHealth(int max, int heal, int armr, int shld, int scaleDegree)
+ {
+ barScaler = 10f * Mathf.Pow(10f, scaleDegree);
+ maxHp = (float)max / barScaler;
+ dmg = maxHp;
+ health = (float)heal / barScaler;
+ armor = (float)armr / barScaler;
+ shield = (float)shld / barScaler;
+ maskBar.rectTransform.localScale = new Vector3(3f / (maxHp + 3f), 1f, 1f);
+ maskBar.rectTransform.sizeDelta = new Vector2(maxHp, 0.25f);
+ dmgBar.rectTransform.sizeDelta = new Vector2(dmg, 0.25f);
+ UpdateHealth(heal, armr, shld, 0f, isBleeding: false, isBurning: false, isPoisoned: false, 0, 0, 0);
+ }
+
+ public void UpdateHealth(int heal, int armr, int shld, float currentSlow, bool isBleeding, bool isBurning, bool isPoisoned, int currentBleed, int currentBurn, int currentPoison)
+ {
+ health = (float)heal / barScaler;
+ armor = (float)armr / barScaler;
+ shield = (float)shld / barScaler;
+ healthBar.rectTransform.sizeDelta = new Vector2(health, 0.25f);
+ armorBar.rectTransform.sizeDelta = new Vector2(armor, 0.25f);
+ shieldBar.rectTransform.sizeDelta = new Vector2(shield, 0.25f);
+ armorBar.rectTransform.localPosition = new Vector3(health - maskBar.rectTransform.sizeDelta.x / 2f, 0f, 0f);
+ shieldBar.rectTransform.localPosition = new Vector3(health + armor - maskBar.rectTransform.sizeDelta.x / 2f, 0f, 0f);
+ Image image = slowImageLeft;
+ float fillAmount = (slowImageRight.fillAmount = currentSlow);
+ image.fillAmount = fillAmount;
+ BleedImage.SetActive(isBleeding);
+ bleedText.gameObject.SetActive(isBleeding);
+ bleedText.text = currentBleed.ToString();
+ BurnImage.SetActive(isBurning);
+ burnText.gameObject.SetActive(isBurning);
+ burnText.text = currentBurn.ToString();
+ PoisonImage.SetActive(isPoisoned);
+ poisonText.gameObject.SetActive(isPoisoned);
+ poisonText.text = currentPoison.ToString();
+ }
+
+ public void UpdateSlow(float currentSlow)
+ {
+ Image image = slowImageLeft;
+ float fillAmount = (slowImageRight.fillAmount = currentSlow);
+ image.fillAmount = fillAmount;
+ }
+
+ public void UpdateBleed(bool status, int amt)
+ {
+ BleedImage.SetActive(status);
+ bleedText.gameObject.SetActive(status);
+ bleedText.text = amt.ToString();
+ }
+
+ public void UpdateBurn(bool status, int amt)
+ {
+ BurnImage.SetActive(status);
+ burnText.gameObject.SetActive(status);
+ burnText.text = amt.ToString();
+ }
+
+ public void UpdatePoison(bool status, int amt)
+ {
+ PoisonImage.SetActive(status);
+ poisonText.gameObject.SetActive(status);
+ poisonText.text = amt.ToString();
+ }
+
+ public void UpdateFortified(float fortTime)
+ {
+ Image image = fortImageLeft;
+ float fillAmount = (fortImageRight.fillAmount = fortTime * 0.083333f);
+ image.fillAmount = fillAmount;
+ }
+
+ public void UpdateHaste(float hastePercentage)
+ {
+ Image image = hasteImageLeft;
+ float fillAmount = (hasteImageRight.fillAmount = hastePercentage);
+ image.fillAmount = fillAmount;
+ }
+}
diff --git a/GameCode/House.cs b/GameCode/House.cs
new file mode 100644
index 0000000..16bb26d
--- /dev/null
+++ b/GameCode/House.cs
@@ -0,0 +1,49 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class House : SpawnableObject
+{
+ private List<Tower> defenders = new List<Tower>();
+
+ [SerializeField]
+ private IncomeGenerator myIncomeGenerator;
+
+ protected override void Start()
+ {
+ base.Start();
+ SpawnManager.instance.houses.Add(this);
+ }
+
+ public void AddDefender(Tower t)
+ {
+ if (!defenders.Contains(t))
+ {
+ defenders.Add(t);
+ }
+ CheckTowers();
+ }
+
+ public void CheckTowers()
+ {
+ for (int num = defenders.Count - 1; num > -1; num--)
+ {
+ if (defenders[num] == null)
+ {
+ defenders.RemoveAt(num);
+ }
+ }
+ myIncomeGenerator.incomeTimesLevel = defenders.Count;
+ }
+
+ public override void SpawnUI()
+ {
+ SimpleUI component = Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>();
+ if (defenders.Count > 0)
+ {
+ string text = "This house is protected by " + defenders.Count + " towers.";
+ text = text + "\nIts next gift will be " + (SpawnManager.instance.level + 1) * defenders.Count + "g.";
+ text = text + "\nNet gold gifted: " + myIncomeGenerator.netGold + "g.";
+ component.SetDiscriptionText(text);
+ }
+ }
+}
diff --git a/GameCode/IBuildable.cs b/GameCode/IBuildable.cs
new file mode 100644
index 0000000..5fcdc7e
--- /dev/null
+++ b/GameCode/IBuildable.cs
@@ -0,0 +1,8 @@
+public interface IBuildable
+{
+ void SetStats();
+
+ void Demolish();
+
+ void SpawnUI();
+}
diff --git a/GameCode/IDamageable.cs b/GameCode/IDamageable.cs
new file mode 100644
index 0000000..d010ef5
--- /dev/null
+++ b/GameCode/IDamageable.cs
@@ -0,0 +1,4 @@
+public interface IDamageable
+{
+ void TakeDamage(TowerType whoHitMe, int baseDmg, int healthDmg, int armorDmg, int shieldDmg, float slowPercentage, float bleedPercentage, float burnPercentage, float poisonPercentage, float critChance, float stunChance);
+}
diff --git a/GameCode/IncomeGenerator.cs b/GameCode/IncomeGenerator.cs
new file mode 100644
index 0000000..9afd8f5
--- /dev/null
+++ b/GameCode/IncomeGenerator.cs
@@ -0,0 +1,39 @@
+using UnityEngine;
+
+public class IncomeGenerator : MonoBehaviour
+{
+ public int incomePerRound;
+
+ public float incomeTimesLevel;
+
+ [SerializeField]
+ private DamageTracker.IncomeType myIncomeType;
+
+ public int netGold { get; private set; }
+
+ protected virtual void Start()
+ {
+ SpawnManager.instance.incomeGenerators.Add(this);
+ netGold = 0;
+ }
+
+ public virtual void GenerateIncome()
+ {
+ int num = incomePerRound + (int)(incomeTimesLevel * (float)SpawnManager.instance.level);
+ if (num > 0)
+ {
+ ResourceManager.instance.AddMoney(num);
+ netGold += num;
+ DamageTracker.instance.AddIncome(myIncomeType, num);
+ SFXManager.instance.PlaySound(Sound.CoinShort, base.transform.position);
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("+" + num + "g", "Grey", 1f);
+ component.SetHoldTime(2.5f);
+ }
+ }
+
+ public void RemoveIncomeGeneration()
+ {
+ SpawnManager.instance.incomeGenerators.Remove(this);
+ }
+}
diff --git a/GameCode/IronVein.cs b/GameCode/IronVein.cs
new file mode 100644
index 0000000..06af952
--- /dev/null
+++ b/GameCode/IronVein.cs
@@ -0,0 +1,3 @@
+public class IronVein : SpawnableObject
+{
+}
diff --git a/GameCode/KeyDisappear.cs b/GameCode/KeyDisappear.cs
new file mode 100644
index 0000000..d997703
--- /dev/null
+++ b/GameCode/KeyDisappear.cs
@@ -0,0 +1,61 @@
+using UnityEngine;
+
+public class KeyDisappear : MonoBehaviour
+{
+ [SerializeField]
+ private GameObject wKey;
+
+ [SerializeField]
+ private GameObject aKey;
+
+ [SerializeField]
+ private GameObject sKey;
+
+ [SerializeField]
+ private GameObject dKey;
+
+ private bool w;
+
+ private bool a;
+
+ private bool s;
+
+ private bool d;
+
+ [SerializeField]
+ private bool destroyOnCompletion = true;
+
+ private float time;
+
+ private void Update()
+ {
+ time += Time.deltaTime;
+ if (time > 3f)
+ {
+ if (Input.GetKeyUp(KeyCode.W))
+ {
+ wKey.SetActive(value: false);
+ w = true;
+ }
+ if (Input.GetKeyUp(KeyCode.A))
+ {
+ aKey.SetActive(value: false);
+ a = true;
+ }
+ if (Input.GetKeyUp(KeyCode.S))
+ {
+ sKey.SetActive(value: false);
+ s = true;
+ }
+ if (Input.GetKeyUp(KeyCode.D))
+ {
+ dKey.SetActive(value: false);
+ d = true;
+ }
+ if (destroyOnCompletion && w && a && s && d)
+ {
+ Object.Destroy(base.gameObject);
+ }
+ }
+ }
+}
diff --git a/GameCode/Landmine.cs b/GameCode/Landmine.cs
new file mode 100644
index 0000000..5422016
--- /dev/null
+++ b/GameCode/Landmine.cs
@@ -0,0 +1,67 @@
+using System.Collections;
+using UnityEngine;
+
+public class Landmine : Projectile
+{
+ public float blastRadius = 0.5f;
+
+ private bool armed;
+
+ [SerializeField]
+ private GameObject explosion;
+
+ protected override void Start()
+ {
+ base.Start();
+ SpawnManager.instance.destroyOnNewLevel.Add(base.gameObject);
+ }
+
+ public void SetEndPosition(Vector3 endPos)
+ {
+ StartCoroutine(ThrowMine(endPos));
+ }
+
+ protected override void FixedUpdate()
+ {
+ }
+
+ private void OnTriggerEnter(Collider other)
+ {
+ if (armed && other.gameObject.layer == LayerMask.NameToLayer("Enemy"))
+ {
+ Explode();
+ }
+ }
+
+ private void Explode()
+ {
+ Collider[] array = Physics.OverlapSphere(base.transform.position, blastRadius, layermask, QueryTriggerInteraction.Collide);
+ for (int i = 0; i < array.Length; i++)
+ {
+ IDamageable component = array[i].GetComponent<IDamageable>();
+ if (component != null)
+ {
+ DealDamage(component);
+ }
+ }
+ Object.Instantiate(explosion, base.transform.position, Quaternion.identity).transform.localScale = Vector3.one * 0.5f;
+ SpawnManager.instance.destroyOnNewLevel.Remove(base.gameObject);
+ Object.Destroy(base.gameObject);
+ }
+
+ private IEnumerator ThrowMine(Vector3 endPos)
+ {
+ Vector3 direction = endPos - base.transform.position;
+ float throwTime = 1f;
+ base.transform.localScale = Vector3.zero;
+ for (float i = 0f; i < 1f; i += Time.deltaTime / throwTime)
+ {
+ base.transform.localScale = Vector3.one * i;
+ base.transform.Translate(direction * Time.deltaTime / throwTime);
+ yield return null;
+ }
+ base.transform.localScale = Vector3.one;
+ base.transform.position = endPos;
+ armed = true;
+ }
+}
diff --git a/GameCode/LevelLoader.cs b/GameCode/LevelLoader.cs
new file mode 100644
index 0000000..1698c15
--- /dev/null
+++ b/GameCode/LevelLoader.cs
@@ -0,0 +1,116 @@
+using System.Collections;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using UnityEngine.UI;
+
+public class LevelLoader : MonoBehaviour
+{
+ public static LevelLoader instance;
+
+ [SerializeField]
+ private Image circle;
+
+ [SerializeField]
+ private Text loadingText;
+
+ [SerializeField]
+ private Text tipText;
+
+ [SerializeField]
+ private string[] tips;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ loadingText.text = "";
+ tipText.text = "";
+ StartCoroutine(CircleExitStart());
+ }
+
+ public void LoadLevel(string sceneName)
+ {
+ if (MusicManager.instance != null)
+ {
+ MusicManager.instance.FadeOut(2.5f);
+ }
+ StartCoroutine(LoadAsyncronously(sceneName));
+ }
+
+ private IEnumerator LoadAsyncronously(string sceneName)
+ {
+ float num = Mathf.Sqrt(Screen.height * Screen.width) * 1.1f;
+ Vector2 endSize = new Vector2(2f, 1f) * num * 2f;
+ Vector2 startSize = new Vector2(0f, 1f) * num * 2f;
+ circle.rectTransform.sizeDelta = startSize;
+ Vector3 endPosition = new Vector3((float)Screen.height / 2f, (float)Screen.height / 2f, 0f);
+ Vector3 startPosition = new Vector3((float)Screen.width * 1.1f, (float)Screen.height / 2f, 0f);
+ circle.transform.position = startPosition;
+ float t = 0f;
+ while (t < 1f)
+ {
+ circle.rectTransform.sizeDelta = Vector2.Lerp(startSize, endSize, t);
+ circle.transform.position = Vector3.Lerp(startPosition, endPosition, t);
+ t += Time.deltaTime / 1.5f;
+ yield return null;
+ }
+ circle.rectTransform.sizeDelta = endSize;
+ circle.transform.position = endPosition;
+ tipText.text = GetTipsText();
+ AsyncOperation operation = SceneManager.LoadSceneAsync(sceneName);
+ operation.allowSceneActivation = false;
+ float waitFill = 0f;
+ while (!operation.isDone || waitFill < 1f)
+ {
+ waitFill = Mathf.Clamp(waitFill + Time.deltaTime / 3f, 0f, 1f);
+ float num2 = Mathf.Clamp(operation.progress / 0.9f, 0f, 1f);
+ if (waitFill >= 1f)
+ {
+ operation.allowSceneActivation = true;
+ }
+ loadingText.text = GetLoadingText((waitFill * num2 + waitFill) / 2f);
+ yield return null;
+ }
+ }
+
+ private string GetLoadingText(float percent)
+ {
+ return "Loading...".Substring(0, (int)Mathf.Round(percent * 10f));
+ }
+
+ private string GetTipsText()
+ {
+ int num = Random.Range(0, tips.Length);
+ int @int = PlayerPrefs.GetInt("PreviousTip", 0);
+ if (num == @int)
+ {
+ num = (num + 1) % tips.Length;
+ }
+ PlayerPrefs.SetInt("PreviousTip", num);
+ return tips[num];
+ }
+
+ private IEnumerator CircleExitStart()
+ {
+ float num = Mathf.Sqrt(Screen.height * Screen.width) * 1.1f;
+ Vector2 startSize = new Vector2(2f, 1f) * num * 2f;
+ Vector2 endSize = new Vector2(0f, 1f) * num * 2f;
+ circle.rectTransform.sizeDelta = startSize;
+ Vector3 startPosition = new Vector3((float)Screen.height / 2f, (float)Screen.height / 2f, 0f);
+ Vector3 endPosition = new Vector3((float)(-Screen.width) * 0.1f, (float)Screen.height / 2f, 0f);
+ circle.transform.position = startPosition;
+ float t = 0f;
+ while (t < 1f)
+ {
+ circle.rectTransform.sizeDelta = Vector2.Lerp(startSize, endSize, t);
+ circle.transform.position = Vector3.Lerp(startPosition, endPosition, t);
+ t += Time.deltaTime / 1.5f;
+ yield return null;
+ }
+ circle.rectTransform.sizeDelta = endSize;
+ circle.transform.position = endPosition;
+ }
+}
diff --git a/GameCode/LightManager.cs b/GameCode/LightManager.cs
new file mode 100644
index 0000000..e38ac0f
--- /dev/null
+++ b/GameCode/LightManager.cs
@@ -0,0 +1,62 @@
+using System.Collections;
+using UnityEngine;
+
+public class LightManager : MonoBehaviour
+{
+ [SerializeField]
+ private Color stage2Color;
+
+ [SerializeField]
+ private Color stage3Color;
+
+ [SerializeField]
+ private Color stage4Color;
+
+ [SerializeField]
+ private Light light;
+
+ private void Start()
+ {
+ }
+
+ public void ChangeColor(int stage)
+ {
+ switch (stage)
+ {
+ case 2:
+ StartCoroutine(ShiftLight(stage2Color, 180f));
+ break;
+ case 3:
+ StartCoroutine(ShiftLight(stage3Color, 180f));
+ break;
+ case 4:
+ StartCoroutine(ShiftLight(stage4Color, 180f));
+ break;
+ }
+ }
+
+ private IEnumerator ShiftLight(Color newColor, float rotationInDeg)
+ {
+ Color startColor = light.color;
+ Vector3 startEuler = light.transform.eulerAngles;
+ Vector3 newEuler = startEuler + new Vector3(0f, rotationInDeg, 0f);
+ float timer = 0f;
+ float count = 0f;
+ while (timer < 1f)
+ {
+ light.color = Color.Lerp(startColor, newColor, timer);
+ light.transform.eulerAngles = Vector3.Lerp(startEuler, newEuler, timer);
+ count += Time.deltaTime;
+ if (count > 60f)
+ {
+ Debug.LogError("Possible infinite loop");
+ break;
+ }
+ timer += Time.deltaTime / 30f;
+ yield return new WaitForEndOfFrame();
+ }
+ light.color = newColor;
+ light.transform.eulerAngles = newEuler;
+ yield return null;
+ }
+}
diff --git a/GameCode/Lookout.cs b/GameCode/Lookout.cs
new file mode 100644
index 0000000..3abd55b
--- /dev/null
+++ b/GameCode/Lookout.cs
@@ -0,0 +1,97 @@
+using UnityEngine;
+
+public class Lookout : Tower
+{
+ [SerializeField]
+ private GameObject markIcon;
+
+ private GameObject currentMark;
+
+ protected override void Update()
+ {
+ if (currentTarget != null)
+ {
+ markIcon.SetActive(value: true);
+ UpdateMark();
+ GainXP();
+ }
+ else
+ {
+ markIcon.SetActive(value: false);
+ }
+ }
+
+ private void UpdateMark()
+ {
+ if (currentTarget != currentMark)
+ {
+ if (currentMark != null)
+ {
+ currentMark.GetComponent<Enemy>().mark = null;
+ }
+ currentMark = currentTarget;
+ currentMark.GetComponent<Enemy>().mark = this;
+ }
+ markIcon.transform.position = currentMark.transform.position;
+ }
+
+ protected override GameObject SelectEnemy(Collider[] possibleTargets)
+ {
+ GameObject result = null;
+ float num = -1f;
+ for (int i = 0; i < possibleTargets.Length; i++)
+ {
+ float num2 = 1f;
+ Enemy component = possibleTargets[i].GetComponent<Enemy>();
+ if (!(component.mark != this) || !(component.mark != null))
+ {
+ if (CheckPriority(Priority.Progress))
+ {
+ num2 /= Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().distanceFromEnd);
+ }
+ if (CheckPriority(Priority.NearDeath))
+ {
+ num2 /= Mathf.Max(0.001f, component.CurrentHealth());
+ }
+ if (CheckPriority(Priority.MostHealth))
+ {
+ num2 *= (float)Mathf.Max(1, component.health);
+ }
+ if (CheckPriority(Priority.MostArmor))
+ {
+ num2 *= (float)Mathf.Max(1, component.armor);
+ }
+ if (CheckPriority(Priority.MostShield))
+ {
+ num2 *= (float)Mathf.Max(1, component.shield);
+ }
+ if (CheckPriority(Priority.LeastHealth))
+ {
+ num2 /= (float)Mathf.Max(1, component.health);
+ }
+ if (CheckPriority(Priority.LeastArmor))
+ {
+ num2 /= (float)Mathf.Max(1, component.armor);
+ }
+ if (CheckPriority(Priority.LeastShield))
+ {
+ num2 /= (float)Mathf.Max(1, component.shield);
+ }
+ if (CheckPriority(Priority.Fastest))
+ {
+ num2 *= Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().speed);
+ }
+ if (CheckPriority(Priority.Slowest))
+ {
+ num2 /= Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().speed);
+ }
+ if (num2 > num)
+ {
+ result = component.gameObject;
+ num = num2;
+ }
+ }
+ }
+ return result;
+ }
+}
diff --git a/GameCode/MainMenu.cs b/GameCode/MainMenu.cs
new file mode 100644
index 0000000..1028d13
--- /dev/null
+++ b/GameCode/MainMenu.cs
@@ -0,0 +1,16 @@
+using UnityEngine;
+
+public class MainMenu : MonoBehaviour
+{
+
+ void Awake()
+ {
+ Screen.fullScreen = false;
+ Screen.SetResolution(1920, 1080, false);
+ }
+
+ public void QuitGame()
+ {
+ Application.Quit();
+ }
+}
diff --git a/GameCode/ManaBank.cs b/GameCode/ManaBank.cs
new file mode 100644
index 0000000..b82d36e
--- /dev/null
+++ b/GameCode/ManaBank.cs
@@ -0,0 +1,68 @@
+using UnityEngine;
+
+public class ManaBank : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private int gatherRatePerSec;
+
+ [SerializeField]
+ private int maxManaIncrease;
+
+ [SerializeField]
+ private GameObject UIObject;
+
+ [SerializeField]
+ private int goldBackOnDemolish;
+
+ private float timer = 1f;
+
+ private void Start()
+ {
+ maxManaIncrease += ResourceManager.instance.manaBankBonusMana;
+ ResourceManager.instance.UpdateManaGatherRate(gatherRatePerSec);
+ ResourceManager.instance.AddMaxMana(maxManaIncrease);
+ ResourceManager.instance.manaBanks.Add(this);
+ }
+
+ private void Update()
+ {
+ if (timer <= 0f)
+ {
+ Gather();
+ timer = 1f;
+ }
+ else if (SpawnManager.instance.combat)
+ {
+ timer -= Time.deltaTime;
+ }
+ }
+
+ public void SetStats()
+ {
+ }
+
+ public void UpgradeMaxMana(int addition)
+ {
+ ResourceManager.instance.AddMaxMana(addition);
+ maxManaIncrease += addition;
+ }
+
+ public void SpawnUI()
+ {
+ SimpleUI component = Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>();
+ component.SetDemolishable(base.gameObject, goldBackOnDemolish);
+ component.SetDiscriptionText("This bank currently stores " + maxManaIncrease + " mana. It is generating " + ((float)Mathf.FloorToInt(10f * ResourceManager.instance.manaMaxRegenPercent * (float)maxManaIncrease) / 10f + 1f) + " mana/s through sorcery and clever investing.");
+ }
+
+ public void Demolish()
+ {
+ ResourceManager.instance.UpdateManaGatherRate(-gatherRatePerSec);
+ ResourceManager.instance.AddMaxMana(-maxManaIncrease);
+ ResourceManager.instance.manaBanks.Remove(this);
+ }
+
+ private void Gather()
+ {
+ ResourceManager.instance.AddMana(gatherRatePerSec);
+ }
+}
diff --git a/GameCode/ManaCrystal.cs b/GameCode/ManaCrystal.cs
new file mode 100644
index 0000000..4b84e56
--- /dev/null
+++ b/GameCode/ManaCrystal.cs
@@ -0,0 +1,3 @@
+public class ManaCrystal : SpawnableObject
+{
+}
diff --git a/GameCode/ManaGenUpgradeCard.cs b/GameCode/ManaGenUpgradeCard.cs
new file mode 100644
index 0000000..e56b63a
--- /dev/null
+++ b/GameCode/ManaGenUpgradeCard.cs
@@ -0,0 +1,21 @@
+using UnityEngine;
+
+public class ManaGenUpgradeCard : UpgradeCard
+{
+ [SerializeField]
+ private float manaPercentRegenIncrease;
+
+ [SerializeField]
+ private int manaBankMaxManaIncrease;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ ResourceManager.instance.AddPercentManaGathering(manaPercentRegenIncrease);
+ ResourceManager.instance.UpdateManaHUD();
+ if (manaBankMaxManaIncrease != 0)
+ {
+ ResourceManager.instance.UpgradeManaBankMaxMana(manaBankMaxManaIncrease);
+ }
+ }
+}
diff --git a/GameCode/ManaSiphon.cs b/GameCode/ManaSiphon.cs
new file mode 100644
index 0000000..89e578e
--- /dev/null
+++ b/GameCode/ManaSiphon.cs
@@ -0,0 +1,92 @@
+using UnityEngine;
+
+public class ManaSiphon : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private int gatherRatePerSec;
+
+ [SerializeField]
+ private LayerMask layermask;
+
+ [SerializeField]
+ private GameObject UIObject;
+
+ [SerializeField]
+ private int goldBackOnDemolish;
+
+ private bool gathering;
+
+ private float timer = 1f;
+
+ private void Start()
+ {
+ DetectMana();
+ if (gathering)
+ {
+ ResourceManager.instance.UpdateManaGatherRate(gatherRatePerSec);
+ }
+ }
+
+ private void Update()
+ {
+ if (timer <= 0f)
+ {
+ Gather();
+ timer = 1f;
+ }
+ else if (gathering && SpawnManager.instance.combat)
+ {
+ timer -= Time.deltaTime;
+ }
+ }
+
+ public void SetStats()
+ {
+ }
+
+ public void SpawnUI()
+ {
+ SimpleUI component = Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>();
+ component.SetDemolishable(base.gameObject, goldBackOnDemolish);
+ if (gathering)
+ {
+ component.SetDiscriptionText("Currently gathering 1 mana/sec.");
+ }
+ }
+
+ public void Demolish()
+ {
+ if (gathering)
+ {
+ ResourceManager.instance.UpdateManaGatherRate(-gatherRatePerSec);
+ }
+ }
+
+ private void Gather()
+ {
+ ResourceManager.instance.AddMana(gatherRatePerSec);
+ }
+
+ private void DetectMana()
+ {
+ if ((!Physics.Raycast(base.transform.position + new Vector3(1f, 1f, 0f), -base.transform.up, out var hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(-1f, 1f, 0f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(0f, 1f, 1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && Physics.Raycast(base.transform.position + new Vector3(0f, 1f, -1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore))
+ {
+ Rotate(hitInfo);
+ }
+ }
+
+ private bool Rotate(RaycastHit hit)
+ {
+ if (hit.collider.GetComponent<ManaCrystal>() == null)
+ {
+ return false;
+ }
+ if ((double)Mathf.Abs(hit.collider.transform.position.y - base.transform.position.y) > 0.001)
+ {
+ return false;
+ }
+ base.transform.LookAt(hit.collider.transform.position, Vector3.up);
+ gathering = true;
+ return true;
+ }
+}
diff --git a/GameCode/MaxHealthUpgradeCard.cs b/GameCode/MaxHealthUpgradeCard.cs
new file mode 100644
index 0000000..467b669
--- /dev/null
+++ b/GameCode/MaxHealthUpgradeCard.cs
@@ -0,0 +1,13 @@
+using UnityEngine;
+
+public class MaxHealthUpgradeCard : UpgradeCard
+{
+ [SerializeField]
+ private int healthIncrease;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ GameManager.instance.IncreaseTowerHealth(healthIncrease);
+ }
+}
diff --git a/GameCode/Mine.cs b/GameCode/Mine.cs
new file mode 100644
index 0000000..55d7b6f
--- /dev/null
+++ b/GameCode/Mine.cs
@@ -0,0 +1,81 @@
+using UnityEngine;
+
+public class Mine : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private int goldBackOnDemolish;
+
+ [SerializeField]
+ private LayerMask layermask;
+
+ [SerializeField]
+ private GameObject UIObject;
+
+ private bool gathering;
+
+ private void Start()
+ {
+ SpawnManager.instance.mines.Add(this);
+ DetectIron();
+ }
+
+ public void SetStats()
+ {
+ }
+
+ public void Repair()
+ {
+ if (gathering && Random.Range(0f, 1f) < 0.1f)
+ {
+ GameManager.instance.RepairTower(10);
+ }
+ }
+
+ private void SetBonus(int value)
+ {
+ GameManager.instance.IncreaseTowerHealth(value);
+ }
+
+ public void SpawnUI()
+ {
+ SimpleUI component = Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>();
+ component.SetDemolishable(base.gameObject, goldBackOnDemolish);
+ if (gathering)
+ {
+ component.SetDiscriptionText("Currently mining. Tower maximum health increased by 1 and a 10% chance to repair damage.");
+ }
+ }
+
+ private void DetectIron()
+ {
+ if ((!Physics.Raycast(base.transform.position + new Vector3(1f, 1f, 0f), -base.transform.up, out var hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(-1f, 1f, 0f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(0f, 1f, 1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && Physics.Raycast(base.transform.position + new Vector3(0f, 1f, -1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore))
+ {
+ Rotate(hitInfo);
+ }
+ }
+
+ private bool Rotate(RaycastHit hit)
+ {
+ if (hit.collider.GetComponent<IronVein>() == null)
+ {
+ return false;
+ }
+ if ((double)Mathf.Abs(hit.collider.transform.position.y - base.transform.position.y) > 0.001)
+ {
+ return false;
+ }
+ base.transform.LookAt(hit.collider.transform.position, Vector3.up);
+ gathering = true;
+ SetBonus(1);
+ return true;
+ }
+
+ public void Demolish()
+ {
+ SpawnManager.instance.mines.Remove(this);
+ if (gathering)
+ {
+ SetBonus(-1);
+ }
+ }
+}
diff --git a/GameCode/MonsterManager.cs b/GameCode/MonsterManager.cs
new file mode 100644
index 0000000..40a7a35
--- /dev/null
+++ b/GameCode/MonsterManager.cs
@@ -0,0 +1,43 @@
+using UnityEngine;
+
+public class MonsterManager : MonoBehaviour
+{
+ public static MonsterManager instance;
+
+ public int extraTowerDamage;
+
+ public int extraGoldDrop;
+
+ public float manaDropOnDeath;
+
+ public float speedBonus;
+
+ public int bonusDamageOnBleed;
+
+ public int bonusDamageOnBurn;
+
+ public int bonusDamageOnPoison;
+
+ public int bonusDamageOnStun;
+
+ public float poisonSlowPercent;
+
+ public float burnSpeedDamagePercentBonus;
+
+ public float bleedingCritChance;
+
+ public float bleedPop;
+
+ public float burnPop;
+
+ public float poisonPop;
+
+ public float slowCapModifier;
+
+ public float hasteCapModifier;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+}
diff --git a/GameCode/MonsterManualEntry.cs b/GameCode/MonsterManualEntry.cs
new file mode 100644
index 0000000..6c5156c
--- /dev/null
+++ b/GameCode/MonsterManualEntry.cs
@@ -0,0 +1,63 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class MonsterManualEntry : MonoBehaviour
+{
+ [SerializeField]
+ private int level;
+
+ [SerializeField]
+ private Enemy prefab;
+
+ [SerializeField]
+ private Text titleText;
+
+ [SerializeField]
+ private Text descriptionText;
+
+ [SerializeField]
+ private Image img;
+
+ [SerializeField]
+ private Sprite unknownSprite;
+
+ private void Start()
+ {
+ int b = 0;
+ b = Mathf.Max(PlayerPrefs.GetInt("Record1", 0), b);
+ b = Mathf.Max(PlayerPrefs.GetInt("Record2", 0), b);
+ b = Mathf.Max(PlayerPrefs.GetInt("Record3", 0), b);
+ if (b + 1 < prefab.level)
+ {
+ titleText.text = "???";
+ descriptionText.text = "???";
+ img.sprite = unknownSprite;
+ return;
+ }
+ string text = descriptionText.text;
+ string text2 = "";
+ text2 = text2 + "Speed: " + prefab.baseSpeed;
+ text2 = text2 + "| Health: " + prefab.baseHealth;
+ if (prefab.baseArmor > 0)
+ {
+ text2 = text2 + "| Armor: " + prefab.baseArmor;
+ }
+ if (prefab.baseShield > 0)
+ {
+ text2 = text2 + "| Shield: " + prefab.baseShield;
+ }
+ if (prefab.healthRegen > 0)
+ {
+ text2 = text2 + "| Heal: " + prefab.healthRegen + "/sec";
+ }
+ if (prefab.armorRegen > 0)
+ {
+ text2 = text2 + "| Armor repair: " + prefab.armorRegen + "/sec";
+ }
+ if (prefab.shieldRegen > 0)
+ {
+ text2 = text2 + "| Shield regen: " + prefab.shieldRegen + "/sec";
+ }
+ descriptionText.text = text2 + "\n" + text;
+ }
+}
diff --git a/GameCode/MonsterUpgradeCard.cs b/GameCode/MonsterUpgradeCard.cs
new file mode 100644
index 0000000..d78ab81
--- /dev/null
+++ b/GameCode/MonsterUpgradeCard.cs
@@ -0,0 +1,57 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class MonsterUpgradeCard : UpgradeCard
+{
+ [SerializeField]
+ private GameObject banditObject;
+
+ [SerializeField]
+ private Text banditText;
+
+ [SerializeField]
+ private int extraTowerDamage;
+
+ [SerializeField]
+ private int extraGoldDrop;
+
+ [SerializeField]
+ private float manaDropOnDeath;
+
+ [SerializeField]
+ private float speedBonus;
+
+ [SerializeField]
+ private float slowCapMod;
+
+ [SerializeField]
+ private float hasteCapMod;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ MonsterManager.instance.extraTowerDamage += extraTowerDamage;
+ MonsterManager.instance.extraGoldDrop += extraGoldDrop;
+ MonsterManager.instance.manaDropOnDeath += manaDropOnDeath;
+ MonsterManager.instance.speedBonus += speedBonus;
+ MonsterManager.instance.slowCapModifier += slowCapMod;
+ MonsterManager.instance.hasteCapModifier += hasteCapMod;
+ if (extraGoldDrop > 0)
+ {
+ banditObject.SetActive(value: true);
+ banditText.text = "+" + MonsterManager.instance.extraGoldDrop + "g";
+ }
+ if (manaDropOnDeath > 0f)
+ {
+ ResourceManager.instance.UpdateManaHUD();
+ }
+ if ((double)MonsterManager.instance.slowCapModifier >= 0.3)
+ {
+ AchievementManager.instance.UnlockAchievement("MaxSlow");
+ }
+ if (MonsterManager.instance.hasteCapModifier <= -0.6f)
+ {
+ AchievementManager.instance.UnlockAchievement("MaxHaste");
+ }
+ }
+}
diff --git a/GameCode/Morter.cs b/GameCode/Morter.cs
new file mode 100644
index 0000000..2170da3
--- /dev/null
+++ b/GameCode/Morter.cs
@@ -0,0 +1,39 @@
+using UnityEngine;
+
+public class Morter : Tower
+{
+ protected override void AimTurret()
+ {
+ }
+
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * finalManaConsumption);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ float num = projectileSpeed * Mathf.Clamp(Vector3.SqrMagnitude(currentTarget.transform.position - base.transform.position) / (2f * baseRange * baseRange), 1f, float.MaxValue);
+ Vector3 vector = currentTarget.GetComponent<Enemy>().GetFuturePosition(num) - turret.transform.position;
+ GameObject gameObject = Object.Instantiate(rotation: Quaternion.LookRotation(new Vector3(vector.x, 0f, vector.z), Vector3.up), original: projectile, position: muzzle.position);
+ gameObject.GetComponent<Projectile>().SetStats(towerType, currentTarget, num, base.damage, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ gameObject.GetComponent<MorterShell>().SetMorterPhysics(currentTarget.GetComponent<Enemy>().GetFuturePosition(num));
+ gameObject.GetComponent<MorterShell>().blastRadius = base.blastRadius;
+ if (extraProjectileFX != null)
+ {
+ GameObject gameObject2 = Object.Instantiate(extraProjectileFX, gameObject.transform.position, gameObject.transform.rotation);
+ gameObject2.transform.SetParent(gameObject.transform);
+ gameObject2.GetComponent<ProjectileFX>().SetFX(base.bleedPercent, base.burnPercent, base.poisonPercent, base.slowPercent, consumesMana);
+ Projectile component = gameObject.GetComponent<Projectile>();
+ if (component.detachOnDestruction == null)
+ {
+ component.detachOnDestruction = gameObject2;
+ component.extraFX = gameObject2.GetComponent<ProjectileFX>();
+ }
+ }
+ }
+}
diff --git a/GameCode/MorterShell.cs b/GameCode/MorterShell.cs
new file mode 100644
index 0000000..ce93441
--- /dev/null
+++ b/GameCode/MorterShell.cs
@@ -0,0 +1,81 @@
+using UnityEngine;
+
+public class MorterShell : Projectile
+{
+ [SerializeField]
+ private GameObject artObject;
+
+ [SerializeField]
+ private LayerMask layersAffectedByBlast;
+
+ public float blastRadius = 1f;
+
+ private Vector3 destination;
+
+ private float timeOfFlight;
+
+ [SerializeField]
+ private float vSpeed;
+
+ [SerializeField]
+ private float hSpeed;
+
+ [SerializeField]
+ private float gravity = 5f;
+
+ [SerializeField]
+ private GameObject explosion;
+
+ private float lookAhead;
+
+ private Vector3 previousPos;
+
+ public void SetMorterPhysics(Vector3 pos)
+ {
+ destination = pos;
+ timeOfFlight = speed;
+ vSpeed = gravity * timeOfFlight / 2f;
+ hSpeed = Vector3.Magnitude(base.transform.position - destination) / timeOfFlight;
+ lookAhead = vSpeed + hSpeed;
+ }
+
+ protected override void MoveProjectile()
+ {
+ previousPos = base.transform.position;
+ base.transform.Translate(Vector3.forward * hSpeed * Time.fixedDeltaTime);
+ base.transform.Translate(Vector3.up * vSpeed * Time.fixedDeltaTime);
+ vSpeed -= gravity * Time.fixedDeltaTime;
+ artObject.transform.rotation = Quaternion.LookRotation(base.transform.position - previousPos, Vector3.up);
+ }
+
+ protected override void CheckForHits()
+ {
+ if (!(vSpeed > 0f) && Physics.Raycast(artObject.transform.position, artObject.transform.forward, out var hitInfo, lookAhead * Time.fixedDeltaTime, layermask, QueryTriggerInteraction.Collide))
+ {
+ OnHit(hitInfo);
+ }
+ }
+
+ protected override void OnHit(RaycastHit hit)
+ {
+ Collider[] array = Physics.OverlapSphere(base.transform.position, blastRadius, layersAffectedByBlast, QueryTriggerInteraction.Collide);
+ for (int i = 0; i < array.Length; i++)
+ {
+ IDamageable component = array[i].GetComponent<IDamageable>();
+ if (component != null)
+ {
+ DealDamage(component);
+ }
+ }
+ if (detachOnDestruction != null)
+ {
+ detachOnDestruction.transform.parent = null;
+ }
+ if (extraFX != null)
+ {
+ extraFX.OnDetach();
+ }
+ Object.Instantiate(explosion, base.transform.position, Quaternion.identity);
+ Object.Destroy(base.gameObject);
+ }
+}
diff --git a/GameCode/MusicManager.cs b/GameCode/MusicManager.cs
new file mode 100644
index 0000000..8a39705
--- /dev/null
+++ b/GameCode/MusicManager.cs
@@ -0,0 +1,164 @@
+using System.Collections;
+using UnityEngine;
+
+public class MusicManager : MonoBehaviour
+{
+ public static MusicManager instance;
+
+ [SerializeField]
+ private AudioSource[] hard;
+
+ [SerializeField]
+ private AudioSource[] soft;
+
+ [SerializeField]
+ private int currentSorceIndex;
+
+ [SerializeField]
+ private int currentSong;
+
+ private float currentSongLoopDuration;
+
+ [SerializeField]
+ private AudioClip[] hardTracks;
+
+ [SerializeField]
+ private AudioClip[] softTracks;
+
+ [SerializeField]
+ private int[] BPMin;
+
+ [SerializeField]
+ private int[] BPMeasure;
+
+ [SerializeField]
+ private int[] measures;
+
+ private int intensity = 1;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ PlaySong(0, action: false);
+ }
+
+ private void Update()
+ {
+ if (hard[currentSorceIndex].time >= currentSongLoopDuration)
+ {
+ int num = (currentSorceIndex + 1) % 2;
+ hard[num].Stop();
+ soft[num].Stop();
+ hard[num].Play();
+ soft[num].Play();
+ currentSorceIndex = num;
+ }
+ int num2 = (currentSorceIndex + 1) % 2;
+ if (hard[num2].time >= hard[num2].clip.length - 0.1f)
+ {
+ hard[num2].Stop();
+ soft[num2].Stop();
+ }
+ }
+
+ public void SetIntensity(bool action)
+ {
+ int num = (action ? 1 : 0);
+ if (num != intensity)
+ {
+ intensity = num;
+ StartCoroutine(CrossFade(intensity));
+ }
+ }
+
+ public void UpdateVolume(float newVolume)
+ {
+ hard[0].volume = newVolume * (float)intensity;
+ hard[1].volume = newVolume * (float)intensity;
+ soft[0].volume = newVolume * (float)(1 - intensity);
+ soft[1].volume = newVolume * (float)(1 - intensity);
+ }
+
+ public void PlaySong(int songNumber, bool action)
+ {
+ StopAllCoroutines();
+ hard[0].Stop();
+ hard[1].Stop();
+ soft[0].Stop();
+ soft[1].Stop();
+ currentSong = songNumber;
+ currentSongLoopDuration = (float)(60 * measures[currentSong] * BPMeasure[currentSong]) / (float)BPMin[currentSong];
+ hard[0].clip = hardTracks[currentSong];
+ hard[1].clip = hardTracks[currentSong];
+ soft[0].clip = softTracks[currentSong];
+ soft[1].clip = softTracks[currentSong];
+ intensity = (action ? 1 : 0);
+ float num = OptionsMenu.instance.masterVolume * OptionsMenu.instance.musicVolume;
+ hard[0].volume = (float)intensity * num;
+ hard[1].volume = (float)intensity * num;
+ soft[0].volume = (float)(1 - intensity) * num;
+ soft[1].volume = (float)(1 - intensity) * num;
+ currentSorceIndex = 0;
+ hard[0].Play();
+ soft[0].Play();
+ }
+
+ private IEnumerator CrossFade(int newIntensity)
+ {
+ float startIntensity = ((newIntensity != 1) ? 1 : 0);
+ float vMod = OptionsMenu.instance.masterVolume * OptionsMenu.instance.musicVolume;
+ float fadeTime = 2f;
+ int saftyCount = 0;
+ for (float f = startIntensity; f != (startIntensity + 1f) % 2f; f = Mathf.Clamp(f + Time.unscaledDeltaTime * ((float)newIntensity - startIntensity) / fadeTime, 0f, 1f))
+ {
+ hard[0].volume = f * vMod;
+ hard[1].volume = f * vMod;
+ soft[0].volume = (1f - f) * vMod;
+ soft[1].volume = (1f - f) * vMod;
+ yield return null;
+ saftyCount++;
+ if ((float)saftyCount > 1000f * fadeTime)
+ {
+ Debug.LogError("Possible infinite loop");
+ break;
+ }
+ }
+ hard[0].volume = (float)newIntensity * vMod;
+ hard[1].volume = (float)newIntensity * vMod;
+ soft[0].volume = (float)(1 - newIntensity) * vMod;
+ soft[1].volume = (float)(1 - newIntensity) * vMod;
+ }
+
+ public void FadeOut(float t)
+ {
+ StartCoroutine(FadeOutCo(t));
+ }
+
+ private IEnumerator FadeOutCo(float fadeTime)
+ {
+ float vMod = OptionsMenu.instance.masterVolume * OptionsMenu.instance.musicVolume;
+ int saftyCount = 0;
+ for (float f = 1f; f > 0f; f -= Time.unscaledDeltaTime / fadeTime)
+ {
+ hard[0].volume = (float)intensity * vMod * f;
+ hard[1].volume = (float)intensity * vMod * f;
+ soft[0].volume = (float)(1 - intensity) * vMod * f;
+ soft[1].volume = (float)(1 - intensity) * vMod * f;
+ yield return null;
+ saftyCount++;
+ if ((float)saftyCount > 1000f * fadeTime)
+ {
+ Debug.LogError("Possible infinite loop");
+ break;
+ }
+ }
+ hard[0].volume = 0f;
+ hard[1].volume = 0f;
+ soft[0].volume = 0f;
+ soft[1].volume = 0f;
+ }
+}
diff --git a/GameCode/Obelisk.cs b/GameCode/Obelisk.cs
new file mode 100644
index 0000000..549f3b4
--- /dev/null
+++ b/GameCode/Obelisk.cs
@@ -0,0 +1,99 @@
+using UnityEngine;
+
+public class Obelisk : Tower
+{
+ [SerializeField]
+ private GameObject beam;
+
+ [SerializeField]
+ private AudioSource audioS;
+
+ private bool soundPlaying;
+
+ private IDamageable lastThingIHit;
+
+ private float timeOnTarget;
+
+ protected override void Update()
+ {
+ if (currentTarget != null)
+ {
+ if (turret != null)
+ {
+ AimTurret();
+ }
+ GainXP();
+ }
+ else
+ {
+ beam.SetActive(value: false);
+ PlaySound(onOff: false);
+ }
+ timeSinceLastShot += Time.deltaTime;
+ if (currentTarget != null && timeSinceLastShot > rps)
+ {
+ Fire();
+ timeSinceLastShot = 0f;
+ }
+ }
+
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * manaConsumptionRate);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ beam.SetActive(value: false);
+ PlaySound(onOff: false);
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ DealDamage();
+ }
+
+ protected override void AimTurret()
+ {
+ Vector3 forward = currentTarget.transform.position - turret.transform.position + Vector3.up * 0.5f;
+ Quaternion rotation = Quaternion.LookRotation(forward, Vector3.up);
+ turret.transform.rotation = rotation;
+ beam.transform.localScale = new Vector3(1f, 1f, forward.magnitude);
+ }
+
+ private void DealDamage()
+ {
+ IDamageable component = currentTarget.GetComponent<IDamageable>();
+ if (component != null)
+ {
+ if (component == lastThingIHit)
+ {
+ timeOnTarget += rps;
+ }
+ else
+ {
+ timeOnTarget = 0f;
+ lastThingIHit = component;
+ }
+ int num = Mathf.Clamp(Mathf.FloorToInt(timeOnTarget * TowerManager.instance.obeliskTimeOnTargetMultiplier), 0, base.damage);
+ component.TakeDamage(towerType, base.damage + num, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ beam.SetActive(value: true);
+ PlaySound(onOff: true);
+ }
+ }
+
+ private void PlaySound(bool onOff)
+ {
+ if (onOff && !soundPlaying)
+ {
+ audioS.volume = OptionsMenu.instance.masterVolume * OptionsMenu.instance.sfxVolume;
+ audioS.Play();
+ soundPlaying = true;
+ }
+ else if (!onOff && soundPlaying)
+ {
+ audioS.Stop();
+ soundPlaying = false;
+ }
+ }
+}
diff --git a/GameCode/ObjectPool.cs b/GameCode/ObjectPool.cs
new file mode 100644
index 0000000..feb9c40
--- /dev/null
+++ b/GameCode/ObjectPool.cs
@@ -0,0 +1,58 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class ObjectPool : MonoBehaviour
+{
+ public enum ObjectType
+ {
+ DamageNumber
+ }
+
+ public static ObjectPool instance;
+
+ [SerializeField]
+ private GameObject damageNumberPrefab;
+
+ [SerializeField]
+ private List<GameObject> pooledDamageNumbers = new List<GameObject>();
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ public GameObject SpawnObject(ObjectType type, Vector3 position, Quaternion rotation)
+ {
+ if (type == ObjectType.DamageNumber)
+ {
+ return SpawnDamageNumber(position, rotation);
+ }
+ Debug.LogError("Failed to spawn object from pool");
+ return null;
+ }
+
+ private GameObject SpawnDamageNumber(Vector3 position, Quaternion rotation)
+ {
+ while (pooledDamageNumbers.Count > 0 && pooledDamageNumbers[0] == null)
+ {
+ pooledDamageNumbers.RemoveAt(0);
+ }
+ if (pooledDamageNumbers.Count > 0)
+ {
+ GameObject gameObject = pooledDamageNumbers[0];
+ pooledDamageNumbers.Remove(gameObject);
+ gameObject.SetActive(value: true);
+ gameObject.transform.position = position;
+ gameObject.transform.rotation = rotation;
+ gameObject.GetComponent<DamageNumber>().Start();
+ return gameObject;
+ }
+ return Object.Instantiate(damageNumberPrefab, position, rotation);
+ }
+
+ public void PoolDamageNumber(GameObject damageNumber)
+ {
+ pooledDamageNumbers.Add(damageNumber);
+ damageNumber.SetActive(value: false);
+ }
+}
diff --git a/GameCode/OptionsMenu.cs b/GameCode/OptionsMenu.cs
new file mode 100644
index 0000000..7377cfd
--- /dev/null
+++ b/GameCode/OptionsMenu.cs
@@ -0,0 +1,187 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class OptionsMenu : MonoBehaviour
+{
+ public static OptionsMenu instance;
+
+ public bool showConditionText;
+
+ public bool showDamageNumbers = true;
+
+ public bool extraProjectileEffects = true;
+
+ public float masterVolume;
+
+ public float musicVolume;
+
+ public float sfxVolume;
+
+ [SerializeField]
+ private Transform[] scalableUIs;
+
+ public float uiScale;
+
+ [SerializeField]
+ private Toggle showDamageToggle;
+
+ [SerializeField]
+ private Toggle showConditionToggle;
+
+ [SerializeField]
+ private Toggle projectileFXToggle;
+
+ [SerializeField]
+ private Slider masterVolumeSlider;
+
+ [SerializeField]
+ private Slider musicVolumeSlider;
+
+ [SerializeField]
+ private Slider sfxVolumeSlider;
+
+ [SerializeField]
+ private Slider uiScaleSlider;
+
+ private float timer = 1f;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Update()
+ {
+ if (timer >= 0f)
+ {
+ timer -= Time.deltaTime;
+ }
+ }
+
+ private void Start()
+ {
+ if (PlayerPrefs.GetInt("ShowConditionText", 0) == 1)
+ {
+ showConditionText = true;
+ }
+ else
+ {
+ showConditionText = false;
+ }
+ if (showConditionToggle != null)
+ {
+ showConditionToggle.isOn = showConditionText;
+ }
+ if (PlayerPrefs.GetInt("ShowDamageNumbers", 1) == 1)
+ {
+ showDamageNumbers = true;
+ }
+ else
+ {
+ showDamageNumbers = false;
+ }
+ if (showDamageToggle != null)
+ {
+ showDamageToggle.isOn = showDamageNumbers;
+ }
+ if (PlayerPrefs.GetInt("ExtraProjectileFX", 1) == 1)
+ {
+ extraProjectileEffects = true;
+ }
+ else
+ {
+ extraProjectileEffects = false;
+ }
+ if (projectileFXToggle != null)
+ {
+ projectileFXToggle.isOn = extraProjectileEffects;
+ }
+ masterVolume = PlayerPrefs.GetFloat("MasterVolume", 1f);
+ musicVolume = PlayerPrefs.GetFloat("MusicVolume", 0.5f);
+ sfxVolume = PlayerPrefs.GetFloat("SFXVolume", 0.5f);
+ masterVolumeSlider.value = masterVolume;
+ musicVolumeSlider.value = musicVolume;
+ sfxVolumeSlider.value = sfxVolume;
+ if (uiScaleSlider != null)
+ {
+ uiScale = PlayerPrefs.GetFloat("UIScale", 0.5f);
+ uiScaleSlider.value = uiScale;
+ }
+ }
+
+ public void ToggleDamageNumbers()
+ {
+ showDamageNumbers = showDamageToggle.isOn;
+ PlayerPrefs.SetInt("ShowDamageNumbers", showDamageNumbers ? 1 : 0);
+ if (timer < 0f)
+ {
+ SFXManager.instance.ButtonClick();
+ }
+ }
+
+ public void ToggleConditionTexts()
+ {
+ showConditionText = showConditionToggle.isOn;
+ PlayerPrefs.SetInt("ShowConditionText", showConditionText ? 1 : 0);
+ if (timer < 0f)
+ {
+ SFXManager.instance.ButtonClick();
+ }
+ }
+
+ public void ToggleProjectileFX()
+ {
+ extraProjectileEffects = projectileFXToggle.isOn;
+ PlayerPrefs.SetInt("ExtraProjectileFX", extraProjectileEffects ? 1 : 0);
+ if (timer < 0f)
+ {
+ SFXManager.instance.ButtonClick();
+ }
+ }
+
+ public void ChangeMasterVolume()
+ {
+ masterVolume = masterVolumeSlider.value;
+ PlayerPrefs.SetFloat("MasterVolume", masterVolume);
+ if (MusicManager.instance != null)
+ {
+ MusicManager.instance.UpdateVolume(masterVolume * musicVolume);
+ }
+ if (SFXManager.instance != null)
+ {
+ SFXManager.instance.volume = masterVolume * sfxVolume;
+ }
+ }
+
+ public void ChangeMusicVolume()
+ {
+ musicVolume = musicVolumeSlider.value;
+ PlayerPrefs.SetFloat("MusicVolume", musicVolume);
+ if (MusicManager.instance != null)
+ {
+ MusicManager.instance.UpdateVolume(masterVolume * musicVolume);
+ }
+ }
+
+ public void ChangeSFXVolume()
+ {
+ sfxVolume = sfxVolumeSlider.value;
+ PlayerPrefs.SetFloat("SFXVolume", sfxVolume);
+ if (SFXManager.instance != null)
+ {
+ SFXManager.instance.volume = masterVolume * sfxVolume;
+ }
+ }
+
+ public void ChangeUIScale()
+ {
+ uiScale = uiScaleSlider.value;
+ PlayerPrefs.SetFloat("UIScale", uiScale);
+ float num = Mathf.Max(uiScale + 0.5f, (uiScale + 0.5f) * 2f - 1f);
+ Transform[] array = scalableUIs;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].localScale = num * Vector3.one;
+ }
+ }
+}
diff --git a/GameCode/ParticleBeam.cs b/GameCode/ParticleBeam.cs
new file mode 100644
index 0000000..6a7bf2a
--- /dev/null
+++ b/GameCode/ParticleBeam.cs
@@ -0,0 +1,19 @@
+using UnityEngine;
+
+public class ParticleBeam : Projectile
+{
+ [SerializeField]
+ private GameObject beamTrail;
+
+ protected override void MoveProjectile()
+ {
+ base.MoveProjectile();
+ beamTrail.transform.position = base.transform.position + new Vector3(Random.Range(-1f, 1f), Random.Range(0f, 1f), Random.Range(-1f, 1f));
+ }
+
+ protected new virtual void OnHit(RaycastHit hit)
+ {
+ beamTrail.transform.position = hit.point;
+ base.OnHit(hit);
+ }
+}
diff --git a/GameCode/Pathfinder.cs b/GameCode/Pathfinder.cs
new file mode 100644
index 0000000..5426574
--- /dev/null
+++ b/GameCode/Pathfinder.cs
@@ -0,0 +1,91 @@
+using UnityEngine;
+
+public class Pathfinder : MonoBehaviour
+{
+ public float distanceFromEnd = 2.1474836E+09f;
+
+ public bool atEnd;
+
+ public float speed = 1f;
+
+ public Waypoint currentWaypoint;
+
+ [SerializeField]
+ private Enemy enemyScript;
+
+ private void Start()
+ {
+ if (enemyScript == null)
+ {
+ enemyScript = GetComponent<Enemy>();
+ }
+ }
+
+ private void FixedUpdate()
+ {
+ CheckWaypointDistance();
+ Move();
+ }
+
+ private void Move()
+ {
+ Vector3 vector = currentWaypoint.transform.position - base.transform.position;
+ vector.Normalize();
+ base.transform.Translate(vector * speed * Time.fixedDeltaTime);
+ distanceFromEnd -= speed * Time.fixedDeltaTime;
+ }
+
+ private void CheckWaypointDistance()
+ {
+ if (Vector3.SqrMagnitude(currentWaypoint.transform.position - base.transform.position) < 4f * speed * speed * Time.fixedDeltaTime * Time.fixedDeltaTime && !GetNewWaypoint())
+ {
+ atEnd = true;
+ enemyScript.AtEnd();
+ }
+ }
+
+ public Vector3 GetFuturePosition(float distance)
+ {
+ Vector3 position = base.transform.position;
+ Waypoint nextWaypoint = currentWaypoint;
+ float num = distance;
+ int num2 = 0;
+ while (num > 0f)
+ {
+ if (Vector3.SqrMagnitude(nextWaypoint.transform.position - position) >= num * num)
+ {
+ return position + (nextWaypoint.transform.position - position).normalized * num;
+ }
+ if (nextWaypoint.GetNextWaypoint() == nextWaypoint)
+ {
+ return nextWaypoint.transform.position;
+ }
+ num -= Vector3.Magnitude(nextWaypoint.transform.position - position);
+ position = nextWaypoint.transform.position;
+ nextWaypoint = nextWaypoint.GetNextWaypoint();
+ num2++;
+ if (num2 > 100)
+ {
+ Debug.LogError("GetFuturePosition looping too much");
+ break;
+ }
+ }
+ Debug.LogError("GetFuturePosition broken");
+ return Vector3.zero;
+ }
+
+ private bool GetNewWaypoint()
+ {
+ if (currentWaypoint.GetNextWaypoint() == currentWaypoint)
+ {
+ return false;
+ }
+ distanceFromEnd = currentWaypoint.distanceFromEnd;
+ currentWaypoint = currentWaypoint.GetNextWaypoint();
+ if (distanceFromEnd <= 24f)
+ {
+ enemyScript.CheckBattleCries(BattleCry.BattleCryTrigger.NearEnd);
+ }
+ return true;
+ }
+}
diff --git a/GameCode/PauseMenu.cs b/GameCode/PauseMenu.cs
new file mode 100644
index 0000000..0ccfc28
--- /dev/null
+++ b/GameCode/PauseMenu.cs
@@ -0,0 +1,140 @@
+using UnityEngine;
+
+public class PauseMenu : MonoBehaviour
+{
+ [SerializeField]
+ private KeyCode pauseKey1;
+
+ [SerializeField]
+ private KeyCode pauseKey2;
+
+ [SerializeField]
+ private KeyCode hideUIKey;
+
+ [SerializeField]
+ private KeyCode damageTrackerKey;
+
+ [SerializeField]
+ private GameObject pauseMenu;
+
+ [SerializeField]
+ private GameObject areYouSureMenu;
+
+ [SerializeField]
+ private GameObject optionsMenu;
+
+ [SerializeField]
+ private GameObject[] hideableUI;
+
+ [SerializeField]
+ private GameObject[] hideableWithTracker;
+
+ private bool uiHidden;
+
+ private bool trackerHidden = true;
+
+ public bool paused;
+
+ public static PauseMenu instance;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Update()
+ {
+ if (Input.GetKeyDown(pauseKey1) || Input.GetKeyDown(pauseKey2))
+ {
+ if (!paused)
+ {
+ UnHideUI();
+ Pause();
+ }
+ else
+ {
+ UnPause();
+ }
+ }
+ if (Input.GetKeyDown(hideUIKey))
+ {
+ if (uiHidden)
+ {
+ UnHideUI();
+ }
+ else
+ {
+ hideUI();
+ }
+ }
+ if (Input.GetKeyDown(damageTrackerKey))
+ {
+ if (trackerHidden)
+ {
+ UnHideTracker();
+ }
+ else
+ {
+ HideTracker();
+ }
+ }
+ }
+
+ public void Pause()
+ {
+ paused = true;
+ Time.timeScale = 0f;
+ pauseMenu.SetActive(value: true);
+ }
+
+ public void UnPause()
+ {
+ pauseMenu.SetActive(value: false);
+ areYouSureMenu.SetActive(value: false);
+ optionsMenu.SetActive(value: false);
+ Time.timeScale = 1f;
+ paused = false;
+ }
+
+ public void hideUI()
+ {
+ uiHidden = true;
+ GameObject[] array = hideableUI;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].SetActive(value: false);
+ }
+ }
+
+ public void UnHideUI()
+ {
+ uiHidden = false;
+ GameObject[] array = hideableUI;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].SetActive(value: true);
+ }
+ }
+
+ public void HideTracker()
+ {
+ trackerHidden = true;
+ DamageTracker.instance.uiObject.SetActive(value: false);
+ GameObject[] array = hideableWithTracker;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].SetActive(value: false);
+ }
+ }
+
+ public void UnHideTracker()
+ {
+ trackerHidden = false;
+ DamageTracker.instance.DisplayDamageTotals();
+ GameObject[] array = hideableWithTracker;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].SetActive(value: true);
+ }
+ }
+}
diff --git a/GameCode/PlayMenu.cs b/GameCode/PlayMenu.cs
new file mode 100644
index 0000000..19514b3
--- /dev/null
+++ b/GameCode/PlayMenu.cs
@@ -0,0 +1,27 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class PlayMenu : MonoBehaviour
+{
+ [SerializeField]
+ private Text record1Text;
+
+ [SerializeField]
+ private Text record2Text;
+
+ [SerializeField]
+ private Text record3Text;
+
+ private void Start()
+ {
+ record1Text.text = "Current Record\nLevel " + PlayerPrefs.GetInt("Record1", 0);
+ record2Text.text = "Current Record\nLevel " + PlayerPrefs.GetInt("Record2", 0);
+ record3Text.text = "Current Record\nLevel " + PlayerPrefs.GetInt("Record3", 0);
+ }
+
+ public void StartGame(int mode)
+ {
+ PlayerPrefs.SetInt("GameMode", Mathf.Clamp(mode, 1, 3));
+ LevelLoader.instance.LoadLevel("GameScene");
+ }
+}
diff --git a/GameCode/Projectile.cs b/GameCode/Projectile.cs
new file mode 100644
index 0000000..3c15fe4
--- /dev/null
+++ b/GameCode/Projectile.cs
@@ -0,0 +1,141 @@
+using UnityEngine;
+
+public class Projectile : MonoBehaviour
+{
+ [SerializeField]
+ protected Sound launchSound;
+
+ [SerializeField]
+ protected Sound hitSound;
+
+ protected TowerType shotBy;
+
+ [SerializeField]
+ protected LayerMask layermask;
+
+ [SerializeField]
+ protected float speed;
+
+ [SerializeField]
+ protected bool homing;
+
+ [SerializeField]
+ protected float maximumLifeTime = 10f;
+
+ [SerializeField]
+ public GameObject detachOnDestruction;
+
+ [SerializeField]
+ public ProjectileFX extraFX;
+
+ protected GameObject target;
+
+ protected int damage;
+
+ protected int healthDamage;
+
+ protected int armorDamage;
+
+ protected int shieldDamage;
+
+ protected float slowPercent;
+
+ protected float bleedPercent;
+
+ protected float burnPercent;
+
+ protected float poisonPercent;
+
+ protected float critChance;
+
+ protected float stunChance;
+
+ protected virtual void Start()
+ {
+ if (launchSound != 0)
+ {
+ SFXManager.instance.PlaySound(launchSound, base.transform.position);
+ }
+ }
+
+ protected virtual void FixedUpdate()
+ {
+ maximumLifeTime -= Time.fixedDeltaTime;
+ if (maximumLifeTime < 0f)
+ {
+ Object.Destroy(base.gameObject);
+ }
+ CheckForHits();
+ MoveProjectile();
+ if (homing)
+ {
+ AlterCourse();
+ }
+ }
+
+ public virtual void SetStats(TowerType whoShotMe, GameObject _target, float spd, int dmg, int healthDmg, int armorDmg, int shieldDmg, float slow, float bleed, float burn, float poison, float crit, float stun)
+ {
+ shotBy = whoShotMe;
+ target = _target;
+ speed = spd;
+ damage = dmg;
+ healthDamage = healthDmg;
+ armorDamage = armorDmg;
+ shieldDamage = shieldDmg;
+ slowPercent = slow;
+ bleedPercent = bleed;
+ burnPercent = burn;
+ poisonPercent = poison;
+ critChance = crit;
+ stunChance = stun;
+ }
+
+ protected virtual void MoveProjectile()
+ {
+ base.transform.Translate(Vector3.forward * speed * Time.fixedDeltaTime);
+ }
+
+ protected virtual void AlterCourse()
+ {
+ if (!(target == null))
+ {
+ Quaternion rotation = Quaternion.LookRotation(0.25f * Vector3.up + target.transform.position - base.transform.position, Vector3.up);
+ base.transform.rotation = rotation;
+ }
+ }
+
+ protected virtual void CheckForHits()
+ {
+ if (Physics.Raycast(base.transform.position, base.transform.forward, out var hitInfo, speed * Time.fixedDeltaTime, layermask, QueryTriggerInteraction.Collide))
+ {
+ OnHit(hitInfo);
+ }
+ }
+
+ protected virtual void OnHit(RaycastHit hit)
+ {
+ IDamageable component = hit.transform.GetComponent<IDamageable>();
+ if (component != null)
+ {
+ DealDamage(component);
+ }
+ if (detachOnDestruction != null)
+ {
+ detachOnDestruction.transform.parent = null;
+ }
+ if (extraFX != null)
+ {
+ extraFX.OnDetach();
+ }
+ Object.Destroy(base.gameObject);
+ }
+
+ protected virtual void DealDamage(IDamageable target)
+ {
+ target.TakeDamage(shotBy, damage, healthDamage, armorDamage, shieldDamage, slowPercent, bleedPercent, burnPercent, poisonPercent, critChance, stunChance);
+ if (hitSound != 0)
+ {
+ SFXManager.instance.PlaySound(hitSound, base.transform.position);
+ }
+ }
+}
diff --git a/GameCode/ProjectileFX.cs b/GameCode/ProjectileFX.cs
new file mode 100644
index 0000000..df82c3d
--- /dev/null
+++ b/GameCode/ProjectileFX.cs
@@ -0,0 +1,47 @@
+using System.Collections;
+using UnityEngine;
+
+public class ProjectileFX : MonoBehaviour
+{
+ [SerializeField]
+ private GameObject bleedingPS;
+
+ [SerializeField]
+ private GameObject burrningPS;
+
+ [SerializeField]
+ private GameObject poisonPS;
+
+ public void SetFX(float bleeding, float burning, float poison, float slow, bool arcane)
+ {
+ if (OptionsMenu.instance.extraProjectileEffects)
+ {
+ if (bleeding > 0f)
+ {
+ bleedingPS.SetActive(value: true);
+ }
+ if (burning > 0f)
+ {
+ burrningPS.SetActive(value: true);
+ }
+ if (poison > 0f)
+ {
+ poisonPS.SetActive(value: true);
+ }
+ }
+ }
+
+ public void OnDetach()
+ {
+ StartCoroutine(Die());
+ }
+
+ private IEnumerator Die()
+ {
+ bleedingPS.GetComponent<ParticleSystem>().Stop(withChildren: true);
+ burrningPS.GetComponent<ParticleSystem>().Stop(withChildren: true);
+ poisonPS.GetComponent<ParticleSystem>().Stop(withChildren: true);
+ yield return new WaitForSeconds(1.1f);
+ Object.Destroy(base.gameObject);
+ }
+}
diff --git a/GameCode/RadarTower.cs b/GameCode/RadarTower.cs
new file mode 100644
index 0000000..e0d8d1d
--- /dev/null
+++ b/GameCode/RadarTower.cs
@@ -0,0 +1,71 @@
+using UnityEngine;
+
+public class RadarTower : Tower
+{
+ [SerializeField]
+ private GameObject biPlanePrefab;
+
+ [SerializeField]
+ private BiPlane myPlane;
+
+ protected override void Start()
+ {
+ if (myPlane == null)
+ {
+ myPlane = Object.Instantiate(biPlanePrefab, base.transform.position + new Vector3(-50f, 5f, 0f), Quaternion.identity).GetComponent<BiPlane>();
+ }
+ base.Start();
+ }
+
+ public override void SetStats()
+ {
+ base.SetStats();
+ if (myPlane == null)
+ {
+ myPlane = Object.Instantiate(biPlanePrefab, base.transform.position + new Vector3(-50f, 5f, 0f), Quaternion.identity).GetComponent<BiPlane>();
+ }
+ myPlane.damage = base.damage;
+ myPlane.healthDamage = base.healthDamage;
+ myPlane.armorDamage = base.armorDamage;
+ myPlane.shieldDamage = base.shieldDamage;
+ myPlane.rps = rps;
+ myPlane.slowPercent = base.slowPercent;
+ myPlane.bleedPercent = base.bleedPercent;
+ myPlane.burnPercent = base.burnPercent;
+ myPlane.poisonPercent = base.poisonPercent;
+ myPlane.critChance = base.critChance;
+ myPlane.stunChance = base.stunChance;
+ }
+
+ protected override void Update()
+ {
+ if (currentTarget != null)
+ {
+ if (turret != null)
+ {
+ AimTurret();
+ }
+ GainXP();
+ }
+ timeSinceLastShot += Time.deltaTime;
+ if (currentTarget != null && timeSinceLastShot > 3f)
+ {
+ SendTargetInfo(currentTarget);
+ timeSinceLastShot = 0f;
+ }
+ }
+
+ private void SendTargetInfo(GameObject target)
+ {
+ if (myPlane.target == null)
+ {
+ myPlane.target = target;
+ }
+ }
+
+ public override void Demolish()
+ {
+ Object.Destroy(myPlane.gameObject);
+ base.Demolish();
+ }
+}
diff --git a/GameCode/ResourceManager.cs b/GameCode/ResourceManager.cs
new file mode 100644
index 0000000..5b9867f
--- /dev/null
+++ b/GameCode/ResourceManager.cs
@@ -0,0 +1,225 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class ResourceManager : MonoBehaviour
+{
+ public static ResourceManager instance;
+
+ [SerializeField]
+ private int gold;
+
+ [SerializeField]
+ private Text goldText;
+
+ [SerializeField]
+ private int mana;
+
+ private float manaLoss = 1f;
+
+ [SerializeField]
+ private int maxMana;
+
+ [SerializeField]
+ private int manaGatherRate;
+
+ [SerializeField]
+ private Text manaText;
+
+ [SerializeField]
+ private Image maskBar;
+
+ [SerializeField]
+ private Image manaBar;
+
+ [SerializeField]
+ private Image manaLossBar;
+
+ private float manaTimer;
+
+ private int manaGen;
+
+ public float manaMaxRegenPercent;
+
+ [SerializeField]
+ private GameObject manaHUDObject;
+
+ [SerializeField]
+ private Text manaHUDText;
+
+ public int enemyBonusGoldDrop;
+
+ public List<ManaBank> manaBanks = new List<ManaBank>();
+
+ public int manaBankBonusMana;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ gold = 1000 + 100 * (PlayerPrefs.GetInt("StartGold1", 0) + PlayerPrefs.GetInt("StartGold2", 0) + PlayerPrefs.GetInt("StartGold3", 0) + PlayerPrefs.GetInt("StartGold4", 0) + PlayerPrefs.GetInt("StartGold5", 0) + PlayerPrefs.GetInt("StartGold6", 0) + PlayerPrefs.GetInt("StartGold7", 0) + PlayerPrefs.GetInt("StartGold8", 0) + PlayerPrefs.GetInt("StartGold9", 0) + PlayerPrefs.GetInt("StartGold10", 0));
+ maxMana = 100 + 20 * (PlayerPrefs.GetInt("MaxMana1", 0) + PlayerPrefs.GetInt("MaxMana2", 0) + PlayerPrefs.GetInt("MaxMana3", 0) + PlayerPrefs.GetInt("MaxMana4", 0) + PlayerPrefs.GetInt("MaxMana5", 0));
+ enemyBonusGoldDrop = PlayerPrefs.GetInt("GoldDrop1", 0) + PlayerPrefs.GetInt("GoldDrop2", 0) + PlayerPrefs.GetInt("GoldDrop3", 0) + PlayerPrefs.GetInt("GoldDrop4", 0) + PlayerPrefs.GetInt("GoldDrop5", 0);
+ manaGen = PlayerPrefs.GetInt("ManaGen1", 0) + PlayerPrefs.GetInt("ManaGen2", 0) + PlayerPrefs.GetInt("ManaGen3", 0);
+ UpdateManaGatherRate(manaGen);
+ SetManaBar();
+ }
+
+ private void Update()
+ {
+ if (manaLoss > (float)mana)
+ {
+ float num = 20f;
+ if (maxMana >= 500)
+ {
+ num = 100f;
+ }
+ manaLoss = Mathf.Clamp(manaLoss - num * Time.deltaTime, mana, manaLoss);
+ manaLossBar.rectTransform.sizeDelta = new Vector2(manaLoss / num, 0.25f);
+ }
+ if (manaTimer <= 0f)
+ {
+ AddMana(manaGen);
+ AddManaPercentMax(manaMaxRegenPercent);
+ manaTimer = 1f;
+ }
+ else if (manaGen > 0 && SpawnManager.instance.combat)
+ {
+ manaTimer -= Time.deltaTime;
+ }
+ }
+
+ public void Spend(int goldCost)
+ {
+ gold -= goldCost;
+ UpdateResourceText();
+ }
+
+ public void AddMoney(int addedGold)
+ {
+ gold += addedGold;
+ UpdateResourceText();
+ }
+
+ public bool CheckMoney(int goldCost)
+ {
+ if (gold >= goldCost)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void SpendMana(int manaCost)
+ {
+ mana -= manaCost;
+ UpdateResourceText();
+ }
+
+ public void AddMana(int addedMana)
+ {
+ mana = Mathf.Clamp(mana + addedMana, 0, maxMana);
+ if (manaLoss < (float)mana)
+ {
+ manaLoss = mana;
+ }
+ UpdateResourceText();
+ }
+
+ public void AddManaPercentMax(float percent)
+ {
+ AddMana(Mathf.FloorToInt(percent * (float)maxMana));
+ }
+
+ public void AddMaxMana(int amount)
+ {
+ maxMana += amount;
+ float num = 20f;
+ if (maxMana >= 500)
+ {
+ num = 100f;
+ }
+ maskBar.rectTransform.localScale = new Vector3(num * 200f / (float)maxMana, 50f, 10f);
+ maskBar.rectTransform.sizeDelta = new Vector2((float)maxMana / num, 0.25f);
+ UpdateResourceText();
+ }
+
+ public bool CheckMana(int manaCost)
+ {
+ if (mana >= manaCost)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ public void UpdateManaGatherRate(int addedGatherRate)
+ {
+ manaGatherRate += addedGatherRate;
+ UpdateResourceText();
+ }
+
+ public void UpdateManaHUD()
+ {
+ string text = "";
+ if (manaMaxRegenPercent > 0f)
+ {
+ text = text + "+" + Mathf.FloorToInt(101f * manaMaxRegenPercent) + "%/s\n";
+ }
+ if (MonsterManager.instance.manaDropOnDeath > 0f)
+ {
+ text = text + Mathf.FloorToInt(101f * MonsterManager.instance.manaDropOnDeath) + "%/kill";
+ }
+ if (text.Length > 1)
+ {
+ manaHUDObject.SetActive(value: true);
+ manaHUDText.text = text;
+ }
+ }
+
+ public void AddPercentManaGathering(float percent)
+ {
+ manaMaxRegenPercent += percent;
+ UpdateResourceText();
+ }
+
+ private void UpdateResourceText()
+ {
+ goldText.text = "Gold: " + gold;
+ manaText.text = "Mana: " + mana + "/" + maxMana + " (+" + (manaGatherRate + Mathf.FloorToInt(manaMaxRegenPercent * (float)maxMana)) + "/s)";
+ float num = 20f;
+ if (maxMana >= 500)
+ {
+ num = 100f;
+ }
+ manaBar.rectTransform.sizeDelta = new Vector2((float)mana / num, 0.25f);
+ }
+
+ public void SetManaBar()
+ {
+ float num = 20f;
+ if (maxMana >= 500)
+ {
+ num = 100f;
+ }
+ maskBar.rectTransform.localScale = new Vector3(num * 200f / (float)maxMana, 50f, 10f);
+ maskBar.rectTransform.sizeDelta = new Vector2((float)maxMana / num, 0.25f);
+ manaLossBar.rectTransform.sizeDelta = new Vector2((float)maxMana / num, 0.25f);
+ UpdateResourceText();
+ }
+
+ public void UpgradeManaBankMaxMana(int addition)
+ {
+ manaBankBonusMana += addition;
+ foreach (ManaBank manaBank in manaBanks)
+ {
+ if (manaBank != null)
+ {
+ manaBank.UpgradeMaxMana(addition);
+ }
+ }
+ }
+}
diff --git a/GameCode/RogueTower.csproj b/GameCode/RogueTower.csproj
new file mode 100644
index 0000000..4530366
--- /dev/null
+++ b/GameCode/RogueTower.csproj
@@ -0,0 +1,260 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <AssemblyName>Assembly-CSharp</AssemblyName>
+ <GenerateAssemblyInfo>False</GenerateAssemblyInfo>
+ <TargetFramework>netstandard2.0</TargetFramework>
+ </PropertyGroup>
+ <PropertyGroup>
+ <LangVersion>11.0</LangVersion>
+ <AllowUnsafeBlocks>True</AllowUnsafeBlocks>
+ </PropertyGroup>
+ <PropertyGroup />
+ <ItemGroup>
+ <Reference Include="Mono.Security">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Mono.Security.dll</HintPath>
+ </Reference>
+ <Reference Include="Newtonsoft.Json">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Newtonsoft.Json.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Configuration">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.Configuration.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Diagnostics.StackTrace">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.Diagnostics.StackTrace.dll</HintPath>
+ </Reference>
+ <Reference Include="System.EnterpriseServices">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.EnterpriseServices.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Globalization.Extensions">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.Globalization.Extensions.dll</HintPath>
+ </Reference>
+ <Reference Include="System.ServiceModel.Internals">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.ServiceModel.Internals.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Xml.XPath.XDocument">
+ <HintPath>..\..\Rogue Tower_Data\Managed\System.Xml.XPath.XDocument.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.Postprocessing.Runtime">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.Postprocessing.Runtime.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.TextMeshPro">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.TextMeshPro.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.Timeline">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.Timeline.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.VisualScripting.Antlr3.Runtime">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.VisualScripting.Antlr3.Runtime.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.VisualScripting.Core">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.VisualScripting.Core.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.VisualScripting.Flow">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.VisualScripting.Flow.dll</HintPath>
+ </Reference>
+ <Reference Include="Unity.VisualScripting.State">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Unity.VisualScripting.State.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AccessibilityModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AccessibilityModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AIModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AIModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AndroidJNIModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AndroidJNIModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AnimationModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AnimationModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ARModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ARModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AssetBundleModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AssetBundleModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ClothModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ClothModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ClusterInputModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ClusterInputModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ClusterRendererModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ClusterRendererModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.CoreModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
+ </Reference>
+ <Reference Include="Assembly-CSharp-firstpass">
+ <HintPath>..\..\Rogue Tower_Data\Managed\Assembly-CSharp-firstpass.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.AudioModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.AudioModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.CrashReportingModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.CrashReportingModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.DirectorModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.DirectorModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.DSPGraphModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.DSPGraphModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.GameCenterModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.GameCenterModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.GIModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.GIModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.GridModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.GridModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.HotReloadModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.HotReloadModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ImageConversionModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ImageConversionModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.IMGUIModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.IMGUIModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.InputModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.InputModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.JSONSerializeModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.JSONSerializeModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.LocalizationModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.LocalizationModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.PerformanceReportingModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.PerformanceReportingModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.Physics2DModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.Physics2DModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.PhysicsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.PhysicsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ProfilerModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ProfilerModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.RuntimeInitializeOnLoadManagerInitializerModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ScreenCaptureModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ScreenCaptureModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.SharedInternalsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.SharedInternalsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.SpriteMaskModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.SpriteMaskModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.SpriteShapeModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.SpriteShapeModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.StreamingModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.StreamingModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.SubstanceModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.SubstanceModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.SubsystemsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.SubsystemsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TerrainModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TerrainModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TerrainPhysicsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TerrainPhysicsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TextCoreModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TextCoreModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TextRenderingModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TextRenderingModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TilemapModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TilemapModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.TLSModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.TLSModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UI">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UI.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.ParticleSystemModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.ParticleSystemModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.InputLegacyModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.InputLegacyModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UIElementsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UIElementsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UIElementsNativeModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UIElementsNativeModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UIModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UIModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UmbraModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UmbraModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UNETModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UNETModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityAnalyticsModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityAnalyticsModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityConnectModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityConnectModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityCurlModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityCurlModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityTestProtocolModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityTestProtocolModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityWebRequestAssetBundleModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityWebRequestAssetBundleModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityWebRequestAudioModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityWebRequestAudioModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityWebRequestModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityWebRequestModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityWebRequestTextureModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityWebRequestTextureModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.UnityWebRequestWWWModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.UnityWebRequestWWWModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.VehiclesModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.VehiclesModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.VFXModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.VFXModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.VideoModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.VideoModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.VirtualTexturingModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.VirtualTexturingModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.VRModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.VRModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.WindModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.WindModule.dll</HintPath>
+ </Reference>
+ <Reference Include="UnityEngine.XRModule">
+ <HintPath>..\..\Rogue Tower_Data\Managed\UnityEngine.XRModule.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/GameCode/SFXManager.cs b/GameCode/SFXManager.cs
new file mode 100644
index 0000000..9c972e5
--- /dev/null
+++ b/GameCode/SFXManager.cs
@@ -0,0 +1,158 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SFXManager : MonoBehaviour
+{
+ public float volume = 0.5f;
+
+ [SerializeField]
+ private AudioClip ballistaClip;
+
+ [SerializeField]
+ private AudioClip mortarClip;
+
+ [SerializeField]
+ private AudioClip teslaClip;
+
+ [SerializeField]
+ private AudioClip[] explosions;
+
+ [SerializeField]
+ private AudioClip biPlaneGunClip;
+
+ [SerializeField]
+ private AudioClip particleCannonClip;
+
+ [SerializeField]
+ private AudioClip shredderClip;
+
+ [SerializeField]
+ private AudioClip[] ballistaHits;
+
+ [SerializeField]
+ private AudioClip frostHitClip;
+
+ [SerializeField]
+ private AudioClip shredderHitClip;
+
+ [SerializeField]
+ private AudioClip[] bulletHits;
+
+ [SerializeField]
+ private AudioClip particleHitClip;
+
+ [SerializeField]
+ private AudioClip[] coinLongClips;
+
+ [SerializeField]
+ private AudioClip[] coinShortClips;
+
+ [SerializeField]
+ private AudioClip buttonClick;
+
+ [SerializeField]
+ private AudioClip[] critSmall;
+
+ [SerializeField]
+ private AudioClip[] critBig;
+
+ [SerializeField]
+ private AudioClip cards;
+
+ [SerializeField]
+ private GameObject sourceObject;
+
+ public List<AudioPoolSource> sources = new List<AudioPoolSource>();
+
+ public static SFXManager instance;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ volume = OptionsMenu.instance.masterVolume * OptionsMenu.instance.sfxVolume;
+ }
+
+ public void ButtonClick()
+ {
+ PlaySound(Sound.ButtonClick, MusicManager.instance.transform.position, MusicManager.instance.transform);
+ }
+
+ public void PlaySound(Sound s, Vector3 pos)
+ {
+ PlaySound(s, pos, null);
+ }
+
+ public void PlaySound(Sound s, Vector3 pos, Transform parent)
+ {
+ if (!(volume <= 0f))
+ {
+ AudioClip clip = GetClip(s);
+ AudioPoolSource audioPoolSource;
+ if (sources.Count < 1)
+ {
+ audioPoolSource = Object.Instantiate(sourceObject).GetComponent<AudioPoolSource>();
+ }
+ else
+ {
+ audioPoolSource = sources[0];
+ sources.Remove(audioPoolSource);
+ }
+ audioPoolSource.transform.position = pos;
+ audioPoolSource.PlayClip(clip, volume, 0.08333f);
+ if (parent != null)
+ {
+ audioPoolSource.transform.parent = parent;
+ }
+ }
+ }
+
+ private AudioClip GetClip(Sound s)
+ {
+ switch (s)
+ {
+ case Sound.Ballista:
+ return ballistaClip;
+ case Sound.Mortar:
+ return mortarClip;
+ case Sound.TeslaZap:
+ return teslaClip;
+ case Sound.Explosion:
+ return explosions[Random.Range(0, explosions.Length)];
+ case Sound.BiPlaneGun:
+ return biPlaneGunClip;
+ case Sound.ParticleCannon:
+ return particleCannonClip;
+ case Sound.Shredder:
+ return shredderClip;
+ case Sound.BallistaHit:
+ return ballistaHits[Random.Range(0, ballistaHits.Length)];
+ case Sound.FrostHit:
+ return frostHitClip;
+ case Sound.ShredderHit:
+ return shredderHitClip;
+ case Sound.BulletHit:
+ return bulletHits[Random.Range(0, bulletHits.Length)];
+ case Sound.ParticleHit:
+ return particleHitClip;
+ case Sound.CoinLong:
+ return coinLongClips[Random.Range(0, coinLongClips.Length)];
+ case Sound.CoinShort:
+ return coinShortClips[Random.Range(0, coinShortClips.Length)];
+ case Sound.ButtonClick:
+ return buttonClick;
+ case Sound.CritSmall:
+ return critSmall[Random.Range(0, critSmall.Length)];
+ case Sound.CritBig:
+ return critBig[Random.Range(0, critBig.Length)];
+ case Sound.Cards:
+ return cards;
+ default:
+ Debug.LogError("No Audio Clip Found Type " + s);
+ return null;
+ }
+ }
+}
diff --git a/GameCode/Sawblade.cs b/GameCode/Sawblade.cs
new file mode 100644
index 0000000..19038df
--- /dev/null
+++ b/GameCode/Sawblade.cs
@@ -0,0 +1,87 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Sawblade : Projectile
+{
+ private Pathfinder targetPathfinder;
+
+ private Waypoint nextWaypoint;
+
+ private bool pathMode;
+
+ private HashSet<Collider> targetsHit = new HashSet<Collider>();
+
+ public override void SetStats(TowerType whoShotMe, GameObject _target, float spd, int dmg, int healthDmg, int armorDmg, int shieldDmg, float slow, float bleed, float burn, float poison, float crit, float stun)
+ {
+ base.SetStats(whoShotMe, _target, spd, dmg, healthDmg, armorDmg, shieldDmg, slow, bleed, burn, poison, crit, stun);
+ targetPathfinder = target.GetComponent<Pathfinder>();
+ nextWaypoint = targetPathfinder.currentWaypoint;
+ }
+
+ protected override void AlterCourse()
+ {
+ if (!pathMode && targetPathfinder != null)
+ {
+ nextWaypoint = targetPathfinder.currentWaypoint;
+ }
+ if (!pathMode && (target == null || Vector3.SqrMagnitude(target.transform.position - base.transform.position) < 0.125f))
+ {
+ nextWaypoint = GetPreviousWaypoint();
+ pathMode = true;
+ }
+ Vector3 position;
+ if (!pathMode)
+ {
+ position = target.transform.position;
+ }
+ else
+ {
+ if (Vector3.SqrMagnitude(nextWaypoint.transform.position - base.transform.position) < 0.125f)
+ {
+ nextWaypoint = GetPreviousWaypoint();
+ }
+ position = nextWaypoint.transform.position;
+ }
+ Quaternion rotation = Quaternion.LookRotation(position - base.transform.position, Vector3.up);
+ base.transform.rotation = rotation;
+ }
+
+ protected override void CheckForHits()
+ {
+ Collider[] array = Physics.OverlapBox(base.transform.position, Vector3.one * 0.25f, Quaternion.identity, layermask, QueryTriggerInteraction.Collide);
+ foreach (Collider collider in array)
+ {
+ if (!pathMode && collider.gameObject == target)
+ {
+ nextWaypoint = GetPreviousWaypoint();
+ pathMode = true;
+ }
+ if (targetsHit.Contains(collider))
+ {
+ continue;
+ }
+ IDamageable component = collider.GetComponent<IDamageable>();
+ if (component != null)
+ {
+ DealDamage(component);
+ damage--;
+ if (damage <= 0)
+ {
+ Object.Destroy(base.gameObject);
+ }
+ }
+ targetsHit.Add(collider);
+ }
+ }
+
+ private Waypoint GetPreviousWaypoint()
+ {
+ Waypoint[] previousWaypoints = nextWaypoint.GetPreviousWaypoints();
+ if (previousWaypoints.Length == 0)
+ {
+ Object.Destroy(base.gameObject);
+ return nextWaypoint;
+ }
+ return previousWaypoints[Random.Range(0, previousWaypoints.Length)];
+ }
+}
diff --git a/GameCode/Shrine.cs b/GameCode/Shrine.cs
new file mode 100644
index 0000000..bbdfd2d
--- /dev/null
+++ b/GameCode/Shrine.cs
@@ -0,0 +1,11 @@
+public class Shrine : SpawnableObject
+{
+ protected override void Start()
+ {
+ base.Start();
+ if (spawned)
+ {
+ AchievementManager.instance.shrineSpawned = true;
+ }
+ }
+}
diff --git a/GameCode/SimpleUI.cs b/GameCode/SimpleUI.cs
new file mode 100644
index 0000000..ec51413
--- /dev/null
+++ b/GameCode/SimpleUI.cs
@@ -0,0 +1,51 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class SimpleUI : MonoBehaviour
+{
+ private GameObject demolishableObject;
+
+ private int goldBackOnDemolish;
+
+ [SerializeField]
+ private Text demolishText;
+
+ [SerializeField]
+ private Text discriptionText;
+
+ [SerializeField]
+ private GameObject demolishButton;
+
+ private void Start()
+ {
+ UIManager.instance.SetNewUI(base.gameObject);
+ }
+
+ public void SetDemolishable(GameObject obj, int goldReturned)
+ {
+ demolishableObject = obj;
+ goldBackOnDemolish = goldReturned;
+ demolishButton.SetActive(value: true);
+ if (demolishText != null)
+ {
+ demolishText.text = "Demolish (" + goldBackOnDemolish + "g)";
+ }
+ }
+
+ public void Demolish()
+ {
+ demolishableObject.GetComponent<IBuildable>()?.Demolish();
+ Object.Destroy(demolishableObject);
+ ResourceManager.instance.AddMoney(goldBackOnDemolish);
+ SFXManager.instance.ButtonClick();
+ UIManager.instance.CloseUI(base.gameObject);
+ }
+
+ public void SetDiscriptionText(string txt)
+ {
+ if (discriptionText != null)
+ {
+ discriptionText.text = txt;
+ }
+ }
+}
diff --git a/GameCode/SlowRotate.cs b/GameCode/SlowRotate.cs
new file mode 100644
index 0000000..7a0306a
--- /dev/null
+++ b/GameCode/SlowRotate.cs
@@ -0,0 +1,12 @@
+using UnityEngine;
+
+public class SlowRotate : MonoBehaviour
+{
+ [SerializeField]
+ private Vector3 rotation;
+
+ private void Update()
+ {
+ base.transform.localEulerAngles += rotation * Time.deltaTime;
+ }
+}
diff --git a/GameCode/SnowFlake.cs b/GameCode/SnowFlake.cs
new file mode 100644
index 0000000..238b829
--- /dev/null
+++ b/GameCode/SnowFlake.cs
@@ -0,0 +1,23 @@
+using UnityEngine;
+
+public class SnowFlake : Projectile
+{
+ protected override void Start()
+ {
+ base.Start();
+ base.transform.Translate(new Vector3(Random.Range(-0.25f, 0.25f), Random.Range(-0.25f, 0.25f), Random.Range(-0.25f, 0.25f)));
+ }
+
+ protected override void MoveProjectile()
+ {
+ base.transform.Translate(Vector3.down * speed * Time.fixedDeltaTime);
+ }
+
+ protected override void CheckForHits()
+ {
+ if (Physics.SphereCast(base.transform.position, 0.125f, Vector3.down, out var hitInfo, speed * Time.fixedDeltaTime, layermask, QueryTriggerInteraction.Collide))
+ {
+ OnHit(hitInfo);
+ }
+ }
+}
diff --git a/GameCode/SocialMediaManager.cs b/GameCode/SocialMediaManager.cs
new file mode 100644
index 0000000..25f761f
--- /dev/null
+++ b/GameCode/SocialMediaManager.cs
@@ -0,0 +1,9 @@
+using UnityEngine;
+
+public class SocialMediaManager : MonoBehaviour
+{
+ public void OpenLink(string s)
+ {
+ Application.OpenURL(s);
+ }
+}
diff --git a/GameCode/Sound.cs b/GameCode/Sound.cs
new file mode 100644
index 0000000..8a1528f
--- /dev/null
+++ b/GameCode/Sound.cs
@@ -0,0 +1,22 @@
+public enum Sound
+{
+ None,
+ Ballista,
+ Mortar,
+ TeslaZap,
+ Explosion,
+ BiPlaneGun,
+ ParticleCannon,
+ Shredder,
+ BallistaHit,
+ FrostHit,
+ ShredderHit,
+ BulletHit,
+ ParticleHit,
+ CoinLong,
+ CoinShort,
+ ButtonClick,
+ CritSmall,
+ CritBig,
+ Cards
+}
diff --git a/GameCode/SpawnManager.cs b/GameCode/SpawnManager.cs
new file mode 100644
index 0000000..4e66c04
--- /dev/null
+++ b/GameCode/SpawnManager.cs
@@ -0,0 +1,495 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class SpawnManager : MonoBehaviour
+{
+ public static SpawnManager instance;
+
+ public Waypoint[] initialSpawns;
+
+ private HashSet<Waypoint> spawnPoints = new HashSet<Waypoint>();
+
+ public HashSet<IncomeGenerator> incomeGenerators = new HashSet<IncomeGenerator>();
+
+ public HashSet<University> universities = new HashSet<University>();
+
+ public HashSet<Mine> mines = new HashSet<Mine>();
+
+ public HashSet<House> houses = new HashSet<House>();
+
+ public HashSet<GameObject> destroyOnNewLevel = new HashSet<GameObject>();
+
+ public HashSet<GameObject> tileSpawnUis = new HashSet<GameObject>();
+
+ public HashSet<Enemy> currentEnemies = new HashSet<Enemy>();
+
+ public bool combat;
+
+ private float levelTime;
+
+ [SerializeField]
+ private Text levelText;
+
+ [SerializeField]
+ private Text scoreText;
+
+ [SerializeField]
+ private int cardDrawFrequency = 5;
+
+ [SerializeField]
+ private float baseRepairChance;
+
+ public int level;
+
+ public int lastLevel = 30;
+
+ [SerializeField]
+ private GameObject level1Enemy;
+
+ [SerializeField]
+ private GameObject level3Enemy;
+
+ [SerializeField]
+ private GameObject level5Enemy;
+
+ [SerializeField]
+ private GameObject level7Enemy;
+
+ [SerializeField]
+ private GameObject level9Enemy;
+
+ [SerializeField]
+ private GameObject level12Enemy;
+
+ [SerializeField]
+ private GameObject level16Enemy;
+
+ [SerializeField]
+ private GameObject level18Enemy;
+
+ [SerializeField]
+ private GameObject level20Enemy;
+
+ [SerializeField]
+ private GameObject level21Enemy;
+
+ [SerializeField]
+ private GameObject level23Enemy;
+
+ [SerializeField]
+ private GameObject level26Enemy;
+
+ [SerializeField]
+ private GameObject level28Enemy;
+
+ [SerializeField]
+ private GameObject level30Enemy;
+
+ [SerializeField]
+ private GameObject level32Enemy;
+
+ [SerializeField]
+ private GameObject level36Enemy;
+
+ [SerializeField]
+ private GameObject level38Enemy;
+
+ [SerializeField]
+ private GameObject level40Enemy;
+
+ [SerializeField]
+ private GameObject level15Boss;
+
+ [SerializeField]
+ private GameObject level25Boss;
+
+ [SerializeField]
+ private GameObject level35Boss;
+
+ [SerializeField]
+ private GameObject level45Boss;
+
+ [SerializeField]
+ private LightManager lightManager;
+
+ [SerializeField]
+ private int secondStageLightLevel;
+
+ [SerializeField]
+ private int thirdStageLightLevel;
+
+ [SerializeField]
+ private int fourthStageLightLevel;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ Waypoint[] array = initialSpawns;
+ foreach (Waypoint item in array)
+ {
+ spawnPoints.Add(item);
+ }
+ levelText.text = "Level: " + level;
+ scoreText.text = "Score: " + 0;
+ cardDrawFrequency = 3 - PlayerPrefs.GetInt("CardFreq1", 0) - PlayerPrefs.GetInt("CardFreq2", 0);
+ baseRepairChance = 34f * (float)(PlayerPrefs.GetInt("TowerRepair1", 0) + PlayerPrefs.GetInt("TowerRepair2", 0) + PlayerPrefs.GetInt("TowerRepair3", 0));
+ }
+
+ private void Update()
+ {
+ if (combat && currentEnemies.Count == 0 && !GameManager.instance.gameOver && levelTime > 2f)
+ {
+ EndWave();
+ }
+ else
+ {
+ levelTime += Time.deltaTime;
+ }
+ }
+
+ private void EndWave()
+ {
+ combat = false;
+ AchievementManager.instance.BeatLevel(level);
+ int @int = PlayerPrefs.GetInt("XP", 0);
+ PlayerPrefs.SetInt("XP", @int + level * GameManager.instance.gameMode);
+ scoreText.text = "Score: " + GameManager.instance.NaturalSum(level) * GameManager.instance.gameMode;
+ if (level >= lastLevel)
+ {
+ GameManager.instance.Victory();
+ return;
+ }
+ ShowSpawnUIs(state: true);
+ if (level % cardDrawFrequency == 0)
+ {
+ ShowSpawnUIs(state: false);
+ CardManager.instance.DrawCards();
+ }
+ if (level == 15 || level == 25 || level == 35)
+ {
+ MusicManager.instance.FadeOut(6f);
+ }
+ else
+ {
+ MusicManager.instance.SetIntensity(action: false);
+ }
+ GameObject[] array = new GameObject[destroyOnNewLevel.Count];
+ destroyOnNewLevel.CopyTo(array);
+ GameObject[] array2 = array;
+ foreach (GameObject gameObject in array2)
+ {
+ destroyOnNewLevel.Remove(gameObject);
+ Object.Destroy(gameObject);
+ }
+ if (tileSpawnUis.Count == 0)
+ {
+ Debug.Log("I GUESS ILL JUST DIE THEN");
+ AchievementManager.instance.UnlockAchievement("NoMorePaths");
+ StartNextWave();
+ }
+ }
+
+ public void StartNextWave()
+ {
+ levelTime = 0f;
+ UpdateSpawnPoints();
+ float num = 1f - (float)(GameManager.instance.health / GameManager.instance.maxHealth);
+ if (Random.Range(1f, 100f) <= baseRepairChance * num)
+ {
+ GameManager.instance.RepairTower(10);
+ }
+ level++;
+ levelText.text = "Level: " + level;
+ if (level == secondStageLightLevel)
+ {
+ lightManager.ChangeColor(2);
+ }
+ else if (level == thirdStageLightLevel)
+ {
+ lightManager.ChangeColor(3);
+ }
+ else if (level == fourthStageLightLevel)
+ {
+ lightManager.ChangeColor(4);
+ }
+ if (level == 15 || level == 25 || level == 35 || level == 45)
+ {
+ SpawnBoss(level);
+ }
+ if (level == 16)
+ {
+ MusicManager.instance.PlaySong(1, action: true);
+ }
+ else if (level == 26)
+ {
+ MusicManager.instance.PlaySong(2, action: true);
+ }
+ else if (level == 36)
+ {
+ MusicManager.instance.PlaySong(3, action: true);
+ }
+ else if (level > 1)
+ {
+ MusicManager.instance.SetIntensity(action: true);
+ }
+ StartCoroutine(Spawn(level * level));
+ combat = true;
+ ShowSpawnUIs(state: false);
+ foreach (House house in houses)
+ {
+ if (house != null)
+ {
+ house.CheckTowers();
+ }
+ }
+ foreach (IncomeGenerator incomeGenerator in incomeGenerators)
+ {
+ if (incomeGenerator != null)
+ {
+ incomeGenerator.GenerateIncome();
+ }
+ }
+ foreach (University university in universities)
+ {
+ if (university != null)
+ {
+ university.Research();
+ }
+ }
+ foreach (Mine mine in mines)
+ {
+ if (mine != null)
+ {
+ mine.Repair();
+ }
+ }
+ }
+
+ public void ShowSpawnUIs(bool state)
+ {
+ foreach (GameObject tileSpawnUi in tileSpawnUis)
+ {
+ tileSpawnUi.SetActive(state);
+ }
+ }
+
+ private void SpawnBoss(int lvl)
+ {
+ Waypoint spawnLocation = null;
+ float num = -1f;
+ foreach (Waypoint spawnPoint in spawnPoints)
+ {
+ if (spawnPoint.distanceFromEnd > num)
+ {
+ num = spawnPoint.distanceFromEnd;
+ spawnLocation = spawnPoint;
+ }
+ }
+ switch (lvl)
+ {
+ case 15:
+ SpawnEnemy(level15Boss, spawnLocation);
+ break;
+ case 25:
+ SpawnEnemy(level25Boss, spawnLocation);
+ break;
+ case 35:
+ SpawnEnemy(level35Boss, spawnLocation);
+ break;
+ case 45:
+ SpawnEnemy(level45Boss, spawnLocation);
+ break;
+ }
+ }
+
+ private IEnumerator Spawn(int num)
+ {
+ int safetyCount = level * level + 100;
+ int count = num;
+ float t = 0.5f / (float)spawnPoints.Count;
+ while (count > 0)
+ {
+ foreach (Waypoint spawnPoint in spawnPoints)
+ {
+ if (count <= 0)
+ {
+ break;
+ }
+ count -= SpawnSelection(spawnPoint);
+ yield return new WaitForSeconds(t);
+ }
+ safetyCount--;
+ if (safetyCount <= 0)
+ {
+ Debug.LogError("Spawn loop might be looping infinitely");
+ break;
+ }
+ }
+ }
+
+ private int SpawnSelection(Waypoint spawnPoint)
+ {
+ if (level >= 40 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level40Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 38 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level38Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 36 && Random.Range(1f, 100f) < 12f)
+ {
+ SpawnEnemy(level36Enemy, spawnPoint);
+ return 9;
+ }
+ if (level >= 32 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level32Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 30 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level30Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 28 && Random.Range(1f, 100f) < 11f)
+ {
+ SpawnEnemy(level28Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 26 && Random.Range(1f, 100f) < 12f)
+ {
+ SpawnEnemy(level26Enemy, spawnPoint);
+ return 7;
+ }
+ if (level >= 23 && Random.Range(1f, 100f) < 9f)
+ {
+ SpawnEnemy(level23Enemy, spawnPoint);
+ return 12;
+ }
+ if (level >= 21 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level21Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 20 && Random.Range(1f, 100f) < 10f)
+ {
+ SpawnEnemy(level20Enemy, spawnPoint);
+ return 10;
+ }
+ if (level >= 18 && Random.Range(1f, 100f) < 12f)
+ {
+ SpawnEnemy(level18Enemy, spawnPoint);
+ return 9;
+ }
+ if (level >= 16 && Random.Range(1f, 100f) < 13f)
+ {
+ SpawnEnemy(level16Enemy, spawnPoint);
+ return 8;
+ }
+ if (level >= 12 && Random.Range(1f, 100f) < 9f)
+ {
+ SpawnEnemy(level12Enemy, spawnPoint);
+ return 12;
+ }
+ if (level >= 9 && Random.Range(1f, 100f) < 12f)
+ {
+ SpawnEnemy(level9Enemy, spawnPoint);
+ return 9;
+ }
+ if (level >= 7 && Random.Range(1f, 100f) < 14f)
+ {
+ SpawnEnemy(level7Enemy, spawnPoint);
+ return 7;
+ }
+ if (level >= 5 && Random.Range(1f, 100f) < 20f)
+ {
+ SpawnEnemy(level5Enemy, spawnPoint);
+ return 5;
+ }
+ if (level >= 3 && Random.Range(1f, 100f) < 34f)
+ {
+ SpawnEnemy(level3Enemy, spawnPoint);
+ return 3;
+ }
+ SpawnEnemy(level1Enemy, spawnPoint);
+ return 1;
+ }
+
+ private void SpawnEnemy(GameObject unit, Waypoint spawnLocation)
+ {
+ Enemy component = Object.Instantiate(unit, spawnLocation.transform.position, Quaternion.identity).GetComponent<Enemy>();
+ component.SetStats();
+ component.SetFirstSpawnPoint(spawnLocation);
+ currentEnemies.Add(component);
+ }
+
+ private void UpdateSpawnPoints()
+ {
+ List<Waypoint> list = new List<Waypoint>();
+ List<Waypoint> list2 = new List<Waypoint>();
+ foreach (Waypoint spawnPoint in spawnPoints)
+ {
+ if (!CheckSpawnPoint(spawnPoint))
+ {
+ continue;
+ }
+ list.Add(spawnPoint);
+ foreach (Waypoint newSpawnPoint in GetNewSpawnPoints(spawnPoint, 1))
+ {
+ list2.Add(newSpawnPoint);
+ }
+ }
+ foreach (Waypoint item in list)
+ {
+ spawnPoints.Remove(item);
+ }
+ foreach (Waypoint item2 in list2)
+ {
+ spawnPoints.Add(item2);
+ }
+ }
+
+ private bool CheckSpawnPoint(Waypoint point)
+ {
+ if (point.GetPreviousWaypoints().Length != 0)
+ {
+ return true;
+ }
+ return false;
+ }
+
+ private List<Waypoint> GetNewSpawnPoints(Waypoint start, int count)
+ {
+ if (count > 100)
+ {
+ Debug.LogError("Possible infinite loop while finding new spawn points (count over 100)");
+ return null;
+ }
+ Waypoint[] previousWaypoints = start.GetPreviousWaypoints();
+ List<Waypoint> list = new List<Waypoint>();
+ if (previousWaypoints.Length == 0)
+ {
+ list.Add(start);
+ return list;
+ }
+ count++;
+ Waypoint[] array = previousWaypoints;
+ foreach (Waypoint start2 in array)
+ {
+ foreach (Waypoint newSpawnPoint in GetNewSpawnPoints(start2, count))
+ {
+ list.Add(newSpawnPoint);
+ }
+ }
+ return list;
+ }
+}
diff --git a/GameCode/SpawnableObject.cs b/GameCode/SpawnableObject.cs
new file mode 100644
index 0000000..fd7b169
--- /dev/null
+++ b/GameCode/SpawnableObject.cs
@@ -0,0 +1,54 @@
+using UnityEngine;
+
+public class SpawnableObject : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private GameObject artStuff;
+
+ [SerializeField]
+ private bool randomRotation = true;
+
+ [SerializeField]
+ private float spawnChance;
+
+ [SerializeField]
+ private Vector2 spawnLevels;
+
+ [SerializeField]
+ protected GameObject UIObject;
+
+ protected bool spawned;
+
+ protected virtual void Start()
+ {
+ if ((float)SpawnManager.instance.level >= spawnLevels.x && (float)SpawnManager.instance.level <= spawnLevels.y && Random.Range(1f, 100f) < 100f * spawnChance)
+ {
+ if (randomRotation)
+ {
+ artStuff.transform.eulerAngles = new Vector3(0f, Random.Range(0, 4) * 90, 0f);
+ }
+ artStuff.SetActive(value: true);
+ spawned = true;
+ }
+ else
+ {
+ Object.Destroy(base.gameObject);
+ }
+ }
+
+ public void SetStats()
+ {
+ }
+
+ public virtual void SpawnUI()
+ {
+ if (UIObject != null)
+ {
+ Object.Instantiate(UIObject, base.transform.position, Quaternion.identity);
+ }
+ }
+
+ public void Demolish()
+ {
+ }
+}
diff --git a/GameCode/SteamManager.cs b/GameCode/SteamManager.cs
new file mode 100644
index 0000000..642003a
--- /dev/null
+++ b/GameCode/SteamManager.cs
@@ -0,0 +1,123 @@
+using System;
+using System.Text;
+using AOT;
+using Steamworks;
+using UnityEngine;
+
+[DisallowMultipleComponent]
+public class SteamManager : MonoBehaviour
+{
+ protected static bool s_EverInitialized;
+
+ protected static SteamManager s_instance;
+
+ protected bool m_bInitialized;
+
+ protected SteamAPIWarningMessageHook_t m_SteamAPIWarningMessageHook;
+
+ protected static SteamManager Instance
+ {
+ get
+ {
+ if (s_instance == null)
+ {
+ return new GameObject("SteamManager").AddComponent<SteamManager>();
+ }
+ return s_instance;
+ }
+ }
+
+ public static bool Initialized => Instance.m_bInitialized;
+
+ [MonoPInvokeCallback(typeof(SteamAPIWarningMessageHook_t))]
+ protected static void SteamAPIDebugTextHook(int nSeverity, StringBuilder pchDebugText)
+ {
+ Debug.LogWarning(pchDebugText);
+ }
+
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ private static void InitOnPlayMode()
+ {
+ s_EverInitialized = false;
+ s_instance = null;
+ }
+
+ protected virtual void Awake()
+ {
+ if (s_instance != null)
+ {
+ UnityEngine.Object.Destroy(base.gameObject);
+ return;
+ }
+ s_instance = this;
+ if (s_EverInitialized)
+ {
+ throw new Exception("Tried to Initialize the SteamAPI twice in one session!");
+ }
+ UnityEngine.Object.DontDestroyOnLoad(base.gameObject);
+ if (!Packsize.Test())
+ {
+ Debug.LogError("[Steamworks.NET] Packsize Test returned false, the wrong version of Steamworks.NET is being run in this platform.", this);
+ }
+ if (!DllCheck.Test())
+ {
+ Debug.LogError("[Steamworks.NET] DllCheck Test returned false, One or more of the Steamworks binaries seems to be the wrong version.", this);
+ }
+ try
+ {
+ if (SteamAPI.RestartAppIfNecessary((AppId_t)1843760u))
+ {
+ Application.Quit();
+ return;
+ }
+ }
+ catch (DllNotFoundException ex)
+ {
+ Debug.LogError("[Steamworks.NET] Could not load [lib]steam_api.dll/so/dylib. It's likely not in the correct location. Refer to the README for more details.\n" + ex, this);
+ Application.Quit();
+ return;
+ }
+ m_bInitialized = SteamAPI.Init();
+ if (!m_bInitialized)
+ {
+ Debug.LogError("[Steamworks.NET] SteamAPI_Init() failed. Refer to Valve's documentation or the comment above this line for more information.", this);
+ }
+ else
+ {
+ s_EverInitialized = true;
+ }
+ }
+
+ protected virtual void OnEnable()
+ {
+ if (s_instance == null)
+ {
+ s_instance = this;
+ }
+ if (m_bInitialized && m_SteamAPIWarningMessageHook == null)
+ {
+ m_SteamAPIWarningMessageHook = SteamAPIDebugTextHook;
+ SteamClient.SetWarningMessageHook(m_SteamAPIWarningMessageHook);
+ }
+ }
+
+ protected virtual void OnDestroy()
+ {
+ if (!(s_instance != this))
+ {
+ s_instance = null;
+ if (m_bInitialized)
+ {
+ SteamAPI.Shutdown();
+ }
+ }
+ }
+
+ protected virtual void Update()
+ {
+ if (m_bInitialized)
+ {
+ SteamAPI.RunCallbacks();
+ }
+ }
+}
diff --git a/GameCode/TerrainTile.cs b/GameCode/TerrainTile.cs
new file mode 100644
index 0000000..932ff37
--- /dev/null
+++ b/GameCode/TerrainTile.cs
@@ -0,0 +1,87 @@
+using UnityEngine;
+
+public class TerrainTile : MonoBehaviour
+{
+ public Waypoint last;
+
+ [SerializeField]
+ private Waypoint left;
+
+ [SerializeField]
+ private Waypoint top;
+
+ [SerializeField]
+ private Waypoint right;
+
+ public Waypoint south;
+
+ public Waypoint west;
+
+ public Waypoint north;
+
+ public Waypoint east;
+
+ public void SetCardinalDirections()
+ {
+ if (base.transform.eulerAngles.y == 0f)
+ {
+ south = last;
+ west = left;
+ north = top;
+ east = right;
+ }
+ else if (base.transform.eulerAngles.y == 90f)
+ {
+ south = right;
+ west = last;
+ north = left;
+ east = top;
+ }
+ else if (base.transform.eulerAngles.y == 180f)
+ {
+ south = top;
+ west = right;
+ north = last;
+ east = left;
+ }
+ else if (base.transform.eulerAngles.y == 270f)
+ {
+ south = left;
+ west = top;
+ north = right;
+ east = last;
+ }
+ else
+ {
+ Debug.LogError(base.name + " not at a proper rotation. Current rotation: " + base.transform.eulerAngles.y);
+ }
+ }
+
+ public void ConnectToTile(TerrainTile next)
+ {
+ if (base.transform.eulerAngles.y == 0f)
+ {
+ last.SetNextWaypoint(next.north);
+ next.north.AddPreviousWaypoint(last);
+ }
+ else if (base.transform.eulerAngles.y == 90f)
+ {
+ last.SetNextWaypoint(next.east);
+ next.east.AddPreviousWaypoint(last);
+ }
+ else if (base.transform.eulerAngles.y == 180f)
+ {
+ last.SetNextWaypoint(next.south);
+ next.south.AddPreviousWaypoint(last);
+ }
+ else if (base.transform.eulerAngles.y == 270f)
+ {
+ last.SetNextWaypoint(next.west);
+ next.west.AddPreviousWaypoint(last);
+ }
+ else
+ {
+ Debug.LogError(base.name + " not at a proper rotation");
+ }
+ }
+}
diff --git a/GameCode/TeslaCoil.cs b/GameCode/TeslaCoil.cs
new file mode 100644
index 0000000..97954e7
--- /dev/null
+++ b/GameCode/TeslaCoil.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections;
+using UnityEngine;
+
+public class TeslaCoil : Tower
+{
+ [SerializeField]
+ private GameObject lightningTrail;
+
+ protected override void AimTurret()
+ {
+ }
+
+ protected override void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)base.damage * manaConsumptionRate);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ Vector3 position = base.transform.position;
+ position.y = 0f;
+ Collider[] array = Physics.OverlapSphere(position, base.range, enemyLayerMask, QueryTriggerInteraction.Collide);
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].GetComponent<IDamageable>()?.TakeDamage(towerType, base.damage, base.healthDamage, base.armorDamage, base.shieldDamage, base.slowPercent, base.bleedPercent, base.burnPercent, base.poisonPercent, base.critChance, base.stunChance);
+ }
+ SFXManager.instance.PlaySound(Sound.TeslaZap, base.transform.position);
+ StartCoroutine(DrawLightning());
+ }
+
+ private IEnumerator DrawLightning()
+ {
+ for (float i = 0f; i <= (float)Math.PI * 2f; i += 0.5f)
+ {
+ Vector3 vector = new Vector3(UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f), UnityEngine.Random.Range(-0.5f, 0.5f));
+ lightningTrail.transform.position = vector + new Vector3(base.transform.position.x + Mathf.Sin(i) * base.range, 0.75f, base.transform.position.z + Mathf.Cos(i) * base.range);
+ yield return new WaitForEndOfFrame();
+ }
+ }
+}
diff --git a/GameCode/TileManager.cs b/GameCode/TileManager.cs
new file mode 100644
index 0000000..592d861
--- /dev/null
+++ b/GameCode/TileManager.cs
@@ -0,0 +1,236 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class TileManager : MonoBehaviour
+{
+ public static TileManager instance;
+
+ [SerializeField]
+ private GameObject tilePlacementFXObject;
+
+ [SerializeField]
+ private GameObject[] deadEndTiles;
+
+ [SerializeField]
+ private GameObject[] Ltiles;
+
+ [SerializeField]
+ private GameObject[] Ttiles;
+
+ [SerializeField]
+ private GameObject[] Rtiles;
+
+ [SerializeField]
+ private GameObject[] LTtiles;
+
+ [SerializeField]
+ private GameObject[] LRtiles;
+
+ [SerializeField]
+ private GameObject[] TRtiles;
+
+ [SerializeField]
+ private GameObject[] LTRtiles;
+
+ public TerrainTile startTile;
+
+ [SerializeField]
+ private GameObject tileSpawnLocation;
+
+ private int[,] intArray = new int[99, 99];
+
+ private TerrainTile[,] tileArray = new TerrainTile[99, 99];
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ intArray[50, 50] = 1;
+ tileArray[50, 50] = startTile;
+ for (int i = 0; i < 99; i++)
+ {
+ intArray[0, i] = 1;
+ intArray[i, 0] = 1;
+ intArray[98, i] = 1;
+ intArray[i, 98] = 1;
+ }
+ UpdateIntArrayFromTile(startTile, 50, 50);
+ }
+
+ private void UpdateIntArrayFromTile(TerrainTile tile, int posX, int posY)
+ {
+ if (tile.south != null && tile.south != tile.last && intArray[posX, posY - 1] == 0)
+ {
+ intArray[posX, posY - 1] = 2;
+ TileSpawnLocation component = Object.Instantiate(tileSpawnLocation, new Vector3(posX - 50, 0f, posY - 1 - 50) * 7f, Quaternion.identity).GetComponent<TileSpawnLocation>();
+ component.eulerAngle = 180;
+ component.posX = posX;
+ component.posY = posY - 1;
+ }
+ if (tile.west != null && tile.west != tile.last && intArray[posX - 1, posY] == 0)
+ {
+ intArray[posX - 1, posY] = 2;
+ TileSpawnLocation component2 = Object.Instantiate(tileSpawnLocation, new Vector3(posX - 1 - 50, 0f, posY - 50) * 7f, Quaternion.identity).GetComponent<TileSpawnLocation>();
+ component2.eulerAngle = 270;
+ component2.posX = posX - 1;
+ component2.posY = posY;
+ }
+ if (tile.north != null && tile.north != tile.last && intArray[posX, posY + 1] == 0)
+ {
+ intArray[posX, posY + 1] = 2;
+ TileSpawnLocation component3 = Object.Instantiate(tileSpawnLocation, new Vector3(posX - 50, 0f, posY + 1 - 50) * 7f, Quaternion.identity).GetComponent<TileSpawnLocation>();
+ component3.eulerAngle = 0;
+ component3.posX = posX;
+ component3.posY = posY + 1;
+ }
+ if (tile.east != null && tile.east != tile.last && intArray[posX + 1, posY] == 0)
+ {
+ intArray[posX + 1, posY] = 2;
+ TileSpawnLocation component4 = Object.Instantiate(tileSpawnLocation, new Vector3(posX + 1 - 50, 0f, posY - 50) * 7f, Quaternion.identity).GetComponent<TileSpawnLocation>();
+ component4.eulerAngle = 90;
+ component4.posX = posX + 1;
+ component4.posY = posY;
+ }
+ }
+
+ public void SpawnNewTile(int posX, int posY, int eulerAngle)
+ {
+ bool flag = false;
+ bool flag2 = false;
+ bool flag3 = false;
+ bool flag4 = false;
+ if (intArray[posX, posY - 1] == 0)
+ {
+ flag = true;
+ }
+ if (intArray[posX - 1, posY] == 0)
+ {
+ flag2 = true;
+ }
+ if (intArray[posX, posY + 1] == 0)
+ {
+ flag3 = true;
+ }
+ if (intArray[posX + 1, posY] == 0)
+ {
+ flag4 = true;
+ }
+ bool flag5 = false;
+ bool flag6 = false;
+ bool flag7 = false;
+ switch (eulerAngle)
+ {
+ case 0:
+ flag5 = flag2;
+ flag6 = flag3;
+ flag7 = flag4;
+ break;
+ case 90:
+ flag5 = flag3;
+ flag6 = flag4;
+ flag7 = flag;
+ break;
+ case 180:
+ flag5 = flag4;
+ flag6 = flag;
+ flag7 = flag2;
+ break;
+ case 270:
+ flag5 = flag;
+ flag6 = flag2;
+ flag7 = flag3;
+ break;
+ default:
+ Debug.LogError("Trying to spawn a tile at an invalid eulerAngle" + eulerAngle);
+ break;
+ }
+ List<GameObject> list = new List<GameObject>();
+ int num = SpawnManager.instance.lastLevel - SpawnManager.instance.level;
+ int count = SpawnManager.instance.tileSpawnUis.Count;
+ bool flag8 = false;
+ bool flag9 = false;
+ if (count + 3 >= num || SpawnManager.instance.level < 3)
+ {
+ flag8 = true;
+ }
+ if (count == num)
+ {
+ flag9 = true;
+ }
+ if (!flag9)
+ {
+ if (flag5)
+ {
+ list.AddRange(Ltiles);
+ }
+ if (flag6)
+ {
+ list.AddRange(Ttiles);
+ }
+ if (flag7)
+ {
+ list.AddRange(Rtiles);
+ }
+ if (flag5 && flag6 && !flag8)
+ {
+ list.AddRange(LTtiles);
+ }
+ if (flag5 && flag7 && !flag8)
+ {
+ list.AddRange(LRtiles);
+ }
+ if (flag6 && flag7 && !flag8)
+ {
+ list.AddRange(TRtiles);
+ }
+ if (flag5 && flag6 && flag7 && !flag8)
+ {
+ list.AddRange(LTRtiles);
+ }
+ }
+ if (list.Count == 0)
+ {
+ list.AddRange(deadEndTiles);
+ }
+ GameObject original = list[Random.Range(0, list.Count)];
+ Vector3 vector = new Vector3(posX - 50, 0f, posY - 50) * 7f;
+ GameObject obj = Object.Instantiate(original, vector, Quaternion.identity);
+ obj.transform.eulerAngles = new Vector3(0f, eulerAngle, 0f);
+ intArray[posX, posY] = 1;
+ TerrainTile component = obj.GetComponent<TerrainTile>();
+ tileArray[posX, posY] = component;
+ component.SetCardinalDirections();
+ UpdateIntArrayFromTile(component, posX, posY);
+ TerrainTile terrainTile = null;
+ switch (eulerAngle)
+ {
+ case 0:
+ terrainTile = tileArray[posX, posY - 1];
+ break;
+ case 90:
+ terrainTile = tileArray[posX - 1, posY];
+ break;
+ case 180:
+ terrainTile = tileArray[posX, posY + 1];
+ break;
+ case 270:
+ terrainTile = tileArray[posX + 1, posY];
+ break;
+ default:
+ Debug.LogError("Trying to spawn a tile at an invalid eulerAngle" + eulerAngle);
+ break;
+ }
+ if (terrainTile == null)
+ {
+ Debug.LogError("Unable to find previous tile");
+ }
+ component.ConnectToTile(terrainTile);
+ if (tilePlacementFXObject != null)
+ {
+ Object.Instantiate(tilePlacementFXObject, vector + Vector3.up, Quaternion.identity).transform.localScale = Vector3.one * 7f;
+ }
+ }
+}
diff --git a/GameCode/TileSpawnLocation.cs b/GameCode/TileSpawnLocation.cs
new file mode 100644
index 0000000..e1575b1
--- /dev/null
+++ b/GameCode/TileSpawnLocation.cs
@@ -0,0 +1,27 @@
+using UnityEngine;
+
+public class TileSpawnLocation : MonoBehaviour
+{
+ public int eulerAngle;
+
+ public int posX;
+
+ public int posY;
+
+ private void Start()
+ {
+ SpawnManager.instance.tileSpawnUis.Add(base.gameObject);
+ if (SpawnManager.instance.combat)
+ {
+ base.gameObject.SetActive(value: false);
+ }
+ }
+
+ public void SpawnTile()
+ {
+ TileManager.instance.SpawnNewTile(posX, posY, eulerAngle);
+ SpawnManager.instance.tileSpawnUis.Remove(base.gameObject);
+ SpawnManager.instance.StartNextWave();
+ Object.Destroy(base.gameObject);
+ }
+}
diff --git a/GameCode/Tower.cs b/GameCode/Tower.cs
new file mode 100644
index 0000000..4098bd6
--- /dev/null
+++ b/GameCode/Tower.cs
@@ -0,0 +1,509 @@
+using UnityEngine;
+
+public class Tower : MonoBehaviour, IBuildable
+{
+ public enum Priority
+ {
+ Progress,
+ NearDeath,
+ MostHealth,
+ MostArmor,
+ MostShield,
+ LeastHealth,
+ LeastArmor,
+ LeastShield,
+ Fastest,
+ Slowest,
+ Marked
+ }
+
+ public TowerType towerType;
+
+ public bool squareUI;
+
+ [SerializeField]
+ protected GameObject towerUI;
+
+ public Priority[] priorities = new Priority[3];
+
+ [SerializeField]
+ protected GameObject turret;
+
+ [SerializeField]
+ protected bool towerVerticalyAims = true;
+
+ [SerializeField]
+ protected Transform muzzle;
+
+ [SerializeField]
+ protected GameObject projectile;
+
+ [SerializeField]
+ protected float projectileSpeed = 10f;
+
+ [SerializeField]
+ protected GameObject extraProjectileFX;
+
+ [SerializeField]
+ protected int baseDamage;
+
+ [SerializeField]
+ protected int baseHealthDamage;
+
+ [SerializeField]
+ protected int baseArmorDamage;
+
+ [SerializeField]
+ protected int baseShieldDamage;
+
+ public float rpm;
+
+ protected float rps;
+
+ [SerializeField]
+ protected float baseRange;
+
+ [SerializeField]
+ protected float baseSlowPercentage;
+
+ [SerializeField]
+ protected float baseBleedPercentage;
+
+ [SerializeField]
+ protected float baseBurnPercentage;
+
+ [SerializeField]
+ protected float basePoisonPercentage;
+
+ [SerializeField]
+ protected LayerMask enemyLayerMask;
+
+ [SerializeField]
+ protected LayerMask buildingLayerMask;
+
+ public bool consumesMana;
+
+ public float manaConsumptionRate;
+
+ public float finalManaConsumption;
+
+ public int upgradeCostMultiplier = 1;
+
+ private int damageUpgrade;
+
+ private int healthDamageUpgrade;
+
+ private int armorDamageUpgrade;
+
+ private int shieldDamageUpgrade;
+
+ private int heightBonus;
+
+ protected float timeSinceLastShot;
+
+ private float lastTargetUpdate = 1f;
+
+ protected GameObject currentTarget;
+
+ [SerializeField]
+ protected float baseBlastRadius;
+
+ public int level { get; private set; }
+
+ public float healthXP { get; private set; }
+
+ public float armorXP { get; private set; }
+
+ public float shieldXP { get; private set; }
+
+ public int damage { get; private set; }
+
+ public int healthDamage { get; private set; }
+
+ public int armorDamage { get; private set; }
+
+ public int shieldDamage { get; private set; }
+
+ public float range { get; private set; }
+
+ public float slowPercent { get; private set; }
+
+ public float bleedPercent { get; private set; }
+
+ public float burnPercent { get; private set; }
+
+ public float poisonPercent { get; private set; }
+
+ public float critChance { get; private set; }
+
+ public float stunChance { get; private set; }
+
+ public float blastRadius { get; private set; }
+
+ protected virtual void Start()
+ {
+ level = 1;
+ SetStats();
+ TowerManager.instance.AddNewTower(this, towerType);
+ DetectHouses();
+ priorities[0] = (priorities[1] = (priorities[2] = Priority.Progress));
+ }
+
+ public virtual void SetStats()
+ {
+ heightBonus = (int)Mathf.Round(base.transform.position.y * 3f - 1f);
+ damage = Mathf.Max(baseDamage + heightBonus + damageUpgrade + TowerManager.instance.GetBonusBaseDamage(towerType), 0);
+ healthDamage = baseHealthDamage + healthDamageUpgrade + TowerManager.instance.GetBonusHealthDamage(towerType);
+ armorDamage = baseArmorDamage + armorDamageUpgrade + TowerManager.instance.GetBonusArmorDamage(towerType);
+ shieldDamage = baseShieldDamage + shieldDamageUpgrade + TowerManager.instance.GetBonusShieldDamage(towerType);
+ rps = 60f / rpm;
+ range = baseRange + (float)heightBonus / 2f + TowerManager.instance.GetBonusRange(towerType);
+ slowPercent = baseSlowPercentage + TowerManager.instance.GetBonusSlow(towerType);
+ bleedPercent = baseBleedPercentage + TowerManager.instance.GetBonusBleed(towerType);
+ burnPercent = baseBurnPercentage + TowerManager.instance.GetBonusBurn(towerType);
+ poisonPercent = basePoisonPercentage + TowerManager.instance.GetBonusPoison(towerType);
+ blastRadius = baseBlastRadius + TowerManager.instance.GetBonusBlast(towerType);
+ finalManaConsumption = manaConsumptionRate + TowerManager.instance.GetManaConsumptionBonus(towerType);
+ if (TowerManager.instance.GetManaConsumptionBonus(towerType) > 0f)
+ {
+ consumesMana = true;
+ }
+ critChance = TowerManager.instance.GetCritChance(towerType) + TowerManager.instance.GetCritChanceLevelMultiplier(towerType) * (float)level / 100f;
+ stunChance = TowerManager.instance.GetStunChance(towerType);
+ }
+
+ private void FixedUpdate()
+ {
+ if (lastTargetUpdate <= 0f)
+ {
+ DetectEnemies();
+ }
+ else
+ {
+ lastTargetUpdate -= Time.fixedDeltaTime;
+ }
+ }
+
+ protected virtual void Update()
+ {
+ if (currentTarget != null)
+ {
+ if (turret != null)
+ {
+ AimTurret();
+ }
+ GainXP();
+ }
+ timeSinceLastShot += Time.deltaTime;
+ if (currentTarget != null && timeSinceLastShot > rps)
+ {
+ Fire();
+ timeSinceLastShot = 0f;
+ }
+ }
+
+ private void DetectHouses()
+ {
+ if (Physics.Raycast(base.transform.position + new Vector3(1f, 1f, 0f), -base.transform.up, out var hitInfo, 1f, buildingLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckForHouse(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(-1f, 1f, 0f), -base.transform.up, out hitInfo, 1f, buildingLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckForHouse(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(0f, 1f, 1f), -base.transform.up, out hitInfo, 1f, buildingLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckForHouse(hitInfo);
+ }
+ if (Physics.Raycast(base.transform.position + new Vector3(0f, 1f, -1f), -base.transform.up, out hitInfo, 1f, buildingLayerMask, QueryTriggerInteraction.Ignore))
+ {
+ CheckForHouse(hitInfo);
+ }
+ }
+
+ private void CheckForHouse(RaycastHit hit)
+ {
+ if (hit.collider.GetComponent<House>() != null && (double)Mathf.Abs(hit.collider.transform.position.y - base.transform.position.y) <= 0.001)
+ {
+ hit.collider.GetComponent<House>().AddDefender(this);
+ }
+ }
+
+ public void TogglePriority(int index, int direction)
+ {
+ priorities[index] = (Priority)(((int)(priorities[index] + direction) % 11 + 11) % 11);
+ }
+
+ protected virtual void Fire()
+ {
+ if (consumesMana)
+ {
+ int manaCost = (int)((float)damage * finalManaConsumption);
+ if (!ResourceManager.instance.CheckMana(manaCost))
+ {
+ return;
+ }
+ ResourceManager.instance.SpendMana(manaCost);
+ }
+ GameObject gameObject = Object.Instantiate(projectile, muzzle.position, muzzle.rotation);
+ gameObject.GetComponent<Projectile>().SetStats(towerType, currentTarget, projectileSpeed, damage, healthDamage, armorDamage, shieldDamage, slowPercent, bleedPercent, burnPercent, poisonPercent, critChance, stunChance);
+ if (extraProjectileFX != null)
+ {
+ GameObject gameObject2 = Object.Instantiate(extraProjectileFX, gameObject.transform.position, gameObject.transform.rotation);
+ gameObject2.transform.SetParent(gameObject.transform);
+ gameObject2.GetComponent<ProjectileFX>().SetFX(bleedPercent, burnPercent, poisonPercent, slowPercent, consumesMana);
+ Projectile component = gameObject.GetComponent<Projectile>();
+ if (component.detachOnDestruction == null)
+ {
+ component.detachOnDestruction = gameObject2;
+ component.extraFX = gameObject2.GetComponent<ProjectileFX>();
+ }
+ }
+ }
+
+ protected virtual void AimTurret()
+ {
+ Vector3 forward = currentTarget.transform.position - turret.transform.position;
+ if (!towerVerticalyAims)
+ {
+ forward.y = 0f;
+ }
+ Quaternion rotation = Quaternion.LookRotation(forward, Vector3.up);
+ turret.transform.rotation = rotation;
+ }
+
+ protected void GainXP()
+ {
+ Enemy component = currentTarget.GetComponent<Enemy>();
+ if (component != null)
+ {
+ if (component.shield > 0)
+ {
+ shieldXP += Time.deltaTime / (2f * range) + Time.deltaTime / 2f;
+ }
+ else if (component.armor > 0)
+ {
+ armorXP += Time.deltaTime / (2f * range) + Time.deltaTime / 2f;
+ }
+ else
+ {
+ healthXP += Time.deltaTime / (2f * range) + Time.deltaTime / 2f;
+ }
+ CheckXPAndLevelUp();
+ }
+ }
+
+ private void CheckXPAndLevelUp()
+ {
+ if (healthXP >= (float)(10 * level))
+ {
+ healthXP -= 10 * level;
+ level++;
+ damageUpgrade++;
+ healthDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ if (armorXP >= (float)(10 * level))
+ {
+ armorXP -= 10 * level;
+ level++;
+ damageUpgrade++;
+ armorDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ if (shieldXP >= (float)(10 * level))
+ {
+ shieldXP -= 10 * level;
+ level++;
+ damageUpgrade++;
+ shieldDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ }
+
+ private void LevelUpText()
+ {
+ Object.Instantiate(BuildingManager.instance.levelUpFX, base.transform.position, Quaternion.identity);
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("LEVEL UP!", "Grey", 1f);
+ component.SetHoldTime(1f);
+ AchievementManager.instance.TowerLevel(level);
+ }
+
+ public void BuyHealthLevel()
+ {
+ int num = (10 * level - (int)healthXP) * upgradeCostMultiplier;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ ResourceManager.instance.Spend(num);
+ DamageTracker.instance.AddCost(towerType, num);
+ healthXP -= 10 * level - num / upgradeCostMultiplier;
+ level++;
+ damageUpgrade++;
+ healthDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ }
+
+ public void BuyArmorLevel()
+ {
+ int num = (10 * level - (int)armorXP) * upgradeCostMultiplier;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ ResourceManager.instance.Spend(num);
+ DamageTracker.instance.AddCost(towerType, num);
+ armorXP -= 10 * level - num / upgradeCostMultiplier;
+ level++;
+ damageUpgrade++;
+ armorDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ }
+
+ public void BuyShieldLevel()
+ {
+ int num = (10 * level - (int)shieldXP) * upgradeCostMultiplier;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ ResourceManager.instance.Spend(num);
+ DamageTracker.instance.AddCost(towerType, num);
+ shieldXP -= 10 * level - num / upgradeCostMultiplier;
+ level++;
+ damageUpgrade++;
+ shieldDamageUpgrade++;
+ SetStats();
+ LevelUpText();
+ }
+ }
+
+ private void DetectEnemies()
+ {
+ Collider[] array = Physics.OverlapSphere(base.transform.position, range, enemyLayerMask, QueryTriggerInteraction.Collide);
+ if (array.Length != 0)
+ {
+ currentTarget = SelectEnemy(array);
+ }
+ else
+ {
+ currentTarget = null;
+ }
+ lastTargetUpdate = Random.Range(0.25f, 1f);
+ }
+
+ protected bool CheckPriority(Priority check)
+ {
+ for (int i = 0; i < priorities.Length; i++)
+ {
+ if (check == priorities[i])
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected int PriorityScale(Priority check)
+ {
+ for (int i = 0; i < priorities.Length; i++)
+ {
+ if (check == priorities[i])
+ {
+ return Mathf.Clamp(3 - i, 1, 3);
+ }
+ }
+ return 1;
+ }
+
+ protected virtual GameObject SelectEnemy(Collider[] possibleTargets)
+ {
+ GameObject result = null;
+ float num = -1f;
+ for (int i = 0; i < possibleTargets.Length; i++)
+ {
+ float num2 = 1f;
+ Enemy component = possibleTargets[i].GetComponent<Enemy>();
+ if (CheckPriority(Priority.Progress))
+ {
+ float f = Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().distanceFromEnd);
+ num2 /= Mathf.Pow(f, PriorityScale(Priority.Progress));
+ }
+ if (CheckPriority(Priority.NearDeath))
+ {
+ float f2 = Mathf.Max(0.001f, component.CurrentHealth());
+ num2 /= Mathf.Pow(f2, PriorityScale(Priority.NearDeath));
+ }
+ if (CheckPriority(Priority.MostHealth))
+ {
+ float f3 = (float)Mathf.Max(1, component.health) / 1000f;
+ num2 *= Mathf.Pow(f3, PriorityScale(Priority.MostHealth));
+ }
+ if (CheckPriority(Priority.MostArmor))
+ {
+ float f4 = (float)Mathf.Max(1, component.armor) / 1000f;
+ num2 *= Mathf.Pow(f4, PriorityScale(Priority.MostArmor));
+ }
+ if (CheckPriority(Priority.MostShield))
+ {
+ float f5 = (float)Mathf.Max(1, component.shield) / 1000f;
+ num2 *= Mathf.Pow(f5, PriorityScale(Priority.MostShield));
+ }
+ if (CheckPriority(Priority.LeastHealth))
+ {
+ float f6 = Mathf.Max(1, component.health);
+ num2 /= Mathf.Pow(f6, PriorityScale(Priority.LeastHealth));
+ }
+ if (CheckPriority(Priority.LeastArmor))
+ {
+ float f7 = Mathf.Max(1, component.armor);
+ num2 /= Mathf.Pow(f7, PriorityScale(Priority.LeastArmor));
+ }
+ if (CheckPriority(Priority.LeastShield))
+ {
+ float f8 = Mathf.Max(1, component.shield);
+ num2 /= Mathf.Pow(f8, PriorityScale(Priority.LeastShield));
+ }
+ if (CheckPriority(Priority.Fastest))
+ {
+ float f9 = Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().speed);
+ num2 *= Mathf.Pow(f9, PriorityScale(Priority.Fastest));
+ }
+ if (CheckPriority(Priority.Slowest))
+ {
+ float f10 = Mathf.Max(0.001f, possibleTargets[i].GetComponent<Pathfinder>().speed);
+ num2 /= Mathf.Pow(f10, PriorityScale(Priority.Slowest));
+ }
+ if (CheckPriority(Priority.Marked))
+ {
+ float f11 = 1f;
+ if (component.mark != null)
+ {
+ f11 = (float)(component.mark.damage * (component.mark.healthDamage + component.mark.armorDamage + component.mark.shieldDamage)) * (1f + component.mark.critChance);
+ }
+ num2 *= Mathf.Pow(f11, PriorityScale(Priority.Marked));
+ }
+ if (num2 > num)
+ {
+ result = component.gameObject;
+ num = num2;
+ }
+ }
+ return result;
+ }
+
+ public void SpawnUI()
+ {
+ Object.Instantiate(towerUI, base.transform.position, Quaternion.identity).GetComponent<TowerUI>().SetStats(this);
+ }
+
+ public virtual void Demolish()
+ {
+ TowerManager.instance.RemoveTower(this, towerType);
+ Object.Destroy(base.gameObject);
+ }
+}
diff --git a/GameCode/TowerFlyweight.cs b/GameCode/TowerFlyweight.cs
new file mode 100644
index 0000000..b63f8b4
--- /dev/null
+++ b/GameCode/TowerFlyweight.cs
@@ -0,0 +1,279 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class TowerFlyweight : MonoBehaviour
+{
+ private HashSet<Tower> towers = new HashSet<Tower>();
+
+ [SerializeField]
+ private BuildButtonUI myBuildButton;
+
+ [SerializeField]
+ private int basePrice;
+
+ [SerializeField]
+ public int priceIncrease;
+
+ [SerializeField]
+ public int currentPrice;
+
+ [SerializeField]
+ public int sellPrice;
+
+ [SerializeField]
+ public float bonusRange;
+
+ [SerializeField]
+ public int bonusBaseDamage;
+
+ [SerializeField]
+ public int bonusHealthDamage;
+
+ [SerializeField]
+ public int bonusArmorDamage;
+
+ [SerializeField]
+ public int bonusShieldDamage;
+
+ [SerializeField]
+ public float bonusSlow;
+
+ [SerializeField]
+ public float bonusBleed;
+
+ [SerializeField]
+ public float bonusBurn;
+
+ [SerializeField]
+ public float bonusPoison;
+
+ [SerializeField]
+ public float bonusBlast;
+
+ [SerializeField]
+ public float critChance;
+
+ [SerializeField]
+ public float critChanceLevelMultiplier;
+
+ [SerializeField]
+ public float stunChance;
+
+ [SerializeField]
+ public float manaConsumptionAddition;
+
+ [SerializeField]
+ private TowerType towerTypeForDamageTracker;
+
+ public void AddNewTower(Tower newTower)
+ {
+ towers.Add(newTower);
+ currentPrice = basePrice + towers.Count * priceIncrease;
+ sellPrice = basePrice + (towers.Count - 1) * priceIncrease;
+ myBuildButton.UpdatePriceText();
+ }
+
+ public void RemoveTower(Tower newTower)
+ {
+ towers.Remove(newTower);
+ currentPrice = basePrice + towers.Count * priceIncrease;
+ sellPrice = basePrice + (towers.Count - 1) * priceIncrease;
+ myBuildButton.UpdatePriceText();
+ ResourceManager.instance.AddMoney(basePrice + towers.Count * priceIncrease);
+ DamageTracker.instance.AddCost(towerTypeForDamageTracker, -(basePrice + towers.Count * priceIncrease));
+ }
+
+ public void UpdateTowerStats()
+ {
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddManaAddition(float bonus)
+ {
+ manaConsumptionAddition += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddStunChance(float bonus)
+ {
+ stunChance += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddCritChance(float bonus)
+ {
+ critChance += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddCritChanceLevelMultiplier(float bonus)
+ {
+ critChanceLevelMultiplier += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusRange(float bonus)
+ {
+ bonusRange += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusBaseDamage(int bonus)
+ {
+ bonusBaseDamage += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusHealthDamage(int bonus)
+ {
+ bonusHealthDamage += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusArmorDamage(int bonus)
+ {
+ bonusArmorDamage += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusShieldDamage(int bonus)
+ {
+ bonusShieldDamage += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusSlow(float bonus)
+ {
+ bonusSlow += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusBleed(float bonus)
+ {
+ bonusBleed += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusBurn(float bonus)
+ {
+ bonusBurn += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusPoison(float bonus)
+ {
+ bonusPoison += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+
+ public void AddBonusBlast(float bonus)
+ {
+ bonusBlast += bonus;
+ foreach (Tower tower in towers)
+ {
+ tower.SetStats();
+ }
+ if (myBuildButton != null)
+ {
+ myBuildButton.UpdateModifiersText();
+ }
+ }
+}
diff --git a/GameCode/TowerManager.cs b/GameCode/TowerManager.cs
new file mode 100644
index 0000000..abdca52
--- /dev/null
+++ b/GameCode/TowerManager.cs
@@ -0,0 +1,1386 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class TowerManager : MonoBehaviour
+{
+ public static TowerManager instance;
+
+ [SerializeField]
+ private TowerFlyweight global;
+
+ [SerializeField]
+ private TowerFlyweight crossbow;
+
+ [SerializeField]
+ private TowerFlyweight morter;
+
+ [SerializeField]
+ private TowerFlyweight teslaCoil;
+
+ [SerializeField]
+ private TowerFlyweight flameThrower;
+
+ [SerializeField]
+ private TowerFlyweight poisonSprayer;
+
+ [SerializeField]
+ private TowerFlyweight frostKeep;
+
+ [SerializeField]
+ private TowerFlyweight radar;
+
+ [SerializeField]
+ private TowerFlyweight obelisk;
+
+ [SerializeField]
+ private TowerFlyweight particleCannon;
+
+ [SerializeField]
+ private TowerFlyweight shredder;
+
+ [SerializeField]
+ private TowerFlyweight encampment;
+
+ [SerializeField]
+ private TowerFlyweight lookout;
+
+ [SerializeField]
+ private TowerFlyweight siphon;
+
+ public HashSet<TowerType> towersUsed = new HashSet<TowerType>();
+
+ private bool usedAllTowers;
+
+ public float obeliskTimeOnTargetMultiplier;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ public void AddNewTower(Tower t, TowerType type)
+ {
+ if (type != TowerType.Siphon)
+ {
+ towersUsed.Add(type);
+ }
+ if (!usedAllTowers)
+ {
+ usedAllTowers = AchievementManager.instance.CheckAllTowers();
+ }
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddNewTower(t);
+ break;
+ case TowerType.Morter:
+ morter.AddNewTower(t);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddNewTower(t);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddNewTower(t);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddNewTower(t);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddNewTower(t);
+ break;
+ case TowerType.Radar:
+ radar.AddNewTower(t);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddNewTower(t);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddNewTower(t);
+ break;
+ case TowerType.Shredder:
+ shredder.AddNewTower(t);
+ break;
+ case TowerType.Encampment:
+ encampment.AddNewTower(t);
+ break;
+ case TowerType.Lookout:
+ lookout.AddNewTower(t);
+ break;
+ default:
+ Debug.LogError("Failed to add tower to flyweight");
+ break;
+ }
+ }
+
+ public void RemoveTower(Tower t, TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.RemoveTower(t);
+ break;
+ case TowerType.Morter:
+ morter.RemoveTower(t);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.RemoveTower(t);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.RemoveTower(t);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.RemoveTower(t);
+ break;
+ case TowerType.Frost:
+ frostKeep.RemoveTower(t);
+ break;
+ case TowerType.Obelisk:
+ obelisk.RemoveTower(t);
+ break;
+ case TowerType.Radar:
+ radar.RemoveTower(t);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.RemoveTower(t);
+ break;
+ case TowerType.Shredder:
+ shredder.RemoveTower(t);
+ break;
+ case TowerType.Encampment:
+ encampment.RemoveTower(t);
+ break;
+ case TowerType.Lookout:
+ lookout.RemoveTower(t);
+ break;
+ default:
+ Debug.LogError("Failed to add tower to flyweight");
+ break;
+ }
+ }
+
+ public void UpdateAllTowers()
+ {
+ crossbow.UpdateTowerStats();
+ morter.UpdateTowerStats();
+ teslaCoil.UpdateTowerStats();
+ flameThrower.UpdateTowerStats();
+ poisonSprayer.UpdateTowerStats();
+ frostKeep.UpdateTowerStats();
+ radar.UpdateTowerStats();
+ obelisk.UpdateTowerStats();
+ particleCannon.UpdateTowerStats();
+ shredder.UpdateTowerStats();
+ encampment.UpdateTowerStats();
+ lookout.UpdateTowerStats();
+ }
+
+ public int GetSellPrice(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return crossbow.sellPrice;
+ case TowerType.Morter:
+ return morter.sellPrice;
+ case TowerType.TeslaCoil:
+ return teslaCoil.sellPrice;
+ case TowerType.FlameThrower:
+ return flameThrower.sellPrice;
+ case TowerType.PoisonSprayer:
+ return poisonSprayer.sellPrice;
+ case TowerType.Frost:
+ return frostKeep.sellPrice;
+ case TowerType.Obelisk:
+ return obelisk.sellPrice;
+ case TowerType.Radar:
+ return radar.sellPrice;
+ case TowerType.ParticleCannon:
+ return particleCannon.sellPrice;
+ case TowerType.Shredder:
+ return shredder.sellPrice;
+ case TowerType.Encampment:
+ return encampment.sellPrice;
+ case TowerType.Lookout:
+ return lookout.sellPrice;
+ default:
+ Debug.LogError("Failed to add tower to flyweight");
+ return -1;
+ }
+ }
+
+ public float GetManaConsumptionBonus(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.manaConsumptionAddition + crossbow.manaConsumptionAddition;
+ case TowerType.Morter:
+ return global.manaConsumptionAddition + morter.manaConsumptionAddition;
+ case TowerType.TeslaCoil:
+ return global.manaConsumptionAddition + teslaCoil.manaConsumptionAddition;
+ case TowerType.FlameThrower:
+ return global.manaConsumptionAddition + flameThrower.manaConsumptionAddition;
+ case TowerType.PoisonSprayer:
+ return global.manaConsumptionAddition + poisonSprayer.manaConsumptionAddition;
+ case TowerType.Frost:
+ return global.manaConsumptionAddition + frostKeep.manaConsumptionAddition;
+ case TowerType.Radar:
+ return global.manaConsumptionAddition + radar.manaConsumptionAddition;
+ case TowerType.Obelisk:
+ return global.manaConsumptionAddition + obelisk.manaConsumptionAddition;
+ case TowerType.ParticleCannon:
+ return global.manaConsumptionAddition + particleCannon.manaConsumptionAddition;
+ case TowerType.Shredder:
+ return global.manaConsumptionAddition + shredder.manaConsumptionAddition;
+ case TowerType.Encampment:
+ return global.manaConsumptionAddition + encampment.manaConsumptionAddition;
+ case TowerType.Lookout:
+ return global.manaConsumptionAddition + lookout.manaConsumptionAddition;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetCritChance(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.critChance + crossbow.critChance;
+ case TowerType.Morter:
+ return global.critChance + morter.critChance;
+ case TowerType.TeslaCoil:
+ return global.critChance + teslaCoil.critChance;
+ case TowerType.FlameThrower:
+ return global.critChance + flameThrower.critChance;
+ case TowerType.PoisonSprayer:
+ return global.critChance + poisonSprayer.critChance;
+ case TowerType.Frost:
+ return global.critChance + frostKeep.critChance;
+ case TowerType.Radar:
+ return global.critChance + radar.critChance;
+ case TowerType.Obelisk:
+ return global.critChance + obelisk.critChance;
+ case TowerType.ParticleCannon:
+ return global.critChance + particleCannon.critChance;
+ case TowerType.Shredder:
+ return global.critChance + shredder.critChance;
+ case TowerType.Encampment:
+ return global.critChance + encampment.critChance;
+ case TowerType.Lookout:
+ return global.critChance + lookout.critChance;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetStunChance(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.stunChance + crossbow.stunChance;
+ case TowerType.Morter:
+ return global.stunChance + morter.stunChance;
+ case TowerType.TeslaCoil:
+ return global.stunChance + teslaCoil.stunChance;
+ case TowerType.FlameThrower:
+ return global.stunChance + flameThrower.stunChance;
+ case TowerType.PoisonSprayer:
+ return global.stunChance + poisonSprayer.stunChance;
+ case TowerType.Frost:
+ return global.stunChance + frostKeep.stunChance;
+ case TowerType.Radar:
+ return global.stunChance + radar.stunChance;
+ case TowerType.Obelisk:
+ return global.stunChance + obelisk.stunChance;
+ case TowerType.ParticleCannon:
+ return global.stunChance + particleCannon.stunChance;
+ case TowerType.Shredder:
+ return global.stunChance + shredder.stunChance;
+ case TowerType.Encampment:
+ return global.stunChance + encampment.stunChance;
+ case TowerType.Lookout:
+ return global.stunChance + lookout.stunChance;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetCritChanceLevelMultiplier(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.critChanceLevelMultiplier + crossbow.critChanceLevelMultiplier;
+ case TowerType.Morter:
+ return global.critChanceLevelMultiplier + morter.critChanceLevelMultiplier;
+ case TowerType.TeslaCoil:
+ return global.critChanceLevelMultiplier + teslaCoil.critChanceLevelMultiplier;
+ case TowerType.FlameThrower:
+ return global.critChanceLevelMultiplier + flameThrower.critChanceLevelMultiplier;
+ case TowerType.PoisonSprayer:
+ return global.critChanceLevelMultiplier + poisonSprayer.critChanceLevelMultiplier;
+ case TowerType.Frost:
+ return global.critChanceLevelMultiplier + frostKeep.critChanceLevelMultiplier;
+ case TowerType.Radar:
+ return global.critChanceLevelMultiplier + radar.critChanceLevelMultiplier;
+ case TowerType.Obelisk:
+ return global.critChanceLevelMultiplier + obelisk.critChanceLevelMultiplier;
+ case TowerType.ParticleCannon:
+ return global.critChanceLevelMultiplier + particleCannon.critChanceLevelMultiplier;
+ case TowerType.Shredder:
+ return global.critChanceLevelMultiplier + shredder.critChanceLevelMultiplier;
+ case TowerType.Encampment:
+ return global.critChanceLevelMultiplier + encampment.critChanceLevelMultiplier;
+ case TowerType.Lookout:
+ return global.critChanceLevelMultiplier + lookout.critChanceLevelMultiplier;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetBonusRange(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusRange + crossbow.bonusRange;
+ case TowerType.Morter:
+ return global.bonusRange + morter.bonusRange;
+ case TowerType.TeslaCoil:
+ return global.bonusRange + teslaCoil.bonusRange;
+ case TowerType.FlameThrower:
+ return global.bonusRange + flameThrower.bonusRange;
+ case TowerType.PoisonSprayer:
+ return global.bonusRange + poisonSprayer.bonusRange;
+ case TowerType.Frost:
+ return global.bonusRange + frostKeep.bonusRange;
+ case TowerType.Radar:
+ return global.bonusRange + radar.bonusRange;
+ case TowerType.Obelisk:
+ return global.bonusRange + obelisk.bonusRange;
+ case TowerType.ParticleCannon:
+ return global.bonusRange + particleCannon.bonusRange;
+ case TowerType.Shredder:
+ return global.bonusRange + shredder.bonusRange;
+ case TowerType.Encampment:
+ return global.bonusRange + encampment.bonusRange;
+ case TowerType.Lookout:
+ return global.bonusRange + lookout.bonusRange;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public int GetBonusBaseDamage(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusBaseDamage + crossbow.bonusBaseDamage;
+ case TowerType.Morter:
+ return global.bonusBaseDamage + morter.bonusBaseDamage;
+ case TowerType.TeslaCoil:
+ return global.bonusBaseDamage + teslaCoil.bonusBaseDamage;
+ case TowerType.FlameThrower:
+ return global.bonusBaseDamage + flameThrower.bonusBaseDamage;
+ case TowerType.PoisonSprayer:
+ return global.bonusBaseDamage + poisonSprayer.bonusBaseDamage;
+ case TowerType.Frost:
+ return global.bonusBaseDamage + frostKeep.bonusBaseDamage;
+ case TowerType.Radar:
+ return global.bonusBaseDamage + radar.bonusBaseDamage;
+ case TowerType.Obelisk:
+ return global.bonusBaseDamage + obelisk.bonusBaseDamage;
+ case TowerType.ParticleCannon:
+ return global.bonusBaseDamage + particleCannon.bonusBaseDamage;
+ case TowerType.Shredder:
+ return global.bonusBaseDamage + shredder.bonusBaseDamage;
+ case TowerType.Encampment:
+ return global.bonusBaseDamage + encampment.bonusBaseDamage;
+ case TowerType.Lookout:
+ return global.bonusBaseDamage + lookout.bonusBaseDamage;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0;
+ }
+ }
+
+ public int GetBonusHealthDamage(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusHealthDamage + crossbow.bonusHealthDamage;
+ case TowerType.Morter:
+ return global.bonusHealthDamage + morter.bonusHealthDamage;
+ case TowerType.TeslaCoil:
+ return global.bonusHealthDamage + teslaCoil.bonusHealthDamage;
+ case TowerType.FlameThrower:
+ return global.bonusHealthDamage + flameThrower.bonusHealthDamage;
+ case TowerType.PoisonSprayer:
+ return global.bonusHealthDamage + poisonSprayer.bonusHealthDamage;
+ case TowerType.Frost:
+ return global.bonusHealthDamage + frostKeep.bonusHealthDamage;
+ case TowerType.Radar:
+ return global.bonusHealthDamage + radar.bonusHealthDamage;
+ case TowerType.Obelisk:
+ return global.bonusHealthDamage + obelisk.bonusHealthDamage;
+ case TowerType.ParticleCannon:
+ return global.bonusHealthDamage + particleCannon.bonusHealthDamage;
+ case TowerType.Shredder:
+ return global.bonusHealthDamage + shredder.bonusHealthDamage;
+ case TowerType.Encampment:
+ return global.bonusHealthDamage + encampment.bonusHealthDamage;
+ case TowerType.Lookout:
+ return global.bonusHealthDamage + lookout.bonusHealthDamage;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0;
+ }
+ }
+
+ public int GetBonusArmorDamage(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusArmorDamage + crossbow.bonusArmorDamage;
+ case TowerType.Morter:
+ return global.bonusArmorDamage + morter.bonusArmorDamage;
+ case TowerType.TeslaCoil:
+ return global.bonusArmorDamage + teslaCoil.bonusArmorDamage;
+ case TowerType.FlameThrower:
+ return global.bonusArmorDamage + flameThrower.bonusArmorDamage;
+ case TowerType.PoisonSprayer:
+ return global.bonusArmorDamage + poisonSprayer.bonusArmorDamage;
+ case TowerType.Frost:
+ return global.bonusArmorDamage + frostKeep.bonusArmorDamage;
+ case TowerType.Radar:
+ return global.bonusArmorDamage + radar.bonusArmorDamage;
+ case TowerType.Obelisk:
+ return global.bonusArmorDamage + obelisk.bonusArmorDamage;
+ case TowerType.ParticleCannon:
+ return global.bonusArmorDamage + particleCannon.bonusArmorDamage;
+ case TowerType.Shredder:
+ return global.bonusArmorDamage + shredder.bonusArmorDamage;
+ case TowerType.Encampment:
+ return global.bonusArmorDamage + encampment.bonusArmorDamage;
+ case TowerType.Lookout:
+ return global.bonusArmorDamage + lookout.bonusArmorDamage;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0;
+ }
+ }
+
+ public int GetBonusShieldDamage(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusShieldDamage + crossbow.bonusShieldDamage;
+ case TowerType.Morter:
+ return global.bonusShieldDamage + morter.bonusShieldDamage;
+ case TowerType.TeslaCoil:
+ return global.bonusShieldDamage + teslaCoil.bonusShieldDamage;
+ case TowerType.FlameThrower:
+ return global.bonusShieldDamage + flameThrower.bonusShieldDamage;
+ case TowerType.PoisonSprayer:
+ return global.bonusShieldDamage + poisonSprayer.bonusShieldDamage;
+ case TowerType.Frost:
+ return global.bonusShieldDamage + frostKeep.bonusShieldDamage;
+ case TowerType.Radar:
+ return global.bonusShieldDamage + radar.bonusShieldDamage;
+ case TowerType.Obelisk:
+ return global.bonusShieldDamage + obelisk.bonusShieldDamage;
+ case TowerType.ParticleCannon:
+ return global.bonusShieldDamage + particleCannon.bonusShieldDamage;
+ case TowerType.Shredder:
+ return global.bonusShieldDamage + shredder.bonusShieldDamage;
+ case TowerType.Encampment:
+ return global.bonusShieldDamage + encampment.bonusShieldDamage;
+ case TowerType.Lookout:
+ return global.bonusShieldDamage + lookout.bonusShieldDamage;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0;
+ }
+ }
+
+ public float GetBonusSlow(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusSlow + crossbow.bonusSlow;
+ case TowerType.Morter:
+ return global.bonusSlow + morter.bonusSlow;
+ case TowerType.TeslaCoil:
+ return global.bonusSlow + teslaCoil.bonusSlow;
+ case TowerType.FlameThrower:
+ return global.bonusSlow + flameThrower.bonusSlow;
+ case TowerType.PoisonSprayer:
+ return global.bonusSlow + poisonSprayer.bonusSlow;
+ case TowerType.Frost:
+ return global.bonusSlow + frostKeep.bonusSlow;
+ case TowerType.Radar:
+ return global.bonusSlow + radar.bonusSlow;
+ case TowerType.Obelisk:
+ return global.bonusSlow + obelisk.bonusSlow;
+ case TowerType.ParticleCannon:
+ return global.bonusSlow + particleCannon.bonusSlow;
+ case TowerType.Shredder:
+ return global.bonusSlow + shredder.bonusSlow;
+ case TowerType.Encampment:
+ return global.bonusSlow + encampment.bonusSlow;
+ case TowerType.Lookout:
+ return global.bonusSlow + lookout.bonusSlow;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetBonusBleed(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusBleed + crossbow.bonusBleed;
+ case TowerType.Morter:
+ return global.bonusBleed + morter.bonusBleed;
+ case TowerType.TeslaCoil:
+ return global.bonusBleed + teslaCoil.bonusBleed;
+ case TowerType.FlameThrower:
+ return global.bonusBleed + flameThrower.bonusBleed;
+ case TowerType.PoisonSprayer:
+ return global.bonusBleed + poisonSprayer.bonusBleed;
+ case TowerType.Frost:
+ return global.bonusBleed + frostKeep.bonusBleed;
+ case TowerType.Radar:
+ return global.bonusBleed + radar.bonusBleed;
+ case TowerType.Obelisk:
+ return global.bonusBleed + obelisk.bonusBleed;
+ case TowerType.ParticleCannon:
+ return global.bonusBleed + particleCannon.bonusBleed;
+ case TowerType.Shredder:
+ return global.bonusBleed + shredder.bonusBleed;
+ case TowerType.Encampment:
+ return global.bonusBleed + encampment.bonusBleed;
+ case TowerType.Lookout:
+ return global.bonusBleed + lookout.bonusBleed;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetBonusBurn(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusBurn + crossbow.bonusBurn;
+ case TowerType.Morter:
+ return global.bonusBurn + morter.bonusBurn;
+ case TowerType.TeslaCoil:
+ return global.bonusBurn + teslaCoil.bonusBurn;
+ case TowerType.FlameThrower:
+ return global.bonusBurn + flameThrower.bonusBurn;
+ case TowerType.PoisonSprayer:
+ return global.bonusBurn + poisonSprayer.bonusBurn;
+ case TowerType.Frost:
+ return global.bonusBurn + frostKeep.bonusBurn;
+ case TowerType.Radar:
+ return global.bonusBurn + radar.bonusBurn;
+ case TowerType.Obelisk:
+ return global.bonusBurn + obelisk.bonusBurn;
+ case TowerType.ParticleCannon:
+ return global.bonusBurn + particleCannon.bonusBurn;
+ case TowerType.Shredder:
+ return global.bonusBurn + shredder.bonusBurn;
+ case TowerType.Encampment:
+ return global.bonusBurn + encampment.bonusBurn;
+ case TowerType.Lookout:
+ return global.bonusBurn + lookout.bonusBurn;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetBonusPoison(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusPoison + crossbow.bonusPoison;
+ case TowerType.Morter:
+ return global.bonusPoison + morter.bonusPoison;
+ case TowerType.TeslaCoil:
+ return global.bonusPoison + teslaCoil.bonusPoison;
+ case TowerType.FlameThrower:
+ return global.bonusPoison + flameThrower.bonusPoison;
+ case TowerType.PoisonSprayer:
+ return global.bonusPoison + poisonSprayer.bonusPoison;
+ case TowerType.Frost:
+ return global.bonusPoison + frostKeep.bonusPoison;
+ case TowerType.Radar:
+ return global.bonusPoison + radar.bonusPoison;
+ case TowerType.Obelisk:
+ return global.bonusPoison + obelisk.bonusPoison;
+ case TowerType.ParticleCannon:
+ return global.bonusPoison + particleCannon.bonusPoison;
+ case TowerType.Shredder:
+ return global.bonusPoison + shredder.bonusPoison;
+ case TowerType.Encampment:
+ return global.bonusPoison + encampment.bonusPoison;
+ case TowerType.Lookout:
+ return global.bonusPoison + lookout.bonusPoison;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public float GetBonusBlast(TowerType type)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ return global.bonusBlast + crossbow.bonusBlast;
+ case TowerType.Morter:
+ return global.bonusBlast + morter.bonusBlast;
+ case TowerType.TeslaCoil:
+ return global.bonusBlast + teslaCoil.bonusBlast;
+ case TowerType.FlameThrower:
+ return global.bonusBlast + flameThrower.bonusBlast;
+ case TowerType.PoisonSprayer:
+ return global.bonusBlast + poisonSprayer.bonusBlast;
+ case TowerType.Frost:
+ return global.bonusBlast + frostKeep.bonusBlast;
+ case TowerType.Radar:
+ return global.bonusBlast + radar.bonusBlast;
+ case TowerType.Obelisk:
+ return global.bonusBlast + obelisk.bonusBlast;
+ case TowerType.ParticleCannon:
+ return global.bonusBlast + particleCannon.bonusBlast;
+ case TowerType.Shredder:
+ return global.bonusBlast + shredder.bonusBlast;
+ case TowerType.Encampment:
+ return global.bonusBlast + encampment.bonusBlast;
+ case TowerType.Lookout:
+ return global.bonusBlast + lookout.bonusBlast;
+ default:
+ Debug.Log("Failed to get global bonus");
+ return 0f;
+ }
+ }
+
+ public void AddManaConsumption(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddManaAddition(value);
+ break;
+ case TowerType.Morter:
+ morter.AddManaAddition(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddManaAddition(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddManaAddition(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddManaAddition(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddManaAddition(value);
+ break;
+ case TowerType.Radar:
+ radar.AddManaAddition(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddManaAddition(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddManaAddition(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddManaAddition(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddManaAddition(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddManaAddition(value);
+ break;
+ case TowerType.Global:
+ global.AddManaAddition(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddCritChance(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddCritChance(value);
+ break;
+ case TowerType.Morter:
+ morter.AddCritChance(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddCritChance(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddCritChance(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddCritChance(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddCritChance(value);
+ break;
+ case TowerType.Radar:
+ radar.AddCritChance(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddCritChance(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddCritChance(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddCritChance(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddCritChance(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddCritChance(value);
+ break;
+ case TowerType.Global:
+ global.AddCritChance(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddStunChance(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddStunChance(value);
+ break;
+ case TowerType.Morter:
+ morter.AddStunChance(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddStunChance(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddStunChance(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddStunChance(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddStunChance(value);
+ break;
+ case TowerType.Radar:
+ radar.AddStunChance(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddStunChance(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddStunChance(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddStunChance(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddStunChance(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddStunChance(value);
+ break;
+ case TowerType.Global:
+ global.AddStunChance(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddCritChanceLevelMultiplier(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Morter:
+ morter.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Radar:
+ radar.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddCritChanceLevelMultiplier(value);
+ break;
+ case TowerType.Global:
+ global.AddCritChanceLevelMultiplier(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusRange(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusRange(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusRange(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusRange(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusRange(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusRange(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusRange(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusRange(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusRange(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusRange(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusRange(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusRange(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusRange(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusRange(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusBaseDamage(TowerType type, int value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusBaseDamage(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusBaseDamage(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusBaseDamage(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusBaseDamage(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusBaseDamage(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusBaseDamage(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusHealthDamage(TowerType type, int value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusHealthDamage(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusHealthDamage(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusHealthDamage(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusHealthDamage(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusHealthDamage(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusHealthDamage(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusArmorDamage(TowerType type, int value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusArmorDamage(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusArmorDamage(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusArmorDamage(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusArmorDamage(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusArmorDamage(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusArmorDamage(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusShieldDamage(TowerType type, int value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusShieldDamage(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusShieldDamage(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusShieldDamage(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusShieldDamage(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusShieldDamage(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusShieldDamage(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusSlow(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusSlow(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusSlow(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusSlow(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusSlow(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusSlow(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusSlow(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusSlow(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusSlow(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusSlow(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusSlow(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusSlow(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusSlow(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusSlow(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusBleed(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusBleed(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusBleed(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusBleed(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusBleed(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusBleed(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusBleed(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusBleed(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusBleed(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusBleed(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusBleed(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusBleed(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusBleed(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusBleed(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusBurn(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusBurn(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusBurn(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusBurn(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusBurn(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusBurn(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusBurn(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusBurn(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusBurn(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusBurn(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusBurn(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusBurn(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusBurn(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusBurn(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusPoison(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusPoison(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusPoison(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusPoison(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusPoison(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusPoison(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusPoison(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusPoison(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusPoison(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusPoison(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusPoison(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusPoison(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusPoison(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusPoison(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+
+ public void AddBonusBlast(TowerType type, float value)
+ {
+ switch (type)
+ {
+ case TowerType.Crossbow:
+ crossbow.AddBonusBlast(value);
+ break;
+ case TowerType.Morter:
+ morter.AddBonusBlast(value);
+ break;
+ case TowerType.TeslaCoil:
+ teslaCoil.AddBonusBlast(value);
+ break;
+ case TowerType.FlameThrower:
+ flameThrower.AddBonusBlast(value);
+ break;
+ case TowerType.PoisonSprayer:
+ poisonSprayer.AddBonusBlast(value);
+ break;
+ case TowerType.Frost:
+ frostKeep.AddBonusBlast(value);
+ break;
+ case TowerType.Radar:
+ radar.AddBonusBlast(value);
+ break;
+ case TowerType.Obelisk:
+ obelisk.AddBonusBlast(value);
+ break;
+ case TowerType.ParticleCannon:
+ particleCannon.AddBonusBlast(value);
+ break;
+ case TowerType.Shredder:
+ shredder.AddBonusBlast(value);
+ break;
+ case TowerType.Encampment:
+ encampment.AddBonusBlast(value);
+ break;
+ case TowerType.Lookout:
+ lookout.AddBonusBlast(value);
+ break;
+ case TowerType.Global:
+ global.AddBonusBlast(value);
+ UpdateAllTowers();
+ break;
+ default:
+ Debug.Log("Failed to set global bonus");
+ break;
+ }
+ }
+}
diff --git a/GameCode/TowerType.cs b/GameCode/TowerType.cs
new file mode 100644
index 0000000..9bbc242
--- /dev/null
+++ b/GameCode/TowerType.cs
@@ -0,0 +1,18 @@
+public enum TowerType
+{
+ Crossbow,
+ Morter,
+ TeslaCoil,
+ Frost,
+ Obelisk,
+ FlameThrower,
+ PoisonSprayer,
+ Siphon,
+ Radar,
+ ParticleCannon,
+ DOT,
+ Global,
+ Shredder,
+ Encampment,
+ Lookout
+}
diff --git a/GameCode/TowerUI.cs b/GameCode/TowerUI.cs
new file mode 100644
index 0000000..7b49139
--- /dev/null
+++ b/GameCode/TowerUI.cs
@@ -0,0 +1,289 @@
+using System;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class TowerUI : MonoBehaviour
+{
+ private Tower myTower;
+
+ private int level;
+
+ private int baseDamage;
+
+ private int healthDamage;
+
+ private int armorDamage;
+
+ private int shieldDamage;
+
+ private float healthXP;
+
+ private float armorXP;
+
+ private float shieldXP;
+
+ private bool usesMana;
+
+ private float range;
+
+ private float manaUseMultiplier;
+
+ private int rpm;
+
+ [SerializeField]
+ private Image healthXPImg;
+
+ [SerializeField]
+ private Image armorXPImg;
+
+ [SerializeField]
+ private Image shieldXPImg;
+
+ [SerializeField]
+ private Text levelText;
+
+ [SerializeField]
+ private Text baseDamageText;
+
+ [SerializeField]
+ private Text healthDamageText;
+
+ [SerializeField]
+ private Text armorDamageText;
+
+ [SerializeField]
+ private Text shieldDamageText;
+
+ [SerializeField]
+ private Text healthXPText;
+
+ [SerializeField]
+ private Text armorXPText;
+
+ [SerializeField]
+ private Text shieldXPText;
+
+ [SerializeField]
+ private Text[] priorityTexts;
+
+ [SerializeField]
+ private Text slowText;
+
+ [SerializeField]
+ private Text bleedText;
+
+ [SerializeField]
+ private Text burnText;
+
+ [SerializeField]
+ private Text poisonText;
+
+ [SerializeField]
+ private Text critText;
+
+ [SerializeField]
+ private Text rangeText;
+
+ [SerializeField]
+ private Text rpmText;
+
+ [SerializeField]
+ private Text manaUseText;
+
+ [SerializeField]
+ private Text demolishText;
+
+ private float timer = 0.5f;
+
+ [SerializeField]
+ private LineRenderer line;
+
+ private void Start()
+ {
+ UIManager.instance.SetNewUI(base.gameObject);
+ }
+
+ private void Update()
+ {
+ if (timer < 0f)
+ {
+ SetStats(myTower);
+ timer = 0.5f;
+ }
+ timer -= Time.deltaTime;
+ if (Input.GetKeyDown(KeyCode.Alpha1) || Input.GetKeyDown(KeyCode.Keypad1))
+ {
+ BuyHealthLevel();
+ }
+ if (Input.GetKeyDown(KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2))
+ {
+ BuyArmorLevel();
+ }
+ if (Input.GetKeyDown(KeyCode.Alpha3) || Input.GetKeyDown(KeyCode.Keypad3))
+ {
+ BuyShieldLevel();
+ }
+ }
+
+ public void SetStats(Tower _myTower)
+ {
+ myTower = _myTower;
+ level = myTower.level;
+ baseDamage = myTower.damage;
+ healthDamage = myTower.healthDamage;
+ armorDamage = myTower.armorDamage;
+ shieldDamage = myTower.shieldDamage;
+ healthXP = myTower.healthXP;
+ armorXP = myTower.armorXP;
+ shieldXP = myTower.shieldXP;
+ slowText.text = (int)(myTower.slowPercent * 100f) + "%";
+ bleedText.text = (int)(myTower.bleedPercent * 100f) + "%";
+ burnText.text = (int)(myTower.burnPercent * 100f) + "%";
+ poisonText.text = (int)(myTower.poisonPercent * 100f) + "%";
+ critText.text = CritText();
+ if (myTower.range != range)
+ {
+ DrawCircle();
+ }
+ range = myTower.range;
+ rpm = (int)myTower.rpm;
+ usesMana = myTower.consumesMana;
+ manaUseMultiplier = myTower.finalManaConsumption;
+ UpdateText();
+ for (int i = 0; i < priorityTexts.Length; i++)
+ {
+ priorityTexts[i].text = myTower.priorities[i].ToString();
+ }
+ }
+
+ private void DrawCircle()
+ {
+ float num = myTower.range;
+ if (myTower.squareUI)
+ {
+ num += 0.5f;
+ line.SetVertexCount(5);
+ line.useWorldSpace = true;
+ Vector3 position = base.transform.position;
+ position.y = 0.4f;
+ line.SetPosition(0, new Vector3(num, 0f, num) + position);
+ line.SetPosition(1, new Vector3(num, 0f, 0f - num) + position);
+ line.SetPosition(2, new Vector3(0f - num, 0f, 0f - num) + position);
+ line.SetPosition(3, new Vector3(0f - num, 0f, num) + position);
+ line.SetPosition(4, new Vector3(num, 0f, num) + position);
+ return;
+ }
+ line.SetVertexCount(61);
+ line.useWorldSpace = true;
+ Vector3 vector = new Vector3(0f, 0f, 0f);
+ Vector3 position2 = base.transform.position;
+ position2.y = 0.4f;
+ float num2 = 0f;
+ for (int i = 0; i < 61; i++)
+ {
+ vector.x = Mathf.Cos((float)Math.PI / 180f * num2) * num;
+ vector.z = Mathf.Sin((float)Math.PI / 180f * num2) * num;
+ line.SetPosition(i, vector + position2);
+ num2 += 6f;
+ }
+ }
+
+ private string CritText()
+ {
+ string text = "x2! ";
+ text = text + (int)Mathf.Clamp(myTower.critChance * 100f, 0f, 50f) + "%";
+ if (myTower.critChance > 0.5f)
+ {
+ text = text + "\nx3!! " + (int)Mathf.Clamp(myTower.critChance * 100f - 50f, 0f, 50f) + "%";
+ }
+ if (myTower.critChance > 1f)
+ {
+ text = text + "\nx4!! " + (int)Mathf.Clamp(myTower.critChance * 100f - 100f, 0f, 50f) + "%";
+ }
+ return text;
+ }
+
+ private void UpdateText()
+ {
+ levelText.text = "Level: " + level;
+ baseDamageText.text = "Base Damage: " + baseDamage;
+ healthDamageText.text = "Health Multiplier: " + healthDamage + " (" + baseDamage * healthDamage + ")";
+ armorDamageText.text = "Armor Multiplier: " + armorDamage + " (" + baseDamage * armorDamage + ")";
+ shieldDamageText.text = "Shield Multiplier: " + shieldDamage + " (" + baseDamage * shieldDamage + ")";
+ healthXPText.text = ((10 * level - (int)healthXP) * myTower.upgradeCostMultiplier).ToString();
+ armorXPText.text = ((10 * level - (int)armorXP) * myTower.upgradeCostMultiplier).ToString();
+ shieldXPText.text = ((10 * level - (int)shieldXP) * myTower.upgradeCostMultiplier).ToString();
+ healthXPImg.rectTransform.sizeDelta = new Vector2(healthXP / (float)level, 0.25f);
+ armorXPImg.rectTransform.sizeDelta = new Vector2(armorXP / (float)level, 0.25f);
+ shieldXPImg.rectTransform.sizeDelta = new Vector2(shieldXP / (float)level, 0.25f);
+ rangeText.text = "Range: " + range;
+ if (myTower.GetComponent<Dropper>() != null)
+ {
+ rpmText.text = "Fire Rate: " + (int)myTower.GetComponent<Dropper>().dropperRPMdisplay + " RPM";
+ }
+ else
+ {
+ rpmText.text = "Fire Rate: " + rpm + " RPM";
+ }
+ if (usesMana)
+ {
+ manaUseText.text = "Mana Use: " + (int)((float)baseDamage * manaUseMultiplier) + "/shot";
+ }
+ else if (!usesMana && manaUseMultiplier > 0f)
+ {
+ manaUseText.text = "Mana Use: " + manaUseMultiplier + "/sec";
+ }
+ else
+ {
+ manaUseText.text = "";
+ }
+ demolishText.text = "Demolish (" + TowerManager.instance.GetSellPrice(myTower.towerType) + "g)";
+ }
+
+ public void BuyHealthLevel()
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.BuyHealthLevel();
+ SetStats(myTower);
+ }
+
+ public void BuyArmorLevel()
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.BuyArmorLevel();
+ SetStats(myTower);
+ }
+
+ public void BuyShieldLevel()
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.BuyShieldLevel();
+ SetStats(myTower);
+ }
+
+ public void TogglePriorityUp(int index)
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.TogglePriority(index, 1);
+ priorityTexts[index].text = myTower.priorities[index].ToString();
+ }
+
+ public void TogglePriorityDown(int index)
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.TogglePriority(index, -1);
+ priorityTexts[index].text = myTower.priorities[index].ToString();
+ }
+
+ public void DemolishTower()
+ {
+ SFXManager.instance.ButtonClick();
+ myTower.Demolish();
+ CloseUI();
+ }
+
+ public void CloseUI()
+ {
+ UIManager.instance.CloseUI(base.gameObject);
+ }
+}
diff --git a/GameCode/TowerUnlockCard.cs b/GameCode/TowerUnlockCard.cs
new file mode 100644
index 0000000..0f14ed3
--- /dev/null
+++ b/GameCode/TowerUnlockCard.cs
@@ -0,0 +1,16 @@
+using UnityEngine;
+
+public class TowerUnlockCard : UpgradeCard
+{
+ [SerializeField]
+ private GameObject towerUIObject;
+
+ [SerializeField]
+ private bool isTower = true;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ TowerUnlockManager.instance.UnlockTower(towerUIObject, isTower);
+ }
+}
diff --git a/GameCode/TowerUnlockManager.cs b/GameCode/TowerUnlockManager.cs
new file mode 100644
index 0000000..d96b9d5
--- /dev/null
+++ b/GameCode/TowerUnlockManager.cs
@@ -0,0 +1,54 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class TowerUnlockManager : MonoBehaviour
+{
+ public static TowerUnlockManager instance;
+
+ [SerializeField]
+ private List<GameObject> unlockedTowers = new List<GameObject>();
+
+ [SerializeField]
+ private List<GameObject> unlockedBuildings = new List<GameObject>();
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ DisplayButtons();
+ }
+
+ public void UnlockTower(GameObject towerUIObject, bool isTower)
+ {
+ towerUIObject.SetActive(value: true);
+ if (isTower)
+ {
+ unlockedTowers.Add(towerUIObject);
+ }
+ else
+ {
+ unlockedBuildings.Add(towerUIObject);
+ }
+ DisplayButtons();
+ }
+
+ private void DisplayButtons()
+ {
+ int num = 0;
+ int num2 = ((unlockedBuildings.Count > 0) ? 1 : 0);
+ foreach (GameObject unlockedTower in unlockedTowers)
+ {
+ unlockedTower.transform.localPosition = new Vector3(num * 100 - unlockedTowers.Count * 50 + 50, 100 * num2, 0f);
+ num++;
+ }
+ num = 0;
+ foreach (GameObject unlockedBuilding in unlockedBuildings)
+ {
+ unlockedBuilding.transform.localPosition = new Vector3(num * 100 - unlockedBuildings.Count * 50 + 50, 0f, 0f);
+ num++;
+ }
+ }
+}
diff --git a/GameCode/TowerUpgradeCard.cs b/GameCode/TowerUpgradeCard.cs
new file mode 100644
index 0000000..c37da5e
--- /dev/null
+++ b/GameCode/TowerUpgradeCard.cs
@@ -0,0 +1,117 @@
+using UnityEngine;
+
+public class TowerUpgradeCard : UpgradeCard
+{
+ [SerializeField]
+ private TowerType towerType;
+
+ [SerializeField]
+ private float range;
+
+ [SerializeField]
+ private int damage;
+
+ [SerializeField]
+ private int healthDmg;
+
+ [SerializeField]
+ private int armorDmg;
+
+ [SerializeField]
+ private int shieldDmg;
+
+ [SerializeField]
+ private float slowPercent;
+
+ [SerializeField]
+ private float bleedPercent;
+
+ [SerializeField]
+ private float burnPercent;
+
+ [SerializeField]
+ private float poisonPercent;
+
+ [SerializeField]
+ private float blast;
+
+ [SerializeField]
+ private float critChance;
+
+ [SerializeField]
+ private float critChanceLevelMultiplier;
+
+ [SerializeField]
+ private float stunChance;
+
+ [SerializeField]
+ private float manaConsumptionAddition;
+
+ [SerializeField]
+ private float obeliskTimeOnTargetMultiplier;
+
+ public override void Upgrade()
+ {
+ if (range != 0f)
+ {
+ TowerManager.instance.AddBonusRange(towerType, range);
+ }
+ if (damage != 0)
+ {
+ TowerManager.instance.AddBonusBaseDamage(towerType, damage);
+ }
+ if (healthDmg != 0)
+ {
+ TowerManager.instance.AddBonusHealthDamage(towerType, healthDmg);
+ }
+ if (armorDmg != 0)
+ {
+ TowerManager.instance.AddBonusArmorDamage(towerType, armorDmg);
+ }
+ if (shieldDmg != 0)
+ {
+ TowerManager.instance.AddBonusShieldDamage(towerType, shieldDmg);
+ }
+ if (slowPercent != 0f)
+ {
+ TowerManager.instance.AddBonusSlow(towerType, slowPercent);
+ }
+ if (bleedPercent != 0f)
+ {
+ TowerManager.instance.AddBonusBleed(towerType, bleedPercent);
+ }
+ if (burnPercent != 0f)
+ {
+ TowerManager.instance.AddBonusBurn(towerType, burnPercent);
+ }
+ if (poisonPercent != 0f)
+ {
+ TowerManager.instance.AddBonusPoison(towerType, poisonPercent);
+ }
+ if (blast != 0f)
+ {
+ TowerManager.instance.AddBonusBlast(towerType, blast);
+ }
+ if (critChance != 0f)
+ {
+ TowerManager.instance.AddCritChance(towerType, critChance);
+ }
+ if (critChanceLevelMultiplier != 0f)
+ {
+ TowerManager.instance.AddCritChanceLevelMultiplier(towerType, critChanceLevelMultiplier);
+ }
+ if (stunChance != 0f)
+ {
+ TowerManager.instance.AddStunChance(towerType, stunChance);
+ }
+ if (manaConsumptionAddition != 0f)
+ {
+ TowerManager.instance.AddManaConsumption(towerType, manaConsumptionAddition);
+ }
+ if (obeliskTimeOnTargetMultiplier != 0f)
+ {
+ TowerManager.instance.obeliskTimeOnTargetMultiplier += obeliskTimeOnTargetMultiplier;
+ }
+ base.Upgrade();
+ }
+}
diff --git a/GameCode/TreasureChest.cs b/GameCode/TreasureChest.cs
new file mode 100644
index 0000000..cf18502
--- /dev/null
+++ b/GameCode/TreasureChest.cs
@@ -0,0 +1,26 @@
+using UnityEngine;
+
+public class TreasureChest : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private GameObject uiObject;
+
+ public int numberOfDrops = 1;
+
+ private void Start()
+ {
+ }
+
+ public void SpawnUI()
+ {
+ Object.Instantiate(uiObject, base.transform.position, Quaternion.identity).GetComponent<TreasureUI>().myChest = base.gameObject;
+ }
+
+ public void Demolish()
+ {
+ }
+
+ public void SetStats()
+ {
+ }
+}
diff --git a/GameCode/TreasureUI.cs b/GameCode/TreasureUI.cs
new file mode 100644
index 0000000..c0e297f
--- /dev/null
+++ b/GameCode/TreasureUI.cs
@@ -0,0 +1,30 @@
+using UnityEngine;
+
+public class TreasureUI : MonoBehaviour
+{
+ public GameObject myChest;
+
+ private int cardDraw = 2;
+
+ private void Start()
+ {
+ UIManager.instance.SetNewUI(base.gameObject);
+ cardDraw = 2 + PlayerPrefs.GetInt("TreasureDraw1", 0);
+ }
+
+ public void Open()
+ {
+ SFXManager.instance.PlaySound(Sound.CritBig, myChest.transform.position);
+ for (int i = 0; i < myChest.GetComponent<TreasureChest>().numberOfDrops; i++)
+ {
+ CardManager.instance.DrawCards(cardDraw);
+ }
+ Die();
+ }
+
+ public void Die()
+ {
+ Object.Destroy(myChest);
+ UIManager.instance.CloseUI(base.gameObject);
+ }
+}
diff --git a/GameCode/UICameraController.cs b/GameCode/UICameraController.cs
new file mode 100644
index 0000000..c309492
--- /dev/null
+++ b/GameCode/UICameraController.cs
@@ -0,0 +1,67 @@
+using UnityEngine;
+
+public class UICameraController : MonoBehaviour
+{
+ [SerializeField]
+ private float cameraSpeed = 10f;
+
+ [SerializeField]
+ private float cameraZoom = 1f;
+
+ private Vector3 oldPos;
+
+ private void Start()
+ {
+ }
+
+ private void Update()
+ {
+ UpdateMovement();
+ UpdateZoom();
+ }
+
+ public void ResetPosition()
+ {
+ base.transform.localPosition = Vector3.zero;
+ cameraZoom = 1f;
+ base.transform.localScale = Vector3.one * cameraZoom;
+ }
+
+ private Vector3 ViewPointToPixels(Vector3 view)
+ {
+ return new Vector3(view.x * (float)Camera.main.scaledPixelWidth, view.y * (float)Camera.main.scaledPixelHeight, 0f);
+ }
+
+ private void UpdateMovement()
+ {
+ if (Input.GetMouseButtonDown(1))
+ {
+ oldPos = ViewPointToPixels(Camera.main.ScreenToViewportPoint(Input.mousePosition));
+ }
+ if (Input.GetMouseButton(1))
+ {
+ Vector3 vector = ViewPointToPixels(Camera.main.ScreenToViewportPoint(Input.mousePosition));
+ Vector3 translation = vector - oldPos;
+ translation.z = 0f;
+ base.transform.Translate(translation);
+ oldPos = vector;
+ }
+ else
+ {
+ Vector3 vector2 = new Vector3(Input.GetAxisRaw("Horizontal"), Input.GetAxisRaw("Vertical"), 0f);
+ if (vector2.sqrMagnitude > 0.1f)
+ {
+ base.transform.Translate(-vector2.normalized * Time.deltaTime * cameraSpeed * (6f / (5f + cameraZoom)));
+ }
+ }
+ }
+
+ private void UpdateZoom()
+ {
+ float num = cameraZoom;
+ cameraZoom = Mathf.Clamp(cameraZoom + Input.mouseScrollDelta.y / 10f, 0.5f, 2f);
+ base.transform.localScale = Vector3.one * cameraZoom;
+ base.transform.localPosition *= cameraZoom / num;
+ base.transform.localPosition += new Vector3(0f, 0.5f * (float)Camera.main.scaledPixelHeight * (cameraZoom - num), 0f);
+ }
+}
diff --git a/GameCode/UIManager.cs b/GameCode/UIManager.cs
new file mode 100644
index 0000000..2a7081f
--- /dev/null
+++ b/GameCode/UIManager.cs
@@ -0,0 +1,47 @@
+using UnityEngine;
+
+public class UIManager : MonoBehaviour
+{
+ public static UIManager instance;
+
+ [SerializeField]
+ private LayerMask clickableMask;
+
+ private GameObject currentUI;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ public void SetNewUI(GameObject newUI)
+ {
+ if (currentUI != null)
+ {
+ Object.Destroy(currentUI);
+ }
+ currentUI = newUI;
+ }
+
+ public void CloseUI(GameObject oldUI)
+ {
+ currentUI = null;
+ Object.Destroy(oldUI);
+ }
+
+ private void Update()
+ {
+ if (Input.GetKeyDown(KeyCode.Escape) && currentUI != null)
+ {
+ CloseUI(currentUI);
+ }
+ if (!BuildingManager.instance.buildMode && Input.GetMouseButtonDown(0) && Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out var hitInfo, 2000f, clickableMask, QueryTriggerInteraction.Collide))
+ {
+ hitInfo.collider.GetComponent<IBuildable>()?.SpawnUI();
+ }
+ if (Input.GetMouseButtonDown(1))
+ {
+ CloseUI(currentUI);
+ }
+ }
+}
diff --git a/GameCode/UniBonusUI.cs b/GameCode/UniBonusUI.cs
new file mode 100644
index 0000000..8d29a9b
--- /dev/null
+++ b/GameCode/UniBonusUI.cs
@@ -0,0 +1,46 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class UniBonusUI : MonoBehaviour
+{
+ public static UniBonusUI instance;
+
+ [SerializeField]
+ private GameObject uniUI;
+
+ [SerializeField]
+ private Text uniUIText;
+
+ private int healthTotal;
+
+ private int armorTotal;
+
+ private int shieldTotal;
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ public void UniBonus(int health, int armor, int shield)
+ {
+ healthTotal += health;
+ armorTotal += armor;
+ shieldTotal += shield;
+ uniUI.SetActive(value: true);
+ string text = "";
+ if (healthTotal > 0)
+ {
+ text = text + "+" + healthTotal + "HD";
+ }
+ if (armorTotal > 0)
+ {
+ text = text + "\n+" + armorTotal + "AD";
+ }
+ if (shieldTotal > 0)
+ {
+ text = text + "\n+" + shieldTotal + "SD";
+ }
+ uniUIText.text = text;
+ }
+}
diff --git a/GameCode/University.cs b/GameCode/University.cs
new file mode 100644
index 0000000..5115f50
--- /dev/null
+++ b/GameCode/University.cs
@@ -0,0 +1,150 @@
+using UnityEngine;
+
+public class University : MonoBehaviour, IBuildable
+{
+ [SerializeField]
+ private GameObject UIObject;
+
+ [SerializeField]
+ private GameObject mainUIObject;
+
+ public int goldBackOnDemolish;
+
+ [SerializeField]
+ private LayerMask layermask;
+
+ private bool active;
+
+ public int healthPercent { get; private set; }
+
+ public int armorPercent { get; private set; }
+
+ public int shieldPercent { get; private set; }
+
+ public int healthGained { get; private set; }
+
+ public int armorGained { get; private set; }
+
+ public int shieldGained { get; private set; }
+
+ private void Start()
+ {
+ SpawnManager.instance.universities.Add(this);
+ DetectShrines();
+ }
+
+ public void SetStats()
+ {
+ }
+
+ public void Research()
+ {
+ if (active)
+ {
+ if (Random.Range(0f, 100f) <= (float)(healthPercent + GameManager.instance.universityBonus))
+ {
+ healthGained++;
+ TowerManager.instance.AddBonusHealthDamage(TowerType.Global, 1);
+ UniBonusUI.instance.UniBonus(1, 0, 0);
+ DamageNumber component = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component.SetText("New Discoveries!", "Grey", 1f);
+ component.SetHoldTime(2f);
+ AchievementManager.instance.NewDiscoveriesAchievement();
+ }
+ if (Random.Range(0f, 100f) <= (float)(armorPercent + GameManager.instance.universityBonus))
+ {
+ armorGained++;
+ UniBonusUI.instance.UniBonus(0, 1, 0);
+ TowerManager.instance.AddBonusArmorDamage(TowerType.Global, 1);
+ DamageNumber component2 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component2.SetText("New Discoveries!", "Grey", 1f);
+ component2.SetHoldTime(2f);
+ AchievementManager.instance.NewDiscoveriesAchievement();
+ }
+ if (Random.Range(0f, 100f) <= (float)(shieldPercent + GameManager.instance.universityBonus))
+ {
+ shieldGained++;
+ UniBonusUI.instance.UniBonus(0, 0, 1);
+ TowerManager.instance.AddBonusShieldDamage(TowerType.Global, 1);
+ DamageNumber component3 = ObjectPool.instance.SpawnObject(ObjectPool.ObjectType.DamageNumber, base.transform.position, Quaternion.identity).GetComponent<DamageNumber>();
+ component3.SetText("New Discoveries!", "Grey", 1f);
+ component3.SetHoldTime(2f);
+ AchievementManager.instance.NewDiscoveriesAchievement();
+ }
+ }
+ }
+
+ public void FundHealthStudies()
+ {
+ int num = (healthPercent + armorPercent + shieldPercent + 1) * 20;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ healthPercent++;
+ ResourceManager.instance.Spend(num);
+ AchievementManager.instance.FundResearchAchievement(num);
+ }
+ }
+
+ public void FundArmorStudies()
+ {
+ int num = (healthPercent + armorPercent + shieldPercent + 1) * 20;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ armorPercent++;
+ ResourceManager.instance.Spend(num);
+ AchievementManager.instance.FundResearchAchievement(num);
+ }
+ }
+
+ public void FundShieldStudies()
+ {
+ int num = (healthPercent + armorPercent + shieldPercent + 1) * 20;
+ if (ResourceManager.instance.CheckMoney(num))
+ {
+ shieldPercent++;
+ ResourceManager.instance.Spend(num);
+ AchievementManager.instance.FundResearchAchievement(num);
+ }
+ }
+
+ public void SpawnUI()
+ {
+ if (!active)
+ {
+ Object.Instantiate(UIObject, base.transform.position, Quaternion.identity).GetComponent<SimpleUI>().SetDemolishable(base.gameObject, goldBackOnDemolish);
+ }
+ else
+ {
+ Object.Instantiate(mainUIObject, base.transform.position, Quaternion.identity).GetComponent<UniversityUI>().SetStats(this);
+ }
+ }
+
+ public void Demolish()
+ {
+ SpawnManager.instance.universities.Remove(this);
+ }
+
+ private void DetectShrines()
+ {
+ if ((!Physics.Raycast(base.transform.position + new Vector3(1f, 1f, 0f), -base.transform.up, out var hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(-1f, 1f, 0f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && (!Physics.Raycast(base.transform.position + new Vector3(0f, 1f, 1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore) || !Rotate(hitInfo)) && Physics.Raycast(base.transform.position + new Vector3(0f, 1f, -1f), -base.transform.up, out hitInfo, 1f, layermask, QueryTriggerInteraction.Ignore))
+ {
+ Rotate(hitInfo);
+ }
+ }
+
+ private bool Rotate(RaycastHit hit)
+ {
+ if (hit.collider.GetComponent<Shrine>() == null)
+ {
+ return false;
+ }
+ if ((double)(hit.collider.transform.position.y - base.transform.position.y) > 0.001)
+ {
+ return false;
+ }
+ base.transform.LookAt(hit.collider.transform.position, Vector3.up);
+ active = true;
+ SetStats();
+ return true;
+ }
+}
diff --git a/GameCode/UniversityUI.cs b/GameCode/UniversityUI.cs
new file mode 100644
index 0000000..bf7b8bd
--- /dev/null
+++ b/GameCode/UniversityUI.cs
@@ -0,0 +1,135 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+public class UniversityUI : MonoBehaviour
+{
+ private University myUniversity;
+
+ [SerializeField]
+ private Text healthCostText;
+
+ [SerializeField]
+ private Text armorCostText;
+
+ [SerializeField]
+ private Text shieldCostText;
+
+ [SerializeField]
+ private Text healthDiscoveresText;
+
+ [SerializeField]
+ private Text armorDiscoveriesText;
+
+ [SerializeField]
+ private Text shieldDiscoveriesText;
+
+ [SerializeField]
+ private Text healthBonusText;
+
+ [SerializeField]
+ private Text armorBonusText;
+
+ [SerializeField]
+ private Text shieldBonusText;
+
+ [SerializeField]
+ private Text healthPercentageText;
+
+ [SerializeField]
+ private Text armorPercentageText;
+
+ [SerializeField]
+ private Text shieldPercentageText;
+
+ [SerializeField]
+ private Image healthPercentageImg;
+
+ [SerializeField]
+ private Image armorPercentageImg;
+
+ [SerializeField]
+ private Image shieldPercentageImg;
+
+ [SerializeField]
+ private Text demolishText;
+
+ private void Start()
+ {
+ UIManager.instance.SetNewUI(base.gameObject);
+ if (demolishText != null)
+ {
+ demolishText.text = "Demolish (" + myUniversity.goldBackOnDemolish + "g)";
+ }
+ }
+
+ public void SetStats(University myUni)
+ {
+ myUniversity = myUni;
+ int num = (myUniversity.healthPercent + myUniversity.armorPercent + myUniversity.shieldPercent + 1) * 20;
+ healthCostText.text = num.ToString();
+ armorCostText.text = num.ToString();
+ shieldCostText.text = num.ToString();
+ healthDiscoveresText.text = "Health Studies: " + myUniversity.healthGained;
+ armorDiscoveriesText.text = "Armor Studies: " + myUniversity.armorGained;
+ shieldDiscoveriesText.text = "Magic Studies: " + myUniversity.shieldGained;
+ healthBonusText.text = "Global Health Damage: +" + myUniversity.healthGained;
+ armorBonusText.text = "Global Armor Damage: +" + myUniversity.armorGained;
+ shieldBonusText.text = "Global Shield Damage: +" + myUniversity.shieldGained;
+ healthPercentageText.text = "Health Studies: " + (myUniversity.healthPercent + GameManager.instance.universityBonus) + "%";
+ armorPercentageText.text = "Armor Studies: " + (myUniversity.armorPercent + GameManager.instance.universityBonus) + "%";
+ shieldPercentageText.text = "Magic Studies: " + (myUniversity.shieldPercent + GameManager.instance.universityBonus) + "%";
+ healthPercentageImg.rectTransform.sizeDelta = new Vector2((float)(myUniversity.healthPercent + GameManager.instance.universityBonus) / 10f, 0.25f);
+ armorPercentageImg.rectTransform.sizeDelta = new Vector2((float)(myUniversity.armorPercent + GameManager.instance.universityBonus) / 10f, 0.25f);
+ shieldPercentageImg.rectTransform.sizeDelta = new Vector2((float)(myUniversity.shieldPercent + GameManager.instance.universityBonus) / 10f, 0.25f);
+ if (demolishText != null)
+ {
+ demolishText.text = "Demolish (" + myUniversity.goldBackOnDemolish + "g)";
+ }
+ }
+
+ private void Update()
+ {
+ if (Input.GetKeyDown(KeyCode.Alpha1) || Input.GetKeyDown(KeyCode.Keypad1))
+ {
+ FundHealthResearch();
+ }
+ if (Input.GetKeyDown(KeyCode.Alpha2) || Input.GetKeyDown(KeyCode.Keypad2))
+ {
+ FundArmorResearch();
+ }
+ if (Input.GetKeyDown(KeyCode.Alpha3) || Input.GetKeyDown(KeyCode.Keypad3))
+ {
+ FundShieldResearch();
+ }
+ }
+
+ public void FundHealthResearch()
+ {
+ SFXManager.instance.ButtonClick();
+ myUniversity.FundHealthStudies();
+ SetStats(myUniversity);
+ }
+
+ public void FundArmorResearch()
+ {
+ SFXManager.instance.ButtonClick();
+ myUniversity.FundArmorStudies();
+ SetStats(myUniversity);
+ }
+
+ public void FundShieldResearch()
+ {
+ SFXManager.instance.ButtonClick();
+ myUniversity.FundShieldStudies();
+ SetStats(myUniversity);
+ }
+
+ public void Demolish()
+ {
+ SFXManager.instance.ButtonClick();
+ myUniversity.Demolish();
+ Object.Destroy(myUniversity.gameObject);
+ ResourceManager.instance.AddMoney(myUniversity.goldBackOnDemolish);
+ UIManager.instance.CloseUI(base.gameObject);
+ }
+}
diff --git a/GameCode/UniversityUpgrade.cs b/GameCode/UniversityUpgrade.cs
new file mode 100644
index 0000000..391c336
--- /dev/null
+++ b/GameCode/UniversityUpgrade.cs
@@ -0,0 +1,13 @@
+using UnityEngine;
+
+public class UniversityUpgrade : UpgradeCard
+{
+ [SerializeField]
+ private int bonus = 1;
+
+ public override void Upgrade()
+ {
+ base.Upgrade();
+ GameManager.instance.universityBonus += bonus;
+ }
+}
diff --git a/GameCode/UpgradeButton.cs b/GameCode/UpgradeButton.cs
new file mode 100644
index 0000000..271976b
--- /dev/null
+++ b/GameCode/UpgradeButton.cs
@@ -0,0 +1,167 @@
+using System.Collections;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class UpgradeButton : MonoBehaviour
+{
+ [SerializeField]
+ private string unlockString;
+
+ [SerializeField]
+ public int xpCost;
+
+ public int cardCountRequirement;
+
+ [SerializeField]
+ private bool countsAsCardUnlock = true;
+
+ [SerializeField]
+ private bool countAsDevelopment;
+
+ [SerializeField]
+ private Sprite unlockedSprite;
+
+ [SerializeField]
+ private bool checkAchievements;
+
+ [SerializeField]
+ private GameObject priceTag;
+
+ private GameObject currentPriceTag;
+
+ private Image img;
+
+ private Button btn;
+
+ [SerializeField]
+ private UpgradeButton previous;
+
+ [SerializeField]
+ private UpgradeButton[] next;
+
+ public bool unlocked;
+
+ private void Start()
+ {
+ img = GetComponent<Image>();
+ btn = GetComponent<Button>();
+ CheckUnlock();
+ StartCoroutine(LateStart());
+ }
+
+ public void Unlock()
+ {
+ if (UpgradeManager.instance.xp >= xpCost)
+ {
+ SFXManager.instance.ButtonClick();
+ UpgradeManager.instance.AddXP(-xpCost);
+ PlayerPrefs.SetInt(unlockString, 1);
+ if (checkAchievements)
+ {
+ AchievementManager.instance.CheckTowerUnlocks();
+ }
+ UpgradeManager.instance.CountCard(countsAsCardUnlock);
+ UpgradeManager.instance.CountDevelopment(countAsDevelopment);
+ btn.enabled = false;
+ img.sprite = unlockedSprite;
+ unlocked = true;
+ UpdateTitleText();
+ UpgradeButton[] array = next;
+ for (int i = 0; i < array.Length; i++)
+ {
+ array[i].CheckEnabled();
+ }
+ }
+ }
+
+ public void ResetUnlock()
+ {
+ PlayerPrefs.SetInt(unlockString, 0);
+ unlocked = false;
+ }
+
+ private void CheckUnlock()
+ {
+ if (PlayerPrefs.GetInt(unlockString, 0) == 1)
+ {
+ btn.enabled = false;
+ img.sprite = unlockedSprite;
+ unlocked = true;
+ }
+ }
+
+ public void CheckEnabled()
+ {
+ if (previous == null)
+ {
+ if (cardCountRequirement <= UpgradeManager.instance.unlockedCardCount)
+ {
+ btn.interactable = true;
+ }
+ else
+ {
+ btn.interactable = false;
+ }
+ UpdateTitleText();
+ }
+ else
+ {
+ if (previous.unlocked && cardCountRequirement <= UpgradeManager.instance.unlockedCardCount)
+ {
+ btn.interactable = true;
+ }
+ else
+ {
+ btn.interactable = false;
+ }
+ UpdateTitleText();
+ }
+ }
+
+ private void UpdateTitleText()
+ {
+ if (!unlocked)
+ {
+ if (currentPriceTag == null)
+ {
+ currentPriceTag = Object.Instantiate(priceTag, base.transform);
+ currentPriceTag.transform.localPosition = new Vector3(-63.7f, 0f, 0f);
+ int num = cardCountRequirement - UpgradeManager.instance.unlockedCardCount;
+ if (num > 0)
+ {
+ currentPriceTag.GetComponent<RectTransform>().sizeDelta = new Vector2(75f, 50f);
+ currentPriceTag.GetComponentInChildren<Text>().text = " Unlock " + num + "\n more cards";
+ }
+ else
+ {
+ currentPriceTag.GetComponent<RectTransform>().sizeDelta = new Vector2(50f, 25f);
+ currentPriceTag.GetComponentInChildren<Text>().text = xpCost + " xp";
+ }
+ }
+ else
+ {
+ int num2 = cardCountRequirement - UpgradeManager.instance.unlockedCardCount;
+ if (num2 > 0)
+ {
+ currentPriceTag.GetComponent<RectTransform>().sizeDelta = new Vector2(75f, 50f);
+ currentPriceTag.GetComponentInChildren<Text>().text = " Unlock " + num2 + "\n more cards";
+ }
+ else
+ {
+ currentPriceTag.GetComponent<RectTransform>().sizeDelta = new Vector2(50f, 25f);
+ currentPriceTag.GetComponentInChildren<Text>().text = xpCost + " xp";
+ }
+ }
+ }
+ else if (currentPriceTag != null)
+ {
+ currentPriceTag.SetActive(value: false);
+ }
+ }
+
+ private IEnumerator LateStart()
+ {
+ yield return new WaitForSeconds(0.1f);
+ CheckEnabled();
+ }
+}
diff --git a/GameCode/UpgradeCard.cs b/GameCode/UpgradeCard.cs
new file mode 100644
index 0000000..45b6ce8
--- /dev/null
+++ b/GameCode/UpgradeCard.cs
@@ -0,0 +1,37 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class UpgradeCard : MonoBehaviour
+{
+ [SerializeField]
+ private string unlockName;
+
+ [SerializeField]
+ private bool unlockedByDefault;
+
+ public bool unlocked;
+
+ public string title;
+
+ public Sprite image;
+
+ public string description;
+
+ public List<UpgradeCard> unlocks = new List<UpgradeCard>();
+
+ private void Awake()
+ {
+ if (unlockedByDefault || PlayerPrefs.GetInt(unlockName, 0) == 1)
+ {
+ unlocked = true;
+ }
+ else
+ {
+ unlocked = false;
+ }
+ }
+
+ public virtual void Upgrade()
+ {
+ }
+}
diff --git a/GameCode/UpgradeManager.cs b/GameCode/UpgradeManager.cs
new file mode 100644
index 0000000..685308e
--- /dev/null
+++ b/GameCode/UpgradeManager.cs
@@ -0,0 +1,120 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class UpgradeManager : MonoBehaviour
+{
+ public static UpgradeManager instance;
+
+ public int xp;
+
+ public int prestige;
+
+ [SerializeField]
+ private Text xpText;
+
+ [SerializeField]
+ private GameObject prestigeDisplay;
+
+ [SerializeField]
+ private Text prestigeText;
+
+ public int unlockedCardCount;
+
+ [SerializeField]
+ private UpgradeButton[] allButtons;
+
+ [SerializeField]
+ private List<UpgradeButton> cardsWhichRequireCardCount = new List<UpgradeButton>();
+
+ private void Awake()
+ {
+ instance = this;
+ }
+
+ private void Start()
+ {
+ allButtons = Object.FindObjectsOfType<UpgradeButton>();
+ UpgradeButton[] array = allButtons;
+ foreach (UpgradeButton upgradeButton in array)
+ {
+ if (upgradeButton.cardCountRequirement > 0)
+ {
+ cardsWhichRequireCardCount.Add(upgradeButton);
+ }
+ }
+ unlockedCardCount = PlayerPrefs.GetInt("UnlockedCardCount", 0);
+ xp = PlayerPrefs.GetInt("XP", 0);
+ xpText.text = "XP: " + xp;
+ prestige = PlayerPrefs.GetInt("Prestige", 0);
+ if (prestige > 0)
+ {
+ prestigeDisplay.SetActive(value: true);
+ prestigeText.text = "Prestige: " + prestige;
+ }
+ }
+
+ public void AddXP(int change)
+ {
+ xp += change;
+ PlayerPrefs.SetInt("XP", xp);
+ xpText.text = "XP: " + xp;
+ }
+
+ public void ResetUpgrades()
+ {
+ int num = 0;
+ UpgradeButton[] array = allButtons;
+ foreach (UpgradeButton upgradeButton in array)
+ {
+ if (upgradeButton.enabled)
+ {
+ if (upgradeButton.unlocked)
+ {
+ num += upgradeButton.xpCost;
+ }
+ upgradeButton.ResetUnlock();
+ }
+ }
+ num += xp / 2;
+ Debug.Log("Refunded xp: " + num);
+ PlayerPrefs.SetInt("Prestige", prestige + num / 1000);
+ xp = 0;
+ PlayerPrefs.SetInt("XP", 0);
+ PlayerPrefs.SetInt("UnlockedCardCount", 0);
+ PlayerPrefs.SetInt("Development", 0);
+ PlayerPrefs.SetInt("Record1", 0);
+ PlayerPrefs.SetInt("Record2", 0);
+ PlayerPrefs.SetInt("Record3", 0);
+ LevelLoader.instance.LoadLevel("MainMenu");
+ }
+
+ public void CountCard(bool countsAsCardUnlock)
+ {
+ if (countsAsCardUnlock)
+ {
+ unlockedCardCount++;
+ Debug.Log("Number of unlocked cards: " + unlockedCardCount);
+ PlayerPrefs.SetInt("UnlockedCardCount", unlockedCardCount);
+ CheckCardCount();
+ }
+ }
+
+ public void CountDevelopment(bool value)
+ {
+ if (value)
+ {
+ int @int = PlayerPrefs.GetInt("Development", 0);
+ PlayerPrefs.SetInt("Development", @int + 1);
+ Debug.Log("Development: " + (@int + 1));
+ }
+ }
+
+ public void CheckCardCount()
+ {
+ foreach (UpgradeButton item in cardsWhichRequireCardCount)
+ {
+ item.CheckEnabled();
+ }
+ }
+}
diff --git a/GameCode/WalkAnimator.cs b/GameCode/WalkAnimator.cs
new file mode 100644
index 0000000..4f2b997
--- /dev/null
+++ b/GameCode/WalkAnimator.cs
@@ -0,0 +1,55 @@
+using System;
+using UnityEngine;
+
+public class WalkAnimator : MonoBehaviour
+{
+ private Pathfinder p;
+
+ [SerializeField]
+ private Transform directionalTransform;
+
+ [SerializeField]
+ private Transform rotationalOffsetTransform;
+
+ [SerializeField]
+ private Transform artTransform;
+
+ [SerializeField]
+ private float strideLength = 1f;
+
+ [SerializeField]
+ private float strideHeight = 1f;
+
+ [SerializeField]
+ private float sideAngles = 20f;
+
+ [SerializeField]
+ private float strideVariationPercentage = 0.1f;
+
+ private float step;
+
+ private Vector3 preiviousPosition;
+
+ private Vector3 direction;
+
+ private void Start()
+ {
+ strideLength = UnityEngine.Random.Range(strideLength * (1f - strideVariationPercentage), strideLength * (1f + strideVariationPercentage));
+ strideHeight = UnityEngine.Random.Range(strideHeight * (1f - strideVariationPercentage), strideHeight * (1f + strideVariationPercentage));
+ p = GetComponent<Pathfinder>();
+ preiviousPosition = base.transform.position;
+ }
+
+ private void FixedUpdate()
+ {
+ direction = base.transform.position - preiviousPosition;
+ preiviousPosition = base.transform.position;
+ if (direction.sqrMagnitude != 0f)
+ {
+ directionalTransform.rotation = Quaternion.LookRotation(direction, Vector3.up);
+ }
+ step = (step + p.speed * Time.fixedDeltaTime / strideLength) % 2f;
+ artTransform.localPosition = strideHeight * Vector3.up * Mathf.Abs(Mathf.Cos(step * (float)Math.PI));
+ artTransform.localEulerAngles = new Vector3(sideAngles * Mathf.Sin(step * (float)Math.PI), 0f, 0f);
+ }
+}
diff --git a/GameCode/Waypoint.cs b/GameCode/Waypoint.cs
new file mode 100644
index 0000000..b250079
--- /dev/null
+++ b/GameCode/Waypoint.cs
@@ -0,0 +1,62 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class Waypoint : MonoBehaviour
+{
+ [SerializeField]
+ private Waypoint next;
+
+ [SerializeField]
+ private List<Waypoint> previous = new List<Waypoint>();
+
+ public float distanceFromEnd;
+
+ public bool trueDistance;
+
+ private void Start()
+ {
+ UpdateDistance();
+ }
+
+ public Waypoint GetNextWaypoint()
+ {
+ if (next != null)
+ {
+ return next;
+ }
+ return this;
+ }
+
+ public void SetNextWaypoint(Waypoint newNext)
+ {
+ next = newNext;
+ }
+
+ public void AddPreviousWaypoint(Waypoint previousWaypoint)
+ {
+ previous.Add(previousWaypoint);
+ }
+
+ public Waypoint[] GetPreviousWaypoints()
+ {
+ return previous.ToArray();
+ }
+
+ public void UpdateDistance()
+ {
+ if (next == null)
+ {
+ trueDistance = true;
+ return;
+ }
+ if (!next.trueDistance)
+ {
+ next.UpdateDistance();
+ }
+ if (!trueDistance)
+ {
+ distanceFromEnd = next.distanceFromEnd + Vector3.Magnitude(next.transform.position - base.transform.position);
+ trueDistance = true;
+ }
+ }
+}