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;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
var valid = new List<(EntityUid Id, T Comp)>();
|
||||
@@ -180,7 +165,7 @@ public sealed class ActionOnInteractSystem : EntitySystem
|
||||
{
|
||||
if (!_actions.TryGetActionData(id, out var baseAction) ||
|
||||
baseAction as T is not { } action ||
|
||||
!ValidAction(action, canReach))
|
||||
!_actions.ValidAction(action, canReach))
|
||||
{
|
||||
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},
|
||||
{"RotateSpeed", float.MaxValue},
|
||||
{"VisionRadius", 10f},
|
||||
{"AggroVisionRadius", 10f},
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
@@ -269,6 +270,13 @@ public sealed partial class NPCBlackboard : IEnumerable<KeyValuePair<string, obj
|
||||
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
|
||||
// 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 RotateSpeed = "RotateSpeed";
|
||||
public const string VisionRadius = "VisionRadius";
|
||||
public const string UtilityTarget = "UtilityTarget";
|
||||
|
||||
private const string VisionRadius = "VisionRadius";
|
||||
private const string AggroVisionRadius = "AggroVisionRadius";
|
||||
|
||||
/// <summary>
|
||||
/// A configurable "order" enum that can be given to an NPC from an external source.
|
||||
/// </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:
|
||||
{
|
||||
var radius = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
||||
var radius = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||
|
||||
if (!TryComp(targetUid, out TransformComponent? targetXform) ||
|
||||
!TryComp(owner, out TransformComponent? xform))
|
||||
@@ -309,13 +309,13 @@ public sealed class NPCUtilitySystem : EntitySystem
|
||||
}
|
||||
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;
|
||||
}
|
||||
case TargetInLOSOrCurrentCon:
|
||||
{
|
||||
var radius = blackboard.GetValueOrDefault<float>(NPCBlackboard.VisionRadius, EntityManager);
|
||||
var radius = blackboard.GetValueOrDefault<float>(blackboard.GetVisionRadiusKey(EntityManager), EntityManager);
|
||||
const float bufferRange = 0.5f;
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#region EquipHandlers
|
||||
|
||||
@@ -14,16 +14,22 @@ using Content.Shared.Movement.Systems;
|
||||
using Content.Shared.Standing;
|
||||
using Content.Shared.StatusEffect;
|
||||
using Content.Shared.Throwing;
|
||||
using Content.Shared.Whitelist;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Physics.Events;
|
||||
using Robust.Shared.Physics.Systems;
|
||||
|
||||
namespace Content.Shared.Stunnable;
|
||||
|
||||
public abstract class SharedStunSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||
[Dependency] private readonly SharedBroadphaseSystem _broadphase = default!;
|
||||
[Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly MovementSpeedModifierSystem _movementSpeedModifier = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly EntityWhitelistSystem _entityWhitelist = default!;
|
||||
[Dependency] private readonly StandingStateSystem _standingState = default!;
|
||||
[Dependency] private readonly StatusEffectsSystem _statusEffect = default!;
|
||||
|
||||
@@ -45,6 +51,9 @@ public abstract class SharedStunSystem : EntitySystem
|
||||
SubscribeLocalEvent<StunnedComponent, ComponentStartup>(UpdateCanMove);
|
||||
SubscribeLocalEvent<StunnedComponent, ComponentShutdown>(UpdateCanMove);
|
||||
|
||||
SubscribeLocalEvent<StunOnContactComponent, ComponentStartup>(OnStunOnContactStartup);
|
||||
SubscribeLocalEvent<StunOnContactComponent, StartCollideEvent>(OnStunOnContactCollide);
|
||||
|
||||
// helping people up if they're knocked down
|
||||
SubscribeLocalEvent<KnockedDownComponent, InteractHandEvent>(OnInteractHand);
|
||||
SubscribeLocalEvent<SlowedDownComponent, RefreshMovementSpeedModifiersEvent>(OnRefreshMovespeed);
|
||||
@@ -104,6 +113,27 @@ public abstract class SharedStunSystem : EntitySystem
|
||||
_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)
|
||||
{
|
||||
_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
|
||||
- type: Item
|
||||
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
|
||||
maxCount: 8
|
||||
groups:
|
||||
- id: MobXeno
|
||||
- id: MobGoliath
|
||||
amount: 1
|
||||
|
||||
#- type: dungeonConfig
|
||||
@@ -175,7 +175,7 @@
|
||||
minCount: 20
|
||||
maxCount: 30
|
||||
groups:
|
||||
- id: MobXeno
|
||||
- id: MobGoliath
|
||||
amount: 1
|
||||
|
||||
#- type: dungeonConfig
|
||||
|
||||
@@ -88,3 +88,10 @@
|
||||
icon: { sprite: /Textures/Objects/Misc/reagent_fillings.rsi, state: powderpile }
|
||||
spawn: MaterialGunpowder
|
||||
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
|
||||
id: Goat
|
||||
|
||||
- type: Tag
|
||||
id: Goliath
|
||||
|
||||
- type: Tag
|
||||
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"
|
||||
}
|
||||
]
|
||||
}
|
||||