-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
316 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
Content.Server/NPC/Components/NPCAbilityCombatComponent.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Exodus-Lavaland | ||
namespace Content.Server.NPC.Components; | ||
|
||
/// <summary> | ||
/// Added to NPCs whenever they're in ability combat so they can be handled by the dedicated system. | ||
/// </summary> | ||
[RegisterComponent] | ||
public sealed partial class NPCAbilityCombatComponent : Component | ||
{ | ||
[ViewVariables] | ||
public EntityUid Target; | ||
|
||
[ViewVariables] | ||
public AbilityCombatStatus Status = AbilityCombatStatus.Normal; | ||
|
||
[ViewVariables] | ||
public TimeSpan NextAction = new(); | ||
|
||
[ViewVariables] | ||
public int ActionsPerUpd = 1; | ||
|
||
[ViewVariables] | ||
public int UsedActionsLastUpd = 0; | ||
|
||
[ViewVariables] | ||
public float ActionsTimeReload = 1.0f; | ||
|
||
} | ||
|
||
public enum AbilityCombatStatus : byte | ||
{ | ||
/// <summary> | ||
/// The target isn't in LOS anymore. | ||
/// </summary> | ||
NotInSight, | ||
|
||
/// <summary> | ||
/// Due to some generic reason we are unable to attack the target. | ||
/// </summary> | ||
Unspecified, | ||
|
||
/// <summary> | ||
/// Set if we can't reach the target for whatever reason. | ||
/// </summary> | ||
TargetUnreachable, | ||
|
||
/// <summary> | ||
/// If the target is outside of our melee range. | ||
/// </summary> | ||
TargetOutOfRange, | ||
|
||
/// <summary> | ||
/// No dramas. | ||
/// </summary> | ||
Normal, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,238 @@ | ||
using System.Numerics; | ||
using Content.Server.NPC.Components; | ||
using Content.Shared.NPC; | ||
using Robust.Shared.Map; | ||
using Robust.Shared.Physics.Components; | ||
using Robust.Shared.Random; | ||
using Content.Shared.ActionBlocker; | ||
using Content.Shared.Actions.Events; | ||
using Content.Shared.Administration.Logs; | ||
using Content.Shared.Database; | ||
|
||
using Content.Shared.Interaction; | ||
using Content.Shared.Actions; | ||
using Content.Server.Actions; | ||
using Content.Shared.Directions; | ||
|
||
namespace Content.Server.NPC.Systems; | ||
|
||
public sealed partial class NPCCombatSystem | ||
{ | ||
[Dependency] private readonly ActionsSystem _actions = default!; | ||
[Dependency] private readonly ActionBlockerSystem _actionBlockerSystem = default!; | ||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; | ||
[Dependency] private readonly RotateToFaceSystem _rotateToFaceSystem = default!; | ||
|
||
private const float TargetAbilityLostRange = 28f; | ||
|
||
private void InitializeAbility() | ||
{ | ||
SubscribeLocalEvent<NPCAbilityCombatComponent, ComponentShutdown>(OnAbilityShutdown); | ||
} | ||
|
||
private void OnAbilityShutdown(EntityUid uid, NPCAbilityCombatComponent component, ComponentShutdown args) | ||
{ | ||
_steering.Unregister(uid); | ||
} | ||
|
||
private void UpdateAbility(float frameTime) | ||
{ | ||
var xformQuery = GetEntityQuery<TransformComponent>(); | ||
var physicsQuery = GetEntityQuery<PhysicsComponent>(); | ||
var curTime = _timing.CurTime; | ||
var query = EntityQueryEnumerator<NPCAbilityCombatComponent, ActiveNPCComponent>(); | ||
|
||
while (query.MoveNext(out var uid, out var comp, out _)) | ||
{ | ||
CastAction(uid, comp, curTime, physicsQuery, xformQuery); | ||
} | ||
} | ||
|
||
private void CastAction(EntityUid uid, NPCAbilityCombatComponent combatComp, TimeSpan curTime, EntityQuery<PhysicsComponent> physicsQuery, EntityQuery<TransformComponent> xformQuery) | ||
{ | ||
combatComp.Status = AbilityCombatStatus.Normal; | ||
|
||
if (!xformQuery.TryGetComponent(uid, out var xform) || | ||
!xformQuery.TryGetComponent(combatComp.Target, out var targetXform)) | ||
{ | ||
combatComp.Status = AbilityCombatStatus.TargetUnreachable; | ||
return; | ||
} | ||
|
||
if (!xform.Coordinates.TryDistance(EntityManager, targetXform.Coordinates, out var distance)) | ||
{ | ||
combatComp.Status = AbilityCombatStatus.TargetUnreachable; | ||
return; | ||
} | ||
|
||
if (distance > TargetMeleeLostRange) | ||
{ | ||
combatComp.Status = AbilityCombatStatus.TargetUnreachable; | ||
return; | ||
} | ||
|
||
if (TryComp<NPCSteeringComponent>(uid, out var steering) && | ||
steering.Status == SteeringStatus.NoPath) | ||
{ | ||
combatComp.Status = AbilityCombatStatus.TargetUnreachable; | ||
return; | ||
} | ||
|
||
_steering.Register(uid, new EntityCoordinates(combatComp.Target, Vector2.Zero), steering); | ||
|
||
if (combatComp.NextAction > curTime) | ||
return; | ||
|
||
// Get Actions | ||
if (!TryComp(uid, out ActionsComponent? actionComp)) | ||
return; | ||
|
||
List<EntityUid> actions = []; | ||
actions.AddRange(actionComp.Actions); | ||
|
||
while (actions.Count > 0) | ||
{ | ||
if (combatComp.UsedActionsLastUpd >= combatComp.ActionsPerUpd) | ||
break; | ||
|
||
var act = _random.PickAndTake(actions); | ||
|
||
var attemptEv = new ActionAttemptEvent(uid); | ||
RaiseLocalEvent(act, ref attemptEv); | ||
if (attemptEv.Cancelled) | ||
return; | ||
|
||
if (TryUseAction(uid, act, distance, combatComp, curTime)) | ||
combatComp.UsedActionsLastUpd++; | ||
} | ||
|
||
if (combatComp.UsedActionsLastUpd >= combatComp.ActionsPerUpd) | ||
{ | ||
combatComp.UsedActionsLastUpd = 0; | ||
combatComp.NextAction = curTime + TimeSpan.FromSeconds(combatComp.ActionsTimeReload); | ||
} | ||
} | ||
|
||
private bool TryUseAction(EntityUid uid, | ||
EntityUid actionUid, | ||
float distance, | ||
NPCAbilityCombatComponent combatComp, | ||
TimeSpan curTime) | ||
{ | ||
if (!TryComp(uid, out ActionsComponent? actionComp)) | ||
return false; | ||
|
||
if (!TryComp(actionUid, out MetaDataComponent? actionMeta)) | ||
return false; | ||
|
||
if (!_actions.TryGetActionData(actionUid, out var action)) | ||
return false; | ||
|
||
if (!action.Enabled) | ||
return false; | ||
|
||
// check for action use prevention | ||
var attemptEv = new ActionAttemptEvent(uid); | ||
RaiseLocalEvent(actionUid, ref attemptEv); | ||
if (attemptEv.Cancelled) | ||
return false; | ||
|
||
if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime) | ||
return false; | ||
|
||
if (action is { Charges: < 1, RenewCharges: true }) | ||
_actions.ResetCharges(actionUid); | ||
|
||
BaseActionEvent? performEvent = null; | ||
|
||
if (action.CheckConsciousness && !_actionBlockerSystem.CanConsciouslyPerformAction(uid)) | ||
return false; | ||
|
||
if (!combatComp.Target.IsValid()) | ||
{ | ||
Log.Error($"Attempted to perform an entity-targeted action without a target! Action: {actionMeta.EntityName}"); | ||
return false; | ||
} | ||
|
||
if (action.MinAIUseRange >= distance || | ||
action.MaxAIUseRange <= distance) | ||
{ | ||
Log.Error("Out"); | ||
return false; | ||
} | ||
|
||
// Validate request by checking action blockers and the like: | ||
switch (action) | ||
{ | ||
case EntityTargetActionComponent entityAction: | ||
var targetWorldPos = _transform.GetWorldPosition(combatComp.Target); | ||
|
||
if (entityAction.Range <= distance) | ||
return false; | ||
|
||
_rotateToFaceSystem.TryFaceCoordinates(uid, targetWorldPos); | ||
|
||
if (!_actions.ValidateEntityTarget(uid, combatComp.Target, (actionUid, entityAction))) | ||
return false; | ||
|
||
_adminLogger.Add(LogType.Action, | ||
$"{ToPrettyString(uid):user} is performing the {actionMeta.EntityName:action} action (provided by {ToPrettyString(action.Container ?? uid):provider}) targeted at {ToPrettyString(combatComp.Target):target}."); | ||
|
||
if (entityAction.Event != null) | ||
{ | ||
entityAction.Event.Target = combatComp.Target; | ||
Dirty(actionUid, entityAction); | ||
performEvent = entityAction.Event; | ||
} | ||
break; | ||
case WorldTargetActionComponent worldAction: | ||
var entityCoordinatesTarget = Transform(combatComp.Target).Coordinates; | ||
|
||
if (worldAction.Range <= distance) | ||
{ | ||
var mapTargetPos = entityCoordinatesTarget.ToMapPos(EntityManager, _transform); | ||
var mapUserPos = Transform(uid).Coordinates.ToMapPos(EntityManager, _transform); | ||
|
||
var direction = mapTargetPos - mapUserPos; | ||
var coefficient = worldAction.Range / distance; | ||
var delta = new Vector2(direction.X * coefficient, direction.Y * coefficient); | ||
entityCoordinatesTarget = new EntityCoordinates(entityCoordinatesTarget.EntityId, mapUserPos + delta); | ||
} | ||
|
||
_rotateToFaceSystem.TryFaceCoordinates(uid, entityCoordinatesTarget.ToMapPos(EntityManager, _transform)); | ||
|
||
if (!_actions.ValidateWorldTarget(uid, entityCoordinatesTarget, (actionUid, worldAction))) | ||
return false; | ||
|
||
_adminLogger.Add(LogType.Action, | ||
$"{ToPrettyString(uid):user} is performing the {actionMeta.EntityName:action} action (provided by {ToPrettyString(action.Container ?? uid):provider}) targeted at {entityCoordinatesTarget:target}."); | ||
|
||
if (worldAction.Event != null) | ||
{ | ||
worldAction.Event.Target = entityCoordinatesTarget; | ||
Dirty(actionUid, worldAction); | ||
performEvent = worldAction.Event; | ||
} | ||
|
||
break; | ||
case InstantActionComponent instantAction: | ||
if (action.CheckCanInteract && !_actionBlockerSystem.CanInteract(uid, null)) | ||
return false; | ||
|
||
_adminLogger.Add(LogType.Action, | ||
$"{ToPrettyString(uid):user} is performing the {actionMeta.EntityName:action} action provided by {ToPrettyString(action.Container ?? uid):provider}."); | ||
|
||
performEvent = instantAction.Event; | ||
break; | ||
} | ||
|
||
if (performEvent != null) | ||
performEvent.Performer = uid; | ||
|
||
// All checks passed. Perform the action! | ||
_actions.PerformAction(uid, actionComp, actionUid, action, performEvent, curTime); | ||
|
||
return true; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
4 changes: 4 additions & 0 deletions
4
Resources/Audio/Exodus/Lavaland/Bosses/Herophant/attributions.yml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
- files: ["spawn.ogg", "bum.ogg"] | ||
license: "CC-BY-SA-3.0" | ||
copyright: "Taken from Zvukipro.com" | ||
source: "https://zvukipro.com/electronic/368-zvuki-lazera.html" |
Binary file not shown.
Binary file not shown.