Compare commits

...

419 Commits

Author SHA1 Message Date
Redrover1760 eebcb303f0 and better 2026-05-07 21:12:27 -04:00
Redrover1760 2ccf6d95b6 makes it even better 2026-05-07 21:12:16 -04:00
Redrover1760 1b953cf4cb ide said so 2026-05-07 21:10:20 -04:00
Redrover1760 9ddc145cc1 aaa 2026-05-07 18:21:30 -04:00
Redrover1760 8e59b16718 throw on bad usage 2026-05-07 17:25:18 -04:00
Redrover1760 bbaeab61b5 blip optimizations round 2 2026-05-07 17:18:49 -04:00
Redrover1760 97e11f66d1 next 2026-05-07 16:42:14 -04:00
monolith8319 80ae6a33a5 Automated Changelog (#3960) 2026-05-07 00:48:53 +00:00
significant harassment 4a3178b4e2 Draupnir Nerf (#3960)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-05-06 20:48:17 -04:00
monolith8319 36faba3835 Automated Changelog (#3962) 2026-05-06 22:28:43 +00:00
Redrover1760 2c6fe74bca A Few More Optimizations (#3962) 2026-05-06 18:28:08 -04:00
monolith8319 3ce24a6dea Automated Changelog (#3939) 2026-05-06 22:19:29 +00:00
kasature90 ff40a7c9ce IT DIDNT NEED TO TAKE THIS LONG (#3939)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-05-06 18:18:54 -04:00
monolith8319 f66007ed82 Automated Changelog (#3945) 2026-05-06 21:57:15 +00:00
Svarshik 116b6b5201 Hotfix - Spasaka Modsuit Helmet Fixed (#3945) 2026-05-06 17:56:48 -04:00
monolith8319 eceaa24095 Automated Changelog (#3959) 2026-05-06 21:34:00 +00:00
Redrover1760 51e43c0218 Starting Gun Update Optimizations (#3959) 2026-05-06 17:33:23 -04:00
monolith8319 d11a891bed Automated Changelog (#3952) 2026-05-06 21:29:12 +00:00
Mrrpip 53222c7db0 Deletes Duplicate Consoles on Tarantula. (#3952) 2026-05-06 17:28:35 -04:00
monolith8319 f875a93d99 Automated Changelog (#3931) 2026-05-06 21:22:00 +00:00
Redrover1760 58e585fafb Emp Emitter Buffs (#3931) 2026-05-06 17:21:28 -04:00
monolith8319 4b371b6f08 Automated Changelog (#3934) 2026-05-06 20:52:47 +00:00
Re-Atomized 61bbc0b04d Miscellaneous Fixes of Things Nobody Else Wanted to Touch Apparently (#3934) 2026-05-06 16:51:59 -04:00
monolith8319 17376acb2b Automated Changelog (#3954) 2026-05-06 20:51:35 +00:00
Mrrpip 2a91d09f34 Renames the NT Secure Vault to Something More Solarian. (#3954) 2026-05-06 16:50:58 -04:00
monolith8319 425732c52b Automated Changelog (#3957) 2026-05-06 17:26:01 +00:00
Ilya246 f2d130f925 Temp Disable Radar Markings (#3957) 2026-05-06 17:25:20 +00:00
monolith8319 5423c7111e Automated Changelog (#3942) 2026-05-05 17:23:31 +00:00
Corrupt dce3a56655 Serkesh Tweaks (#3942)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-05-05 13:22:54 -04:00
monolith8319 b4fe165b53 Automated Changelog (#3949) 2026-05-05 17:18:09 +00:00
Redrover1760 5c883e42d7 Fixes Biomass Being Shit (#3949) 2026-05-05 13:17:33 -04:00
monolith8319 f99ef195df Automated Changelog (#3944) 2026-05-05 16:37:17 +00:00
Mrrpip a5d51193cb Decreases Eject Delay on the ChemVend to NanoMed Plus' Value. (#3944)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-05-05 12:36:39 -04:00
Minerva b8cfff831f Cherrypicks some Nix-Related Commits From Wizard's Den (#3948)
Co-authored-by: Tobias Berger <toby@tobot.dev>
Co-authored-by: EchoOfNothing <52498373+EchoOfNothing@users.noreply.github.com>
Co-authored-by: MidoriKurage <104000416+mdrkrg@users.noreply.github.com>
2026-05-05 12:32:28 -04:00
monolith8319 84b077432d Automated Changelog (#3947) 2026-05-05 13:13:11 +00:00
Corrupt 901f1dad81 Remove Merchant (#3947) 2026-05-05 15:12:29 +02:00
monolith8319 9b0c66171a Automated Changelog (#3941) 2026-05-04 18:11:55 +00:00
Corrupt 8f354a91f8 merge ts NOW 2026-05-04 20:11:12 +02:00
monolith8319 f9dad2c68a Automated Changelog (#3943) 2026-05-04 16:38:31 +00:00
Redrover1760 730f0758b9 Shader Fixes V2 (#3943) 2026-05-04 12:38:01 -04:00
monolith8319 8956b8693a Automated Changelog (#3437) 2026-05-04 06:53:53 +00:00
Corrupt 8ec089e18a New Ship: MAWC Merchant (#3437)
Co-authored-by: Isratosh <Isratosh@hotmail.com>
Co-authored-by: kasature90 <kasature.asakim@gmail.com>
2026-05-04 02:53:21 -04:00
monolith8319 1415ce6c0c Automated Changelog (#3796) 2026-05-04 06:52:31 +00:00
Huaqas 94fcc2457a Resomi Visual Update (#3796)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-05-04 02:52:01 -04:00
monolith8319 cee68ab5f9 Automated Changelog (#3769) 2026-05-04 06:50:26 +00:00
significant harassment 4c6401cf0f Shipgun Reworking (#3769)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-05-04 02:49:50 -04:00
monolith8319 b1580d2dc0 Automated Changelog (#3938) 2026-05-04 02:52:32 +00:00
Redrover1760 9cb45515cf Crash Fix (#3938) 2026-05-03 22:52:04 -04:00
monolith8319 6835481c97 Automated Changelog (#3932) 2026-05-03 16:38:57 +00:00
Redrover1760 236fcf75c5 Decreases Cerberus Range (#3932) 2026-05-03 12:38:28 -04:00
monolith8319 04a7bb42b5 Automated Changelog (#3935) 2026-05-03 16:37:37 +00:00
significant harassment fd6c0569d1 Fix Not Being Able to Unassign Normal Deeds (#3935)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-05-03 12:37:06 -04:00
monolith8319 4028405a5a Automated Changelog (#3933) 2026-05-03 09:39:54 +00:00
lopsidedmanboobs 919dc543e0 Rulechanges (#3933) 2026-05-03 09:39:22 +00:00
monolith8319 83b4f9a5d7 Automated Changelog (#3886) 2026-05-03 01:22:03 +00:00
significant harassment cbe7f6c3f5 Faction LPC Rework (#3886)
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: kasature90 <kasature.asakim@gmail.com>
2026-05-02 21:21:34 -04:00
monolith8319 9c9ad7248c Automated Changelog (#3926) 2026-05-03 00:06:26 +00:00
kasature90 dfbe98cbbb Nuke Separate Iconsmooth for Walls (#3926)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-05-02 20:05:54 -04:00
monolith8319 1dae5d84f5 Automated Changelog (#3928) 2026-05-02 22:22:00 +00:00
kasature90 07c99c87d7 ACTUALLY FIX GRAIN SHADER (#3928)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-05-02 18:21:30 -04:00
monolith8319 7944589905 Automated Changelog (#3887) 2026-05-02 14:41:59 +00:00
kasature90 690299e366 Buran NFR (#3887)
Co-authored-by: starch <starchpersonal@gmail.com>
Co-authored-by: UnicornOnLSD <149102472+UnicornOnLSD@users.noreply.github.com>
2026-05-02 10:41:24 -04:00
monolith8319 cbef529e94 Automated Changelog (#3923) 2026-05-02 14:22:18 +00:00
Maples c3f506e6aa Some Slang Additions (#3923)
Co-authored-by: Maples <just like ping me on discord>
2026-05-02 10:21:48 -04:00
monolith8319 dc7e3a8b0f Automated Changelog (#3924) 2026-05-02 07:39:08 +00:00
kasature90 00f103890e Fix an Issue with VHS Filter (#3924)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-05-02 03:38:38 -04:00
monolith8319 2b979960ad Automated Changelog (#3902) 2026-05-02 05:46:33 +00:00
kasature90 c83930bf2f Ultimate Silicon Pain Operation of 2026 (#3902)
Co-authored-by: evergreenBloom <ofthefathoms@gmail.com>
Co-authored-by: starch <starchpersonal@gmail.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-05-01 22:46:04 -07:00
Ed 8287d6a9bc CSP Rising: Revengeance (#3922) 2026-05-02 01:20:11 -04:00
monolith8319 5085871129 Automated Changelog (#3919) 2026-05-02 05:20:06 +00:00
Maples 17aed77cc0 Adds Corner Plating and Lattices to the RCD (#3919)
Co-authored-by: Maples <just like ping me on discord>
2026-05-02 01:19:39 -04:00
monolith8319 090b0d49db Automated Changelog (#3893) 2026-05-02 05:18:44 +00:00
Ark 4099cf5c5f [PORT] Ambient Occlusion + Observation Admin Tool (#3893)
Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Co-authored-by: Pieter-Jan Briers <pieterjan.briers+git@gmail.com>
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
2026-05-02 01:18:07 -04:00
Ark 662670d3eb [PORT] Minor Refactor AdminNameOverlay (#3895)
Co-authored-by: Schrödinger <132720404+schrodinger71@users.noreply.github.com>
Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com>
2026-05-02 01:17:45 -04:00
Redrover1760 ae3ad17500 Spritecomponent Warn Cleanups (#3911)
Co-authored-by: Tayrtahn <tayrtahn@gmail.com>
2026-04-29 20:30:46 -04:00
monolith8319 780bac284c Automated Changelog (#3867) 2026-04-29 23:04:54 +00:00
kasature90 0ec333ebed Copper & Lithium for Industry/Economy Rework (#3867)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-29 19:04:16 -04:00
monolith8319 33552b44a2 Automated Changelog (#3908) 2026-04-29 21:25:46 +00:00
significant harassment 87ab827f04 Structure IconSmooth Separation (#3908)
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-29 17:25:12 -04:00
Redrover1760 a7761b1742 Test Improvements (#3910) 2026-04-29 05:04:19 -04:00
monolith8319 3cdf4cf142 Automated Changelog (#3897) 2026-04-29 00:00:12 +00:00
kasature90 ffa0efdb7c Nuke Mask Flashprot (#3897)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-28 19:59:40 -04:00
monolith8319 2cdb0021ea Automated Changelog (#3872) 2026-04-28 22:55:21 +00:00
monolith8319 cc94e52caa Automated Changelog (#3881) 2026-04-28 22:54:51 +00:00
Corrupt 98e512fbba VG Jumpsuit Price Fix (#3872) 2026-04-28 18:54:38 -04:00
kasature90 d66474366c Heavy-Duty Airlocks (#3881)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-28 18:54:13 -04:00
monolith8319 30a736ec45 Automated Changelog (#3904) 2026-04-28 22:53:55 +00:00
Ilya246 49035ada5c Ship Slowdownening (#3904) 2026-04-28 18:53:11 -04:00
Mrrpip 36a752851c Typos: The Sequel. (#3905) 2026-04-28 18:52:56 -04:00
monolith8319 fcfb8feff6 Automated Changelog (#3907) 2026-04-28 22:12:08 +00:00
significant harassment cee5f2a57e Energy Shield Nerf + Smilodon Buff (#3907)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-28 18:11:33 -04:00
monolith8319 6cfedd70f2 Automated Changelog (#3898) 2026-04-28 22:09:33 +00:00
kasature90 57d41c69b2 Some TGMC Grenade Sprites. (#3898)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-28 18:08:56 -04:00
monolith8319 94dd84d1fd Automated Changelog (#3899) 2026-04-28 22:04:41 +00:00
Re-Atomized e9229f2946 Organ Harvesting (#3899) 2026-04-28 18:04:08 -04:00
Mrrpip d70855f08d A Couple of Typo Fixes (#3892)
Co-authored-by: kasature90 <kasature.asakim@gmail.com>
2026-04-26 17:42:02 -07:00
monolith8319 e7b11673dd Automated Changelog (#3894) 2026-04-27 00:08:21 +00:00
Ark 7d19340a0a Crystallizer GUI Fix (#3894) 2026-04-26 20:07:50 -04:00
monolith8319 217a647528 Automated Changelog (#3891) 2026-04-26 21:52:10 +00:00
kasature90 9b8604c8e9 Fix Parallax Being Default (#3891)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-26 17:51:41 -04:00
monolith8319 50a6912312 Automated Changelog (#3889) 2026-04-26 17:12:44 +00:00
kasature90 daf3801a89 Makes Reactor Explosions Big and Strong (#3889)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-26 13:12:16 -04:00
monolith8319 ebdc1c9f50 Automated Changelog (#3861) 2026-04-26 16:23:29 +00:00
Redrover1760 5c73fa2e64 Decreases ADC Piloting Boost (#3861) 2026-04-26 19:23:01 +03:00
monolith8319 5abadca6eb Automated Changelog (#3888) 2026-04-26 16:20:34 +00:00
tonotom1 7dbd1fc4ee Medchem Tweak (#3888) 2026-04-26 19:20:04 +03:00
Ilya246 69a088ab1c Prevent Hitscans From Spawning Phantom Entities (#3890) 2026-04-26 09:24:53 +00:00
monolith8319 11bd89ab29 Automated Changelog (#3787) 2026-04-26 01:37:34 +00:00
chuga-git 1c4707ef20 Ammo Loader Cartridge Fix (#3787) 2026-04-25 21:37:06 -04:00
monolith8319 e13dd8e284 Automated Changelog (#3738) 2026-04-26 01:35:01 +00:00
GOLDMASK990 c37fdece10 Ussp Remnant Description Update (#3738) 2026-04-25 21:34:30 -04:00
monolith8319 368f9f66eb Automated Changelog (#3882) 2026-04-26 01:33:21 +00:00
Ilya246 bb804fb4eb World Loader Speed Compensation (#3882) 2026-04-25 21:32:53 -04:00
monolith8319 687d0baf38 Automated Changelog (#3878) 2026-04-25 14:42:58 +00:00
kasature90 01c3731dd6 Fix Loadout Window Title (#3878)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-25 10:42:30 -04:00
monolith8319 07b89a7ddd Automated Changelog (#3838) 2026-04-25 09:28:27 +00:00
kasature90 a12713d824 Nuclear Fission Reactor Port (#3838)
Co-authored-by: Kyle Tyo <36606155+VerinSenpai@users.noreply.github.com>
Co-authored-by: starch <starchpersonal@gmail.com>
Co-authored-by: mogaiskii <sani.mog@gmail.com>
Co-authored-by: princess-gurchi <kharitonovich.m+pg@gmail.com>
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: significant harassment <moguls-charade4n@icloud.com>
2026-04-25 05:27:57 -04:00
Saint 827ac02e14 Fix EVA Suits (#3880) 2026-04-25 01:40:33 -04:00
monolith8319 643e335430 Automated Changelog (#3879) 2026-04-25 03:03:19 +00:00
lopsidedmanboobs 9fd9c8e0d3 I Hate Dating! (#3879) 2026-04-24 23:02:50 -04:00
monolith8319 751236b093 Automated Changelog (#3862) 2026-04-25 01:06:17 +00:00
Redrover1760 546ded6a0f Autopulser Tweaks (#3862) 2026-04-24 21:05:47 -04:00
monolith8319 93477eb322 Automated Changelog (#3849) 2026-04-25 01:05:36 +00:00
Redrover1760 65d50d7b46 Fixes Tarantula with Correct File (#3849) 2026-04-24 21:05:07 -04:00
significant harassment ceec2eda2f ShuttleVoucher Purchase Cooldown Functionality (#3435)
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com>
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
2026-04-24 21:03:11 -04:00
Diesel 1058c5e8c1 Hot Fix - More Access Changes (#3875)
Co-authored-by: kasature90 <kasature.asakim@gmail.com>
2026-04-24 21:01:06 -04:00
monolith8319 aea7d67573 Automated Changelog (#3876) 2026-04-24 19:27:30 +00:00
Redrover1760 4f26f02318 Removes Sultan Price (#3876) 2026-04-24 15:27:01 -04:00
monolith8319 905757742d Automated Changelog (#3870) 2026-04-24 18:13:45 +00:00
kasature90 9ea1fe9d58 Fix ASM-220s From Blowing Themselves Up (#3870)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-24 14:13:14 -04:00
monolith8319 788f30a40d Automated Changelog (#3874) 2026-04-24 15:27:39 +00:00
SixplyDev 6f6eeef1d2 Reduce Sultan Shotgun Sell Price (#3874) 2026-04-24 11:27:02 -04:00
monolith8319 fe335ba114 Automated Changelog (#3864) 2026-04-24 13:32:42 +00:00
Redrover1760 b7c39e31ca Flatpack Opening Uses Collision of Flatpacked Entity (#3864) 2026-04-24 16:32:11 +03:00
monolith8319 7dfbc4441e Automated Changelog (#3863) 2026-04-23 13:24:10 +00:00
Redrover1760 6ffca1c96d M92x Active Recharge Rate Buff (#3863) 2026-04-23 09:23:34 -04:00
monolith8319 f7394c02a3 Automated Changelog (#3866) 2026-04-23 09:55:09 +00:00
kasature90 8afd0a1ac0 Fix Shipyard Testfails (#3866)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-23 05:54:38 -04:00
monolith8319 51d1122dc2 Automated Changelog (#3203) 2026-04-23 01:50:14 +00:00
Loldaddy5676 343c6dd98c Ravager Updates/CC Sec and Overseer Modsuit Updates (#3203)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com>
2026-04-22 21:49:46 -04:00
monolith8319 17795e629d Automated Changelog (#3566) 2026-04-23 01:18:49 +00:00
Loldaddy5676 a906504c18 SEC Vessel Spitfire Has Been Added (#3566) 2026-04-22 21:18:14 -04:00
significant harassment e9bd143612 Industry Stuff Pt. 1 (#3858)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-22 20:25:45 -04:00
monolith8319 3f9487e507 Automated Changelog (#3859) 2026-04-23 00:17:43 +00:00
kasature90 dfb81dd5c4 Bunkbeds (#3859)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-22 20:17:13 -04:00
monolith8319 d1218ad15b Automated Changelog (#3860) 2026-04-23 00:01:46 +00:00
Redrover1760 5d82605e4c Retweaks Shield Balance (#3860) 2026-04-22 20:01:18 -04:00
monolith8319 ff856ddcbc Automated Changelog (#3703) 2026-04-22 23:15:49 +00:00
EckoAurum c14df33b47 Large Machine Frame Systems | Large Thruster Construction/Upgrading (#3703) 2026-04-22 19:15:17 -04:00
monolith8319 aa3aef3786 Automated Changelog (#3852) 2026-04-22 21:56:29 +00:00
kasature90 12ff1257f7 Shiptest Casing Sprites Too I Guess (#3852)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-22 17:56:00 -04:00
monolith8319 878fe89966 Automated Changelog (#3857) 2026-04-22 21:48:23 +00:00
Mrrpip bfcad4b309 Swaps Names for the Cargo Dravon and AK-570 Autoloader Crates (#3857) 2026-04-22 17:47:54 -04:00
monolith8319 89b7db6957 Automated Changelog (#3419) 2026-04-22 21:47:20 +00:00
EldritchPineapple 6c402241db Removed Xenos From Lancelots and Added a More... Exotic Threat. (#3419) 2026-04-22 17:46:51 -04:00
monolith8319 c228375dc1 Automated Changelog (#3856) 2026-04-22 18:20:37 +00:00
Redrover1760 1be5bc68d5 Fixes and Improves Flatpacks (#3856) 2026-04-22 14:19:59 -04:00
monolith8319 1122d4d30f Automated Changelog (#3853) 2026-04-22 15:23:24 +00:00
Redrover1760 bc0cd37fb6 Generation 0 Memory Optimizations (#3853). 2026-04-22 11:22:53 -04:00
monolith8319 182c63c87e Automated Changelog (#3854) 2026-04-22 13:26:16 +00:00
kasature90 a16b01982a T2-T5 Rad Blocking Stats (#3854)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-22 09:25:41 -04:00
monolith8319 260e34054d Automated Changelog (#3596) 2026-04-22 02:22:34 +00:00
Redrover1760 73b055c6cb Shield Fixing and Some Optimizations/Refactoring (#3596) 2026-04-21 22:22:06 -04:00
monolith8319 dea71210b0 Automated Changelog (#3848) 2026-04-21 22:24:46 +00:00
kasature90 17289457c0 Baystation Medkit & Portafib Sprites (#3848)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-21 18:24:15 -04:00
monolith8319 94d3092c33 Automated Changelog (#3851) 2026-04-21 02:21:29 +00:00
Redrover1760 66c9bd6455 Buffs PDV Bank (#3851) 2026-04-20 22:20:59 -04:00
TheRex191 154f242bb8 Meteor Docking Take 2 (#3817)
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
2026-04-20 20:45:34 +00:00
monolith8319 1e6bd607fc Automated Changelog (#3823) 2026-04-20 19:15:43 +00:00
significant harassment dd467907ec Let There Be Chaos (PDV Music Addition) (#3823)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-20 15:15:12 -04:00
monolith8319 9a8c753491 Automated Changelog (#3833) 2026-04-20 18:34:54 +00:00
Ilya246 d354f90036 More Ship Ai Improvements (#3833) 2026-04-20 18:34:15 +00:00
monolith8319 a33eb842f7 Automated Changelog (#3824) 2026-04-20 00:56:46 +00:00
Ilya246 d7ea4b55c4 Mech Wideswing (#3824) 2026-04-19 20:56:17 -04:00
monolith8319 a40ece4b7c Automated Changelog (#3828) 2026-04-20 00:51:51 +00:00
Ilya246 0b8062b471 Fix Projectile Penetration (#3828) 2026-04-19 20:51:23 -04:00
monolith8319 dadcdfc692 Automated Changelog (#3816) 2026-04-20 00:49:20 +00:00
Redrover1760 e4177484ca Makes Jumpsuit Signatures Require No Grid to Show (#3816) 2026-04-19 20:48:52 -04:00
monolith8319 5629674bbb Automated Changelog (#3831) 2026-04-20 00:48:25 +00:00
Mrrpip 253eb267d2 Adjusts the USSP and Winter Solstice Stamp. Also Adds a USSP TACCOM Stamp. (#3831)
Co-authored-by: kasature90 <kasature.asakim@gmail.com>
2026-04-19 20:47:56 -04:00
monolith8319 0650ef5f24 Automated Changelog (#3832) 2026-04-20 00:07:29 +00:00
kasature90 cf19d8ed54 General LMG/MMG Buffs/Rebalance (#3832)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-19 20:07:01 -04:00
monolith8319 ff66ed37dd Automated Changelog (#3842) 2026-04-20 00:06:50 +00:00
kasature90 e7f615461d Larpy Asteroid Names (#3842)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-19 20:06:18 -04:00
monolith8319 4ef14f0dce Automated Changelog (#3841) 2026-04-19 23:55:28 +00:00
Redrover1760 3296e8d2cd Readds Handheld Radio to Asakim Loadout (#3841) 2026-04-19 19:54:58 -04:00
monolith8319 8b78b737cd Automated Changelog (#3822) 2026-04-19 23:32:48 +00:00
significant harassment 3f7bfcb78e Armor Plate Rework + Slight Armor Tweaks (#3822)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-19 19:32:21 -04:00
monolith8319 fb1103c457 Automated Changelog (#3840) 2026-04-19 23:08:44 +00:00
Redrover1760 af2d45dd55 Fixes AI Interaction (#3840) 2026-04-19 19:08:15 -04:00
monolith8319 560e64c13c Automated Changelog (#3834) 2026-04-19 21:42:53 +00:00
Redrover1760 5054665057 Emp Emitter and Bullets Nerf (#3834) 2026-04-19 17:42:23 -04:00
kasature90 5fd442171f Greeble Stuff (#3835)
Co-authored-by: starch <starchpersonal@gmail.com>
2026-04-19 17:41:53 -04:00
monolith8319 8894d4e5ff Automated Changelog (#3827) 2026-04-19 02:04:51 +00:00
Mrrpip eef28959e2 Makes Osiris Inhands Visible. (#3827) 2026-04-18 22:04:23 -04:00
monolith8319 4ea83d389a Automated Changelog (#3821) 2026-04-18 15:48:18 +00:00
Diesel 02f74c26e9 Hot Fix - PTech Access Change (#3821) 2026-04-18 18:47:50 +03:00
monolith8319 c0ea5adf5d Automated Changelog (#3815) 2026-04-18 14:20:48 +00:00
significant harassment 6daf9f02e6 Various Ground Balancing Tweaks (#3815)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-18 10:20:18 -04:00
monolith8319 afabe747f2 Automated Changelog (#3818) 2026-04-18 14:18:17 +00:00
burrrkeye 699232f653 Asakim Breathe Air Update (#3818) 2026-04-18 10:17:50 -04:00
monolith8319 262aa8cd4b Automated Changelog (#3814) 2026-04-17 20:43:04 +00:00
significant harassment bcb9440046 TSFMC Guidebook Slop (#3814)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-17 16:42:34 -04:00
monolith8319 93bd8b8356 Automated Changelog (#3798) 2026-04-17 17:01:02 +00:00
Redrover1760 d14dd88149 Asakim Changes (#3798) 2026-04-17 13:00:31 -04:00
monolith8319 f520ddf911 Automated Changelog (#3812) 2026-04-17 16:56:49 +00:00
Corrupt c2ea1eaf94 Wyvern Tweaks! (#3812) 2026-04-17 12:56:15 -04:00
monolith8319 fc105b61fc Automated Changelog (#3811) 2026-04-17 14:52:46 +00:00
Ilya246 271c4e7efc Even More Ship HTN Upgrades (#3811) 2026-04-17 14:52:16 +00:00
monolith8319 a4138f4b11 Automated Changelog (#3809) 2026-04-17 00:42:16 +00:00
lopsidedmanboobs ff74cf3ae1 Brush up Rules (#3809) 2026-04-16 20:41:48 -04:00
monolith8319 663425a050 Automated Changelog (#3808) 2026-04-16 19:44:40 +00:00
NazrinNya b4e4a284bc De-WL Overseer (#3808)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-04-16 22:44:11 +03:00
monolith8319 3a8ac92ef8 Automated Changelog (#3807) 2026-04-16 19:32:57 +00:00
TheRex191 d3d3ab531e Gas Dock Not Docking, Now Docking. (#3807) 2026-04-16 22:32:26 +03:00
monolith8319 71cf5f42c7 Automated Changelog (#3454) 2026-04-16 18:28:15 +00:00
Diesel fce9e95910 Faction ID Console Compatibility and Fixes Company Banks (#3454)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-16 21:27:44 +03:00
monolith8319 ee337596d6 Automated Changelog (#3804) 2026-04-16 16:36:28 +00:00
NazrinNya a2b3ddecdf Components Adjustment, RCD Buff. (#3804)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-04-16 19:35:53 +03:00
monolith8319 0939533dc1 Automated Changelog (#3531) 2026-04-16 16:19:14 +00:00
Mrrpip 42f6f53be9 Sentinel Fuel Locker Hotfix. (#3531) 2026-04-16 19:18:43 +03:00
monolith8319 2651c00cfd Automated Changelog (#3538) 2026-04-16 16:17:32 +00:00
Lily Autumn a462d09fb9 Yip Woof Bark (#3538) 2026-04-16 19:16:57 +03:00
monolith8319 ce617c185e Automated Changelog (#3781) 2026-04-16 16:04:47 +00:00
doublechest 131c5e79a5 Auto Disable Jetpack when Entering Grid with Enabled Magboots (#3781)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-16 19:04:09 +03:00
monolith8319 86c9a843b1 Automated Changelog (#3794) 2026-04-16 15:55:59 +00:00
Ssar’keth of the First Clutch dd910f1550 Add Radar Mode Toggles and UI Injection Wrapper (#3794)
Co-authored-by: Ssar'keth of the First Clutch <ziz11@users.noreply.github.com>
Co-authored-by: Ilya246 <ilyukarno@gmail.com>
2026-04-16 18:55:16 +03:00
monolith8319 cd8d59c095 Automated Changelog (#3803) 2026-04-16 15:24:48 +00:00
Redrover1760 20bc7af2a4 Unbreaks Yowie Randomized Last Names (#3803) 2026-04-16 18:24:17 +03:00
monolith8319 65ea89b673 Automated Changelog (#3789) 2026-04-16 12:46:01 +00:00
Ilya246 44445f3d2a Explosions Tempbuff (#3789)
Co-authored-by: significant harassment <moguls-charade4n@icloud.com>
2026-04-16 12:45:27 +00:00
monolith8319 648cc99aff Automated Changelog (#3800) 2026-04-15 22:26:41 +00:00
Redrover1760 e8c2b72aa0 Dewhitelists Faction AIs (#3800) 2026-04-15 18:26:12 -04:00
monolith8319 7d85dbca00 Automated Changelog (#3799) 2026-04-15 20:57:28 +00:00
Redrover1760 85e0cb1c76 CPRing Now Checks for Unrevivable + CPR Revivals Now Notify Ghost (#3799) 2026-04-15 16:56:57 -04:00
monolith8319 3c32d2f6f5 Automated Changelog (#3792) 2026-04-15 20:16:30 +00:00
Redrover1760 9d8d0b9016 Ensures Mechs Can't Be Entered if Broken (#3792) 2026-04-15 16:15:59 -04:00
monolith8319 02e72a4c1d Automated Changelog (#3795) 2026-04-15 10:37:26 +00:00
PunishedJoe 5fac554802 Fixes Balaclava and Neck Layers (#3795) 2026-04-15 19:36:56 +09:00
Redrover1760 bc62bf3700 Close Prs on Master (#3793) 2026-04-14 19:55:45 -04:00
monolith8319 accb1404b0 Automated Changelog (#3276) 2026-04-14 21:02:17 +00:00
Redrover1760 326440aa22 Tarantula Revamp (#3276)
Co-authored-by: UnicornOnLSD <149102472+UnicornOnLSD@users.noreply.github.com>
2026-04-14 17:01:36 -04:00
monolith8319 b4fe4ab5e7 Automated Changelog (#3785) 2026-04-14 19:17:32 +00:00
Redrover1760 8ff52fcd7b Infinite FMC/DC Contra Lathe Fix (#3785) 2026-04-14 15:16:58 -04:00
monolith8319 e8a620e044 Automated Changelog (#3788) 2026-04-14 16:21:49 +00:00
chuga-git 9ea80d6405 Revert "Fix Projectile Raycast" (#3788) 2026-04-14 12:21:16 -04:00
monolith8319 abec847ca3 Automated Changelog (#3790) 2026-04-14 13:51:22 +00:00
Ilya246 36214087e9 Fix Thermal Signatures (#3790) 2026-04-14 13:50:47 +00:00
monolith8319 2b4f4f2ba0 Automated Changelog (#3736) 2026-04-13 18:31:43 +00:00
Mrrpip 258bc12f16 WOE, YE FIEND. STAMPS UPON YE. (#3736)
Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com>
2026-04-13 21:31:11 +03:00
monolith8319 d73cf7aeaf Automated Changelog (#3033) 2026-04-13 18:14:00 +00:00
thoughtlessuser b1e200408d Polaris Re-Evil-Ening (#3033)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-13 21:13:27 +03:00
monolith8319 95227750d9 Automated Changelog (#3710) 2026-04-13 18:03:18 +00:00
chuga-git 34f71b59b9 Buffs "Entropy" Electron Torpedo Speed (#3710)
Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com>
2026-04-13 21:02:19 +03:00
monolith8319 547420d4bc Automated Changelog (#3441) 2026-04-13 17:44:48 +00:00
Mrrpip 510434fb65 Unlimited Mil-Fabs and Servers in the USSPvend (#3441) 2026-04-13 20:43:55 +03:00
monolith8319 862ce8352c Automated Changelog (#3484) 2026-04-13 17:43:20 +00:00
significant harassment 552ab5718a Large APC (#3484)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-13 20:42:50 +03:00
monolith8319 2bacec206b Automated Changelog (#3576) 2026-04-13 17:28:12 +00:00
Kiithnaras bd8ae0ec9b Medicine Improvement Attempt (#3576)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-13 20:27:35 +03:00
monolith8319 6ba17c6e1b Automated Changelog (#3702) 2026-04-13 17:10:50 +00:00
Ghost581 50d7de7818 Bolt Down Camelot's Military Assembler + Fax + Silo (#3702) 2026-04-13 20:10:17 +03:00
monolith8319 02348e780e Automated Changelog (#3712) 2026-04-13 15:47:27 +00:00
tonotom1 80cc460d69 Repairable CC Turret (#3712) 2026-04-13 18:46:56 +03:00
monolith8319 e95a87a1d8 Automated Changelog (#3713) 2026-04-13 15:46:07 +00:00
Redrover1760 78ecd13c0a Scythe Nerfs (#3713) 2026-04-13 18:45:38 +03:00
Ghost581 9eb4c8333c Fix some Wording in VG's Description (#3724) 2026-04-13 18:42:24 +03:00
monolith8319 882b1acae7 Automated Changelog (#3732) 2026-04-13 15:40:49 +00:00
Ilya246 df39f7bd6d Turret Shootthrough (#3732) 2026-04-13 18:40:17 +03:00
monolith8319 c68d329dd2 Automated Changelog (#3733) 2026-04-13 15:39:50 +00:00
Svarshik 5a2341407b Modsuits Yml Cleanup and Minor Buff (#3733) 2026-04-13 18:39:20 +03:00
monolith8319 b4b49016d9 Automated Changelog (#3741) 2026-04-13 15:25:57 +00:00
Redrover1760 53a40c6c3c Black Cloth Wraps (#3741) 2026-04-13 18:25:26 +03:00
monolith8319 fbf93d50be Automated Changelog (#3751) 2026-04-13 15:01:14 +00:00
Corrupt 68144857e8 RSC Ship Tweaks (#3751) 2026-04-13 18:00:42 +03:00
monolith8319 820a70c695 Automated Changelog (#3754) 2026-04-13 14:57:51 +00:00
Corrupt bff7436490 RS-2 Malandro (#3754) 2026-04-13 17:57:19 +03:00
monolith8319 2b76b0a55a Automated Changelog (#3761) 2026-04-13 14:40:32 +00:00
ToastEnjoyer fa61e959fb Fix Vox Neck Displacements (#3761) 2026-04-13 17:40:01 +03:00
ondrej008 b427f087b0 Adds Missing Locale Strings (#3775) 2026-04-13 17:19:10 +03:00
monolith8319 7a8b90339f Automated Changelog (#3780) 2026-04-13 14:16:14 +00:00
iramoye cecc52bff9 Adds Maintenance Jack to 'Expanding and Repairing Station' Guidebook (#3780) 2026-04-13 17:15:41 +03:00
monolith8319 378592a0ba Automated Changelog (#3767) 2026-04-13 12:55:55 +00:00
Corrupt 89dff2ec03 Remove Flashpoint (#3767) 2026-04-13 14:55:26 +02:00
monolith8319 4faad89b0c Automated Changelog (#3778) 2026-04-13 09:48:48 +00:00
lopsidedmanboobs 8f8b94862a I HATE ALL MEDICS (#3778) 2026-04-13 11:48:20 +02:00
monolith8319 7c29b33d27 Automated Changelog (#3766) 2026-04-12 16:17:24 +00:00
doublechest 8180e5d436 Increase Cargo Order Capacity From 20 to 60 in the Trade Mall (#3766) 2026-04-12 12:16:54 -04:00
monolith8319 e081dd59c6 Automated Changelog (#3765) 2026-04-12 10:10:37 +00:00
Ilya246 295ef88a95 Fix Poi Ftling (#3765) 2026-04-12 10:10:05 +00:00
monolith8319 39292553b3 Automated Changelog (#3764) 2026-04-12 09:27:34 +00:00
Ilya246 2652423c4d Fix New Ship Speed (#3764) 2026-04-12 09:27:05 +00:00
monolith8319 fb836a0ee7 Automated Changelog (#3756) 2026-04-11 20:32:31 +00:00
Ilya246 7a63aba489 Fix Projectile Raycast (#3756) 2026-04-11 16:32:01 -04:00
monolith8319 7ec10d578c Automated Changelog (#3753) 2026-04-11 15:45:44 +00:00
Ilya246 91f4eb7b51 More Gun Fixes (#3753) 2026-04-11 15:45:12 +00:00
Ssar’keth of the First Clutch e552e2498f Add Compass and Azimuth Ring to Shuttle Radar (#3737)
Co-authored-by: Ssar'keth of the First Clutch <ziz11@users.noreply.github.com>
2026-04-11 12:01:06 +00:00
Ilya246 ef34f255bf Also Fix Borg Mass Scanner (#3750) 2026-04-11 11:43:08 +00:00
monolith8319 0f3390aba8 Automated Changelog (#3749) 2026-04-11 08:48:00 +00:00
Ilya246 25033ae9c1 Shipgun Aiming Hotfix (#3749) 2026-04-11 08:47:32 +00:00
monolith8319 d32fd9dd0a Automated Changelog (#3748) 2026-04-11 06:05:33 +00:00
Ilya246 728c10974e Fix Scanner Rotation Stuff (#3748) 2026-04-11 06:05:04 +00:00
monolith8319 50c5891b4e Automated Changelog (#3746) 2026-04-10 18:58:54 +00:00
Ilya246 51818f6dc3 Companies Fix (#3746) 2026-04-10 18:58:20 +00:00
monolith8319 dcaefa16d7 Automated Changelog (#3745) 2026-04-10 15:17:15 +00:00
Ilya246 15936634b1 Increase Shiphtn Grid Evasion Buffer (#3745) 2026-04-10 15:16:43 +00:00
monolith8319 574bb25522 Automated Changelog (#3705) 2026-04-10 14:50:16 +00:00
Ilya246 1125715a3f No Gunnery Rotation (#3705)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-10 14:49:46 +00:00
monolith8319 9e3a270988 Automated Changelog (#3711) 2026-04-10 12:39:36 +00:00
Antoine bf1a02bfcb Fixed Radio Highlighting (#3711) 2026-04-10 21:39:07 +09:00
monolith8319 57c59e3684 Automated Changelog (#3586) 2026-04-10 12:25:32 +00:00
tonotom1 0e31d3885f Plant Analyzer Friction Deletion (#3586) 2026-04-10 15:25:02 +03:00
monolith8319 27b05a3b75 Automated Changelog (#3743) 2026-04-10 10:19:13 +00:00
Ilya246 f99e36e101 Ship Htn Hotfix (#3743) 2026-04-10 10:18:45 +00:00
monolith8319 5a17e7781f Automated Changelog (#3731) 2026-04-09 21:23:34 +00:00
significant harassment 98f8d334a1 Gun-Based Damage Modifier Port (#3731)
Co-authored-by: VMSolidus <evilexecutive@gmail.com>
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-09 17:23:05 -04:00
monolith8319 c9bba3367d Automated Changelog (#3528) 2026-04-09 18:21:59 +00:00
Redrover1760 4ac8f937fb 12 Gauge Buff (#3528) 2026-04-09 14:21:28 -04:00
monolith8319 9648f54fc3 Automated Changelog (#3735) 2026-04-09 17:40:58 +00:00
Redrover1760 33e0bf8293 Gun Health Oversights (#3735) 2026-04-09 13:40:26 -04:00
monolith8319 81ed57e9fb Automated Changelog (#3734) 2026-04-09 17:23:09 +00:00
Redrover1760 0943abba3e Lying Down Makes Projectiles Sometimes Miss You (#3734) 2026-04-09 13:22:37 -04:00
monolith8319 886752cd7a Automated Changelog (#3698) 2026-04-09 16:17:45 +00:00
Ilya246 f8ba24d3f1 Make Shuttle Speedcap Slowdown Instead (#3698) 2026-04-09 19:17:10 +03:00
EckoAurum 1765c7d506 Company Commands for Offline Users (#3721) 2026-04-10 00:57:26 +09:00
monolith8319 16f40a5289 Automated Changelog (#3579) 2026-04-09 15:19:41 +00:00
Ghost581 b80fb6d554 No More Crawling for Non-Targetted Evasion (#3579)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-09 15:19:10 +00:00
Redrover1760 e1932935fe Decreases Thruster SRD Cost (#3729) 2026-04-08 20:33:33 -04:00
monolith8319 5aa06e8fc0 Automated Changelog (#3728) 2026-04-08 23:37:12 +00:00
Redrover1760 914c5529ec Minor Spelling Mistake (#3726) 2026-04-08 19:36:54 -04:00
Ilya246 f406be9a5d 2x Drone Targeting Range (#3728) 2026-04-08 19:36:44 -04:00
monolith8319 002ba09258 Automated Changelog (#3723) 2026-04-07 18:36:29 +00:00
Ilya246 f89e18df88 Kill Gunnery Server Thermal Sigs (#3723) 2026-04-07 14:35:47 -04:00
monolith8319 a7dcf1afea Automated Changelog (#3720) 2026-04-07 01:44:26 +00:00
EckoAurum 670785fd48 HotFix - AI Control Round Remove (#3720) 2026-04-06 21:43:58 -04:00
Ilya246 654a657788 Hotfix Company Owner Setting (#3714) 2026-04-06 18:01:33 +00:00
monolith8319 52137b967a Automated Changelog (#3716) 2026-04-06 17:16:14 +00:00
Redrover1760 8e0bff68cb Siebel Blast Doors Start Open (#3716) 2026-04-06 13:15:41 -04:00
monolith8319 af99be86ed Automated Changelog (#3707) 2026-04-06 15:53:43 +00:00
significant harassment 3ed4f7cc1a Fmc/Dc Value Buff (#3707)
Co-authored-by: Onezero0 <onezero0@outlook.com>
2026-04-06 11:53:14 -04:00
Ilya246 e72a06c19d emergency postgres migration fix 2026-04-06 16:42:30 +04:00
monolith8319 6619538fb9 Automated Changelog (#3709) 2026-04-05 22:14:21 +00:00
Redrover1760 005950f2a0 Hristov Structrual Buff (#3709) 2026-04-05 18:13:51 -04:00
monolith8319 468be1435c Automated Changelog (#3334) 2026-04-05 21:43:24 +00:00
Redrover1760 685422209e Increases 57mm Explosive Damage (#3334)
Co-authored-by: NazrinNya <137837419+NazrinNya@users.noreply.github.com>
2026-04-05 17:42:55 -04:00
monolith8319 ae58c4d021 Automated Changelog (#3075) 2026-04-05 21:35:16 +00:00
significant harassment 464975916d Kino Weapons Update - Finished (#3075)
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-05 17:34:46 -04:00
monolith8319 2f39b9edce Automated Changelog (#3708) 2026-04-05 20:31:10 +00:00
10KE a18831a0c5 Update Shift Duration in Guidebook (#3708) 2026-04-05 16:30:35 -04:00
monolith8319 42dcb56e22 Automated Changelog (#3706) 2026-04-05 18:43:20 +00:00
Ilya246 3eeb83a16e Kill Nf Console Depower Autoanchor (#3706) 2026-04-05 18:42:53 +00:00
monolith8319 abf8bd7218 Automated Changelog (#3583) 2026-04-05 18:34:22 +00:00
Redrover1760 c6de815de4 Refactors and Depowercreeps Shipgun HP (#3583) 2026-04-05 14:33:51 -04:00
Maxim Bychkov 53b02b5830 Unhardcode Companies Whitelist (#3553)
Co-authored-by: Ilya246 <ilyukarno@gmail.com>
2026-04-05 18:32:46 +00:00
monolith8319 40d2f70e08 Automated Changelog (#3691) 2026-04-05 18:23:35 +00:00
Redrover1760 1791e631f7 Adjusts ADS Spawntable and Decreases Wyvern Spawnrate (#3691) 2026-04-05 14:23:06 -04:00
monolith8319 f79b69a3f6 Automated Changelog (#3688) 2026-04-05 18:04:46 +00:00
Redrover1760 b8d5ca7022 Scythe Firerate Nerf (#3688) 2026-04-05 14:03:54 -04:00
monolith8319 f37ef8c994 Automated Changelog (#3489) 2026-04-05 14:22:53 +00:00
Corrupt b4ae011b87 RSC-Serkesh (#3489)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
Co-authored-by: UnicornOnLSD <149102472+UnicornOnLSD@users.noreply.github.com>
2026-04-05 16:21:51 +02:00
monolith8319 0adcb3c8a4 Automated Changelog (#3696) 2026-04-05 14:00:48 +00:00
NazrinNya ad1a263596 Lawbringer Port [Bounty] (#3696)
Co-authored-by: Centronias <me@centronias.com>
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-04-05 17:00:18 +03:00
monolith8319 591a6ba333 Automated Changelog (#3699) 2026-04-05 12:21:54 +00:00
Ilya246 ebf2e56159 Hotfix New Drone Targeting (#3699) 2026-04-05 12:21:26 +00:00
monolith8319 c6b36bf126 Automated Changelog (#3697) 2026-04-05 10:31:02 +00:00
NazrinNya 2cd30797ac Revert "Stinky Trait (#594)" (#3697)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-04-05 13:30:34 +03:00
monolith8319 2aad60a3d7 Automated Changelog (#3693) 2026-04-05 06:49:30 +00:00
Ilya246 f561fcb45e Make Drones Target People in Space (#3693) 2026-04-05 06:49:03 +00:00
monolith8319 a68c0d48ba Automated Changelog (#3521) 2026-04-04 21:45:28 +00:00
Mrrpip ec2c5a521d Shortens the Names of Missile Launchers. (#3521)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-04 17:44:56 -04:00
monolith8319 39efe6f747 Automated Changelog (#3592) 2026-04-04 21:38:41 +00:00
Redrover1760 0816b4457c Changes Jetpack Stealth to Detection Radius (#3592) 2026-04-04 17:38:13 -04:00
monolith8319 6c87da3dc3 Automated Changelog (#3535) 2026-04-04 20:50:20 +00:00
Ghost581 05d4b2eebd Viper Group Tech Tree and Jugsuit Slowdown Alleviation + More YAML Roadrage (#3535) 2026-04-04 16:49:50 -04:00
monolith8319 6ee1e7d8f3 Automated Changelog (#3658) 2026-04-04 19:54:13 +00:00
EckoAurum f097148d99 Ai Control Port (#3658) 2026-04-04 19:53:42 +00:00
monolith8319 ef2ce6565d Automated Changelog (#3686) 2026-04-04 13:44:43 +00:00
EckoAurum 98ad67497b Fix Mechlag (Hopefully) (#3686) 2026-04-04 13:44:14 +00:00
monolith8319 67644068b6 Automated Changelog (#3332) 2026-04-04 11:15:08 +00:00
TheScienceOfKSP 1b64380dbb SEC KS-P Pacem Class Corvette (#3332)
Co-authored-by: UnicornOnLSD <149102472+UnicornOnLSD@users.noreply.github.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
Co-authored-by: Corrupt <compeldominik6@gmail.com>
2026-04-04 13:14:40 +02:00
monolith8319 465006f16b Automated Changelog (#3685) 2026-04-04 03:50:48 +00:00
Redrover1760 e1ec12fb3e Updates Datafarm Descriptions (#3685) 2026-04-03 23:50:20 -04:00
Redrover1760 d62e3a42d0 Lazy Yaml Slop Fix (#3683) 2026-04-03 21:21:06 -04:00
Redrover1760 02be1ef282 Merge branch 'main' of https://github.com/Monolith-Station/Monolith 2026-04-03 20:48:51 -04:00
Redrover1760 85c4a0c9ec fixes grilles protecting biomass 2026-04-03 20:48:12 -04:00
monolith8319 baeb53ee82 Automated Changelog (#3578) 2026-04-03 18:29:42 +00:00
Redrover1760 933dc75c33 Buffs Standard Borg HP, Removes Negative Burn Armor (#3578) 2026-04-03 21:29:10 +03:00
monolith8319 fc45f016ee Automated Changelog (#3602) 2026-04-03 18:28:53 +00:00
Ilya246 6a719f7c39 Adc Piloting Boost (#3602) 2026-04-03 21:28:23 +03:00
monolith8319 d83db373fe Automated Changelog (#3667) 2026-04-03 18:27:01 +00:00
monolith8319 ccec7894c6 Automated Changelog (#3680) 2026-04-03 18:26:27 +00:00
Redrover1760 3319b5f0d3 Makes Tsf Mech Blips Lighter (#3667) 2026-04-03 21:26:26 +03:00
EldritchPineapple 81d9ae37e3 Medicated Suture and Meshes Buff (#3680) 2026-04-03 21:25:56 +03:00
monolith8319 1990f2a272 Automated Changelog (#3636) 2026-04-03 17:46:40 +00:00
Kiithnaras 3665639420 Laser Weapon Improvements (#3636) 2026-04-03 20:46:11 +03:00
monolith8319 20e2ceb0db Automated Changelog (#3673) 2026-04-03 17:45:43 +00:00
chuga-git 84a116fd27 Torpedo/GPO Speed Buffs (#3673) 2026-04-03 20:45:12 +03:00
monolith8319 c3c402d380 Automated Changelog (#3681) 2026-04-03 17:43:07 +00:00
Redrover1760 92e8677e82 Research Datafarms Buffs (#3681) 2026-04-03 20:42:36 +03:00
monolith8319 65365135e3 Automated Changelog (#3285) 2026-04-03 11:09:38 +00:00
monolith8319 62e372c9d2 Automated Changelog (#3195) 2026-04-03 11:09:08 +00:00
NazrinNya 4279c1977d Hullmods (#3285)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-04-03 14:08:54 +03:00
NazrinNya 6406a7cf4f Non-POI Vends Are Now Free. (#3195)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
Co-authored-by: UnicornOnLSD <149102472+UnicornOnLSD@users.noreply.github.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-04-03 14:08:39 +03:00
monolith8319 806e22e8b4 Automated Changelog (#3679) 2026-04-03 09:51:27 +00:00
EckoAurum c2934d0bd7 Fix Borg Hand Whitelist Bug (#3679) 2026-04-03 09:50:59 +00:00
monolith8319 5316f1fdd9 Automated Changelog (#3678) 2026-04-03 09:04:37 +00:00
Ilya246 66fed8d2a2 Kill Shield Ramming (#3678) 2026-04-03 12:04:09 +03:00
monolith8319 28e3584a67 Automated Changelog (#3677) 2026-04-02 18:53:22 +00:00
Ilya246 78c94ddbc4 Far Reaches IV, New Drone (#3677) 2026-04-02 20:52:53 +02:00
monolith8319 cef95a03bf Automated Changelog (#3559) 2026-04-02 17:25:51 +00:00
Ilya246 bd3a35ee64 New Drone: Bracket, Refactor Drone Yaml a Bit (#3559) 2026-04-02 17:25:02 +00:00
Redrover1760 0b50c919e4 Stops Sloppy Vgroid Debug/Localhost Crashing (#3597) 2026-04-02 10:37:55 -04:00
monolith8319 afcf994545 Automated Changelog (#3672) 2026-04-01 22:09:20 +00:00
UnicornOnLSD 5aaee26a09 Adds a Single OREBOX to the DIS Rig (#3672) 2026-04-02 00:08:49 +02:00
monolith8319 fb3c49bd09 Automated Changelog (#3635) 2026-04-01 16:22:35 +00:00
Redrover1760 2c381cbcd0 Makes Biomass Less Painful to Deal With (#3635) 2026-04-01 12:22:03 -04:00
Ilya246 f4e3ac2478 Fix Logspam 2 (#3661) 2026-03-31 09:12:58 -04:00
Anri 31de4af5e2 Remove Repeat Work in ThermalSignatureSystem (#3631)
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
2026-03-31 11:28:47 +00:00
Ilya246 5e67e63032 Fix Server Log Spam (#3657) 2026-03-31 06:46:54 +00:00
2946 changed files with 113816 additions and 60314 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ jobs:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
- name: Get Engine version
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
- name: Setup Submodule
run: |
@@ -38,7 +38,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 10.0.x
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
- name: Setup Submodule
run: |
@@ -38,7 +38,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 10.0.x
+1 -1
View File
@@ -14,6 +14,6 @@ jobs:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
- name: Check for CRLF
run: Tools/check_crlf.py
+12 -12
View File
@@ -10,18 +10,18 @@ jobs:
if: ${{github.head_ref == 'master' || github.head_ref == 'main' || github.head_ref == 'develop'}}
steps:
# - uses: superbrothers/close-pull-request@v3
# with:
# comment: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch."
- uses: superbrothers/close-pull-request@v3
with:
comment: "Thank you for contributing to the Monolith repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch."
# If you prefer to just comment on the pr and not close it, uncomment the bellow and comment the above
- uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: ${{ github.event.number }},
owner: context.repo.owner,
repo: context.repo.repo,
body: "Thank you for contributing to the Space Station 14 repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name` and resetting the master branch. \n\n This pr won't be automatically closed. However, a maintainer may close it for this reason."
})
#- uses: actions/github-script@v7
# with:
# script: |
# github.rest.issues.createComment({
# issue_number: ${{ github.event.number }},
# owner: context.repo.owner,
# repo: context.repo.repo,
# body: "Thank you for contributing to the Monolith repository. Unfortunately, it looks like you submitted your pull request from the master branch. We suggest you follow [our git usage documentation](https://docs.spacestation14.com/en/general-development/setup/git-for-the-ss14-developer.html) \n\n You can move your current work from the master branch to another branch by doing `git branch <branch_name>` and resetting the master branch. \n\n This pr won't be automatically closed. However, a maintainer may close it for this reason."
# })
+3 -3
View File
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
- name: Setup Submodule
run: |
@@ -35,9 +35,9 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install Dependencies
run: dotnet restore
+3 -3
View File
@@ -13,13 +13,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Get Engine Tag
run: |
+2 -2
View File
@@ -15,11 +15,11 @@ jobs:
- name: Install dependencies
run: sudo apt-get install -y python3-paramiko python3-lxml
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
with:
submodules: 'recursive'
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 10.0.x
+1 -1
View File
@@ -15,7 +15,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
- name: Get changed files
id: files
+2 -2
View File
@@ -38,7 +38,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v3.6.0
uses: actions/checkout@v4.2.2
- name: Setup Submodule
run: |
@@ -53,7 +53,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 10.0.x
+1 -1
View File
@@ -13,7 +13,7 @@ jobs:
if: github.repository == 'Monolith-Station/Monolith' # Mono: space-wizards/space-station-14 > Monolith-Station/Monolith
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
with:
ref: main
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
name: Validate RSIs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
- name: Setup Submodule
run: git submodule update --init
- name: Pull engine updates
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.6.0
- uses: actions/checkout@v4.2.2
- name: Setup submodule
run: |
git submodule update --init --recursive
@@ -28,7 +28,7 @@ jobs:
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v3.2.0
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 10.0.x
- name: Install dependencies
@@ -51,6 +51,8 @@ internal sealed class AdminNameOverlay : Overlay
//TODO make this adjustable via GUI
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
var playTime = _config.GetCVar(CCVars.AdminOverlayPlaytime);
var startingJob = _config.GetCVar(CCVars.AdminOverlayStartingJob);
foreach (var playerInfo in _system.PlayerList)
{
@@ -77,25 +79,50 @@ internal sealed class AdminNameOverlay : Overlay
}
var uiScale = _userInterfaceManager.RootControl.UIScale;
var lineoffset = new Vector2(0f, 11f) * uiScale;
var lineoffset = new Vector2(0f, 14f) * uiScale;
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
var balance = playerInfo.Balance == int.MinValue ? "NO BALANCE" : BankSystemExtensions.ToCurrencyString(playerInfo.Balance); // Frontier
var currentOffset = Vector2.Zero;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
currentOffset += lineoffset;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
currentOffset += lineoffset;
var balance = playerInfo.Balance == int.MinValue
? "NO BALANCE"
: BankSystemExtensions.ToCurrencyString(playerInfo.Balance);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, $"Balance: {balance}", uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
currentOffset += lineoffset;
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && playTime)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White);
currentOffset += lineoffset;
}
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && startingJob)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White);
currentOffset += lineoffset;
}
if (classic && playerInfo.Antag)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 3), _antagLabelClassic, uiScale, _antagColorClassic); // Frontier: 2<3
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, _antagLabelClassic, uiScale, Color.OrangeRed);
currentOffset += lineoffset;
}
else if (!classic && _filter.Contains(playerInfo.RoleProto))
{
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var color = playerInfo.RoleProto.Color;
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 3), label, uiScale, color); // Frontier: 2<3
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, label, uiScale, color);
currentOffset += lineoffset;
}
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates + lineoffset * 2, $"Balance: {balance}", uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White); // Frontier
}
}
}
@@ -0,0 +1,20 @@
<Control
xmlns="https://spacestation14.io"
xmlns:viewport="clr-namespace:Content.Client.Viewport"
MouseFilter="Stop">
<PanelContainer StyleClasses="BackgroundDark" Name="AdminCameraWindowRoot" Access="Public">
<BoxContainer Orientation="Vertical" Access="Public">
<!-- Camera -->
<Control VerticalExpand="True" Name="CameraViewBox">
<viewport:ScalingViewport Name="CameraView"
MinSize="100 100"
MouseFilter="Ignore" />
</Control>
<!-- Controller buttons -->
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5">
<Button StyleClasses="OpenRight" Name="FollowButton" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-follow'}" />
<Button StyleClasses="OpenLeft" Name="PopControl" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-pop-out'}" />
</BoxContainer>
</BoxContainer>
</PanelContainer>
</Control>
@@ -0,0 +1,101 @@
using System.Numerics;
using Content.Client.Eye;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.Timing;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client.Administration.UI.AdminCamera;
[GenerateTypedNameReferences]
public sealed partial class AdminCameraControl : Control
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IClientGameTiming _timing = default!;
public event Action? OnFollow;
public event Action? OnPopoutControl;
private readonly EyeLerpingSystem _eyeLerpingSystem;
private readonly FixedEye _defaultEye = new();
private AdminCameraEuiState? _nextState;
private const float MinimumZoom = 0.1f;
private const float MaximumZoom = 2.0f;
public EntityUid? CurrentCamera;
public float Zoom = 1.0f;
public bool IsPoppedOut;
public AdminCameraControl()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_eyeLerpingSystem = _entManager.System<EyeLerpingSystem>();
CameraView.Eye = _defaultEye;
FollowButton.OnPressed += _ => OnFollow?.Invoke();
PopControl.OnPressed += _ => OnPopoutControl?.Invoke();
CameraView.OnResized += OnResized;
}
private new void OnResized()
{
var width = Math.Max(CameraView.PixelWidth, (int)Math.Floor(CameraView.MinWidth));
var height = Math.Max(CameraView.PixelHeight, (int)Math.Floor(CameraView.MinHeight));
CameraView.ViewportSize = new Vector2i(width, height);
}
protected override void MouseWheel(GUIMouseWheelEventArgs args)
{
base.MouseWheel(args);
if (CameraView.Eye == null)
return;
Zoom = Math.Clamp(Zoom - args.Delta.Y * 0.15f * Zoom, MinimumZoom, MaximumZoom);
CameraView.Eye.Zoom = new Vector2(Zoom, Zoom);
args.Handle();
}
public void SetState(AdminCameraEuiState state)
{
_nextState = state;
}
// I know that this is awful, but I copied this from the solution editor anyways.
// This is needed because EUIs update before the gamestate is applied, which means it will fail to get the uid from the net entity.
// The suggestion from the comment in the solution editor saying to use a BUI is not ideal either:
// - We would need to bind the UI to an entity, but with how BUIs currently work we cannot open it in the same tick as we spawn that entity on the server.
// - We want the UI opened by the user session, not by their currently attached entity. Otherwise it would close in cases where admins move from one entity to another, for example when ghosting.
protected override void FrameUpdate(FrameEventArgs args)
{
if (_nextState == null || _timing.LastRealTick < _nextState.Tick) // make sure the last gamestate has been applied
return;
if (!_entManager.TryGetEntity(_nextState.Camera, out var cameraUid))
return;
if (CurrentCamera == null)
{
_eyeLerpingSystem.AddEye(cameraUid.Value);
CurrentCamera = cameraUid;
}
else if (CurrentCamera != cameraUid)
{
_eyeLerpingSystem.RemoveEye(CurrentCamera.Value);
_eyeLerpingSystem.AddEye(cameraUid.Value);
CurrentCamera = cameraUid;
}
if (_entManager.TryGetComponent<EyeComponent>(CurrentCamera, out var eye))
CameraView.Eye = eye.Eye ?? _defaultEye;
}
}
@@ -0,0 +1,117 @@
using System.Numerics;
using Content.Client.Eui;
using Content.Shared.Administration;
using Content.Shared.Eui;
using JetBrains.Annotations;
using Robust.Client.UserInterface.Controls;
namespace Content.Client.Administration.UI.AdminCamera;
/// <summary>
/// Admin Eui for opening a viewport window to observe entities.
/// Use the "Open Camera" admin verb or the "camera" command to open.
/// </summary>
[UsedImplicitly]
public sealed partial class AdminCameraEui : BaseEui
{
private readonly AdminCameraWindow _window;
private readonly AdminCameraControl _control;
// If not null the camera is in "popped out" mode and is in an external window.
private OSWindow? _OSWindow;
// The last location the window was located at in game.
// Is used for getting knowing where to "pop in" external windows.
private Vector2 _lastLocation;
public AdminCameraEui()
{
_window = new AdminCameraWindow();
_control = new AdminCameraControl();
_window.Contents.AddChild(_control);
_control.OnFollow += () => SendMessage(new AdminCameraFollowMessage());
_window.OnClose += () =>
{
if (!_control.IsPoppedOut)
SendMessage(new CloseEuiMessage());
};
_control.OnPopoutControl += () =>
{
if (_control.IsPoppedOut)
PopIn();
else
PopOut();
};
}
// Pop the window out into an external OS window
private void PopOut()
{
_lastLocation = _window.Position;
// TODO: When there is a way to have a minimum window size, enforce something!
_OSWindow = new OSWindow
{
SetSize = _window.Size,
Title = _window.Title ?? Loc.GetString("admin-camera-window-title-placeholder"),
};
_OSWindow.Show();
if (_OSWindow.Root == null)
return;
_control.Orphan();
_OSWindow.Root.AddChild(_control);
_OSWindow.Closed += () =>
{
if (_control.IsPoppedOut)
SendMessage(new CloseEuiMessage());
};
_control.IsPoppedOut = true;
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-in");
_window.Close();
}
// Pop the window back into the in game window.
private void PopIn()
{
_control.Orphan();
_window.Contents.AddChild(_control);
_window.Open(_lastLocation);
_control.IsPoppedOut = false;
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-out");
_OSWindow?.Close();
_OSWindow = null;
}
public override void Opened()
{
base.Opened();
_window.OpenCentered();
}
public override void Closed()
{
base.Closed();
_window.Close();
}
public override void HandleState(EuiStateBase baseState)
{
if (baseState is not AdminCameraEuiState state)
return;
_window.SetState(state);
_control.SetState(state);
}
}
@@ -0,0 +1,6 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc admin-camera-window-title-placeholder}"
SetSize="425 550"
MinSize="200 225"
Name="Window">
</DefaultWindow>
@@ -0,0 +1,23 @@
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Administration.UI.AdminCamera;
[GenerateTypedNameReferences]
public sealed partial class AdminCameraWindow : DefaultWindow
{
public AdminCameraWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
ContentsContainer.Margin = new Thickness(5, 0, 5, 0);
}
public void SetState(AdminCameraEuiState state)
{
Title = Loc.GetString("admin-camera-window-title", ("name", state.Name));
}
}
@@ -30,6 +30,7 @@
<Button Name="FreezeAndMuteToggleButton" Text="{Loc player-panel-freeze-and-mute}" Disabled="True"/>
<Button Name="BanButton" Text="{Loc player-panel-ban}" Disabled="True"/>
<controls:ConfirmButton Name="RejuvenateButton" Text="{Loc player-panel-rejuvenate}" Disabled="True"/>
<Button Name="CameraButton" Text="{Loc player-panel-camera}" Disabled="True"/>
</GridContainer>
<Button Name="JobWhitelistsButton" Text="{Loc player-panel-job-whitelists}" SetWidth="136" SetHeight="27" Disabled="True"/> <!-- DeltaV: Job whitelists -->
</BoxContainer>
@@ -19,6 +19,7 @@ public sealed partial class PlayerPanel : FancyWindow
public event Action<NetUserId?>? OnOpenBans;
public event Action<NetUserId?>? OnAhelp;
public event Action<string?>? OnKick;
public event Action<string?>? OnCamera;
public event Action<NetUserId?>? OnOpenBanPanel;
public event Action<NetUserId?, bool>? OnWhitelistToggle;
public event Action? OnFreezeAndMuteToggle;
@@ -40,6 +41,7 @@ public sealed partial class PlayerPanel : FancyWindow
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
CameraButton.OnPressed += _ => OnCamera?.Invoke(TargetUsername);
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
@@ -132,5 +134,6 @@ public sealed partial class PlayerPanel : FancyWindow
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
JobWhitelistsButton.Disabled = !_adminManager.HasFlag(AdminFlags.Whitelist); // DeltaV
CameraButton.Disabled = !_adminManager.CanCommand("camera");
}
}
@@ -25,6 +25,7 @@ public sealed class PlayerPanelEui : BaseEui
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
// Kick command does not support GUIDs
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
PlayerPanel.OnCamera += username => _console.ExecuteCommand($"camera \"{username}\"");
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
@@ -8,6 +8,8 @@ namespace Content.Client.AlertLevel;
public sealed class AlertLevelDisplaySystem : EntitySystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
@@ -21,26 +23,26 @@ public sealed class AlertLevelDisplaySystem : EntitySystem
{
return;
}
var layer = args.Sprite.LayerMapReserveBlank(AlertLevelDisplay.Layer);
var layer = _sprite.LayerMapReserve((uid, args.Sprite), AlertLevelDisplay.Layer);
if (args.AppearanceData.TryGetValue(AlertLevelDisplay.Powered, out var poweredObject))
{
args.Sprite.LayerSetVisible(layer, poweredObject is true);
_sprite.LayerSetVisible((uid, args.Sprite), layer, poweredObject is true);
}
if (!args.AppearanceData.TryGetValue(AlertLevelDisplay.CurrentLevel, out var level))
{
args.Sprite.LayerSetState(layer, alertLevelDisplay.AlertVisuals.Values.First());
_sprite.LayerSetRsiState((uid, args.Sprite), layer, alertLevelDisplay.AlertVisuals.Values.First());
return;
}
if (alertLevelDisplay.AlertVisuals.TryGetValue((string) level, out var visual))
if (alertLevelDisplay.AlertVisuals.TryGetValue((string)level, out var visual))
{
args.Sprite.LayerSetState(layer, visual);
_sprite.LayerSetRsiState((uid, args.Sprite), layer, visual);
}
else
{
args.Sprite.LayerSetState(layer, alertLevelDisplay.AlertVisuals.Values.First());
_sprite.LayerSetRsiState((uid, args.Sprite), layer, alertLevelDisplay.AlertVisuals.Values.First());
}
}
}
+13 -12
View File
@@ -12,6 +12,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -64,7 +65,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
visualState = JukeboxVisualState.On;
}
UpdateAppearance(uid, visualState, component, sprite);
UpdateAppearance((uid, sprite), visualState, component);
}
private void OnAppearanceChange(EntityUid uid, JukeboxComponent component, ref AppearanceChangeEvent args)
@@ -78,25 +79,25 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
visualState = JukeboxVisualState.On;
}
UpdateAppearance(uid, visualState, component, args.Sprite);
UpdateAppearance((uid, args.Sprite), visualState, component);
}
private void UpdateAppearance(EntityUid uid, JukeboxVisualState visualState, JukeboxComponent component, SpriteComponent sprite)
private void UpdateAppearance(Entity<SpriteComponent> entity, JukeboxVisualState visualState, JukeboxComponent component)
{
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
SetLayerState(JukeboxVisualLayers.Base, component.OffState, entity);
switch (visualState)
{
case JukeboxVisualState.On:
SetLayerState(JukeboxVisualLayers.Base, component.OnState, sprite);
SetLayerState(JukeboxVisualLayers.Base, component.OnState, entity);
break;
case JukeboxVisualState.Off:
SetLayerState(JukeboxVisualLayers.Base, component.OffState, sprite);
SetLayerState(JukeboxVisualLayers.Base, component.OffState, entity);
break;
case JukeboxVisualState.Select:
PlayAnimation(uid, JukeboxVisualLayers.Base, component.SelectState, 1.0f, sprite);
PlayAnimation(entity.Owner, JukeboxVisualLayers.Base, component.SelectState, 1.0f, entity);
break;
}
}
@@ -109,7 +110,7 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
if (!_animationPlayer.HasRunningAnimation(uid, state))
{
var animation = GetAnimation(layer, state, animationTime);
sprite.LayerSetVisible(layer, true);
_sprite.LayerSetVisible((uid, sprite), layer, true);
_animationPlayer.Play(uid, animation, state);
}
}
@@ -133,13 +134,13 @@ public sealed class JukeboxSystem : SharedJukeboxSystem
};
}
private void SetLayerState(JukeboxVisualLayers layer, string? state, SpriteComponent sprite)
private void SetLayerState(JukeboxVisualLayers layer, string? state, Entity<SpriteComponent> sprite)
{
if (string.IsNullOrEmpty(state))
return;
sprite.LayerSetVisible(layer, true);
sprite.LayerSetAutoAnimated(layer, true);
sprite.LayerSetState(layer, state);
_sprite.LayerSetVisible(sprite.AsNullable(), layer, true);
_sprite.LayerSetAutoAnimated(sprite.AsNullable(), layer, true);
_sprite.LayerSetRsiState(sprite.AsNullable(), layer, state);
}
}
@@ -1,11 +1,14 @@
using Content.Client.Botany.Components;
using Content.Shared.Botany;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Botany;
public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVisualsComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
@@ -17,8 +20,8 @@ public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVi
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
sprite.LayerMapReserveBlank(PlantHolderLayers.Plant);
sprite.LayerSetVisible(PlantHolderLayers.Plant, false);
_sprite.LayerMapReserve((uid, sprite), PlantHolderLayers.Plant);
_sprite.LayerSetVisible((uid, sprite), PlantHolderLayers.Plant, false);
}
protected override void OnAppearanceChange(EntityUid uid, PlantHolderVisualsComponent component, ref AppearanceChangeEvent args)
@@ -31,12 +34,12 @@ public sealed class PlantHolderVisualizerSystem : VisualizerSystem<PlantHolderVi
{
var valid = !string.IsNullOrWhiteSpace(state);
args.Sprite.LayerSetVisible(PlantHolderLayers.Plant, valid);
_sprite.LayerSetVisible((uid, args.Sprite), PlantHolderLayers.Plant, valid);
if (valid)
{
args.Sprite.LayerSetRSI(PlantHolderLayers.Plant, rsi);
args.Sprite.LayerSetState(PlantHolderLayers.Plant, state);
_sprite.LayerSetRsi((uid, args.Sprite), PlantHolderLayers.Plant, new ResPath(rsi));
_sprite.LayerSetRsiState((uid, args.Sprite), PlantHolderLayers.Plant, state);
}
}
}
+5 -4
View File
@@ -14,6 +14,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
[Dependency] private readonly RotationVisualizerSystem _rotationVisualizerSystem = default!;
[Dependency] private readonly IEyeManager _eye = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -73,11 +74,11 @@ internal sealed class BuckleSystem : SharedBuckleSystem
{
// This will only assign if empty, it won't get overwritten by new depth on multiple calls, which do happen easily
buckle.OriginalDrawDepth ??= buckledSprite.DrawDepth;
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
_sprite.SetDrawDepth((buckledEntity, buckledSprite), strapSprite.DrawDepth - 1);
}
else if (buckle.OriginalDrawDepth.HasValue)
{
buckledSprite.DrawDepth = buckle.OriginalDrawDepth.Value;
_sprite.SetDrawDepth((buckledEntity, buckledSprite), buckle.OriginalDrawDepth.Value);
buckle.OriginalDrawDepth = null;
}
}
@@ -101,7 +102,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
return;
ent.Comp.OriginalDrawDepth ??= buckledSprite.DrawDepth;
buckledSprite.DrawDepth = strapSprite.DrawDepth - 1;
_sprite.SetDrawDepth((ent.Owner, buckledSprite), strapSprite.DrawDepth - 1);
}
/// <summary>
@@ -115,7 +116,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
if (!ent.Comp.OriginalDrawDepth.HasValue)
return;
buckledSprite.DrawDepth = ent.Comp.OriginalDrawDepth.Value;
_sprite.SetDrawDepth((ent.Owner, buckledSprite), ent.Comp.OriginalDrawDepth.Value);
ent.Comp.OriginalDrawDepth = null;
}
@@ -50,7 +50,7 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
if (args.Sprite == null)
return;
if (!args.Sprite.LayerMapTryGet(component.Layer, out var fillLayer))
if (!SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.Layer, out var fillLayer, false))
return;
var maxFillLevels = component.MaxFillLevels;
@@ -68,9 +68,9 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
}
if (component.Metamorphic)
{
if (args.Sprite.LayerMapTryGet(component.BaseLayer, out var baseLayer))
if (SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.BaseLayer, out var baseLayer, false))
{
var hasOverlay = args.Sprite.LayerMapTryGet(component.OverlayLayer, out var overlayLayer);
var hasOverlay = SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.OverlayLayer, out var overlayLayer, false);
if (AppearanceSystem.TryGetData<string>(uid, SolutionContainerVisuals.BaseOverride,
out var baseOverride,
@@ -80,35 +80,35 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
if (reagentProto?.MetamorphicSprite is { } sprite)
{
args.Sprite.LayerSetSprite(baseLayer, sprite);
SpriteSystem.LayerSetSprite((uid, args.Sprite), baseLayer, sprite);
if (reagentProto.MetamorphicMaxFillLevels > 0)
{
args.Sprite.LayerSetVisible(fillLayer, true);
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
maxFillLevels = reagentProto.MetamorphicMaxFillLevels;
fillBaseName = reagentProto.MetamorphicFillBaseName;
changeColor = reagentProto.MetamorphicChangeColor;
fillSprite = sprite;
}
else
args.Sprite.LayerSetVisible(fillLayer, false);
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, false);
if (hasOverlay)
args.Sprite.LayerSetVisible(overlayLayer, false);
SpriteSystem.LayerSetVisible((uid, args.Sprite), overlayLayer, false);
}
else
{
args.Sprite.LayerSetVisible(fillLayer, true);
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
if (hasOverlay)
args.Sprite.LayerSetVisible(overlayLayer, true);
SpriteSystem.LayerSetVisible((uid, args.Sprite), overlayLayer, true);
if (component.MetamorphicDefaultSprite != null)
args.Sprite.LayerSetSprite(baseLayer, component.MetamorphicDefaultSprite);
SpriteSystem.LayerSetSprite((uid, args.Sprite), baseLayer, component.MetamorphicDefaultSprite);
}
}
}
}
else
{
args.Sprite.LayerSetVisible(fillLayer, true);
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, true);
}
var closestFillSprite = ContentHelpers.RoundToLevels(fraction, 1, maxFillLevels + 1);
@@ -120,25 +120,25 @@ public sealed class SolutionContainerVisualsSystem : VisualizerSystem<SolutionCo
var stateName = fillBaseName + closestFillSprite;
if (fillSprite != null)
args.Sprite.LayerSetSprite(fillLayer, fillSprite);
args.Sprite.LayerSetState(fillLayer, stateName);
SpriteSystem.LayerSetSprite((uid, args.Sprite), fillLayer, fillSprite);
SpriteSystem.LayerSetRsiState((uid, args.Sprite), fillLayer, stateName);
if (changeColor && AppearanceSystem.TryGetData<Color>(uid, SolutionContainerVisuals.Color, out var color, args.Component))
args.Sprite.LayerSetColor(fillLayer, color);
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, color);
else
args.Sprite.LayerSetColor(fillLayer, Color.White);
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, Color.White);
}
else
{
if (component.EmptySpriteName == null)
args.Sprite.LayerSetVisible(fillLayer, false);
SpriteSystem.LayerSetVisible((uid, args.Sprite), fillLayer, false);
else
{
args.Sprite.LayerSetState(fillLayer, component.EmptySpriteName);
SpriteSystem.LayerSetRsiState((uid, args.Sprite), fillLayer, component.EmptySpriteName);
if (changeColor)
args.Sprite.LayerSetColor(fillLayer, component.EmptySpriteColor);
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, component.EmptySpriteColor);
else
args.Sprite.LayerSetColor(fillLayer, Color.White);
SpriteSystem.LayerSetColor((uid, args.Sprite), fillLayer, Color.White);
}
}
+7 -5
View File
@@ -4,12 +4,14 @@ using Content.Shared.Cuffs.Components;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
using Robust.Shared.GameStates;
using Robust.Shared.Utility;
namespace Content.Client.Cuffs;
public sealed class CuffableSystem : SharedCuffableSystem
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -22,7 +24,7 @@ public sealed class CuffableSystem : SharedCuffableSystem
private void OnCuffableShutdown(EntityUid uid, CuffableComponent component, ComponentShutdown args)
{
if (TryComp<SpriteComponent>(uid, out var sprite))
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, false);
_sprite.LayerSetVisible((uid, sprite), HumanoidVisualLayers.Handcuffs, false);
}
private void OnCuffableHandleState(EntityUid uid, CuffableComponent component, ref ComponentHandleState args)
@@ -39,22 +41,22 @@ public sealed class CuffableSystem : SharedCuffableSystem
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
var cuffed = cuffState.NumHandsCuffed > 0;
sprite.LayerSetVisible(HumanoidVisualLayers.Handcuffs, cuffed);
_sprite.LayerSetVisible((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffed);
// if they are not cuffed, that means that we didn't get a valid color,
// iconstate, or RSI. that also means we don't need to update the sprites.
if (!cuffed)
return;
sprite.LayerSetColor(HumanoidVisualLayers.Handcuffs, cuffState.Color!.Value);
_sprite.LayerSetColor((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffState.Color!.Value);
if (!Equals(component.CurrentRSI, cuffState.RSI) && cuffState.RSI != null) // we don't want to keep loading the same RSI
{
component.CurrentRSI = cuffState.RSI;
sprite.LayerSetState(HumanoidVisualLayers.Handcuffs, cuffState.IconState, component.CurrentRSI);
_sprite.LayerSetRsi((uid, sprite), _sprite.LayerMapGet((uid, sprite), HumanoidVisualLayers.Handcuffs), new ResPath(component.CurrentRSI), cuffState.IconState);
}
else
{
sprite.LayerSetState(HumanoidVisualLayers.Handcuffs, cuffState.IconState);
_sprite.LayerSetRsiState((uid, sprite), HumanoidVisualLayers.Handcuffs, cuffState.IconState);
}
}
}
+7 -6
View File
@@ -11,6 +11,7 @@ public sealed class DoorSystem : SharedDoorSystem
{
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -85,7 +86,7 @@ public sealed class DoorSystem : SharedDoorSystem
state = DoorState.Closed;
if (AppearanceSystem.TryGetData<string>(entity, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
UpdateSpriteLayers(args.Sprite, baseRsi);
UpdateSpriteLayers((entity.Owner, args.Sprite), baseRsi);
if (_animationSystem.HasRunningAnimation(entity, DoorComponent.AnimationKey))
_animationSystem.Stop(entity.Owner, DoorComponent.AnimationKey);
@@ -95,21 +96,21 @@ public sealed class DoorSystem : SharedDoorSystem
private void UpdateAppearanceForDoorState(Entity<DoorComponent> entity, SpriteComponent sprite, DoorState state)
{
sprite.DrawDepth = state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth;
_sprite.SetDrawDepth((entity.Owner, sprite), state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth);
switch (state)
{
case DoorState.Open:
foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates)
{
sprite.LayerSetState(layer, layerState);
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
}
return;
case DoorState.Closed:
foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates)
{
sprite.LayerSetState(layer, layerState);
_sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState);
}
return;
@@ -138,7 +139,7 @@ public sealed class DoorSystem : SharedDoorSystem
}
}
private void UpdateSpriteLayers(SpriteComponent sprite, string baseRsi)
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string baseRsi)
{
if (!_resourceCache.TryGetResource<RSIResource>(SpriteSpecifierSerializer.TextureRoot / baseRsi, out var res))
{
@@ -146,6 +147,6 @@ public sealed class DoorSystem : SharedDoorSystem
return;
}
sprite.BaseRSI = res.RSI;
_sprite.SetBaseRsi(sprite.AsNullable(), res.RSI);
}
}
@@ -11,6 +11,7 @@ namespace Content.Client.Electrocution;
public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<ElectrocutionHUDVisualsComponent>
{
[Dependency] private readonly IPlayerManager _playerMan = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -53,15 +54,12 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
private void ShowHUD()
{
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
while (electrifiedQuery.MoveNext(out var uid, out _, out var appearanceComp, out var spriteComp))
{
if (!AppearanceSystem.TryGetData<bool>(uid, ElectrifiedVisuals.IsElectrified, out var electrified, appearanceComp))
continue;
if (electrified)
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, true);
else
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
_sprite.LayerSetVisible((uid, spriteComp), ElectrifiedLayers.HUD, electrified);
}
}
@@ -70,10 +68,9 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
private void RemoveHUD()
{
var electrifiedQuery = AllEntityQuery<ElectrocutionHUDVisualsComponent, AppearanceComponent, SpriteComponent>();
while (electrifiedQuery.MoveNext(out var uid, out var _, out var appearanceComp, out var spriteComp))
while (electrifiedQuery.MoveNext(out var uid, out _, out _, out var spriteComp))
{
spriteComp.LayerSetVisible(ElectrifiedLayers.HUD, false);
_sprite.LayerSetVisible((uid, spriteComp), ElectrifiedLayers.HUD, false);
}
}
@@ -87,9 +84,6 @@ public sealed class ElectrocutionHUDVisualizerSystem : VisualizerSystem<Electroc
return;
var player = _playerMan.LocalEntity;
if (electrified && HasComp<ShowElectrocutionHUDComponent>(player))
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, true);
else
args.Sprite.LayerSetVisible(ElectrifiedLayers.HUD, false);
_sprite.LayerSetVisible((uid, args.Sprite), ElectrifiedLayers.HUD, electrified && HasComp<ShowElectrocutionHUDComponent>(player));
}
}
@@ -1,12 +1,14 @@
using Content.Shared.Ensnaring;
using Content.Shared.Ensnaring.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Ensnaring;
public sealed class EnsnareableSystem : SharedEnsnareableSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -19,25 +21,25 @@ public sealed class EnsnareableSystem : SharedEnsnareableSystem
{
base.OnEnsnareInit(ent, ref args);
if(!TryComp<SpriteComponent>(ent.Owner, out var sprite))
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite))
return;
// TODO remove this, this should just be in yaml.
sprite.LayerMapReserveBlank(EnsnaredVisualLayers.Ensnared);
_sprite.LayerMapReserve((ent.Owner, sprite), EnsnaredVisualLayers.Ensnared);
}
private void OnAppearanceChange(EntityUid uid, EnsnareableComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null || !args.Sprite.LayerMapTryGet(EnsnaredVisualLayers.Ensnared, out var layer))
if (args.Sprite == null || !_sprite.LayerMapTryGet((uid, args.Sprite), EnsnaredVisualLayers.Ensnared, out var layer, false))
return;
if (_appearance.TryGetData<bool>(uid, EnsnareableVisuals.IsEnsnared, out var isEnsnared, args.Component))
{
if (component.Sprite != null)
{
args.Sprite.LayerSetRSI(layer, component.Sprite);
args.Sprite.LayerSetState(layer, component.State);
args.Sprite.LayerSetVisible(layer, isEnsnared);
_sprite.LayerSetRsi((uid, args.Sprite), layer, new ResPath(component.Sprite));
_sprite.LayerSetRsiState((uid, args.Sprite), layer, component.State);
_sprite.LayerSetVisible((uid, args.Sprite), layer, isEnsnared);
}
}
}
+3
View File
@@ -38,6 +38,7 @@ using Robust.Shared.Prototypes;
using Robust.Shared.Replays;
using Robust.Shared.Timing;
using Content.Client._NF.Emp.Overlays; // Frontier
using Content.Client._Mono.Company; // Mono
namespace Content.Client.Entry
{
@@ -75,6 +76,7 @@ namespace Content.Client.Entry
[Dependency] private readonly DebugMonitorManager _debugMonitorManager = default!;
[Dependency] private readonly TitleWindowManager _titleWindowManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
public override void Init()
{
@@ -137,6 +139,7 @@ namespace Content.Client.Entry
_extendedDisconnectInformation.Initialize();
_jobRequirements.Initialize();
_playbackMan.Initialize();
_companyManager.Initialize();
//AUTOSCALING default Setup!
_configManager.SetCVar("interface.resolutionAutoScaleUpperCutoffX", 1080);
+8 -7
View File
@@ -10,6 +10,7 @@ namespace Content.Client.Fluids;
public sealed class PuddleSystem : SharedPuddleSystem
{
[Dependency] private readonly IconSmoothSystem _smooth = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -27,7 +28,7 @@ public sealed class PuddleSystem : SharedPuddleSystem
if (args.AppearanceData.TryGetValue(PuddleVisuals.CurrentVolume, out var volumeObj))
{
volume = (float) volumeObj;
volume = (float)volumeObj;
}
// Update smoothing and sprite based on volume.
@@ -35,19 +36,19 @@ public sealed class PuddleSystem : SharedPuddleSystem
{
if (volume < LowThreshold)
{
args.Sprite.LayerSetState(0, $"{smooth.StateBase}a");
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}a");
_smooth.SetEnabled(uid, false, smooth);
}
else if (volume < MediumThreshold)
{
args.Sprite.LayerSetState(0, $"{smooth.StateBase}b");
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}b");
_smooth.SetEnabled(uid, false, smooth);
}
else
{
if (!smooth.Enabled)
{
args.Sprite.LayerSetState(0, $"{smooth.StateBase}0");
_sprite.LayerSetRsiState((uid, args.Sprite), 0, $"{smooth.StateBase}0");
_smooth.SetEnabled(uid, true, smooth);
_smooth.DirtyNeighbours(uid);
}
@@ -58,12 +59,12 @@ public sealed class PuddleSystem : SharedPuddleSystem
if (args.AppearanceData.TryGetValue(PuddleVisuals.SolutionColor, out var colorObj))
{
var color = (Color) colorObj;
args.Sprite.Color = color * baseColor;
var color = (Color)colorObj;
_sprite.SetColor((uid, args.Sprite), color * baseColor);
}
else
{
args.Sprite.Color *= baseColor;
_sprite.SetColor((uid, args.Sprite), args.Sprite.Color * baseColor);
}
}
@@ -0,0 +1,90 @@
using Robust.Client.Graphics;
namespace Content.Client.Graphics;
/// <summary>
/// A cache for <see cref="Overlay"/>s to store per-viewport render resources, such as render targets.
/// </summary>
/// <typeparam name="T">The type of data stored in the cache.</typeparam>
public sealed class OverlayResourceCache<T> : IDisposable where T : class, IDisposable
{
private readonly Dictionary<long, CacheEntry> _cache = new();
/// <summary>
/// Get the data for a specific viewport, creating a new entry if necessary.
/// </summary>
/// <remarks>
/// The cached data may be cleared at any time if <see cref="IClydeViewport.ClearCachedResources"/> gets invoked.
/// </remarks>
/// <param name="viewport">The viewport for which to retrieve cached data.</param>
/// <param name="factory">A delegate used to create the cached data, if necessary.</param>
public T GetForViewport(IClydeViewport viewport, Func<IClydeViewport, T> factory)
{
return GetForViewport(viewport, out _, factory);
}
/// <summary>
/// Get the data for a specific viewport, creating a new entry if necessary.
/// </summary>
/// <remarks>
/// The cached data may be cleared at any time if <see cref="IClydeViewport.ClearCachedResources"/> gets invoked.
/// </remarks>
/// <param name="viewport">The viewport for which to retrieve cached data.</param>
/// <param name="wasCached">True if the data was pulled from cache, false if it was created anew.</param>
/// <param name="factory">A delegate used to create the cached data, if necessary.</param>
public T GetForViewport(IClydeViewport viewport, out bool wasCached, Func<IClydeViewport, T> factory)
{
if (_cache.TryGetValue(viewport.Id, out var entry))
{
wasCached = true;
return entry.Data;
}
wasCached = false;
entry = new CacheEntry
{
Data = factory(viewport),
Viewport = new WeakReference<IClydeViewport>(viewport),
};
_cache.Add(viewport.Id, entry);
viewport.ClearCachedResources += ViewportOnClearCachedResources;
return entry.Data;
}
private void ViewportOnClearCachedResources(ClearCachedViewportResourcesEvent ev)
{
if (!_cache.Remove(ev.ViewportId, out var entry))
{
// I think this could theoretically happen if you manually dispose the cache *after* a leaked viewport got
// GC'd, but before its ClearCachedResources got invoked.
return;
}
entry.Data.Dispose();
if (ev.Viewport != null)
ev.Viewport.ClearCachedResources -= ViewportOnClearCachedResources;
}
public void Dispose()
{
foreach (var entry in _cache)
{
if (entry.Value.Viewport.TryGetTarget(out var viewport))
viewport.ClearCachedResources -= ViewportOnClearCachedResources;
entry.Value.Data.Dispose();
}
_cache.Clear();
}
private struct CacheEntry
{
public T Data;
public WeakReference<IClydeViewport> Viewport;
}
}
+7 -5
View File
@@ -19,6 +19,7 @@ using Robust.Shared.Containers;
using Robust.Shared.GameStates;
using Robust.Shared.Player;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Hands.Systems
{
@@ -30,6 +31,7 @@ namespace Content.Client.Hands.Systems
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly StrippableSystem _stripSys = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
[Dependency] private readonly DisplacementMapSystem _displacement = default!;
@@ -330,7 +332,7 @@ namespace Content.Client.Hands.Systems
{
foreach (var key in revealedLayers)
{
sprite.RemoveLayer(key);
_sprite.RemoveLayer((uid, sprite), key);
}
revealedLayers.Clear();
@@ -366,7 +368,7 @@ namespace Content.Client.Hands.Systems
continue;
}
var index = sprite.LayerMapReserveBlank(key);
var index = _sprite.LayerMapReserve((uid, sprite), key);
// In case no RSI is given, use the item's base RSI as a default. This cuts down on a lot of unnecessary yaml entries.
if (layerData.RsiPath == null
@@ -374,12 +376,12 @@ namespace Content.Client.Hands.Systems
&& sprite[index].Rsi == null)
{
if (TryComp<ItemComponent>(held, out var itemComponent) && itemComponent.RsiPath != null)
sprite.LayerSetRSI(index, itemComponent.RsiPath);
_sprite.LayerSetRsi((uid, sprite), index, new ResPath(itemComponent.RsiPath));
else if (TryComp(held, out SpriteComponent? clothingSprite))
sprite.LayerSetRSI(index, clothingSprite.BaseRSI);
_sprite.LayerSetRsi((uid, sprite), index, clothingSprite.BaseRSI);
}
sprite.LayerSetData(index, layerData);
_sprite.LayerSetData((uid, sprite), index, layerData);
//Add displacement maps
if (handComp.HandDisplacement is not null)
+2
View File
@@ -24,6 +24,7 @@ using Content.Shared.Administration.Managers;
using Content.Shared.Chat;
using Content.Shared.Players.PlayTimeTracking;
using Content.Shared.Players.RateLimiting;
using Content.Client._Mono.Company; // Mono
namespace Content.Client.IoC
{
@@ -60,6 +61,7 @@ namespace Content.Client.IoC
collection.Register<PlayerRateLimitManager>();
collection.Register<SharedPlayerRateLimitManager, PlayerRateLimitManager>();
collection.Register<TitleWindowManager>();
collection.Register<CompanyManager>(); // Mono
}
}
}
@@ -30,6 +30,7 @@ public sealed class AfterLightTargetOverlay : Overlay
return;
var lightOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
var lightRes = lightOverlay.GetCachedForViewport(args.Viewport);
var bounds = args.WorldBounds;
// at 1-1 render scale it's mostly fine but at 4x4 it's way too fkn big
@@ -38,7 +39,7 @@ public sealed class AfterLightTargetOverlay : Overlay
var localMatrix =
viewport.LightRenderTarget.GetWorldToLocalMatrix(viewport.Eye, newScale);
var diff = (lightOverlay.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
var diff = (lightRes.EnlargedLightTarget.Size - viewport.LightRenderTarget.Size);
var halfDiff = diff / 2;
// Pixels -> Metres -> Half distance.
@@ -53,7 +54,7 @@ public sealed class AfterLightTargetOverlay : Overlay
viewport.LightRenderTarget.Size.Y + halfDiff.Y);
worldHandle.SetTransform(localMatrix);
worldHandle.DrawTextureRectRegion(lightOverlay.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
worldHandle.DrawTextureRectRegion(lightRes.EnlargedLightTarget.Texture, bounds, subRegion: subRegion);
}, Color.Transparent);
}
}
@@ -0,0 +1,140 @@
using System.Numerics;
using Content.Shared.CCVar;
using Content.Shared.Maps;
using Robust.Client.Graphics;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Light;
/// <summary>
/// Applies ambient-occlusion to the viewport.
/// </summary>
public sealed class AmbientOcclusionOverlay : Overlay
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly ITileDefinitionManager _tileDefManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowEntities;
private IRenderTexture? _aoTarget;
// Couldn't figure out a way to avoid this so if you can then please do.
private IRenderTexture? _aoStencilTarget;
public AmbientOcclusionOverlay()
{
IoCManager.InjectDependencies(this);
ZIndex = AfterLightTargetOverlay.ContentZIndex + 1;
}
protected override void Draw(in OverlayDrawArgs args)
{
/*
* tl;dr
* - we draw a black square on each "ambient occlusion" entity.
* - we blur this.
* - We apply it to the viewport.
*
* We do this while ignoring lighting because it will wash out the actual effect.
* In 3D ambient occlusion is more complicated due top having to calculate normals but in 2D
* we don't have a concept of depth / corners necessarily.
*/
var viewport = args.Viewport;
var mapId = args.MapId;
var worldBounds = args.WorldBounds;
var worldHandle = args.WorldHandle;
var color = Color.FromHex(_cfgManager.GetCVar(CCVars.AmbientOcclusionColor));
var distance = _cfgManager.GetCVar(CCVars.AmbientOcclusionDistance);
//var color = Color.Red;
var target = viewport.RenderTarget;
var lightScale = target.Size / (Vector2) viewport.Size;
var scale = viewport.RenderScale / (Vector2.One / lightScale);
var maps = _entManager.System<SharedMapSystem>();
var lookups = _entManager.System<EntityLookupSystem>();
var query = _entManager.System<OccluderSystem>();
var xformSystem = _entManager.System<SharedTransformSystem>();
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
if (_aoTarget?.Texture.Size != target.Size)
{
_aoTarget?.Dispose();
_aoTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-target");
}
if (_aoStencilTarget?.Texture.Size != target.Size)
{
_aoStencilTarget?.Dispose();
_aoStencilTarget = _clyde.CreateRenderTarget(target.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "ambient-occlusion-stencil-target");
}
// Draw the texture data to the texture.
args.WorldHandle.RenderInRenderTarget(_aoTarget,
() =>
{
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
var invMatrix = _aoTarget.GetWorldToLocalMatrix(viewport.Eye!, scale);
foreach (var entry in query.QueryAabb(mapId, worldBounds))
{
DebugTools.Assert(entry.Component.Enabled);
var matrix = xformSystem.GetWorldMatrix(entry.Transform);
var localMatrix = Matrix3x2.Multiply(matrix, invMatrix);
worldHandle.SetTransform(localMatrix);
// 4 pixels
worldHandle.DrawRect(Box2.UnitCentered.Enlarged(distance / EyeManager.PixelsPerMeter), Color.White);
}
}, Color.Transparent);
_clyde.BlurRenderTarget(viewport, _aoTarget, _aoTarget, viewport.Eye!, 14f);
// Need to do stencilling after blur as it will nuke it.
// Draw stencil for the grid so we don't draw in space.
args.WorldHandle.RenderInRenderTarget(_aoStencilTarget,
() =>
{
// Don't want lighting affecting it.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("unshaded").Instance());
foreach (var grid in _mapManager.FindGridsIntersecting(mapId, worldBounds))
{
var transform = xformSystem.GetWorldMatrix(grid.Owner);
var worldToTextureMatrix = Matrix3x2.Multiply(transform, invMatrix);
var tiles = maps.GetTilesEnumerator(grid.Owner, grid, worldBounds);
worldHandle.SetTransform(worldToTextureMatrix);
while (tiles.MoveNext(out var tileRef))
{
if (tileRef.IsSpace(_tileDefManager))
continue;
var bounds = lookups.GetLocalBounds(tileRef, grid.TileSize);
worldHandle.DrawRect(bounds, Color.White);
}
}
}, Color.Transparent);
// Draw the stencil texture to depth buffer.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_aoStencilTarget!.Texture, worldBounds);
// Draw the Blurred AO texture finally.
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilEqualDraw").Instance());
worldHandle.DrawTextureRect(_aoTarget!.Texture, worldBounds, color);
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilClear").Instance());
worldHandle.DrawRect(worldBounds, Color.White);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
args.WorldHandle.UseShader(null);
}
}
@@ -1,4 +1,4 @@
using System.Numerics;
using Content.Client.Graphics;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
@@ -13,7 +13,8 @@ public sealed class BeforeLightTargetOverlay : Overlay
[Dependency] private readonly IClyde _clyde = default!;
public IRenderTexture EnlargedLightTarget = default!;
private readonly OverlayResourceCache<CachedResources> _resources = new();
public Box2Rotated EnlargedBounds;
/// <summary>
@@ -36,16 +37,42 @@ public sealed class BeforeLightTargetOverlay : Overlay
var size = args.Viewport.LightRenderTarget.Size + (int) (_skirting * EyeManager.PixelsPerMeter);
EnlargedBounds = args.WorldBounds.Enlarged(_skirting / 2f);
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
// This just exists to copy the lightrendertarget and write back to it.
if (EnlargedLightTarget?.Size != size)
if (res.EnlargedLightTarget?.Size != size)
{
EnlargedLightTarget = _clyde
res.EnlargedLightTarget = _clyde
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-copy");
}
args.WorldHandle.RenderInRenderTarget(EnlargedLightTarget,
args.WorldHandle.RenderInRenderTarget(res.EnlargedLightTarget,
() =>
{
}, _clyde.GetClearColor(args.MapUid));
}
internal CachedResources GetCachedForViewport(IClydeViewport viewport)
{
return _resources.GetForViewport(viewport,
static _ => throw new InvalidOperationException(
"Expected BeforeLightTargetOverlay to have created its resources"));
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
internal sealed class CachedResources : IDisposable
{
public IRenderTexture EnlargedLightTarget = default!;
public void Dispose()
{
EnlargedLightTarget?.Dispose();
}
}
}
@@ -6,6 +6,8 @@ namespace Content.Client.Light.EntitySystems;
public sealed class EmergencyLightSystem : VisualizerSystem<EmergencyLightComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, EmergencyLightComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
@@ -14,13 +16,13 @@ public sealed class EmergencyLightSystem : VisualizerSystem<EmergencyLightCompon
if (!AppearanceSystem.TryGetData<bool>(uid, EmergencyLightVisuals.On, out var on, args.Component))
on = false;
args.Sprite.LayerSetVisible(EmergencyLightVisualLayers.LightOff, !on);
args.Sprite.LayerSetVisible(EmergencyLightVisualLayers.LightOn, on);
_sprite.LayerSetVisible((uid, args.Sprite), EmergencyLightVisualLayers.LightOff, !on);
_sprite.LayerSetVisible((uid, args.Sprite), EmergencyLightVisualLayers.LightOn, on);
if (AppearanceSystem.TryGetData<Color>(uid, EmergencyLightVisuals.Color, out var color, args.Component))
{
args.Sprite.LayerSetColor(EmergencyLightVisualLayers.LightOn, color);
args.Sprite.LayerSetColor(EmergencyLightVisualLayers.LightOff, color);
_sprite.LayerSetColor((uid, args.Sprite), EmergencyLightVisualLayers.LightOn, color);
_sprite.LayerSetColor((uid, args.Sprite), EmergencyLightVisualLayers.LightOff, color);
}
}
}
@@ -5,6 +5,8 @@ namespace Content.Client.Light.Visualizers;
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
@@ -16,13 +18,13 @@ public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
switch (state)
{
case LightBulbState.Normal:
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.NormalSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break;
case LightBulbState.Broken:
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.BrokenSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break;
case LightBulbState.Burned:
args.Sprite.LayerSetState(LightBulbVisualLayers.Base, comp.BurnedSpriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break;
}
}
@@ -30,7 +32,7 @@ public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
// also update sprites color
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{
args.Sprite.Color = color;
_sprite.SetColor((uid, args.Sprite), color);
}
}
}
@@ -1,17 +1,51 @@
using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Shared.Configuration;
namespace Content.Client.Light.EntitySystems;
public sealed class PlanetLightSystem : EntitySystem
{
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
[Dependency] private readonly IOverlayManager _overlayMan = default!;
/// <summary>
/// Enables / disables the ambient occlusion overlay.
/// </summary>
public bool AmbientOcclusion
{
get => _ambientOcclusion;
set
{
if (_ambientOcclusion == value)
return;
_ambientOcclusion = value;
if (value)
{
_overlayMan.AddOverlay(new AmbientOcclusionOverlay());
}
else
{
_overlayMan.RemoveOverlay<AmbientOcclusionOverlay>();
}
}
}
private bool _ambientOcclusion;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<GetClearColorEvent>(OnClearColor);
_cfgManager.OnValueChanged(CCVars.AmbientOcclusion, val =>
{
AmbientOcclusion = val;
}, true);
_overlayMan.AddOverlay(new BeforeLightTargetOverlay());
_overlayMan.AddOverlay(new RoofOverlay(EntityManager));
_overlayMan.AddOverlay(new TileEmissionOverlay(EntityManager));
+1 -1
View File
@@ -44,7 +44,7 @@ public sealed class HandheldLightSystem : SharedHandheldLightSystem
return;
}
if (!_appearance.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
if (!_appearance.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
{
return;
}
+27 -6
View File
@@ -1,3 +1,4 @@
using Content.Client.Graphics;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
@@ -15,7 +16,7 @@ public sealed class LightBlurOverlay : Overlay
public const int ContentZIndex = TileEmissionOverlay.ContentZIndex + 1;
private IRenderTarget? _blurTarget;
private readonly OverlayResourceCache<CachedResources> _resources = new();
public LightBlurOverlay()
{
@@ -29,16 +30,36 @@ public sealed class LightBlurOverlay : Overlay
return;
var beforeOverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
var size = beforeOverlay.EnlargedLightTarget.Size;
var beforeLightRes = beforeOverlay.GetCachedForViewport(args.Viewport);
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (_blurTarget?.Size != size)
var size = beforeLightRes.EnlargedLightTarget.Size;
if (res.BlurTarget?.Size != size)
{
_blurTarget = _clyde
res.BlurTarget = _clyde
.CreateRenderTarget(size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "enlarged-light-blur");
}
var target = beforeOverlay.EnlargedLightTarget;
var target = beforeLightRes.EnlargedLightTarget;
// Yeah that's all this does keep walkin.
_clyde.BlurRenderTarget(args.Viewport, target, _blurTarget, args.Viewport.Eye, 14f * 5f);
_clyde.BlurRenderTarget(args.Viewport, target, res.BlurTarget, args.Viewport.Eye, 14f * 5f);
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
private sealed class CachedResources : IDisposable
{
public IRenderTarget? BlurTarget;
public void Dispose()
{
BlurTarget?.Dispose();
}
}
}
+5 -19
View File
@@ -43,7 +43,7 @@ public sealed class RoofOverlay : Overlay
protected override void Draw(in OverlayDrawArgs args)
{
if (args.Viewport.Eye == null)
if (args.Viewport.Eye == null || !_entManager.HasComponent<MapLightComponent>(args.MapUid))
return;
var viewport = args.Viewport;
@@ -52,37 +52,23 @@ public sealed class RoofOverlay : Overlay
var worldHandle = args.WorldHandle;
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
var bounds = lightoverlay.EnlargedBounds;
var target = lightoverlay.EnlargedLightTarget;
var target = lightoverlay.GetCachedForViewport(viewport).EnlargedLightTarget;
_grids.Clear();
_mapManager.FindGridsIntersecting(args.MapId, bounds, ref _grids);
for (var i = _grids.Count - 1; i >= 0; i--)
{
var grid = _grids[i];
if (_entManager.HasComponent<RoofComponent>(grid.Owner))
continue;
_grids.RemoveAt(i);
}
if (_grids.Count == 0)
return;
_mapManager.FindGridsIntersecting(args.MapId, bounds, ref _grids, approx: true, includeMap: true);
var lightScale = viewport.LightRenderTarget.Size / (Vector2) viewport.Size;
var scale = viewport.RenderScale / (Vector2.One / lightScale);
worldHandle.RenderInRenderTarget(target,
() =>
{
var invMatrix = target.GetWorldToLocalMatrix(eye, scale);
foreach (var grid in _grids)
{
if (!_entManager.TryGetComponent(grid.Owner, out RoofComponent? roof))
continue;
var invMatrix = target.GetWorldToLocalMatrix(eye, scale);
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
+41 -12
View File
@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Graphics;
using Content.Shared.Light.Components;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
@@ -11,6 +12,8 @@ namespace Content.Client.Light;
public sealed class SunShadowOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> MixShader = "Mix";
public override OverlaySpace Space => OverlaySpace.BeforeLighting;
[Dependency] private readonly IClyde _clyde = default!;
@@ -22,8 +25,7 @@ public sealed class SunShadowOverlay : Overlay
private readonly HashSet<Entity<SunShadowCastComponent>> _shadows = new();
private IRenderTexture? _blurTarget;
private IRenderTexture? _target;
private readonly OverlayResourceCache<CachedResources> _resources = new();
public SunShadowOverlay()
{
@@ -53,16 +55,18 @@ public sealed class SunShadowOverlay : Overlay
var worldBounds = args.WorldBounds;
var targetSize = viewport.LightRenderTarget.Size;
if (_target?.Size != targetSize)
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (res.Target?.Size != targetSize)
{
_target = _clyde
res.Target = _clyde
.CreateRenderTarget(targetSize,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
name: "sun-shadow-target");
if (_blurTarget?.Size != targetSize)
if (res.BlurTarget?.Size != targetSize)
{
_blurTarget = _clyde
res.BlurTarget = _clyde
.CreateRenderTarget(targetSize, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "sun-shadow-blur");
}
}
@@ -91,11 +95,11 @@ public sealed class SunShadowOverlay : Overlay
_shadows.Clear();
// Draw shadow polys to stencil
args.WorldHandle.RenderInRenderTarget(_target,
args.WorldHandle.RenderInRenderTarget(res.Target,
() =>
{
var invMatrix =
_target.GetWorldToLocalMatrix(eye, scale);
res.Target.GetWorldToLocalMatrix(eye, scale);
var indices = new Vector2[PhysicsConstants.MaxPolygonVertices * 2];
// Go through shadows in range.
@@ -114,7 +118,9 @@ public sealed class SunShadowOverlay : Overlay
foreach (var ent in _shadows)
{
var xform = _entManager.GetComponent<TransformComponent>(ent.Owner);
var worldMatrix = _xformSys.GetWorldMatrix(xform);
var (worldPos, worldRot) = _xformSys.GetWorldPositionRotation(xform);
// Need no rotation on matrix as sun shadow direction doesn't care.
var worldMatrix = Matrix3x2.CreateTranslation(worldPos);
var renderMatrix = Matrix3x2.Multiply(worldMatrix, invMatrix);
var pointCount = ent.Comp.Points.Length;
@@ -122,6 +128,10 @@ public sealed class SunShadowOverlay : Overlay
for (var i = 0; i < pointCount; i++)
{
// Update point based on entity rotation.
indices[i] = worldRot.RotateVec(indices[i]);
// Add the offset point by the sun shadow direction.
indices[pointCount + i] = indices[i] + direction;
}
@@ -134,7 +144,7 @@ public sealed class SunShadowOverlay : Overlay
Color.Transparent);
// Slightly blur it just to avoid aliasing issues on the later viewport-wide blur.
_clyde.BlurRenderTarget(viewport, _target, _target, eye, 1f);
_clyde.BlurRenderTarget(viewport, res.Target, res.BlurTarget!, eye, 1f);
// Draw stencil (see roofoverlay).
args.WorldHandle.RenderInRenderTarget(viewport.LightRenderTarget,
@@ -144,11 +154,30 @@ public sealed class SunShadowOverlay : Overlay
viewport.LightRenderTarget.GetWorldToLocalMatrix(eye, scale);
worldHandle.SetTransform(invMatrix);
var maskShader = _protoManager.Index<ShaderPrototype>("Mix").Instance();
var maskShader = _protoManager.Index(MixShader).Instance();
worldHandle.UseShader(maskShader);
worldHandle.DrawTextureRect(_target.Texture, worldBounds, Color.Black.WithAlpha(alpha));
worldHandle.DrawTextureRect(res.Target.Texture, worldBounds, Color.Black.WithAlpha(alpha));
}, null);
}
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
private sealed class CachedResources : IDisposable
{
public IRenderTexture? BlurTarget;
public IRenderTexture? Target;
public void Dispose()
{
BlurTarget?.Dispose();
Target?.Dispose();
}
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ public sealed class TileEmissionOverlay : Overlay
var worldHandle = args.WorldHandle;
var lightoverlay = _overlay.GetOverlay<BeforeLightTargetOverlay>();
var bounds = lightoverlay.EnlargedBounds;
var target = lightoverlay.EnlargedLightTarget;
var target = lightoverlay.GetCachedForViewport(args.Viewport).EnlargedLightTarget;
var viewport = args.Viewport;
_grids.Clear();
_mapManager.FindGridsIntersecting(mapId, bounds, ref _grids, approx: true);
@@ -12,6 +12,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
{
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -28,16 +29,16 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
return;
if (comp.SpriteStateMap.TryGetValue(state, out var spriteState))
args.Sprite.LayerSetState(PoweredLightLayers.Base, spriteState);
_sprite.LayerSetRsiState((uid, args.Sprite), PoweredLightLayers.Base, spriteState);
if (args.Sprite.LayerExists(PoweredLightLayers.Glow))
if (_sprite.LayerExists((uid, args.Sprite), PoweredLightLayers.Glow))
{
if (TryComp<PointLightComponent>(uid, out var light))
{
args.Sprite.LayerSetColor(PoweredLightLayers.Glow, light.Color);
_sprite.LayerSetColor((uid, args.Sprite), PoweredLightLayers.Glow, light.Color);
}
args.Sprite.LayerSetVisible(PoweredLightLayers.Glow, state == PoweredLightState.On);
_sprite.LayerSetVisible((uid, args.Sprite), PoweredLightLayers.Glow, state == PoweredLightState.On);
}
SetBlinkingAnimation(
@@ -55,8 +56,7 @@ public sealed class PoweredLightVisualizerSystem : VisualizerSystem<PoweredLight
{
if (args.Key != PoweredLightVisualsComponent.BlinkingAnimationKey)
return;
if(!comp.IsBlinking)
if (!comp.IsBlinking)
return;
AnimationSystem.Play(uid, Comp<AnimationPlayerComponent>(uid), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey);
+4 -1
View File
@@ -1,4 +1,5 @@
using System.Linq;
using Content.Client._Mono.Company; // Mono
using Content.Client._Mono.MonoCoins;
using Content.Client.Guidebook;
using Content.Client.Humanoid;
@@ -41,6 +42,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly JobRequirementsManager _requirements = default!;
[Dependency] private readonly MarkingManager _markings = default!;
[Dependency] private readonly CompanyManager _companyManager = default!; // Mono
[UISystemDependency] private readonly HumanoidAppearanceSystem _humanoid = default!;
[UISystemDependency] private readonly ClientInventorySystem _inventory = default!;
[UISystemDependency] private readonly StationSpawningSystem _spawn = default!;
@@ -350,7 +352,8 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
_prototypeManager,
_resourceCache,
_requirements,
_markings);
_markings,
_companyManager); // Mono
_profileEditor.OnOpenGuidebook += _guide.OpenHelp;
@@ -35,6 +35,7 @@ using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
using Direction = Robust.Shared.Maths.Direction;
using Content.Client._Mono.Company;
namespace Content.Client.Lobby.UI
{
@@ -52,6 +53,7 @@ namespace Content.Client.Lobby.UI
private readonly JobRequirementsManager _requirements;
private readonly LobbyUIController _controller;
private readonly EntityWhitelistSystem _whitelist; // Frontier
private readonly CompanyManager _companyManager; // Mono
private FlavorText.FlavorText? _flavorText;
private TextEdit? _flavorTextEdit;
@@ -116,7 +118,8 @@ namespace Content.Client.Lobby.UI
IPrototypeManager prototypeManager,
IResourceManager resManager,
JobRequirementsManager requirements,
MarkingManager markings)
MarkingManager markings,
CompanyManager manager) // Mono
{
RobustXamlLoader.Load(this);
_sawmill = logManager.GetSawmill("profile.editor");
@@ -127,6 +130,7 @@ namespace Content.Client.Lobby.UI
_prototypeManager = prototypeManager;
_markingManager = markings;
_preferencesManager = preferencesManager;
_companyManager = manager; // Mono
_resManager = resManager;
_requirements = requirements;
_controller = UserInterfaceManager.GetUIController<LobbyUIController>();
@@ -446,12 +450,9 @@ namespace Content.Client.Lobby.UI
// Clear any existing items
CompanyButton.Clear();
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
// Add all companies from prototypes - use consistent sorting with UpdateCompanyControls
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
//.Where(c => !c.Disabled) // Filter out disabled companies
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
.Where(c => !c.Disabled && _companyManager.IsAllowed(c.ID))
.ToList();
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
@@ -1451,7 +1452,7 @@ namespace Content.Client.Lobby.UI
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
{
Title = jobProto?.ID + "-loadout",
Title = jobProto?.LocalizedName + " loadout", // Mono - replace the "-" with a space in "-loadout", change to LocalizedName from ID
};
// Refresh the buttons etc.
@@ -2194,11 +2195,8 @@ namespace Content.Client.Lobby.UI
if (Profile is null)
return;
var username = _playerManager.LocalPlayer?.Session?.Name; //Lua modified - company login support
var companies = _prototypeManager.EnumeratePrototypes<CompanyPrototype>()
//.Where(c => !c.Disabled) // Filter out disabled companies
.Where(c => !c.Disabled || (username != null && c.Logins.Contains(username))) //Lua modified - company login support
.Where(c => !c.Disabled && _companyManager.IsAllowed(c.ID))
.ToList();
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
@@ -14,6 +14,7 @@
<ui:OptionDropDown Name="DropDownLightingQuality" Title="{Loc 'ui-options-lighting-label'}" />
<CheckBox Name="ViewportLowResCheckBox" Text="{Loc 'ui-options-vp-low-res'}" />
<CheckBox Name="ParallaxLowQualityCheckBox" Text="{Loc 'ui-options-parallax-low-quality'}" />
<CheckBox Name="AmbientOcclusionCheckBox" Text="{Loc 'ui-options-ambient-occlusion'}" />
<!-- Interface -->
<Label Text="{Loc 'ui-options-interface-label'}" StyleClasses="LabelKeyText"/>
@@ -20,6 +20,7 @@ public sealed partial class GraphicsTab : Control
RobustXamlLoader.Load(this);
Control.AddOptionCheckBox(CVars.DisplayVSync, VSyncCheckBox);
Control.AddOptionCheckBox(CCVars.AmbientOcclusion, AmbientOcclusionCheckBox);
Control.AddOption(new OptionFullscreen(Control, _cfg, FullscreenCheckBox));
Control.AddOption(new OptionLightingQuality(Control, _cfg, DropDownLightingQuality));
@@ -7,7 +7,11 @@ namespace Content.Client.Overlays;
public sealed partial class StencilOverlay
{
private void DrawRestrictedRange(in OverlayDrawArgs args, RestrictedRangeComponent rangeComp, Matrix3x2 invMatrix)
private void DrawRestrictedRange(
in OverlayDrawArgs args,
CachedResources res,
RestrictedRangeComponent rangeComp,
Matrix3x2 invMatrix)
{
var worldHandle = args.WorldHandle;
var renderScale = args.Viewport.RenderScale.X;
@@ -38,20 +42,20 @@ public sealed partial class StencilOverlay
// Cut out the irrelevant bits via stencil
// This is why we don't just use parallax; we might want specific tiles to get drawn over
// particularly for planet maps or stations.
worldHandle.RenderInRenderTarget(_blep!, () =>
worldHandle.RenderInRenderTarget(res.Blep!, () =>
{
worldHandle.UseShader(_shader);
worldHandle.DrawRect(localAABB, Color.White);
}, Color.Transparent);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
worldHandle.DrawTextureRect(res.Blep!.Texture, worldBounds);
var curTime = _timing.RealTime;
var sprite = _sprite.GetFrame(new SpriteSpecifier.Texture(new ResPath("/Textures/Parallaxes/noise.png")), curTime);
// Draw the rain
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, new Vector2(0.5f, 0f));
}
}
@@ -11,7 +11,12 @@ public sealed partial class StencilOverlay
{
private List<Entity<MapGridComponent>> _grids = new();
private void DrawWeather(in OverlayDrawArgs args, WeatherPrototype weatherProto, float alpha, Matrix3x2 invMatrix)
private void DrawWeather(
in OverlayDrawArgs args,
CachedResources res,
WeatherPrototype weatherProto,
float alpha,
Matrix3x2 invMatrix)
{
var worldHandle = args.WorldHandle;
var mapId = args.MapId;
@@ -22,7 +27,7 @@ public sealed partial class StencilOverlay
// Cut out the irrelevant bits via stencil
// This is why we don't just use parallax; we might want specific tiles to get drawn over
// particularly for planet maps or stations.
worldHandle.RenderInRenderTarget(_blep!, () =>
worldHandle.RenderInRenderTarget(res.Blep!, () =>
{
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
_grids.Clear();
@@ -55,13 +60,13 @@ public sealed partial class StencilOverlay
}, Color.Transparent);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_blep!.Texture, worldBounds);
worldHandle.UseShader(_protoManager.Index(StencilMask).Instance());
worldHandle.DrawTextureRect(res.Blep!.Texture, worldBounds);
var curTime = _timing.RealTime;
var sprite = _sprite.GetFrame(weatherProto.Sprite, curTime);
// Draw the rain
worldHandle.UseShader(_protoManager.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.UseShader(_protoManager.Index(StencilDraw).Instance());
_parallax.DrawParallax(worldHandle, worldAABB, sprite, curTime, position, Vector2.Zero, modulate: (weatherProto.Color ?? Color.White).WithAlpha(alpha));
worldHandle.SetTransform(Matrix3x2.Identity);
+29 -6
View File
@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Graphics;
using Content.Client.Parallax;
using Content.Client.Weather;
using Content.Shared.Salvage;
@@ -17,6 +18,9 @@ namespace Content.Client.Overlays;
/// </summary>
public sealed partial class StencilOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> StencilMask = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilDraw = "StencilDraw";
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -30,7 +34,7 @@ public sealed partial class StencilOverlay : Overlay
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
private IRenderTexture? _blep;
private readonly OverlayResourceCache<CachedResources> _resources = new();
private readonly ShaderInstance _shader;
@@ -51,10 +55,12 @@ public sealed partial class StencilOverlay : Overlay
var mapUid = _mapManager.GetMapEntityId(args.MapId);
var invMatrix = args.Viewport.GetWorldToLocalMatrix();
if (_blep?.Texture.Size != args.Viewport.Size)
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (res.Blep?.Texture.Size != args.Viewport.Size)
{
_blep?.Dispose();
_blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
res.Blep?.Dispose();
res.Blep = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "weather-stencil");
}
if (_entManager.TryGetComponent<WeatherComponent>(mapUid, out var comp))
@@ -65,16 +71,33 @@ public sealed partial class StencilOverlay : Overlay
continue;
var alpha = _weather.GetPercent(weather, mapUid);
DrawWeather(args, weatherProto, alpha, invMatrix);
DrawWeather(args, res, weatherProto, alpha, invMatrix);
}
}
if (_entManager.TryGetComponent<RestrictedRangeComponent>(mapUid, out var restrictedRangeComponent))
{
DrawRestrictedRange(args, restrictedRangeComponent, invMatrix);
DrawRestrictedRange(args, res, restrictedRangeComponent, invMatrix);
}
args.WorldHandle.UseShader(null);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
private sealed class CachedResources : IDisposable
{
public IRenderTexture? Blep;
public void Dispose()
{
Blep?.Dispose();
}
}
}
@@ -6,18 +6,28 @@ namespace Content.Client.Paper.UI;
public sealed class PaperVisualizerSystem : VisualizerSystem<PaperVisualsComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, PaperVisualsComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status , out var writingStatus, args.Component))
args.Sprite.LayerSetVisible(PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status, out var writingStatus, args.Component))
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
if (AppearanceSystem.TryGetData<string>(uid, PaperVisuals.Stamp, out var stampState, args.Component))
{
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
if (stampState != string.Empty)
{
_sprite.LayerSetRsiState((uid, args.Sprite), PaperVisualLayers.Stamp, stampState);
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Stamp, true);
}
else
{
_sprite.LayerSetVisible((uid, args.Sprite), PaperVisualLayers.Stamp, false);
}
}
}
}
@@ -1,4 +1,3 @@
using System.Linq;
using Content.Shared.Singularity.Components;
using Robust.Client.GameObjects;
@@ -6,12 +5,14 @@ namespace Content.Client.ParticleAccelerator;
public sealed class ParticleAcceleratorPartVisualizerSystem : VisualizerSystem<ParticleAcceleratorPartVisualsComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, ParticleAcceleratorPartVisualsComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
if (!args.Sprite.LayerMapTryGet(ParticleAcceleratorVisualLayers.Unlit, out var index))
if (!_sprite.LayerMapTryGet((uid, args.Sprite), ParticleAcceleratorVisualLayers.Unlit, out var index, false))
return;
if (!AppearanceSystem.TryGetData<ParticleAcceleratorVisualState>(uid, ParticleAcceleratorVisuals.VisualState, out var state, args.Component))
@@ -21,12 +22,12 @@ public sealed class ParticleAcceleratorPartVisualizerSystem : VisualizerSystem<P
if (state != ParticleAcceleratorVisualState.Unpowered)
{
args.Sprite.LayerSetVisible(index, true);
args.Sprite.LayerSetState(index, comp.StateBase + comp.StatesSuffixes[state]);
_sprite.LayerSetVisible((uid, args.Sprite), index, true);
_sprite.LayerSetRsiState((uid, args.Sprite), index, comp.StateBase + comp.StatesSuffixes[state]);
}
else
{
args.Sprite.LayerSetVisible(index, false);
_sprite.LayerSetVisible((uid, args.Sprite), index, false);
}
}
}
+5 -4
View File
@@ -9,6 +9,7 @@ namespace Content.Client.PowerCell;
public sealed class PowerCellSystem : SharedPowerCellSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -44,19 +45,19 @@ public sealed class PowerCellSystem : SharedPowerCellSystem
if (args.Sprite == null)
return;
if (!args.Sprite.TryGetLayer((int) PowerCellVisualLayers.Unshaded, out var unshadedLayer))
if (!_sprite.LayerExists((uid, args.Sprite), PowerCellVisualLayers.Unshaded))
return;
if (_appearance.TryGetData<byte>(uid, PowerCellVisuals.ChargeLevel, out var level, args.Component))
{
if (level == 0)
{
unshadedLayer.Visible = false;
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false);
return;
}
unshadedLayer.Visible = true;
args.Sprite.LayerSetState(PowerCellVisualLayers.Unshaded, $"o{level}");
_sprite.LayerSetVisible((uid, args.Sprite), PowerCellVisualLayers.Unshaded, false);
_sprite.LayerSetRsiState((uid, args.Sprite), PowerCellVisualLayers.Unshaded, $"o{level}");
}
}
+14
View File
@@ -12,9 +12,16 @@
<!-- Entry layer (shows main categories) -->
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100" ReserveSpaceForHiddenChildren="False">
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-walls-and-flooring'}" TargetLayer="WallsAndFlooring" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/walls_and_flooring.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-latticetiles'}" TargetLayer="LatticeTiles" Visible="False"> <!-- Mono -->
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lattice.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-platingtiles'}" TargetLayer="PlatingTiles" Visible="False"> <!-- Mono -->
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/plating.png"/>
</ui:RadialMenuTextureButtonWithSector>
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-windows-and-grilles'}" TargetLayer="WindowsAndGrilles" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/windows_and_grilles.png"/>
</ui:RadialMenuTextureButtonWithSector>
@@ -27,11 +34,18 @@
<ui:RadialMenuTextureButtonWithSector SetSize="64 64" ToolTip="{Loc 'rcd-component-lighting'}" TargetLayer="Lighting" Visible="False">
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2 2" TexturePath="/Textures/Interface/Radial/RCD/lighting.png"/>
</ui:RadialMenuTextureButtonWithSector>
</ui:RadialContainer>
<!-- Walls and flooring -->
<ui:RadialContainer Name="WallsAndFlooring" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Lattices Mono -->
<ui:RadialContainer Name="LatticeTiles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Plating Mono -->
<ui:RadialContainer Name="PlatingTiles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
<!-- Windows and grilles -->
<ui:RadialContainer Name="WindowsAndGrilles" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"/>
@@ -77,6 +77,16 @@ public sealed partial class RoboticsConsoleWindow : FancyWindow
if (_selected is {} selected && !_cyborgs.ContainsKey(selected))
_selected = null;
// Corvax-Next-AiRemoteControl-Start
var isAiControllable = false;
if (_selected != null)
{
_cyborgs.TryGetValue(_selected, out var data);
isAiControllable = data.IsAiControllable;
}
// Corvax-Next-AiRemoteControl-End
var hasCyborgs = _cyborgs.Count > 0;
NoCyborgs.Visible = !hasCyborgs;
CyborgsContainer.Visible = hasCyborgs;
@@ -138,6 +138,293 @@ public partial class BaseShuttleControl : MapGridControl
}
// End Frontier Corvax
// Mono
protected void DrawAzimuthScale(DrawingHandleScreen handle, Angle rotation)
{
const int azimuthMinorTickStepDegrees = 10;
const int azimuthMajorTickInterval = 3;
const int azimuthCardinalTickInterval = 9;
const float edgeInset = 10f;
const float tickInset = 4f;
const float minorTickLength = 5f;
const float majorTickLength = 9f;
const float cardinalTickLength = 12f;
const float labelPadding = 3f;
const float labelOffset = tickInset + cardinalTickLength + labelPadding;
const float regularLabelScale = 0.62f;
const float cardinalLabelScale = 0.72f;
var origin = MidPointVector;
var viewportSize = new Vector2(PixelWidth, PixelHeight);
var subtleGray = Color.FromHex("#676767");
var northAccent = Color.FromHex("#7F7442");
var eastAccent = Color.FromHex("#456273");
var baseTickColor = subtleGray.WithAlpha(0.34f);
var majorTickColor = subtleGray.WithAlpha(0.5f);
var cardinalTickColor = subtleGray.WithAlpha(0.68f);
var regularLabelColor = subtleGray.WithAlpha(0.76f);
var cardinalLabelColor = subtleGray.WithAlpha(0.9f);
var labelShadowColor = Color.Black.WithAlpha(0.36f);
for (var step = 0; step < 360 / azimuthMinorTickStepDegrees; step++)
{
var degrees = step * azimuthMinorTickStepDegrees;
var direction = GetAzimuthDirection(rotation, degrees);
var isCardinal = step % azimuthCardinalTickInterval == 0;
var isMajor = step % azimuthMajorTickInterval == 0;
var isNorth = degrees == 0;
var isEast = degrees == 90;
var tickLength = isCardinal
? cardinalTickLength
: isMajor
? majorTickLength
: minorTickLength;
if (!TryGetViewportEdgePoint(origin, direction, viewportSize, edgeInset, out var edgePoint, out var inwardNormal))
continue;
var outer = edgePoint + inwardNormal * tickInset;
var inner = outer + inwardNormal * tickLength;
var tickColor = isNorth
? northAccent.WithAlpha(0.58f)
: isEast
? eastAccent.WithAlpha(0.58f)
: isCardinal
? cardinalTickColor
: isMajor
? majorTickColor
: baseTickColor;
handle.DrawLine(outer, inner, tickColor);
if (isNorth)
{
var sideOffset = new Vector2(-inwardNormal.Y, inwardNormal.X) * 0.35f;
handle.DrawLine(outer + sideOffset, inner + sideOffset, tickColor.WithAlpha(0.46f));
}
if (!isMajor)
continue;
var label = degrees.ToString();
var labelScale = isCardinal ? cardinalLabelScale : regularLabelScale;
var labelColor = isEast
? eastAccent.WithAlpha(0.76f)
: isCardinal
? cardinalLabelColor
: regularLabelColor;
var labelSize = handle.GetDimensions(Font, label, labelScale);
var labelPosition = GetEdgeLabelPosition(edgePoint, inwardNormal, labelSize, viewportSize, labelOffset);
handle.DrawString(Font, labelPosition + new Vector2(1f, 1f), label, labelScale, labelShadowColor);
handle.DrawString(Font, labelPosition, label, labelScale, labelColor);
}
}
protected void DrawAzimuthRing(DrawingHandleScreen handle, Angle rotation)
{
const int azimuthMinorTickStepDegrees = 10;
const int azimuthMajorTickInterval = 3;
const int azimuthCardinalTickInterval = 9;
const float ringInset = 16f;
const float minorTickLength = 5f;
const float majorTickLength = 9f;
const float cardinalTickLength = 12f;
const float labelPadding = 5f;
const float regularLabelScale = 0.62f;
const float cardinalLabelScale = 0.72f;
var origin = MidPointVector;
var radius = MathF.Max(8f, SizeFull * 0.5f - ringInset);
var subtleGray = Color.FromHex("#676767");
var northAccent = Color.FromHex("#7F7442");
var eastAccent = Color.FromHex("#456273");
var baseTickColor = subtleGray.WithAlpha(0.34f);
var majorTickColor = subtleGray.WithAlpha(0.5f);
var cardinalTickColor = subtleGray.WithAlpha(0.68f);
var regularLabelColor = subtleGray.WithAlpha(0.76f);
var cardinalLabelColor = subtleGray.WithAlpha(0.9f);
var labelShadowColor = Color.Black.WithAlpha(0.36f);
handle.DrawCircle(origin, radius, subtleGray.WithAlpha(0.12f), false);
for (var step = 0; step < 360 / azimuthMinorTickStepDegrees; step++)
{
var degrees = step * azimuthMinorTickStepDegrees;
var direction = GetAzimuthDirection(rotation, degrees);
var isCardinal = step % azimuthCardinalTickInterval == 0;
var isMajor = step % azimuthMajorTickInterval == 0;
var isNorth = degrees == 0;
var isEast = degrees == 90;
var tickLength = isCardinal
? cardinalTickLength
: isMajor
? majorTickLength
: minorTickLength;
var outer = origin + direction * radius;
var inner = origin + direction * (radius - tickLength);
var tickColor = isNorth
? northAccent.WithAlpha(0.58f)
: isEast
? eastAccent.WithAlpha(0.58f)
: isCardinal
? cardinalTickColor
: isMajor
? majorTickColor
: baseTickColor;
handle.DrawLine(outer, inner, tickColor);
if (isNorth)
{
var sideOffset = new Vector2(-direction.Y, direction.X) * 0.35f;
handle.DrawLine(outer + sideOffset, inner + sideOffset, tickColor.WithAlpha(0.46f));
}
if (!isMajor)
continue;
var label = degrees.ToString();
var labelScale = isCardinal ? cardinalLabelScale : regularLabelScale;
var labelColor = isEast
? eastAccent.WithAlpha(0.76f)
: isCardinal
? cardinalLabelColor
: regularLabelColor;
var labelSize = handle.GetDimensions(Font, label, labelScale);
var labelPosition = origin + direction * (radius - tickLength - labelPadding) - labelSize * 0.5f;
handle.DrawString(Font, labelPosition + new Vector2(1f, 1f), label, labelScale, labelShadowColor);
handle.DrawString(Font, labelPosition, label, labelScale, labelColor);
}
}
protected void DrawCompassOverlay(DrawingHandleScreen handle, Angle heading)
{
var compassCenter = new Vector2(44f, 44f);
const float compassRadius = 24f;
var ringColor = Color.White.WithAlpha(0.08f);
var northColor = Color.FromHex("#776C43");
var eastColor = Color.FromHex("#4C6572");
handle.DrawCircle(compassCenter, compassRadius, ringColor, false);
handle.DrawCircle(compassCenter, 2.5f, ringColor.WithAlpha(0.48f), true);
DrawCompassNeedle(handle, compassCenter, compassRadius, GetAzimuthDirection(heading, 0f), "N", northColor);
DrawCompassNeedle(handle, compassCenter, compassRadius * 0.82f, GetAzimuthDirection(heading, 90f), "E", eastColor);
}
private void DrawCompassNeedle(
DrawingHandleScreen handle,
Vector2 compassCenter,
float radius,
Vector2 direction,
string label,
Color color)
{
var tip = compassCenter + direction * radius;
var tail = compassCenter - direction * (radius * 0.35f);
DrawArrowLine(handle, tail, tip, color.WithAlpha(0.58f), 6f);
var labelSize = handle.GetDimensions(Font, label, 0.9f);
var labelPosition = tip + direction * 6f - new Vector2(labelSize.X * 0.5f, labelSize.Y * 0.5f);
handle.DrawString(Font, labelPosition, label, 0.9f, color.WithAlpha(0.7f));
}
private static void DrawArrowLine(
DrawingHandleScreen handle,
Vector2 start,
Vector2 end,
Color color,
float headLength)
{
var direction = end - start;
if (direction.LengthSquared() <= 0.001f)
return;
direction = Vector2.Normalize(direction);
var left = new Vector2(-direction.Y, direction.X);
handle.DrawLine(start, end, color);
handle.DrawLine(end, end - direction * headLength + left * (headLength * 0.45f), color);
handle.DrawLine(end, end - direction * headLength - left * (headLength * 0.45f), color);
}
private static bool TryGetViewportEdgePoint(
Vector2 origin,
Vector2 direction,
Vector2 viewportSize,
float inset,
out Vector2 edgePoint,
out Vector2 inwardNormal)
{
edgePoint = default;
inwardNormal = default;
if (direction.LengthSquared() <= 0.001f)
return false;
direction = Vector2.Normalize(direction);
var min = new Vector2(inset, inset);
var max = viewportSize - min;
if (max.X <= min.X || max.Y <= min.Y)
return false;
var tx = direction.X > 0f
? (max.X - origin.X) / direction.X
: direction.X < 0f
? (min.X - origin.X) / direction.X
: float.PositiveInfinity;
var ty = direction.Y > 0f
? (max.Y - origin.Y) / direction.Y
: direction.Y < 0f
? (min.Y - origin.Y) / direction.Y
: float.PositiveInfinity;
var t = Math.Min(tx, ty);
if (!float.IsFinite(t) || t <= 0f)
return false;
edgePoint = origin + direction * t;
if (tx < ty)
inwardNormal = direction.X > 0f ? -Vector2.UnitX : Vector2.UnitX;
else
inwardNormal = direction.Y > 0f ? -Vector2.UnitY : Vector2.UnitY;
return true;
}
private static Vector2 GetEdgeLabelPosition(
Vector2 edgePoint,
Vector2 inwardNormal,
Vector2 labelSize,
Vector2 viewportSize,
float offset)
{
var anchor = edgePoint + inwardNormal * offset;
Vector2 requested;
if (Math.Abs(inwardNormal.X) > Math.Abs(inwardNormal.Y))
{
requested = inwardNormal.X > 0f
? new Vector2(anchor.X, anchor.Y - labelSize.Y * 0.5f)
: new Vector2(anchor.X - labelSize.X, anchor.Y - labelSize.Y * 0.5f);
}
else
{
requested = inwardNormal.Y > 0f
? new Vector2(anchor.X - labelSize.X * 0.5f, anchor.Y)
: new Vector2(anchor.X - labelSize.X * 0.5f, anchor.Y - labelSize.Y);
}
var minPosition = new Vector2(offset, offset);
var maxPosition = viewportSize - labelSize - minPosition;
return Vector2.Clamp(requested, minPosition, maxPosition);
}
private static Vector2 GetAzimuthDirection(Angle baseAngle, float azimuthDegrees)
{
var angle = baseAngle + Angle.FromDegrees(azimuthDegrees);
var radians = (float) angle.Theta - MathF.PI / 2f;
return new Vector2(MathF.Cos(radians), MathF.Sin(radians));
}
// End Mono
protected void DrawGrid(DrawingHandleScreen handle, Matrix3x2 gridToView, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
{
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
@@ -28,7 +28,7 @@
Orientation="Vertical">
<!-- FTL Lock Section -->
<controls1:StripeBack MinSize="48 48">
<controls:Label Text="{controls:Loc 'FTL Lock'}" HorizontalAlignment="Center"/>
<controls:Label Text="{controls:Loc 'shuttle-console-ftl-lock'}" HorizontalAlignment="Center"/>
</controls1:StripeBack>
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
<controls:PanelContainer.PanelOverride>
@@ -36,12 +36,12 @@
</controls:PanelContainer.PanelOverride>
<controls:BoxContainer Orientation="Horizontal" HorizontalAlignment="Stretch" Margin="5 5 5 5">
<controls:Button Name="FTLLockEnabledButton"
Text="{controls:Loc 'Enabled'}"
Text="{controls:Loc 'shuttle-console-ftl-lock-enabled'}"
ToggleMode="True"
HorizontalExpand="True"
StyleClasses="OpenRight"/>
<controls:Button Name="FTLLockDisabledButton"
Text="{controls:Loc 'Disabled'}"
Text="{controls:Loc 'shuttle-console-ftl-lock-disabled'}"
ToggleMode="True"
HorizontalExpand="True"
StyleClasses="OpenLeft"/>
@@ -50,7 +50,7 @@
<!-- Docks Section -->
<controls1:StripeBack MinSize="48 48">
<controls:Label Text="{controls:Loc 'Docks'}" HorizontalAlignment="Center"/>
<controls:Label Text="{controls:Loc 'shuttle-console-docks-label'}" HorizontalAlignment="Center"/>
</controls1:StripeBack>
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
<controls:PanelContainer.PanelOverride>
@@ -62,7 +62,7 @@
<graphics:StyleBoxFlat BorderThickness="2" BorderColor="#333333" BackgroundColor="#121212"/>
</controls:PanelContainer.PanelOverride>
<controls:Button Name="UndockAllButton"
Text="{controls:Loc 'Undock All'}"
Text="{controls:Loc 'shuttle-console-undock-all'}"
Margin="5 5 5 5"
StyleClasses="ButtonSquare"/>
</controls:PanelContainer>
+16 -10
View File
@@ -4,12 +4,14 @@
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
<ui:ShuttleNavControl Name="NavRadar"
MouseFilter="Stop"
VerticalAlignment="Stretch"
VerticalExpand="True"
HorizontalExpand="True"
Margin="5 4 10 5"/>
<!-- Mono - wrap -->
<controls:LayoutContainer Margin="5 4 10 5">
<ui:ShuttleNavControl Name="NavRadar"
MouseFilter="Stop"
VerticalAlignment="Stretch"
VerticalExpand="True"
HorizontalExpand="True"/>
</controls:LayoutContainer>
<!-- Nav controls -->
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
<controls:PanelContainer.PanelOverride>
@@ -173,11 +175,8 @@
<!-- Frontier - Maximum Shuttle Speed -->
<controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="MaximumShuttleSpeedBox" Margin="4 4 4 4">
<controls:Label Text="{controls:Loc 'shuttle-console-maximum-speed'}"/>
<controls:SliderIntInput Name="MaximumShuttleSpeedValue"
<controls:LineEdit Name="MaximumShuttleSpeedValue"
Access="Public"
MinValue="0"
MaxValue="140"
Value="140"
HorizontalExpand="True"/>
</controls:BoxContainer>
</controls:BoxContainer>
@@ -215,36 +214,43 @@
Text="{controls:Loc 'shuttle-console-device-button-1'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton2"
Text="{controls:Loc 'shuttle-console-device-button-2'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton3"
Text="{controls:Loc 'shuttle-console-device-button-3'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton4"
Text="{controls:Loc 'shuttle-console-device-button-4'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton5"
Text="{controls:Loc 'shuttle-console-device-button-5'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton6"
Text="{controls:Loc 'shuttle-console-device-button-6'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton7"
Text="{controls:Loc 'shuttle-console-device-button-7'}"
TextAlign="Center"
StyleClasses="ButtonSquare"
ToolTip="{controls:Loc 'shuttle-console-network-connect-tooltip'}"
SetWidth="55"/>
<controls:Button Name="DeviceButton8"
Text="{controls:Loc 'shuttle-console-device-button-8'}"
+4 -3
View File
@@ -176,14 +176,15 @@ public sealed partial class NavScreen : BoxContainer
var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform);
var worldPos = Vector2.Transform(gridBody.LocalCenter, worldMatrix);
// Get the positive reduced angle.
var displayRot = -worldRot.Reduced();
// Mono - remap to [0, 360)
var displayRot = (-worldRot).Reduced();
var displayRotDegrees = displayRot.FlipPositive().Degrees;
GridPosition.Text = Loc.GetString("shuttle-console-position-value",
("X", $"{worldPos.X:0.0}"),
("Y", $"{worldPos.Y:0.0}"));
GridOrientation.Text = Loc.GetString("shuttle-console-orientation-value",
("angle", $"{displayRot.Degrees:0.0}"));
("angle", $"{displayRotDegrees:0.0}"));
var gridVelocity = gridBody.LinearVelocity;
gridVelocity = displayRot.RotateVec(gridVelocity);
@@ -4,8 +4,10 @@
Title="{Loc 'radar-console-window-title'}"
SetSize="648 672"
MinSize="256 256">
<ui:ShuttleNavControl Name="RadarScreen"
Margin="4"
HorizontalExpand = "True"
VerticalExpand = "True"/>
<LayoutContainer Margin="4">
<!-- Mono - wrap -->
<ui:ShuttleNavControl Name="RadarScreen"
HorizontalExpand = "True"
VerticalExpand = "True"/>
</LayoutContainer>
</controls:FancyWindow>
@@ -13,6 +13,7 @@ using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Input;
using Robust.Shared.Map;
@@ -45,9 +46,10 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
/// <summary>
/// Entity of controlling console
/// </summary>
private EntityUid? _consoleEntity;
protected EntityUid? _consoleEntity; // Mono
protected Angle? _rotation; // Mono
// Mono - is world rotation of the view.
protected Angle? _rotation = Angle.Zero;
private Dictionary<NetEntity, List<DockingPortState>> _docks = new();
@@ -65,7 +67,6 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
public bool ShowIFF { get; set; } = true;
public bool ShowIFFShuttles { get; set; } = true;
public bool ShowDocks { get; set; } = true;
public bool RotateWithEntity { get; set; } = true;
public float MaximumIFFDistance { get; set; } = 3000f; // Frontier // Mono - 3000 by default to not gigaclutter
public bool HideCoords { get; set; } = false; // Frontier
@@ -88,14 +89,16 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
public List<EntityUid>? Detectors = null;
#region Mono
public bool RelativePanning = false;
// These 2 handle timing updates
protected const float RadarUpdateInterval = 0f;
protected float _updateAccumulator = 0f;
// doesn't support absolute panning and angle follow, but why would you need that?
protected bool _relativePanning = false;
protected bool _angleFollow = true;
private bool _radarModeDefaultsApplied;
private bool _wasPanned = false;
private EntityCoordinates? _oldCoordinates;
#endregion
protected bool _isMouseDown;
@@ -104,6 +107,20 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
protected float _lastFireTime;
protected const float FireRateLimit = 0.1f; // 100ms between shots
private enum RadarAzimuthMode : byte
{
Edge,
Ring,
Off,
}
private BoxContainer? _radarModeButtons;
private RadarModeButton? _radarAzimuthButton;
private RadarModeButton? _radarRotationButton;
private RadarModeButton? _radarAnchorButton;
private RadarModeButton? _radarResetButton;
private RadarAzimuthMode _azimuthMode;
public ShuttleNavControl() : this(64f, 256f, 256f) { } // Mono
public ShuttleNavControl(float minRange, float maxRange, float range) : base(minRange, maxRange, range) // Mono
@@ -135,12 +152,279 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
return;
_coordinates = coordinates;
_rotation = angle;
// Mono - TODO: either remove this or make this do anything
// _rotation = angle;
}
public void SetConsole(EntityUid? consoleEntity)
{
if (_consoleEntity == consoleEntity)
return;
_consoleEntity = consoleEntity;
_radarModeDefaultsApplied = false;
ApplyRadarModeDefaults();
}
// converts Offset to world-relative offset so we can modify radar parameters without conversion headaches
// call this before potentially rotation-modifying operations and PanToEnt() after
private void PanToWorld()
{
if (!_angleFollow || _coordinates is not { } cord)
return;
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
Offset = coordEntRot.RotateVec(Offset);
}
// converts world-relative Offset to relative to current view
// call this after potentially rotation-modifying operations and PanToWorld() before
private void PanToEnt()
{
if (!_angleFollow || _coordinates is not { } cord)
return;
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
Offset = (-coordEntRot).RotateVec(Offset);
}
// reanchor our view to console or grid
private void ReanchorView()
{
if (_consoleEntity is not { } cons
|| _coordinates is not { } cord
|| !EntManager.TryGetComponent<TransformComponent>(cons, out var xform))
return;
PanToWorld();
var anchTo = cons;
if (xform.GridUid != null)
anchTo = xform.GridUid.Value;
_coordinates = _transform.WithEntityId(cord, anchTo);
_relativePanning = true;
PanToEnt();
}
private void EnsureRadarModeButtons()
{
if (_radarModeButtons != null)
return;
if (Parent is not LayoutContainer parent)
return;
LayoutContainer.SetAnchorPreset(this, LayoutContainer.LayoutPreset.Wide);
_radarModeButtons = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Horizontal,
SeparationOverride = 2,
HorizontalExpand = false,
VerticalExpand = false,
MouseFilter = MouseFilterMode.Stop,
};
_radarAzimuthButton = CreateRadarModeButton(RadarModeButtonIcon.Azimuth);
_radarAzimuthButton.ToolTip = Loc.GetString("shuttle-console-button-azimuth");
_radarAzimuthButton.OnPressed += _ =>
{
_azimuthMode = _azimuthMode switch
{
RadarAzimuthMode.Edge => RadarAzimuthMode.Ring,
RadarAzimuthMode.Ring => RadarAzimuthMode.Off,
_ => RadarAzimuthMode.Edge,
};
};
_radarRotationButton = CreateRadarModeButton(RadarModeButtonIcon.Rotation);
_radarRotationButton.ToolTip = Loc.GetString("shuttle-console-button-rotation");
_radarRotationButton.OnPressed += _ =>
{
PanToWorld();
_angleFollow ^= true;
PanToEnt();
};
_radarAnchorButton = CreateRadarModeButton(RadarModeButtonIcon.Anchor);
_radarAnchorButton.ToolTip = Loc.GetString("shuttle-console-button-anchor");
_radarAnchorButton.OnPressed += _ =>
{
if (_coordinates is not { } cord)
return;
// if we're anchored or not yet bound to map, just deanchor
if (_relativePanning || !EntManager.HasComponent<MapComponent>(cord.EntityId))
{
PanToWorld();
// we're deanchoring, so simply detach our view to the map
_coordinates = _transform.ToCoordinates(_transform.ToMapCoordinates(cord));
_relativePanning = false;
_wasPanned = true;
PanToEnt();
}
else
{
ReanchorView();
}
};
_radarResetButton = CreateRadarModeButton(RadarModeButtonIcon.Reset);
_radarResetButton.ToolTip = Loc.GetString("shuttle-console-button-reset");
_radarResetButton.OnPressed += _ =>
{
_radarModeDefaultsApplied = false;
ApplyRadarModeDefaults();
if (EntManager.TryGetComponent<TransformComponent>(_consoleEntity, out var xform))
{
Offset = Vector2.Zero;
_coordinates = xform.Coordinates;
ReanchorView();
}
};
_radarModeButtons.AddChild(_radarAzimuthButton);
_radarModeButtons.AddChild(_radarRotationButton);
_radarModeButtons.AddChild(_radarAnchorButton);
_radarModeButtons.AddChild(_radarResetButton);
parent.AddChild(_radarModeButtons);
LayoutContainer.SetAnchorAndMarginPreset(_radarModeButtons, LayoutContainer.LayoutPreset.BottomLeft, margin: 10);
}
private void ApplyRadarModeDefaults()
{
if (_radarModeDefaultsApplied)
return;
if (!EntManager.TryGetComponent<RadarConsoleComponent>(_consoleEntity, out var radar))
return;
_radarModeDefaultsApplied = true;
Draggable = radar.Pannable;
_angleFollow = !radar.NoRotate;
_relativePanning = radar.RelativePanning;
}
private static RadarModeButton CreateRadarModeButton(RadarModeButtonIcon icon = RadarModeButtonIcon.None)
{
return new RadarModeButton(icon)
{
MinWidth = 16f,
MaxWidth = 16f,
MinHeight = 16f,
MaxHeight = 16f,
HorizontalExpand = false,
VerticalExpand = false,
ModulateSelfOverride = Color.White.WithAlpha(0.55f),
};
}
internal enum RadarModeButtonIcon : byte
{
None,
Azimuth,
Rotation,
Anchor,
Reset
}
public sealed class RadarModeButton : BaseButton
{
private readonly RadarModeButtonIcon _icon;
internal RadarModeButton(RadarModeButtonIcon icon)
{
_icon = icon;
}
protected override Vector2 MeasureOverride(Vector2 availableSize)
{
return new Vector2(16f, 16f);
}
protected override void Draw(DrawingHandleScreen handle)
{
var alpha = DrawMode switch
{
DrawModeEnum.Pressed => 0.62f,
DrawModeEnum.Hover => 0.48f,
_ => 0.32f,
};
var center = new Vector2(PixelSize.X * 0.5f, PixelSize.Y * 0.5f);
var radius = MathF.Min(PixelSize.X, PixelSize.Y) * 0.42f;
handle.DrawCircle(center, radius, Color.White.WithAlpha(alpha));
var up = new Vector2(0f, -radius);
var down = new Vector2(0f, radius);
var leftOffs = new Vector2(-radius, 0f);
var rightOffs = new Vector2(radius, 0f);
var top = center + up;
var bottom = center + down;
var left = center + leftOffs;
var right = center + rightOffs;
switch (_icon) {
case RadarModeButtonIcon.Azimuth:
{
var iconColor = Color.Black.WithAlpha(0.95f);
var topLeft = center + (up + leftOffs) / MathF.Sqrt(2f);
var topRight = center + (up + rightOffs) / MathF.Sqrt(2f);
var bottomLeft = center + (down + leftOffs) / MathF.Sqrt(2f);
var bottomRight = center + (down + rightOffs) / MathF.Sqrt(2f);
handle.DrawLine(topLeft, bottomRight, iconColor);
handle.DrawLine(bottomLeft, topRight, iconColor);
handle.DrawLine(left, right, iconColor);
handle.DrawLine(bottom, top, iconColor);
break;
}
case RadarModeButtonIcon.Rotation:
{
var iconColor = Color.Yellow.WithAlpha(0.95f);
var innerRatio = 0.8f;
var segCount = 3;
var prevOffs = up * innerRatio;
for (var i = 0; i < segCount; i++)
{
var angle = Angle.FromDegrees(120f * i / (float)segCount - 15f);
var thisOffs = angle.RotateVec(up * innerRatio);
var thisPointUp = center + thisOffs;
var thisPointDown = center - thisOffs;
var oldPointUp = center + prevOffs;
var oldPointDown = center - prevOffs;
handle.DrawLine(oldPointUp, thisPointUp, iconColor);
handle.DrawLine(oldPointDown, thisPointDown, iconColor);
prevOffs = thisOffs;
}
break;
}
case RadarModeButtonIcon.Anchor:
{
var iconColor = Color.Blue.WithAlpha(0.95f);
var stemTop = center + up * 0.5f;
var crossLeft = center + leftOffs * 0.3f + up * 0.7f;
var crossRight = center + rightOffs * 0.3f + up * 0.7f;;
var leftFluke = center + leftOffs * 0.6f + down * 0.6f;
var rightFluke = center + rightOffs * 0.6f + down * 0.6f;
var innerRadius = radius * 0.4f;
var innerTop = center + up * 0.6f;
handle.DrawCircle(innerTop, innerRadius, iconColor, false);
handle.DrawLine(stemTop, bottom, iconColor);
handle.DrawLine(crossLeft, crossRight, iconColor);
handle.DrawLine(bottom, leftFluke, iconColor);
handle.DrawLine(bottom, rightFluke, iconColor);
break;
}
case RadarModeButtonIcon.Reset:
{
var iconColor = Color.Red.WithAlpha(0.95f);
handle.DrawLine(left, right, iconColor);
break;
}
default:
break;
}
}
}
// Mono - evil hack
@@ -153,18 +437,15 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
if (!_draggin || _coordinates == null)
return;
if (!_wasPanned)
{
_wasPanned = true;
_oldCoordinates = RelativePanning ? _coordinates.Value : _transform.ToCoordinates(_transform.ToMapCoordinates(_coordinates.Value));
if (!RelativePanning)
_rotation = new Angle(0);
}
if (_oldCoordinates == null)
return;
if (!_relativePanning
&& _coordinates?.EntityId is { } coordEnt
&& EntManager.TryGetComponent<TransformComponent>(coordEnt, out var coordXform)
&& coordXform.MapUid != coordEnt
)
_coordinates = _transform.ToCoordinates(_transform.ToMapCoordinates(_coordinates.Value));
var offs = (_rotation ?? new Angle(0)).RotateVec(InverseMapPosition(MidPointVector));
_coordinates = _oldCoordinates.Value.Offset(offs);
_wasPanned = true;
}
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
@@ -184,21 +465,11 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
base.KeyBindUp(args);
if (args.Function != EngineKeyFunctions.UIClick)
{
return;
}
_isMouseDown = false;
if (_coordinates == null || _rotation == null || OnRadarClick == null)
{
return;
}
var a = InverseScalePosition(args.RelativePosition);
var relativeWorldPos = a with { Y = -a.Y };
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
var coords = _coordinates.Value.Offset(relativeWorldPos);
var coords = GetMouseEntityCoordinates(args.RelativePosition);
OnRadarClick?.Invoke(coords);
}
@@ -235,13 +506,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
private void TryFireAtPosition(Vector2 relativePosition)
{
if (_coordinates == null || _rotation == null || OnRadarClick == null)
return;
var a = InverseScalePosition(relativePosition);
var relativeWorldPos = new Vector2(a.X, -a.Y);
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
var coords = _coordinates.Value.Offset(relativeWorldPos);
var coords = GetMouseEntityCoordinates(relativePosition);
OnRadarClick?.Invoke(coords);
}
@@ -257,14 +522,13 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
}
var pos = _uiManager.MousePositionScaled.Position - GlobalPosition;
var relativeWorldPos = _rotation.Value.RotateVec(pos);
// I am not sure why the resulting point is 20 units under the mouse.
return _coordinates.Value.Offset(relativeWorldPos);
return GetMouseEntityCoordinates(pos);
}
public void UpdateState(NavInterfaceState state)
{
EnsureRadarModeButtons();
ApplyRadarModeDefaults();
SetMatrix(EntManager.GetCoordinates(state.Coordinates), state.Angle);
WorldMaxRange = state.MaxRange;
@@ -279,14 +543,6 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
ActualRadarRange = Math.Clamp(ActualRadarRange, WorldMinRange, WorldMaxRange);
// Mono
RelativePanning = state.RelativePanning;
Draggable = state.Pannable;
if (!Draggable)
_wasPanned = false; // also reset
RotateWithEntity = state.RotateWithEntity;
// Frontier
if (state.MaxIffRange != null)
MaximumIFFDistance = state.MaxIffRange.Value;
@@ -321,12 +577,24 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
return;
}
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
var posMatrix = Matrix3Helpers.CreateTransform(_coordinates.Value.Position, _rotation.Value);
var ourEntRot = _wasPanned && !RelativePanning ? new Angle(0) : RotateWithEntity ? _transform.GetWorldRotation(xform) : _rotation.Value;
var ourEntMatrix = Matrix3Helpers.CreateTransform(_transform.GetWorldPosition(xform), ourEntRot);
var shuttleToWorld = Matrix3x2.Multiply(posMatrix, ourEntMatrix);
Matrix3x2.Invert(shuttleToWorld, out var worldToShuttle);
// Mono
if (EntManager.TryGetComponent<RadarConsoleComponent>(_consoleEntity, out var radar))
{
Draggable = radar.Pannable;
}
// Mono - TODO: remove _rotation and make it a method instead
var coordEnt = _coordinates.Value.EntityId;
var coordEntRot = _transform.GetWorldRotation(coordEnt);
if (_angleFollow && EntManager.TryGetComponent<TransformComponent>(coordEnt, out var coordXform))
_rotation = coordEntRot;
else
_rotation = Angle.Zero;
var worldRot = _rotation.Value;
var mapPos = _transform.ToMapCoordinates(_coordinates.Value).Offset(_rotation.Value.RotateVec(Offset));
var mapCoord = _transform.ToCoordinates(mapPos);
var worldToShuttle = Matrix3Helpers.CreateTranslation(-mapCoord.Position) * Matrix3Helpers.CreateRotation(-worldRot);
Matrix3x2.Invert(worldToShuttle, out var shuttleToWorld);
var shuttleToView = Matrix3x2.CreateScale(new Vector2(MinimapScale, -MinimapScale)) * Matrix3x2.CreateTranslation(MidPointVector);
var worldToView = worldToShuttle * shuttleToView;
@@ -334,8 +602,23 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
DrawShields(handle, xform, worldToShuttle);
// Frontier Corvax: north line drawing
var rot = ourEntRot + _rotation.Value;
DrawNorthLine(handle, rot);
DrawNorthLine(handle, worldRot);
// Mono
switch (_azimuthMode)
{
case RadarAzimuthMode.Edge:
DrawAzimuthScale(handle, worldRot);
break;
case RadarAzimuthMode.Ring:
DrawAzimuthRing(handle, worldRot);
break;
}
if (worldRot != Angle.Zero)
{
DrawCompassOverlay(handle, coordEntRot);
}
// Draw our grid in detail
var ourGridId = xform.GridUid;
@@ -361,7 +644,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, radarPosVerts, Color.Lime);
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), rot, mapPos.Position);
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), worldRot, mapPos.Position);
var viewAABB = viewBounds.CalcBoundingBox();
_grids.Clear();
@@ -426,9 +709,8 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
//shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, mapCenter, curGridToWorld); // Frontier code
// Frontier: range checks // Mono
var gridMapPos = _transform.ToMapCoordinates(new EntityCoordinates(gUid, gridBody.LocalCenter)).Position;
var ourPos = _transform.ToMapCoordinates(_coordinates.Value);
if (!hideLabel) // Mono - show thermal signatures even at long range
shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, ourPos.Position - gridMapPos);
shouldDrawIFF = NfCheckShouldDrawIffRangeCondition(shouldDrawIFF, mapPos.Position - gridMapPos);
// End Frontier
// Mono
@@ -629,7 +911,7 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
if (blip.Config.RespectZoom)
box.Box = new Box2(box.Box.BottomLeft * MinimapScale, box.Box.TopRight * MinimapScale);
if (blip.Config.Rotate)
box.Rotation = ourEntRot - blip.Rotation;
box.Rotation = worldRot - blip.Rotation;
if (blip.GridUid is { } grid)
{
@@ -873,11 +1155,24 @@ public partial class ShuttleNavControl : BaseShuttleControl // Mono
protected Vector2 InverseScalePosition(Vector2 value)
{
// Account for UI scaling: value is unscaled, so adjust by UIScale
var scaledValue = value * UIScale;
return (scaledValue - MidPointVector) / MinimapScale;
}
// Mono
protected EntityCoordinates GetMouseEntityCoordinates(Vector2 relativePosition)
{
if (_coordinates is not { } cord || _rotation is not { } rot)
return new();
// multiply by UIScale because UI is Very Intelligent and doesn't scale mouse position for whatever reason
var screenRelativeWorldPos = InverseMapPosition(relativePosition * UIScale);
var relativeWorldPos = rot.RotateVec(screenRelativeWorldPos);
var coordEntRot = _transform.GetWorldRotation(cord.EntityId);
var coords = cord.Offset((-coordEntRot).RotateVec(relativeWorldPos));
return coords;
}
public class BlipData
{
public bool IsOutsideRadarCircle { get; set; }
@@ -1,4 +1,5 @@
using System.Numerics;
using Content.Client.Graphics;
using Content.Shared.Silicons.StationAi;
using Robust.Client.Graphics;
using Robust.Client.Player;
@@ -12,6 +13,10 @@ namespace Content.Client.Silicons.StationAi;
public sealed class StationAiOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> CameraStaticShader = "CameraStatic";
private static readonly ProtoId<ShaderPrototype> StencilMaskShader = "StencilMask";
private static readonly ProtoId<ShaderPrototype> StencilDrawShader = "StencilDraw";
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
@@ -22,8 +27,7 @@ public sealed class StationAiOverlay : Overlay
private readonly HashSet<Vector2i> _visibleTiles = new();
private IRenderTexture? _staticTexture;
private IRenderTexture? _stencilTexture;
private readonly OverlayResourceCache<CachedResources> _resources = new();
private float _updateRate = 1f / 30f;
private float _accumulator;
@@ -35,12 +39,14 @@ public sealed class StationAiOverlay : Overlay
protected override void Draw(in OverlayDrawArgs args)
{
if (_stencilTexture?.Texture.Size != args.Viewport.Size)
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (res.StencilTexture?.Texture.Size != args.Viewport.Size)
{
_staticTexture?.Dispose();
_stencilTexture?.Dispose();
_stencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil");
_staticTexture = _clyde.CreateRenderTarget(args.Viewport.Size,
res.StaticTexture?.Dispose();
res.StencilTexture?.Dispose();
res.StencilTexture = _clyde.CreateRenderTarget(args.Viewport.Size, new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb), name: "station-ai-stencil");
res.StaticTexture = _clyde.CreateRenderTarget(args.Viewport.Size,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
name: "station-ai-static");
}
@@ -74,7 +80,7 @@ public sealed class StationAiOverlay : Overlay
var matty = Matrix3x2.Multiply(gridMatrix, invMatrix);
// Draw visible tiles to stencil
worldHandle.RenderInRenderTarget(_stencilTexture!, () =>
worldHandle.RenderInRenderTarget(res.StencilTexture!, () =>
{
worldHandle.SetTransform(matty);
@@ -87,11 +93,11 @@ public sealed class StationAiOverlay : Overlay
Color.Transparent);
// Once this is gucci optimise rendering.
worldHandle.RenderInRenderTarget(_staticTexture!,
worldHandle.RenderInRenderTarget(res.StaticTexture!,
() =>
{
worldHandle.SetTransform(invMatrix);
var shader = _proto.Index<ShaderPrototype>("CameraStatic").Instance();
var shader = _proto.Index(CameraStaticShader).Instance();
worldHandle.UseShader(shader);
worldHandle.DrawRect(worldBounds, Color.White);
},
@@ -100,12 +106,12 @@ public sealed class StationAiOverlay : Overlay
// Not on a grid
else
{
worldHandle.RenderInRenderTarget(_stencilTexture!, () =>
worldHandle.RenderInRenderTarget(res.StencilTexture!, () =>
{
},
Color.Transparent);
worldHandle.RenderInRenderTarget(_staticTexture!,
worldHandle.RenderInRenderTarget(res.StaticTexture!,
() =>
{
worldHandle.SetTransform(Matrix3x2.Identity);
@@ -114,15 +120,34 @@ public sealed class StationAiOverlay : Overlay
}
// Use the lighting as a mask
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilMask").Instance());
worldHandle.DrawTextureRect(_stencilTexture!.Texture, worldBounds);
worldHandle.UseShader(_proto.Index(StencilMaskShader).Instance());
worldHandle.DrawTextureRect(res.StencilTexture!.Texture, worldBounds);
// Draw the static
worldHandle.UseShader(_proto.Index<ShaderPrototype>("StencilDraw").Instance());
worldHandle.DrawTextureRect(_staticTexture!.Texture, worldBounds);
worldHandle.UseShader(_proto.Index(StencilDrawShader).Instance());
worldHandle.DrawTextureRect(res.StaticTexture!.Texture, worldBounds);
worldHandle.SetTransform(Matrix3x2.Identity);
worldHandle.UseShader(null);
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
private sealed class CachedResources : IDisposable
{
public IRenderTexture? StaticTexture;
public IRenderTexture? StencilTexture;
public void Dispose()
{
StaticTexture?.Dispose();
StencilTexture?.Dispose();
}
}
}
+5 -4
View File
@@ -11,6 +11,7 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
{
[Dependency] private readonly IReflectionManager _reflection = default!;
[Dependency] private readonly ClientClothingSystem _clothing = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -63,10 +64,10 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
int index;
if (_reflection.TryParseEnumReference(layer.Key, out var @enum))
{
if (!sprite.LayerMapTryGet(@enum, out index, logError: true))
if (!_sprite.LayerMapTryGet((uid, sprite), @enum, out index, logMissing: true))
continue;
}
else if (!sprite.LayerMapTryGet(layer.Key, out index))
else if (!_sprite.LayerMapTryGet((uid, sprite), layer.Key, out index, false))
{
if (layer.Key is not { } strKey || !int.TryParse(strKey, out index))
{
@@ -74,8 +75,8 @@ public sealed class RandomSpriteSystem : SharedRandomSpriteSystem
continue;
}
}
sprite.LayerSetState(index, layer.Value.State);
sprite.LayerSetColor(index, layer.Value.Color ?? Color.White);
_sprite.LayerSetRsiState((uid, sprite), index, layer.Value.State);
_sprite.LayerSetColor((uid, sprite), index, layer.Value.Color ?? Color.White);
}
}
}
@@ -10,6 +10,7 @@ namespace Content.Client.Storage.Systems;
public sealed class ItemCounterSystem : SharedItemCounterSystem
{
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -41,23 +42,23 @@ public sealed class ItemCounterSystem : SharedItemCounterSystem
public void ProcessOpaqueSprite(EntityUid uid, string layer, int count, int maxCount, List<string> states, bool hide = false, SpriteComponent? sprite = null)
{
if (!Resolve(uid, ref sprite)
|| !sprite.LayerMapTryGet(layer, out var layerKey, logError: true))
|| !_sprite.LayerMapTryGet((uid, sprite), layer, out var layerKey, logMissing: true))
return;
var activeState = ContentHelpers.RoundToEqualLevels(count, maxCount, states.Count);
sprite.LayerSetState(layerKey, states[activeState]);
sprite.LayerSetVisible(layerKey, !hide);
_sprite.LayerSetRsiState((uid, sprite), layerKey, states[activeState]);
_sprite.LayerSetVisible((uid, sprite), layerKey, !hide);
}
public void ProcessCompositeSprite(EntityUid uid, int count, int maxCount, List<string> layers, bool hide = false, SpriteComponent? sprite = null)
{
if(!Resolve(uid, ref sprite))
if (!Resolve(uid, ref sprite))
return;
var activeTill = ContentHelpers.RoundToNearestLevels(count, maxCount, layers.Count);
for(var i = 0; i < layers.Count; ++i)
for (var i = 0; i < layers.Count; ++i)
{
sprite.LayerSetVisible(layers[i], !hide && i < activeTill);
_sprite.LayerSetVisible((uid, sprite), layers[i], !hide && i < activeTill);
}
}
@@ -9,6 +9,7 @@ namespace Content.Client.Storage.Systems;
public sealed class ItemMapperSystem : SharedItemMapperSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -48,9 +49,9 @@ public sealed class ItemMapperSystem : SharedItemMapperSystem
foreach (var sprite in component.SpriteLayers)
{
spriteComponent.LayerMapReserveBlank(sprite);
spriteComponent.LayerSetSprite(sprite, new SpriteSpecifier.Rsi(component.RSIPath!.Value, sprite));
spriteComponent.LayerSetVisible(sprite, false);
_sprite.LayerMapReserve((owner, spriteComponent), sprite);
_sprite.LayerSetSprite((owner, spriteComponent), sprite, new SpriteSpecifier.Rsi(component.RSIPath!.Value, sprite));
_sprite.LayerSetVisible((owner, spriteComponent), sprite, false);
}
}
@@ -63,7 +64,7 @@ public sealed class ItemMapperSystem : SharedItemMapperSystem
foreach (var layerName in component.SpriteLayers)
{
var show = wrapper.QueuedEntities.Contains(layerName);
spriteComponent.LayerSetVisible(layerName, show);
_sprite.LayerSetVisible((owner, spriteComponent), layerName, show);
}
}
}
@@ -8,6 +8,8 @@ namespace Content.Client.Storage.Systems;
/// <inheritdoc cref="StorageContainerVisualsComponent"/>
public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageContainerVisualsComponent>
{
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, StorageContainerVisualsComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
@@ -19,9 +21,9 @@ public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageCont
if (!AppearanceSystem.TryGetData<int>(uid, StorageVisuals.Capacity, out var capacity, args.Component))
return;
var fraction = used / (float) capacity;
var fraction = used / (float)capacity;
if (!args.Sprite.LayerMapTryGet(component.FillLayer, out var fillLayer))
if (!_sprite.LayerMapTryGet((uid, args.Sprite), component.FillLayer, out var fillLayer, false))
return;
var closestFillSprite = Math.Min(ContentHelpers.RoundToNearestLevels(fraction, 1, component.MaxFillLevels + 1),
@@ -32,13 +34,13 @@ public sealed class StorageContainerVisualsSystem : VisualizerSystem<StorageCont
if (component.FillBaseName == null)
return;
args.Sprite.LayerSetVisible(fillLayer, true);
_sprite.LayerSetVisible((uid, args.Sprite), fillLayer, true);
var stateName = component.FillBaseName + closestFillSprite;
args.Sprite.LayerSetState(fillLayer, stateName);
_sprite.LayerSetRsiState((uid, args.Sprite), fillLayer, stateName);
}
else
{
args.Sprite.LayerSetVisible(fillLayer, false);
_sprite.LayerSetVisible((uid, args.Sprite), fillLayer, false);
}
}
}
@@ -57,6 +57,7 @@ public sealed partial class SurveillanceCameraMonitorWindow : DefaultWindow
CameraView.ViewportSize = new Vector2i(500, 500);
CameraView.Eye = _defaultEye; // sure
CameraView.Shader = _prototypeManager.Index<ShaderPrototype>("crt_vhs").Instance().Duplicate(); // Mono - fix shader ID
CameraViewBackground.Stretch = TextureRect.StretchMode.Scale;
CameraViewBackground.Texture = texture;
CameraViewBackground.ShaderOverride = shader;
@@ -1,32 +0,0 @@
using Content.Shared.Hands.Components;
namespace Content.Client.Toggleable;
/// <summary>
/// Component that handles the toggling the visuals of some light emitting entity.
/// </summary>
/// <remarks>
/// This will toggle the visibility of layers on an entity's sprite, the in-hand visuals, and the clothing/equipment
/// visuals. This will modify the color of any attached point lights.
/// </remarks>
[RegisterComponent]
public sealed partial class ToggleableLightVisualsComponent : Component
{
/// <summary>
/// Sprite layer that will have its visibility toggled when this item is toggled.
/// </summary>
[DataField("spriteLayer")]
public string? SpriteLayer = "light";
/// <summary>
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
/// </summary>
[DataField("inhandVisuals")]
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
/// <summary>
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
/// </summary>
[DataField("clothingVisuals")]
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
}
@@ -1,126 +0,0 @@
using Content.Client.Clothing;
using Content.Client.Items.Systems;
using Content.Shared.Clothing;
using Content.Shared.Hands;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Toggleable;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
using System.Linq;
namespace Content.Client.Toggleable;
public sealed class ToggleableLightVisualsSystem : VisualizerSystem<ToggleableLightVisualsComponent>
{
[Dependency] private readonly SharedItemSystem _itemSys = default!;
[Dependency] private readonly SharedPointLightSystem _lights = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals, after: new[] { typeof(ItemSystem) });
SubscribeLocalEvent<ToggleableLightVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals, after: new[] { typeof(ClientClothingSystem) });
}
protected override void OnAppearanceChange(EntityUid uid, ToggleableLightVisualsComponent component, ref AppearanceChangeEvent args)
{
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, args.Component))
return;
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, args.Component);
// Update the item's sprite
if (args.Sprite != null && component.SpriteLayer != null && args.Sprite.LayerMapTryGet(component.SpriteLayer, out var layer))
{
args.Sprite.LayerSetVisible(layer, enabled);
if (modulate)
args.Sprite.LayerSetColor(layer, color);
}
// Update any point-lights
if (TryComp(uid, out PointLightComponent? light))
{
DebugTools.Assert(!light.NetSyncEnabled, "light visualizers require point lights without net-sync");
_lights.SetEnabled(uid, enabled, light);
if (enabled && modulate)
{
_lights.SetColor(uid, color, light);
}
}
// update clothing & in-hand visuals.
_itemSys.VisualsChanged(uid);
}
/// <summary>
/// Add the unshaded light overlays to any clothing sprites.
/// </summary>
private void OnGetEquipmentVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetEquipmentVisualsEvent args)
{
if (!TryComp(uid, out AppearanceComponent? appearance)
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|| !enabled)
return;
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
return;
List<PrototypeLayerData>? layers = null;
// attempt to get species specific data
if (inventory.SpeciesId != null)
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
// No species specific data. Try to default to generic data.
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
return;
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
var i = 0;
foreach (var layer in layers)
{
var key = layer.MapKeys?.FirstOrDefault();
if (key == null)
{
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
i++;
}
if (modulate)
layer.Color = color;
args.Layers.Add((key, layer));
}
}
private void OnGetHeldVisuals(EntityUid uid, ToggleableLightVisualsComponent component, GetInhandVisualsEvent args)
{
if (!TryComp(uid, out AppearanceComponent? appearance)
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableLightVisuals.Enabled, out var enabled, appearance)
|| !enabled)
return;
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
return;
var modulate = AppearanceSystem.TryGetData<Color>(uid, ToggleableLightVisuals.Color, out var color, appearance);
var i = 0;
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
foreach (var layer in layers)
{
var key = layer.MapKeys?.FirstOrDefault();
if (key == null)
{
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
i++;
}
if (modulate)
layer.Color = color;
args.Layers.Add((key, layer));
}
}
}
@@ -0,0 +1,30 @@
using Content.Shared.Hands.Components;
namespace Content.Client.Toggleable;
/// <summary>
/// Component that handles toggling the visuals of an entity, including layers on an entity's sprite,
/// the in-hand visuals, and the clothing/equipment visuals.
/// </summary>
/// <see cref="ToggleableVisualsSystem"/>
[RegisterComponent]
public sealed partial class ToggleableVisualsComponent : Component
{
/// <summary>
/// Sprite layer that will have its visibility toggled when this item is toggled.
/// </summary>
[DataField(required: true)]
public string? SpriteLayer;
/// <summary>
/// Layers to add to the sprite of the player that is holding this entity (while the component is toggled on).
/// </summary>
[DataField]
public Dictionary<HandLocation, List<PrototypeLayerData>> InhandVisuals = new();
/// <summary>
/// Layers to add to the sprite of the player that is wearing this entity (while the component is toggled on).
/// </summary>
[DataField]
public Dictionary<string, List<PrototypeLayerData>> ClothingVisuals = new();
}
@@ -0,0 +1,140 @@
using System.Linq;
using Content.Client.Clothing;
using Content.Client.Items.Systems;
using Content.Shared.Clothing;
using Content.Shared.Hands;
using Content.Shared.Inventory;
using Content.Shared.Item;
using Content.Shared.Light.Components;
using Content.Shared.Toggleable;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
namespace Content.Client.Toggleable;
/// <summary>
/// Implements the behavior of <see cref="ToggleableVisualsComponent"/> by reacting to
/// <see cref="AppearanceChangeEvent"/>, for the sprite directly; <see cref="OnGetHeldVisuals"/> for the
/// in-hand visuals; and <see cref="OnGetEquipmentVisuals"/> for the clothing visuals.
/// </summary>
/// <see cref="ToggleableVisualsComponent"/>
public sealed class ToggleableVisualsSystem : VisualizerSystem<ToggleableVisualsComponent>
{
[Dependency] private readonly SharedItemSystem _item = default!;
[Dependency] private readonly SharedPointLightSystem _pointLight = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ToggleableVisualsComponent, GetInhandVisualsEvent>(OnGetHeldVisuals,
after: [typeof(ItemSystem)]);
SubscribeLocalEvent<ToggleableVisualsComponent, GetEquipmentVisualsEvent>(OnGetEquipmentVisuals,
after: [typeof(ClientClothingSystem)]);
}
protected override void OnAppearanceChange(EntityUid uid,
ToggleableVisualsComponent component,
ref AppearanceChangeEvent args)
{
if (!AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, args.Component))
return;
var modulateColor =
AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, args.Component);
// Update the item's sprite
if (args.Sprite != null && component.SpriteLayer != null &&
SpriteSystem.LayerMapTryGet((uid, args.Sprite), component.SpriteLayer, out var layer, false))
{
SpriteSystem.LayerSetVisible((uid, args.Sprite), layer, enabled);
if (modulateColor)
SpriteSystem.LayerSetColor((uid, args.Sprite), component.SpriteLayer, color);
}
// If there's a `ItemTogglePointLightComponent` that says to apply the color to attached lights, do so.
if (TryComp<ItemTogglePointLightComponent>(uid, out var toggleLights) &&
TryComp(uid, out PointLightComponent? light))
{
DebugTools.Assert(!light.NetSyncEnabled,
$"{typeof(ItemTogglePointLightComponent)} requires point lights without net-sync");
_pointLight.SetEnabled(uid, enabled, light);
if (modulateColor && toggleLights.ToggleableVisualsColorModulatesLights)
{
_pointLight.SetColor(uid, color, light);
}
}
// update clothing & in-hand visuals.
_item.VisualsChanged(uid);
}
private void OnGetEquipmentVisuals(EntityUid uid,
ToggleableVisualsComponent component,
GetEquipmentVisualsEvent args)
{
if (!TryComp(uid, out AppearanceComponent? appearance)
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|| !enabled)
return;
if (!TryComp(args.Equipee, out InventoryComponent? inventory))
return;
List<PrototypeLayerData>? layers = null;
// attempt to get species specific data
if (inventory.SpeciesId != null)
component.ClothingVisuals.TryGetValue($"{args.Slot}-{inventory.SpeciesId}", out layers);
// No species specific data. Try to default to generic data.
if (layers == null && !component.ClothingVisuals.TryGetValue(args.Slot, out layers))
return;
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
var i = 0;
foreach (var layer in layers)
{
var key = layer.MapKeys?.FirstOrDefault();
if (key == null)
{
key = i == 0 ? $"{args.Slot}-toggle" : $"{args.Slot}-toggle-{i}";
i++;
}
if (modulateColor)
layer.Color = color;
args.Layers.Add((key, layer));
}
}
private void OnGetHeldVisuals(EntityUid uid, ToggleableVisualsComponent component, GetInhandVisualsEvent args)
{
if (!TryComp(uid, out AppearanceComponent? appearance)
|| !AppearanceSystem.TryGetData<bool>(uid, ToggleableVisuals.Enabled, out var enabled, appearance)
|| !enabled)
return;
if (!component.InhandVisuals.TryGetValue(args.Location, out var layers))
return;
var modulateColor = AppearanceSystem.TryGetData<Color>(uid, ToggleableVisuals.Color, out var color, appearance);
var i = 0;
var defaultKey = $"inhand-{args.Location.ToString().ToLowerInvariant()}-toggle";
foreach (var layer in layers)
{
var key = layer.MapKeys?.FirstOrDefault();
if (key == null)
{
key = i == 0 ? defaultKey : $"{defaultKey}-{i}";
i++;
}
if (modulateColor)
layer.Color = color;
args.Layers.Add((key, layer));
}
}
}
@@ -257,7 +257,7 @@ public sealed partial class ChatUIController : IOnSystemChanged<CharacterInfoSys
}
// Make it so it doesn't hightlight your own name when you are talking
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*)");
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*|""\[/color\].*)");
return keyword;
}
@@ -76,7 +76,7 @@ public sealed class DamageOverlayUiController : UIController
{
if (mobState == null && !EntityManager.TryGetComponent(entity, out mobState) ||
thresholds == null && !EntityManager.TryGetComponent(entity, out thresholds) ||
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
damageable == null && !EntityManager.TryGetComponent(entity, out damageable))
return;
if (!_mobThresholdSystem.TryGetIncapThreshold(entity, out var foundThreshold, thresholds))
@@ -94,47 +94,48 @@ public sealed class DamageOverlayUiController : UIController
switch (mobState.CurrentState)
{
case MobState.Alive:
{
if (EntityManager.HasComponent<PainNumbnessComponent>(entity))
{
_overlay.BruteLevel = 0;
}
else if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
{
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
}
if (!EntityManager.HasComponent<PainNumbnessComponent>(entity)) // Mono - makes this look better
{
if (damageable.DamagePerGroup.TryGetValue("Brute", out var bruteDamage))
_overlay.BruteLevel = FixedPoint2.Min(1f, bruteDamage / critThreshold).Float();
if (damageable.DamagePerGroup.TryGetValue("Burn", out var burnDamage)) // Adds burn damage to pain overlay - Mono
_overlay.BruteLevel = FixedPoint2.Min(1f, _overlay.BruteLevel + (burnDamage / critThreshold)).Float();
}
else
_overlay.BruteLevel = 0;
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
{
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
}
if (damageable.DamagePerGroup.TryGetValue("Airloss", out var oxyDamage))
{
_overlay.OxygenLevel = FixedPoint2.Min(1f, oxyDamage / critThreshold).Float();
}
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
{
_overlay.BruteLevel = 0;
}
if (_overlay.BruteLevel < 0.05f) // Don't show damage overlay if they're near enough to max.
{
_overlay.BruteLevel = 0;
}
_overlay.CritLevel = 0;
_overlay.DeadLevel = 0;
break;
}
_overlay.CritLevel = 0;
_overlay.DeadLevel = 0;
break;
}
case MobState.Critical:
{
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
return;
_overlay.CritLevel = critLevel.Value.Float();
{
if (!_mobThresholdSystem.TryGetDeadPercentage(entity,
FixedPoint2.Max(0.0, damageable.TotalDamage), out var critLevel))
return;
_overlay.CritLevel = critLevel.Value.Float();
_overlay.BruteLevel = 0;
_overlay.DeadLevel = 0;
break;
}
_overlay.BruteLevel = 0;
_overlay.DeadLevel = 0;
break;
}
case MobState.Dead:
{
_overlay.BruteLevel = 0;
_overlay.CritLevel = 0;
break;
}
{
_overlay.BruteLevel = 0;
_overlay.CritLevel = 0;
break;
}
}
}
}
@@ -82,7 +82,7 @@ namespace Content.Client.VendingMachines.UI
/// Populates the list of available items on the vending machine interface
/// and sets icons based on their prototypes
/// </summary>
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance) // Frontier: add balance, cashSlotBalance
public void Populate(List<VendingMachineInventoryEntry> inventory, float priceModifier, int balance, int? cashSlotBalance, bool requiresCash) // Frontier: add balance, cashSlotBalance
{
UpdateBalance(balance); // Frontier
UpdateCashSlotBalance(cashSlotBalance); // Frontier
@@ -124,84 +124,11 @@ namespace Content.Client.VendingMachines.UI
_dummies.Add(entry.ID, dummy);
}
// Frontier: item pricing
// ok so we dont really have access to the pricing system so we are doing a quick price check
// based on prototype info since the items inside a vending machine dont actually exist as entities
// until they are spawned. So this little alg does the following:
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
// stacks and other items have 0 cost.
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
// it should have the other too, and then calculates the price based on that.
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
// determined dynamically by their contents/inventory), it then falls back to the default mystery
// hardcoded value of 20xMarketModifier.
var cost = 20;
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
{
if (priceComponent.Price != 0)
{
var price = (float)priceComponent.Price;
cost = (int)(price * priceModifier);
}
else
{
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
{
var price = stackPrice.Price * stack.Count;
cost = (int)(price * priceModifier);
}
else
cost = (int)(cost * priceModifier);
}
}
else
cost = (int)(cost * priceModifier);
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
{
if (priceSolutions.Solutions != null)
{
foreach (var solution in priceSolutions.Solutions.Values)
{
foreach (var (reagent, quantity) in solution.Contents)
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
out var reagentProto))
continue;
// TODO check ReagentData for price information?
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
cost += (int)(costReagent * priceModifier);
}
}
}
}
// End Frontier: item pricing
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
if (prototype != null)
{
var price = 0.0;
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
{
price += staticComp.VendPrice;
}
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
{
price += stackComp.VendPrice;
}
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
if (price > 0.0)
{
cost = (int)price;
}
}
// End Frontier
var itemName = Identity.Name(dummy, _entityManager);
var cost = 0; // mono
if (requiresCash) // frontier
cost = GetPrice(entry, prototype, priceModifier);
string itemText;
// Frontier: unlimited vending
@@ -211,7 +138,6 @@ namespace Content.Client.VendingMachines.UI
itemText = $"[{BankSystemExtensions.ToSpesoString(cost)}] {itemName}";
// End Frontier: unlimited vending
if (itemText.Length > longestEntry.Length)
longestEntry = itemText;
@@ -223,6 +149,88 @@ namespace Content.Client.VendingMachines.UI
SetSizeAfterUpdate(longestEntry.Length, inventory.Count);
}
// Mono: Moved out frontier pricing logic to the separate method
private int GetPrice(VendingMachineInventoryEntry entry, EntityPrototype? prototype, float priceModifier){
// Frontier: item pricing
// ok so we dont really have access to the pricing system so we are doing a quick price check
// based on prototype info since the items inside a vending machine dont actually exist as entities
// until they are spawned. So this little alg does the following:
// first, checks for a staticprice component, and if it has one, checks to make sure its not 0 since
// stacks and other items have 0 cost.
// If the price is 0, then we check for both a stack price and a stack component, since if it has one
// it should have the other too, and then calculates the price based on that.
// If the price is still 0 or non-existant (this is the case for food and containers since their value is
// determined dynamically by their contents/inventory), it then falls back to the default mystery
// hardcoded value of 20xMarketModifier.
var cost = 20;
if (prototype != null && prototype.TryGetComponent<StaticPriceComponent>(out var priceComponent, _entityManager.ComponentFactory))
{
if (priceComponent.Price != 0)
{
var price = (float)priceComponent.Price;
cost = (int)(price * priceModifier);
}
else
{
if (prototype.TryGetComponent<StackPriceComponent>(out var stackPrice, _entityManager.ComponentFactory)
&& prototype.TryGetComponent<StackComponent>(out var stack, _entityManager.ComponentFactory))
{
var price = stackPrice.Price * stack.Count;
cost = (int)(price * priceModifier);
}
else
cost = (int)(cost * priceModifier);
}
}
else
cost = (int)(cost * priceModifier);
if (prototype != null && prototype.TryGetComponent<SolutionContainerManagerComponent>(out var priceSolutions, _entityManager.ComponentFactory))
{
if (priceSolutions.Solutions != null)
{
foreach (var solution in priceSolutions.Solutions.Values)
{
foreach (var (reagent, quantity) in solution.Contents)
{
if (!_prototypeManager.TryIndex<ReagentPrototype>(reagent.Prototype,
out var reagentProto))
continue;
// TODO check ReagentData for price information?
var costReagent = quantity.Float() * reagentProto.PricePerUnit;
cost += (int)(costReagent * priceModifier);
}
}
}
}
// End Frontier: item pricing
// Frontier: calculate vending price (this duplicates Content.Server.PricingSystem.GetVendPrice - this should be moved to Content.Shared if possible)
if (prototype != null)
{
var price = 0.0;
if (prototype.TryGetComponent<StaticPriceComponent>(out var staticComp, _entityManager.ComponentFactory) && staticComp.VendPrice > 0.0)
{
price += staticComp.VendPrice;
}
else if (prototype.TryGetComponent<StackPriceComponent>(out var stackComp, _entityManager.ComponentFactory) && stackComp.VendPrice > 0.0)
{
price += stackComp.VendPrice;
}
// If there is anything that explicitly sets vending price - higher OR lower, override the base.
if (price > 0.0)
{
cost = (int)price;
}
}
return cost;
// End Frontier
}
// Frontier
public void UpdateBalance(int balance)
{
@@ -30,6 +30,8 @@ namespace Content.Client.VendingMachines
[ViewVariables]
private int _cashSlotBalance = 0;
// End Frontier
[ViewVariables]
private bool _requiresCash; // mono
public VendingMachineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
@@ -74,6 +76,7 @@ namespace Content.Client.VendingMachines
if (EntMan.TryGetComponent<VendingMachineComponent>(Owner, out var vendingMachine))
{
_cashSlotBalance = vendingMachine.CashSlotBalance;
_requiresCash = vendingMachine.RequiresCash; // mono
if (vendingMachine.CashSlotName != null)
cashSlotValue = _cashSlotBalance;
}
@@ -83,7 +86,7 @@ namespace Content.Client.VendingMachines
}
// End Frontier
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue); // Frontier: add _balance
_menu?.Populate(_cachedInventory, _mod, _balance, cashSlotValue, _requiresCash); // Frontier: add _balance, mono: add _requiresCash
}
private void OnItemSelected(GUIBoundKeyEventArgs args, ListData data)
@@ -37,6 +37,10 @@ namespace Content.Client.Viewport
private readonly List<CopyPixelsDelegate<Rgba32>> _queuedScreenshots = new();
// Fire edit start
public ShaderInstance? Shader;
// Fire edit end
public int CurrentRenderScale => _curRenderScale;
/// <summary>
@@ -170,7 +174,11 @@ namespace Content.Client.Viewport
var drawBox = GetDrawBox();
var drawBoxGlobal = drawBox.Translated(GlobalPixelPosition);
_viewport.RenderScreenOverlaysBelow(handle, this, drawBoxGlobal);
// Fire edit start
handle.DrawingHandleScreen.UseShader(Shader);
handle.DrawingHandleScreen.DrawTextureRect(_viewport.RenderTarget.Texture, drawBox);
handle.DrawingHandleScreen.UseShader(null);
// Fire edit end
_viewport.RenderScreenOverlaysAbove(handle, this, drawBoxGlobal);
}
@@ -7,6 +7,7 @@ namespace Content.Client.Weapons.Marker;
public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -20,16 +21,16 @@ public sealed class DamageMarkerSystem : SharedDamageMarkerSystem
if (!_timing.ApplyingState || component.Effect == null || !TryComp<SpriteComponent>(uid, out var sprite))
return;
var layer = sprite.LayerMapReserveBlank(DamageMarkerKey.Key);
sprite.LayerSetState(layer, component.Effect.RsiState, component.Effect.RsiPath);
var layer = _sprite.LayerMapReserve((uid, sprite), DamageMarkerKey.Key);
_sprite.LayerSetRsi((uid, sprite), layer, component.Effect.RsiPath, component.Effect.RsiState);
}
private void OnMarkerShutdown(EntityUid uid, DamageMarkerComponent component, ComponentShutdown args)
{
if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !sprite.LayerMapTryGet(DamageMarkerKey.Key, out var weh))
if (!_timing.ApplyingState || !TryComp<SpriteComponent>(uid, out var sprite) || !_sprite.LayerMapTryGet((uid, sprite), DamageMarkerKey.Key, out var weh, false))
return;
sprite.RemoveLayer(weh);
_sprite.RemoveLayer((uid, sprite), weh);
}
private enum DamageMarkerKey : byte
@@ -18,7 +18,7 @@ public sealed partial class MeleeWeaponSystem
/// <summary>
/// Does all of the melee effects for a player that are predicted, i.e. character lunge and weapon animation.
/// </summary>
public override void DoLunge(EntityUid user, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
public override void DoLunge(EntityUid user, EntityUid playerUid, EntityUid weapon, Angle angle, Vector2 localPos, string? animation, bool predicted = true)
{
if (!Timing.IsFirstTimePredicted)
return;
@@ -66,10 +66,11 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
var entity = entityNull.Value;
if (!TryGetWeapon(entity, out var weaponUid, out var weapon))
// Mono - add user override
if (!TryGetWeapon(entity, out var weaponUid, out var weapon, out var userOverride))
return;
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(entity, weapon: (weaponUid, weapon)))
if (!CombatMode.IsInCombatMode(entity) || !Blocker.CanAttack(userOverride, weapon: (weaponUid, weapon)))
{
weapon.Attacking = false;
return;
@@ -120,15 +121,15 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
switch(altFireComponent.AttackType)
{
case AltFireAttackType.Light:
ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon);
ClientLightAttack(userOverride, mousePos, coordinates, weaponUid, weapon);
break;
case AltFireAttackType.Heavy:
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);
ClientHeavyAttack(userOverride, coordinates, weaponUid, weapon);
break;
case AltFireAttackType.Disarm:
ClientDisarm(entity, mousePos, coordinates);
ClientDisarm(userOverride, mousePos, coordinates);
break;
}
@@ -139,7 +140,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
if (altDown == BoundKeyState.Down)
{
// If it's an unarmed attack then do a disarm
if (weapon.AltDisarm && weaponUid == entity)
if (weapon.AltDisarm && weaponUid == userOverride)
{
EntityUid? target = null;
@@ -155,7 +156,7 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
// WD EDIT START
if (TryComp(weaponUid, out BlinkComponent? blink) && blink.IsActive)
{
if (!_xformQuery.TryGetComponent(entity, out var userXform))
if (!_xformQuery.TryGetComponent(userOverride, out var userXform))
return;
var targetMap = _transform.ToMapCoordinates(coordinates);
@@ -171,14 +172,14 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
}
// WD EDIT END
ClientHeavyAttack(entity, coordinates, weaponUid, weapon);
ClientHeavyAttack(userOverride, coordinates, weaponUid, weapon);
return;
}
// Light attack
if (useDown == BoundKeyState.Down)
{
var attackerPos = TransformSystem.GetMapCoordinates(entity);
var attackerPos = TransformSystem.GetMapCoordinates(userOverride);
if (mousePos.MapId != attackerPos.MapId ||
(attackerPos.Position - mousePos.Position).Length() > weapon.Range)
@@ -193,15 +194,15 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
target = screen.GetDamageableClickedEntity(mousePos); // Goob edit
}
// Don't light-attack if interaction will be handling this instead
if (Interaction.CombatModeCanHandInteract(entity, target))
// Don't light-attack if interaction will be handling this instead // Mono - add hands check (why is this duplicated?)
if (HasComp<HandsComponent>(userOverride) && Interaction.CombatModeCanHandInteract(userOverride, target))
return;
RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
}
if (useDown == BoundKeyState.Down)
ClientLightAttack(entity, mousePos, coordinates, weaponUid, weapon);
ClientLightAttack(userOverride, mousePos, coordinates, weaponUid, weapon);
}
protected override bool InRange(EntityUid user, EntityUid target, float range, ICommonSession? session)
@@ -298,8 +299,8 @@ public sealed partial class MeleeWeaponSystem : SharedMeleeWeaponSystem
if (_stateManager.CurrentState is GameplayStateBase screen)
target = screen.GetClickedEntity(mousePos);
// Don't light-attack if interaction will be handling this instead
if (Interaction.CombatModeCanHandInteract(attacker, target))
// Don't light-attack if interaction will be handling this instead // Mono - only if attacker has hands
if (HasComp<HandsComponent>(attacker) && Interaction.CombatModeCanHandInteract(attacker, target))
return;
RaisePredictiveEvent(new LightAttackEvent(GetNetEntity(target), GetNetEntity(weaponUid), GetNetCoordinates(coordinates)));
@@ -17,6 +17,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
[Dependency] private readonly IOverlayManager _overlay = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly MapSystem _mapSystem = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
@@ -33,7 +34,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
if (!TryComp<SpriteComponent>(component.Tethered, out var sprite))
return;
sprite.Color = component.LineColor;
_sprite.SetColor((component.Tethered.Value, sprite), component.LineColor);
}
public override void Shutdown()
@@ -58,7 +59,7 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
var player = _player.LocalEntity;
if (player == null ||
!TryGetTetherGun(player.Value, out var gunUid, out var gun) ||
!TryGetTetherGun(player.Value, out _, out var gun) ||
gun.TetherEntity == null)
{
return;
@@ -81,11 +82,11 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
coords = TransformSystem.ToCoordinates(_mapSystem.GetMap(mouseWorldPos.MapId), mouseWorldPos);
}
const float BufferDistance = 0.1f;
const float bufferDistance = 0.1f;
if (TryComp(gun.TetherEntity, out TransformComponent? tetherXform) &&
tetherXform.Coordinates.TryDistance(EntityManager, TransformSystem, coords, out var distance) &&
distance < BufferDistance)
distance < bufferDistance)
{
return;
}
@@ -105,11 +106,11 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
if (TryComp<ForceGunComponent>(component.Tetherer, out var force))
{
sprite.Color = force.LineColor;
_sprite.SetColor((uid, sprite), force.LineColor);
}
else if (TryComp<TetherGunComponent>(component.Tetherer, out var tether))
{
sprite.Color = tether.LineColor;
_sprite.SetColor((uid, sprite), tether.LineColor);
}
}
@@ -118,6 +119,6 @@ public sealed class TetherGunSystem : SharedTetherGunSystem
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
sprite.Color = Color.White;
_sprite.SetColor((uid, sprite), Color.White);
}
}
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared._CorvaxNext.Silicons.Borgs;
namespace Content.Client._CorvaxNext.Silicons.Borgs;
public sealed partial class AiRemoteControlSystem : SharedAiRemoteControlSystem
{
}
@@ -0,0 +1,18 @@
<Control xmlns="https://spacestation14.io"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Margin="0 0 0 10">
<PanelContainer VerticalExpand="True" StyleClasses="BackgroundDark">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True"
Margin="5 5 5 5">
<customControls:HSeparator Margin="0 5 0 5"/>
<RichTextLabel Name="DeviceName"/>
<BoxContainer Name="RemoteControlButtons" Orientation="Horizontal" HorizontalExpand="True" Margin="0 5 0 0">
<Button Name="MoveButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-ui-menu-moveto}"/>
<Control Margin="2 0 0 0"/>
<Button Name="TakeControlButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-remote-control}"/>
</BoxContainer>
</BoxContainer>
</PanelContainer>
</Control>
@@ -0,0 +1,39 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Robust.Client.UserInterface;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
[GenerateTypedNameReferences]
public sealed partial class RemoteDeviceDisplay : Control
{
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
public RemoteDeviceDisplay(NetEntity netEntityUid, string displayName)
{
RobustXamlLoader.Load(this);
DeviceName.SetMessage(displayName);
MoveButton.OnPressed += _ =>
{
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
RemoteDeviceActionEvent.RemoteDeviceActionType.MoveToDevice,
netEntityUid));
};
TakeControlButton.OnPressed += _ =>
{
OnRemoteDeviceAction?.Invoke(new RemoteDeviceActionEvent(
RemoteDeviceActionEvent.RemoteDeviceActionType.TakeControl,
netEntityUid));
};
}
}
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
using Robust.Client.UserInterface;
using static Content.Shared._CorvaxNext.Silicons.Borgs.Components.AiRemoteControllerComponent;
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
public sealed class RemoteDevicesBoundUserInterface : BoundUserInterface
{
private RemoteDevicesMenu? _menu;
private EntityUid _owner;
public RemoteDevicesBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
_owner = owner;
}
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<RemoteDevicesMenu>();
_menu.OnRemoteDeviceAction += SendAction;
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not RemoteDevicesBuiState msg)
return;
_menu?.Update(_owner, msg);
}
public void SendAction(RemoteDeviceActionEvent action)
{
SendMessage(new RemoteDeviceActionMessage(action));
}
}
@@ -0,0 +1,27 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc ai-remote-ui-menu-title}"
MinSize="355 100"
SetSize="355 415">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<PanelContainer VerticalExpand="True" Margin="10 10 10 10">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E"/>
</PanelContainer.PanelOverride>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer
Name="RemoteDevicesDisplayContainer"
Orientation="Vertical"
VerticalExpand="True"
Margin="10 10 10 0">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
</controls:FancyWindow>
@@ -0,0 +1,43 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ImHoks <imhokzzzz@gmail.com>
// SPDX-FileCopyrightText: 2025 KillanGenifer <killangenifer@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
using Robust.Client.UserInterface.XAML;
using FancyWindow = Content.Client.UserInterface.Controls.FancyWindow;
using Robust.Client.AutoGenerated;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
namespace Content.Client._CorvaxNext.Silicons.Laws.Ui;
[GenerateTypedNameReferences]
public sealed partial class RemoteDevicesMenu : FancyWindow
{
public event Action<RemoteDeviceActionEvent>? OnRemoteDeviceAction;
public RemoteDevicesMenu()
{
RobustXamlLoader.Load(this);
}
public void Update(EntityUid uid, RemoteDevicesBuiState state)
{
RemoteDevicesDisplayContainer.Children.Clear();
if (state.DeviceList == null)
return;
foreach (var device in state.DeviceList)
{
var control = new RemoteDeviceDisplay(device.NetEntityUid, device.DisplayName);
control.OnRemoteDeviceAction += action =>
{
OnRemoteDeviceAction?.Invoke(action);
};
RemoteDevicesDisplayContainer.AddChild(control);
}
}
}
@@ -23,10 +23,13 @@
</BoxContainer>
</BoxContainer>
<ui:ShuttleNavControl Name="NavRadar"
HorizontalExpand="True"
VerticalExpand="True"
MinHeight="400"/>
<!-- Mono - wrap -->
<LayoutContainer>
<ui:ShuttleNavControl Name="NavRadar"
HorizontalExpand="True"
VerticalExpand="True"
MinHeight="400"/>
</LayoutContainer>
</GridContainer>
</controls:FancyWindow>
@@ -21,6 +21,7 @@ public sealed class ShipShieldOverlay : Overlay
private readonly IEntityManager _entManager;
private readonly ShaderInstance _unshadedShader;
private readonly List<DrawVertexUV2D> _verts = new(128); // Mono
private readonly Texture _shieldTexture;
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowWorld;
@@ -30,6 +31,7 @@ public sealed class ShipShieldOverlay : Overlay
_entManager = entityManager;
_fixture = _entManager.EntitySysManager.GetEntitySystem<FixtureSystem>();
_physics = _entManager.EntitySysManager.GetEntitySystem<Robust.Client.Physics.PhysicsSystem>();
_shieldTexture = _resourceCache.GetTexture("/Textures/_Crescent/ShipShields/shieldtex.png");
_unshadedShader = prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
@@ -56,9 +58,7 @@ public sealed class ShipShieldOverlay : Overlay
if (fixture is not { Shape: ChainShape chain })
continue;
var texture = _resourceCache.GetTexture("/Textures/_Crescent/ShipShields/shieldtex.png");
DrawShield(handle, uid, chain, xform, texture, visuals.ShieldColor, _verts);
DrawShield(handle, uid, chain, xform, _shieldTexture, visuals.ShieldColor, _verts);
_verts.Clear(); // Clear for next shield - Mono
}
}
@@ -0,0 +1,87 @@
using Robust.Shared.Map;
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
using Content.Client.Examine;
using Robust.Client.GameObjects;
using Robust.Client.ResourceManagement;
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
public sealed class NuclearReactorSystem : SharedNuclearReactorSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<NuclearReactorComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<NuclearReactorComponent, ClientExaminedEvent>(ReactorExamined);
SubscribeLocalEvent<NuclearReactorComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnInit(EntityUid uid, NuclearReactorComponent comp, ref ComponentInit args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
if (!_resourceCache.TryGetResource("/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_component_cap.rsi", out RSIResource? resource))
return;
Entity<SpriteComponent?> entSprite = (uid, sprite);
var xspace = comp.Gridbounds[0] / 32f;
var yspace = comp.Gridbounds[1] / 32f;
var xoff = comp.Gridbounds[2] / 32f;
var yoff = comp.Gridbounds[3] / 32f;
var gridWidth = comp.ReactorGridWidth;
var gridHeight = comp.ReactorGridHeight;
var xAdj = (gridWidth - 1) / 2f;
var yAdj = (gridHeight - 1) / 2f;
for (var x = 0; x < gridWidth; x++)
{
for (var y = 0; y < gridHeight; y++)
{
var layerID = _sprite.AddRsiLayer(entSprite, "empty_cap", resource.RSI);
_sprite.LayerMapSet(entSprite, FormatMap(x, y), layerID);
_sprite.LayerSetOffset(entSprite, layerID, new((xspace * (y - yAdj)) - xoff, (-yspace * (x - xAdj)) - yoff));
_sprite.LayerSetColor(entSprite, layerID, Color.Black);
}
}
}
private static string FormatMap(int x, int y) => "NuclearReactorCap" + x + "/" + y;
private void ReactorExamined(EntityUid uid, NuclearReactorComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));
private void OnAppearanceChange(EntityUid uid, NuclearReactorComponent comp, ref AppearanceChangeEvent args)
{
for (var x = 0; x < comp.ReactorGridWidth; x++)
{
for (var y = 0; y < comp.ReactorGridHeight; y++)
{
if(comp.VisualData.TryGetValue(new(x,y), out var data))
UpdateRodAppearance(uid, FormatMap(x,y), data.cap, data.color);
else
UpdateRodAppearance(uid, FormatMap(x, y), "empty_cap", Color.Black);
}
}
}
private void UpdateRodAppearance(EntityUid uid, string map, string state, Color color)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
Entity<SpriteComponent?> entSprite = (uid, sprite);
if (!_sprite.LayerMapTryGet(entSprite, map, out var layer, false))
return;
_sprite.LayerSetRsiState(entSprite, layer, state);
_sprite.LayerSetColor(entSprite, layer, color);
}
}
@@ -0,0 +1,34 @@
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
using Robust.Client.GameObjects;
using Robust.Shared.Prototypes;
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
public sealed class ReactorPartSystem : SharedReactorPartSystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ReactorPartComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<ReactorPartComponent, ComponentInit>(OnComponentInit);
}
private void OnAppearanceChange(EntityUid uid, ReactorPartComponent component, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// Re-enable if/when there are multiple sprites
//if (!_sprite.LayerMapTryGet((uid, args.Sprite), ReactorCapVisualLayers.Sprite, out var layer, false))
// return;
_sprite.LayerSetColor((uid, args.Sprite), 0, _proto.Index(component.Material).Color);
}
private void OnComponentInit(Entity<ReactorPartComponent> ent, ref ComponentInit args)
=> _sprite.LayerSetColor((ent.Owner, EntityManager.GetComponent<SpriteComponent>(ent.Owner)), 0, _proto.Index(ent.Comp.Material).Color);
}
@@ -0,0 +1,105 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Map;
using Robust.Client.GameObjects;
using Content.Shared.Repairable;
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
using Content.Client.Popups;
using Content.Client.Examine;
using Robust.Client.Animations;
namespace Content.Client._FarHorizons.Power.Generation.FissionGenerator;
public sealed class TurbineSystem : SharedTurbineSystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<TurbineComponent, ClientExaminedEvent>(TurbineExamined);
SubscribeLocalEvent<TurbineComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<TurbineComponent, AnimationCompletedEvent>(OnAnimationCompleted);
}
protected override void OnRepairTurbineFinished(Entity<TurbineComponent> ent, ref RepairFinishedEvent args)
{
if (args.Cancelled)
return;
if (!TryComp(ent.Owner, out TurbineComponent? comp))
return;
_popupSystem.PopupClient(Loc.GetString("turbine-repair", ("target", ent.Owner), ("tool", args.Used!)), ent.Owner, args.User);
}
private void TurbineExamined(EntityUid uid, TurbineComponent comp, ClientExaminedEvent args) => Spawn(comp.ArrowPrototype, new EntityCoordinates(uid, 0, 0));
private void OnAnimationCompleted(Entity<TurbineComponent> ent, ref AnimationCompletedEvent args) => PlayAnimation(ent);
private void OnAppearanceChange(Entity<TurbineComponent> ent, ref AppearanceChangeEvent args) => PlayAnimation(ent);
protected override void AccUpdate()
{
var query = EntityQueryEnumerator<TurbineComponent>();
while (query.MoveNext(out var uid, out var component))
{
// Makes sure the anim doesn't get stuck at low RPM
if (Math.Abs(component.RPM - component.AnimRPM) > component.BestRPM * 0.1)
PlayAnimation((uid, component));
}
}
private void PlayAnimation(Entity<TurbineComponent> ent)
{
if (ent.Comp.RPM < 1)
return;
if (!TryComp<SpriteComponent>(ent.Owner, out var sprite) || !_sprite.TryGetLayer((ent.Owner,sprite), TurbineVisualLayers.TurbineSpeed, out var layer, false))
return;
var state = "speedanim";
if (_animationPlayer.HasRunningAnimation(ent.Owner, state))
_animationPlayer.Stop(ent.Owner, state);
if (_animationPlayer.HasRunningAnimation(ent.Owner, state)) // Despite what you'd think, this is not always true
return;
ent.Comp.AnimRPM = ent.Comp.RPM;
var layerKey = TurbineVisualLayers.TurbineSpeed;
var time = 0.5f * ent.Comp.BestRPM / ent.Comp.RPM;
var timestep = time/12;
var animation = new Animation
{
Length = TimeSpan.FromSeconds(time),
AnimationTracks =
{
new AnimationTrackSpriteFlick
{
LayerKey = layerKey,
KeyFrames =
{
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_00", 0),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_01", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_02", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_03", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_04", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_05", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_06", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_07", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_08", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_09", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_10", timestep),
new AnimationTrackSpriteFlick.KeyFrame("turbinerun_11", timestep)
}
}
}
};
_sprite.LayerSetVisible(layer, true);
_animationPlayer.Play(ent.Owner, animation, state);
}
}
@@ -0,0 +1,77 @@
using Robust.Client.UserInterface;
using JetBrains.Annotations;
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
namespace Content.Client._FarHorizons.Power.UI;
/// <summary>
/// Initializes a <see cref="NuclearReactorWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class NuclearReactorBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IEntityManager _entityManager = default!;
[ViewVariables]
private NuclearReactorWindow? _window;
public NuclearReactorBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
EntityUid? reactorUid = null;
if (_entityManager.TryGetComponent<NuclearReactorMonitorComponent>(Owner, out var reactorMonitorComponent))
{
if (!_entityManager.TryGetEntity(reactorMonitorComponent.reactor, out reactorUid) || reactorUid == null
|| !_entityManager.TryGetComponent<NuclearReactorComponent>(reactorUid, out var monitoredReactorComponent) || monitoredReactorComponent.Melted)
return;
}
else if (!_entityManager.TryGetComponent<NuclearReactorComponent>(Owner, out var reactorComponent) || reactorComponent.Melted)
return;
base.Open();
_window = this.CreateWindow<NuclearReactorWindow>();
if (_entityManager.EntityExists(reactorUid))
_window.SetEntity(reactorUid.Value, Owner);
else
_window.SetEntity(Owner);
_window.ItemActionButtonPressed += OnActionButtonPressed;
_window.EjectButtonPressed += OnEjectButtonPressed;
_window.ControlRodModify += OnControlRodModify;
Update();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
if (state is not NuclearReactorBuiState reactorState)
return;
_window?.Update(reactorState);
}
private void OnActionButtonPressed(Vector2d vector)
{
if (_window is null ) return;
SendPredictedMessage(new ReactorItemActionMessage(vector));
}
private void OnEjectButtonPressed()
{
if (_window is null) return;
SendPredictedMessage(new ReactorEjectItemMessage());
}
private void OnControlRodModify(float amount)
{
if (_window is null) return;
SendPredictedMessage(new ReactorControlRodModifyMessage(amount));
}
}
@@ -0,0 +1,136 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="690 445" Title="Nuclear Reactor">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<!--Main Content-->
<BoxContainer Name="MainContent" Orientation="Horizontal" VerticalExpand="True" Margin="4">
<!--Left Panel-->
<BoxContainer Name="LeftPanel" Orientation="Vertical">
<!--Insert/remove controls-->
<BoxContainer Name="Controls" Orientation="Vertical">
<GridContainer Name="ControlGrid" Columns="3" Margin="2 2 2 2">
<Button Name="XDecrement" Text="-X" Disabled="true"/>
<Label Name="XPos" Text="0" StyleClasses="LabelKeyText" HorizontalExpand="True" HorizontalAlignment="Center"/>
<Button Name="XIncrement" Text="+X"/>
<Button Name="YDecrement" Text="-Y" Disabled="true"/>
<Label Name="YPos" Text="0" StyleClasses="LabelKeyText" HorizontalExpand="True" HorizontalAlignment="Center"/>
<Button Name="YIncrement" Text="+Y"/>
</GridContainer>
<controls:StripeBack>
<Button Name="ItemAction" Text="{Loc comp-nuclear-reactor-ui-insert-button}"/>
<Label Name="ItemActionLock" Text="{Loc comp-nuclear-reactor-ui-locked}" Visible="false" HorizontalAlignment="Center" VerticalAlignment="Center" />
</controls:StripeBack>
</BoxContainer>
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="150" Margin="2 5" />
<!--Target info-->
<BoxContainer Name="TargetData" Orientation="Vertical" VerticalExpand="True">
<BoxContainer Name="TargetInfo" Orientation="Vertical">
<Label Name="TargetName" Text="Rod" ClipText="True"/>
<GridContainer Name="TargetTemperatureGrid" Columns="2" Margin="2 2 2 2">
<Label Text="T: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Button Name="TargetTemperature" Text="20C" HorizontalAlignment="Right"/>
</GridContainer>
<GridContainer Name="TargetNRadiationGrid" Columns="2" Margin="2 2 2 2">
<Label Text="N: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="TargetNRadiation" Text="0" HorizontalAlignment="Right"/>
</GridContainer>
<GridContainer Name="TargetRadiationGrid" Columns="2" Margin="2 2 2 2">
<Label Text="R: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="TargetRadiation" Text="0" HorizontalAlignment="Right"/>
</GridContainer>
<GridContainer Name="TargetSpentGrid" Columns="2" Margin="2 2 2 2">
<Label Text="S: " StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="TargetSpent" Text="0" HorizontalAlignment="Right"/>
</GridContainer>
</BoxContainer>
</BoxContainer>
<!--"Shelf"-->
<BoxContainer Name="Shelf" Orientation="Vertical">
<Button Name="EjectItem" Text="{Loc comp-nuclear-reactor-ui-eject-button}" />
<Label Name="ItemName" Text="empty" ClipText="True"/>
</BoxContainer>
</BoxContainer>
<!--Reactor display-->
<PanelContainer Name="GridVisuals" StyleClasses="Inset" HorizontalExpand="True" Margin="5 5 5 5">
<BoxContainer Orientation="Vertical">
<Button Name="ChangeViewButton" Text="{Loc comp-nuclear-reactor-ui-view-change}"/>
<Label Name="ViewLabel" Text="{Loc comp-nuclear-reactor-ui-view-temp}" StyleClasses="LabelKeyText" />
<GridContainer Name="ReactorGrid" Columns="7" VerticalExpand="True" Margin="2 2 2 2">
</GridContainer>
</BoxContainer>
</PanelContainer>
<!--Right Panel-->
<BoxContainer Name="RightPanel" Orientation="Vertical">
<Label Name="StatusPanelLabel" Text="{Loc comp-nuclear-reactor-ui-status-panel}" StyleClasses="LabelKeyText" />
<BoxContainer Name="StatusIndicators" Orientation="Vertical">
<BoxContainer Name="ReactorTemp" Orientation="Horizontal" HorizontalExpand="True">
<Label Name="ReactorTempLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-temp}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="ReactorTempValue" Text="20C" HorizontalAlignment="Right"/>
</BoxContainer>
<ProgressBar Name="ReactorTempBar" MaxValue="2000" SetSize="150 5" Margin="5" />
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
<BoxContainer Name="ReactorRads" Orientation="Horizontal">
<Label Name="ReactorRadsLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-rads}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="ReactorRadsValue" Text="0" HorizontalAlignment="Right"/>
</BoxContainer>
<ProgressBar Name="ReactorRadsBar" MaxValue="50" SetSize="150 5" Margin="5" />
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
<BoxContainer Name="ReactorTherm" Orientation="Horizontal" HorizontalExpand="True">
<Label Name="ReactorThermLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-therm}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="ReactorThermValue" Text="0" HorizontalAlignment="Right"/>
</BoxContainer>
<ProgressBar Name="ReactorThermBar" MaxValue="10000000" SetSize="150 5" Margin="5" />
<PanelContainer SetHeight="2" VerticalAlignment="Top" SetWidth="200" Margin="2 5" />
<BoxContainer Name="ControlRodsText" Orientation="Horizontal">
<Label Name="ControlRodsLabel" Text="{Loc comp-nuclear-reactor-ui-reactor-control}" StyleClasses="StatusFieldTitle" HorizontalExpand="True"/>
<Label Name="ControlRodsValue" Text="100%" HorizontalAlignment="Right"/>
</BoxContainer>
<BoxContainer Name="ControlRods" Orientation="Horizontal">
<ProgressBar Name="ControlRodsActual" MaxValue="2" SetSize="5 100" Margin="5" Vertical="True" />
<ProgressBar Name="ControlRodsSet" MaxValue="2" SetSize="5 100" Margin="5" Vertical="True" />
<controls:StripeBack>
<BoxContainer Name="ControlRodsButtons" Orientation="Vertical">
<Button Name="ControlRodsInsertLarge" Text="+ +"/>
<Button Name="ControlRodsInsert" Text=" + "/>
<Button Name="ControlRodsRemove" Text=" - "/>
<Button Name="ControlRodsRemoveLarge" Text="- -"/>
</BoxContainer>
<Label Name="ControlRodsLock" Text="{Loc comp-nuclear-reactor-ui-locked}" Visible="false" HorizontalAlignment="Center" VerticalAlignment="Center" />
</controls:StripeBack>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!--Footer-->
<BoxContainer Name="Footer" Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc comp-nuclear-reactor-ui-footer-left}" StyleClasses="WindowFooterText" />
<Label Text="{Loc comp-nuclear-reactor-ui-footer-right}" StyleClasses="WindowFooterText"
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19" />
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>
@@ -0,0 +1,355 @@
using Content.Client.UserInterface.Controls;
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
using Content.Shared.Atmos;
using Content.Shared.Lock;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Timing;
namespace Content.Client._FarHorizons.Power.UI;
/// <summary>
/// Client-side UI used to view a nuclear reactor.
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class NuclearReactorWindow : FancyWindow
{
[Dependency] private readonly IEntityManager _entityManager = null!;
private readonly LockSystem _lock;
private readonly Dictionary<Vector2i, StyleBoxFlat> _reactorGrid = [];
private readonly Dictionary<Vector2i, TextureRect> _reactorRect = [];
private readonly Dictionary<Vector2i, Button> _reactorButton = [];
private readonly StyleBoxFlat _temperatureBar = new(Color.Black);
private readonly StyleBoxFlat _radiationBar = new(Color.Black);
private readonly StyleBoxFlat _powerBar = new(Color.Black);
private Dictionary<Vector2i, ReactorSlotBUIData> _data = [];
private int _gridWidth = 0;
private int _gridHeight = 0;
private byte _displayMode = 1<<0;
private enum DisplayModes : byte
{
Temperature = 1 << 0,
Neutron = 1 << 1,
Target = 1 << 2
}
private byte _temperatureMode = 1 << 0;
private enum TemperatureModes : byte
{
Celcius = 1 << 0,
Kelvin = 1 << 1,
// Fahrenheit = 1 << 2
}
private int _targetX = 0;
private int _targetY = 0;
public event Action<Vector2d>? ItemActionButtonPressed;
public event Action? EjectButtonPressed;
public event Action<float>? ControlRodModify;
private EntityUid _reactor;
private bool _isMonitor = false;
private EntityUid _monitor;
public NuclearReactorWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_lock = _entityManager.System<LockSystem>();
ReactorTempBar.ForegroundStyleBoxOverride = _temperatureBar;
ReactorRadsBar.ForegroundStyleBoxOverride = _radiationBar;
ReactorThermBar.ForegroundStyleBoxOverride = _powerBar;
ChangeViewButton.OnPressed += _ => OnChangeViewButtonPressed();
XIncrement.OnPressed += _ => MoveTarget(1, 0);
XDecrement.OnPressed += _ => MoveTarget(-1, 0);
YIncrement.OnPressed += _ => MoveTarget(0, 1);
YDecrement.OnPressed += _ => MoveTarget(0, -1);
ItemAction.OnPressed += _ => ItemActionButtonPressed?.Invoke(new(_targetY, _targetX));
EjectItem.OnPressed += _ => EjectButtonPressed?.Invoke();
ControlRodsInsertLarge.OnPressed += _ => AdjustControlRods(0.1f);
ControlRodsInsert.OnPressed += _ => AdjustControlRods(0.01f);
ControlRodsRemove.OnPressed += _ => AdjustControlRods(-0.01f);
ControlRodsRemoveLarge.OnPressed += _ => AdjustControlRods(-0.1f);
TargetTemperature.OnPressed += _ => ChangeTemp();
}
public void Update(NuclearReactorBuiState msg)
{
_data = msg.SlotData;
ReactorTempValue.Text = FormatTemperature(msg.ReactorTemp);
ReactorTempBar.Value = msg.ReactorTemp;
_temperatureBar.BackgroundColor = GetColor(Atmospherics.T20C, ReactorTempBar.MaxValue * 0.75, msg.ReactorTemp);
ReactorRadsValue.Text = Math.Round(msg.ReactorRads, 1).ToString();
ReactorRadsBar.Value = msg.ReactorRads;
_radiationBar.BackgroundColor = GetColor(0, ReactorRadsBar.MaxValue * 0.5, msg.ReactorRads);
ReactorThermValue.Text = FormatPower(msg.ReactorTherm);
ReactorThermBar.Value = msg.ReactorTherm;
_powerBar.BackgroundColor = GetSteppedColor(ReactorThermBar.MaxValue * 0.75, ReactorThermBar.MaxValue, msg.ReactorTherm);
ControlRodsValue.Text = Math.Round(msg.ControlRodActual * 50, 1).ToString() + "%";
ControlRodsActual.Value = msg.ControlRodActual;
ControlRodsSet.Value = msg.ControlRodSet;
var locktarget = _isMonitor ? _monitor : _reactor;
ControlRodsButtons.Visible = !_lock.IsLocked(locktarget);
ItemAction.Visible = !_lock.IsLocked(_reactor) && !_isMonitor;
ItemActionLock.Visible = _lock.IsLocked(_reactor) && !_isMonitor;
ControlRodsLock.Visible = _lock.IsLocked(locktarget);
Shelf.Visible = !_isMonitor;
ItemName.Text = msg.ItemName ?? "empty";
}
public void SetEntity(EntityUid reactor, EntityUid? monitor = null)
{
_reactor = reactor;
if (monitor != null)
{
_monitor = monitor.Value;
_isMonitor = true;
}
this.SetInfoFromEntity(_entityManager, _isMonitor ? _monitor : _reactor);
if (!_entityManager.TryGetComponent<NuclearReactorComponent>(_reactor, out var reactorComponent))
return;
_gridWidth = reactorComponent.ReactorGridWidth;
_gridHeight = reactorComponent.ReactorGridHeight;
ReactorGrid.Columns = _gridWidth;
ReactorTempBar.MaxValue = reactorComponent.ReactorMeltdownTemp;
ReactorRadsBar.MaxValue = reactorComponent.MaximumRadiation;
ReactorThermBar.MaxValue = reactorComponent.MaximumThermalPower;
InitReactorGrid();
}
public void InitReactorGrid()
{
for (var x = 0; x < _gridWidth; x++)
{
for (var y = 0; y < _gridHeight; y++)
{
var styleBox = new StyleBoxFlat();
var icon = new TextureRect()
{
SetSize = new(32, 32),
TexturePath = "/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_part_inserted/base.png"
};
var button = new Button
{
Margin = new(0),
StyleBoxOverride = new StyleBoxFlat(Color.Transparent),
ToolTip = "",
TooltipDelay = 0.5f
};
var vect = new Vector2i(x, y);
_reactorGrid.Add(vect, styleBox);
_reactorRect.Add(vect, icon);
_reactorButton.Add(vect, button);
var control = new PanelContainer
{
Margin = new Thickness(2),
PanelOverride = styleBox,
HorizontalExpand = true,
VerticalExpand = true,
};
control.AddChild(button);
var nvect = new Vector2i(y, x); // Can't be declared as a part of the line below for some reason, will break if you try
button.OnPressed += _ => SetTarget(nvect);
button.AddChild(icon);
ReactorGrid.AddChild(control);
}
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
//Update the grid
for (var x = 0; x < _gridWidth; x++)
{
for (var y = 0; y < _gridHeight; y++)
{
var vect = new Vector2i(x, y);
var exists = _data.ContainsKey(vect);
var box = _reactorGrid[vect];
switch (_displayMode)
{
case (byte)DisplayModes.Temperature:
box.BackgroundColor = GetColor(293.15, 1200, exists ? _data[vect].Temperature : 0);
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-temp");
break;
case (byte)DisplayModes.Neutron:
box.BackgroundColor = GetColor(0, 7, exists ? _data[vect].NeutronCount : 0);
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-neutron");
break;
case (byte)DisplayModes.Target:
box.BackgroundColor = y == _targetX && x == _targetY ? Color.Yellow : Color.Gray;
ViewLabel.Text = Loc.GetString("comp-nuclear-reactor-ui-view-target");
break;
}
var icon = exists ? _data[vect].IconName : "base";
_reactorRect[vect].TexturePath = "/Textures/_FarHorizons/Structures/Power/Generation/FissionGenerator/reactor_part_inserted/" + icon + ".png";
_reactorButton[vect].ToolTip = exists && _data[vect].SpentFuel > 0
? "Fuel Level: " + (int)Math.Round((1 - (_data[vect].SpentFuel / (_data[vect].SpentFuel + (_data[vect].Radioactivity * 0.5) + (_data[vect].NeutronRadioactivity * 0.25)))) * 100) + "%"
: "";
}
}
UpdateTargetInfo();
UpdateItemAction();
}
private static Color GetColor(double pointA, double pointB, double value)
{
var mid = pointA+((pointB-pointA) / 2);
Color result;
if (value < pointA && pointA > 0)
result = Color.InterpolateBetween(Color.DarkBlue, Color.FromHex("#31843E"), (float)(value / pointA));
else if (value >= pointA && value < mid)
result = Color.InterpolateBetween(Color.FromHex("#31843E"), Color.FromHex("#BBBB00"), (float)((value - pointA) / (mid - pointA)));
else if (value >= mid && value < pointB)
result = Color.InterpolateBetween(Color.FromHex("#BBBB00"), Color.FromHex("#BB3232"), (float)((value - mid) / (pointB - mid)));
else if (value >= pointB && value < pointB * 1.4)
result = Color.InterpolateBetween(Color.FromHex("#BB3232"), Color.FromHex("#550000"), (float)((value - pointB) / ((pointB * 1.4) - pointB)));
else if (value >= pointB * 1.4)
result = Color.FromHex("#550000"); // Death.
else
result = Color.Black;
return result;
}
private static Color GetSteppedColor(double pointA, double pointB, double value)
{
Color result;
if (value > pointB)
result = Color.FromHex("#BB3232");
else if (value > pointA)
result = Color.FromHex("#BBBB00");
else
result = Color.FromHex("#31843E");
return result;
}
private void OnChangeViewButtonPressed()
{
// The bits go marching one by one...
_displayMode <<= 1;
if (_displayMode >= 1 << Enum.GetNames<DisplayModes>().Length)
_displayMode = 1 << 0;
}
private void ChangeTemp()
{
_temperatureMode <<= 1;
if (_temperatureMode >= 1 << Enum.GetNames<TemperatureModes>().Length)
_temperatureMode = 1 << 0;
}
private string FormatTemperature(double temperature) => _temperatureMode switch
{
(byte)TemperatureModes.Celcius => Math.Round(temperature - Atmospherics.T0C, 1).ToString() + "C",
(byte)TemperatureModes.Kelvin => Math.Round(temperature, 1).ToString() + "K",
// (byte)TemperatureModes.Fahrenheit => Math.Round(((temperature - Atmospherics.T0C) * 9 / 5) + 32, 1).ToString() + "F",
_ => "NaN",
};
private void UpdateTargetInfo()
{
var vect = new Vector2i(_targetY, _targetX);
if(!_data.TryGetValue(vect, out var value))
{
TargetName.Text = "empty";
TargetTemperatureGrid.Visible = TargetNRadiationGrid.Visible = TargetRadiationGrid.Visible = TargetSpentGrid.Visible = false;
return;
}
TargetName.Text = value.PartName;
TargetTemperatureGrid.Visible = value.Temperature > 0;
TargetTemperature.Text = FormatTemperature(value.Temperature);
TargetNRadiationGrid.Visible = value.NeutronRadioactivity > 0;
TargetNRadiation.Text = Math.Round(value.NeutronRadioactivity, 2).ToString();
TargetRadiationGrid.Visible = value.Radioactivity > 0;
TargetRadiation.Text = Math.Round(value.Radioactivity, 2).ToString();
TargetSpentGrid.Visible = value.SpentFuel > 0;
TargetSpent.Text = Math.Round(value.SpentFuel, 2).ToString();
}
private void UpdateItemAction()
{
if(ItemName.Text == "empty" == (TargetName.Text == "empty"))
{
ItemAction.Disabled = true;
return;
}
else
ItemAction.Disabled = false;
ItemAction.Text = TargetName.Text != "empty"
? Loc.GetString("comp-nuclear-reactor-ui-remove-button")
: Loc.GetString("comp-nuclear-reactor-ui-insert-button");
}
private void MoveTarget(int x, int y) => SetTarget(_targetX + x, _targetY + y);
private void SetTarget(Vector2i vect) => SetTarget(vect.X, vect.Y);
private void SetTarget(int x, int y)
{
_targetX = Math.Clamp(x, 0, _gridWidth - 1);
_targetY = Math.Clamp(y, 0, _gridHeight - 1);
XPos.Text = _targetX.ToString();
YPos.Text = _targetY.ToString();
XIncrement.Disabled = _targetX >= _gridWidth - 1;
XDecrement.Disabled = _targetX <= 0;
YIncrement.Disabled = _targetY >= _gridHeight - 1;
YDecrement.Disabled = _targetY <= 0;
}
public void SetItemName(string? itemName) => ItemName.Text = itemName ?? "empty";
private static string FormatPower(float power) => Loc.GetString("comp-nuclear-reactor-ui-therm-format", ("power", power));
private void AdjustControlRods(float amount) => ControlRodModify?.Invoke(amount);
}
@@ -0,0 +1,104 @@
using Robust.Client.UserInterface;
using Robust.Client.Timing;
using JetBrains.Annotations;
using Content.Shared._FarHorizons.Power.Generation.FissionGenerator;
using Content.Client.UserInterface;
using System.Diagnostics.CodeAnalysis;
namespace Content.Client._FarHorizons.Power.UI;
/// <summary>
/// Initializes a <see cref="TurbineWindow"/> and updates it when new server messages are received.
/// </summary>
[UsedImplicitly]
public sealed class TurbineBoundUserInterface : BoundUserInterface, IBuiPreTickUpdate
{
[Dependency] private readonly IClientGameTiming _gameTiming = null!;
[Dependency] private readonly IEntityManager _entityManager = null!;
[ViewVariables]
private TurbineWindow? _window;
private BuiPredictionState? _pred;
private InputCoalescer<float> _flowRateCoalescer;
private InputCoalescer<float> _statorLoadCoalescer;
public TurbineBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
EntityUid? turbineUid = null;
if (_entityManager.TryGetComponent<GasTurbineMonitorComponent>(Owner, out var turbineMonitorComponent))
if (!_entityManager.TryGetEntity(turbineMonitorComponent.turbine, out turbineUid) || turbineUid == null
|| !_entityManager.HasComponent<TurbineComponent>(turbineUid))
return;
base.Open();
_pred = new BuiPredictionState(this, _gameTiming);
_window = this.CreateWindow<TurbineWindow>();
if (_entityManager.EntityExists(turbineUid))
_window.SetEntity(turbineUid.Value, Owner);
else
_window.SetEntity(Owner);
_window.TurbineFlowRateChanged += val => _flowRateCoalescer.Set(val);
_window.TurbineStatorLoadChanged += val => _statorLoadCoalescer.Set(val);
Update();
}
void IBuiPreTickUpdate.PreTickUpdate()
{
if (_flowRateCoalescer.CheckIsModified(out var flowRateValue))
_pred!.SendMessage(new TurbineChangeFlowRateMessage(flowRateValue));
if (_statorLoadCoalescer.CheckIsModified(out var statorLoadValue))
_pred!.SendMessage(new TurbineChangeStatorLoadMessage(statorLoadValue));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
if (state is not TurbineBuiState turbineState)
return;
if (!_entityManager.TryGetComponent<TurbineComponent>(Owner, out var comp))
if(!TryGetTurbineComp(Owner, out comp))
return;
foreach (var replayMsg in _pred!.MessagesToReplay())
{
switch (replayMsg)
{
case TurbineChangeFlowRateMessage setFlowRate:
turbineState.FlowRate = Math.Clamp(setFlowRate.FlowRate, 0f, comp.FlowRateMax);
break;
case TurbineChangeStatorLoadMessage setStatorLoad:
turbineState.StatorLoad = Math.Clamp(setStatorLoad.StatorLoad, 1000f, comp.StatorLoadMax);
break;
}
}
_window?.Update(turbineState);
}
public bool TryGetTurbineComp(EntityUid uid, [NotNullWhen(true)] out TurbineComponent? turbineComponent)
{
turbineComponent = null;
if (!_entityManager.TryGetComponent<GasTurbineMonitorComponent>(uid, out var turbineMonitor))
return false;
if (!_entityManager.TryGetEntity(turbineMonitor.turbine, out var turbineUid) || turbineUid == null)
return false;
if (!_entityManager.TryGetComponent<TurbineComponent>(turbineUid, out var turbine))
return false;
turbineComponent = turbine;
return true;
}
}

Some files were not shown because too many files have changed in this diff Show More