Files
project-reset/Assets/Scripts/Units/Player.cs
2025-10-20 21:23:07 -04:00

159 lines
4.7 KiB
C#

using System.Collections;
using Drawing;
using Reset.Core;
using Sirenix.OdinInspector;
using UnityEngine;
using Unity.Netcode;
namespace Reset.Units{
public class Player : Unit, IKillable, IInteractable{
[HideInInspector] public PlayerControls controls;
public float maxHealth{ get; set; }
public float currentHealth{ get; set; }
public NetworkVariable<bool> _isDowned;
public bool IsDowned => _isDowned.Value;
private float timeDowned;
public GameObject pickupTarget;
void Awake(){
controls = GetComponent<PlayerControls>();
maxHealth = 20f;
}
public void Attach(){
if (IsLocalPlayer || !UnitIsNetworked()) { //
PlayerManager.Player = gameObject;
PlayerManager.RequestNewController();
GetComponent<LockOnManager>().AttachCamera(gameObject);
}
}
public override void UnitStart(){
base.UnitStart();
SetPlayerName();
Attach();
((IKillable)this).IKillableInitialize();
}
private void SetPlayerName(){
name = "Player";
if (UnitIsNetworked()){
name += IsLocalPlayer ? ", Local" : ", Network";
}
}
public override void UnitUpdate(){
GetComponent<IKillable>().DrawHealthDebug();
}
public void TakeDamage(DamageSource[] sources){
foreach (DamageSource source in sources) {
TakeDamage(source);
}
}
public void TakeDamage(DamageSource source){
((IKillable)this).currentHealth -= source.damageDealt;
if (UnitIsNetworked()) {
SetNewHealthRpc(currentHealth);
}
}
// TODO: Move somewhere not stupid so there's not an identical method in Enemy.cs
[Rpc(SendTo.Everyone)]
public void SetNewHealthRpc(float health){
currentHealth = health;
// NOTE: only here for testing. Move out of here into some "post damage" check method
if (((IKillable)this).currentHealth <= 0) {
// if (UnitIsNetworked()) {
Down();
// } else {
// Kill();
// }
}
}
public void Down(){
Graph.SendEvent("Downed");
}
public void Kill(){
Graph.SendEvent("Killed");
}
public void Interact(){
// Check if the other player can be interacted with at all
if (pickupTarget&& pickupTarget.GetComponent<Player>().CanInteract()) {
// Tell the local player to start picking up the ally and switch states
Graph.SendEvent("Picking Up Ally");
// Tell the target player to start getting picked up.
pickupTarget.GetComponent<Player>().StartPickupRpc();
// Wait for the pickup timer to finish
StartCoroutine(PickupTimer());
}
}
[Rpc(SendTo.Owner)]
public void StartPickupRpc(){
// When picked up by another player, move into the pick up state
// TODO: Turn all these send events into a goddamn task maybe? State stuff should happen in the state machine. Concurrent stuff should happen in code
Graph.SendEvent("Pick Up Start");
}
IEnumerator PickupTimer(){
// Start a timer and wait for it to complete
float elapsed = 0f;
while (elapsed < 7f) {
elapsed += 1f * Time.deltaTime;
yield return null;
}
Graph.SendEvent("Pick Up Success");
}
public bool CanInteract(){
return IsDowned;
}
public void CancelInteract(){
Graph.SendEvent("Pick Up Failed");
}
public void OnObserverDetected(EnvironmentObserver observer){
// Try and get a Player component from the current hit object
// The rest of the logic will continue as expected so long as an
pickupTarget = observer.hit.collider.gameObject;
DrawInteractStatus();
}
void DrawInteractStatus(){
using (Draw.WithColor(Color.blue)) {
Draw.ingame.Label2D(transform.position + Vector3.up * 2.5f, "Interactable",
Color.orchid);
}
}
void LateUpdate(){
// Clear the pickupTarget every frame
// NOTE: Will this work online?
pickupTarget = null;
}
}
}