From d81024d883caaa9e859710b710b3731a39fb5d33 Mon Sep 17 00:00:00 2001 From: Gavreau Date: Wed, 15 Apr 2026 16:33:57 -0400 Subject: [PATCH] Psionic Refactor (#4788) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Refactoring * Last PR * forgot to switch * Redone Psionic System * more refactoring * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Another refactorio * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * I don't know why I tried to overcomplicate this I could have just made it a popup message from the start instead of wasting three hours * another big refactorio * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update lighthouse.yml (#4849) * Pebble hotfix 1: Added ore processor (#4874) * Added ore processor * Forgot grills * Automatic changelog update * Terra: The Holiday Episode (#4843) SNOW * Automatic changelog update * Academy 1.3.0 (#4863) * * make festive for winturrr * fixed emergency light in viro checkpoint * fixed always on light outside dispo * fixed light * Map Edits: The Hive (#4887) Co-authored-by: Velcroboy * Automatic changelog update * Map Hotfix: Tortuga (#4878) * Map Hotfix: Tortuga * invalid * Circuit imprinter * increase detective 1->2 * return of the circuit imprinter * fish power --------- Co-authored-by: Velcroboy * Automatic changelog update * And another one! * Fix yaml linter * Fix mapping section in CONTRIBUTING.md (#4894) Co-authored-by: Velcroboy * Give dummy avali and chitnid moth eye sprites (#4884) 1 commit ops * Midpoint Festive Season (#4891) * Midpoint Festive Season * Made frosty lights not be powered by festive magic * Automatic changelog update * Make pride clothing be able to chameleon into similar pieces of pride clothing (#4845) * Expedite gender reassignment (#36894) * Remove Misgendering (#40425) fix misgendering * make pride scarfs and cloaks chameleon * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add missing comments * Attempt Toby's suggestion: rename chameleon stuff * rename the chameleon verb & window --------- Co-authored-by: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * kill speedboots (#4883) * remove speedboots * no more reverse engineering * Update Resources/Prototypes/Research/civilianservices.yml Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Proxy also works :3 <98499019+Proxysseia@users.noreply.github.com> --------- Signed-off-by: Proxy also works :3 <98499019+Proxysseia@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Automatic changelog update * Centcomm roles QOL (#4880) * first commit yippee * ERT now gets centcomm implants too. Also fixes ERT not having death rattle implants * Always bigger fi- oh nevermind (Feroxi Emote) (#4877) * feroxi changes Wideswing,10 pierce, tail thump, better storage of body emotes for feroxis * Direction Changes Removed wideswing dropped feroxi melee back to 7 Signed-off-by: Halo3moth --------- Signed-off-by: Halo3moth * Automatic changelog update * Update Credits (#4897) Co-authored-by: DeltaV-Bot * Add Chemical, Paramedic, and Surgeon wall lockers (#4899) * woo wall lockers! * chemical * clean up * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Fix respawn timer (#4895) * fix * Automatic changelog update * Demetashield epsilon part 2 (#4481) * demetashield epsilon part 2 * bug fix * annoucement.yml fix * some redundant stuff removed fix * Make the cane loadout group include the librarian cane (#4904) * Atmos Ops: dP Guidebook Entry (#4906) Atmos dP Guidebook Entry (#40194) * Add Atmos dP guidebook * Update Resources/ServerInfo/Guidebook/Engineering/DeltaPressure.xml --------- (cherry picked from commit a93f6b8cdf5bdc4c0bf509e0706126eea500b531) Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Automatic changelog update * Xenoarch tweaks (#4486) * Initial glimmer change * Added glimmer multiplier * Adds scalar of research properly like the old glimmer system * Left over code * Glimmer changes for Analysis Console UI * Accidental Namespace added * Fixed small Math issue with glimmer values * Fixed reference to Nyano * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Fixed Deltav comment references Went though various files and added comments to signify where delta v changes are to the upstream code * More DeltaV Comment fixes Added a few more comments for DeltaV specific changes, I also removed an unused line of code, and renamed to variables for better clarity * Commenting and moving one file to _DV I cant read apparently and only saw some fixes requested, here are the rest apologies * Update Content.Shared/Xenoarchaeology/Equipment/Components/ArtifactAnalyzerComponent.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/Content.Shared.csproj Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/_DV/Xenoarchaeology/BUI/AnalysisConsoleBoundUserInterfaceState.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Addressed issues of cleaning up a function to one that already exists, restoring an old comment from upstream, and moving localization data to _DV folder * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs Co-authored-by: Charlie Morley Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Missed one line to bring over to _DV * Fixed github and IDE complains about null ref --------- Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Co-authored-by: Charlie Morley * It Snew On Lady Elegance (Christmas PR) (#4903) * Update elegance.yml * Update elegance.yml * those who snow * nukes the tattered cloak for real * Update elegance.yml * Automatic changelog update * Winterhouse (#4910) Update lighthouse.yml * Hivemas (#4898) * Hivemas * variantize --------- Co-authored-by: Velcroboy * Automatic changelog update * It's a Tortuga Xmas! (#4893) * It's a Tortuga Xmas! * ... --------- Co-authored-by: Velcroboy * Automatic changelog update * Reinforced Tinted Windows (#4875) * completion * fix * moved directories * fixed * Apply suggestion from @Toby222 Signed-off-by: Tobias Berger --------- Signed-off-by: Tobias Berger Co-authored-by: Tobias Berger * Automatic changelog update * Manual changelog update * Manual changelog update * Harpies Flightless No More! (#4888) * Initial port of EE Harpy flying (#919) * Added better friction and acceleration modifiers. * Moved server's flight system to shared. I don't know why it isn't in the first place. * Shifting things around. * Code tweaks to make it better * Comment fixes * Fixed footsteps and footstep sounds * Added a quick conditional if InitialStaminaCost greater than zero * Removed a pop-up that wasn't showing * Additional adjustments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed a few more systems to call the Flying system instead of the component * Comment consistency. DeltaV comments where I made it better. EE comments where they originally had things * YAML fixes * Guidebook * Apply suggestions from code review Co-authored-by: Tobias Berger Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Removed DoAfterDashEvent * Fixed suggestions * Updated comment * Added new copyright free wingflaps with proper attribution * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Appeased YAML gods --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tobias Berger * Automatic changelog update * Adds nightvision to rat kings and xenos (#4881) * ISeeDeadPeople * Apply suggestions from code review Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Automatic changelog update * Give custom laws to Salv Borg (#4866) * Give custom laws to Salv Borg Give a custom lawset to salvage borg * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * updated laws explain it better and look cleaner Signed-off-by: BungleBoss <100389635+BunglesnortBoss@users.noreply.github.com> * Update laws.ftl Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: BungleBoss <100389635+BunglesnortBoss@users.noreply.github.com> --------- Signed-off-by: BungleBoss <100389635+BunglesnortBoss@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Automatic changelog update * Protect arrivals and evac from meteors (slightly) (#4820) * Protect arrivals and evac from meteors (slightly) * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * change meteor protection zone to a MarkerBase * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * xenoarch: remove impossible artifact trigger combinations (#4740) * xenoarch trigger combination blacklists * minor review comments * review comments * Automatic changelog update * xenoarch: node scanner UI overhaul (#4814) * xenoarch: node scanner UI overhaul * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Charlie Morley * Update Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Charlie Morley --------- Signed-off-by: Charlie Morley Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Automatic changelog update * Syndie borg fixes (#4913) * Moved law boards to their own file, similar to the structure that upstream uses. * Fixed syndicate medical borg ghost role. Moved DV lawsets. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Feroxi: Blood in the Water (#4902) * Added shark vision to Feroxi. * Fixed a bug if someone was holding a container of blood. * Updated pulse time and useDelay for the pulse. * Updated guidebook * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Cleanup * More clean-up * Removed vision goggles sound from ability * Got rid of an unused namespace * last fix I swear * Apply suggestions from code review Co-authored-by: Tobias Berger Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Apply suggestion from @Toby222 Co-authored-by: Tobias Berger Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Removed SharedLightSystem from SharkVisionOverlay --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tobias Berger * Automatic changelog update * Byoin TEG burn chamber edits! (#4911) Update byoin.yml * Automatic changelog update * Reinforced Tinted Windows Bug Fix (#4918) fix * Automatic changelog update * Vulnerable String Sanitization (Prevent `cmdlink`) (#4922) * Fix label markup escapeing (#40600) fix * Sanitize NanoChat * Escape PDA Owner/Job * Escape ID Examine * Early merge of https://github.com/space-wizards/space-station-14/pull/41799 --------- Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: Tobias Berger * 100% Ion resistance on Ninja Suit and Visor NVG. (#4916) * Add NVG to visor, Ion Immunity and no EMP armor ignore * Comments.. * .ftl from up to _DV * Rewrote visor description. * Remove ftl newline, forgor desc comment. * Automatic changelog update * AI HUD and Comms Update (#4905) * test one * Removed mind shield hud * Automatic changelog update * Syndicat ghost role description tweak (#4889) * final tweaks I promise. * minor desc change requests * typo buh * Automatic changelog update * Chibi update 08DEC2025 (#4914) * omg chibi so kawaii!~ >w< * take a wild fucking guess * cameras, fixrotations, tilewindows * Automatic changelog update * Fixes coffe typo (#4930) * Add more festive holiday clothing to AutoDrobe (#4909) * sprites galore * no Santa monkey * autodrobe * cost up * Automatic changelog update * EntropicDegen Fix (#4931) lol * Automatic changelog update * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Merge Upstream * One refactor to rule them all (#15) * Add xenoborg laws to ion storm chance (#41532) add xenoborg laws to ion storm chance * Replace (almost) all existing storage fills with entity table container fills (#41360) * Replace all existing storage fills with container fills, adjust some formatting to accomadate * Forgot to remove that * I really need to spell better * Right, those are 3x3 * Change some NestedSelectors to GroupSelectors * Actual container fills, test fix (maybe), and a few minor formatting changes * Fix some more mistakes * Forgot to add to that comment * Some parenting fixes * Test optimisation, maybe * Roll holiday lights instead of individually listing it * Move eletrical toolbox to NestedSelector * Back to entity tables (except for vending) * More nesting (and a TODO) * Yet another fix * Probably shoulda done it this way originally * Entity tables for vending restocks * I am good at formatting * Make added table names consistent * Make grand lottery weights a little more readable * Use existing fills for bio suit crates * Get rid of some more redundant amounts * Nuke the StorageFill test section and update some comments * Apply changes from code review * Split off changes to vending restocks * Split off changes to vending restocks * Apply more changes from code review * Attempt #2 of detangling this mess (oh shit I missed the pin) * They look so similar * I am great at formatting * Missed a newline * How did I manage to do that * Move toy in FoodMealHappyHonkClown to top to prioritise * Apply changes from review and move dank pizza to organise by weight * Replace all storage fills with entity table container fills * Balloons now float and slide smoothly. (#41102) * The balloons became airy * Attributions + Sound * Add BaseBalloon * Proto fixes --------- Co-authored-by: ReWAFFlution <239567049+brokendot@users.noreply.github.com> * Add Whitelist.Components yaml valiation (#40916) * Add Whitelist.Components yaml valiation * poke_tests * fix linter --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Remove blacklist methods in EntityWhitelistSystem (#40932) * replace blacklist methods * VS is trolling me * Remove blacklist methods from fork changes * Add test checking for multiple container fills, mark StorageFillComponent as obsolete (#41562) add test, obsolete storagefill * No more sub wizards in xenoborg preset (#41565) no more sub wizards in xenoborg preset * Fix gas filters (#41567) fix gas filters * Fix stunbaton component namespace (#34790) * Fixed incorrect namespace * .Components was weird so I removed it * Predict powercells, chargers and PowerCellDraw (#41379) * cleanup * fix fixtures * prediction * fix test * review * fix svalinn visuals * fix chargers * fix portable recharger and its unlit visuals * fix borgs * oomba review * fix examination prediction * Fix and mark microwave time addition for BeingMicrowaved * Fix for missing namespace in Nanochat * Fix several systems and protos after powercell prediction change * Fix typo in Ion Storm lawsets (#41574) Pizzaria → Pizzeria * Remove commented items (#41577) * Tourniquets can now fit in medical belts (#41584) * Fewer War Crimes (#41541) * Fewer War Crimes Re-colour the syndicate medical bags to not use the legally restricted Red Cross symbol and instead use the same blue cross colours as used across the rest of the game's medical symbolism. Because no-one wants to go to the Hauge. Also consistency is nice. * Update meta.json * Update another meta.json * Update meta.json again additional detail * Make gameticker spawn code more modular (#41588) gameticker dospawn * Meatball Salvage wreck remake (#41589) Remake-Meatball-Wreck * Update mothership again (#41491) * add xenoborg recharging station * it was backwards * make it a tad darker * touch ups * add xenoborg blast door sprites * update sprites * add xenoborg blast door * improve blast door sprite * typo * make it thinner * update mothership * moved yml lines * updated xenoborg charger sprite * removed unused state * fixes to the mothership * Fix helmet lights (#41599) fix helmet lights * Change thief backpack ui name and description with Component fields (#41583) * Add new name and description fields to the thiefbackpack component and ui * Change fields to locids, remove Title from menu.xaml, add comments to thiefbackpackui.cs * Helper method for get charge level (#41601) * get-charge-percent * review * Adds option to whitelist or blacklist store entries based on buyer objectives (#41493) * BuyerObjectiveWhitelistCondition done * fix to check every buyer objective for a blacklisted objective before checking whitelisted objectives * resolved requested changes * forgot a line * Update Content.Server/Store/Conditions/BuyerObjectiveWhitelistCondition.cs * Replaced IsBlacklistPass with IsWhitelistPass * cleaning up * placed whitelist == null check above objective loop * Moved empty whitelist check below foreach loop * final cleanup i pray --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix looking at verbs causing sounds or popups (#41609) * fix verb popups * spelling * Properly document AtmosDeviceEnabled(Disabled)Event (#41613) kill misnomers * Felinase/Caninase Reagent Tweaks (#41527) * Tweaked reagents required for recipes, increased output of recipe, lowered time of accent per unit, and tweaked chem thresholds * tweaks i was lazy * product change lazy again * yuh * pluh --------- Co-authored-by: NoreUhh * Change to add shot glasses to the bartender guidebook entry (#41618) adds shot glass to bartender guidebook * Add StatusIcon component to MobBaseAncestor (#41624) add StatusIcon to ancestor * Make xenoborg round end text better (#41623) * make xenoborg win color easier to read * display the max number of xenoborgs and the number of crew alive in the end * make it a datafield * add : (literally) * add period * make text better * Fix EquipmentVerbs not showing up in strip menu (#41631) commit Co-authored-by: iaada * Fix clientsided alerts being overwritten by server (#37033) Initial commit * Predict borgs (#41600) * predict borgs * small fix * fix MMI item slot serialization * fix movement speed for mothership core * review and minor improvement * fix resolve * review * predict name identifiers (#41605) * Make Firespread logical (#41636) * FIRE * code comment fix --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * fix rcd overlay getting stuck for borg modules (#41648) fix rcd overlay getting stuck * Add water flower for clowns (#41469) * Spray! * Add to clown loadout * Fix the easy things * lot nicer * spray update.. * Fix yaml * fixes * changed it to warning! * review * review * sku * multi reagent bloodstream (#41489) * multi reagent bloodstream * pluralize the comments * fix TryModifyBloodLevel return logic * now with quantity * now with solution * implement suggestions * fix forensics * minor thing * Nevermind undo that caps matters. --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Fork fixes for multi reagent bloodstreams * [HOTFIX] FIX LIGHT MELEE LOGS (#41655) God fugcking Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Add Changeling DNA store (#41632) * created changeling dna currency and locale currency name * created changeling store category, created changeling-catalog.ftl * added store and action to Urist McLing * found the entity * make armblade a purchase * comment * created ApplyToMob datafield and applied to armblade * ApplyToMob check in storesystem * Update Resources/Prototypes/Catalog/changeling_catalog.yml --------- Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> * Fix Damageable API (#41657) Fix damageable api Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Add debug hitscan weapon (#41658) * debug gun * predict * fix AI battery alert (#41879) * fix AI battery alert * fix copy paste error * Fix rigged power cells exploding early (#41813) * fix riggable * fix * Unify BatteryComponent and PredictedBatteryComponent (#41867) * unify * cleanup and merge conflicts * floating points --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Downstream fixes for battery unification * Fix xenoborg modules (#41625) * change name from heavy laser to laser cannon * fixed some ids * fix name and description * change cost of upgrade modules * remove unwield penalty * commentary * change name of scout modules * same firerate as before * typo Co-authored-by: āda * new line at end of file * duplicated --------- Co-authored-by: āda * Only run StationPowerTests over stations in DeltaV * Five bounty arbitrage fixes (#41846) * commit * tweak * more bounties --------- Co-authored-by: iaada * Stop the laser gun bounty allowing LaserTag guns * Fix power draw on Defib cabinet when not charging anything * Mail Cart (#41482) * Add mailcart crate type * Add recent progress, move location of mailcart.rsi * Allow letters and packages to be inserted * Add updated sprites for mailcart * Add storage for letters * Bugfix for sprite * Add fix for mincount mailcart sprite updates * Add dynamic names to packages in mailcart * Add additional whitelist items * Update tag comments * Remove max item size * Update fixture for mailcart, remove unused code from crates.yml * Add mail cart to cargo ordering * Update meta.json * Remove unused comment * Update copyright * Add missing ContainerContainer * Update ContainerContainer * Revert "Update ContainerContainer" This reverts commit 1805dcd58d8c0f52baca9db0d3173940ee241159. * Remove container slots and increase storage * Remove unused tags * Remove LetterDelivery tag --------- Co-authored-by: Justin Pfeifler * fix solution contents duplication on spill behavior (#33231) * I’M SCREAMING INTO THE VOID AND IT’S NOT LISTENING * review * explodes pancakes with mind * graaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa * Meteors RAAAAAAAAAAH * I'm so tired of solutions * whhop * revert --------- Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> * Disable xenoborgs at roundstart * Fix issue with weaponUids not being passed correctly for weapons used by entities * Upa APC max load to 40 kW * Use existing hardsuit extra table * Move xenoborg lawsets above DeltaV * Apply #41583 to all thief stash-like entities * Also apply #41149 to all magazines and ammo boxes * 1984 engineering holoprojector * change to 6 charges * Security longcoat resprite (#5288) * My coats are so long * making the hos longcoat more hoslike * Automatic changelog update * Printable plasteel (#5284) * Printable plasteel * Actually make it a bit faster * Move to that other recipe pack * Recipes are named after their result --------- Co-authored-by: Tobias Berger * Automatic changelog update * Disable Wizard until Rebalanced (#5228) * Remove wizard from midround antags until rebalanced * Removed wizard from roundstart antags --------- Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * Automatic changelog update * Apply #41461 to _DV security borg modules * Tank Dispenser Recolor (#5267) * Add files via upload Tank Dispenser Recolor Signed-off-by: Thingy461 * Add files via upload Minor tweak to vending machine YAML for tank dispenser recolor Signed-off-by: Thingy461 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix line endings * Apply suggestions from code review Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Apply suggestion from @ShepardToTheStars Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Update Resources/Prototypes/Entities/Structures/Machines/vending_machines.yml Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> --------- Signed-off-by: Thingy461 Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Tobias Berger Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> * epic grammar ops * hotfix-plasteel (#5298) * Automatic changelog update * Make unholy water actually have a color (#5312) make unholy water actually have a color * Tiny things not worth requesting changes for * Changelog stuff! * Merge pull request #5293 from Pharaz4/cherrypickattempt LIZARDS GET TAILS ON HARDSUITS!!! * Automatic changelog update * Remove Syndie comms from disaster victims and guards (#5280) * remove syndicomms from disaster victims * removed from syndicate guard as well. (dropship ruin has a guard) * fix harpy wing showing on cc hardsuit * Automatic changelog update * Halve Space Law Sentencing Times (#5249) * Reduce times in space law and SOP, in guidebook and wiki * cat c 12 minutes instead of 10 * update crime assist * CentComm stuff (#5315) * CentComm stuff * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * hmm... --------- Signed-off-by: Field Command <159087063+FieldCommand@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Add missing link in changelog * Uncloneable in my medical scanner? Yes. (#5310) * Update OrganManipulation.xml * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * no more taser! * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * Merge branch 'DeltaV-Station:master' into master * MMm yummy fixes * Adsd the UNcloneable to the scanner! * fixing some guidebook things about the cloning * Fixed the guidebook entry * undone a change to splaw * textlinkd * COMMENTS RAHHHHH * Update CryoPodSystem.cs Co-authored-by: Tobias Berger Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> * Update HealthAnalyzerSystem.cs Co-authored-by: Tobias Berger Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> * Update HealthAnalyzerSystem.cs Co-authored-by: Tobias Berger Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> * Automatic changelog update * Health Analyzer Uncloneable Refactor (#5330) * Partial revert of #5310 * Refactored uncloneable out of the HealthAnalyzerScannedUserMessage BUI. * Automatic changelog update * Centcomm fixes (#5321) * Automatic changelog update * epic guidebook ops * fix EngieeringRegulations type * minor fixes * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update Credits (#5341) Co-authored-by: DeltaV-Bot * Invisible Guitar Wield Bugfix (#5331) chec * Automatic changelog update * Brigmed Beret (#5333) brigmed * Automatic changelog update * bandolier buff? tweak? (#5313) ok functional * Automatic changelog update * Port admin logging of AAC messages (#5309) fix: Admin log AAC tablet messages * Automatic changelog update * Changed enabled mapped pressure pump to 300 kpa (#5336) lowered pressure by 10 kpa * Automatic changelog update * Port sneaking status icon from Lagrange (#5308) * Make the icon for sneaking show sneaking status (#358) * Make the icon for sneaking show sneaking status * Specify sprite (cherry picked from commit dd5fa91e1e1b9f350f7dac15579430d503b2ab6b) * Update from review Co-authored-by: Tobias Berger Signed-off-by: Madison Rye Progress --------- Signed-off-by: Madison Rye Progress Co-authored-by: Tobias Berger * Automatic changelog update * Updates random solar panel spawner (#5323) * Updates random solar panel spawner * Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/solar.yml Co-authored-by: Tobias Berger Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> * Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/solar.yml Co-authored-by: Tobias Berger Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> * Update Resources/Prototypes/_DV/Entities/Markers/Spawners/Random/solar.yml Co-authored-by: Tobias Berger Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> --------- Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy Co-authored-by: Tobias Berger * feat: Add a few bowls to the perma chefvend (#5304) * Automatic changelog update * The Reclaimer MK2 returns! (#5245) * The Reclaimer returns * Added the ability to name GridSpawns with a specific name * formatting --------- Co-authored-by: Vanessa * Automatic changelog update * Midpoint Updata: CC now have an office on Midpoint (#5231) * Midpoint CC * Update maps.yml * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fixed spelling! --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Feroxi Wagging (#5319) * uno * fix * fml * mark 2 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Fix harpy ERT, CC, suits (#5325) * Fix harpy ERT suits (not elite ones, change enabled mapped pump from 310kPa to 300kPa. * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix harpy suits not working (probably) * fix the harpy suits not displaying * Fixed Lizards missing a sprite for the rhino hardsuit, also some minor comment adjustments * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Hid upstream ERT spawners * added 3 sprites for reptiles * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix firesuit (And its namespace) and add rd suit sprite for lizards * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * elite ert sprites because toby hasnt reviewed yet * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * still not merged so im adding this, will fix debug test failing on prs * fixed attributions --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Emagnum patch (#5352) * Update projectiles.yml * Now that I’m thinking about it giving it opaque could lead to unintended interactions * Apply suggestion from @ShepardToTheStars Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Update Credits (#5356) Co-authored-by: DeltaV-Bot * Small Pebble fixes (#5347) * Small Pebble fixes * Small pebble fix * Automatic changelog update * Byoin and Basu update (#5232) * Byoin and Basu update * Byoin Fixes * more small edits. * minor fixes * Automatic changelog update * Shoko update (#5334) * Updayed shokou to modern standard, see pr for more info * changed stuff based on feedback in discord * added mailing unit between botany and chem. fixed window in bathroom. added to the 4th wall breaking note in station anchor. * fixed mail unit placement in botany * added drain to salv airlock and disposals. redid parts of epistemics. redid perma bathroom and removed condiments. added colors to atmos pipes and changed up the pipe layour of distro and waste. added firebot to teg room. * added salvage job board to salv dock * made changes based on feedback from FC * fixed missing change * Automatic changelog update * Map Hotfix: The Hive (#5368) * Map Hotfix: The Hive * switcheroo --------- Co-authored-by: Velcroboy * Automatic changelog update * Hotfix: Obliterated the second LO locker on Pebble (#5371) Obliterated the second LO locker * Automatic changelog update * adds 4 new ruins based off of /tg/ stations space ruins. (#5283) * adds 4 new ruins based off of /tg/ stations space ruins. * fixed up window missing a grille and made mines a 33% chance to spawn instead of garuntee * Automatic changelog update * Update Credits (#5383) Co-authored-by: DeltaV-Bot * the fabled single line change * Update PlayerProvidedCharacterRecords.cs * Revert Soretizone buff (#5307) * Update medicine.yml Update medicine.yml Update medicine.yml Revert "Update medicine.yml" This reverts commit 905c5cac070c836cd674f9cbeffed880c243d7d6. Update medicine.yml * Update medicine.yml * agono make you numb also * condenser power usage change (10kW ->2kW) (#5365) * Auto label port * Make it so it works/comments * Post review changes. * Post review change part two. * review pt 3 * Fixed client UI from not getting updated. * Gas condenser power usage to 2k --------- Co-authored-by: Vanessa * Automatic changelog update * shoko update numba 3 (#5401) * shoko update numba 3 * 88 LINES ADDED 82LINES REMOVED * Automatic changelog update * Fix pew sprite, benches, and make these benches constructible. (#5375) * fix pew sprite, fix incorrect labelling of benches and sofa, add snapgrid center to benches * make it so you can construct/deconstruct and relocate namespace * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Add migration --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Velcroboy * Automatic changelog update * Fixes to suffix (#5353) * Reverts Arena weapon spawner change (#5322) Reverts change from prob to weight Co-authored-by: Velcroboy * Removed a dead pixel on a book sprite (#5362) * Update Credits (#5413) Co-authored-by: DeltaV-Bot * Hotfix: Pebble, small changes. (#5415) Pebble fixes * Automatic changelog update * Lighthouse Tweaks... again... (#5246) * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Automatic changelog update * old_infiltrator hotfix (#5405) * oldinfiltrator hotfix * put command corpse spawner and add exception for cc loot to the infiltrator ruin * fix wrong file path * webedit Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * fuck github --------- Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * Removed ability for limbs to be trashed * removed bodyparts ability to be placed in trashbag * Removed estoc slowdown (#5242) * Removed estoc slowdown * Re-added speed penalty, but reduced to 0.9x instead of 0.75x * Automatic changelog update * Adding purifying salt to the med vend, and a new cryo chem for holy and shadow (#5276) * new cryo chem and adding topicals to medivends and med's techfab * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * me when dumb and tired and on phone Signed-off-by: keekee38 * woof * Update Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: keekee38 * Update Resources/Prototypes/_DV/Reagents/medicine.yml Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: keekee38 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * ok maybe that fixes it if not will look at later Signed-off-by: keekee38 * did the /> Signed-off-by: keekee38 --------- Signed-off-by: keekee38 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Automatic changelog update * Asterisk fix (#5373) * first part * last one * last * last one maybe * last fix * Automatic changelog update * Academy 1.6.0 (#5412) * boxketball court is wooden again * gave botany a window and moved their machines around, extra crate, extra tools * added masks to clown and mime rooms, someone complained "it's barren" * fixed hop diectional sign above robo * added crashcart to med and viro * added mailcart to mail * converted maint passage to hallway between epistemics and cargo, and updated directional signs * removed christmas cakes from kitchen and cargo cafe * replaced winter with spring/summer * added gold and silver to cargo * Automatic changelog update * Automatic changelog update * add emoting component to skia (#5366) * Automatic changelog update * Fix shrapnel landmines not triggering properly (#5379) * Fix shrapnel landmines not triggering properly * Re-enable landmines in the landmine entity table * Automatic changelog update * Make the chemvend's bluespace beaker only available through an emag (#5392) * move the chemvend bluespace beaker to emaggedInventory * forgot to commit * Automatic changelog update * removing machine pistol mag compatability for the viper and pollock and fixing practice mags (#5296) * sighs * :3 oopsie daisy,,,,, * adding the thingy Signed-off-by: keekee38 * Fix comment Signed-off-by: Stop-Signs --------- Signed-off-by: keekee38 Signed-off-by: Stop-Signs Co-authored-by: Stop-Signs * Automatic changelog update * New Gun Safe Sprites (#5314) * Automatic changelog update * Automatic changelog update * Automatic changelog update * infested frigate ruin fix (#5431) * Automatic changelog update * Merge pull request #5218 from DisposableCrewmember42/conspirator-port feat: add Conspirators from Harmony * Automatic changelog update * Slavic Accent character trait (#5264) * port slavic accent * change some replacements * bobr * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * eliminate redundant regex * bozhe moy * order matters * Merge branch 'master' into slavicAccent Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Apply suggestions from code review Co-authored-by: Tobias Berger Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> * Update Content.Server/_DV/Speech/EntitySystems/SlavicAccentSystem.cs Co-authored-by: BarryNorfolk Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> * tryfix * Merge branch 'slavicAccent' of https://github.com/zelezniciar1/Delta-v into slavicAccent * Automatic changelog update * Free Suit Storage (#5300) what fun * Automatic changelog update * Elegance Update Number I Lost Track (#5387) * Update elegance.yml * holy shit is that kris deltarune * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * dghjdsgjkdfs * that one random brick decal under the window: * stuff * Kill that line Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> * Kill that line 2 but it's actually redoing them Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> --------- Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Crusher Right Click Wideswing (#5327) commit * Automatic changelog update * Map Edits: The Hive (#5438) Co-authored-by: Velcroboy * Automatic changelog update * Reclaimer MK2 hotfix (#5440) Fixes #5428 * Map Edits: Arena (#5445) * Map Edits: Arena * AI router --------- Co-authored-by: Velcroboy * Automatic changelog update * PKA fixes try 2 (#5311) * easier to just redo the PR rather then fix the old one * remove comment Co-authored-by: Tobias Berger Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> --------- Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Co-authored-by: Tobias Berger * Automatic changelog update * fix: (re)remove forced accents (#5343) fix: (re)remove clothing/item/role-forced accents * Automatic changelog update * Let Engineering choose their starter suit (#5317) * test * a lot of stuff * Changed voidsuit speed to 15%. Added a locker variant to just have the hardsuit, with a shitty id * test re-run * deltaV addition comments * goob copyright (just in case bro, don't sue us) * Automatic changelog update * Add new Lobby and Jukebox music by Darklyac (#5424) * darkylac muisic real * the time * Automatic changelog update * Add cloth wraps (#5335) add cloth wraps * Automatic changelog update * Borg Crew Monitor Fixes (#5342) * Update handheld_crew_monitor.yml * suggested clarity * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> * Automatic changelog update * Update Credits (#5453) Co-authored-by: DeltaV-Bot * Edge fix (#5439) * Edge * Edge Part two * Edge final * Automatic changelog update * move bluekies to DV namespace (#5414) * move bluekies to DV namespace * FIX FAIL * fix harpy paramed suit * Automatic changelog update * Remove ballistic reflect from energy swords (#5429) * reduce reflect chance on ninja and cult swords Energy weapons by default now only reflect energy projectiles * Revert "reduce reflect chance on ninja and cult swords Energy weapons by default now only reflect energy projectiles" This reverts commit 444d9a4921efae77f0ea5a62e56d0ee817e0ce54. * Energy swords only reflect energy shots * Enable wizard's teleport scroll and wizard's den map (#5215) * Enable wizard's teleport scroll and wizard's den map * mapPath * longer cable in cave --------- Co-authored-by: Velcroboy * Automatic changelog update * add blacklist to cigpackbase for chems (#5402) * add blacklist to cigpackbase for bottle and vial * hold up i forgot half the shit * Automatic changelog update * Tweak the Health Analyzer Triage System (#5110) * guh * applied suggestions + made medical access only * Automatic changelog update * Minor Rat King Tweaks (#5326) * baby rat king tweaks * requested changes * Automatic changelog update * Port over Funky's unary pipe layers (#5372) * port over funkies unary layers and put them under _funky namespace * fix construction thingies having names that cause yaml linter to fail * why does yamllinter not show errors on my machine * Apply suggestions from code review Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * addd attributions Co-authored-by: BarryNorfolk Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * add attributions 2 Co-authored-by: BarryNorfolk Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * add attributions 3 Co-authored-by: BarryNorfolk Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * Update Resources/Prototypes/_Funkystation/Entities/Structures/Piping/Atmospherics/alt_layers.yml Signed-off-by: BarryNorfolk --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Signed-off-by: BarryNorfolk Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: BarryNorfolk * Automatic changelog update * Hos Aura Buff (#5425) * that should do it * copyright update * more work done * Rename Icon.png to icon.png Signed-off-by: Halo3moth --------- Signed-off-by: Halo3moth * Automatic changelog update * Map Edits: Tortuga (#5437) * Map Edits: Tortuga * Additional changes * Passive vents * oh yeah * review fixes --------- Co-authored-by: Velcroboy * Automatic changelog update * Submarine Fixes (#5394) MASS UPDATE * Automatic changelog update * Map Edits: Evac Shuttles (#5457) Co-authored-by: Velcroboy * Automatic changelog update * Lighthouse Arrivals (#5419) * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Update lighthouse.yml * Automatic changelog update * Update division.yml (#5465) * Terra Hotfix 4 - It's Alive! (#5432) * Terra Hotfix 4 - It's Alive! * Removed all bombing targets from warp points * Renamed Vault substatin, extended cargo dock, added AI restore to AI Sat, changed arrivals screen * Automatic changelog update * fix missing exception on lavaland ruin (#5472) * Elegance Engineering Crates Hotfix (#5468) * Update elegance.yml * Update elegance.yml * Update elegance.yml * Automatic changelog update * Merge Upstream --------- Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Signed-off-by: Thingy461 Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Field Command <159087063+FieldCommand@users.noreply.github.com> Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> Signed-off-by: Madison Rye Progress Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Signed-off-by: keekee38 Signed-off-by: Stop-Signs Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Signed-off-by: BarryNorfolk Signed-off-by: Halo3moth Co-authored-by: Samuka <47865393+Samuka-C@users.noreply.github.com> Co-authored-by: SnappingOpossum Co-authored-by: BarryNorfolk Co-authored-by: Hohenzolaren <153686236+ReWAFFlution@users.noreply.github.com> Co-authored-by: ReWAFFlution <239567049+brokendot@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: Jessey van Hoeijen <114107545+LMSNoise@users.noreply.github.com> Co-authored-by: Stefano Pigozzi Co-authored-by: Connor Huffine Co-authored-by: YoungThug Co-authored-by: CDWimmer <31413434+CDWimmer@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: Alzore <140123969+Blackern5000@users.noreply.github.com> Co-authored-by: imatsoup <93290208+imatsoup@users.noreply.github.com> Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com> Co-authored-by: alexalexmax <149889301+alexalexmax@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: NoreUhh <85219416+NoreUhh@users.noreply.github.com> Co-authored-by: NoreUhh Co-authored-by: JackspajfMain <105893899+JackspajfMain@users.noreply.github.com> Co-authored-by: Minemoder5000 Co-authored-by: āda Co-authored-by: iaada Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: Ignaz "Ian" Kraft Co-authored-by: Noah Beverly Co-authored-by: Justin Pfeifler Co-authored-by: Milon Co-authored-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Co-authored-by: Halo3moth Co-authored-by: Delta-V bot <135767721+DeltaV-Bot@users.noreply.github.com> Co-authored-by: Astra <226853568+EmberAstra@users.noreply.github.com> Co-authored-by: Tobias Berger Co-authored-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Co-authored-by: Thingy461 Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: ConstantlyConfused <67894661+Eternally-Confused@users.noreply.github.com> Co-authored-by: Theo Co-authored-by: Field Command <159087063+FieldCommand@users.noreply.github.com> Co-authored-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: DeltaV-Bot Co-authored-by: verybigman <171044200+verybigman311@users.noreply.github.com> Co-authored-by: keekee38 Co-authored-by: Madison Rye Progress Co-authored-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy Co-authored-by: Vanessa Co-authored-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Co-authored-by: marigauge Co-authored-by: MD360 <74264906+MantasDab360@users.noreply.github.com> Co-authored-by: Katie Co-authored-by: turtlemutt Co-authored-by: Hamey Co-authored-by: Woljif <243318409+Woljif@users.noreply.github.com> Co-authored-by: snowywinters <134970424+snowywinters@users.noreply.github.com> Co-authored-by: biddygelson <4606269+Calis007@users.noreply.github.com> Co-authored-by: Stop-Signs Co-authored-by: Stxcking <217132821+Stxcking@users.noreply.github.com> Co-authored-by: DisposableCrewmember42 Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Co-authored-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Co-authored-by: Cepelinas1 Co-authored-by: wesker <53323309+weskified@users.noreply.github.com> Co-authored-by: pootslap <211648338+pootslap@users.noreply.github.com> Co-authored-by: Vape <76235372+Vapetastic-Gaming@users.noreply.github.com> Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com> Co-authored-by: SirrRequiem * Revert "One refactor to rule them all (#15)" This reverts commit b2a2613ad7719516c02ab3e0dbd3de5f7e3e3fad. * fix fails * THE LAST REFACTOR * Fix test fails * more test fails * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Test fails BE GONE * small fix * Remove Mime powers from Psionics * Adress Initial Reviews Co-Authored-By: BarryNorfolk <190395382+BarryNorfolk@users.noreply.github.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Fix csproj file Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * Fix the other Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * Adress other review * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Update Content.Shared.csproj Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * Remove CSProj change Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * Erase empty space Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * das * UNTOUCH THE GODDAMN FILE * untouch file Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> * Fix goidacode * Merge fixes * Adhere to the messaiah --------- Signed-off-by: Proxy also works :3 <98499019+Proxysseia@users.noreply.github.com> Signed-off-by: Halo3moth Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> Signed-off-by: Tobias Berger Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: BungleBoss <100389635+BunglesnortBoss@users.noreply.github.com> Signed-off-by: Charlie Morley Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Signed-off-by: Thingy461 Signed-off-by: Field Command <159087063+FieldCommand@users.noreply.github.com> Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> Signed-off-by: Madison Rye Progress Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Signed-off-by: keekee38 Signed-off-by: Stop-Signs Signed-off-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Signed-off-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Signed-off-by: BarryNorfolk Signed-off-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> Co-authored-by: Sir Warock <67167466+SirWarock@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Co-authored-by: turtlemutt Co-authored-by: Field Command <159087063+FieldCommand@users.noreply.github.com> Co-authored-by: Delta-V bot <135767721+DeltaV-Bot@users.noreply.github.com> Co-authored-by: SirrRequiem Co-authored-by: biddygelson <4606269+Calis007@users.noreply.github.com> Co-authored-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy Co-authored-by: HTML/Crystal <152909599+HTMLSystem@users.noreply.github.com> Co-authored-by: ConstantlyConfused <67894661+Eternally-Confused@users.noreply.github.com> Co-authored-by: IProduceWidgets <107586145+IProduceWidgets@users.noreply.github.com> Co-authored-by: Proxy also works :3 <98499019+Proxysseia@users.noreply.github.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: Dorragon <101672978+Dorragon@users.noreply.github.com> Co-authored-by: Halo3moth Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: DeltaV-Bot Co-authored-by: Stxcking Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: Cepelinas1 Co-authored-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Co-authored-by: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com> Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com> Co-authored-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Co-authored-by: Charlie Morley Co-authored-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Co-authored-by: verybigman <171044200+verybigman311@users.noreply.github.com> Co-authored-by: Tobias Berger Co-authored-by: BungleBoss <100389635+BunglesnortBoss@users.noreply.github.com> Co-authored-by: MD360 <74264906+MantasDab360@users.noreply.github.com> Co-authored-by: snowywinters <134970424+snowywinters@users.noreply.github.com> Co-authored-by: Colin-Tel <113523727+Colin-Tel@users.noreply.github.com> Co-authored-by: Minerva <218184747+mnva0@users.noreply.github.com> Co-authored-by: Samuka <47865393+Samuka-C@users.noreply.github.com> Co-authored-by: SnappingOpossum Co-authored-by: BarryNorfolk Co-authored-by: Hohenzolaren <153686236+ReWAFFlution@users.noreply.github.com> Co-authored-by: ReWAFFlution <239567049+brokendot@users.noreply.github.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: Jessey van Hoeijen <114107545+LMSNoise@users.noreply.github.com> Co-authored-by: Stefano Pigozzi Co-authored-by: Connor Huffine Co-authored-by: YoungThug Co-authored-by: CDWimmer <31413434+CDWimmer@users.noreply.github.com> Co-authored-by: Errant <35878406+Errant-4@users.noreply.github.com> Co-authored-by: Alzore <140123969+Blackern5000@users.noreply.github.com> Co-authored-by: imatsoup <93290208+imatsoup@users.noreply.github.com> Co-authored-by: Pok <113675512+Pok27@users.noreply.github.com> Co-authored-by: alexalexmax <149889301+alexalexmax@users.noreply.github.com> Co-authored-by: NoreUhh <85219416+NoreUhh@users.noreply.github.com> Co-authored-by: NoreUhh Co-authored-by: JackspajfMain <105893899+JackspajfMain@users.noreply.github.com> Co-authored-by: Minemoder5000 Co-authored-by: āda Co-authored-by: iaada Co-authored-by: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Princess-Cheeseballs@users.noreply.github.com> Co-authored-by: Princess Cheeseballs <66055347+Pronana@users.noreply.github.com> Co-authored-by: Ignaz "Ian" Kraft Co-authored-by: Noah Beverly Co-authored-by: Justin Pfeifler Co-authored-by: Milon Co-authored-by: Astra <226853568+EmberAstra@users.noreply.github.com> Co-authored-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Co-authored-by: Thingy461 Co-authored-by: Theo Co-authored-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> Co-authored-by: keekee38 Co-authored-by: Madison Rye Progress Co-authored-by: Vanessa Co-authored-by: marigauge Co-authored-by: Katie Co-authored-by: Hamey Co-authored-by: Woljif <243318409+Woljif@users.noreply.github.com> Co-authored-by: Stop-Signs Co-authored-by: Stxcking <217132821+Stxcking@users.noreply.github.com> Co-authored-by: DisposableCrewmember42 Co-authored-by: wesker <53323309+weskified@users.noreply.github.com> Co-authored-by: pootslap <211648338+pootslap@users.noreply.github.com> Co-authored-by: Vape <76235372+Vapetastic-Gaming@users.noreply.github.com> Co-authored-by: BarryNorfolk <190395382+BarryNorfolk@users.noreply.github.com> --- .../Psionics/TelegnosisPowerSystem.cs | 5 - .../Chat/PsionicChatUpdateSystem.cs | 2 +- .../Psionics/UI/AcceptPsionicsEUI.cs | 42 --- .../Psionics/UI/AcceptPsionicsWindow.cs | 62 ---- .../Psionics/FracturedFormPowerSystem.cs | 5 - .../PsionicPowers/DispelPowerSystem.cs | 8 + .../PsionicPowers/FracturedFormPowerSystem.cs | 6 + .../PsionicPowers/MassSleepPowerSystem.cs | 15 + .../PsionicPowers/MindSwapPowerSystem.cs | 8 + .../PsionicPowers/NoosphericZapPowerSystem.cs | 15 + .../PsionicPowers/PrecognitionPowerSystem.cs | 8 + .../PsionicPowers/PyrokinesisPowerSystem.cs | 14 + .../PsionicPowers/TelegnosisPowerSystem.cs | 16 + .../_DV/Psionics/Systems/PsionicSystem.cs | 5 + .../_DV/Psionics/UI/AcceptPsionicsEUI.cs | 36 ++ .../_DV/Psionics/UI/AcceptPsionicsWindow.cs | 45 +++ .../Anomaly/AnomalySystem.Psionics.cs | 27 -- Content.Server/Anomaly/AnomalySystem.cs | 2 - Content.Server/Cloning/CloningPodSystem.cs | 2 +- .../Effects/MakeSentientEntityEffectSystem.cs | 2 +- .../Psionics/Abilities/DispelPowerSystem.cs | 150 -------- .../Abilities/MetapsionicPowerSystem.cs | 68 ---- .../Psionics/Abilities/MindSwapPowerSystem.cs | 245 ------------- .../Abilities/NoosphericZapPowerSystem.cs | 62 ---- .../PsionicInvisibilityPowerSystem.cs | 139 ------- .../PsionicRegenerationPowerSystem.cs | 129 ------- .../Abilities/PyrokinesisPowerSystem.cs | 69 ---- .../Abilities/TelegnosisPowerSystem.cs | 115 ------ .../Psionics/PsionicAbilitiesSystem.cs | 135 ------- .../GlimmerMonitorCartridgeComponent.cs | 3 +- .../Nyanotrasen/Chat/NyanoChatSystem.cs | 203 +++++----- .../Chat/TelepathicRepeaterComponent.cs | 11 - .../Systems/BecomePsionicConditionSystem.cs | 1 + .../Nyanotrasen/Psionics/AcceptPsionicsEui.cs | 34 -- .../Psionics/AntiPsychicWeaponComponent.cs | 24 -- .../Psionics/Glimmer/GlimmerReactiveSystem.cs | 3 - .../Structures/GlimmerSourceComponent.cs | 41 +-- .../Structures/GlimmerStructuresSystem.cs | 129 +++---- .../Invisibility/PsionicInvisibilitySystem.cs | 142 ------- .../PsionicInvisibleContactsComponent.cs | 19 - .../PsionicInvisibleContactsSystem.cs | 70 ---- .../PsionicallyInvisibleComponent.cs | 6 - .../Psionics/NPC/PsionicNpcCombatSystem.cs | 5 +- .../Psionics/PotentialPsionicComponent.cs | 15 - .../PsionicAwaitingPlayerComponent.cs | 9 - .../Psionics/PsionicBonusChanceComponent.cs | 18 - .../Nyanotrasen/Psionics/PsionicsCommands.cs | 14 +- .../Nyanotrasen/Psionics/PsionicsSystem.cs | 190 ---------- .../Research/Oracle/OracleComponent.cs | 2 +- .../Research/Oracle/OracleSystem.cs | 64 ++-- .../SophicScribe/SophicScribeSystem.cs | 8 +- .../Components/GlimmerEventComponent.cs | 8 +- .../Components/MassMindSwapRuleComponent.cs | 2 +- .../Components/NoosphericFryRuleComponent.cs | 9 - .../NoosphericStormRuleComponent.cs | 30 -- .../Components/NoosphericZapRuleComponent.cs | 9 - .../PsionicCatGotYourTongueRuleComponent.cs | 18 - .../Events/GlimmerEventSystem.cs | 37 +- .../StationEvents/Events/MassMindSwapRule.cs | 40 +- .../StationEvents/Events/NoosphericFryRule.cs | 131 ------- .../StationEvents/Events/NoosphericZapRule.cs | 55 --- .../Events/PsionicCatGotYourTongueRule.cs | 52 --- .../Zombies/ZombieSystem.Transform.cs | 21 +- .../Psionics/FracturedFormPowerSystem.cs | 346 ------------------ .../Psionics/PrecognitionPowerSystem.cs | 248 ------------- .../PsionicEruptionComponent.cs | 29 -- .../PsionicEruption/PsionicEruptionSystem.cs | 235 ------------ .../PsychokineticScreamPowerSystem.cs | 63 ---- .../Abilities/ShatterLightsAbilitySystem.cs | 90 ----- .../_DV/Chapel/SacrificialAltarSystem.cs | 2 +- ...emovePsionicAbilitiesEntityEffectSystem.cs | 23 +- ...erollPsionicAbilitiesEntityEffectSystem.cs | 24 -- .../RollPsionicAbilityEntityEffectSystem.cs | 19 + .../Rules/ParadoxCloneRuleSystem.DeltaV.cs | 7 +- .../_DV/Light/BreakLightsOnSpawnSystem.cs | 6 +- .../PsionicPowers/DispelPowerSystem.cs | 36 ++ .../PsionicPowers/FracturedFormPowerSystem.cs | 212 +++++++++++ .../PsionicPowers/MassSleepPowerSystem.cs | 82 +++++ .../PsionicPowers/MindSwapPowerSystem.cs | 30 ++ .../PsionicPowers/NoosphericZapPowerSystem.cs | 24 ++ .../PsionicPowers/PrecognitionPowerSystem.cs | 194 ++++++++++ .../PsionicEruptionPowerSystem.cs | 194 ++++++++++ .../PsionicPowers/PyrokinesisPowerSystem.cs | 25 ++ .../PsionicPowers/TelegnosisPowerSystem.cs | 71 ++++ .../_DV/Psionics/Systems/PsionicSystem.cs | 27 ++ .../Systems/SubSystems/PsionicSystem.Items.cs | 44 +++ .../_DV/Psionics/UI/AcceptPsionicsEui.cs | 25 ++ .../Psionics/{ => UI}/EruptionWarningEui.cs | 0 .../Components/DebrisSpawnerRuleComponent.cs | 6 +- .../Components/FugitiveRuleComponent.cs | 5 +- .../Components/GlimmerMobRuleComponent.cs | 2 +- .../Components/LoadFarGridRuleComponent.cs | 4 +- .../Components/LockProbersRuleComponent.cs | 6 +- .../Components/MeteorSwarmRuleComponent.cs | 4 +- .../MinorMassMindSwapRuleComponent.cs | 2 +- .../Components/NoosphericFryRuleComponent.cs | 25 ++ .../NoosphericSilenceRuleComponent.cs | 13 + .../NoosphericStormRuleComponent.cs | 44 +++ .../Components/NoosphericZapRuleComponent.cs | 6 + .../PsionicNosebleedRuleComponent.cs | 2 +- .../RandomAnimationRuleComponent.cs | 2 +- .../ThavenMoodUpsetRuleComponent.cs | 2 +- .../DebrisSpawnerRule.cs | 9 +- .../{Events => GameRules}/EpsilonEventRule.cs | 0 .../{Events => GameRules}/FugitiveRule.cs | 9 +- .../GlimmerFoxfireSpawnRule.cs | 0 .../GlimmerMobSpawnRule.cs | 6 +- .../GlimmerRestyleRule.cs | 14 +- .../{Events => GameRules}/LoadFarGridRule.cs | 5 +- .../{Events => GameRules}/LockProbersRule.cs | 4 +- .../{Events => GameRules}/MeteorSwarmRule.cs | 5 +- .../MinorMassMindSwapRule.cs | 34 +- .../GameRules/NoosphericFryRule.cs | 87 +++++ .../GameRules/NoosphericSilenceRule.cs | 49 +++ .../GameRules}/NoosphericStormRule.cs | 41 +-- .../GameRules/NoosphericZapRule.cs | 64 ++++ .../PsionicNosebleedRule.cs | 21 +- .../RandomAnimationRule.cs | 2 +- .../RandomMultipleSpawnRule.cs | 4 +- .../{Events => GameRules}/ThavenMoodUpset.cs | 2 +- .../NextEvent/NextEventSystem.cs | 2 +- .../PsionicProducingArtifactComponent.cs | 22 -- .../Effects/Systems/GlimmerArtifactSystem.cs | 27 -- .../Systems/PsionicProducingArtifactSystem.cs | 53 --- .../ArtifactMetapsionicTriggerComponent.cs | 8 - .../ArtifactMetapsionicTriggerSystem.cs | 57 --- .../Abilities/Mime/MimePowersSystem.cs | 10 - Content.Shared/Anomaly/SharedAnomalySystem.cs | 4 +- .../Inventory/InventorySystem.Relay.cs | 9 +- .../Dispel/DamageOnDispelComponent.cs | 17 - .../Abilities/Dispel/DispelPowerComponent.cs | 20 - .../Abilities/Dispel/DispellableComponent.cs | 6 - .../MassSleep/MassSleepPowerComponent.cs | 30 -- .../MassSleep/MassSleepPowerSystem.cs | 105 ------ .../Metapsionics/MetapsionicPowerComponent.cs | 22 -- .../MindSwap/MindSwapPowerComponent.cs | 14 - .../NoosphericZapPowerComponent.cs | 17 - .../PsionicInvisibilityPowerComponent.cs | 16 - .../PsionicInvisibilityUsedComponent.cs | 17 - .../PsionicRegenerationPowerComponent.cs | 31 -- .../Pyrokinesis/PyrokinesisPowerComponent.cs | 19 - .../Telegnosis/SharedTelegnosisPowerSystem.cs | 29 -- .../Telegnosis/TelegnosisPowerComponent.cs | 25 -- .../TelegnosticProjectionComponent.cs | 4 - .../ClothingGrantPsionicPowerComponent.cs | 10 - .../Psionics/Items/PsionicItemsSystem.cs | 81 ---- .../Psionics/Items/TinfoilHatComponent.cs | 17 - .../Abilities/Psionics/PsionicComponent.cs | 20 - .../Psionics/PsionicInsulationComponent.cs | 10 - .../Psionics/PsionicsDisabledComponent.cs | 11 - .../Psionics/SharedPsionicAbilitiesSystem.cs | 124 ------- .../Actions/Events/DispelPowerActionEvent.cs | 2 - .../Events/MassSleepPowerActionEvent.cs | 2 - .../Events/MetapsionicPowerActionEvent.cs | 2 - .../Events/MindSwapPowerActionEvent.cs | 3 - .../Events/NoosphericZapPowerActionEvent.cs | 2 - .../Events/PsionicEruptionPowerActionEvent.cs | 3 - .../PsionicInvisibilityPowerActionEvent.cs | 2 - .../PsionicRegenerationPowerActionEvent.cs | 2 - .../Events/PyrokinesisPowerActionEvent.cs | 2 - ...ePsionicInvisibilityOffPowerActionEvent.cs | 2 - .../Events/TelegnosisPowerActionEvent.cs | 2 - .../StatusEffectSystem.Relay.cs | 6 + .../Artifact/SharedXenoArtifactSystem.Node.cs | 2 +- .../Artifact/SharedXenoArtifactSystem.XAT.cs | 4 +- .../Psionics/FracturedFormPowerComponent.cs | 42 --- .../Psionics/MindSwappedComponent.cs | 16 - .../Psionics/PrecognitionPowerComponent.cs | 31 -- .../SharedFracturedFormPowerSystem.cs | 19 - .../ShatterLightsAbilityComponent.cs | 54 --- .../Events/FracturedFormPowerActionEvent.cs | 3 - .../Anomaly/SharedAnomalySystem.Psionics.cs | 27 ++ .../Components/TelepathicRepeaterComponent.cs | 9 + ...t.cs => RollPsionicAbilityEntityEffect.cs} | 6 +- .../Systems/MovementModStatusSystem.cs | 11 - .../Components/AntiPsionicWeaponComponent.cs | 45 +++ .../Components/DamageOnDispelComponent.cs | 31 ++ .../Components/DispellableComponent.cs | 14 + .../Components}/FracturedFormBodyComponent.cs | 4 +- .../Components/PotentialPsionicComponent.cs | 46 +++ .../PrecognitionResultComponent.cs | 3 + .../Psionics/Components/PsionicComponent.cs | 23 ++ .../PsionicInvisibilityUsedComponent.cs | 23 ++ .../PsionicPowerDetectorComponent.cs | 17 + .../BasePsionicPowerComponent.cs | 99 +++++ .../PsionicPowers/DispelPowerComponent.cs | 16 + .../FracturedFormPowerComponent.cs | 124 +++++++ .../PsionicPowers/MassSleepPowerComponent.cs | 40 ++ .../MetapsionicPulsePowerComponent.cs | 22 ++ .../PsionicPowers/MindSwapPowerComponent.cs | 16 + .../MindSwappedReturnPowerComponent.cs | 19 + .../NoosphericZapPowerComponent.cs | 34 ++ .../PrecognitionPowerComponent.cs | 59 +++ .../PsionicEruptionPowerComponent.cs | 49 +++ .../PsionicInvisibilityPowerComponent.cs | 16 + .../PsionicRegenerationPowerComponent.cs | 41 +++ .../PsychokineticScreamPowerComponent.cs | 27 +- .../PyrokinesisPowerComponent.cs | 22 ++ .../PsionicPowers/TelegnosisPowerComponent.cs | 23 ++ .../PsionicallyInsulativeComponent.cs | 43 +++ .../PsionicallyInvisibleComponent.cs | 17 + .../Components/PsionicsDisabledComponent.cs | 10 + .../ShieldedFromPsionicsComponent.cs | 10 + .../TelegnosticProjectionComponent.cs | 7 + Content.Shared/_DV/Psionics/Events.cs | 12 - .../_DV/Psionics/Events/DispelledEvent.cs | 18 + .../_DV/Psionics/Events/NoosphericFryEvent.cs | 18 + .../DispelPowerActionEvent.cs | 5 + .../FracturedFormPowerActionEvent.cs | 5 + .../MassSleepPowerActionEvent.cs | 5 + .../MetapsionicPulsePowerActionEvent.cs | 9 + .../MindSwapPowerActionEvent.cs | 7 + .../NoosphericZapPowerActionEvent.cs | 5 + .../PsionicEruptionPowerActionEvent.cs | 9 + .../PsionicInvisibilityPowerActionEvent.cs | 5 + .../PsionicRegenerationPowerActionEvent.cs | 5 + .../PsychokineticScreamPowerActionEvent.cs | 9 + .../PyrokinesisPowerActionEvent.cs | 5 + .../TelegnosisPowerActionEvent.cs | 5 + .../FracturedFormDoAfterEvent.cs | 7 + .../MassSleepDoAfterEvent.cs | 7 + .../PrecognitionDoAfterEvent.cs | 7 + .../PsionicEruptionDoAfterEvent.cs | 7 + .../Psionics/Events/PsionicMindBrokenEvent.cs | 23 ++ .../Events/PsionicPowerDetectedEvent.cs | 9 + .../Events/PsionicPowerUseAttemptEvent.cs | 15 + .../Psionics/Events/PsionicPowerUsedEvent.cs | 14 + .../Psionics/Events/PsionicShieldedEvent.cs | 8 + .../Events/PsionicStoppedShieldedEvent.cs | 8 + .../Events/PsionicStoppedSuppressedEvent.cs | 8 + .../Psionics/Events/PsionicSuppressedEvent.cs | 8 + .../Events/TargetedByPsionicPowerEvent.cs | 15 + .../PsionicPowers/BasePsionicPowerSystem.cs | 208 +++++++++++ .../MetapsionicPulsePowerSystem.cs | 55 +++ .../MindSwappedReturnPowerSystem.cs | 76 ++++ .../PsionicInvisibilityPowerSystem.cs | 87 +++++ .../PsionicRegenerationPowerSystem.cs | 69 ++++ .../PsychokineticScreamPowerSystem.cs | 75 ++++ .../PsionicPowers/SharedDispelPowerSystem.cs | 139 +++++++ .../SharedFracturedFormPowerSystem.cs | 150 ++++++++ .../SharedMassSleepPowerSystem.cs | 6 + .../SharedMindSwapPowerSystem.cs | 167 +++++++++ .../SharedNoosphericZapPowerSystem.cs | 9 + .../SharedPrecognitionPowerSystem.cs | 51 +++ .../SharedPyrokinesisPowerSystem.cs | 6 + .../SharedTelegnosisPowerSystem.cs | 59 +++ .../Psionics/Systems/SharedPsionicSystem.cs | 130 +++++++ .../SharedPsionicSystem.Detection.cs | 54 +++ .../SubSystems/SharedPsionicSystem.Effects.cs | 65 ++++ .../SharedPsionicSystem.Invisibility.cs | 103 ++++++ .../SubSystems/SharedPsionicSystem.Items.cs | 124 +++++++ .../SubSystems/SharedPsionicSystem.Rolling.cs | 101 +++++ .../_DV/Roles/ModifyPsionicChanceSpecial.cs | 30 ++ .../Events/GlimmerEventEndedEvent.cs | 14 + .../Components/XAEDetailsComponent.cs | 5 +- .../Components/XAEModifyGlimmerComponent.cs | 8 +- .../Components/XAEPsionicInducerComponent.cs | 16 + .../Effects/Systems/XAEModifyGlimmerSystem.cs | 27 ++ .../Systems/XAEPsionicInducerSystem.cs | 33 ++ .../Components/XATPsionicUsageComponent.cs | 8 + .../Triggers/Systems/XATPsionicUsageSystem.cs | 44 +++ .../Locale/en-US/_DV/abilities/psionic.ftl | 67 +++- .../Locale/en-US/_DV/chemistry/effects.ftl | 12 + .../en-US/_DV/reagents/meta/biological.ftl | 6 + .../en-US/_DV/reagents/meta/medicine.ftl | 3 + .../en-US/_DV/reagents/meta/narcotic.ftl | 2 + .../Locale/en-US/_DV/reagents/meta/toxins.ftl | 3 + .../en-US/nyanotrasen/abilities/psionic.ftl | 7 - .../en-US/nyanotrasen/chemistry/effects.ftl | 10 - .../en-US/nyanotrasen/reagents/toxins.ftl | 3 - .../Clothing/Head/hardsuit-helmets.yml | 8 +- .../Entities/Mobs/NPCs/regalrat.yml | 1 - .../Entities/Mobs/NPCs/revenant.yml | 1 + .../Prototypes/Entities/Mobs/NPCs/xeno.yml | 3 - .../Entities/Mobs/Player/familiars.yml | 2 - .../Prototypes/Entities/Mobs/Player/human.yml | 10 +- .../Entities/Mobs/Player/humanoid.yml | 5 +- .../Prototypes/Entities/Mobs/Species/base.yml | 2 +- .../Xenoarchaeology/xenoartifacts.yml | 3 +- .../Clothing/Head/hardsuit-helmets.yml | 8 +- .../Entities/Clothing/Head/hats.yml | 14 +- .../Clothing/OuterClothing/hardsuits.yml | 3 +- .../Entities/Mobs/Player/special.yml | 4 +- .../Objects/Specific/Medical/pills.yml | 14 - .../Entities/Objects/Weapons/Melee/knives.yml | 13 +- .../Structures/Research/sophicscribe.yml | 1 - .../Nyanotrasen/Reagents/psionic.yml | 29 -- .../Roles/Jobs/Epistemics/forensicmantis.yml | 3 +- .../Prototypes/Nyanotrasen/psionicPowers.yml | 15 - .../Prototypes/Nyanotrasen/status_effects.yml | 5 - Resources/Prototypes/Reagents/medicine.yml | 9 - Resources/Prototypes/Reagents/narcotics.yml | 2 +- .../Roles/Jobs/Cargo/quartermaster.yml | 4 + .../Roles/Jobs/Civilian/chaplain.yml | 4 +- .../Prototypes/Roles/Jobs/Command/captain.yml | 4 +- .../Roles/Jobs/Command/head_of_personnel.yml | 4 +- .../Roles/Jobs/Engineering/chief_engineer.yml | 4 +- .../Jobs/Medical/chief_medical_officer.yml | 4 +- .../Roles/Jobs/Science/scientist.yml | 7 +- .../Roles/Jobs/Security/head_of_security.yml | 4 +- Resources/Prototypes/XenoArch/triggers.yml | 2 +- Resources/Prototypes/_DV/Actions/psionic.yml | 32 +- Resources/Prototypes/_DV/Actions/skia.yml | 9 +- .../Clothing/Head/hardsuit-helmets.yml | 11 +- .../Clothing/Psionic/psionic_clothing.yml | 12 +- .../_DV/Entities/Effects/psionic.yml | 29 ++ .../_DV/Entities/Effects/puddles.yml | 2 + .../_DV/Entities/Mobs/NPCs/familiars.yml | 3 +- .../Prototypes/_DV/Entities/Mobs/NPCs/fun.yml | 2 +- .../Entities/Mobs/NPCs/glimmer_creatures.yml | 4 +- .../_DV/Entities/Mobs/NPCs/skia.yml | 5 +- .../_DV/Entities/Mobs/Player/kitsune.yml | 2 + .../_DV/Entities/Mobs/Species/kitsune.yml | 2 - .../Objects/Specific/Medical/pills.yml | 13 + .../_DV/Entities/StatusEffects/movement.yml | 4 - .../_DV/Entities/StatusEffects/psionics.yml | 35 ++ .../_DV/GameRules/glimmer_events.yml | 6 +- .../Prototypes/_DV/Psionics/psionicPowers.yml | 94 +++++ .../Prototypes/_DV/Reagents/biological.yml | 36 ++ .../Prototypes/_DV/Reagents/medicine.yml | 21 ++ .../Prototypes/_DV/Reagents/narcotics.yml | 28 ++ Resources/Prototypes/_DV/Reagents/toxins.yml | 14 + .../_DV/Recipes/Reactions/medicine.yml | 10 + .../_DV/Roles/Jobs/Justice/chief_justice.yml | 6 +- Resources/Prototypes/_DV/XenoArch/effects.yml | 8 +- .../Prototypes/_DV/XenoArch/triggers.yml | 5 +- 326 files changed, 5398 insertions(+), 4863 deletions(-) delete mode 100644 Content.Client/Nyanotrasen/Abilities/Psionics/TelegnosisPowerSystem.cs delete mode 100644 Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsEUI.cs delete mode 100644 Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsWindow.cs delete mode 100644 Content.Client/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs create mode 100644 Content.Client/_DV/Psionics/Systems/PsionicSystem.cs create mode 100644 Content.Client/_DV/Psionics/UI/AcceptPsionicsEUI.cs create mode 100644 Content.Client/_DV/Psionics/UI/AcceptPsionicsWindow.cs delete mode 100644 Content.Server/Anomaly/AnomalySystem.Psionics.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/DispelPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Abilities/Psionics/PsionicAbilitiesSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Chat/TelepathicRepeaterComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/AcceptPsionicsEui.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/AntiPsychicWeaponComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibilitySystem.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsSystem.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicallyInvisibleComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/PotentialPsionicComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/PsionicAwaitingPlayerComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/PsionicBonusChanceComponent.cs delete mode 100644 Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Components/NoosphericFryRuleComponent.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Components/NoosphericStormRuleComponent.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Components/NoosphericZapRuleComponent.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Components/PsionicCatGotYourTongueRuleComponent.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Events/NoosphericFryRule.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs delete mode 100644 Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs delete mode 100644 Content.Server/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs delete mode 100644 Content.Server/_DV/Abilities/Psionics/PrecognitionPowerSystem.cs delete mode 100644 Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionComponent.cs delete mode 100644 Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionSystem.cs delete mode 100644 Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs delete mode 100644 Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs delete mode 100644 Content.Server/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffectSystem.cs create mode 100644 Content.Server/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffectSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/PsionicEruptionPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/PsionicSystem.cs create mode 100644 Content.Server/_DV/Psionics/Systems/SubSystems/PsionicSystem.Items.cs create mode 100644 Content.Server/_DV/Psionics/UI/AcceptPsionicsEui.cs rename Content.Server/_DV/Psionics/{ => UI}/EruptionWarningEui.cs (100%) create mode 100644 Content.Server/_DV/StationEvents/Components/NoosphericFryRuleComponent.cs create mode 100644 Content.Server/_DV/StationEvents/Components/NoosphericSilenceRuleComponent.cs create mode 100644 Content.Server/_DV/StationEvents/Components/NoosphericStormRuleComponent.cs create mode 100644 Content.Server/_DV/StationEvents/Components/NoosphericZapRuleComponent.cs rename Content.Server/_DV/StationEvents/{Events => GameRules}/DebrisSpawnerRule.cs (93%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/EpsilonEventRule.cs (100%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/FugitiveRule.cs (97%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/GlimmerFoxfireSpawnRule.cs (100%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/GlimmerMobSpawnRule.cs (96%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/GlimmerRestyleRule.cs (88%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/LoadFarGridRule.cs (94%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/LockProbersRule.cs (97%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/MeteorSwarmRule.cs (98%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/MinorMassMindSwapRule.cs (80%) create mode 100644 Content.Server/_DV/StationEvents/GameRules/NoosphericFryRule.cs create mode 100644 Content.Server/_DV/StationEvents/GameRules/NoosphericSilenceRule.cs rename Content.Server/{Nyanotrasen/StationEvents/Events => _DV/StationEvents/GameRules}/NoosphericStormRule.cs (51%) create mode 100644 Content.Server/_DV/StationEvents/GameRules/NoosphericZapRule.cs rename Content.Server/_DV/StationEvents/{Events => GameRules}/PsionicNosebleedRule.cs (62%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/RandomAnimationRule.cs (97%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/RandomMultipleSpawnRule.cs (85%) rename Content.Server/_DV/StationEvents/{Events => GameRules}/ThavenMoodUpset.cs (93%) delete mode 100644 Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs delete mode 100644 Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs delete mode 100644 Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs delete mode 100644 Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs delete mode 100644 Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DamageOnDispelComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispelPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispellableComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerSystem.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Metapsionics/MetapsionicPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MindSwap/MindSwapPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZap/NoosphericZapPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityUsedComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegeneration/PsionicRegenerationPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Pyrokinesis/PyrokinesisPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/SharedTelegnosisPowerSystem.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosisPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosticProjectionComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Items/ClothingGrantPsionicPowerComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Items/PsionicItemsSystem.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/Items/TinfoilHatComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicInsulationComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicsDisabledComponent.cs delete mode 100644 Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/DispelPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/MassSleepPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/MetapsionicPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/MindSwapPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/NoosphericZapPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/PsionicEruptionPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/PsionicInvisibilityPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/PsionicRegenerationPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/PyrokinesisPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/RemovePsionicInvisibilityOffPowerActionEvent.cs delete mode 100644 Content.Shared/Nyanotrasen/Actions/Events/TelegnosisPowerActionEvent.cs delete mode 100644 Content.Shared/_DV/Abilities/Psionics/FracturedFormPowerComponent.cs delete mode 100644 Content.Shared/_DV/Abilities/Psionics/MindSwappedComponent.cs delete mode 100644 Content.Shared/_DV/Abilities/Psionics/PrecognitionPowerComponent.cs delete mode 100644 Content.Shared/_DV/Abilities/Psionics/SharedFracturedFormPowerSystem.cs delete mode 100644 Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs delete mode 100644 Content.Shared/_DV/Actions/Events/FracturedFormPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Anomaly/SharedAnomalySystem.Psionics.cs create mode 100644 Content.Shared/_DV/Chat/Components/TelepathicRepeaterComponent.cs rename Content.Shared/_DV/EntityEffects/Effects/Psionics/{RerollPsionicAbilitiesEntityEffect.cs => RollPsionicAbilityEntityEffect.cs} (63%) delete mode 100644 Content.Shared/_DV/Movement/Systems/MovementModStatusSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Components/AntiPsionicWeaponComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/DamageOnDispelComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/DispellableComponent.cs rename Content.Shared/_DV/{Abilities/Psionics => Psionics/Components}/FracturedFormBodyComponent.cs (70%) create mode 100644 Content.Shared/_DV/Psionics/Components/PotentialPsionicComponent.cs rename Content.Shared/_DV/{Abilities/Psionics => Psionics/Components}/PrecognitionResultComponent.cs (77%) create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicInvisibilityUsedComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowerDetectorComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/BasePsionicPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/DispelPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/FracturedFormPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/MassSleepPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/MetapsionicPulsePowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwapPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwappedReturnPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/NoosphericZapPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/PrecognitionPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicEruptionPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicInvisibilityPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicRegenerationPowerComponent.cs rename Content.Shared/_DV/{Abilities/Psionics => Psionics/Components/PsionicPowers}/PsychokineticScreamPowerComponent.cs (63%) create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/PyrokinesisPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicPowers/TelegnosisPowerComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicallyInsulativeComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicallyInvisibleComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/PsionicsDisabledComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/ShieldedFromPsionicsComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Components/TelegnosticProjectionComponent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/DispelledEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/NoosphericFryEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/DispelPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/FracturedFormPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/MassSleepPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/MetapsionicPulsePowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/MindSwapPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/NoosphericZapPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicEruptionPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicInvisibilityPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicRegenerationPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsychokineticScreamPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/PyrokinesisPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerActionEvents/TelegnosisPowerActionEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/FracturedFormDoAfterEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/MassSleepDoAfterEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PrecognitionDoAfterEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PsionicEruptionDoAfterEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicMindBrokenEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicPowerDetectedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicPowerUseAttemptEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicPowerUsedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicShieldedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicStoppedShieldedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicStoppedSuppressedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/PsionicSuppressedEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Events/TargetedByPsionicPowerEvent.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/BasePsionicPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/MetapsionicPulsePowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/MindSwappedReturnPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicInvisibilityPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicRegenerationPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsychokineticScreamPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedDispelPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedFracturedFormPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMassSleepPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMindSwapPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedNoosphericZapPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPrecognitionPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPyrokinesisPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedTelegnosisPowerSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SharedPsionicSystem.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Detection.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Effects.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Invisibility.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Items.cs create mode 100644 Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Rolling.cs create mode 100644 Content.Shared/_DV/Roles/ModifyPsionicChanceSpecial.cs create mode 100644 Content.Shared/_DV/StationEvents/Events/GlimmerEventEndedEvent.cs rename Content.Shared/_DV/Xenoarcheology/{Artifact => XenoArtifacts/Effects}/Components/XAEDetailsComponent.cs (82%) rename Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs => Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEModifyGlimmerComponent.cs (64%) create mode 100644 Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEPsionicInducerComponent.cs create mode 100644 Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEModifyGlimmerSystem.cs create mode 100644 Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEPsionicInducerSystem.cs create mode 100644 Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Components/XATPsionicUsageComponent.cs create mode 100644 Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Systems/XATPsionicUsageSystem.cs create mode 100644 Resources/Locale/en-US/_DV/chemistry/effects.ftl create mode 100644 Resources/Locale/en-US/_DV/reagents/meta/narcotic.ftl delete mode 100644 Resources/Prototypes/Nyanotrasen/psionicPowers.yml delete mode 100644 Resources/Prototypes/Nyanotrasen/status_effects.yml create mode 100644 Resources/Prototypes/_DV/Entities/Effects/psionic.yml create mode 100644 Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/pills.yml delete mode 100644 Resources/Prototypes/_DV/Entities/StatusEffects/movement.yml create mode 100644 Resources/Prototypes/_DV/Entities/StatusEffects/psionics.yml create mode 100644 Resources/Prototypes/_DV/Psionics/psionicPowers.yml create mode 100644 Resources/Prototypes/_DV/Reagents/narcotics.yml create mode 100644 Resources/Prototypes/_DV/Reagents/toxins.yml diff --git a/Content.Client/Nyanotrasen/Abilities/Psionics/TelegnosisPowerSystem.cs b/Content.Client/Nyanotrasen/Abilities/Psionics/TelegnosisPowerSystem.cs deleted file mode 100644 index 8ddc15347c..0000000000 --- a/Content.Client/Nyanotrasen/Abilities/Psionics/TelegnosisPowerSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared.Abilities.Psionics; - -namespace Content.Client.Abilities.Psionics; - -public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem; diff --git a/Content.Client/Nyanotrasen/Chat/PsionicChatUpdateSystem.cs b/Content.Client/Nyanotrasen/Chat/PsionicChatUpdateSystem.cs index 84602052fe..13a89a5e33 100644 --- a/Content.Client/Nyanotrasen/Chat/PsionicChatUpdateSystem.cs +++ b/Content.Client/Nyanotrasen/Chat/PsionicChatUpdateSystem.cs @@ -1,5 +1,5 @@ -using Content.Shared.Abilities.Psionics; using Content.Client.Chat.Managers; +using Content.Shared._DV.Psionics.Components; using Robust.Client.Player; namespace Content.Client.Nyanotrasen.Chat diff --git a/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsEUI.cs b/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsEUI.cs deleted file mode 100644 index 87d11a92ee..0000000000 --- a/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsEUI.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Content.Client.Eui; -using Content.Shared.Psionics; -using JetBrains.Annotations; -using Robust.Client.Graphics; - -namespace Content.Client.Psionics.UI -{ - [UsedImplicitly] - public sealed class AcceptPsionicsEui : BaseEui - { - private readonly AcceptPsionicsWindow _window; - - public AcceptPsionicsEui() - { - _window = new AcceptPsionicsWindow(); - - _window.DenyButton.OnPressed += _ => - { - SendMessage(new AcceptPsionicsChoiceMessage(AcceptPsionicsUiButton.Deny)); - _window.Close(); - }; - - _window.AcceptButton.OnPressed += _ => - { - SendMessage(new AcceptPsionicsChoiceMessage(AcceptPsionicsUiButton.Accept)); - _window.Close(); - }; - } - - public override void Opened() - { - IoCManager.Resolve().RequestWindowAttention(); - _window.OpenCentered(); - } - - public override void Closed() - { - _window.Close(); - } - - } -} diff --git a/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsWindow.cs b/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsWindow.cs deleted file mode 100644 index 883d9f0797..0000000000 --- a/Content.Client/Nyanotrasen/Psionics/UI/AcceptPsionicsWindow.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.Numerics; -using Robust.Client.UserInterface; -using Robust.Client.UserInterface.Controls; -using Robust.Client.UserInterface.CustomControls; -using Robust.Shared.Localization; -using static Robust.Client.UserInterface.Controls.BoxContainer; - -namespace Content.Client.Psionics.UI -{ - public sealed class AcceptPsionicsWindow : DefaultWindow - { - public readonly Button DenyButton; - public readonly Button AcceptButton; - - public AcceptPsionicsWindow() - { - - Title = Loc.GetString("accept-psionics-window-title"); - - Contents.AddChild(new BoxContainer - { - Orientation = LayoutOrientation.Vertical, - Children = - { - new BoxContainer - { - Orientation = LayoutOrientation.Vertical, - Children = - { - (new Label() - { - Text = Loc.GetString("accept-psionics-window-prompt-text-part") - }), - new BoxContainer - { - Orientation = LayoutOrientation.Horizontal, - Align = AlignMode.Center, - Children = - { - (AcceptButton = new Button - { - Text = Loc.GetString("accept-cloning-window-accept-button"), - }), - - (new Control() - { - MinSize = new Vector2(20, 0) - }), - - (DenyButton = new Button - { - Text = Loc.GetString("accept-cloning-window-deny-button"), - }) - } - }, - } - }, - } - }); - } - } -} diff --git a/Content.Client/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs b/Content.Client/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs deleted file mode 100644 index 0150a49df6..0000000000 --- a/Content.Client/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs +++ /dev/null @@ -1,5 +0,0 @@ -using Content.Shared._DV.Abilities.Psionics; - -namespace Content.Client._DV.Abilities.Psionics; - -public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem; diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs new file mode 100644 index 0000000000..55ce55e57a --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This solely exist for client-side prediction. +/// +public sealed class DispelPowerSystem : SharedDispelPowerSystem; diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs new file mode 100644 index 0000000000..73959e2dc6 --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs @@ -0,0 +1,6 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +// This does nothing here. The code is all in the shared/server version. +public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem; diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs new file mode 100644 index 0000000000..1f16a8ee40 --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs @@ -0,0 +1,15 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This solely exists for prediction. +/// +public sealed class MassSleepPowerSystem : SharedMassSleepPowerSystem +{ + protected override void OnPowerUsed(Entity psionic, ref MassSleepPowerActionEvent args) + { + } +} diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs new file mode 100644 index 0000000000..234d40a88c --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This solely exists for prediction. +/// +public sealed class MindSwapPowerSystem : SharedMindSwapPowerSystem; diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs new file mode 100644 index 0000000000..59ab44be7a --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs @@ -0,0 +1,15 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This is solely for prediction. +/// +public sealed class NoosphericZapPowerSystem : SharedNoosphericZapPowerSystem +{ + protected override void OnPowerUsed(Entity psionic, ref NoosphericZapPowerActionEvent args) + { + } +} diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs new file mode 100644 index 0000000000..bd19d6533a --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs @@ -0,0 +1,8 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This solely exists for prediction. +/// +public sealed class PrecognitionPowerSystem : SharedPrecognitionPowerSystem; diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs new file mode 100644 index 0000000000..7208968f7d --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs @@ -0,0 +1,14 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This exists solely for prediction. +/// +public sealed class PyrokinesisPowerSystem : SharedPyrokinesisPowerSystem +{ + protected override void OnPowerUsed(Entity psionic, ref Shared._DV.Psionics.Events.PowerActionEvents.PyrokinesisPowerActionEvent args) + { + } +} diff --git a/Content.Client/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs new file mode 100644 index 0000000000..36644fe534 --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs @@ -0,0 +1,16 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Client._DV.Psionics.Systems.PsionicPowers; + +/// +/// This is here solely for predictive handling of using the power and not being able to. +/// It'll send popups this way. +/// +public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem +{ + protected override void OnPowerUsed(Entity psionic, ref TelegnosisPowerActionEvent args) + { + } +} diff --git a/Content.Client/_DV/Psionics/Systems/PsionicSystem.cs b/Content.Client/_DV/Psionics/Systems/PsionicSystem.cs new file mode 100644 index 0000000000..eadc50aba3 --- /dev/null +++ b/Content.Client/_DV/Psionics/Systems/PsionicSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared._DV.Psionics.Systems; + +namespace Content.Client._DV.Psionics.Systems; + +public sealed class PsionicSystem : SharedPsionicSystem; diff --git a/Content.Client/_DV/Psionics/UI/AcceptPsionicsEUI.cs b/Content.Client/_DV/Psionics/UI/AcceptPsionicsEUI.cs new file mode 100644 index 0000000000..ebe1e1b8dd --- /dev/null +++ b/Content.Client/_DV/Psionics/UI/AcceptPsionicsEUI.cs @@ -0,0 +1,36 @@ +using Content.Client.Eui; +using Content.Shared.Psionics; +using JetBrains.Annotations; +using Robust.Client.Graphics; + +namespace Content.Client._DV.Psionics.UI; + +[UsedImplicitly] +public sealed class AcceptPsionicsEui : BaseEui +{ + private readonly AcceptPsionicsWindow _window; + + public AcceptPsionicsEui() + { + _window = new AcceptPsionicsWindow(); + _window.DenyButton.OnPressed += _ => + { + SendMessage(new AcceptPsionicsChoiceMessage(AcceptPsionicsUiButton.Deny)); + _window.Close(); + }; + _window.AcceptButton.OnPressed += _ => + { + SendMessage(new AcceptPsionicsChoiceMessage(AcceptPsionicsUiButton.Accept)); + _window.Close(); + }; + } + public override void Opened() + { + IoCManager.Resolve().RequestWindowAttention(); + _window.OpenCentered(); + } + public override void Closed() + { + _window.Close(); + } +} diff --git a/Content.Client/_DV/Psionics/UI/AcceptPsionicsWindow.cs b/Content.Client/_DV/Psionics/UI/AcceptPsionicsWindow.cs new file mode 100644 index 0000000000..21941b6acc --- /dev/null +++ b/Content.Client/_DV/Psionics/UI/AcceptPsionicsWindow.cs @@ -0,0 +1,45 @@ +using System.Numerics; +using Robust.Client.UserInterface; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using static Robust.Client.UserInterface.Controls.BoxContainer; + +namespace Content.Client._DV.Psionics.UI; + +public sealed class AcceptPsionicsWindow : DefaultWindow +{ + public readonly Button DenyButton; + public readonly Button AcceptButton; + + public AcceptPsionicsWindow() + { + Title = Loc.GetString("accept-psionics-window-title"); + Contents.AddChild(new BoxContainer + { + Orientation = LayoutOrientation.Vertical, Children = + { + new BoxContainer + { + Orientation = LayoutOrientation.Vertical, Children = + { + new Label() + { + Text = Loc.GetString("accept-psionics-window-prompt-text-part") + }, + new BoxContainer + { + Orientation = LayoutOrientation.Horizontal, Align = AlignMode.Center, Children = + { + (AcceptButton = new Button + { + Text = Loc.GetString("accept-cloning-window-accept-button"), + }), + new Control() { MinSize = new Vector2(20, 0) }, (DenyButton = new Button { Text = Loc.GetString("accept-cloning-window-deny-button"), }) + } + }, + } + }, + } + }); + } +} diff --git a/Content.Server/Anomaly/AnomalySystem.Psionics.cs b/Content.Server/Anomaly/AnomalySystem.Psionics.cs deleted file mode 100644 index 8aaf3aac6b..0000000000 --- a/Content.Server/Anomaly/AnomalySystem.Psionics.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server.Abilities.Psionics; -using Content.Shared._DV.CosmicCult; -using Content.Shared.Anomaly; -using Content.Shared.Anomaly.Components; -using Robust.Shared.Random; - -namespace Content.Server.Anomaly; - -public sealed partial class AnomalySystem -{ - [Dependency] private readonly DispelPowerSystem _dispel = default!; - - private void InitializePsionics() - { - SubscribeLocalEvent(OnDispelled); - } - - //Nyano - Summary: gives dispellable behavior to Anomalies. - private void OnDispelled(Entity ent, ref DispelledEvent args) - { - if (HasComp(ent)) // begone nyanocode interference with cosmic cult - return; - _dispel.DealDispelDamage(ent); - ChangeAnomalyHealth(ent, 0 - _random.NextFloat(0.4f, 0.8f), ent.Comp); - args.Handled = true; - } -} diff --git a/Content.Server/Anomaly/AnomalySystem.cs b/Content.Server/Anomaly/AnomalySystem.cs index 7a39544178..4955b4e336 100644 --- a/Content.Server/Anomaly/AnomalySystem.cs +++ b/Content.Server/Anomaly/AnomalySystem.cs @@ -57,8 +57,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem InitializeGenerator(); InitializeVessel(); InitializeCommands(); - - InitializePsionics(); //Nyano - Summary: stats up psionic related behavior. } private void OnMapInit(Entity anomaly, ref MapInitEvent args) diff --git a/Content.Server/Cloning/CloningPodSystem.cs b/Content.Server/Cloning/CloningPodSystem.cs index 96c660b136..1b7f6ae457 100644 --- a/Content.Server/Cloning/CloningPodSystem.cs +++ b/Content.Server/Cloning/CloningPodSystem.cs @@ -7,7 +7,7 @@ using Content.Server.Fluids.EntitySystems; using Content.Server.Materials; using Content.Server.Popups; using Content.Server.Power.EntitySystems; -using Content.Server.Psionics; // DeltaV +using Content.Shared._DV.Psionics.Components; // DeltaV using Content.Shared._EE.Silicon.Components; // Goobstation using Content.Shared.Atmos; using Content.Shared.CCVar; diff --git a/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs b/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs index 19eb516c85..c41e07e583 100644 --- a/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs +++ b/Content.Server/EntityEffects/Effects/MakeSentientEntityEffectSystem.cs @@ -1,6 +1,6 @@ using Content.Server.Ghost.Roles.Components; using Content.Server.Speech.Components; -using Content.Server.Psionics; // DeltaV +using Content.Shared._DV.Psionics.Components; // DeltaV using Content.Shared.EntityEffects; using Content.Shared.EntityEffects.Effects; using Content.Shared.Mind.Components; diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/DispelPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/DispelPowerSystem.cs deleted file mode 100644 index 7599cd8b01..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/DispelPowerSystem.cs +++ /dev/null @@ -1,150 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.StatusEffect; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Damage; -using Content.Shared.Damage.Systems; -using Content.Shared.Revenant.Components; -using Content.Server.Guardian; -using Content.Server.Bible.Components; -using Content.Server.Popups; -using Robust.Shared.Player; -using Robust.Shared.Random; -using Content.Shared.Actions.Events; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class DispelPowerSystem : EntitySystem - { - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly GuardianSystem _guardianSystem = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - - SubscribeLocalEvent(OnDispelled); - SubscribeLocalEvent(OnDmgDispelled); - // Upstream stuff we're just gonna handle here - SubscribeLocalEvent(OnGuardianDispelled); - SubscribeLocalEvent(OnFamiliarDispelled); - SubscribeLocalEvent(OnRevenantDispelled); - } - - private void OnInit(EntityUid uid, DispelPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.DispelActionEntity, component.DispelActionId ); - if (_actions.GetAction(component.DispelActionEntity) is not { } actionData) - return; - - if (actionData.Comp.UseDelay is not null) - { - _actions.StartUseDelay(component.DispelActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.DispelActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, DispelPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.DispelActionEntity); - - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(DispelPowerActionEvent args) - { - if (HasComp(args.Target)) - return; - - var ev = new DispelledEvent(); - RaiseLocalEvent(args.Target, ev, false); - - if (ev.Handled) - { - args.Handled = true; - _psionics.LogPowerUsed(args.Performer, "dispel"); - } - } - - private void OnDispelled(EntityUid uid, DispellableComponent component, DispelledEvent args) - { - QueueDel(uid); - Spawn("Ash", Transform(uid).Coordinates); - _popupSystem.PopupCoordinates(Loc.GetString("psionic-burns-up", ("item", uid)), Transform(uid).Coordinates, Filter.Pvs(uid), true, Shared.Popups.PopupType.MediumCaution); - _audioSystem.PlayEntity(new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"), Filter.Pvs(uid), uid, true); - args.Handled = true; - } - - private void OnDmgDispelled(EntityUid uid, DamageOnDispelComponent component, DispelledEvent args) - { - var damage = component.Damage; - var modifier = (1 + component.Variance) - (_random.NextFloat(0, component.Variance * 2)); - - damage *= modifier; - DealDispelDamage(uid, damage); - args.Handled = true; - } - - private void OnGuardianDispelled(EntityUid uid, GuardianComponent guardian, DispelledEvent args) - { - if (TryComp(guardian.Host, out var host)) - _guardianSystem.ToggleGuardian(guardian.Host.Value, host); - - DealDispelDamage(uid); - args.Handled = true; - } - - private void OnFamiliarDispelled(EntityUid uid, FamiliarComponent component, DispelledEvent args) - { - if (component.Source != null) - EnsureComp(component.Source.Value); - - args.Handled = true; - } - - private void OnRevenantDispelled(EntityUid uid, RevenantComponent component, DispelledEvent args) - { - DealDispelDamage(uid); - _statusEffects.TryAddStatusEffect(uid, "Corporeal", TimeSpan.FromSeconds(30), false, "Corporeal"); - args.Handled = true; - } - - public void DealDispelDamage(EntityUid uid, DamageSpecifier? damage = null) - { - if (Deleted(uid)) - return; - - _popupSystem.PopupCoordinates(Loc.GetString("psionic-burn-resist", ("item", uid)), Transform(uid).Coordinates, Filter.Pvs(uid), true, Shared.Popups.PopupType.SmallCaution); - _audioSystem.PlayEntity(new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"), Filter.Pvs(uid), uid, true); - - if (damage == null) - { - damage = new(); - damage.DamageDict.Add("Blunt", 100); - } - _damageableSystem.TryChangeDamage(uid, damage, true, true); - } - } - public sealed class DispelledEvent : HandledEntityEventArgs {} -} - - diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs deleted file mode 100644 index fc4669c82a..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MetapsionicPowerSystem.cs +++ /dev/null @@ -1,68 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Popups; -using Content.Shared.Actions.Events; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class MetapsionicPowerSystem : EntitySystem - { - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - } - - private void OnInit(EntityUid uid, MetapsionicPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.MetapsionicActionEntity, component.MetapsionicActionId ); - - if (_actions.GetAction(component.MetapsionicActionEntity) is { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.MetapsionicActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.MetapsionicActionEntity; - psionic.ActivePowers.Add(component); - } - - } - - private void OnShutdown(EntityUid uid, MetapsionicPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.MetapsionicActionEntity); - - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(EntityUid uid, MetapsionicPowerComponent component, MetapsionicPowerActionEvent args) - { - foreach (var entity in _lookup.GetEntitiesInRange(uid, component.Range)) - { - if (HasComp(entity) && entity != uid && !HasComp(entity) && - !(HasComp(entity) && Transform(entity).ParentUid == uid)) - { - _popups.PopupEntity(Loc.GetString("metapsionic-pulse-success"), uid, uid, PopupType.LargeCaution); - args.Handled = true; - return; - } - } - _popups.PopupEntity(Loc.GetString("metapsionic-pulse-failure"), uid, uid, PopupType.Large); - _psionics.LogPowerUsed(uid, "metapsionic pulse", 2, 4); - - args.Handled = true; - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs deleted file mode 100644 index c7b76b20d7..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/MindSwapPowerSystem.cs +++ /dev/null @@ -1,245 +0,0 @@ -using Content.Server.GameTicking; -using Content.Server.Ghost; -using Content.Server.Mind; -using Content.Server.Popups; -using Content.Server.Psionics; -using Content.Shared.Actions; -using Content.Shared.Actions.Events; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Speech; -using Content.Shared.Stealth.Components; -using Content.Shared.Mind; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs; -using Content.Shared.Damage; -using Content.Shared.Damage.Components; -using Content.Shared.Mobs.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; -using Content.Shared._DV.Abilities.Psionics; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class MindSwapPowerSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnPowerReturned); - SubscribeLocalEvent(OnDispelled); - SubscribeLocalEvent(OnMobStateChanged); - SubscribeLocalEvent(OnGhostAttempt); - // - SubscribeLocalEvent(OnSwapInit); - } - - private void OnInit(EntityUid uid, MindSwapPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.MindSwapActionEntity, component.MindSwapActionId ); - - if (_actions.GetAction(component.MindSwapActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.MindSwapActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.MindSwapActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, MindSwapPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.MindSwapActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(MindSwapPowerActionEvent args) - { - if (!(TryComp(args.Target, out var damageable) && damageable.DamageContainerID == "Biological")) - return; - - if (HasComp(args.Target)) - return; - - Swap(args.Performer, args.Target); - - _psionics.LogPowerUsed(args.Performer, "mind swap"); - args.Handled = true; - } - - private void OnPowerReturned(EntityUid uid, MindSwappedComponent component, MindSwapPowerReturnActionEvent args) - { - if (HasComp(component.OriginalEntity) || HasComp(uid)) - return; - - if (HasComp(uid) && !_mobStateSystem.IsAlive(uid)) - return; - - // How do we get trapped? - // 1. Original target doesn't exist - if (!component.OriginalEntity.IsValid() || Deleted(component.OriginalEntity)) - { - GetTrapped(uid); - return; - } - // 1. Original target is no longer mindswapped - if (!TryComp(component.OriginalEntity, out var targetMindSwap)) - { - GetTrapped(uid); - return; - } - - // 2. Target has undergone a different mind swap - if (targetMindSwap.OriginalEntity != uid) - { - GetTrapped(uid); - return; - } - - // 3. Target is dead - if (HasComp(component.OriginalEntity) && _mobStateSystem.IsDead(component.OriginalEntity)) - { - GetTrapped(uid); - return; - } - - Swap(uid, component.OriginalEntity, true); - } - - private void OnDispelled(EntityUid uid, MindSwappedComponent component, DispelledEvent args) - { - Swap(uid, component.OriginalEntity, true); - args.Handled = true; - } - - private void OnMobStateChanged(EntityUid uid, MindSwappedComponent component, MobStateChangedEvent args) - { - if (args.NewMobState == MobState.Dead) - RemComp(uid); - } - - private void OnGhostAttempt(GhostAttemptHandleEvent args) - { - if (args.Handled) - return; - - //No idea where the viaCommand went. It's on the internal OnGhostAttempt, but not this layer. Maybe unnecessary. - /*if (!args.viaCommand) - return;*/ - - // DeltaV - start of trapped ghost fix - // If you're able to swap back to your original body, you should swap back before you ghost. - if (TryComp(args.Mind.CurrentEntity, out var component) - && _actions.GetAction(component.MindSwapReturnActionEntity) is { } action - && action.Comp.AttachedEntity is not null) - { - args.Result = false; - args.Handled = true; - } - // DeltaV - end of trapped ghost fix - } - - private void OnSwapInit(EntityUid uid, MindSwappedComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.MindSwapReturnActionEntity, component.MindSwapReturnActionId ); - - if (_actions.GetAction(component.MindSwapReturnActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.MindSwapReturnActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - psionic.PsionicAbility = component.MindSwapReturnActionEntity; - } - - public void Swap(EntityUid performer, EntityUid target, bool end = false, int ReturnSwapCooldown = 0) - { - if (end && (!HasComp(performer) || !HasComp(target))) - return; - - // Get the minds first. On transfer, they'll be gone. - MindComponent? performerMind = null; - MindComponent? targetMind = null; - - // This is here to prevent missing MindContainerComponent Resolve errors. - if(!_mindSystem.TryGetMind(performer, out var performerMindId, out performerMind)){ - performerMind = null; - }; - - if(!_mindSystem.TryGetMind(target, out var targetMindId, out targetMind)){ - targetMind = null; - }; - //This is a terrible way to 'unattach' minds. I wanted to use UnVisit but in TransferTo's code they say - //To unnatch the minds, do it like this. - //Have to unnattach the minds before we reattach them via transfer. Still feels weird, but seems to work well. - _mindSystem.TransferTo(performerMindId, null); - // Do the transfer. - if (targetMind != null) - _mindSystem.TransferTo(targetMindId, performer, ghostCheckOverride: true, false, targetMind); - if (performerMind != null) - _mindSystem.TransferTo(performerMindId, target, ghostCheckOverride: true, false, performerMind); - - if (end) - { - var performerMindPowerComp = Comp(performer); - var targetMindPowerComp = Comp(target); - _actions.RemoveAction(performer, performerMindPowerComp.MindSwapReturnActionEntity); - _actions.RemoveAction(target, targetMindPowerComp.MindSwapReturnActionEntity); - - RemComp(performer); - RemComp(target); - return; - } - - var perfComp = EnsureComp(performer); - var targetComp = EnsureComp(target); - - // Delta V - Cooldown for Returning back - if (ReturnSwapCooldown > 0) - { - var cooldown = TimeSpan.FromSeconds(ReturnSwapCooldown); - _actions.SetCooldown(perfComp.MindSwapReturnActionEntity, cooldown); - _actions.SetCooldown(targetComp.MindSwapReturnActionEntity, cooldown); - } - // Delta V - - perfComp.OriginalEntity = target; - targetComp.OriginalEntity = performer; - } - - public void GetTrapped(EntityUid uid) - { - - _popupSystem.PopupEntity(Loc.GetString("mindswap-trapped"), uid, uid, Shared.Popups.PopupType.LargeCaution); - var perfComp = EnsureComp(uid); - _actions.RemoveAction(perfComp.MindSwapReturnActionEntity); - - if (HasComp(uid)) - { - RemComp(uid); - RemComp(uid); - EnsureComp(uid); - EnsureComp(uid); - _metaDataSystem.SetEntityName(uid, Loc.GetString("telegnostic-trapped-entity-name")); - _metaDataSystem.SetEntityDescription(uid, Loc.GetString("telegnostic-trapped-entity-desc")); - } - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs deleted file mode 100644 index 24968f335f..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZapPowerSystem.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Abilities.Psionics; -using Content.Server.Psionics; -using Content.Server.Lightning; -using Content.Shared.Actions.Events; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class NoosphericZapPowerSystem : EntitySystem - { - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly LightningSystem _lightning = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - } - - private void OnInit(EntityUid uid, NoosphericZapPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.NoosphericZapActionEntity, component.NoosphericZapActionId ); - - if (_actions.GetAction(component.NoosphericZapActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.NoosphericZapActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.NoosphericZapActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, NoosphericZapPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.NoosphericZapActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(NoosphericZapPowerActionEvent args) - { - if (!HasComp(args.Target)) - return; - - if (HasComp(args.Target)) - return; - - _lightning.ShootLightning(args.Performer, args.Target); - - _psionics.LogPowerUsed(args.Performer, "noospheric zap"); - args.Handled = true; - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs deleted file mode 100644 index dff2d63225..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibilityPowerSystem.cs +++ /dev/null @@ -1,139 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.CombatMode.Pacification; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Damage.Systems; -using Content.Shared.Stunnable; -using Content.Shared.Stealth; -using Content.Shared.Stealth.Components; -using Content.Server.Psionics; -using Robust.Shared.Prototypes; -using Robust.Shared.Player; -using Robust.Shared.Audio; -using Robust.Shared.Timing; -using Content.Server.Mind; -using Content.Shared.Actions.Events; -using Robust.Shared.Audio.Systems; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class PsionicInvisibilityPowerSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedStunSystem _stunSystem = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly SharedStealthSystem _stealth = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnPowerOff); - SubscribeLocalEvent(OnStart); - SubscribeLocalEvent(OnEnd); - SubscribeLocalEvent(OnDamageChanged); - } - - private void OnInit(EntityUid uid, PsionicInvisibilityPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.PsionicInvisibilityActionEntity, component.PsionicInvisibilityActionId ); - - if (_actions.GetAction(component.PsionicInvisibilityActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.PsionicInvisibilityActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.PsionicInvisibilityActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, PsionicInvisibilityPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.PsionicInvisibilityActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityPowerActionEvent args) - { - if (HasComp(uid)) - return; - - ToggleInvisibility(args.Performer); - var action = Spawn(PsionicInvisibilityUsedComponent.PsionicInvisibilityUsedActionPrototype); - _actions.AddAction(uid, action, action); - - if (_actions.GetAction(action) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(action); - } - - _psionics.LogPowerUsed(uid, "psionic invisibility"); - args.Handled = true; - } - - private void OnPowerOff(RemovePsionicInvisibilityOffPowerActionEvent args) - { - if (!HasComp(args.Performer)) - return; - - ToggleInvisibility(args.Performer); - args.Handled = true; - } - - private void OnStart(EntityUid uid, PsionicInvisibilityUsedComponent component, ComponentInit args) - { - EnsureComp(uid); - EnsureComp(uid); - var stealth = EnsureComp(uid); - _stealth.SetVisibility(uid, 0.66f, stealth); - _audio.PlayPvs("/Audio/Effects/toss.ogg", uid); - - } - - private void OnEnd(EntityUid uid, PsionicInvisibilityUsedComponent component, ComponentShutdown args) - { - if (Terminating(uid)) - return; - - RemComp(uid); - RemComp(uid); - RemComp(uid); - _audio.PlayPvs("/Audio/Effects/toss.ogg", uid); - //Pretty sure this DOESN'T work as intended. - _actions.RemoveAction(uid, component.PsionicInvisibilityUsedActionEntity); - - _stunSystem.TryAddParalyzeDuration(uid, TimeSpan.FromSeconds(8)); - DirtyEntity(uid); - } - - private void OnDamageChanged(EntityUid uid, PsionicInvisibilityUsedComponent component, DamageChangedEvent args) - { - if (!args.DamageIncreased) - return; - - ToggleInvisibility(uid); - } - - public void ToggleInvisibility(EntityUid uid) - { - if (!HasComp(uid)) - { - EnsureComp(uid); - } else - { - RemComp(uid); - } - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs deleted file mode 100644 index ab6595e605..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegenerationPowerSystem.cs +++ /dev/null @@ -1,129 +0,0 @@ -using Robust.Shared.Audio; -using Robust.Server.GameObjects; -using Robust.Shared.Player; -using Robust.Shared.Prototypes; -using Content.Server.Body.Systems; -using Content.Server.Chemistry.Containers.EntitySystems; -using Content.Server.Chemistry.EntitySystems; -using Content.Server.DoAfter; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Body.Components; -using Content.Shared.Chemistry.Components; -using Content.Shared.DoAfter; -using Content.Shared.FixedPoint; -using Content.Shared.Popups; -using Content.Shared.Psionics.Events; -using Content.Shared.Examine; -using static Content.Shared.Examine.ExamineSystemShared; -using Robust.Shared.Timing; -using Content.Server.Mind; -using Content.Shared.Actions.Events; -using Content.Shared.Chemistry.EntitySystems; -using Robust.Server.Audio; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class PsionicRegenerationPowerSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; - [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; - [Dependency] private readonly AudioSystem _audioSystem = default!; - [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - [Dependency] private readonly ExamineSystemShared _examine = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - - SubscribeLocalEvent(OnDispelled); - SubscribeLocalEvent(OnDoAfter); - } - - private void OnInit(EntityUid uid, PsionicRegenerationPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.PsionicRegenerationActionEntity, component.PsionicRegenerationActionId ); - - if (_actions.GetAction(component.PsionicRegenerationActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.PsionicRegenerationActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.PsionicRegenerationActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnPowerUsed(EntityUid uid, PsionicRegenerationPowerComponent component, PsionicRegenerationPowerActionEvent args) - { - var ev = new PsionicRegenerationDoAfterEvent(_gameTiming.CurTime); - var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid); - - _doAfterSystem.TryStartDoAfter(doAfterArgs, out var doAfterId); - - component.DoAfter = doAfterId; - - _popupSystem.PopupEntity(Loc.GetString("psionic-regeneration-begin", ("entity", uid)), - uid, - // TODO: Use LoS-based Filter when one is available. - Filter.Pvs(uid).RemoveWhereAttachedEntity(entity => !_examine.InRangeUnOccluded(uid, entity, ExamineRange, null)), - true, - PopupType.Medium); - - _audioSystem.PlayPvs(component.SoundUse, component.Owner, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); - _psionics.LogPowerUsed(uid, "psionic regeneration"); - args.Handled = true; - } - - private void OnShutdown(EntityUid uid, PsionicRegenerationPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.PsionicRegenerationActionEntity); - - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnDispelled(EntityUid uid, PsionicRegenerationPowerComponent component, DispelledEvent args) - { - if (component.DoAfter == null) - return; - - _doAfterSystem.Cancel(component.DoAfter); - component.DoAfter = null; - - args.Handled = true; - } - - private void OnDoAfter(EntityUid uid, PsionicRegenerationPowerComponent component, PsionicRegenerationDoAfterEvent args) - { - component.DoAfter = null; - - if (!TryComp(uid, out var stream)) - return; - - // DoAfter has no way to run a callback during the process to give - // small doses of the reagent, so we wait until either the action - // is cancelled (by being dispelled) or complete to give the - // appropriate dose. A timestamp delta is used to accomplish this. - var percentageComplete = Math.Min(1f, (_gameTiming.CurTime - args.StartedAt).TotalSeconds / component.UseDelay); - - var solution = new Solution(); - solution.AddReagent("Prometheum", FixedPoint2.New(component.EssenceAmount * percentageComplete)); - _bloodstreamSystem.TryAddToBloodstream((uid, stream), solution); - } - } -} - diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs deleted file mode 100644 index fc299b2ac8..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/PyrokinesisPowerSystem.cs +++ /dev/null @@ -1,69 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Atmos.Components; -using Content.Server.Atmos.EntitySystems; -using Content.Server.Popups; -using Robust.Shared.Prototypes; -using Robust.Shared.Timing; -using Content.Server.Mind; -using Content.Shared.Actions.Events; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class PyrokinesisPowerSystem : EntitySystem - { - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly FlammableSystem _flammableSystem = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly MindSystem _mindSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - } - - private void OnInit(EntityUid uid, PyrokinesisPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.PyrokinesisActionEntity, component.PyrokinesisActionId ); - - if (_actions.GetAction(component.PyrokinesisActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.PyrokinesisActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.PyrokinesisActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, PyrokinesisPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.PyrokinesisActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(PyrokinesisPowerActionEvent args) - { - if (!TryComp(args.Target, out var flammableComponent)) - return; - - flammableComponent.FireStacks += 3; - _flammableSystem.Ignite(args.Target, args.Target); - _popupSystem.PopupEntity(Loc.GetString("pyrokinesis-power-used", ("target", args.Target)), args.Target, Shared.Popups.PopupType.LargeCaution); - - _psionics.LogPowerUsed(args.Performer, "pyrokinesis"); - args.Handled = true; - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs deleted file mode 100644 index bb40d503fb..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/Abilities/TelegnosisPowerSystem.cs +++ /dev/null @@ -1,115 +0,0 @@ -using Content.Server.Atmos.EntitySystems; -using Content.Server.Body.Systems; -using Content.Server.Disposal.Unit; -using Content.Shared._DV.Abilities.Psionics; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Actions.Events; -using Content.Shared.Atmos; -using Content.Shared.Body.Components; -using Content.Shared.Examine; -using Content.Shared.Mech.Components; -using Content.Shared.Medical.Cryogenics; -using Content.Shared.Mind; -using Content.Shared.Mind.Components; -using Content.Shared.Storage.Components; -using Robust.Server.GameObjects; -using System.Numerics; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem - { - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly MindSwapPowerSystem _mindSwap = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly AtmosphereSystem _atmos = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnMindRemoved); - SubscribeLocalEvent(OnInhaleLocation, after: [typeof(InsideCryoPodComponent), typeof(InternalsComponent), typeof(BeingDisposedComponent), typeof(InsideEntityStorageComponent), typeof(MechPilotComponent)]); - SubscribeLocalEvent(OnExamine); - } - - private void OnInit(EntityUid uid, TelegnosisPowerComponent component, ComponentInit args) - { - _actions.AddAction(uid, ref component.TelegnosisActionEntity, component.TelegnosisActionId); - - if (_actions.GetAction(component.TelegnosisActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(component.TelegnosisActionEntity); - } - - if (TryComp(uid, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.TelegnosisActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void OnShutdown(EntityUid uid, TelegnosisPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.TelegnosisActionEntity); - if (TryComp(uid, out var psionic)) - { - psionic.ActivePowers.Remove(component); - } - } - - private void OnPowerUsed(EntityUid uid, TelegnosisPowerComponent component, TelegnosisPowerActionEvent args) - { - var projection = Spawn(component.Prototype, Transform(uid).Coordinates); - - _transform.AttachToGridOrMap(projection); - _mindSwap.Swap(uid, projection); - - _psionics.LogPowerUsed(uid, "telegnosis"); - args.Handled = true; - } - private void OnMindRemoved(EntityUid uid, TelegnosticProjectionComponent component, MindRemovedMessage args) - { - QueueDel(uid); - } - - public EntityUid GetCasterProjection(Entity entity) - { - if (!TryComp(entity, out var mindSwapped) || - !HasComp(mindSwapped.OriginalEntity)) - { - return default; - } - return mindSwapped.OriginalEntity; - } - - private void OnInhaleLocation(Entity entity, ref InhaleLocationEvent args) - { - var sensorUid = GetCasterProjection(entity); - if (sensorUid == default) - return; - // Determine the distance to the sensor, this will be used to dilute the amount of air we take in. - var sensorPosition = _transform.GetWorldPosition(sensorUid); - var projectionPosition = _transform.GetWorldPosition(entity); - // A linear curve from 1.0 at 7 tiles away, to 0 at 57 tiles away - var distance = Vector2.Distance(sensorPosition, projectionPosition); - float gasMult = Math.Clamp(1f - (distance - 7f) / 50f, 0f, 1f); - args.Gas = (args.Gas ?? _atmos.GetContainingMixture(entity.Owner, excite: true))?.RemoveVolume(Atmospherics.BreathVolume * gasMult); - if (args.Gas == null) - return; - args.Gas.Volume = Math.Min(args.Gas.Volume, Atmospherics.BreathVolume); - } - - private void OnExamine(Entity entity, ref ExaminedEvent args) - { - if (GetCasterProjection(entity) == default) - return; - - args.PushMarkup($"[color=yellow]{Loc.GetString("telegnosis-power-ssd", ("ent", entity))}[/color]"); - } - } -} diff --git a/Content.Server/Nyanotrasen/Abilities/Psionics/PsionicAbilitiesSystem.cs b/Content.Server/Nyanotrasen/Abilities/Psionics/PsionicAbilitiesSystem.cs deleted file mode 100644 index 12d712f974..0000000000 --- a/Content.Server/Nyanotrasen/Abilities/Psionics/PsionicAbilitiesSystem.cs +++ /dev/null @@ -1,135 +0,0 @@ -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Psionics.Glimmer; -using Content.Shared.Random; -using Content.Shared.Random.Helpers; -using Content.Server.EUI; -using Content.Server.Psionics; -using Content.Shared.Jittering; -using Content.Shared.StatusEffect; -using Robust.Shared.Random; -using Robust.Shared.Prototypes; -using Robust.Shared.Player; - -namespace Content.Server.Abilities.Psionics -{ - public sealed class PsionicAbilitiesSystem : EntitySystem - { - [Dependency] private readonly IComponentFactory _componentFactory = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; - [Dependency] private readonly EuiManager _euiManager = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - [Dependency] private readonly SharedJitteringSystem _jittering = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly IPrototypeManager _prototypeManager = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnPlayerAttached); - } - - private void OnPlayerAttached(EntityUid uid, PsionicAwaitingPlayerComponent component, PlayerAttachedEvent args) - { - if (TryComp(uid, out var bonus) && bonus.Warn == true) - _euiManager.OpenEui(new AcceptPsionicsEui(uid, this), args.Player); - else - AddRandomPsionicPower(uid); - RemCompDeferred(uid); - } - - public void AddPsionics(EntityUid uid, bool warn = true) - { - if (Deleted(uid)) - return; - - if (HasComp(uid)) - return; - - //Don't know if this will work. New mind state vs old. - if (!TryComp(uid, out var actor)) - { - EnsureComp(uid); - return; - } - - if (warn) - _euiManager.OpenEui(new AcceptPsionicsEui(uid, this), actor.PlayerSession); - else - AddRandomPsionicPower(uid); - } - - public void AddPsionics(EntityUid uid, string powerComp) - { - if (Deleted(uid)) - return; - - if (HasComp(uid)) - return; - - AddComp(uid); - - var newComponent = (Component) _componentFactory.GetComponent(powerComp); - newComponent.Owner = uid; - - EntityManager.AddComponent(uid, newComponent); - } - - public void AddRandomPsionicPower(EntityUid uid) - { - AddComp(uid); - - if (!_prototypeManager.TryIndex("RandomPsionicPowerPool", out var pool)) - { - Logger.Error("Can't index the random psionic power pool!"); - return; - } - - // uh oh, stinky! - var newComponent = (Component) _componentFactory.GetComponent(pool.Pick()); - newComponent.Owner = uid; - - EntityManager.AddComponent(uid, newComponent); - - _glimmerSystem.Glimmer += _random.Next(1, 5); - } - - public void RemovePsionics(EntityUid uid) - { - if (!TryComp(uid, out var psionic)) - return; - - if (!psionic.Removable) - return; - - if (!_prototypeManager.TryIndex("RandomPsionicPowerPool", out var pool)) - { - Logger.Error("Can't index the random psionic power pool!"); - return; - } - - foreach (var compName in pool.Weights.Keys) - { - // component moment - var comp = _componentFactory.GetComponent(compName); - if (EntityManager.TryGetComponent(uid, comp.GetType(), out var psionicPower)) - RemComp(uid, psionicPower); - } - if (psionic.PsionicAbility != null){ - if (_actionsSystem.GetAction(psionic.PsionicAbility) is { } psiAbility) - { - _actionsSystem.RemoveAction(uid, psiAbility.Owner); - } - } - - _glimmerSystem.Glimmer -= _random.Next(50, 70); - - _statusEffectsSystem.TryAddStatusEffect(uid, "Stutter", TimeSpan.FromMinutes(1), false, "StutteringAccent"); - _statusEffectsSystem.TryAddStatusEffect(uid, "KnockedDown", TimeSpan.FromSeconds(3), false, "KnockedDown"); - _jittering.DoJitter(uid, TimeSpan.FromSeconds(10), false); - - RemComp(uid); - } - } -} diff --git a/Content.Server/Nyanotrasen/CartridgeLoader/GlimmerMonitorCartridgeComponent.cs b/Content.Server/Nyanotrasen/CartridgeLoader/GlimmerMonitorCartridgeComponent.cs index 40ba46647c..a93bf10929 100644 --- a/Content.Server/Nyanotrasen/CartridgeLoader/GlimmerMonitorCartridgeComponent.cs +++ b/Content.Server/Nyanotrasen/CartridgeLoader/GlimmerMonitorCartridgeComponent.cs @@ -1,5 +1,4 @@ namespace Content.Server.CartridgeLoader.Cartridges; [RegisterComponent] -public sealed partial class GlimmerMonitorCartridgeComponent : Component -{ } +public sealed partial class GlimmerMonitorCartridgeComponent : Component; diff --git a/Content.Server/Nyanotrasen/Chat/NyanoChatSystem.cs b/Content.Server/Nyanotrasen/Chat/NyanoChatSystem.cs index 246390cfd7..eff80cda57 100644 --- a/Content.Server/Nyanotrasen/Chat/NyanoChatSystem.cs +++ b/Content.Server/Nyanotrasen/Chat/NyanoChatSystem.cs @@ -2,7 +2,6 @@ using Content.Server.Administration.Logs; using Content.Server.Administration.Managers; using Content.Server.Chat.Managers; using Content.Server.Chat.Systems; -using Content.Shared.Abilities.Psionics; using Content.Shared.Bed.Sleep; using Content.Shared.Chat; using Content.Shared.Database; @@ -15,114 +14,116 @@ using Robust.Shared.Player; using Robust.Shared.Random; using System.Linq; using System.Text; +using Content.Server._DV.Psionics.Systems; +using Content.Shared._DV.Chat.Components; -namespace Content.Server.Nyanotrasen.Chat +namespace Content.Server.Nyanotrasen.Chat; + +/// +/// Extensions for nyano's chat stuff +/// + +public sealed class NyanoChatSystem : EntitySystem { - /// - /// Extensions for nyano's chat stuff - /// + [Dependency] private readonly IAdminManager _adminManager = default!; + [Dependency] private readonly IChatManager _chatManager = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + [Dependency] private readonly ChatSystem _chatSystem = default!; + [Dependency] private readonly PsionicSystem _psionicSystem = default!; - public sealed class NyanoChatSystem : EntitySystem + private IEnumerable GetPsionicChatClients() { - [Dependency] private readonly IAdminManager _adminManager = default!; - [Dependency] private readonly IChatManager _chatManager = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly ChatSystem _chatSystem = default!; - private IEnumerable GetPsionicChatClients() + return Filter.Empty() + .AddWhereAttachedEntity(IsEligibleForTelepathy) + .Recipients + .Select(p => p.Channel); + } + + private IEnumerable GetAdminClients() + { + return _adminManager.ActiveAdmins + .Select(p => p.Channel); + } + + private List GetDreamers(IEnumerable removeList) + { + var filtered = Filter.Empty() + .AddWhereAttachedEntity(entity => HasComp(entity) || HasComp(entity) && IsEligibleForTelepathy(entity)) + .Recipients + .Select(p => p.Channel); + + var filteredList = filtered.ToList(); + + foreach (var entity in removeList) + filteredList.Remove(entity); + + return filteredList; + } + + private bool IsEligibleForTelepathy(EntityUid entity) + { + return _psionicSystem.CanUsePsionicAbility(entity) + && (!TryComp(entity, out var mobstate) + || mobstate.CurrentState == MobState.Alive); + } + + public void SendTelepathicChat(EntityUid source, string message, bool hideChat) + { + if (!IsEligibleForTelepathy(source)) + return; + + var clients = GetPsionicChatClients(); + var admins = GetAdminClients(); + string messageWrap; + string adminMessageWrap; + + messageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message", + ("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")), ("message", message)); + + adminMessageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message-admin", + ("source", source), ("message", message)); + + _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Telepathic chat from {ToPrettyString(source):Player}: {message}"); + + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, messageWrap, source, hideChat, true, clients.ToList(), Color.PaleVioletRed); + + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, adminMessageWrap, source, hideChat, true, admins, Color.PaleVioletRed); + + if (_random.Prob(0.1f)) + _glimmerSystem.Glimmer++; + + if (_random.Prob(Math.Min(0.33f + ((float) _glimmerSystem.Glimmer / 1500), 1))) { - return Filter.Empty() - .AddWhereAttachedEntity(IsEligibleForTelepathy) - .Recipients - .Select(p => p.Channel); + float obfuscation = (0.25f + (float) _glimmerSystem.Glimmer / 2000); + var obfuscated = ObfuscateMessageReadability(message, obfuscation); + _chatManager.ChatMessageToMany(ChatChannel.Telepathic, obfuscated, messageWrap, source, hideChat, false, GetDreamers(clients), Color.PaleVioletRed); } - private IEnumerable GetAdminClients() + foreach (var repeater in EntityQuery()) { - return _adminManager.ActiveAdmins - .Select(p => p.Channel); - } - - private List GetDreamers(IEnumerable removeList) - { - var filtered = Filter.Empty() - .AddWhereAttachedEntity(entity => HasComp(entity) || HasComp(entity) && !HasComp(entity) && !HasComp(entity)) - .Recipients - .Select(p => p.Channel); - - var filteredList = filtered.ToList(); - - foreach (var entity in removeList) - filteredList.Remove(entity); - - return filteredList; - } - - private bool IsEligibleForTelepathy(EntityUid entity) - { - return HasComp(entity) - && !HasComp(entity) - && !HasComp(entity) - && (!TryComp(entity, out var mobstate) || mobstate.CurrentState == MobState.Alive); - } - - public void SendTelepathicChat(EntityUid source, string message, bool hideChat) - { - if (!IsEligibleForTelepathy(source)) - return; - - var clients = GetPsionicChatClients(); - var admins = GetAdminClients(); - string messageWrap; - string adminMessageWrap; - - messageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message", - ("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")), ("message", message)); - - adminMessageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message-admin", - ("source", source), ("message", message)); - - _adminLogger.Add(LogType.Chat, LogImpact.Low, $"Telepathic chat from {ToPrettyString(source):Player}: {message}"); - - _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, messageWrap, source, hideChat, true, clients.ToList(), Color.PaleVioletRed); - - _chatManager.ChatMessageToMany(ChatChannel.Telepathic, message, adminMessageWrap, source, hideChat, true, admins, Color.PaleVioletRed); - - if (_random.Prob(0.1f)) - _glimmerSystem.Glimmer++; - - if (_random.Prob(Math.Min(0.33f + ((float) _glimmerSystem.Glimmer / 1500), 1))) - { - float obfuscation = (0.25f + (float) _glimmerSystem.Glimmer / 2000); - var obfuscated = ObfuscateMessageReadability(message, obfuscation); - _chatManager.ChatMessageToMany(ChatChannel.Telepathic, obfuscated, messageWrap, source, hideChat, false, GetDreamers(clients), Color.PaleVioletRed); - } - - foreach (var repeater in EntityQuery()) - { - _chatSystem.TrySendInGameICMessage(repeater.Owner, message, InGameICChatType.Speak, false); - } - } - - private string ObfuscateMessageReadability(string message, float chance) - { - var modifiedMessage = new StringBuilder(message); - - for (var i = 0; i < message.Length; i++) - { - if (char.IsWhiteSpace((modifiedMessage[i]))) - { - continue; - } - - if (_random.Prob(1 - chance)) - { - modifiedMessage[i] = '~'; - } - } - - return modifiedMessage.ToString(); + _chatSystem.TrySendInGameICMessage(repeater.Owner, message, InGameICChatType.Speak, false); } } + + private string ObfuscateMessageReadability(string message, float chance) + { + var modifiedMessage = new StringBuilder(message); + + for (var i = 0; i < message.Length; i++) + { + if (char.IsWhiteSpace((modifiedMessage[i]))) + { + continue; + } + + if (_random.Prob(1 - chance)) + { + modifiedMessage[i] = '~'; + } + } + + return modifiedMessage.ToString(); + } } diff --git a/Content.Server/Nyanotrasen/Chat/TelepathicRepeaterComponent.cs b/Content.Server/Nyanotrasen/Chat/TelepathicRepeaterComponent.cs deleted file mode 100644 index fc199f4332..0000000000 --- a/Content.Server/Nyanotrasen/Chat/TelepathicRepeaterComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Content.Server.Nyanotrasen.Chat -{ - /// - /// Repeats whatever is happening in telepathic chat. - /// - [RegisterComponent] - public sealed partial class TelepathicRepeaterComponent : Component - { - - } -} diff --git a/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs b/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs index 7ee5da29c5..756fe67eae 100644 --- a/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs +++ b/Content.Server/Nyanotrasen/Objectives/Systems/BecomePsionicConditionSystem.cs @@ -1,5 +1,6 @@ using Content.Shared.Abilities.Psionics; using Content.Server.Objectives.Components; +using Content.Shared._DV.Psionics.Components; using Content.Shared.Mind; using Content.Shared.Objectives.Components; diff --git a/Content.Server/Nyanotrasen/Psionics/AcceptPsionicsEui.cs b/Content.Server/Nyanotrasen/Psionics/AcceptPsionicsEui.cs deleted file mode 100644 index 80fd8946f2..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/AcceptPsionicsEui.cs +++ /dev/null @@ -1,34 +0,0 @@ -using Content.Shared.Psionics; -using Content.Shared.Eui; -using Content.Server.EUI; -using Content.Server.Abilities.Psionics; - -namespace Content.Server.Psionics -{ - public sealed class AcceptPsionicsEui : BaseEui - { - private readonly PsionicAbilitiesSystem _psionicsSystem; - private readonly EntityUid _entity; - - public AcceptPsionicsEui(EntityUid entity, PsionicAbilitiesSystem psionicsSys) - { - _entity = entity; - _psionicsSystem = psionicsSys; - } - - public override void HandleMessage(EuiMessageBase msg) - { - base.HandleMessage(msg); - - if (msg is not AcceptPsionicsChoiceMessage choice || - choice.Button == AcceptPsionicsUiButton.Deny) - { - Close(); - return; - } - - _psionicsSystem.AddRandomPsionicPower(_entity); - Close(); - } - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/AntiPsychicWeaponComponent.cs b/Content.Server/Nyanotrasen/Psionics/AntiPsychicWeaponComponent.cs deleted file mode 100644 index ee81380a9c..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/AntiPsychicWeaponComponent.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Shared.Damage; - -namespace Content.Server.Psionics -{ - [RegisterComponent] - public sealed partial class AntiPsionicWeaponComponent : Component - { - - [DataField("modifiers", required: true)] - public DamageModifierSet Modifiers = default!; - - [DataField("psychicStaminaDamage")] - public float PsychicStaminaDamage = 30f; - - [DataField("disableChance")] - public float DisableChance = 0.3f; - - /// - /// Punish when used against a non-psychic. - /// +/// Adds to glimmer at regular intervals. We'll use it for glimmer drains too when we get there. +/// +[RegisterComponent] +public sealed partial class GlimmerSourceComponent : Component { - [RegisterComponent] + + [DataField] public float Accumulator = 0f; + + [DataField] public bool Active = true; + /// - /// Adds to glimmer at regular intervals. We'll use it for glimmer drains too when we get there. + /// Since glimmer is an int, we'll do it like this. /// - public sealed partial class GlimmerSourceComponent : Component - { - [DataField("accumulator")] - public float Accumulator = 0f; + [DataField] public float SecondsPerGlimmer = 10f; - [DataField("active")] - public bool Active = true; - - /// - /// Since glimmer is an int, we'll do it like this. - /// - [DataField("secondsPerGlimmer")] - public float SecondsPerGlimmer = 10f; - - /// - /// True if it produces glimmer, false if it subtracts it. - /// - [DataField("addToGlimmer")] - public bool AddToGlimmer = true; - } + /// + /// True if it produces glimmer, false if it subtracts it. + /// + [DataField] public bool AddToGlimmer = true; } + diff --git a/Content.Server/Nyanotrasen/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs b/Content.Server/Nyanotrasen/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs index 33adfec243..dc952ca1fa 100644 --- a/Content.Server/Nyanotrasen/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/Glimmer/Structures/GlimmerStructuresSystem.cs @@ -1,85 +1,78 @@ using Content.Server.Anomaly.Components; -using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Anomaly.Components; using Content.Shared.Power; using Content.Shared.Psionics.Glimmer; -namespace Content.Server.Psionics.Glimmer +namespace Content.Server.Psionics.Glimmer; + +/// +/// Handles structures which add/subtract glimmer. +/// +public sealed class GlimmerStructuresSystem : EntitySystem { - /// - /// Handles structures which add/subtract glimmer. - /// - public sealed class GlimmerStructuresSystem : EntitySystem + [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; + [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + + public override void Initialize() { - [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + base.Initialize(); + SubscribeLocalEvent(OnAnomalyVesselPowerChanged); + SubscribeLocalEvent(OnAnomalyPulse); + SubscribeLocalEvent(OnAnomalySupercritical); + } - public override void Initialize() + private void OnAnomalyVesselPowerChanged(EntityUid uid, AnomalyVesselComponent component, ref PowerChangedEvent args) + { + if (TryComp(component.Anomaly, out var glimmerSource)) + glimmerSource.Active = args.Powered; + } + + private void OnAnomalyPulse(EntityUid uid, GlimmerSourceComponent component, ref AnomalyPulseEvent args) + { + // Anomalies are meant to have GlimmerSource on them with the + // active flag set to false, as they will be set to actively + // generate glimmer when scanned to an anomaly vessel for + // harvesting research points. + // + // It is not a bug that glimmer increases on pulse or + // supercritical with an inactive glimmer source. + // + // However, this will need to be reworked if a distinction + // needs to be made in the future. I suggest a GlimmerAnomaly + // component. + if (TryComp(uid, out var anomaly)) + _glimmerSystem.Glimmer += (int) (5f * anomaly.Severity); + } + + private void OnAnomalySupercritical(EntityUid uid, GlimmerSourceComponent component, ref AnomalySupercriticalEvent args) + { + _glimmerSystem.Glimmer += 100; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + foreach (var source in EntityQuery()) { - base.Initialize(); - - SubscribeLocalEvent(OnAnomalyVesselPowerChanged); - - SubscribeLocalEvent(OnAnomalyPulse); - SubscribeLocalEvent(OnAnomalySupercritical); - } - - private void OnAnomalyVesselPowerChanged(EntityUid uid, AnomalyVesselComponent component, ref PowerChangedEvent args) - { - if (TryComp(component.Anomaly, out var glimmerSource)) - glimmerSource.Active = args.Powered; - } - - private void OnAnomalyPulse(EntityUid uid, GlimmerSourceComponent component, ref AnomalyPulseEvent args) - { - // Anomalies are meant to have GlimmerSource on them with the - // active flag set to false, as they will be set to actively - // generate glimmer when scanned to an anomaly vessel for - // harvesting research points. - // - // It is not a bug that glimmer increases on pulse or - // supercritical with an inactive glimmer source. - // - // However, this will need to be reworked if a distinction - // needs to be made in the future. I suggest a GlimmerAnomaly - // component. - - if (TryComp(uid, out var anomaly)) - _glimmerSystem.Glimmer += (int) (5f * anomaly.Severity); - } - - private void OnAnomalySupercritical(EntityUid uid, GlimmerSourceComponent component, ref AnomalySupercriticalEvent args) - { - _glimmerSystem.Glimmer += 100; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - foreach (var source in EntityQuery()) + if (!_powerReceiverSystem.IsPowered(source.Owner)) + continue; + if (!source.Active) + continue; + source.Accumulator += frameTime; + if (source.Accumulator > source.SecondsPerGlimmer) { - if (!_powerReceiverSystem.IsPowered(source.Owner)) - continue; - - if (!source.Active) - continue; - - source.Accumulator += frameTime; - - if (source.Accumulator > source.SecondsPerGlimmer) + source.Accumulator -= source.SecondsPerGlimmer; + if (source.AddToGlimmer) { - source.Accumulator -= source.SecondsPerGlimmer; - if (source.AddToGlimmer) - { - _glimmerSystem.Glimmer++; - } - else - { - _glimmerSystem.Glimmer--; - } + _glimmerSystem.Glimmer++; + } + else + { + _glimmerSystem.Glimmer--; } } } } } + diff --git a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibilitySystem.cs b/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibilitySystem.cs deleted file mode 100644 index 325d55cbcc..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibilitySystem.cs +++ /dev/null @@ -1,142 +0,0 @@ -using Content.Shared.Abilities.Psionics; -using Content.Server.Abilities.Psionics; -using Content.Shared.Eye; -using Content.Shared.NPC.Systems; -using Robust.Shared.Containers; -using Robust.Server.GameObjects; -using Content.Shared.NPC.Prototypes; -using Robust.Shared.Prototypes; - -namespace Content.Server.Psionics -{ - public sealed class PsionicInvisibilitySystem : EntitySystem - { - [Dependency] private readonly VisibilitySystem _visibility = default!; - [Dependency] private readonly PsionicInvisibilityPowerSystem _invisSystem = default!; - [Dependency] private readonly NpcFactionSystem _faction = default!; - [Dependency] private readonly SharedEyeSystem _eye = default!; - - private static readonly ProtoId PsionicInterloperProtoId = "PsionicInterloper"; - private static readonly ProtoId GlimmerMonsterProtoId = "GlimmerMonster"; - - - public override void Initialize() - { - base.Initialize(); - /// Masking - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnInsulInit); - SubscribeLocalEvent(OnInsulShutdown); - SubscribeLocalEvent(OnEyeInit); - - /// Layer - SubscribeLocalEvent(OnInvisInit); - SubscribeLocalEvent(OnInvisShutdown); - - // PVS Stuff - SubscribeLocalEvent(OnEntInserted); - SubscribeLocalEvent(OnEntRemoved); - } - - private void OnInit(EntityUid uid, PotentialPsionicComponent component, ComponentInit args) - { - SetCanSeePsionicInvisiblity(uid, false); - } - - private void OnInsulInit(EntityUid uid, PsionicInsulationComponent component, ComponentInit args) - { - if (!HasComp(uid)) - return; - - if (HasComp(uid)) - _invisSystem.ToggleInvisibility(uid); - - if (_faction.IsMember(uid, PsionicInterloperProtoId)) - { - component.SuppressedFactions.Add(PsionicInterloperProtoId); - _faction.RemoveFaction(uid, PsionicInterloperProtoId); - } - - if (_faction.IsMember(uid, GlimmerMonsterProtoId)) - { - component.SuppressedFactions.Add(GlimmerMonsterProtoId); - _faction.RemoveFaction(uid, GlimmerMonsterProtoId); - } - - SetCanSeePsionicInvisiblity(uid, true); - } - - private void OnInsulShutdown(EntityUid uid, PsionicInsulationComponent component, ComponentShutdown args) - { - if (!HasComp(uid)) - return; - - SetCanSeePsionicInvisiblity(uid, false); - - if (!HasComp(uid)) - { - component.SuppressedFactions.Clear(); - return; - } - - foreach (var faction in component.SuppressedFactions) - { - _faction.AddFaction(uid, faction); - } - component.SuppressedFactions.Clear(); - } - - private void OnInvisInit(EntityUid uid, PsionicallyInvisibleComponent component, ComponentInit args) - { - var visibility = EnsureComp(uid); - var ent = (uid, visibility); - - _visibility.AddLayer(ent, (int) VisibilityFlags.PsionicInvisibility, false); - _visibility.RemoveLayer(ent, (int) VisibilityFlags.Normal, false); - _visibility.RefreshVisibility(ent); - } - - - private void OnInvisShutdown(EntityUid uid, PsionicallyInvisibleComponent component, ComponentShutdown args) - { - if (!TryComp(uid, out var visibility)) - return; - - var ent = (uid, visibility); - _visibility.RemoveLayer(ent, (int) VisibilityFlags.PsionicInvisibility, false); - _visibility.AddLayer(ent, (int) VisibilityFlags.Normal, false); - _visibility.RefreshVisibility(ent); - } - - private void OnEyeInit(EntityUid uid, EyeComponent component, ComponentInit args) - { - //SetCanSeePsionicInvisiblity(uid, true); //JJ Comment - Not allowed to modifies .yml on spawn any longer. See UninitializedSaveTest. - } - private void OnEntInserted(EntityUid uid, PsionicallyInvisibleComponent component, EntInsertedIntoContainerMessage args) - { - DirtyEntity(args.Entity); - } - - private void OnEntRemoved(EntityUid uid, PsionicallyInvisibleComponent component, EntRemovedFromContainerMessage args) - { - DirtyEntity(args.Entity); - } - - public void SetCanSeePsionicInvisiblity(EntityUid uid, bool set) - { - if (set == true) - { - if (EntityManager.TryGetComponent(uid, out EyeComponent? eye)) - { - _eye.SetVisibilityMask(uid, eye.VisibilityMask | (int) VisibilityFlags.PsionicInvisibility, eye); - } - } else - { - if (EntityManager.TryGetComponent(uid, out EyeComponent? eye)) - { - _eye.SetVisibilityMask(uid, eye.VisibilityMask & ~ (int) VisibilityFlags.PsionicInvisibility, eye); - } - } - } - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsComponent.cs b/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsComponent.cs deleted file mode 100644 index 859ceb7b83..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared.Whitelist; -using Robust.Shared.Timing; - -namespace Content.Server.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicInvisibleContactsComponent : Component - { - [DataField("whitelist", required: true)] - public EntityWhitelist Whitelist = default!; - - /// - /// This tracks how many valid entities are being contacted, - /// so when you stop touching one, you don't immediately lose invisibility. - /// - [DataField("stages")] - public int Stages = 0; - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsSystem.cs b/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsSystem.cs deleted file mode 100644 index c0c91fcc69..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicInvisibleContactsSystem.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Content.Shared.Stealth; -using Content.Shared.Stealth.Components; -using Content.Shared.Whitelist; -using Robust.Shared.Physics.Events; -using Robust.Shared.Physics.Systems; -using Robust.Shared.Timing; - -namespace Content.Server.Psionics -{ - /// - /// Allows an entity to become psionically invisible when touching certain entities. - /// - public sealed class PsionicInvisibleContactsSystem : EntitySystem - { - [Dependency] private readonly SharedStealthSystem _stealth = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnEntityEnter); - SubscribeLocalEvent(OnEntityExit); - - UpdatesAfter.Add(typeof(SharedPhysicsSystem)); - } - - private void OnEntityEnter(EntityUid uid, PsionicInvisibleContactsComponent component, ref StartCollideEvent args) - { - var otherUid = args.OtherEntity; - var ourEntity = args.OurEntity; - - if (_whitelistSystem.IsWhitelistFail(component.Whitelist, otherUid)) - return; - - // This will go up twice per web hit, since webs also have a flammable fixture. - // It goes down twice per web exit, so everything's fine. - ++component.Stages; - - if (HasComp(ourEntity)) - return; - - EnsureComp(ourEntity); - var stealth = EnsureComp(ourEntity); - _stealth.SetVisibility(ourEntity, 0.66f, stealth); - } - - private void OnEntityExit(EntityUid uid, PsionicInvisibleContactsComponent component, ref EndCollideEvent args) - { - var otherUid = args.OtherEntity; - var ourEntity = args.OurEntity; - - if (_whitelistSystem.IsWhitelistFail(component.Whitelist, otherUid)) - return; - - if (!HasComp(ourEntity)) - return; - - if (--component.Stages > 0) - return; - - RemComp(ourEntity); - var stealth = EnsureComp(ourEntity); - // Just to be sure... - _stealth.SetVisibility(ourEntity, 1f, stealth); - - RemComp(ourEntity); - } - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicallyInvisibleComponent.cs b/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicallyInvisibleComponent.cs deleted file mode 100644 index 5352f5737f..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/Invisibility/PsionicallyInvisibleComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Server.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicallyInvisibleComponent : Component - {} -} diff --git a/Content.Server/Nyanotrasen/Psionics/NPC/PsionicNpcCombatSystem.cs b/Content.Server/Nyanotrasen/Psionics/NPC/PsionicNpcCombatSystem.cs index 15531d999b..d37613b99b 100644 --- a/Content.Server/Nyanotrasen/Psionics/NPC/PsionicNpcCombatSystem.cs +++ b/Content.Server/Nyanotrasen/Psionics/NPC/PsionicNpcCombatSystem.cs @@ -1,8 +1,7 @@ -using Content.Shared.Abilities.Psionics; using Content.Shared.Actions; using Content.Server.NPC.Events; using Content.Server.NPC.Components; -using Content.Server.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components.PsionicPowers; using Content.Shared.Actions.Components; using Robust.Shared.Timing; @@ -24,7 +23,7 @@ public sealed class PsionicNpcCombatSystem : EntitySystem private void ZapCombat(Entity ent, ref NPCSteeringEvent args) { var (uid, comp) = ent; - if (comp.NoosphericZapActionEntity is not {} action) + if (comp.ActionEntity is not {} action) return; var target = Comp(action); diff --git a/Content.Server/Nyanotrasen/Psionics/PotentialPsionicComponent.cs b/Content.Server/Nyanotrasen/Psionics/PotentialPsionicComponent.cs deleted file mode 100644 index 65c30d079c..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/PotentialPsionicComponent.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace Content.Server.Psionics -{ - [RegisterComponent] - public sealed partial class PotentialPsionicComponent : Component - { - [DataField("chance")] - public float Chance = 0.04f; - - /// - /// YORO (you only reroll once) - /// - [DataField] - public bool Rerolled = false; - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/PsionicAwaitingPlayerComponent.cs b/Content.Server/Nyanotrasen/Psionics/PsionicAwaitingPlayerComponent.cs deleted file mode 100644 index f9cc9339d4..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/PsionicAwaitingPlayerComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Server.Psionics -{ - /// - /// Will open the 'accept psionics' UI when a player attaches. - /// - [RegisterComponent] - public sealed partial class PsionicAwaitingPlayerComponent : Component - {} -} diff --git a/Content.Server/Nyanotrasen/Psionics/PsionicBonusChanceComponent.cs b/Content.Server/Nyanotrasen/Psionics/PsionicBonusChanceComponent.cs deleted file mode 100644 index d9cbc51147..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/PsionicBonusChanceComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace Content.Server.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicBonusChanceComponent : Component - { - [DataField("multiplier")] - public float Multiplier = 1f; - [DataField("flatBonus")] - public float FlatBonus = 0; - - /// - /// Whether we should warn the user they are about to receive psionics. - /// It's here because AddComponentSpecial can't overwrite a component, and this is very role dependent. - /// - [DataField("warn")] - public bool Warn = true; - } -} diff --git a/Content.Server/Nyanotrasen/Psionics/PsionicsCommands.cs b/Content.Server/Nyanotrasen/Psionics/PsionicsCommands.cs index 4810004c14..16c0d3e473 100644 --- a/Content.Server/Nyanotrasen/Psionics/PsionicsCommands.cs +++ b/Content.Server/Nyanotrasen/Psionics/PsionicsCommands.cs @@ -1,4 +1,5 @@ using Content.Server.Administration; +using Content.Shared._DV.Psionics.Components; using Content.Shared.Administration; using Content.Shared.Abilities.Psionics; using Content.Shared.Mobs.Components; @@ -21,15 +22,14 @@ public sealed class ListPsionicsCommand : IConsoleCommand var entMan = IoCManager.Resolve(); foreach (var (actor, mob, psionic, meta) in entMan.EntityQuery()){ // filter out xenos, etc, with innate telepathy - if (actions.GetAction(psionic.PsionicAbility) is not { } actionData) + if (psionic.PsionicPowersActionEntities.Count == 0) return; - if (actionData.Comp.ToString() == null) - return; - - var psiPowerName = actionData.ToString(); - if (psiPowerName == null) - return; + var psiPowerName = ""; + foreach (var power in psionic.PsionicPowersActionEntities) + { + psiPowerName += power; + } shell.WriteLine(meta.EntityName + " (" + meta.Owner + ") - " + actor.PlayerSession.Name + Loc.GetString(psiPowerName)); } diff --git a/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs b/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs deleted file mode 100644 index 1e8dd575ea..0000000000 --- a/Content.Server/Nyanotrasen/Psionics/PsionicsSystem.cs +++ /dev/null @@ -1,190 +0,0 @@ -using Content.Shared.Abilities.Psionics; -using Content.Shared.StatusEffect; -using Content.Shared.Mobs; -using Content.Shared.Psionics.Glimmer; -using Content.Shared.Weapons.Melee.Events; -using Content.Shared.Damage.Events; -using Content.Shared._DV.CCVars; -using Content.Shared.IdentityManagement; -using Content.Server.Abilities.Psionics; -using Content.Server.Chat.Systems; -using Content.Server.Electrocution; -using Content.Server.NPC.Components; -using Content.Server.NPC.Systems; -using Content.Shared.NPC.Components; -using Content.Shared.NPC.Systems; -using Robust.Shared.Audio; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Configuration; -using Robust.Shared.Random; -using Content.Shared._DV.Abilities.Psionics; - -namespace Content.Server.Psionics -{ - public sealed class PsionicsSystem : EntitySystem - { - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly ElectrocutionSystem _electrocutionSystem = default!; - [Dependency] private readonly MindSwapPowerSystem _mindSwapPowerSystem = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly NpcFactionSystem _faction = default!; - [Dependency] private readonly IConfigurationManager _cfg = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - - /// - /// Unfortunately, since spawning as a normal role and anything else is so different, - /// this is the only way to unify them, for now at least. - /// - Queue<(PotentialPsionicComponent component, EntityUid uid)> _rollers = new(); - public override void Update(float frameTime) - { - base.Update(frameTime); - foreach (var roller in _rollers) - { - RollPsionics(roller.uid, roller.component, false); - } - _rollers.Clear(); - } - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnStartup); - SubscribeLocalEvent(OnMeleeHit); - SubscribeLocalEvent(OnStamHit); - - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnRemove); - } - - private void OnStartup(EntityUid uid, PotentialPsionicComponent component, MapInitEvent args) - { - if (HasComp(uid)) - return; - - _rollers.Enqueue((component, uid)); - } - - private void OnMeleeHit(EntityUid uid, AntiPsionicWeaponComponent component, MeleeHitEvent args) - { - foreach (var entity in args.HitEntities) - { - if (HasComp(entity)) - { - _audio.PlayPvs("/Audio/Effects/lightburn.ogg", entity); - args.ModifiersList.Add(component.Modifiers); - if (_random.Prob(component.DisableChance)) - _statusEffects.TryAddStatusEffect(entity, "PsionicsDisabled", TimeSpan.FromSeconds(10), true, "PsionicsDisabled"); - } - - if (TryComp(entity, out var swapped)) - { - _mindSwapPowerSystem.Swap(entity, swapped.OriginalEntity, true); - return; - } - - if (component.Punish && HasComp(entity) && !HasComp(entity) && _random.Prob(0.5f)) - _electrocutionSystem.TryDoElectrocution(args.User, null, 20, TimeSpan.FromSeconds(5), false); - } - } - - private void OnInit(EntityUid uid, PsionicComponent component, ComponentInit args) - { - if (!component.Removable) - return; - - if (!TryComp(uid, out var factions)) - return; - - if (_faction.IsMember((uid, factions), "GlimmerMonster")) - return; - - _faction.AddFaction((uid, factions), "PsionicInterloper"); - } - - private void OnRemove(EntityUid uid, PsionicComponent component, ComponentRemove args) - { - if (!TryComp(uid, out var factions)) - return; - - _faction.RemoveFaction((uid, factions), "PsionicInterloper"); - } - - private void OnStamHit(EntityUid uid, AntiPsionicWeaponComponent component, StaminaMeleeHitEvent args) - { - var bonus = false; - foreach (var stam in args.HitList) - { - if (HasComp(stam.Entity)) - bonus = true; - } - - if (!bonus) - return; - - - args.FlatModifier += component.PsychicStaminaDamage; - } - - /// - /// Makes the entity psionic if it is possible. - /// Ignores rolling and rerolling prevention. - /// - public bool TryMakePsionic(Entity ent) - { - if (HasComp(ent)) - return false; - - if (!_cfg.GetCVar(DCCVars.PsionicRollsEnabled)) - return false; - - var warn = CompOrNull(ent)?.Warn ?? true; - _psionicAbilitiesSystem.AddPsionics(ent, warn); - return true; - } - - public void RollPsionics(EntityUid uid, PotentialPsionicComponent component, bool applyGlimmer = true, float multiplier = 1f) - { - - var chance = component.Chance; - if (TryComp(uid, out var bonus)) - { - chance *= bonus.Multiplier; - chance += bonus.FlatBonus; - } - - if (applyGlimmer) - chance += ((float) _glimmerSystem.Glimmer / 1000); - - chance *= multiplier; - - chance = Math.Clamp(chance, 0, 1); - - if (_random.Prob(chance)) - TryMakePsionic((uid, component)); - } - - public void RerollPsionics(EntityUid uid, PotentialPsionicComponent? psionic = null, float bonusMuliplier = 1f) - { - if (!Resolve(uid, ref psionic, false)) - return; - - if (psionic.Rerolled) - return; - - RollPsionics(uid, psionic, multiplier: bonusMuliplier); - psionic.Rerolled = true; - } - - public void GrantNewPsionicReroll(EntityUid uid, PotentialPsionicComponent? psionic = null) - { - if (!Resolve(uid, ref psionic, false)) - return; - - psionic.Rerolled = false; - } - } -} diff --git a/Content.Server/Nyanotrasen/Research/Oracle/OracleComponent.cs b/Content.Server/Nyanotrasen/Research/Oracle/OracleComponent.cs index 9651ff89bd..fbd8e03bd0 100644 --- a/Content.Server/Nyanotrasen/Research/Oracle/OracleComponent.cs +++ b/Content.Server/Nyanotrasen/Research/Oracle/OracleComponent.cs @@ -41,7 +41,7 @@ public sealed partial class OracleComponent : Component [DataField("rewardReagents", customTypeSerializer: typeof(PrototypeIdListSerializer))] public IReadOnlyList RewardReagents = new[] { - "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "Wine", "Blood", "Ichor" + "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "Ambrosia", "Nectar", "Wine", "Blood", "Ichor", }; [DataField("demandMessages")] diff --git a/Content.Server/Nyanotrasen/Research/Oracle/OracleSystem.cs b/Content.Server/Nyanotrasen/Research/Oracle/OracleSystem.cs index b04df990bd..d501ea0890 100644 --- a/Content.Server/Nyanotrasen/Research/Oracle/OracleSystem.cs +++ b/Content.Server/Nyanotrasen/Research/Oracle/OracleSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Chat.Systems; using Content.Server.Chemistry.Containers.EntitySystems; using Content.Server.Fluids.EntitySystems; using Content.Server.Psionics; +using Content.Shared._DV.Psionics.Components; using Content.Shared.Abilities.Psionics; using Content.Shared.Chat; using Content.Shared.Chemistry.Components; @@ -29,15 +30,16 @@ namespace Content.Server.Research.Oracle; public sealed class OracleSystem : EntitySystem { + [Dependency] private readonly IAdminLogManager _adminLog = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly IChatManager _chatManager = default!; - [Dependency] private readonly SolutionContainerSystem _solutionSystem = default!; + [Dependency] private readonly ISharedPlayerManager _playerManager = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionSystem = default!; + [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; [Dependency] private readonly PuddleSystem _puddleSystem = default!; - [Dependency] private readonly IAdminLogManager _adminLog = default!; - [Dependency] private readonly SharedResearchSystem _research = default!; + [Dependency] private readonly SharedResearchSystem _research = default!; public override void Update(float frameTime) { @@ -79,49 +81,41 @@ public sealed class OracleSystem : EntitySystem NextItem(component); } - private void OnInteractHand(EntityUid uid, OracleComponent component, InteractHandEvent args) + private void OnInteractHand(Entity oracle, ref InteractHandEvent args) { - if (!HasComp(args.User) || HasComp(args.User)) + if (!HasComp(args.User) + || !_playerManager.TryGetSessionByEntity(args.User, out var session)) return; - if (!TryComp(args.User, out var actor)) - return; - - var message = Loc.GetString("oracle-current-item", ("item", component.DesiredPrototype.Name)); + var message = Loc.GetString("oracle-current-item", ("item", oracle.Comp.DesiredPrototype.Name)); var messageWrap = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message", ("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")), ("message", message)); _chatManager.ChatMessageToOne(ChatChannel.Telepathic, - message, messageWrap, uid, false, actor.PlayerSession.Channel, Color.PaleVioletRed); + message, messageWrap, oracle, false, session.Channel, Color.PaleVioletRed); - if (component.LastDesiredPrototype != null) + if (oracle.Comp.LastDesiredPrototype != null) { - var message2 = Loc.GetString("oracle-previous-item", ("item", component.LastDesiredPrototype.Name)); + var message2 = Loc.GetString("oracle-previous-item", ("item", oracle.Comp.LastDesiredPrototype.Name)); var messageWrap2 = Loc.GetString("chat-manager-send-telepathic-chat-wrap-message", ("telepathicChannelName", Loc.GetString("chat-manager-telepathic-channel-name")), ("message", message2)); _chatManager.ChatMessageToOne(ChatChannel.Telepathic, - message2, messageWrap2, uid, false, actor.PlayerSession.Channel, Color.PaleVioletRed); + message2, messageWrap2, oracle, false, session.Channel, Color.PaleVioletRed); } } private void AddInsertDesiredItemVerb(Entity ent, ref GetVerbsEvent args) { - if (!args.CanAccess || !args.CanInteract || args.Using == null) - return; - - if (HasComp(args.Using)) - return; - - if (!TryComp(args.Using, out MetaDataComponent? meta)) - return; - - if (HasComp(args.User)) - return; - - if (meta.EntityPrototype == null) + if (!args.CanAccess + || !args.CanInteract + || args.Using == null + || HasComp(args.Using) + || !TryComp(args.Using, out MetaDataComponent? meta) + || HasComp(args.User) + || meta.EntityPrototype == null) return; var argsUser = args.User; @@ -147,16 +141,10 @@ public sealed class OracleSystem : EntitySystem private void DoOnInteractUsing(Entity oracle, EntityUid user, EntityUid used) { - if (HasComp(used)) - return; - - if (!TryComp(used, out MetaDataComponent? meta)) - return; - - if (HasComp(user)) - return; - - if (meta.EntityPrototype == null) + if (HasComp(used) + || !TryComp(used, out MetaDataComponent? meta) + || HasComp(user) + || meta.EntityPrototype == null) return; var validItem = CheckValidity(meta.EntityPrototype, oracle.Comp.DesiredPrototype); @@ -291,7 +279,7 @@ public sealed class OracleSystem : EntitySystem { if ( researchServers.Count == 0 - || researchServers.Any(server => + || researchServers.Any(server => _research.IsTechnologyUnlocked(server.database.Owner, tech, server.database.Comp) || _research.IsTechnologyAvailable(server.database.Comp, tech, server.disciplineTiers) ) diff --git a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs index ca0e494967..6320783b44 100644 --- a/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs +++ b/Content.Server/Nyanotrasen/Research/SophicScribe/SophicScribeSystem.cs @@ -1,8 +1,8 @@ using Content.Server.Chat.Systems; -using Content.Server.Nyanotrasen.StationEvents.Events; using Content.Server.Radio.EntitySystems; using Content.Shared.Chat; -using Content.Shared._DV.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.StationEvents.Events; using Content.Shared.Interaction; using Content.Shared.Psionics.Glimmer; using Content.Shared.Radio; @@ -65,7 +65,7 @@ public sealed partial class SophicScribeSystem : EntitySystem _chat.TrySendInGameICMessage(uid, Loc.GetString("glimmer-report", ("level", _glimmerSystem.Glimmer)), InGameICChatType.Speak, true); } - private void OnGlimmerEventEnded(GlimmerEventEndedEvent args) + private void OnGlimmerEventEnded(ref GlimmerEventEndedEvent args) { var query = EntityQueryEnumerator(); while (query.MoveNext(out var scribe, out _)) @@ -74,7 +74,7 @@ public sealed partial class SophicScribeSystem : EntitySystem // mind entities when... var speaker = scribe; - if (TryComp(scribe, out var swapped)) + if (TryComp(scribe, out var swapped)) { speaker = swapped.OriginalEntity; } diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/GlimmerEventComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/GlimmerEventComponent.cs index 7d10f6a440..e989f7929e 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Components/GlimmerEventComponent.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Components/GlimmerEventComponent.cs @@ -6,27 +6,27 @@ public sealed partial class GlimmerEventComponent : Component /// /// Minimum glimmer value for event to be eligible. (Should be 100 at lowest.) /// - [DataField("minimumGlimmer")] + [DataField] public int MinimumGlimmer = 100; /// /// Maximum glimmer value for event to be eligible. (Remember 1000 is max glimmer period.) /// - [DataField("maximumGlimmer")] + [DataField] public int MaximumGlimmer = 1000; /// /// Will be used for _random.Next and subtracted from glimmer. /// Lower bound. /// - [DataField("glimmerBurnLower")] + [DataField] public int GlimmerBurnLower = 25; /// /// Will be used for _random.Next and subtracted from glimmer. /// Upper bound. /// - [DataField("glimmerBurnUpper")] + [DataField] public int GlimmerBurnUpper = 70; [DataField("report")] diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/MassMindSwapRuleComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/MassMindSwapRuleComponent.cs index 7c17495b1c..16706665c2 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Components/MassMindSwapRuleComponent.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Components/MassMindSwapRuleComponent.cs @@ -10,7 +10,7 @@ public sealed partial class MassMindSwapRuleComponent : Component /// /// The mind swap is only temporary if true. /// - [DataField("isTemporary")] + [DataField] public bool IsTemporary; [DataField] diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericFryRuleComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericFryRuleComponent.cs deleted file mode 100644 index 6f5b0f4d9c..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericFryRuleComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Server.Nyanotrasen.StationEvents.Events; -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(NoosphericFryRule))] -public sealed partial class NoosphericFryRuleComponent : Component -{ -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericStormRuleComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericStormRuleComponent.cs deleted file mode 100644 index fe3911ccfa..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericStormRuleComponent.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Content.Server.Nyanotrasen.StationEvents.Events; -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(NoosphericStormRule))] -public sealed partial class NoosphericStormRuleComponent : Component -{ - /// - /// How many potential psionics should be awakened at most. - /// - [DataField("maxAwaken")] - public int MaxAwaken = 3; - - /// - /// - [DataField("baseGlimmerAddMin")] - public int BaseGlimmerAddMin = 65; - - /// - /// - [DataField("baseGlimmerAddMax")] - public int BaseGlimmerAddMax = 85; - - /// - /// Multiply the EventSeverityModifier by this to determine how much extra glimmer to add. - /// - [DataField("glimmerSeverityCoefficient")] - public float GlimmerSeverityCoefficient = 0.25f; -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericZapRuleComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericZapRuleComponent.cs deleted file mode 100644 index e6fa370f93..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Components/NoosphericZapRuleComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Content.Server.Nyanotrasen.StationEvents.Events; -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(NoosphericZapRule))] -public sealed partial class NoosphericZapRuleComponent : Component -{ -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Components/PsionicCatGotYourTongueRuleComponent.cs b/Content.Server/Nyanotrasen/StationEvents/Components/PsionicCatGotYourTongueRuleComponent.cs deleted file mode 100644 index 8f35639982..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Components/PsionicCatGotYourTongueRuleComponent.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Content.Server.Nyanotrasen.StationEvents.Events; -using Robust.Shared.Audio; -using Content.Server.StationEvents.Events; - -namespace Content.Server.StationEvents.Components; - -[RegisterComponent, Access(typeof(PsionicCatGotYourTongueRule))] -public sealed partial class PsionicCatGotYourTongueRuleComponent : Component -{ - [DataField("minDuration")] - public TimeSpan MinDuration = TimeSpan.FromSeconds(20); - - [DataField("maxDuration")] - public TimeSpan MaxDuration = TimeSpan.FromSeconds(80); - - [DataField("sound")] - public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Nyanotrasen/Voice/Felinid/cat_scream1.ogg"); -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs index 07356026c9..094c0df86a 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/GlimmerEventSystem.cs @@ -1,35 +1,24 @@ using Content.Server.Psionics.Glimmer; using Content.Server.StationEvents.Events; +using Content.Shared._DV.StationEvents.Events; using Content.Shared.GameTicking.Components; using Content.Shared.Psionics.Glimmer; -namespace Content.Server.Nyanotrasen.StationEvents.Events +namespace Content.Server.Nyanotrasen.StationEvents.Events; + +public sealed class GlimmerEventSystem : StationEventSystem { - public sealed class GlimmerEventSystem : StationEventSystem + [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + + protected override void Ended(EntityUid uid, GlimmerEventComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) { - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + base.Ended(uid, component, gameRule, args); + var glimmerBurned = RobustRandom.Next(component.GlimmerBurnLower, component.GlimmerBurnUpper); - protected override void Ended(EntityUid uid, GlimmerEventComponent component, GameRuleComponent gameRule, GameRuleEndedEvent args) - { - base.Ended(uid, component, gameRule, args); + _glimmerSystem.Glimmer -= glimmerBurned; - var glimmerBurned = RobustRandom.Next(component.GlimmerBurnLower, component.GlimmerBurnUpper); - _glimmerSystem.Glimmer -= glimmerBurned; - - var reportEv = new GlimmerEventEndedEvent(component.SophicReport, glimmerBurned); - RaiseLocalEvent(reportEv); - } - } - - public sealed class GlimmerEventEndedEvent : EntityEventArgs - { - public string Message = ""; - public int GlimmerBurned = 0; - - public GlimmerEventEndedEvent(string message, int glimmerBurned) - { - Message = message; - GlimmerBurned = glimmerBurned; - } + var reportEv = new GlimmerEventEndedEvent(component.SophicReport, glimmerBurned); + RaiseLocalEvent(ref reportEv); } } + diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs index 5b0f24350a..bb8d744d6b 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs +++ b/Content.Server/Nyanotrasen/StationEvents/Events/MassMindSwapRule.cs @@ -1,9 +1,9 @@ -using Content.Server.Abilities.Psionics; using Content.Server.Chat.Systems; -using Content.Server.Psionics; using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Events; -using Content.Shared.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; using Content.Shared.GameTicking.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; @@ -20,10 +20,11 @@ namespace Content.Server.Nyanotrasen.StationEvents.Events; internal sealed class MassMindSwapRule : StationEventSystem { [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly MindSwapPowerSystem _mindSwap = default!; - [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly SharedMindSwapPowerSystem _mindSwap = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; private TimeSpan _warningSoundLength; private ResolvedSoundSpecifier _resolvedWarningSound = String.Empty; @@ -70,17 +71,17 @@ internal sealed class MassMindSwapRule : StationEventSystem psionicActors = new(); var query = EntityQueryEnumerator(); - while (query.MoveNext(out var psion, out _, out _)) + while (query.MoveNext(out var psion, out _, out var mobState)) { - if (_mobStateSystem.IsAlive(psion) && !HasComp(psion)) - { - psionicPool.Add(psion); + if (!_mobStateSystem.IsAlive(psion, mobState) || !_psionic.CanBeTargeted(psion)) + continue; - if (HasComp(psion)) - { - // This is so we don't bother mindswapping NPCs with NPCs. - psionicActors.Add(psion); - } + psionicPool.Add(psion); + + if (HasComp(psion)) + { + // This is so we don't bother mindswapping NPCs with NPCs. + psionicActors.Add(psion); } } @@ -107,13 +108,8 @@ internal sealed class MassMindSwapRule : StationEventSystem -/// Fries tinfoil hats and cages -/// -internal sealed class NoosphericFryRule : StationEventSystem -{ - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly InventorySystem _inventorySystem = default!; - [Dependency] private readonly SharedAudioSystem _audioSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly DamageableSystem _damageableSystem = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly FlammableSystem _flammableSystem = default!; - [Dependency] private readonly GlimmerReactiveSystem _glimmerReactiveSystem = default!; - [Dependency] private readonly AnchorableSystem _anchorableSystem = default!; - [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; - [Dependency] private readonly SharedTransformSystem _transformSystem = default!; - - protected override void Started(EntityUid uid, NoosphericFryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - - List<(EntityUid wearer, TinfoilHatComponent worn)> psionicList = new(); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var psion, out _, out _)) - { - if (!_mobStateSystem.IsAlive(psion)) - continue; - - if (!_inventorySystem.TryGetSlotEntity(psion, "head", out var headItem)) - continue; - - if (!TryComp(headItem, out var tinfoil)) - continue; - - psionicList.Add((psion, tinfoil)); - } - - foreach (var pair in psionicList) - { - if (pair.worn.DestroyOnFry) - { - QueueDel(pair.worn.Owner); - Spawn("Ash", Transform(pair.wearer).Coordinates); - _popupSystem.PopupEntity(Loc.GetString("psionic-burns-up", ("item", pair.worn.Owner)), pair.wearer, Filter.Pvs(pair.worn.Owner), true, Shared.Popups.PopupType.MediumCaution); - _audioSystem.PlayEntity("/Audio/Effects/lightburn.ogg", Filter.Pvs(pair.worn.Owner), pair.worn.Owner, true); - } else - { - _popupSystem.PopupEntity(Loc.GetString("psionic-burn-resist", ("item", pair.worn.Owner)), pair.wearer, Filter.Pvs(pair.worn.Owner), true, Shared.Popups.PopupType.SmallCaution); - _audioSystem.PlayEntity("/Audio/Effects/lightburn.ogg", Filter.Pvs(pair.worn.Owner), pair.worn.Owner, true); - } - - DamageSpecifier damage = new(); - damage.DamageDict.Add("Heat", 2.5); - damage.DamageDict.Add("Shock", 2.5); - - if (_glimmerSystem.Glimmer > 500 && _glimmerSystem.Glimmer < 750) - { - damage *= 2; - if (TryComp(pair.wearer, out var flammableComponent)) - { - flammableComponent.FireStacks += 1; - _flammableSystem.Ignite(pair.wearer, pair.wearer, flammableComponent); - } - } else if (_glimmerSystem.Glimmer > 750) - { - damage *= 3; - if (TryComp(pair.wearer, out var flammableComponent)) - { - flammableComponent.FireStacks += 2; - _flammableSystem.Ignite(pair.wearer, pair.wearer, flammableComponent); - } - } - - _damageableSystem.TryChangeDamage(pair.wearer, damage, true, true); - } - - // for probers: - var queryReactive = EntityQueryEnumerator(); - while (queryReactive.MoveNext(out var reactive, out _, out var xform, out var physics)) - { - // shoot out one bolt of lighting... - _glimmerReactiveSystem.BeamRandomNearProber(reactive, 1, 12); - - // try to anchor if we can - if (!xform.Anchored) - { - var coordinates = xform.Coordinates; - var gridUid = xform.GridUid; - if (!TryComp(gridUid, out var grid)) - continue; - - var tileIndices = grid.TileIndicesFor(coordinates); - - if (_anchorableSystem.TileFree(grid, tileIndices, physics.CollisionLayer, physics.CollisionMask)) - _transformSystem.AnchorEntity(reactive, xform); - } - - if (!TryComp(reactive, out var power)) - continue; - - // If it's been turned off, turn it back on. - if (power.PowerDisabled) - _powerReceiverSystem.TogglePower(reactive, false); - } - } -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs deleted file mode 100644 index 39cdc77ab0..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericZapRule.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Content.Server.Popups; -using Content.Server.Psionics; -using Content.Server.StationEvents.Components; -using Content.Server.StationEvents.Events; -using Content.Server.Stunnable; -using Content.Shared.Abilities.Psionics; -using Content.Shared.GameTicking.Components; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.StatusEffect; - -namespace Content.Server.Nyanotrasen.StationEvents.Events; - -/// -/// Zaps everyone, rolling psionics and disorienting them -/// -internal sealed class NoosphericZapRule : StationEventSystem -{ - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly StunSystem _stunSystem = default!; - [Dependency] private readonly PopupSystem _popupSystem = default!; - [Dependency] private readonly PsionicsSystem _psionicsSystem = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - - protected override void Started(EntityUid uid, NoosphericZapRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - - var query = EntityQueryEnumerator(); - - while (query.MoveNext(out var psion, out var potentialPsionicComponent, out _)) - { - if (!_mobStateSystem.IsAlive(psion) || HasComp(psion)) - continue; - - _stunSystem.TryAddParalyzeDuration(psion, TimeSpan.FromSeconds(5)); - _statusEffectsSystem.TryAddStatusEffect(psion, "Stutter", TimeSpan.FromSeconds(10), false, "StutteringAccent"); - - if (HasComp(psion)) - _popupSystem.PopupEntity(Loc.GetString("noospheric-zap-seize"), psion, psion, Shared.Popups.PopupType.LargeCaution); - else - { - if (potentialPsionicComponent.Rerolled) - { - potentialPsionicComponent.Rerolled = false; - _popupSystem.PopupEntity(Loc.GetString("noospheric-zap-seize-potential-regained"), psion, psion, Shared.Popups.PopupType.LargeCaution); - } else - { - _psionicsSystem.RollPsionics(psion, potentialPsionicComponent, multiplier: 0.25f); - _popupSystem.PopupEntity(Loc.GetString("noospheric-zap-seize"), psion, psion, Shared.Popups.PopupType.LargeCaution); - } - } - } - } -} diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs b/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs deleted file mode 100644 index fdc4b259ce..0000000000 --- a/Content.Server/Nyanotrasen/StationEvents/Events/PsionicCatGotYourTongueRule.cs +++ /dev/null @@ -1,52 +0,0 @@ -using Content.Server.Psionics; -using Content.Server.StationEvents.Components; -using Content.Server.StationEvents.Events; -using Content.Shared.Abilities.Psionics; -using Content.Shared.GameTicking.Components; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.StatusEffect; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Player; -using Robust.Shared.Random; - -namespace Content.Server.Nyanotrasen.StationEvents.Events; - -/// -/// Mutes everyone for a random amount of time. -/// -internal sealed class PsionicCatGotYourTongueRule : StationEventSystem -{ - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - [Dependency] private readonly SharedAudioSystem _sharedAudioSystem = default!; - - - protected override void Started(EntityUid uid, PsionicCatGotYourTongueRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) - { - base.Started(uid, component, gameRule, args); - - List psionicList = new(); - - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var psion, out _, out _)) - { - if (_mobStateSystem.IsAlive(psion) && !HasComp(psion)) - psionicList.Add(psion); - } - - foreach (var psion in psionicList) - { - var duration = _robustRandom.Next(component.MinDuration, component.MaxDuration); - - _statusEffectsSystem.TryAddStatusEffect(psion, - "Muted", - duration, - false, - "Muted"); - - _sharedAudioSystem.PlayGlobal(component.Sound, Filter.Entities(psion), false); - } - } -} diff --git a/Content.Server/Zombies/ZombieSystem.Transform.cs b/Content.Server/Zombies/ZombieSystem.Transform.cs index c8149ef2b5..a0845a76c5 100644 --- a/Content.Server/Zombies/ZombieSystem.Transform.cs +++ b/Content.Server/Zombies/ZombieSystem.Transform.cs @@ -1,3 +1,4 @@ +using Content.Server._DV.Psionics.Systems; using Content.Server.Administration.Managers; using Content.Server.Atmos.Components; using Content.Server.Body.Components; @@ -13,7 +14,7 @@ using Content.Server.NPC.HTN; using Content.Server.NPC.Systems; using Content.Server.StationEvents.Components; using Content.Server.Speech.Components; -using Content.Shared.Abilities.Psionics; // DeltaV +using Content.Shared._DV.Psionics.Components; // DeltaV using Content.Shared.Body.Components; using Content.Shared.CombatMode; using Content.Shared.CombatMode.Pacification; @@ -71,6 +72,7 @@ public sealed partial class ZombieSystem [Dependency] private readonly NPCSystem _npc = default!; [Dependency] private readonly TagSystem _tag = default!; [Dependency] private readonly ISharedPlayerManager _player = default!; + [Dependency] private readonly PsionicSystem _psionic = default!; // DeltaV private static readonly ProtoId InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell"; private static readonly ProtoId CannotSuicideTag = "CannotSuicide"; @@ -144,18 +146,11 @@ public sealed partial class ZombieSystem RemComp(target); RemComp(target); - if (TryComp(target, out var psionic)) // DeltaV - Prevent psionic zombies - { - if (psionic.ActivePowers.Count > 0) - { - foreach (var power in psionic.ActivePowers) - { - RemComp(target, power); - } - psionic.ActivePowers.Clear(); - } - RemComp(target); - } + // DeltaV Start - Prevent Psionic Zombies + RemComp(target); + if (HasComp(target)) + _psionic.MindBreakEntity(target, false, true); + // DeltaV End - Prevent Psionic Zombies //funny voice var accentType = "zombie"; diff --git a/Content.Server/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs b/Content.Server/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs deleted file mode 100644 index b3b1929f73..0000000000 --- a/Content.Server/_DV/Abilities/Psionics/FracturedFormPowerSystem.cs +++ /dev/null @@ -1,346 +0,0 @@ -using Content.Server.Abilities.Psionics; -using Content.Server.Chat.Systems; -using Content.Server.Cloning; -using Content.Server.DoAfter; -using Content.Server.Mind; -using Content.Server.Psionics; -using Content.Server.Station.Systems; -using Content.Shared._DV.Abilities.Psionics; -using Content.Shared._DV.Species; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Actions.Events; -using Content.Shared.Bed.Sleep; -using Content.Shared.Chat; -using Content.Shared.DoAfter; -using Content.Shared.Examine; -using Content.Shared.Humanoid.Prototypes; -using Content.Shared.Mind.Components; -using Content.Shared.Mobs.Components; -using Content.Shared.Mobs.Systems; -using Content.Shared.Popups; -using Content.Shared.Preferences; -using Content.Shared.Psionics.Events; -using Content.Shared.SSDIndicator; -using Robust.Server.GameObjects; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server._DV.Abilities.Psionics; - -public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem -{ - [Dependency] private readonly ChatSystem _chat = default!; - [Dependency] private readonly CloningSystem _cloning = default!; - [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly IGameTiming _timing = default!; - [Dependency] private readonly IPrototypeManager _prototype = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly MobStateSystem _mobState = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly SleepingSystem _sleeping = default!; - [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; - [Dependency] private readonly StationSystem _station = default!; - [Dependency] private readonly TransformSystem _transform = default!; - - // holy initialize perf? but better for it to happen once than the double dict lookup every tick!! - private EntityQuery _mindContainerQuery; - private EntityQuery _bodyQuery; - private EntityQuery _sleepingQuery; - private EntityQuery _ssdQuery; - private EntityQuery _fracturedQuery; - private EntityQuery _mobStateQuery; - private EntityQuery _forcedSleepQuery; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnDispelled); - SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnExamine); - - _sleepingQuery = GetEntityQuery(); - _ssdQuery = GetEntityQuery(); - _fracturedQuery = GetEntityQuery(); - _mindContainerQuery = GetEntityQuery(); - _bodyQuery = GetEntityQuery(); - _mobStateQuery = GetEntityQuery(); - _forcedSleepQuery = GetEntityQuery(); - } - - private void OnInit(Entity entity, ref ComponentInit args) - { - var component = entity.Comp; - _actions.AddAction(entity, ref component.FracturedFormActionEntity, component.FracturedFormActionId); - _actions.StartUseDelay(component.FracturedFormActionEntity); - if (TryComp(entity, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.FracturedFormActionEntity; - psionic.ActivePowers.Add(component); - } - - // Next random swap is between 5 to 20 minutes. - component.NextSwap = _timing.CurTime + TimeSpan.FromSeconds(_random.Next(300, 1200)); - - if (HasComp(entity)) return; // Don't generate a new body if we're already part of a network. - var bodyComp = AddComp(entity); - bodyComp.ControllingForm = entity.Owner; - component.Bodies.Add(entity); - GenerateForm(entity); - } - - private void OnShutdown(Entity entity, ref ComponentShutdown args) - { - _actions.RemoveAction(entity.Owner, entity.Comp.FracturedFormActionEntity); - - if (TryComp(entity, out var psionic)) - { - psionic.ActivePowers.Remove(entity.Comp); - } - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - var curTime = _timing.CurTime; - var warnThreshold = TimeSpan.FromSeconds(5); // really should go on the comp - - var ents = EntityQueryEnumerator(); - while (ents.MoveNext(out var uid, out var comp, out var mobState)) - { - // Check sleep warning - if (!comp.SleepWarned && curTime > comp.NextSwap - warnThreshold) - { - comp.SleepWarned = true; - _popups.PopupEntity(Loc.GetString("fractured-form-sleepy"), uid, uid, PopupType.LargeCaution); - _chat.TryEmoteWithChat(uid, "Yawn"); - } - - // Swap check - if (_sleepingQuery.HasComp(uid) || _mobState.IsIncapacitated(uid, mobState) || curTime > comp.NextSwap) - { - Swap((uid, comp)); - } - } - - // Process bodies - var bodies = EntityQueryEnumerator(); - while (bodies.MoveNext(out var uid, out var comp, out var mobState)) - { - // Put to sleep if no sleeping component and no mind - if (!_sleepingQuery.HasComp(uid) && !_mind.GetMind(uid).HasValue) - { - _sleeping.TrySleeping((uid, mobState)); - } - - // Handle SSD indicator - if (_ssdQuery.TryComp(uid, out var ssd) && ssd.IsSSD) - { - ssd.IsSSD = false; - } - - // Cleanup invalid bodies - if (!comp.ControllingForm.IsValid() - || Deleted(comp.ControllingForm) - || !_fracturedQuery.HasComp(comp.ControllingForm)) - { - RemCompDeferred(uid); - } - } - } - - private EntityUid GenerateForm(Entity original) - { - // Form: - // - Same appearance as original - // - Different apperance, still humanoid - // Equipment: - // - Same as original body - // - Nude and helpless - - var xform = Transform(original); - - bool hasClothes = _random.Prob(0.4f); - - EntityUid? newBody; - if (_random.Prob(0.6f) || !_cloning.TryCloning(original, _transform.GetMapCoordinates(original), hasClothes ? original.Comp.CopyClothed : original.Comp.CopyNaked, out newBody)) // Slightly lower chance to copy the original body - { - // Either the dice rolled poorly, or the cloning failed. Either way, make a new body instead. (Or try to) - var validSpecies = new List>(); - var speciesPrototypes = _prototype.EnumeratePrototypes(); - foreach (var proto in speciesPrototypes) - { - var speciesEntityPrototype = _prototype.Index(proto.Prototype); - - if (proto.RoundStart && speciesEntityPrototype.TryGetComponent(out var canBePsionic, Factory) && !SpeciesHiderSystem.IsHidden(proto.ID)) - { - var chance = canBePsionic.Chance; - - if (speciesEntityPrototype.TryGetComponent(out var bonusChance, Factory)) - chance = (chance * bonusChance.Multiplier) + bonusChance.FlatBonus; - - if (chance > 0) - validSpecies.Add(proto.ID); - } - } - var species = _random.Pick(validSpecies); - var character = HumanoidCharacterProfile.RandomWithSpecies(species); - newBody = _stationSpawning.SpawnPlayerMob(xform.Coordinates, hasClothes ? original.Comp.VisitorJob : original.Comp.NakedJob, character, _station.GetOwningStation(original.Owner)); - if (newBody is not { } bodyV || Deleted(bodyV)) - { - Log.Error($"Failed to create a new body for {ToPrettyString(original)}. This is a bug."); - return EntityUid.Invalid; - } - } - - if (newBody is { } body && !Deleted(body)) - { - var bodyComp = AddComp(body); - original.Comp.Bodies.Add(body); - bodyComp.ControllingForm = original.Owner; - return body; - } - - return default!; - } - - private bool IsValidBody(Entity entity, EntityUid body) - { - if (body == entity.Owner) - return false; - if (!entity.Comp.Bodies.Contains(body)) - return false; - if (!_mindContainerQuery.TryComp(body, out var cmind)) - return false; - if (cmind.HasMind) - return false; - if (_forcedSleepQuery.HasComp(body)) - return false; - if (!_mobStateQuery.TryComp(body, out var mobState)) - return false; - if (_mobState.IsIncapacitated(body, mobState)) - return false; - return true; - } - - private bool TryGetValidBody(Entity entity, out EntityUid validBody) - { - foreach (var body in entity.Comp.Bodies) - { - if (!IsValidBody(entity, body)) - continue; - - validBody = body; - return true; - } - validBody = default; - return false; - } - - public bool CanSwap(Entity entity) - { - foreach (var body in entity.Comp.Bodies) - { - if (IsValidBody(entity, body)) - return true; - } - return false; - } - - private void Swap(Entity entity) - { - if (!TryGetValidBody(entity, out var targetBody)) - return; - - _audio.PlayPredicted(entity.Comp.SwapSound, entity, entity); - - // Transfer mind if present - if (_mindContainerQuery.TryComp(entity, out var mindContainer) && mindContainer.Mind.HasValue) - { - _mind.TransferTo(mindContainer.Mind.Value, targetBody); - - } - - // Wake up the target body - if (_sleepingQuery.TryComp(targetBody, out var sleeping)) - { - _sleeping.TryWaking((targetBody, sleeping), false, entity); - } - - // Create new component on target and copy data - var duplicate = AddComp(targetBody); - duplicate.Bodies = entity.Comp.Bodies; - - // Update all body references - foreach (var body in duplicate.Bodies) - { - if (_bodyQuery.TryComp(body, out var bodyComp)) - { - bodyComp.ControllingForm = targetBody; - } - } - - RemCompDeferred(entity, entity.Comp); - } - - private void OnPowerUsed(Entity entity, ref FracturedFormPowerActionEvent args) - { - if (!CanSwap(entity)) - { - _popups.PopupEntity(Loc.GetString("fractured-form-nobodies"), entity, entity, PopupType.Large); - return; - } - - entity.Comp.SleepWarned = true; - _chat.TryEmoteWithChat(entity.Owner, "Yawn", ChatTransmitRange.Normal); - _popups.PopupEntity(Loc.GetString("fractured-form-sleepy"), entity, entity, PopupType.LargeCaution); - var ev = new FracturedFormDoAfterEvent(); - var doAfterArgs = new DoAfterArgs(EntityManager, entity, entity.Comp.ManualSwapTime, ev, entity); - _doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId); - entity.Comp.DoAfter = doAfterId; - _psionics.LogPowerUsed(entity, "fractured form swap", 1, 3); - - args.Handled = true; - } - - private void OnDispelled(Entity entity, ref DispelledEvent args) - { - if (entity.Comp.DoAfter == null) - return; - - _doAfter.Cancel(entity.Comp.DoAfter); - entity.Comp.DoAfter = null; - - args.Handled = true; - } - - private void OnDoAfter(Entity entity, ref FracturedFormDoAfterEvent args) - { - entity.Comp.DoAfter = null; - - if (args.Cancelled || args.Handled) - return; - - _sleeping.TrySleeping(entity.Owner); - } - - private void OnExamine(Entity entity, ref ExaminedEvent args) - { - if (HasComp(entity)) - return; - if (TryComp(args.Examiner, out var fracturedHost) && fracturedHost.Bodies.Contains(entity.Owner)) - args.PushMarkup($"[color=yellow]{Loc.GetString("fractured-form-examine-self", ("ent", entity))}[/color]"); - else - args.PushMarkup($"[color=yellow]{Loc.GetString("fractured-form-ssd", ("ent", entity))}[/color]"); - } -} diff --git a/Content.Server/_DV/Abilities/Psionics/PrecognitionPowerSystem.cs b/Content.Server/_DV/Abilities/Psionics/PrecognitionPowerSystem.cs deleted file mode 100644 index d29b8ab66b..0000000000 --- a/Content.Server/_DV/Abilities/Psionics/PrecognitionPowerSystem.cs +++ /dev/null @@ -1,248 +0,0 @@ -using Content.Server.Chat.Managers; -using Content.Server.DoAfter; -using Content.Server._DV.StationEvents.NextEvent; -using Content.Server.GameTicking; -using Content.Server.Mind; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions.Events; -using Content.Shared.Actions; -using Content.Shared.Actions.Components; -using Content.Shared.Chat; -using Content.Shared.DoAfter; -using Content.Shared.Eye.Blinding.Components; -using Content.Shared.Movement.Systems; -using Content.Shared.Popups; -using Content.Shared.Psionics.Events; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; -using Robust.Shared.Audio.Systems; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; -using Robust.Shared.Player; - -namespace Content.Server.Abilities.Psionics; - -public sealed class PrecognitionPowerSystem : EntitySystem -{ - [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly GameTicker _gameTicker = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedAudioSystem _audio = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly IChatManager _chat = default!; - [Dependency] private readonly IComponentFactory _factory = default!; - [Dependency] private readonly IPrototypeManager _proto = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly MovementModStatusSystem _movementMod = default!; - - - /// - /// A map between game rule prototypes and their results to give. - /// - public Dictionary Results = new(); - - public override void Initialize() - { - base.Initialize(); - CachePrecognitionResults(); - - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnDoAfter); - SubscribeLocalEvent(OnPrototypesReloaded); - } - - private void OnMapInit(Entity ent, ref MapInitEvent args) - { - _actions.AddAction(ent, ref ent.Comp.PrecognitionActionEntity, ent.Comp.PrecognitionActionId); - _actions.StartUseDelay(ent.Comp.PrecognitionActionEntity); - if (TryComp(ent, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = ent.Comp.PrecognitionActionEntity; - psionic.ActivePowers.Add(ent.Comp); - } - } - - private void OnShutdown(EntityUid uid, PrecognitionPowerComponent component, ComponentShutdown args) - { - _actions.RemoveAction(uid, component.PrecognitionActionEntity); - if (TryComp(uid, out var psionic)) - psionic.ActivePowers.Remove(component); - } - - private void OnPowerUsed(EntityUid uid, PrecognitionPowerComponent component, PrecognitionPowerActionEvent args) - { - var ev = new PrecognitionDoAfterEvent(); - var doAfterArgs = new DoAfterArgs(EntityManager, uid, component.UseDelay, ev, uid) - { - BreakOnDamage = true - }; - - // A custom shader for seeing visions would be nice but this will do for now. - _statusEffects.TryAddStatusEffect(uid, "TemporaryBlindness", component.UseDelay, true); - _movementMod.TryUpdateMovementSpeedModDuration(uid, MovementModStatusSystem.PsionicSlowdown, component.UseDelay, 0.5f); - - _doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId); - component.DoAfter = doAfterId; - - var player = _audio.PlayGlobal(component.VisionSound, Filter.Entities(uid), true); - if (player != null) - component.SoundStream = player.Value.Entity; - _psionics.LogPowerUsed(uid, "Precognition"); - args.Handled = true; - } - - /// - /// Upon completion will send a message to the user corrosponding to the next station event to occour. - /// - /// - /// - /// - private void OnDoAfter(EntityUid uid, PrecognitionPowerComponent component, PrecognitionDoAfterEvent args) - { - if (args.Handled) - return; - - if (args.Cancelled) - { - // Need to clean up the applied effects in case of cancel and alert the player. - component.SoundStream = _audio.Stop(component.SoundStream); - _statusEffects.TryRemoveStatusEffect(uid, "TemporaryBlindness"); - _statusEffects.TryRemoveStatusEffect(uid, "SlowedDown"); - - _popup.PopupEntity( - Loc.GetString("psionic-power-precognition-failure-by-damage"), - uid, - uid, - PopupType.SmallCaution); - - if (_actions.GetAction(component.PrecognitionActionEntity) is {} actionData) - { - _actions.SetCooldown( - actionData.Owner, - _gameTicker.RoundDuration(), - _gameTicker.RoundDuration() + TimeSpan.FromSeconds(15)); - } - } - - // Determines the window that will be looked at for events, avoiding events that are too close or too far to be useful. - var minDetectWindow = TimeSpan.FromSeconds(30); - var maxDetectWindow = TimeSpan.FromMinutes(10); - - if (!TryComp(uid, out var actor)) - return; - var nextEvent = FindEarliestNextEvent(minDetectWindow, maxDetectWindow); - LocId? message = nextEvent?.NextEventId is {} nextEventId - ? GetResultMessage(nextEventId) - // A special message given if there is no event within the time window. - : "psionic-power-precognition-no-event-result-message"; - - if (_random.Prob(component.RandomResultChance)) // This will replace the proper result message with a random one occasionaly to simulate some unreliablity. - message = GetRandomResult(); - - if (message is not {} locId) // If there is no message to send don't bother trying to send it. - return; - - // Send a message describing the vision they see - var msg = Loc.GetString(locId); - _chat.ChatMessageToOne(ChatChannel.Server, - msg, - Loc.GetString("chat-manager-server-wrap-message", ("message", msg)), - uid, - false, - actor.PlayerSession.Channel, - Color.PaleVioletRed); - - component.DoAfter = null; - } - - /// - /// Gets the precognition result message corosponding to the passed event id. - /// - /// message string corosponding to the event id passed - private LocId? GetResultMessage(EntProtoId eventId) - { - if (!Results.TryGetValue(eventId, out var result)) - { - Log.Error($"Prototype {eventId} does not have an associated precognitionResult!"); - return null; - } - - return result.Message; - } - - /// - /// - /// The locale message id of a weighted randomly chosen precognition result - public LocId? GetRandomResult() - { - // funny weighted random - var sumOfWeights = 0f; - foreach (var precognitionResult in Results.Values) - sumOfWeights += precognitionResult.Weight; - - sumOfWeights = (float) _random.Next((double) sumOfWeights); - foreach (var precognitionResult in Results.Values) - { - sumOfWeights -= precognitionResult.Weight; - - if (sumOfWeights <= 0f) - return precognitionResult.Message; - } - - Log.Error("Precognition result was not found after weighted pick process!"); - return null; - } - - /// - /// Gets the soonest nextEvent to occur within the window. - /// - /// The earliest reletive time that will be return a nextEvent - /// The latest reletive latest time that will be return a nextEvent - /// Component for the next event to occour if one exists in the window. - private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow) - { - TimeSpan? earliestNextEventTime = null; - NextEventComponent? earliestNextEvent = null; - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var nextEventComponent)) - { - // Update if the event is the most recent event that isnt too close or too far from happening to be of use - if (nextEventComponent.NextEventTime > _gameTicker.RoundDuration() + minDetectWindow - && nextEventComponent.NextEventTime < _gameTicker.RoundDuration() + maxDetectWindow - && earliestNextEvent == null - || nextEventComponent.NextEventTime < earliestNextEventTime) - { - earliestNextEvent ??= nextEventComponent; - } - } - return earliestNextEvent; - } - - private void OnPrototypesReloaded(PrototypesReloadedEventArgs args) - { - if (!args.WasModified()) - return; - - CachePrecognitionResults(); - } - - private void CachePrecognitionResults() - { - Results.Clear(); - foreach (var prototype in _proto.EnumeratePrototypes()) - { - if (prototype.Abstract) - continue; - - if (!prototype.TryGetComponent(out var precognitionResult, _factory)) - continue; - - Results.Add(prototype.ID, precognitionResult); - } - } -} diff --git a/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionComponent.cs b/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionComponent.cs deleted file mode 100644 index 752e2933b5..0000000000 --- a/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionComponent.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Robust.Shared.Audio; -using Content.Shared.Actions; -using Content.Shared.DoAfter; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared._DV.Abilities.Psionics; // why is this in the Shared namespace but the Server folder? - -[RegisterComponent, AutoGenerateComponentPause] -public sealed partial class PsionicEruptionPowerComponent : Component -{ - [DataField] - public DoAfterId? DoAfter; - [DataField] - public EntProtoId EruptionActionId = "ActionEruption"; - [DataField] - public EntityUid? EruptionActionEntity; - [DataField] - public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/heartbeat_fast.ogg"); - [DataField] - public SoundSpecifier SoundDetonate = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/eruption.ogg"); - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] - public TimeSpan NextAnnoy = TimeSpan.FromSeconds(5); - [DataField(customTypeSerializer: typeof(TimeOffsetSerializer)), AutoPausedField] - public TimeSpan NextSpark = TimeSpan.MaxValue; - [DataField] - public bool Warned = false; -} diff --git a/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionSystem.cs b/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionSystem.cs deleted file mode 100644 index d01a2047e8..0000000000 --- a/Content.Server/_DV/Abilities/Psionics/PsionicEruption/PsionicEruptionSystem.cs +++ /dev/null @@ -1,235 +0,0 @@ -using Content.Server._DV.Psionics; -using Content.Server.Abilities.Psionics; -using Content.Server.DoAfter; -using Content.Server.EUI; -using Content.Server.Explosion.EntitySystems; -using Content.Server.Jittering; -using Content.Server.Lightning; -using Content.Server.Mind; -using Content.Shared._DV.Abilities.Psionics; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Actions.Events; -using Content.Shared.Body.Components; -using Content.Shared.Body.Systems; -using Content.Shared.DoAfter; -using Content.Shared.Gibbing; -using Content.Shared.Mind; -using Content.Shared.Popups; -using Content.Shared.Psionics.Events; -using Content.Shared.Psionics.Glimmer; -using Robust.Server.Audio; -using Robust.Server.Player; -using Robust.Shared.Audio; -using Robust.Shared.Prototypes; -using Robust.Shared.Random; -using Robust.Shared.Timing; - -namespace Content.Server._DV.Abilities.Psionics; - -public sealed class PsionicEruptionSystem : EntitySystem -{ - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly DoAfterSystem _doAfter = default!; - [Dependency] private readonly EuiManager _eui = default!; - [Dependency] private readonly ExplosionSystem _explosion = default!; - [Dependency] private readonly GlimmerSystem _glimmer = default!; - [Dependency] private readonly IGameTiming _gameTiming = default!; - [Dependency] private readonly IPlayerManager _player = default!; - [Dependency] private readonly IRobustRandom _random = default!; - [Dependency] private readonly JitteringSystem _jittering = default!; - [Dependency] private readonly LightningSystem _lightning = default!; - [Dependency] private readonly MindSystem _mind = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedBodySystem _body = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - [Dependency] private readonly GibbingSystem _gibbing = default!; - - private static readonly EntProtoId? Sparks = "EffectSparks"; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - - SubscribeLocalEvent(OnDispelled); - SubscribeLocalEvent(OnDoAfter); - } - - private void OnInit(Entity entity, ref ComponentInit args) - { - var component = entity.Comp; - _actions.AddAction(entity, ref component.EruptionActionEntity, component.EruptionActionId); - - if (_actions.GetAction(component.EruptionActionEntity) is { Comp.UseDelay: not null } action) - { - _actions.StartUseDelay(action.Owner); - } - - if (TryComp(entity, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = component.EruptionActionEntity; - psionic.ActivePowers.Add(component); - } - } - - private void ShowWarning(Entity entity) - { - var comp = entity.Comp; - if (comp.Warned) - return; - MindComponent? mind; - if (!_mind.TryGetMind(entity, out var _, out mind)) - return; - if (mind.UserId == null || !_player.TryGetSessionById(mind.UserId.Value, out var client)) - return; - _eui.OpenEui(new EruptionWarningEui(), client); - comp.Warned = true; - } - - private void OnShutdown(Entity entity, ref ComponentShutdown args) - { - _actions.RemoveAction(entity.Owner, entity.Comp.EruptionActionEntity); - - if (TryComp(entity, out var psionic)) - { - psionic.ActivePowers.Remove(entity.Comp); - } - } - - private void OnDispelled(Entity entity, ref DispelledEvent args) - { - if (entity.Comp.DoAfter == null) - return; - - _doAfter.Cancel(entity.Comp.DoAfter); - entity.Comp.DoAfter = null; - - args.Handled = true; - } - - private void OnPowerUsed(Entity entity, ref PsionicEruptionPowerActionEvent args) - { - var component = entity.Comp; - int detonateTime = 10; - int sparkFrom = 5; - - if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical) - { - detonateTime = 30; - sparkFrom = 15; - } - - var ev = new PsionicEruptionDoAfterEvent(); - var doAfterArgs = new DoAfterArgs(EntityManager, entity, detonateTime, ev, entity); - _doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId); - component.DoAfter = doAfterId; - _popups.PopupEntity(Loc.GetString("psionic-eruption-begin", ("user", entity)), entity, PopupType.LargeCaution); // Loc.GetString("psionic-regeneration-begin") - _audio.PlayPvs(component.SoundUse, entity, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); - _psionics.LogPowerUsed(entity, "psionic eruption", 2, 4); - // Start Jittering - _jittering.DoJitter(entity, TimeSpan.FromSeconds(sparkFrom), true, 10, 16); - - component.NextSpark = _gameTiming.CurTime + TimeSpan.FromSeconds(sparkFrom); - args.Handled = true; - } - - public override void Update(float frameTime) - { - base.Update(frameTime); - - // Occasionally pester users of the Psionic Eruption power to use it. - var t = _gameTiming.CurTime; - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp)) - { - // All of the timespan data should really get moved out of the system - ShowWarning((uid, comp)); // I'm a bad coder. - if (comp.DoAfter != null) - { - if (t > comp.NextSpark) - { - if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical && _random.Prob(0.125f)) - { - _lightning.ShootRandomLightnings(uid, 5f, _random.Next(1, 3)); - } - _jittering.DoJitter(uid, TimeSpan.FromSeconds(5), true, 10, 32); - Spawn(Sparks, Transform(uid).Coordinates); - - comp.NextSpark = t + TimeSpan.FromMilliseconds(500); - } - continue; - } - if (t < comp.NextAnnoy) - continue; - var msg = "psionic-eruption-annoy-minimal"; - var msgSize = PopupType.Small; - var minwait = TimeSpan.FromSeconds(60); // How many seconds to wait before the next annoyance. - _glimmer.Glimmer += _random.Next(1, 5); // Increase glimmer by a random amount. - switch (_glimmer.GetGlimmerTier(_glimmer.Glimmer)) - { - case GlimmerTier.Minimal: - msg = "psionic-eruption-annoy-minimal"; - minwait = TimeSpan.FromSeconds(60); - break; - case GlimmerTier.Low: - msg = "psionic-eruption-annoy-low"; - minwait = TimeSpan.FromSeconds(45); - break; - case GlimmerTier.Moderate: - msg = "psionic-eruption-annoy-moderate"; - minwait = TimeSpan.FromSeconds(30); - break; - case GlimmerTier.High: - msg = "psionic-eruption-annoy-high"; - minwait = TimeSpan.FromSeconds(25); - msgSize = PopupType.Medium; - Spawn(Sparks, Transform(uid).Coordinates); - break; - case GlimmerTier.Dangerous: - msg = "psionic-eruption-annoy-dangerous"; - minwait = TimeSpan.FromSeconds(20); - msgSize = PopupType.Large; - Spawn(Sparks, Transform(uid).Coordinates); - break; - case GlimmerTier.Critical: - msg = "psionic-eruption-annoy-critical"; - minwait = TimeSpan.FromSeconds(10); - msgSize = PopupType.LargeCaution; - Spawn(Sparks, Transform(uid).Coordinates); - break; - } - // Prompt the user to use the power. - _popups.PopupEntity(Loc.GetString(msg, ("user", uid)), uid, uid, msgSize); - comp.NextAnnoy = t + minwait + TimeSpan.FromSeconds(_random.Next(0, 10)); // Add a random delay to the next annoyance. - } - } - private void OnDoAfter(Entity entity, ref PsionicEruptionDoAfterEvent args) - { - entity.Comp.DoAfter = null; - - if (args.Cancelled || args.Handled) - return; - - if (!TryComp(entity, out var body)) - return; - - var pos = _transform.GetMapCoordinates(entity); - _gibbing.Gib(entity); - int boom = _glimmer.GetGlimmerTier(_glimmer.Glimmer) switch - { - GlimmerTier.Minimal => 4, - GlimmerTier.Low => 8, - GlimmerTier.Moderate => 12, - GlimmerTier.High => 16, - GlimmerTier.Dangerous => 32, - GlimmerTier.Critical => 64, - _ => 0 - }; - _explosion.QueueExplosion(pos, ExplosionSystem.DefaultExplosionPrototypeId, boom, 1, 5, entity, maxTileBreak: 0); - } -} diff --git a/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs b/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs deleted file mode 100644 index 2dec6d625b..0000000000 --- a/Content.Server/_DV/Abilities/Psionics/PsychokineticScreamPowerSystem.cs +++ /dev/null @@ -1,63 +0,0 @@ -using Content.Shared._DV.Abilities; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Actions; -using Content.Shared.Coordinates; -using Robust.Server.Audio; - -namespace Content.Server._DV.Abilities; - -public sealed partial class PsychokineticScreamPowerSystem : EntitySystem -{ - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly ShatterLightsAbilitySystem _shatterLights = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnShatterLightsAction); - } - - private void OnMapInit(Entity ent, ref MapInitEvent args) - { - _actions.AddAction(ent, ref ent.Comp.PsychokineticScreamActionEntity, ent.Comp.ShatterLightsActionId); - _actions.StartUseDelay(ent.Comp.PsychokineticScreamActionEntity); - - if (TryComp(ent, out var psionic) && psionic.PsionicAbility == null) - { - psionic.PsionicAbility = ent.Comp.PsychokineticScreamActionEntity; - psionic.ActivePowers.Add(ent.Comp); - } - } - - private void OnShutdown(Entity entity, ref ComponentShutdown args) - { - _actions.RemoveAction(entity.Owner, entity.Comp.PsychokineticScreamActionEntity); - if (TryComp(entity, out var psionic)) - { - psionic.ActivePowers.Remove(entity.Comp); - } - } - - private void OnShatterLightsAction(Entity entity, ref ShatterLightsActionEvent args) - { - if (args.Handled) - return; - - if (entity.Comp.AbilitySound != null) - _audio.PlayPvs(entity.Comp.AbilitySound, entity); - - _shatterLights.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); - - SpawnAttachedTo(entity.Comp.Effect, entity.Owner.ToCoordinates()); - - _psionics.LogPowerUsed(entity.Owner, "psychokinetic scream", 3, 6); - - args.Handled = true; - } - -} diff --git a/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs b/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs deleted file mode 100644 index d355494c2b..0000000000 --- a/Content.Server/_DV/Abilities/ShatterLightsAbilitySystem.cs +++ /dev/null @@ -1,90 +0,0 @@ - -using Content.Server.Light.EntitySystems; -using Content.Shared._DV.Abilities; -using Content.Shared.Actions; -using Content.Shared.Coordinates; -using Content.Shared.Light.Components; -using Content.Shared.Physics; -using Robust.Server.Audio; -using Robust.Shared.Map; -using Robust.Shared.Physics; -using Robust.Shared.Physics.Systems; -using System.Linq; -using System.Numerics; - -namespace Content.Server._DV.Abilities; - -public sealed partial class ShatterLightsAbilitySystem : EntitySystem -{ - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PoweredLightSystem _light = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; - [Dependency] private readonly SharedTransformSystem _transform = default!; - - private readonly HashSet> _lightsInRange = new(); - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnMapInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnShatterLightsAction); - } - - private void OnMapInit(Entity ent, ref MapInitEvent args) - { - _actions.AddAction(ent, ref ent.Comp.ShatterLightsActionEntity, ent.Comp.ShatterLightsActionId); - _actions.StartUseDelay(ent.Comp.ShatterLightsActionEntity); - } - - private void OnShutdown(Entity entity, ref ComponentShutdown args) - { - _actions.RemoveAction(entity.Owner, entity.Comp.ShatterLightsActionEntity); - } - - private void OnShatterLightsAction(Entity entity, ref ShatterLightsActionEvent args) - { - if (args.Handled) - return; - - if (entity.Comp.AbilitySound != null) - _audio.PlayPvs(entity.Comp.AbilitySound, entity); - - ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight, entity.Comp.PenetratingRadius); - - SpawnAttachedTo(entity.Comp.Effect, entity.Owner.ToCoordinates()); - args.Handled = true; - } - - public void ShatterLightsAround(EntityUid center, float range, bool lineOfSight, float penetratingRadius = 0f) - { - var pos = _transform.GetWorldPosition(center); - - // Get all light entities within the specified radius - _lightsInRange.Clear(); - _lookup.GetEntitiesInRange(Transform(center).Coordinates, range, _lightsInRange); - foreach (var light in _lightsInRange) - { - if (lineOfSight) // If LoS is required, test it. - { - var lightPos = _transform.GetWorldPosition(light); - var sqrDistance = Vector2.DistanceSquared(pos, lightPos); - if (sqrDistance > penetratingRadius * penetratingRadius) - { - // If the light is outside the penetrating radius, do a LoS check. - var ray = new CollisionRay(pos, (lightPos - pos).Normalized(), (int)CollisionGroup.Opaque); - var hit = _physics.IntersectRay(_transform.GetMapId(center), ray, MathF.Sqrt(sqrDistance) - 0.5f, returnOnFirstHit: true); - if (hit.Any() && hit.First().Distance != 0) - continue; - } - } - - // If we reach here, the light is unobstructed and within range, break it. - _light.TryDestroyBulb(light, light.Comp); - } - } - -} diff --git a/Content.Server/_DV/Chapel/SacrificialAltarSystem.cs b/Content.Server/_DV/Chapel/SacrificialAltarSystem.cs index d425c57243..7500bf0df2 100644 --- a/Content.Server/_DV/Chapel/SacrificialAltarSystem.cs +++ b/Content.Server/_DV/Chapel/SacrificialAltarSystem.cs @@ -1,11 +1,11 @@ using Content.Server.Bible.Components; -using Content.Shared.Abilities.Psionics; using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; using Content.Shared.Body.Systems; using Content.Shared.Database; using Content.Shared.Gibbing; using Content.Shared._DV.Chapel; +using Content.Shared._DV.Psionics.Components; using Content.Shared.DoAfter; using Content.Shared.EntityTable; using Content.Shared.Humanoid; diff --git a/Content.Server/_DV/EntityEffects/Effects/Psionics/RemovePsionicAbilitiesEntityEffectSystem.cs b/Content.Server/_DV/EntityEffects/Effects/Psionics/RemovePsionicAbilitiesEntityEffectSystem.cs index b0587709c8..258b912b8c 100644 --- a/Content.Server/_DV/EntityEffects/Effects/Psionics/RemovePsionicAbilitiesEntityEffectSystem.cs +++ b/Content.Server/_DV/EntityEffects/Effects/Psionics/RemovePsionicAbilitiesEntityEffectSystem.cs @@ -1,27 +1,20 @@ +using Content.Server._DV.Psionics.Systems; using Content.Shared._DV.EntityEffects.Effects.Psionics; using Content.Shared.EntityEffects; -using Content.Shared.StatusEffect; -using Content.Server.Abilities.Psionics; -using Content.Server.Psionics; - -using Robust.Shared.Prototypes; +using Content.Shared._DV.Psionics.Components; namespace Content.Server._DV.EntityEffects.Effects.Psionics; /// -/// Removes psionic abilities when at least 1u of the reagent is in the system. +/// Removes psionic abilities. /// /// -public sealed partial class RemovePsionicAbilitiesEntityEffectSystem : EntityEffectSystem +public sealed partial class RemovePsionicAbilitiesEntityEffectSystem : EntityEffectSystem { - [Dependency] private readonly PsionicsSystem _psionic = default!; - [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!; - protected override void Effect(Entity entity, ref EntityEffectEvent args) - { - if (args.Scale != 1f) - return; + [Dependency] private readonly PsionicSystem _psionicSystem = default!; - _psionicAbilities.RemovePsionics(entity); - _psionic.GrantNewPsionicReroll(entity); + protected override void Effect(Entity psionic, ref EntityEffectEvent args) + { + _psionicSystem.MindBreakEntity(psionic.Owner); } } diff --git a/Content.Server/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffectSystem.cs b/Content.Server/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffectSystem.cs deleted file mode 100644 index e2538fa0c1..0000000000 --- a/Content.Server/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffectSystem.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Content.Server.Psionics; -using Content.Shared._DV.EntityEffects.Effects.Psionics; -using Content.Shared.EntityEffects; -using Content.Shared.StatusEffect; -using Content.Server.Abilities.Psionics; -using Robust.Shared.Prototypes; - -namespace Content.Server._DV.EntityEffects.Effects.Psionics; - -/// -/// Rerolls psionic abilities when at least 1u of the reagent is in the system. -/// -/// -public sealed partial class RerollPsionicAbilitiesEntityEffectSystem : EntityEffectSystem -{ - [Dependency] private readonly PsionicsSystem _psionic = default!; - protected override void Effect(Entity entity, ref EntityEffectEvent args) - { - if (args.Scale != 1f) - return; - - _psionic.RerollPsionics(entity, bonusMuliplier: args.Effect.BonusMultiplier); - } -} diff --git a/Content.Server/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffectSystem.cs b/Content.Server/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffectSystem.cs new file mode 100644 index 0000000000..738bf38212 --- /dev/null +++ b/Content.Server/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffectSystem.cs @@ -0,0 +1,19 @@ +using Content.Server._DV.Psionics.Systems; +using Content.Shared._DV.EntityEffects.Effects.Psionics; +using Content.Shared.EntityEffects; +using Content.Shared._DV.Psionics.Components; + +namespace Content.Server._DV.EntityEffects.Effects.Psionics; + +/// +/// Rolls for a new psionic power. +/// +/// +public sealed partial class RollPsionicAbilityEntityEffectSystem : EntityEffectSystem +{ + [Dependency] private readonly PsionicSystem _psionic = default!; + protected override void Effect(Entity psionic, ref EntityEffectEvent args) + { + _psionic.TryRollPsionic(psionic, args.Effect.BonusMultiplier); + } +} diff --git a/Content.Server/_DV/GameTicking/Rules/ParadoxCloneRuleSystem.DeltaV.cs b/Content.Server/_DV/GameTicking/Rules/ParadoxCloneRuleSystem.DeltaV.cs index 75929a86f8..f159861534 100644 --- a/Content.Server/_DV/GameTicking/Rules/ParadoxCloneRuleSystem.DeltaV.cs +++ b/Content.Server/_DV/GameTicking/Rules/ParadoxCloneRuleSystem.DeltaV.cs @@ -1,4 +1,5 @@ -using Content.Server.Psionics; +using Content.Server._DV.Psionics.Systems; +using Content.Shared._DV.Psionics.Components; using Content.Shared.Mind; using Content.Shared.Roles; using Content.Shared.Roles.Components; @@ -10,7 +11,7 @@ namespace Content.Server.GameTicking.Rules; /// public sealed partial class ParadoxCloneRuleSystem { - [Dependency] private readonly PsionicsSystem _psionics = default!; + [Dependency] private readonly PsionicSystem _psionic = default!; [Dependency] private readonly SharedRoleSystem _role = default!; private void FilterTargets(HashSet> minds) @@ -26,6 +27,6 @@ public sealed partial class ParadoxCloneRuleSystem { // guaranteed psionic power var psi = EnsureComp(mob); - _psionics.RollPsionics(mob, psi, false, 100); + _psionic.AddRandomPsionicPower((mob, psi), false); } } diff --git a/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs b/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs index b240782443..ed33d663c9 100644 --- a/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs +++ b/Content.Server/_DV/Light/BreakLightsOnSpawnSystem.cs @@ -1,11 +1,11 @@ -using Content.Server._DV.Abilities; using Content.Shared._DV.Light; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; namespace Content.Server._DV.Light; public sealed partial class BreakLightsOnSpawnSystem : EntitySystem { - [Dependency] private readonly ShatterLightsAbilitySystem _shatterLights = default!; + [Dependency] private readonly PsychokineticScreamPowerSystem _psychokineticScream = default!; public override void Initialize() { @@ -15,6 +15,6 @@ public sealed partial class BreakLightsOnSpawnSystem : EntitySystem private void OnMapInit(Entity entity, ref MapInitEvent args) { - _shatterLights.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); + _psychokineticScream.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight); } } diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs new file mode 100644 index 0000000000..a9b15e6fcf --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/DispelPowerSystem.cs @@ -0,0 +1,36 @@ +using Content.Server.Bible.Components; +using Content.Server.Guardian; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class DispelPowerSystem : SharedDispelPowerSystem +{ + [Dependency] private readonly GuardianSystem _guardian = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGuardianDispelled); + SubscribeLocalEvent(OnFamiliarDispelled); + } + + private void OnGuardianDispelled(Entity guardian, ref DispelledEvent args) + { + if (TryComp(guardian.Comp.Host, out var host)) + _guardian.ToggleGuardian(guardian.Comp.Host.Value, host); + + DealDispelDamage(guardian, dispeller: args.Dispeller); + args.Handled = true; + } + + private void OnFamiliarDispelled(Entity familiar, ref DispelledEvent args) + { + if (familiar.Comp.Source != null) + EnsureComp(familiar.Comp.Source.Value); + + args.Handled = true; + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs new file mode 100644 index 0000000000..43b3a25a29 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/FracturedFormPowerSystem.cs @@ -0,0 +1,212 @@ +using Content.Server.Cloning; +using Content.Server.Mind; +using Content.Server.Station.Systems; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared._DV.Species; +using Content.Shared.Bed.Sleep; +using Content.Shared.Body.Components; +using Content.Shared.Humanoid.Prototypes; +using Content.Shared.Mobs.Components; +using Content.Shared.Popups; +using Content.Shared.Preferences; +using Robust.Server.GameObjects; +using Robust.Server.GameStates; +using Robust.Server.Player; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem +{ + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly CloningSystem _cloning = default!; + [Dependency] private readonly MindSystem _mind = default!; + [Dependency] private readonly StationSpawningSystem _stationSpawning = default!; + [Dependency] private readonly StationSystem _station = default!; + [Dependency] private readonly TransformSystem _transform = default!; + [Dependency] private readonly PvsOverrideSystem _pvsOverride = default!; + + // holy initialize performance? but better for it to happen once than the double dict lookup every tick!! + private EntityQuery _bodyQuery; + private EntityQuery _sleepingQuery; + + public override void Initialize() + { + base.Initialize(); + + _bodyQuery = GetEntityQuery(); + _sleepingQuery = GetEntityQuery(); + } + + protected override void OnPowerInit(Entity power, ref MapInitEvent args) + { + base.OnPowerInit(power, ref args); + + // The next random swap is between 5 and 20 minutes. + var randomTime = Random.Next(power.Comp.NextSwapMinTime, power.Comp.NextSwapMaxTime); + power.Comp.NextSwap = Timing.CurTime + randomTime; + power.Comp.NextVoluntarySwap = Timing.CurTime + power.Comp.VoluntarySwapCooldown; + + // Don't generate a new body if we're already part of a network. + if (HasComp(power)) + return; + + // Don't make bodies if there is no body. This is solely for test fails. + if (!HasComp(power)) + return; + + var bodyComp = AddComp(power); + bodyComp.ControllingForm = power.Owner; + power.Comp.Bodies.Add(power); + var body = GenerateForm(power); + // hide the SSD indicator. + if (SsdQuery.TryComp(body, out var ssdComp)) + ssdComp.IsSSD = false; + } + + private EntityUid GenerateForm(Entity original) + { + // Form: + // - Same appearance as original + // - Different apperance, still humanoid + // Equipment: + // - Same as original body + // - Nude and helpless + + var xform = Transform(original); + + var hasGear = Random.Prob(original.Comp.HasGearChance); + + if (Random.Prob(original.Comp.DifferentSpeciesChance) || !_cloning.TryCloning(original, _transform.GetMapCoordinates(original), hasGear ? original.Comp.CopyClothed : original.Comp.CopyNaked, out var newBody)) // Slightly lower chance to copy the original body + { + // Either the dice rolled poorly, or the cloning failed. Either way, make a new body instead. (Or try to) + var validSpecies = new List>(); + var speciesPrototypes = _prototype.EnumeratePrototypes(); + foreach (var proto in speciesPrototypes) + { + var speciesEntityPrototype = _prototype.Index(proto.Prototype); + // If they have the PotentialPsionicComponent, they can be psionic. + if (proto.RoundStart && speciesEntityPrototype.TryGetComponent(out _, Factory) && !SpeciesHiderSystem.IsHidden(proto.ID)) + validSpecies.Add(proto.ID); + } + var species = Random.Pick(validSpecies); + var character = HumanoidCharacterProfile.RandomWithSpecies(species); + newBody = _stationSpawning.SpawnPlayerMob(xform.Coordinates, hasGear ? original.Comp.VisitorJob : original.Comp.NakedJob, character, _station.GetOwningStation(original.Owner)); + if (newBody is not { } bodyV || Deleted(bodyV)) + { + Log.Error($"Failed to create a new body for {ToPrettyString(original)}. This is a bug."); + return EntityUid.Invalid; + } + } + + if (newBody is not { } body || Deleted(body)) + return default!; + + var bodyComp = AddComp(body); + original.Comp.Bodies.Add(body); + bodyComp.ControllingForm = original.Owner; + + if (_player.TryGetSessionByEntity(original, out var session)) + _pvsOverride.AddSessionOverride(body, session); + + Dirty(original); + + return body; + } + + private bool TryGetValidBody(Entity psionic, out EntityUid validBody) + { + foreach (var body in psionic.Comp.Bodies) + { + if (!IsValidBody(psionic, body)) + continue; + + validBody = body; + return true; + } + validBody = default; + return false; + } + + private void Swap(Entity psionic) + { + if (!TryGetValidBody(psionic, out var targetBody)) + return; + + _audio.PlayPvs(psionic.Comp.SwapSound, psionic); + // Transfer mind if present + if (MindContainerQuery.TryComp(psionic, out var mindContainer) && mindContainer.Mind.HasValue) + _mind.TransferTo(mindContainer.Mind.Value, targetBody); + // Wake up the new body + Sleeping.TryWaking(targetBody); + // Remove the action. + Action.RemoveAction(psionic.Comp.ActionEntity); + // Create new component on target and copy data + var duplicate = EnsureComp(targetBody); + duplicate.Bodies = psionic.Comp.Bodies; + // Update all body references + foreach (var body in duplicate.Bodies) + { + if (_bodyQuery.TryComp(body, out var bodyComp)) + bodyComp.ControllingForm = targetBody; + } + + if (_player.TryGetSessionByEntity(targetBody, out var session)) + { + _pvsOverride.AddSessionOverride(psionic, session); + _pvsOverride.RemoveSessionOverride(targetBody, session); + } + + RemCompDeferred(psionic); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + List> swapTargets = []; + + var entities = EntityQueryEnumerator(); + while (entities.MoveNext(out var uid, out var comp, out var mobState)) + { + // Check sleep warning + if (!comp.SleepWarned && Timing.CurTime > comp.NextSwap - comp.WarningTimeBeforeSleep) + { + comp.SleepWarned = true; + Popup.PopupEntity(Loc.GetString("psionic-power-fractured-form-sleepy"), uid, uid, PopupType.LargeCaution); + Chat.TryEmoteWithChat(uid, "Yawn"); + } + // Swap check + if ((_sleepingQuery.HasComp(uid) || MobState.IsIncapacitated(uid, mobState)) && Timing.CurTime > comp.NextVoluntarySwap + || Timing.CurTime > comp.NextSwap) + swapTargets.Add((uid, comp)); + } + + foreach (var target in swapTargets) + { + Swap(target); + } + + // Process bodies + var bodies = EntityQueryEnumerator(); + while (bodies.MoveNext(out var uid, out var comp, out var mobState)) + { + // Put to sleep if no sleeping component and no mind + if (!_sleepingQuery.HasComp(uid) && !_mind.GetMind(uid).HasValue && !FracturedQuery.HasComp(uid)) + Sleeping.TrySleeping((uid, mobState)); + // Cleanup invalid bodies + if (!comp.ControllingForm.IsValid() + || Deleted(comp.ControllingForm) + || !FracturedQuery.HasComp(comp.ControllingForm)) + { + RemCompDeferred(uid); + } + } + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs new file mode 100644 index 0000000000..44a1829bbb --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/MassSleepPowerSystem.cs @@ -0,0 +1,82 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Bed.Sleep; +using Content.Shared.DoAfter; +using Content.Shared.Movement.Systems; +using Content.Shared.Popups; +using Robust.Shared.Prototypes; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class MassSleepPowerSystem : SharedMassSleepPowerSystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly Shared.StatusEffectNew.StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly MovementModStatusSystem _movementMod = default!; + + public static readonly EntProtoId MassSleepSlowdown = "MassSleepSlowdownStatusEffect"; + private EntityQuery _psionicDetectorQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMassSleepDoAfter); + _psionicDetectorQuery = GetEntityQuery(); + } + + protected override void OnPowerUsed(Entity psionic, ref MassSleepPowerActionEvent args) + { + var ev = new MassSleepDoAfterEvent(); + var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, psionic.Comp.UseDelay, ev, args.Performer) + { + BreakOnDamage = true, + }; + + if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + foreach (var target in _lookup.GetEntitiesInRange(args.Performer, psionic.Comp.WarningRadius)) + { + // TODO: Metapsionic Users won't see this message, as it would otherwise overlap their usual power detected popup. Fix it. + if (args.Performer != target && Psionic.CanBeTargeted(target) && !_psionicDetectorQuery.HasComp(target)) + { + Popup.PopupEntity(Loc.GetString("psionic-power-mass-sleep-warning"), + target, + target, + PopupType.LargeCaution); + } + } + + _movementMod.TryUpdateMovementSpeedModDuration(args.Performer, MassSleepSlowdown, psionic.Comp.UseDelay, 0.5f); + + psionic.Comp.SaveDoAfterId(doAfterId.Value); + + Dirty(psionic); + AfterPowerUsed(psionic, args.Performer); + } + + private void OnMassSleepDoAfter(Entity psionic, ref MassSleepDoAfterEvent args) + { + if (args.Handled) + return; + args.Handled = true; + psionic.Comp.RemoveSavedDoAfterId(); + Dirty(psionic); + + if (args.Cancelled) + { + _statusEffects.TryRemoveStatusEffect(psionic, MassSleepSlowdown); + return; + } + + foreach (var target in _lookup.GetEntitiesInRange(args.User, psionic.Comp.Radius)) + { + if (args.Used != target && Psionic.CanBeTargeted(target)) + _statusEffects.TryUpdateStatusEffectDuration(target, SleepingSystem.StatusEffectForcedSleeping, psionic.Comp.Duration); + } + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs new file mode 100644 index 0000000000..109a4cd779 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/MindSwapPowerSystem.cs @@ -0,0 +1,30 @@ +using Content.Server.Ghost; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class MindSwapPowerSystem : SharedMindSwapPowerSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGhostAttempt); + } + + private void OnGhostAttempt(GhostAttemptHandleEvent args) + { + if (args.Handled) + return; + + // If you're able to swap back to your original body, you should swap back before you ghost. + if (TryComp(args.Mind.CurrentEntity, out var component) + && Action.GetAction(component.ActionEntity) is { } action + && action.Comp.AttachedEntity is not null) + { + args.Result = false; + args.Handled = true; + } + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs new file mode 100644 index 0000000000..d018abea50 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/NoosphericZapPowerSystem.cs @@ -0,0 +1,24 @@ +using Content.Server.Electrocution; +using Content.Server.Lightning; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class NoosphericZapPowerSystem : SharedNoosphericZapPowerSystem +{ + [Dependency] private readonly LightningSystem _lightning = default!; + [Dependency] private readonly ElectrocutionSystem _electrocution = default!; + + protected override void OnPowerUsed(Entity psionic, ref NoosphericZapPowerActionEvent args) + { + if (!Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer)) + return; + + _lightning.ShootLightning(args.Performer, args.Target, psionic.Comp.LightningPrototpyeId); + _electrocution.TryDoElectrocution(args.Target, args.Performer, psionic.Comp.ShockDamage, psionic.Comp.StunDuration, true); + + AfterPowerUsed(psionic, args.Performer); + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs new file mode 100644 index 0000000000..9201cffa5a --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PrecognitionPowerSystem.cs @@ -0,0 +1,194 @@ +using Content.Server._DV.StationEvents.NextEvent; +using Content.Server.Chat.Managers; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Abilities.Psionics; +using Content.Shared.Chat; +using Content.Shared.Popups; +using Robust.Server.Player; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +/// +/// This system lets a psionic user foretell the next event with some inconsistency. +/// +public sealed class PrecognitionPowerSystem : SharedPrecognitionPowerSystem +{ + [Dependency] private readonly IChatManager _chat = default!; + [Dependency] private readonly IComponentFactory _factory = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IPrototypeManager _prototype = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IGameTiming _timing = default!; + + /// + /// A map between game rule prototypes and their results to give. + /// + public Dictionary Results = new(); + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnPrototypesReloaded); + + CachePrecognitionResults(); + } + + /// + /// Send a message to the user about the next station event. + /// + /// The source of the psionic usage. + /// The doAfter event. + private void OnDoAfter(Entity psionic, ref PrecognitionDoAfterEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + psionic.Comp.RemoveSavedDoAfterId(); + Dirty(psionic); + + if (args.Cancelled) + { + // Need to clean up the applied effects in case of cancel and alert the player. + // TODO: Port over the TemporaryBlindness effect to the new StatusEffectSystem. + // When Upstream ports it over, replace this with it. + psionic.Comp.SoundStream = Audio.Stop(psionic.Comp.SoundStream); + StatusEffects.TryRemoveStatusEffect(args.User, "TemporaryBlindness"); + Movement.TryUpdateMovementSpeedModDuration(args.User, PrecognitionSlowdown, TimeSpan.Zero, 0.5f); + + Popup.PopupEntity( + Loc.GetString("psionic-power-precognition-failure-by-damage"), + args.User, + args.User, + PopupType.SmallCaution); + + if (Action.GetAction(psionic.Comp.ActionEntity) is {} actionData) + Action.SetCooldown(actionData.Owner, _timing.CurTime, _timing.CurTime + psionic.Comp.CancellationCooldown); + return; + } + + // Determines the window that will be looked at for events, avoiding events that are too close or too far to be useful. + + if (!_player.TryGetSessionByEntity(args.User, out var session)) + return; + + var nextEvent = FindEarliestNextEvent(psionic.Comp.MinEventTimeDistance, psionic.Comp.MaxEventTimeDistance); + + LocId? message = nextEvent?.NextEventId is {} nextEventId + ? GetResultMessage(nextEventId) + // A special message given if there is no event within the time window. + : "psionic-power-precognition-no-event-result-message"; + + if (_random.Prob(psionic.Comp.RandomResultChance)) // This will replace the proper result message with a random one occasionally to simulate some unreliability. + message = GetRandomResult(); + + if (message is not {} locId) // If there is no message to send, don't bother trying to send it. + return; + + // Send a message describing the vision they see + var msg = Loc.GetString(locId); + _chat.ChatMessageToOne(ChatChannel.Server, + msg, + Loc.GetString("chat-manager-server-wrap-message", ("message", msg)), + psionic, + false, + session.Channel, + Color.PaleVioletRed); + } + + /// + /// Gets the precognition result message corosponding to the passed event id. + /// + /// message string corosponding to the event id passed + private LocId? GetResultMessage(EntProtoId eventId) + { + if (Results.TryGetValue(eventId, out var result)) + return result.Message; + + Log.Error($"Prototype {eventId} does not have an associated precognitionResult!"); + return null; + + } + + /// + /// + /// The locale message id of a weighted randomly chosen precognition result + public LocId? GetRandomResult() + { + // funny weighted random + var sumOfWeights = 0f; + foreach (var precognitionResult in Results.Values) + { + sumOfWeights += precognitionResult.Weight; + } + + sumOfWeights = (float) _random.Next(sumOfWeights); + foreach (var precognitionResult in Results.Values) + { + sumOfWeights -= precognitionResult.Weight; + + if (sumOfWeights <= 0f) + return precognitionResult.Message; + } + + Log.Error("Precognition result was not found after weighted pick process!"); + return null; + } + + /// + /// Gets the soonest nextEvent to occur within the window. + /// + /// The earliest reletive time that will be return a nextEvent + /// The latest reletive latest time that will be return a nextEvent + /// Component for the next event to occour if one exists in the window. + private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow) + { + TimeSpan? earliestNextEventTime = null; + NextEventComponent? earliestNextEvent = null; + var query = EntityQueryEnumerator(); + + var windowStart = _timing.CurTime + minDetectWindow; + var windowEnd = _timing.CurTime + maxDetectWindow; + + while (query.MoveNext(out var nextEventComponent)) + { + // Update if the event is the most recent event that isn't too close or too far from happening to be of use + if (windowStart < nextEventComponent.NextEventTime && nextEventComponent.NextEventTime < windowEnd + && earliestNextEvent == null + || nextEventComponent.NextEventTime < earliestNextEventTime) + { + earliestNextEvent ??= nextEventComponent; + } + } + return earliestNextEvent; + } + + + private void OnPrototypesReloaded(PrototypesReloadedEventArgs args) + { + if (!args.WasModified()) + return; + + CachePrecognitionResults(); + } + + private void CachePrecognitionResults() + { + Results.Clear(); + foreach (var prototype in _prototype.EnumeratePrototypes()) + { + if (prototype.Abstract + || !prototype.TryGetComponent(out var precognitionResult, _factory)) + continue; + + Results.Add(prototype.ID, precognitionResult); + } + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/PsionicEruptionPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PsionicEruptionPowerSystem.cs new file mode 100644 index 0000000000..f5bf8bedb5 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PsionicEruptionPowerSystem.cs @@ -0,0 +1,194 @@ +using Content.Server.DoAfter; +using Content.Server.EUI; +using Content.Server.Explosion.EntitySystems; +using Content.Server.Jittering; +using Content.Server.Lightning; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Body.Components; +using Content.Shared.DoAfter; +using Content.Shared.Gibbing; +using Content.Shared.Popups; +using Content.Shared.Psionics.Glimmer; +using Robust.Server.Audio; +using Robust.Server.Player; +using Robust.Shared.Audio; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class PsionicEruptionSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly DoAfterSystem _doAfter = default!; + [Dependency] private readonly EuiManager _eui = default!; + [Dependency] private readonly ExplosionSystem _explosion = default!; + [Dependency] private readonly GibbingSystem _gibbing = default!; + [Dependency] private readonly GlimmerSystem _glimmer = default!; + [Dependency] private readonly JitteringSystem _jittering = default!; + [Dependency] private readonly LightningSystem _lightning = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + private static readonly EntProtoId? Sparks = "EffectSparks"; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDoAfter); + } + + protected override void OnPowerInit(Entity power, ref MapInitEvent args) + { + base.OnPowerInit(power, ref args); + + if (!_player.TryGetSessionByEntity(power, out var session)) + return; + + _eui.OpenEui(new EruptionWarningEui(), session); + power.Comp.NextAnnoy = Timing.CurTime + TimeSpan.FromSeconds(60); // Minute grace period + } + + protected override void OnPowerUsed(Entity psionic, ref PsionicEruptionPowerActionEvent args) + { + TimeSpan detonateTime; + + if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical) + detonateTime = psionic.Comp.MaxDetonateDelay; + else + detonateTime = psionic.Comp.MinDetonateDelay; + + var sparkFrom = detonateTime / 2; + + // Start the DoAfter. + var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, detonateTime, new PsionicEruptionDoAfterEvent(), args.Performer); + if (!_doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + psionic.Comp.SaveDoAfterId(doAfterId.Value); // Save the DoAfterID to reference it later. + + var message = Loc.GetString("psionic-eruption-begin", ("user", args.Performer)); + Popup.PopupEntity(message, args.Performer, PopupType.LargeCaution); + _audio.PlayPvs(psionic.Comp.SoundUse, args.Performer, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); + _jittering.DoJitter(args.Performer, sparkFrom, true, 10, 16); + + psionic.Comp.NextSpark = Timing.CurTime + sparkFrom; + AfterPowerUsed(psionic, args.Performer); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + // Occasionally pester users of the Psionic Eruption power to use it. + var curTime = Timing.CurTime; + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var psionic, out var comp)) + { + if (comp.GetDoAfterId() != null) + { + if (curTime >= comp.NextSpark) + CauseSparks(psionic, comp, curTime); + continue; + } + + if (curTime < comp.NextAnnoy) + continue; + + _glimmer.Glimmer += _random.Next(1, 5); // Increase glimmer by a random amount. + + var msg = GetSeverityMessage(psionic, out var messageSize, out var minWait); + // Prompt the user to use the power. + Popup.PopupEntity(Loc.GetString(msg, ("user", psionic)), psionic, psionic, messageSize); + comp.NextAnnoy = curTime + minWait + TimeSpan.FromSeconds(_random.Next(0, 10)); // Add a random delay to the next annoyance. + } + } + + private string GetSeverityMessage(EntityUid psionic, out PopupType messageSize, out TimeSpan minWait) + { + string message; + switch (_glimmer.GetGlimmerTier(_glimmer.Glimmer)) + { + case GlimmerTier.Minimal: + default: + message = "psionic-eruption-annoy-minimal"; + minWait = TimeSpan.FromSeconds(60); + messageSize = PopupType.Small; + break; + case GlimmerTier.Low: + message = "psionic-eruption-annoy-low"; + minWait = TimeSpan.FromSeconds(45); + messageSize = PopupType.Small; + break; + case GlimmerTier.Moderate: + message = "psionic-eruption-annoy-moderate"; + minWait = TimeSpan.FromSeconds(30); + messageSize = PopupType.Small; + break; + case GlimmerTier.High: + message = "psionic-eruption-annoy-high"; + minWait = TimeSpan.FromSeconds(25); + messageSize = PopupType.Medium; + Spawn(Sparks, Transform(psionic).Coordinates); + break; + case GlimmerTier.Dangerous: + message = "psionic-eruption-annoy-dangerous"; + minWait = TimeSpan.FromSeconds(20); + messageSize = PopupType.Large; + Spawn(Sparks, Transform(psionic).Coordinates); + break; + case GlimmerTier.Critical: + message = "psionic-eruption-annoy-critical"; + minWait = TimeSpan.FromSeconds(10); + messageSize = PopupType.LargeCaution; + Spawn(Sparks, Transform(psionic).Coordinates); + break; + } + + return message; + } + + private void CauseSparks(EntityUid psionic, PsionicEruptionPowerComponent comp, TimeSpan curTime) + { + if (_glimmer.GetGlimmerTier(_glimmer.Glimmer) == GlimmerTier.Critical && _random.Prob(0.125f)) + { + _lightning.ShootRandomLightnings(psionic, 5f, _random.Next(1, 3)); + } + _jittering.DoJitter(psionic, TimeSpan.FromSeconds(5), true, 10, 32); + Spawn(Sparks, Transform(psionic).Coordinates); + + comp.NextSpark = curTime + TimeSpan.FromMilliseconds(500); + } + + private void OnDoAfter(Entity psionic, ref PsionicEruptionDoAfterEvent args) + { + if (args.Handled) + return; + args.Handled = true; + psionic.Comp.RemoveSavedDoAfterId(); + + if (args.Cancelled || !TryComp(args.User, out var body)) + return; + + var pos = _transform.GetMapCoordinates(args.User); + _gibbing.Gib(args.User, user: args.User); + + int boom = _glimmer.GetGlimmerTier(_glimmer.Glimmer) switch + { + GlimmerTier.Minimal => 4, + GlimmerTier.Low => 8, + GlimmerTier.Moderate => 12, + GlimmerTier.High => 16, + GlimmerTier.Dangerous => 32, + GlimmerTier.Critical => 64, + _ => 0 + }; + _explosion.QueueExplosion(pos, ExplosionSystem.DefaultExplosionPrototypeId, boom, 1, 5, psionic, maxTileBreak: 0); + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs new file mode 100644 index 0000000000..435bdee56f --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/PyrokinesisPowerSystem.cs @@ -0,0 +1,25 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Atmos.Components; +using Content.Shared.Popups; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class PyrokinesisPowerSystem : SharedPyrokinesisPowerSystem +{ + [Dependency] private readonly FlammableSystem _flammableSystem = default!; + + protected override void OnPowerUsed(Entity psionic, ref PyrokinesisPowerActionEvent args) + { + if (!Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer) || !TryComp(args.Target, out var flammableComponent)) + return; + + flammableComponent.FireStacks += psionic.Comp.AddedFirestacks; + _flammableSystem.Ignite(args.Target, args.Target); + Popup.PopupEntity(Loc.GetString("pyrokinesis-power-used", ("target", args.Target)), args.Target, args.Performer, PopupType.LargeCaution); + + AfterPowerUsed(psionic, args.Performer); + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs new file mode 100644 index 0000000000..2efd9a4fd7 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicPowers/TelegnosisPowerSystem.cs @@ -0,0 +1,71 @@ +using System.Numerics; +using Content.Server.Atmos.EntitySystems; +using Content.Server.Body.Systems; +using Content.Server.Disposal.Unit; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Atmos; +using Content.Shared.Body.Components; +using Content.Shared.Mech.Components; +using Content.Shared.Medical.Cryogenics; +using Content.Shared.Mindshield.Components; +using Content.Shared.Popups; +using Content.Shared.Storage.Components; + +namespace Content.Server._DV.Psionics.Systems.PsionicPowers; + +public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem +{ + [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly SharedMindSwapPowerSystem _mindSwap = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnInhaleLocation, after: [typeof(InsideCryoPodComponent), typeof(InternalsComponent), typeof(BeingDisposedComponent), typeof(InsideEntityStorageComponent), typeof(MechPilotComponent)]); + } + + // This needs to be here on serverside, because mindswap CANNOT be predicted. + // The logic for transferring minds is server-side only. If we don't put this here, it'll look bad for the person. + protected override void OnPowerUsed(Entity psionic, ref TelegnosisPowerActionEvent args) + { + // TODO: Fix this. MindSwapSystem cannot handle popups when called from serverside while the performer is the cause. + if (HasComp(psionic)) + { + Popup.PopupEntity(Loc.GetString("psionic-power-mindswap-own-mindshield"), psionic, psionic, PopupType.SmallCaution); + return; + } + + var projection = Spawn(psionic.Comp.Prototype, Transform(psionic).Coordinates); + + _transform.AttachToGridOrMap(projection); + if (!_mindSwap.SwapMinds(args.Performer, projection)) + { + // If swap didn't work out, delete the spawned projection. + QueueDel(projection); + return; + } + + AfterPowerUsed(psionic, args.Performer); + } + + private void OnInhaleLocation(Entity entity, ref InhaleLocationEvent args) + { + var sensorUid = GetCasterProjection(entity); + if (sensorUid == default) + return; + // Determine the distance to the sensor, this will be used to dilute the amount of air we take in. + var sensorPosition = _transform.GetWorldPosition(sensorUid); + var projectionPosition = _transform.GetWorldPosition(entity); + // A linear curve from 1.0 at 7 tiles away, to 0 at 57 tiles away + var distance = Vector2.Distance(sensorPosition, projectionPosition); + float gasMult = Math.Clamp(1f - (distance - 7f) / 50f, 0f, 1f); + args.Gas = (args.Gas ?? _atmos.GetContainingMixture(entity.Owner, excite: true))?.RemoveVolume(Atmospherics.BreathVolume * gasMult); + if (args.Gas == null) + return; + args.Gas.Volume = Math.Min(args.Gas.Volume, Atmospherics.BreathVolume); + } +} diff --git a/Content.Server/_DV/Psionics/Systems/PsionicSystem.cs b/Content.Server/_DV/Psionics/Systems/PsionicSystem.cs new file mode 100644 index 0000000000..2edd50d1e7 --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/PsionicSystem.cs @@ -0,0 +1,27 @@ +using Content.Server._DV.Psionics.UI; +using Content.Server.EUI; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; +using Content.Shared.GameTicking; + +namespace Content.Server._DV.Psionics.Systems; + +public sealed partial class PsionicSystem : SharedPsionicSystem +{ + [Dependency] private readonly EuiManager _euiManager = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPlayerSpawnComplete); + + InitializeItems(); + } + + private void OnPlayerSpawnComplete(Entity potPsionic, ref PlayerSpawnCompleteEvent args) + { + if (RollChance(potPsionic)) + _euiManager.OpenEui(new AcceptPsionicsEui(potPsionic, this), args.Player); + } +} diff --git a/Content.Server/_DV/Psionics/Systems/SubSystems/PsionicSystem.Items.cs b/Content.Server/_DV/Psionics/Systems/SubSystems/PsionicSystem.Items.cs new file mode 100644 index 0000000000..3e68fbe96b --- /dev/null +++ b/Content.Server/_DV/Psionics/Systems/SubSystems/PsionicSystem.Items.cs @@ -0,0 +1,44 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.Atmos.Components; +using Content.Shared.Damage.Systems; +using Content.Shared.Inventory; +using Content.Shared.Popups; + +namespace Content.Server._DV.Psionics.Systems; + +public sealed partial class PsionicSystem +{ + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly FlammableSystem _flammable = default!; + + public void InitializeItems() + { + SubscribeLocalEvent>(OnFry); + } + + private void OnFry(Entity gear, ref InventoryRelayedEvent args) + { + if (gear.Comp.CanBeFried) + { + Popup.PopupEntity(Loc.GetString("psionic-burns-up", ("item", gear)), gear.Owner, PopupType.MediumCaution); + Audio.PlayEntity(gear.Comp.FrySound, gear, gear); + Spawn("Ash", Transform(gear).Coordinates); + QueueDel(gear); + } + else + { + Popup.PopupEntity(Loc.GetString("psionic-burn-resist", ("item", gear)), gear.Owner, PopupType.MediumCaution); + Audio.PlayEntity(gear.Comp.FrySound, gear, gear); + } + + _damageable.TryChangeDamage(args.Owner, args.Args.Damage); + + if (!TryComp(args.Owner, out var flammable)) + return; + + _flammable.AdjustFireStacks(args.Owner, args.Args.FireStacks, flammable); + _flammable.Ignite(args.Owner, gear, flammable); + } +} diff --git a/Content.Server/_DV/Psionics/UI/AcceptPsionicsEui.cs b/Content.Server/_DV/Psionics/UI/AcceptPsionicsEui.cs new file mode 100644 index 0000000000..041ed4ead1 --- /dev/null +++ b/Content.Server/_DV/Psionics/UI/AcceptPsionicsEui.cs @@ -0,0 +1,25 @@ +using Content.Shared.Psionics; +using Content.Shared.Eui; +using Content.Server.EUI; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; + +namespace Content.Server._DV.Psionics.UI; + +public sealed class AcceptPsionicsEui(Entity potPsionic, SharedPsionicSystem psionicsSystem, bool midRound = false) : BaseEui +{ + public override void HandleMessage(EuiMessageBase message) + { + base.HandleMessage(message); + + if (message is not AcceptPsionicsChoiceMessage choice || + choice.Button == AcceptPsionicsUiButton.Deny) + { + Close(); + return; + } + + psionicsSystem.AddRandomPsionicPower(potPsionic, midRound); + Close(); + } +} diff --git a/Content.Server/_DV/Psionics/EruptionWarningEui.cs b/Content.Server/_DV/Psionics/UI/EruptionWarningEui.cs similarity index 100% rename from Content.Server/_DV/Psionics/EruptionWarningEui.cs rename to Content.Server/_DV/Psionics/UI/EruptionWarningEui.cs diff --git a/Content.Server/_DV/StationEvents/Components/DebrisSpawnerRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/DebrisSpawnerRuleComponent.cs index 0f89e161fc..3f8a8b5443 100644 --- a/Content.Server/_DV/StationEvents/Components/DebrisSpawnerRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/DebrisSpawnerRuleComponent.cs @@ -4,13 +4,13 @@ * See AGPLv3.txt for details. */ -using Content.Server.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; -namespace Content.Server.StationEvents.Components; +namespace Content.Server._DV.StationEvents.Components; /// /// Spawns random debris in space around a loaded grid. -/// Requires . +/// Requires . /// [RegisterComponent, Access(typeof(DebrisSpawnerRule))] public sealed partial class DebrisSpawnerRuleComponent : Component diff --git a/Content.Server/_DV/StationEvents/Components/FugitiveRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/FugitiveRuleComponent.cs index ad3feeb012..741250067a 100644 --- a/Content.Server/_DV/StationEvents/Components/FugitiveRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/FugitiveRuleComponent.cs @@ -1,10 +1,9 @@ +using Content.Server._DV.StationEvents.GameRules; using Content.Shared.Dataset; -using Content.Server.StationEvents.Events; using Robust.Shared.Prototypes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; -using Robust.Shared.Utility; -namespace Content.Server.StationEvents.Components; +namespace Content.Server._DV.StationEvents.Components; /// /// Makes a GALPOL announcement and creates a report some time after an antag spawns. diff --git a/Content.Server/_DV/StationEvents/Components/GlimmerMobRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/GlimmerMobRuleComponent.cs index ac008322bc..73a65219ac 100644 --- a/Content.Server/_DV/StationEvents/Components/GlimmerMobRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/GlimmerMobRuleComponent.cs @@ -1,4 +1,4 @@ -using Content.Server._DV.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; using Content.Shared.Psionics.Glimmer; using Robust.Shared.Prototypes; diff --git a/Content.Server/_DV/StationEvents/Components/LoadFarGridRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/LoadFarGridRuleComponent.cs index 2c67e491f1..68bbffdc21 100644 --- a/Content.Server/_DV/StationEvents/Components/LoadFarGridRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/LoadFarGridRuleComponent.cs @@ -1,7 +1,7 @@ -using Content.Server.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; using Robust.Shared.Utility; -namespace Content.Server.StationEvents.Components; +namespace Content.Server._DV.StationEvents.Components; /// /// Loads a grid far away from a random station. diff --git a/Content.Server/_DV/StationEvents/Components/LockProbersRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/LockProbersRuleComponent.cs index 86aee762af..3334fec697 100644 --- a/Content.Server/_DV/StationEvents/Components/LockProbersRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/LockProbersRuleComponent.cs @@ -1,6 +1,4 @@ -using Content.Server._DV.StationEvents.Events; -using Content.Shared.Psionics.Glimmer; -using Robust.Shared.Prototypes; +using Content.Server._DV.StationEvents.GameRules; namespace Content.Server._DV.StationEvents.Components; @@ -8,4 +6,4 @@ namespace Content.Server._DV.StationEvents.Components; /// Locks all the probers on the station and turns them into bombs. /// [RegisterComponent, Access(typeof(GlimmerMobRule))] -public sealed partial class LockProbersRuleComponent : Component { } +public sealed partial class LockProbersRuleComponent : Component; diff --git a/Content.Server/_DV/StationEvents/Components/MeteorSwarmRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/MeteorSwarmRuleComponent.cs index e08d7a1a8a..d08209dc55 100644 --- a/Content.Server/_DV/StationEvents/Components/MeteorSwarmRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/MeteorSwarmRuleComponent.cs @@ -1,6 +1,6 @@ -using Content.Server.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; -namespace Content.Server.StationEvents.Components; +namespace Content.Server._DV.StationEvents.Components; [RegisterComponent, Access(typeof(MeteorSwarmRule))] public sealed partial class MeteorSwarmRuleComponent : Component diff --git a/Content.Server/_DV/StationEvents/Components/MinorMassMindSwapRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/MinorMassMindSwapRuleComponent.cs index 9b7f57fc93..fc4a0a089d 100644 --- a/Content.Server/_DV/StationEvents/Components/MinorMassMindSwapRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/MinorMassMindSwapRuleComponent.cs @@ -1,4 +1,4 @@ -using Content.Server._DV.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; using Robust.Shared.Audio; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; diff --git a/Content.Server/_DV/StationEvents/Components/NoosphericFryRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/NoosphericFryRuleComponent.cs new file mode 100644 index 0000000000..4c8d13701b --- /dev/null +++ b/Content.Server/_DV/StationEvents/Components/NoosphericFryRuleComponent.cs @@ -0,0 +1,25 @@ +using Content.Server._DV.StationEvents.GameRules; +using Content.Shared.Damage; +using Content.Shared.FixedPoint; + +namespace Content.Server._DV.StationEvents.Components; + +[RegisterComponent, Access(typeof(NoosphericFryRule))] +public sealed partial class NoosphericFryRuleComponent : Component +{ + /// + /// The damage dealt to everyone wearing insulative gear. + /// + [DataField] + public DamageSpecifier Damage = new() + { + DamageDict = new Dictionary + { + {"Heat", 10}, + {"Shock", 10}, + } + }; + + [DataField] + public int FireStacks = 2; +} diff --git a/Content.Server/_DV/StationEvents/Components/NoosphericSilenceRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/NoosphericSilenceRuleComponent.cs new file mode 100644 index 0000000000..157ef75bd9 --- /dev/null +++ b/Content.Server/_DV/StationEvents/Components/NoosphericSilenceRuleComponent.cs @@ -0,0 +1,13 @@ +using Content.Server._DV.StationEvents.GameRules; + +namespace Content.Server._DV.StationEvents.Components; + +[RegisterComponent, Access(typeof(NoosphericSilenceRule))] +public sealed partial class NoosphericSilenceRuleComponent : Component +{ + [DataField] + public TimeSpan MinDuration = TimeSpan.FromSeconds(20); + + [DataField] + public TimeSpan MaxDuration = TimeSpan.FromSeconds(80); +} diff --git a/Content.Server/_DV/StationEvents/Components/NoosphericStormRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/NoosphericStormRuleComponent.cs new file mode 100644 index 0000000000..4826e018a8 --- /dev/null +++ b/Content.Server/_DV/StationEvents/Components/NoosphericStormRuleComponent.cs @@ -0,0 +1,44 @@ +using Content.Server._DV.StationEvents.GameRules; + +namespace Content.Server._DV.StationEvents.Components; + +[RegisterComponent, Access(typeof(NoosphericStormRule))] +public sealed partial class NoosphericStormRuleComponent : Component +{ + /// + /// The minimum amount of psionics that are created with each storm. + /// + [DataField] + public int MinAwaken = 1; + + /// + /// The maximum amount of psionics that are created with each storm. + /// + [DataField] + public int MaxAwaken = 3; + + /// + /// The coefficient for additional psionic awakenings. + /// It's calculated via glimmer / coefficient, rounded down. + /// + [DataField] + public float AdditionalAwokenPerGlimmer = 100f; + + /// + /// The minimum amount of glimmer added. + /// + [DataField] + public int BaseGlimmerAddMin = 65; + + /// + /// The maximum amount of glimmer added. + /// + [DataField] + public int BaseGlimmerAddMax = 85; + + /// + /// Multiply the EventSeverityModifier by this to determine how much extra glimmer to add. + /// + [DataField] + public float GlimmerSeverityCoefficient = 0.25f; +} diff --git a/Content.Server/_DV/StationEvents/Components/NoosphericZapRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/NoosphericZapRuleComponent.cs new file mode 100644 index 0000000000..17c043944f --- /dev/null +++ b/Content.Server/_DV/StationEvents/Components/NoosphericZapRuleComponent.cs @@ -0,0 +1,6 @@ +using Content.Server._DV.StationEvents.GameRules; + +namespace Content.Server._DV.StationEvents.Components; + +[RegisterComponent, Access(typeof(NoosphericZapRule))] +public sealed partial class NoosphericZapRuleComponent : Component; diff --git a/Content.Server/_DV/StationEvents/Components/PsionicNosebleedRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/PsionicNosebleedRuleComponent.cs index 7f1438f47d..45c767ba68 100644 --- a/Content.Server/_DV/StationEvents/Components/PsionicNosebleedRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/PsionicNosebleedRuleComponent.cs @@ -1,4 +1,4 @@ -using Content.Server._DV.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; namespace Content.Server._DV.StationEvents.Components; diff --git a/Content.Server/_DV/StationEvents/Components/RandomAnimationRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/RandomAnimationRuleComponent.cs index a3aa6d089b..279659da8e 100644 --- a/Content.Server/_DV/StationEvents/Components/RandomAnimationRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/RandomAnimationRuleComponent.cs @@ -1,4 +1,4 @@ -using Content.Server._DV.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; namespace Content.Server._DV.StationEvents.Components; diff --git a/Content.Server/_DV/StationEvents/Components/ThavenMoodUpsetRuleComponent.cs b/Content.Server/_DV/StationEvents/Components/ThavenMoodUpsetRuleComponent.cs index dca0b3db67..07b6df08fc 100644 --- a/Content.Server/_DV/StationEvents/Components/ThavenMoodUpsetRuleComponent.cs +++ b/Content.Server/_DV/StationEvents/Components/ThavenMoodUpsetRuleComponent.cs @@ -1,4 +1,4 @@ -using Content.Server._DV.StationEvents.Events; +using Content.Server._DV.StationEvents.GameRules; namespace Content.Server._DV.StationEvents.Components; diff --git a/Content.Server/_DV/StationEvents/Events/DebrisSpawnerRule.cs b/Content.Server/_DV/StationEvents/GameRules/DebrisSpawnerRule.cs similarity index 93% rename from Content.Server/_DV/StationEvents/Events/DebrisSpawnerRule.cs rename to Content.Server/_DV/StationEvents/GameRules/DebrisSpawnerRule.cs index 772ed6ce12..2a526e4d67 100644 --- a/Content.Server/_DV/StationEvents/Events/DebrisSpawnerRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/DebrisSpawnerRule.cs @@ -4,20 +4,19 @@ * See AGPLv3.txt for details. */ +using System.Linq; +using Content.Server._DV.StationEvents.Components; using Content.Server.GameTicking.Rules; -using Content.Server.Station.Components; -using Content.Server.StationEvents.Components; +using Content.Server.StationEvents.Events; using Content.Shared.CCVar; using Content.Shared.Salvage; using Robust.Shared.Configuration; using Robust.Shared.EntitySerialization.Systems; -using Robust.Shared.Map; using Robust.Shared.Map.Components; using Robust.Shared.Prototypes; using Robust.Shared.Random; -using System.Linq; -namespace Content.Server.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class DebrisSpawnerRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/EpsilonEventRule.cs b/Content.Server/_DV/StationEvents/GameRules/EpsilonEventRule.cs similarity index 100% rename from Content.Server/_DV/StationEvents/Events/EpsilonEventRule.cs rename to Content.Server/_DV/StationEvents/GameRules/EpsilonEventRule.cs diff --git a/Content.Server/_DV/StationEvents/Events/FugitiveRule.cs b/Content.Server/_DV/StationEvents/GameRules/FugitiveRule.cs similarity index 97% rename from Content.Server/_DV/StationEvents/Events/FugitiveRule.cs rename to Content.Server/_DV/StationEvents/GameRules/FugitiveRule.cs index 7bfebbe8d5..200f8c8f33 100644 --- a/Content.Server/_DV/StationEvents/Events/FugitiveRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/FugitiveRule.cs @@ -1,14 +1,13 @@ +using Content.Server._DV.StationEvents.Components; using Content.Server.Antag; using Content.Server.Communications; -using Content.Server.Forensics; -using Content.Server.StationEvents.Components; +using Content.Server.StationEvents.Events; +using Content.Shared.Forensics.Components; using Content.Shared.GameTicking.Components; using Content.Shared.Ghost; using Content.Shared.Hands.EntitySystems; using Content.Shared.Humanoid; -using Content.Shared.Humanoid.Prototypes; using Content.Shared.Inventory; -using Content.Shared.Forensics.Components; using Content.Shared.Paper; using Content.Shared.Popups; using Content.Shared.Random.Helpers; @@ -17,7 +16,7 @@ using Content.Shared.Storage.EntitySystems; using Robust.Shared.Physics.Components; using Robust.Shared.Utility; -namespace Content.Server.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class FugitiveRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/GlimmerFoxfireSpawnRule.cs b/Content.Server/_DV/StationEvents/GameRules/GlimmerFoxfireSpawnRule.cs similarity index 100% rename from Content.Server/_DV/StationEvents/Events/GlimmerFoxfireSpawnRule.cs rename to Content.Server/_DV/StationEvents/GameRules/GlimmerFoxfireSpawnRule.cs diff --git a/Content.Server/_DV/StationEvents/Events/GlimmerMobSpawnRule.cs b/Content.Server/_DV/StationEvents/GameRules/GlimmerMobSpawnRule.cs similarity index 96% rename from Content.Server/_DV/StationEvents/Events/GlimmerMobSpawnRule.cs rename to Content.Server/_DV/StationEvents/GameRules/GlimmerMobSpawnRule.cs index 4ca2d4e044..cb28033bc9 100644 --- a/Content.Server/_DV/StationEvents/Events/GlimmerMobSpawnRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/GlimmerMobSpawnRule.cs @@ -4,14 +4,14 @@ using Content.Server.Station.Systems; using Content.Server.StationEvents; using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Events; -using Content.Shared.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components; using Content.Shared.GameTicking.Components; using Content.Shared.NPC.Components; using Content.Shared.Psionics.Glimmer; -using Robust.Shared.Random; using Robust.Shared.Map; +using Robust.Shared.Random; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class GlimmerMobRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs b/Content.Server/_DV/StationEvents/GameRules/GlimmerRestyleRule.cs similarity index 88% rename from Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs rename to Content.Server/_DV/StationEvents/GameRules/GlimmerRestyleRule.cs index 46448957b1..73a48d47e6 100644 --- a/Content.Server/_DV/StationEvents/Events/GlimmerRestyleRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/GlimmerRestyleRule.cs @@ -2,6 +2,9 @@ using System.Linq; using Content.Server._DV.StationEvents.Components; using Content.Server.Psionics; using Content.Server.StationEvents.Events; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Systems; using Content.Shared.Abilities.Psionics; using Content.Shared.GameTicking.Components; using Content.Shared.Humanoid; @@ -16,10 +19,11 @@ namespace Content.Server._DV.StationEvents.Events; public sealed class GlimmerRestyleRule : StationEventSystem { - [Dependency] private readonly MobStateSystem _mob = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly MobStateSystem _mob = default!; [Dependency] private readonly MarkingManager _markingManager = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; protected override void Started(EntityUid uid, GlimmerRestyleRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) { @@ -30,9 +34,11 @@ public sealed class GlimmerRestyleRule : StationEventSystem(entity) || !HasComp(entity)) + if (!_mob.IsAlive(entity, mobState) || !HasComp(entity)) continue; - potentialTargets.Add((entity, humanoid)); + + if (_psionic.CanBeTargeted(entity)) + potentialTargets.Add((entity, humanoid)); } _random.Shuffle(potentialTargets); diff --git a/Content.Server/_DV/StationEvents/Events/LoadFarGridRule.cs b/Content.Server/_DV/StationEvents/GameRules/LoadFarGridRule.cs similarity index 94% rename from Content.Server/_DV/StationEvents/Events/LoadFarGridRule.cs rename to Content.Server/_DV/StationEvents/GameRules/LoadFarGridRule.cs index 30bb5babd0..42feae8220 100644 --- a/Content.Server/_DV/StationEvents/Events/LoadFarGridRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/LoadFarGridRule.cs @@ -1,13 +1,14 @@ +using Content.Server._DV.StationEvents.Components; using Content.Server.GameTicking.Rules; using Content.Server.StationEvents.Components; +using Content.Server.StationEvents.Events; using Content.Shared.GameTicking.Components; using Content.Shared.Station.Components; using Robust.Shared.EntitySerialization.Systems; using Robust.Shared.Map; using Robust.Shared.Map.Components; -using Robust.Shared.Random; -namespace Content.Server.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class LoadFarGridRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/LockProbersRule.cs b/Content.Server/_DV/StationEvents/GameRules/LockProbersRule.cs similarity index 97% rename from Content.Server/_DV/StationEvents/Events/LockProbersRule.cs rename to Content.Server/_DV/StationEvents/GameRules/LockProbersRule.cs index d5c1d04e17..c3136c2b04 100644 --- a/Content.Server/_DV/StationEvents/Events/LockProbersRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/LockProbersRule.cs @@ -1,12 +1,12 @@ +using System.Linq; using Content.Server._DV.StationEvents.Components; using Content.Server.Psionics.Glimmer; using Content.Server.Station.Systems; using Content.Server.StationEvents.Events; using Content.Shared.GameTicking.Components; using Content.Shared.Psionics.Glimmer; -using System.Linq; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class LockProbersRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/MeteorSwarmRule.cs b/Content.Server/_DV/StationEvents/GameRules/MeteorSwarmRule.cs similarity index 98% rename from Content.Server/_DV/StationEvents/Events/MeteorSwarmRule.cs rename to Content.Server/_DV/StationEvents/GameRules/MeteorSwarmRule.cs index e4f39807d6..e1cadbba5b 100644 --- a/Content.Server/_DV/StationEvents/Events/MeteorSwarmRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/MeteorSwarmRule.cs @@ -1,8 +1,9 @@ using System.Linq; using System.Numerics; +using Content.Server._DV.StationEvents.Components; using Content.Server.Atmos.Components; -using Content.Server.GameTicking.Rules.Components; using Content.Server.StationEvents.Components; +using Content.Server.StationEvents.Events; using Content.Shared.GameTicking.Components; using Robust.Shared.Map; using Robust.Shared.Map.Components; @@ -11,7 +12,7 @@ using Robust.Shared.Physics.Systems; using Robust.Shared.Random; using Robust.Shared.Spawners; -namespace Content.Server.StationEvents.Events +namespace Content.Server._DV.StationEvents.GameRules { public sealed class MeteorSwarmRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/MinorMassMindSwapRule.cs b/Content.Server/_DV/StationEvents/GameRules/MinorMassMindSwapRule.cs similarity index 80% rename from Content.Server/_DV/StationEvents/Events/MinorMassMindSwapRule.cs rename to Content.Server/_DV/StationEvents/GameRules/MinorMassMindSwapRule.cs index 8f3d578575..816af06b24 100644 --- a/Content.Server/_DV/StationEvents/Events/MinorMassMindSwapRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/MinorMassMindSwapRule.cs @@ -1,9 +1,9 @@ -using Content.Server.Abilities.Psionics; -using Content.Server.Chat.Systems; +using Content.Server._DV.Psionics.Systems; using Content.Server._DV.StationEvents.Components; -using Content.Server.Psionics; +using Content.Server.Chat.Systems; using Content.Server.StationEvents.Events; -using Content.Shared.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; using Content.Shared.GameTicking.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; @@ -12,18 +12,19 @@ using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Random; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; /// /// Forces a mind swap on a small amount of non-insulated psionic entities. /// internal sealed class MinorMassMindSwapRule : StationEventSystem { - [Dependency] private readonly AudioSystem _audio = default!; - [Dependency] private readonly MindSwapPowerSystem _mindSwap = default!; - [Dependency] private readonly ChatSystem _chat = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AudioSystem _audio = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly SharedMindSwapPowerSystem _mindSwap = default!; [Dependency] private readonly MobStateSystem _mobstateSystem = default!; + [Dependency] private readonly PsionicSystem _psionic = default!; private TimeSpan _warningSoundLength; private ResolvedSoundSpecifier _resolvedWarningSound = string.Empty; @@ -75,19 +76,18 @@ internal sealed class MinorMassMindSwapRule : StationEventSystem psionicActors = new(); + List psionicActors = []; var query = EntityQueryEnumerator(); - while (query.MoveNext(out var psion, out _, out _)) + while (query.MoveNext(out var psion, out _, out var mobState)) { - if (_mobstateSystem.IsAlive(psion) && !HasComp(psion) - && HasComp(psion)) + if (_mobstateSystem.IsAlive(psion, mobState) && HasComp(psion) && _psionic.CanBeTargeted(psion)) // Only a list of Players psionicActors.Add(psion); } // We go with 4 pairs for now - List actorsToSwap = new(); + List actorsToSwap = []; var swapPairCount = _random.Next(1, component.MaxNumberOfPairs); for (; swapPairCount > 0 && psionicActors.Count > 1; swapPairCount--) @@ -95,13 +95,7 @@ internal sealed class MinorMassMindSwapRule : StationEventSystem +/// Fries tinfoil hats and cages +/// +internal sealed class NoosphericFryRule : StationEventSystem +{ + [Dependency] private readonly AnchorableSystem _anchorableSystem = default!; + [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; + [Dependency] private readonly GlimmerReactiveSystem _glimmerReactiveSystem = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly PowerReceiverSystem _powerReceiverSystem = default!; + [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + + protected override void Started(EntityUid uid, NoosphericFryRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var damage = component.Damage; + var fireStacks = component.FireStacks; + + switch (_glimmerSystem.Glimmer) + { + case > 500 and < 750: + damage *= 2; + fireStacks += 1; + break; + case > 750: + damage *= 3; + fireStacks += 2; + break; + } + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var psion, out var _)) + { + if (!_mobStateSystem.IsAlive(psion)) + continue; + + var ev = new NoosphericFryEvent(damage, fireStacks); + RaiseLocalEvent(psion, ref ev); + } + + // for probers: + var queryReactive = EntityQueryEnumerator(); + while (queryReactive.MoveNext(out var reactive, out _, out var xform, out var physics)) + { + // shoot out one bolt of lighting... + _glimmerReactiveSystem.BeamRandomNearProber(reactive, 1, 12); + + // try to anchor if we can + if (!xform.Anchored) + { + var coordinates = xform.Coordinates; + var gridUid = xform.GridUid; + if (!TryComp(gridUid, out var grid)) + continue; + + var tileIndices = grid.TileIndicesFor(coordinates); + + if (_anchorableSystem.TileFree(grid, tileIndices, physics.CollisionLayer, physics.CollisionMask)) + _transformSystem.AnchorEntity(reactive, xform); + } + + if (!TryComp(reactive, out var power)) + continue; + + // If it's been turned off, turn it back on. + if (power.PowerDisabled) + _powerReceiverSystem.TogglePower(reactive, false); + } + } +} diff --git a/Content.Server/_DV/StationEvents/GameRules/NoosphericSilenceRule.cs b/Content.Server/_DV/StationEvents/GameRules/NoosphericSilenceRule.cs new file mode 100644 index 0000000000..721afcc4bc --- /dev/null +++ b/Content.Server/_DV/StationEvents/GameRules/NoosphericSilenceRule.cs @@ -0,0 +1,49 @@ +using Content.Server._DV.StationEvents.Components; +using Content.Server.StationEvents.Events; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; +using Content.Shared.GameTicking.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.StatusEffect; +using Robust.Shared.Random; + +namespace Content.Server._DV.StationEvents.GameRules; + +/// +/// Mutes everyone for a random amount of time. +/// +internal sealed class NoosphericSilenceRule : StationEventSystem +{ + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffectsSystem = default!; + + protected override void Started(EntityUid uid, NoosphericSilenceRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var potPsion, out _, out _)) + { + if (!_mobStateSystem.IsAlive(potPsion)) + continue; + + if (_psionic.CanBeTargeted(potPsion)) + Silence(potPsion, component); + } + } + + private void Silence(EntityUid potPsion, NoosphericSilenceRuleComponent ruleComp) + { + var duration = _robustRandom.Next(ruleComp.MinDuration, ruleComp.MaxDuration); + + // TODO Replace with statusEffectSystemNew when Upstream makes a muted prototype. + _statusEffectsSystem.TryAddStatusEffect(potPsion, + "Muted", + duration, + false, + "Muted"); + } +} diff --git a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs b/Content.Server/_DV/StationEvents/GameRules/NoosphericStormRule.cs similarity index 51% rename from Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs rename to Content.Server/_DV/StationEvents/GameRules/NoosphericStormRule.cs index e99aa0ebcc..0221c4ab45 100644 --- a/Content.Server/Nyanotrasen/StationEvents/Events/NoosphericStormRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/NoosphericStormRule.cs @@ -1,59 +1,58 @@ -using Content.Server.Abilities.Psionics; -using Content.Server.Psionics; -using Content.Server.StationEvents.Components; +using System.Linq; +using Content.Server._DV.StationEvents.Components; using Content.Server.StationEvents.Events; -using Content.Shared.Abilities.Psionics; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; using Content.Shared.GameTicking.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Psionics.Glimmer; -using Content.Shared.Zombies; using Robust.Shared.Random; -namespace Content.Server.Nyanotrasen.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; internal sealed class NoosphericStormRule : StationEventSystem { - [Dependency] private readonly PsionicAbilitiesSystem _psionicAbilitiesSystem = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; [Dependency] private readonly MobStateSystem _mobStateSystem = default!; [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; protected override void Started(EntityUid uid, NoosphericStormRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) { base.Started(uid, component, gameRule, args); - List validList = new(); + Dictionary validList = []; var query = EntityManager.EntityQueryEnumerator(); - while (query.MoveNext(out var potentialPsionic, out var potentialPsionicComponent)) + while (query.MoveNext(out var potPsionic, out var potPsionicComp)) { - if (_mobStateSystem.IsDead(potentialPsionic)) + if (!_mobStateSystem.IsAlive(potPsionic) + || HasComp(potPsionic)) // Skip over already psionic entities. continue; - // Skip over those who are already psionic or those who are insulated, or zombies. - if (HasComp(potentialPsionic) || HasComp(potentialPsionic) || HasComp(potentialPsionic)) + if (!_psionic.CanBeTargeted(potPsionic)) // Skip over shielded entities. continue; - validList.Add(potentialPsionic); + validList.Add(potPsionic, potPsionicComp); } // Give some targets psionic abilities. - RobustRandom.Shuffle(validList); + var keyList = validList.Keys.ToList(); + _robustRandom.Shuffle(keyList); var toAwaken = RobustRandom.Next(1, component.MaxAwaken); + var additional = _glimmerSystem.Glimmer / component.AdditionalAwokenPerGlimmer; + toAwaken = (int) MathF.Round(toAwaken, 0, MidpointRounding.ToZero); - foreach (var target in validList) + foreach (var target in keyList.TakeWhile(_ => toAwaken-- != 0)) { - if (toAwaken-- == 0) - break; - - _psionicAbilitiesSystem.AddPsionics(target); + _psionic.AddRandomPsionicPower((target, validList[target]), midRound: true); } // Increase glimmer. var baseGlimmerAdd = _robustRandom.Next(component.BaseGlimmerAddMin, component.BaseGlimmerAddMax); //var glimmerSeverityMod = 1 + (component.GlimmerSeverityCoefficient * (GetSeverityModifier() - 1f)); - var glimmerAdded = (int) baseGlimmerAdd; // Math.Round(baseGlimmerAdd * glimmerSeverityMod); + var glimmerAdded = baseGlimmerAdd; // Math.Round(baseGlimmerAdd * glimmerSeverityMod); _glimmerSystem.Glimmer += glimmerAdded; } diff --git a/Content.Server/_DV/StationEvents/GameRules/NoosphericZapRule.cs b/Content.Server/_DV/StationEvents/GameRules/NoosphericZapRule.cs new file mode 100644 index 0000000000..909851d8c1 --- /dev/null +++ b/Content.Server/_DV/StationEvents/GameRules/NoosphericZapRule.cs @@ -0,0 +1,64 @@ +using Content.Server._DV.Psionics.Systems; +using Content.Server._DV.StationEvents.Components; +using Content.Server.Popups; +using Content.Server.StationEvents.Events; +using Content.Shared._DV.Psionics.Components; +using Content.Shared.GameTicking.Components; +using Content.Shared.Jittering; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.Speech.EntitySystems; +using Content.Shared.Stunnable; + +namespace Content.Server._DV.StationEvents.GameRules; + +/// +/// Zaps everyone, rolling psionics and disorienting them +/// +internal sealed class NoosphericZapRule : StationEventSystem +{ + [Dependency] private readonly SharedJitteringSystem _jittering = default!; + [Dependency] private readonly MobStateSystem _mobStateSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly PsionicSystem _psionic = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedStutteringSystem _stuttering = default!; + + protected override void Started(EntityUid uid, NoosphericZapRuleComponent component, GameRuleComponent gameRule, GameRuleStartedEvent args) + { + base.Started(uid, component, gameRule, args); + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var potPsion, out var potPsionComponent, out var mobStateComponent)) + { + if (!_mobStateSystem.IsAlive(potPsion, mobStateComponent)) + continue; + + if (!_psionic.CanBeTargeted(potPsion)) + continue; + + // Zap non-psionics only if they spent their roll already. + if (potPsionComponent.Rolled) + Zap(potPsion, potPsionComponent); + // Then zap all other psionics regardless. + else if (HasComp(potPsion)) + Zap(potPsion, potPsionComponent); + } + } + + private void Zap(EntityUid psionic, PotentialPsionicComponent potPsionComp) + { + _stun.TryUpdateParalyzeDuration(psionic, TimeSpan.FromSeconds(5)); + _jittering.DoJitter(psionic, TimeSpan.FromSeconds(5), false); + _stuttering.DoStutter(psionic, TimeSpan.FromSeconds(15), false); + + var grantedRoll = _psionic.GrantPsionicRoll((psionic, potPsionComp)); + var message = grantedRoll + ? "gamerule-noospheric-zap-seize-potential-regained" + : "gamerule-noospheric-zap-seize"; + + _popupSystem.PopupEntity(Loc.GetString(message), psionic, psionic, PopupType.LargeCaution); + } +} diff --git a/Content.Server/_DV/StationEvents/Events/PsionicNosebleedRule.cs b/Content.Server/_DV/StationEvents/GameRules/PsionicNosebleedRule.cs similarity index 62% rename from Content.Server/_DV/StationEvents/Events/PsionicNosebleedRule.cs rename to Content.Server/_DV/StationEvents/GameRules/PsionicNosebleedRule.cs index d541585e21..5d056946ea 100644 --- a/Content.Server/_DV/StationEvents/Events/PsionicNosebleedRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/PsionicNosebleedRule.cs @@ -1,19 +1,23 @@ using Content.Server._DV.StationEvents.Components; using Content.Server.Body.Systems; using Content.Server.StationEvents.Events; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Systems; using Content.Shared.Abilities.Psionics; using Content.Shared.GameTicking.Components; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class PsionicNosebleedRule : StationEventSystem { - [Dependency] private readonly MobStateSystem _mob = default!; [Dependency] private readonly BloodstreamSystem _bloodstream = default!; + [Dependency] private readonly MobStateSystem _mob = default!; [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; protected override void Started(EntityUid uid, PsionicNosebleedRuleComponent comp, GameRuleComponent gameRule, GameRuleStartedEvent args) { @@ -22,11 +26,14 @@ public sealed class PsionicNosebleedRule : StationEventSystem(); while (query.MoveNext(out var psion, out _, out var mobState)) { - if (_mob.IsAlive(psion, mobState) && !HasComp(psion)) - { - _popup.PopupEntity(Loc.GetString("psionic-nosebleed-message"), psion, psion, PopupType.MediumCaution); - _bloodstream.TryModifyBleedAmount(psion, comp.BleedAmount); - } + if (!_mob.IsAlive(psion, mobState)) + continue; + + if (!_psionic.CanBeTargeted(psion)) + continue; + + _popup.PopupEntity(Loc.GetString("psionic-nosebleed-message"), psion, psion, PopupType.MediumCaution); + _bloodstream.TryModifyBleedAmount(psion, comp.BleedAmount); } } } diff --git a/Content.Server/_DV/StationEvents/Events/RandomAnimationRule.cs b/Content.Server/_DV/StationEvents/GameRules/RandomAnimationRule.cs similarity index 97% rename from Content.Server/_DV/StationEvents/Events/RandomAnimationRule.cs rename to Content.Server/_DV/StationEvents/GameRules/RandomAnimationRule.cs index 67c78ae0b8..6f4b1bf0ae 100644 --- a/Content.Server/_DV/StationEvents/Events/RandomAnimationRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/RandomAnimationRule.cs @@ -7,7 +7,7 @@ using Content.Shared.Magic.Components; using Robust.Shared.Containers; using Robust.Shared.Random; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class RandomAnimationRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/RandomMultipleSpawnRule.cs b/Content.Server/_DV/StationEvents/GameRules/RandomMultipleSpawnRule.cs similarity index 85% rename from Content.Server/_DV/StationEvents/Events/RandomMultipleSpawnRule.cs rename to Content.Server/_DV/StationEvents/GameRules/RandomMultipleSpawnRule.cs index e5a2ebbb88..7589c5984e 100644 --- a/Content.Server/_DV/StationEvents/Events/RandomMultipleSpawnRule.cs +++ b/Content.Server/_DV/StationEvents/GameRules/RandomMultipleSpawnRule.cs @@ -1,11 +1,9 @@ using Content.Server._DV.StationEvents.Components; -using Content.Server.GameTicking.Rules.Components; -using Content.Server.StationEvents.Components; using Content.Server.StationEvents.Events; using Content.Shared.GameTicking.Components; using Robust.Shared.Random; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class RandomMultipleSpawnRule : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/Events/ThavenMoodUpset.cs b/Content.Server/_DV/StationEvents/GameRules/ThavenMoodUpset.cs similarity index 93% rename from Content.Server/_DV/StationEvents/Events/ThavenMoodUpset.cs rename to Content.Server/_DV/StationEvents/GameRules/ThavenMoodUpset.cs index 8e988875cf..cbd490c8a2 100644 --- a/Content.Server/_DV/StationEvents/Events/ThavenMoodUpset.cs +++ b/Content.Server/_DV/StationEvents/GameRules/ThavenMoodUpset.cs @@ -4,7 +4,7 @@ using Content.Server.StationEvents.Events; using Content.Shared._Impstation.Thaven.Components; using Content.Shared.GameTicking.Components; -namespace Content.Server._DV.StationEvents.Events; +namespace Content.Server._DV.StationEvents.GameRules; public sealed class ThavenMoodUpset : StationEventSystem { diff --git a/Content.Server/_DV/StationEvents/NextEvent/NextEventSystem.cs b/Content.Server/_DV/StationEvents/NextEvent/NextEventSystem.cs index 203b0de6d9..ceeb44e5cf 100644 --- a/Content.Server/_DV/StationEvents/NextEvent/NextEventSystem.cs +++ b/Content.Server/_DV/StationEvents/NextEvent/NextEventSystem.cs @@ -10,7 +10,7 @@ public sealed class NextEventSystem : EntitySystem /// public EntProtoId? UpdateNextEvent(NextEventComponent component, EntProtoId newEventId, TimeSpan newEventTime) { - EntProtoId? oldEventId = component.NextEventId; // Store components current NextEventId for return + var oldEventId = component.NextEventId; // Store components current NextEventId for return component.NextEventId = newEventId; component.NextEventTime = newEventTime; return oldEventId; diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs b/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs deleted file mode 100644 index 374b532acc..0000000000 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/PsionicProducingArtifactComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Systems; - -namespace Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Components; - -/// -/// Makes people in a radius around it psionic when triggered. -/// -[RegisterComponent, Access(typeof(PsionicProducingArtifactSystem))] -public sealed partial class PsionicProducingArtifactComponent : Component -{ - /// - /// Range to look for potential psionics in. - /// - [DataField(required: true)] - public float Range; - - /// - /// Number of times this node can trigger before it switches to doing nothing. - /// - [DataField] - public int Limit = 1; -} diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs b/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs deleted file mode 100644 index 60b16c8c6f..0000000000 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/GlimmerArtifactSystem.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Components; -using Content.Shared.Xenoarchaeology.Artifact; -using Content.Shared.Psionics.Glimmer; - -namespace Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Systems; - -public sealed class GlimmerArtifactSystem : EntitySystem -{ - [Dependency] private readonly GlimmerSystem _glimmer = default!; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnActivated); - } - - private void OnActivated(Entity ent, ref XenoArtifactActivatedEvent args) - { - var range = ent.Comp.Range; - var current = _glimmer.Glimmer; - if (current < range.Min || current > range.Max) - return; - - _glimmer.Glimmer += ent.Comp.Change; - } -} diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs b/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs deleted file mode 100644 index e5c3e9bca5..0000000000 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Systems/PsionicProducingArtifactSystem.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Linq; -using Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Components; -using Content.Server.Psionics; -using Content.Shared.Xenoarchaeology.Artifact; -using Content.Shared.Xenoarchaeology.Artifact.Components; - -namespace Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Systems; -public sealed class PsionicProducingArtifactSystem : EntitySystem -{ - [Dependency] private readonly SharedXenoArtifactSystem _artifact = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly PsionicsSystem _psionics = default!; - - public const string NodeDataPsionicAmount = "nodeDataPsionicAmount"; - - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnActivated); - } - - private void OnActivated(Entity ent, ref XenoArtifactActivatedEvent args) - { - var (uid, comp) = ent; - - // Resolve the artifact entity from the node - if (!TryComp(uid, out var artifactComp)) - return; - - var artifactEntity = new Entity(uid, artifactComp); - - // Pick first active node - var node = _artifact.GetActiveNodes(artifactEntity).FirstOrDefault(); - - // Track psionic usage using ConsumedResearchValue - var currentAmount = _artifact.GetResearchValue(node); - - if (currentAmount >= comp.Limit) - return; - - var coords = Transform(uid).Coordinates; - - foreach (var target in _lookup.GetEntitiesInRange(coords, comp.Range)) - { - _psionics.TryMakePsionic(target); - } - - // Update node usage - _artifact.SetConsumedResearchValue(node, currentAmount + 1); - } - -} diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs b/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs deleted file mode 100644 index 64688732c1..0000000000 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Components/ArtifactMetapsionicTriggerComponent.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Content.Server._DV.Xenoarchaeology.XenoArtifacts.Triggers.Components; - -/// -/// Triggers if a psionic power is used nearby. -/// Requires MetapsionicPowerComponent to be added too. -/// -[RegisterComponent] -public sealed partial class ArtifactMetapsionicTriggerComponent : Component; diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs b/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs deleted file mode 100644 index 995c6d0ad8..0000000000 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactMetapsionicTriggerSystem.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Content.Server._DV.Xenoarchaeology.XenoArtifacts.Triggers.Components; -using Content.Server.Nyanotrasen.StationEvents.Events; -using Content.Server.Xenoarchaeology.Artifact; -using Content.Shared.Abilities.Psionics; -using Content.Shared.Xenoarchaeology.Artifact.Components; -using Content.Shared.Xenoarchaeology.Artifact.XAT; - -namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; - -public sealed class ArtifactMetapsionicTriggerSystem : BaseXATSystem -{ - private EntityQuery _xenoArtifactQuery; - - public override void Initialize() - { - base.Initialize(); - - _xenoArtifactQuery = GetEntityQuery(); - - SubscribeLocalEvent(OnPowerDetected); - - SubscribeLocalEvent(OnGlimmerEventEnded); - } - - private void OnPowerDetected(Entity ent, ref PsionicPowerDetectedEvent args) - { - if (!TryComp(ent, out var node)) - return; - - if (node.Attached == null) - return; - - var artifact = _xenoArtifactQuery.Get(node.Attached.Value); - - if (!CanTrigger(artifact, (ent.Owner, node))) - return; - - Trigger(artifact, (ent.Owner, ent.Comp, node)); - } - - private void OnGlimmerEventEnded(GlimmerEventEndedEvent args) - { - var query = EntityQueryEnumerator(); - while (query.MoveNext(out var uid, out var comp, out var node)) - { - if (node.Attached == null) - continue; - - var artifact = _xenoArtifactQuery.Get(node.Attached.Value); - - if (!CanTrigger(artifact, (uid, node))) - continue; - - Trigger(artifact, (uid, comp, node)); - } - } -} diff --git a/Content.Shared/Abilities/Mime/MimePowersSystem.cs b/Content.Shared/Abilities/Mime/MimePowersSystem.cs index df0c6a4a3e..aa77ccb803 100644 --- a/Content.Shared/Abilities/Mime/MimePowersSystem.cs +++ b/Content.Shared/Abilities/Mime/MimePowersSystem.cs @@ -1,5 +1,4 @@ using Content.Shared.Popups; -using Content.Shared.Abilities.Psionics; //Nyano - Summary: Makes Mime psionic. using Content.Shared.Actions; using Content.Shared.Actions.Events; using Content.Shared.Alert; @@ -20,7 +19,6 @@ public sealed class MimePowersSystem : EntitySystem [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; // DeltaV [Dependency] private readonly TurfSystem _turf = default!; [Dependency] private readonly IMapManager _mapMan = default!; [Dependency] private readonly SharedContainerSystem _container = default!; @@ -71,10 +69,6 @@ public sealed class MimePowersSystem : EntitySystem _alertsSystem.ShowAlert(ent.Owner, ent.Comp.VowAlert); _actionsSystem.AddAction(ent, ref ent.Comp.InvisibleWallActionEntity, ent.Comp.InvisibleWallAction); - - //DeltaV - Summary: Add Psionic Ability to Mime. - if (TryComp(ent.Owner, out var psionic) && psionic.PsionicAbility == null) - psionic.PsionicAbility = ent.Comp.InvisibleWallActionEntity; } private void OnComponentShutdown(Entity ent, ref ComponentShutdown args) @@ -108,10 +102,6 @@ public sealed class MimePowersSystem : EntitySystem return; } - // Begin DeltaV Additions - _psionics.LogPowerUsed(ent.Owner, "invisible wall"); - // End DeltaV Additions - var messageSelf = Loc.GetString("mime-invisible-wall-popup-self", ("mime", Identity.Entity(ent.Owner, EntityManager))); var messageOthers = Loc.GetString("mime-invisible-wall-popup-others", ("mime", Identity.Entity(ent.Owner, EntityManager))); _popupSystem.PopupPredicted(messageSelf, messageOthers, ent, ent); diff --git a/Content.Shared/Anomaly/SharedAnomalySystem.cs b/Content.Shared/Anomaly/SharedAnomalySystem.cs index 4799d82b7a..92dcc860fd 100644 --- a/Content.Shared/Anomaly/SharedAnomalySystem.cs +++ b/Content.Shared/Anomaly/SharedAnomalySystem.cs @@ -24,7 +24,7 @@ using Content.Shared.Actions; namespace Content.Shared.Anomaly; -public abstract class SharedAnomalySystem : EntitySystem +public abstract partial class SharedAnomalySystem : EntitySystem // DeltaV - Made Partial { [Dependency] protected readonly IGameTiming Timing = default!; [Dependency] private readonly INetManager _net = default!; @@ -44,6 +44,8 @@ public abstract class SharedAnomalySystem : EntitySystem SubscribeLocalEvent(OnAnomalyThrowStart); SubscribeLocalEvent(OnLand); + + InitializePsionics(); // DeltaV - Introduce Dispellable behavior to Anomalies. } private void OnAnomalyThrowStart(Entity ent, ref MeleeThrowOnHitStartEvent args) diff --git a/Content.Shared/Inventory/InventorySystem.Relay.cs b/Content.Shared/Inventory/InventorySystem.Relay.cs index 431b79865f..8b5ca620e3 100644 --- a/Content.Shared/Inventory/InventorySystem.Relay.cs +++ b/Content.Shared/Inventory/InventorySystem.Relay.cs @@ -1,4 +1,5 @@ -using Content.Shared._DV.Overlays; // DeltaV +using Content.Shared._DV.Overlays; +using Content.Shared._DV.Psionics.Events; // DeltaV using Content.Shared.Armor; using Content.Shared.Atmos; using Content.Shared.Chat; @@ -80,6 +81,12 @@ public partial class InventorySystem SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); SubscribeLocalEvent(RefRelayInventoryEvent); + // DeltaV Start - Psionic Events + SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); + SubscribeLocalEvent(RefRelayInventoryEvent); + // DeltaV End - Psionic Events // Eye/vision events SubscribeLocalEvent(RelayInventoryEvent); diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DamageOnDispelComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DamageOnDispelComponent.cs deleted file mode 100644 index ce86111fc4..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DamageOnDispelComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Damage; - -namespace Content.Shared.Abilities.Psionics -{ - /// - /// Takes damage when dispelled. - /// - [RegisterComponent] - public sealed partial class DamageOnDispelComponent : Component - { - [DataField("damage", required: true)] - public DamageSpecifier Damage = default!; - - [DataField("variance")] - public float Variance = 0.5f; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispelPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispelPowerComponent.cs deleted file mode 100644 index cd88786636..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispelPowerComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class DispelPowerComponent : Component - { - [DataField("range")] - public float Range = 10f; - - [DataField("dispelActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? DispelActionId = "ActionDispel"; - - [DataField("dispelActionEntity")] - public EntityUid? DispelActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispellableComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispellableComponent.cs deleted file mode 100644 index 4035200418..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Dispel/DispellableComponent.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class DispellableComponent : Component - {} -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerComponent.cs deleted file mode 100644 index 5858c9f211..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerComponent.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class MassSleepPowerComponent : Component - { - public float Radius = 1.25f; - [DataField("massSleepActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? MassSleepActionId = "ActionMassSleep"; - - [DataField("massSleepActionEntity")] - public EntityUid? MassSleepActionEntity; - - [DataField] - public DoAfterId? DoAfter; - - [DataField] - public TimeSpan UseDelay = TimeSpan.FromSeconds(4); - - [DataField] - public float Duration = 5f; - - [DataField] - public float WarningRadius = 6f; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerSystem.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerSystem.cs deleted file mode 100644 index 670eb1c1ad..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MassSleep/MassSleepPowerSystem.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.DoAfter; -using Content.Shared.Bed.Sleep; -using Content.Shared.Damage.Components; -using Content.Shared.Mobs.Components; -using Robust.Shared.Prototypes; -using Content.Shared.Actions.Events; -using Content.Shared.Movement.Systems; -using Content.Shared.Popups; -using Content.Shared.Psionics.Events; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; - -namespace Content.Shared.Abilities.Psionics -{ - public sealed class MassSleepPowerSystem : EntitySystem - { - public ProtoId StatusEffectKey = "ForcedSleep"; - [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedPopupSystem _popup = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psionics = default!; - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly MovementModStatusSystem _movementMod = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - SubscribeLocalEvent(OnMassSleepDoAfter); - } - - private void OnInit(Entity ent, ref ComponentInit args) - { - _actions.AddAction(ent, ref ent.Comp.MassSleepActionEntity, ent.Comp.MassSleepActionId ); - - if (_actions.GetAction(ent.Comp.MassSleepActionEntity) is not { Comp.UseDelay: not null }) - { - _actions.StartUseDelay(ent.Comp.MassSleepActionEntity); - } - - if (TryComp(ent, out var psionic) && psionic.PsionicAbility == null) - psionic.PsionicAbility = ent.Comp.MassSleepActionEntity; - } - - private void OnShutdown(Entity ent, ref ComponentShutdown args) - { - _actions.RemoveAction(ent.Owner, ent.Comp.MassSleepActionEntity); - } - - private void OnPowerUsed(Entity ent, ref MassSleepPowerActionEvent args) - { - var ev = new MassSleepDoAfterEvent(); - var doAfterArgs = new DoAfterArgs(EntityManager, ent, ent.Comp.UseDelay, ev, ent) - { - BreakOnDamage = true - }; - - foreach (var entity in _lookup.GetEntitiesInRange(args.Performer, ent.Comp.WarningRadius)) - { - if (HasComp(entity) && entity != (EntityUid)ent && !HasComp(entity)) - { - _popup.PopupEntity(Loc.GetString("psionic-power-mass-sleep-warning"), - entity, - entity, - PopupType.LargeCaution); - } - } - - _movementMod.TryUpdateMovementSpeedModDuration(ent, MovementModStatusSystem.PsionicSlowdown, ent.Comp.UseDelay, 0.5f); - - _doAfter.TryStartDoAfter(doAfterArgs, out var doAfterId); - ent.Comp.DoAfter = doAfterId; - - _psionics.LogPowerUsed(ent, "mass sleep"); - args.Handled = true; - } - - private void OnMassSleepDoAfter(Entity ent, ref MassSleepDoAfterEvent args) - { - if (args.Handled) - return; - - if (args.Cancelled) - { - _statusEffects.TryRemoveStatusEffect(ent, "SlowedDown"); - return; - } - - foreach (var entity in _lookup.GetEntitiesInRange(args.User, ent.Comp.Radius)) - { - if (HasComp(entity) && entity != (EntityUid)ent && !HasComp(entity)) - { - if (TryComp(entity, out var damageable) && damageable.DamageContainerID == "Biological") - _statusEffects.TryAddStatusEffect(entity, StatusEffectKey, TimeSpan.FromSeconds(ent.Comp.Duration), false); - } - } - - ent.Comp.DoAfter = null; - } - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Metapsionics/MetapsionicPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Metapsionics/MetapsionicPowerComponent.cs deleted file mode 100644 index 39d1b0f3a2..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Metapsionics/MetapsionicPowerComponent.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Actions.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class MetapsionicPowerComponent : Component - { - [DataField("range")] - public float Range = 5f; - - public InstantActionComponent? MetapsionicPowerAction = null; - [DataField("metapsionicActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? MetapsionicActionId = "ActionMetapsionic"; - - [DataField("metapsionicActionEntity")] - public EntityUid? MetapsionicActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MindSwap/MindSwapPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MindSwap/MindSwapPowerComponent.cs deleted file mode 100644 index 7d07857f07..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/MindSwap/MindSwapPowerComponent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Abilities.Psionics; - -[RegisterComponent, NetworkedComponent] -public sealed partial class MindSwapPowerComponent : Component -{ - [DataField] - public EntProtoId? MindSwapActionId = "ActionMindSwap"; - - [DataField] - public EntityUid? MindSwapActionEntity; -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZap/NoosphericZapPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZap/NoosphericZapPowerComponent.cs deleted file mode 100644 index 0e91894b1d..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/NoosphericZap/NoosphericZapPowerComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class NoosphericZapPowerComponent : Component - { - [DataField("noosphericZapActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? NoosphericZapActionId = "ActionNoosphericZap"; - - [DataField("noosphericZapActionEntity")] - public EntityUid? NoosphericZapActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerComponent.cs deleted file mode 100644 index 3e198aa930..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityPowerComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicInvisibilityPowerComponent : Component - { - [DataField("psionicInvisibilityActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? PsionicInvisibilityActionId = "ActionPsionicInvisibility"; - - [DataField("psionicInvisibilityActionEntity")] - public EntityUid? PsionicInvisibilityActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityUsedComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityUsedComponent.cs deleted file mode 100644 index 9037b8bcdf..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicInvisibility/PsionicInvisibilityUsedComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicInvisibilityUsedComponent : Component - { - [ValidatePrototypeId] - public const string PsionicInvisibilityUsedActionPrototype = "ActionPsionicInvisibilityUsed"; - [DataField("psionicInvisibilityUsedActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? PsionicInvisibilityUsedActionId = "ActionPsionicInvisibilityUsed"; - - [DataField("psionicInvisibilityUsedActionEntity")] - public EntityUid? PsionicInvisibilityUsedActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegeneration/PsionicRegenerationPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegeneration/PsionicRegenerationPowerComponent.cs deleted file mode 100644 index 4a62e84d19..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/PsionicRegeneration/PsionicRegenerationPowerComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Robust.Shared.Audio; -using Content.Shared.DoAfter; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicRegenerationPowerComponent : Component - { - [DataField("doAfter")] - public DoAfterId? DoAfter; - - [DataField("essence")] - public float EssenceAmount = 20; - - [DataField("useDelay")] - public float UseDelay = 8f; - [DataField("soundUse")] - - public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/heartbeat_fast.ogg"); - - [DataField("psionicRegenerationActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? PsionicRegenerationActionId = "ActionPsionicRegeneration"; - - [DataField("psionicRegenerationActionEntity")] - public EntityUid? PsionicRegenerationActionEntity; - } -} - diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Pyrokinesis/PyrokinesisPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Pyrokinesis/PyrokinesisPowerComponent.cs deleted file mode 100644 index 9efd674133..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Pyrokinesis/PyrokinesisPowerComponent.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Actions.Components; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class PyrokinesisPowerComponent : Component - { - public EntityTargetActionComponent? PyrokinesisPowerAction = null; - [DataField("pyrokinesisActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? PyrokinesisActionId = "ActionPyrokinesis"; - - [DataField("pyrokinesisActionEntity")] - public EntityUid? PyrokinesisActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/SharedTelegnosisPowerSystem.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/SharedTelegnosisPowerSystem.cs deleted file mode 100644 index 17bdefe8e6..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/SharedTelegnosisPowerSystem.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Content.Shared._DV.Abilities.Psionics; -using Content.Shared._DV.Mind; -using Content.Shared.Interaction.Events; - -namespace Content.Shared.Abilities.Psionics; - -public abstract class SharedTelegnosisPowerSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - - SubscribeLocalEvent(OnInteractionAttempt); - SubscribeLocalEvent(OnShowSSDIndicator); - } - - private void OnInteractionAttempt(Entity ent, ref InteractionAttemptEvent args) - { - // no astrally stealing someones shoes - args.Cancelled = true; - } - - private void OnShowSSDIndicator(Entity entity, ref ShowSSDIndicatorEvent args) - { - if (!TryComp(entity, out var mindSwapped) || !HasComp(mindSwapped.OriginalEntity)) - return; // Only hide if currently projecting - args.Hidden = true; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosisPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosisPowerComponent.cs deleted file mode 100644 index 4d8bfbae15..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosisPowerComponent.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Actions.Components; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; -using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; - - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent, NetworkedComponent, Access(typeof(SharedTelegnosisPowerSystem))] - public sealed partial class TelegnosisPowerComponent : Component - { - [DataField("prototype")] - public string Prototype = "MobObserverTelegnostic"; - public InstantActionComponent? TelegnosisPowerAction = null; - [ValidatePrototypeId] - public const string TelegnosisActionPrototype = "ActionTelegnosis"; - [DataField("telegnosisActionId", - customTypeSerializer: typeof(PrototypeIdSerializer))] - public string? TelegnosisActionId = "ActionTelegnosis"; - - [DataField("telegnosisActionEntity")] - public EntityUid? TelegnosisActionEntity; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosticProjectionComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosticProjectionComponent.cs deleted file mode 100644 index 8de2b046d8..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Abilities/Telegnosis/TelegnosticProjectionComponent.cs +++ /dev/null @@ -1,4 +0,0 @@ -namespace Content.Shared.Abilities.Psionics; - -[RegisterComponent, Access(typeof(SharedTelegnosisPowerSystem))] -public sealed partial class TelegnosticProjectionComponent : Component; diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/ClothingGrantPsionicPowerComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/ClothingGrantPsionicPowerComponent.cs deleted file mode 100644 index 4cbb05c839..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/ClothingGrantPsionicPowerComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class ClothingGrantPsionicPowerComponent : Component - { - [DataField("power", required: true)] - public string Power = ""; - public bool IsActive = false; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/PsionicItemsSystem.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/PsionicItemsSystem.cs deleted file mode 100644 index f88acf61f3..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/PsionicItemsSystem.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Content.Shared.Inventory.Events; -using Content.Shared.Clothing.Components; -using Content.Shared.StatusEffect; - -namespace Content.Shared.Abilities.Psionics -{ - public sealed class PsionicItemsSystem : EntitySystem - { - [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; - [Dependency] private readonly IComponentFactory _componentFactory = default!; - [Dependency] private readonly SharedPsionicAbilitiesSystem _psiAbilities = default!; - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnTinfoilEquipped); - SubscribeLocalEvent(OnTinfoilUnequipped); - SubscribeLocalEvent(OnGranterEquipped); - SubscribeLocalEvent(OnGranterUnequipped); - } - private void OnTinfoilEquipped(EntityUid uid, TinfoilHatComponent component, GotEquippedEvent args) - { - // This only works on clothing - if (!TryComp(uid, out var clothing)) - return; - // Is the clothing in its actual slot? - if (!clothing.Slots.HasFlag(args.SlotFlags)) - return; - - var insul = EnsureComp(args.Equipee); - insul.Passthrough = component.Passthrough; - component.IsActive = true; - _psiAbilities.SetPsionicsThroughEligibility(args.Equipee); - } - - private void OnTinfoilUnequipped(EntityUid uid, TinfoilHatComponent component, GotUnequippedEvent args) - { - if (!component.IsActive) - return; - - if (!_statusEffects.HasStatusEffect(uid, "PsionicallyInsulated")) - RemComp(args.Equipee); - - component.IsActive = false; - _psiAbilities.SetPsionicsThroughEligibility(args.Equipee); - } - - private void OnGranterEquipped(EntityUid uid, ClothingGrantPsionicPowerComponent component, GotEquippedEvent args) - { - // This only works on clothing - if (!TryComp(uid, out var clothing)) - return; - // Is the clothing in its actual slot? - if (!clothing.Slots.HasFlag(args.SlotFlags)) - return; - // does the user already has this power? - var componentType = _componentFactory.GetRegistration(component.Power).Type; - if (EntityManager.HasComponent(args.Equipee, componentType)) return; - - - var newComponent = (Component) _componentFactory.GetComponent(componentType); - newComponent.Owner = args.Equipee; - - EntityManager.AddComponent(args.Equipee, newComponent); - - component.IsActive = true; - } - - private void OnGranterUnequipped(EntityUid uid, ClothingGrantPsionicPowerComponent component, GotUnequippedEvent args) - { - if (!component.IsActive) - return; - - component.IsActive = false; - var componentType = _componentFactory.GetRegistration(component.Power).Type; - if (EntityManager.HasComponent(args.Equipee, componentType)) - { - EntityManager.RemoveComponent(args.Equipee, componentType); - } - } - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/TinfoilHatComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/TinfoilHatComponent.cs deleted file mode 100644 index 5086b9f497..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/Items/TinfoilHatComponent.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class TinfoilHatComponent : Component - { - public bool IsActive = false; - - [DataField("passthrough")] - public bool Passthrough = false; - - /// - /// Whether this will turn to ash when its psionically fried. - /// - [DataField("destroyOnFry")] - public bool DestroyOnFry = true; - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicComponent.cs deleted file mode 100644 index 9091e03cfc..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicComponent.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.GameStates; - -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent, NetworkedComponent] - public sealed partial class PsionicComponent : Component - { - public EntityUid? PsionicAbility = null; - - /// - /// Ifrits, revenants, etc are explicitly magical beings that shouldn't get mindbreakered. - /// - [DataField("removable")] - public bool Removable = true; - - [DataField("activePowers")] - public HashSet ActivePowers = new(); - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicInsulationComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicInsulationComponent.cs deleted file mode 100644 index 12370da5ae..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicInsulationComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Shared.Abilities.Psionics -{ - [RegisterComponent] - public sealed partial class PsionicInsulationComponent : Component - { - public bool Passthrough = false; - - public List SuppressedFactions = new(); - } -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicsDisabledComponent.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicsDisabledComponent.cs deleted file mode 100644 index 28e7157a9d..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/PsionicsDisabledComponent.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Robust.Shared.GameStates; - -namespace Content.Shared.Abilities.Psionics -{ - /// - /// Only use this for the status effect, please. - /// - [RegisterComponent, NetworkedComponent] - public sealed partial class PsionicsDisabledComponent : Component - {} -} diff --git a/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs b/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs deleted file mode 100644 index 899a49424e..0000000000 --- a/Content.Shared/Nyanotrasen/Abilities/Psionics/SharedPsionicAbilitiesSystem.cs +++ /dev/null @@ -1,124 +0,0 @@ -using Content.Shared.Actions; -using Content.Shared.Administration.Logs; -using Content.Shared.Mobs; -using Content.Shared.Mobs.Components; -using Content.Shared.Popups; -using Content.Shared.Psionics.Glimmer; -using Robust.Shared.Random; -using Robust.Shared.Serialization; - -namespace Content.Shared.Abilities.Psionics; - -public sealed class SharedPsionicAbilitiesSystem : EntitySystem -{ - [Dependency] private readonly SharedActionsSystem _actions = default!; - [Dependency] private readonly EntityLookupSystem _lookup = default!; - [Dependency] private readonly SharedPopupSystem _popups = default!; - [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; - [Dependency] private readonly GlimmerSystem _glimmerSystem = default!; - [Dependency] private readonly IRobustRandom _robustRandom = default!; - - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnShutdown); - SubscribeLocalEvent(OnPowerUsed); - - SubscribeLocalEvent(OnMobStateChanged); - } - - private void OnPowerUsed(EntityUid uid, PsionicComponent component, PsionicPowerUsedEvent args) - { - var ev = new PsionicPowerDetectedEvent(uid, args.Power); - var coords = Transform(uid).Coordinates; - foreach (var ent in _lookup.GetEntitiesInRange(coords, 10f)) - { - if (ent.Owner != uid && !(TryComp(ent, out var insul) && !insul.Passthrough)) - { - RaiseLocalEvent(ent, ref ev); - _popups.PopupEntity(Loc.GetString("metapsionic-pulse-power", ("power", args.Power)), ent, ent, PopupType.LargeCaution); - args.Handled = true; - } - } - } - - private void OnInit(EntityUid uid, PsionicsDisabledComponent component, ComponentInit args) - { - SetPsionicsThroughEligibility(uid); - } - - private void OnShutdown(EntityUid uid, PsionicsDisabledComponent component, ComponentShutdown args) - { - SetPsionicsThroughEligibility(uid); - } - - private void OnMobStateChanged(EntityUid uid, PsionicComponent component, MobStateChangedEvent args) - { - SetPsionicsThroughEligibility(uid); - } - - /// - /// Checks whether the entity is eligible to use its psionic ability. This should be run after anything that could effect psionic eligibility. - /// - public void SetPsionicsThroughEligibility(EntityUid uid) - { - PsionicComponent? component = null; - if (!Resolve(uid, ref component, false)) - return; - - if (component.PsionicAbility == null) - return; - - if (_actions.GetAction(component.PsionicAbility) is not { } actionData) - return; - - _actions.SetEnabled(actionData.Owner, IsEligibleForPsionics(uid)); - } - - - - private bool IsEligibleForPsionics(EntityUid uid) - { - return !HasComp(uid) - && (!TryComp(uid, out var mobstate) || mobstate.CurrentState == MobState.Alive); - } - - public void LogPowerUsed(EntityUid uid, string power, int minGlimmer = 8, int maxGlimmer = 12) - { - _adminLogger.Add(Database.LogType.Psionics, Database.LogImpact.Medium, $"{ToPrettyString(uid):player} used {power}"); - var ev = new PsionicPowerUsedEvent(uid, power); - RaiseLocalEvent(uid, ev, false); - - _glimmerSystem.Glimmer += _robustRandom.Next(minGlimmer, maxGlimmer); - } -} - -/// -/// Event raised on a metapsionic entity when someone used a psionic power nearby. -/// -[ByRefEvent] -public record struct PsionicPowerDetectedEvent(EntityUid Psionic, string Power); - -public sealed class PsionicPowerUsedEvent : HandledEntityEventArgs -{ - public EntityUid User { get; } - public string Power = string.Empty; - - public PsionicPowerUsedEvent(EntityUid user, string power) - { - User = user; - Power = power; - } -} - -[Serializable] -[NetSerializable] -public sealed class PsionicsChangedEvent : EntityEventArgs -{ - public readonly NetEntity Euid; - public PsionicsChangedEvent(NetEntity euid) - { - Euid = euid; - } -} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/DispelPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/DispelPowerActionEvent.cs deleted file mode 100644 index 9f2c0c2e6b..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/DispelPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class DispelPowerActionEvent : EntityTargetActionEvent { } diff --git a/Content.Shared/Nyanotrasen/Actions/Events/MassSleepPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/MassSleepPowerActionEvent.cs deleted file mode 100644 index 5d775c2cc3..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/MassSleepPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class MassSleepPowerActionEvent : InstantActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/MetapsionicPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/MetapsionicPowerActionEvent.cs deleted file mode 100644 index b28801efe7..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/MetapsionicPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class MetapsionicPowerActionEvent : InstantActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/MindSwapPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/MindSwapPowerActionEvent.cs deleted file mode 100644 index 85701b0c5d..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/MindSwapPowerActionEvent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class MindSwapPowerActionEvent : EntityTargetActionEvent {} -public sealed partial class MindSwapPowerReturnActionEvent : InstantActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/NoosphericZapPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/NoosphericZapPowerActionEvent.cs deleted file mode 100644 index b725431094..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/NoosphericZapPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class NoosphericZapPowerActionEvent : EntityTargetActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/PsionicEruptionPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/PsionicEruptionPowerActionEvent.cs deleted file mode 100644 index 2468a67126..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/PsionicEruptionPowerActionEvent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Content.Shared.Actions.Events; - -public sealed partial class PsionicEruptionPowerActionEvent : InstantActionEvent { } diff --git a/Content.Shared/Nyanotrasen/Actions/Events/PsionicInvisibilityPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/PsionicInvisibilityPowerActionEvent.cs deleted file mode 100644 index cff2a5be6e..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/PsionicInvisibilityPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class PsionicInvisibilityPowerActionEvent : InstantActionEvent {} \ No newline at end of file diff --git a/Content.Shared/Nyanotrasen/Actions/Events/PsionicRegenerationPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/PsionicRegenerationPowerActionEvent.cs deleted file mode 100644 index b29cec924b..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/PsionicRegenerationPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class PsionicRegenerationPowerActionEvent : InstantActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/PyrokinesisPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/PyrokinesisPowerActionEvent.cs deleted file mode 100644 index 896ec0bb63..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/PyrokinesisPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class PyrokinesisPowerActionEvent : EntityTargetActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/RemovePsionicInvisibilityOffPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/RemovePsionicInvisibilityOffPowerActionEvent.cs deleted file mode 100644 index 0360ed4b04..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/RemovePsionicInvisibilityOffPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class RemovePsionicInvisibilityOffPowerActionEvent : InstantActionEvent {} diff --git a/Content.Shared/Nyanotrasen/Actions/Events/TelegnosisPowerActionEvent.cs b/Content.Shared/Nyanotrasen/Actions/Events/TelegnosisPowerActionEvent.cs deleted file mode 100644 index 70ce1a4ab8..0000000000 --- a/Content.Shared/Nyanotrasen/Actions/Events/TelegnosisPowerActionEvent.cs +++ /dev/null @@ -1,2 +0,0 @@ -namespace Content.Shared.Actions.Events; -public sealed partial class TelegnosisPowerActionEvent : InstantActionEvent {} \ No newline at end of file diff --git a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs index cafe5075c9..2522034f0a 100644 --- a/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs +++ b/Content.Shared/StatusEffectNew/StatusEffectSystem.Relay.cs @@ -1,3 +1,4 @@ +using Content.Shared._DV.Psionics.Events; // DeltaV - Psionics Refactor using Content.Shared.Body.Events; using Content.Shared.Damage.Events; using Content.Shared.Mobs.Events; @@ -35,6 +36,11 @@ public sealed partial class StatusEffectsSystem SubscribeLocalEvent(RelayStatusEffectEvent); SubscribeLocalEvent(RefRelayStatusEffectEvent); + + // DeltaV Start - Psionics Refactor + SubscribeLocalEvent(RefRelayStatusEffectEvent); + SubscribeLocalEvent(RefRelayStatusEffectEvent); + // DeltaV End - Psionics Refactor } private void RefRelayStatusEffectEvent(EntityUid uid, StatusEffectContainerComponent component, ref T args) where T : struct diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs index bf1a0c8cea..8ca4578ff8 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Node.cs @@ -1,5 +1,5 @@ using System.Linq; -using Content.Shared._DV.Xenoarchaeology.Artifact.Components; // DeltaV +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; // DeltaV using Content.Shared.EntityTable; using Content.Shared.NameIdentifier; using Content.Shared.Xenoarchaeology.Artifact.Components; diff --git a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs index 541602770e..ec26632d4a 100644 --- a/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs +++ b/Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.XAT.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared._DV.Psionics.Events; // DeltaV using Content.Shared.Chemistry; using Content.Shared.Damage; using Content.Shared.Damage.Systems; @@ -24,6 +25,7 @@ public abstract partial class SharedXenoArtifactSystem XATRelayLocalEvent(); XATRelayLocalEvent(); XATRelayLocalEvent(); + XATRelayLocalEvent(); // DeltaV // special case this one because we need to order the messages SubscribeLocalEvent(OnExamined); @@ -115,7 +117,7 @@ public abstract partial class SharedXenoArtifactSystem // faster unlock effect: if ( - ent.Comp.UnlockCompleteDuration is {} completeDuration + ent.Comp.UnlockCompleteDuration is {} completeDuration && TryGetNodeFromUnlockState((ent.Owner, unlockingComp, ent.Comp), out var unlockingNode) ) { diff --git a/Content.Shared/_DV/Abilities/Psionics/FracturedFormPowerComponent.cs b/Content.Shared/_DV/Abilities/Psionics/FracturedFormPowerComponent.cs deleted file mode 100644 index d123389c00..0000000000 --- a/Content.Shared/_DV/Abilities/Psionics/FracturedFormPowerComponent.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Content.Shared.Cloning; -using Robust.Shared.Prototypes; -using Content.Shared.Roles; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Content.Shared.DoAfter; - -namespace Content.Shared._DV.Abilities.Psionics; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class FracturedFormPowerComponent : Component -{ - [DataField] - public EntProtoId FracturedFormActionId = "ActionFracturedForm"; - - [DataField, AutoNetworkedField] - public EntityUid? FracturedFormActionEntity; - [DataField] - public DoAfterId? DoAfter; - - [DataField] - public ProtoId CopyNaked = "CloningPod"; - [DataField] - public ProtoId CopyClothed = "Antag"; - [DataField] - public ProtoId VisitorJob = "Passenger"; - [DataField] - public ProtoId? NakedJob = null; // Scary null, but we explicitely want no job for naked spawns. - [DataField] - public TimeSpan NextSwap = TimeSpan.MaxValue; - [DataField] - public float ManualSwapTime = 5f; - [DataField] - public bool SleepWarned = false; - [DataField] - public List Bodies { get; set; } = new(); - [DataField] - public SoundSpecifier SwapSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg") - { - Params = AudioParams.Default.WithVariation(0.05f) - }; -} diff --git a/Content.Shared/_DV/Abilities/Psionics/MindSwappedComponent.cs b/Content.Shared/_DV/Abilities/Psionics/MindSwappedComponent.cs deleted file mode 100644 index 6341cbdd72..0000000000 --- a/Content.Shared/_DV/Abilities/Psionics/MindSwappedComponent.cs +++ /dev/null @@ -1,16 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared._DV.Abilities.Psionics; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class MindSwappedComponent : Component -{ - [DataField, AutoNetworkedField] - public EntityUid OriginalEntity = default!; - [DataField] - public EntProtoId? MindSwapReturnActionId = "ActionMindSwapReturn"; - - [DataField] - public EntityUid? MindSwapReturnActionEntity; -} diff --git a/Content.Shared/_DV/Abilities/Psionics/PrecognitionPowerComponent.cs b/Content.Shared/_DV/Abilities/Psionics/PrecognitionPowerComponent.cs deleted file mode 100644 index 83051bd788..0000000000 --- a/Content.Shared/_DV/Abilities/Psionics/PrecognitionPowerComponent.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Content.Shared.DoAfter; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Abilities.Psionics; - -[RegisterComponent, NetworkedComponent] -public sealed partial class PrecognitionPowerComponent : Component -{ - [DataField] - public float RandomResultChance = 0.2f; - - [DataField] - public SoundSpecifier VisionSound = new SoundPathSpecifier("/Audio/_DV/Effects/clang2.ogg"); - - [DataField] - public EntityUid? SoundStream; - - [DataField] - public DoAfterId? DoAfter; - - [DataField] - public TimeSpan UseDelay = TimeSpan.FromSeconds(8.35); // The length of the sound effect - - [DataField] - public EntProtoId PrecognitionActionId = "ActionPrecognition"; - - [DataField] - public EntityUid? PrecognitionActionEntity; -} diff --git a/Content.Shared/_DV/Abilities/Psionics/SharedFracturedFormPowerSystem.cs b/Content.Shared/_DV/Abilities/Psionics/SharedFracturedFormPowerSystem.cs deleted file mode 100644 index 4ee3105430..0000000000 --- a/Content.Shared/_DV/Abilities/Psionics/SharedFracturedFormPowerSystem.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Content.Shared._DV.Mind; - -namespace Content.Shared._DV.Abilities.Psionics; - -public abstract class SharedFracturedFormPowerSystem : EntitySystem -{ - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnShowSSDIndicator); - } - - private void OnShowSSDIndicator(Entity entity, ref ShowSSDIndicatorEvent args) - { - if (HasComp(entity)) - return; - args.Hidden = true; - } -} diff --git a/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs b/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs deleted file mode 100644 index aa26a2e1e5..0000000000 --- a/Content.Shared/_DV/Abilities/ShatterLightsAbilityComponent.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Content.Shared.Actions; -using Robust.Shared.Audio; -using Robust.Shared.GameStates; -using Robust.Shared.Prototypes; - -namespace Content.Shared._DV.Abilities; - -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class ShatterLightsAbilityComponent : Component -{ - /// - /// The radius in which lights will be broken. - /// - [DataField] - public float Radius = 10f; - - /// - /// If true, lights will only be broken if the entity has line of sight to them. - /// - [DataField] - public bool LineOfSight = false; - - /// - /// The radius to ignore line of sight restrictions. - /// - [DataField] - public float PenetratingRadius = 0f; - - /// - /// The action that triggers the shatter lights ability. - /// - [DataField] - public EntProtoId ShatterLightsActionId = "ActionShatterLights"; - - /// - /// Standing reference to the action entity, if it exists. - /// - [DataField, AutoNetworkedField] - public EntityUid? ShatterLightsActionEntity; - - /// - /// The sound to play when the ability is used. - /// - [DataField] - public SoundSpecifier AbilitySound = new SoundPathSpecifier("/Audio/_DV/Effects/creepyshriek.ogg"); - - /// - /// The effect to spawn when the ability is used. - /// - [DataField] - public EntProtoId Effect = "CMEffectScreech"; -} - -public sealed partial class ShatterLightsActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Actions/Events/FracturedFormPowerActionEvent.cs b/Content.Shared/_DV/Actions/Events/FracturedFormPowerActionEvent.cs deleted file mode 100644 index e64939e0a7..0000000000 --- a/Content.Shared/_DV/Actions/Events/FracturedFormPowerActionEvent.cs +++ /dev/null @@ -1,3 +0,0 @@ -namespace Content.Shared.Actions.Events; - -public sealed partial class FracturedFormPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Anomaly/SharedAnomalySystem.Psionics.cs b/Content.Shared/_DV/Anomaly/SharedAnomalySystem.Psionics.cs new file mode 100644 index 0000000000..bd8bad229a --- /dev/null +++ b/Content.Shared/_DV/Anomaly/SharedAnomalySystem.Psionics.cs @@ -0,0 +1,27 @@ +using Content.Shared._DV.CosmicCult; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Anomaly.Components; + +namespace Content.Shared.Anomaly; + +public abstract partial class SharedAnomalySystem +{ + [Dependency] private readonly SharedDispelPowerSystem _dispel = default!; + + private void InitializePsionics() + { + SubscribeLocalEvent(OnDispelled); + } + + //Nyano - Summary: gives dispellable behavior to Anomalies. + private void OnDispelled(Entity anomaly, ref DispelledEvent args) + { + if (HasComp(anomaly)) // begone nyanocode interference with cosmic cult + return; + + _dispel.DealDispelDamage(anomaly, dispeller: args.Dispeller); + ChangeAnomalyHealth(anomaly, 0 - Random.NextFloat(0.4f, 0.8f), anomaly.Comp); + args.Handled = true; + } +} diff --git a/Content.Shared/_DV/Chat/Components/TelepathicRepeaterComponent.cs b/Content.Shared/_DV/Chat/Components/TelepathicRepeaterComponent.cs new file mode 100644 index 0000000000..2fce91260e --- /dev/null +++ b/Content.Shared/_DV/Chat/Components/TelepathicRepeaterComponent.cs @@ -0,0 +1,9 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Chat.Components; + +/// +/// Repeats whatever is happening in telepathic chat. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class TelepathicRepeaterComponent : Component; diff --git a/Content.Shared/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffect.cs b/Content.Shared/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffect.cs similarity index 63% rename from Content.Shared/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffect.cs rename to Content.Shared/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffect.cs index 90365a87b7..0421682190 100644 --- a/Content.Shared/_DV/EntityEffects/Effects/Psionics/RerollPsionicAbilitiesEntityEffect.cs +++ b/Content.Shared/_DV/EntityEffects/Effects/Psionics/RollPsionicAbilityEntityEffect.cs @@ -4,14 +4,14 @@ using Robust.Shared.Prototypes; namespace Content.Shared._DV.EntityEffects.Effects.Psionics; /// -public sealed partial class RerollPsionicAbilities : EntityEffectBase +public sealed partial class RollPsionicAbility : EntityEffectBase { /// /// Reroll multiplier. /// - [DataField("bonusMultiplier")] + [DataField] public float BonusMultiplier = 1f; public override string? EntityEffectGuidebookText(IPrototypeManager prototype, IEntitySystemManager entSys) - => Loc.GetString("reagent-effect-guidebook-chem-reroll-psionic", ("chance", Probability)); + => Loc.GetString("reagent-effect-guidebook-chem-roll-psionic", ("multiplier", BonusMultiplier)); } diff --git a/Content.Shared/_DV/Movement/Systems/MovementModStatusSystem.cs b/Content.Shared/_DV/Movement/Systems/MovementModStatusSystem.cs deleted file mode 100644 index 9997f1dc93..0000000000 --- a/Content.Shared/_DV/Movement/Systems/MovementModStatusSystem.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Content.Shared.Movement.Components; -using Content.Shared.Movement.Events; -using Content.Shared.StatusEffectNew; -using Robust.Shared.Prototypes; - -namespace Content.Shared.Movement.Systems; - -public sealed partial class MovementModStatusSystem : EntitySystem -{ - public static readonly EntProtoId PsionicSlowdown = "PsionicSlowdownStatusEffect"; -} diff --git a/Content.Shared/_DV/Psionics/Components/AntiPsionicWeaponComponent.cs b/Content.Shared/_DV/Psionics/Components/AntiPsionicWeaponComponent.cs new file mode 100644 index 0000000000..b4e2ecb4bd --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/AntiPsionicWeaponComponent.cs @@ -0,0 +1,45 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class AntiPsionicWeaponComponent : Component +{ + /// + /// The DamageModifiers for each DamageType. + /// + [DataField(required: true)] + public DamageModifierSet Modifiers = default!; + + /// + /// The additional stamina damage dealt by anti-psionic weaponry. + /// + [DataField] + public float StaminaDamageMultiplier = 1f; + + /// + /// The chance to disable the target's psionic abilities on hit. + /// + [DataField] + public float DisableChance = 0.3f; + + /// + /// Punish the user when used against a non-psionic target. + /// + [DataField] + public bool Punish; + + /// + /// The chance for the weapon to punish the user when used against a non-psionic target. + /// + [DataField] + public float PunishChance = 0.5f; + + /// + /// The sound created when hitting a psionic user with the weapon or being punished. + /// + [DataField] + public SoundSpecifier? HitSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); +} diff --git a/Content.Shared/_DV/Psionics/Components/DamageOnDispelComponent.cs b/Content.Shared/_DV/Psionics/Components/DamageOnDispelComponent.cs new file mode 100644 index 0000000000..ceeea94997 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/DamageOnDispelComponent.cs @@ -0,0 +1,31 @@ +using Content.Shared.Damage; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Takes damage when dispelled. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class DamageOnDispelComponent : Component +{ + /// + /// The damage dealt to them on being dispelled. + /// + [DataField(required: true)] + public DamageSpecifier Damage = default!; + + /// + /// The variance that will be multiplied with the initial value. + /// A variance of 0.5 will lead to the damage dealing either minimum half the damage, or 1.5x the damage maximum. + /// + [DataField] + public float Variance = 0.5f; + + /// + /// The sound that occurs when being dispelled. + /// + [DataField] + public SoundSpecifier DispelSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); +} diff --git a/Content.Shared/_DV/Psionics/Components/DispellableComponent.cs b/Content.Shared/_DV/Psionics/Components/DispellableComponent.cs new file mode 100644 index 0000000000..a40f6a669c --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/DispellableComponent.cs @@ -0,0 +1,14 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class DispellableComponent : Component +{ + /// + /// The sound that occurs when being dispelled. + /// + [DataField] + public SoundSpecifier DispelSound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); +} diff --git a/Content.Shared/_DV/Abilities/Psionics/FracturedFormBodyComponent.cs b/Content.Shared/_DV/Psionics/Components/FracturedFormBodyComponent.cs similarity index 70% rename from Content.Shared/_DV/Abilities/Psionics/FracturedFormBodyComponent.cs rename to Content.Shared/_DV/Psionics/Components/FracturedFormBodyComponent.cs index 6082a01c12..6dfe1885f1 100644 --- a/Content.Shared/_DV/Abilities/Psionics/FracturedFormBodyComponent.cs +++ b/Content.Shared/_DV/Psionics/Components/FracturedFormBodyComponent.cs @@ -1,6 +1,6 @@ using Robust.Shared.GameStates; -namespace Content.Shared._DV.Abilities.Psionics; +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; [RegisterComponent, NetworkedComponent] public sealed partial class FracturedFormBodyComponent : Component @@ -9,5 +9,5 @@ public sealed partial class FracturedFormBodyComponent : Component /// The entity uid of the form that is currently being controlled. /// [DataField] - public EntityUid ControllingForm { get; set; } = default!; + public EntityUid ControllingForm { get; set; } } diff --git a/Content.Shared/_DV/Psionics/Components/PotentialPsionicComponent.cs b/Content.Shared/_DV/Psionics/Components/PotentialPsionicComponent.cs new file mode 100644 index 0000000000..89c02a41de --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PotentialPsionicComponent.cs @@ -0,0 +1,46 @@ +using Content.Shared.EntityTable; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Entities with this component can become psionics at roundstart or via rerolling. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PotentialPsionicComponent : Component +{ + /// + /// The Base Chance for all potential psionics to become one at roundstart and gain a power. + /// + [DataField] + public float BaseChance = 0.15f; + + /// + /// Additive bonus for from your role. + /// + /// Command roles and the Chaplain get an additive bonus to being psionic. + [DataField, AutoNetworkedField] + public float JobBonusChance; + + /// + /// Additive bonus for from your species. + /// + /// Noöspheric attuned species like Kitsunes get an additive bonus. + [DataField] + public float SpeciesBonusChance; + + /// + /// Whether you've already attempted to roll a new power via other means. + /// + /// Lotophagoi Oil will attempt to roll a new power for the consumer if this is false. + /// It'll then be set true. + [DataField, AutoNetworkedField] + public bool Rolled; + + /// + /// The Prototype ID of the table containing the available psionic powers to roll. + /// + [DataField] + public ProtoId PsionicPowerTableId = "PsionicPowerTable"; +} diff --git a/Content.Shared/_DV/Abilities/Psionics/PrecognitionResultComponent.cs b/Content.Shared/_DV/Psionics/Components/PrecognitionResultComponent.cs similarity index 77% rename from Content.Shared/_DV/Abilities/Psionics/PrecognitionResultComponent.cs rename to Content.Shared/_DV/Psionics/Components/PrecognitionResultComponent.cs index 059ada567f..9f812cb8d8 100644 --- a/Content.Shared/_DV/Abilities/Psionics/PrecognitionResultComponent.cs +++ b/Content.Shared/_DV/Psionics/Components/PrecognitionResultComponent.cs @@ -6,6 +6,9 @@ namespace Content.Shared.Abilities.Psionics; [RegisterComponent] public sealed partial class PrecognitionResultComponent : Component { + /// + /// The message that will warn the psionic about the coming event. + /// [DataField(required: true)] public LocId Message; diff --git a/Content.Shared/_DV/Psionics/Components/PsionicComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicComponent.cs new file mode 100644 index 0000000000..63ab1ba5f4 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Entities with this component are psionics and can use powers. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PsionicComponent : Component +{ + /// + /// The list of the action buttons for every power. + /// + [DataField] + public HashSet PsionicPowersActionEntities = []; + + /// + /// Whether the psionic gets stunned when a psionic power gets removed. This doesn't mean they lost all psionic powers. + /// Psionic powers themselves regulate if they can be removed. + /// + [DataField] + public bool StunOnRemoval = true; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicInvisibilityUsedComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicInvisibilityUsedComponent.cs new file mode 100644 index 0000000000..00c641abe3 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicInvisibilityUsedComponent.cs @@ -0,0 +1,23 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class PsionicInvisibilityUsedComponent : Component +{ + /// + /// The sound that plays when going invisible. + /// + public SoundSpecifier Sound = new SoundPathSpecifier("/Audio/Effects/toss.ogg"); + + /// + /// They are fully invisible. This only makes their sprite appear shaded so they know they're invisible. + /// + public float InvisibilityStrength = 0.66f; + + /// + /// How long the user will be stunned when leaving invisibility. + /// + public TimeSpan StunDuration = TimeSpan.FromSeconds(4); +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowerDetectorComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowerDetectorComponent.cs new file mode 100644 index 0000000000..16e1ce1b6c --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowerDetectorComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Entities with this component can detect psionic usage nearby. +/// This is usually paired with the metapsionic pulse power. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PsionicPowerDetectorComponent : Component +{ + /// + /// In case the detection is provided by an item, remember the wearer. + /// + [DataField, AutoNetworkedField] + public EntityUid? Wearer; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/BasePsionicPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/BasePsionicPowerComponent.cs new file mode 100644 index 0000000000..54d91e4316 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/BasePsionicPowerComponent.cs @@ -0,0 +1,99 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +/// +/// Entities with this component are psionically insulated from a source. +/// +public abstract partial class BasePsionicPowerComponent : Component +{ + /// + /// The actual UID for the action entity. It'll be saved here when the component is initialized. + /// + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + + /// + /// The prototype ID for the action. + /// It's set up in the component, optionally overriden via YML and then referenced via a string here. + /// + [DataField] + public abstract EntProtoId ActionProtoId { get; set; } + + /// + /// The Loc string for the name of the power. + /// + [DataField] + public abstract string PowerName { get; set; } + + /// + /// The minimum glimmer amount that will be changed upon use of the psionic power. + /// Should be lower than . + /// + [DataField] + public abstract int MinGlimmerChanged { get; set; } + + /// + /// The maximum glimmer amount that will be changed upon use of the psionic power. + /// Should be higher than . + /// + [DataField] + public abstract int MaxGlimmerChanged { get; set; } + + /// + /// Whether this ability can be removed via mindbreaking. + /// + /// Revenants shouldn't be able to lose their powers. + [DataField] + public bool CanBeRemoved = true; + + /// + /// When a power uses a DoAfter, the ID can be saved here for convenience. + /// It'll handle being dispelled automatically. + /// It'll need to be broken up into the DoAfter EntityUid and ushort index first. + /// + [DataField, AutoNetworkedField] + public EntityUid? DoAfterEntityId; + + /// + /// When a power uses a DoAfter, the index can be saved here for convenience. + /// It'll handle being dispelled automatically. + /// It'll need to be broken up into the DoAfter EntityUid and ushort index first. + /// + [DataField, AutoNetworkedField] + public ushort? DoAfterIdIndex; + + /// + /// Helper method to save a DoAfterId as DoAfterIds are not serializable and therefore cannot be networked. + /// It's parts can be though, and can be rebuilt. + /// + /// The DoAfterId to save. If null, it'll remove the saved DoAfterId. + public void SaveDoAfterId(DoAfterId doAfterId) + { + DoAfterEntityId = doAfterId.Uid; + DoAfterIdIndex = doAfterId.Index; + } + + /// + /// Helper method to remove the saved DoAfterId. + /// + public void RemoveSavedDoAfterId() + { + DoAfterEntityId = null; + DoAfterIdIndex = null; + } + + /// + /// A helper method to get a saved DoAfterId. + /// + /// Returns a DoAfterId if one is present, null if not. + public DoAfterId? GetDoAfterId() + { + if (DoAfterEntityId is not { } doAfterId + || DoAfterIdIndex is not { } doAfterIndex) + return null; + + return new DoAfterId(doAfterId, doAfterIndex); + } +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/DispelPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/DispelPowerComponent.cs new file mode 100644 index 0000000000..9b656c8963 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/DispelPowerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class DispelPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionDispel"; + + public override string PowerName { get; set; } = "psionic-power-name-dispel"; + + public override int MinGlimmerChanged { get; set; } = 5; + + public override int MaxGlimmerChanged { get; set; } = 10; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/FracturedFormPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/FracturedFormPowerComponent.cs new file mode 100644 index 0000000000..6dc9539e49 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/FracturedFormPowerComponent.cs @@ -0,0 +1,124 @@ +using Content.Shared.Cloning; +using Content.Shared.Roles; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class FracturedFormPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionFracturedForm"; + + public override string PowerName { get; set; } = "psionic-power-name-fractured-form"; + + public override int MinGlimmerChanged { get; set; } = 5; + + public override int MaxGlimmerChanged { get; set; } = 30; + + /// + /// The chance for the clone to have the starting gear of the psionic. + /// + [DataField] + public float HasGearChance = 0.4f; + + /// + /// The chance for the clone to have a different species than the psionic. + /// + [DataField] + public float DifferentSpeciesChance = 0.6f; + + /// + /// These are settings for creating the second body. + /// + [DataField] + public ProtoId CopyNaked = "CloningPod"; + + /// + /// These are settings for creating the second body. + /// + [DataField] + public ProtoId CopyClothed = "Antag"; + + /// + /// These are settings for creating the second body. + /// + [DataField] + public ProtoId VisitorJob = "Passenger"; + + /// + /// These are settings for creating the second body. + /// + [DataField] + public ProtoId? NakedJob; // Scary null, but we explicitly want no job for naked spawns. + + /// + /// The minimum time between forced swaps. + /// + [DataField] + public TimeSpan NextSwapMinTime = TimeSpan.FromMinutes(5); + + /// + /// The maximum time between swaps. + /// + [DataField] + public TimeSpan NextSwapMaxTime = TimeSpan.FromMinutes(20); + + /// + /// The current timer for when the next swap is forced. + /// + [DataField, AutoPausedField] + public TimeSpan NextSwap = TimeSpan.FromMinutes(5); + + /// + /// The minimum time between voluntary swaps by sleeping. + /// + [DataField] + public TimeSpan VoluntarySwapCooldown = TimeSpan.FromSeconds(10); + + /// + /// The minimum time between voluntary swaps by sleeping. + /// + [DataField, AutoPausedField] + public TimeSpan NextVoluntarySwap = TimeSpan.FromSeconds(10); + + /// + /// The length of the DoAfter for swapping bodies. + /// + [DataField] + public float ManualSwapTime = 5f; + + /// + /// The time the psionic needs to be asleep to swap bodies. + /// + [DataField] + public TimeSpan SwapTimeAsleep = TimeSpan.FromSeconds(3); + + /// + /// The time length where the warning is sent before the forced swap. + /// + [DataField] + public TimeSpan WarningTimeBeforeSleep = TimeSpan.FromSeconds(5); + + /// + /// A boolean check for when the person has been warned. + /// + [DataField] + public bool SleepWarned; + + /// + /// The bodies that the psionic user can swap into. + /// + [DataField, AutoNetworkedField] + public List Bodies { get; set; } = []; + + /// + /// The sound that plays when the swap happens. + /// + [DataField] + public SoundSpecifier SwapSound = new SoundPathSpecifier("/Audio/Effects/teleport_arrival.ogg") + { + Params = AudioParams.Default.WithVariation(0.05f), + }; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/MassSleepPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MassSleepPowerComponent.cs new file mode 100644 index 0000000000..a5998c95ce --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MassSleepPowerComponent.cs @@ -0,0 +1,40 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MassSleepPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionMassSleep"; + + public override string PowerName { get; set; } = "psionic-power-name-mass-sleep"; + + public override int MinGlimmerChanged { get; set; } = 10; + + public override int MaxGlimmerChanged { get; set; } = 20; + + /// + /// The radius to where people will fall asleep. + /// + [DataField] + public float Radius = 2f; + + /// + /// The duration of the DoAfter. Casting time, per say. + /// + [DataField] + public TimeSpan UseDelay = TimeSpan.FromSeconds(4); + + /// + /// How long the victims will be asleep. + /// + [DataField] + public TimeSpan Duration = TimeSpan.FromSeconds(5); + + /// + /// The radius for where people will be warned about being mass slept. + /// + [DataField] + public float WarningRadius = 6f; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/MetapsionicPulsePowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MetapsionicPulsePowerComponent.cs new file mode 100644 index 0000000000..559336c11c --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MetapsionicPulsePowerComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MetapsionicPulsePowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionMetapsionicPulse"; + + public override string PowerName { get; set; } = "psionic-power-name-metapsionic"; + + public override int MinGlimmerChanged { get; set; } = 1; + + public override int MaxGlimmerChanged { get; set; } = 10; + + /// + /// The radius of the pulse. + /// + [DataField] + public float Range = 1.5f; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwapPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwapPowerComponent.cs new file mode 100644 index 0000000000..7215d19cd3 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwapPowerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MindSwapPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionMindSwapPsionic"; + + public override string PowerName { get; set; } = "psionic-power-name-mindswap"; + + public override int MinGlimmerChanged { get; set; } = 5; + + public override int MaxGlimmerChanged { get; set; } = 30; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwappedReturnPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwappedReturnPowerComponent.cs new file mode 100644 index 0000000000..84d40b780d --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/MindSwappedReturnPowerComponent.cs @@ -0,0 +1,19 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class MindSwappedReturnPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionMindSwapReturn"; + + public override string PowerName { get; set; } = "psionic-power-name-mindswap-return"; + + public override int MinGlimmerChanged { get; set; } = 0; + + public override int MaxGlimmerChanged { get; set; } = 0; + + [DataField, AutoNetworkedField] + public EntityUid OriginalEntity; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/NoosphericZapPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/NoosphericZapPowerComponent.cs new file mode 100644 index 0000000000..8273d8e3b0 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/NoosphericZapPowerComponent.cs @@ -0,0 +1,34 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class NoosphericZapPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionNoosphericZap"; + + public override string PowerName { get; set; } = "psionic-power-name-noospheric-zap"; + + public override int MinGlimmerChanged { get; set; } = 10; + + public override int MaxGlimmerChanged { get; set; } = 50; + + /// + /// The prototype for the lightning. + /// + [DataField] + public EntProtoId LightningPrototpyeId = "PsionicLightning"; + + /// + /// How much damage the lightning will do if the target isn't insulated. + /// + [DataField] + public int ShockDamage = 15; + + /// + /// How long the target will be stunned. + /// + [DataField] + public TimeSpan StunDuration = TimeSpan.FromSeconds(3); +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/PrecognitionPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PrecognitionPowerComponent.cs new file mode 100644 index 0000000000..1fc51c403e --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PrecognitionPowerComponent.cs @@ -0,0 +1,59 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PrecognitionPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionPrecognition"; + + public override string PowerName { get; set; } = "psionic-power-name-precognition"; + + public override int MinGlimmerChanged { get; set; } = 5; + + public override int MaxGlimmerChanged { get; set; } = 10; + + /// + /// This dictates the chance that it'll return a wrong message, seeding unreliance. + /// + [DataField] + public float RandomResultChance = 0.2f; + + /// + /// The time it takes for the precognition to finish. Casting time, so to speak. + /// + [DataField] + public TimeSpan UseDelay = TimeSpan.FromSeconds(8.35); // The length of the sound effect + + /// + /// It'll have a shorter cooldown if it was cancelled. + /// + [DataField] + public TimeSpan CancellationCooldown = TimeSpan.FromSeconds(15); + + /// + /// The minimum time distance to the next event to be considered as a result for the precognition. + /// + [DataField] + public TimeSpan MinEventTimeDistance = TimeSpan.FromSeconds(30); + + /// + /// The maximum time distance to the next event to be considered as a result for the precognition. + /// + [DataField] + public TimeSpan MaxEventTimeDistance = TimeSpan.FromMinutes(10); + + /// + /// The sound that plays when you start the DoAfter. + /// + [DataField] + public SoundSpecifier VisionSound = new SoundPathSpecifier("/Audio/_DV/Effects/clang2.ogg"); + + /// + /// Cached SoundStream, so it can be stopped if the DoAfter is prematurely cancelled. + /// + [DataField] + public EntityUid? SoundStream; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicEruptionPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicEruptionPowerComponent.cs new file mode 100644 index 0000000000..19b08bd33b --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicEruptionPowerComponent.cs @@ -0,0 +1,49 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause] +public sealed partial class PsionicEruptionPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionEruption"; + + public override string PowerName { get; set; } = "psionic-power-name-eruption"; + + public override int MinGlimmerChanged { get; set; } = -200; + + public override int MaxGlimmerChanged { get; set; } = -100; + + /// + /// Minimum time for the Detonation DoAfter to take effect. + /// Half of it is the time for every spark. + /// + [DataField] + public TimeSpan MinDetonateDelay = TimeSpan.FromSeconds(10); + + /// + /// Maximum time for the Detonation DoAfter to take effect. + /// Half of it is the time for every spark. + /// + [DataField] + public TimeSpan MaxDetonateDelay = TimeSpan.FromSeconds(30); + + /// + /// The sound that appears when the action is pressed. + /// + [DataField] + public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/heartbeat_fast.ogg"); + + /// + /// The timespan for the next annoy popup. This will be refreshed depending on the glimmer amount. + /// + [DataField, AutoPausedField] + public TimeSpan NextAnnoy = TimeSpan.FromSeconds(5); + + /// + /// The timespan for the next spark to appear. + /// + [DataField, AutoPausedField] + public TimeSpan? NextSpark; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicInvisibilityPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicInvisibilityPowerComponent.cs new file mode 100644 index 0000000000..5a9276441a --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicInvisibilityPowerComponent.cs @@ -0,0 +1,16 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PsionicInvisibilityPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionPsionicInvisibility"; + + public override string PowerName { get; set; } = "psionic-power-name-psionic-invisibility"; + + public override int MinGlimmerChanged { get; set; } = 10; + + public override int MaxGlimmerChanged { get; set; } = 25; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicRegenerationPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicRegenerationPowerComponent.cs new file mode 100644 index 0000000000..d203554e08 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsionicRegenerationPowerComponent.cs @@ -0,0 +1,41 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PsionicRegenerationPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionPsionicRegeneration"; + + public override string PowerName { get; set; } = "psionic-power-name-psionic-regeneration"; + + public override int MinGlimmerChanged { get; set; } = 20; + + public override int MaxGlimmerChanged { get; set; } = 30; + + /// + /// How much prometheum essence will be injected into the psionic on full completion. + /// + [DataField] + public float EssenceAmount = 20; + + /// + /// The reagent ID that will be inserted into the bloodstream. + /// + [DataField] + public string ReagentId = "Prometheum"; + + /// + /// How long the DoAfter lasts. + /// + [DataField] + public float UseDelay = 8f; + + /// + /// The sound that plays when activating the ability. + /// + [DataField] + public SoundSpecifier SoundUse = new SoundPathSpecifier("/Audio/Nyanotrasen/Psionics/heartbeat_fast.ogg"); +} diff --git a/Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsychokineticScreamPowerComponent.cs similarity index 63% rename from Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs rename to Content.Shared/_DV/Psionics/Components/PsionicPowers/PsychokineticScreamPowerComponent.cs index 6def2b2a86..3906e6d9b2 100644 --- a/Content.Shared/_DV/Abilities/Psionics/PsychokineticScreamPowerComponent.cs +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PsychokineticScreamPowerComponent.cs @@ -1,13 +1,20 @@ -using Content.Shared.Actions; using Robust.Shared.Audio; using Robust.Shared.GameStates; using Robust.Shared.Prototypes; -namespace Content.Shared._DV.Abilities; +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -public sealed partial class PsychokineticScreamPowerComponent : Component +public sealed partial class PsychokineticScreamPowerComponent : BasePsionicPowerComponent { + public override EntProtoId ActionProtoId { get; set; } = "ActionPsychokineticScream"; + + public override string PowerName { get; set; } = "psionic-power-name-psychokinetic"; + + public override int MinGlimmerChanged { get; set; } = 20; + + public override int MaxGlimmerChanged { get; set; } = 30; + /// /// The radius in which lights will be broken. /// @@ -18,19 +25,13 @@ public sealed partial class PsychokineticScreamPowerComponent : Component /// If true, lights will only be broken if the entity has line of sight to them. /// [DataField] - public bool LineOfSight = false; + public bool LineOfSight = true; /// - /// The action that triggers the psychokinetic scream ability. + /// The radius to ignore line of sight restrictions. /// [DataField] - public EntProtoId ShatterLightsActionId = "ActionPsychokineticScream"; - - /// - /// Standing reference to the action entity, if it exists. - /// - [DataField, AutoNetworkedField] - public EntityUid? PsychokineticScreamActionEntity; + public float PenetratingRadius; /// /// The sound to play when the ability is used. @@ -44,5 +45,3 @@ public sealed partial class PsychokineticScreamPowerComponent : Component [DataField] public EntProtoId Effect = "CMEffectScreech"; } - -public sealed partial class ShatterLightsActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/PyrokinesisPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PyrokinesisPowerComponent.cs new file mode 100644 index 0000000000..34ba801d0b --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/PyrokinesisPowerComponent.cs @@ -0,0 +1,22 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class PyrokinesisPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionPyrokinesis"; + + public override string PowerName { get; set; } = "psionic-power-name-Pyrokinesis"; + + public override int MinGlimmerChanged { get; set; } = 10; + + public override int MaxGlimmerChanged { get; set; } = 30; + + /// + /// How many firestacks will be added on the target. + /// + [DataField] + public int AddedFirestacks = 5; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicPowers/TelegnosisPowerComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicPowers/TelegnosisPowerComponent.cs new file mode 100644 index 0000000000..1abec39512 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicPowers/TelegnosisPowerComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Components.PsionicPowers; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedTelegnosisPowerSystem))] +public sealed partial class TelegnosisPowerComponent : BasePsionicPowerComponent +{ + public override EntProtoId ActionProtoId { get; set; } = "ActionTelegnosis"; + + public override string PowerName { get; set; } = "psionic-power-name-telegnosis"; + + public override int MinGlimmerChanged { get; set; } = 10; + + public override int MaxGlimmerChanged { get; set; } = 50; + + /// + /// The mob prototype that will be used to telegnosis. + /// + [DataField] + public EntProtoId Prototype = "MobObserverTelegnostic"; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicallyInsulativeComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicallyInsulativeComponent.cs new file mode 100644 index 0000000000..c720034fa1 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicallyInsulativeComponent.cs @@ -0,0 +1,43 @@ +using Robust.Shared.Audio; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Clothing with this component insulates the wearer upon being worn. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PsionicallyInsulativeComponent : Component +{ + /// + /// Whether the wearer can still use their own psionics. + /// + /// The mantis' jacket is insulated and allows passthrough, which protects them from psionics while allowing them to use their own. + [DataField] + public bool AllowsPsionicUsage; + + /// + /// Whether the wearer is shielded from psionic influences. + /// + /// The security headcage stops the wearer from using their own psionics, but can still be affected by others. + [DataField] + public bool ShieldsFromPsionics = true; + + /// + /// Whether it's active when it's equipped in pockets. + /// + [DataField] + public bool ActiveInPocket; + + /// + /// If yes, it'll be destroyed in a noöspheric fry event. + /// + [DataField] + public bool CanBeFried; + + /// + /// What sound it makes when a noöspheric fry event happens. + /// + [DataField] + public SoundSpecifier? FrySound = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg"); +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicallyInvisibleComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicallyInvisibleComponent.cs new file mode 100644 index 0000000000..0bad0ef659 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicallyInvisibleComponent.cs @@ -0,0 +1,17 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// This is not the power, this makes you just invisible to anyone potentially psionic. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class PsionicallyInvisibleComponent : Component +{ + /// + /// Whether the invisibility is currently active. + /// This can be toggled via psionically surpressing gear. + /// + [DataField] + public bool Active = true; +} diff --git a/Content.Shared/_DV/Psionics/Components/PsionicsDisabledComponent.cs b/Content.Shared/_DV/Psionics/Components/PsionicsDisabledComponent.cs new file mode 100644 index 0000000000..37ef745609 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/PsionicsDisabledComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Entities with this component cannot use psionic powers. +/// +/// This should solely be used for StatusEffects. For insulative gear, see +[RegisterComponent, NetworkedComponent] +public sealed partial class PsionicsDisabledComponent : Component; diff --git a/Content.Shared/_DV/Psionics/Components/ShieldedFromPsionicsComponent.cs b/Content.Shared/_DV/Psionics/Components/ShieldedFromPsionicsComponent.cs new file mode 100644 index 0000000000..8ed0345d43 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/ShieldedFromPsionicsComponent.cs @@ -0,0 +1,10 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +/// +/// Entities with this component are shielded from psionic powers. +/// +/// This should solely be used for StatusEffects. For insulative gear, see +[RegisterComponent, NetworkedComponent] +public sealed partial class ShieldedFromPsionicsComponent : Component; diff --git a/Content.Shared/_DV/Psionics/Components/TelegnosticProjectionComponent.cs b/Content.Shared/_DV/Psionics/Components/TelegnosticProjectionComponent.cs new file mode 100644 index 0000000000..5341577e2b --- /dev/null +++ b/Content.Shared/_DV/Psionics/Components/TelegnosticProjectionComponent.cs @@ -0,0 +1,7 @@ +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Robust.Shared.GameStates; + +namespace Content.Shared._DV.Psionics.Components; + +[RegisterComponent, NetworkedComponent, Access(typeof(SharedTelegnosisPowerSystem))] +public sealed partial class TelegnosticProjectionComponent : Component; diff --git a/Content.Shared/_DV/Psionics/Events.cs b/Content.Shared/_DV/Psionics/Events.cs index 9b54efd125..8c64c4ad43 100644 --- a/Content.Shared/_DV/Psionics/Events.cs +++ b/Content.Shared/_DV/Psionics/Events.cs @@ -3,17 +3,5 @@ using Robust.Shared.Serialization; namespace Content.Shared.Psionics.Events; -[Serializable, NetSerializable] -public sealed partial class PrecognitionDoAfterEvent : SimpleDoAfterEvent; - [Serializable, NetSerializable] public sealed partial class GlimmerWispDrainDoAfterEvent : SimpleDoAfterEvent; - -[Serializable, NetSerializable] -public sealed partial class MassSleepDoAfterEvent : SimpleDoAfterEvent; - -[Serializable, NetSerializable] -public sealed partial class PsionicEruptionDoAfterEvent : SimpleDoAfterEvent; - -[Serializable, NetSerializable] -public sealed partial class FracturedFormDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_DV/Psionics/Events/DispelledEvent.cs b/Content.Shared/_DV/Psionics/Events/DispelledEvent.cs new file mode 100644 index 0000000000..ec7e782859 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/DispelledEvent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Inventory; + +namespace Content.Shared._DV.Psionics.Events; + +public sealed class DispelledEvent(EntityUid dispeller, EntityUid target) : HandledEntityEventArgs, IInventoryRelayEvent +{ + /// + /// The person who used dispel on the target. + /// + public EntityUid Dispeller = dispeller; + + /// + /// The entity being dispelled. + /// + public EntityUid Target = target; + + public SlotFlags TargetSlots => SlotFlags.WITHOUT_POCKET; +} diff --git a/Content.Shared/_DV/Psionics/Events/NoosphericFryEvent.cs b/Content.Shared/_DV/Psionics/Events/NoosphericFryEvent.cs new file mode 100644 index 0000000000..1de34da17e --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/NoosphericFryEvent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Damage; +using Content.Shared.Inventory; + +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event raised on an entity when a noöspheric fry gamerule happens. +/// +/// The damage dealt to every entity wearing insulative gear. +/// The firestacks added to each +[ByRefEvent] +public readonly struct NoosphericFryEvent(DamageSpecifier damage, int fireStacks) : IInventoryRelayEvent +{ + public readonly DamageSpecifier Damage = damage; + public readonly int FireStacks = fireStacks; + + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET; +}; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/DispelPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/DispelPowerActionEvent.cs new file mode 100644 index 0000000000..20407dfe9e --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/DispelPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class DispelPowerActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/FracturedFormPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/FracturedFormPowerActionEvent.cs new file mode 100644 index 0000000000..81f5acba1a --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/FracturedFormPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class FracturedFormPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MassSleepPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MassSleepPowerActionEvent.cs new file mode 100644 index 0000000000..8db7659fb1 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MassSleepPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class MassSleepPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MetapsionicPulsePowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MetapsionicPulsePowerActionEvent.cs new file mode 100644 index 0000000000..d7b2ec7074 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MetapsionicPulsePowerActionEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +/// +/// This gets fired when someone uses the MetapsionicPulse action. +/// +[ByRefEvent] +public sealed partial class MetapsionicPulsePowerActionEvent : WorldTargetActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MindSwapPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MindSwapPowerActionEvent.cs new file mode 100644 index 0000000000..dd1fead352 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/MindSwapPowerActionEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class MindSwapPowerActionEvent : EntityTargetActionEvent; + +public sealed partial class MindSwappedReturnPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/NoosphericZapPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/NoosphericZapPowerActionEvent.cs new file mode 100644 index 0000000000..70d82a260c --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/NoosphericZapPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class NoosphericZapPowerActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicEruptionPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicEruptionPowerActionEvent.cs new file mode 100644 index 0000000000..267c83cc1f --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicEruptionPowerActionEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +/// +/// This gets fired when someone uses the PsionicEruption action. +/// +[ByRefEvent] +public sealed partial class PsionicEruptionPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicInvisibilityPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicInvisibilityPowerActionEvent.cs new file mode 100644 index 0000000000..243272965f --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicInvisibilityPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class PsionicInvisibilityPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicRegenerationPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicRegenerationPowerActionEvent.cs new file mode 100644 index 0000000000..15f894389d --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsionicRegenerationPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class PsionicRegenerationPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsychokineticScreamPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsychokineticScreamPowerActionEvent.cs new file mode 100644 index 0000000000..7d53f9e8be --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PsychokineticScreamPowerActionEvent.cs @@ -0,0 +1,9 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +/// +/// This gets fired when someone uses the ShatterLightsPower action. +/// +[ByRefEvent] +public sealed partial class PsychokineticScreamPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PyrokinesisPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PyrokinesisPowerActionEvent.cs new file mode 100644 index 0000000000..c50725bc4b --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/PyrokinesisPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class PyrokinesisPowerActionEvent : EntityTargetActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerActionEvents/TelegnosisPowerActionEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/TelegnosisPowerActionEvent.cs new file mode 100644 index 0000000000..c381c1e032 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerActionEvents/TelegnosisPowerActionEvent.cs @@ -0,0 +1,5 @@ +using Content.Shared.Actions; + +namespace Content.Shared._DV.Psionics.Events.PowerActionEvents; + +public sealed partial class TelegnosisPowerActionEvent : InstantActionEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/FracturedFormDoAfterEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/FracturedFormDoAfterEvent.cs new file mode 100644 index 0000000000..d8230270f1 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/FracturedFormDoAfterEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; + +[Serializable, NetSerializable] +public sealed partial class FracturedFormDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/MassSleepDoAfterEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/MassSleepDoAfterEvent.cs new file mode 100644 index 0000000000..a6c863f908 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/MassSleepDoAfterEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; + +[Serializable, NetSerializable] +public sealed partial class MassSleepDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PrecognitionDoAfterEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PrecognitionDoAfterEvent.cs new file mode 100644 index 0000000000..8e7701684a --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PrecognitionDoAfterEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; + +[Serializable, NetSerializable] +public sealed partial class PrecognitionDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PsionicEruptionDoAfterEvent.cs b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PsionicEruptionDoAfterEvent.cs new file mode 100644 index 0000000000..83ef936e05 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PowerDoAfterEvents/PsionicEruptionDoAfterEvent.cs @@ -0,0 +1,7 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Serialization; + +namespace Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; + +[Serializable, NetSerializable] +public sealed partial class PsionicEruptionDoAfterEvent : SimpleDoAfterEvent; diff --git a/Content.Shared/_DV/Psionics/Events/PsionicMindBrokenEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicMindBrokenEvent.cs new file mode 100644 index 0000000000..4a037a460a --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicMindBrokenEvent.cs @@ -0,0 +1,23 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event raised on an entity that is being mindbroken. +/// +[ByRefEvent] +public sealed class PsionicMindBrokenEvent(bool force) +{ + /// + /// Whether even unremovable abilities should be removed. + /// + public readonly bool Force = force; + + /// + /// This is true if at least one ability got removed. + /// + public bool Success; + + /// + /// This remains true if ALL abilities were removed. + /// + public bool AllRemoved = true; +}; diff --git a/Content.Shared/_DV/Psionics/Events/PsionicPowerDetectedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicPowerDetectedEvent.cs new file mode 100644 index 0000000000..b4bcff02b8 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicPowerDetectedEvent.cs @@ -0,0 +1,9 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event raised on an entity that can detect when someone used a psionic power nearby. +/// +/// The psionic that detected the psionic usage. +/// The psionic power that was used. +[ByRefEvent] +public readonly record struct PsionicPowerDetectedEvent(EntityUid Psionic, string Power); diff --git a/Content.Shared/_DV/Psionics/Events/PsionicPowerUseAttemptEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicPowerUseAttemptEvent.cs new file mode 100644 index 0000000000..e355718f13 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicPowerUseAttemptEvent.cs @@ -0,0 +1,15 @@ +using Content.Shared.Inventory; + +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event raised on an entity that is attempting to use a psionic power. +/// +/// returns true if able to use psionic powers, false if not. +[ByRefEvent] +public record struct PsionicPowerUseAttemptEvent() : IInventoryRelayEvent +{ + public bool CanUsePower = true; + + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET; +}; diff --git a/Content.Shared/_DV/Psionics/Events/PsionicPowerUsedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicPowerUsedEvent.cs new file mode 100644 index 0000000000..84956a8f97 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicPowerUsedEvent.cs @@ -0,0 +1,14 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event that gets raised whenever someone uses a psionic power. +/// +/// The performer who used the power. +/// The source of the psionic power. +/// The psionic power used. +public sealed class PsionicPowerUsedEvent(EntityUid user, EntityUid psionicSource, string power) : HandledEntityEventArgs +{ + public EntityUid User = user; + public EntityUid PsionicSource = psionicSource; + public string Power = power; +} diff --git a/Content.Shared/_DV/Psionics/Events/PsionicShieldedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicShieldedEvent.cs new file mode 100644 index 0000000000..185d057f94 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicShieldedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// This is raised on an entity when they are psionically shielded. +/// +/// The entity that is psionically shielded. +[ByRefEvent] +public record struct PsionicShieldedEvent(EntityUid Shielded); diff --git a/Content.Shared/_DV/Psionics/Events/PsionicStoppedShieldedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicStoppedShieldedEvent.cs new file mode 100644 index 0000000000..21dfb6e7b2 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicStoppedShieldedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// This is raised on an entity when they are no longer psionically shielded anymore. +/// +/// The entity who is no longer psionically shielded. +[ByRefEvent] +public record struct PsionicStoppedShieldedEvent(EntityUid Shielded); diff --git a/Content.Shared/_DV/Psionics/Events/PsionicStoppedSuppressedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicStoppedSuppressedEvent.cs new file mode 100644 index 0000000000..0914097c76 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicStoppedSuppressedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// This is raised on an entity that has been psionically suppressed, stopping them from using psionics. +/// +/// The entity that is no longer psionically suppressed. +[ByRefEvent] +public record struct PsionicStoppedSuppressedEvent(EntityUid Victim); diff --git a/Content.Shared/_DV/Psionics/Events/PsionicSuppressedEvent.cs b/Content.Shared/_DV/Psionics/Events/PsionicSuppressedEvent.cs new file mode 100644 index 0000000000..5188fc3cf0 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/PsionicSuppressedEvent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Raised on an entity when they are no longer psionically suppressed from using psionic abilities. +/// +/// The entity who is psionically suppressed +[ByRefEvent] +public record struct PsionicSuppressedEvent(EntityUid Victim); diff --git a/Content.Shared/_DV/Psionics/Events/TargetedByPsionicPowerEvent.cs b/Content.Shared/_DV/Psionics/Events/TargetedByPsionicPowerEvent.cs new file mode 100644 index 0000000000..86a7e1ae5d --- /dev/null +++ b/Content.Shared/_DV/Psionics/Events/TargetedByPsionicPowerEvent.cs @@ -0,0 +1,15 @@ +using Content.Shared.Inventory; + +namespace Content.Shared._DV.Psionics.Events; + +/// +/// Event raised on an entity that is being targetted by a psionic power. +/// +/// returns true if able to use psionic powers, false if not. +[ByRefEvent] +public record struct TargetedByPsionicPowerEvent() : IInventoryRelayEvent +{ + public bool IsShielded; + + SlotFlags IInventoryRelayEvent.TargetSlots => SlotFlags.WITHOUT_POCKET; +}; diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/BasePsionicPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/BasePsionicPowerSystem.cs new file mode 100644 index 0000000000..e3bec3b65c --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/BasePsionicPowerSystem.cs @@ -0,0 +1,208 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.Actions; +using Content.Shared.Administration.Logs; +using Content.Shared.DoAfter; +using Content.Shared.Inventory; +using Content.Shared.Popups; +using Content.Shared.Psionics.Glimmer; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This is a base psionic power system that handles being mindbroken and checks for being able to use psionic powers automagically! +/// You WILL NEED to parent of this. +/// +public abstract class BasePsionicPowerSystem : EntitySystem where T : BasePsionicPowerComponent where T1 : BaseActionEvent +{ + [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] protected readonly IRobustRandom Random = default!; + [Dependency] protected readonly IGameTiming Timing = default!; + [Dependency] protected readonly SharedActionsSystem Action = default!; + [Dependency] protected readonly SharedDoAfterSystem DoAfter = default!; + [Dependency] private readonly GlimmerSystem _glimmer = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] protected readonly SharedPsionicSystem Psionic = default!; + + /// + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnPowerInit); + + SubscribeLocalEvent(OnPowerActionUsed); + SubscribeLocalEvent(OnDispelled); + SubscribeLocalEvent(OnMindBroken); + SubscribeLocalEvent(OnGrantingClothingEquipped); + SubscribeLocalEvent(OnPsionicallySuppressed); + SubscribeLocalEvent(OnStoppedPsionicallySuppressed); + } + + /// + /// This is called on every power upon initialization, so the action gets put into the action container. + /// The only exemption is powers that don't have just one action or require special actions at initialize. + /// + /// The psionic power whose action is put into the container. + /// The args from the event. + protected virtual void OnPowerInit(Entity power, ref MapInitEvent args) + { + Action.AddAction(power, ref power.Comp.ActionEntity, power.Comp.ActionProtoId); + + var psionicComp = EnsureComp(power); + psionicComp.PsionicPowersActionEntities.Add(power.Comp.ActionEntity); + Dirty(power); + } + + /// + /// This is called whenever an entity pushes the psionic power action button. + /// + /// The psionic who attempts to use a psionic power. + /// The action event for said power. + private void OnPowerActionUsed(Entity psionic, ref T1 args) + { + if (Timing.ApplyingState || args.Handled) + return; + + args.Handled = true; + + if (Psionic.CanUsePsionicAbility(psionic)) + { + OnPowerUsed(psionic, ref args); + return; + } + + Popup.PopupClient(Loc.GetString("psionic-cannot-use-psionics"), args.Performer, args.Performer); + } + + /// + /// This is the creme of the system. If you add a new power, you will want to call this. + /// This is where the actual power takes place that follows after the button press. + /// You will need to call LogPowerUsed() at the end. + /// You will need to call Psionic.CanBeTargeted(target) if you add a power that can target things. + /// IMPORTANT! The psionic entity DOES NOT HAVE TO BE THE PLAYER! It can be a clothing! + /// If you want something to affect the player everytime, use args.Performer! + /// psionic.Owner => Source of the power. args.Performer => The user of the power. + /// + /// The source of the power. + /// The event. + protected abstract void OnPowerUsed(Entity psionic, ref T1 args); + + /// + /// This handles being dispelled while having an active DoAfter. + /// It'll be enough for most usages. + /// But you can overwrite it if you have multiple DoAfters or require a specific interaction with being dispelled. + /// + /// The psionic who has been dispelled. + /// The DispelledEvent. + protected virtual void OnDispelled(Entity psionic, ref DispelledEvent args) + { + if (args.Handled + || psionic.Comp.GetDoAfterId() is not { } doAfterId) + return; + + DoAfter.Cancel(doAfterId); + Popup.PopupClient(Loc.GetString("psionic-dispelled"), args.Target, args.Target, PopupType.MediumCaution); + psionic.Comp.RemoveSavedDoAfterId(); + + args.Handled = true; + Dirty(psionic); + } + + /// + /// This handles when the psionic gets mindbroken via chemicals or other. + /// + /// The psionic who is being mindbroken. + /// The mindbreaking event. + protected virtual void OnMindBroken(Entity psionic, ref PsionicMindBrokenEvent args) + { + if (psionic.Comp.CanBeRemoved || args.Force) + { + args.Success = true; + TryStopDoAfter(psionic, psionic.Owner); // Mindbreaking already has a popup, so no popup for breaking doAfters. + Action.RemoveAction(psionic.Comp.ActionEntity); + RemComp(psionic); + return; + } + + args.AllRemoved = false; + } + + /// + /// This handles equipping gear that has psionic abilities. These will be automatically relayed to the user. + /// + /// The clothing that grants the power and has the component. + /// The event. + private void OnGrantingClothingEquipped(Entity psionicClothing, ref GetItemActionsEvent args) + { + if (args.SlotFlags is null or SlotFlags.POCKET + || !HasComp(args.User)) // IPCs and non-player organics shouldn't be able to use abilities. + return; + + args.AddAction(psionicClothing.Comp.ActionEntity); + } + + /// + /// This interrupts DoAfters when being suppressed psionically. + /// No putting on skullcaps AFTER casting your DoAfter to be secure from Dispels. + /// + /// The power source. + /// The event. + protected virtual void OnPsionicallySuppressed(Entity power, ref PsionicSuppressedEvent args) + { + if (Timing.ApplyingState) + return; + + TryStopDoAfter(power, args.Victim, "psionic-equipped-shielded-in-doafter"); + } + + /// + /// This is raised when the suppression stops. + /// This allows powers to handle special behavior. + /// + /// The psionic who is no longer suppressed. + /// The event. + protected virtual void OnStoppedPsionicallySuppressed(Entity psionic, ref PsionicStoppedSuppressedEvent args) + { + } + + /// + /// This will log the power usage and increase glimmer, as well as making sure metapsionics hear it. + /// + /// The SOURCE of the psionic power. + /// The entity that PERFORMED the power. + protected void AfterPowerUsed(Entity psionicSource, EntityUid performer) + { + var power = Loc.GetString(psionicSource.Comp.PowerName); + _adminLogger.Add(Database.LogType.Psionics, Database.LogImpact.Medium, $"{ToPrettyString(psionicSource):player} used {power}"); + + var ev = new PsionicPowerUsedEvent(performer, psionicSource, power); + RaiseLocalEvent(psionicSource, ev); + + _glimmer.Glimmer += Random.Next(psionicSource.Comp.MinGlimmerChanged, psionicSource.Comp.MaxGlimmerChanged); + } + + /// + /// Stops the current DoAfter of the psionic user. + /// + /// The source of the power. + /// The entity performing the power. + /// The FTL string for the popup. If null, no popup. + /// Returns true if an doAfter was interrupted, otherwise false. + private bool TryStopDoAfter(Entity psionic, EntityUid performer, string? popup = null) + { + if (psionic.Comp.GetDoAfterId() is not { } doAfterId) + return false; + + DoAfter.Cancel(doAfterId); + if (popup != null) + Popup.PopupClient(Loc.GetString(popup), performer, performer, PopupType.MediumCaution); + + psionic.Comp.RemoveSavedDoAfterId(); + Dirty(psionic); + return true; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MetapsionicPulsePowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MetapsionicPulsePowerSystem.cs new file mode 100644 index 0000000000..e376d7cc76 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MetapsionicPulsePowerSystem.cs @@ -0,0 +1,55 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Popups; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This system allows a psionic user to spot other psionic entities via a pulse. +/// +public sealed class MetapsionicPulsePowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly EntityLookupSystem _lookupSystem = default!; + + protected override void OnPowerInit(Entity power, ref MapInitEvent args) + { + base.OnPowerInit(power, ref args); + + EnsureComp(power); + } + + protected override void OnPowerUsed(Entity psionic, ref MetapsionicPulsePowerActionEvent args) + { + foreach (var target in _lookupSystem.GetEntitiesInRange(args.Target, psionic.Comp.Range)) + { + if (target.Owner == args.Performer + || Transform(target).ParentUid == args.Performer) + continue; + + if (!Psionic.CanBeTargeted(target)) // Cannot detect shielded psionics. + continue; + + Popup.PopupClient(Loc.GetString("psionic-power-metapsionic-success"), args.Performer, args.Performer, PopupType.LargeCaution); + AfterPowerUsed(psionic, args.Performer); + + args.Handled = true; + return; + } + Popup.PopupClient(Loc.GetString("psionic-power-metapsionic-failure"), args.Performer, args.Performer, PopupType.Large); + AfterPowerUsed(psionic, args.Performer); + + args.Handled = true; + } + + protected override void OnMindBroken(Entity psionic, ref PsionicMindBrokenEvent args) + { + base.OnMindBroken(psionic, ref args); + // If the mindbreak was successful, remove the detector component too. + if (!psionic.Comp.Deleted) + return; + + RemComp(psionic); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MindSwappedReturnPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MindSwappedReturnPowerSystem.cs new file mode 100644 index 0000000000..50a16ae8ea --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/MindSwappedReturnPowerSystem.cs @@ -0,0 +1,76 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Popups; +using Content.Shared.Speech; +using Content.Shared.Stealth.Components; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// Sorry if this is shitcode, but the return power actually should behave like a normal power - So it gets its own system. +/// That way, we have automatical power inits, dispelled and mindbreaking, as well as checks for if someone can use a power. +/// This is NOT its own power. It can ONLY exist if someone used a mindswap or a minor mass mind swap happened. +/// +public sealed class MindSwappedReturnPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedMindSwapPowerSystem _mindSwap = default!; + [Dependency] private readonly MetaDataSystem _metaDataSystem = default!; + + private EntityQuery _mindSwappedQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnShutDown); + + _mindSwappedQuery = GetEntityQuery(); + } + + private void OnShutDown(Entity psionic, ref ComponentShutdown args) + { + // If the person is gibbed or otherwise deleted, it'll remove the links. + if (Timing.ApplyingState + || !TerminatingOrDeleted(psionic) + || !_mindSwappedQuery.TryComp(psionic.Comp.OriginalEntity, out var targetComp)) + return; + + RemoveLink((psionic.Comp.OriginalEntity, targetComp)); + } + + protected override void OnPowerUsed(Entity psionic, ref MindSwappedReturnPowerActionEvent args) + { + _mindSwap.SwapMinds(psionic, psionic.Comp.OriginalEntity); + AfterPowerUsed(psionic, args.Performer); + } + + protected override void OnDispelled(Entity psionic, ref DispelledEvent args) + { + _mindSwap.SwapMinds(psionic, psionic.Comp.OriginalEntity, false); + } + + public void RemoveLink(Entity victim, bool showPopup = true) + { + // Sometimes people lose their link without having the component - MassMindSwap for example is a situation like that. + if (showPopup) + Popup.PopupEntity(Loc.GetString("psionic-power-mindswap-original-lost"), victim, victim, PopupType.MediumCaution); + + if (!Resolve(victim, ref victim.Comp, false)) + return; + // Remove the first action and link. + Action.RemoveAction(victim.Comp.ActionEntity); + RemCompDeferred(victim, victim.Comp); + + if (!HasComp(victim)) + return; + + RemComp(victim); + RemComp(victim); + EnsureComp(victim); + EnsureComp(victim); + _metaDataSystem.SetEntityName(victim, Loc.GetString("telegnostic-trapped-entity-name")); + _metaDataSystem.SetEntityDescription(victim, Loc.GetString("telegnostic-trapped-entity-desc")); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicInvisibilityPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicInvisibilityPowerSystem.cs new file mode 100644 index 0000000000..4bb8f3864f --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicInvisibilityPowerSystem.cs @@ -0,0 +1,87 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.CombatMode.Pacification; +using Content.Shared.Damage.Systems; +using Content.Shared.Stealth; +using Content.Shared.Stealth.Components; +using Content.Shared.Stunnable; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public sealed class PsionicInvisibilityPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedStealthSystem _stealth = default!; + [Dependency] private readonly SharedStunSystem _stunSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnDamageChanged); + SubscribeLocalEvent(OnDispelled); + } + + protected override void OnPowerUsed(Entity psionic, ref PsionicInvisibilityPowerActionEvent args) + { + ToggleInvisibility(args.Performer); + AfterPowerUsed(psionic, args.Performer); + } + + private void OnInit(Entity invisible, ref MapInitEvent args) + { + EnsureComp(invisible); + EnsureComp(invisible); + var stealth = EnsureComp(invisible); + _stealth.SetVisibility(invisible, invisible.Comp.InvisibilityStrength, stealth); + _audio.PlayPredicted(invisible.Comp.Sound, invisible, invisible); + Psionic.SetCanSeePsionicInvisiblity(invisible, true); + } + + private void OnShutdown(Entity invisible, ref ComponentShutdown args) + { + if (Terminating(invisible)) + return; + + RemComp(invisible); + RemComp(invisible); + RemComp(invisible); + _audio.PlayPredicted(invisible.Comp.Sound, invisible, invisible); + Psionic.SetCanSeePsionicInvisiblity(invisible, false); + + _stunSystem.TryAddParalyzeDuration(invisible, invisible.Comp.StunDuration); + } + + private void OnDamageChanged(Entity invisible, ref DamageChangedEvent args) + { + if (!args.DamageIncreased) + return; + + ToggleInvisibility(invisible); + } + + protected override void OnMindBroken(Entity psionic, ref PsionicMindBrokenEvent args) + { + base.OnMindBroken(psionic, ref args); + // If the mindbreak was successful, remove the invisibility component too. + if (!psionic.Comp.Deleted) + return; + + RemComp(psionic); + } + + private void OnDispelled(Entity invisible, ref DispelledEvent args) + { + ToggleInvisibility(invisible); + } + + public void ToggleInvisibility(EntityUid invisible) + { + if (!RemComp(invisible)) + EnsureComp(invisible); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicRegenerationPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicRegenerationPowerSystem.cs new file mode 100644 index 0000000000..43cd31ccf9 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsionicRegenerationPowerSystem.cs @@ -0,0 +1,69 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Chemistry.Components; +using Content.Shared.DoAfter; +using Content.Shared.FixedPoint; +using Content.Shared.Popups; +using Content.Shared.Psionics.Events; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public sealed class PsionicRegenerationPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedAudioSystem _audioSystem = default!; + [Dependency] private readonly SharedBloodstreamSystem _bloodstreamSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDoAfter); + } + + protected override void OnPowerUsed(Entity psionic, ref PsionicRegenerationPowerActionEvent args) + { + var ev = new PsionicRegenerationDoAfterEvent(Timing.CurTime); + var performer = args.Performer; + var doAfterArgs = new DoAfterArgs(EntityManager, performer, psionic.Comp.UseDelay, ev, psionic); + + if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + psionic.Comp.SaveDoAfterId(doAfterId.Value); + + Popup.PopupPredicted(Loc.GetString("psionic-regeneration-begin", ("entity", performer)), + performer, + performer, + PopupType.Medium); + + _audioSystem.PlayPredicted(psionic.Comp.SoundUse, args.Performer, args.Performer, AudioParams.Default.WithVolume(8f).WithMaxDistance(1.5f).WithRolloffFactor(3.5f)); + AfterPowerUsed(psionic, args.Performer); + } + + private void OnDoAfter(Entity psionic, ref PsionicRegenerationDoAfterEvent args) + { + if (args.Handled) + return; + + args.Handled = true; + psionic.Comp.RemoveSavedDoAfterId(); + Dirty(psionic); + + if (!TryComp(psionic, out var stream)) + return; + + // DoAfter has no way to run a callback during the process to give + // small doses of the reagent, so we wait until either the action + // is cancelled (by being dispelled) or complete to give the + // appropriate dose. A timestamp delta is used to accomplish this. + var percentageComplete = Math.Min(1f, (Timing.CurTime - args.StartedAt).TotalSeconds / psionic.Comp.UseDelay); + + var solution = new Solution(); + solution.AddReagent(psionic.Comp.ReagentId, FixedPoint2.New(psionic.Comp.EssenceAmount * percentageComplete)); + _bloodstreamSystem.TryAddToBloodstream((psionic, stream), solution); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsychokineticScreamPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsychokineticScreamPowerSystem.cs new file mode 100644 index 0000000000..08c43e2456 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/PsychokineticScreamPowerSystem.cs @@ -0,0 +1,75 @@ +using System.Linq; +using System.Numerics; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Coordinates; +using Content.Shared.Light.Components; +using Content.Shared.Light.EntitySystems; +using Content.Shared.Physics; +using JetBrains.Annotations; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Physics; +using Robust.Shared.Physics.Systems; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This system enables a psionic being to break lights around them. +/// +public sealed class PsychokineticScreamPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly SharedPoweredLightSystem _light = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + + protected override void OnPowerUsed(Entity psionic, ref PsychokineticScreamPowerActionEvent args) + { + if (psionic.Comp.AbilitySound != null) + _audio.PlayPredicted(psionic.Comp.AbilitySound, psionic, psionic); + + ShatterLightsAround(psionic.Owner, psionic.Comp.Radius, psionic.Comp.LineOfSight, psionic.Comp.PenetratingRadius); + + SpawnAttachedTo(psionic.Comp.Effect, psionic.Owner.ToCoordinates()); + args.Handled = true; + } + + /// + /// Shatter lights around an entity. + /// This is public because other systems require it. + /// + /// The entity that caused it. + /// The range where the lights are broken within. + /// Whether line of sight is required. + /// How far it ignores the line of sight. + [PublicAPI] + public void ShatterLightsAround(EntityUid source, float range, bool lineOfSight, float penetratingRadius = 0f) + { + var pos = _transform.GetWorldPosition(source); + + // Get all light entities within the specified radius + HashSet> lightsInRange = []; + _lookup.GetEntitiesInRange(Transform(source).Coordinates, range, lightsInRange); + + foreach (var light in lightsInRange) + { + if (lineOfSight) // If LoS is required, test it. + { + var lightPos = _transform.GetWorldPosition(light); + var sqrDistance = Vector2.DistanceSquared(pos, lightPos); + if (sqrDistance > penetratingRadius * penetratingRadius) + { + // If the light is outside the penetrating radius, do a LoS check. + var ray = new CollisionRay(pos, (lightPos - pos).Normalized(), (int)CollisionGroup.Opaque); + var hit = _physics.IntersectRay(_transform.GetMapId(source), ray, MathF.Sqrt(sqrDistance) - 0.5f, returnOnFirstHit: true); + if (hit.Any() && hit.First().Distance != 0) + continue; + } + } + + // If we reach here, the light is unobstructed and within range, break it. + _light.TryDestroyBulb(light, light.Comp, source); + } + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedDispelPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedDispelPowerSystem.cs new file mode 100644 index 0000000000..27ea004660 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedDispelPowerSystem.cs @@ -0,0 +1,139 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Damage; +using Content.Shared.Damage.Systems; +using Content.Shared.Popups; +using Content.Shared.Revenant.Components; +using Content.Shared.StatusEffect; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This enables psionic users to dispel noospheric beings and actions. +/// +public abstract class SharedDispelPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly DamageableSystem _damageable = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + [Dependency] private readonly StatusEffectNew.StatusEffectsSystem _statusEffectsNew = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDispelled); + SubscribeLocalEvent(OnDmgDispelled); + SubscribeLocalEvent(OnPsionicShieldingStopped, null,[typeof(SharedPsionicSystem)]); + // Upstream stuff we're just gonna handle here + SubscribeLocalEvent(OnRevenantDispelled); + } + + protected override void OnPowerInit(Entity power, ref MapInitEvent args) + { + base.OnPowerInit(power, ref args); + // Dispell psionics can now see invisible entities to dispell them. + Psionic.SetCanSeePsionicInvisiblity(power.Owner, true); + } + + protected override void OnPowerUsed(Entity psionic, ref DispelPowerActionEvent args) + { + if (!Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer)) + return; + + var ev = new DispelledEvent(args.Performer, args.Target); + RaiseLocalEvent(args.Target, ev); + + if (ev.Handled) + AfterPowerUsed(psionic, args.Performer); + } + + protected override void OnPsionicallySuppressed(Entity power, ref PsionicSuppressedEvent args) + { + if (Timing.ApplyingState) + return; + // Don't let them see if they're suppressed. + Psionic.SetCanSeePsionicInvisiblity(args.Victim, false); + } + + protected override void OnStoppedPsionicallySuppressed(Entity psionic, ref PsionicStoppedSuppressedEvent args) + { + if (Timing.ApplyingState) + return; + // Let mah people SEE + Psionic.SetCanSeePsionicInvisiblity(args.Victim, true); + } + + /// + /// This is necessary to avoid dispel users to lose their invisibility sight. + /// This fires after the System where losing psionic shielding makes you unable to see invisible entities. + /// + /// The psionic who lost their shielding. + /// The event. + private void OnPsionicShieldingStopped(Entity psionic, ref PsionicStoppedShieldedEvent args) + { + if (_statusEffectsNew.HasStatusEffect(psionic, "StatusEffectPsionicsDisabled")) + return; + // Losing the shielding causes you to lose vision of the invisible. This is to prevent that. + Psionic.SetCanSeePsionicInvisiblity(args.Shielded, true); + } + + protected override void OnMindBroken(Entity psionic, ref PsionicMindBrokenEvent args) + { + base.OnMindBroken(psionic, ref args); + // If the mindbreak was successful, make 'em blind. + if (!psionic.Comp.Deleted) + return; + + Psionic.SetCanSeePsionicInvisiblity(psionic, false); + } + + private void OnDispelled(Entity dispellable, ref DispelledEvent args) + { + QueueDel(dispellable); + Spawn("Ash", Transform(dispellable).Coordinates); + Popup.PopupPredicted(Loc.GetString("psionic-burns-up", ("item", dispellable)), dispellable, args.Dispeller, PopupType.MediumCaution); + _audio.PlayPredicted(dispellable.Comp.DispelSound, dispellable.Owner, args.Dispeller); + args.Handled = true; + } + + private void OnDmgDispelled(Entity damaged, ref DispelledEvent args) + { + var damage = damaged.Comp.Damage; + var modifier = Random.NextFloat(damaged.Comp.Variance, 1 + damaged.Comp.Variance); + + damage *= modifier; + DealDispelDamage(damaged, damage, args.Dispeller, damaged.Comp.DispelSound); + args.Handled = true; + } + + private void OnRevenantDispelled(Entity revenant, ref DispelledEvent args) + { + DealDispelDamage(revenant, dispeller: args.Dispeller); + // TODO: Port over the new StatusEffectSystem when upstream ports over the Corporeal status effect to the new system. + _statusEffects.TryAddStatusEffect(revenant, "Corporeal", TimeSpan.FromSeconds(30), false, "Corporeal"); + args.Handled = true; + } + + public void DealDispelDamage(EntityUid dispelled, DamageSpecifier? damage = null, EntityUid? dispeller = null, SoundSpecifier? sound = null) + { + if (Deleted(dispelled)) + return; + + Popup.PopupPredicted(Loc.GetString("psionic-burn-resist", ("item", dispelled)), dispelled, dispeller, PopupType.SmallCaution); + _audio.PlayPredicted(sound, dispelled, dispeller); + + if (damage == null) + { + damage = new DamageSpecifier(); + damage.DamageDict.Add("Blunt", 100); + } + + _damageable.TryChangeDamage(dispelled, damage, ignoreResistances: true); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedFracturedFormPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedFracturedFormPowerSystem.cs new file mode 100644 index 0000000000..4ddf11ed79 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedFracturedFormPowerSystem.cs @@ -0,0 +1,150 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; +using Content.Shared.Bed.Sleep; +using Content.Shared.Chat; +using Content.Shared.DoAfter; +using Content.Shared.Examine; +using Content.Shared.Mind.Components; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Popups; +using Content.Shared.SSDIndicator; +using Robust.Shared.Player; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public abstract class SharedFracturedFormPowerSystem : BasePsionicPowerSystem +{ + [Dependency] protected readonly SharedChatSystem Chat = default!; + [Dependency] protected readonly MobStateSystem MobState = default!; + [Dependency] protected readonly SleepingSystem Sleeping = default!; + + protected EntityQuery FracturedQuery; + private EntityQuery _forcedSleepQuery; + protected EntityQuery MindContainerQuery; + private EntityQuery _mobStateQuery; + protected EntityQuery SsdQuery; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnDoAfter); + SubscribeLocalEvent(OnPlayerDetached, after: [typeof(SSDIndicatorSystem)]); + SubscribeLocalEvent(OnExamine); + + FracturedQuery = GetEntityQuery(); + _forcedSleepQuery = GetEntityQuery(); + MindContainerQuery = GetEntityQuery(); + _mobStateQuery = GetEntityQuery(); + SsdQuery = GetEntityQuery(); + } + + protected override void OnPowerUsed(Entity psionic, ref FracturedFormPowerActionEvent args) + { + if (!CanSwap(psionic)) + { + Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-nobodies"), psionic, args.Performer, PopupType.Large); + return; + } + + Chat.TryEmoteWithChat(psionic.Owner, "Yawn"); + Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-sleepy"), psionic, args.Performer, PopupType.LargeCaution); + + var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, psionic.Comp.ManualSwapTime, new FracturedFormDoAfterEvent(), psionic); + + if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + psionic.Comp.SaveDoAfterId(doAfterId.Value); + Dirty(psionic); + AfterPowerUsed(psionic, args.Performer); + } + + private void OnDoAfter(Entity psionic, ref FracturedFormDoAfterEvent args) + { + if (args.Cancelled || args.Handled) + return; + args.Handled = true; + psionic.Comp.RemoveSavedDoAfterId(); + + Dirty(psionic); + Sleeping.TrySleeping(psionic.Owner); + } + + private void OnPlayerDetached(Entity body, ref PlayerDetachedEvent args) + { + if (!SsdQuery.TryComp(body, out var comp) + || FracturedQuery.HasComp(body)) + return; + + comp.IsSSD = false; + } + + private void OnExamine(Entity psionic, ref ExaminedEvent args) + { + if (HasComp(psionic)) + return; + + if (TryComp(args.Examiner, out var fracturedHost) && fracturedHost.Bodies.Contains(psionic.Owner)) + args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-examine-self", ("ent", psionic))}[/color]"); + else + args.PushMarkup($"[color=yellow]{Loc.GetString("psionic-power-fractured-form-ssd", ("ent", psionic))}[/color]"); + } + + /// + /// We're overriding the dispelled method to introduce some special behavior. + /// + protected override void OnDispelled(Entity psionic, ref DispelledEvent args) + { + if (args.Handled) + return; + args.Handled = true; + + // This resets the swap timer. + var randomTime = Random.Next(psionic.Comp.NextSwapMinTime, psionic.Comp.NextSwapMaxTime); + psionic.Comp.NextSwap = Timing.CurTime + randomTime; + + if (psionic.Comp.GetDoAfterId() is not { } doAfterId) + { + Popup.PopupClient(Loc.GetString("psionic-power-fractured-form-dispelled"), psionic, PopupType.MediumCaution); + return; + } + + DoAfter.Cancel(doAfterId); + Popup.PopupClient(Loc.GetString("psionic-dispelled"), psionic, PopupType.MediumCaution); + psionic.Comp.RemoveSavedDoAfterId(); + } + + protected bool IsValidBody(Entity psionic, EntityUid otherBody) + { + if (otherBody == psionic.Owner) + return false; + if (!psionic.Comp.Bodies.Contains(otherBody)) + return false; + if (!MindContainerQuery.TryComp(otherBody, out var cmind)) + return false; + if (cmind.HasMind) + return false; + if (_forcedSleepQuery.HasComp(otherBody)) + return false; + if (!_mobStateQuery.TryComp(otherBody, out var mobState)) + return false; + if (MobState.IsIncapacitated(otherBody, mobState)) + return false; + + return true; + } + + public bool CanSwap(Entity psionic) + { + foreach (var body in psionic.Comp.Bodies) + { + if (IsValidBody(psionic, body)) + return true; + } + return false; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMassSleepPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMassSleepPowerSystem.cs new file mode 100644 index 0000000000..d30151ebc4 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMassSleepPowerSystem.cs @@ -0,0 +1,6 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public abstract class SharedMassSleepPowerSystem : BasePsionicPowerSystem; diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMindSwapPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMindSwapPowerSystem.cs new file mode 100644 index 0000000000..bfd9116326 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedMindSwapPowerSystem.cs @@ -0,0 +1,167 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Mind; +using Content.Shared.Mindshield.Components; +using Content.Shared.Popups; +using JetBrains.Annotations; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public abstract class SharedMindSwapPowerSystem : BasePsionicPowerSystem +{ + [Dependency] private readonly SharedMindSystem _mindSystem = default!; + [Dependency] private readonly MindSwappedReturnPowerSystem _mindSwapped = default!; + + private EntityQuery _mindSwappedQuery; + private EntityQuery _mindshieldQuery; + + public override void Initialize() + { + base.Initialize(); + + _mindSwappedQuery = GetEntityQuery(); + _mindshieldQuery = GetEntityQuery(); + } + + protected override void OnPowerUsed(Entity psionic, ref MindSwapPowerActionEvent args) + { + SwapMinds(args.Performer, args.Target); + AfterPowerUsed(psionic, args.Performer); + } + + // TODO: Fix the prediction issue when calling from server while the performer is the cause. + // PopupClient() only shows up on client - But Telegnosis can't be put into shared. + // So Telegnosis calls from server side - Leaving no PopupClient(). + // Using PopupEntity() will cause it to be called 12x times client-side. + /// + /// Checks whether the two entities can swap their minds. + /// This handles whether the performer caused the mindswap, and whether they have mindshields. + /// This also handles popups. + /// + /// The entity performing or causing the swap. + /// The entity being targeted. + /// Whether the check should ignore mindshields. + /// Whether the check should ignore psionic shielding. + /// Whether the performer actively caused or performed the swap. + /// Returns true if possible, false if not. + public bool CanSwap(EntityUid performer, EntityUid target, bool ignoreMindshields = false, bool ignorePsionicShielding = false, bool performerIsCause = true) + { + EntityUid? aggressor = performerIsCause ? performer : null; + + if (performerIsCause && !ignorePsionicShielding && !Psionic.CanUsePsionicAbility(performer)) + { + // client-side prediction. + Popup.PopupClient(Loc.GetString("psionic-cannot-use-psionics"), performer, performer); + return false; + } + // If the performer isn't the cause, like mindswap events, they can be shielded. + if (!performerIsCause && !ignorePsionicShielding && !Psionic.CanBeTargeted(performer)) + { + // Popup is handled in CanBeTargeted(). + return false; + } + // Mindshields actually shielding the mind?!?! Unplayable. + if (_mindshieldQuery.HasComp(performer) && !ignoreMindshields) + { + // Different messages whether they're the cause or not. + if (!performerIsCause) + { + Popup.PopupEntity(Loc.GetString("psionic-power-mindswap-target-mindshielded"), performer, performer, PopupType.MediumCaution); + return false; + } + Popup.PopupClient(Loc.GetString("psionic-power-mindswap-own-mindshield"), performer, performer, PopupType.SmallCaution); + return false; + } + if (_mindshieldQuery.HasComponent(target) && !ignoreMindshields) + { + // Performer should only be notified if they're the cause of the attempt. + if (performerIsCause) + Popup.PopupClient(Loc.GetString("psionic-cannot-target-shielded"), performer, performer, PopupType.SmallCaution); + Popup.PopupEntity(Loc.GetString("psionic-power-mindswap-target-mindshielded"), target, target, PopupType.MediumCaution); + return false; + } + if (!Psionic.CanBeTargeted(target, hasAggressor: aggressor) && !ignorePsionicShielding) + { + // Popup is handled in CanBeTargeted(). + return false; + } + + return true; + } + + /// + /// Swaps two minds. + /// + /// The entity performing or causing the swap/being targeted. + /// The entity being targeted. + /// Whether the performer is actively causing the swap. + /// Whether the swap is reversible via the return power. + /// Whether the swap should ignore mindshields. + /// Whether the swap should ignore psionic shielding. + /// True if the two were swapped, false if otherwise. + [PublicAPI] + public bool SwapMinds(EntityUid performer, EntityUid target, bool performerIsCause = true, bool reversible = true, bool ignoreMindshields = false, bool ignorePsionicShielding = false) + { + if (!CanSwap(performer, target, ignoreMindshields, ignorePsionicShielding, performerIsCause)) + return false; + + // Get the minds first. On transfer, they'll be gone. + // This is here to prevent missing MindContainerComponent Resolve errors. + if (!_mindSystem.TryGetMind(performer, out var performerMindId, out var performerMind)) + performerMind = null; + + if (!_mindSystem.TryGetMind(target, out var targetMindId, out var targetMind)) + targetMind = null; + + switch (performerMind) + { + // If no mind can be swapped, return. + case null when targetMind == null: + return false; + // If performer has no mind, but target does, switch places. + case null: + (performer, target) = (target, performer); + (performerMind, targetMind) = (targetMind, performerMind); + (performerMindId, targetMindId) = (targetMindId, performerMindId); + break; + } + + //This is a terrible way to 'unattach' minds. I wanted to use UnVisit but in TransferTo's code they say + //To unnatch the minds, do it like this. + //Have to unnattach the minds before we reattach them via transfer. Still feels weird, but seems to work well. + _mindSystem.TransferTo(performerMindId, null); + // Do the transfer. + if (targetMind != null) + _mindSystem.TransferTo(targetMindId, performer, ghostCheckOverride: true, false, targetMind); + + _mindSystem.TransferTo(performerMindId, target, ghostCheckOverride: true, false, performerMind); + + if (_mindSwappedQuery.TryComp(performer, out var performerSwapped) && _mindSwappedQuery.TryComp(target, out var targetSwapped)) + { + // Sanity check + if (performerSwapped.OriginalEntity == target && targetSwapped.OriginalEntity == performer) + { + _mindSwapped.RemoveLink((performer, performerSwapped), false); + _mindSwapped.RemoveLink((target, targetSwapped), false); + return true; + } + } + + if (!reversible) + { + _mindSwapped.RemoveLink(performer); + _mindSwapped.RemoveLink(target); + return true; + } + + var perfComp = EnsureComp(performer); + var targetComp = EnsureComp(target); + + perfComp.OriginalEntity = target; + targetComp.OriginalEntity = performer; + + Dirty(performer, perfComp); + Dirty(target, targetComp); + return true; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedNoosphericZapPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedNoosphericZapPowerSystem.cs new file mode 100644 index 0000000000..d8a1c6f990 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedNoosphericZapPowerSystem.cs @@ -0,0 +1,9 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This is solely for prediction. +/// +public abstract class SharedNoosphericZapPowerSystem : BasePsionicPowerSystem; diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPrecognitionPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPrecognitionPowerSystem.cs new file mode 100644 index 0000000000..c00deaf0f7 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPrecognitionPowerSystem.cs @@ -0,0 +1,51 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerDoAfterEvents; +using Content.Shared.Actions.Events; +using Content.Shared.DoAfter; +using Content.Shared.Eye.Blinding.Components; +using Content.Shared.Movement.Systems; +using Content.Shared.StatusEffect; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Player; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +/// +/// This solely exist for client-side action handling prediction. +/// +public abstract class SharedPrecognitionPowerSystem : BasePsionicPowerSystem +{ + [Dependency] protected readonly SharedAudioSystem Audio = default!; + [Dependency] protected readonly StatusEffectsSystem StatusEffects = default!; + [Dependency] protected readonly MovementModStatusSystem Movement = default!; + + public static readonly EntProtoId PrecognitionSlowdown = "PrecognitionSlowdownStatusEffect"; + + protected override void OnPowerUsed(Entity psionic, ref PrecognitionPowerActionEvent args) + { + var ev = new PrecognitionDoAfterEvent(); + var doAfterArgs = new DoAfterArgs(EntityManager, args.Performer, psionic.Comp.UseDelay, ev, psionic) + { + BreakOnDamage = true, + }; + + if (!DoAfter.TryStartDoAfter(doAfterArgs, out var doAfterId)) + return; + + // A custom shader for seeing visions would be nice but this will do for now. + // TODO: Port over the TemporaryBlindness effect to the new StatusEffectSystem. + // When Upstream ports it over, replace this with it. + StatusEffects.TryAddStatusEffect(psionic, "TemporaryBlindness", psionic.Comp.UseDelay, true); + Movement.TryUpdateMovementSpeedModDuration(args.Performer, PrecognitionSlowdown, psionic.Comp.UseDelay, 0.5f); + + psionic.Comp.SaveDoAfterId(doAfterId.Value); + + var player = Audio.PlayGlobal(psionic.Comp.VisionSound, Filter.Entities(args.Performer), true); + if (player != null) + psionic.Comp.SoundStream = player.Value.Entity; + + Dirty(psionic); + AfterPowerUsed(psionic, args.Performer); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPyrokinesisPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPyrokinesisPowerSystem.cs new file mode 100644 index 0000000000..dce385c570 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedPyrokinesisPowerSystem.cs @@ -0,0 +1,6 @@ +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public abstract class SharedPyrokinesisPowerSystem : BasePsionicPowerSystem; diff --git a/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedTelegnosisPowerSystem.cs b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedTelegnosisPowerSystem.cs new file mode 100644 index 0000000000..86a51373ce --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/PsionicPowers/SharedTelegnosisPowerSystem.cs @@ -0,0 +1,59 @@ +using Content.Shared._DV.Mind; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Components.PsionicPowers; +using Content.Shared._DV.Psionics.Events.PowerActionEvents; +using Content.Shared.Examine; +using Content.Shared.Interaction.Events; +using Content.Shared.Mind.Components; + +namespace Content.Shared._DV.Psionics.Systems.PsionicPowers; + +public abstract class SharedTelegnosisPowerSystem : BasePsionicPowerSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnMindRemoved); + SubscribeLocalEvent(OnInteractionAttempt); + SubscribeLocalEvent(OnShowSSDIndicator); + SubscribeLocalEvent(OnExamine); + } + + private void OnMindRemoved(Entity projection, ref MindRemovedMessage args) + { + QueueDel(projection); + } + + private void OnInteractionAttempt(Entity projection, ref InteractionAttemptEvent args) + { + // no astrally stealing someone's shoes + args.Cancelled = true; + } + + private void OnShowSSDIndicator(Entity psionic, ref ShowSSDIndicatorEvent args) + { + if (!TryComp(psionic, out var mindSwapped) || !HasComp(mindSwapped.OriginalEntity)) + return; + // Only hide if currently projecting + args.Hidden = true; + } + + private void OnExamine(Entity entity, ref ExaminedEvent args) + { + if (GetCasterProjection(entity) == default) + return; + + args.PushMarkup($"[color=yellow]{Loc.GetString("telegnosis-power-ssd", ("ent", entity))}[/color]"); + } + + public EntityUid GetCasterProjection(Entity entity) + { + if (!TryComp(entity, out var mindSwapped) || + !HasComp(mindSwapped.OriginalEntity)) + { + return default; + } + return mindSwapped.OriginalEntity; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/SharedPsionicSystem.cs b/Content.Shared/_DV/Psionics/Systems/SharedPsionicSystem.cs new file mode 100644 index 0000000000..65e2469c66 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SharedPsionicSystem.cs @@ -0,0 +1,130 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.Jittering; +using Content.Shared.Popups; +using Content.Shared.Psionics.Glimmer; +using Content.Shared.Speech.EntitySystems; +using Content.Shared.Stunnable; +using JetBrains.Annotations; +using Robust.Shared.Random; + +namespace Content.Shared._DV.Psionics.Systems; + +/// +/// The system to deal with all psionics. Each part of the System is in a subsystem. +/// +public abstract partial class SharedPsionicSystem : EntitySystem +{ + [Dependency] protected readonly IRobustRandom Random = default!; + [Dependency] protected readonly GlimmerSystem Glimmer = default!; + [Dependency] private readonly SharedJitteringSystem _jittering = default!; + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + [Dependency] private readonly SharedStutteringSystem _stuttering = default!; + + protected EntityQuery PotentialQuery; + protected EntityQuery PsionicQuery; + + public override void Initialize() + { + base.Initialize(); + + PotentialQuery = GetEntityQuery(); + PsionicQuery = GetEntityQuery(); + + InitializeDetection(); + InitializeStatusEffects(); + InitializeInvisibility(); + InitializeItems(); + } + + /// + /// Call this when you want to mindbreak an entity, causing them to lose all psionic abilities. + /// + /// The entity to be mindbroken. + /// Whether this should stun the entity. + /// Whether this should remove unremovable psionics too. + /// Zombies shouldn't be stunned on zombification, but also lose ALL psionic abilities.> + [PublicAPI] + public void MindBreakEntity(Entity psionic, bool stun = true, bool force = false) + { + if (!Resolve(psionic, ref psionic.Comp)) + return; + + var ev = new PsionicMindBrokenEvent(force); + RaiseLocalEvent(psionic, ref ev); + + // If no abilities got removed, refrain from doing anything further. Nothing ever happens. + if (!ev.Success) + return; + // Reduce glimmer a bit. + Glimmer.Glimmer -= Random.Next(50, 70); + // Stun if stun is desired. Zombies shouldn't get stunned on zombification. + if (stun && psionic.Comp.StunOnRemoval) + { + _stuttering.DoStutter(psionic, TimeSpan.FromMinutes(1), false); + _stun.TryKnockdown(psionic.Owner, TimeSpan.FromSeconds(5), false, drop: false); + _jittering.DoJitter(psionic, TimeSpan.FromSeconds(5), false); + } + // If all were removed, remove the psionic component. + if (!ev.AllRemoved) + { + Popup.PopupClient(Loc.GetString("psionic-partly-mindbrokenpsionic-partly-mindbroken"), psionic, PopupType.Medium); + return; + } + + RemComp(psionic); + GrantPsionicRoll(psionic.Owner); + + Popup.PopupClient(Loc.GetString("psionic-mindbroken"), psionic, PopupType.Medium); + } + + // TODO: Fix prediction issues. When calling from a server side, there is no popups. + // Introducing server-side popups will cause them to be spammed client-side. + // PopupPredicted() cannot be used or it'll not show any popups clientside when called serverside. + /// + /// Whether the target can be targeted by psionic influence. + /// + /// The target of the psionic ability. + /// Whether the target needs to be a potential psionic to be eligible. + /// Whether it should show popups. + /// Whether the psionic influence originates from an entity. + /// Returns true if targetable, false otherwise. Will handle popups itself. + [PublicAPI] + public bool CanBeTargeted(EntityUid target, bool ignorePsionicRequirement = false, bool showPopup = true, EntityUid? hasAggressor = null) + { + // Normal abilities cannot target Borgs and Simplemobs that aren't psionic. + if (!PotentialQuery.HasComp(target) && !PsionicQuery.HasComp(target) && !ignorePsionicRequirement) + return false; + + var ev = new TargetedByPsionicPowerEvent(); + RaiseLocalEvent(target, ref ev); + + if (!showPopup || !ev.IsShielded) + return !ev.IsShielded; + + Popup.PopupEntity(Loc.GetString("psionic-shielded-from-attempt"), target, target, PopupType.MediumCaution); + + if (hasAggressor is { } aggressor) + { + var message = Loc.GetString("psionic-cannot-target-shielded"); + Popup.PopupClient(message, aggressor, aggressor, PopupType.SmallCaution); + } + + return !ev.IsShielded; + } + + /// + /// Whether the entity can use psionic abilities. + /// + /// The entity attempting to use psionic abilities. + /// Returns true if yes, false otherwise. + public bool CanUsePsionicAbility(EntityUid psionic) + { + var ev = new PsionicPowerUseAttemptEvent(); + RaiseLocalEvent(psionic, ref ev); + + return ev.CanUsePower; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Detection.cs b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Detection.cs new file mode 100644 index 0000000000..c36a0689f3 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Detection.cs @@ -0,0 +1,54 @@ +using System.Linq; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.Clothing; +using Content.Shared.Popups; + +namespace Content.Shared._DV.Psionics.Systems; + +public abstract partial class SharedPsionicSystem +{ + private void InitializeDetection() + { + SubscribeLocalEvent(OnPowerUsed); + + SubscribeLocalEvent(OnGrantingClothingEquipped); + SubscribeLocalEvent(OnGrantingClothingUnequipped); + } + + private void OnGrantingClothingEquipped(Entity detector, ref ClothingGotEquippedEvent args) + { + if (!HasComp(args.Wearer)) // IPCs and non-player organics shouldn't be able to use abilities. + return; + + detector.Comp.Wearer = args.Wearer; + Dirty(detector); + } + + private void OnGrantingClothingUnequipped(Entity detector, ref ClothingGotUnequippedEvent args) + { + detector.Comp.Wearer = null; + Dirty(detector); + } + + private void OnPowerUsed(Entity psionic, ref PsionicPowerUsedEvent args) + { + var coords = Transform(args.User).Coordinates; + + foreach (var detector in _lookup.GetEntitiesInRange(coords, 10f).Select(detectorPower => detectorPower.Comp.Wearer ?? detectorPower.Owner)) + { + if (detector == args.User) + continue; + + if (!CanUsePsionicAbility(detector)) + continue; + // This is for artifacts. + var detectEv = new PsionicPowerDetectedEvent(args.User, args.Power); + RaiseLocalEvent(detector, ref detectEv); + + Popup.PopupEntity(Loc.GetString("psionic-power-metapsionic-power-detected", ("power", args.Power)), detector, detector, PopupType.LargeCaution); + } + + args.Handled = true; + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Effects.cs b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Effects.cs new file mode 100644 index 0000000000..dd0aae8f33 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Effects.cs @@ -0,0 +1,65 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.StatusEffectNew; +using Robust.Shared.Prototypes; + +namespace Content.Shared._DV.Psionics.Systems; + +public abstract partial class SharedPsionicSystem +{ + public static readonly EntProtoId PsionicsDisabledProtoId = "StatusEffectPsionicsDisabled"; + public void InitializeStatusEffects() + { + SubscribeLocalEvent>(OnPowerUseAttempt); + SubscribeLocalEvent>(OnTargetedByPsionicPower); + + SubscribeLocalEvent(OnStatusEffectApplied); + SubscribeLocalEvent(OnStatusEffectRemoved); + SubscribeLocalEvent(OnStatusEffectApplied); + SubscribeLocalEvent(OnStatusEffectRemoved); + } + + private void OnPowerUseAttempt(Entity psionic, ref StatusEffectRelayedEvent args) + { + var ev = args.Args; + ev.CanUsePower = false; + args.Args = ev; + } + + private void OnTargetedByPsionicPower(Entity psionic, ref StatusEffectRelayedEvent args) + { + var ev = args.Args; + ev.IsShielded = true; + args.Args = ev; + } + + private void OnStatusEffectApplied(Entity statusEffect, ref StatusEffectAppliedEvent args) + { + var ev = new PsionicSuppressedEvent(args.Target); + RaiseLocalEvent(args.Target, ref ev); + } + + private void OnStatusEffectRemoved(Entity statusEffect, ref StatusEffectRemovedEvent args) + { + if (!CanUsePsionicAbility(args.Target)) + return; + + var ev = new PsionicStoppedSuppressedEvent(args.Target); + RaiseLocalEvent(args.Target, ref ev); + } + + private void OnStatusEffectApplied(Entity statusEffect, ref StatusEffectAppliedEvent args) + { + var ev = new PsionicShieldedEvent(args.Target); + RaiseLocalEvent(args.Target, ref ev); + } + + private void OnStatusEffectRemoved(Entity statusEffect, ref StatusEffectRemovedEvent args) + { + if (!CanBeTargeted(args.Target, showPopup: false)) + return; + + var ev = new PsionicStoppedShieldedEvent(args.Target); + RaiseLocalEvent(args.Target, ref ev); + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Invisibility.cs b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Invisibility.cs new file mode 100644 index 0000000000..6854665a5a --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Invisibility.cs @@ -0,0 +1,103 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared.Eye; + +namespace Content.Shared._DV.Psionics.Systems; + +public abstract partial class SharedPsionicSystem +{ + [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] private readonly SharedVisibilitySystem _visibility = default!; + + private void InitializeInvisibility() + { + SubscribeLocalEvent(OnInvisInit); + SubscribeLocalEvent(OnInvisShutdown); + + SubscribeLocalEvent(OnInit); + + SubscribeLocalEvent(OnSuppression); + SubscribeLocalEvent(OnSuppressionStop); + SubscribeLocalEvent(OnShielded); + SubscribeLocalEvent(OnShieldedStop); + } + + private void OnInvisInit(Entity invisible, ref MapInitEvent args) + { + if (!CanUsePsionicAbility(invisible)) + invisible.Comp.Active = false; + + SetPsionicInvisibility(invisible.Owner, invisible.Comp.Active); + } + + private void OnInvisShutdown(Entity invisible, ref ComponentShutdown args) + { + SetPsionicInvisibility(invisible.Owner, false); + } + + private void OnInit(Entity potPsionic, ref MapInitEvent args) + { + SetCanSeePsionicInvisiblity(potPsionic, false); + } + + private void OnSuppression(Entity invisible, ref PsionicSuppressedEvent args) + { + invisible.Comp.Active = false; + SetPsionicInvisibility(invisible.Owner, invisible.Comp.Active); + } + + private void OnSuppressionStop(Entity invisible, ref PsionicStoppedSuppressedEvent args) + { + // This event only raises when they can use psionic abilities again, so no need for a check. + invisible.Comp.Active = true; + SetPsionicInvisibility(invisible.Owner, invisible.Comp.Active); + } + + private void OnShielded(Entity potPsionic, ref PsionicShieldedEvent args) + { + SetCanSeePsionicInvisiblity(potPsionic, true); + } + + private void OnShieldedStop(Entity potPsionic, ref PsionicStoppedShieldedEvent args) + { + //This only gets raised when they are no longer shielded, so no need for a check. + SetCanSeePsionicInvisiblity(potPsionic, false); + } + + /// + /// Enables or disables an entity to see a psionically invisible entity. + /// + /// The entity whose ability to see psionically invisible entities is being changed. + /// Whether they can see psionically invisible entities. + public void SetCanSeePsionicInvisiblity(EntityUid potPsionic, bool canSee) + { + if (!TryComp(potPsionic, out var eye)) + return; + + if (canSee) + _eye.SetVisibilityMask(potPsionic, eye.VisibilityMask | (int) VisibilityFlags.PsionicInvisibility, eye); + else + _eye.SetVisibilityMask(potPsionic, eye.VisibilityMask & ~ (int) VisibilityFlags.PsionicInvisibility, eye); + } + + /// + /// Set their psionic invisibility. + /// + /// The entity that attempts to toggle their psionic invisibility. + /// Whether they're visible or invisible. + public void SetPsionicInvisibility(Entity visible, bool invisible) + { + if (invisible) + { + // Remove them from the normal layer and add them to the psionic layer. + _visibility.AddLayer(visible, (int) VisibilityFlags.PsionicInvisibility, false); + _visibility.RemoveLayer(visible, (int) VisibilityFlags.Normal); + } + else + { + // Remove them from the psionic layer and add them to the normal layer. + _visibility.RemoveLayer(visible, (int) VisibilityFlags.PsionicInvisibility, false); + _visibility.AddLayer(visible, (int) VisibilityFlags.Normal); + } + } +} diff --git a/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Items.cs b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Items.cs new file mode 100644 index 0000000000..b736a88731 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Items.cs @@ -0,0 +1,124 @@ +using System.Linq; +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.Psionics.Systems.PsionicPowers; +using Content.Shared.Damage.Events; +using Content.Shared.Inventory; +using Content.Shared.Inventory.Events; +using Content.Shared.StatusEffectNew; +using Content.Shared.Weapons.Melee.Events; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Shared._DV.Psionics.Systems; + +public abstract partial class SharedPsionicSystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; + [Dependency] private readonly SharedMindSwapPowerSystem _mindSwapPowerSystem = default!; + [Dependency] private readonly StatusEffectsSystem _statusEffects = default!; + + private void InitializeItems() + { + SubscribeLocalEvent(OnInsulativeGearEquipped); + SubscribeLocalEvent(OnInsulativeGearUnequipped); + + + SubscribeLocalEvent>(OnPowerUseAttempt); + SubscribeLocalEvent>(OnTargetedByPsionicPower); + + SubscribeLocalEvent(OnAntiPsionicMeleeHit); + SubscribeLocalEvent(OnAntiPsionicStamHit); + } + + private void OnInsulativeGearEquipped(Entity gear, ref GotEquippedEvent args) + { + if (_timing.ApplyingState) + return; + + if (!gear.Comp.AllowsPsionicUsage) + { + var ev = new PsionicSuppressedEvent(args.Equipee); + RaiseLocalEvent(args.Equipee, ref ev); + } + if (gear.Comp.ShieldsFromPsionics) + { + var ev = new PsionicShieldedEvent(args.Equipee); + RaiseLocalEvent(args.Equipee, ref ev); + } + } + + private void OnInsulativeGearUnequipped(Entity gear, ref GotUnequippedEvent args) + { + if (_timing.ApplyingState) + return; + + if (!gear.Comp.AllowsPsionicUsage && CanUsePsionicAbility(args.Equipee)) + { + var ev = new PsionicStoppedSuppressedEvent(args.Equipee); + RaiseLocalEvent(args.Equipee, ref ev); + } + if (gear.Comp.ShieldsFromPsionics && CanBeTargeted(args.Equipee, showPopup: false)) + { + var ev = new PsionicStoppedShieldedEvent(args.Equipee); + RaiseLocalEvent(args.Equipee, ref ev); + } + } + + #region EventHandling + private void OnPowerUseAttempt(Entity gear, ref InventoryRelayedEvent args) + { + // If one gear blocks psionic usage, psionics cannot be used. + args.Args.CanUsePower &= gear.Comp.AllowsPsionicUsage; + } + + private void OnTargetedByPsionicPower(Entity gear, ref InventoryRelayedEvent args) + { + // If one gear shields from psionics, they're shielded. + args.Args.IsShielded |= gear.Comp.ShieldsFromPsionics; + } + #endregion + + #region AntiPsionicWeaponry + private void OnAntiPsionicMeleeHit(Entity weapon, ref MeleeHitEvent args) + { + foreach (var target in args.HitEntities) + { + if (HasComp(target)) + { + Audio.PlayPredicted(weapon.Comp.HitSound, target, args.User); + args.ModifiersList.Add(weapon.Comp.Modifiers); + + if (Random.Prob(weapon.Comp.DisableChance)) + _statusEffects.TryUpdateStatusEffectDuration(target, PsionicsDisabledProtoId, TimeSpan.FromSeconds(10)); + } + + if (TryComp(target, out var swapped)) + { + _mindSwapPowerSystem.SwapMinds(target, swapped.OriginalEntity, false); + return; + } + + if (!weapon.Comp.Punish + || !HasComp(target) + || HasComp(target) + || !Random.Prob(weapon.Comp.PunishChance)) + continue; + + _stuttering.DoStutter(args.User, TimeSpan.FromMinutes(5), false); + _stun.TryKnockdown(args.User, TimeSpan.FromSeconds(5), false, drop: false); + _jittering.DoJitter(args.User, TimeSpan.FromSeconds(5), false); + } + } + + private void OnAntiPsionicStamHit(Entity weapon, ref StaminaMeleeHitEvent args) + { + if (args.HitList.Any(targetStamina => HasComp(targetStamina.Entity))) + { + args.Multiplier *= weapon.Comp.StaminaDamageMultiplier; + } + } + #endregion +} diff --git a/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Rolling.cs b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Rolling.cs new file mode 100644 index 0000000000..2f535f9655 --- /dev/null +++ b/Content.Shared/_DV/Psionics/Systems/SubSystems/SharedPsionicSystem.Rolling.cs @@ -0,0 +1,101 @@ +using System.Linq; +using Content.Shared._DV.Psionics.Components; +using Content.Shared.EntityTable; +using Content.Shared.Popups; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; + +namespace Content.Shared._DV.Psionics.Systems; + +public abstract partial class SharedPsionicSystem +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly EntityTableSystem _entityTable = default!; + + public bool TryRollPsionic(Entity potPsionic, float multiplier = 1.0f) + { + if (potPsionic.Comp.Rolled) + return false; + + potPsionic.Comp.Rolled = true; + + if (!RollChance(potPsionic, multiplier)) + { + Popup.PopupEntity(Loc.GetString("psionic-roll-failed"), potPsionic, potPsionic, PopupType.Medium); + return false; + } + + AddRandomPsionicPower(potPsionic, true); + Dirty(potPsionic); + return true; + } + + protected bool RollChance(Entity potPsionic, float multiplier = 1.0f) + { + var chance = potPsionic.Comp.BaseChance; + // Jobs like Command and Chaplains get a bonus on their roll. + chance += potPsionic.Comp.JobBonusChance; + // Species like Kitsunes get a bonus on their roll. + chance += potPsionic.Comp.SpeciesBonusChance; + + // Rolling with chemicals can have multipliers. + chance *= multiplier; + + chance = Math.Clamp(chance, 0, 1); + return Random.Prob(chance); + } + + public void AddRandomPsionicPower(Entity psionic, bool midRound) + { + if (!_prototypeManager.Resolve(psionic.Comp.PsionicPowerTableId, out var powerTable)) + return; + + var random = Random.GetRandom(); // This is called in GetSpawns(). We simply call it once to avoid calling it multiple times. + var attempts = 0; + while (attempts < 20) // 20 attempts to get a unique psionic power. + { + var spawns = _entityTable.GetSpawns(powerTable, random); + + foreach (var entProtoId in spawns) + { + if (TryAddPsionicPower(psionic, midRound, entProtoId)) + return; + + attempts++; + } + } + + Popup.PopupEntity(Loc.GetString("psionic-roll-failed"), psionic, psionic, PopupType.Medium); + } + + private bool TryAddPsionicPower(Entity psionic, bool midRound, EntProtoId entProtoId) + { + if (!_prototypeManager.Resolve(entProtoId, out var powerEntity)) + return false; + // If the psionic already has that power, do not add it again. + if (powerEntity.Components.Any(psionicComponent => + EntityManager.HasComponent(psionic, psionicComponent.Value.Component.GetType()))) + return false; + // If they don't have it already, add it. + EntityManager.AddComponents(psionic, powerEntity); + + if (!midRound) + return true; + // For alternative means of getting psionics that aren't via spawning in, cause them to suffer. + _stuttering.DoStutter(psionic, TimeSpan.FromMinutes(1), false); + _stun.TryKnockdown(psionic.Owner, TimeSpan.FromSeconds(3), false, drop: false); + _jittering.DoJitter(psionic, TimeSpan.FromSeconds(3), false); + + return true; + } + + public bool GrantPsionicRoll(Entity potPsionic) + { + if (!Resolve(potPsionic, ref potPsionic.Comp) || !potPsionic.Comp.Rolled) + return false; + + potPsionic.Comp.Rolled = false; + Dirty(potPsionic); + return true; + } +} diff --git a/Content.Shared/_DV/Roles/ModifyPsionicChanceSpecial.cs b/Content.Shared/_DV/Roles/ModifyPsionicChanceSpecial.cs new file mode 100644 index 0000000000..be3fde0c4e --- /dev/null +++ b/Content.Shared/_DV/Roles/ModifyPsionicChanceSpecial.cs @@ -0,0 +1,30 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared.Roles; + +namespace Content.Shared._DV.Roles; + +public sealed partial class ModifyPsionicChanceSpecial : JobSpecial +{ + /// + /// The value to replace the JobChance with. + /// + [DataField(required: true)] + public float JobBonusChance; + + /// + /// If not null, it'll replace species bonus too. + /// + [DataField] + public float? SpeciesBonusChance; + + public override void AfterEquip(EntityUid mob) + { + var entityManager = IoCManager.Resolve(); + if (!entityManager.TryGetComponent(mob, out PotentialPsionicComponent? psionic)) + return; + + psionic.JobBonusChance = JobBonusChance; + if (SpeciesBonusChance.HasValue) + psionic.SpeciesBonusChance = SpeciesBonusChance.Value; + } +} diff --git a/Content.Shared/_DV/StationEvents/Events/GlimmerEventEndedEvent.cs b/Content.Shared/_DV/StationEvents/Events/GlimmerEventEndedEvent.cs new file mode 100644 index 0000000000..80956ebf19 --- /dev/null +++ b/Content.Shared/_DV/StationEvents/Events/GlimmerEventEndedEvent.cs @@ -0,0 +1,14 @@ +namespace Content.Shared._DV.StationEvents.Events; + +/// +/// After every glimmer event, it'll raise this event. +/// It's used for the sophic scribe to report glimmer changes. +/// +/// The message for the Sophic scribe to repeat. +/// How much glimmer was burned through the event. +[ByRefEvent] +public readonly struct GlimmerEventEndedEvent(string message, int glimmerBurned) +{ + public readonly string Message = message; + public readonly int GlimmerBurned = glimmerBurned; +} diff --git a/Content.Shared/_DV/Xenoarcheology/Artifact/Components/XAEDetailsComponent.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEDetailsComponent.cs similarity index 82% rename from Content.Shared/_DV/Xenoarcheology/Artifact/Components/XAEDetailsComponent.cs rename to Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEDetailsComponent.cs index 9d9375709c..1bc511a6e0 100644 --- a/Content.Shared/_DV/Xenoarcheology/Artifact/Components/XAEDetailsComponent.cs +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEDetailsComponent.cs @@ -1,7 +1,4 @@ -using Content.Shared.Chemistry.Reagent; -using Robust.Shared.Prototypes; - -namespace Content.Shared._DV.Xenoarchaeology.Artifact.Components; +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; /// /// Contains description / tips about the effect. diff --git a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEModifyGlimmerComponent.cs similarity index 64% rename from Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs rename to Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEModifyGlimmerComponent.cs index 1d205f67bf..57340cf364 100644 --- a/Content.Server/_DV/Xenoarchaeology/XenoArtifacts/Effects/Components/GlimmerArtifactComponent.cs +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEModifyGlimmerComponent.cs @@ -1,13 +1,13 @@ -using Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Systems; +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Systems; using Content.Shared.Destructible.Thresholds; -namespace Content.Server._DV.Xenoarchaeology.XenoArtifacts.Effects.Components; +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; /// /// Raises or lowers glimmer when this artifact is triggered. /// -[RegisterComponent, Access(typeof(GlimmerArtifactSystem))] -public sealed partial class GlimmerArtifactComponent : Component +[RegisterComponent, Access(typeof(XAEModifyGlimmerSystem))] +public sealed partial class XAEModifyGlimmerComponent : Component { /// /// If glimmer is not in this range it won't do anything. diff --git a/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEPsionicInducerComponent.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEPsionicInducerComponent.cs new file mode 100644 index 0000000000..9419fa7b68 --- /dev/null +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Components/XAEPsionicInducerComponent.cs @@ -0,0 +1,16 @@ +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Systems; + +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; + +/// +/// Makes people in a radius around it psionic when triggered. +/// +[RegisterComponent, Access(typeof(XAEPsionicInducerSystem))] +public sealed partial class XAEPsionicInducerComponent : Component +{ + /// + /// Range to look for potential psionics in. + /// + [DataField(required: true)] + public float Range; +} diff --git a/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEModifyGlimmerSystem.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEModifyGlimmerSystem.cs new file mode 100644 index 0000000000..529337ce90 --- /dev/null +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEModifyGlimmerSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; +using Content.Shared.Psionics.Glimmer; +using Content.Shared.Xenoarchaeology.Artifact; + +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Systems; + +public sealed class XAEModifyGlimmerSystem : EntitySystem +{ + [Dependency] private readonly GlimmerSystem _glimmer = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActivated); + } + + private void OnActivated(Entity arti, ref XenoArtifactNodeActivatedEvent args) + { + var range = arti.Comp.Range; + var current = _glimmer.Glimmer; + if (range.Min > current || current > range.Max) + return; + + _glimmer.Glimmer += arti.Comp.Change; + } +} diff --git a/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEPsionicInducerSystem.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEPsionicInducerSystem.cs new file mode 100644 index 0000000000..3a82fc438e --- /dev/null +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Effects/Systems/XAEPsionicInducerSystem.cs @@ -0,0 +1,33 @@ +using Content.Shared._DV.Psionics.Components; +using Content.Shared._DV.Psionics.Systems; +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Components; +using Content.Shared.Xenoarchaeology.Artifact; + +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Effects.Systems; + +public sealed class XAEPsionicInducerSystem : EntitySystem +{ + [Dependency] private readonly EntityLookupSystem _lookup = default!; + [Dependency] private readonly SharedPsionicSystem _psionic = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnActivated); + } + + private void OnActivated(Entity arti, ref XenoArtifactNodeActivatedEvent args) + { + var coords = Transform(arti).Coordinates; + foreach (var target in _lookup.GetEntitiesInRange(coords, arti.Comp.Range)) + { + // No additional ability for already psionic beings. + if (HasComp(target)) + continue; + + if (_psionic.CanBeTargeted(target)) + _psionic.AddRandomPsionicPower(target, true); + } + } +} diff --git a/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Components/XATPsionicUsageComponent.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Components/XATPsionicUsageComponent.cs new file mode 100644 index 0000000000..37b223f157 --- /dev/null +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Components/XATPsionicUsageComponent.cs @@ -0,0 +1,8 @@ +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Triggers.Components; + +/// +/// Triggers if a psionic power is used nearby. +/// Requires to be added too. +/// +[RegisterComponent] +public sealed partial class XATPsionicUsageComponent : Component; diff --git a/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Systems/XATPsionicUsageSystem.cs b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Systems/XATPsionicUsageSystem.cs new file mode 100644 index 0000000000..02272642f9 --- /dev/null +++ b/Content.Shared/_DV/Xenoarcheology/XenoArtifacts/Triggers/Systems/XATPsionicUsageSystem.cs @@ -0,0 +1,44 @@ +using Content.Shared._DV.Psionics.Events; +using Content.Shared._DV.StationEvents.Events; +using Content.Shared._DV.Xenoarcheology.XenoArtifacts.Triggers.Components; +using Content.Shared.Xenoarchaeology.Artifact.Components; +using Content.Shared.Xenoarchaeology.Artifact.XAT; + +namespace Content.Shared._DV.Xenoarcheology.XenoArtifacts.Triggers.Systems; + +public sealed class XATPsionicUsageSystem : BaseXATSystem +{ + private EntityQuery _xenoArtifactQuery; + + public override void Initialize() + { + base.Initialize(); + + _xenoArtifactQuery = GetEntityQuery(); + + XATSubscribeDirectEvent(OnPowerDetected); + SubscribeLocalEvent(OnGlimmerEventEnded); + } + + private void OnPowerDetected(Entity artifact, Entity node, ref PsionicPowerDetectedEvent args) + { + Trigger(artifact, node); + } + + private void OnGlimmerEventEnded(ref GlimmerEventEndedEvent args) + { + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp, out var node)) + { + if (node.Attached == null) + continue; + + var artifact = _xenoArtifactQuery.Get(node.Attached.Value); + + if (!CanTrigger(artifact, (uid, node))) + continue; + + Trigger(artifact, (uid, comp, node)); + } + } +} diff --git a/Resources/Locale/en-US/_DV/abilities/psionic.ftl b/Resources/Locale/en-US/_DV/abilities/psionic.ftl index 30f23aa7ad..2429afe1d0 100644 --- a/Resources/Locale/en-US/_DV/abilities/psionic.ftl +++ b/Resources/Locale/en-US/_DV/abilities/psionic.ftl @@ -1,15 +1,50 @@ -psionic-nosebleed-message = Your nose starts gushing blood! +## Names of Psionic Powers +psionic-power-name-dispel = Dispel +psionic-power-name-eruption = Psionic Eruption +psionic-power-name-fractured-form = Fractured Form +psionic-power-name-mass-sleep = Mass Sleep +psionic-power-name-mindswap = Mind Swap +psionic-power-name-mindswap-return = Mind Swap Return +psionic-power-name-metapsionic = Metapsionic Pulse +psionic-power-name-noospheric-zap = Noospheric Zap +psionic-power-name-precognition = Precognition +psionic-power-name-psychokinetic = Psychokinetic Scream +psionic-power-name-psionic-invisibility = Psionic Invisibility +psionic-power-name-psionic-regeneration = Psionic Regeneration +psionic-power-name-pyrokinesis = Pyrokinesis +psionic-power-name-telegnosis = Telegnosis -mass-mind-swap-event-announcement = Warning: abnormal glimmer discharge detected. Mass consciousness transfer event imminent, T-{$time} seconds. Please equip psionically-insulating headwear immediately. +## Psionic Rolling & Mindbreaking Messages +psionic-roll-failed = The noöspheric influence leaves no mark on your mind... +psionic-partly-mindbroken = The psionic influence on your mind weakens.. +psionic-mindbroken = Your mind retreats from abstraction to reality. -mass-mind-swap-event-sender = Sophic Grammateus +## Base Psionic messages +psionic-cannot-use-psionics = Your psionic energy can't escape your mind! +psionic-shielded-from-attempt = A psionic influence faltered against your shield! +psionic-cannot-target-shielded = They remain steadfast against your psionic grasp! +psionic-equipped-shielded-in-doafter = The insulative gear broke your concentration.. +psionic-dispelled = Someone dispelled your psionic concentration! -minor-mass-mind-swap-event-announcement = Warning: abnormal glimmer discharge detected. Minor Mass Consciousness transfer event imminent, T-{$time} seconds. Please equip psionically-insulating headwear immedieately. +## Specific Psionic messages +# Fractured Form +psionic-power-fractured-form-nobodies = You have no alternate forms to switch to! +psionic-power-fractured-form-sleepy = You feel very sleepy... You should find somewhere to rest. +psionic-power-fractured-form-ssd = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } in a deep sleep. { CAPITALIZE(POSS-ADJ($ent)) } eyes seem to be darting around as if dreaming. +psionic-power-fractured-form-examine-self = You feel a strange connection to { OBJECT($ent) }. +psionic-power-fractured-form-dispelled = Someone dispelled your sleepiness.. -minor-mass-mind-swap-event-sender = Sophic Grammateus +# Metapsionic Pulse +psionic-power-metapsionic-success = You detect psychic presence there. +psionic-power-metapsionic-failure = You don't detect any psychic presence there. +psionic-power-metapsionic-power-detected = You detect that {$power} was used nearby. -psionic-power-mass-sleep-warning = Your eyelids begin to droop... +# Mindswap +psionic-power-mindswap-target-mindshielded = Your mindshield.. surprisingly shielded your mind from an psionic influence. +psionic-power-mindswap-own-mindshield = Your mindshield.. stops your mind from leaving your body. +psionic-power-mindswap-original-lost = The psionic tether to your original body was severed! +# Precognition psionic-power-precognition-failure-by-damage = Your concentration was broken! You fail to decipher anything of use. psionic-power-precognition-no-event-result-message = You see a vision of an undisturbed lake. @@ -62,6 +97,7 @@ psionic-power-precognition-syndicate-armsdealer-result-message = You see a visio psionic-power-precognition-rift-spawn-result-message = You see a small spark of energy, quickly expanding as it tears reality apart, twisting everything around it. psionic-power-precognition-asakim-spawn-result-message = You smell stale air from a cryopod opening, and the faint echo of an intelligence far away but very near. +# Psionic Eruption psionic-eruption-begin = {CAPITALIZE(THE($user))} is being consumed by a psionic energy! psionic-eruption-annoy-minimal = You feel a pressure building up in your mind. psionic-eruption-annoy-low = Your head aches from the psionic energy. @@ -86,11 +122,20 @@ eruption-warning-window-prompt-text-part = You feel a strong pressure building u Do you understand? eruption-warning-window-acknowledge-button = I Understand +## Psionic Gamerule Messages +gamerule-noospheric-zap-seize = An external eruption overwhelms your mind! +gamerule-noospheric-zap-seize-potential-regained = Your mind restructures.. it demands knowledge... + +psionic-nosebleed-message = Your nose starts gushing blood! + +mass-mind-swap-event-announcement = Warning: abnormal glimmer discharge detected. Mass consciousness transfer event imminent, T-{$time} seconds. Please equip psionically-insulating headwear immediately. +mass-mind-swap-event-sender = Sophic Grammateus + +minor-mass-mind-swap-event-announcement = Warning: abnormal glimmer discharge detected. Minor Mass Consciousness transfer event imminent, T-{$time} seconds. Please equip psionically-insulating headwear immedieately. +minor-mass-mind-swap-event-sender = Sophic Grammateus + +psionic-power-mass-sleep-warning = Your eyelids begin to droop... + telegnosis-power-ssd = { CAPITALIZE(POSS-ADJ($ent)) } eyes are unfocused and darting around, as if trying to see something that isn't there. -fractured-form-nobodies = You have no alternate forms to switch to! -fractured-form-sleepy = You feel very sleepy... You should find somewhere to rest. -fractured-form-ssd = { CAPITALIZE(SUBJECT($ent)) } { CONJUGATE-BE($ent) } in a deep sleep. { CAPITALIZE(POSS-ADJ($ent)) } eyes seem to be darting around as if dreaming. -fractured-form-examine-self = You feel a strange connection to { OBJECT($ent) }. - glimmer-restyle-event = You feel like something changed about your looks... diff --git a/Resources/Locale/en-US/_DV/chemistry/effects.ftl b/Resources/Locale/en-US/_DV/chemistry/effects.ftl new file mode 100644 index 0000000000..0db9a6a009 --- /dev/null +++ b/Resources/Locale/en-US/_DV/chemistry/effects.ftl @@ -0,0 +1,12 @@ +reagent-effect-guidebook-chem-remove-psionic = + { $chance -> + [1] Removes + *[other] {$chance} to remove + } all psionic powers + +reagent-effect-guidebook-chem-roll-psionic = + Has a chance to grant another, different psionic power + { $multiplier -> + [1] {""} + *[other] with a chance multiplier of {$multiplier} + } diff --git a/Resources/Locale/en-US/_DV/reagents/meta/biological.ftl b/Resources/Locale/en-US/_DV/reagents/meta/biological.ftl index d3fcceb245..221c2a18e9 100644 --- a/Resources/Locale/en-US/_DV/reagents/meta/biological.ftl +++ b/Resources/Locale/en-US/_DV/reagents/meta/biological.ftl @@ -1,2 +1,8 @@ reagent-name-sentient-grey-matter = sentient grey matter reagent-desc-sentient-grey-matter = Thought juice, the stuff that leaks out of your ears. It seems to be sparkling when you look at it in the right light. That's odd... + +reagent-name-nectar = nectar +reagent-desc-nectar = A drink from the gods of old, a rare delicacy amongst the spiritual. It temporarily shields the mind from external psionic influences and quenches the bodily thirst. + +reagent-name-ambrosia = ambrosia +reagent-desc-ambrosia = A food from the gods of old, a rare delicacy amongst the spiritual. It temporarily strengthens the body, gifting speed and stamina, while quenching it's hunger. diff --git a/Resources/Locale/en-US/_DV/reagents/meta/medicine.ftl b/Resources/Locale/en-US/_DV/reagents/meta/medicine.ftl index 3534a5b6f9..f31662de28 100644 --- a/Resources/Locale/en-US/_DV/reagents/meta/medicine.ftl +++ b/Resources/Locale/en-US/_DV/reagents/meta/medicine.ftl @@ -14,6 +14,9 @@ reagent-desc-anodynafil = An effective short-lasting anesthetic that doesn't int reagent-desc-dexalin-deltav = Used for treating minor oxygen deprivation and bloodloss. A required reagent for salbutamol and convermol. +reagent-name-sanctussal = sanctus sal +reagent-desc-sanctussal = A primitive mixture that wards off noöspheric influences. Historically a scam, but sourcing the holy water from a divine source yields factual results. + reagent-name-photoxadone = photoxadone reagent-desc-photoxadone = a strange, faintly glowing cryogenics chemical. Seems to have some effect on damages deeper than physical, even affecting the dead. diff --git a/Resources/Locale/en-US/_DV/reagents/meta/narcotic.ftl b/Resources/Locale/en-US/_DV/reagents/meta/narcotic.ftl new file mode 100644 index 0000000000..46e86714c1 --- /dev/null +++ b/Resources/Locale/en-US/_DV/reagents/meta/narcotic.ftl @@ -0,0 +1,2 @@ +reagent-name-lotophagoi-oil = lotophagoi oil +reagent-desc-lotophagoi-oil = A divine drug sourced from the fruits of an ancient tree. It temporarily tears into the patient's mind, reshaping it to suit the Noösphere. diff --git a/Resources/Locale/en-US/_DV/reagents/meta/toxins.ftl b/Resources/Locale/en-US/_DV/reagents/meta/toxins.ftl index 66d1726468..191b6b8862 100644 --- a/Resources/Locale/en-US/_DV/reagents/meta/toxins.ftl +++ b/Resources/Locale/en-US/_DV/reagents/meta/toxins.ftl @@ -1 +1,4 @@ reagent-desc-heartbreaker-toxin-deltav = A hallucinogenic compound derived from mindbreaker toxin. it blocks neurological signals to the respiratory system, causing asphyxiation. Epinephrine will filter it out, however. + +reagent-name-lernaea = Lernaea +reagent-desc-lernaea = A vicious poison, speculated to originate from a river of death. It ravenously consumes noöspheric energies in the mind while it's present. diff --git a/Resources/Locale/en-US/nyanotrasen/abilities/psionic.ftl b/Resources/Locale/en-US/nyanotrasen/abilities/psionic.ftl index 4b9198663a..a03bb4e078 100644 --- a/Resources/Locale/en-US/nyanotrasen/abilities/psionic.ftl +++ b/Resources/Locale/en-US/nyanotrasen/abilities/psionic.ftl @@ -3,10 +3,6 @@ cage-resist-third-person = {CAPITALIZE(THE($user))} starts removing {POSS-ADJ($u cage-uncage-verb = Uncage -metapsionic-pulse-success = You detect psychic presence nearby. -metapsionic-pulse-failure = You don't detect any psychic presence nearby. -metapsionic-pulse-power = You detect that {$power} was used nearby. - accept-psionics-window-title = Psionic! accept-psionics-window-prompt-text-part = You rolled a psionic power! It's possible that certain anti-psychic forces may hunt you, @@ -18,9 +14,6 @@ glimmer-event-report-generic = Noöspheric discharge detected. Glimmer level has glimmer-event-report-signatures = New psionic signatures manifested. Glimmer level has decreased by {$decrease} to {$level}Ψ. glimmer-event-awakened-prefix = awakened {$entity} -noospheric-zap-seize = You seize up! -noospheric-zap-seize-potential-regained = You seize up! Some mental block seems to have cleared, too. - mindswap-trapped = Seems you're trapped in this vessel. telegnostic-trapped-entity-name = severed telegnostic projection diff --git a/Resources/Locale/en-US/nyanotrasen/chemistry/effects.ftl b/Resources/Locale/en-US/nyanotrasen/chemistry/effects.ftl index a82aae768c..aa0636eac1 100644 --- a/Resources/Locale/en-US/nyanotrasen/chemistry/effects.ftl +++ b/Resources/Locale/en-US/nyanotrasen/chemistry/effects.ftl @@ -4,17 +4,7 @@ reagent-effect-guidebook-change-glimmer-reaction-effect = *[other] modify } the glimmer count by {$amount} points -reagent-effect-guidebook-chem-remove-psionic = - { $chance -> - [1] Removes - *[other] remove - } psionic powers -reagent-effect-guidebook-chem-reroll-psionic = - { $chance -> - [1] Allows - *[other] allow - } a chance to get a different psionic power ## Disease System support diff --git a/Resources/Locale/en-US/nyanotrasen/reagents/toxins.ftl b/Resources/Locale/en-US/nyanotrasen/reagents/toxins.ftl index 43e35c191c..60ae2e80df 100644 --- a/Resources/Locale/en-US/nyanotrasen/reagents/toxins.ftl +++ b/Resources/Locale/en-US/nyanotrasen/reagents/toxins.ftl @@ -1,8 +1,5 @@ reagent-name-soulbreaker-toxin = soulbreaker toxin reagent-desc-soulbreaker-toxin = An anti-psionic about 4 times as powerful as mindbreaker toxin. -reagent-name-lotophagoi-oil = lotophagoi oil -reagent-desc-lotophagoi-oil = A super potent drug that is much better at inducing psionics than normal hallucinogens, but with worse side effects. - reagent-name-ectoplasm = ectoplasm reagent-desc-ectoplasm = The physical component of semi-corporeal spirits. diff --git a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml index 20b6043207..7cc9f496c0 100644 --- a/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml @@ -908,11 +908,9 @@ color: "#ffffff" # Begin DeltaV Additions - type: Psionic - - type: TinfoilHat - passthrough: true - destroyOnFry: false - - type: ClothingGrantPsionicPower - power: DispelPower + - type: PsionicallyInsulative + allowsPsionicUsage: true + - type: DispelPower - type: GuideHelp guides: - Psionics diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index ded670f3b4..74ca47a432 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -130,7 +130,6 @@ attributes: gender: male - type: Sharp - - type: PotentialPsionic # Nyano - type: NightVision #Delta-v - Added nightvision color: "#808080" diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml index 8f43711c98..1c61cc3f65 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/revenant.yml @@ -93,6 +93,7 @@ - type: Reactive groups: Acidic: [Touch] + - type: Psionic # DeltaV - Revenant is of Noospheric Origin - type: Tag tags: - SlowImmune diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml index 9d0adc3a4f..101baf4daf 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/xeno.yml @@ -125,10 +125,7 @@ - type: Speech speechVerb: LargeMob speechSounds: Xenonid # DeltaV: Use RMC's speech sounds for sentient xenos - - type: PotentialPsionic #Nyano - Summary: makes potentially psionic. - chance: -2 - type: Psionic #Nyano - Summary: makes psionic by default. - removable: false - type: NightVision #Delta-v - Added nightvision color: "#808080" diff --git a/Resources/Prototypes/Entities/Mobs/Player/familiars.yml b/Resources/Prototypes/Entities/Mobs/Player/familiars.yml index 58548c1a96..a8a2e8ca44 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/familiars.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/familiars.yml @@ -37,7 +37,6 @@ - type: Alerts - type: Familiar - type: Psionic #Nyano - Summary: Makes psionic on creation. - removable: false - type: entity name: Cerberus @@ -98,7 +97,6 @@ - type: Familiar - type: Dispellable - type: Psionic #Nyano - Summary: makes psionic on creation. - removable: false - type: Vocal sounds: Male: Cerberus diff --git a/Resources/Prototypes/Entities/Mobs/Player/human.yml b/Resources/Prototypes/Entities/Mobs/Player/human.yml index 1ae214addd..50929073c6 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/human.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/human.yml @@ -82,9 +82,8 @@ components: - type: NukeOperative - type: RandomHumanoidAppearance - - type: PsionicBonusChance #Nyano - Summary: makes more likely to be psionic. - multiplier: 7 - warn: false + - type: PotentialPsionic # DeltaV - Psionic Refactor + jobBonusChance: 0.05 # Additional 5% chance to roll a psionic power. - type: entity categories: [ HideSpawnMenu ] @@ -106,9 +105,8 @@ - type: NpcFactionMember factions: - Syndicate - - type: PsionicBonusChance # DeltaV - multiplier: 7 - warn: false + - type: PotentialPsionic # DeltaV - Psionic Refactor + jobBonusChance: 0.05 # Additional 5% chance to roll a psionic power. # Wizard - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml index 4f4cf4ba23..12c5234e1d 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/humanoid.yml @@ -648,9 +648,8 @@ - type: RandomHumanoidAppearance randomizeName: false - type: NukeOperative - - type: PsionicBonusChance #Nyano - Summary: makes more likely to be psionic. - multiplier: 7 - warn: false + - type: PotentialPsionic # DeltaV - Psionic Refactor + jobBonusChance: 0.05 # Additional 5% chance to roll a psionic power. - type: entity id: RandomHumanoidSpawnerCluwne diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 5ddb26fbe3..e00b180fa2 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -224,7 +224,6 @@ - FootstepSound - DoorBumpOpener - AnomalyHost - - type: PotentialPsionic # DeltaV - organic species can all be psionic - type: Targeting # Shitmed Change - type: SurgeryTarget # Shitmed Change - type: CosmicCenserTarget # DeltaV - Cosmic Cult @@ -302,6 +301,7 @@ alternateState: Standing #- type: StunVisuals # DeltaV - No stars - type: CanDoCPR # DeltaV - Addition of CPR + - type: PotentialPsionic # DeltaV - organic species can all be psionic - type: entity save: false diff --git a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml index 23277c3ce9..e48c62a8ff 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Xenoarchaeology/xenoartifacts.yml @@ -44,6 +44,7 @@ ents: [] - type: Psionic # DeltaV - sentient artifacts are psionic # These components are needed for certain triggers to work. + - type: PsionicPowerDetector # DeltaV - Required for PsionicPowerUsage Trigger. - type: RadiationReceiver selfSourceMultiplier: 0.1 # DeltaV - a radiation effect triggering a radiation trigger on the same artifact is often annoying - type: Reactive @@ -64,7 +65,7 @@ useDelay: 300 - type: InstantAction event: !type:ArtifactSelfActivateEvent - + - type: entity id: SimpleXenoArtifact parent: ComplexXenoArtifact diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hardsuit-helmets.yml index 81feb2ca9b..08613c201b 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hardsuit-helmets.yml @@ -11,12 +11,8 @@ sprite: _DV/Clothing/Head/Hardsuits/rd.rsi - type: PointLight color: "#d6adff" - - type: Psionic - - type: TinfoilHat - passthrough: true - destroyOnFry: false - - type: ClothingGrantPsionicPower - power: MetapsionicPower + - type: PsionicallyInsulative + allowsPsionicUsage: true - type: GuideHelp guides: - Psionics diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hats.yml b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hats.yml index ee2f5c6b75..3fa15bd3b6 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hats.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/Head/hats.yml @@ -55,7 +55,8 @@ sprite: Nyanotrasen/Clothing/Head/Hats/tinfoil.rsi - type: Clothing sprite: Nyanotrasen/Clothing/Head/Hats/tinfoil.rsi - - type: TinfoilHat + - type: PsionicallyInsulative + canBeFried: true - type: Armor modifiers: coefficients: @@ -79,7 +80,7 @@ sprite: Nyanotrasen/Clothing/Head/Hats/bellhop.rsi - type: entity - parent: ClothingHeadTinfoil + parent: ClothingHeadBase id: ClothingHeadCage name: insulative headcage suffix: SelfUnremovable @@ -92,12 +93,18 @@ equipDelay: 5 sprite: Nyanotrasen/Clothing/Head/Hats/cage.rsi - type: SelfUnremovableClothing + - type: PsionicallyInsulative + shieldsFromPsionics: false + canBeFried: true + - type: GuideHelp + guides: + - Psionics - type: Tag tags: - ForensicBeltEquip - type: entity - parent: ClothingHeadTinfoil + parent: ClothingHeadBase id: ClothingHeadHelmetInsulated name: insulative skullcap description: Psionically insulates whoevers head is inside it. It has some protection from physical damage. @@ -106,6 +113,7 @@ sprite: Nyanotrasen/Clothing/Head/Helmets/insulative_skullcap.rsi - type: Clothing sprite: Nyanotrasen/Clothing/Head/Helmets/insulative_skullcap.rsi + - type: PsionicallyInsulative - type: Armor modifiers: coefficients: diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/OuterClothing/hardsuits.yml b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/OuterClothing/hardsuits.yml index 8b4e3b4f0d..e17d0d17ae 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Clothing/OuterClothing/hardsuits.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Clothing/OuterClothing/hardsuits.yml @@ -2,7 +2,7 @@ parent: ClothingOuterHardsuitRd id: ClothingOuterHardsuitMystagogue name: mystagogue's hardsuit - description: A heavy-duty hardsuit specialized for research that includes a built-in psionic nullification and detection system in the helmet. Modified bluespace embedded into the plating allows it to be folded into a smaller space then one would expect. + description: A heavy-duty hardsuit specialized for research that includes a built-in psionic one-way nullification system in the helmet and a detection system in the core suit. Modified bluespace embedded into the plating allows it to be folded into a smaller space than one would expect. components: - type: Sprite sprite: _DV/Clothing/OuterClothing/Hardsuits/rd.rsi @@ -10,6 +10,7 @@ sprite: _DV/Clothing/OuterClothing/Hardsuits/rd.rsi - type: ToggleableClothing clothingPrototype: ClothingHeadHelmetHardsuitMystagogue + - type: MetapsionicPulsePower # The suit gives the ability to pulse and seek psionic energies. - type: entity parent: [ClothingOuterHardsuitMystagogue, ClothingOuterStorageFoldableBaseOpened] diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/special.yml b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/special.yml index 22b739a46c..bed7d67927 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/special.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Mobs/Player/special.yml @@ -17,12 +17,12 @@ - type: MovementSpeedModifier baseSprintSpeed: 6 baseWalkSpeed: 4 - #- type: PsionicallyInvisible + - type: PsionicallyInvisible - type: Eye drawFov: false visMask: - Normal - - TelegnosticProjection + - PsionicInvisibility - type: Input context: "ghost" - type: TelegnosticProjection diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Medical/pills.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Medical/pills.yml index 962f535194..3aa516064b 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Medical/pills.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Specific/Medical/pills.yml @@ -11,17 +11,3 @@ reagents: - ReagentId: MindbreakerToxin Quantity: 20 - -- type: entity - name: cryptobiolin - parent: Pill - id: PillCryptobiolin - description: A long-lasting drug which causes mild confusion, but renders you psionically insulated. - components: - - type: SolutionContainerManager - solutions: - food: - maxVol: 20 - reagents: - - ReagentId: Cryptobiolin - Quantity: 20 diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/knives.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/knives.yml index 7ba4910d8d..9c5b2bf590 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/knives.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Weapons/Melee/knives.yml @@ -1,24 +1,19 @@ - type: entity parent: CombatKnife id: AntiPsychicKnife - name: anti-psychic knife - description: A special knife designed for killing psychics. + name: anti-psionic knife + description: A special knife designed for killing psionics. components: - type: MeleeWeapon - attackRate: 1.5 damage: types: Slash: 10 Holy: 5 - - type: StaminaDamageOnHit - damage: 0 - type: AntiPsionicWeapon modifiers: coefficients: - Blunt: 1.2 - Slash: 1.2 - Piercing: 1.2 - Holy: 1.2 + Slash: 2 + Holy: 2 - type: Sprite sprite: Nyanotrasen/Objects/Weapons/Melee/anti_psychic_knife.rsi state: icon diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml index 908658acaa..2d66afc981 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Structures/Research/sophicscribe.yml @@ -24,7 +24,6 @@ channels: - Common - Science - - type: PotentialPsionic #this makes her easier to access for glimmer events, dw about it - type: Psionic - type: Grammar attributes: diff --git a/Resources/Prototypes/Nyanotrasen/Reagents/psionic.yml b/Resources/Prototypes/Nyanotrasen/Reagents/psionic.yml index 42d1e7cc3a..41197742c4 100644 --- a/Resources/Prototypes/Nyanotrasen/Reagents/psionic.yml +++ b/Resources/Prototypes/Nyanotrasen/Reagents/psionic.yml @@ -62,35 +62,6 @@ reagent: SoulbreakerToxin min: 5 -- type: reagent - id: LotophagoiOil - name: reagent-name-lotophagoi-oil - group: Narcotics - desc: reagent-desc-lotophagoi-oil - flavor: enthralling - color: "#FFBF00" - physicalDesc: reagent-physical-desc-overpowering - metabolisms: - Narcotic: - metabolismRate: 0.2 - effects: - - !type:ModifyStatusEffect - effectProto: StatusEffectSeeingRainbow - time: 5 - type: Add - - !type:RerollPsionicAbilities - bonusMultiplier: 4 - conditions: - - !type:ReagentCondition - reagent: LotophagoiOil - min: 20 - - !type:ModifyStatusEffect - effectProto: StatusEffectScrambled - type: Update - - !type:GenericStatusEffect - key: RepeatingTelepath - component: TelepathicRepeater - - type: reagent id: Ectoplasm name: reagent-name-ectoplasm diff --git a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml index dfc699bb16..6dbde7fd0e 100644 --- a/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml +++ b/Resources/Prototypes/Nyanotrasen/Roles/Jobs/Epistemics/forensicmantis.yml @@ -22,8 +22,7 @@ special: - !type:AddComponentSpecial components: - - type: Psionic - - type: MetapsionicPower + - type: MetapsionicPulsePower - type: startingGear id: ForensicMantisGear diff --git a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml b/Resources/Prototypes/Nyanotrasen/psionicPowers.yml deleted file mode 100644 index c01c99520b..0000000000 --- a/Resources/Prototypes/Nyanotrasen/psionicPowers.yml +++ /dev/null @@ -1,15 +0,0 @@ -- type: weightedRandom - id: RandomPsionicPowerPool - weights: - MetapsionicPower: 1 - DispelPower: 1 - TelegnosisPower: 1 - PsionicRegenerationPower: 1 - PrecognitionPower: 1 # DeltaV - PsychokineticScreamPower: 1 - MassSleepPower: 0.3 - NoosphericZapPower: 0.3 - FracturedFormPower: 0.3 -# PsionicInvisibilityPower: 0.15 - MindSwapPower: 0.15 - PsionicEruptionPower: 0.1 diff --git a/Resources/Prototypes/Nyanotrasen/status_effects.yml b/Resources/Prototypes/Nyanotrasen/status_effects.yml deleted file mode 100644 index 322fcc9988..0000000000 --- a/Resources/Prototypes/Nyanotrasen/status_effects.yml +++ /dev/null @@ -1,5 +0,0 @@ -- type: statusEffect - id: PsionicsDisabled - -- type: statusEffect - id: PsionicallyInsulated diff --git a/Resources/Prototypes/Reagents/medicine.yml b/Resources/Prototypes/Reagents/medicine.yml index 78760c202a..9742cd24bd 100644 --- a/Resources/Prototypes/Reagents/medicine.yml +++ b/Resources/Prototypes/Reagents/medicine.yml @@ -10,7 +10,6 @@ color: "#081a80" metabolisms: Medicine: - metabolismRate: 0.1 effects: - !type:ModifyStatusEffect effectProto: StatusEffectScrambled @@ -18,14 +17,6 @@ - !type:ModifyStatusEffect effectProto: StatusEffectWoozy time: 10.0 # DeltaV - 20 is ridiculous - - !type:GenericStatusEffect # DeltaV - key: PsionicsDisabled - component: PsionicsDisabled - type: Add - - !type:GenericStatusEffect # DeltaV - key: PsionicallyInsulated - component: PsionicInsulation - type: Add - type: reagent id: Dylovene diff --git a/Resources/Prototypes/Reagents/narcotics.yml b/Resources/Prototypes/Reagents/narcotics.yml index 106a3025fe..e28fd79334 100644 --- a/Resources/Prototypes/Reagents/narcotics.yml +++ b/Resources/Prototypes/Reagents/narcotics.yml @@ -332,7 +332,7 @@ type: Add time: 5 # DeltaV Additions - - !type:RerollPsionicAbilities # DeltaV - Roll for psionics + - !type:RollPsionicAbility # DeltaV - Roll for psionics conditions: - !type:ReagentCondition reagent: SpaceDrugs diff --git a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml index 4cee1fe9be..09e97e5482 100644 --- a/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml +++ b/Resources/Prototypes/Roles/Jobs/Cargo/quartermaster.yml @@ -38,6 +38,10 @@ - !type:AddComponentSpecial components: - type: CommandStaff + # Start DeltaV Additions + - !type:ModifyPsionicChanceSpecial # Command are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. + # End DeltaV Additions - type: startingGear id: QuartermasterGear diff --git a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml index 156f348319..bc37d8c00b 100644 --- a/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml +++ b/Resources/Prototypes/Roles/Jobs/Civilian/chaplain.yml @@ -17,8 +17,8 @@ - type: BibleUser #Lets them heal with bibles - type: Condemned soulOwnedNotDevil: true # This just means they won't suffer the effects of being condemned, while still not being able to sell their soul. - Goobstation - - type: PsionicBonusChance # DeltaV - makes it more likely to become psionic. - multiplier: 3 + - !type:ModifyPsionicChanceSpecial # DeltaV - Chaplains are much more likely to be psionic. + jobBonusChance: 0.15 # Additional 15% chance. - type: startingGear id: ChaplainGear diff --git a/Resources/Prototypes/Roles/Jobs/Command/captain.yml b/Resources/Prototypes/Roles/Jobs/Command/captain.yml index 23ef3f418e..d1c4570bc2 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/captain.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/captain.yml @@ -35,10 +35,10 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - type: PsionicBonusChance # DeltaV - makes it more likely to become psionic. - flatBonus: 0.025 - type: Condemned # Goobstation - Nanotrasen owns your soul pal. soulOwnedNotDevil: true + - !type:ModifyPsionicChanceSpecial # DeltaV - Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: CaptainGear diff --git a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml index 192d42cc7e..580b9706eb 100644 --- a/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml +++ b/Resources/Prototypes/Roles/Jobs/Command/head_of_personnel.yml @@ -69,8 +69,8 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - type: PsionicBonusChance # DeltaV: makes it more likely to become psionic. - flatBonus: 0.025 + - !type:ModifyPsionicChanceSpecial # DeltaV - Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: HoPGear diff --git a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml index 03391edd34..2291221d05 100644 --- a/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml +++ b/Resources/Prototypes/Roles/Jobs/Engineering/chief_engineer.yml @@ -34,8 +34,8 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - type: PsionicBonusChance # DeltaV - makes it more likely to become psionic. - flatBonus: 0.025 + - !type:ModifyPsionicChanceSpecial # DeltaV - Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: ChiefEngineerGear diff --git a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml index bf54d8f8f2..f9d63d48ea 100644 --- a/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml +++ b/Resources/Prototypes/Roles/Jobs/Medical/chief_medical_officer.yml @@ -42,8 +42,8 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - type: PsionicBonusChance # DeltaV - makes it more likely to become psionic. - flatBonus: 0.025 + - !type:ModifyPsionicChanceSpecial # DeltaV - Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: CMOGear diff --git a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml index 8835909ccc..09e4c87342 100644 --- a/Resources/Prototypes/Roles/Jobs/Science/scientist.yml +++ b/Resources/Prototypes/Roles/Jobs/Science/scientist.yml @@ -13,8 +13,13 @@ access: - Research - Maintenance - extendedAccess: # DeltaV - Scientists get robotics access on lowpop + # Begin DeltaV Additions + extendedAccess: - Robotics + special: + - !type:ModifyPsionicChanceSpecial # Scientists are much more likely to be psionic. + jobBonusChance: 0.10 # Additional 10% chance. + # End DeltaV Additions - type: startingGear id: ScientistGear diff --git a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml index b031f77456..68fb8b425c 100644 --- a/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml +++ b/Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml @@ -42,8 +42,8 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - type: PsionicBonusChance # DeltaV - makes it more likely to become psionic. - flatBonus: 0.025 + - !type:ModifyPsionicChanceSpecial # DeltaV - Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: HoSGear diff --git a/Resources/Prototypes/XenoArch/triggers.yml b/Resources/Prototypes/XenoArch/triggers.yml index 9c8886ffe6..d82396fc7b 100644 --- a/Resources/Prototypes/XenoArch/triggers.yml +++ b/Resources/Prototypes/XenoArch/triggers.yml @@ -1,7 +1,7 @@ - type: weightedRandomXenoArchTrigger id: DefaultTriggers weights: - TriggerMetapsionic: 1 # DeltaV + TriggerPsionicUsage: 1 # DeltaV TriggerMusic: 1 # DeltaV - make less frequent #TriggerHeat: 1 diff --git a/Resources/Prototypes/_DV/Actions/psionic.yml b/Resources/Prototypes/_DV/Actions/psionic.yml index 29e14f3617..5bd9fed95a 100644 --- a/Resources/Prototypes/_DV/Actions/psionic.yml +++ b/Resources/Prototypes/_DV/Actions/psionic.yml @@ -5,6 +5,7 @@ components: - type: Action itemIconStyle: BigAction + startDelay: true - type: entity parent: BasePsionicAction @@ -71,7 +72,7 @@ useDelay: 20 checkCanInteract: false - type: InstantAction - event: !type:MindSwapPowerReturnActionEvent + event: !type:MindSwappedReturnPowerActionEvent - type: entity parent: BasePsionicAction @@ -109,9 +110,9 @@ - type: entity parent: BasePsionicAction - id: ActionMetapsionic + id: ActionMetapsionicPulse name: Metapsionic Pulse - description: Send a mental pulse through the area to see if there are any psychics nearby. + description: Send a mental pulse through a specified area to see if there are any psychics. components: - type: Action icon: @@ -119,8 +120,11 @@ state: metapsionic useDelay: 45 checkCanInteract: false - - type: InstantAction - event: !type:MetapsionicPowerActionEvent + - type: TargetAction + checkCanAccess: false + range: -1 # You can cast it anywhere on your screen. + - type: WorldTargetAction + event: !type:MetapsionicPulsePowerActionEvent - type: entity parent: BasePsionicAction @@ -182,20 +186,6 @@ - type: InstantAction event: !type:PsionicInvisibilityPowerActionEvent -- type: entity - parent: BasePsionicAction - id: ActionPsionicInvisibilityUsed - name: Turn Off Psionic Invisibility - description: Return to visibility, and receive a stun. - components: - - type: Action - icon: - sprite: _DV/Interface/Actions/actions_psionics.rsi - state: invisibility_off - checkCanInteract: false - - type: InstantAction - event: !type:RemovePsionicInvisibilityOffPowerActionEvent - - type: entity parent: BasePsionicAction id: ActionPrecognition @@ -213,6 +203,7 @@ event: !type:PrecognitionPowerActionEvent - type: entity + parent: BasePsionicAction id: ActionPsychokineticScream name: Psychokinetic Scream description: Emit a blood-curdling scream that shatters all lights in the area. @@ -221,9 +212,10 @@ icon: { sprite: _DV/Actions/shatterlights.rsi, state: shatter-lights } useDelay: 60 - type: InstantAction - event: !type:ShatterLightsActionEvent + event: !type:PsychokineticScreamPowerActionEvent - type: entity + parent: BasePsionicAction id: ActionFracturedForm name: Swap Form description: Switch to one of your alternate forms, if you have one. This can also be done by sleeping. diff --git a/Resources/Prototypes/_DV/Actions/skia.yml b/Resources/Prototypes/_DV/Actions/skia.yml index 06fdd429fe..8406681e9f 100644 --- a/Resources/Prototypes/_DV/Actions/skia.yml +++ b/Resources/Prototypes/_DV/Actions/skia.yml @@ -1,18 +1,17 @@ - type: entity - id: ActionShatterLights - name: Shatter Lights - description: Emit a blood-curdling scream that shatters all lights in the area. + parent: ActionPsychokineticScream + id: ActionPsychokineticScreamSkia # This one has a smaller cooldown components: - type: Action icon: { sprite: _DV/Actions/shatterlights.rsi, state: shatter-lights } useDelay: 30 - type: InstantAction - event: !type:ShatterLightsActionEvent + event: !type:PsychokineticScreamPowerActionEvent - type: entity id: ActionTechnokineticPulse name: Technokinetic Pulse - description: Unleash a burst of Technokinetic energy, disabling and destroying nearby electronics. + description: Unleash a burst of Technokinetic energy, disabling or destroying nearby electronics. components: - type: Action icon: { sprite: _DV/Actions/technokineticpulse.rsi, state: technokinetic-pulse } diff --git a/Resources/Prototypes/_DV/Entities/Clothing/Head/hardsuit-helmets.yml b/Resources/Prototypes/_DV/Entities/Clothing/Head/hardsuit-helmets.yml index c99ae61647..042326443c 100644 --- a/Resources/Prototypes/_DV/Entities/Clothing/Head/hardsuit-helmets.yml +++ b/Resources/Prototypes/_DV/Entities/Clothing/Head/hardsuit-helmets.yml @@ -163,8 +163,7 @@ heldPrefix: off - type: PointLight color: red - - type: TinfoilHat - destroyOnFry: false + - type: PsionicallyInsulative - type: GuideHelp guides: - Psionics @@ -205,11 +204,9 @@ - type: PointLight color: "#ffffff" - type: Psionic - - type: TinfoilHat - passthrough: true - destroyOnFry: false - - type: ClothingGrantPsionicPower - power: DispelPower + - type: PsionicallyInsulative + allowsPsionicUsage: true + - type: DispelPower - type: GuideHelp guides: - Psionics diff --git a/Resources/Prototypes/_DV/Entities/Clothing/Psionic/psionic_clothing.yml b/Resources/Prototypes/_DV/Entities/Clothing/Psionic/psionic_clothing.yml index a0336593ae..c2fa3f7a62 100644 --- a/Resources/Prototypes/_DV/Entities/Clothing/Psionic/psionic_clothing.yml +++ b/Resources/Prototypes/_DV/Entities/Clothing/Psionic/psionic_clothing.yml @@ -21,8 +21,7 @@ - type: Clothing sprite: Clothing/Eyes/Glasses/glasses.rsi - type: VisionCorrection - - type: ClothingGrantPsionicPower - power: TelegnosisPower + - type: TelegnosisPower - type: entity id: BedsheetInvisibilityCloak @@ -36,8 +35,7 @@ state: sheetblack - type: Clothing sprite: Clothing/Neck/Bedsheets/black.rsi - - type: ClothingGrantPsionicPower - power: PsionicInvisibilityPower + - type: PsionicInvisibilityPower - type: entity parent: @@ -55,8 +53,7 @@ fiberMaterial: fibers-leather fiberColor: fibers-brown - type: FingerprintMask - - type: ClothingGrantPsionicPower - power: DispelPower + - type: DispelPower - type: entity parent: @@ -74,5 +71,4 @@ fiberMaterial: fibers-insulative fiberColor: fibers-yellow - type: FingerprintMask - - type: ClothingGrantPsionicPower - power: NoosphericZapPower + - type: NoosphericZapPower diff --git a/Resources/Prototypes/_DV/Entities/Effects/psionic.yml b/Resources/Prototypes/_DV/Entities/Effects/psionic.yml new file mode 100644 index 0000000000..31be9d0e67 --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Effects/psionic.yml @@ -0,0 +1,29 @@ +# We need to copy paste the entire thing because we don't want Electrified component on that thang. +- type: entity + name: lightning + id: PsionicLightning + categories: [ HideSpawnMenu ] + components: + - type: Sprite + sprite: /Textures/Effects/lightning.rsi + drawdepth: Effects + layers: + - state: "lightning_1" + shader: unshaded + - type: Physics + canCollide: false + - type: Lightning + - type: PointLight + enabled: true + color: "#4080FF" + radius: 3.5 + softness: 1 + autoRot: true + castShadows: false + - type: Beam + sound: /Audio/Effects/Lightning/lightningshock.ogg + - type: TimedDespawn + lifetime: 3 + - type: Tag + tags: + - HideContextMenu diff --git a/Resources/Prototypes/_DV/Entities/Effects/puddles.yml b/Resources/Prototypes/_DV/Entities/Effects/puddles.yml index 1a34855d46..5ecfa546c1 100644 --- a/Resources/Prototypes/_DV/Entities/Effects/puddles.yml +++ b/Resources/Prototypes/_DV/Entities/Effects/puddles.yml @@ -176,6 +176,8 @@ - ToxinTrash - Nausium - LotophagoiOil + - Ambrosia + - Nectar - type: entity parent: MarkerBase diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/familiars.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/familiars.yml index 5040ba22e3..7d23d16cea 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/familiars.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/familiars.yml @@ -88,10 +88,9 @@ - type: ComplexInteraction - type: RandomMetadata nameSegments: [NamesGolem] - - type: PotentialPsionic - type: Psionic - removable: false - type: PyrokinesisPower + canBeRemoved: false - type: Grammar attributes: proper: true diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/fun.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/fun.yml index 07d534c139..f515ba09a7 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/fun.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/fun.yml @@ -58,8 +58,8 @@ - type: MindExaminable - type: PotentialPsionic - type: Psionic - removable: false - type: PyrokinesisPower + canBeRemoved: false - type: NpcFactionMember factions: - Passive diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/glimmer_creatures.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/glimmer_creatures.yml index 8a2dd6b6bf..2b04099286 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/glimmer_creatures.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/glimmer_creatures.yml @@ -21,7 +21,6 @@ reagents: - ReagentId: Ectoplasm Quantity: 15 - - type: PotentialPsionic - type: Psionic - type: GlimmerSource - type: AmbientSound @@ -72,10 +71,9 @@ - type: MovementIgnoreGravity - type: Speech # powers - - type: PotentialPsionic - type: Psionic - removable: false - type: NoosphericZapPower + canBeRemoved: false - type: LifeDrainer damage: types: diff --git a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml index 7bd86ae3ab..7ef1c710f6 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/NPCs/skia.yml @@ -102,10 +102,11 @@ resurrectDesc: skia-resurrecting-desc - type: Bloodstream maxBleedAmount: 0 - - type: ShatterLightsAbility - lineOfSight: true + - type: PsychokineticScreamPower radius: 7.0 penetratingRadius: 2.0 + actionProtoId: ActionPsychokineticScreamSkia + canBeRemoved: false - type: TechnokineticPulseAbility range: 5.0 energyConsumption: 20000 diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Player/kitsune.yml b/Resources/Prototypes/_DV/Entities/Mobs/Player/kitsune.yml index a848cf217a..3929c4626b 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Player/kitsune.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Player/kitsune.yml @@ -13,3 +13,5 @@ damage: types: Heat: 5 + - type: PotentialPsionic + speciesBonusChance: 0.05 # Additional 5% chance to roll a psionic power. diff --git a/Resources/Prototypes/_DV/Entities/Mobs/Species/kitsune.yml b/Resources/Prototypes/_DV/Entities/Mobs/Species/kitsune.yml index 3d71dcffc5..8255939a30 100644 --- a/Resources/Prototypes/_DV/Entities/Mobs/Species/kitsune.yml +++ b/Resources/Prototypes/_DV/Entities/Mobs/Species/kitsune.yml @@ -37,8 +37,6 @@ interactSuccessSound: path: /Audio/Animals/fox_squeak.ogg - type: Kitsune - - type: PsionicBonusChance - flatBonus: 0.5 - type: LightweightDrunk boozeStrengthMultiplier: 3 - type: PseudoItem diff --git a/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/pills.yml b/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/pills.yml new file mode 100644 index 0000000000..6d9adb204f --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/Objects/Specific/Medical/pills.yml @@ -0,0 +1,13 @@ +- type: entity + name: sanctus sal + parent: Pill + id: PillSanctusSal + description: A long-lasting drug which prevents and shields from psionic usage. + components: + - type: SolutionContainerManager + solutions: + food: + maxVol: 20 + reagents: + - ReagentId: SanctusSal + Quantity: 20 diff --git a/Resources/Prototypes/_DV/Entities/StatusEffects/movement.yml b/Resources/Prototypes/_DV/Entities/StatusEffects/movement.yml deleted file mode 100644 index 5718d99b8a..0000000000 --- a/Resources/Prototypes/_DV/Entities/StatusEffects/movement.yml +++ /dev/null @@ -1,4 +0,0 @@ -- type: entity - parent: StatusEffectSlowdown - id: PsionicSlowdownStatusEffect - name: affected by slowdown from your powers diff --git a/Resources/Prototypes/_DV/Entities/StatusEffects/psionics.yml b/Resources/Prototypes/_DV/Entities/StatusEffects/psionics.yml new file mode 100644 index 0000000000..eb003d1a3d --- /dev/null +++ b/Resources/Prototypes/_DV/Entities/StatusEffects/psionics.yml @@ -0,0 +1,35 @@ +# Prevents the affected entity from using psionic powers. +- type: entity + parent: MobStatusEffectDebuff + id: StatusEffectPsionicsDisabled + name: psionics disabled + components: + - type: PsionicsDisabled + +# Shields the affected entity from external psionic powers. +- type: entity + parent: MobStatusEffectBase + id: StatusEffectShieldedFromPsionics + name: shielded from psionics + components: + - type: ShieldedFromPsionics + +# Causes the entity to repeat what is said in telepathic chat. +- type: entity + parent: MobStatusEffectDebuff + id: StatusEffectTelepathicRepeater + name: telepathic repeater + components: + - type: TelepathicRepeater + +# Slows down the user when using the precognition ability. +- type: entity + parent: [ StatusEffectSlowdown, MobStatusEffectDebuff ] + id: PrecognitionSlowdownStatusEffect + name: slowed by precognition power + +# Slows down the user when using the mass sleep ability. +- type: entity + parent: [ StatusEffectSlowdown, MobStatusEffectDebuff ] + id: MassSleepSlowdownStatusEffect + name: slowed by mass sleep power diff --git a/Resources/Prototypes/_DV/GameRules/glimmer_events.yml b/Resources/Prototypes/_DV/GameRules/glimmer_events.yml index 8120eb75e2..10c49da5ac 100644 --- a/Resources/Prototypes/_DV/GameRules/glimmer_events.yml +++ b/Resources/Prototypes/_DV/GameRules/glimmer_events.yml @@ -5,7 +5,7 @@ - id: MundaneDischarge - id: NoosphericZap - id: NoosphericFry - - id: PsionicCatGotYourTongue + - id: NoosphericSilence - id: MassMindSwap - id: GlimmerWispSpawn - id: FreeProber @@ -71,7 +71,7 @@ - type: NoosphericFryRule - type: entity - id: PsionicCatGotYourTongue + id: NoosphericSilence parent: BaseGlimmerEvent components: - type: GlimmerEvent @@ -79,7 +79,7 @@ maximumGlimmer: 900 glimmerBurnLower: 18 glimmerBurnUpper: 40 - - type: PsionicCatGotYourTongueRule + - type: NoosphericSilenceRule - type: entity id: MassMindSwap diff --git a/Resources/Prototypes/_DV/Psionics/psionicPowers.yml b/Resources/Prototypes/_DV/Psionics/psionicPowers.yml new file mode 100644 index 0000000000..98474df482 --- /dev/null +++ b/Resources/Prototypes/_DV/Psionics/psionicPowers.yml @@ -0,0 +1,94 @@ +# Table +- type: entityTable + id: PsionicPowerTable + table: !type:GroupSelector + children: + - id: DispelPowerEntity + weight: 1 + - id: PrecognitionPowerEntity + weight: 1 + - id: PsionicRegenerationPowerEntity + weight: 1 + - id: PsychokineticScreamPowerEntity + weight: 1 + - id: TelegnosisPowerEntity + weight: 1 + - id: FracturedFormPowerEntity + weight: 0.3 + - id: MassSleepPowerEntity + weight: 0.3 + - id: MetapsionicPulsePowerEntity + weight: 0.5 + - id: NoosphericZapPowerEntity + weight: 0.3 + - id: MindSwapPowerEntity + weight: 0.15 + - id: PsionicEruptionPowerEntity + weight: 0.1 + +# Powers +- type: entity + id: DispelPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: DispelPower + +- type: entity + id: MetapsionicPulsePowerEntity + categories: [ HideSpawnMenu ] + components: + - type: MetapsionicPulsePower + +- type: entity + id: PsionicEruptionPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: PsionicEruptionPower + +- type: entity + id: FracturedFormPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: FracturedFormPower + +- type: entity + id: PsychokineticScreamPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: PsychokineticScreamPower + +- type: entity + id: PrecognitionPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: PrecognitionPower + +- type: entity + id: MassSleepPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: MassSleepPower + +- type: entity + id: NoosphericZapPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: NoosphericZapPower + +- type: entity + id: MindSwapPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: MindSwapPower + +- type: entity + id: PsionicRegenerationPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: PsionicRegenerationPower + +- type: entity + id: TelegnosisPowerEntity + categories: [ HideSpawnMenu ] + components: + - type: TelegnosisPower diff --git a/Resources/Prototypes/_DV/Reagents/biological.yml b/Resources/Prototypes/_DV/Reagents/biological.yml index b45bf8a435..a693a9804d 100644 --- a/Resources/Prototypes/_DV/Reagents/biological.yml +++ b/Resources/Prototypes/_DV/Reagents/biological.yml @@ -3,3 +3,39 @@ parent: GreyMatter name: reagent-name-sentient-grey-matter desc: reagent-desc-sentient-grey-matter + +- type: reagent + id: Nectar + name: reagent-name-nectar + group: Biological + desc: reagent-desc-nectar + flavor: enthralling + color: "#FFBF00" + physicalDesc: reagent-physical-desc-refreshing + metabolisms: + Drink: + effects: + - !type:ModifyStatusEffect + effectProto: StatusEffectShieldedFromPsionics + type: Add + time: 20 + - !type:SatiateThirst + factor: 10 # A drink for the Gods after all. + +- type: reagent + id: Ambrosia + name: reagent-name-ambrosia + group: Biological + desc: reagent-desc-ambrosia + flavor: enthralling + color: "#FFBF00" + physicalDesc: reagent-physical-desc-thick + metabolisms: + Narcotic: + effects: + - !type:Jitter + - !type:MovementSpeedModifier + walkSpeedModifier: 1.3 + sprintSpeedModifier: 1.3 + - !type:SatiateHunger + factor: 10 # A food for the Gods after all. diff --git a/Resources/Prototypes/_DV/Reagents/medicine.yml b/Resources/Prototypes/_DV/Reagents/medicine.yml index 56f69c365c..216a33b036 100644 --- a/Resources/Prototypes/_DV/Reagents/medicine.yml +++ b/Resources/Prototypes/_DV/Reagents/medicine.yml @@ -129,3 +129,24 @@ - !type:TemperatureCondition min: 293.15 amount: -10000 + +- type: reagent + id: SanctusSal + name: reagent-name-sanctussal + group: Medicine + desc: reagent-desc-sanctussal + physicalDesc: reagent-physical-desc-salty + flavor: medicine + color: "#91C3F7" + metabolisms: + Medicine: + metabolismRate: 0.5 + effects: + - !type:ModifyStatusEffect + effectProto: StatusEffectShieldedFromPsionics + type: Add + time: 60 + - !type:ModifyStatusEffect + effectProto: StatusEffectPsionicsDisabled + type: Add + time: 60 diff --git a/Resources/Prototypes/_DV/Reagents/narcotics.yml b/Resources/Prototypes/_DV/Reagents/narcotics.yml new file mode 100644 index 0000000000..4f88ab9f3c --- /dev/null +++ b/Resources/Prototypes/_DV/Reagents/narcotics.yml @@ -0,0 +1,28 @@ +- type: reagent + id: LotophagoiOil + name: reagent-name-lotophagoi-oil + group: Narcotics + desc: reagent-desc-lotophagoi-oil + flavor: enthralling + color: "#FFBF00" + physicalDesc: reagent-physical-desc-overpowering + metabolisms: + Narcotic: + metabolismRate: 0.2 + effects: + - !type:ModifyStatusEffect + effectProto: StatusEffectSeeingRainbow + type: Add + time: 5 + - !type:ModifyStatusEffect + effectProto: StatusEffectScrambled + type: Update + - !type:ModifyStatusEffect + effectProto: StatusEffectTelepathicRepeater + type: Add + - !type:RollPsionicAbility + bonusMultiplier: 4 + conditions: + - !type:ReagentCondition + reagent: LotophagoiOil + min: 20 diff --git a/Resources/Prototypes/_DV/Reagents/toxins.yml b/Resources/Prototypes/_DV/Reagents/toxins.yml new file mode 100644 index 0000000000..969dfde5e4 --- /dev/null +++ b/Resources/Prototypes/_DV/Reagents/toxins.yml @@ -0,0 +1,14 @@ +- type: reagent + id: Lernaea + name: reagent-name-lernaea + group: Toxins + desc: reagent-desc-lernaea + color: "#2E3E64" + physicalDesc: reagent-physical-desc-sickly + metabolisms: + Poison: + effects: + - !type:ModifyStatusEffect + effectProto: StatusEffectPsionicsDisabled + type: Add + time: 5 diff --git a/Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml b/Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml index 7926f41977..0129bf909d 100644 --- a/Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml +++ b/Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml @@ -71,3 +71,13 @@ amount: 1 products: Photoxadone: 3 + +- type: reaction + id: SanctusSal + reactants: + Holywater: + amount: 4 + TableSalt: + amount: 1 + products: + SanctusSal: 5 diff --git a/Resources/Prototypes/_DV/Roles/Jobs/Justice/chief_justice.yml b/Resources/Prototypes/_DV/Roles/Jobs/Justice/chief_justice.yml index d8ca590424..b831289f9b 100644 --- a/Resources/Prototypes/_DV/Roles/Jobs/Justice/chief_justice.yml +++ b/Resources/Prototypes/_DV/Roles/Jobs/Justice/chief_justice.yml @@ -38,10 +38,8 @@ - !type:AddComponentSpecial components: - type: CommandStaff - - !type:AddComponentSpecial - components: - - type: PsionicBonusChance #Nyano - Summary: makes it more likely to become psionic. - flatBonus: 0.025 + - !type:ModifyPsionicChanceSpecial # Command Roles are more likely to be psionic. + jobBonusChance: 0.05 # Additional 5% chance. - type: startingGear id: CJGear diff --git a/Resources/Prototypes/_DV/XenoArch/effects.yml b/Resources/Prototypes/_DV/XenoArch/effects.yml index 742596bb8c..d407b2618c 100644 --- a/Resources/Prototypes/_DV/XenoArch/effects.yml +++ b/Resources/Prototypes/_DV/XenoArch/effects.yml @@ -20,7 +20,7 @@ - type: XAEDetails specificTip: xenoarch-effect-tip-specific-glimmer vagueTip: xenoarch-effect-tip-vague-energy-manipulation - - type: GlimmerArtifact + - type: XAEModifyGlimmer range: min: 0 max: 450 # cant instantly get ended by a looping artifact @@ -31,7 +31,7 @@ id: XenoArtifactRaiseGlimmerLarge categories: [ HideSpawnMenu ] components: - - type: GlimmerArtifact + - type: XAEModifyGlimmer range: min: 0 max: 700 # cant get to 1000 from a looping artifact @@ -42,7 +42,7 @@ id: XenoArtifactLowerGlimmer categories: [ HideSpawnMenu ] components: - - type: GlimmerArtifact + - type: XAEModifyGlimmer range: min: 400 # for recovering from serious glimmer emergencies, not a full drain replacement max: 1000 @@ -56,5 +56,5 @@ - type: XAEDetails specificTip: xenoarch-effect-tip-specific-psionic vagueTip: xenoarch-effect-tip-vague-energy-manipulation - - type: PsionicProducingArtifact + - type: XAEPsionicInducer range: 5 diff --git a/Resources/Prototypes/_DV/XenoArch/triggers.yml b/Resources/Prototypes/_DV/XenoArch/triggers.yml index e286c74a22..293ec9150d 100644 --- a/Resources/Prototypes/_DV/XenoArch/triggers.yml +++ b/Resources/Prototypes/_DV/XenoArch/triggers.yml @@ -1,8 +1,7 @@ - type: xenoArchTrigger - id: TriggerMetapsionic + id: TriggerPsionicUsage tip: xenoarch-trigger-tip-psionic components: - - type: ArtifactMetapsionicTrigger - - type: MetapsionicPower # so the trigger receives the event from people using psionics + - type: XATPsionicUsage # TODO: when golemization is ported, add a trigger for golemizing someone, target 7 or something