Files

212 lines
7.2 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{
// IKillable
public float maxHealth{ get; set; }
public float currentHealth{ get; set; }
// State management (don't like this being used like this)
public NetworkVariable<bool> _isDowned;
public bool IsDowned{
get{
if (UnitIsLocal()){
_isDowned.Value = FSM.currentRootStateName == "Downed";
}
return _isDowned.Value;
}
}
// References
public GameObject pickupTarget; // NOTE: Might be removed in a refactor (https://thunderstar.codecks.io/card/15v-refactor-interaction-handler)
// (TEMP) Revive UI
// NOTE: When I make the actual UI it's a good idea to have them somehow inherit from a class or something that
// will make them persist for more than a frame since Rpc calls will make it flash
public float persistDrawingRevive;
public float lastKnownReviveTime;
void Awake(){
maxHealth = 20f;
}
void AttachToGame(){
if (IsLocalPlayer || !UnitIsNetworked()) { //
PlayerManager.Player = gameObject;
PlayerManager.RequestNewController();
GetComponent<LockOnManager>().AttachCamera(gameObject);
}
}
public override void UnitStart(){
base.UnitStart();
SetPlayerName();
AttachToGame();
((IKillable)this).IKillableInitialize();
}
private void SetPlayerName(){
name = "Player";
if (UnitIsNetworked()){
name += IsLocalPlayer ? ", Local" : ", Network";
}
}
protected override void Update(){
base.Update();
// Draw Revive UI for at least .5 seconds to prevent flashing
if (persistDrawingRevive > 0) {
persistDrawingRevive -= 1f * Time.deltaTime;
DrawReviveBarRpc(lastKnownReviveTime);
}
}
public void TakeDamage(DamageSource[] sources){
foreach (DamageSource source in sources) {
TakeDamage(source);
}
}
public void TakeDamage(DamageSource source){
// Calculate health after damage, locally
float newHealth = ((IKillable)this).currentHealth - source.damageDealt;
// Tell every unit to set the new health value
if (UnitIsNetworked()) {
SetHealthRpc(newHealth);
}
}
[Rpc(SendTo.Everyone)]
public void SetHealthRpc(float health){
// Set health to new value, clamped to 0
health = Mathf.Max(health, 0f);
currentHealth = health;
// For local players, run things based on health value.
// This Rpc is global but only the owner checks health
CheckHealth();
}
void CheckHealth(){
if (UnitIsLocal()){
if (currentHealth <= 0f) {
Down();
}
}
}
void Down(){
Graph.SendEvent("Downed");
}
public void Kill(){
Graph.SendEvent("Killed");
}
public void Interact(){
// Interaction for picking up allies
// 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
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);
}
}
[Rpc(SendTo.Everyone)]
public void DrawReviveBarRpc(float elapsedTime){
// Draw a (shoddy) UI bar that shows the revie progress
using (Draw.ingame.WithLineWidth(5f)) {
using (Draw.InLocalSpace(transform)){
// Set width of the bar
float width = .9f;
float widthDone = width * elapsedTime / 5f; // 5f assumes a 5 second revive time
// Clamp the red bar showing how much is progresseed to the max of width of the purple bar background (to prevent it going outside)
widthDone = Mathf.Clamp(widthDone, 0, width);
// Draw background bar
Vector3 pos = Vector3.up * 1.8f + Vector3.left * width + Vector3.forward * +.01f;
Draw.ingame.Line(
transform.position + Camera.main.transform.rotation * pos,
transform.position + Camera.main.transform.rotation * (pos + Vector3.right * width * 2f),
Color.rebeccaPurple
);
// Draw foreground bar
Vector3 donePos = Vector3.up * 1.8f + Vector3.left * widthDone;
Draw.ingame.Line(
transform.position + Camera.main.transform.rotation * donePos,
transform.position + Camera.main.transform.rotation * (donePos + Vector3.right * widthDone * 2f),
Color.red
);
}
}
// Set last known time so that online players don't have flashing UIs
lastKnownReviveTime = elapsedTime;
}
void LateUpdate(){
// Clear the pickupTarget every frame
// NOTE: Will this work online?
pickupTarget = null;
}
}
}