using BumpCombat.Defense; using UnityEngine; namespace BumpCombat.Constants { [CreateAssetMenu(menuName = "BumpCombat/Constants/Defense Prototype")] public sealed class DefenseConstants : ScriptableObject { [Header("Prototype economy")] [Tooltip("Initial construction currency for the test mode.")] [Min(0)] public int InitialConstructionCurrency = 100; [Min(0)] public int DirectTowerCost = 30; [Min(0)] public int SummonTowerCost = 40; [Min(0)] public int PortalRewardCurrency = 20; [Min(0)] public int EnemyKillCurrency = 15; [Min(0)] public int LightningTowerCost = 35; [Min(0)] public int ColdTowerCost = 35; [Min(0)] public int SeismicTowerCost = 35; [Min(0)] public int EmpoweringTowerCost = 35; [Min(0)] public int Tier2UpgradeCost = 20; [Min(0)] public int Tier3UpgradeCost = 30; [Min(0)] public int Tier4UpgradeCost = 40; [Min(0.1f)] public float BuildInteractionRadius = 1.5f; [Header("Prototype arena")] public Vector2 ArenaCenter = Vector2.zero; public Vector2 ArenaPlayableHalfExtents = new(11f, 6f); [Header("Prototype bases and routes")] [Min(1)] public int BaseMaximumHealth = 12; [Min(1)] public int PortalEnemyCount = 8; [Min(0)] public int ArrivalBaseDamage = 1; [Min(0.1f)] public float PortalSpawnInterval = 1.25f; [Min(0.1f)] public float RouteSpeedMultiplier = 0.75f; [Min(0f)] public float PostAttackProgressSeconds = 0.65f; public Vector2[] PortalPositions = { new(-9f, 3.25f), new(-9f, -3.25f), }; public Vector2[] BasePositions = { new(9f, 0f), new(9f, 0f), }; [Tooltip("Build-only branches sit outside the enemy travel lanes.")] public Vector2[] BuildPadPositions = { new(-3.25f, 4.9f), new(3.25f, -4.9f), new(-3.25f, -4.9f), new(3.25f, 4.9f), new(0f, 4.9f), new(0f, -4.9f), }; [Tooltip("Two ordered waypoints per lane, indexed by lane * 2 + waypoint.")] public Vector2[] WaypointPositions = { new(-3f, 3.25f), new(3f, 3.25f), new(-3f, -3.25f), new(3f, -3.25f), }; [Tooltip("The tower source kind ignored by each portal's enemies.")] public DefenseTowerKind[] PortalImmuneTowerKinds = { DefenseTowerKind.Direct, DefenseTowerKind.Summon, }; [Header("Prototype tower sight and damage")] [Min(0.1f)] public float PlayerSightRadius = 6f; [Range(0f, 1f)] public float OutsideSightDamageEfficiency = 0.7f; [Min(0.1f)] public float TowerRange = 5.5f; [Min(0.1f)] public float DirectTowerDamage = 10f; [Min(0.05f)] public float DirectTowerAttackInterval = 0.75f; [Min(0.1f)] public float SummonDamage = 6f; [Min(0.05f)] public float SummonAttackInterval = 0.9f; [Min(1)] public int SummonUnitCount = 2; [Min(0.1f)] public float SummonAreaRadius = 2.25f; [Min(0.1f)] public float SummonUnitMoveSpeed = 2.5f; [Min(0.1f)] public float SummonAttackRange = 1.2f; [Header("Six tower family comparison values")] [Min(0.1f)] public float LightningDamage = 6f; [Min(0.05f)] public float LightningInterval = 1.1f; [Min(0.1f)] public float LightningRadius = 1.3f; [Min(0.1f)] public float ColdDamage = 3f; [Min(0.05f)] public float ColdInterval = 1.2f; [Min(0f)] public float ColdSlowFraction = 0.35f; [Min(0f)] public float ColdSlowSeconds = 1.5f; [Min(0.1f)] public float SeismicDamage = 2f; [Min(0.05f)] public float SeismicInterval = 2f; [Min(0f)] public float SeismicStunSeconds = 0.45f; [Min(0.1f)] public float SeismicRadius = 1.4f; [Min(0.1f)] public float SupportRadius = 3.5f; [Range(0f, 1f)] public float SupportRangeAlpha = 0.4f; // Alpha bounds in the 100x100 source cells, top-left origin, by frame. public static readonly RectInt[] SupportRangeAlphaBounds = { new(35, 54, 30, 15), new(34, 54, 31, 16), new(34, 54, 32, 16), new(35, 54, 30, 15), new(34, 54, 31, 16), new(34, 54, 32, 16), }; public Color[] SupportTierColors = { new(151, 169, 119, 102), new(153, 183, 108, 148), new(158, 201, 96, 194), new(170, 218, 103, 242), }; [Min(0f)] public float SupportDamageBonusPerTier = 0.1f; [Min(0f)] public float SmotheringDamageBonus = 0.3f; [Range(0.1f, 1f)] public float ImbuingAttackIntervalMultiplier = 0.75f; [Min(0f)] public float TowerDamageBonusPerTier = 0.25f; [Header("Prototype player recovery")] [Min(0f)] public float PlayerReviveDelaySeconds = 5f; [Min(0f)] public float PlayerReviveInvulnerabilitySeconds = 1f; [Min(0.1f)] public float RewardPickupRadius = 1.35f; public Vector2 GetPortalPosition(int lane) { return GetPosition(PortalPositions, lane, new Vector2(-9f, lane == 0 ? 3.25f : -3.25f)); } public Vector2 GetBasePosition(int lane) { return GetPosition(BasePositions, lane, new Vector2(9f, 0f)); } public Vector2 GetBuildPadPosition(int index) { return GetPosition(BuildPadPositions, index, Vector2.zero); } public Vector2 GetWaypointPosition(int lane, int waypointIndex) { int index = lane * 2 + waypointIndex; Vector2 fallback = new( waypointIndex == 0 ? -3f : 3f, lane == 0 ? 3.25f : -3.25f); return GetPosition(WaypointPositions, index, fallback); } public DefenseTowerKind GetPortalImmuneTowerKind(int lane) { if (PortalImmuneTowerKinds != null && lane >= 0 && lane < PortalImmuneTowerKinds.Length) { return PortalImmuneTowerKinds[lane]; } return (DefenseTowerKind)Mathf.Clamp(lane, 0, 1); } private static Vector2 GetPosition(Vector2[] positions, int index, Vector2 fallback) { return positions != null && index >= 0 && index < positions.Length ? positions[index] : fallback; } } }