Data Farms (#2620)

Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: monolith8319 <195513600+monolith8319@users.noreply.github.com>
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
Co-authored-by: Ilya246 <ilyukarno@gmail.com>
This commit is contained in:
significant harassment
2026-03-22 15:46:27 -04:00
committed by GitHub
parent 19d869bda1
commit 881ee08f91
35 changed files with 637 additions and 85 deletions
@@ -3,9 +3,9 @@ namespace Content.Server.Research.Components;
[RegisterComponent]
public sealed partial class ResearchPointSourceComponent : Component
{
[DataField("pointspersecond"), ViewVariables(VVAccess.ReadWrite)]
[DataField]
public int PointsPerSecond;
[DataField("active"), ViewVariables(VVAccess.ReadWrite)]
public bool Active;
[DataField]
public bool Active = true;
}
@@ -3,6 +3,7 @@ using Content.Shared.Stacks;
using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._Goobstation.ItemMiner;
@@ -10,6 +11,7 @@ namespace Content.Server._Goobstation.ItemMiner;
public sealed class ItemMinerSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _gambling = default!;
[Dependency] private readonly PowerReceiverSystem _power = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StackSystem _stack = default!;
@@ -64,6 +66,9 @@ public sealed class ItemMinerSystem : EntitySystem
continue;
miner.NextAt += miner.Interval;
if (miner.SpawnChance < 1f && !_gambling.Prob(miner.SpawnChance))
continue;
// mine
var minedUid = Spawn(proto, xform.Coordinates);
var ev = new ItemMinedEvent(minedUid);
@@ -0,0 +1,45 @@
namespace Content.Server._Mono.Cargo;
/// <summary>
/// Assigns a price to an object that drifts over time.
/// </summary>
[RegisterComponent]
public sealed partial class DriftingPriceComponent : Component
{
/// <summary>
/// Minimum price to initialise to.
/// </summary>
[DataField(required: true)]
public double MinInitial;
/// <summary>
/// Maximum price to initialise to.
/// </summary>
[DataField(required: true)]
public double MaxInitial;
/// <summary>
/// Base price to tend towards.
/// </summary>
[DataField(required: true)]
public double BasePrice;
/// <summary>
/// Current price.
/// </summary>
[DataField]
public double CurrentPrice;
/// <summary>
/// How much to pull the price back towards the base.
/// 0 means do not pull at all.
/// </summary>
[DataField]
public double Stability = 0.5;
/// <summary>
/// How fast to drift the price, fraction per second.
/// </summary>
[DataField]
public double DriftRate = 0.01;
}
@@ -0,0 +1,48 @@
using Content.Server.Cargo.Systems;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server._Mono.Cargo;
public sealed class DriftingPriceSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private float _updateSpacing = 1f;
private float _updateAccum = 0f;
public override void Initialize()
{
SubscribeLocalEvent<DriftingPriceComponent, MapInitEvent>(OnInit);
SubscribeLocalEvent<DriftingPriceComponent, PriceCalculationEvent>(OnGetPrice);
}
private void OnInit(Entity<DriftingPriceComponent> ent, ref MapInitEvent args)
{
ent.Comp.CurrentPrice = _random.NextDouble(ent.Comp.MinInitial, ent.Comp.MaxInitial);
}
private void OnGetPrice(Entity<DriftingPriceComponent> ent, ref PriceCalculationEvent args)
{
args.Price += ent.Comp.CurrentPrice;
}
public override void Update(float frameTime)
{
_updateAccum += frameTime;
if (_updateAccum < _updateSpacing)
return;
_updateAccum -= _updateSpacing;
var query = EntityQueryEnumerator<DriftingPriceComponent>();
while (query.MoveNext(out var uid, out var price))
{
var driftTotal = price.CurrentPrice * price.DriftRate * _updateSpacing;
var priceScale = price.CurrentPrice / price.BasePrice;
var priceOffset = Math.Pow(priceScale, price.Stability);
var driftPoint = Math.Pow(_random.NextDouble(), priceOffset) * 2 - 1;
price.CurrentPrice += driftTotal * driftPoint;
}
}
}
@@ -41,6 +41,12 @@ public sealed partial class MachineBoardComponent : Component
/// </summary>
[DataField(required: true)]
public EntProtoId Prototype;
/// <summary>
/// Monolith - Whether this can be inserted into a flatpacker.
/// </summary>
[DataField]
public bool Flatpackable = true;
}
[DataDefinition, Serializable]
@@ -45,7 +45,7 @@ public abstract class SharedFlatpackSystem : EntitySystem
if (args.Slot.ID != ent.Comp.SlotId || args.Cancelled)
return;
if (HasComp<MachineBoardComponent>(args.Item))
if (TryComp<MachineBoardComponent>(args.Item, out var board) && board.Flatpackable) // Mono
return;
if (TryComp<ComputerBoardComponent>(args.Item, out var computer) && computer.Prototype != null)
@@ -41,6 +41,12 @@ public sealed partial class ItemMinerComponent : Component
[DataField]
public TimeSpan Interval = TimeSpan.FromSeconds(10.0f);
/// <summary>
/// Chance to actually spawn the result each interval.
/// </summary>
[DataField]
public float SpawnChance = 1f;
/// <summary>
/// Whether to need to be anchored to run.
/// </summary>
@@ -2,5 +2,7 @@ research-technology-xenopsychology = Xenopsychology
research-technology-bluespace-tethering = Bluespace Tethering
research-technology-basic-parts = Basic Components
research-technology-data-farms = Data Farming
research-technology-basic-research = Basic Research
research-technology-advanced-research = Advanced Research
research-technology-advanced-research = Advanced Research
@@ -137,6 +137,9 @@
points: 10000
- type: StaticPrice # Frontier
price: 2000 # Mono
- type: Tag
tags:
- ResearchDisk10000
- type: entity
parent: ResearchDisk
@@ -1,14 +1,10 @@
- type: entity
abstract: true
parent: BaseStructure
id: BaseMachine
id: BaseMachineIndestructible # Mono: Changed to BaseMachineIndestructible and removed destructible/damageable components
components:
- type: Animateable
- type: InteractionOutline
- type: Anchorable
delay: 2
- type: Physics
bodyType: Static
- type: Transform
noRot: true
- type: Fixtures
@@ -22,26 +18,6 @@
- MachineMask
layer:
- MachineLayer
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: StructuralMetallic
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 200
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 100
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- type: LanguageSpeaker # Einstein Engines - Language - Yes, on BASE MACHINE. This is all I need to make sure that in 99% of cases, machines use the language system.
- type: LanguageKnowledge # Einstein Engines - Language
speaks:
@@ -53,6 +29,34 @@
footstepSoundCollection:
collection: FootstepHull
- type: entity
abstract: true
parent: BaseMachineIndestructible
id: BaseMachine # Mono: Changed to parent off of previous BaseMachine but without destructible/damageable components
components:
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 200
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 100
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: StructuralMetallic
- type: Anchorable
delay: 2
- type: entity
abstract: true
parent: BaseMachine
@@ -66,61 +66,75 @@
- type: entity
id: BaseResearchAndDevelopmentPointSource
parent: BaseMachinePowered
name: "base R&D supercomputer"
parent: DatafarmResearchFaction # Mono: Changed to parent off of new research generation servers to replace existing mapped generators
name: bolted data farm (Research) # Mono
description: A power-hungry server dedicated towards scraping data from... somewhere. This one generates research points at a rate of 35 per second. It seems to be bolted to the floor. # Mono
# We should make this abstract once there are actual point sources.
components:
- type: Sprite
sprite: Structures/Machines/rndpointsource.rsi
layers:
- state: rndpointsource-off
- state: rndpointsource
shader: unshaded
map: ["enum.PowerDeviceVisualLayers.Powered"]
- type: ResearchClient
- type: ResearchPointSource
pointspersecond: 35
pointsPerSecond: 35
active: true
- type: PointLight
radius: 1.5
energy: 1.6
color: "#3db83b"
- type: ActivatableUI
key: enum.ResearchClientUiKey.Key
- type: ActivatableUIRequiresPower
- type: UserInterface
interfaces:
enum.ResearchClientUiKey.Key:
type: ResearchClientBoundUserInterface
- type: Appearance
- type: GenericVisualizer
visuals:
enum.PowerDeviceVisuals.Powered:
enum.PowerDeviceVisualLayers.Powered:
True: {visible: true}
False: {visible: false}
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 400
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 200
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- !type:SpawnEntitiesBehavior
spawn:
SheetSteel1:
min: 1
max: 1
- type: GuideHelp
guides:
- Science
energy: 1.5
color: "#93005b"
- type: Sprite # Mono: Changed sprite
sprite: _Mono/Structures/Machines/neapolitan_server.rsi
layers:
- state: icon
- state: unlit
shader: unshaded
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
- state: stripe
color: "#b3aa79"
- state: stripe2
color: "#5b0093"
#- type: ActivatableUI
# key: enum.ResearchClientUiKey.Key
#- type: ActivatableUIRequiresPower
#- type: UserInterface
# interfaces:
# enum.ResearchClientUiKey.Key:
# type: ResearchClientBoundUserInterface
#- type: Appearance
#- type: GenericVisualizer
# visuals:
# enum.PowerDeviceVisuals.Powered:
# enum.PowerDeviceVisualLayers.Powered:
# True: {visible: true}
# False: {visible: false}
#- type: Destructible
# thresholds:
# - trigger:
# !type:DamageTrigger
# damage: 400
# behaviors:
# - !type:DoActsBehavior
# acts: [ "Destruction" ]
# - trigger:
# !type:DamageTrigger
# damage: 200
# behaviors:
# - !type:DoActsBehavior
# acts: ["Destruction"]
# - !type:PlaySoundBehavior
# sound:
# collection: MetalBreak
# - !type:SpawnEntitiesBehavior
# spawn:
# SheetSteel1:
# min: 1
# max: 1
#- type: GuideHelp
# guides:
# - Science
#- type: Sprite
# sprite: Structures/Machines/rndpointsource.rsi
# layers:
# - state: rndpointsource-off
# - state: rndpointsource
# shader: unshaded
# map: ["enum.PowerDeviceVisualLayers.Powered"]
# / Mono end
@@ -98,6 +98,7 @@
- APECircuitboard
- ArtifactAnalyzerMachineCircuitboard
- ArtifactCrusherMachineCircuitboard
- DataFarmResearchCircuitboard
- type: latheRecipePack
id: CameraBoards
@@ -0,0 +1,22 @@
- type: entity
name: random bitcoin
id: SpawnLootDatafarmBitcoin
parent: MarkerBasePlaceFree
suffix: "Datafarm Bitcoin Spawner"
components:
- type: Sprite
layers:
- state: green
scale: 0.7, 0.7
- sprite: _NF/Markers/general.rsi
state: questionmark
color: red
- type: RandomSpawner
prototypes:
- BitcoinContribcoin
- BitcoinIdeascoin
chance: 0.75
offset: 0.0
rarePrototypes:
- BitcoinGoidacoin
rareChance: 0.1
@@ -0,0 +1,51 @@
- type: entity
id: DataFarmResearchCircuitboard
parent: BaseMachineCircuitboard
name: data farm (research) circuitboard
description: Looks like you could use a screwdriver to change the board type. Unable to be flatpacked due to complex components.
components:
- type: Sprite
sprite: _Mono/Objects/Misc/module.rsi
state: datafarm_research
- type: MachineBoard
prototype: DatafarmResearch
flatpackable: false
requirements:
Capacitor: 4
stackRequirements:
Cable: 5
CableHV: 5
tagRequirements:
ResearchDisk10000:
amount: 2
defaultPrototype: ResearchDisk10000
- type: Construction
deconstructionTarget: null
graph: Datafarm
node: research
- type: entity
id: DataFarmCryptoCircuitboard
parent: BaseMachineCircuitboard
name: data farm (crypto) circuitboard
description: Looks like you could use a screwdriver to change the board type. Unable to be flatpacked due to complex components.
components:
- type: Sprite
sprite: _Mono/Objects/Misc/module.rsi
state: datafarm_crypto
- type: MachineBoard
prototype: DatafarmCrypto
flatpackable: false
requirements:
Capacitor: 4
stackRequirements:
Cable: 5
CableHV: 5
tagRequirements:
ResearchDisk10000:
amount: 2
defaultPrototype: ResearchDisk10000
- type: Construction
deconstructionTarget: null
graph: Datafarm
node: crypto
@@ -0,0 +1,81 @@
- type: entity
parent: BaseItem
id: BaseBitcoin
name: basic bitcoin
abstract: true
description: A valuable bitcoin, composed of arbitrary data. Definitely a good investment.
components:
- type: EmitSoundOnPickup
sound:
path: /Audio/_Goobstation/Items/handling/disk_pickup.ogg
params:
volume: -6
- type: EmitSoundOnDrop
sound:
path: /Audio/_Goobstation/Items/handling/disk_drop.ogg
params:
volume: -6
- type: EmitSoundOnLand
sound:
path: /Audio/_Goobstation/Items/handling/disk_drop.ogg
params:
volume: -6
- type: Sprite
sprite: _Mono/Objects/Misc/bitcoin.rsi
layers:
- state: icon
- state: fluffcoin
- type: DriftingPrice
minInitial: 8000
maxInitial: 12000
basePrice: 10000
stability: 0.4
driftRate: 0.025
- type: entity
parent: BaseBitcoin
id: BitcoinContribcoin
name: Contribcoin™
description: A weirdly valuable bitcoin composed of many generic definitions, raw data and operating presets. Slightly volatile.
components:
- type: Sprite
sprite: _Mono/Objects/Misc/bitcoin.rsi
layers:
- state: icon
- state: fluffcoin
- type: entity
parent: BaseBitcoin
id: BitcoinIdeascoin
name: Ideascoin™
description: A strangely valuable bitcoin composed of raw conceptual data of a multitude of completely hypothetical things. Moderately volatile.
components:
- type: Sprite
sprite: _Mono/Objects/Misc/bitcoin.rsi
layers:
- state: icon
- state: kyrescoin
- type: DriftingPrice
minInitial: 12000
maxInitial: 18000
basePrice: 15000
stability: 0.2
driftRate: 0.05
- type: entity
parent: BaseBitcoin
id: BitcoinGoidacoin
name: Goidacoin™
description: An absurdly valuable and rare bitcoin, made up of tons of high(?)-quality code and generic systems. Highly volatile.
components:
- type: Sprite
sprite: _Mono/Objects/Misc/bitcoin.rsi
layers:
- state: icon
- state: spiritcoin
- type: DriftingPrice
minInitial: 10000
maxInitial: 30000
basePrice: 20000
stability: 0.1
driftRate: 0.1
@@ -0,0 +1,154 @@
- type: entity
id: BaseDataFarmIndestructible
parent: BaseMachineIndestructible
name: base datafarm
abstract: true
components:
- type: Sprite
sprite: _Mono/Structures/Machines/neapolitan_server.rsi
layers:
- state: icon
- state: unlit
shader: unshaded
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
- state: stripe
color: "#b3aa79"
- state: stripe2
color: "#5b0093"
- type: Appearance
- type: GenericVisualizer
visuals:
enum.PowerDeviceVisuals.Powered:
enum.PowerDeviceVisualLayers.Powered:
True: {visible: true}
False: {visible: false}
- type: ApcPowerReceiver
powerLoad: 40000
- type: AmbientSound
volume: -9
range: 5
sound:
path: /Audio/Ambience/Objects/server_fans.ogg
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: StructuralMetallic
- type: PassiveThermalSignature
signature: 8000000 # ~8km
- type: ThermalSignature
- type: ExtensionCableReceiver
- type: LightningTarget
priority: 1
- type: entity
id: BaseDataFarm
parent: BaseDataFarmIndestructible
name: base datafarm
abstract: true
components:
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 200
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- trigger:
!type:DamageTrigger
damage: 100
behaviors:
- !type:DoActsBehavior
acts: [ "Destruction" ]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- type: Anchorable
- type: entity
id: BaseDataFarmResearch
parent: BaseDataFarm
abstract: true
components:
- type: Sprite
sprite: _Mono/Structures/Machines/neapolitan_server.rsi
layers:
- state: icon
- state: unlit
shader: unshaded
map: ["enum.PowerDeviceVisualLayers.Powered"]
- state: stripe
color: "#b3aa79"
- state: stripe2
color: "#5b0093"
- type: ResearchClient
- type: ActivatableUI
key: enum.ResearchClientUiKey.Key
- type: ActivatableUIRequiresPower
- type: UserInterface
interfaces:
enum.ResearchClientUiKey.Key:
type: ResearchClientBoundUserInterface
- type: PointLight
radius: 1
energy: 1
color: "#5b0093"
- type: GuideHelp
guides:
- Science
- type: ResearchPointSource
pointsPerSecond: 10
active: true
- type: entity
id: DatafarmResearchFaction
parent: BaseDataFarmIndestructible
name: bolted data farm (Research)
description: A power-hungry server dedicated towards scraping data from... somewhere. This one generates research points at a rate of 35 per second. It seems to be bolted to the floor.
components:
- type: ResearchPointSource
pointsPerSecond: 35
active: true
- type: ApcPowerReceiver
powerLoad: 10000
- type: entity
id: DatafarmResearch
parent: [BaseDataFarm, ConstructibleMachine]
name: data farm (Research)
description: A power-hungry server dedicated towards scraping data from... somewhere. This one generates research points at a rate of 10 per second.
components:
- type: PointLight
radius: 1.1
energy: 1.1
- type: Machine
board: DataFarmResearchCircuitboard
- type: entity
id: DatafarmCrypto
parent: [BaseDataFarm, ConstructibleMachine]
name: data farm (Crypto)
description: A power-hungry server dedicated towards scraping data from... somewhere. This one generates valuable but random cryptocurrency at a rate of one every 10 minutes.
components:
- type: Sprite
sprite: _Mono/Structures/Machines/neapolitan_server.rsi
layers:
- state: icon
- state: unlit
shader: unshaded
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
- state: stripe
color: "#b3aa79"
- state: stripe2
color: "#009360"
- type: PointLight
radius: 1.1
energy: 1.1
color: "#009360"
- type: ItemMiner
proto: SpawnLootDatafarmBitcoin
interval: 6
spawnChance: 0.01 # highly random like real cryptomining
minedSound: /Audio/Effects/Cargo/ping.ogg
needApcPower: true
- type: Machine
board: DataFarmCryptoCircuitboard
@@ -14,7 +14,7 @@
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: ResearchServer
- type: ResearchPointSource
pointspersecond: 30
pointsPerSecond: 30
active: true
- type: TechnologyDatabase
supportedDisciplines:
@@ -180,4 +180,4 @@
- state: server_o
map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: StaticPrice
price: 4000
price: 4000
@@ -0,0 +1,18 @@
- type: constructionGraph
id: Datafarm
start: research
graph:
- node: research
entity: DataFarmResearchCircuitboard
edges:
- to: crypto
steps:
- tool: Screwing
doAfter: 2
- node: crypto
entity: DataFarmCryptoCircuitboard
edges:
- to: research
steps:
- tool: Screwing
doAfter: 2
@@ -83,6 +83,13 @@
id: SmartfridgeCircuitboard
result: SmartfridgeCircuitboard
# Datafarm
- type: latheRecipe
parent: BaseGoldCircuitboardRecipe
id: DataFarmResearchCircuitboard
result: DataFarmResearchCircuitboard
# economy
- type: latheRecipe
@@ -72,3 +72,16 @@
technologyPrerequisites:
- BasicResearch
position: -1, -2
- type: technology
id: DataFarmingTechnology
name: research-technology-data-farms
entityIcon: DatafarmCrypto
discipline: Experimental
tier: 3
cost: 25000
recipeUnlocks:
- DataFarmResearchCircuitboard
technologyPrerequisites:
- SuperParts
position: 1, 18
+4 -1
View File
@@ -440,11 +440,14 @@
- type: Tag
id: ChimeraFloor
# hhhh (Mono tech disk, whoever put it unsorted fuck you)
# Mono tech disks
- type: Tag
id: TechDiskMono
- type: Tag
id: ResearchDisk10000
# survival kit
- type: Tag
Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 429 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

@@ -0,0 +1,23 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Made by Onezero0 for Monolith",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "fluffcoin"
},
{
"name": "kyrescoin"
},
{
"name": "spiritcoin"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 527 B

@@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Made by Supernoob6677 (discord)",
"copyright": "CPU rogue Made by Supernoob6677 (discord), Datafarm sprites made by Onezero0 for Monolith",
"size": {
"x": 32,
"y": 32
@@ -9,6 +9,12 @@
"states": [
{
"name": "cpu_rogue"
},
{
"name": "datafarm_research"
},
{
"name": "datafarm_crypto"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 406 B

@@ -0,0 +1,40 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/blob/9c3494fd79e6bf8dc532300b9de4f688ff276ac9/icons/obj/machines/telecomms.dmi, edited by Onezero0 for Monolith",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "unlit",
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "icon"
},
{
"name": "panel"
},
{
"name": "stripe"
},
{
"name": "stripe2"
}
]
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB