Skip to content

Commit 31c3c3f

Browse files
authored
[MTTB-409] Fix a Healer ability doesn't work (#893) (#893)
* [MTTB-409] Fix a Healer ability doesn't work (#893) * Updated CHANGELOG.md * Updated CHANGELOG.md (A richer description of this PR)
1 parent 9a1ca64 commit 31c3c3f

File tree

10 files changed

+98
-19
lines changed

10 files changed

+98
-19
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
version https://git-lfs.github.com/spec/v1
2-
oid sha256:6c16b018e4bfa01b23bea3e0f6ef366ac529f16a749e38c93384e1e38a5c7ab8
3-
size 1159
2+
oid sha256:5f28c042dceb2a438a0faec834feaf3b3470fc34629e4ab5e489af33b780ed32
3+
size 1185

Assets/Scripts/Gameplay/Action/ActionUtils.cs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,35 @@ public static class ActionUtils
3030
const float k_VeryCloseTeleportRange = k_CloseDistanceOffset + 1;
3131

3232
/// <summary>
33-
/// Does a melee foe hit detect.
33+
/// Detects friends and/or foes near us.
3434
/// </summary>
35-
/// <param name="isNPC">true if the attacker is an NPC (and therefore should hit PCs). False for the reverse.</param>
35+
/// <param name="wantPcs">true if we should detect PCs</param>
36+
/// <param name="wantNpcs">true if we should detect NPCs</param>
3637
/// <param name="attacker">The collider of the attacking GameObject.</param>
37-
/// <param name="range">The range in meters to check for foes.</param>
38+
/// <param name="range">The range in meters to check.</param>
39+
/// <param name="radius">The radius in meters to check.</param>
3840
/// <param name="results">Place an uninitialized RayCastHit[] ref in here. It will be set to the results array. </param>
39-
/// <remarks>
40-
/// This method does not alloc. It returns a maximum of 4 results. Consume the results immediately, as the array will be overwritten with
41-
/// the next similar query.
42-
/// </remarks>
43-
/// <returns>Total number of foes encountered. </returns>
44-
public static int DetectMeleeFoe(bool isNPC, Collider attacker, float range, out RaycastHit[] results)
41+
/// <returns></returns>
42+
public static int DetectNearbyEntitiesUseSphere(bool wantPcs, bool wantNpcs, Collider attacker, float range, float radius, out RaycastHit[] results)
4543
{
46-
return DetectNearbyEntities(isNPC, !isNPC, attacker, range, out results);
44+
var myBounds = attacker.bounds;
45+
46+
if (s_PCLayer == -1)
47+
s_PCLayer = LayerMask.NameToLayer("PCs");
48+
if (s_NpcLayer == -1)
49+
s_NpcLayer = LayerMask.NameToLayer("NPCs");
50+
51+
int mask = 0;
52+
if (wantPcs)
53+
mask |= (1 << s_PCLayer);
54+
if (wantNpcs)
55+
mask |= (1 << s_NpcLayer);
56+
57+
int numResults = Physics.SphereCastNonAlloc(attacker.transform.position, radius,
58+
attacker.transform.forward, s_Hits, range, mask);
59+
60+
results = s_Hits;
61+
return numResults;
4762
}
4863

4964
/// <summary>

Assets/Scripts/Gameplay/Action/ConcreteActions/DashAttackAction.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private void PerformMeleeAttack(ServerCharacter parent)
9898
// perform a typical melee-hit. But note that we are using the Radius field for range, not the Range field!
9999
IDamageable foe = MeleeAction.GetIdealMeleeFoe(Config.IsFriendly ^ parent.IsNpc,
100100
parent.physicsWrapper.DamageCollider,
101-
Config.Radius,
101+
Config.Radius, 0.0f,
102102
(Data.TargetIds != null && Data.TargetIds.Length > 0 ? Data.TargetIds[0] : 0));
103103

104104
if (foe != null)

Assets/Scripts/Gameplay/Action/ConcreteActions/MeleeAction.cs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public override bool OnUpdate(ServerCharacter clientCharacter)
8585
/// <returns></returns>
8686
private IDamageable DetectFoe(ServerCharacter parent, ulong foeHint = 0)
8787
{
88-
return GetIdealMeleeFoe(Config.IsFriendly ^ parent.IsNpc, parent.physicsWrapper.DamageCollider, Config.Range, foeHint);
88+
return GetIdealMeleeFoe(Config.IsFriendly ^ parent.IsNpc, parent.physicsWrapper.DamageCollider, Config.Range, Config.Radius, foeHint);
8989
}
9090

9191
/// <summary>
@@ -96,25 +96,48 @@ private IDamageable DetectFoe(ServerCharacter parent, ulong foeHint = 0)
9696
/// <param name="isNPC">true if the attacker is an NPC (and therefore should hit PCs). False for the reverse.</param>
9797
/// <param name="ourCollider">The collider of the attacking GameObject.</param>
9898
/// <param name="meleeRange">The range in meters to check for foes.</param>
99+
/// <param name="meleeRadius">The radius in meters to check for foes.</param>
99100
/// <param name="preferredTargetNetworkId">The NetworkObjectId of our preferred foe, or 0 if no preference</param>
100101
/// <returns>ideal target's IDamageable, or null if no valid target found</returns>
101-
public static IDamageable GetIdealMeleeFoe(bool isNPC, Collider ourCollider, float meleeRange, ulong preferredTargetNetworkId)
102+
/// <remarks>
103+
/// If a Radius value is set (greater than 0), collision checking will be done with a Sphere the size of the Radius, not the size of the Box.
104+
/// Also, if multiple targets collide as a result, the target with the highest total damage is prioritized.
105+
/// </remarks>
106+
public static IDamageable GetIdealMeleeFoe(bool isNPC, Collider ourCollider, float meleeRange, float meleeRadius, ulong preferredTargetNetworkId)
102107
{
103108
RaycastHit[] results;
104-
int numResults = ActionUtils.DetectMeleeFoe(isNPC, ourCollider, meleeRange, out results);
109+
int numResults = 0.0f < meleeRadius
110+
? ActionUtils.DetectNearbyEntitiesUseSphere(isNPC, !isNPC, ourCollider, meleeRange, meleeRadius, out results)
111+
: ActionUtils.DetectNearbyEntities(isNPC, !isNPC, ourCollider, meleeRange, out results);
105112

106113
IDamageable foundFoe = null;
107114

108115
//everything that got hit by the raycast should have an IDamageable component, so we can retrieve that and see if they're appropriate targets.
109116
//we always prefer the hinted foe. If he's still in range, he should take the damage, because he's who the client visualization
110117
//system will play the hit-react on (in case there's any ambiguity).
118+
//if that is not the case, we prioritize the target with the highest total damage.
119+
int maxDamage = int.MinValue;
120+
111121
for (int i = 0; i < numResults; i++)
112122
{
113123
var damageable = results[i].collider.GetComponent<IDamageable>();
114-
if (damageable != null && damageable.IsDamageable() &&
115-
(damageable.NetworkObjectId == preferredTargetNetworkId || foundFoe == null))
124+
if (damageable == null || !damageable.IsDamageable())
125+
{
126+
continue;
127+
}
128+
129+
if (damageable.NetworkObjectId == preferredTargetNetworkId)
130+
{
131+
foundFoe = damageable;
132+
maxDamage = int.MaxValue;
133+
continue;
134+
}
135+
136+
var totalDamage = damageable.GetTotalDamage();
137+
if (foundFoe == null || maxDamage < totalDamage)
116138
{
117139
foundFoe = damageable;
140+
maxDamage = totalDamage;
118141
}
119142
}
120143

Assets/Scripts/Gameplay/GameplayObjects/Breakable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ public void ReceiveHP(ServerCharacter inflicter, int HP)
116116
}
117117
}
118118

119+
public int GetTotalDamage()
120+
{
121+
return Math.Max(0, m_MaxHealth.Value - m_NetworkHealthState.HitPoints.Value);
122+
}
123+
119124
private void Break()
120125
{
121126
IsBroken.Value = true;

Assets/Scripts/Gameplay/GameplayObjects/Character/ServerCharacter.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using System;
12
using System.Collections;
23
using Unity.BossRoom.ConnectionManagement;
34
using Unity.BossRoom.Gameplay.Actions;
@@ -156,6 +157,7 @@ public override void OnNetworkSpawn()
156157
NetLifeState.LifeState.OnValueChanged += OnLifeStateChanged;
157158
m_DamageReceiver.DamageReceived += ReceiveHP;
158159
m_DamageReceiver.CollisionEntered += CollisionEntered;
160+
m_DamageReceiver.GetTotalDamageFunc += GetTotalDamage;
159161

160162
if (IsNpc)
161163
{
@@ -179,6 +181,7 @@ public override void OnNetworkDespawn()
179181
{
180182
m_DamageReceiver.DamageReceived -= ReceiveHP;
181183
m_DamageReceiver.CollisionEntered -= CollisionEntered;
184+
m_DamageReceiver.GetTotalDamageFunc -= GetTotalDamage;
182185
}
183186
}
184187

@@ -393,6 +396,11 @@ void CollisionEntered(Collision collision)
393396
}
394397
}
395398

399+
int GetTotalDamage()
400+
{
401+
return Math.Max(0, CharacterClass.BaseHP.Value - HitPoints);
402+
}
403+
396404
/// <summary>
397405
/// This character's AIBrain. Will be null if this is not an NPC.
398406
/// </summary>

Assets/Scripts/Gameplay/GameplayObjects/DamageReceiver.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ public class DamageReceiver : NetworkBehaviour, IDamageable
1111

1212
public event Action<Collision> CollisionEntered;
1313

14+
public event Func<int> GetTotalDamageFunc;
15+
1416
[SerializeField]
1517
NetworkLifeState m_NetworkLifeState;
1618

@@ -22,6 +24,16 @@ public void ReceiveHP(ServerCharacter inflicter, int HP)
2224
}
2325
}
2426

27+
public int GetTotalDamage()
28+
{
29+
if (!IsDamageable())
30+
{
31+
return 0;
32+
}
33+
34+
return GetTotalDamageFunc?.Invoke() ?? 0;
35+
}
36+
2537
public IDamageable.SpecialDamageFlags GetSpecialDamageFlags()
2638
{
2739
return IDamageable.SpecialDamageFlags.None;

Assets/Scripts/Gameplay/GameplayObjects/IDamageable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ public interface IDamageable
1717
/// <param name="HP">The damage done. Negative value is damage, positive is healing.</param>
1818
void ReceiveHP(ServerCharacter inflicter, int HP);
1919

20+
/// <summary>
21+
/// Get the total damage value.
22+
/// </summary>
23+
/// <returns>The return value is your total health minus your current health.</returns>
24+
int GetTotalDamage();
25+
2026
/// <summary>
2127
/// The NetworkId of this object.
2228
/// </summary>

Assets/Scripts/Gameplay/UserInput/ClientInputSender.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,11 @@ void PopulateSkillRequest(Vector3 hitPoint, ActionID actionID, ref ActionRequest
420420
// figure out the Direction in case we want to send it
421421
Vector3 offset = hitPoint - m_PhysicsWrapper.Transform.position;
422422
offset.y = 0;
423-
Vector3 direction = offset.normalized;
423+
424+
//there is a bug where the direction is flipped if the hitPos and current position are almost the same,
425+
//so we use the character's direction instead.
426+
float directionLength = offset.magnitude;
427+
Vector3 direction = 1.0f/*epsilon*/ <= directionLength ? (offset / directionLength) : m_PhysicsWrapper.Transform.forward;
424428

425429
switch (actionConfig.Logic)
426430
{

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,12 @@ Additional documentation and release notes are available at [Multiplayer Documen
2020
### Cleanup
2121
* Removed ParrelSync from the project
2222

23+
### Fixed
24+
* Fix a Healer ability doesn't work (#893)
25+
* Changed the way characters are oriented when using skills.
26+
* Added the GetTotalDamage API to the IDamagble interface. This number is your maximum health minus your current health.
27+
* Changed the way MeleeAction selects a target when there are multiple targets to collide with. The target with the highest GetTotalDamage value (mentioned above) will be selected.
28+
2329

2430
## [2.5.0] - 2024-04-18
2531

0 commit comments

Comments
 (0)