Compare commits

...

257 Commits

Author SHA1 Message Date
Redrover1760 fe2b09b6d9 buffs ore rates 2026-04-09 18:12:02 -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
Redrover1760 f3919fafc1 Revert "Merge branch 'april1st' into main" (#3655) 2026-03-30 21:01:19 -04:00
Redrover1760 974b78863b Merge branch 'april1st' into main 2026-03-30 20:48:47 -04:00
monolith8319 1654bd5cf8 Automated Changelog (#3650) 2026-03-30 19:42:07 +00:00
mikus d353667f97 [April 1st] Only True Noticers (#3650) 2026-03-30 15:41:35 -04:00
monolith8319 47116d8ba7 Automated Changelog (#3648) 2026-03-30 18:16:21 +00:00
Ilya246 9bd6d5f3f2 [April 1st] Asteroid Update (#3648) 2026-03-30 18:15:50 +00:00
Corrupt 1dc696deb6 AAAAA (#3646) 2026-03-30 19:34:15 +02:00
monolith8319 f2df23dc81 Automated Changelog (#3645) 2026-03-30 17:33:41 +00:00
mikus 76d6ea96e0 [April 1st] New Company (#3645)
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
2026-03-30 17:33:10 +00:00
monolith8319 93ba265e00 Automated Changelog (#3642) 2026-03-30 17:08:08 +00:00
tonotom1 16f73ec031 [April 1st] Buffs Space Pen (#3642) 2026-03-30 17:07:38 +00:00
monolith8319 9d3b0778b3 Automated Changelog (#3643) 2026-03-30 16:56:33 +00:00
lopsidedmanboobs 32b15951e2 PIrate CBT (#3643) 2026-03-30 18:56:00 +02:00
monolith8319 7a0f0d04c5 Automated Changelog (#3641) 2026-03-30 16:42:14 +00:00
mikus bcce8e67a3 [April 1st] 65 (#3641) 2026-03-30 16:41:45 +00:00
monolith8319 5afcdd9379 Automated Changelog (#3615) 2026-03-30 16:22:32 +00:00
mikus 01c7ea464a [April 1st] 65 (#3615) 2026-03-30 16:22:01 +00:00
monolith8319 55f6108312 Automated Changelog (#3514) 2026-03-30 16:16:08 +00:00
UnicornOnLSD 8be27c1658 Reworks the DIS Rig (#3514)
Co-authored-by: Corrupt <compeldominik6@gmail.com>
2026-03-30 18:15:38 +02:00
monolith8319 4df38cc9cc Automated Changelog (#3640) 2026-03-30 15:45:53 +00:00
mikus 8c0d2a50df [April 1st] Clanker Rebalance (#3640) 2026-03-30 15:45:19 +00:00
monolith8319 2d8105de13 Automated Changelog (#3611) 2026-03-30 15:43:28 +00:00
burrrkeye 01bb73bf56 [APRIL 1st] Charon Rebalance (#3611)
Co-authored-by: Corrupt <compeldominik6@gmail.com>
Co-authored-by: monolith8319 <195513600+monolith8319@users.noreply.github.com>
2026-03-30 15:42:55 +00:00
monolith8319 fb09b866f2 Automated Changelog (#3639) 2026-03-30 13:57:16 +00:00
tonotom1 0458a500ff Harmonica-Fy All Handheld Instruments (#3639) 2026-03-30 09:56:46 -04:00
monolith8319 4334b07093 Automated Changelog (#3638) 2026-03-30 09:23:54 +00:00
Isratosh ad2dd99a9b Thruster Capacitor Uniformity (#3638) 2026-03-30 13:23:22 +04:00
monolith8319 f11c83a96e Automated Changelog (#3626) 2026-03-30 04:38:58 +00:00
mikus 36d1740829 [April 1st] Adjusts (#3626) 2026-03-30 08:38:28 +04:00
Ghost581 1e201bb8d2 Viper Group Whitelist Fix (#3634) 2026-03-29 23:36:29 -04:00
monolith8319 c7a34dfc22 Automated Changelog (#3627) 2026-03-29 18:38:24 +00:00
Redrover1760 e366c4d7cc [April 1st] Adds a Very Fair Drone to the Game (#3627)
Co-authored-by: Corrupt <compeldominik6@gmail.com>
Co-authored-by: monolith8319 <195513600+monolith8319@users.noreply.github.com>
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
Co-authored-by: EckoAurum <202461799+EckoAurum@users.noreply.github.com>
Co-authored-by: ScarletLightweaver <82061820+ScarletLightweaverSS14@users.noreply.github.com>
2026-03-29 14:37:54 -04:00
monolith8319 bc0151e540 Automated Changelog (#3564) 2026-03-29 18:15:53 +00:00
UnicornOnLSD b2307660de Uniformises Shutter Sprites (#3564) 2026-03-29 20:15:23 +02:00
monolith8319 5d5f6b4654 Automated Changelog (#3622) 2026-03-29 18:10:30 +00:00
mikus 165f9950b5 {April 1st] Brings Back the Tactics of Evil Hidden TSF Ninja Warriors (#3622) 2026-03-29 14:10:01 -04:00
monolith8319 ef9858936d Automated Changelog (#3619) 2026-03-29 18:09:42 +00:00
Redrover1760 d0a95f5d8b [April 1st] Upgrades Bomb Collars (#3619)
Co-authored-by: Corrupt <compeldominik6@gmail.com>
Co-authored-by: monolith8319 <195513600+monolith8319@users.noreply.github.com>
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
Co-authored-by: EckoAurum <202461799+EckoAurum@users.noreply.github.com>
Co-authored-by: ScarletLightweaver <82061820+ScarletLightweaverSS14@users.noreply.github.com>
2026-03-29 14:09:11 -04:00
monolith8319 ee386b0633 Automated Changelog (#3623) 2026-03-29 18:08:59 +00:00
mikus de38cc18ef [April 1st] Buffs Shortband (#3623) 2026-03-29 14:08:28 -04:00
monolith8319 ca928bdc83 Automated Changelog (#3625) 2026-03-29 17:56:37 +00:00
mikus cee6ce9566 [April 1st] Updates Ownership of the Treasury to the Bank Of America (#3625) 2026-03-29 17:56:06 +00:00
monolith8319 984a2c9b71 Automated Changelog (#3624) 2026-03-29 17:16:08 +00:00
Ilya246 548e9eccb6 [April 1st] Mobile Pois 2 (#3624) 2026-03-29 17:15:36 +00:00
monolith8319 cd817abd2c Automated Changelog (#3620) 2026-03-29 16:54:09 +00:00
NazrinNya f276df8fe9 [1 April] Add Larp (#3620)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-03-29 16:53:40 +00:00
monolith8319 dd323be01d Automated Changelog (#3621) 2026-03-29 16:53:31 +00:00
mikus 0cf9b35f08 [April 1st] Adds New Forms of Energy Production (#3621) 2026-03-29 19:52:59 +03:00
monolith8319 114a09f290 Automated Changelog (#3618) 2026-03-29 16:20:15 +00:00
Redrover1760 dc4b50e71e updates VSC/build configurations (#3618) 2026-03-29 12:19:46 -04:00
monolith8319 c405c32c89 Automated Changelog (#3598) 2026-03-29 15:49:50 +00:00
monolith8319 34ff30a736 Automated Changelog (#3599) 2026-03-29 15:49:22 +00:00
ScarletLightweaver c8e162baa1 Made Bulwark Gauntlets Wideswing (#3598) 2026-03-29 11:49:12 -04:00
Ilya246 ffcbc32c43 Add Carpsuit to Xenopsychology (#3599) 2026-03-29 11:48:53 -04:00
monolith8319 351b225102 Automated Changelog (#3616) 2026-03-29 15:47:58 +00:00
gonz0 496432881e [April 1st] Detroit Borers (#3616) 2026-03-29 11:47:30 -04:00
monolith8319 9fd2a7b5af Automated Changelog (#3614) 2026-03-29 15:16:12 +00:00
EckoAurum 8972408ec1 Drone Turret Small Fix (#3614) 2026-03-29 11:15:43 -04:00
monolith8319 5798b57788 Automated Changelog (#3612) 2026-03-29 14:46:04 +00:00
mikus ec88162ac0 [April 1st] Adds New Type of Bluespace Drive (#3612) 2026-03-29 14:45:36 +00:00
monolith8319 3992aa846f Automated Changelog (#3610) 2026-03-29 14:18:14 +00:00
Ilya246 4b62f35f10 [April 1st] ilya changes round 2 (#3610) 2026-03-29 14:17:46 +00:00
Ilya246 cc1c91f39f Actually Fix Changelog (#3609) 2026-03-29 14:10:42 +00:00
mikus c5241854ce [April 1st] Piras314 Wrote This (#3606) 2026-03-29 13:57:07 +00:00
Ilya246 d02895a50f prune changelog 2026-03-29 13:55:25 +00:00
Ilya246 c41d005cf4 fix changelog workflow (#3607) 2026-03-29 13:52:43 +00:00
monolith8319 013f57a73d Automated Changelog (#3605) 2026-03-29 13:40:51 +00:00
mikus c3f687a493 [April 1st] Rebalances Gunnery Controls (#3605) 2026-03-29 13:40:19 +00:00
monolith8319 f837710be1 Automated Changelog (#3604) 2026-03-29 13:25:12 +00:00
Ilya246 7b00d623e0 [April 1st] Ilya April1st Changes Round 1 (#3603) 2026-03-29 13:24:54 +00:00
mikus dafbb5f3d8 [April 1st] Makes Borgs Balanced (#3604) 2026-03-29 13:24:41 +00:00
Corrupt d4487d9a5a RSC WL Update no.3 (#3601)
brr
2026-03-29 15:03:48 +02:00
monolith8319 7b9f602c55 Automated Changelog (#3571) 2026-03-29 06:31:08 +00:00
Antoine 97509dd3b6 Chat Message Highlighting (#3571)
Co-authored-by: Ilya246 <57039557+Ilya246@users.noreply.github.com>
2026-03-29 06:30:38 +00:00
monolith8319 55c2a03ea4 Automated Changelog (#3594) 2026-03-29 05:46:36 +00:00
Redrover1760 543c19645f Gigabuffs Signaller Range (#3594) 2026-03-29 01:46:05 -04:00
monolith8319 da8b19dd05 Automated Changelog (#3595) 2026-03-29 02:40:33 +00:00
Redrover1760 4fc44d3a9c Really Ensures Delayed Gamerules Don't Trigger Early (#3595) 2026-03-28 22:40:06 -04:00
Redrover1760 55688cc9e0 Nuke Collar Buff (#3593) 2026-03-28 18:29:23 -04:00
Redrover1760 5357469644 Adds Tsf/Pdv Borgs to Debug Map (#3591) 2026-03-28 17:59:46 -04:00
monolith8319 6040135534 Automated Changelog (#3590) 2026-03-28 19:05:03 +00:00
Ilya246 52649a871e Fastify Apoc (#3590) 2026-03-28 15:04:33 -04:00
monolith8319 fef33511d8 Automated Changelog (#3444) 2026-03-28 18:43:41 +00:00
EckoAurum fec5bfce0a Faction Borg Specialization | Independent Cyborg Selectable Whitelist (#3444) 2026-03-28 14:43:12 -04:00
monolith8319 11f82f95d3 Automated Changelog (#3459) 2026-03-28 18:17:06 +00:00
NazrinNya 3dbad97455 Small Mech Rebalance. (#3459)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-03-28 21:16:36 +03:00
monolith8319 a193ab828e Automated Changelog (#3574) 2026-03-28 16:46:17 +00:00
Vinny Shick c75083140d Vasnyx 150mm Launcher Buff (#3574) 2026-03-28 19:45:49 +03:00
Redrover1760 106f9b6d28 Actually Removes Lancelot Wildcard Trade Crate Selling (#3577) 2026-03-28 19:45:07 +03:00
monolith8319 fe24fc22bc Automated Changelog (#3589) 2026-03-28 16:43:10 +00:00
Redrover1760 85c9636003 Buffs Thruster Speed Increase (#3589) 2026-03-28 19:42:39 +03:00
monolith8319 a1d2f4dcbc Automated Changelog (#3587) 2026-03-28 16:22:15 +00:00
NazrinNya 3381fe165c Various Patches. (#3587)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
2026-03-28 12:21:43 -04:00
monolith8319 a913c35b21 Automated Changelog (#3588) 2026-03-28 16:18:20 +00:00
Redrover1760 0706b759d4 Fixes Diagonal Collisions, Can Kill Kudzu/Chimerazu Under It (#3588) 2026-03-28 12:17:49 -04:00
monolith8319 73a91c0b0a Automated Changelog (#3585) 2026-03-28 06:42:25 +00:00
Redrover1760 9e390680bd Speciest Turrets Fix (#3585) 2026-03-28 02:41:55 -04:00
monolith8319 d3d391f5a0 Automated Changelog (#3582) 2026-03-27 21:58:06 +00:00
Redrover1760 a6506c7f46 Anti-Chimera RIP Turrets to CC and MD (#3582) 2026-03-27 17:57:35 -04:00
monolith8319 958421b6c9 Automated Changelog (#3461) 2026-03-27 19:26:53 +00:00
NazrinNya c513938ed9 Make Heavy Ballistic Turrets Less Painful. (#3461)
Co-authored-by: NazrinNya <macrohard@macrohard.localhost>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-03-27 15:26:22 -04:00
monolith8319 a6431c9bab Automated Changelog (#3570) 2026-03-27 17:58:37 +00:00
Ilya246 f3390a81cc Gamemode Fixes and Changes (#3570)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-03-27 13:58:07 -04:00
monolith8319 6ba80381cc Automated Changelog (#3565) 2026-03-27 07:21:00 +00:00
Redrover1760 ef095aec88 Mono Claws Guidebook Update (#3565) 2026-03-27 03:20:31 -04:00
monolith8319 d02738089c Automated Changelog (#3575) 2026-03-27 07:19:51 +00:00
Redrover1760 b9f0de8576 Improves Hydrakin Head Displacement (#3575) 2026-03-27 03:19:24 -04:00
monolith8319 5a6d4b74b1 Automated Changelog (#3524) 2026-03-27 05:49:41 +00:00
significant harassment 1ac13bf597 Triple Rot Delay (#3524)
Co-authored-by: Onezero0 <onezero0@outlook.com>
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-03-27 01:49:14 -04:00
monolith8319 90ee912816 Automated Changelog (#3573) 2026-03-26 22:06:02 +00:00
Redrover1760 27665829e6 Viscerators No Longer Drop Lootboxes (#3572) 2026-03-26 18:05:41 -04:00
Redrover1760 e1459b0d43 Trade Crates Can No Longer Be Sold on Helios or Lancelot (#3573) 2026-03-26 18:05:32 -04:00
monolith8319 395d334dd7 Automated Changelog (#3560) 2026-03-26 05:55:40 +00:00
UnicornOnLSD 75e166e9b3 Maintains the DIS Femur (#3560)
Merged by Corrupt, Has 1 Fakefail.
2026-03-26 06:55:12 +01:00
monolith8319 d987cfe88d Automated Changelog (#3563) 2026-03-25 22:19:01 +00:00
Redrover1760 a942f3e7b1 Reflect Fixes (#3563) 2026-03-25 18:18:31 -04:00
monolith8319 4108933834 Automated Changelog (#3542) 2026-03-25 19:54:26 +00:00
Redrover1760 6af6f3b386 Fireteam Time Requirement Tweak (#3542) 2026-03-25 15:53:53 -04:00
monolith8319 9a590069d2 Automated Changelog (#3555) 2026-03-25 18:33:51 +00:00
Redrover1760 a60321f201 Ports CMO Webbing From Goob (#3555) 2026-03-25 19:33:22 +01:00
monolith8319 dfabb71045 Automated Changelog (#3554) 2026-03-25 06:13:58 +00:00
Redrover1760 ce034c3f19 Claws Tweak, Claws-Based Chemicals (#3554) 2026-03-25 02:13:30 -04:00
monolith8319 79c9dee112 Automated Changelog (#3558) 2026-03-25 05:50:21 +00:00
lopsidedmanboobs 6a048def33 Kill Ransom (#3558) 2026-03-25 01:49:54 -04:00
monolith8319 e41ef48441 Automated Changelog (#3556) 2026-03-25 04:12:58 +00:00
Redrover1760 27e7b2f70a Research Client Fix (#3556) 2026-03-25 00:12:27 -04:00
monolith8319 2bd317dadb Automated Changelog (#3551) 2026-03-25 00:09:05 +00:00
Maxim Bychkov 1145822797 Fix Voidboots Ability To Lie In Space (#3551) 2026-03-24 20:08:36 -04:00
monolith8319 27dca32ef3 Automated Changelog (#3260) 2026-03-24 23:42:18 +00:00
Avalon 4bdad8f6ee Ullman Coat 2 Electric Boogaloo (#3260)
Co-authored-by: Redrover1760 <39284090+Redrover1760@users.noreply.github.com>
2026-03-24 19:41:50 -04:00
monolith8319 38ebc2699c Automated Changelog (#3552) 2026-03-24 19:54:37 +00:00
Ilya246 82f75ff37f Nerf Big Ame (#3552) 2026-03-24 19:54:07 +00:00
Maxim Bychkov c84cde54cb Fix Map Painter (#3544) 2026-03-24 19:28:05 +00:00
monolith8319 f0c6624762 Automated Changelog (#3550) 2026-03-24 19:01:03 +00:00
Ilya246 277ad91140 Bugfixes (#3550) 2026-03-24 19:00:31 +00:00
monolith8319 b9419b25a1 Automated Changelog (#3155) 2026-03-24 15:08:01 +00:00
gelliii e1a5488f14 New Scrap Ship - Horsefly (#3155) 2026-03-24 16:07:30 +01:00
Redrover1760 d9b46ca6dc Update README.md 2026-03-24 09:15:30 -04:00
tonotom1 a672e9bd28 Revert "Cable Destruction Lag Reduction" (#3532) 2026-03-24 08:18:13 -04:00
monolith8319 2a5fe8fe6a Automated Changelog (#3548) 2026-03-24 12:07:19 +00:00
Redrover1760 e069f2ba9d Makes Printed Contraband Worth Things Again (#3548) 2026-03-24 08:06:49 -04:00
monolith8319 db3675b3e8 Automated Changelog (#3549) 2026-03-24 11:45:08 +00:00
chuga-git 79bf0fec68 Buffs MARAUDER Projectile Speed (#3549) 2026-03-24 07:44:39 -04:00
monolith8319 41b3fb9113 Automated Changelog (#3543) 2026-03-23 17:24:06 +00:00
Redrover1760 7cd749dfa6 Datafarm Fixes (#3543) 2026-03-23 13:23:36 -04:00
monolith8319 eb44deeeda Automated Changelog (#3534) 2026-03-23 06:05:19 +00:00
Redrover1760 7035e752a9 Majorly Buffs Part Wound Tending (#3534) 2026-03-23 02:04:49 -04:00
monolith8319 2fdd1cd573 Automated Changelog (#3381) 2026-03-23 04:08:14 +00:00
Kiithnaras 6c4101e0c0 Added Roofing and Emergency Lighting to Comet (#3381) 2026-03-23 00:00:28 -04:00
monolith8319 5f90433697 Automated Changelog (#3536) 2026-03-23 03:42:43 +00:00
Redrover1760 9f54374593 Research Datafarm Fix (#3536) 2026-03-22 23:42:15 -04:00
monolith8319 5272291275 Automated Changelog (#3533) 2026-03-22 23:23:01 +00:00
Redrover1760 4a0f4cec3a Buffed Chems and Surgery Part Healing/Damage (#3533) 2026-03-22 19:22:33 -04:00
monolith8319 5d68716090 Automated Changelog (#3530) 2026-03-22 22:54:38 +00:00
Redrover1760 a4b1a5317c People No Longer Thermal Regulate While Dead, Hydrakin Do Not While Crit Either (#3530) 2026-03-22 18:54:10 -04:00
monolith8319 ee48853724 Automated Changelog (#3520) 2026-03-22 22:41:50 +00:00
tonotom1 a8651f6457 Cable Destruction Lag Reduction (#3520) 2026-03-22 18:41:10 -04:00
750 changed files with 68090 additions and 32509 deletions
+5 -5
View File
@@ -1,5 +1,5 @@
name: PR Changelogs
concurrency: commit_action
concurrency: commit_action-${{ github.event.pull_request.base.ref }} # Mono
on:
pull_request_target:
types: [closed]
@@ -16,11 +16,11 @@ jobs:
permissions:
contents: write
steps:
- name: Checkout Master
- name: Checkout Base Branch # Mono
uses: actions/checkout@v3
with:
token: ${{ secrets.BOT_TOKEN }}
ref: "${{ vars.CHANGELOG_BRANCH }}"
ref: "${{ github.event.pull_request.base.ref }}" # Mono
- name: Setup Git
run: |
@@ -47,9 +47,9 @@ jobs:
- name: Commit Changelog
run: |
git pull origin main
git pull origin "${{ github.event.pull_request.base.ref }}"
git add "${{ env.CHANGELOG_DIR }}"
git commit -m "Automated Changelog (#${{ env.PR_NUMBER }})"
git push
git push origin "${{ github.event.pull_request.base.ref }}"
shell: bash
continue-on-error: true
+1 -1
View File
@@ -23,5 +23,5 @@ jobs:
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."
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."
})
+53 -7
View File
@@ -13,6 +13,26 @@
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Client - Tools",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build - Tools",
"program": "${workspaceFolder}/bin/Content.Client/Content.Client.dll",
"args": [],
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Client - Release",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build - Release",
"program": "${workspaceFolder}/bin/Content.Client/Content.Client.dll",
"args": [],
"console": "internalConsole",
"stopAtEntry": false
},
{
"name": "Client (Compatibility renderer)",
"type": "coreclr",
@@ -31,6 +51,26 @@
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": "Server - Tools",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build - Tools",
"program": "${workspaceFolder}/bin/Content.Server/Content.Server.dll",
"args": [""],
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": "Server - Release",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build - Release",
"program": "${workspaceFolder}/bin/Content.Server/Content.Server.dll",
"args": [""],
"console": "integratedTerminal",
"stopAtEntry": false
},
{
"name": "YAML Linter",
"type": "coreclr",
@@ -40,7 +80,7 @@
"cwd": "${workspaceFolder}/Content.YAMLLinter",
"console": "internalConsole",
"stopAtEntry": false
}
},
],
"compounds": [
{
@@ -51,14 +91,21 @@
],
"preLaunchTask": "build"
},
// Frontier begin
{
"name": "Server/Client (Release)",
"name": "Server/Client - Tools",
"configurations": [
"Server",
"Client"
"Server - Tools",
"Client - Tools"
],
"preLaunchTask": "build-release"
"preLaunchTask": "build - Tools"
},
{
"name": "Server/Client - Release",
"configurations": [
"Server - Release",
"Client - Release"
],
"preLaunchTask": "build - Release"
},
{
"name": "Server/Client (No Build)",
@@ -67,6 +114,5 @@
"Client"
]
}
// Frontier end
]
}
+26 -6
View File
@@ -21,26 +21,46 @@
},
"problemMatcher": "$msCompile"
},
// Frontier begin
{
"label": "build-release",
"label": "build - Tools",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--configuration=Release", // Build in release mode. Note: --, not /. /configuration doesn't work, because Microsoft.
"--configuration",
"Tools",
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
"/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
"/consoleloggerparameters:'ForceNoAlign;NoSummary'", // Do not generate summary otherwise it leads to duplicate errors in Problems panel
],
"group": {
"kind": "build"
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
{
"label": "build - Release",
"command": "dotnet",
"type": "shell",
"args": [
"build",
"--configuration",
"Release",
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
"/consoleloggerparameters:'ForceNoAlign;NoSummary'", // Do not generate summary otherwise it leads to duplicate errors in Problems panel
],
"group": {
"kind": "build",
"isDefault": false
},
"presentation": {
"reveal": "silent"
},
"problemMatcher": "$msCompile"
},
// Frontier end
{
"label": "build-yaml-linter",
"command": "dotnet",
@@ -32,6 +32,10 @@ public sealed class CharacterInfoSystem : EntitySystem
private void OnCharacterInfoEvent(CharacterInfoEvent msg, EntitySessionEventArgs args)
{
var entity = GetEntity(msg.NetEntity);
// Mono - fix
if (TerminatingOrDeleted(entity))
return;
var data = new CharacterData(entity, msg.JobTitle, msg.Objectives, msg.Briefing, Name(entity));
OnCharacterUpdate?.Invoke(data);
+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);
+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
}
}
}
+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;
}
+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));
@@ -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))
.ToList();
companies.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
@@ -0,0 +1,7 @@
<Control xmlns="https://spacestation14.io">
<BoxContainer Orientation="Vertical">
<Label Name="TitleLabel" Access="Public" />
<Label Name="ExampleLabel" Access="Public" />
<ColorSelectorSliders Name="Slider" Access="Public" HorizontalExpand="True" />
</BoxContainer>
</Control>
@@ -0,0 +1,31 @@
using Content.Client.Options.UI;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
namespace Content.Client.Options.UI;
/// <summary>
/// Standard UI control used for color sliders in the options menu. Intended for use with <see cref="OptionsTabControlRow"/>.
/// </summary>
/// <seealso cref="OptionsTabControlRow.AddOptionColorSlider"/>
[GenerateTypedNameReferences]
public sealed partial class OptionColorSlider : Control
{
/// <summary>
/// The text describing what this slider affects.
/// </summary>
public string? Title
{
get => TitleLabel.Text;
set => TitleLabel.Text = value;
}
/// <summary>
/// The example text showing the current color of the slider.
/// </summary>
public string? Example
{
get => ExampleLabel.Text;
set => ExampleLabel.Text = value;
}
}
@@ -143,6 +143,19 @@ public sealed partial class OptionsTabControlRow : Control
return AddOption(new OptionSliderIntCVar(this, _cfg, cVar, slider, min, max, format ?? FormatInt));
}
/// <summary>
/// Add a color slider option, backed by a simple string CVar.
/// </summary>
/// <param name="cVar">The CVar represented by the slider.</param>
/// <param name="slider">The UI control for the option.</param>
/// <returns>The option instance backing the added option.</returns>
public OptionColorSliderCVar AddOptionColorSlider(
CVarDef<string> cVar,
OptionColorSlider slider)
{
return AddOption(new OptionColorSliderCVar(this, _cfg, cVar, slider));
}
/// <summary>
/// Add a drop-down option, backed by a CVar.
/// </summary>
@@ -518,6 +531,54 @@ public sealed class OptionSliderFloatCVar : BaseOptionCVar<float>
}
}
public sealed class OptionColorSliderCVar : BaseOptionCVar<string>
{
private readonly OptionColorSlider _slider;
protected override string Value
{
get => _slider.Slider.Color.ToHex();
set
{
_slider.Slider.Color = Color.FromHex(value);
UpdateLabelColor();
}
}
/// <summary>
/// Creates a new instance of this type.
/// </summary>
/// <remarks>
/// <para>
/// It is generally more convenient to call overloads on <see cref="OptionsTabControlRow"/>
/// such as <see cref="OptionsTabControlRow.AddOptionPercentSlider"/> instead of instantiating this type directly.
/// </para>
/// </remarks>
/// <param name="controller">The control row that owns this option.</param>
/// <param name="cfg">The configuration manager to get and set values from.</param>
/// <param name="cVar">The CVar that is being controlled by this option.</param>
/// <param name="slider">The UI control for the option.</param>
public OptionColorSliderCVar(
OptionsTabControlRow controller,
IConfigurationManager cfg,
CVarDef<string> cVar,
OptionColorSlider slider) : base(controller, cfg, cVar)
{
_slider = slider;
slider.Slider.OnColorChanged += _ =>
{
ValueChanged();
UpdateLabelColor();
};
}
private void UpdateLabelColor()
{
_slider.ExampleLabel.FontColorOverride = Color.FromHex(Value);
}
}
/// <summary>
/// Implementation of a CVar option that simply corresponds with an integer <see cref="OptionSlider"/>.
/// </summary>
@@ -12,6 +12,10 @@
<ui:OptionSlider Name="SpeechBubbleTextOpacitySlider" Title="{Loc 'ui-options-speech-bubble-text-opacity'}" />
<ui:OptionSlider Name="SpeechBubbleSpeakerOpacitySlider" Title="{Loc 'ui-options-speech-bubble-speaker-opacity'}" />
<ui:OptionSlider Name="SpeechBubbleBackgroundOpacitySlider" Title="{Loc 'ui-options-speech-bubble-background-opacity'}" />
<CheckBox Name="AutoFillHighlightsCheckBox" Text="{Loc 'ui-options-auto-fill-highlights'}" />
<ui:OptionColorSlider Name="HighlightsColorSlider"
Title="{Loc 'ui-options-highlights-color'}"
Example="{Loc 'ui-options-highlights-color-example'}"/>
</BoxContainer>
</ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />
@@ -20,6 +20,8 @@ public sealed partial class AccessibilityTab : Control
Control.AddOptionPercentSlider(CCVars.SpeechBubbleTextOpacity, SpeechBubbleTextOpacitySlider);
Control.AddOptionPercentSlider(CCVars.SpeechBubbleSpeakerOpacity, SpeechBubbleSpeakerOpacitySlider);
Control.AddOptionPercentSlider(CCVars.SpeechBubbleBackgroundOpacity, SpeechBubbleBackgroundOpacitySlider);
Control.AddOptionCheckBox(CCVars.ChatAutoFillHighlights, AutoFillHighlightsCheckBox);
Control.AddOptionColorSlider(CCVars.ChatHighlightsColor, HighlightsColorSlider);
Control.Initialize();
}
@@ -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;
+1 -4
View File
@@ -173,11 +173,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>
@@ -33,9 +33,22 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
// Goobstation: Customizable borgs sprites
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
HelpGuidebookIds = GuidebookEntries;
SubtypeSelection.SubtypeSelected += () =>
ConfirmTypeButton.Disabled = false;
}
// foreach (var borgType in _prototypeManager.EnumeratePrototypes<BorgTypePrototype>().OrderBy(PrototypeName))
// Mono: Selectable borg whitelist
public void Populate(IReadOnlyList<ProtoId<BorgTypePrototype>> whitelist)
{
IEnumerable<BorgTypePrototype> types = whitelist.Select(id => _prototypeManager.Index(id)).OrderBy(PrototypeName);
foreach (var borgType in types)
// Mono: Selectable borg whitelist end
{
// Goobstation-Start: Customizable borgs sprites
var chassisList = new EntityPrototypeView
{
Scale = new Vector2(2, 2),
@@ -49,13 +62,6 @@ public sealed partial class BorgSelectTypeMenu : FancyWindow
};
SelectionsContainer.AddChild(chassisList);
}
ConfirmTypeButton.OnPressed += ConfirmButtonPressed;
HelpGuidebookIds = GuidebookEntries;
SubtypeSelection.SubtypeSelected += () =>
ConfirmTypeButton.Disabled = false;
// Goobstation-End: Customizable borgs sprites
}
@@ -23,8 +23,12 @@ public sealed class BorgSelectTypeUserInterface : BoundUserInterface
protected override void Open()
{
base.Open();
//Mono: Selectable borg whitelist
EntMan.TryGetComponent<BorgSwitchableTypeComponent>(Owner, out var comp);
var whitelist = comp?.TypeWhitelist ?? [];
_menu = this.CreateWindow<BorgSelectTypeMenu>();
_menu.Populate(whitelist); //Mono
_menu.ConfirmedBorgType += (prototype, subtype) => SendPredictedMessage(new BorgSelectTypeMessage(prototype, subtype));
}
}
@@ -46,18 +46,15 @@ public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
{
if (TryComp(entity, out SpriteComponent? sprite))
{
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Light, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteBodyState);
var rsiPath = SpriteSpecifierSerializer.TextureRoot / subtypePrototype.SpritePath;
if (_resourceCache.TryGetResource<RSIResource>(rsiPath, out var resource))
{
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Body, resource.RSI);
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Light, resource.RSI);
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.LightStatus, resource.RSI);
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Body, resource.RSI, prototype.SpriteBodyState);
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.Light, resource.RSI, prototype.SpriteBodyState);
_sprite.LayerSetRsi((entity, sprite), BorgVisualLayers.LightStatus, resource.RSI, prototype.SpriteToggleLightState);
}
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.Body, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteBodyState);
_sprite.LayerSetRsiState((entity, sprite), BorgVisualLayers.LightStatus, prototype.SpriteToggleLightState);
}
@@ -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));
}
}
}
@@ -0,0 +1,264 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Content.Shared.CCVar;
using Content.Client.CharacterInfo;
using static Content.Client.CharacterInfo.CharacterInfoSystem;
namespace Content.Client.UserInterface.Systems.Chat;
public sealed partial class ChatUIController : IOnSystemChanged<CharacterInfoSystem>
{
[Dependency] private readonly ILocalizationManager _loc = default!;
[UISystemDependency] private readonly CharacterInfoSystem _characterInfo = default!;
private static readonly Regex StartDoubleQuote = new("\"$");
private static readonly Regex EndDoubleQuote = new("^\"|(?<=^@)\"");
private static readonly Regex StartAtSign = new("^@");
private readonly List<string> _highlights = new();
private readonly List<string> _autoFillRawKeywords = new();
private string? _highlightsColor;
private bool _autoFillHighlightsEnabled;
private bool _charInfoIsAttach = false;
private string? _currentCharacterName;
public event Action<string>? HighlightsUpdated;
public event Action<string>? AutoFillUpdated;
private void InitializeHighlights()
{
_config.OnValueChanged(CCVars.ChatAutoFillHighlights, (value) => { _autoFillHighlightsEnabled = value; }, true);
_config.OnValueChanged(CCVars.ChatHighlightsColor, (value) => { _highlightsColor = value; }, true);
var cvar = _config.GetCVar(CCVars.ChatHighlights);
}
public void OnSystemLoaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate += OnCharacterUpdated;
}
public void OnSystemUnloaded(CharacterInfoSystem system)
{
system.OnCharacterUpdate -= OnCharacterUpdated;
}
private void UpdateAutoFillHighlights()
{
_currentCharacterName = null;
_autoFillRawKeywords.Clear();
_highlights.Clear();
AutoFillUpdated?.Invoke("");
HighlightsUpdated?.Invoke("");
_charInfoIsAttach = true;
_characterInfo.RequestCharacterInfo();
}
/// <summary>
/// Saves the user's custom keywords for the given character into the CVar,
/// then rebuilds the active highlight list.
/// Called when the user submits keywords from the popup.
/// </summary>
public void UpdateHighlights(string customKeywords)
{
if (_currentCharacterName == null)
{
return;
}
SaveCustomKeywords(_currentCharacterName, customKeywords);
RebuildHighlightsList(customKeywords);
HighlightsUpdated?.Invoke(customKeywords);
}
private void OnCharacterUpdated(CharacterData data)
{
if (!_charInfoIsAttach)
{
return;
}
var (_, job, _, _, entityName) = data;
_currentCharacterName = entityName;
_autoFillRawKeywords.Clear();
var autoFillDisplay = "";
if (_autoFillHighlightsEnabled)
{
// Special rules for names can be added here
var nameHighlights = "@" + entityName;
// Split on spaces first (handles any number of space-separated words).
if (nameHighlights.Contains(' '))
nameHighlights = nameHighlights.Replace(" ", "\n@");
// Handle hyphenated tokens: single hyphen splits normally (e.g. "First-Last"),
// multiple hyphens use the lizard rule — keep only first and last part (e.g. "Eats-The-Food" → @Eats @Food).
var dashParts = nameHighlights.Split('-');
if (dashParts.Length == 2)
nameHighlights = nameHighlights.Replace("-", "\n@");
else if (dashParts.Length > 2)
nameHighlights = dashParts[0] + "\n@" + dashParts[^1];
foreach (var token in nameHighlights.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
_autoFillRawKeywords.Add(token);
}
var jobKey = job.Replace(' ', '-').ToLower();
if (_loc.TryGetString($"highlights-{jobKey}", out var jobMatches))
{
foreach (var jobWord in jobMatches.Replace(", ", "\n").Split('\n', StringSplitOptions.RemoveEmptyEntries))
_autoFillRawKeywords.Add(jobWord);
}
autoFillDisplay = string.Join("\n", _autoFillRawKeywords);
}
var customKeywords = LoadCustomKeywords(entityName);
RebuildHighlightsList(customKeywords);
AutoFillUpdated?.Invoke(autoFillDisplay);
HighlightsUpdated?.Invoke(customKeywords);
_charInfoIsAttach = false;
}
private string LoadCustomKeywords(string characterName)
{
var cvar = _config.GetCVar(CCVars.ChatHighlights);
if (string.IsNullOrEmpty(cvar))
return "";
// Shouldn't be needed as no-one should have data by the time this rolls out but we never know
if (!cvar.Contains("||"))
{
return cvar;
}
foreach (var line in cvar.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
var sep = line.IndexOf("||", StringComparison.Ordinal);
if (sep < 0)
continue;
if (line[..sep] == characterName)
return DecodeKeywords(line[(sep + 2)..]);
}
return "";
}
private void SaveCustomKeywords(string characterName, string customKeywords)
{
var cvar = _config.GetCVar(CCVars.ChatHighlights);
var entries = new Dictionary<string, string>();
if (!string.IsNullOrEmpty(cvar) && cvar.Contains("||"))
{
foreach (var line in cvar.Split('\n', StringSplitOptions.RemoveEmptyEntries))
{
var sep = line.IndexOf("||", StringComparison.Ordinal);
if (sep < 0)
continue;
entries[line[..sep]] = line[(sep + 2)..];
}
}
entries[characterName] = EncodeKeywords(customKeywords);
var sb = new StringBuilder();
foreach (var (name, encoded) in entries)
sb.Append(name).Append("||").Append(encoded).Append('\n');
var newValue = sb.ToString().TrimEnd('\n');
_config.SetCVar(CCVars.ChatHighlights, newValue);
_config.SaveToFile();
}
private static string EncodeKeywords(string raw)
{
return raw
.Replace(@"\", @"\\")
.Replace("|", @"\|")
.Replace("\n", @"\n");
}
private static string DecodeKeywords(string encoded)
{
var sb = new StringBuilder(encoded.Length);
for (var i = 0; i < encoded.Length; i++)
{
if (encoded[i] == '\\' && i + 1 < encoded.Length)
{
switch (encoded[i + 1])
{
case 'n': sb.Append('\n'); i++; break;
case '|': sb.Append('|'); i++; break;
case '\\': sb.Append('\\'); i++; break;
default: sb.Append(encoded[i]); break;
}
}
else
{
sb.Append(encoded[i]);
}
}
return sb.ToString();
}
private void RebuildHighlightsList(string customRaw)
{
_highlights.Clear();
foreach (var raw in _autoFillRawKeywords)
_highlights.Add(ProcessKeyword(raw));
if (!string.IsNullOrWhiteSpace(customRaw))
{
foreach (var raw in customRaw.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
_highlights.Add(ProcessKeyword(raw));
}
_highlights.Sort((x, y) => y.Length.CompareTo(x.Length));
}
private string ProcessKeyword(string raw)
{
// Replace every "\" to prevent "\n", "\0", etc. in regex.
var keyword = raw.Replace(@"\", @"\\");
// Escape special regex characters.
keyword = Regex.Escape(keyword);
// Re-escape "[" for markup-adjacent positions.
keyword = keyword.Replace(@"\[", @"\\\[");
// words in double quotes will only be hightlighted as unique, not within another word
if (keyword.Any(c => c == '"'))
{
keyword = StartDoubleQuote.Replace(keyword, "(?!\\w)");
keyword = EndDoubleQuote.Replace(keyword, "(?<!\\w)");
}
// Make it so it doesn't hightlight your own name when you are talking
keyword = StartAtSign.Replace(keyword, @"(?<=\[BubbleContent\].*)");
return keyword;
}
}
@@ -44,7 +44,7 @@ using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Chat;
public sealed class ChatUIController : UIController
public sealed partial class ChatUIController : UIController
{
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IChatManager _manager = default!;
@@ -248,6 +248,8 @@ public sealed class ChatUIController : UIController
_config.OnValueChanged(CCVars.ChatWindowOpacity, OnChatWindowOpacityChanged);
InitializeHighlights();
}
public void OnScreenLoad()
@@ -434,6 +436,7 @@ public sealed class ChatUIController : UIController
private void OnAttachedChanged(EntityUid uid)
{
UpdateChannelPermissions();
UpdateAutoFillHighlights();
}
private void AddSpeechBubble(ChatMessage msg, SpeechBubble.SpeechType speechType)
@@ -877,6 +880,11 @@ public sealed class ChatUIController : UIController
}
}
foreach (var highlight in _highlights)
{
msg.WrappedMessage = SharedChatSystem.InjectTagAroundString(msg, highlight, "color", _highlightsColor);
}
// Log all incoming chat to repopulate when filter is un-toggled
if (!msg.HideChat)
{
@@ -912,7 +920,6 @@ public sealed class ChatUIController : UIController
case ChatChannel.Dead:
if (_ghost is not {IsGhost: true})
break;
AddSpeechBubble(msg, SpeechBubble.SpeechType.Say);
break;
@@ -1,10 +1,30 @@
<controls:ChannelFilterPopup
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls">
xmlns:controls="clr-namespace:Content.Client.UserInterface.Systems.Chat.Controls"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
<PanelContainer Name="FilterPopupPanel" StyleClasses="BorderedWindowPanel">
<BoxContainer Orientation="Horizontal">
<Control MinSize="4 0"/>
<BoxContainer Name="FilterVBox" MinWidth="110" Margin="0 10" Orientation="Vertical" SeparationOverride="4"/>
<BoxContainer Name="HighlightsVBox" MinWidth="120" Margin="0 10" Orientation="Vertical" SeparationOverride="4">
<BoxContainer Name="AutoFillVBox" Orientation="Vertical" SeparationOverride="2" Visible="False">
<Label Text="{Loc 'hud-chatbox-autofill-highlights'}"/>
<PanelContainer>
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#262736"/>
</PanelContainer.PanelOverride>
<RichTextLabel Name="AutoFillLabel" Margin="5 5"/>
</PanelContainer>
</BoxContainer>
<Label Text="{Loc 'hud-chatbox-highlights'}"/>
<PanelContainer>
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="#323446"/>
</PanelContainer.PanelOverride>
<TextEdit Name="HighlightEdit" MinHeight="150" Margin="5 5"/>
</PanelContainer>
<Button Name="HighlightButton" Text="{Loc 'hud-chatbox-highlights-button'}" ToolTip="{Loc 'hud-chatbox-highlights-tooltip'}"/>
</BoxContainer>
</BoxContainer>
</PanelContainer>
</controls:ChannelFilterPopup>
@@ -2,6 +2,7 @@ using Content.Shared.Chat;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.UserInterface.Systems.Chat.Controls;
@@ -30,10 +31,13 @@ public sealed partial class ChannelFilterPopup : Popup
private readonly Dictionary<ChatChannel, ChannelFilterCheckbox> _filterStates = new();
public event Action<ChatChannel, bool>? OnChannelFilter;
public event Action<string>? OnNewHighlights;
public ChannelFilterPopup()
{
RobustXamlLoader.Load(this);
HighlightButton.OnPressed += HighlightsEntered;
HighlightEdit.Placeholder = new Rope.Leaf(Loc.GetString("hud-chatbox-highlights-placeholder"));
}
public bool IsActive(ChatChannel channel)
@@ -95,4 +99,36 @@ public sealed partial class ChannelFilterPopup : Popup
if (_filterStates.TryGetValue(channel, out var checkbox))
checkbox.UpdateUnreadCount(unread);
}
// Highlight
/// <summary>
/// Updates the read-only auto-fill section with name/job tokens.
/// </summary>
public void UpdateAutoFill(string autoFillKeywords)
{
AutoFillVBox.Visible = !string.IsNullOrEmpty(autoFillKeywords);
if (string.IsNullOrEmpty(autoFillKeywords))
return;
var msg = new FormattedMessage();
var lines = autoFillKeywords.Split('\n', StringSplitOptions.RemoveEmptyEntries);
for (var i = 0; i < lines.Length; i++)
{
if (i > 0) msg.PushNewline();
msg.AddText(lines[i]);
}
AutoFillLabel.SetMessage(msg);
}
public void UpdateHighlights(string highlights)
{
HighlightEdit.TextRope = new Rope.Leaf(highlights);
}
private void HighlightsEntered(ButtonEventArgs _args)
{
OnNewHighlights?.Invoke(Rope.Collapse(HighlightEdit.TextRope));
}
}
@@ -47,9 +47,12 @@ public partial class ChatBox : UIWidget
ChatInput.Input.OnFocusExit += OnFocusExit;
ChatInput.ChannelSelector.OnChannelSelect += OnChannelSelect;
ChatInput.FilterButton.Popup.OnChannelFilter += OnChannelFilter;
ChatInput.FilterButton.Popup.OnNewHighlights += OnNewHighlights;
_controller = UserInterfaceManager.GetUIController<ChatUIController>();
_controller.MessageAdded += OnMessageAdded;
_controller.HighlightsUpdated += OnHighlightsUpdated;
_controller.AutoFillUpdated += OnAutoFillUpdated;
_controller.RegisterChat(this);
// WD EDIT START
@@ -133,6 +136,21 @@ public partial class ChatBox : UIWidget
}
}
private void OnNewHighlights(string highlighs)
{
_controller.UpdateHighlights(highlighs);
}
private void OnHighlightsUpdated(string highlights)
{
ChatInput.FilterButton.Popup.UpdateHighlights(highlights);
}
private void OnAutoFillUpdated(string autoFillKeywords)
{
ChatInput.FilterButton.Popup.UpdateAutoFill(autoFillKeywords);
}
public void AddLine(string message, Color color, int repeat = 0) // WD EDIT
{
var formatted = new FormattedMessage(4); // WD EDIT // specifying size beforehand smells like a useless microoptimisation, but i'll give them the benefit of doubt
@@ -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)
+5 -1
View File
@@ -64,7 +64,11 @@ namespace Content.Client.Voting.UI
var entry = _vote.Entries[i];
if (_vote.DisplayVotes)
{
_voteButtons[i].Text = Loc.GetString("ui-vote-button", ("text", entry.Text), ("votes", entry.Votes));
// Mono
if (entry.RealVotes != entry.Votes)
_voteButtons[i].Text = Loc.GetString("ui-vote-button-weighted", ("text", entry.Text), ("votes", entry.Votes), ("realvotes", entry.RealVotes));
else
_voteButtons[i].Text = Loc.GetString("ui-vote-button", ("text", entry.Text), ("votes", entry.Votes));
}
else
{
+2
View File
@@ -192,6 +192,7 @@ namespace Content.Client.Voting
for (var i = 0; i < message.Options.Length; i++)
{
existingVote.Entries[i].Votes = message.Options[i].votes;
existingVote.Entries[i].RealVotes = message.Options[i].realVotes; // Mono
}
if (@new && _popupContainer != null)
@@ -259,6 +260,7 @@ namespace Content.Client.Voting
{
public string Text { get; }
public int Votes { get; set; }
public int RealVotes; // Mono
public VoteEntry(string text)
{
@@ -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);
}
}
}
@@ -0,0 +1,12 @@
<ui:RadialMenu xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls;assembly=Content.Client"
BackButtonStyleClass="RadialMenuBackButton"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True"
MinSize="450 450">
<ui:RadialContainer Name="Main" VerticalExpand="True" HorizontalExpand="True" InitialRadius="100"
ReserveSpaceForHiddenChildren="False" />
</ui:RadialMenu>
@@ -0,0 +1,93 @@
using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared._Goobstation.Weapons.AmmoSelector;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client._Goobstation.AmmoSelector;
[GenerateTypedNameReferences]
public sealed partial class AmmoSelectorMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
private SpriteSystem _sprites;
public event Action<ProtoId<SelectableAmmoPrototype>>? SendAmmoSelectorSystemMessageAction;
private EntityUid _item;
public AmmoSelectorMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_sprites = _entManager.System<SpriteSystem>();
}
public void SetEntity(EntityUid uid)
{
_item = uid;
Refresh();
}
public void Refresh()
{
var main = FindControl<RadialContainer>("Main");
main.RemoveAllChildren();
if (!_entManager.TryGetComponent(_item, out AmmoSelectorComponent? ammoSelector))
return;
foreach (var ammo in ammoSelector.Prototypes)
{
if (!_protoManager.TryIndex(ammo, out var prototype))
continue;
var button = new AmmoSelectorMenuButton
{
SetSize = new Vector2(64, 64),
ToolTip = Loc.GetString(prototype.Desc),
ProtoId = prototype.ID
};
var texture = new TextureRect
{
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Texture = _sprites.Frame0(prototype.Icon),
TextureScale = new Vector2(2f, 2f)
};
button.AddChild(texture);
main.AddChild(button);
}
AddAmmoSelectorMenuButtonOnClickActions(main);
}
private void AddAmmoSelectorMenuButtonOnClickActions(RadialContainer control)
{
foreach (var child in control.Children)
{
if (child is not AmmoSelectorMenuButton castChild)
continue;
castChild.OnButtonUp += _ =>
{
SendAmmoSelectorSystemMessageAction?.Invoke(castChild.ProtoId);
Close();
};
}
}
}
public sealed class AmmoSelectorMenuButton : RadialMenuTextureButtonWithSector
{
public ProtoId<SelectableAmmoPrototype> ProtoId { get; set; }
}
@@ -0,0 +1,39 @@
using Content.Shared._Goobstation.Weapons.AmmoSelector;
using JetBrains.Annotations;
using Robust.Client.Graphics;
using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client._Goobstation.AmmoSelector;
[UsedImplicitly]
public sealed class AmmoSelectorMenuBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IClyde _displayManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
private AmmoSelectorMenu? _menu;
public AmmoSelectorMenuBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<AmmoSelectorMenu>();
_menu.SetEntity(Owner);
_menu.SendAmmoSelectorSystemMessageAction += SendAmmoSelectorSystemMessage;
var vpSize = _displayManager.ScreenSize;
_menu.OpenCenteredAt(_inputManager.MouseScreenPosition.Position / vpSize);
}
public void SendAmmoSelectorSystemMessage(ProtoId<SelectableAmmoPrototype> protoId)
{
SendPredictedMessage(new AmmoSelectedMessage(protoId));
}
}
@@ -0,0 +1,45 @@
using Content.Shared._Mono.CCVar;
using Content.Shared._Mono.Company;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
namespace Content.Client._Mono.Company;
public sealed partial class CompanyManager
{
[Dependency] private readonly INetManager _net = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private HashSet<ProtoId<CompanyPrototype>> _whitelist = new();
public void Initialize()
{
_net.RegisterNetMessage<MsgCompanyWhitelist>(OnWhitelistMsg);
}
private void OnWhitelistMsg(MsgCompanyWhitelist msg)
{
_whitelist = msg.Whitelist;
}
public bool IsPlayerWhitelisted(ProtoId<CompanyPrototype> company)
{
return _whitelist.Contains(company);
}
public bool IsAllowed(ProtoId<CompanyPrototype> company)
{
if (!_config.GetCVar(MonoCVars.CompanyWhitelist))
return true;
if (!_proto.TryIndex(company, out var companyPrototype) ||
!companyPrototype.Whitelisted)
{
return true;
}
return IsPlayerWhitelisted(company);
}
}
@@ -24,15 +24,14 @@ namespace Content.Client.Shuttles.BUI
});
}
private void OnMaxShuttleSpeedChanged(NetEntity? entityUid, float maxSpeed)
private void OnMaxShuttleSpeedChanged(float? maxSpeed)
{
SendMessage(new SetMaxShuttleSpeedRequest
{
ShuttleEntityUid = entityUid,
MaxSpeed = maxSpeed,
});
}
private void OnNetworkPortButtonPressed(string sourcePort, string targetPort)
{
SendMessage(new ShuttlePortButtonPressedMessage
@@ -3,6 +3,8 @@
// See AGPLv3.txt for details.
using Content.Shared._NF.Shuttles.Events;
using Robust.Client.UserInterface.Controls;
using System.Text.RegularExpressions;
using System;
namespace Content.Client.Shuttles.UI
{
@@ -10,7 +12,7 @@ namespace Content.Client.Shuttles.UI
{
private readonly ButtonGroup _buttonGroup = new();
public event Action<NetEntity?, InertiaDampeningMode>? OnInertiaDampeningModeChanged;
public event Action<NetEntity?, float>? OnMaxShuttleSpeedChanged;
public event Action<float?>? OnMaxShuttleSpeedChanged;
public event Action<string, string>? OnNetworkPortButtonPressed;
private void NfInitialize()
@@ -23,8 +25,7 @@ namespace Content.Client.Shuttles.UI
MaximumIFFDistanceValue.OnValueChanged += args => OnRangeFilterChanged(args);
// Frontier - Maximum Shuttle Speed
MaximumShuttleSpeedValue.GetChild(0).GetChild(1).Margin = new Thickness(8, 0, 0, 0);
MaximumShuttleSpeedValue.OnValueChanged += args => OnMaxSpeedChanged(args);
MaximumShuttleSpeedValue.OnTextChanged += args => OnMaxSpeedChanged(args);
DampenerOff.OnPressed += _ => SetDampenerMode(InertiaDampeningMode.Off);
DampenerOn.OnPressed += _ => SetDampenerMode(InertiaDampeningMode.Dampen);
@@ -99,10 +100,11 @@ namespace Content.Client.Shuttles.UI
}
// Frontier - Maximum Shuttle Speed
private void OnMaxSpeedChanged(int value)
private void OnMaxSpeedChanged(LineEdit.LineEditEventArgs value)
{
_entManager.TryGetNetEntity(_shuttleEntity, out var shuttle);
OnMaxShuttleSpeedChanged?.Invoke(shuttle, value);
MaximumShuttleSpeedValue.Text = Regex.Replace(MaximumShuttleSpeedValue.Text, "[^0-9]", "");
float.TryParse(MaximumShuttleSpeedValue.Text, out var speed);
OnMaxShuttleSpeedChanged?.Invoke(MaximumShuttleSpeedValue.Text == "" ? null : speed);
}
private void NfAddShuttleDesignation(EntityUid? shuttle)
@@ -111,11 +113,11 @@ namespace Content.Client.Shuttles.UI
if (_entManager.TryGetComponent<MetaDataComponent>(shuttle, out var metadata))
{
var shipName = metadata.EntityName;
// Try to find a designation in the format XXX-### (like CIV-748)
// by checking each word in the ship name
var shipNameParts = shipName.Split(' ');
foreach (var part in shipNameParts)
{
// Check if this part matches the designation format (e.g., CIV-748)
@@ -132,7 +134,7 @@ namespace Content.Client.Shuttles.UI
}
}
}
// If we get here, no designation was found, so just show the full name
NavDisplayLabel.Text = shipName;
// Leave ShuttleDesignation.Text as "Unknown" (the default)
@@ -8,7 +8,7 @@ namespace Content.Client.Shuttles.UI
public sealed partial class ShuttleConsoleWindow
{
public event Action<NetEntity?, InertiaDampeningMode>? OnInertiaDampeningModeChanged;
public event Action<NetEntity?, float>? OnMaxShuttleSpeedChanged;
public event Action<float?>? OnMaxShuttleSpeedChanged;
public event Action<string, string>? OnNetworkPortButtonPressed;
private void NfInitialize()
@@ -18,11 +18,11 @@ namespace Content.Client.Shuttles.UI
OnInertiaDampeningModeChanged?.Invoke(entity, mode);
};
NavContainer.OnMaxShuttleSpeedChanged += (entityUid, maxSpeed) =>
NavContainer.OnMaxShuttleSpeedChanged += (maxSpeed) =>
{
OnMaxShuttleSpeedChanged?.Invoke(entityUid, maxSpeed);
OnMaxShuttleSpeedChanged?.Invoke(maxSpeed);
};
NavContainer.OnNetworkPortButtonPressed += (sourcePort, targetPort) =>
{
OnNetworkPortButtonPressed?.Invoke(sourcePort, targetPort);
@@ -39,6 +39,8 @@ namespace Content.MapRenderer.Painters
// Seriously whoever made MapPainter use GameMapPrototype I wish you step on a lego one time.
Map = map,
});
pair.ServerLogHandler.FailureLevel = LogLevel.Fatal;
pair.ClientLogHandler.FailureLevel = LogLevel.Fatal;
await foreach (var image in RenderPair(stopwatch, pair))
yield return image;
@@ -56,6 +58,8 @@ namespace Content.MapRenderer.Painters
Connected = true,
Fresh = false,
});
pair.ServerLogHandler.FailureLevel = LogLevel.Fatal;
pair.ClientLogHandler.FailureLevel = LogLevel.Fatal;
var server = pair.Server;
var client = pair.Client;
@@ -69,6 +73,14 @@ namespace Content.MapRenderer.Painters
foreach (var grid in sMapManager.GetAllGrids(mapId))
sEntityManager.QueueDeleteEntity(grid);
});
await pair.RunTicksSync(10);
await Task.WhenAll(client.WaitIdleAsync(), server.WaitIdleAsync());
await server.WaitPost(() =>
{
var mapId = sEntityManager.System<GameTicker>().DefaultMap;
try
{
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,22 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
/// <inheritdoc />
public partial class CompanyMembers2 : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Postgres
{
/// <inheritdoc />
public partial class CompanyMembers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "company_members",
columns: table => new
{
player_user_id = table.Column<Guid>(type: "uuid", nullable: false),
company_id = table.Column<string>(type: "text", nullable: false),
owner = table.Column<bool>(type: "boolean", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_company_members", x => new { x.player_user_id, x.company_id });
table.ForeignKey(
name: "FK_company_members_player_player_user_id",
column: x => x.player_user_id,
principalTable: "player",
principalColumn: "user_id",
onDelete: ReferentialAction.Cascade);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "company_members");
}
}
}
@@ -20,7 +20,7 @@ namespace Content.Server.Database.Migrations.Postgres
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "9.0.1")
.HasAnnotation("ProductVersion", "10.0.0")
.HasAnnotation("Relational:MaxIdentifierLength", 63);
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
@@ -581,6 +581,26 @@ namespace Content.Server.Database.Migrations.Postgres
b.ToTable("blacklist", (string)null);
});
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
{
b.Property<Guid>("PlayerUserId")
.HasColumnType("uuid")
.HasColumnName("player_user_id");
b.Property<string>("CompanyId")
.HasColumnType("text")
.HasColumnName("company_id");
b.Property<bool>("Owner")
.HasColumnType("boolean")
.HasColumnName("owner");
b.HasKey("PlayerUserId", "CompanyId")
.HasName("PK_company_members");
b.ToTable("company_members", (string)null);
});
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
{
b.Property<int>("Id")
@@ -1680,6 +1700,19 @@ namespace Content.Server.Database.Migrations.Postgres
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
{
b.HasOne("Content.Server.Database.Player", "Player")
.WithMany("CompanyMembers")
.HasForeignKey("PlayerUserId")
.HasPrincipalKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_company_members_player_player_user_id");
b.Navigation("Player");
});
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
{
b.HasOne("Content.Server.Database.Server", "Server")
@@ -2080,6 +2113,8 @@ namespace Content.Server.Database.Migrations.Postgres
b.Navigation("AdminWatchlistsReceived");
b.Navigation("CompanyMembers");
b.Navigation("JobWhitelists");
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,41 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace Content.Server.Database.Migrations.Sqlite
{
/// <inheritdoc />
public partial class CompanyMembers : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "company_members",
columns: table => new
{
player_user_id = table.Column<Guid>(type: "TEXT", nullable: false),
company_id = table.Column<string>(type: "TEXT", nullable: false),
owner = table.Column<bool>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_company_members", x => new { x.player_user_id, x.company_id });
table.ForeignKey(
name: "FK_company_members_player_player_user_id",
column: x => x.player_user_id,
principalTable: "player",
principalColumn: "user_id",
onDelete: ReferentialAction.Cascade);
});
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "company_members");
}
}
}
@@ -15,7 +15,7 @@ namespace Content.Server.Database.Migrations.Sqlite
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "9.0.1");
modelBuilder.HasAnnotation("ProductVersion", "10.0.0");
modelBuilder.Entity("Content.Server.Database.Admin", b =>
{
@@ -550,6 +550,26 @@ namespace Content.Server.Database.Migrations.Sqlite
b.ToTable("blacklist", (string)null);
});
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
{
b.Property<Guid>("PlayerUserId")
.HasColumnType("TEXT")
.HasColumnName("player_user_id");
b.Property<string>("CompanyId")
.HasColumnType("TEXT")
.HasColumnName("company_id");
b.Property<bool>("Owner")
.HasColumnType("INTEGER")
.HasColumnName("owner");
b.HasKey("PlayerUserId", "CompanyId")
.HasName("PK_company_members");
b.ToTable("company_members", (string)null);
});
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
{
b.Property<int>("Id")
@@ -1604,6 +1624,19 @@ namespace Content.Server.Database.Migrations.Sqlite
b.Navigation("Profile");
});
modelBuilder.Entity("Content.Server.Database.CompanyMember", b =>
{
b.HasOne("Content.Server.Database.Player", "Player")
.WithMany("CompanyMembers")
.HasForeignKey("PlayerUserId")
.HasPrincipalKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired()
.HasConstraintName("FK_company_members_player_player_user_id");
b.Navigation("Player");
});
modelBuilder.Entity("Content.Server.Database.ConnectionLog", b =>
{
b.HasOne("Content.Server.Database.Server", "Server")
@@ -1632,7 +1665,7 @@ namespace Content.Server.Database.Migrations.Sqlite
b1.HasKey("ConnectionLogId");
b1.ToTable("connection_log");
b1.ToTable("connection_log", (string)null);
b1.WithOwner()
.HasForeignKey("ConnectionLogId")
@@ -1677,7 +1710,7 @@ namespace Content.Server.Database.Migrations.Sqlite
b1.HasKey("PlayerId");
b1.ToTable("player");
b1.ToTable("player", (string)null);
b1.WithOwner()
.HasForeignKey("PlayerId")
@@ -1800,7 +1833,7 @@ namespace Content.Server.Database.Migrations.Sqlite
b1.HasKey("ServerBanId");
b1.ToTable("server_ban");
b1.ToTable("server_ban", (string)null);
b1.WithOwner()
.HasForeignKey("ServerBanId")
@@ -1877,7 +1910,7 @@ namespace Content.Server.Database.Migrations.Sqlite
b1.HasKey("ServerRoleBanId");
b1.ToTable("server_role_ban");
b1.ToTable("server_role_ban", (string)null);
b1.WithOwner()
.HasForeignKey("ServerRoleBanId")
@@ -2004,6 +2037,8 @@ namespace Content.Server.Database.Migrations.Sqlite
b.Navigation("AdminWatchlistsReceived");
b.Navigation("CompanyMembers");
b.Navigation("JobWhitelists");
});
+24
View File
@@ -46,6 +46,7 @@ namespace Content.Server.Database
public DbSet<RoleWhitelist> RoleWhitelists { get; set; } = null!;
public DbSet<BanTemplate> BanTemplate { get; set; } = null!;
public DbSet<IPIntelCache> IPIntelCache { get; set; } = null!;
public DbSet<CompanyMember> CompanyMembers { get; set; } = null!;
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
@@ -371,6 +372,14 @@ namespace Content.Server.Database
.OwnsOne(p => p.HWId)
.Property(p => p.Type)
.HasDefaultValue(HwidType.Legacy);
// Mono
modelBuilder.Entity<CompanyMember>()
.HasOne(w => w.Player)
.WithMany(p => p.CompanyMembers)
.HasForeignKey(w => w.PlayerUserId)
.HasPrincipalKey(p => p.UserId)
.OnDelete(DeleteBehavior.Cascade);
}
public virtual IQueryable<AdminLog> SearchLogs(IQueryable<AdminLog> query, string searchText)
@@ -600,6 +609,7 @@ namespace Content.Server.Database
public List<ServerRoleBan> AdminServerRoleBansCreated { get; set; } = null!;
public List<ServerRoleBan> AdminServerRoleBansLastEdited { get; set; } = null!;
public List<RoleWhitelist> JobWhitelists { get; set; } = null!;
public List<CompanyMember> CompanyMembers { get; set; } = null!; // Mono
}
[Table("whitelist")]
@@ -1337,4 +1347,18 @@ namespace Content.Server.Database
/// </summary>
public float Score { get; set; }
}
// Mono-Start
[PrimaryKey(nameof(PlayerUserId), nameof(CompanyId))]
public class CompanyMember
{
[Required, ForeignKey("Player")]
public Guid PlayerUserId { get; set; }
public Player Player { get; set; } = default!;
public bool Owner { get; set; } = false;
[Required]
public string CompanyId { get; set; } = default!;
}
// Mono-End
}
+1 -1
View File
@@ -178,7 +178,7 @@ public sealed class AmeNodeGroup : BaseNodeGroup
// return MathF.Max(200000f * MathF.Log10(2 * fuel * MathF.Pow(cores, (float)-0.5)), 0); // Frontier: preferring old calculation for now
// return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow(0.75f, cores - 1); // Frontier: preferring old calculation for now
if (cores > 0 && fuel > 0) // Mono - default zero power unless conditions are met
return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow( 1.11111f, cores - 1); // Mono - Added positive returns from extra cores. Running AME at 2 fuel per core yields roughly the same power per core at all sizes
return 200000f * MathF.Log10(fuel * fuel) * MathF.Pow( 1.11111f, fuel / 2f - 1); // Mono - Exponential scaling at high injection
return 0;
}
@@ -287,7 +287,7 @@ namespace Content.Server.Atmos.EntitySystems
// This is intended so that matches & candles can re-use code for un-shaded layers on in-hand sprites.
// However, this could cause conflicts if something is ACTUALLY both a toggleable light and flammable.
// if that ever happens, then fire visuals will need to implement their own in-hand sprite management.
_appearance.SetData(uid, ToggleableLightVisuals.Enabled, flammable.OnFire, appearance);
_appearance.SetData(uid, ToggleableVisuals.Enabled, flammable.OnFire, appearance);
}
public void AdjustFireStacks(EntityUid uid, float relativeFireStacks, FlammableComponent? flammable = null, bool ignite = false)
@@ -61,4 +61,16 @@ public sealed partial class ThermalRegulatorComponent : Component
/// </summary>
[DataField]
public float ThermalRegulationTemperatureThreshold;
/// <summary>
/// Process regulation while they're dead if there is a MobStateComponent? - Mono Hydrakin
/// </summary>
[DataField]
public bool ProcessWhileDead = false;
/// <summary>
/// Process regulation in critical condition if there is a MobStateComponent? - Mono Hydrakin
/// </summary>
[DataField]
public bool ProcessWhileCrit = true;
}
@@ -2,6 +2,8 @@ using Content.Server.Body.Components;
using Content.Server.Temperature.Components;
using Content.Server.Temperature.Systems;
using Content.Shared.ActionBlocker;
using Content.Shared.Mobs; // Mono
using Content.Shared.Mobs.Components; // Mono
using Robust.Shared.Timing;
namespace Content.Server.Body.Systems;
@@ -51,6 +53,14 @@ public sealed class ThermalRegulatorSystem : EntitySystem
if (!Resolve(ent, ref ent.Comp2, logMissing: false))
return;
// mono begin
if (ent.Comp1.ProcessWhileDead == false && TryComp<MobStateComponent>(ent, out var mobComp1) && mobComp1.CurrentState == MobState.Dead)
return;
if (ent.Comp1.ProcessWhileCrit == false && TryComp<MobStateComponent>(ent, out var mobComp2) && mobComp2.CurrentState == MobState.Critical)
return;
// mono end
var totalMetabolismTempChange = ent.Comp1.MetabolismHeat - ent.Comp1.RadiatedHeat;
// implicit heat regulation
@@ -28,8 +28,6 @@ public sealed partial class BotanySystem : EntitySystem
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly MetaDataSystem _metaData = default!;
[Dependency] private readonly RandomHelperSystem _randomHelper = default!;
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
public override void Initialize()
{
base.Initialize();
@@ -100,7 +98,6 @@ public sealed partial class BotanySystem : EntitySystem
public EntityUid SpawnSeedPacket(SeedData proto, EntityCoordinates coords, EntityUid user, float? healthOverride = null)
{
var seed = SpawnAtPosition(proto.PacketPrototype, coords); // Frontier: Spawn<SpawnAtPosition
_contraband.ClearContrabandValue(seed); // Frontier
var seedComp = EnsureComp<SeedComponent>(seed);
seedComp.Seed = proto;
seedComp.HealthOverride = healthOverride;
@@ -160,7 +157,6 @@ public sealed partial class BotanySystem : EntitySystem
var product = _robustRandom.Pick(proto.ProductPrototypes);
var entity = SpawnAtPosition(product, position); // Frontier: Spawn<SpawnAtPosition
_contraband.ClearContrabandValue(entity); // Frontier
_randomHelper.RandomOffset(entity, 0.25f);
products.Add(entity);
+116
View File
@@ -6,8 +6,10 @@ using System.Runtime.CompilerServices;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Content.Server._Mono.Company;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Shared._Mono.Company;
using Content.Shared.Administration.Logs;
using Content.Shared.Database;
using Content.Shared.Ghost.Roles;
@@ -1940,6 +1942,120 @@ INSERT INTO player_round (players_id, rounds_id) VALUES ({players[player]}, {id}
#endregion
// Mono
#region Company
public async Task<bool> AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
{
await using var db = await GetDb();
var exists = await db.DbContext.CompanyMembers
.Where(w => w.PlayerUserId == player)
.Where(w => w.CompanyId == company.Id)
.AnyAsync();
if (exists)
return false;
var member = new CompanyMember
{
PlayerUserId = player,
CompanyId = company,
};
db.DbContext.CompanyMembers.Add(member);
await db.DbContext.SaveChangesAsync();
return true;
}
public async Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel)
{
await using var db = await GetDb(cancel);
return await db.DbContext.CompanyMembers
.Where(w => w.PlayerUserId == player)
.Select(w => w.CompanyId)
.ToListAsync(cancel);
}
public async Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel)
{
await using var db = await GetDb(cancel);
var members = await db.DbContext.CompanyMembers
.Where(w => w.CompanyId == company.Id)
.Include(c => c.Player)
.ToListAsync(cancel);
return members.Select(m => new CompanyMemberRecord()
{
Company = company,
Owner = m.Owner,
PlayerUserId = m.PlayerUserId,
LastSeenUserName = m.Player.LastSeenUserName,
});
}
public async Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel)
{
await using var db = await GetDb(cancel);
var members = await db.DbContext.CompanyMembers
.Include(c => c.Player)
.ToListAsync(cancel);
return members.Select(m => new CompanyMemberRecord()
{
Company = m.CompanyId,
Owner = m.Owner,
PlayerUserId = m.PlayerUserId,
LastSeenUserName = m.Player.LastSeenUserName,
});
}
public async Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel)
{
await using var db = await GetDb(cancel);
var member = await db.DbContext.CompanyMembers
.Where(w => w.CompanyId == company.Id)
.Where(w => w.PlayerUserId == player)
.Include(c => c.Player)
.FirstOrDefaultAsync();
if (member == null)
return null;
return new CompanyMemberRecord()
{
Company = company,
LastSeenUserName = member.Player.LastSeenUserName,
Owner = member.Owner,
PlayerUserId = member.PlayerUserId,
};
}
public async Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner)
{
await using var db = await GetDb();
await db.DbContext.CompanyMembers
.Where(w => w.CompanyId == company.Id)
.Where(w => w.PlayerUserId == player)
.ExecuteUpdateAsync(setters => setters.SetProperty(m => m.Owner, owner));
}
public async Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
{
await using var db = await GetDb();
var entry = await db.DbContext.CompanyMembers
.Where(w => w.PlayerUserId == player)
.Where(w => w.CompanyId == company.Id)
.SingleOrDefaultAsync();
if (entry == null)
return false;
db.DbContext.CompanyMembers.Remove(entry);
await db.DbContext.SaveChangesAsync();
return true;
}
#endregion
public abstract Task SendNotification(DatabaseNotification notification);
// SQLite returns DateTime as Kind=Unspecified, Npgsql actually knows for sure it's Kind=Utc.
+60 -1
View File
@@ -22,6 +22,8 @@ using Robust.Shared.Network;
using Robust.Shared.Prototypes;
using LogLevel = Robust.Shared.Log.LogLevel;
using MSLogLevel = Microsoft.Extensions.Logging.LogLevel;
using Content.Shared._Mono.Company;
using Content.Server._Mono.Company; // Mono
namespace Content.Server.Database
{
@@ -351,6 +353,19 @@ namespace Content.Server.Database
#endregion
// Mono
#region Company
Task AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company);
Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel = default);
Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel = default);
Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel = default);
Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel = default);
Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner);
Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company);
#endregion
#region DB Notifications
void SubscribeToNotifications(Action<DatabaseNotification> handler);
@@ -1058,7 +1073,7 @@ namespace Content.Server.Database
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.RemoveJobWhitelist(player, job));
}
// Frontier: ghost role DB ops
public Task AddGhostRoleWhitelist(Guid player, ProtoId<GhostRolePrototype> ghostRole)
{
@@ -1095,6 +1110,50 @@ namespace Content.Server.Database
return RunDbCommand(() => _db.CleanIPIntelCache(range));
}
// Mono-Start
public Task AddCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
{
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.AddCompanyMember(player, company));
}
public Task<List<string>> GetPlayerCompanies(Guid player, CancellationToken cancel = default)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetPlayerCompanies(player, cancel));
}
public Task<IEnumerable<CompanyMemberRecord>> GetCompanyMembers(ProtoId<CompanyPrototype> company, CancellationToken cancel = default)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetCompanyMembers(company, cancel));
}
public Task<IEnumerable<CompanyMemberRecord>> GetAllCompanyMembers(CancellationToken cancel = default)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetAllCompanyMembers(cancel));
}
public Task<CompanyMemberRecord?> GetCompanyMember(ProtoId<CompanyPrototype> company, Guid player, CancellationToken cancel = default)
{
DbReadOpsMetric.Inc();
return RunDbCommand(() => _db.GetCompanyMember(company, player, cancel));
}
public Task SetCompanyOwner(ProtoId<CompanyPrototype> company, Guid player, bool owner)
{
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.SetCompanyOwner(company, player, owner));
}
public Task<bool> RemoveCompanyMember(Guid player, ProtoId<CompanyPrototype> company)
{
DbWriteOpsMetric.Inc();
return RunDbCommand(() => _db.RemoveCompanyMember(player, company));
}
// Mono-End
public void SubscribeToNotifications(Action<DatabaseNotification> handler)
{
lock (_notificationHandlers)
@@ -171,7 +171,7 @@ namespace Content.Server.EntityEffects.Effects
interruptsDoAfters: false,
// Shitmed Change Start
targetPart: TargetBodyPart.All,
partMultiplier: 0.5f,
partMultiplier: 1.00f, // Mono, 0.5f->1.00f
canSever: false);
// Shitmed Change End
}
+2
View File
@@ -1,3 +1,4 @@
using Content.Server._Mono.Company; // Mono
using Content.Server._NF.Auth;
using Content.Server.Acz;
using Content.Server.Administration;
@@ -155,6 +156,7 @@ namespace Content.Server.Entry
_euiManager.Initialize();
IoCManager.Resolve<CompanyManager>().Initialize(); // Mono
IoCManager.Resolve<IGameMapManager>().Initialize();
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<GameTicker>().PostInitialize();
IoCManager.Resolve<IBanManager>().Initialize();
@@ -1,9 +1,15 @@
namespace Content.Server.Explosion.Components;
/// <summary>
/// calls the trigger when the object is initialized
/// </summary>
[RegisterComponent]
public sealed partial class TriggerOnSpawnComponent : Component
namespace Content.Server.Explosion.Components
{
/// <summary>
/// calls the trigger when the object is initialized
/// </summary>
[RegisterComponent]
public sealed partial class TriggerOnSpawnComponent : Component
{
/// <summary>
/// Starts a timer on spawn rather than instantly triggering
/// </summary>
[DataField("timerOnly")] // Mono: Added "timerOnly" boolean
public bool timerOnly = false;
}
}
@@ -1,3 +1,4 @@
using Content.Server._Mono.Planets;
using Content.Server.Administration.Logs;
using Content.Server.Body.Systems;
using Content.Server.Explosion.Components;
@@ -39,6 +40,7 @@ using Content.Shared.Projectiles; // Frontier: embed triggers
using Content.Shared.Mind;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Robust.Shared.Map.Components;
namespace Content.Server.Explosion.EntitySystems
{
@@ -83,6 +85,7 @@ namespace Content.Server.Explosion.EntitySystems
[Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly ElectrocutionSystem _electrocution = default!;
[Dependency] private readonly StationSystem _station = default!; // Frontier: medical insurance
[Dependency] private readonly SharedMapSystem _map = default!; // Frontier: medical insurance
public override void Initialize()
{
@@ -252,19 +255,19 @@ namespace Content.Server.Explosion.EntitySystems
var posText = $"({x}, {y})";
// Frontier: Gets station location of the implant
var station = _station.GetOwningStation(uid);
var stationText = station is null ? null : $"{Name(station.Value)} ";
var grid = ownerXform.GridUid;
var gridText = grid is null ? "" : MetaData(grid.Value).EntityName;
if (stationText == null)
stationText = "";
if (HasComp<MapComponent>(grid) && !HasComp<PlanetMapComponent>(grid))
gridText = "";
// Frontier: Gets species of the implant user
var speciesText = $"";
if (TryComp<HumanoidAppearanceComponent>(implanted.ImplantedEntity, out var species))
speciesText = $" ({species!.Species})";
speciesText = $" ({species.Species})";
var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", stationText!), ("position", posText));
var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", stationText!), ("position", posText));
var critMessage = Loc.GetString(component.CritMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", gridText), ("position", posText));
var deathMessage = Loc.GetString(component.DeathMessage, ("user", implanted.ImplantedEntity.Value), ("specie", speciesText), ("grid", gridText), ("position", posText));
if (!TryComp<MobStateComponent>(implanted.ImplantedEntity, out var mobstate))
return;
@@ -300,7 +303,10 @@ namespace Content.Server.Explosion.EntitySystems
private void OnSpawnTriggered(EntityUid uid, TriggerOnSpawnComponent component, MapInitEvent args)
{
Trigger(uid);
if (component.timerOnly == true)
StartTimer(uid,uid);
else
Trigger(uid);
}
private void OnActivate(EntityUid uid, TriggerOnActivateComponent component, ActivateInWorldEvent args)
@@ -130,6 +130,10 @@ public sealed partial class GameTicker
if (MetaData(ruleEntity).EntityPrototype?.ID is not { } id) // you really fucked up
return false;
// Mono - ensure delayed start rules don't trigger ahead of time.
if (TryComp<DelayedStartRuleComponent>(ruleEntity, out var delayComp) && delayComp.RuleStartTime > _gameTiming.CurTime)
return false;
// If we already have it, then we just skip the delay as it has already happened.
if (!RemComp<DelayedStartRuleComponent>(ruleEntity) && ruleData.Delay != null)
{
@@ -42,5 +42,11 @@ namespace Content.Server.GameTicking.Presets
/// </summary>
[DataField("supportedMaps", customTypeSerializer: typeof(PrototypeIdSerializer<GameMapPoolPrototype>))]
public string? MapPool;
/// <summary>
/// Mono - as how much votes should each player click count.
/// </summary>
[DataField]
public float Weight = 1f;
}
}
+2
View File
@@ -1,3 +1,4 @@
using Content.Server._Mono.Company; // Mono
using Content.Server._NF.Auth;
using Content.Server.Administration;
using Content.Server.Administration.Logs;
@@ -78,6 +79,7 @@ namespace Content.Server.IoC
IoCManager.Register<MultiServerKickManager>();
IoCManager.Register<CVarControlManager>();
IoCManager.Register<MiniAuthManager>(); //Frontier
IoCManager.Register<CompanyManager>(); // Mono
IoCManager.Register<DiscordLink>();
IoCManager.Register<DiscordChatLink>();
-5
View File
@@ -59,7 +59,6 @@ namespace Content.Server.Lathe
[Dependency] private readonly SharedSolutionContainerSystem _solution = default!;
[Dependency] private readonly StackSystem _stack = default!;
[Dependency] private readonly TransformSystem _transform = default!;
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
[Dependency] private readonly DeviceLinkSystem _deviceLink = default!; // Mono
/// <summary>
@@ -326,11 +325,7 @@ namespace Content.Server.Lathe
// Frontier: adjust price before merge (stack prices changed once)
if (result.Valid)
{
ModifyPrintedEntityPrice(uid, comp, result);
_contraband.ClearContrabandValue(result);
}
// End Frontier
_stack.TryMergeToContacts(result);
+7 -1
View File
@@ -121,8 +121,14 @@ public sealed partial class MechSystem : SharedMechSystem
if (!TryComp<BatteryComponent>(ent.Comp.BatterySlot.ContainedEntity, out var battery))
return;
ent.Comp.CriticalPowerState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
//ent.Comp.CriticalPowerState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
// Mono
var criticalState = battery.CurrentCharge / battery.MaxCharge <= 0.05f;
if (criticalState == ent.Comp.CriticalPowerState)
return;
ent.Comp.CriticalPowerState = criticalState;
// End Mono
Dirty(ent);
_movementSpeed.RefreshMovementSpeedModifiers(ent);
}
@@ -35,12 +35,13 @@ public sealed class JetpackSystem : SharedJetpackSystem
/// </summary>
private void OnJetpackActivated(EntityUid uid, ActiveJetpackComponent component, ComponentStartup args)
{
if (TryComp<JetpackComponent>(uid, out var jetpack) && jetpack.Stealth == false)
if (TryComp<JetpackComponent>(uid, out var jetpack) && jetpack.DetectionRange > 0)
{
var blip = EnsureComp<RadarBlipComponent>(uid);
blip.Config.Color = Color.Cyan;
blip.Config.Bounds = new(-0.25f, -0.25f, 0.25f, 0.25f);
blip.VisibleFromOtherGrids = true;
blip.MaxDistance = jetpack.DetectionRange;
}
}
@@ -0,0 +1,10 @@
// Mono - whole file
namespace Content.Server.NPC.Queries.Considerations;
/// <summary>
/// Returns 1f if the target is alive or can't be alive, or 0f if not.
/// </summary>
public sealed partial class TargetIsAliveOrNACon : UtilityConsideration
{
}
@@ -6,12 +6,12 @@ using Content.Shared.Whitelist;
namespace Content.Server.NPC.Queries.Queries;
/// <summary>
/// Returns nearby shuttles considered hostile from <see cref="FactionSystem"/>
/// Returns nearby entities tagged with ShipNpcTargetComponent.
/// </summary>
public sealed partial class NearbyHostileShuttlesQuery : UtilityQuery
public sealed partial class NearbyNpcTargetsQuery : UtilityQuery
{
[DataField]
public float Range = 2000f;
public float Range = 4000f;
// TODO: make this use factions
[DataField]
@@ -1,6 +1,7 @@
using Content.Server.NPC.Queries.Considerations;
using Content.Server.NPC.Queries.Queries;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Array; // Mono
namespace Content.Server.NPC.Queries;
@@ -10,12 +11,22 @@ namespace Content.Server.NPC.Queries;
/// These results are then run through the considerations.
/// </summary>
[Prototype]
public sealed partial class UtilityQueryPrototype : IPrototype
public sealed partial class UtilityQueryPrototype : IPrototype, IInheritingPrototype // Mono
{
[IdDataField]
public string ID { get; private set; } = default!;
[ViewVariables(VVAccess.ReadWrite), DataField("query")]
// <Mono>
[ParentDataField(typeof(AbstractPrototypeIdArraySerializer<UtilityQueryPrototype>))]
public string[]? Parents { get; private set; }
/// <inheritdoc />
[NeverPushInheritance]
[AbstractDataField]
public bool Abstract { get; private set; }
// </Mono>
[ViewVariables(VVAccess.ReadWrite), DataField("query"), AlwaysPushInheritance]
public List<UtilityQuery> Query = new();
[ViewVariables(VVAccess.ReadWrite), DataField("considerations")]
+13 -6
View File
@@ -390,6 +390,11 @@ public sealed class NPCUtilitySystem : EntitySystem
return _statusEffectsSystem.HasStatusEffect(targetUid, "Stun") ? 1f : 0f;
}
// End Frontier
// Mono
case TargetIsAliveOrNACon:
{
return !TryComp<MobStateComponent>(targetUid, out var mobState) || _mobState.IsAlive(targetUid, mobState) ? 1f : 0f;
}
default:
throw new NotImplementedException();
}
@@ -494,7 +499,7 @@ public sealed class NPCUtilitySystem : EntitySystem
break;
}
// Mono - TODO: consider factions
case NearbyHostileShuttlesQuery shuttlesQuery:
case NearbyNpcTargetsQuery shuttlesQuery:
{
var xform = Transform(owner);
var ownGrid = xform.GridUid;
@@ -502,11 +507,13 @@ public sealed class NPCUtilitySystem : EntitySystem
{
var targetXform = Transform(target);
var targetGrid = targetXform.GridUid;
if (targetComp.NeedGrid && targetGrid == null ||
targetGrid == ownGrid ||
(_transform.GetWorldPosition(target) - _transform.GetWorldPosition(xform)).Length() > shuttlesQuery.Range ||
targetComp.NeedPower && !this.IsPowered(target, EntityManager) ||
targetGrid != null && _whitelistSystem.IsBlacklistPass(shuttlesQuery.Blacklist, targetGrid.Value))
if (targetComp.NeedGrid != NpcTargetGridMode.Either // if we care about grid..
// ..and our (non-)need for grid is equal to the (non-)absence of a grid
&& (targetComp.NeedGrid == NpcTargetGridMode.OnGrid) == (targetGrid == null)
|| targetGrid == ownGrid
|| (_transform.GetWorldPosition(target) - _transform.GetWorldPosition(xform)).Length() > shuttlesQuery.Range
|| targetComp.NeedPower && !this.IsPowered(target, EntityManager)
|| targetGrid != null && _whitelistSystem.IsBlacklistPass(shuttlesQuery.Blacklist, targetGrid.Value))
{
continue;
}
@@ -75,6 +75,7 @@ public sealed class MoverController : SharedMoverController
return;
args.Input = input;
args.SetMaxVelocity = entity.Comp.SetMaxVelocity;
}
private void PilotedShuttleRelayEvent<TEvent>(Entity<PilotedShuttleComponent> entity, ref TEvent args)
@@ -273,19 +274,27 @@ public sealed class MoverController : SharedMoverController
// Mono: all below code handling shuttle movement has been heavily modified by Monolith
//
/// <summary>
/// Get a shuttle's torque.
/// </summary>
public float GetTorque(ShuttleComponent shuttle)
{
return shuttle.AngularThrust * shuttle.AngularMultiplier;
}
/// <summary>
/// Get a shuttle's angular acceleration.
/// </summary>
public float GetAngularAcceleration(ShuttleComponent shuttle, PhysicsComponent body)
{
return shuttle.AngularThrust * body.InvI;
return GetTorque(shuttle) * body.InvI;
}
/// <summary>
/// Get shuttle thrust force in a given direction.
/// Takes local direction.
/// </summary>
public Vector2 GetDirectionThrust(Vector2 dir, ShuttleComponent shuttle)
public Vector2 GetDirectionThrust(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body, TransformComponent xform)
{
if (dir.Length() == 0f)
return Vector2.Zero;
@@ -302,28 +311,45 @@ public sealed class MoverController : SharedMoverController
// prevent NaNs
dir *= dir.X == 0 ? vertScale : dir.Y == 0 ? horizScale : MathF.Min(horizScale, vertScale);
return dir;
}
var northAngle = xform.LocalRotation;
var localVel = (-northAngle).RotateVec(body.LinearVelocity);
public Vector2 GetDirectionAccel(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body)
{
return GetDirectionThrust(dir, shuttle) * body.InvMass;
// scale our velocity-wards component by 1 / (vel/basemax + 1)
var dot = Vector2.Dot(dir, localVel);
if (dot > 0f)
{
var velLenSq = localVel.LengthSquared();
var dirCompVel = localVel * dot / velLenSq;
var scaledComp = dirCompVel / MathF.Pow(2f, localVel.Length() / shuttle.BaseMaxLinearVelocity + 1f);
dir = dir - dirCompVel + scaledComp;
}
return dir * shuttle.AccelerationMultiplier;
}
/// <summary>
/// Helper function to extrapolate max velocity for a given Vector2 (really, its angle) and shuttle.
/// Get shuttle acceleration in a given direction.
/// Takes local direction.
/// </summary>
public Vector2 ObtainMaxVel(Vector2 vel, ShuttleComponent shuttle, PhysicsComponent body) // mono
public Vector2 GetDirectionAccel(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body, TransformComponent xform)
{
if (vel.Length() == 0f)
return Vector2.Zero;
return GetDirectionThrust(dir, shuttle, body, xform) * body.InvMass;
}
var thrust = GetDirectionThrust(vel, shuttle);
var twr = thrust.Length() / body.Mass;
var twrMult = MathF.Pow(twr / shuttle.BaseMaxVelocityTWR, shuttle.MaxVelocityScalingExponent);
/// <summary>
/// Get shuttle thrust force in a given world direction.
/// </summary>
public Vector2 GetWorldDirectionThrust(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body, TransformComponent xform)
{
return xform.LocalRotation.RotateVec(GetDirectionThrust((-xform.LocalRotation).RotateVec(dir), shuttle, body, xform));
}
return vel.Normalized() * MathF.Min(shuttle.BaseMaxLinearVelocity * twrMult, MathF.Min(shuttle.UpperMaxVelocity, shuttle.SetMaxVelocity));
/// <summary>
/// Get shuttle acceleration in a given world direction.
/// </summary>
public Vector2 GetWorldDirectionAccel(Vector2 dir, ShuttleComponent shuttle, PhysicsComponent body, TransformComponent xform)
{
return GetWorldDirectionThrust(dir, shuttle, body, xform) * body.InvMass;
}
private void HandleShuttleMovement(float frameTime)
@@ -335,6 +361,9 @@ public sealed class MoverController : SharedMoverController
// query all our pilots for input
var toRemove = new List<EntityUid>();
var angularMul = 0f;
var accelMul = 0f;
var setMaxVel = (float?)0f;
foreach (var pilot in piloted.InputSources)
{
var inputsEv = new GetShuttleInputsEvent(frameTime, uid);
@@ -343,7 +372,15 @@ public sealed class MoverController : SharedMoverController
if (!inputsEv.GotInput)
toRemove.Add(pilot);
else if (inputsEv.Input != null)
{
inputs.Add(inputsEv.Input.Value);
angularMul += inputsEv.AngularMul;
accelMul += inputsEv.AccelMul;
if (setMaxVel != null && inputsEv.SetMaxVelocity != null)
setMaxVel += inputsEv.SetMaxVelocity;
else
setMaxVel = null;
}
}
foreach (var remUid in toRemove)
@@ -359,6 +396,7 @@ public sealed class MoverController : SharedMoverController
{
_thruster.DisableLinearThrusters(shuttle);
PhysicsSystem.SetSleepingAllowed(uid, body, true);
shuttle.AngularMultiplier = shuttle.AccelerationMultiplier = 1f;
continue;
}
PhysicsSystem.SetSleepingAllowed(uid, body, false);
@@ -377,8 +415,17 @@ public sealed class MoverController : SharedMoverController
angularInput /= count;
brakeInput /= count;
angularMul /= count;
accelMul /= count;
if (setMaxVel != null)
setMaxVel /= count;
shuttle.AngularMultiplier = angularMul;
shuttle.AccelerationMultiplier = accelMul;
var shuttleNorthAngle = _xformSystem.GetWorldRotation(uid);
var xform = Transform(uid);
// handle movement: brake
if (brakeInput > 0f)
{
@@ -389,7 +436,7 @@ public sealed class MoverController : SharedMoverController
// Get velocity relative to the shuttle so we know which thrusters to fire
var shuttleVelocity = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
var force = GetDirectionThrust(-shuttleVelocity, shuttle);
var force = GetDirectionThrust(-shuttleVelocity, shuttle, body, xform);
if (force.X < 0f)
{
@@ -473,7 +520,7 @@ public sealed class MoverController : SharedMoverController
var linearDir = angle.GetDir();
var dockFlag = linearDir.AsFlag();
var totalForce = GetDirectionThrust(linearInput, shuttle);
var totalForce = GetDirectionThrust(linearInput, shuttle, body, xform);
// Won't just do cardinal directions.
foreach (DirectionFlag dir in Enum.GetValues(typeof(DirectionFlag)))
@@ -497,17 +544,20 @@ public sealed class MoverController : SharedMoverController
}
var localVel = (-shuttleNorthAngle).RotateVec(body.LinearVelocity);
// vector of max velocity we can be traveling with along current direction
var maxVelocity = ObtainMaxVel(localVel, shuttle, body);
// vector of max velocity we can be traveling with along wish-direction
var maxWishVelocity = ObtainMaxVel(totalForce, shuttle, body);
// if we're going faster than we can be, thrust to adjust our velocity to the max wish-direction velocity
if (localVel.LengthSquared() > maxVelocity.LengthSquared())
if (setMaxVel is { } speed && localVel.LengthSquared() != 0f && totalForce.LengthSquared() != 0f)
{
var velDelta = maxWishVelocity - localVel;
var maxForceLength = velDelta.Length() * body.Mass / frameTime;
var appliedLength = MathF.Min(totalForce.Length(), maxForceLength);
totalForce = velDelta.Length() == 0 ? Vector2.Zero : velDelta.Normalized() * appliedLength;
// vector of max velocity we can be traveling with along current direction
var maxVelocity = localVel.Normalized() * speed;
// vector of max velocity we can be traveling with along wish-direction
var maxWishVelocity = totalForce.Normalized() * speed;
// if we're going faster than we can be, thrust to adjust our velocity to the max wish-direction velocity
if (localVel.Length() / maxVelocity.Length() > 0.999f)
{
var velDelta = maxWishVelocity - localVel;
var maxForceLength = velDelta.Length() * body.Mass / frameTime;
var appliedLength = MathF.Min(totalForce.Length(), maxForceLength);
totalForce = velDelta.Length() == 0 ? Vector2.Zero : velDelta.Normalized() * appliedLength;
}
}
totalForce = shuttleNorthAngle.RotateVec(totalForce);
@@ -524,7 +574,7 @@ public sealed class MoverController : SharedMoverController
}
else
{
var torque = shuttle.AngularThrust * -angularInput;
var torque = GetTorque(shuttle) * -angularInput;
// Need to cap the velocity if 1 tick of input brings us over cap so we don't continuously
// edge onto the cap over and over.
@@ -9,9 +9,15 @@ public record struct ShuttleInput(Vector2 Strafe, float Rotation, float Brakes);
/// <summary>
/// Raised on pilots to get inputs given to a shuttle.
/// If GotInput is false, this piloted is removed from input sources.
/// Also queries for multipliers to acceleration and max speed.
/// </summary>
[ByRefEvent]
public record struct GetShuttleInputsEvent(float FrameTime, EntityUid ShuttleUid, ShuttleInput? Input = null, bool GotInput = false);
public record struct GetShuttleInputsEvent(float FrameTime, EntityUid ShuttleUid, ShuttleInput? Input = null, bool GotInput = false)
{
public float AngularMul = 1f;
public float AccelMul = 1f;
public float? SetMaxVelocity = null;
}
[ByRefEvent]
public record struct PilotedShuttleRelayedEvent<TEvent>(TEvent Args);
@@ -21,6 +21,7 @@ namespace Content.Server.Preferences.Managers
IEnumerable<KeyValuePair<NetUserId, ICharacterProfile>> GetSelectedProfilesForPlayers(List<NetUserId> userIds);
bool HavePreferencesLoaded(ICommonSession session);
Task RefreshPreferencesAsync(ICommonSession session, CancellationToken cancel);
Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile);
Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile,
bool authoritative = true); // Mono
}
}
@@ -87,10 +87,11 @@ namespace Content.Server.Preferences.Managers
if (message.Profile == null)
_sawmill.Error($"User {userId} sent a {nameof(MsgUpdateCharacter)} with a null profile in slot {message.Slot}.");
else
await SetProfile(userId, message.Slot, message.Profile);
await SetProfile(userId, message.Slot, message.Profile, false);
}
public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile)
public async Task SetProfile(NetUserId userId, int slot, ICharacterProfile profile,
bool authoritative = true) // Mono
{
if (!_cachedPlayerPrefs.TryGetValue(userId, out var prefsData) || !prefsData.PrefsLoaded)
{
@@ -105,6 +106,14 @@ namespace Content.Server.Preferences.Managers
var session = _playerManager.GetSessionById(userId);
profile.EnsureValid(session, _dependencies);
// Mono
if (!authoritative && profile is HumanoidCharacterProfile humanoid)
{
if (curPrefs.Characters.TryGetValue(slot, out var oldProfile) && oldProfile is HumanoidCharacterProfile oldHumanoid)
profile = humanoid.WithBankBalance(oldHumanoid.BankBalance);
else
profile = humanoid.WithBankBalance(HumanoidCharacterProfile.DefaultBalance);
}
var profiles = new Dictionary<int, ICharacterProfile>(curPrefs.Characters)
@@ -69,6 +69,8 @@ public sealed partial class ResearchSystem
if (serverComponent.Clients.Contains(client))
return;
UnregisterClient(client, clientComponent); // Mono Research Fix, unregister a client if applicable before registering to a new server
serverComponent.Clients.Add(client);
clientComponent.Server = server;
SyncClientWithServer(client, clientComponent: clientComponent);
+12 -3
View File
@@ -22,6 +22,7 @@ using Robust.Shared.Timing;
using Content.Shared.DeviceNetwork.Components;
using Timer = Robust.Shared.Timing.Timer;
using Content.Server._NF.SectorServices; // Frontier
using Content.Shared.GameTicking.Components; // Mono
namespace Content.Server.RoundEnd
{
@@ -354,9 +355,17 @@ namespace Content.Server.RoundEnd
public override void Update(float frameTime)
{
// Check if we should auto-call.
int mins = _autoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
: _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallTime);
if (mins != 0 && _gameTiming.CurTime - AutoCallStartTime > TimeSpan.FromMinutes(mins))
var mins = TimeSpan.FromMinutes(_autoCalledBefore ? _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallExtensionTime)
: _cfg.GetCVar(CCVars.EmergencyShuttleAutoCallTime));
// Mono
var query = EntityQueryEnumerator<RoundEndTimeRuleComponent, ActiveGameRuleComponent>();
while (query.MoveNext(out _, out var endTimeComp, out _))
{
mins = endTimeComp.EndAt;
}
if (mins.TotalSeconds != 0 && _gameTiming.CurTime - AutoCallStartTime > mins)
{
if (!_shuttle.EmergencyShuttleArrived && ExpectedCountdownEnd is null)
{
@@ -0,0 +1,13 @@
using System;
namespace Content.Server.RoundEnd;
/// <summary>
/// If a gamerule with this component is present, override the roundend time to the time set in it.
/// </summary>
[RegisterComponent]
public sealed partial class RoundEndTimeRuleComponent : Component
{
[DataField]
public TimeSpan EndAt;
}
@@ -17,10 +17,10 @@ namespace Content.Server.Shuttles.Components
public const float BrakeCoefficient = 1.5f;
/// <summary>
/// Maximum velocity assuming TWR is BaseMaxVelocityTWR.
/// Mono - velocity that 2x slowdown will be applied at. (4x at 2x this, 8x at 3x this, etc.)
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float BaseMaxLinearVelocity = 75f; // Mono
public float BaseMaxLinearVelocity = 80f;
public const float MaxAngularVelocity = 4f;
@@ -76,35 +76,23 @@ namespace Content.Server.Shuttles.Components
public float DampingModifier;
// <Mono>
/// <summary>
/// Limit to max velocity set by a shuttle console.
/// </summary>
[DataField]
public float SetMaxVelocity = 140f;
/// <summary>
/// At what Thrust-Weight-Ratio should this ship have the base max velocity as its maximum velocity.
/// </summary>
[DataField]
public float BaseMaxVelocityTWR = 8f;
/// <summary>
/// How much should TWR affect max velocity.
/// </summary>
[DataField]
public float MaxVelocityScalingExponent = 0.25f; // 16x thrust = 2x max speed
/// <summary>
/// Don't allow max velocity to go beyond this value.
/// </summary>
[DataField]
public float UpperMaxVelocity = 140f; // we ball
/// <summary>
/// Thrust acceleration vector last update.
/// </summar>
[DataField]
public Vector2 LastThrust = Vector2.Zero;
/// <summary>
/// Multiplier to angular thrust. Set depending on pilot.
/// </summary
[ViewVariables]
public float AngularMultiplier = 1f;
/// <summary>
/// Multiplier to linear thrust. Set depending on pilot.
/// </summary
[ViewVariables]
public float AccelerationMultiplier = 1f;
// </Mono>
}
}
@@ -71,7 +71,7 @@ namespace Content.Server.Shuttles.Components
public string MachinePartThrust = "Capacitor";
[DataField("partRatingThrustMultiplier")]
public float PartRatingThrustMultiplier = 1.15f; // Frontier - PR #1292 1.5f<1.15f
public float PartRatingThrustMultiplier = 1.25f; // Mono: 1.15->1.25 (frontier: 1.5->1.15)
/// <summary>
/// Frontier - Amount of charge this needs from an APC per second to function.
@@ -202,7 +202,6 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
{
DockingInterfaceState? dockState = null;
UpdateState(uid, ref dockState);
_shuttle.NfSetPowered(uid, component, args.Powered); // Frontier
// Handle job slots when power changes
HandleJobSlotsOnPowerChange(uid, component, args.Powered);
@@ -225,16 +225,19 @@ public sealed partial class ShuttleSystem
var inelasticVel = totalInertia / (ourMass + otherMass);
// Mono Edit - partial credit to https://github.com/Sector-Crescent/Hullrot/pull/692
//ShipShieldedComp is removed when shields are broken, only reduces energy delivered when shields are active. ShipShieldsSystem ln 256.
//ShipShieldedComp is removed when shields are broken, reduces both energies when shields are active. ShipShieldsSystem ln 256.
float shieldFactor = 1f;
if (TryComp<ShipShieldedComponent>(args.OurEntity, out var ShipShieldedComponent) //Our ship collision resistance
&& TryComp<ShipShieldEmitterComponent>(ShipShieldedComponent.Source, out var ShipShieldEmitterComponent)
)
toUsEnergy *= ShipShieldEmitterComponent.CollisionResistanceMultiplier;
shieldFactor *= ShipShieldEmitterComponent.CollisionResistanceMultiplier;
if (TryComp<ShipShieldedComponent>(args.OtherEntity, out var OtherShipShieldedComponent) //Other ship collision resistance
&& TryComp<ShipShieldEmitterComponent>(OtherShipShieldedComponent.Source, out var OtherShipShieldEmitterComponent)
)
toOtherEnergy *= OtherShipShieldEmitterComponent.CollisionResistanceMultiplier;
shieldFactor *= OtherShipShieldEmitterComponent.CollisionResistanceMultiplier;
toUsEnergy *= shieldFactor;
toOtherEnergy *= shieldFactor;
// Mono Edit end
DoGridImpact((args.OurEntity, ourGrid, ourXform, ourBody), args.OurFixture, inelasticVel, ourVelocity, ourTile, ourTiles, toUsEnergy);
@@ -1,5 +1,6 @@
using Content.Server.Inventory;
using Content.Server.Radio.Components;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
using Content.Shared.Inventory;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
@@ -28,6 +29,23 @@ public sealed class BorgSwitchableTypeSystem : SharedBorgSwitchableTypeSystem
if (TryComp(ent, out ActiveRadioComponent? activeRadio))
activeRadio.Channels = [.. radioChannels];
// Corvax-Next-AiRemoteControl-Start
if (TryComp(ent, out AiRemoteControllerComponent? aiRemoteComp))
{
if (TryComp(aiRemoteComp.AiHolder, out IntrinsicRadioTransmitterComponent? stationAiTransmitter) && transmitter != null)
{
aiRemoteComp.PreviouslyTransmitterChannels = [.. radioChannels];
transmitter.Channels = [.. stationAiTransmitter.Channels];
}
if (TryComp(aiRemoteComp.AiHolder, out ActiveRadioComponent? stationAiActiveRadio) && activeRadio != null)
{
aiRemoteComp.PreviouslyActiveRadioChannels = [.. radioChannels];
activeRadio.Channels = [.. stationAiActiveRadio.Channels];
}
}
// Corvax-Next-AiRemoteControl-End
// Borg transponder for the robotics console
if (TryComp(ent, out BorgTransponderComponent? transponder))
{
@@ -6,7 +6,9 @@ using Content.Shared.Silicons.Borgs.Components;
using Content.Shared.DeviceNetwork.Components;
using Content.Shared.DeviceNetwork.Events;
using Content.Shared.Emag.Systems;
using Robust.Shared.Player;
using Robust.Shared.Utility;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components; // Corvax-Next-AiRemoteControl
namespace Content.Server.Silicons.Borgs;
@@ -47,7 +49,8 @@ public sealed partial class BorgSystem
charge,
chassis.ModuleCount,
hasBrain,
canDisable);
canDisable,
HasComp<AiRemoteControllerComponent>(uid)); // Corvax-Next-AiRemoteControl
var payload = new NetworkPayload()
{
@@ -5,6 +5,7 @@ using Content.Server.DeviceNetwork.Systems;
using Content.Server.Explosion.EntitySystems;
using Content.Server.Hands.Systems;
using Content.Server.PowerCell;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components;
using Content.Shared.UserInterface;
using Content.Shared.Access;
using Content.Shared.Access.Components;
@@ -25,6 +26,8 @@ using Content.Shared.PowerCell.Components;
using Content.Shared.Roles;
using Content.Shared.Silicons.Borgs;
using Content.Shared.Silicons.Borgs.Components;
using Content.Shared.Silicons.StationAi;
using Content.Shared.StationAi;
using Content.Shared.Throwing;
using Content.Shared.Whitelist;
using Content.Shared.Wires;
@@ -144,6 +147,21 @@ public sealed partial class BorgSystem : SharedBorgSystem
args.Handled = true;
UpdateUI(uid, component);
}
// Corvax-Next-AiRemoteControl-Start
if (component.BrainEntity == null && HasComp<AiRemoteBrainComponent>(used) &&
_whitelistSystem.IsWhitelistPassOrNull(component.BrainWhitelist, used))
{
EnsureComp<AiRemoteControllerComponent>(uid);
_container.Insert(used, component.BrainContainer);
_adminLog.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(args.User):player} installed ai remote brain {ToPrettyString(used)} into borg {ToPrettyString(uid)}");
args.Handled = true;
BorgActivate(uid, component);
UpdateUI(uid, component);
}
// Corvax-Next-AiRemoteControl-End
}
/// <summary>
@@ -178,6 +196,15 @@ public sealed partial class BorgSystem : SharedBorgSystem
{
_mind.TransferTo(mindId, args.Entity, mind: mind);
}
// Corvax-Next-AiRemoteControl-Start
if (HasComp<AiRemoteBrainComponent>(args.Entity))
{
BorgDeactivate(uid, component);
RemComp<AiRemoteControllerComponent>(uid);
RemComp<StationAiVisionComponent>(uid);
}
// Corvax-Next-AiRemoteControl-End
}
private void OnMindAdded(EntityUid uid, BorgChassisComponent component, MindAddedMessage args)
@@ -4,6 +4,7 @@ using Content.Server.Chat.Managers;
using Content.Server.Radio.Components;
using Content.Server.Roles;
using Content.Server.Station.Systems;
using Content.Shared._CorvaxNext.Silicons.Borgs.Components; // Corvax-Next-AiRemoteControl
using Content.Shared.Administration;
using Content.Shared.Chat;
using Content.Shared.Emag.Systems;
@@ -13,6 +14,9 @@ using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Content.Shared.Silicons.Laws;
using Content.Shared.Silicons.Laws.Components;
using Content.Shared.Silicons.StationAi; // Corvax-Next-AiRemoteControl
using Content.Shared.Tag; // Corvax-Next-AiRemoteControl
using Content.Shared.Wires;
using Robust.Server.GameObjects;
using Robust.Shared.Audio;
using Robust.Shared.Containers;
@@ -33,6 +37,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly TagSystem _tagSystem = default!; // Corvax-Next-AiRemoteControl
[Dependency] private readonly IMapManager _map = default!; // Mono - Law update is grid-only now.
/// <inheritdoc/>
@@ -63,6 +68,12 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
if (!TryComp<ActorComponent>(uid, out var actor))
return;
// Corvax-Next-AiRemoteControl-Start
if (HasComp<AiRemoteControllerComponent>(uid)
|| _tagSystem.HasTag(uid, "StationAi")) // skip a law's notification for remotable and AI
return;
// Corvax-Next-AiRemoteControl-End
var msg = Loc.GetString("laws-notify");
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", msg));
_chatManager.ChatMessageToOne(ChatChannel.Server, msg, wrappedMessage, default, false, actor.PlayerSession.Channel, colorOverride: Color.FromHex("#5ed7aa"));
@@ -155,6 +166,11 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
if (component.Lawset == null)
component.Lawset = GetLawset(component.Laws);
// Corvax-Next-AiRemoteControl-Start
if (HasComp<AiRemoteControllerComponent>(uid)) // You can't emag controllable entities
return;
// Corvax-Next-AiRemoteControl-End
// Show the silicon has been subverted.
component.Subverted = true;
@@ -281,7 +297,7 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
/// <summary>
/// Set the laws of a silicon entity while notifying the player.
/// </summary>
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null)
public void SetLaws(List<SiliconLaw> newLaws, EntityUid target, SoundSpecifier? cue = null, bool silent = false) // AiRemoteControl - silent
{
if (!TryComp<SiliconLawProviderComponent>(target, out var component))
return;
@@ -290,7 +306,8 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
component.Lawset = new SiliconLawset();
component.Lawset.Laws = newLaws;
NotifyLawsChanged(target, cue);
if (!silent) // AiRemoteControl - silent
NotifyLawsChanged(target, cue);
}
protected override void OnUpdaterInsert(Entity<SiliconLawUpdaterComponent> ent, ref EntInsertedIntoContainerMessage args)
@@ -312,8 +329,16 @@ public sealed class SiliconLawSystem : SharedSiliconLawSystem
continue;
SetLaws(lawset, update, provider.LawUploadSound);
// Mono edit end
// Corvax-Next-AiRemoteControl-Start
if (TryComp<StationAiHeldComponent>(update, out var stationAiHeldComp)
&& stationAiHeldComp.CurrentConnectedEntity != null
&& HasComp<SiliconLawProviderComponent>(stationAiHeldComp.CurrentConnectedEntity))
{
SetLaws(lawset, stationAiHeldComp.CurrentConnectedEntity.Value, provider.LawUploadSound);
}
// Corvax-Next-AiRemoteControl-End
}
// Mono edit end
}
}
@@ -69,6 +69,12 @@ public sealed partial class StationEventComponent : Component
[DataField]
public ProtoId<RadioChannelPrototype> EndRadioAnnouncementChannel = "Supply"; // Frontier
/// <summary>
/// Mono - sender of start/warn/end announcements.
/// </summary>
[DataField]
public LocId? AnnounceSender = null;
/// <summary>
/// In minutes, when is the first round time this event can start
/// </summary>
@@ -50,7 +50,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
if (stationEvent.StartAnnouncement != null)
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), playSound: false, colorOverride: stationEvent.StartAnnouncementColor);
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.StartAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.StartAnnouncementColor);
// Frontier
if (stationEvent.StartRadioAnnouncement != null)
@@ -97,7 +97,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame);
if (stationEvent.EndAnnouncement != null)
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.EndAnnouncement), playSound: false, colorOverride: stationEvent.EndAnnouncementColor);
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.EndAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.EndAnnouncementColor);
// Frontier: radio announcements
if (stationEvent.EndRadioAnnouncement != null)
@@ -139,7 +139,7 @@ public abstract class StationEventSystem<T> : GameRuleSystem<T> where T : ICompo
{
Filter allPlayersInGame = Filter.Empty().AddWhere(GameTicker.UserHasJoinedGame); // we don't want to send to players who aren't in game (i.e. in the lobby)
if (stationEvent.WarningAnnouncement != null)
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.WarningAnnouncement), playSound: false, colorOverride: stationEvent.WarningAnnouncementColor);
ChatSystem.DispatchFilteredAnnouncement(allPlayersInGame, Loc.GetString(stationEvent.WarningAnnouncement), sender: stationEvent.AnnounceSender is { } send ? Loc.GetString(send) : null, playSound: false, colorOverride: stationEvent.WarningAnnouncementColor);
if (stationEvent.WarningRadioAnnouncement != null)
{
var message = Loc.GetString(stationEvent.WarningRadioAnnouncement);
@@ -58,7 +58,6 @@ namespace Content.Server.VendingMachines
[Dependency] private readonly BankSystem _bankSystem = default!; // Frontier
[Dependency] private readonly PopupSystem _popupSystem = default!; // Frontier
[Dependency] private readonly IAdminLogManager _adminLogger = default!; // Frontier
[Dependency] private readonly ContrabandTurnInSystem _contraband = default!; // Frontier
[Dependency] private readonly StackSystem _stack = default!; // Frontier
[Dependency] private readonly VendingMachinePurchaseSystem _vendingPurchase = default!; // Mono
@@ -347,11 +346,11 @@ namespace Content.Server.VendingMachines
if (TryComp<MarketModifierComponent>(component.Owner, out var modifier))
price *= modifier.Mod;
var totalPrice = (int) price;
var totalPrice = component.RequiresCash ? (int) price : 0;
// If any price has a vendor price, explicitly use its value - higher OR lower, over others.
var priceVend = _pricing.GetEstimatedVendPrice(proto);
if (priceVend > 0.0) // if vending price exists, overwrite it.
if (priceVend > 0.0 && component.RequiresCash) // if vending price exists, overwrite it.
totalPrice = (int) priceVend;
if (IsAuthorized(uid, sender, component))
@@ -545,8 +544,6 @@ namespace Content.Server.VendingMachines
var ent = Spawn(vendComponent.NextItemToEject, spawnCoordinates);
_contraband.ClearContrabandValue(ent); // Frontier
// Mono: Track vending machine purchases for pricing modifications
// Only track if this was a paid purchase (not a random eject or force eject)
if (!forceEject && vendComponent.LastPurchasePrice.HasValue)
@@ -233,7 +233,9 @@ namespace Content.Server.Voting.Managers
foreach (var (k, v) in presets)
{
options.Options.Add((Loc.GetString(v), k));
options.Options.Add((Loc.GetString(v.Name), k));
if (v.Weight != 1f)
options.Weights.Add(k, v.Weight);
}
WirePresetVoteInitiator(options, initiator);
@@ -247,13 +249,13 @@ namespace Content.Server.Voting.Managers
{
picked = (string) _random.Pick(args.Winners);
_chatManager.DispatchServerAnnouncement(
Loc.GetString("ui-vote-gamemode-tie", ("picked", Loc.GetString(presets[picked]))));
Loc.GetString("ui-vote-gamemode-tie", ("picked", Loc.GetString(presets[picked].Name))));
}
else
{
picked = (string) args.Winner;
_chatManager.DispatchServerAnnouncement(
Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked]))));
Loc.GetString("ui-vote-gamemode-win", ("winner", Loc.GetString(presets[picked].Name))));
}
_adminLogger.Add(LogType.Vote, LogImpact.Medium, $"Preset vote finished: {picked}");
var ticker = _entityManager.EntitySysManager.GetEntitySystem<GameTicker>();
@@ -585,9 +587,9 @@ namespace Content.Server.Voting.Managers
DirtyCanCallVoteAll();
}
private Dictionary<string, string> GetGamePresets()
private Dictionary<string, (string Name, float Weight)> GetGamePresets() // Mono - add weight
{
var presets = new Dictionary<string, string>();
var presets = new Dictionary<string, (string, float)>();
foreach (var preset in _prototypeManager.EnumeratePrototypes<GamePresetPrototype>())
{
@@ -600,7 +602,7 @@ namespace Content.Server.Voting.Managers
if(_playerManager.PlayerCount > (preset.MaxPlayers ?? int.MaxValue))
continue;
presets[preset.ID] = preset.ModeTitle;
presets[preset.ID] = (preset.ModeTitle, preset.Weight);
}
return presets;
}
+10 -5
View File
@@ -206,7 +206,8 @@ namespace Content.Server.Voting.Managers
{
var id = _nextVoteId++;
var entries = options.Options.Select(o => new VoteEntry(o.data, o.text)).ToArray();
// Mono - added weight
var entries = options.Options.Select(o => new VoteEntry(o.data, o.text, options.Weights.TryGetValue(o.data, out var weight) ? weight : 1f)).ToArray();
var start = _timing.RealTime;
var end = start + options.Duration;
@@ -286,11 +287,12 @@ namespace Content.Server.Voting.Managers
msg.DisplayVotes = true;
}
msg.Options = new (ushort votes, string name)[v.Entries.Length];
msg.Options = new (ushort votes, string name, ushort realVotes)[v.Entries.Length];
for (var i = 0; i < msg.Options.Length; i++)
{
ref var entry = ref v.Entries[i];
msg.Options[i] = (msg.DisplayVotes ? (ushort) entry.Votes : (ushort) 0, entry.Text);
var votes = msg.DisplayVotes ? (ushort) entry.Votes : (ushort) 0; // Mono
msg.Options[i] = (votes, entry.Text, (ushort) (votes * entry.Weight)); // Mono - account for weight
}
player.Channel.SendMessage(msg);
@@ -394,7 +396,7 @@ namespace Content.Server.Voting.Managers
// Find winner or stalemate.
var winners = v.Entries
.GroupBy(e => e.Votes)
.GroupBy(e => e.Votes * e.Weight) // Mono - add weight
.OrderByDescending(g => g.Key)
.First()
.Select(e => e.Data)
@@ -536,12 +538,15 @@ namespace Content.Server.Voting.Managers
public object Data;
public string Text;
public int Votes;
public float Weight; // Mono
public VoteEntry(object data, string text)
public VoteEntry(object data, string text,
float weight = 1f) // Mono
{
Data = data;
Text = text;
Votes = 0;
Weight = weight; // Mono
}
}
+4 -1
View File
@@ -39,6 +39,9 @@ namespace Content.Server.Voting
/// </summary>
public List<(string text, object data)> Options { get; set; } = new();
// Mono - this is evil but allows seamless integration with the code
public Dictionary<object, float> Weights = new();
/// <summary>
/// Which sessions may send a vote. Used when only a subset of players should be able to vote. Defaults to all.
/// </summary>
@@ -50,7 +53,7 @@ namespace Content.Server.Voting
public bool DisplayVotes = true;
/// <summary>
/// Whether the vote should have an entity attached to it, to be used for things like letting ghosts follow it.
/// Whether the vote should have an entity attached to it, to be used for things like letting ghosts follow it.
/// </summary>
public NetEntity? TargetEntity = null;
@@ -165,7 +165,8 @@ public sealed partial class GunSystem : SharedGunSystem
CreateAndFireProjectiles(ent.Value, newAmmo, offset);
break;
case HitscanAmmoComponent:
case HitscanAmmoComponent hitscanammo:
if (ent == null)
break;
@@ -179,10 +180,13 @@ public sealed partial class GunSystem : SharedGunSystem
};
RaiseLocalEvent(ent.Value, ref hitscanEv);
Del(ent);
Audio.PlayPredicted(gun.SoundGunshotModified, gunUid, user);
// Mono start
Spawn(hitscanammo.CasingPrototype, fromEnt);
Del(ent);
// Mono end
break;
default:
throw new ArgumentOutOfRangeException();
}

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