using System.Collections;
using UnityEngine;
using UnityEngine.InputSystem;
/// <summary>
/// GAME 1303 Tier 3 — Slide down walls, jump off them. Zap Attack, Stardust,
/// Spark of Corruption.
///
/// SETUP
/// 1. Add to the player (PlayerController2D v1.1). Create an empty child "WallCheck"
/// at chest height on the RIGHT side of the sprite and drag it in. (It flips with
/// the sprite, so one check covers both sides.)
/// 2. Wall Layer: usually the same as Ground.
/// 3. Slide Speed: how fast you slip down while pressing into the wall.
/// 4. Wall Jump Force (x, y): x pushes you AWAY from the wall. Lock Time keeps you from
/// steering straight back into the wall for a few frames.
/// 5. Animator: sets bool "IsWallSliding" if the parameter exists.
///
/// Also refills DoubleJump when you touch a wall (feels good; turn off Refill Jumps if not).
/// </summary>
public class WallSlideJump : MonoBehaviour
{
public Transform wallCheck;
public float checkDistance = 0.2f;
public LayerMask wallLayer;
[Header("Slide")]
public float slideSpeed = 2f;
[Tooltip("Slide only when pushing toward the wall (Hollow Knight) vs. whenever touching (Mega Man X).")]
public bool requirePushingIntoWall = true;
[Header("Wall jump")]
public Vector2 wallJumpForce = new Vector2(10f, 14f);
public float lockTime = 0.15f;
public bool refillJumps = true;
public string sfxName = "jump";
public bool IsWallSliding { get; private set; }
public bool TouchingWall { get; private set; }
private PlayerController2D controller;
private Rigidbody2D rb;
private Animator anim;
private DoubleJump doubleJump;
private static readonly int SlideHash = Animator.StringToHash("IsWallSliding");
private bool hasParam;
private bool locked;
void Awake()
{
controller = GetComponent<PlayerController2D>();
rb = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
doubleJump = GetComponent<DoubleJump>();
if (anim != null)
foreach (var p in anim.parameters) if (p.nameHash == SlideHash) hasParam = true;
}
void FixedUpdate()
{
if (wallCheck == null) return;
TouchingWall = Physics2D.Raycast(wallCheck.position, controller.Facing, checkDistance, wallLayer);
bool pushing = !requirePushingIntoWall ||
(controller.MoveInput > 0.1f && controller.FacingRight) ||
(controller.MoveInput < -0.1f && !controller.FacingRight);
bool wantSlide = TouchingWall && !controller.IsGrounded && pushing && !locked
&& !controller.MovementLocked && rb.linearVelocity.y < 0f;
if (wantSlide && !IsWallSliding && refillJumps && doubleJump != null) doubleJump.Refill();
IsWallSliding = wantSlide;
if (IsWallSliding && rb.linearVelocity.y < -slideSpeed)
rb.linearVelocity = new Vector2(rb.linearVelocity.x, -slideSpeed);
if (hasParam) anim.SetBool(SlideHash, IsWallSliding);
}
void OnJump(InputValue v)
{
if (!v.isPressed || !controller.InputEnabled || locked) return;
// Wall jump if sliding, or touching a wall in the air (more forgiving)
if (!(IsWallSliding || (TouchingWall && !controller.IsGrounded && !controller.CanCoyoteJump))) return;
float away = controller.FacingRight ? -1f : 1f;
rb.linearVelocity = new Vector2(away * wallJumpForce.x, wallJumpForce.y);
controller.ClearJumpBuffer();
controller.Flip(); // face away from the wall
controller.OnJumped?.Invoke();
if (!string.IsNullOrEmpty(sfxName)) AudioManager.Instance?.PlaySFX(sfxName);
StartCoroutine(Lock());
}
IEnumerator Lock()
{
locked = true;
controller.MovementLocked = true;
yield return new WaitForSeconds(lockTime);
controller.MovementLocked = false;
locked = false;
}
void OnDrawGizmosSelected()
{
if (wallCheck == null) return;
Gizmos.color = Color.magenta;
Vector3 dir = Application.isPlaying && controller != null ? (Vector3)controller.Facing : Vector3.right;
Gizmos.DrawLine(wallCheck.position, wallCheck.position + dir * checkDistance);
}
}