From 00f4b8facce671c831b00602304188deb1e08959 Mon Sep 17 00:00:00 2001 From: Cethet Date: Fri, 30 May 2025 16:19:02 +0400 Subject: [PATCH 1/2] Bonfire --- .../Medieval/Bonfire/BonfireSystem.cs | 26 +++ .../Medieval/Bonfire/BonfireSystem.cs | 216 ++++++++++++++++++ .../Medieval/Bonfire/BonfireComponent.cs | 44 ++++ .../ru-RU/Imperial/Medieval/medieval.ftl | 4 + .../Objects/Consumable/Food/produce.yml | 5 + .../Prototypes/Imperial/Medieval/medieval.yml | 70 ++++++ .../Prototypes/Imperial/Medieval/tags.yml | 3 + .../Medieval/Decor/campfire.rsi/fire.png | Bin 0 -> 1130 bytes .../Medieval/Decor/campfire.rsi/meta.json | 27 +++ .../Decor/campfire.rsi/wood-burned.png | Bin 0 -> 1223 bytes .../Medieval/Decor/campfire.rsi/wood.png | Bin 0 -> 1012 bytes 11 files changed, 395 insertions(+) create mode 100644 Content.Client/Imperial/Medieval/Bonfire/BonfireSystem.cs create mode 100644 Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs create mode 100644 Content.Shared/Imperial/Medieval/Bonfire/BonfireComponent.cs create mode 100644 Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/fire.png create mode 100644 Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/meta.json create mode 100644 Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood-burned.png create mode 100644 Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood.png diff --git a/Content.Client/Imperial/Medieval/Bonfire/BonfireSystem.cs b/Content.Client/Imperial/Medieval/Bonfire/BonfireSystem.cs new file mode 100644 index 00000000000..a4e97064109 --- /dev/null +++ b/Content.Client/Imperial/Medieval/Bonfire/BonfireSystem.cs @@ -0,0 +1,26 @@ +using Robust.Client.GameObjects; +using Content.Shared.Imperial.Medieval.Bonfire; + +namespace Content.Client.Imperial.Medieval.Bonfire; + +public sealed class BonfireSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAppearanceChange); + } + private void OnAppearanceChange(EntityUid uid, BonfireComponent component, ref AppearanceChangeEvent args) + { + if (args.Sprite == null) + return; + + if (_appearance.TryGetData(uid, BonfireVisualLayers.Fire, out var isFireVisible, args.Component)) + { + args.Sprite.LayerSetVisible(BonfireVisualLayers.Fire, isFireVisible); + } + } +} diff --git a/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs b/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs new file mode 100644 index 00000000000..ef5f3ba0658 --- /dev/null +++ b/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs @@ -0,0 +1,216 @@ +using Robust.Server.GameObjects; +using Content.Shared.Interaction; +using Content.Shared.Stacks; +using Robust.Shared.Audio; +using Content.Server.Audio; +using Robust.Shared.Audio.Systems; +using Content.Shared.Imperial.Medieval.Bonfire; +using Content.Server.Imperial.Medieval.Igniter; +using Content.Shared.Examine; +using Content.Shared.Popups; +using Content.Shared.Tag; +using Content.Shared.Audio; +using Content.Shared.Placeable; +using Content.Shared.DoAfter; +using Content.Server.Temperature.Systems; + +namespace Content.Server.Imperial.Medieval.Bonfire; + +public sealed class BonfireSystem : EntitySystem +{ + [Dependency] protected readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPointLightSystem _lights = default!; + [Dependency] private readonly AmbientSoundSystem _ambientSound = default!; + [Dependency] private readonly SharedPopupSystem _popupSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly TagSystem _tagSystem = default!; + [Dependency] private readonly SharedStackSystem _stack = default!; + [Dependency] private readonly TemperatureSystem _temperature = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!; + + private const float FuelDecreasePerSecond = 0.11f; + private const float BoardFuelAmount = 20f; + private const float LogFuelAmount = 40f; + private const float SheetFuelAmount = 15f; + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnIgnitionDoAfter); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var bonfire, out var placer)) + { + if (bonfire.IsLit == BonfireVisuals.Off) + continue; + + var heat = bonfire.HeatingPower * frameTime; + foreach (var ent in placer.PlacedEntities) + { + _temperature.ChangeHeat(ent, heat); + } + + bonfire.CurrentFuel = MathF.Max(0, bonfire.CurrentFuel - FuelDecreasePerSecond * frameTime); + UpdateBonfireVisuals(uid, bonfire); + + if (bonfire.CurrentFuel <= 0) + { + bonfire.CurrentFuel = 0; + ExtinguishBonfire(uid, bonfire); + } + } + } + + private void UpdateBonfireVisuals(EntityUid uid, BonfireComponent component) + { + var fuelPercentage = component.CurrentFuel / component.MaxFuel; + var radius = fuelPercentage switch + { + > 0.8f => 5f, + > 0.5f => 4.5f, + > 0.3f => 3.5f, + > 0.1f => 2.8f, + _ => 2f + }; + + if (TryComp(uid, out var light)) + { + _lights.SetRadius(uid, radius, light); + } + + var energy = fuelPercentage switch + { + > 0.8f => 3f, + > 0.5f => 2.5f, + > 0.3f => 2f, + > 0.1f => 1.5f, + _ => 1f + }; + _lights.SetEnergy(uid, energy); + } + + private void OnExamined(EntityUid uid, BonfireComponent component, ExaminedEvent args) + { + if (component.IsLit == BonfireVisuals.Off) + { + args.PushText("Костёр не горит."); + return; + } + + var fuelLevel = (int)(component.CurrentFuel / component.MaxFuel * 100); + var fuelDescription = fuelLevel switch + { + > 80 => "яркое", + > 50 => "теплое", + > 20 => "обычное", + _ => "тухлое" + }; + + args.PushText($"У костра {fuelDescription} пламя."); + } + + private void OnInteractUsing(EntityUid uid, BonfireComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (component.IsLit == BonfireVisuals.Off && HasComp(args.Used)) + { + if (component.CurrentFuel <= 0) + { + _popupSystem.PopupEntity("Нет топлива для розжига!", uid, args.User, PopupType.Medium); + return; + } + + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, 2f, new IgnitionDoAfterEvent(), uid, args.Target, args.Used) + { + BreakOnMove = true, + BreakOnDamage = true, + NeedHand = true, + BreakOnDropItem = true + }; + + _doAfterSystem.TryStartDoAfter(doAfterArgs); + args.Handled = true; + return; + } + + if (_tagSystem.HasTag(args.Used, "Wooden") || _tagSystem.HasTag(args.Used, "Log") || _tagSystem.HasTag(args.Used, "Sheet")) + { + float fuelAmount = 0; + + switch (true) + { + case bool when _tagSystem.HasTag(args.Used, "Log"): + fuelAmount = LogFuelAmount; + break; + case bool when _tagSystem.HasTag(args.Used, "Sheet"): + fuelAmount = SheetFuelAmount; + break; + case bool when _tagSystem.HasTag(args.Used, "Wooden"): + fuelAmount = BoardFuelAmount; + break; + } + + if (TryComp(args.Used, out var stack) && stack.Count > 1) + { + _stack.SetCount(args.Used, stack.Count - 1); + } + else + { + EntityManager.DeleteEntity(args.Used); + } + + component.CurrentFuel = Math.Min(component.CurrentFuel + fuelAmount, component.MaxFuel); + _popupSystem.PopupEntity("Вы подложили топливо в костер", uid, args.User, PopupType.Medium); + UpdateBonfireVisuals(uid, component); + args.Handled = true; + } + } + + private void OnIgnitionDoAfter(EntityUid uid, BonfireComponent component, IgnitionDoAfterEvent args) + { + if (args.Handled || args.Cancelled) + return; + + LightBonfire(uid, component); + args.Handled = true; + } + + private void LightBonfire(EntityUid uid, BonfireComponent component) + { + component.IsLit = BonfireVisuals.Fire; + + EnsureComp(uid); + + _lights.SetEnabled(uid, true); + _lights.SetColor(uid, Color.FromHex("#FFC90C")); + _lights.SetEnergy(uid, 3); + + UpdateBonfireVisuals(uid, component); + + EnsureComp(uid); + + _ambientSound.SetSound(uid, new SoundPathSpecifier("/Audio/Ambience/Objects/fireplace.ogg")); + _ambientSound.SetRange(uid, 5); + _ambientSound.SetVolume(uid, -5); + + _appearance.SetData(uid, BonfireVisualLayers.Fire, true); + _audio.PlayPvs(new SoundPathSpecifier(component.IgnitionSound), uid); + } + + private void ExtinguishBonfire(EntityUid uid, BonfireComponent component) + { + component.IsLit = BonfireVisuals.Off; + _lights.SetEnabled(uid, false); + _ambientSound.SetAmbience(uid, false); + _appearance.SetData(uid, BonfireVisualLayers.Fire, false); + _audio.PlayPvs(new SoundPathSpecifier(component.ExtinguishSound), uid); + } +} diff --git a/Content.Shared/Imperial/Medieval/Bonfire/BonfireComponent.cs b/Content.Shared/Imperial/Medieval/Bonfire/BonfireComponent.cs new file mode 100644 index 00000000000..fd8cdf784cf --- /dev/null +++ b/Content.Shared/Imperial/Medieval/Bonfire/BonfireComponent.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Serialization; +using Robust.Shared.GameStates; +using Content.Shared.DoAfter; + +namespace Content.Shared.Imperial.Medieval.Bonfire; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class BonfireComponent : Component +{ + [DataField] + public string IgnitionSound = "/Audio/Items/Flare/flare_on.ogg"; + + [DataField] + public string ExtinguishSound = "/Audio/Items/candle_blowing.ogg"; + + [DataField, AutoNetworkedField] + public BonfireVisuals IsLit = BonfireVisuals.Off; + + [DataField] + public float MaxFuel = 100f; + + [DataField, AutoNetworkedField] + public float CurrentFuel = 0f; + + [DataField] + public float HeatingPower = 1200f; +} + +[Serializable, NetSerializable] +public sealed partial class IgnitionDoAfterEvent : SimpleDoAfterEvent { } + +[Serializable, NetSerializable] +public enum BonfireVisuals : byte +{ + Off, + Fire +} + +[Serializable, NetSerializable] +public enum BonfireVisualLayers : byte +{ + Bonfire, + Fire +} diff --git a/Resources/Locale/ru-RU/Imperial/Medieval/medieval.ftl b/Resources/Locale/ru-RU/Imperial/Medieval/medieval.ftl index a9916132823..505d8b7d5e1 100644 --- a/Resources/Locale/ru-RU/Imperial/Medieval/medieval.ftl +++ b/Resources/Locale/ru-RU/Imperial/Medieval/medieval.ftl @@ -290,6 +290,10 @@ ent-MedievalShovel = лопатка ent-MedievalCampfire = костёр .desc = Оружие инквизиции... .suffix = { "Средневековье, станок" } +ent-MedievalBonfire = костёр + .desc = Источник света и тепла! + .suffix = { "Средневековье" } + ent-MedievalMobFox = лиса .desc = Она имеет густой рыжий мех и длинный хвост. Некоторые люди считают, что лисы - это коварные создания, но на самом деле они очень умные и адаптивные. .suffix = { "Средневековье, моб" } diff --git a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml index 333354531cd..68414dc198e 100644 --- a/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml +++ b/Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml @@ -197,6 +197,11 @@ - type: Produce seedId: towercap - type: Log + #imperial-medieval-start + - type: Tag + tags: + - Log + #imperial-medieval-end - type: entity name: steel-cap log diff --git a/Resources/Prototypes/Imperial/Medieval/medieval.yml b/Resources/Prototypes/Imperial/Medieval/medieval.yml index 5348ca8434e..cbc794d9d28 100644 --- a/Resources/Prototypes/Imperial/Medieval/medieval.yml +++ b/Resources/Prototypes/Imperial/Medieval/medieval.yml @@ -7738,3 +7738,73 @@ doAfter: 4 - node: MedievalMortarTool entity: MedievalMortarTool + +- type: entity + id: MedievalBonfire + parent: BaseStructure + name: Campfire + description: A source of light and heat! + components: + - type: Sprite + noRot: true + sprite: Imperial/Medieval/Decor/campfire.rsi + layers: + - state: wood + map: ["enum.BonfireVisualLayers.Bonfire"] + - state: fire + map: ["enum.BonfireVisualLayers.Fire"] + visible: false + - type: Appearance + - type: Damageable + damageContainer: StructuralInorganic + damageModifierSet: Wood + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 50 + behaviors: + - !type:DoActsBehavior + acts: [ "Destruction" ] + - type: Construction + graph: Campfire + node: campfire + - type: Bonfire + - type: ItemPlacer + maxEntities: 2 + whitelist: + components: + - Temperature + - type: PlaceableSurface + +- type: construction + name: Костер + id: CampfireConstruction + graph: Campfire + startNode: start + targetNode: campfire + category: construction-category-misc + description: Тёплый огонь для ночных посиделок. + icon: + sprite: Imperial/Medieval/Decor/campfire.rsi + state: wood + objectType: Structure + placementMode: SnapgridCenter + canBuildInImpassable: false + conditions: + - !type:TileNotBlocked + +- type: constructionGraph + id: Campfire + start: start + graph: + - node: start + edges: + - to: campfire + steps: + - tag: Log + name: "Бревно" + doAfter: 3 + + - node: campfire + entity: MedievalBonfire diff --git a/Resources/Prototypes/Imperial/Medieval/tags.yml b/Resources/Prototypes/Imperial/Medieval/tags.yml index 77b789119ec..7990a015ba7 100644 --- a/Resources/Prototypes/Imperial/Medieval/tags.yml +++ b/Resources/Prototypes/Imperial/Medieval/tags.yml @@ -81,3 +81,6 @@ - type: Tag id: GoblinArmor + +- type: Tag + id: Log diff --git a/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/fire.png b/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/fire.png new file mode 100644 index 0000000000000000000000000000000000000000..6ede4d7abe73ea28626d53fc7dda702d18304078 GIT binary patch literal 1130 zcmV-w1eN=VP)Px(BuPX;RA@u(SvzjqKoA`Q>Jl4}4nQM)y3}?~kvfiCJ3xE^N*AyJr}igxa*5b= zx?I3KfCyMgM_`jT-dPVNN?egk3j&snZHd~MeedmiLph;%Z54rJtJPz1iUe#~1d)J9 zz?Ko{P~LnVO_Iymq{9-sZ9yRbXbT={6Pb)CnRX#zXDuic0Bs4t=RyczX9)-eKwAPt z0Lf+13Byhwet0!72Y<^>Yxn}Ns5S<`n2eID1%yzgPft=mznM=D{*zSD#f%JPo`qJ)@)HsrS=fQ#P^GkRYT z{Txz$JtsK{=5%QKAVhD@ssF5?{Oy3!Zza*)h{LcA2u%Wz_aQm~nvP}}UKaw=o08~$ zxN#_Z698aw-05%z#xr{N+%`af3exfZ|G{9{7JN6j?mpO^Q`!$Qx<36P76Kg=d=g|G z2u1(hr4M)FZ_6nI{iS$X?)*uBECTefV2MpNy$JvSup3srfRByicLBh@2SP$-gSNZc z`sc&vzAo%ZuogjvDx@?4vg&=;g8+S2xP3DLz~-!=iQOZ z?#U7^Hr*7`egvEjjbm9FkfVprZ1tNqEd^(T<^~i@(I0;LFkloo60HD@w z+*c4{J)Ci}Ut;#=FF0POs5MtHE{OsJa#EcodOCZld& z)2_?|8^V$U9LfTKnmX`$lmGgh0DX_kBU_V6R==ZJ@J z8}GkZeR(dBe#D~M2(L8(O)8D=`ymhs@WZLMyCMO-LF>10B)|`+-tLM7^aicp!jS+! woO-(}63`p8ehWte{BY{+u1G*{(E2U>4})9XKe44NzW@LL07*qoM6N<$g5T@?ZvX%Q literal 0 HcmV?d00001 diff --git a/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/meta.json b/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/meta.json new file mode 100644 index 00000000000..b464e35a1a1 --- /dev/null +++ b/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/meta.json @@ -0,0 +1,27 @@ +{ + "version": 1, + "license": "this content is under ICLA licence, read more on https://wiki.imperialspace.net/icla", + "copyright": "by prazat911", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "wood" + }, + { + "name": "wood-burned" + }, + { + "name": "fire", + "delays": [ + [ + 0.3, + 0.3, + 0.3 + ] + ] + } + ] +} diff --git a/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood-burned.png b/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood-burned.png new file mode 100644 index 0000000000000000000000000000000000000000..d3ddcf6594cdfdb056ba4ea7b2e792a14b051392 GIT binary patch literal 1223 zcmV;&1UUPNP)Px(fk{L`R9HvtR!wgkRTMpT8awf1Y{$+xsbj}&NK#rMO2tQugeagQBsQ={toaoz z`2nmDVvWjzP1ztJp+YMVh0umV)TBkrI-Q!E|!rl zn21KB*s0ef=4NFJ)$O`Rflm&%3FOQwuFWr6W$2c_JoLg?9HBrTV!DQz)hw

pX5(*drs!gs&Dop#j4p&3vlc<|G!H!&PDP^nbVY5{{G4Vmn^ zSiu#KC*;}*ob|x0{IT-TQ4<{r*b7Fr+vVwb1p50C)C0)o3f3zVW5VL)7PJBsurS=`g;yzUsj2NTAhk#6{EcEv%$=Plubx6Vs;I}%`U zl2~5%qS~lSAt`WHG1ryZWl1qDdCAbQyz9e8<^eW#dJqcgcyla*Kg~TM!4~G^jOhK) z{Z%M&S8f+A;jW{Y!zdXIC6V>+l{Ww21WRYWUb38K(BoPjUWMFYA5)^bM z-U~Z8pS!jpizT;Ng<01yZbT%Inu;V=G`GZGl^qhG${P=IVhvkQjc%U3vA8N{+`f(n z2&g*XLDoWJFDwMqOn>+6luR>4fZ5^am;^|cA0Jw$NO1TxcJhdYG)&(=NWzr>i=|pI z@yp$1tZlRhi`|m1r~k6*!%n~m4nX(&93%)%@0Fm(TEGmD0N3TKzv9lrrg%pMw8aP} zEdW|@e|-bDmez#eGwt~!N>vdj2Ag>GR0`UFF7X~qKy?DI3S1FlR%i?NndV@>-%`xF zVV`rUR%0=fyj@UN1ll^3<^NcGzC5`^Rf1 lFt`1#GSDgDQ55fw^$(JXY(M$#tUdq$002ovPDHLkV1hYDL*)Pf literal 0 HcmV?d00001 diff --git a/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood.png b/Resources/Textures/Imperial/Medieval/Decor/campfire.rsi/wood.png new file mode 100644 index 0000000000000000000000000000000000000000..58f889785ee155704f48f043aa4ce9836c4c4b23 GIT binary patch literal 1012 zcmVPx&u1Q2eR9Hvtmd#ERVHAcBLJO4Pr$t)Of(aneumFifjSD4lwnVyxg%!F z=Bi!Ul`K=DigBeSIx&F+?p|ptP?c))eGm4M316{D)$>%tE$T8KyVV$26{|l z9W@Ab#ABZ6u}Ls7jPAY!NDhg~g=0oSKvZ@rC&n|2Mq;Xi04~>-G}ctR3yZUiFeIW* z#28nLdpfe{79O~Yb=P0&*!6*e+ndey1RjAfDikBZ?-ju{tK_`;HW_>ngKXFU^CswPgxw&Y^hDRCS}$af$Codm85?*H=foL<*O zD&30xpJ!$rKSACus3ZdR22(|72~{x1tZE2Q9l`yBJD1Jh+B#h}N`l|~;DJ+MgR2_a zLp-7KQn5;V|IusHru};D&#U(({Y!=lyugCO#jI=vw=33jL6!a$fuNUdv-;O-F|eTL iB`HuW;bAKF=lTP;`)fbrS6$cu0000 Date: Tue, 3 Jun 2025 19:01:09 +0400 Subject: [PATCH 2/2] fix --- Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs b/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs index ef5f3ba0658..201820f4378 100644 --- a/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs +++ b/Content.Server/Imperial/Medieval/Bonfire/BonfireSystem.cs @@ -200,6 +200,7 @@ private void LightBonfire(EntityUid uid, BonfireComponent component) _ambientSound.SetSound(uid, new SoundPathSpecifier("/Audio/Ambience/Objects/fireplace.ogg")); _ambientSound.SetRange(uid, 5); _ambientSound.SetVolume(uid, -5); + _ambientSound.SetAmbience(uid, true); _appearance.SetData(uid, BonfireVisualLayers.Fire, true); _audio.PlayPvs(new SoundPathSpecifier(component.IgnitionSound), uid);