UI fixes, add npc base class
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class BlockingAnimation : Attribute { }
|
||||
public abstract class BaseCharacter : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
protected NavMeshAgent _navAgent;
|
||||
|
||||
protected Animator _animator;
|
||||
private const string WALK_VELOCITY = "WalkVelocity";
|
||||
private readonly Queue<PlayerTasks> _tasks = new Queue<PlayerTasks>();
|
||||
private PlayerTasks _currentTask;
|
||||
|
||||
private CharacterDescriptor _characterDescriptor;
|
||||
private Action _OnAnimationFinish;
|
||||
private AnimationStates _currentAnimation;
|
||||
|
||||
private CharacterSex _characterSex;
|
||||
|
||||
protected void IntCharacter()
|
||||
{
|
||||
_animator = GetComponentInChildren<Animator>();
|
||||
_characterDescriptor = GetComponentInChildren<CharacterDescriptor>();
|
||||
_characterSex = _characterDescriptor.Sex;
|
||||
SetPlayerAnimation(AnimationStates.Walking);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (PlayerHelper.IsBlockingAnimation(_currentAnimation))
|
||||
{
|
||||
if (IsAnimationStatePlaying(0))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_OnAnimationFinish?.Invoke();
|
||||
_OnAnimationFinish = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_currentTask == null || _currentTask.Status == TaskStatus.Complete)
|
||||
{
|
||||
_tasks.TryDequeue(out _currentTask);
|
||||
}
|
||||
if (_currentTask != null)
|
||||
{
|
||||
if (_currentTask.Status == TaskStatus.Waiting)
|
||||
Debug.Log($"Current task {_currentTask.Task}");
|
||||
switch (_currentTask.Task)
|
||||
{
|
||||
case Tasks.Rotate:
|
||||
_currentTask.UpdateStatus(Rotate(_currentTask.TagretObject._interactionPoint.forward));
|
||||
break;
|
||||
case Tasks.Move:
|
||||
if (_currentAnimation == AnimationStates.Sitting)
|
||||
{
|
||||
SetPlayerAnimation(AnimationStates.Standing);
|
||||
return;
|
||||
}
|
||||
_navAgent.SetDestination(_currentTask.TagretObject._interactionPoint.position);
|
||||
_currentTask.UpdateStatus(MoveToPoint());
|
||||
break;
|
||||
case Tasks.Interact:
|
||||
_currentTask.UpdateStatus(TaskStatus.Waiting);
|
||||
|
||||
_currentTask.UpdateStatus(_currentTask.TagretObject.Interact());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TaskStatus MoveToPoint()
|
||||
{
|
||||
_navAgent.isStopped = false;
|
||||
SetPlayerAnimation(AnimationStates.Walking);
|
||||
|
||||
_animator.SetFloat(WALK_VELOCITY, _navAgent.velocity.magnitude);
|
||||
return IsPathComplete(_navAgent.destination) ? TaskStatus.Complete : TaskStatus.InProgress;
|
||||
}
|
||||
|
||||
public bool IsPathComplete(Vector3 destination)
|
||||
{
|
||||
var dest = new Vector3(destination.x, 0, destination.z);
|
||||
var pos = new Vector3(_navAgent.transform.position.x, 0, _navAgent.transform.position.z);
|
||||
|
||||
if (Vector3.Distance(dest, pos) <= _navAgent.radius)
|
||||
{
|
||||
transform.position = destination;
|
||||
if (!_navAgent.hasPath || _navAgent.velocity.sqrMagnitude < 0.2f)
|
||||
{
|
||||
SetPlayerAnimation(AnimationStates.Idle);
|
||||
_navAgent.isStopped = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected TaskStatus Rotate(Vector3 target)
|
||||
{
|
||||
var targetRot = Quaternion.LookRotation(target);
|
||||
Quaternion rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(target), 10 * Time.deltaTime);
|
||||
rotation.x = 0;
|
||||
transform.rotation = rotation;
|
||||
if (IsApproximate(targetRot, transform.rotation, 0.000004f))
|
||||
{
|
||||
return TaskStatus.Complete;
|
||||
}
|
||||
return TaskStatus.InProgress;
|
||||
}
|
||||
|
||||
protected void AddTask(PlayerTasks task)
|
||||
{
|
||||
_tasks.Enqueue(task);
|
||||
}
|
||||
|
||||
public bool IsAnimationStatePlaying(int animLayer)
|
||||
{
|
||||
string stateName = PlayerHelper.GetEnumMemberValue(_currentAnimation);
|
||||
var stateInfo = _animator.GetCurrentAnimatorStateInfo(animLayer);
|
||||
return stateInfo.IsName(stateName) && stateInfo.normalizedTime < 1.0f;
|
||||
}
|
||||
|
||||
public void SetPlayerAnimation(AnimationStates newState, Action onAnimationFinish)
|
||||
{
|
||||
_OnAnimationFinish = onAnimationFinish;
|
||||
SetPlayerAnimation(newState);
|
||||
}
|
||||
|
||||
public void SetPlayerAnimation(AnimationStates newState)
|
||||
{
|
||||
if (newState == _currentAnimation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var stringState=PlayerHelper.GetEnumMemberValue(newState);
|
||||
stringState= string.Format(stringState,_characterSex.ToString());
|
||||
Debug.Log($"Animation state {stringState}");
|
||||
_animator.Play(stringState);
|
||||
_currentAnimation = newState;
|
||||
}
|
||||
|
||||
private bool IsApproximate(Quaternion q1, Quaternion q2, float precision)
|
||||
{
|
||||
return Mathf.Abs(Quaternion.Dot(q1, q2)) >= 1 - precision;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ae6d3e3aea34ae447a6aa1914a953e8e
|
||||
@@ -0,0 +1,19 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
public class CharacterDescriptor : MonoBehaviour
|
||||
{
|
||||
[SerializeField]
|
||||
public CharacterSex Sex;
|
||||
private NavMeshAgent _navMeshAgent;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
_navMeshAgent = GetComponentInParent<NavMeshAgent>();
|
||||
}
|
||||
|
||||
private void OnAnimatorMove()
|
||||
{
|
||||
transform.parent.position = _navMeshAgent.nextPosition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cf9ddd6d77ff9a7479edaeb2c8ae1078
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 64bc13a0b6923c14ab5d87e8338aca0c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class HoldPoint : MonoBehaviour
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99527458161e16442b3fb76c8f585ec8
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Assets.Scripts.Characters
|
||||
{
|
||||
public class Npc : BaseCharacter
|
||||
{
|
||||
private void Awake()
|
||||
{
|
||||
base.IntCharacter();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 57bc7fa5d71cfbf4685a51d4fd4be2ab
|
||||
@@ -0,0 +1,49 @@
|
||||
using Assets.Scripts.Interfaces;
|
||||
|
||||
public class NumericStat : INumericStat
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public float Value { get; private set; }
|
||||
public float Price { get; private set; }
|
||||
public float Quantity { get; private set; }
|
||||
public float MaxValue { get; private set; }
|
||||
|
||||
public NumericStat(string name, float startValue, float maxValue)
|
||||
{
|
||||
Name = name;
|
||||
Value = startValue;
|
||||
MaxValue = maxValue;
|
||||
}
|
||||
|
||||
public NumericStat(string name, float price, float quantity, float maxValue)
|
||||
{
|
||||
Name = name;
|
||||
Price = price;
|
||||
Quantity = quantity;
|
||||
MaxValue = maxValue;
|
||||
}
|
||||
|
||||
public void increase(float byAmount)
|
||||
{
|
||||
if (Value < MaxValue)
|
||||
{
|
||||
Value += byAmount;
|
||||
}
|
||||
}
|
||||
|
||||
public bool deduct(float amount)
|
||||
{
|
||||
if (Value >= amount)
|
||||
{
|
||||
Value -= amount;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void forceDeduct(float amount)
|
||||
{
|
||||
Value -= amount;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6d941c5108c66a44da3ab31f96e40d8e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,168 @@
|
||||
using Assets.Scripts.Interfaces;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class Player : BaseCharacter
|
||||
{
|
||||
public event EventHandler<bool> OnContainerChanged;
|
||||
public static Player Instance { get; private set; }
|
||||
|
||||
private HoldPoint _holdPoint;
|
||||
|
||||
private PlayerStates _currentActing;
|
||||
|
||||
public Dictionary<StatsId, object> Stats;
|
||||
public JobPositions JobPosition { get; set; }
|
||||
|
||||
private List<EducationInfoSO> _completedCourses = new();
|
||||
public EducationInfoSO ActiveCourse { get; set; }
|
||||
public EducationSkill Education { get; set; }
|
||||
|
||||
private ContainerItem _containerItem;
|
||||
|
||||
private string _locationName;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
|
||||
if (Instance != null)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
Debug.Log("There's more than one player instance");
|
||||
return;
|
||||
}
|
||||
PlayerPrefs.SetString("lastExitName", string.Empty);
|
||||
Instance = this;
|
||||
Stats = PlayerStats.CreateInitialStats();
|
||||
JobPosition = JobPositions.Unemployed;
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
base.IntCharacter();
|
||||
_holdPoint = GetComponentInChildren<HoldPoint>();
|
||||
GameManager.Instance.Time.OnMinuteChanged += UpdateStatsByClock;
|
||||
_animator.applyRootMotion = true;
|
||||
_navAgent.updatePosition = false;
|
||||
|
||||
_currentActing = PlayerStates.Awake;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
GameManager.Instance.Time.OnMinuteChanged -= UpdateStatsByClock;
|
||||
}
|
||||
|
||||
public void SetPosition(Transform desiredPosition)
|
||||
{
|
||||
_navAgent.Warp(desiredPosition.position);
|
||||
_navAgent.updatePosition = false;
|
||||
Rotate(desiredPosition.forward * -1);
|
||||
}
|
||||
|
||||
public void GoToPoint(BaseInteractableObject point)
|
||||
{
|
||||
AddTask(new PlayerTasks(Tasks.Move, point));
|
||||
}
|
||||
|
||||
public async void Interact(BaseInteractableObject interactionItem)
|
||||
{
|
||||
var result = await interactionItem.ShowPopupMenu(this);
|
||||
if (result != InteractionStatus.Complete)
|
||||
{
|
||||
if (!IsPathComplete(interactionItem._interactionPoint.position))
|
||||
{
|
||||
AddTask(new PlayerTasks(Tasks.Move, interactionItem));
|
||||
AddTask(new PlayerTasks(Tasks.Rotate, interactionItem));
|
||||
AddTask(new PlayerTasks(Tasks.Interact, interactionItem));
|
||||
}
|
||||
else
|
||||
{
|
||||
AddTask(new PlayerTasks(Tasks.Interact, interactionItem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void SetPlayerActing(PlayerStates state)
|
||||
{
|
||||
_currentActing = state;
|
||||
}
|
||||
|
||||
public void UpdateStatsByClock()
|
||||
{
|
||||
switch (_currentActing)
|
||||
{
|
||||
case PlayerStates.Eating:
|
||||
(Stats[StatsId.Food] as INumericStat).increase(10f);
|
||||
break;
|
||||
case PlayerStates.Sleeping:
|
||||
(Stats[StatsId.Energy] as INumericStat).increase(0.2f);
|
||||
(Stats[StatsId.Food] as INumericStat).deduct(0.03f);
|
||||
break;
|
||||
default:
|
||||
(Stats[StatsId.Food] as INumericStat).deduct(0.05f); // 48 hours it's 100, 100/2880=~0.034 per minute
|
||||
(Stats[StatsId.Energy] as INumericStat).deduct(0.1f); // 24 hours it's 100, 100/1440=~0.096 per minute
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Pay(float amount)
|
||||
{
|
||||
(Stats[StatsId.Money] as INumericStat).deduct(amount);
|
||||
}
|
||||
|
||||
public void AddMoney(float amount)
|
||||
{
|
||||
(Stats[StatsId.Money] as INumericStat).increase(amount);
|
||||
}
|
||||
|
||||
public void SetContainerItem(ContainerItem containerItem)
|
||||
{
|
||||
containerItem.transform.parent = _holdPoint.transform;
|
||||
containerItem.transform.localPosition = Vector3.zero;
|
||||
_containerItem = containerItem;
|
||||
OnContainerChanged.Invoke(this, true);
|
||||
}
|
||||
|
||||
public void ClearContainerItem()
|
||||
{
|
||||
if (_containerItem == null)
|
||||
return;
|
||||
Destroy(_containerItem.gameObject);
|
||||
OnContainerChanged.Invoke(this, false);
|
||||
_containerItem = null;
|
||||
}
|
||||
|
||||
public ContainerItem GetContainerItem()
|
||||
{
|
||||
return _containerItem;
|
||||
}
|
||||
|
||||
public bool IsHoldContainerItem()
|
||||
{
|
||||
return _containerItem != null;
|
||||
}
|
||||
|
||||
public void SetLocationName(string locationName)
|
||||
{
|
||||
_locationName = locationName;
|
||||
}
|
||||
public string GetLocationName()
|
||||
{
|
||||
return _locationName;
|
||||
}
|
||||
|
||||
internal void Learn(TimeSpan time)
|
||||
{
|
||||
ActiveCourse.PlayerProgress += time.Hours;
|
||||
if (ActiveCourse.PlayerProgress >= ActiveCourse.Duration)
|
||||
{
|
||||
Education++;
|
||||
print($"Congratulation player finish curse {ActiveCourse.Description} and his education now {Education.ToString()}");
|
||||
ActiveCourse = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c802a0d9d32b0c04b841113e87b83e4b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
public static class PlayerHelper
|
||||
{
|
||||
|
||||
public static string GetEnumMemberValue<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
return typeof(T)
|
||||
.GetTypeInfo()
|
||||
.DeclaredMembers
|
||||
.SingleOrDefault(x => x.Name == value.ToString())
|
||||
?.GetCustomAttribute<EnumMemberAttribute>(false)
|
||||
?.Value;
|
||||
}
|
||||
|
||||
public static bool IsBlockingAnimation<T>(T value)
|
||||
where T : struct, IConvertible
|
||||
{
|
||||
var enumType = typeof(T);
|
||||
var memInfo = enumType.GetMember(value.ToString());
|
||||
var attr = memInfo.FirstOrDefault()?.GetCustomAttributes(false).OfType<BlockingAnimation>().FirstOrDefault();
|
||||
return attr != null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e0c9736e301f4f4d89a127e9efde6c8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
|
||||
public class PlayerStats
|
||||
{
|
||||
public static Dictionary<StatsId, object> CreateInitialStats()
|
||||
{
|
||||
return new Dictionary<StatsId, object>()
|
||||
{
|
||||
{StatsId.Money, new NumericStat("Money", 100.0f,10000000f)},
|
||||
{StatsId.RentAccount, new NumericStat("Rent Account", 0,10f)},
|
||||
{StatsId.Food, new NumericStat("Food Energy", 50f,100f) },
|
||||
{StatsId.Energy,new NumericStat("Energy", 50f,100f) },
|
||||
{StatsId.LocationName,new StringStat("Location","Nowhere") },
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
//// Knowledge for University Jobs
|
||||
//public Stat literatureKnowledge = new Stat("LiteratureKnowledge", 0);
|
||||
//public Stat mathematicsKnowledge = new Stat("MathematicsKnowledge", 0);
|
||||
//public Stat computerScienceKnowledge = new Stat("ComputerScienceKnowledge", 0);
|
||||
|
||||
//// Knowledge for Factory Jobs
|
||||
//public Stat electronicsKnowledge = new Stat("ElectronicsKnowledge", 0);
|
||||
//public Stat roboticsKnowledge = new Stat("RoboticsKnowledge", 0);
|
||||
//public Stat industrialDesignKnowledge = new Stat("IndustrialDesignKnowledge", 0);
|
||||
|
||||
//public Stat careerPoints = new Stat("careerPoints", 0);
|
||||
|
||||
//// Achievements - to WIN the game
|
||||
//public Stat whealthAchievement = new Stat("whealthAchievement", 999999);
|
||||
//public Stat educationAchievement = new Stat("educationAchievement", 999999);
|
||||
//public Stat careerAchievement = new Stat("careerAchievement", 999999);
|
||||
//public Stat happinessAchievement = new Stat("happinessAchievement", 999999);
|
||||
|
||||
//// Inventory items
|
||||
//public Stat freezerItem = new Stat("Freezer Item", 400, 1);
|
||||
//public Stat clothesItem = new Stat("Clothes Item", 200, 3);
|
||||
//public Stat booksItem = new Stat("Books Item", 150, 2);
|
||||
//public Stat tvItem = new Stat("Tv Item", 1500, 1);
|
||||
//public Stat laptopItem = new Stat("Laptop Item", 3000, 1);
|
||||
// Update is called once per frame
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 90d55fcea99d3d14db40fb1ce5deca37
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
public class PlayerTasks
|
||||
{
|
||||
public Tasks Task { get; private set; }
|
||||
public BaseInteractableObject TagretObject { get; private set; }
|
||||
public TaskStatus Status { get; private set; }
|
||||
|
||||
public PlayerTasks(Tasks task, BaseInteractableObject gameObject)
|
||||
{
|
||||
Task = task;
|
||||
TagretObject = gameObject;
|
||||
Status = TaskStatus.Waiting;
|
||||
}
|
||||
|
||||
public void UpdateStatus(TaskStatus status)
|
||||
{
|
||||
Status = status;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a64ec35bd6b0f784cba543d437d7cdd4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,18 @@
|
||||
using Assets.Scripts.Interfaces;
|
||||
|
||||
public class StringStat : IStringStat
|
||||
{
|
||||
public string Name { get; private set; }
|
||||
public string Value { get; private set; }
|
||||
|
||||
public StringStat(string name, string startValue)
|
||||
{
|
||||
Name = name;
|
||||
Value = startValue;
|
||||
}
|
||||
|
||||
public void SetValue(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c57f2033235f86b4585b18775c652653
|
||||
Reference in New Issue
Block a user