|
| 1 | +using System.Collections; |
1 | 2 | using System.Collections.Generic; |
2 | 3 | using System.Linq; |
3 | 4 | using _project.Scripts.Core; |
| 5 | +using DG.Tweening; |
4 | 6 | using TMPro; |
5 | 7 | using UnityEngine; |
6 | 8 |
|
@@ -44,27 +46,26 @@ public void ResetMoneys() |
44 | 46 |
|
45 | 47 | public static void UpdateMoneysText(int modifier = 0) |
46 | 48 | { |
47 | | - if (CardGameMaster.Instance.moneysText) |
48 | | - { |
49 | | - var turnController = CardGameMaster.Instance.turnController; |
50 | | - var displayMoney = Moneys + modifier; |
51 | | - |
52 | | - // Display different format based on game mode |
53 | | - if (turnController.currentGameMode == GameMode.Campaign) |
54 | | - { |
55 | | - CardGameMaster.Instance.moneysText!.text = |
56 | | - $"Money: ${displayMoney} Rent Due: ${turnController.moneyGoal}"; |
57 | | - } |
58 | | - else |
59 | | - { |
60 | | - // Tutorial or Endless mode - original format |
61 | | - CardGameMaster.Instance.moneysText!.text = |
62 | | - "Moneys: " + "$" + displayMoney + "/" + turnController.moneyGoal; |
63 | | - } |
64 | | - } |
| 49 | + var displayMoney = Moneys + modifier; |
| 50 | + var turnController = CardGameMaster.Instance ? CardGameMaster.Instance.turnController : null; |
| 51 | + |
| 52 | + if (CardGameMaster.Instance.moneysText && turnController) |
| 53 | + CardGameMaster.Instance.moneysText!.text = FormatMainMoneyText(displayMoney, turnController); |
65 | 54 |
|
66 | 55 | if (CardGameMaster.Instance.shopMoneyText) |
67 | | - CardGameMaster.Instance.shopMoneyText!.text = "Moneys: " + "$" + (Moneys + modifier); |
| 56 | + CardGameMaster.Instance.shopMoneyText!.text = FormatShopMoneyText(displayMoney); |
| 57 | + } |
| 58 | + |
| 59 | + private static string FormatMainMoneyText(int value, TurnController tc) |
| 60 | + { |
| 61 | + return tc.currentGameMode == GameMode.Campaign |
| 62 | + ? $"Money: ${value} Rent Due: ${tc.moneyGoal}" |
| 63 | + : $"Moneys: ${value}/{tc.moneyGoal}"; |
| 64 | + } |
| 65 | + |
| 66 | + private static string FormatShopMoneyText(int value) |
| 67 | + { |
| 68 | + return $"Moneys: ${value}"; |
68 | 69 | } |
69 | 70 |
|
70 | 71 | public static void SetScore(int newScore) |
@@ -187,5 +188,115 @@ private int CalculateBonuses() |
187 | 188 | var totalBonus = bonuses.Sum(b => b.BonusValue); |
188 | 189 | return totalBonus; |
189 | 190 | } |
| 191 | + |
| 192 | + #region Score Animation |
| 193 | + |
| 194 | + private static readonly Color GainColor = new(0.2f, 0.9f, 0.3f); |
| 195 | + private static readonly Color LossColor = new(0.95f, 0.25f, 0.2f); |
| 196 | + private const float CountDuration = 1.0f; |
| 197 | + private const float PunchScale = 0.35f; |
| 198 | + private const float PunchDuration = 1.5f; |
| 199 | + |
| 200 | + private Sequence _moneyAnimSequence; |
| 201 | + |
| 202 | + private void SafeKillSequence() |
| 203 | + { |
| 204 | + if (_moneyAnimSequence != null && _moneyAnimSequence.IsActive()) |
| 205 | + _moneyAnimSequence.Kill(); |
| 206 | + _moneyAnimSequence = null; |
| 207 | + } |
| 208 | + |
| 209 | + private void OnDestroy() => SafeKillSequence(); |
| 210 | + |
| 211 | + /// <summary> |
| 212 | + /// Animates the money display from <paramref name="previousScore" /> to <paramref name="finalScore" /> |
| 213 | + /// with a counting tween, color flash, and scale punch. |
| 214 | + /// Call after <see cref="CalculateScore" /> which sets the canonical value. |
| 215 | + /// </summary> |
| 216 | + public IEnumerator AnimateScoreChange(int previousScore, int finalScore) |
| 217 | + { |
| 218 | + var delta = finalScore - previousScore; |
| 219 | + if (delta == 0) yield break; |
| 220 | + |
| 221 | + SafeKillSequence(); |
| 222 | + |
| 223 | + var moneysText = CardGameMaster.Instance ? CardGameMaster.Instance.moneysText : null; |
| 224 | + var shopText = CardGameMaster.Instance ? CardGameMaster.Instance.shopMoneyText : null; |
| 225 | + |
| 226 | + if (!moneysText && !shopText) yield break; |
| 227 | + |
| 228 | + var flashColor = delta > 0 ? GainColor : LossColor; |
| 229 | + var displayValue = previousScore; |
| 230 | + |
| 231 | + // Reset display to old value — CalculateScore already set the final value |
| 232 | + SetMoneyTextRaw(previousScore); |
| 233 | + |
| 234 | + var seq = DOTween.Sequence(); |
| 235 | + var origMainColor = moneysText ? moneysText.color : Color.white; |
| 236 | + var origShopColor = shopText ? shopText.color : Color.white; |
| 237 | + |
| 238 | + // Set flash color immediately |
| 239 | + if (moneysText) moneysText.color = flashColor; |
| 240 | + if (shopText) shopText.color = flashColor; |
| 241 | + |
| 242 | + // Count tween: animate displayValue from previous to final |
| 243 | + seq.Append( |
| 244 | + DOTween.To(() => displayValue, x => |
| 245 | + { |
| 246 | + displayValue = x; |
| 247 | + SetMoneyTextRaw(x); |
| 248 | + }, finalScore, CountDuration) |
| 249 | + .SetEase(Ease.OutQuart) |
| 250 | + ); |
| 251 | + |
| 252 | + // Scale punch runs in parallel with count |
| 253 | + if (moneysText) |
| 254 | + seq.Join(moneysText.transform.DOPunchScale(Vector3.one * PunchScale, PunchDuration, 6, 0.7f)); |
| 255 | + if (shopText) |
| 256 | + seq.Join(shopText.transform.DOPunchScale(Vector3.one * PunchScale, PunchDuration, 6, 0.7f)); |
| 257 | + |
| 258 | + // Color fades back to the original over the count duration |
| 259 | + if (moneysText) |
| 260 | + seq.Join( |
| 261 | + DOTween.To(() => moneysText.color, c => moneysText.color = c, origMainColor, CountDuration) |
| 262 | + .SetEase(Ease.InQuad) |
| 263 | + ); |
| 264 | + if (shopText) |
| 265 | + seq.Join( |
| 266 | + DOTween.To(() => shopText.color, c => shopText.color = c, origShopColor, CountDuration) |
| 267 | + .SetEase(Ease.InQuad) |
| 268 | + ); |
| 269 | + |
| 270 | + // Ensure canonical final state |
| 271 | + var complete = false; |
| 272 | + seq.OnComplete(() => |
| 273 | + { |
| 274 | + if (moneysText) moneysText.color = origMainColor; |
| 275 | + if (shopText) shopText.color = origShopColor; |
| 276 | + UpdateMoneysText(); |
| 277 | + complete = true; |
| 278 | + }); |
| 279 | + |
| 280 | + seq.SetLink(gameObject, LinkBehaviour.KillOnDisable); |
| 281 | + _moneyAnimSequence = seq; |
| 282 | + |
| 283 | + // Wait for the sequence to actually finish (robust against timing changes) |
| 284 | + yield return new WaitUntil(() => complete || _moneyAnimSequence == null); |
| 285 | + } |
| 286 | + |
| 287 | + private static void SetMoneyTextRaw(int displayValue) |
| 288 | + { |
| 289 | + var moneysText = CardGameMaster.Instance ? CardGameMaster.Instance.moneysText : null; |
| 290 | + var shopText = CardGameMaster.Instance ? CardGameMaster.Instance.shopMoneyText : null; |
| 291 | + var turnController = CardGameMaster.Instance ? CardGameMaster.Instance.turnController : null; |
| 292 | + |
| 293 | + if (moneysText && turnController) |
| 294 | + moneysText.text = FormatMainMoneyText(displayValue, turnController); |
| 295 | + |
| 296 | + if (shopText) |
| 297 | + shopText.text = FormatShopMoneyText(displayValue); |
| 298 | + } |
| 299 | + |
| 300 | + #endregion |
190 | 301 | } |
191 | 302 | } |
0 commit comments