make dragons breathe fire (#26746)
* 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>
This commit is contained in:
@@ -81,6 +81,13 @@ public sealed partial class ExplosiveComponent : Component
|
|||||||
[DataField("deleteAfterExplosion")]
|
[DataField("deleteAfterExplosion")]
|
||||||
public bool? 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>
|
/// <summary>
|
||||||
/// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion.
|
/// Avoid somehow double-triggering this explosion (e.g. by damaging this entity from its own explosion.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -160,7 +160,7 @@ public sealed partial class ExplosionSystem : EntitySystem
|
|||||||
if (explosive.Exploded)
|
if (explosive.Exploded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
explosive.Exploded = true;
|
explosive.Exploded = !explosive.Repeatable;
|
||||||
|
|
||||||
// Override the explosion intensity if optional arguments were provided.
|
// Override the explosion intensity if optional arguments were provided.
|
||||||
if (radius != null)
|
if (radius != null)
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered);
|
SubscribeLocalEvent<TriggerOnStepTriggerComponent, StepTriggeredOffEvent>(OnStepTriggered);
|
||||||
SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered);
|
SubscribeLocalEvent<TriggerOnSlipComponent, SlipEvent>(OnSlipTriggered);
|
||||||
SubscribeLocalEvent<TriggerWhenEmptyComponent, OnEmptyGunShotEvent>(OnEmptyTriggered);
|
SubscribeLocalEvent<TriggerWhenEmptyComponent, OnEmptyGunShotEvent>(OnEmptyTriggered);
|
||||||
|
SubscribeLocalEvent<RepeatingTriggerComponent, MapInitEvent>(OnRepeatInit);
|
||||||
|
|
||||||
SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(OnSpawnTrigger);
|
SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(OnSpawnTrigger);
|
||||||
SubscribeLocalEvent<DeleteOnTriggerComponent, TriggerEvent>(HandleDeleteTrigger);
|
SubscribeLocalEvent<DeleteOnTriggerComponent, TriggerEvent>(HandleDeleteTrigger);
|
||||||
@@ -241,6 +242,11 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
Trigger(uid, args.EmptyGun);
|
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)
|
public bool Trigger(EntityUid trigger, EntityUid? user = null)
|
||||||
{
|
{
|
||||||
var triggerEvent = new TriggerEvent(trigger, user);
|
var triggerEvent = new TriggerEvent(trigger, user);
|
||||||
@@ -323,6 +329,7 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
UpdateProximity();
|
UpdateProximity();
|
||||||
UpdateTimer(frameTime);
|
UpdateTimer(frameTime);
|
||||||
UpdateTimedCollide(frameTime);
|
UpdateTimedCollide(frameTime);
|
||||||
|
UpdateRepeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateTimer(float frameTime)
|
private void UpdateTimer(float frameTime)
|
||||||
@@ -357,5 +364,19 @@ namespace Content.Server.Explosion.EntitySystems
|
|||||||
_appearance.SetData(uid, TriggerVisuals.VisualState, TriggerVisualState.Unprimed, appearance);
|
_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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
41
Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs
Normal file
41
Content.Shared/Weapons/Ranged/Systems/ActionGunSystem.cs
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -91,6 +91,12 @@
|
|||||||
speedModifierThresholds:
|
speedModifierThresholds:
|
||||||
250: 0.7
|
250: 0.7
|
||||||
400: 0.5
|
400: 0.5
|
||||||
|
# disable taking damage from fire, since its a fire breathing dragon
|
||||||
|
- type: Flammable
|
||||||
|
damage:
|
||||||
|
types: {}
|
||||||
|
- type: Temperature
|
||||||
|
heatDamageThreshold: 800
|
||||||
- type: Metabolizer
|
- type: Metabolizer
|
||||||
solutionOnBody: false
|
solutionOnBody: false
|
||||||
updateInterval: 0.25
|
updateInterval: 0.25
|
||||||
@@ -141,10 +147,33 @@
|
|||||||
spawnRiftAction: ActionSpawnRift
|
spawnRiftAction: ActionSpawnRift
|
||||||
- type: GenericAntag
|
- type: GenericAntag
|
||||||
rule: Dragon
|
rule: Dragon
|
||||||
|
- type: ActionGun
|
||||||
|
action: ActionDragonsBreath
|
||||||
|
gunProto: DragonsBreathGun
|
||||||
- type: GuideHelp
|
- type: GuideHelp
|
||||||
guides:
|
guides:
|
||||||
- MinorAntagonists
|
- 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
|
- type: entity
|
||||||
parent: BaseMobDragon
|
parent: BaseMobDragon
|
||||||
id: MobDragonDungeon
|
id: MobDragonDungeon
|
||||||
@@ -183,6 +212,7 @@
|
|||||||
state: icon
|
state: icon
|
||||||
event: !type:DragonSpawnRiftActionEvent
|
event: !type:DragonSpawnRiftActionEvent
|
||||||
useDelay: 1
|
useDelay: 1
|
||||||
|
priority: 3
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: ActionDevour
|
id: ActionDevour
|
||||||
@@ -194,3 +224,18 @@
|
|||||||
icon: { sprite : Interface/Actions/devour.rsi, state: icon }
|
icon: { sprite : Interface/Actions/devour.rsi, state: icon }
|
||||||
iconOn: { sprite : Interface/Actions/devour.rsi, state: icon-on }
|
iconOn: { sprite : Interface/Actions/devour.rsi, state: icon-on }
|
||||||
event: !type:DevourActionEvent
|
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
|
||||||
|
|||||||
@@ -30,6 +30,39 @@
|
|||||||
- type: IgniteOnCollide
|
- type: IgniteOnCollide
|
||||||
fireStacks: 0.35
|
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
|
- type: entity
|
||||||
id: ProjectileAnomalyFireball
|
id: ProjectileAnomalyFireball
|
||||||
name: fireball
|
name: fireball
|
||||||
|
|||||||
Reference in New Issue
Block a user