]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Goliath mob (#30839)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Sun, 18 Aug 2024 16:22:36 +0000 (12:22 -0400)
committerGitHub <noreply@github.com>
Sun, 18 Aug 2024 16:22:36 +0000 (12:22 -0400)
* Goliath mob

* Update asteroid.yml

* mcfuck yourself

* add cloak

* fixes

* Update materials.yml

29 files changed:
Content.Server/Actions/ActionOnInteractSystem.cs
Content.Server/NPC/Components/NPCUseActionOnTargetComponent.cs [new file with mode: 0644]
Content.Server/NPC/NPCBlackboard.cs
Content.Server/NPC/Systems/NPCUseActionOnTargetSystem.cs [new file with mode: 0644]
Content.Server/NPC/Systems/NPCUtilitySystem.cs
Content.Shared/Abilities/Goliath/GoliathSummonTentacleAction.cs [new file with mode: 0644]
Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs [new file with mode: 0644]
Content.Shared/Actions/SharedActionsSystem.cs
Content.Shared/Stunnable/SharedStunSystem.cs
Content.Shared/Stunnable/StunOnContactComponent.cs [new file with mode: 0644]
Resources/Locale/en-US/abilities/goliath.ftl [new file with mode: 0644]
Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml [new file with mode: 0644]
Resources/Prototypes/Entities/Objects/Materials/materials.yml
Resources/Prototypes/NPCs/goliath.yml [new file with mode: 0644]
Resources/Prototypes/Procedural/vgroid.yml
Resources/Prototypes/Stacks/Materials/materials.yml
Resources/Prototypes/tags.yml
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_alert.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_dead.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_preattack.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_retract.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_spawn.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_wiggle.png [new file with mode: 0644]
Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide.png [new file with mode: 0644]
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_2.png [new file with mode: 0644]
Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_3.png [new file with mode: 0644]
Resources/Textures/Objects/Materials/hide.rsi/meta.json [new file with mode: 0644]

index 36e5920b32561b5c33caeb551d88f28a9f7dbe39..a1f02ed1ecbb9be95d6cb84abe259cee2b19e76d 100644 (file)
@@ -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;
             }
diff --git a/Content.Server/NPC/Components/NPCUseActionOnTargetComponent.cs b/Content.Server/NPC/Components/NPCUseActionOnTargetComponent.cs
new file mode 100644 (file)
index 0000000..f022a45
--- /dev/null
@@ -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;
+}
index 322ff0f85bec7d38ec3accfcfa1c2f765d0d00e2..ffdb55045d358598c2fd78a027663561e9f850ee 100644 (file)
@@ -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>
diff --git a/Content.Server/NPC/Systems/NPCUseActionOnTargetSystem.cs b/Content.Server/NPC/Systems/NPCUseActionOnTargetSystem.cs
new file mode 100644 (file)
index 0000000..f6cc2d1
--- /dev/null
@@ -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);
+        }
+    }
+}
index 5e1823227a8984b6197df268e1e319c02e09587d..a2fb285ac1bf9e2688c8c479dda6c2b791c35be6 100644 (file)
@@ -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)
         {
diff --git a/Content.Shared/Abilities/Goliath/GoliathSummonTentacleAction.cs b/Content.Shared/Abilities/Goliath/GoliathSummonTentacleAction.cs
new file mode 100644 (file)
index 0000000..abdb0d5
--- /dev/null
@@ -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;
+};
diff --git a/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs b/Content.Shared/Abilities/Goliath/GoliathTentacleSystem.cs
new file mode 100644 (file)
index 0000000..98dbc5e
--- /dev/null
@@ -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;
+    }
+}
index 635d78b8dd939046ed7712dc76ed05c3217805f9..fb9415096fbfa0b2ba8ea547a9beab4f173937c0 100644 (file)
@@ -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
index 092da8fe5ab3960cfbfa7eed85b5f48393a57634..8f828131f5c58f16efa49234117ac84fd05d4eab 100644 (file)
@@ -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);
diff --git a/Content.Shared/Stunnable/StunOnContactComponent.cs b/Content.Shared/Stunnable/StunOnContactComponent.cs
new file mode 100644 (file)
index 0000000..cc4af53
--- /dev/null
@@ -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();
+}
diff --git a/Resources/Locale/en-US/abilities/goliath.ftl b/Resources/Locale/en-US/abilities/goliath.ftl
new file mode 100644 (file)
index 0000000..953d32a
--- /dev/null
@@ -0,0 +1 @@
+tentacle-ability-use-popup = {CAPITALIZE(THE($entity))} digs its tentacles under the ground!
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml b/Resources/Prototypes/Entities/Mobs/NPCs/asteroid.yml
new file mode 100644 (file)
index 0000000..8cbd40b
--- /dev/null
@@ -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
index e00f3e65d3508f21fc9603130cf052d89dc7dcef..867b83f9ff6f0e4182a8ddc825bad3dc3b291887 100644 (file)
     flavors:
       - banana
   - type: Food
-    trash: 
+    trash:
     - TrashBananiumPeel
   - type: BadFood
   - type: SolutionContainerManager
       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
diff --git a/Resources/Prototypes/NPCs/goliath.yml b/Resources/Prototypes/NPCs/goliath.yml
new file mode 100644 (file)
index 0000000..0befc5d
--- /dev/null
@@ -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
index 4371e9cad57e29738b9446c0543c3b523cf76e1f..315f908fad2aa43dbee2ba271829f9380957a51f 100644 (file)
     minCount: 5
     maxCount: 8
     groups:
-    - id: MobXeno
+    - id: MobGoliath
       amount: 1
 
 #- type: dungeonConfig
     minCount: 20
     maxCount: 30
     groups:
-    - id: MobXeno
+    - id: MobGoliath
       amount: 1
 
 #- type: dungeonConfig
index 1157dc3f004c9d3e3c8c8c7c3320c2219bdb59e0..049f538bccd6b4725f80030544d5d5049e902bef 100644 (file)
   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
index 5e1b5cbe4dac318d0719fca16b2e299229321406..1e3ae71958aec02b432018c86bb588881ef5a5f9 100644 (file)
 - type: Tag
   id: Goat
 
+- type: Tag
+  id: Goliath
+
 - type: Tag
   id: GPS
 
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath.png
new file mode 100644 (file)
index 0000000..5839a35
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_alert.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_alert.png
new file mode 100644 (file)
index 0000000..c4c0b8e
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_alert.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_dead.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_dead.png
new file mode 100644 (file)
index 0000000..e6978b2
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_dead.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_preattack.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_preattack.png
new file mode 100644 (file)
index 0000000..743e9f3
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_preattack.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_retract.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_retract.png
new file mode 100644 (file)
index 0000000..d0895cc
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_retract.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_spawn.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_spawn.png
new file mode 100644 (file)
index 0000000..9172b00
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_spawn.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_wiggle.png b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_wiggle.png
new file mode 100644 (file)
index 0000000..f762213
Binary files /dev/null and b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/goliath_tentacle_wiggle.png differ
diff --git a/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/meta.json b/Resources/Textures/Mobs/Aliens/Asteroid/goliath.rsi/meta.json
new file mode 100644 (file)
index 0000000..b9d3f2b
--- /dev/null
@@ -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
+        ]
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide.png b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide.png
new file mode 100644 (file)
index 0000000..04526a2
Binary files /dev/null and b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide.png differ
diff --git a/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_2.png b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_2.png
new file mode 100644 (file)
index 0000000..9b03018
Binary files /dev/null and b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_2.png differ
diff --git a/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_3.png b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_3.png
new file mode 100644 (file)
index 0000000..c9a53d7
Binary files /dev/null and b/Resources/Textures/Objects/Materials/hide.rsi/goliath_hide_3.png differ
diff --git a/Resources/Textures/Objects/Materials/hide.rsi/meta.json b/Resources/Textures/Objects/Materials/hide.rsi/meta.json
new file mode 100644 (file)
index 0000000..0ec2b25
--- /dev/null
@@ -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"
+    }
+  ]
+}
\ No newline at end of file