From ad691931c6e000d74f57780305295653385c0da4 Mon Sep 17 00:00:00 2001 From: deltanedas <39013340+deltanedas@users.noreply.github.com> Date: Sun, 18 Aug 2024 22:34:43 +0000 Subject: [PATCH] add memory cell and rework logic construction (#24983) * rework construction to be deconstructable, add memory cell * update textures * add code * add memory cell and ports, empty circuit * d --------- Co-authored-by: deltanedas <@deltanedas:kde.org> --- .../Components/MemoryCellComponent.cs | 40 +++++++++ .../DeviceLinking/Systems/MemoryCellSystem.cs | 74 +++++++++++++++ .../en-US/machine-linking/receiver_ports.ftl | 5 ++ .../Prototypes/DeviceLinking/sink_ports.yml | 10 +++ .../Prototypes/Entities/Structures/gates.yml | 49 ++++++++-- .../Construction/Graphs/tools/logic_gate.yml | 84 ++++++++++++++++-- .../Prototypes/Recipes/Construction/tools.yml | 11 +++ .../Objects/Devices/gates.rsi/base.png | Bin 7579 -> 7307 bytes .../Devices/gates.rsi/edge_detector.png | Bin 7375 -> 7036 bytes .../Objects/Devices/gates.rsi/logic.png | Bin 0 -> 7196 bytes .../Objects/Devices/gates.rsi/memory_cell.png | Bin 0 -> 228 bytes .../Objects/Devices/gates.rsi/meta.json | 8 +- .../Devices/gates.rsi/power_sensor.png | Bin 7376 -> 7090 bytes 13 files changed, 264 insertions(+), 17 deletions(-) create mode 100644 Content.Server/DeviceLinking/Components/MemoryCellComponent.cs create mode 100644 Content.Server/DeviceLinking/Systems/MemoryCellSystem.cs create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/logic.png create mode 100644 Resources/Textures/Objects/Devices/gates.rsi/memory_cell.png diff --git a/Content.Server/DeviceLinking/Components/MemoryCellComponent.cs b/Content.Server/DeviceLinking/Components/MemoryCellComponent.cs new file mode 100644 index 0000000000..d55042978a --- /dev/null +++ b/Content.Server/DeviceLinking/Components/MemoryCellComponent.cs @@ -0,0 +1,40 @@ +using Content.Server.DeviceLinking.Systems; +using Content.Shared.DeviceLinking; +using Robust.Shared.Prototypes; + +namespace Content.Server.DeviceLinking.Components; + +/// +/// Memory cell that sets the output to the input when enabled. +/// +[RegisterComponent, Access(typeof(MemoryCellSystem))] +public sealed partial class MemoryCellComponent : Component +{ + /// + /// Name of the input port. + /// + [DataField] + public ProtoId InputPort = "MemoryInput"; + + /// + /// Name of the enable port. + /// + [DataField] + public ProtoId EnablePort = "MemoryEnable"; + + /// + /// Name of the output port. + /// + [DataField] + public ProtoId OutputPort = "Output"; + + // State + [DataField] + public SignalState InputState = SignalState.Low; + + [DataField] + public SignalState EnableState = SignalState.Low; + + [DataField] + public bool LastOutput; +} diff --git a/Content.Server/DeviceLinking/Systems/MemoryCellSystem.cs b/Content.Server/DeviceLinking/Systems/MemoryCellSystem.cs new file mode 100644 index 0000000000..56a6f45c3b --- /dev/null +++ b/Content.Server/DeviceLinking/Systems/MemoryCellSystem.cs @@ -0,0 +1,74 @@ +using Content.Server.DeviceLinking.Components; +using Content.Server.DeviceLinking.Events; +using Content.Server.DeviceNetwork; +using Content.Shared.DeviceLinking; + +namespace Content.Server.DeviceLinking.Systems; + +/// +/// Handles the control of output based on the input and enable ports. +/// +public sealed class MemoryCellSystem : EntitySystem +{ + [Dependency] private readonly DeviceLinkSystem _deviceLink = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnSignalReceived); + } + + public override void Update(float deltaTime) + { + base.Update(deltaTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var source)) + { + if (comp.InputState == SignalState.Momentary) + comp.InputState = SignalState.Low; + if (comp.EnableState == SignalState.Momentary) + comp.EnableState = SignalState.Low; + + UpdateOutput((uid, comp, source)); + } + } + + private void OnInit(Entity ent, ref ComponentInit args) + { + var (uid, comp) = ent; + _deviceLink.EnsureSinkPorts(uid, comp.InputPort, comp.EnablePort); + _deviceLink.EnsureSourcePorts(uid, comp.OutputPort); + } + + private void OnSignalReceived(Entity ent, ref SignalReceivedEvent args) + { + var state = SignalState.Momentary; + args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state); + + if (args.Port == ent.Comp.InputPort) + ent.Comp.InputState = state; + else if (args.Port == ent.Comp.EnablePort) + ent.Comp.EnableState = state; + + UpdateOutput(ent); + } + + private void UpdateOutput(Entity ent) + { + if (!Resolve(ent, ref ent.Comp2)) + return; + + if (ent.Comp1.EnableState == SignalState.Low) + return; + + var value = ent.Comp1.InputState != SignalState.Low; + if (value == ent.Comp1.LastOutput) + return; + + ent.Comp1.LastOutput = value; + _deviceLink.SendSignal(ent, ent.Comp1.OutputPort, value, ent.Comp2); + } +} diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl index dc45677c8d..a0d2fd3ec4 100644 --- a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl +++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl @@ -81,3 +81,8 @@ signal-port-description-logic-input-b = Second input of a logic gate. signal-port-name-logic-input = Input signal-port-description-logic-input = Input to the edge detector, cannot be a pulse signal. + +signal-port-description-logic-memory-input = Signal to load into the memory cell, when enabled. + +signal-port-name-logic-enable = Enable +signal-port-description-logic-enable = Only loads the input signal into the memory cell when HIGH. diff --git a/Resources/Prototypes/DeviceLinking/sink_ports.yml b/Resources/Prototypes/DeviceLinking/sink_ports.yml index 56b10ec4fc..339b814175 100644 --- a/Resources/Prototypes/DeviceLinking/sink_ports.yml +++ b/Resources/Prototypes/DeviceLinking/sink_ports.yml @@ -93,6 +93,16 @@ name: signal-port-name-logic-input description: signal-port-description-logic-input +- type: sinkPort + id: MemoryInput + name: signal-port-name-logic-input + description: signal-port-description-logic-memory-input + +- type: sinkPort + id: MemoryEnable + name: signal-port-name-logic-enable + description: signal-port-description-logic-enable + - type: sinkPort id: SetParticleDelta name: signal-port-name-set-particle-delta diff --git a/Resources/Prototypes/Entities/Structures/gates.yml b/Resources/Prototypes/Entities/Structures/gates.yml index 6b02840ba0..8e60962321 100644 --- a/Resources/Prototypes/Entities/Structures/gates.yml +++ b/Resources/Prototypes/Entities/Structures/gates.yml @@ -1,12 +1,24 @@ - type: entity - abstract: true parent: BaseItem - id: BaseLogicItem + id: LogicEmptyCircuit + name: empty circuit + description: Something seems to be missing. components: - type: Sprite sprite: Objects/Devices/gates.rsi + layers: + - state: base - type: Anchorable - type: Rotatable + - type: Construction + graph: LogicGate + node: empty + +- type: entity + abstract: true + parent: LogicEmptyCircuit + id: BaseLogicItem + components: - type: DeviceNetwork deviceNetId: Wireless receiveFrequencyId: BasicDevice @@ -23,6 +35,7 @@ - type: Sprite layers: - state: base + - state: logic - state: or map: [ "enum.LogicGateLayers.Gate" ] - type: LogicGate @@ -38,7 +51,6 @@ lastSignals: Output: false - type: Construction - graph: LogicGate node: logic_gate - type: Appearance - type: GenericVisualizer @@ -124,7 +136,9 @@ description: Splits rising and falling edges into unique pulses and detects how edgy you are. components: - type: Sprite - state: edge_detector + layers: + - state: base + - state: edge_detector - type: EdgeDetector - type: DeviceLinkSink ports: @@ -137,7 +151,6 @@ OutputHigh: false OutputLow: false - type: Construction - graph: LogicGate node: edge_detector - type: entity @@ -147,7 +160,9 @@ description: Generates signals in response to powernet changes. Can be cycled between cable voltages. components: - type: Sprite - state: power_sensor + layers: + - state: base + - state: power_sensor - type: PowerSensor - type: PowerSwitchable examineText: power-sensor-voltage-examine @@ -183,5 +198,25 @@ PowerCharging: false PowerDischarging: false - type: Construction - graph: LogicGate node: power_sensor + +- type: entity + parent: BaseLogicItem + id: MemoryCell + name: memory cell + description: A D-Latch circuit that stores a signal which can be changed depending on input and enable ports. + components: + - type: Sprite + layers: + - state: base + - state: memory_cell + - type: MemoryCell + - type: DeviceLinkSink + ports: + - MemoryInput + - MemoryEnable + - type: DeviceLinkSource + ports: + - Output + - type: Construction + node: memory_cell diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml b/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml index d366a2ea59..37c85e07a3 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/tools/logic_gate.yml @@ -4,38 +4,104 @@ graph: - node: start edges: - - to: logic_gate + - to: empty steps: - material: Steel amount: 3 doAfter: 1 + - node: empty + entity: LogicEmptyCircuit + edges: + - to: start + steps: + - tool: Screwing + doAfter: 2 + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 3 + - !type:DeleteEntity {} + - to: logic_gate + steps: - material: Cable amount: 2 doAfter: 1 - to: edge_detector steps: - - material: Steel - amount: 3 + - material: Glass + amount: 2 doAfter: 1 - material: Cable amount: 2 doAfter: 1 - to: power_sensor steps: - - material: Steel - amount: 3 - doAfter: 1 - - material: Cable - amount: 2 - doAfter: 1 - tag: Multitool icon: sprite: Objects/Tools/multitool.rsi state: icon name: a multitool + - material: Cable + amount: 2 + doAfter: 1 + - to: memory_cell + steps: + - tag: CapacitorStockPart + icon: + sprite: Objects/Misc/stock_parts.rsi + state: capacitor + name: a capacitor + - material: Cable + amount: 2 + doAfter: 1 - node: logic_gate entity: LogicGateOr + edges: + - to: empty + steps: + - tool: Prying + doAfter: 2 + completed: + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 2 - node: edge_detector entity: EdgeDetector + edges: + - to: empty + steps: + - tool: Prying + doAfter: 2 + completed: + - !type:SpawnPrototype + prototype: SheetGlass1 + amount: 2 + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 2 - node: power_sensor entity: PowerSensor + edges: + - to: empty + steps: + - tool: Prying + doAfter: 2 + completed: + - !type:SpawnPrototype + prototype: Multitool + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 2 + - node: memory_cell + entity: MemoryCell + edges: + - to: empty + steps: + - tool: Prying + doAfter: 2 + completed: + - !type:SpawnPrototype + prototype: CapacitorStockPart + - !type:SpawnPrototype + prototype: CableApcStack1 + amount: 2 diff --git a/Resources/Prototypes/Recipes/Construction/tools.yml b/Resources/Prototypes/Recipes/Construction/tools.yml index a6d9e33f4c..bb89c5f244 100644 --- a/Resources/Prototypes/Recipes/Construction/tools.yml +++ b/Resources/Prototypes/Recipes/Construction/tools.yml @@ -41,3 +41,14 @@ description: A power network checking device for signals. icon: { sprite: Objects/Devices/gates.rsi, state: power_sensor } objectType: Item + +- type: construction + name: memory cell + id: MemoryCell + graph: LogicGate + startNode: start + targetNode: memory_cell + category: construction-category-tools + description: A memory cell for signals. + icon: { sprite: Objects/Devices/gates.rsi, state: memory_cell } + objectType: Item diff --git a/Resources/Textures/Objects/Devices/gates.rsi/base.png b/Resources/Textures/Objects/Devices/gates.rsi/base.png index 946bfb4a29157d41a0cfb1c9a536a9f3d90945e9..dd963225770168488e86fb2669a5b6eb63ea3933 100644 GIT binary patch delta 570 zcmV-A0>%BCJBvAxBeR_WZw8Z32r>r>He@z2HaC-q2to%6GBPtUGcc3U2tol0lllil zlNAXNlXeL;2{JG;FfuSVI0};<2}HA}32+jB=H0005fNkl zRv`o&oTZCHsdOncOE+=p&`;n8aP$L6SHDRohfr|sBBhHKN;m23AOs=)jac8iJ})*o zW1`Rw`N8Ggy(9PD{obF1u^2I8{4o-;jaI9*PNh;Toldh{E*I+1Y&MHBlgZ%wfJ&u* zk_dH3v)Np4#7Tx}nroRICCHI_y?(IMqtOVZWm#KYr_*7x*^EVY0O;&r^|$MxHDm?7 zfOE5a!S3G&9z7xdoJTpCOr+O}icetW;p?20&PuqR&*$MCSS%JO zp$E{7%(9@KjDw+eaf;H@WD>meZQG8{05lTHbO0D@7diEGYel=<$boMH2SJ*IL>qKN zH|VBDZ;W#ovP7gVxVOrJ97raU2jvDZg9joT9FLzYx&s90=ad5!3T~vk(|km64v0_~ zsu4W|e)Z1dKlN0D`_^Hh99s8APpseWL7bqx$^rPKR;yK<>$>jg?_uCLPWBYy#IX+uL$X=7sm04R}lk-bYoQ5eR5YEqQtP+&xagGEC@6hvd$ z;36Srkkz6$KLS54_bMT+K}&DW*vI> zDyQIhhO1oNpO>6wzCFq5DMK(jFj zY!ZKyXfTif00C%8L_t(o!|hg0YJ@Npj{l-v2zrk80<#wJ60U@T2k6pe!4p(GKv!M4 z5!Z8q(i6BBq*U=2+hpo=pkp*zunRv3FGiF1<;#070 zKsim*Ty(y^?;~3j#ppNm)FLd)t`0Pu0+R%Io?q-(RaIoWt|L2w^7X!deEE3}g5c_8 z+qP#BX_|(NfOz{veP;pI5hR8qLDMIJtEwUo!n&@J#nk}ld`uk(P1BIg^Bk!PH%5OP z14AKMg3ADI-`~g-7-WOha12vN5XbSke{(oevk8105Bz_S6k-)&f zaM;tuF(l$}a)Jcw;sg;MgM=fhf${PFq1{r_(w>(ceAyouceh^SQ+!e^mc&z7Q1HXQ z?*^( zuw5#e6UflD{@d0SsVr48$=&IzS`!Y;_YZ#&CX~d>C^uWV=j+#VEVbUf8VfXd8+49~ znU)kYn94X#En=9h{31#~aI$iNsEdnb!wjRjX@L$!wtl|s0lNc@xZDnENK9d1m>TYG UcSZSe63{;kp00i_>zopr0NJ&T-2eap delta 798 zcmV+(1L6GqHqSYbBYy#HX+uL$X=7sm04R}lk zY|>g~ahaLj*;V^vnwd?Tt8#Hr4&vh1!GFM2xl&4zlHA<=x=7=Bty*dKo!)sqec$){ z^vrj_7tIbTs zlPSl5x<=tK!?ImrnTF`9Ybo9oZZ|SoQusi4(3Sy(FO{yB`AKoQ%x{YAcx(!Ms%I$u zhxI>fWbB-9zi>F0EgHY`s4oeo^7C_2yJUCc5M!DMA#{qYki}(4YF;v{?i>E`%}@}J zA#aNkwm8yc#D6;|YEinTq?)3qRK4?XYe z2|*rDze8i%VDl000SaNU2F*0Q{FfwK`GB9BZlh6bd0yZ?0p#?G(3N~amH8y28G&eIcW-~D| zWiv4{W|QFtED16)GchwTGdBv876&5%Ig`Z(BmoMOT?ax3GB7tZHaQBDj0QK8IR_C7 z3N~goWiwFq5DM zKmiJqe+VocFkvz z005myL_t(o!|jzz4uBvGMAh_Qa5BQl;AVc75|F|ah)Lhh)H+ipmTglMWiflH;G8=p zg}nD!Evht$h~L8$YlJj2C;zMy=Of0LF7X&+^&OJR*joE~4k57mNJ^Y5m~yNXhYg_O zO%bFSx8yuD0u=TMM?4qJ)Cf=$B6}cbV(KD7{s@)<&Q2c|fz*}UWdOa3 c6-D_47w{D{GitNP7XSbN07*qoM6N<$g7E7~IsgCw diff --git a/Resources/Textures/Objects/Devices/gates.rsi/logic.png b/Resources/Textures/Objects/Devices/gates.rsi/logic.png new file mode 100644 index 0000000000000000000000000000000000000000..cb120ce4961ab0ffcd9153c0f8d594e19cf9a057 GIT binary patch literal 7196 zcmd5>3v3is6dkY_94)D)q{aZ*0x|v~v+vEknUMr2P@yIgt-qo`*w3ugh!g=S5>ZjB zd?eK7ZZpgVvP8q5D6M18u8pWGrRM4x7%IXw9aNe?|1IG z=iYaxnXb8J){JWX0$tOz>ZX~~n{kiPIs8mqm)w2#@3@`SHgon8O}l&?okQ9;O}(Q*-*DzZ_Sh9-Jn( znXp9MHgPqXVcWvRv%?J2hMsA1Ad_M)t<^1G&}>K84yQ7MGv;v+!j*G|AA0ahppUMM zLoOcIxr2rk3=lN=48fxQhdR(O9r}gOe=r6K^KjAJXl+}X(sa7|xe+rR-7!NQwFo4O+21cM(;@){gyx8*raK7n~PFvU)ZMUxJW|G zwYknyJYyp`A}+W{3d@Gzg5-zj!sBU(mq{j*8OG4>3X^&)n_Fg@8)h0o<(P|f*GZ&k^!cjk5cE?O-pqGm6Gx-foo(t`%%Ltkrs1YdU(g>>VvEh*@)v0 z42ld&umN;%K)z5pMHIqgBO_BADUKXN#(3K$lb^6%Mt zRQ{6UGy4UILe(!o#Bs2JqIu_QHrR{`BUd(~LT6O4vVqiPg{^@A>dhII)j%*(E%w$@ z7**zs3atRCLzT7q2`KwstIT?_+5N<*4`n1jFoVx1U#AKfIJBjGZg`;lS{RFVN`0VI#vKwzB9c5sl(;B3K*5YRGkT+({-i*NbNY?R-asp z`qC$$DEq+ziaHbJ(}%y0RG5JT>e(5U(_o3MvWT{dFe=xXD2x=Fz9|&>%1+XCfAlEM)ao^XPHYC+Fz2W+n_mAE<@wSJi)Q&#&{KZvsXKcCurghuSy0re#(B_-Z zoYU~Zu(vOH^3j%eM?Uy!*SyY}ofA5qdUi#p{p*6R+A*CI*B{*e)2bQTh*8dFe$j!m zKRcoAT0EtuDz=+_@!aF%>n~nA_REJqU-r!Ur;hBn_po<<$NuHrPjC6bSy#QOb!~d1 z_rtu}#%C|v{YTY&=jy%P``VBFJ?W~&*VgV|^z-&gs+y>9Xh8WdENL0Cnt7ca$REX zzL~Q=(H4xV8$JB$y`6hbjA|Tt#g%i;ct1A#fg?ljn0)Z#vBrbdv!;zY++4Hk__lW@ z&V6ZT)uah8{`e%$L-!CR=Rs?#}57ND(jpxUK`o?m-SuK=Ua~+KeGH#*QuR#_l#NGJa6*H SG5q&NYigV|{q?EySN{i0cq3H+ literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/gates.rsi/memory_cell.png b/Resources/Textures/Objects/Devices/gates.rsi/memory_cell.png new file mode 100644 index 0000000000000000000000000000000000000000..bcb0d509b96affd4bf078ac7d4fa3c54ae94b59d GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz&H|6fVg?31We{epSZZGe6rASi z;usQfI5|Orb#a17Pt!y`iO#?+_5c6 literal 0 HcmV?d00001 diff --git a/Resources/Textures/Objects/Devices/gates.rsi/meta.json b/Resources/Textures/Objects/Devices/gates.rsi/meta.json index 9d10f53279..761bdab87a 100644 --- a/Resources/Textures/Objects/Devices/gates.rsi/meta.json +++ b/Resources/Textures/Objects/Devices/gates.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "or.png originally created by Kevin Zheng, 2022. All are modified by deltanedas (github) for SS14, 2023.", + "copyright": "or.png originally created by Kevin Zheng, 2022. All are modified by deltanedas (github) for SS14, 2024.", "size": { "x": 32, "y": 32 @@ -10,6 +10,9 @@ { "name": "base" }, + { + "name": "logic" + }, { "name": "or" }, @@ -36,6 +39,9 @@ }, { "name": "power_sensor" + }, + { + "name": "memory_cell" } ] } diff --git a/Resources/Textures/Objects/Devices/gates.rsi/power_sensor.png b/Resources/Textures/Objects/Devices/gates.rsi/power_sensor.png index 3bd6de6d1039fb6b7b3ffc6e497523e836fa88eb..c374715b4eec6f0b358a0a036303dc9571fb01a5 100644 GIT binary patch delta 342 zcmV-c0jd7bIkGp9BeR?VI0ln&2o#e{2n+)XGc%LG2Q-r>2oM7_ISP{p2r-jy2tt$e z2p$0nlb;AMlh6nlvlt2f5`X69$Y1~f0QpHoK~z}7V_+BsqhJ&aFkr+bQD0wARf`%M z8%fhZO$XpJi-pz>A>?xwVhnL`aQKf)&e72kOz+>nA4BfIfdh<$%*N_~AD1%zbLcfP z!h8U90A9-}0ZiBgIrL^Q!h#-G6d+rU&G!^JfDjtAeJ^%m! delta 643 zcmV-}0(||lH_$ndBYy#HX+uL$X=7sm04R}lkv&VpP!xurR>iMM2Sr32I;c1(NCk1! zI=B>(s$kWnNt0GcYe-T=Tm=^g!9jHKYw#cFD!3{Lf+C2xx%;(9iRT)jBK3xw^YGsL z9!}msIBDqxw{{c?MbAl1Bw};v`B?okjkE(p#51~Uk4;S_`G0qP+!9jtWLJh*hy6t$vG7ZTU&sMxC+@|LXfWen$orzErvv@RQr~@-2dk3nnUrRdn5sMkG}uf1NGZb-tzs=L*FkS zLCpi47c2f{$!X+W^sS24y${iKIK8N7+76s;K*znEaWdahjqI>#!pjM?q@nd1nihhr ze$F|*gPAq?MOU!92fcDXy7&cAmuN5YMaUrl000SaNUEELbI6%1QUPQ9WxyO z005pzL_t(o!|hc;4!|G?bHs<~%ZxA6pKZ$$Pm_tjVod18L!+hGfO7_eVKEMjNGY9~ zLOJKImLFVF5x>o#oR?fCI{)N7<&h8q%EcI;vy}4UYOTY!LE^57O8^?;J@>yBpe4>K zKvSF?LhR4649pY;ZZW1hfH^ype2cgP(oDcg@qOn2H}UKc_GWrpQj~cwDnM%`vkH)| dVuQi31ulAgH!*^}bj1Jw002ovPDHLkV1n^?A(j9D -- 2.51.2