PlayerController
unknown
csharp
8 months ago
26 kB
6
Indexable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Grappling;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Rendering.PostProcessing;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class PlayerController : MonoBehaviour
{
public static PlayerController Singleton;
[Header("References")]
[SerializeField] Rigidbody2D rb;
[HideInInspector] public GrapplingGun grapplingGun;
[SerializeField] new ParticleSystem particleSystem;
[SerializeField] PostProcessVolume volume;
TrailRenderer tr;
[Header("Stats")]
[SerializeField] bool invincible = false;
public bool canAttack { get; private set; }
public int coins = 0;
[Tooltip("How much damage the player does")] public float damageMult = 1;
[Tooltip("How much damage the player takes")] public float takeDamageMult = 1;
public float critChance;
[Tooltip("Period after taking damage when you are invincible")] [SerializeField] float invincibilityTime = 0.1f;
[Header("Bounds")]
[SerializeField] Color heavenColor;
[SerializeField] float heavenVignetteIntensity;
[SerializeField] int heavenSize = 9;
[SerializeField] float outOfBoundsDamage;
[SerializeField] float outOfBoundsDamageCooldown;
[Tooltip("The velocity at which the player will be bounced up if he lands on a barrier")] [SerializeField] float outOfBoundsFallingGraceVelocity;
[Header("Health Regeneration")]
[Tooltip("How long it takes until player starts regenerating health after taking daamge")]
[SerializeField] float damageRegenCooldown = 3;
[HideInInspector] public float additionalDamageRegenCooldown;
[Tooltip("How long it takes for another tick of regeneration to occur")]
[SerializeField] float regenTickCooldown = 0.25f;
[Tooltip("How much each tick of regeneration heals for")]
[SerializeField] public float regenValue = 0.75f;
[HideInInspector] public float additionalRegenValue;
[SerializeField] float healVignetteIntensity = 0.5f;
[SerializeField] float healVignetteFlashTime = 0.15f;
[SerializeField] ParticleSystem regenParticles;
[Header("Movement")]
[SerializeField] float movementSpeed;
[SerializeField] float friction;
[SerializeField] float airFriction;
float _friction;
[HideInInspector] public float speedMult;
[SerializeField] float jumpForce;
[HideInInspector] public float jumpMult;
[SerializeField] float jumpCooldown = 0.3f;
[SerializeField] float raycastDist;
[SerializeField] LayerMask groundLayer;
[SerializeField] float baseMaxFallSpeed;
public int maxDoubleJump = 1;
public bool grounded;
[SerializeField] float coyoteTime = 0.2f;
ParticleSystem groundParticles;
Vector2 groundColliderSize;
[SerializeField] float footstepsPerSecondPerSpeed;
[Header("Dashing")]
[SerializeField] float dashForce;
public float dashDuration;
public float dashCooldown;
[SerializeField] float gravityResetSpeed;
[Header("Parachute")]
[HideInInspector] public float maxFallSpeed = -10;
[HideInInspector] public float gravityReductionMult;
[Header("Health")]
[SerializeField] public float maxHealth;
[SerializeField] static float health;
float defence;
[SerializeField] Slider healthbar;
[Header("Chests")]
[HideInInspector] public ChestSpawnData chestWithinRangeData;
[Header("Damage camera shake")]
[SerializeField] float intensity;
[SerializeField] float duration;
[SerializeField] float frequency;
[Header("Item upgrades")]
[SerializeField] float sniperScopeIncrement = 0.1f;
[SerializeField] float bloodyDaggerIncrement = 0.05f;
float randomBleedChance = 0;
[SerializeField] float stunPerkIncrement = 0.075f;
float randomStunChance;
[SerializeField] float airDefenceIncrement = 0.05f;
float airDamageReduction;
[SerializeField] float cactusIncrement = 0.1f;
[SerializeField] float cactusKnockback = 5;
float cactus;
[SerializeField] float stonksIncrease;
float stonks;
float _gravity;
float spawnOffset;
private void Start()
{
canJump = true;
tr = GetComponent<TrailRenderer>();
tr.emitting = false;
groundParticles = transform.GetChild(3).GetChild(3).GetComponent<ParticleSystem>();
groundColliderSize = GetComponent<BoxCollider2D>().size;
Singleton = this;
spawnOffset = FindObjectOfType<TerrainGenerator>().chunkSize / -2;
transform.position = new Vector2(spawnOffset, spawnOffset);
healthbar.maxValue = maxHealth;
health = maxHealth;
healthbar.value = health;
speedMult = 1;
jumpMult = 1;
canDash = false;
gravityReductionMult = 1;
stonks = 1;
_gravity = rb.gravityScale;
canAttack = true;
ResetAllStaticFields(typeof(DataPersistency));
// Starter items:
ItemSelectionMenuSystem.Singleton.OpenItemSelectionMenu(UnityEngine.Random.Range(0, 9999), 2, null, ItemSelectionMenuSystem.ItemSelectionType.StarterItemsOnly);
// Give player pickaxe:
StartCoroutine(GivePlayerPickaxe());
}
private IEnumerator GivePlayerPickaxe()
{
yield return null;
InventoryManager.Singleton.AddItem(0);
}
void ResetAllStaticFields(Type targetType)
{
// Get all static fields in the class
FieldInfo[] fields = targetType.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
int resets = 0;
foreach (var field in fields)
{
if (field.FieldType == typeof(float))
{
// Set static float fields to -1
field.SetValue(null, -1f); // Use 'null' because these are static fields
resets++;
}
else if (field.FieldType == typeof(bool))
{
// Set static bool fields to false
field.SetValue(null, false);
resets++;
}
else if (field.FieldType == typeof(int))
{
// Static int
field.SetValue(null, -1);
resets++;
}
}
Debug.Log($"Reset {resets} of {fields.Count()} fields.");
}
bool canBeHelpedOutOfBounds;
bool grappling;
bool invincibleBeforeInHeaven;
private void Update()
{
Movement();
if (grapplingGun != null) grappling = grapplingGun.grappling;
// Health regeneration
PlayerNaturalHealthRegeneration();
if (naturallyRegening && !regenParticles.isPlaying) regenParticles.Play();
else if (!naturallyRegening && regenParticles.isPlaying) regenParticles.Stop();
// Heaven
float y = transform.position.y;
bool inHeaven = false;
if (!enteredHeaven && invincible) invincibleBeforeInHeaven = true;
for (int i = 0; i < 2; i++)
{
if (y < (i + 1) * -200 - heavenSize && y > (i + 1) * -200) {
inHeaven = true;
InHeaven();
}
}
// Exit heaven
if (!inHeaven && enteredHeaven) {
canAttack = true;
enteredHeaven = false;
PlayerVignetteManager.Singleton.RemoveVignette("heaven vignette");
// Only undo invincibility if we were not invincible before
if (!invincibleBeforeInHeaven) invincible = false;
}
if (health <= 0) {
Die();
}
if (playerOutOfBounds) {
if (rb.velocity.y < outOfBoundsFallingGraceVelocity && canBeHelpedOutOfBounds) {
rb.velocity = new Vector2(rb.velocity.x, 15);
canBeHelpedOutOfBounds = false;
return;
}
canBeHelpedOutOfBounds = false;
PlayerOutOfBounds();
} else if (!playerOutOfBounds) canBeHelpedOutOfBounds = true;
// Chest
if (Input.GetKeyDown(KeyCode.E) && withinChestRange)
{
if (!itemSelectionMenuOpen)
{
int seed = chestWithinRangeData.LootSeed;
int count = chestWithinRangeData.ItemOptionCount;
ItemSelectionMenuSystem.Singleton.OpenItemSelectionMenu(seed, count, chestWithinRangeData);
itemSelectionMenuOpen = true;
} else {
ItemSelectionMenuSystem.Singleton.CloseItemSelectionMenu();
itemSelectionMenuOpen = false;
}
}
DebugLogs();
}
private void Die()
{
SceneManager.LoadScene(0);
for (int i = 0; i < times.Count; i++)
{
Debug.Log($"Stage {i} duration: {TimeSpan.FromSeconds(times[i])}");
}
}
Coroutine footstepCoroutine;
bool canJump = true;
[HideInInspector] public int doubleJumps = 0; // Amount of double jumps used in air
private void Movement()
{
// Return if we are dashing
if (dashing) return;
// Movement
float x = Input.GetAxis("Horizontal") * movementSpeed * speedMult;
bool velocityWithinAffectionRange = rb.velocity.x <= movementSpeed * speedMult + 0.001f && rb.velocity.x >= -movementSpeed * speedMult - 0.001f;
if (!grappling && !dashing && velocityWithinAffectionRange) rb.velocity = new Vector2(x, rb.velocity.y);
if (!velocityWithinAffectionRange) {
if (grounded)
_friction = friction;
else
_friction = airFriction;
ApplyFriction();
}
// Ground check
if (Physics2D.BoxCast(transform.position, new Vector2(groundColliderSize.x / 2, transform.localScale.y), 0, Vector2.down, raycastDist, groundLayer))
{
defence = 1;
grounded = true;
doubleJumps = 0;
coyoteTimeCoroutine = null;
} else {
defence = 1 - airDamageReduction;
grounded = false;
if (coyoteTimeCoroutine == null) coyoteTimeCoroutine = StartCoroutine(CoyoteTime());
}
// Footsteps
if (Mathf.Abs(x) > 0.1f && grounded) {
if (footstepCoroutine == null) footstepCoroutine = StartCoroutine(Footstep());
}
// Ground particles
if (grounded && !groundParticles.isPlaying) groundParticles.Play();
else if (!grounded && groundParticles.isPlaying) groundParticles.Stop();
// Double jumping
if (Input.GetKeyDown(KeyCode.Space) && !grounded && !canJumpCoyoteTime && doubleJumps < maxDoubleJump && canJump)
{
doubleJumps++;
Jump();
}
// Jumping
if (Input.GetKeyDown(KeyCode.Space) && canJump && (grounded || canJumpCoyoteTime))
{
Jump();
}
if (Input.GetKeyDown(KeyCode.Space) && grappling && grapplingGun != null) grapplingGun.StopGrappling();
Debug.DrawRay(transform.position, Vector2.down * raycastDist, Color.red);
// Dashing
if (Input.GetKeyDown(KeyCode.LeftShift) && canDash && !dashing && !grappling) StartCoroutine(Dash());
// Lerp gravity
if (lerpGravity) {
time += Time.deltaTime / gravityResetSpeed;
gravity = Mathf.Lerp(0, _gravity, time);
rb.gravityScale = gravity;
if (gravity >= _gravity) {
rb.gravityScale = _gravity;
lerpGravity = false;
time = 0;
gravity = 0;
}
}
// Gliding / Parachuting
if (Input.GetKey(KeyCode.LeftShift) && !grounded && rb.velocity.y < 0 && !dashing && gravityReductionMult != 1 && !grappling) {
rb.gravityScale = _gravity * gravityReductionMult;
if (rb.velocity.y < maxFallSpeed) rb.velocity = new Vector2(rb.velocity.x, maxFallSpeed);
if (!tr.emitting) tr.emitting = true;
} else if (!dashing) {
rb.gravityScale = _gravity;
if (tr.emitting) tr.emitting = false;
}
if (rb.velocity.y < baseMaxFallSpeed) rb.velocity = new Vector2(rb.velocity.x, baseMaxFallSpeed);
//if (grounded && !dashing && grappling && !velocityWithinAffectionRange) ApplyFriction();
// Grappling
if (Input.GetMouseButtonDown(1)) { // If we right click and we can swap items
Vector2 dir = transform.position - Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, 10));
if (Physics2D.Raycast(transform.position, -dir.normalized, GrapplingGun.minPullDistance, 1 << 3)) return;
if (InventoryManager.Singleton.canDeselect && TimingManager.Singleton._CooldownEnded("grappleCooldown"))
{
if (!EventSystem.current.IsPointerOverGameObject()) {
previousItemGrapple = InventoryManager.Singleton.currentItemSelected;
InventoryManager.Singleton.RightClickFavoritedItem(2); // Select grappling gun
StartCoroutine(GrappleSetDelay());
}
}
}
}
float time = 0;
private IEnumerator Footstep()
{
// Play sound
int length = SoundManager.Singleton.soundLists[3].sounds.Length;
int i = UnityEngine.Random.Range(0, length - 1);
SoundManager.Singleton.PlaySound("Player walking", "", i, 0.1f);
yield return new WaitForSeconds(footstepsPerSecondPerSpeed / movementSpeed);
footstepCoroutine = null;
}
bool enteredHeaven;
private void InHeaven()
{
if (!enteredHeaven) {
// Enter heaven
enteredHeaven = true;
PlayerVignetteManager.Singleton.RequestVignette(new VignetteRequest(heavenColor, heavenVignetteIntensity, 5), "heaven vignette");
invincible = true;
canAttack = false;
}
}
int previousItemGrapple;
private IEnumerator GrappleSetDelay()
{
yield return null;
if (grapplingGun != null) {
grapplingGun.previusItemEquiped = previousItemGrapple;
}
}
private void Jump()
{
StartCoroutine(_Jump());
}
private IEnumerator _Jump()
{
rb.velocity = new Vector2(rb.velocity.x, 0);
rb.AddForce(Vector3.up * jumpForce * 100 * jumpMult);
if (doubleJumps == 0) particleSystem.transform.GetChild(0).gameObject.SetActive(true);
else particleSystem.transform.GetChild(0).gameObject.SetActive(false);
if (!particleSystem.isPlaying) particleSystem.Play();
canJump = false;
StartCoroutine(DustParticles());
yield return new WaitForSeconds(jumpCooldown);
canJump = true;
}
bool canJumpCoyoteTime;
Coroutine coyoteTimeCoroutine;
private IEnumerator CoyoteTime()
{
canJumpCoyoteTime = true;
yield return new WaitForSeconds(coyoteTime);
canJumpCoyoteTime = false;
}
private IEnumerator DustParticles()
{
particleSystem.Play();
yield return new WaitForSeconds(0.15f);
particleSystem.Stop();
}
public void DamagePlayerTest()
{
DamagePlayer(new DamageData(1, 0, Vector2.zero, null));
}
Coroutine damageCooldownCoroutine;
public void DamagePlayer(DamageData damageData)
{
if (invincible || damageCooldownCoroutine != null) return;
damageCooldownCoroutine = StartCoroutine(DamageCooldown());
float damage = damageData.Damage * takeDamageMult * defence;
float knockback = damageData.Knockback;
Vector2 knockbackDir = damageData.Vector;
health -= damage;
UpdateHealthBar();
timeSinceDamage = 0;
Knockback(knockback, knockbackDir);
// Camera shake
CameraShakeManager.Singleton._CameraShake(intensity, duration, frequency, CameraShakeManager.CurveType.InverseParabola);
// Damage particles
transform.GetChild(3).GetChild(2).GetComponent<ParticleSystem>().Play();
// Damage sound
SoundManager.Singleton.PlaySound("Player sounds", "PlayerDamage", -1, 0.1f);
if (!BarrierMain.Singleton.outOfBounds) PlayerVignetteManager.Singleton.DamagePlayerVignette();
if (cactus != 0 && damageData.Attacker != null) {
PlayerDealDamage(damageData.Attacker, new DamageData(damage * cactus, cactusKnockback, transform.position - damageData.Attacker.transform.position));
}
}
Color originColor;
private IEnumerator DamageCooldown()
{
originColor = GetComponent<SpriteRenderer>().color;
GetComponent<SpriteRenderer>().color = Color.white;
yield return new WaitForSeconds(invincibilityTime);
damageCooldownCoroutine = null;
GetComponent<SpriteRenderer>().color = originColor;
}
public void GiveCoins(int amount)
{
coins += Mathf.FloorToInt(amount * stonks);
}
public void UpdateHealthBar()
{
healthbar.value = health;
healthbar.maxValue = maxHealth;
healthbar.transform.GetChild(2).GetComponent<TextMeshProUGUI>().text = $"{Mathf.Round(health)} / {maxHealth}";
}
float timeSinceDamage;
bool naturallyRegening;
private void PlayerNaturalHealthRegeneration()
{
if (health != maxHealth && timeSinceDamage >= damageRegenCooldown + additionalDamageRegenCooldown) {
if (healCoroutine == null)
{
naturallyRegening = true;
// Start healing
healCoroutine = StartCoroutine(Heal());
}
} else naturallyRegening = false;
timeSinceDamage += Time.deltaTime;
}
Coroutine healCoroutine;
private IEnumerator Heal()
{
yield return new WaitForSeconds(regenTickCooldown);
//Debug.Log("Healing player");
HealPlayer(regenValue + additionalRegenValue, false);
healCoroutine = null;
}
public void HealPlayer(float healAmount, bool vignette)
{
health += healAmount;
health = Mathf.Round(health * 10) / 10;
health = Mathf.Clamp(health, 0, maxHealth);
UpdateHealthBar();
}
public void SetPlayerHealth(float value)
{
health = value;
UpdateHealthBar();
}
[HideInInspector] public bool canDash = false;
bool dashing = false;
bool lerpGravity = false;
float gravity = 0;
IEnumerator Dash()
{
rb.gravityScale = 0;
canDash = false;
dashing = true;
Vector2 movementVector = new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")).normalized;
rb.velocity = movementVector * dashForce;
tr.emitting = true;
yield return new WaitForSeconds(dashDuration);
rb.gravityScale = _gravity;
dashing = false;
rb.velocity = new Vector2(0, 0);
lerpGravity = true;
tr.emitting = false;
yield return new WaitForSeconds(dashCooldown);
canDash = true;
}
private void ApplyFriction()
{
Vector2 velocity = rb.velocity;
if (Mathf.Abs(velocity.x) > 0.1f)
velocity.x = Mathf.Lerp(velocity.x, 0, Time.deltaTime * _friction);
else
velocity.x = 0;
rb.velocity = velocity;
}
private void Knockback(float Knockback, Vector2 dir)
{
if (dir == Vector2.zero) return;
rb.velocity = dir.normalized * Knockback;
}
[HideInInspector] public bool playerOutOfBounds;
Coroutine oobDamageCoroutine;
private void PlayerOutOfBounds()
{
if (oobDamageCoroutine == null && playerOutOfBounds) oobDamageCoroutine = StartCoroutine(OutOfBoundsDamage());
}
private IEnumerator OutOfBoundsDamage()
{
yield return new WaitForSeconds(outOfBoundsDamageCooldown);
if (playerOutOfBounds) DamagePlayer(new DamageData(outOfBoundsDamage, 0, Vector2.zero));
oobDamageCoroutine = null;
}
public void PlayerDealDamage(GameObject target, DamageData damageData)
{
float lifeSteal = DataPersistency.player_lifeStealPercentage;
if (lifeSteal != -1) HealPlayer(damageData.Damage * lifeSteal, true);
float critMult = UnityEngine.Random.Range(0, 100) < critChance * 100 ? 2 : 1;
DamageData finalDamage = new DamageData(damageData.Damage * damageMult * critMult, damageData.Knockback, damageData.Vector);
if (UnityEngine.Random.Range(0f, 1f) < randomBleedChance) PlayerBleedEnemy(target, 5);
if (UnityEngine.Random.Range(0f, 1f) < randomStunChance) PlayerStunEnemy(target, 1);
target.SendMessage("DamageEnemy", finalDamage, SendMessageOptions.DontRequireReceiver);
}
public void PlayerBleedEnemy(GameObject target, float bleedDamage)
{
target.SendMessage("BleedEnemy", bleedDamage, SendMessageOptions.DontRequireReceiver);
}
public void PlayerStunEnemy(GameObject target, float stunTime)
{
target.SendMessage("StunEnemy", stunTime, SendMessageOptions.DontRequireReceiver);
}
[HideInInspector] public bool withinChestRange;
bool itemSelectionMenuOpen;
void OnTriggerEnter2D(Collider2D collider)
{
// Player trigger
if (collider.gameObject.layer == 15)
{
// Chest
if (collider.gameObject.tag == "Chest")
{
PlayerInfoBoard.Singleton.ShowInfoBoard("[E] Open chest");
withinChestRange = true;
foreach (ChestSpawnData chestData in TerrainGenerator.Singleton.chunkChestLookup.Values)
{
if (chestData.GlobalPosition == Vector3.zero) continue;
if (chestData.GlobalPosition == collider.transform.position) {
chestWithinRangeData = chestData;
break;
}
}
}
}
}
void OnTriggerExit2D(Collider2D collider)
{
// Player trigger
if (collider.gameObject.layer == 15)
{
// Chest
if (collider.gameObject.tag == "Chest")
{
PlayerInfoBoard.Singleton.HideInfoBoard();
withinChestRange = false;
itemSelectionMenuOpen = false;
ItemSelectionMenuSystem.Singleton.CloseItemSelectionMenu();
}
}
}
List<float> times;
EnemySpawnManager enemySpawnManager;
private void DebugLogs()
{
if (enemySpawnManager == null) {
enemySpawnManager = EnemySpawnManager.Singletone;
times = new List<float> { 0, 0, 0, 0, 0 };
}
times[enemySpawnManager.stage - 1] += Time.deltaTime;
}
public void PurchaseSniperScope()
{
Debug.Log("Pruchase sniper scope");
critChance += sniperScopeIncrement;
}
public void PurchaseBloodyDagger()
{
Debug.Log("Purchase bloody dagger");
randomBleedChance += bloodyDaggerIncrement;
}
public void PurchaseStunPerk()
{
Debug.Log("Purchase stun perk");
randomStunChance += stunPerkIncrement;
}
public void PurchaseAirDefence()
{
Debug.Log("Purchase air defences");
airDamageReduction += airDefenceIncrement;
}
public void PurchaseCactus()
{
Debug.Log("Purchase cactus");
cactus += cactusIncrement;
}
public void PurchaseStonks()
{
Debug.Log("Purchase cactus");
stonks += stonksIncrease;
}
[SerializeField] float healthUpgraqeIncr;
public void PurchaseHealthUpgrade()
{
Debug.Log("Purchase health");
maxHealth += healthUpgraqeIncr;
UpdateHealthBar();
}
[SerializeField] float regenUpgrade = 0.2f;
public void PurchaseBandaid()
{
Debug.Log("Purchase regen");
regenValue += regenUpgrade;
}
[SerializeField] float lifeStealUpgrade = 0.2f;
public void PurchaseLifeSteal()
{
Debug.Log("Purchase lifesteal");
DataPersistency.player_lifeStealPercentage = lifeStealUpgrade;
}
[SerializeField] float movementSpeedUpgrade = 0.25f;
public void PurchaseSneaker()
{
Debug.Log("Purchase sneaker");
movementSpeed *= movementSpeedUpgrade + 1;
}
[SerializeField] float jumpBoostUpgade = 0.2f;
public void PurchaseJumpBoost()
{
Debug.Log("Purchase jump boost");
jumpForce += jumpBoostUpgade;
}
[SerializeField] int doubleJumpUpgrade = 2;
public void PurchaseDoubleJump()
{
Debug.Log("Purchase double jump");
maxDoubleJump += doubleJumpUpgrade;
}
}
public class DamageData
{
public float Damage;
public float Knockback;
public Vector2 Vector;
public GameObject Attacker;
public DamageData(float damage, float knockback, Vector2 vector, GameObject attacker = null)
{
Damage = damage;
Knockback = knockback;
Vector = vector;
Attacker = attacker;
}
}Editor is loading...
Leave a Comment