Goliath mob (#30839)
* Goliath mob * Update asteroid.yml * mcfuck yourself * add cloak * fixes * Update materials.yml
@@ -154,21 +154,6 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidAction(BaseActionComponent action, bool canReach = true)
|
|
||||||
{
|
|
||||||
if (!action.Enabled)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (action.Charges.HasValue && action.Charges <= 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var curTime = _timing.CurTime;
|
|
||||||
if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return canReach || action is BaseTargetActionComponent { CheckCanAccess: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<(EntityUid Id, T Comp)> GetValidActions<T>(List<EntityUid>? actions, bool canReach = true) where T : BaseActionComponent
|
private List<(EntityUid Id, T Comp)> GetValidActions<T>(List<EntityUid>? actions, bool canReach = true) where T : BaseActionComponent
|
||||||
{
|
{
|
||||||
var valid = new List<(EntityUid Id, T Comp)>();
|
var valid = new List<(EntityUid Id, T Comp)>();
|
||||||
@@ -180,7 +165,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
if (!_actions.TryGetActionData(id, out var baseAction) ||
|
if (!_actions.TryGetActionData(id, out var baseAction) ||
|
||||||
baseAction as T is not { } action ||
|
baseAction as T is not { } action ||
|
||||||
!ValidAction(action, canReach))
|
!_actions.ValidAction(action, canReach))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using Content.Server.NPC.Systems;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Server.NPC.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for an NPC that constantly tries to use an action on a given target.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(NPCUseActionOnTargetSystem))]
|
||||||
|
public sealed partial class NPCUseActionOnTargetComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// HTN blackboard key for the target entity
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string TargetKey = "Target";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Action that's going to attempt to be used.
|
||||||
|
/// </summary>
|
||||||
|
[DataField(required: true)]
|
||||||
|
public EntProtoId<EntityWorldTargetActionComponent> ActionId;
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public EntityUid? ActionEnt;
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
{"RangedRange", 10f},
|
{"RangedRange", 10f},
|
||||||
{"RotateSpeed", float.MaxValue},
|
{"RotateSpeed", float.MaxValue},
|
||||||
{"VisionRadius", 10f},
|
{"VisionRadius", 10f},
|
||||||
|
{"AggroVisionRadius", 10f},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -269,6 +270,13 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
return _blackboard.Remove(key);
|
return _blackboard.Remove(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetVisionRadiusKey(IEntityManager entMan)
|
||||||
|
{
|
||||||
|
return TryGetValue<EntityUid>("Target", out _, entMan)
|
||||||
|
? AggroVisionRadius
|
||||||
|
: VisionRadius;
|
||||||
|
}
|
||||||
|
|
||||||
// I Ummd and Ahhd about using strings vs enums and decided on tags because
|
// I Ummd and Ahhd about using strings vs enums and decided on tags because
|
||||||
// if a fork wants to do their own thing they don't need to touch the enum.
|
// if a fork wants to do their own thing they don't need to touch the enum.
|
||||||
|
|
||||||
@@ -317,9 +325,11 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
|||||||
public const string PathfindKey = "MovementPathfind";
|
public const string PathfindKey = "MovementPathfind";
|
||||||
|
|
||||||
public const string RotateSpeed = "RotateSpeed";
|
public const string RotateSpeed = "RotateSpeed";
|
||||||
public const string VisionRadius = "VisionRadius";
|
|
||||||
public const string UtilityTarget = "UtilityTarget";
|
public const string UtilityTarget = "UtilityTarget";
|
||||||
|
|
||||||
|
private const string VisionRadius = "VisionRadius";
|
||||||
|
private const string AggroVisionRadius = "AggroVisionRadius";
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A configurable "order" enum that can be given to an NPC from an external source.
|
/// A configurable "order" enum that can be given to an NPC from an external source.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
68
Content.Server/NPC/Systems/NPCUseActionOnTargetSystem.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using Content.Server.NPC.Components;
|
||||||
|
using Content.Server.NPC.HTN;
|
||||||
|
using Content.Shared.Actions;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Server.NPC.Systems;
|
||||||
|
|
||||||
|
public sealed class NPCUseActionOnTargetSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly SharedActionsSystem _actions = default!;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<NPCUseActionOnTargetComponent, MapInitEvent>(OnMapInit);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(Entity<NPCUseActionOnTargetComponent> ent, ref MapInitEvent args)
|
||||||
|
{
|
||||||
|
ent.Comp.ActionEnt = _actions.AddAction(ent, ent.Comp.ActionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TryUseTentacleAttack(Entity<NPCUseActionOnTargetComponent?> user, EntityUid target)
|
||||||
|
{
|
||||||
|
if (!Resolve(user, ref user.Comp, false))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!TryComp<EntityWorldTargetActionComponent>(user.Comp.ActionEnt, out var action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_actions.ValidAction(action))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (action.Event != null)
|
||||||
|
{
|
||||||
|
action.Event.Performer = user;
|
||||||
|
action.Event.Action = user.Comp.ActionEnt.Value;
|
||||||
|
action.Event.Coords = Transform(target).Coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
_actions.PerformAction(user,
|
||||||
|
null,
|
||||||
|
user.Comp.ActionEnt.Value,
|
||||||
|
action,
|
||||||
|
action.BaseEvent,
|
||||||
|
_timing.CurTime,
|
||||||
|
false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
// Tries to use the attack on the current target.
|
||||||
|
var query = EntityQueryEnumerator<NPCUseActionOnTargetComponent, HTNComponent>();
|
||||||
|
while (query.MoveNext(out var uid, out var comp, out var htn))
|
||||||
|
{
|
||||||
|
if (!htn.Blackboard.TryGetValue<EntityUid>(comp.TargetKey, out var target, EntityManager))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
TryUseTentacleAttack((uid, comp), target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -264,7 +264,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
}
|
}
|
||||||
case TargetDistanceCon:
|
case TargetDistanceCon:
|
||||||
{
|
{
|
||||||
var radius = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
var radius = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||||
|
|
||||||
if (!TryComp(targetUid, out TransformComponent? targetXform) ||
|
if (!TryComp(targetUid, out TransformComponent? targetXform) ||
|
||||||
!TryComp(owner, out TransformComponent? xform))
|
!TryComp(owner, out TransformComponent? xform))
|
||||||
@@ -309,13 +309,13 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
}
|
}
|
||||||
case TargetInLOSCon:
|
case TargetInLOSCon:
|
||||||
{
|
{
|
||||||
var radius = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
var radius = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||||
|
|
||||||
return _examine.InRangeUnOccluded(owner, targetUid, radius + 0.5f, null) ? 1f : 0f;
|
return _examine.InRangeUnOccluded(owner, targetUid, radius + 0.5f, null) ? 1f : 0f;
|
||||||
}
|
}
|
||||||
case TargetInLOSOrCurrentCon:
|
case TargetInLOSOrCurrentCon:
|
||||||
{
|
{
|
||||||
var radius = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
var radius = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||||
const float bufferRange = 0.5f;
|
const float bufferRange = 0.5f;
|
||||||
|
|
||||||
if (blackboard.TryGetValue<EntityUid>("Target", out var currentTarget, EntityManager) &&
|
if (blackboard.TryGetValue<EntityUid>("Target", out var currentTarget, EntityManager) &&
|
||||||
@@ -375,7 +375,7 @@ public sealed class NPCUtilitySystem : EntitySystem
|
|||||||
private void Add(NPCBlackboard blackboard, HashSet<EntityUid> entities, UtilityQuery query)
|
private void Add(NPCBlackboard blackboard, HashSet<EntityUid> entities, UtilityQuery query)
|
||||||
{
|
{
|
||||||
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
var owner = blackboard.GetValue<EntityUid>(NPCBlackboard.Owner);
|
||||||
var vision = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
var vision = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||||
|
|
||||||
switch (query)
|
switch (query)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using Content.Shared.Actions;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
|
||||||
|
namespace Content.Shared.Abilities.Goliath;
|
||||||
|
|
||||||
|
public sealed partial class GoliathSummonTentacleAction : EntityWorldTargetActionEvent
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ID of the entity that is spawned.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public EntProtoId EntityId = "EffectGoliathTentacleSpawn";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Directions determining where the entities will spawn.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public List<Direction> OffsetDirections = new()
|
||||||
|
{
|
||||||
|
Direction.North,
|
||||||
|
Direction.South,
|
||||||
|
Direction.East,
|
||||||
|
Direction.West,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many entities will spawn beyond the original one at the target location?
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int ExtraSpawns = 3;
|
||||||
|
};
|
||||||
69
Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using Content.Shared.Directions;
|
||||||
|
using Content.Shared.Maps;
|
||||||
|
using Content.Shared.Physics;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Stunnable;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Network;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Shared.Abilities.Goliath;
|
||||||
|
|
||||||
|
public sealed class GoliathTentacleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly INetManager _net = default!;
|
||||||
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
|
[Dependency] private readonly SharedStunSystem _stun = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
[Dependency] private readonly TurfSystem _turf = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<GoliathSummonTentacleAction>(OnSummonAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSummonAction(GoliathSummonTentacleAction args)
|
||||||
|
{
|
||||||
|
if (args.Handled || args.Coords is not { } coords)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: animation
|
||||||
|
|
||||||
|
_popup.PopupPredicted(Loc.GetString("tentacle-ability-use-popup", ("entity", args.Performer)), args.Performer, args.Performer, type: PopupType.SmallCaution);
|
||||||
|
_stun.TryStun(args.Performer, TimeSpan.FromSeconds(0.8f), false);
|
||||||
|
|
||||||
|
List<EntityCoordinates> spawnPos = new();
|
||||||
|
spawnPos.Add(coords);
|
||||||
|
|
||||||
|
var dirs = new List<Direction>();
|
||||||
|
dirs.AddRange(args.OffsetDirections);
|
||||||
|
|
||||||
|
for (var i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
var dir = _random.PickAndTake(dirs);
|
||||||
|
spawnPos.Add(coords.Offset(dir));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_transform.GetGrid(coords) is not { } grid || !TryComp<MapGridComponent>(grid, out var gridComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var pos in spawnPos)
|
||||||
|
{
|
||||||
|
if (!_map.TryGetTileRef(grid, gridComp, pos, out var tileRef) ||
|
||||||
|
tileRef.IsSpace() ||
|
||||||
|
_turf.IsTileBlocked(tileRef, CollisionGroup.Impassable))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_net.IsServer)
|
||||||
|
Spawn(args.EntityId, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -958,6 +958,21 @@ public abstract class SharedActionsSystem : EntitySystem
|
|||||||
// See client-side system for UI code.
|
// See client-side system for UI code.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool ValidAction(BaseActionComponent action, bool canReach = true)
|
||||||
|
{
|
||||||
|
if (!action.Enabled)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (action.Charges.HasValue && action.Charges <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var curTime = GameTiming.CurTime;
|
||||||
|
if (action.Cooldown.HasValue && action.Cooldown.Value.End > curTime)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return canReach || action is BaseTargetActionComponent { CheckCanAccess: false };
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region EquipHandlers
|
#region EquipHandlers
|
||||||
|
|||||||
@@ -14,16 +14,22 @@ using Content.Shared.Movement.Systems;
|
|||||||
using Content.Shared.Standing;
|
using Content.Shared.Standing;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Shared.StatusEffect;
|
||||||
using Content.Shared.Throwing;
|
using Content.Shared.Throwing;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using Robust.Shared.Audio.Systems;
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Physics.Events;
|
||||||
|
using Robust.Shared.Physics.Systems;
|
||||||
|
|
||||||
namespace Content.Shared.Stunnable;
|
namespace Content.Shared.Stunnable;
|
||||||
|
|
||||||
public abstract class SharedStunSystem : EntitySystem
|
public abstract class SharedStunSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
|
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
||||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||||
|
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||||
[Dependency] private readonly StandingStateSystem _standingState = default!;
|
[Dependency] private readonly StandingStateSystem _standingState = default!;
|
||||||
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
|
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
|
||||||
|
|
||||||
@@ -45,6 +51,9 @@ public abstract class SharedStunSystem : EntitySystem
|
|||||||
SubscribeLocalEvent<StunnedComponent, ComponentStartup>(UpdateCanMove);
|
SubscribeLocalEvent<StunnedComponent, ComponentStartup>(UpdateCanMove);
|
||||||
SubscribeLocalEvent<StunnedComponent, ComponentShutdown>(UpdateCanMove);
|
SubscribeLocalEvent<StunnedComponent, ComponentShutdown>(UpdateCanMove);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<StunOnContactComponent, ComponentStartup>(OnStunOnContactStartup);
|
||||||
|
SubscribeLocalEvent<StunOnContactComponent, StartCollideEvent>(OnStunOnContactCollide);
|
||||||
|
|
||||||
// helping people up if they're knocked down
|
// helping people up if they're knocked down
|
||||||
SubscribeLocalEvent<KnockedDownComponent, InteractHandEvent>(OnInteractHand);
|
SubscribeLocalEvent<KnockedDownComponent, InteractHandEvent>(OnInteractHand);
|
||||||
SubscribeLocalEvent<SlowedDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
|
SubscribeLocalEvent<SlowedDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
|
||||||
@@ -104,6 +113,27 @@ public abstract class SharedStunSystem : EntitySystem
|
|||||||
_blocker.UpdateCanMove(uid);
|
_blocker.UpdateCanMove(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnStunOnContactStartup(Entity<StunOnContactComponent> ent, ref ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (TryComp<PhysicsComponent>(ent, out var body))
|
||||||
|
_broadphase.RegenerateContacts(ent, body);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStunOnContactCollide(Entity<StunOnContactComponent> ent, ref StartCollideEvent args)
|
||||||
|
{
|
||||||
|
if (args.OurFixtureId != ent.Comp.FixtureId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (_entityWhitelist.IsBlacklistPass(ent.Comp.Blacklist, args.OtherEntity))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!TryComp<StatusEffectsComponent>(args.OtherEntity, out var status))
|
||||||
|
return;
|
||||||
|
|
||||||
|
TryStun(args.OtherEntity, ent.Comp.Duration, true, status);
|
||||||
|
TryKnockdown(args.OtherEntity, ent.Comp.Duration, true, status);
|
||||||
|
}
|
||||||
|
|
||||||
private void OnKnockInit(EntityUid uid, KnockedDownComponent component, ComponentInit args)
|
private void OnKnockInit(EntityUid uid, KnockedDownComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
_standingState.Down(uid);
|
_standingState.Down(uid);
|
||||||
|
|||||||
23
Content.Shared/Stunnable/StunOnContactComponent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Stunnable;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedStunSystem))]
|
||||||
|
public sealed partial class StunOnContactComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The fixture the entity must collide with to be stunned
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string FixtureId = "fix";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The duration of the stun.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan Duration = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
[DataField]
|
||||||
|
public EntityWhitelist Blacklist = new();
|
||||||
|
}
|
||||||
1
Resources/Locale/en-US/abilities/goliath.ftl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
tentacle-ability-use-popup = {CAPITALIZE(THE($entity))} digs its tentacles under the ground!
|
||||||
182
Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
- type: entity
|
||||||
|
id: BaseMobAsteroid
|
||||||
|
parent:
|
||||||
|
- BaseMob
|
||||||
|
- MobDamageable
|
||||||
|
- MobAtmosExposed
|
||||||
|
- MobCombat
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Reactive
|
||||||
|
groups:
|
||||||
|
Flammable: [Touch]
|
||||||
|
Extinguish: [Touch]
|
||||||
|
Acidic: [Touch, Ingestion]
|
||||||
|
- type: Body
|
||||||
|
prototype: Animal
|
||||||
|
- type: Climbing
|
||||||
|
- type: NameIdentifier
|
||||||
|
group: GenericNumber
|
||||||
|
- type: StatusEffects
|
||||||
|
allowed:
|
||||||
|
- SlowedDown
|
||||||
|
- Stutter
|
||||||
|
- Stun
|
||||||
|
- Electrocution
|
||||||
|
- TemporaryBlindness
|
||||||
|
- RadiationProtection
|
||||||
|
- Drowsiness
|
||||||
|
- type: StandingState
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- DoorBumpOpener
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: MobGoliath
|
||||||
|
parent: BaseMobAsteroid
|
||||||
|
name: goliath
|
||||||
|
description: A massive beast that uses long tentacles to ensnare its prey, threatening them is not advised under any conditions.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Aliens/Asteroid/goliath.rsi
|
||||||
|
layers:
|
||||||
|
- map: ["enum.DamageStateVisualLayers.Base"]
|
||||||
|
state: goliath
|
||||||
|
- type: DamageStateVisuals
|
||||||
|
states:
|
||||||
|
Alive:
|
||||||
|
Base: goliath
|
||||||
|
Dead:
|
||||||
|
Base: goliath_dead
|
||||||
|
- type: MovementSpeedModifier
|
||||||
|
baseWalkSpeed : 2.50
|
||||||
|
baseSprintSpeed : 2.50
|
||||||
|
- type: MobThresholds
|
||||||
|
thresholds:
|
||||||
|
0: Alive
|
||||||
|
300: Dead
|
||||||
|
- type: MeleeWeapon
|
||||||
|
soundHit:
|
||||||
|
path: "/Audio/Weapons/smash.ogg"
|
||||||
|
angle: 0
|
||||||
|
attackRate: 0.75
|
||||||
|
animation: WeaponArcPunch
|
||||||
|
damage:
|
||||||
|
types:
|
||||||
|
Slash: 15
|
||||||
|
Piercing: 10
|
||||||
|
- type: NpcFactionMember
|
||||||
|
factions:
|
||||||
|
- SimpleHostile
|
||||||
|
- type: HTN
|
||||||
|
rootTask:
|
||||||
|
task: GoliathCompound
|
||||||
|
blackboard:
|
||||||
|
VisionRadius: !type:Single
|
||||||
|
6
|
||||||
|
AggroVisionRadius: !type:Single
|
||||||
|
10
|
||||||
|
- type: NPCUseActionOnTarget
|
||||||
|
actionId: ActionGoliathTentacle
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- CannotSuicide
|
||||||
|
- Goliath
|
||||||
|
- FootstepSound
|
||||||
|
- type: NoSlip
|
||||||
|
- type: Butcherable
|
||||||
|
spawned:
|
||||||
|
- id: FoodMeatGoliath
|
||||||
|
amount: 3
|
||||||
|
- id: MaterialGoliathHide1
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: ActionGoliathTentacle
|
||||||
|
name: "[color=red]Tentacle Slam[/color]"
|
||||||
|
description: Use your tentacles to grab and stun a target player!
|
||||||
|
components:
|
||||||
|
- type: EntityWorldTargetAction
|
||||||
|
raiseOnUser: true
|
||||||
|
icon:
|
||||||
|
sprite: Mobs/Aliens/Asteroid/goliath.rsi
|
||||||
|
state: goliath_tentacle_spawn
|
||||||
|
iconOn:
|
||||||
|
sprite: Mobs/Aliens/Asteroid/goliath.rsi
|
||||||
|
state: goliath_tentacle_wiggle
|
||||||
|
sound:
|
||||||
|
path: "/Audio/Weapons/slash.ogg"
|
||||||
|
event: !type:GoliathSummonTentacleAction
|
||||||
|
useDelay: 8
|
||||||
|
range: 10
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: GoliathTentacle
|
||||||
|
name: tentacle
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
anchored: True
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Static
|
||||||
|
canCollide: true
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Aliens/Asteroid/goliath.rsi
|
||||||
|
layers:
|
||||||
|
- state: goliath_tentacle_wiggle
|
||||||
|
- type: StunOnContact
|
||||||
|
blacklist:
|
||||||
|
tags:
|
||||||
|
- Goliath
|
||||||
|
- type: Fixtures
|
||||||
|
fixtures:
|
||||||
|
fix:
|
||||||
|
shape:
|
||||||
|
!type:PhysShapeAabb
|
||||||
|
bounds: "-0.45,-0.45,0.45,0.45"
|
||||||
|
mask:
|
||||||
|
- Impassable
|
||||||
|
layer:
|
||||||
|
- Impassable
|
||||||
|
hard: false
|
||||||
|
- type: TimedDespawn #do this shit by hand because of fucking course.
|
||||||
|
lifetime: 0.4
|
||||||
|
- type: SpawnOnDespawn
|
||||||
|
prototype: EffectGoliathTentacleRetract
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: BaseEffectGoliathTentacleSpawn
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
name: tentacle
|
||||||
|
abstract: true
|
||||||
|
components:
|
||||||
|
- type: Transform
|
||||||
|
anchored: True
|
||||||
|
- type: Physics
|
||||||
|
bodyType: Static
|
||||||
|
canCollide: false
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Mobs/Aliens/Asteroid/goliath.rsi
|
||||||
|
- type: InteractionOutline
|
||||||
|
- type: TimedDespawn
|
||||||
|
lifetime: 0.7
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: EffectGoliathTentacleSpawn
|
||||||
|
parent: BaseEffectGoliathTentacleSpawn
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
name: tentacle
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: goliath_tentacle_spawn
|
||||||
|
- type: SpawnOnDespawn
|
||||||
|
prototype: GoliathTentacle
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: EffectGoliathTentacleRetract
|
||||||
|
parent: BaseEffectGoliathTentacleSpawn
|
||||||
|
categories: [ HideSpawnMenu ]
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: goliath_tentacle_retract
|
||||||
|
- type: EffectVisuals
|
||||||
|
- type: AnimationPlayer
|
||||||
@@ -654,3 +654,40 @@
|
|||||||
Gunpowder: 100
|
Gunpowder: 100
|
||||||
- type: Item
|
- type: Item
|
||||||
size: Tiny
|
size: Tiny
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: MaterialBase
|
||||||
|
id: MaterialGoliathHide
|
||||||
|
name: goliath hide plates
|
||||||
|
description: Pieces of a goliath's rocky hide, these might be able to make your suit a bit more durable to attack from the local fauna.
|
||||||
|
suffix: Full
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Objects/Materials/hide.rsi
|
||||||
|
layers:
|
||||||
|
- state: goliath_hide
|
||||||
|
map: [ "base" ]
|
||||||
|
- type: StaticPrice
|
||||||
|
price: 0
|
||||||
|
- type: StackPrice
|
||||||
|
price: 1500
|
||||||
|
- type: Appearance
|
||||||
|
- type: Stack
|
||||||
|
stackType: GoliathHide
|
||||||
|
baseLayer: base
|
||||||
|
layerStates:
|
||||||
|
- goliath_hide
|
||||||
|
- goliath_hide_2
|
||||||
|
- goliath_hide_3
|
||||||
|
- type: Item
|
||||||
|
size: Large
|
||||||
|
shape:
|
||||||
|
- 0,0,2,2
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: MaterialGoliathHide
|
||||||
|
id: MaterialGoliathHide1
|
||||||
|
suffix: 1
|
||||||
|
components:
|
||||||
|
- type: Stack
|
||||||
|
count: 1
|
||||||
|
|||||||
75
Resources/Prototypes/NPCs/goliath.yml
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
- type: htnCompound
|
||||||
|
id: GoliathCompound
|
||||||
|
branches:
|
||||||
|
- tasks:
|
||||||
|
- !type:HTNCompoundTask
|
||||||
|
task: GoliathMeleeCombatPrecondition
|
||||||
|
- tasks:
|
||||||
|
- !type:HTNCompoundTask
|
||||||
|
task: IdleCompound
|
||||||
|
|
||||||
|
- type: htnCompound
|
||||||
|
id: GoliathMeleeCombatPrecondition
|
||||||
|
branches:
|
||||||
|
- preconditions:
|
||||||
|
- !type:BuckledPrecondition
|
||||||
|
isBuckled: true
|
||||||
|
tasks:
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:UnbuckleOperator
|
||||||
|
shutdownState: TaskFinished
|
||||||
|
|
||||||
|
- preconditions:
|
||||||
|
- !type:InContainerPrecondition
|
||||||
|
isInContainer: true
|
||||||
|
tasks:
|
||||||
|
- !type:HTNCompoundTask
|
||||||
|
task: EscapeCompound
|
||||||
|
|
||||||
|
- preconditions:
|
||||||
|
- !type:PulledPrecondition
|
||||||
|
isPulled: true
|
||||||
|
tasks:
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:UnPullOperator
|
||||||
|
shutdownState: TaskFinished
|
||||||
|
|
||||||
|
- tasks:
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:UtilityOperator
|
||||||
|
proto: NearbyMeleeTargets
|
||||||
|
- !type:HTNCompoundTask
|
||||||
|
task: GoliathAttackTargetCompound
|
||||||
|
|
||||||
|
- type: htnCompound
|
||||||
|
id: GoliathAttackTargetCompound
|
||||||
|
branches:
|
||||||
|
- preconditions:
|
||||||
|
- !type:KeyExistsPrecondition
|
||||||
|
key: Target
|
||||||
|
tasks:
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:MoveToOperator
|
||||||
|
shutdownState: PlanFinished
|
||||||
|
pathfindInPlanning: true
|
||||||
|
removeKeyOnFinish: false
|
||||||
|
targetKey: TargetCoordinates
|
||||||
|
pathfindKey: TargetPathfind
|
||||||
|
rangeKey: MeleeRange
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:JukeOperator
|
||||||
|
jukeType: AdjacentTile
|
||||||
|
- !type:HTNPrimitiveTask
|
||||||
|
operator: !type:MeleeOperator
|
||||||
|
targetKey: Target
|
||||||
|
preconditions:
|
||||||
|
- !type:KeyExistsPrecondition
|
||||||
|
key: Target
|
||||||
|
- !type:TargetInRangePrecondition
|
||||||
|
targetKey: Target
|
||||||
|
rangeKey: MeleeRange
|
||||||
|
services:
|
||||||
|
- !type:UtilityService
|
||||||
|
id: MeleeService
|
||||||
|
proto: NearbyMeleeTargets
|
||||||
|
key: Target
|
||||||
@@ -145,7 +145,7 @@
|
|||||||
minCount: 5
|
minCount: 5
|
||||||
maxCount: 8
|
maxCount: 8
|
||||||
groups:
|
groups:
|
||||||
- id: MobXeno
|
- id: MobGoliath
|
||||||
amount: 1
|
amount: 1
|
||||||
|
|
||||||
#- type: dungeonConfig
|
#- type: dungeonConfig
|
||||||
@@ -175,7 +175,7 @@
|
|||||||
minCount: 20
|
minCount: 20
|
||||||
maxCount: 30
|
maxCount: 30
|
||||||
groups:
|
groups:
|
||||||
- id: MobXeno
|
- id: MobGoliath
|
||||||
amount: 1
|
amount: 1
|
||||||
|
|
||||||
#- type: dungeonConfig
|
#- type: dungeonConfig
|
||||||
|
|||||||
@@ -88,3 +88,10 @@
|
|||||||
icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile }
|
icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile }
|
||||||
spawn: MaterialGunpowder
|
spawn: MaterialGunpowder
|
||||||
maxCount: 60
|
maxCount: 60
|
||||||
|
|
||||||
|
- type: stack
|
||||||
|
id: GoliathHide
|
||||||
|
name: goliath hide
|
||||||
|
icon: { sprite: /Textures/Objects/Materials/hide.rsi, state: goliath_hide }
|
||||||
|
spawn: MaterialGoliathHide1
|
||||||
|
maxCount: 30
|
||||||
|
|||||||
@@ -656,6 +656,9 @@
|
|||||||
- type: Tag
|
- type: Tag
|
||||||
id: Goat
|
id: Goat
|
||||||
|
|
||||||
|
- type: Tag
|
||||||
|
id: Goliath
|
||||||
|
|
||||||
- type: Tag
|
- type: Tag
|
||||||
id: GPS
|
id: GPS
|
||||||
|
|
||||||
|
|||||||
BIN
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 968 B |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
111
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/meta.json
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from Paradise at https://github.com/ParadiseSS13/Paradise/blob/5a68c5f6d3b60ee82c06e0287d1eb8108d2e1fe2/icons/mob/lavaland/lavaland_monsters.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "goliath",
|
||||||
|
"directions": 4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_alert",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
0.2,
|
||||||
|
1,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
0.2,
|
||||||
|
1,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
0.2,
|
||||||
|
1,
|
||||||
|
0.5
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
0.2,
|
||||||
|
1,
|
||||||
|
0.5
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_preattack",
|
||||||
|
"directions": 4,
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_dead"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_tentacle_spawn",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_tentacle_wiggle",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1,
|
||||||
|
0.1
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_tentacle_retract",
|
||||||
|
"delays": [
|
||||||
|
[
|
||||||
|
0.1,
|
||||||
|
0.2,
|
||||||
|
0.2,
|
||||||
|
0.2
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
BIN
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide.png
Normal file
|
After Width: | Height: | Size: 353 B |
BIN
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_2.png
Normal file
|
After Width: | Height: | Size: 479 B |
BIN
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_3.png
Normal file
|
After Width: | Height: | Size: 554 B |
20
Resources/Textures/Objects/Materials/hide.rsi/meta.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"license": "CC-BY-SA-3.0",
|
||||||
|
"copyright": "Taken from Paradise at https://github.com/ParadiseSS13/Paradise/blob/88d236d5c91e9a57e103957555c2e9a8c63b68fa/icons/obj/stacks/organic.dmi",
|
||||||
|
"size": {
|
||||||
|
"x": 32,
|
||||||
|
"y": 32
|
||||||
|
},
|
||||||
|
"states": [
|
||||||
|
{
|
||||||
|
"name": "goliath_hide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_hide_2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "goliath_hide_3"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||