something is no yes
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using RebootKit.Engine.Extensions;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Network;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootReality.jelycho.Player;
|
||||
using TriInspector;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
@@ -23,6 +25,41 @@ namespace RebootReality.jelycho.Enemies {
|
||||
}
|
||||
}
|
||||
|
||||
public enum ZombieBodyPartType {
|
||||
Body,
|
||||
Head,
|
||||
LeftArm,
|
||||
RightArm,
|
||||
LeftLeg,
|
||||
RightLeg
|
||||
}
|
||||
|
||||
// @TODO: weird naming
|
||||
[Serializable]
|
||||
public class ZombieBodyPart {
|
||||
public Transform root;
|
||||
public Transform[] wearables;
|
||||
public Collider[] colliders;
|
||||
public ParticleSystem bloodStreamParticles;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAlive() {
|
||||
return root.gameObject.activeSelf;
|
||||
}
|
||||
|
||||
public void HideParts() {
|
||||
root.gameObject.SetActive(false);
|
||||
foreach (Transform wearable in wearables) {
|
||||
wearable.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
foreach (Collider collider in colliders) {
|
||||
collider.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[DeclareBoxGroup("Body parts")]
|
||||
public class ZombieActor : Actor, IKillable {
|
||||
static readonly Logger s_Logger = new Logger(nameof(ZombieActor));
|
||||
|
||||
@@ -41,14 +78,19 @@ namespace RebootReality.jelycho.Enemies {
|
||||
|
||||
[SerializeField] NavMeshAgent m_NavAgent;
|
||||
|
||||
[SerializeField] Collider[] m_RagdollColliders;
|
||||
[SerializeField] Collider m_RootCollider;
|
||||
[SerializeField] Rigidbody[] m_RagdollRigidbodies;
|
||||
[SerializeField] Collider[] m_Hitboxes;
|
||||
|
||||
[SerializeField] float m_MaxAttackDistance = 1.0f;
|
||||
[SerializeField] float m_LoseInterestMinDistance = 10.0f;
|
||||
[SerializeField] ulong m_BaseDamage = 10;
|
||||
[SerializeField] float m_AttackDelay = 1.0f;
|
||||
|
||||
[SerializeField, Group("Body parts")] ZombieBodyPart m_Head;
|
||||
[SerializeField, Group("Body parts")] ZombieBodyPart m_LeftArm;
|
||||
[SerializeField, Group("Body parts")] ZombieBodyPart m_RightArm;
|
||||
[SerializeField, Group("Body parts")] ZombieBodyPart m_LeftLeg;
|
||||
[SerializeField, Group("Body parts")] ZombieBodyPart m_RightLeg;
|
||||
|
||||
AIState m_State = AIState.Idle;
|
||||
|
||||
@@ -144,7 +186,7 @@ namespace RebootReality.jelycho.Enemies {
|
||||
SetIdleState();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (distToPlayerSq <= m_MaxAttackDistance * m_MaxAttackDistance) {
|
||||
m_NextAttackTimer -= dt;
|
||||
if (m_NextAttackTimer <= 0.0f) {
|
||||
@@ -181,6 +223,7 @@ namespace RebootReality.jelycho.Enemies {
|
||||
void Die() {
|
||||
s_Logger.Info("Die");
|
||||
EnableRagdoll();
|
||||
m_NavAgent.enabled = false;
|
||||
|
||||
m_State = AIState.Dead;
|
||||
died.Invoke();
|
||||
@@ -235,19 +278,13 @@ namespace RebootReality.jelycho.Enemies {
|
||||
}
|
||||
|
||||
void SetRagdollLocal(bool active) {
|
||||
foreach (Collider ragdollCollider in m_RagdollColliders) {
|
||||
ragdollCollider.enabled = active;
|
||||
}
|
||||
m_RootCollider.enabled = !active;
|
||||
|
||||
foreach (Rigidbody ragdollRigidbody in m_RagdollRigidbodies) {
|
||||
ragdollRigidbody.isKinematic = !active;
|
||||
}
|
||||
|
||||
m_Animator.enabled = !active;
|
||||
|
||||
foreach (Collider hitbox in m_Hitboxes) {
|
||||
hitbox.enabled = !active;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
@@ -280,6 +317,70 @@ namespace RebootReality.jelycho.Enemies {
|
||||
|
||||
return damage;
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: damage?
|
||||
//
|
||||
public void HitFeedback(float3 hitPosition) {
|
||||
if (RR.World.Context is WorldContext worldContext) {
|
||||
worldContext.FeedbacksManager.SpawnBloodSplash(hitPosition);
|
||||
}
|
||||
}
|
||||
|
||||
public void ReceiveDamage(ulong damage, ZombieBodyPartType bodyPartType) {
|
||||
if (!IsAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ZombieBodyPart bodyPart = GetBodyParty(bodyPartType);
|
||||
if (!bodyPart.IsAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bodyPart.HideParts();
|
||||
bodyPart.bloodStreamParticles.Play();
|
||||
|
||||
Die();
|
||||
}
|
||||
|
||||
public bool HasBodyPart(ZombieBodyPartType bodyPart) {
|
||||
if (Health <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (bodyPart == ZombieBodyPartType.Body) {
|
||||
return true;
|
||||
}
|
||||
|
||||
ZombieBodyPart part = GetBodyParty(bodyPart);
|
||||
if (part == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return part.IsAlive();
|
||||
}
|
||||
|
||||
ZombieBodyPart GetBodyParty(ZombieBodyPartType bodyPart) {
|
||||
return bodyPart switch {
|
||||
ZombieBodyPartType.Body => null,
|
||||
ZombieBodyPartType.Head => m_Head,
|
||||
ZombieBodyPartType.LeftArm => m_LeftArm,
|
||||
ZombieBodyPartType.RightArm => m_RightArm,
|
||||
ZombieBodyPartType.LeftLeg => m_LeftLeg,
|
||||
ZombieBodyPartType.RightLeg => m_RightLeg,
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(bodyPart), bodyPart, null)
|
||||
};
|
||||
}
|
||||
|
||||
///
|
||||
/// @MARK: Utility
|
||||
///
|
||||
[Button]
|
||||
void SetupHurtboxes() {
|
||||
foreach (ZombieHurtbox hurtbox in GetComponentsInChildren<ZombieHurtbox>()) {
|
||||
hurtbox.owner = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ZombieActorEvents {
|
||||
|
||||
28
Assets/jelycho/Code/Enemies/ZombieHurtbox.cs
Normal file
28
Assets/jelycho/Code/Enemies/ZombieHurtbox.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Assertions;
|
||||
|
||||
namespace RebootReality.jelycho.Enemies {
|
||||
public class ZombieHurtbox : MonoBehaviour, IHurtbox {
|
||||
public ZombieActor owner;
|
||||
[SerializeField] ZombieBodyPartType m_BodyPart = ZombieBodyPartType.Body;
|
||||
|
||||
void Awake() {
|
||||
Assert.IsNotNull(owner);
|
||||
}
|
||||
|
||||
public void ReceiveDamage(Actor attacker, ulong damage, float3 worldHitPos) {
|
||||
if (!owner.HasBodyPart(m_BodyPart)) {
|
||||
return;
|
||||
}
|
||||
|
||||
owner.ReceiveDamage(damage, m_BodyPart);
|
||||
|
||||
if (RR.World.Context is WorldContext worldContext) {
|
||||
worldContext.FeedbacksManager.SpawnBloodSplash(worldHitPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/Enemies/ZombieHurtbox.cs.meta
Normal file
3
Assets/jelycho/Code/Enemies/ZombieHurtbox.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 37d2b8f1679f41288194769e98584e0e
|
||||
timeCreated: 1756341610
|
||||
@@ -24,8 +24,10 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
public class FeedbacksManagerActor : Actor {
|
||||
static readonly Logger s_Logger = new Logger(nameof(FeedbacksManagerActor));
|
||||
|
||||
[SerializeField] Volume m_Volume;
|
||||
[SerializeField] ParticleSystem m_BloodSplashParticles;
|
||||
|
||||
[SerializeField] Volume m_Volume;
|
||||
|
||||
[SerializeField] float m_QuickAttackIndicatorDuration = 0.3f;
|
||||
[SerializeField] float m_QuickAttackIndicatorPaniniAppearSpeed = 2.0f;
|
||||
[SerializeField] float m_QuickAttackIndicatorChromaticAppearSpeed = 2.0f;
|
||||
@@ -115,6 +117,14 @@ namespace RebootReality.jelycho.Feedbacks {
|
||||
m_IsChargeReady = false;
|
||||
}
|
||||
|
||||
public void SpawnBloodSplash(float3 worldPosition) {
|
||||
ParticleSystem.EmitParams emitParams = new ParticleSystem.EmitParams {
|
||||
position = worldPosition
|
||||
};
|
||||
|
||||
m_BloodSplashParticles.Emit(emitParams, 128);
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Actor
|
||||
//
|
||||
|
||||
86
Assets/jelycho/Code/HitboxManagerActor.cs
Normal file
86
Assets/jelycho/Code/HitboxManagerActor.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System;
|
||||
using Oddworm.Framework;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho {
|
||||
public class HitboxManagerActor : Actor {
|
||||
static readonly Logger s_Logger = new Logger(nameof(HitboxManagerActor));
|
||||
static readonly Collider[] s_CollidersBuffer = new Collider[512];
|
||||
|
||||
[ConfigVar("debug.hitbox", 0, "Shows hitboxes")]
|
||||
static ConfigVar s_ShowHitboxesDebug;
|
||||
|
||||
static bool ShouldShowHitboxes() {
|
||||
return s_ShowHitboxesDebug.IndexValue > 0;
|
||||
}
|
||||
|
||||
[SerializeField] Color m_HitboxDebugColor = Color.red;
|
||||
[SerializeField] float m_HitboxDebugDuration = 3.0f;
|
||||
|
||||
[SerializeField] LayerMask m_HurtboxesLayerMask;
|
||||
|
||||
//
|
||||
// @MARK: HitboxManager
|
||||
//
|
||||
public void HitSphere(float3 origin, float radius, Actor attacker, ulong damage) {
|
||||
if (RR.IsServer()) {
|
||||
HitSphereLocal(origin, radius, attacker, damage);
|
||||
} else {
|
||||
throw new NotImplementedException("Hitting on client not implemented yet!");
|
||||
}
|
||||
}
|
||||
|
||||
void HitSphereLocal(float3 origin, float radius, Actor attacker, ulong damage) {
|
||||
if (ShouldShowHitboxes()) {
|
||||
DbgDraw.Sphere(origin,
|
||||
Quaternion.identity,
|
||||
Vector3.one * radius,
|
||||
m_HitboxDebugColor,
|
||||
m_HitboxDebugDuration);
|
||||
}
|
||||
|
||||
int count = Physics.OverlapSphereNonAlloc(origin, radius, s_CollidersBuffer, m_HurtboxesLayerMask);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (s_CollidersBuffer[i].TryGetComponent(out IHurtbox hurtbox)) {
|
||||
float3 p = s_CollidersBuffer[i].ClosestPoint(origin);
|
||||
if (ShouldShowHitboxes()) {
|
||||
DbgDraw.Sphere(p, Quaternion.identity, Vector3.one * 0.1f, Color.magenta, m_HitboxDebugDuration);
|
||||
}
|
||||
|
||||
hurtbox.ReceiveDamage(attacker, damage, p);
|
||||
}
|
||||
|
||||
// if (s_CollidersBuffer[i].TryGetComponent(out IKillable killable)) {
|
||||
// Actor killableActor = killable as Actor;
|
||||
// if (killableActor == attacker) {
|
||||
// continue;
|
||||
// }
|
||||
//
|
||||
// if (killable is IHasHitFeedback hitFeedback) {
|
||||
// float3 p = s_CollidersBuffer[i].ClosestPoint(origin);
|
||||
// if (ShouldShowHitboxes()) {
|
||||
// DbgDraw.Sphere(p, Quaternion.identity, Vector3.one * 0.1f, Color.magenta, m_HitboxDebugDuration);
|
||||
// }
|
||||
//
|
||||
// hitFeedback.HitFeedback(p);
|
||||
// }
|
||||
//
|
||||
// // @TODO: send command to server to hit an killable
|
||||
// killable.OnHit(attacker, damage);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// @MARK: Actor
|
||||
//
|
||||
protected override IActorData CreateActorData() {
|
||||
return new NoActorData();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/HitboxManagerActor.cs.meta
Normal file
3
Assets/jelycho/Code/HitboxManagerActor.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f3973c6291404892a6d48dcb056b6de5
|
||||
timeCreated: 1755826207
|
||||
8
Assets/jelycho/Code/IHurtbox.cs
Normal file
8
Assets/jelycho/Code/IHurtbox.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using RebootKit.Engine.Simulation;
|
||||
using Unity.Mathematics;
|
||||
|
||||
namespace RebootReality.jelycho {
|
||||
public interface IHurtbox {
|
||||
void ReceiveDamage(Actor attacker, ulong damage, float3 worldHitPos);
|
||||
}
|
||||
}
|
||||
3
Assets/jelycho/Code/IHurtbox.cs.meta
Normal file
3
Assets/jelycho/Code/IHurtbox.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 82f5148be9a14cd4a59245f69d6f4079
|
||||
timeCreated: 1756341517
|
||||
@@ -9,8 +9,6 @@ namespace RebootReality.jelycho.Items {
|
||||
static readonly Logger s_Logger = new Logger(nameof(ItemActor));
|
||||
|
||||
class ItemActorData : IActorData {
|
||||
public ItemConfig Config;
|
||||
|
||||
public void Serialize(NetworkBufferWriter writer) { }
|
||||
public void Deserialize(NetworkBufferReader reader) { }
|
||||
|
||||
@@ -33,9 +31,7 @@ namespace RebootReality.jelycho.Items {
|
||||
// @MARK: Actor
|
||||
//
|
||||
protected override IActorData CreateActorData() {
|
||||
return new ItemActorData {
|
||||
Config = Config
|
||||
};
|
||||
return new ItemActorData { };
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -37,6 +37,9 @@ namespace RebootReality.jelycho.Items {
|
||||
public class ItemConfig {
|
||||
public Sprite icon;
|
||||
|
||||
[Header("Stats")]
|
||||
public ulong baseDamage = 10;
|
||||
|
||||
[Header("Mounting")]
|
||||
[MaxLength(32)] public string characterEquippedMountSlotName = "hand_right";
|
||||
|
||||
|
||||
@@ -1,31 +1,24 @@
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootReality.jelycho.Enemies;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Simulation;
|
||||
using RebootReality.jelycho.Player;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RebootReality.jelycho.Items {
|
||||
public class ItemQuickAttackAction : IItemQuickAttackAction {
|
||||
static readonly Collider[] s_CollidersBuffer = new Collider[512];
|
||||
|
||||
public void Attack(Actor attacker, ItemActor itemActor) {
|
||||
if (RR.World.Context is not WorldContext worldContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
PlayerActor playerActor = attacker as PlayerActor;
|
||||
if (playerActor == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 attackPosition = playerActor.GetAttackPosition();
|
||||
float attackRadius = 1.0f;
|
||||
|
||||
int count = Physics.OverlapSphereNonAlloc(attackPosition, 3.0f, s_CollidersBuffer);
|
||||
for (int i = 0; i < count; ++i) {
|
||||
if (s_CollidersBuffer[i].TryGetComponent(out IKillable killable)) {
|
||||
Actor killableActor = killable as Actor;
|
||||
if (killableActor == attacker) {
|
||||
continue;
|
||||
}
|
||||
|
||||
playerActor.DealDamage(killable);
|
||||
}
|
||||
}
|
||||
worldContext.HitboxManager.HitSphere(attackPosition, attackRadius, attacker, itemActor.Config.baseDamage);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using Oddworm.Framework;
|
||||
using R3;
|
||||
using RebootKit.Engine.Extensions;
|
||||
using RebootKit.Engine.Foundation;
|
||||
using RebootKit.Engine.Main;
|
||||
using RebootKit.Engine.Network;
|
||||
using RebootKit.Engine.Simulation;
|
||||
@@ -11,7 +9,6 @@ using RebootReality.jelycho.Items;
|
||||
using Unity.Collections;
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using Logger = RebootKit.Engine.Foundation.Logger;
|
||||
|
||||
namespace RebootReality.jelycho.Player {
|
||||
@@ -298,6 +295,10 @@ namespace RebootReality.jelycho.Player {
|
||||
m_QuickAttackComboCounter = 0;
|
||||
PlayQuickAttackAnimation(m_QuickAttackComboCounter);
|
||||
m_QuickAttackState = QuickAttackState.PlayingAnimation;
|
||||
|
||||
if (m_EquippedItem.Config.quickAttackAction != null) {
|
||||
m_EquippedItem.Config.quickAttackAction.Attack(this, m_EquippedItem);
|
||||
}
|
||||
} else if (m_QuickAttackState == QuickAttackState.PlayingAnimation) {
|
||||
m_QuickAttackComboCounter = 0;
|
||||
} else if (m_QuickAttackState == QuickAttackState.WaitingForNextAttack) {
|
||||
@@ -872,7 +873,10 @@ namespace RebootReality.jelycho.Player {
|
||||
}
|
||||
|
||||
public Vector3 GetAttackPosition() {
|
||||
return m_Camera.transform.position + m_Camera.transform.forward * 1.5f;
|
||||
float3 origin = m_Camera.Camera.transform.position;
|
||||
float3 dir = m_Camera.Camera.transform.forward;
|
||||
float3 pos = origin + dir * 1.5f;
|
||||
return pos;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
@@ -14,5 +14,6 @@ namespace RebootReality.jelycho {
|
||||
[field: SerializeField] public FeedbacksManagerActor FeedbacksManager { get; private set; }
|
||||
[field: SerializeField] public InfectedAreaManager InfectedAreaManager { get; private set; }
|
||||
[field: SerializeField] public WavesManagerActor WavesManager { get; private set; }
|
||||
[field: SerializeField] public HitboxManagerActor HitboxManager { get; private set; }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user