You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
6.7 KiB
C#
161 lines
6.7 KiB
C#
1 week ago
|
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.ClientPlayActionRpc(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.ClientStopChargingUpRpc(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];
|
||
|
}
|
||
|
}
|
||
|
}
|