-
Notifications
You must be signed in to change notification settings - Fork 1
/
ChargedLaunchProjectileAction.cs
160 lines (140 loc) · 6.67 KB
/
ChargedLaunchProjectileAction.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
using System;
using Unity.BossRoom.Gameplay.GameplayObjects.Character;
using Unity.Netcode;
using UnityEngine;
namespace Unity.BossRoom.Gameplay.Actions
{
/// <summary>
/// A version of LaunchProjectileAction that can be "powered up" by holding down the attack key.
/// </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, but also cancels firing.
///
/// Once charge-up stops, the projectile is fired (unless it was stopped due to being attacked.)
///
/// The projectile can have various stats depending on how "charged up" the attack was. The ActionDescription's
/// Projectiles array should contain each tier of projectile, sorted from weakest to strongest.
///
/// </remarks>
[CreateAssetMenu(menuName = "BossRoom/Actions/Charged Launch Projectile Action")]
public partial class ChargedLaunchProjectileAction : LaunchProjectileAction
{
/// <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;
/// <summary>
/// Were we attacked while charging up? (If so, we won't actually fire.)
/// </summary>
private bool m_HitByAttack = false;
public override bool OnStart(ServerCharacter serverCharacter)
{
// if we have an explicit target, make sure we're aimed at them.
// (But if the player just clicked on an attack button, there won't be an explicit target, so we should stay facing however we're facing.)
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
serverCharacter.physicsWrapper.Transform.LookAt(initialTarget.transform.position);
}
}
serverCharacter.serverAnimationHandler.NetworkAnimator.SetTrigger(Config.Anim);
// start the "charging up" ActionFX
serverCharacter.clientCharacter.RecvDoActionClientRPC(Data);
// sanity-check our data a bit
Debug.Assert(Config.Projectiles.Length > 1, $"Action {name} has {Config.Projectiles.Length} Projectiles. Expected at least 2!");
foreach (var projectileInfo in Config.Projectiles)
{
Debug.Assert(projectileInfo.ProjectilePrefab, $"Action {name}: one of the Projectiles is missing its prefab!");
Debug.Assert(projectileInfo.Range > 0, $"Action {name}: one of the Projectiles has invalid Range!");
Debug.Assert(projectileInfo.Speed_m_s > 0, $"Action {name}: one of the Projectiles has invalid Speed_m_s!");
}
return true;
}
public override void Reset()
{
base.Reset();
m_ChargeEnded = false;
m_StoppedChargingUpTime = 0;
m_HitByAttack = false;
m_Graphics.Clear();
}
public override bool OnUpdate(ServerCharacter clientCharacter)
{
if (m_StoppedChargingUpTime == 0 && GetPercentChargedUp() >= 1)
{
// we haven't explicitly stopped charging up... but we've reached max charge, so that implicitly stops us
StopChargingUp(clientCharacter);
}
// we end as soon as we've stopped charging up (and have fired the projectile)
return m_StoppedChargingUpTime == 0;
}
public override void OnGameplayActivity(ServerCharacter serverCharacter, GameplayActivity activityType)
{
if (activityType == GameplayActivity.AttackedByEnemy)
{
// if we get attacked while charging up, we don't actually get to shoot!
m_HitByAttack = true;
StopChargingUp(serverCharacter);
}
else if (activityType == GameplayActivity.StoppedChargingUp)
{
StopChargingUp(serverCharacter);
}
}
public override void Cancel(ServerCharacter serverCharacter)
{
StopChargingUp(serverCharacter);
}
public override void End(ServerCharacter serverCharacter)
{
StopChargingUp(serverCharacter);
}
private void StopChargingUp(ServerCharacter parent)
{
if (m_StoppedChargingUpTime == 0)
{
m_StoppedChargingUpTime = Time.time;
if (!string.IsNullOrEmpty(Config.Anim2))
{
parent.serverAnimationHandler.NetworkAnimator.SetTrigger(Config.Anim2);
}
parent.clientCharacter.RecvStopChargingUpClientRpc(GetPercentChargedUp());
if (!m_HitByAttack)
{
LaunchProjectile(parent);
}
}
}
private float GetPercentChargedUp()
{
return ActionUtils.GetPercentChargedUp(m_StoppedChargingUpTime, TimeRunning, TimeStarted, Config.ExecTimeSeconds);
}
/// <summary>
/// Overridden from base-class to choose a different projectile depending on how "charged up" we got.
/// To do this, we assume that the Projectiles list is ordered from weakest to strongest.
/// </summary>
/// <remarks>
/// To reward players that fully charge-up their attack, we only return the strongest projectile when the
/// charge-up is at 100%. The other tiers of projectile are used for lesser charge-up amounts.
/// </remarks>
/// <returns>the projectile that should be used</returns>
protected override ProjectileInfo GetProjectileInfo()
{
if (Config.Projectiles.Length == 0) // uh oh, this is bad data
throw new System.Exception($"Action {name} has no Projectiles!");
// choose which prefab to use based on how charged-up we got.
// Note how we cast the result to an int, which implicitly rounds down.
// Thus, only a 100% maxed charge can return the most powerful prefab.
int projectileIdx = (int)(GetPercentChargedUp() * (Config.Projectiles.Length - 1));
return Config.Projectiles[projectileIdx];
}
}
}