convert game playing..
This commit is contained in:
@@ -5,6 +5,7 @@ using BumpCombat.Combat;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Core;
|
||||
using BumpCombat.Enemies;
|
||||
using BumpCombat.Progression;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
@@ -339,6 +340,13 @@ namespace BumpCombat.Player
|
||||
{
|
||||
UpdateTemporaryPlayerModifiers();
|
||||
|
||||
if (playerHealth != null && playerHealth.CurrentHealth <= 0f)
|
||||
{
|
||||
CancelArtifactInput();
|
||||
CancelExecution();
|
||||
return;
|
||||
}
|
||||
|
||||
if (playerHealth != null && playerHealth.IsHurtMovementLocked)
|
||||
{
|
||||
CancelArtifactInput();
|
||||
@@ -450,6 +458,7 @@ namespace BumpCombat.Player
|
||||
if (definition == null
|
||||
|| IsExecutingArtifact
|
||||
|| (dashController != null && dashController.IsDashing)
|
||||
|| (playerHealth != null && playerHealth.CurrentHealth <= 0f)
|
||||
|| (playerHealth != null && playerHealth.IsHurtMovementLocked)
|
||||
|| (charged && !IsArtifactChargeUnlocked))
|
||||
{
|
||||
@@ -546,7 +555,7 @@ namespace BumpCombat.Player
|
||||
return true;
|
||||
}
|
||||
|
||||
private int GetOwnedArtifactColorCount(ArtifactColor color)
|
||||
public int GetOwnedArtifactColorCount(ArtifactColor color)
|
||||
{
|
||||
int count = 0;
|
||||
for (int i = 0; i < ownedArtifacts.Count; i++)
|
||||
@@ -1168,6 +1177,7 @@ namespace BumpCombat.Player
|
||||
float nextHitAt = 0f;
|
||||
int hitIndex = 0;
|
||||
int hitCount = GetScheduledHitCount(duration, hitInterval);
|
||||
bool movementBlocked = false;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
visual.transform.position = body.position
|
||||
@@ -1206,10 +1216,24 @@ namespace BumpCombat.Player
|
||||
|
||||
yield return new WaitForFixedUpdate();
|
||||
elapsed += Time.fixedDeltaTime;
|
||||
body.MovePosition(Vector2.Lerp(
|
||||
start,
|
||||
end,
|
||||
Mathf.Clamp01(elapsed / duration)));
|
||||
if (!movementBlocked
|
||||
&& playerController != null
|
||||
&& !playerController.IsTransitionLocked)
|
||||
{
|
||||
Vector2 intendedEnd = Vector2.Lerp(
|
||||
start,
|
||||
end,
|
||||
Mathf.Clamp01(elapsed / duration));
|
||||
Vector2 clampedEnd = playerController.ClampMovementToTerrain(
|
||||
body.position,
|
||||
intendedEnd);
|
||||
movementBlocked = (clampedEnd - intendedEnd).sqrMagnitude > 0.0001f;
|
||||
body.MovePosition(clampedEnd);
|
||||
}
|
||||
else if (playerController != null && playerController.IsTransitionLocked)
|
||||
{
|
||||
movementBlocked = true;
|
||||
}
|
||||
visual.transform.position = body.position
|
||||
+ GetCycloneShadowOffsetWorld();
|
||||
}
|
||||
@@ -1236,7 +1260,6 @@ namespace BumpCombat.Player
|
||||
hitIndex++;
|
||||
}
|
||||
|
||||
body.position = end;
|
||||
DestroyVisual(visual);
|
||||
EndExecution();
|
||||
}
|
||||
@@ -2190,6 +2213,134 @@ namespace BumpCombat.Player
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryEmitPassiveRelicEcho(
|
||||
Vector2 origin,
|
||||
Vector2 direction,
|
||||
float damageMultiplier)
|
||||
{
|
||||
ActiveArtifactDefinition definition = CurrentArtifact;
|
||||
if (definition == null
|
||||
|| definition.NormalDamage <= 0f
|
||||
|| damageMultiplier <= 0f
|
||||
|| definition.Effect == ActiveArtifactEffect.Pulse)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (direction.sqrMagnitude < MinimumDirectionSqrMagnitude)
|
||||
{
|
||||
direction = definition.Effect == ActiveArtifactEffect.ThunderCrash
|
||||
? GetThunderCrashDirection()
|
||||
: GetUseDirection();
|
||||
}
|
||||
direction.Normalize();
|
||||
int castIdentity = ++nextArtifactCastIdentity;
|
||||
float echoIgniteTickDamage = playerStats.CalculateDamage(
|
||||
definition.NormalIgniteTickDamage * damageMultiplier,
|
||||
definition.DamageTag);
|
||||
if (definition.Effect == ActiveArtifactEffect.ChainLightning)
|
||||
{
|
||||
EnemyController first = FindInitialChainTarget(origin, definition.NormalRange);
|
||||
if (first == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool applied = false;
|
||||
foreach (ChainStrike strike in PlanArcChain(definition, first, origin))
|
||||
{
|
||||
applied |= ApplyArtifactHit(
|
||||
strike.Enemy,
|
||||
definition,
|
||||
strike.Damage * damageMultiplier,
|
||||
(strike.End - strike.Start).normalized,
|
||||
strike.Knockback,
|
||||
true,
|
||||
false,
|
||||
echoIgniteTickDamage,
|
||||
castIdentity,
|
||||
true);
|
||||
}
|
||||
|
||||
return applied;
|
||||
}
|
||||
|
||||
Vector2 center = origin;
|
||||
Vector2 radii = new Vector2(definition.NormalRange, definition.NormalRange);
|
||||
float rayRange = definition.NormalRange;
|
||||
float rayHalfWidth = Mathf.Max(0.05f, definition.NormalWidth * 0.5f);
|
||||
switch (definition.Effect)
|
||||
{
|
||||
case ActiveArtifactEffect.Cyclone:
|
||||
center += GetCycloneHitCenterOffset(definition.NormalRange, false);
|
||||
radii = GetCycloneHitRadii(definition.NormalRange, false);
|
||||
break;
|
||||
case ActiveArtifactEffect.ThunderCrash:
|
||||
center = origin + direction * 0.45f;
|
||||
radii = GetThunderCrashHitRadii(definition.NormalRange, direction);
|
||||
break;
|
||||
}
|
||||
|
||||
List<EnemyController> enemies = FindNearestTargets(
|
||||
definition.Effect == ActiveArtifactEffect.Dash
|
||||
|| definition.Effect == ActiveArtifactEffect.Phoenix
|
||||
? origin + direction * (rayRange * 0.5f)
|
||||
: center,
|
||||
Mathf.Max(radii.x, radii.y, rayRange),
|
||||
null);
|
||||
int hitCount = 0;
|
||||
bool anyDamage = false;
|
||||
foreach (EnemyController enemy in enemies)
|
||||
{
|
||||
if (hitCount >= definition.NormalMaxTargets)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Vector2 position = enemy.GroundAnchorPosition;
|
||||
Vector2 offset = position - origin;
|
||||
bool inside = definition.Effect switch
|
||||
{
|
||||
ActiveArtifactEffect.Dash or ActiveArtifactEffect.Phoenix =>
|
||||
Vector2.Dot(offset, direction) >= 0f
|
||||
&& Vector2.Dot(offset, direction) <= rayRange
|
||||
&& Mathf.Abs(Vector2.Dot(offset, new Vector2(-direction.y, direction.x))) <= rayHalfWidth,
|
||||
_ => GetEllipseDistance(position - center, radii) <= 1.0001f,
|
||||
};
|
||||
if (!inside)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector2 hitDirection = definition.Effect == ActiveArtifactEffect.Dash
|
||||
|| definition.Effect == ActiveArtifactEffect.Phoenix
|
||||
? direction
|
||||
: (position - center).normalized;
|
||||
if (hitDirection.sqrMagnitude < MinimumDirectionSqrMagnitude)
|
||||
{
|
||||
hitDirection = direction;
|
||||
}
|
||||
|
||||
bool damaged = ApplyArtifactHit(
|
||||
enemy,
|
||||
definition,
|
||||
definition.NormalDamage * damageMultiplier,
|
||||
hitDirection,
|
||||
definition.NormalKnockback,
|
||||
true,
|
||||
false,
|
||||
echoIgniteTickDamage,
|
||||
castIdentity,
|
||||
true);
|
||||
anyDamage |= damaged;
|
||||
if (damaged)
|
||||
{
|
||||
hitCount++;
|
||||
}
|
||||
}
|
||||
|
||||
return anyDamage;
|
||||
}
|
||||
private bool ApplyArtifactHit(
|
||||
EnemyController enemy,
|
||||
ActiveArtifactDefinition definition,
|
||||
@@ -2199,7 +2350,8 @@ namespace BumpCombat.Player
|
||||
bool showsHitFeedback = true,
|
||||
bool charged = false,
|
||||
float igniteTickDamage = 0f,
|
||||
int castIdentity = 0)
|
||||
int castIdentity = 0,
|
||||
bool passiveRelicEcho = false)
|
||||
{
|
||||
if (enemy == null || enemy.IsDead)
|
||||
{
|
||||
@@ -2219,7 +2371,9 @@ namespace BumpCombat.Player
|
||||
direction,
|
||||
knockback,
|
||||
definition.ArtifactColor,
|
||||
castIdentity);
|
||||
castIdentity,
|
||||
true,
|
||||
EnemyDamageSource.Player);
|
||||
if (result == EnemyArtifactHitResult.Rejected)
|
||||
{
|
||||
return false;
|
||||
@@ -2233,7 +2387,7 @@ namespace BumpCombat.Player
|
||||
|
||||
if (result == EnemyArtifactHitResult.Shielded)
|
||||
{
|
||||
if (enemy.LastArtifactContactCounted)
|
||||
if (!passiveRelicEcho && enemy.LastArtifactContactCounted)
|
||||
{
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
enemy.gameObject,
|
||||
@@ -2262,7 +2416,7 @@ namespace BumpCombat.Player
|
||||
igniteTickDamage);
|
||||
}
|
||||
|
||||
if (damageWasApplied)
|
||||
if (damageWasApplied && !passiveRelicEcho)
|
||||
{
|
||||
CombatEvents.RaiseArtifactEffectiveHit(
|
||||
enemy.gameObject,
|
||||
@@ -2283,7 +2437,7 @@ namespace BumpCombat.Player
|
||||
false,
|
||||
false,
|
||||
definition.DamageTag,
|
||||
definition.ArtifactId,
|
||||
passiveRelicEcho ? PassiveRelicController.EchoSourceId : definition.ArtifactId,
|
||||
false,
|
||||
result == EnemyArtifactHitResult.DamageApplied
|
||||
&& showsHitFeedback,
|
||||
|
||||
@@ -44,7 +44,8 @@ namespace BumpCombat.Player
|
||||
|| IsDashing
|
||||
|| playerController == null
|
||||
|| playerController.IsDamageKnockbackActive
|
||||
|| playerController.IsHurtMovementLocked)
|
||||
|| playerController.IsHurtMovementLocked
|
||||
|| playerController.IsTransitionLocked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -81,19 +82,29 @@ namespace BumpCombat.Player
|
||||
: dashDuration;
|
||||
Vector2 start = body.position;
|
||||
Vector2 end = playerController.ClampToArena(start + direction * distance);
|
||||
Vector2 finalPosition = start;
|
||||
|
||||
combatResolver.BeginDashPathContactSession();
|
||||
float elapsed = 0f;
|
||||
while (elapsed < duration)
|
||||
{
|
||||
yield return new WaitForFixedUpdate();
|
||||
if (playerController.IsTransitionLocked)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
combatFeedback.EmitDashAfterimage(body.position, isStrongDash);
|
||||
Vector2 segmentStart = body.position;
|
||||
elapsed += Time.fixedDeltaTime;
|
||||
Vector2 segmentEnd = Vector2.Lerp(
|
||||
Vector2 intendedEnd = Vector2.Lerp(
|
||||
start,
|
||||
end,
|
||||
Mathf.Clamp01(elapsed / duration));
|
||||
Vector2 segmentEnd = playerController.ClampMovementToTerrain(
|
||||
segmentStart,
|
||||
intendedEnd);
|
||||
bool blocked = (segmentEnd - intendedEnd).sqrMagnitude > 0.0001f;
|
||||
Vector2 segment = segmentEnd - segmentStart;
|
||||
if (segment.sqrMagnitude > 0.000001f)
|
||||
{
|
||||
@@ -116,9 +127,14 @@ namespace BumpCombat.Player
|
||||
}
|
||||
|
||||
body.MovePosition(segmentEnd);
|
||||
finalPosition = segmentEnd;
|
||||
if (blocked)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
body.position = end;
|
||||
body.position = finalPosition;
|
||||
IsDashing = false;
|
||||
IsStrongDashing = false;
|
||||
animator.SetBool(IsDashingParameter, false);
|
||||
|
||||
@@ -16,6 +16,8 @@ namespace BumpCombat.Player
|
||||
Animator.StringToHash("Base Layer.BackBumpAttack");
|
||||
private static readonly int ArtifactUseState =
|
||||
Animator.StringToHash("Base Layer.ArtifactUse");
|
||||
private static readonly int DeathState =
|
||||
Animator.StringToHash("Base Layer.Death");
|
||||
|
||||
public const string BumpAttackStateName = "BumpAttack";
|
||||
public const string BackBumpAttackStateName = "BackBumpAttack";
|
||||
@@ -45,6 +47,7 @@ namespace BumpCombat.Player
|
||||
public bool IsDamageKnockbackActive { get; private set; }
|
||||
public bool IsHurtMovementLocked =>
|
||||
playerHealth != null && playerHealth.IsHurtMovementLocked;
|
||||
public bool IsTransitionLocked { get; private set; }
|
||||
public Vector2 PlayerClampHalfExtents
|
||||
{
|
||||
get
|
||||
@@ -95,6 +98,23 @@ namespace BumpCombat.Player
|
||||
return TryPlayCombatAnimation(ArtifactUseState, false);
|
||||
}
|
||||
|
||||
public float PlayDeathAnimation()
|
||||
{
|
||||
if (animator == null
|
||||
|| animator.runtimeAnimatorController == null
|
||||
|| !animator.isActiveAndEnabled
|
||||
|| !animator.HasState(0, DeathState))
|
||||
{
|
||||
return 0f;
|
||||
}
|
||||
|
||||
SetTransitionLocked(true);
|
||||
animator.SetBool(IsMovingParameter, false);
|
||||
animator.Play(DeathState, 0, 0f);
|
||||
animator.Update(0f);
|
||||
return Mathf.Max(0f, animator.GetCurrentAnimatorStateInfo(0).length);
|
||||
}
|
||||
|
||||
private bool TryPlayCombatAnimation(
|
||||
int stateHash,
|
||||
bool blockActiveArtifactUse)
|
||||
@@ -148,7 +168,7 @@ namespace BumpCombat.Player
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!RunManager.GameplayInputEnabled || IsHurtMovementLocked)
|
||||
if (!RunManager.GameplayInputEnabled || IsHurtMovementLocked || IsTransitionLocked)
|
||||
{
|
||||
SetMoveInput(Vector2.zero);
|
||||
return;
|
||||
@@ -172,6 +192,13 @@ namespace BumpCombat.Player
|
||||
private void FixedUpdate()
|
||||
{
|
||||
AttackIntentVelocity = Vector2.zero;
|
||||
if (IsTransitionLocked)
|
||||
{
|
||||
if (body != null) body.linearVelocity = Vector2.zero;
|
||||
CurrentVelocity = Vector2.zero;
|
||||
return;
|
||||
}
|
||||
|
||||
if (dashController != null && dashController.IsDashing)
|
||||
{
|
||||
pendingImpactRecoil = Vector2.zero;
|
||||
@@ -244,6 +271,57 @@ namespace BumpCombat.Player
|
||||
- GameplayConstants.Current.Player.ArenaPadding.y));
|
||||
}
|
||||
|
||||
public Vector2 ClampMovementToTerrain(Vector2 start, Vector2 desiredPosition)
|
||||
{
|
||||
Vector2 desired = ClampToArena(desiredPosition);
|
||||
Vector2 delta = desired - start;
|
||||
float distance = delta.magnitude;
|
||||
if (distance <= 0.0001f)
|
||||
{
|
||||
return desired;
|
||||
}
|
||||
|
||||
Collider2D ownCollider = GetComponent<Collider2D>();
|
||||
float radius = 0.2f;
|
||||
if (ownCollider != null)
|
||||
{
|
||||
Vector2 extents = ownCollider.bounds.extents;
|
||||
radius = Mathf.Max(0.05f, Mathf.Min(extents.x, extents.y) * 0.9f);
|
||||
}
|
||||
|
||||
Vector2 direction = delta / distance;
|
||||
RaycastHit2D[] hits = Physics2D.CircleCastAll(
|
||||
start,
|
||||
radius,
|
||||
direction,
|
||||
distance,
|
||||
Physics2D.AllLayers);
|
||||
float allowedDistance = distance;
|
||||
for (int i = 0; i < hits.Length; i++)
|
||||
{
|
||||
Collider2D collider = hits[i].collider;
|
||||
if (collider == null
|
||||
|| collider.isTrigger
|
||||
|| collider == ownCollider
|
||||
|| collider.transform.IsChildOf(transform))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool isTerrain = collider.GetComponentInParent<BumpCombat.Stage.StageObstacle>() != null
|
||||
|| collider.name.StartsWith("Wall_")
|
||||
|| collider.gameObject.name.Contains("Wall");
|
||||
if (isTerrain)
|
||||
{
|
||||
allowedDistance = Mathf.Min(
|
||||
allowedDistance,
|
||||
Mathf.Max(0f, hits[i].distance - 0.02f));
|
||||
}
|
||||
}
|
||||
|
||||
return start + direction * allowedDistance;
|
||||
}
|
||||
|
||||
public bool TryReposition(Vector2 position)
|
||||
{
|
||||
if (body == null)
|
||||
@@ -338,5 +416,36 @@ namespace BumpCombat.Player
|
||||
spriteRenderer.flipX = input.x < 0f;
|
||||
}
|
||||
}
|
||||
|
||||
public void SetTransitionLocked(bool locked)
|
||||
{
|
||||
IsTransitionLocked = locked;
|
||||
if (locked)
|
||||
{
|
||||
moveInput = Vector2.zero;
|
||||
CurrentVelocity = Vector2.zero;
|
||||
AttackIntentVelocity = Vector2.zero;
|
||||
if (body != null)
|
||||
{
|
||||
body.linearVelocity = Vector2.zero;
|
||||
}
|
||||
if (animator != null && animator.isActiveAndEnabled)
|
||||
{
|
||||
animator.SetBool(IsMovingParameter, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetFacingDirection(Vector2 direction)
|
||||
{
|
||||
if (direction.sqrMagnitude > 0.0001f)
|
||||
{
|
||||
FacingDirection = direction.normalized;
|
||||
if (spriteRenderer != null && !Mathf.Approximately(direction.x, 0f))
|
||||
{
|
||||
spriteRenderer.flipX = direction.x < 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using BumpCombat.Combat;
|
||||
using BumpCombat.Core;
|
||||
using BumpCombat.Constants;
|
||||
using BumpCombat.Defense;
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using UnityEngine.InputSystem.LowLevel;
|
||||
@@ -235,8 +236,29 @@ namespace BumpCombat.Player
|
||||
|
||||
if (CurrentHealth <= 0f)
|
||||
{
|
||||
float deathAnimationDuration = playerController.PlayDeathAnimation();
|
||||
OnDied?.Invoke();
|
||||
RunManager.Instance?.EndRun();
|
||||
RunManager runManager = RunManager.Instance;
|
||||
if (runManager != null
|
||||
&& runManager.IsDefensePrototypeRun
|
||||
&& DefensePrototypeController.Instance != null
|
||||
&& DefensePrototypeController.Instance.HandlePlayerDied())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (runManager == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (deathAnimationDuration > 0f)
|
||||
{
|
||||
runManager.BeginGameOverPresentation(deathAnimationDuration);
|
||||
}
|
||||
else
|
||||
{
|
||||
runManager.EndRun();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -247,6 +269,26 @@ namespace BumpCombat.Player
|
||||
return value >= 0f ? value : configuredValue;
|
||||
}
|
||||
|
||||
public bool ReviveForDefense(float invulnerabilitySeconds)
|
||||
{
|
||||
if (CurrentHealth > 0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CurrentHealth = MaxHealth;
|
||||
invulnerableUntil = Time.time + Mathf.Max(0f, invulnerabilitySeconds);
|
||||
hurtMovementLockedUntil = 0f;
|
||||
guardUntil = 0f;
|
||||
guardCooldownUntil = 0f;
|
||||
activeGuardHasBlockedAttack = false;
|
||||
playerController.SetTransitionLocked(false);
|
||||
animator.Rebind();
|
||||
animator.Update(0f);
|
||||
OnHealthChanged?.Invoke(CurrentHealth, MaxHealth);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Heal(float amount)
|
||||
{
|
||||
if (amount <= 0f || CurrentHealth <= 0f)
|
||||
|
||||
Reference in New Issue
Block a user