Add craft for bonfire and bonfire with stake (#42211)

* Add craft for bonfire and bonfire with stake

* review

* review

* nullable MaxFireStacks

* retry

* retry

* review

* I will change it and hope that they will agree with me

* Revert "I will change it and hope that they will agree with me"

This reverts commit 83823692d0116bf9aa9eceb85a10e95c88b51fb9.

* construction

Merged the graphs
Changed the bonfire stake ID to follow the proper naming scheme

* add destroy sound

* planks Instead of logs

* of course I forgot about ftl

* Slarti review + Princess review

Yes I sneaked an admeme abuse change here

* a small fix

* clamp firestacks

* This was on purpose, leave as is

This reverts commit 7d63e38b66cb63e5e50b7fac5030013e2ef508b5.

* irrelevant

* Fixtures

* cleanup

---------

Co-authored-by: ScarKy0 <scarky0@onet.eu>
Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com>
This commit is contained in:
B_Kirill 2026-01-09 14:00:36 +10:00 committed by BarryNorfolk
parent 44e41d5019
commit 1cf546113d
11 changed files with 302 additions and 9 deletions

View File

@ -0,0 +1,70 @@
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);
}
}
}

View File

@ -0,0 +1,41 @@
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;
}

View File

@ -0,0 +1,31 @@
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;
}

View File

@ -1,15 +1,22 @@
- 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
@ -23,6 +30,9 @@
!type:DamageTrigger
damage: 50
behaviors:
- !type:PlaySoundBehavior
sound:
collection: WoodDestroy
- !type:DoActsBehavior
acts: [ "Destruction" ]
- type: AmbientSound
@ -31,15 +41,51 @@
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"

View File

@ -1328,4 +1328,30 @@
canRotate: false
canBuildInImpassable: false
conditions:
- !type:TileNotBlocked
- !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

View File

@ -0,0 +1,42 @@
- 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

@ -0,0 +1,37 @@
{
"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
]
]
}
]
}