asset modified
This commit is contained in:
@@ -91,6 +91,18 @@ namespace BumpCombat.Constants
|
||||
[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;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using System.Collections.Generic;
|
||||
using BumpCombat.Defense;
|
||||
using BumpCombat.Enemies;
|
||||
using UnityEngine;
|
||||
namespace BumpCombat.Defense
|
||||
{
|
||||
internal static class DefenseTowerArtDurations
|
||||
@@ -113,6 +116,7 @@ namespace BumpCombat.Defense
|
||||
["Effects/Support/T1-Aura"] = new[] { 140, 140, 140, 140, 140, 140 },
|
||||
["Effects/Support/T2-Aura"] = new[] { 140, 140, 140, 140, 140, 140 },
|
||||
["Effects/Support/T3-Aura"] = new[] { 140, 140, 140, 140, 140, 140 },
|
||||
["SupportRange/T1-Aura"] = new[] { 140, 140, 140, 140, 140, 140 },
|
||||
["Effects/Control/StoneGaze"] = new[] { 90, 90, 90, 90, 90, 90, 90, 90 },
|
||||
["Effects/Control/Temporal"] = new[] { 170, 170, 170, 170, 170, 170, 170, 170 },
|
||||
["Effects/Support/Imbuing"] = new[] { 130, 130, 130, 130, 130, 130, 130, 130 },
|
||||
@@ -157,3 +161,70 @@ namespace BumpCombat.Defense
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
namespace BumpCombat.Constants
|
||||
{
|
||||
/// <summary>Offline pixel measurements for non-readable defense art and original shadows.</summary>
|
||||
public static class DefenseTowerArtBounds
|
||||
{
|
||||
private static readonly Vector2Int[] LightningBeamBounds =
|
||||
{
|
||||
new(8, 56), new(8, 89), new(8, 89), new(8, 89),
|
||||
new(8, 89), new(8, 89), new(24, 89), new(24, 89),
|
||||
};
|
||||
private static readonly Vector2[] ImpactWaveCenters =
|
||||
{
|
||||
new(50f, 39.5f), new(50f, 40f), new(50f, 40.5f),
|
||||
new(49.5f, 40f), new(50.5f, 40f),
|
||||
};
|
||||
|
||||
public static Vector2Int LightningBeamXBounds(int frame) =>
|
||||
LightningBeamBounds[Mathf.Clamp(frame, 0, LightningBeamBounds.Length - 1)];
|
||||
|
||||
public static Vector2 ImpactWaveCenterPixels(int frame, Sprite sprite)
|
||||
{
|
||||
return frame >= ImpactWaveCenters.Length
|
||||
? sprite.pivot
|
||||
: ImpactWaveCenters[Mathf.Clamp(frame, 0, ImpactWaveCenters.Length - 1)];
|
||||
}
|
||||
|
||||
public static Vector2 CharacterShadowOffset(DefenseTowerKind kind, bool flipX = false)
|
||||
{
|
||||
Vector2 shadowPixelCenter = kind switch
|
||||
{
|
||||
DefenseTowerKind.Summon => new Vector2(50.5f, 42.5f),
|
||||
DefenseTowerKind.Seismic or DefenseTowerKind.Empowering => new Vector2(50.5f, 42f),
|
||||
_ => new Vector2(50.5f, 42.5f),
|
||||
};
|
||||
Vector2 localPixels = shadowPixelCenter - new Vector2(50f, 50f);
|
||||
if (flipX) localPixels.x = -localPixels.x;
|
||||
return Vector2.Scale(localPixels / 32f, new Vector2(2.1f, 2.1f));
|
||||
}
|
||||
|
||||
public static Vector2 EnemyShadowOffsetWorld(EnemyController enemy)
|
||||
{
|
||||
if (enemy == null) return Vector2.zero;
|
||||
SpriteRenderer renderer = enemy.GetComponent<SpriteRenderer>();
|
||||
Sprite sprite = renderer != null ? renderer.sprite : null;
|
||||
if (sprite == null || enemy.Definition == null) return Vector2.zero;
|
||||
Vector2 pixelCenter = EnemyShadowCenterPixels(enemy.Definition.Kind);
|
||||
Vector2 localOffset = (pixelCenter - sprite.pivot) / sprite.pixelsPerUnit;
|
||||
if (renderer.flipX) localOffset.x = -localOffset.x;
|
||||
return renderer.transform.TransformVector(localOffset);
|
||||
}
|
||||
|
||||
public static Vector2 EnemyShadowWorldPosition(EnemyController enemy, Vector2 fallback) =>
|
||||
enemy != null ? (Vector2)enemy.transform.position + EnemyShadowOffsetWorld(enemy) : fallback;
|
||||
|
||||
private static Vector2 EnemyShadowCenterPixels(EnemyKind kind) => kind switch
|
||||
{
|
||||
EnemyKind.Slime => new Vector2(48.5f, 42f),
|
||||
EnemyKind.Lancer => new Vector2(51f, 42.5f),
|
||||
EnemyKind.Necrofire => new Vector2(52.5f, 42.5f),
|
||||
EnemyKind.NecroGolem => new Vector2(51.5f, 42.5f),
|
||||
EnemyKind.Werebear => new Vector2(51.5f, 42.5f),
|
||||
EnemyKind.Werewolf => new Vector2(50.5f, 42f),
|
||||
_ => new Vector2(50.5f, 42.5f),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -508,6 +508,38 @@ namespace BumpCombat.Defense
|
||||
return targets;
|
||||
}
|
||||
|
||||
public DefenseTower GetActiveSupportFor(DefenseTower tower)
|
||||
{
|
||||
if (!CanOperateTowers || tower == null || !tower.IsOperational
|
||||
|| tower.Kind == DefenseTowerKind.Empowering)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
DefenseTower strongestSupport = null;
|
||||
float strongestBonus = 0f;
|
||||
foreach (DefenseTower support in towers)
|
||||
{
|
||||
if (support == null || support == tower || !support.IsOperational
|
||||
|| support.Kind != DefenseTowerKind.Empowering
|
||||
|| Vector2.Distance(support.transform.position,
|
||||
tower.transform.position) > tuning.SupportRadius)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
float strength = support.Tier * GetSupportSightEfficiency(support);
|
||||
if (strength > strongestBonus
|
||||
&& tuning.SupportDamageBonusPerTier * strength > 0f)
|
||||
{
|
||||
strongestSupport = support;
|
||||
strongestBonus = strength;
|
||||
}
|
||||
}
|
||||
|
||||
return strongestSupport;
|
||||
}
|
||||
|
||||
public float GetTowerAttackIntervalMultiplier(DefenseTower tower)
|
||||
{
|
||||
if (!CanOperateTowers || tower == null)
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
using UnityEngine;
|
||||
using BumpCombat.Constants;
|
||||
|
||||
namespace BumpCombat.Defense
|
||||
{
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class DefenseSpriteSequence : MonoBehaviour
|
||||
{
|
||||
private SpriteRenderer target;
|
||||
private Sprite[] frames;
|
||||
private string key;
|
||||
private float frameRemaining;
|
||||
private int frame;
|
||||
private Sprite[] nextFrames;
|
||||
private string nextKey;
|
||||
private bool destroyWhenFinished;
|
||||
private float delayRemaining;
|
||||
private bool animationFinished;
|
||||
private Vector2 travelStart;
|
||||
private Vector2 travelEnd;
|
||||
private float travelDuration;
|
||||
private float travelElapsed;
|
||||
private bool fitsLightningBeam;
|
||||
private Vector2 beamStart;
|
||||
private Vector2 beamEnd;
|
||||
private float beamThickness;
|
||||
private bool centersImpactWave;
|
||||
private Vector2 impactWaveCenter;
|
||||
private float impactWaveScale;
|
||||
|
||||
public void Play(SpriteRenderer renderer, string sequenceKey, Sprite[] sequenceFrames,
|
||||
bool destroyOnFinish, string followingKey = null,
|
||||
Sprite[] followingFrames = null, float startDelay = 0f)
|
||||
{
|
||||
target = renderer;
|
||||
key = sequenceKey;
|
||||
frames = sequenceFrames;
|
||||
destroyWhenFinished = destroyOnFinish;
|
||||
nextKey = followingKey;
|
||||
nextFrames = followingFrames;
|
||||
delayRemaining = Mathf.Max(0f, startDelay);
|
||||
if (target == null || frames == null || frames.Length == 0)
|
||||
{
|
||||
enabled = false;
|
||||
if (destroyWhenFinished) Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
frame = 0;
|
||||
target.sprite = frames[frame];
|
||||
target.enabled = delayRemaining <= 0f;
|
||||
frameRemaining = DefenseTowerArt.FrameSeconds(key, frame);
|
||||
UpdateVisualAlignment();
|
||||
}
|
||||
|
||||
public void FitLightningBeam(Vector2 start, Vector2 end, float thickness)
|
||||
{
|
||||
fitsLightningBeam = true;
|
||||
beamStart = start;
|
||||
beamEnd = end;
|
||||
beamThickness = thickness;
|
||||
UpdateVisualAlignment();
|
||||
}
|
||||
|
||||
public void CenterImpactWave(Vector2 center, float uniformScale)
|
||||
{
|
||||
centersImpactWave = true;
|
||||
impactWaveCenter = center;
|
||||
impactWaveScale = uniformScale;
|
||||
UpdateVisualAlignment();
|
||||
}
|
||||
|
||||
public void Travel(Vector2 start, Vector2 end, float duration)
|
||||
{
|
||||
travelStart = start;
|
||||
travelEnd = end;
|
||||
travelDuration = Mathf.Max(0.001f, duration);
|
||||
travelElapsed = 0f;
|
||||
transform.position = start;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (target == null || frames == null || frames.Length == 0)
|
||||
{
|
||||
FinishSequence();
|
||||
return;
|
||||
}
|
||||
if (delayRemaining > 0f)
|
||||
{
|
||||
delayRemaining -= Time.deltaTime;
|
||||
if (delayRemaining > 0f) return;
|
||||
target.enabled = true;
|
||||
}
|
||||
if (travelDuration > 0f && travelElapsed < travelDuration)
|
||||
{
|
||||
travelElapsed += Time.deltaTime;
|
||||
transform.position = Vector2.Lerp(
|
||||
travelStart, travelEnd, Mathf.Clamp01(travelElapsed / travelDuration));
|
||||
}
|
||||
if (animationFinished)
|
||||
{
|
||||
if (travelDuration <= 0f || travelElapsed >= travelDuration) FinishSequence();
|
||||
return;
|
||||
}
|
||||
|
||||
frameRemaining -= Time.deltaTime;
|
||||
while (frameRemaining <= 0f)
|
||||
{
|
||||
frame++;
|
||||
if (frame >= frames.Length)
|
||||
{
|
||||
if (nextFrames != null && nextFrames.Length > 0)
|
||||
{
|
||||
frames = nextFrames;
|
||||
key = nextKey;
|
||||
nextFrames = null;
|
||||
frame = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
animationFinished = true;
|
||||
if (travelDuration <= 0f || travelElapsed >= travelDuration) FinishSequence();
|
||||
return;
|
||||
}
|
||||
}
|
||||
target.sprite = frames[frame];
|
||||
UpdateVisualAlignment();
|
||||
frameRemaining += DefenseTowerArt.FrameSeconds(key, frame);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateVisualAlignment()
|
||||
{
|
||||
if (target == null || target.sprite == null) return;
|
||||
if (fitsLightningBeam)
|
||||
{
|
||||
Vector2 direction = beamEnd - beamStart;
|
||||
float distance = direction.magnitude;
|
||||
Vector2Int bounds = DefenseTowerArtBounds.LightningBeamXBounds(frame);
|
||||
float pixelsPerUnit = target.sprite.pixelsPerUnit;
|
||||
float visibleWidth = (bounds.y - bounds.x) / pixelsPerUnit;
|
||||
float scaleX = distance <= 0f || visibleWidth <= 0f
|
||||
? 0f : distance / visibleWidth;
|
||||
float localStart = (bounds.x - target.sprite.pivot.x) / pixelsPerUnit * scaleX;
|
||||
transform.rotation = Quaternion.Euler(0f, 0f,
|
||||
distance > 0f ? Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg : 0f);
|
||||
transform.position = beamStart - (Vector2)(transform.right * localStart);
|
||||
transform.localScale = new Vector3(scaleX, beamThickness, 1f);
|
||||
}
|
||||
if (centersImpactWave)
|
||||
{
|
||||
transform.localScale = Vector3.one * impactWaveScale;
|
||||
Vector2 localCenter = (DefenseTowerArtBounds.ImpactWaveCenterPixels(frame, target.sprite)
|
||||
- target.sprite.pivot) / target.sprite.pixelsPerUnit;
|
||||
transform.position = impactWaveCenter - (Vector2)transform.TransformVector(localCenter);
|
||||
}
|
||||
}
|
||||
|
||||
private void FinishSequence()
|
||||
{
|
||||
if (destroyWhenFinished)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (target != null) target.enabled = false;
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54af8e9464f3423e9799dbb80339a7f6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
@@ -5,15 +5,22 @@ namespace BumpCombat.Defense
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class DefenseSummonUnit : MonoBehaviour
|
||||
{
|
||||
private static Sprite[] idleFrames;
|
||||
private static Sprite[] attackFrames;
|
||||
private Sprite[] idleFrames;
|
||||
private Sprite[] attackFrames;
|
||||
private string idleKey;
|
||||
private string attackKey;
|
||||
private float spriteFrameTime;
|
||||
private int spriteFrame;
|
||||
private bool playingAttack;
|
||||
private bool isWalking;
|
||||
private string walkKey;
|
||||
private Sprite[] walkFrames;
|
||||
|
||||
private DefenseTower owner;
|
||||
private DefensePrototypeController controller;
|
||||
private Vector2 anchorLocalPosition;
|
||||
private SpriteRenderer spriteRenderer;
|
||||
private float attackCooldown;
|
||||
private float attackAnimationTime = -1f;
|
||||
|
||||
public void Configure(
|
||||
DefenseTower tower,
|
||||
@@ -24,22 +31,42 @@ namespace BumpCombat.Defense
|
||||
controller = prototypeController;
|
||||
anchorLocalPosition = localAnchor;
|
||||
spriteRenderer = gameObject.AddComponent<SpriteRenderer>();
|
||||
idleFrames ??= Resources.LoadAll<Sprite>("Tutorial/Swordsman_Idle");
|
||||
attackFrames ??= Resources.LoadAll<Sprite>("Tutorial/Swordsman_BumpAttack-v1");
|
||||
spriteRenderer.color = Color.white;
|
||||
spriteRenderer.sortingOrder = 11;
|
||||
RefreshArt();
|
||||
}
|
||||
|
||||
public void RefreshArt()
|
||||
{
|
||||
if (spriteRenderer == null || owner == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
string family = owner.Tier == 4 && owner.Branch == DefenseTowerBranch.First
|
||||
? "GiantSlime"
|
||||
: owner.Tier == 4 && owner.Branch == DefenseTowerBranch.Second
|
||||
? "LavaSlime" : "Slime";
|
||||
idleKey = $"Summons/{family}/Idle";
|
||||
attackKey = $"Summons/{family}/Attack01";
|
||||
walkKey = $"Summons/{family}/Walk";
|
||||
idleFrames = DefenseTowerArt.Load(idleKey);
|
||||
attackFrames = DefenseTowerArt.Load(attackKey);
|
||||
walkFrames = DefenseTowerArt.Load(walkKey);
|
||||
spriteFrame = 0;
|
||||
spriteFrameTime = 0f;
|
||||
playingAttack = false;
|
||||
if (idleFrames.Length > 0)
|
||||
{
|
||||
spriteRenderer.sprite = idleFrames[0];
|
||||
}
|
||||
spriteRenderer.color = new Color32(255, 234, 137, 255);
|
||||
spriteRenderer.sortingOrder = 11;
|
||||
float scale = owner.Tier == 4 && owner.Branch == DefenseTowerBranch.First
|
||||
? 1.35f : 0.9f;
|
||||
float scale = family == "GiantSlime" ? 1.55f
|
||||
: family == "LavaSlime" ? 1.15f : 1.1f;
|
||||
transform.localScale = new Vector3(scale, scale, 1f);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateAttackSprite();
|
||||
UpdateSpriteAnimation();
|
||||
if (owner == null
|
||||
|| !owner.isActiveAndEnabled
|
||||
|| controller == null
|
||||
@@ -56,19 +83,20 @@ namespace BumpCombat.Defense
|
||||
DefenseTowerKind.Summon);
|
||||
if (target == null)
|
||||
{
|
||||
Vector2 previous = transform.position;
|
||||
transform.position = Vector2.MoveTowards(
|
||||
transform.position,
|
||||
anchor,
|
||||
controller.Tuning.SummonUnitMoveSpeed * Time.deltaTime);
|
||||
previous, anchor, controller.Tuning.SummonUnitMoveSpeed * Time.deltaTime);
|
||||
isWalking = ((Vector2)transform.position - previous).sqrMagnitude > 0.000001f;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 targetPosition = target.transform.position;
|
||||
spriteRenderer.flipX = targetPosition.x < transform.position.x;
|
||||
Vector2 previousPosition = transform.position;
|
||||
transform.position = Vector2.MoveTowards(
|
||||
transform.position,
|
||||
targetPosition,
|
||||
previousPosition, targetPosition,
|
||||
controller.Tuning.SummonUnitMoveSpeed * Time.deltaTime);
|
||||
isWalking = ((Vector2)transform.position - previousPosition).sqrMagnitude > 0.000001f;
|
||||
float attackRange = controller.Tuning.SummonAttackRange
|
||||
* (owner.Tier == 4 && owner.Branch == DefenseTowerBranch.Second ? 2.5f : 1f);
|
||||
if (attackCooldown <= 0f
|
||||
@@ -86,34 +114,45 @@ namespace BumpCombat.Defense
|
||||
|
||||
private void PlayAttackSprite()
|
||||
{
|
||||
if (attackFrames.Length == 0)
|
||||
if (attackFrames == null || attackFrames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
attackAnimationTime = 0f;
|
||||
playingAttack = true;
|
||||
spriteFrameTime = 0f;
|
||||
spriteFrame = 0;
|
||||
spriteRenderer.sprite = attackFrames[0];
|
||||
}
|
||||
|
||||
private void UpdateAttackSprite()
|
||||
private void UpdateSpriteAnimation()
|
||||
{
|
||||
if (attackAnimationTime < 0f || attackFrames == null
|
||||
|| attackFrames.Length == 0)
|
||||
Sprite[] frames = playingAttack ? attackFrames : isWalking ? walkFrames : idleFrames;
|
||||
string key = playingAttack ? attackKey : isWalking ? walkKey : idleKey;
|
||||
if (spriteRenderer == null || frames == null || frames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
attackAnimationTime += Time.deltaTime;
|
||||
int frame = Mathf.FloorToInt(attackAnimationTime * 12f);
|
||||
if (frame >= attackFrames.Length)
|
||||
spriteFrameTime += Time.deltaTime;
|
||||
while (spriteFrameTime >= DefenseTowerArt.FrameSeconds(key, spriteFrame))
|
||||
{
|
||||
attackAnimationTime = -1f;
|
||||
if (idleFrames != null && idleFrames.Length > 0)
|
||||
spriteFrameTime -= DefenseTowerArt.FrameSeconds(key, spriteFrame);
|
||||
spriteFrame++;
|
||||
if (spriteFrame >= frames.Length)
|
||||
{
|
||||
spriteRenderer.sprite = idleFrames[0];
|
||||
if (playingAttack)
|
||||
{
|
||||
playingAttack = false;
|
||||
spriteFrame = 0;
|
||||
frames = idleFrames;
|
||||
key = idleKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
spriteFrame = 0;
|
||||
}
|
||||
}
|
||||
return;
|
||||
spriteRenderer.sprite = frames[spriteFrame];
|
||||
}
|
||||
spriteRenderer.sprite = attackFrames[frame];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,21 @@ namespace BumpCombat.Defense
|
||||
private LineRenderer effectRange;
|
||||
private static Material effectMaterial;
|
||||
private int summonCount;
|
||||
private SpriteRenderer bodyRenderer;
|
||||
private SpriteRenderer supportAuraRenderer;
|
||||
private SpriteRenderer supportRangeRenderer;
|
||||
private Sprite[] supportRangeFrames;
|
||||
private float supportRangeFrameTime;
|
||||
private int supportRangeFrame;
|
||||
private Sprite[] bodyIdleFrames;
|
||||
private Sprite[] bodyAttackFrames;
|
||||
private string bodyIdleKey;
|
||||
private string bodyAttackKey;
|
||||
private float bodyFrameTime;
|
||||
private int bodyFrame;
|
||||
private bool bodyAttacking;
|
||||
private string supportAuraKey;
|
||||
private Sprite[] supportAuraFrames;
|
||||
|
||||
public DefenseTowerKind Kind { get; private set; }
|
||||
public DefenseTowerBranch Branch { get; private set; }
|
||||
@@ -77,11 +92,33 @@ namespace BumpCombat.Defense
|
||||
|
||||
UpdateLabel();
|
||||
UpdateEffectRange();
|
||||
UpdateBodyArt();
|
||||
UpdateSupportAura();
|
||||
foreach (DefenseSummonUnit unit in GetComponentsInChildren<DefenseSummonUnit>())
|
||||
{
|
||||
unit.RefreshArt();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (supportAuraRenderer != null)
|
||||
{
|
||||
supportAuraRenderer.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateBodyAnimation();
|
||||
if (Kind == DefenseTowerKind.Empowering)
|
||||
UpdateEffectRange();
|
||||
else if (effectRange != null)
|
||||
effectRange.transform.localPosition = DefenseTowerArtBounds.CharacterShadowOffset(
|
||||
Kind, bodyRenderer != null && bodyRenderer.flipX);
|
||||
UpdateSupportAura();
|
||||
UpdateSupportRangeAnimation();
|
||||
if (!IsOperational || Kind == DefenseTowerKind.Summon
|
||||
|| Kind == DefenseTowerKind.Empowering)
|
||||
{
|
||||
@@ -133,6 +170,8 @@ namespace BumpCombat.Defense
|
||||
}
|
||||
|
||||
Vector2 center = target.transform.position;
|
||||
Vector2 lightningVisualCenter = DefenseTowerArtBounds.EnemyShadowWorldPosition(
|
||||
target.Enemy, target.transform.position);
|
||||
float scale = 1f + tuning.TowerDamageBonusPerTier * (Tier - 1);
|
||||
float impactRadius = 0f;
|
||||
bool cone = false;
|
||||
@@ -213,7 +252,13 @@ namespace BumpCombat.Defense
|
||||
|
||||
if (applied)
|
||||
{
|
||||
ShowAttackEffect(center, impactRadius, cone);
|
||||
PlayBodyAttack(center.x < transform.position.x);
|
||||
bool shadowCenteredCircle = Kind == DefenseTowerKind.Seismic
|
||||
|| (Kind == DefenseTowerKind.Cold && Tier == 4
|
||||
&& Branch == DefenseTowerBranch.Second);
|
||||
ShowAttackEffect(center, impactRadius, cone,
|
||||
visualCenter: Kind == DefenseTowerKind.Lightning || shadowCenteredCircle
|
||||
? lightningVisualCenter : null);
|
||||
}
|
||||
return applied;
|
||||
}
|
||||
@@ -319,13 +364,26 @@ namespace BumpCombat.Defense
|
||||
{
|
||||
GameObject rangeObject = new("Effect Range");
|
||||
rangeObject.transform.SetParent(transform, false);
|
||||
if (Kind == DefenseTowerKind.Empowering)
|
||||
{
|
||||
supportRangeRenderer = rangeObject.AddComponent<SpriteRenderer>();
|
||||
supportRangeRenderer.sortingOrder = 3;
|
||||
supportRangeFrames = DefenseTowerArt.Load("SupportRange/T1-Aura");
|
||||
if (supportRangeFrames.Length > 0)
|
||||
{
|
||||
supportRangeRenderer.sprite = supportRangeFrames[0];
|
||||
supportRangeRenderer.color = new Color(1f, 1f, 1f, tuning.SupportRangeAlpha);
|
||||
}
|
||||
UpdateEffectRange();
|
||||
return;
|
||||
}
|
||||
effectRange = rangeObject.AddComponent<LineRenderer>();
|
||||
effectRange.useWorldSpace = false;
|
||||
effectRange.loop = true;
|
||||
effectRange.positionCount = 48;
|
||||
effectRange.sharedMaterial = GetEffectMaterial();
|
||||
effectRange.sortingOrder = 3;
|
||||
Color color = GetComponent<SpriteRenderer>().color;
|
||||
Color color = GetTowerColor();
|
||||
color.a = Kind == DefenseTowerKind.Empowering ? 0.35f : 0.22f;
|
||||
effectRange.startColor = color;
|
||||
effectRange.endColor = color;
|
||||
@@ -334,10 +392,38 @@ namespace BumpCombat.Defense
|
||||
|
||||
private void UpdateEffectRange()
|
||||
{
|
||||
if (Kind == DefenseTowerKind.Empowering && supportRangeRenderer != null)
|
||||
{
|
||||
Sprite sprite = supportRangeRenderer.sprite;
|
||||
if (sprite == null) return;
|
||||
RectInt bounds = DefenseConstants.SupportRangeAlphaBounds[Mathf.Clamp(supportRangeFrame, 0, DefenseConstants.SupportRangeAlphaBounds.Length - 1)];
|
||||
Vector2 lossyScale = transform.lossyScale;
|
||||
Vector2 localScale = new(
|
||||
tuning.SupportRadius * 2f * sprite.pixelsPerUnit / (bounds.width * Mathf.Max(0.001f, lossyScale.x)),
|
||||
tuning.SupportRadius * 2f * sprite.pixelsPerUnit / (bounds.height * Mathf.Max(0.001f, lossyScale.y)));
|
||||
Color[] colors = tuning.SupportTierColors;
|
||||
Color sourceColor = colors != null && colors.Length > 0
|
||||
? colors[Mathf.Clamp(Tier - 1, 0, colors.Length - 1)]
|
||||
: new Color32(151, 169, 119, 102);
|
||||
sourceColor.a *= tuning.SupportRangeAlpha;
|
||||
supportRangeRenderer.color = sourceColor;
|
||||
Vector2 alphaCenterBottomLeft = new(
|
||||
bounds.x + bounds.width * 0.5f,
|
||||
100f - bounds.y - bounds.height * 0.5f);
|
||||
Vector2 correctionLocal = Vector2.Scale(
|
||||
(alphaCenterBottomLeft - sprite.pivot) / sprite.pixelsPerUnit, localScale);
|
||||
Vector2 shadow = DefenseTowerArtBounds.CharacterShadowOffset(
|
||||
Kind, bodyRenderer != null && bodyRenderer.flipX);
|
||||
supportRangeRenderer.transform.localPosition = shadow - correctionLocal;
|
||||
supportRangeRenderer.transform.localScale = new Vector3(localScale.x, localScale.y, 1f);
|
||||
return;
|
||||
}
|
||||
if (effectRange == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
effectRange.transform.localPosition = DefenseTowerArtBounds.CharacterShadowOffset(
|
||||
Kind, bodyRenderer != null && bodyRenderer.flipX);
|
||||
float worldScale = Mathf.Max(0.001f, transform.lossyScale.x);
|
||||
float radius = GetEffectRange() / worldScale;
|
||||
effectRange.startWidth = 0.055f / worldScale;
|
||||
@@ -352,46 +438,67 @@ namespace BumpCombat.Defense
|
||||
}
|
||||
|
||||
private void ShowAttackEffect(
|
||||
Vector2 center, float radius, bool cone, Vector2? source = null)
|
||||
Vector2 center, float radius, bool cone, Vector2? source = null,
|
||||
Vector2? visualCenter = null)
|
||||
{
|
||||
Vector2 origin = source ?? (Vector2)transform.position;
|
||||
Vector2 direction = center - origin;
|
||||
GameObject effect = new($"{Kind} Attack Effect");
|
||||
effect.transform.SetParent(transform.parent, false);
|
||||
effect.transform.position = center;
|
||||
Color color = GetComponent<SpriteRenderer>().color;
|
||||
bool shadowCenteredCircle = Kind == DefenseTowerKind.Seismic
|
||||
|| (Kind == DefenseTowerKind.Cold && Tier == 4
|
||||
&& Branch == DefenseTowerBranch.Second);
|
||||
Vector2 effectCenter = shadowCenteredCircle ? visualCenter ?? center : center;
|
||||
effect.transform.position = effectCenter;
|
||||
Color color = GetTowerColor();
|
||||
color.a = 0.9f;
|
||||
Vector2 direction = center - origin;
|
||||
|
||||
if (Kind == DefenseTowerKind.Lightning && direction.sqrMagnitude > 0.001f)
|
||||
switch (Kind)
|
||||
{
|
||||
LineRenderer bolt = CreateEffectLine(effect.transform, "Bolt", color, 0.13f);
|
||||
bolt.positionCount = 5;
|
||||
Vector2 side = new(-direction.y, direction.x);
|
||||
side.Normalize();
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
float fraction = i / 4f;
|
||||
float offset = i == 1 ? 0.22f : i == 2 ? -0.16f
|
||||
: i == 3 ? 0.18f : 0f;
|
||||
bolt.SetPosition(i, origin - center
|
||||
+ direction * fraction + side * offset);
|
||||
}
|
||||
}
|
||||
else if (Kind != DefenseTowerKind.Seismic
|
||||
&& direction.sqrMagnitude > 0.001f)
|
||||
{
|
||||
SpriteRenderer shot = CreateEffectSprite(
|
||||
effect.transform, "Shot Sprite",
|
||||
origin + direction * 0.5f,
|
||||
new Vector2(direction.magnitude,
|
||||
Kind == DefenseTowerKind.Direct ? 0.2f : 0.13f), color);
|
||||
shot.transform.rotation = Quaternion.Euler(
|
||||
0f, 0f, Mathf.Atan2(direction.y, direction.x)
|
||||
* Mathf.Rad2Deg);
|
||||
case DefenseTowerKind.Direct:
|
||||
ShowFireArt(effect.transform, origin, center, direction);
|
||||
break;
|
||||
case DefenseTowerKind.Lightning:
|
||||
Vector2 shadowOrigin = transform.TransformPoint(DefenseTowerArtBounds.CharacterShadowOffset(Kind, bodyRenderer != null && bodyRenderer.flipX));
|
||||
Vector2 impactCenter = visualCenter ?? center;
|
||||
ShowLightningArt(effect.transform, shadowOrigin, impactCenter,
|
||||
impactCenter - shadowOrigin, radius);
|
||||
break;
|
||||
case DefenseTowerKind.Cold:
|
||||
ShowColdArt(effect.transform, origin, center, direction,
|
||||
shadowCenteredCircle ? effectCenter : (Vector2?)null);
|
||||
break;
|
||||
case DefenseTowerKind.Seismic:
|
||||
string controlKey = Tier == 4
|
||||
? (Branch == DefenseTowerBranch.First
|
||||
? "Effects/Control/StoneGaze" : "Effects/Control/Temporal")
|
||||
: $"Effects/Control/T{Tier}-Aura";
|
||||
int visibleWidth = Tier == 4
|
||||
? (Branch == DefenseTowerBranch.First ? 40 : 45)
|
||||
: 32 + (Tier - 1) * 6;
|
||||
float controlScale = ScaleToVisibleWidth(radius * 2f, visibleWidth);
|
||||
CreateAnimatedArt(effect.transform, "Impact Sprite", controlKey,
|
||||
effectCenter, Vector2.one * controlScale, 0f, 13);
|
||||
break;
|
||||
case DefenseTowerKind.Summon:
|
||||
if (Tier == 4 && Branch == DefenseTowerBranch.Second)
|
||||
{
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
Vector2 spikePosition = Vector2.Lerp(origin, center, (i + 1) / 5f);
|
||||
CreateAnimatedArt(effect.transform, $"Shot Sprite {i + 1}",
|
||||
"Summons/LavaSlime/GroundSpike-Segment", spikePosition,
|
||||
Vector2.one, 0f, 14, startDelay: i * 0.12f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateAnimatedArt(effect.transform, "Impact Sprite",
|
||||
"Effects/Fire/Impact", center, Vector2.one, 0f, 13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
CreateEffectSprite(effect.transform, "Impact Sprite",
|
||||
center, new Vector2(0.42f, 0.42f), color);
|
||||
if (cone)
|
||||
{
|
||||
LineRenderer outline = CreateEffectLine(
|
||||
@@ -403,6 +510,7 @@ namespace BumpCombat.Defense
|
||||
+ (Vector2)(Quaternion.Euler(0f, 0f, -60f) * forward));
|
||||
outline.SetPosition(2, origin - center
|
||||
+ (Vector2)(Quaternion.Euler(0f, 0f, 60f) * forward));
|
||||
Destroy(outline.gameObject, 0.22f);
|
||||
}
|
||||
else if (radius > 0f)
|
||||
{
|
||||
@@ -417,26 +525,124 @@ namespace BumpCombat.Defense
|
||||
Mathf.Cos(angle) * radius,
|
||||
Mathf.Sin(angle) * radius, 0f));
|
||||
}
|
||||
Destroy(area.gameObject, 0.22f);
|
||||
}
|
||||
|
||||
Destroy(effect, 0.22f);
|
||||
Destroy(effect, 1.6f);
|
||||
}
|
||||
|
||||
private static SpriteRenderer CreateEffectSprite(
|
||||
Transform parent, string name, Vector2 position,
|
||||
Vector2 size, Color color)
|
||||
private void ShowFireArt(
|
||||
Transform parent, Vector2 origin, Vector2 center, Vector2 direction)
|
||||
{
|
||||
GameObject shape = new(name);
|
||||
shape.transform.SetParent(parent, false);
|
||||
shape.transform.position = position;
|
||||
shape.transform.localScale = new Vector3(size.x, size.y, 1f);
|
||||
SpriteRenderer renderer = shape.AddComponent<SpriteRenderer>();
|
||||
renderer.sprite = DefensePrototypeController.WhiteSprite;
|
||||
renderer.color = color;
|
||||
renderer.sortingOrder = 15;
|
||||
if (Tier == 4 && Branch == DefenseTowerBranch.First)
|
||||
{
|
||||
SpriteRenderer meteor = CreateAnimatedArt(parent, "Impact Sprite",
|
||||
"Effects/Fire/Meteor-Flight", center + new Vector2(-0.81f, 0.81f),
|
||||
Vector2.one, 0f, 13, "Effects/Fire/Meteor-Impact");
|
||||
DefenseSpriteSequence sequence = meteor.GetComponent<DefenseSpriteSequence>();
|
||||
Vector2 start = center + new Vector2(-0.81f, 0.81f);
|
||||
meteor.transform.position = start;
|
||||
sequence.Travel(start, center, 0.32f);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Tier == 4 && Branch == DefenseTowerBranch.Second)
|
||||
{
|
||||
float visibleWidth = 88f / 32f;
|
||||
float scaleX = Mathf.Max(0.1f, direction.magnitude / visibleWidth);
|
||||
CreateAnimatedArt(parent, "Shot Sprite", "Effects/Fire/Flamethrower",
|
||||
origin, new Vector2(scaleX, 1f),
|
||||
Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg,
|
||||
14);
|
||||
}
|
||||
else
|
||||
{
|
||||
string key = $"Effects/Fire/T{Tier}-Fireball";
|
||||
SpriteRenderer shot = CreateAnimatedArt(parent, "Shot Sprite", key,
|
||||
Vector2.Lerp(origin, center, 0.5f), Vector2.one * 0.85f,
|
||||
Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg, 14);
|
||||
DefenseSpriteSequence sequence = shot.GetComponent<DefenseSpriteSequence>();
|
||||
sequence.Travel(origin, center, 0.18f);
|
||||
}
|
||||
CreateAnimatedArt(parent, "Impact Sprite", "Effects/Fire/Impact",
|
||||
center, Vector2.one, 0f, 13, startDelay: 0.18f);
|
||||
}
|
||||
|
||||
private void ShowLightningArt(
|
||||
Transform parent, Vector2 origin, Vector2 center,
|
||||
Vector2 direction, float radius)
|
||||
{
|
||||
if (Tier == 4 && Branch == DefenseTowerBranch.Second)
|
||||
{
|
||||
float scale = Mathf.Max(0.8f, radius / 1.2f);
|
||||
CreateAnimatedArt(parent, "Shot Sprite", "Effects/Lightning/LightningStorm",
|
||||
center, Vector2.one * scale, 0f, 13);
|
||||
}
|
||||
else
|
||||
{
|
||||
string key = Tier == 4
|
||||
? "Effects/Lightning/Arc" : $"Effects/Lightning/T{Tier}-Bolt";
|
||||
SpriteRenderer bolt = CreateAnimatedArt(parent, "Shot Sprite", key, origin,
|
||||
Vector2.one, Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg, 14);
|
||||
bolt.GetComponent<DefenseSpriteSequence>().FitLightningBeam(origin, center, 1f);
|
||||
}
|
||||
float waveScale = Mathf.Max(0.8f, radius / 1.4f);
|
||||
SpriteRenderer wave = CreateAnimatedArt(parent, "Impact Sprite", "Effects/Lightning/ImpactWave",
|
||||
center, Vector2.one * waveScale, 0f, 13, startDelay: 0.15f);
|
||||
wave.GetComponent<DefenseSpriteSequence>().CenterImpactWave(center, waveScale);
|
||||
}
|
||||
|
||||
private void ShowColdArt(
|
||||
Transform parent, Vector2 origin, Vector2 center, Vector2 direction,
|
||||
Vector2? visualCenter = null)
|
||||
{
|
||||
if (Tier == 4 && Branch == DefenseTowerBranch.Second)
|
||||
{
|
||||
float cageScale = ScaleToVisibleWidth(3.3f, 48f);
|
||||
Vector2 cageCenter = visualCenter ?? center;
|
||||
CreateAnimatedArt(parent, "Shot Sprite", "Effects/Ice/GlacialCage-Back",
|
||||
cageCenter, Vector2.one * cageScale, 0f, 12);
|
||||
CreateAnimatedArt(parent, "Impact Sprite", "Effects/Ice/GlacialCage-Front",
|
||||
cageCenter, Vector2.one * cageScale, 0f, 14);
|
||||
return;
|
||||
}
|
||||
|
||||
string key = Tier == 4
|
||||
? "Effects/Ice/Freezebolt" : $"Effects/Ice/T{Tier}-Volley";
|
||||
SpriteRenderer shot = CreateAnimatedArt(parent, "Shot Sprite", key,
|
||||
Vector2.Lerp(origin, center, 0.5f), Vector2.one,
|
||||
Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg, 14);
|
||||
shot.GetComponent<DefenseSpriteSequence>().Travel(origin, center, 0.18f);
|
||||
CreateAnimatedArt(parent, "Impact Sprite", "Effects/Ice/Impact",
|
||||
center, Vector2.one, 0f, 13, startDelay: 0.18f);
|
||||
}
|
||||
|
||||
private static SpriteRenderer CreateAnimatedArt(
|
||||
Transform parent, string name, string key, Vector2 position,
|
||||
Vector2 scale, float rotation, int sortingOrder,
|
||||
string nextKey = null, float startDelay = 0f)
|
||||
{
|
||||
GameObject spriteObject = new(name);
|
||||
spriteObject.transform.SetParent(parent, false);
|
||||
spriteObject.transform.position = position;
|
||||
spriteObject.transform.localScale = new Vector3(scale.x, scale.y, 1f);
|
||||
spriteObject.transform.rotation = Quaternion.Euler(0f, 0f, rotation);
|
||||
SpriteRenderer renderer = spriteObject.AddComponent<SpriteRenderer>();
|
||||
renderer.color = Color.white;
|
||||
renderer.sortingOrder = sortingOrder;
|
||||
Sprite[] frames = DefenseTowerArt.Load(key);
|
||||
Sprite[] next = string.IsNullOrEmpty(nextKey)
|
||||
? null : DefenseTowerArt.Load(nextKey);
|
||||
spriteObject.AddComponent<DefenseSpriteSequence>().Play(
|
||||
renderer, key, frames, true, nextKey, next, startDelay);
|
||||
return renderer;
|
||||
}
|
||||
|
||||
private static float ScaleToVisibleWidth(float worldWidth, float visiblePixels)
|
||||
{
|
||||
return worldWidth / (visiblePixels / 32f);
|
||||
}
|
||||
|
||||
private static LineRenderer CreateEffectLine(
|
||||
Transform parent, string name, Color color, float width)
|
||||
{
|
||||
@@ -479,32 +685,23 @@ namespace BumpCombat.Defense
|
||||
|
||||
private void CreateVisuals()
|
||||
{
|
||||
SpriteRenderer renderer = gameObject.GetComponent<SpriteRenderer>();
|
||||
if (renderer == null)
|
||||
SpriteRenderer rootRenderer = gameObject.GetComponent<SpriteRenderer>();
|
||||
if (rootRenderer == null)
|
||||
{
|
||||
renderer = gameObject.AddComponent<SpriteRenderer>();
|
||||
rootRenderer = gameObject.AddComponent<SpriteRenderer>();
|
||||
}
|
||||
renderer.sprite = DefensePrototypeController.WhiteSprite;
|
||||
renderer.color = Kind switch
|
||||
{
|
||||
DefenseTowerKind.Direct => new Color32(240, 96, 65, 255),
|
||||
DefenseTowerKind.Summon => new Color32(244, 154, 74, 255),
|
||||
DefenseTowerKind.Lightning => new Color32(238, 218, 83, 255),
|
||||
DefenseTowerKind.Cold => new Color32(65, 211, 240, 255),
|
||||
DefenseTowerKind.Seismic => new Color32(175, 141, 100, 255),
|
||||
_ => new Color32(148, 228, 122, 255),
|
||||
};
|
||||
renderer.sortingOrder = 8;
|
||||
rootRenderer.sprite = DefensePrototypeController.WhiteSprite;
|
||||
rootRenderer.color = Color.white;
|
||||
rootRenderer.enabled = false;
|
||||
transform.localScale = new Vector3(0.72f, 0.72f, 1f);
|
||||
|
||||
GameObject crown = new("Tower Crown");
|
||||
crown.transform.SetParent(transform, false);
|
||||
crown.transform.localPosition = new Vector3(0f, 0.42f, 0f);
|
||||
crown.transform.localScale = new Vector3(0.42f, 0.22f, 1f);
|
||||
SpriteRenderer crownRenderer = crown.AddComponent<SpriteRenderer>();
|
||||
crownRenderer.sprite = DefensePrototypeController.WhiteSprite;
|
||||
crownRenderer.color = Color.white;
|
||||
crownRenderer.sortingOrder = 9;
|
||||
GameObject bodyObject = new("Tower Body");
|
||||
bodyObject.transform.SetParent(transform, false);
|
||||
bodyObject.transform.localScale = new Vector3(2.1f, 2.1f, 1f);
|
||||
bodyRenderer = bodyObject.AddComponent<SpriteRenderer>();
|
||||
bodyRenderer.color = Color.white;
|
||||
bodyRenderer.sortingOrder = 8;
|
||||
UpdateBodyArt();
|
||||
|
||||
GameObject labelObject = new("Tower Label");
|
||||
labelObject.transform.SetParent(transform, false);
|
||||
@@ -520,6 +717,144 @@ namespace BumpCombat.Defense
|
||||
CreateEffectRange();
|
||||
}
|
||||
|
||||
private void UpdateBodyArt()
|
||||
{
|
||||
bodyIdleKey = DefenseTowerArt.BodyKey(Kind, Tier, "Idle");
|
||||
bodyIdleFrames = DefenseTowerArt.Load(bodyIdleKey);
|
||||
bodyAttackKey = Kind switch
|
||||
{
|
||||
DefenseTowerKind.Direct or DefenseTowerKind.Lightning or DefenseTowerKind.Cold
|
||||
=> DefenseTowerArt.BodyKey(Kind, Tier, "Attack01"),
|
||||
DefenseTowerKind.Seismic => DefenseTowerArt.BodyKey(Kind, Tier, "Attack"),
|
||||
DefenseTowerKind.Empowering => DefenseTowerArt.BodyKey(Kind, Tier, "Heal"),
|
||||
_ => null,
|
||||
};
|
||||
bodyAttackFrames = bodyAttackKey == null
|
||||
? System.Array.Empty<Sprite>() : DefenseTowerArt.Load(bodyAttackKey);
|
||||
bodyAttacking = false;
|
||||
bodyFrame = 0;
|
||||
bodyFrameTime = 0f;
|
||||
if (bodyRenderer != null && bodyIdleFrames.Length > 0)
|
||||
{
|
||||
bodyRenderer.sprite = bodyIdleFrames[0];
|
||||
}
|
||||
}
|
||||
|
||||
private void PlayBodyAttack(bool faceLeft)
|
||||
{
|
||||
if (bodyRenderer == null || bodyAttackFrames == null || bodyAttackFrames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bodyRenderer.flipX = faceLeft;
|
||||
bodyAttacking = true;
|
||||
bodyFrame = 0;
|
||||
bodyFrameTime = 0f;
|
||||
bodyRenderer.sprite = bodyAttackFrames[0];
|
||||
}
|
||||
|
||||
private void UpdateBodyAnimation()
|
||||
{
|
||||
if (bodyRenderer == null || bodyIdleFrames == null || bodyIdleFrames.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
bodyFrameTime += Time.deltaTime;
|
||||
Sprite[] frames = bodyAttacking ? bodyAttackFrames : bodyIdleFrames;
|
||||
string key = bodyAttacking ? bodyAttackKey : bodyIdleKey;
|
||||
if (frames == null || frames.Length == 0)
|
||||
{
|
||||
bodyAttacking = false;
|
||||
return;
|
||||
}
|
||||
while (bodyFrameTime >= DefenseTowerArt.FrameSeconds(key, bodyFrame))
|
||||
{
|
||||
bodyFrameTime -= DefenseTowerArt.FrameSeconds(key, bodyFrame);
|
||||
bodyFrame++;
|
||||
if (bodyFrame >= frames.Length)
|
||||
{
|
||||
if (bodyAttacking)
|
||||
{
|
||||
bodyAttacking = false;
|
||||
bodyFrame = 0;
|
||||
frames = bodyIdleFrames;
|
||||
key = bodyIdleKey;
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyFrame = 0;
|
||||
}
|
||||
}
|
||||
bodyRenderer.sprite = frames[bodyFrame];
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSupportAura()
|
||||
{
|
||||
DefenseTower support = owner != null ? owner.GetActiveSupportFor(this) : null;
|
||||
if (support == null)
|
||||
{
|
||||
if (supportAuraRenderer != null) supportAuraRenderer.enabled = false;
|
||||
return;
|
||||
}
|
||||
if (supportAuraRenderer == null)
|
||||
{
|
||||
GameObject auraObject = new("Support Aura Art");
|
||||
auraObject.transform.SetParent(transform, false);
|
||||
supportAuraRenderer = auraObject.AddComponent<SpriteRenderer>();
|
||||
}
|
||||
string motionKey = bodyAttacking && !string.IsNullOrEmpty(bodyAttackKey) ? bodyAttackKey : bodyIdleKey;
|
||||
string key = motionKey.Replace("Bodies/", "SupportMasks/");
|
||||
if (supportAuraKey != key)
|
||||
{
|
||||
supportAuraKey = key;
|
||||
supportAuraFrames = DefenseTowerArt.Load(key);
|
||||
}
|
||||
int maskFrame = 0;
|
||||
if (bodyRenderer != null && supportAuraFrames != null)
|
||||
{
|
||||
for (int i = 0; i < supportAuraFrames.Length; i++)
|
||||
if (supportAuraFrames[i].name == bodyRenderer.sprite.name) { maskFrame = i; break; }
|
||||
}
|
||||
supportAuraRenderer.sprite = supportAuraFrames != null && supportAuraFrames.Length > maskFrame
|
||||
? supportAuraFrames[maskFrame] : null;
|
||||
supportAuraRenderer.flipX = bodyRenderer != null && bodyRenderer.flipX;
|
||||
supportAuraRenderer.transform.localPosition = bodyRenderer != null ? bodyRenderer.transform.localPosition : Vector3.zero;
|
||||
supportAuraRenderer.transform.localScale = bodyRenderer != null ? bodyRenderer.transform.localScale : Vector3.one;
|
||||
supportAuraRenderer.sortingOrder = bodyRenderer != null ? bodyRenderer.sortingOrder + 1 : 9;
|
||||
Color[] colors = tuning.SupportTierColors;
|
||||
supportAuraRenderer.color = colors != null && colors.Length > 0
|
||||
? colors[Mathf.Clamp(support.Tier - 1, 0, colors.Length - 1)]
|
||||
: new Color32(151, 169, 119, 102);
|
||||
supportAuraRenderer.enabled = supportAuraFrames != null && supportAuraFrames.Length > 0 && supportAuraRenderer.sprite != null;
|
||||
}
|
||||
|
||||
private void UpdateSupportRangeAnimation()
|
||||
{
|
||||
if (supportRangeRenderer == null || supportRangeFrames == null || supportRangeFrames.Length == 0) return;
|
||||
supportRangeFrameTime += Time.deltaTime;
|
||||
while (supportRangeFrameTime >= 0.14f)
|
||||
{
|
||||
supportRangeFrameTime -= 0.14f;
|
||||
supportRangeFrame = (supportRangeFrame + 1) % supportRangeFrames.Length;
|
||||
supportRangeRenderer.sprite = supportRangeFrames[supportRangeFrame];
|
||||
UpdateEffectRange();
|
||||
}
|
||||
}
|
||||
|
||||
private Color GetTowerColor()
|
||||
{
|
||||
return Kind switch
|
||||
{
|
||||
DefenseTowerKind.Direct => new Color32(240, 96, 65, 255),
|
||||
DefenseTowerKind.Summon => new Color32(244, 154, 74, 255),
|
||||
DefenseTowerKind.Lightning => new Color32(238, 218, 83, 255),
|
||||
DefenseTowerKind.Cold => new Color32(65, 211, 240, 255),
|
||||
DefenseTowerKind.Seismic => new Color32(175, 141, 100, 255),
|
||||
_ => new Color32(148, 228, 122, 255),
|
||||
};
|
||||
}
|
||||
|
||||
private void UpdateLabel()
|
||||
{
|
||||
if (label == null)
|
||||
|
||||
Reference in New Issue
Block a user