]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
make dragons breathe fire (#26746)
authordeltanedas <39013340+deltanedas@users.noreply.github.com>
Wed, 8 May 2024 00:25:41 +0000 (00:25 +0000)
committerGitHub <noreply@github.com>
Wed, 8 May 2024 00:25:41 +0000 (17:25 -0700)
* add ActionGun system

* add RepeatingTrigger

* dragons breath projectile, repeatedly explodes

* give dragon fire breathing action, fireproof it

* oop

* oop 2

* prevent troll

* proper repeating thing

* pro

* webedit ops

* realops

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>
Content.Server/Explosion/Components/ExplosiveComponent.cs
Content.Server/Explosion/Components/RepeatingTriggerComponent.cs [new file with mode: 0644]
Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
Content.Server/Explosion/EntitySystems/TriggerSystem.cs
Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs [new file with mode: 0644]
Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs [new file with mode: 0644]
Resources/Prototypes/Entities/Mobs/Player/dragon.yml
Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/magic.yml

index 04a08955a35d6c2dd857db507aba6709b34ddf07..2b27a89d9db78fb87d22a4fc5a85d61dbe16d358 100644 (file)
@@ -81,6 +81,13 @@ public sealed partial class ExplosiveComponent : Component
     [DataField("deleteAfterExplosion")]
     public bool? DeleteAfterExplosion;
 
+    /// <summary>
+    /// Whether to not set <see cref="Exploded"/> to true, allowing it to explode multiple times.
+    /// This should never be used if it is damageable.
+    /// </summary>
+    [DataField]
+    public bool Repeatable;
+
     /// <summary>
     ///     Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion.
     /// </summary>
diff --git a/Content.Server/Explosion/Components/RepeatingTriggerComponent.cs b/Content.Server/Explosion/Components/RepeatingTriggerComponent.cs
new file mode 100644 (file)
index 0000000..cc08de5
--- /dev/null
@@ -0,0 +1,25 @@
+using Content.Server.Explosion.EntitySystems;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Server.Explosion.Components;
+
+/// <summary>
+/// Constantly triggers after being added to an entity.
+/// </summary>
+[RegisterComponent, Access(typeof(TriggerSystem))]
+[AutoGenerateComponentPause]
+public sealed partial class RepeatingTriggerComponent : Component
+{
+    /// <summary>
+    /// How long to wait between triggers.
+    /// The first trigger starts this long after the component is added.
+    /// </summary>
+    [DataField]
+    public TimeSpan Delay = TimeSpan.FromSeconds(1);
+
+    /// <summary>
+    /// When the next trigger will be.
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField]
+    public TimeSpan NextTrigger;
+}
index 23543895e6fcff588b24b61bd7c5c9bf9995166c..8734c054d648fd78245a53cc013f61a58a6ad8d1 100644 (file)
@@ -160,7 +160,7 @@ public sealed partial class ExplosionSystem : EntitySystem
         if (explosive.Exploded)
             return;
 
-        explosive.Exploded = true;
+        explosive.Exploded = !explosive.Repeatable;
 
         // Override the explosion intensity if optional arguments were provided.
         if (radius != null)
index 3675c214b14fa5676a66c90d8c4e9b445b704ce2..8e0a75ea24f93f41bd03a61bb9bc0c50d5e3de98 100644 (file)
@@ -94,6 +94,7 @@ namespace Content.Server.Explosion.EntitySystems
             SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered);
             SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered);
             SubscribeLocalEvent<TriggerWhenEmptyComponent, OnEmptyGunShotEvent>(OnEmptyTriggered);
+            SubscribeLocalEvent<RepeatingTriggerComponent, MapInitEvent>(OnRepeatInit);
 
             SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(OnSpawnTrigger);
             SubscribeLocalEvent<DeleteOnTriggerComponent, TriggerEvent>(HandleDeleteTrigger);
@@ -241,6 +242,11 @@ namespace Content.Server.Explosion.EntitySystems
             Trigger(uid, args.EmptyGun);
         }
 
+        private void OnRepeatInit(Entity<RepeatingTriggerComponent> ent, ref MapInitEvent args)
+        {
+            ent.Comp.NextTrigger = _timing.CurTime + ent.Comp.Delay;
+        }
+
         public bool Trigger(EntityUid trigger, EntityUid? user = null)
         {
             var triggerEvent = new TriggerEvent(trigger, user);
@@ -323,6 +329,7 @@ namespace Content.Server.Explosion.EntitySystems
             UpdateProximity();
             UpdateTimer(frameTime);
             UpdateTimedCollide(frameTime);
+            UpdateRepeat();
         }
 
         private void UpdateTimer(float frameTime)
@@ -357,5 +364,19 @@ namespace Content.Server.Explosion.EntitySystems
                     _appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Unprimed, appearance);
             }
         }
+
+        private void UpdateRepeat()
+        {
+            var now = _timing.CurTime;
+            var query = EntityQueryEnumerator<RepeatingTriggerComponent>();
+            while (query.MoveNext(out var uid, out var comp))
+            {
+                if (comp.NextTrigger > now)
+                    continue;
+
+                comp.NextTrigger = now + comp.Delay;
+                Trigger(uid);
+            }
+        }
     }
 }
diff --git a/Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs b/Content.Shared/Weapons/Ranged/Components/ActionGunComponent.cs
new file mode 100644 (file)
index 0000000..112339e
--- /dev/null
@@ -0,0 +1,37 @@
+using Content.Shared.Actions;
+using Content.Shared.Weapons.Ranged.Systems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Weapons.Ranged.Components;
+
+/// <summary>
+/// Lets you shoot a gun using an action.
+/// </summary>
+[RegisterComponent, NetworkedComponent, Access(typeof(ActionGunSystem))]
+public sealed partial class ActionGunComponent : Component
+{
+    /// <summary>
+    /// Action to create, must use <see cref="ActionGunShootEvent"/>.
+    /// </summary>
+    [DataField(required: true)]
+    public EntProtoId Action = string.Empty;
+
+    [DataField]
+    public EntityUid? ActionEntity;
+
+    /// <summary>
+    /// Prototype of gun entity to spawn.
+    /// Deleted when this component is removed.
+    /// </summary>
+    [DataField(required: true)]
+    public EntProtoId GunProto = string.Empty;
+
+    [DataField]
+    public EntityUid? Gun;
+}
+
+/// <summary>
+/// Action event for <see cref="ActionGunComponent"/> to shoot at a position.
+/// </summary>
+public sealed partial class ActionGunShootEvent : WorldTargetActionEvent;
diff --git a/Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs b/Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs
new file mode 100644 (file)
index 0000000..f3dfe8a
--- /dev/null
@@ -0,0 +1,41 @@
+using Content.Shared.Actions;
+using Content.Shared.Weapons.Ranged.Components;
+
+namespace Content.Shared.Weapons.Ranged.Systems;
+
+public sealed class ActionGunSystem : EntitySystem
+{
+    [Dependency] private readonly SharedActionsSystem _actions = default!;
+    [Dependency] private readonly SharedGunSystem _gun = default!;
+
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<ActionGunComponent, MapInitEvent>(OnMapInit);
+        SubscribeLocalEvent<ActionGunComponent, ComponentShutdown>(OnShutdown);
+        SubscribeLocalEvent<ActionGunComponent, ActionGunShootEvent>(OnShoot);
+    }
+
+    private void OnMapInit(Entity<ActionGunComponent> ent, ref MapInitEvent args)
+    {
+        if (string.IsNullOrEmpty(ent.Comp.Action))
+            return;
+
+        _actions.AddAction(ent, ref ent.Comp.ActionEntity, ent.Comp.Action);
+        ent.Comp.Gun = Spawn(ent.Comp.GunProto);
+    }
+
+    private void OnShutdown(Entity<ActionGunComponent> ent, ref ComponentShutdown args)
+    {
+        if (ent.Comp.Gun is {} gun)
+            QueueDel(gun);
+    }
+
+    private void OnShoot(Entity<ActionGunComponent> ent, ref ActionGunShootEvent args)
+    {
+        if (TryComp<GunComponent>(ent.Comp.Gun, out var gun))
+            _gun.AttemptShoot(ent, ent.Comp.Gun.Value, gun, args.Target);
+    }
+}
+
index ee0db34fc2dc255ef9d95158a94edbb0e784c4e5..cb9dc1c911a28c79a1d602ee4ea29495b8b7b4e7 100644 (file)
     speedModifierThresholds:
       250: 0.7
       400: 0.5
+  # disable taking damage from fire, since its a fire breathing dragon
+  - type: Flammable
+    damage:
+      types: {}
+  - type: Temperature
+    heatDamageThreshold: 800
   - type: Metabolizer
     solutionOnBody: false
     updateInterval: 0.25
     spawnRiftAction: ActionSpawnRift
   - type: GenericAntag
     rule: Dragon
+  - type: ActionGun
+    action: ActionDragonsBreath
+    gunProto: DragonsBreathGun
   - type: GuideHelp
     guides:
     - MinorAntagonists
 
+- type: entity
+  noSpawn: true
+  id: DragonsBreathGun
+  name: dragon's lung
+  description: For dragon's breathing
+  components:
+  - type: RechargeBasicEntityAmmo
+    rechargeCooldown: 5
+    rechargeSound:
+      path: /Audio/Animals/space_dragon_roar.ogg
+  - type: BasicEntityAmmoProvider
+    proto: ProjectileDragonsBreath
+    capacity: 1
+    count: 1
+  - type: Gun
+    soundGunshot:
+      path: /Audio/Animals/space_dragon_roar.ogg
+    soundEmpty: null
+    projectileSpeed: 5
+
 - type: entity
   parent: BaseMobDragon
   id: MobDragonDungeon
       state: icon
     event: !type:DragonSpawnRiftActionEvent
     useDelay: 1
+    priority: 3
 
 - type: entity
   id: ActionDevour
     icon: { sprite : Interface/Actions/devour.rsi, state: icon }
     iconOn: { sprite : Interface/Actions/devour.rsi, state: icon-on }
     event: !type:DevourActionEvent
+    priority: 1
+
+- type: entity
+  noSpawn: true
+  id: ActionDragonsBreath
+  name: "[color=orange]Dragon's Breath[/color]"
+  description: Spew out flames at anyone foolish enough to attack you!
+  components:
+  - type: WorldTargetAction
+    # TODO: actual sprite
+    icon: { sprite : Objects/Weapons/Guns/Projectiles/magic.rsi, state: fireball }
+    event: !type:ActionGunShootEvent
+    priority: 2
+    checkCanAccess: false
+    range: 0
index 3556d1c8f8b21734ba3205828c0e795519f1c710..d6adcd614e344edf9ea89cb653651c79bd6ce6bb 100644 (file)
   - type: IgniteOnCollide
     fireStacks: 0.35
 
+- type: entity
+  noSpawn: true
+  parent: BaseBulletTrigger
+  id: ProjectileDragonsBreath
+  name: dragon's breath
+  description: Try not to get toasted.
+  components:
+  - type: PointLight
+    color: "#E25822"
+    radius: 3.0
+    energy: 5.0
+  - type: Sprite
+    sprite: Objects/Weapons/Guns/Projectiles/magic.rsi
+    layers:
+    - state: fireball
+      shader: unshaded
+  - type: IgnitionSource
+    temperature: 1000
+    ignited: true
+  - type: RepeatingTrigger
+    delay: 0.5 # line of fire as well as if it hits something
+  - type: ExplodeOnTrigger
+  - type: Explosive
+    explosionType: FireBomb
+    totalIntensity: 5 # low intensity, the point is to burn attackers not to break open walls, dragons can just eat them
+    intensitySlope: 1
+    maxIntensity: 3
+    canCreateVacuum: false
+    deleteAfterExplosion: false
+    repeatable: true
+  - type: TimedDespawn
+    lifetime: 5
+
 - type: entity
   id: ProjectileAnomalyFireball
   name: fireball