-
Notifications
You must be signed in to change notification settings - Fork 1
/
ChargedShieldAction.cs
176 lines (153 loc) · 8.12 KB
/
ChargedShieldAction.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using System;
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Assertions;
namespace Unity.BossRoom.Gameplay.Actions
{
/// <summary>
/// A defensive action where the character becomes resistant to damage.
/// </summary>
/// <remarks>
/// The player can hold down the button for this ability to "charge it up" and make it more effective. Once it's been
/// charging for Description.ExecTimeSeconds, it reaches maximum charge. If the player is attacked by an enemy, that
/// also immediately stops the charge-up.
///
/// Once the charge-up stops (for any reason), the Action lasts for Description.EffectTimeSeconds before elapsing. During
/// this time, all incoming damage is reduced by a percentage from 50% to 100%, depending on how "charged up" it was.
///
/// When the Action is fully charged up, it provides a special additional benefit: if the boss tries to trample this
/// character, the boss becomes Stunned.
/// </remarks>
[CreateAssetMenu(menuName = "BossRoom/Actions/Charged Shield Action")]
public partial class ChargedShieldAction : Action
{
/// <summary>
/// Set once we've stopped charging up, for any reason:
/// - the player has let go of the button,
/// - we were attacked,
/// - or the maximum charge was reached.
/// </summary>
private float m_StoppedChargingUpTime = 0;
public override bool OnStart(ServerCharacter serverCharacter)
{
if (m_Data.TargetIds != null && m_Data.TargetIds.Length > 0)
{
NetworkObject initialTarget = NetworkManager.Singleton.SpawnManager.SpawnedObjects[m_Data.TargetIds[0]];
if (initialTarget)
{
// face our target, if we had one
serverCharacter.physicsWrapper.Transform.LookAt(initialTarget.transform.position);
}
}
// because this action can be visually started and stopped as often and as quickly as the player wants, it's possible
// for several copies of this action to be playing at once. This can lead to situations where several
// dying versions of the action raise the end-trigger, but the animator only lowers it once, leaving the trigger
// in a raised state. So we'll make sure that our end-trigger isn't raised yet. (Generally a good idea anyway.)
serverCharacter.serverAnimationHandler.NetworkAnimator.ResetTrigger(Config.Anim2);
// raise the start trigger to start the animation loop!
serverCharacter.serverAnimationHandler.NetworkAnimator.SetTrigger(Config.Anim);
serverCharacter.clientCharacter.RecvDoActionClientRPC(Data);
return true;
}
public override void Reset()
{
base.Reset();
m_ChargeGraphics = null;
m_ShieldGraphics = null;
m_StoppedChargingUpTime = 0;
}
private bool IsChargingUp()
{
return m_StoppedChargingUpTime == 0;
}
public override bool OnUpdate(ServerCharacter clientCharacter)
{
if (m_StoppedChargingUpTime == 0)
{
// we haven't explicitly stopped charging up... but if we've reached max charge, that implicitly stops us
if (TimeRunning >= Config.ExecTimeSeconds)
{
StopChargingUp(clientCharacter);
}
}
// we stop once the charge-up has ended and our effect duration has elapsed
return m_StoppedChargingUpTime == 0 || Time.time < (m_StoppedChargingUpTime + Config.EffectDurationSeconds);
}
public override bool ShouldBecomeNonBlocking()
{
return m_StoppedChargingUpTime != 0;
}
private float GetPercentChargedUp()
{
return ActionUtils.GetPercentChargedUp(m_StoppedChargingUpTime, TimeRunning, TimeStarted, Config.ExecTimeSeconds);
}
public override void BuffValue(BuffableValue buffType, ref float buffedValue)
{
if (buffType == BuffableValue.PercentDamageReceived)
{
float percentChargedUp = GetPercentChargedUp();
// the amount of damage reduction starts at 50% (for not-charged-up), then slowly increases to 100% depending on how charged-up we got
float percentDamageReduction = 0.5f + ((percentChargedUp * percentChargedUp) / 2);
// Now that we know how much damage to reduce it by, we need to set buffedValue to the inverse (because
// it's looking for how much damage to DO, not how much to REDUCE BY). Also note how we don't just SET
// buffedValue... we multiply our buff in with the current value. This lets our Action "stack"
// with any other Actions that also alter this variable.)
buffedValue *= 1 - percentDamageReduction;
}
else if (buffType == BuffableValue.ChanceToStunTramplers)
{
// if we are at "full charge", we stun enemies that try to trample us!
if (GetPercentChargedUp() >= 1)
{
buffedValue = 1;
}
}
}
public override void OnGameplayActivity(ServerCharacter serverCharacter, GameplayActivity activityType)
{
// for this particular type of Action, being attacked immediately causes you to stop charging up
if (activityType == GameplayActivity.AttackedByEnemy || activityType == GameplayActivity.StoppedChargingUp)
{
StopChargingUp(serverCharacter);
}
}
public override void Cancel(ServerCharacter serverCharacter)
{
StopChargingUp(serverCharacter);
// if stepped into invincibility, decrement invincibility counter
if (Mathf.Approximately(GetPercentChargedUp(), 1f))
{
serverCharacter.serverAnimationHandler.NetworkAnimator.Animator.SetInteger(Config.OtherAnimatorVariable,
serverCharacter.serverAnimationHandler.NetworkAnimator.Animator.GetInteger(Config.OtherAnimatorVariable) - 1);
}
}
private void StopChargingUp(ServerCharacter parent)
{
if (IsChargingUp())
{
m_StoppedChargingUpTime = Time.time;
parent.clientCharacter.RecvStopChargingUpClientRpc(GetPercentChargedUp());
parent.serverAnimationHandler.NetworkAnimator.SetTrigger(Config.Anim2);
parent.serverAnimationHandler.NetworkAnimator.ResetTrigger(Config.Anim);
//tell the animator controller to enter "invincibility mode" (where we don't flinch from damage)
if (Mathf.Approximately(GetPercentChargedUp(), 1f))
{
// increment our "invincibility counter". We use an integer count instead of a boolean because the player
// can restart their shield before the first one has ended, thereby getting two stacks of invincibility.
// So each active copy of the charge-up increments the invincibility counter, and the animator controller
// knows anything greater than zero means we shouldn't show hit-reacts.
parent.serverAnimationHandler.NetworkAnimator.Animator.SetInteger(Config.OtherAnimatorVariable,
parent.serverAnimationHandler.NetworkAnimator.Animator.GetInteger(Config.OtherAnimatorVariable) + 1);
}
}
}
public override bool OnStartClient(ClientCharacter clientCharacter)
{
Assert.IsTrue(Config.Spawns.Length == 2, $"Found {Config.Spawns.Length} spawns for action {name}. Should be exactly 2: a charge-up particle and a fully-charged particle");
base.OnStartClient(clientCharacter);
m_ChargeGraphics = InstantiateSpecialFXGraphic(Config.Spawns[0], clientCharacter.transform, true);
return true;
}
}
}