Initial commit: Tiny Tackle Heroes Unity project

This commit is contained in:
2026-09-15 17:37:42 +09:00
commit 591fa9a826
1623 changed files with 182420 additions and 0 deletions
@@ -0,0 +1,263 @@
using System.Collections.Generic;
using BumpCombat.Combat;
using BumpCombat.Core;
using BumpCombat.Constants;
using BumpCombat.Enemies;
using UnityEngine;
namespace BumpCombat.Player
{
[DisallowMultipleComponent]
public sealed class PlayerStats : CharacterModel
{
private const string PressureStreakSourceId = "player.pressure-streak";
private readonly HashSet<int> pressureStreakProcessedEnemyLifetimes = new();
private RunManager subscribedRunManager;
private PlayerHealth playerHealth;
private float pressureStreakExpiresAt = -1f;
private float pressureStreakLastKillAt = -1f;
public override float MoveSpeed => Evaluate(
CharacterStat.MoveSpeed,
GameplayConstants.Current.Player.BaseMoveSpeed);
public override float MaxHealth => Evaluate(
CharacterStat.MaxHealth,
GameplayConstants.Current.Player.BaseMaxHealth);
public int PressureStreakKillCount { get; private set; }
public int PressureStreakKillTarget => Mathf.Max(
1,
GameplayConstants.Current.Player.PressureStreakKillTarget);
public float PressureStreakRemaining => Mathf.Max(
0f,
pressureStreakExpiresAt - GetCombatTime());
public bool IsPressureStreakWindowActive => PressureStreakRemaining > 0f;
public bool IsPressureStreakActive => IsPressureStreakWindowActive
&& GetStackCount(
PressureStreakSourceId,
CharacterStat.MoveSpeed) > 0;
private void OnEnable()
{
playerHealth = GetComponent<PlayerHealth>();
if (playerHealth != null)
{
playerHealth.OnDied += HandlePlayerDied;
}
CombatEvents.OnValidHit += HandleValidHit;
RefreshRunManagerSubscription();
}
private void Start()
{
RefreshRunManagerSubscription();
if (subscribedRunManager != null
&& !subscribedRunManager.IsTitleScreen
&& !subscribedRunManager.IsGameOver)
{
HandleRunStarted();
}
}
private void Update()
{
RefreshRunManagerSubscription();
RefreshPressureStreak();
}
private void OnDisable()
{
CombatEvents.OnValidHit -= HandleValidHit;
if (playerHealth != null)
{
playerHealth.OnDied -= HandlePlayerDied;
}
UnsubscribeRunManager();
ResetPressureStreak();
}
private void OnDestroy()
{
CombatEvents.OnValidHit -= HandleValidHit;
if (playerHealth != null)
{
playerHealth.OnDied -= HandlePlayerDied;
}
UnsubscribeRunManager();
}
public void RefreshPressureStreak()
{
float now = GetCombatTime();
PrunePressureStreak(now);
if (pressureStreakExpiresAt > 0f && now >= pressureStreakExpiresAt)
{
ClearPressureStreakBuff();
}
}
private void HandleValidHit(CombatHitResult result)
{
if (result.Attacker != gameObject
|| !result.IsOrdinaryBump
|| result.IsArtifactHit
|| result.Damage <= 0f
|| result.Target == null)
{
return;
}
if ((playerHealth != null && playerHealth.CurrentHealth <= 0f)
|| RunManager.Instance?.IsGameOver == true)
{
return;
}
EnemyController enemy = result.Target.GetComponent<EnemyController>()
?? result.Target.GetComponentInParent<EnemyController>();
if (enemy == null || !enemy.IsDead)
{
return;
}
int lifetimeIdentity = enemy.SpawnLifetimeIdentity;
if (lifetimeIdentity <= 0)
{
lifetimeIdentity = enemy.GetInstanceID();
}
if (!pressureStreakProcessedEnemyLifetimes.Add(lifetimeIdentity))
{
return;
}
float now = GetCombatTime();
RefreshPressureStreak();
if (pressureStreakLastKillAt < 0f
|| now - pressureStreakLastKillAt
> Mathf.Max(
0f,
GameplayConstants.Current.Player.PressureStreakWindowSeconds))
{
PressureStreakKillCount = 0;
}
PressureStreakKillCount++;
pressureStreakLastKillAt = now;
pressureStreakExpiresAt = now
+ Mathf.Max(
0f,
GameplayConstants.Current.Player.PressureStreakDuration);
if (PressureStreakKillCount >= PressureStreakKillTarget)
{
EnsurePressureStreakBuff();
}
else
{
RemoveModifiersFromSource(PressureStreakSourceId);
}
}
private void PrunePressureStreak(float now)
{
if (pressureStreakLastKillAt >= 0f
&& now - pressureStreakLastKillAt
> Mathf.Max(
0f,
GameplayConstants.Current.Player.PressureStreakWindowSeconds))
{
PressureStreakKillCount = 0;
pressureStreakLastKillAt = -1f;
ClearPressureStreakBuff();
}
}
private void EnsurePressureStreakBuff()
{
if (GetStackCount(
PressureStreakSourceId,
CharacterStat.MoveSpeed) > 0)
{
return;
}
AddModifier(new StatModifier(
PressureStreakSourceId,
CharacterStat.MoveSpeed,
ModifierOperation.Increased,
Mathf.Max(
0f,
GameplayConstants.Current.Player.PressureStreakMoveSpeedIncrease)));
}
private void ClearPressureStreakBuff()
{
RemoveModifiersFromSource(PressureStreakSourceId);
pressureStreakExpiresAt = -1f;
}
private void ResetPressureStreak()
{
pressureStreakProcessedEnemyLifetimes.Clear();
PressureStreakKillCount = 0;
pressureStreakLastKillAt = -1f;
ClearPressureStreakBuff();
}
private void HandlePlayerDied()
{
ResetPressureStreak();
}
private void HandleRunStarted()
{
ResetPressureStreak();
}
private void HandleGameOver()
{
ResetPressureStreak();
}
private float GetCombatTime()
{
RunManager current = RunManager.Instance;
return current != null ? current.ElapsedTime : Time.time;
}
private void RefreshRunManagerSubscription()
{
RunManager current = RunManager.Instance;
if (subscribedRunManager == current)
{
return;
}
UnsubscribeRunManager();
subscribedRunManager = current;
if (subscribedRunManager == null)
{
return;
}
subscribedRunManager.OnRunStarted += HandleRunStarted;
subscribedRunManager.OnGameOver += HandleGameOver;
}
private void UnsubscribeRunManager()
{
if (subscribedRunManager == null)
{
return;
}
subscribedRunManager.OnRunStarted -= HandleRunStarted;
subscribedRunManager.OnGameOver -= HandleGameOver;
subscribedRunManager = null;
}
}
}