--- /dev/null
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos.Components;
+using Content.Shared.Buckle.Components;
+using Robust.Shared.Timing;
+
+namespace Content.Server.Buckle.Systems;
+
+public sealed class IgniteOnBuckleSystem : EntitySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly FlammableSystem _flammable = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<IgniteOnBuckleComponent, StrappedEvent>(OnStrapped);
+ SubscribeLocalEvent<IgniteOnBuckleComponent, UnstrappedEvent>(OnUnstrapped);
+
+ SubscribeLocalEvent<ActiveIgniteOnBuckleComponent, MapInitEvent>(ActiveOnInit);
+ }
+
+ private void OnStrapped(Entity<IgniteOnBuckleComponent> ent, ref StrappedEvent args)
+ {
+ // We cache the values here to the other component.
+ // This is done so we have to do less lookups
+ var comp = EnsureComp<ActiveIgniteOnBuckleComponent>(args.Buckle);
+ comp.FireStacks = ent.Comp.FireStacks;
+ comp.MaxFireStacks = ent.Comp.MaxFireStacks;
+ comp.IgniteTime = ent.Comp.IgniteTime;
+ }
+
+ private void ActiveOnInit(Entity<ActiveIgniteOnBuckleComponent> ent, ref MapInitEvent args)
+ {
+ // Handle this via a separate MapInit so the component can be added by itself if need be.
+ ent.Comp.NextIgniteTime = _timing.CurTime + ent.Comp.NextIgniteTime;
+ Dirty(ent);
+ }
+
+ private void OnUnstrapped(Entity<IgniteOnBuckleComponent> ent, ref UnstrappedEvent args)
+ {
+ RemCompDeferred<ActiveIgniteOnBuckleComponent>(args.Buckle);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var curTime = _timing.CurTime;
+
+ var query = EntityQueryEnumerator<ActiveIgniteOnBuckleComponent, FlammableComponent>();
+ while (query.MoveNext(out var uid, out var igniteComponent, out var flammableComponent))
+ {
+ if (curTime < igniteComponent.NextIgniteTime)
+ continue;
+
+ igniteComponent.NextIgniteTime += TimeSpan.FromSeconds(igniteComponent.IgniteTime);
+ Dirty(uid, igniteComponent);
+
+ if (flammableComponent.FireStacks > igniteComponent.MaxFireStacks)
+ continue;
+
+ var stacks = flammableComponent.FireStacks + igniteComponent.FireStacks;
+ if (igniteComponent.MaxFireStacks.HasValue)
+ stacks = Math.Min(stacks, igniteComponent.MaxFireStacks.Value);
+
+ _flammable.SetFireStacks(uid, stacks, flammableComponent, true);
+ }
+ }
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Buckle.Components;
+
+/// <summary>
+/// Component for entities that are currently being ignited by <see cref="IgniteOnBuckleComponent"/>
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentPause, AutoGenerateComponentState]
+public sealed partial class ActiveIgniteOnBuckleComponent : Component
+{
+ // We cache data in this component and apply it to those who get buckled to have to do less lookups.
+
+ /// <summary>
+ /// How many fire stacks to add per cycle.
+ /// </summary>
+ [DataField]
+ public float FireStacks = 0.5f;
+
+ /// <summary>
+ /// How frequently the ignition should be applied, in seconds.
+ /// </summary>
+ [DataField]
+ public float IgniteTime = 1f;
+
+ /// <summary>
+ /// Maximum fire stacks that can be added by this source.
+ /// If target already has this many or more fire stacks, no additional stacks will be added.
+ /// Null means unlimited.
+ /// </summary>
+ [DataField]
+ public float? MaxFireStacks = 2.5f;
+
+ /// <summary>
+ /// Next time that fire stacks will be applied.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [AutoPausedField, AutoNetworkedField]
+ public TimeSpan NextIgniteTime = TimeSpan.Zero;
+}
--- /dev/null
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Buckle.Components;
+
+/// <summary>
+/// Component that makes an entity ignite entities that are buckled to it.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class IgniteOnBuckleComponent : Component
+{
+ /// <summary>
+ /// How many fire stacks to add per cycle.
+ /// </summary>
+ [DataField]
+ public float FireStacks = 0.5f;
+
+ /// <summary>
+ /// How frequently the ignition should be applied, in seconds.
+ /// </summary>
+ [DataField]
+ public float IgniteTime = 1f;
+
+ /// <summary>
+ /// Maximum fire stacks that can be added by this source.
+ /// If target already has this many or more fire stacks, no additional stacks will be added.
+ /// Null means unlimited.
+ /// </summary>
+ [DataField]
+ public float? MaxFireStacks = 2.5f;
+}
- type: entity
- id: Bonfire
+ abstract: true
parent: BaseStructure
- name: bonfire
- description: What can be better than a late evening under the sky with guitar and friends?
+ id: BaseBonfire
components:
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeCircle
+ radius: 0.4
+ mask:
+ - Impassable
+ layer:
+ - Opaque
+ - BulletImpassable
- type: Sprite
noRot: true
sprite: Structures/Decoration/bonfire.rsi
- layers:
- - state: bonfire
- - state: burning
- type: PointLight
radius: 5
energy: 3
!type:DamageTrigger
damage: 50
behaviors:
+ - !type:PlaySoundBehavior
+ sound:
+ collection: WoodDestroy
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: AmbientSound
sound:
path: /Audio/Ambience/Objects/fireplace.ogg
- type: AlwaysHot
+ - type: IgnitionSource
+ temperature: 700
+ ignited: true
+
+- type: entity
+ parent: BaseBonfire
+ id: Bonfire
+ name: bonfire
+ description: What can be better than a late evening under the sky with guitar and friends?
+ components:
+ - type: Sprite
+ layers:
+ - state: burning
+ - type: Construction
+ graph: Bonfire
+ node: bonfire
+
+- type: entity
+ parent: BaseBonfire
+ id: BonfireStake
+ name: bonfire with stake
+ description: A sinister bonfire with a stake for... ceremonial purposes. Best not to ask.
+ components:
+ - type: Sprite
+ sprite: Structures/Decoration/bonfire_stake.rsi
+ layers:
+ - state: burning
+ offset: "0, 0.5"
+ - type: Strap
+ position: Stand
+ buckleOffset: "0, 0.5"
+ buckleDoafterTime: 5
+ - type: IgniteOnBuckle
+ - type: Construction
+ graph: Bonfire
+ node: bonfireStake
- type: entity
+ parent: BaseBonfire
id: LegionnaireBonfire
- parent: Bonfire
name: legionnaire bonfire
description: There, in the land of lava and ash, place to to cook marshmallow and potato.
components:
- type: Sprite
layers:
- - state: legionnaire_bonfire
+ - state: legionnaire_bonfire
- type: PointLight
color: "#FF5601"
canRotate: false
canBuildInImpassable: false
conditions:
- - !type:TileNotBlocked
\ No newline at end of file
+ - !type:TileNotBlocked
+
+- type: construction
+ id: Bonfire
+ graph: Bonfire
+ startNode: start
+ targetNode: bonfire
+ category: construction-category-structures
+ objectType: Structure
+ placementMode: SnapgridCenter
+ canRotate: false
+ canBuildInImpassable: false
+ conditions:
+ - !type:TileNotBlocked
+
+- type: construction
+ id: BonfireStake
+ graph: Bonfire
+ startNode: start
+ targetNode: bonfireStake
+ category: construction-category-structures
+ objectType: Structure
+ placementMode: SnapgridCenter
+ canRotate: false
+ canBuildInImpassable: false
+ conditions:
+ - !type:TileNotBlocked
--- /dev/null
+- type: constructionGraph
+ id: Bonfire
+ start: start
+ graph:
+
+ - node: start
+ edges:
+ - to: bonfire
+ steps:
+ - material: WoodPlank
+ amount: 10
+ doAfter: 4
+
+ - node: bonfire
+ entity: Bonfire
+ edges:
+ - to: start
+ completed:
+ - !type:SpawnPrototype
+ prototype: MaterialWoodPlank1
+ amount: 10
+ - !type:DeleteEntity { }
+ steps:
+ - tool: Prying
+ doAfter: 4
+ - to: bonfireStake
+ steps:
+ - material: WoodPlank
+ amount: 2
+ doAfter: 2
+
+ - node: bonfireStake
+ entity: BonfireStake
+ edges:
+ - to: bonfire
+ completed:
+ - !type:SpawnPrototype
+ prototype: MaterialWoodPlank1
+ amount: 2
+ steps:
+ - tool: Prying
+ doAfter: 1
--- /dev/null
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from /tg/station at commit 28b476ab6d17014e6f9e18a748d7c96be28de9a1, modified by B_Kirill",
+ "size": {
+ "x": 32,
+ "y": 64
+ },
+ "states": [
+ {
+ "name": "bonfire"
+ },
+ {
+ "name": "bonfire_extinguished"
+ },
+ {
+ "name": "burning",
+ "delays": [
+ [
+ 0.3,
+ 0.3,
+ 0.3
+ ]
+ ]
+ },
+ {
+ "name": "legionnaire_bonfire",
+ "delays": [
+ [
+ 0.3,
+ 0.3,
+ 0.3
+ ]
+ ]
+ }
+ ]
+}