Psionic Refactor (#4788)
* 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 <velcroboy333@hotmail.com> * 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 <velcroboy333@hotmail.com> * Automatic changelog update * And another one! * Fix yaml linter * Fix mapping section in CONTRIBUTING.md (#4894) Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * 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 <mdavid.wininger@gmail.com> --------- Signed-off-by: Halo3moth <mdavid.wininger@gmail.com> * Automatic changelog update * Update Credits (#4897) Co-authored-by: DeltaV-Bot <github@deltav.gay> * 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 <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/Xenoarchaeology/Equipment/Components/AnalysisConsoleComponent.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/Content.Shared.csproj Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleBoundUserInterface.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Shared/_DV/Xenoarchaeology/BUI/AnalysisConsoleBoundUserInterfaceState.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Resources/Locale/en-US/xenoarchaeology/artifact-analyzer.ftl Co-authored-by: Charlie Morley <cmorley191@gmail.com> 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 <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Server/Xenoarchaeology/Equipment/ArtifactAnalyzerSystem.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> * Update Content.Client/Xenoarchaeology/Ui/AnalysisConsoleMenu.xaml.cs Co-authored-by: Charlie Morley <cmorley191@gmail.com> 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 <cmorley191@gmail.com> * 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 <velcroboy333@hotmail.com> * Automatic changelog update * It's a Tortuga Xmas! (#4893) * It's a Tortuga Xmas! * ... --------- Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * Automatic changelog update * Reinforced Tinted Windows (#4875) * completion * fix * moved directories * fixed * Apply suggestion from @Toby222 Signed-off-by: Tobias Berger <toby@tobot.dev> --------- Signed-off-by: Tobias Berger <toby@tobot.dev> Co-authored-by: Tobias Berger <toby@tobot.dev> * 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 <toby@tobot.dev> 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 <toby@tobot.dev> * 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 <cmorley191@gmail.com> * Update Content.Shared/Xenoarchaeology/Artifact/SharedXenoArtifactSystem.Unlock.cs Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Charlie Morley <cmorley191@gmail.com> --------- Signed-off-by: Charlie Morley <cmorley191@gmail.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 * 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 <toby@tobot.dev> Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> * Apply suggestion from @Toby222 Co-authored-by: Tobias Berger <toby@tobot.dev> 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 <toby@tobot.dev> * 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 <toby@tobot.dev> * 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 <noreuhh@gmail.com> * 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 <iaada@users.noreply.github.com> * 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 <ss.adasts@gmail.com> * new line at end of file * duplicated --------- Co-authored-by: āda <ss.adasts@gmail.com> * Only run StationPowerTests over stations in DeltaV * Five bounty arbitrage fixes (#41846) * commit * tweak * more bounties --------- Co-authored-by: iaada <iaada@users.noreply.github.com> * 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 <jrpl101998@gmail.com> * 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 <toby@tobot.dev> * 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 <darmentici@gmail.com> * Add files via upload Minor tweak to vending machine YAML for tank dispenser recolor Signed-off-by: Thingy461 <darmentici@gmail.com> * [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 <darmentici@gmail.com> 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 <toby@tobot.dev> 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 <toby@tobot.dev> Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> * Update HealthAnalyzerSystem.cs Co-authored-by: Tobias Berger <toby@tobot.dev> Signed-off-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> * Update HealthAnalyzerSystem.cs Co-authored-by: Tobias Berger <toby@tobot.dev> 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 <github@deltav.gay> * 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 <toby@tobot.dev> Signed-off-by: Madison Rye Progress <makyo@drab-makyo.com> --------- Signed-off-by: Madison Rye Progress <makyo@drab-makyo.com> Co-authored-by: Tobias Berger <toby@tobot.dev> * 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 <toby@tobot.dev> 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 <toby@tobot.dev> 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 <toby@tobot.dev> Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> --------- Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy <velcroboy333@hotmail.com> Co-authored-by: Tobias Berger <toby@tobot.dev> * 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 <vanessalouwagie@gmail.com> * 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 <github@deltav.gay> * 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 <velcroboy333@hotmail.com> * 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 <github@deltav.gay> * 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 commit905c5cac07. 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 <vanessalouwagie@gmail.com> * 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 <velcroboy333@hotmail.com> * Automatic changelog update * Fixes to suffix (#5353) * Reverts Arena weapon spawner change (#5322) Reverts change from prob to weight Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * Removed a dead pixel on a book sprite (#5362) * Update Credits (#5413) Co-authored-by: DeltaV-Bot <github@deltav.gay> * 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 <iamabanana372456@gmail.com> * woof * Update Resources/Prototypes/_DV/Recipes/Reactions/medicine.yml Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: keekee38 <iamabanana372456@gmail.com> * Update Resources/Prototypes/_DV/Reagents/medicine.yml Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: keekee38 <iamabanana372456@gmail.com> * [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 <iamabanana372456@gmail.com> * did the /> Signed-off-by: keekee38 <iamabanana372456@gmail.com> --------- Signed-off-by: keekee38 <iamabanana372456@gmail.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 * 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 <iamabanana372456@gmail.com> * Fix comment Signed-off-by: Stop-Signs <stopsign221@gmail.com> --------- Signed-off-by: keekee38 <iamabanana372456@gmail.com> Signed-off-by: Stop-Signs <stopsign221@gmail.com> Co-authored-by: Stop-Signs <stopsign221@gmail.com> * 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 <toby@tobot.dev> Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> * Update Content.Server/_DV/Speech/EntitySystems/SlavicAccentSystem.cs Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> 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 <velcroboy333@hotmail.com> * Automatic changelog update * Reclaimer MK2 hotfix (#5440) Fixes #5428 * Map Edits: Arena (#5445) * Map Edits: Arena * AI router --------- Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * 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 <toby@tobot.dev> 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 <toby@tobot.dev> * 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 <github@deltav.gay> * 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 commit444d9a4921. * 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 <velcroboy333@hotmail.com> * 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 <barrynorfolkman@protonmail.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * add attributions 2 Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> * add attributions 3 Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> 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 <barrynorfolkman@protonmail.com> --------- Signed-off-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Signed-off-by: BarryNorfolk <barrynorfolkman@protonmail.com> Co-authored-by: Vanessa <908648+ShepardToTheStars@users.noreply.github.com> Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> * 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 <mdavid.wininger@gmail.com> --------- Signed-off-by: Halo3moth <mdavid.wininger@gmail.com> * Automatic changelog update * Map Edits: Tortuga (#5437) * Map Edits: Tortuga * Additional changes * Passive vents * oh yeah * review fixes --------- Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * Automatic changelog update * Submarine Fixes (#5394) MASS UPDATE * Automatic changelog update * Map Edits: Evac Shuttles (#5457) Co-authored-by: Velcroboy <velcroboy333@hotmail.com> * 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 <darmentici@gmail.com> 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 <makyo@drab-makyo.com> Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Signed-off-by: keekee38 <iamabanana372456@gmail.com> Signed-off-by: Stop-Signs <stopsign221@gmail.com> 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 <barrynorfolkman@protonmail.com> Signed-off-by: Halo3moth <mdavid.wininger@gmail.com> Co-authored-by: Samuka <47865393+Samuka-C@users.noreply.github.com> Co-authored-by: SnappingOpossum <snappingopossum@outlook.com> Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> 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 <me@steffo.eu> Co-authored-by: Connor Huffine <chuffine@gmail.com> Co-authored-by: YoungThug <ramialanbagy@gmail.com> 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 <noreuhh@gmail.com> Co-authored-by: JackspajfMain <105893899+JackspajfMain@users.noreply.github.com> Co-authored-by: Minemoder5000 <minemoder50000@gmail.com> Co-authored-by: āda <ss.adasts@gmail.com> Co-authored-by: iaada <iaada@users.noreply.github.com> 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 <ignaz.k@live.de> Co-authored-by: Noah Beverly <noahbeverly@gmail.com> Co-authored-by: Justin Pfeifler <jrpl101998@gmail.com> Co-authored-by: Milon <milonpl.git@proton.me> Co-authored-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Co-authored-by: Halo3moth <mdavid.wininger@gmail.com> 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 <toby@tobot.dev> Co-authored-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Co-authored-by: Thingy461 <darmentici@gmail.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> Co-authored-by: ConstantlyConfused <67894661+Eternally-Confused@users.noreply.github.com> Co-authored-by: Theo <rebe83a@gmail.com> 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 <github@deltav.gay> Co-authored-by: verybigman <171044200+verybigman311@users.noreply.github.com> Co-authored-by: keekee38 <iamabanana372456@gmail.com> Co-authored-by: Madison Rye Progress <makyo@drab-makyo.com> Co-authored-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy <velcroboy333@hotmail.com> Co-authored-by: Vanessa <vanessalouwagie@gmail.com> Co-authored-by: This_Is_Gray <167829864+IAmNotGray@users.noreply.github.com> Co-authored-by: marigauge <thesaiyandude3@gmail.com> Co-authored-by: MD360 <74264906+MantasDab360@users.noreply.github.com> Co-authored-by: Katie <Katiethekater@gmail.com> Co-authored-by: turtlemutt <wolfybriggan575@gmail.com> Co-authored-by: Hamey <jhammett3229@gmail.com> 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 <stopsign221@gmail.com> Co-authored-by: Stxcking <217132821+Stxcking@users.noreply.github.com> Co-authored-by: DisposableCrewmember42 <disposablecrewmember42@proton.me> Co-authored-by: KOTOB <59124164+kotobdev@users.noreply.github.com> Co-authored-by: AeraAulin <133451603+AeraAuling@users.noreply.github.com> Co-authored-by: Cepelinas1 <kakelis01@gmail.com> 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 <webrules11@yahoo.com> * Revert "One refactor to rule them all (#15)" This reverts commitb2a2613ad7. * 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 <mdavid.wininger@gmail.com> Signed-off-by: SirSmith148 <64059804+SirSmith148@users.noreply.github.com> Signed-off-by: Tobias Berger <toby@tobot.dev> 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 <cmorley191@gmail.com> Signed-off-by: zelezniciar1 <39102800+zelezniciar1@users.noreply.github.com> Signed-off-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Signed-off-by: Thingy461 <darmentici@gmail.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 <makyo@drab-makyo.com> Signed-off-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Signed-off-by: keekee38 <iamabanana372456@gmail.com> Signed-off-by: Stop-Signs <stopsign221@gmail.com> 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 <barrynorfolkman@protonmail.com> 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 <wolfybriggan575@gmail.com> 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 <webrules11@yahoo.com> Co-authored-by: biddygelson <4606269+Calis007@users.noreply.github.com> Co-authored-by: Velcroboy <107660393+IamVelcroboy@users.noreply.github.com> Co-authored-by: Velcroboy <velcroboy333@hotmail.com> 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 <mdavid.wininger@gmail.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: DeltaV-Bot <github@deltav.gay> Co-authored-by: Stxcking <tamthaotran5@outlook.com> Co-authored-by: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Co-authored-by: Cepelinas1 <kakelis01@gmail.com> 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 <cmorley191@gmail.com> 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 <toby@tobot.dev> 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 <snappingopossum@outlook.com> Co-authored-by: BarryNorfolk <barrynorfolkman@protonmail.com> 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 <me@steffo.eu> Co-authored-by: Connor Huffine <chuffine@gmail.com> Co-authored-by: YoungThug <ramialanbagy@gmail.com> 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 <noreuhh@gmail.com> Co-authored-by: JackspajfMain <105893899+JackspajfMain@users.noreply.github.com> Co-authored-by: Minemoder5000 <minemoder50000@gmail.com> Co-authored-by: āda <ss.adasts@gmail.com> Co-authored-by: iaada <iaada@users.noreply.github.com> 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 <ignaz.k@live.de> Co-authored-by: Noah Beverly <noahbeverly@gmail.com> Co-authored-by: Justin Pfeifler <jrpl101998@gmail.com> Co-authored-by: Milon <milonpl.git@proton.me> Co-authored-by: Astra <226853568+EmberAstra@users.noreply.github.com> Co-authored-by: Pharaz4 <143268074+Pharaz4@users.noreply.github.com> Co-authored-by: Thingy461 <darmentici@gmail.com> Co-authored-by: Theo <rebe83a@gmail.com> Co-authored-by: PureBreadBagel <146181383+PureBreadBagel@users.noreply.github.com> Co-authored-by: keekee38 <iamabanana372456@gmail.com> Co-authored-by: Madison Rye Progress <makyo@drab-makyo.com> Co-authored-by: Vanessa <vanessalouwagie@gmail.com> Co-authored-by: marigauge <thesaiyandude3@gmail.com> Co-authored-by: Katie <Katiethekater@gmail.com> Co-authored-by: Hamey <jhammett3229@gmail.com> Co-authored-by: Woljif <243318409+Woljif@users.noreply.github.com> Co-authored-by: Stop-Signs <stopsign221@gmail.com> Co-authored-by: Stxcking <217132821+Stxcking@users.noreply.github.com> Co-authored-by: DisposableCrewmember42 <disposablecrewmember42@proton.me> 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>
This commit is contained in:
parent
d65cdfff90
commit
d81024d883
|
|
@ -1,5 +0,0 @@
|
|||
using Content.Shared.Abilities.Psionics;
|
||||
|
||||
namespace Content.Client.Abilities.Psionics;
|
||||
|
||||
public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem;
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<IClyde>().RequestWindowAttention();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
|
||||
public override void Closed()
|
||||
{
|
||||
_window.Close();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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"),
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
using Content.Shared._DV.Abilities.Psionics;
|
||||
|
||||
namespace Content.Client._DV.Abilities.Psionics;
|
||||
|
||||
public sealed class FracturedFormPowerSystem : SharedFracturedFormPowerSystem;
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
namespace Content.Client._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
/// <summary>
|
||||
/// This solely exist for client-side prediction.
|
||||
/// </summary>
|
||||
public sealed class DispelPowerSystem : SharedDispelPowerSystem;
|
||||
|
|
@ -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;
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This solely exists for prediction.
|
||||
/// </summary>
|
||||
public sealed class MassSleepPowerSystem : SharedMassSleepPowerSystem
|
||||
{
|
||||
protected override void OnPowerUsed(Entity<MassSleepPowerComponent> psionic, ref MassSleepPowerActionEvent args)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
namespace Content.Client._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
/// <summary>
|
||||
/// This solely exists for prediction.
|
||||
/// </summary>
|
||||
public sealed class MindSwapPowerSystem : SharedMindSwapPowerSystem;
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This is solely for prediction.
|
||||
/// </summary>
|
||||
public sealed class NoosphericZapPowerSystem : SharedNoosphericZapPowerSystem
|
||||
{
|
||||
protected override void OnPowerUsed(Entity<NoosphericZapPowerComponent> psionic, ref NoosphericZapPowerActionEvent args)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
using Content.Shared._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
namespace Content.Client._DV.Psionics.Systems.PsionicPowers;
|
||||
|
||||
/// <summary>
|
||||
/// This solely exists for prediction.
|
||||
/// </summary>
|
||||
public sealed class PrecognitionPowerSystem : SharedPrecognitionPowerSystem;
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This exists solely for prediction.
|
||||
/// </summary>
|
||||
public sealed class PyrokinesisPowerSystem : SharedPyrokinesisPowerSystem
|
||||
{
|
||||
protected override void OnPowerUsed(Entity<PyrokinesisPowerComponent> psionic, ref Shared._DV.Psionics.Events.PowerActionEvents.PyrokinesisPowerActionEvent args)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This is here solely for predictive handling of using the power and not being able to.
|
||||
/// It'll send popups this way.
|
||||
/// </summary>
|
||||
public sealed class TelegnosisPowerSystem : SharedTelegnosisPowerSystem
|
||||
{
|
||||
protected override void OnPowerUsed(Entity<TelegnosisPowerComponent> psionic, ref TelegnosisPowerActionEvent args)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
using Content.Shared._DV.Psionics.Systems;
|
||||
|
||||
namespace Content.Client._DV.Psionics.Systems;
|
||||
|
||||
public sealed class PsionicSystem : SharedPsionicSystem;
|
||||
|
|
@ -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<IClyde>().RequestWindowAttention();
|
||||
_window.OpenCentered();
|
||||
}
|
||||
public override void Closed()
|
||||
{
|
||||
_window.Close();
|
||||
}
|
||||
}
|
||||
|
|
@ -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"), })
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AnomalyComponent, DispelledEvent>(OnDispelled);
|
||||
}
|
||||
|
||||
//Nyano - Summary: gives dispellable behavior to Anomalies.
|
||||
private void OnDispelled(Entity<AnomalyComponent> ent, ref DispelledEvent args)
|
||||
{
|
||||
if (HasComp<CosmicCultExamineComponent>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<AnomalyComponent> anomaly, ref MapInitEvent args)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<DispelPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<DispelPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<DispelPowerActionEvent>(OnPowerUsed);
|
||||
|
||||
SubscribeLocalEvent<DispellableComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<DamageOnDispelComponent, DispelledEvent>(OnDmgDispelled);
|
||||
// Upstream stuff we're just gonna handle here
|
||||
SubscribeLocalEvent<GuardianComponent, DispelledEvent>(OnGuardianDispelled);
|
||||
SubscribeLocalEvent<FamiliarComponent, DispelledEvent>(OnFamiliarDispelled);
|
||||
SubscribeLocalEvent<RevenantComponent, DispelledEvent>(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<PsionicComponent>(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<PsionicComponent>(uid, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerUsed(DispelPowerActionEvent args)
|
||||
{
|
||||
if (HasComp<PsionicInsulationComponent>(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<GuardianHostComponent>(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<SummonableRespawningComponent>(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 {}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -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<MetapsionicPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<MetapsionicPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<MetapsionicPowerComponent, MetapsionicPowerActionEvent>(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<PsionicComponent>(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<PsionicComponent>(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<PsionicComponent>(entity) && entity != uid && !HasComp<PsionicInsulationComponent>(entity) &&
|
||||
!(HasComp<ClothingGrantPsionicPowerComponent>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<MindSwapPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<MindSwapPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<MindSwapPowerActionEvent>(OnPowerUsed);
|
||||
SubscribeLocalEvent<MindSwappedComponent, MindSwapPowerReturnActionEvent>(OnPowerReturned);
|
||||
SubscribeLocalEvent<MindSwappedComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<MindSwappedComponent, MobStateChangedEvent>(OnMobStateChanged);
|
||||
SubscribeLocalEvent<GhostAttemptHandleEvent>(OnGhostAttempt);
|
||||
//
|
||||
SubscribeLocalEvent<MindSwappedComponent, ComponentInit>(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<PsionicComponent>(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<PsionicComponent>(uid, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerUsed(MindSwapPowerActionEvent args)
|
||||
{
|
||||
if (!(TryComp<DamageableComponent>(args.Target, out var damageable) && damageable.DamageContainerID == "Biological"))
|
||||
return;
|
||||
|
||||
if (HasComp<PsionicInsulationComponent>(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<PsionicInsulationComponent>(component.OriginalEntity) || HasComp<PsionicInsulationComponent>(uid))
|
||||
return;
|
||||
|
||||
if (HasComp<MobStateComponent>(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<MindSwappedComponent>(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<MobStateComponent>(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<MindSwappedComponent>(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<MindSwappedComponent>(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<PsionicComponent>(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<MindSwappedComponent>(performer) || !HasComp<MindSwappedComponent>(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<MindSwappedComponent>(performer);
|
||||
var targetMindPowerComp = Comp<MindSwappedComponent>(target);
|
||||
_actions.RemoveAction(performer, performerMindPowerComp.MindSwapReturnActionEntity);
|
||||
_actions.RemoveAction(target, targetMindPowerComp.MindSwapReturnActionEntity);
|
||||
|
||||
RemComp<MindSwappedComponent>(performer);
|
||||
RemComp<MindSwappedComponent>(target);
|
||||
return;
|
||||
}
|
||||
|
||||
var perfComp = EnsureComp<MindSwappedComponent>(performer);
|
||||
var targetComp = EnsureComp<MindSwappedComponent>(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<MindSwappedComponent>(uid);
|
||||
_actions.RemoveAction(perfComp.MindSwapReturnActionEntity);
|
||||
|
||||
if (HasComp<TelegnosticProjectionComponent>(uid))
|
||||
{
|
||||
RemComp<PsionicallyInvisibleComponent>(uid);
|
||||
RemComp<StealthComponent>(uid);
|
||||
EnsureComp<SpeechComponent>(uid);
|
||||
EnsureComp<DispellableComponent>(uid);
|
||||
_metaDataSystem.SetEntityName(uid, Loc.GetString("telegnostic-trapped-entity-name"));
|
||||
_metaDataSystem.SetEntityDescription(uid, Loc.GetString("telegnostic-trapped-entity-desc"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<NoosphericZapPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<NoosphericZapPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<NoosphericZapPowerActionEvent>(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<PsionicComponent>(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<PsionicComponent>(uid, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerUsed(NoosphericZapPowerActionEvent args)
|
||||
{
|
||||
if (!HasComp<PotentialPsionicComponent>(args.Target))
|
||||
return;
|
||||
|
||||
if (HasComp<PsionicInsulationComponent>(args.Target))
|
||||
return;
|
||||
|
||||
_lightning.ShootLightning(args.Performer, args.Target);
|
||||
|
||||
_psionics.LogPowerUsed(args.Performer, "noospheric zap");
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicInvisibilityPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicInvisibilityPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PsionicInvisibilityPowerComponent, PsionicInvisibilityPowerActionEvent>(OnPowerUsed);
|
||||
SubscribeLocalEvent<RemovePsionicInvisibilityOffPowerActionEvent>(OnPowerOff);
|
||||
SubscribeLocalEvent<PsionicInvisibilityUsedComponent, ComponentInit>(OnStart);
|
||||
SubscribeLocalEvent<PsionicInvisibilityUsedComponent, ComponentShutdown>(OnEnd);
|
||||
SubscribeLocalEvent<PsionicInvisibilityUsedComponent, DamageChangedEvent>(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<PsionicComponent>(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<PsionicComponent>(uid, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerUsed(EntityUid uid, PsionicInvisibilityPowerComponent component, PsionicInvisibilityPowerActionEvent args)
|
||||
{
|
||||
if (HasComp<PsionicInvisibilityUsedComponent>(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<PsionicInvisibilityUsedComponent>(args.Performer))
|
||||
return;
|
||||
|
||||
ToggleInvisibility(args.Performer);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnStart(EntityUid uid, PsionicInvisibilityUsedComponent component, ComponentInit args)
|
||||
{
|
||||
EnsureComp<PsionicallyInvisibleComponent>(uid);
|
||||
EnsureComp<PacifiedComponent>(uid);
|
||||
var stealth = EnsureComp<StealthComponent>(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<PsionicallyInvisibleComponent>(uid);
|
||||
RemComp<PacifiedComponent>(uid);
|
||||
RemComp<StealthComponent>(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<PsionicInvisibilityUsedComponent>(uid))
|
||||
{
|
||||
EnsureComp<PsionicInvisibilityUsedComponent>(uid);
|
||||
} else
|
||||
{
|
||||
RemComp<PsionicInvisibilityUsedComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicRegenerationPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicRegenerationPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PsionicRegenerationPowerComponent, PsionicRegenerationPowerActionEvent>(OnPowerUsed);
|
||||
|
||||
SubscribeLocalEvent<PsionicRegenerationPowerComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<PsionicRegenerationPowerComponent, PsionicRegenerationDoAfterEvent>(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<PsionicComponent>(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<PsionicComponent>(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<BloodstreamComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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<PyrokinesisPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PyrokinesisPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PyrokinesisPowerActionEvent>(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<PsionicComponent>(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<PsionicComponent>(uid, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPowerUsed(PyrokinesisPowerActionEvent args)
|
||||
{
|
||||
if (!TryComp<FlammableComponent>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TelegnosisPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<TelegnosisPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<TelegnosisPowerComponent, TelegnosisPowerActionEvent>(OnPowerUsed);
|
||||
SubscribeLocalEvent<TelegnosticProjectionComponent, MindRemovedMessage>(OnMindRemoved);
|
||||
SubscribeLocalEvent<TelegnosisPowerComponent, InhaleLocationEvent>(OnInhaleLocation, after: [typeof(InsideCryoPodComponent), typeof(InternalsComponent), typeof(BeingDisposedComponent), typeof(InsideEntityStorageComponent), typeof(MechPilotComponent)]);
|
||||
SubscribeLocalEvent<TelegnosisPowerComponent, ExaminedEvent>(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<PsionicComponent>(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<PsionicComponent>(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<TelegnosisPowerComponent> entity)
|
||||
{
|
||||
if (!TryComp<MindSwappedComponent>(entity, out var mindSwapped) ||
|
||||
!HasComp<TelegnosticProjectionComponent>(mindSwapped.OriginalEntity))
|
||||
{
|
||||
return default;
|
||||
}
|
||||
return mindSwapped.OriginalEntity;
|
||||
}
|
||||
|
||||
private void OnInhaleLocation(Entity<TelegnosisPowerComponent> 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<TelegnosisPowerComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (GetCasterProjection(entity) == default)
|
||||
return;
|
||||
|
||||
args.PushMarkup($"[color=yellow]{Loc.GetString("telegnosis-power-ssd", ("ent", entity))}[/color]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicAwaitingPlayerComponent, PlayerAttachedEvent>(OnPlayerAttached);
|
||||
}
|
||||
|
||||
private void OnPlayerAttached(EntityUid uid, PsionicAwaitingPlayerComponent component, PlayerAttachedEvent args)
|
||||
{
|
||||
if (TryComp<PsionicBonusChanceComponent>(uid, out var bonus) && bonus.Warn == true)
|
||||
_euiManager.OpenEui(new AcceptPsionicsEui(uid, this), args.Player);
|
||||
else
|
||||
AddRandomPsionicPower(uid);
|
||||
RemCompDeferred<PsionicAwaitingPlayerComponent>(uid);
|
||||
}
|
||||
|
||||
public void AddPsionics(EntityUid uid, bool warn = true)
|
||||
{
|
||||
if (Deleted(uid))
|
||||
return;
|
||||
|
||||
if (HasComp<PsionicComponent>(uid))
|
||||
return;
|
||||
|
||||
//Don't know if this will work. New mind state vs old.
|
||||
if (!TryComp<ActorComponent>(uid, out var actor))
|
||||
{
|
||||
EnsureComp<PsionicAwaitingPlayerComponent>(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<PsionicComponent>(uid))
|
||||
return;
|
||||
|
||||
AddComp<PsionicComponent>(uid);
|
||||
|
||||
var newComponent = (Component) _componentFactory.GetComponent(powerComp);
|
||||
newComponent.Owner = uid;
|
||||
|
||||
EntityManager.AddComponent(uid, newComponent);
|
||||
}
|
||||
|
||||
public void AddRandomPsionicPower(EntityUid uid)
|
||||
{
|
||||
AddComp<PsionicComponent>(uid);
|
||||
|
||||
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>("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<PsionicComponent>(uid, out var psionic))
|
||||
return;
|
||||
|
||||
if (!psionic.Removable)
|
||||
return;
|
||||
|
||||
if (!_prototypeManager.TryIndex<WeightedRandomPrototype>("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<PsionicComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
namespace Content.Server.CartridgeLoader.Cartridges;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed partial class GlimmerMonitorCartridgeComponent : Component
|
||||
{ }
|
||||
public sealed partial class GlimmerMonitorCartridgeComponent : Component;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Extensions for nyano's chat stuff
|
||||
/// </summary>
|
||||
|
||||
public sealed class NyanoChatSystem : EntitySystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Extensions for nyano's chat stuff
|
||||
/// </summary>
|
||||
[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<INetChannel> 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<INetChannel> GetPsionicChatClients()
|
||||
return Filter.Empty()
|
||||
.AddWhereAttachedEntity(IsEligibleForTelepathy)
|
||||
.Recipients
|
||||
.Select(p => p.Channel);
|
||||
}
|
||||
|
||||
private IEnumerable<INetChannel> GetAdminClients()
|
||||
{
|
||||
return _adminManager.ActiveAdmins
|
||||
.Select(p => p.Channel);
|
||||
}
|
||||
|
||||
private List<INetChannel> GetDreamers(IEnumerable<INetChannel> removeList)
|
||||
{
|
||||
var filtered = Filter.Empty()
|
||||
.AddWhereAttachedEntity(entity => HasComp<SleepingComponent>(entity) || HasComp<SeeingRainbowsStatusEffectComponent>(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<MobStateComponent>(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<INetChannel> GetAdminClients()
|
||||
foreach (var repeater in EntityQuery<TelepathicRepeaterComponent>())
|
||||
{
|
||||
return _adminManager.ActiveAdmins
|
||||
.Select(p => p.Channel);
|
||||
}
|
||||
|
||||
private List<INetChannel> GetDreamers(IEnumerable<INetChannel> removeList)
|
||||
{
|
||||
var filtered = Filter.Empty()
|
||||
.AddWhereAttachedEntity(entity => HasComp<SleepingComponent>(entity) || HasComp<SeeingRainbowsStatusEffectComponent>(entity) && !HasComp<PsionicsDisabledComponent>(entity) && !HasComp<PsionicInsulationComponent>(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<PsionicComponent>(entity)
|
||||
&& !HasComp<PsionicsDisabledComponent>(entity)
|
||||
&& !HasComp<PsionicInsulationComponent>(entity)
|
||||
&& (!TryComp<MobStateComponent>(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<TelepathicRepeaterComponent>())
|
||||
{
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +0,0 @@
|
|||
namespace Content.Server.Nyanotrasen.Chat
|
||||
{
|
||||
/// <summary>
|
||||
/// Repeats whatever is happening in telepathic chat.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class TelepathicRepeaterComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Punish when used against a non-psychic.
|
||||
/// </summary
|
||||
[DataField("punish")]
|
||||
public bool Punish = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -3,12 +3,10 @@ using Content.Server.Power.Components;
|
|||
using Content.Server.Electrocution;
|
||||
using Content.Server.Lightning;
|
||||
using Content.Server.Explosion.EntitySystems;
|
||||
using Content.Server.Construction;
|
||||
using Content.Server.Ghost;
|
||||
using Content.Server.Revenant.EntitySystems;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Construction.EntitySystems;
|
||||
using Content.Shared.Coordinates.Helpers;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Psionics.Glimmer;
|
||||
using Content.Shared.Verbs;
|
||||
|
|
@ -16,7 +14,6 @@ using Content.Shared.StatusEffect;
|
|||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.Destructible;
|
||||
using Content.Shared.Construction.Components;
|
||||
using Content.Shared.Mind;
|
||||
using Content.Shared.Mind.Components;
|
||||
using Content.Shared.Power;
|
||||
using Content.Shared.Weapons.Melee.Components;
|
||||
|
|
|
|||
|
|
@ -1,27 +1,24 @@
|
|||
namespace Content.Server.Psionics.Glimmer
|
||||
namespace Content.Server.Psionics.Glimmer;
|
||||
|
||||
/// <summary>
|
||||
/// Adds to glimmer at regular intervals. We'll use it for glimmer drains too when we get there.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class GlimmerSourceComponent : Component
|
||||
{
|
||||
[RegisterComponent]
|
||||
|
||||
[DataField] public float Accumulator = 0f;
|
||||
|
||||
[DataField] public bool Active = true;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public sealed partial class GlimmerSourceComponent : Component
|
||||
{
|
||||
[DataField("accumulator")]
|
||||
public float Accumulator = 0f;
|
||||
[DataField] public float SecondsPerGlimmer = 10f;
|
||||
|
||||
[DataField("active")]
|
||||
public bool Active = true;
|
||||
|
||||
/// <summary>
|
||||
/// Since glimmer is an int, we'll do it like this.
|
||||
/// </summary>
|
||||
[DataField("secondsPerGlimmer")]
|
||||
public float SecondsPerGlimmer = 10f;
|
||||
|
||||
/// <summary>
|
||||
/// True if it produces glimmer, false if it subtracts it.
|
||||
/// </summary>
|
||||
[DataField("addToGlimmer")]
|
||||
public bool AddToGlimmer = true;
|
||||
}
|
||||
/// <summary>
|
||||
/// True if it produces glimmer, false if it subtracts it.
|
||||
/// </summary>
|
||||
[DataField] public bool AddToGlimmer = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Handles structures which add/subtract glimmer.
|
||||
/// </summary>
|
||||
public sealed class GlimmerStructuresSystem : EntitySystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles structures which add/subtract glimmer.
|
||||
/// </summary>
|
||||
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<AnomalyVesselComponent, PowerChangedEvent>(OnAnomalyVesselPowerChanged);
|
||||
SubscribeLocalEvent<GlimmerSourceComponent, AnomalyPulseEvent>(OnAnomalyPulse);
|
||||
SubscribeLocalEvent<GlimmerSourceComponent, AnomalySupercriticalEvent>(OnAnomalySupercritical);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
private void OnAnomalyVesselPowerChanged(EntityUid uid, AnomalyVesselComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (TryComp<GlimmerSourceComponent>(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<AnomalyComponent>(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<GlimmerSourceComponent>())
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<AnomalyVesselComponent, PowerChangedEvent>(OnAnomalyVesselPowerChanged);
|
||||
|
||||
SubscribeLocalEvent<GlimmerSourceComponent, AnomalyPulseEvent>(OnAnomalyPulse);
|
||||
SubscribeLocalEvent<GlimmerSourceComponent, AnomalySupercriticalEvent>(OnAnomalySupercritical);
|
||||
}
|
||||
|
||||
private void OnAnomalyVesselPowerChanged(EntityUid uid, AnomalyVesselComponent component, ref PowerChangedEvent args)
|
||||
{
|
||||
if (TryComp<GlimmerSourceComponent>(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<AnomalyComponent>(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<GlimmerSourceComponent>())
|
||||
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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<NpcFactionPrototype> PsionicInterloperProtoId = "PsionicInterloper";
|
||||
private static readonly ProtoId<NpcFactionPrototype> GlimmerMonsterProtoId = "GlimmerMonster";
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
/// Masking
|
||||
SubscribeLocalEvent<PotentialPsionicComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicInsulationComponent, ComponentInit>(OnInsulInit);
|
||||
SubscribeLocalEvent<PsionicInsulationComponent, ComponentShutdown>(OnInsulShutdown);
|
||||
SubscribeLocalEvent<EyeComponent, ComponentInit>(OnEyeInit);
|
||||
|
||||
/// Layer
|
||||
SubscribeLocalEvent<PsionicallyInvisibleComponent, ComponentInit>(OnInvisInit);
|
||||
SubscribeLocalEvent<PsionicallyInvisibleComponent, ComponentShutdown>(OnInvisShutdown);
|
||||
|
||||
// PVS Stuff
|
||||
SubscribeLocalEvent<PsionicallyInvisibleComponent, EntInsertedIntoContainerMessage>(OnEntInserted);
|
||||
SubscribeLocalEvent<PsionicallyInvisibleComponent, EntRemovedFromContainerMessage>(OnEntRemoved);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, PotentialPsionicComponent component, ComponentInit args)
|
||||
{
|
||||
SetCanSeePsionicInvisiblity(uid, false);
|
||||
}
|
||||
|
||||
private void OnInsulInit(EntityUid uid, PsionicInsulationComponent component, ComponentInit args)
|
||||
{
|
||||
if (!HasComp<PotentialPsionicComponent>(uid))
|
||||
return;
|
||||
|
||||
if (HasComp<PsionicInvisibilityUsedComponent>(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<PotentialPsionicComponent>(uid))
|
||||
return;
|
||||
|
||||
SetCanSeePsionicInvisiblity(uid, false);
|
||||
|
||||
if (!HasComp<PsionicComponent>(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<VisibilityComponent>(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<VisibilityComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// This tracks how many valid entities are being contacted,
|
||||
/// so when you stop touching one, you don't immediately lose invisibility.
|
||||
/// </summary>
|
||||
[DataField("stages")]
|
||||
public int Stages = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows an entity to become psionically invisible when touching certain entities.
|
||||
/// </summary>
|
||||
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<PsionicInvisibleContactsComponent, StartCollideEvent>(OnEntityEnter);
|
||||
SubscribeLocalEvent<PsionicInvisibleContactsComponent, EndCollideEvent>(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<PsionicallyInvisibleComponent>(ourEntity))
|
||||
return;
|
||||
|
||||
EnsureComp<PsionicallyInvisibleComponent>(ourEntity);
|
||||
var stealth = EnsureComp<StealthComponent>(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<PsionicallyInvisibleComponent>(ourEntity))
|
||||
return;
|
||||
|
||||
if (--component.Stages > 0)
|
||||
return;
|
||||
|
||||
RemComp<PsionicallyInvisibleComponent>(ourEntity);
|
||||
var stealth = EnsureComp<StealthComponent>(ourEntity);
|
||||
// Just to be sure...
|
||||
_stealth.SetVisibility(ourEntity, 1f, stealth);
|
||||
|
||||
RemComp<StealthComponent>(ourEntity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
namespace Content.Server.Psionics
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class PsionicallyInvisibleComponent : Component
|
||||
{}
|
||||
}
|
||||
|
|
@ -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<NoosphericZapPowerComponent> ent, ref NPCSteeringEvent args)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (comp.NoosphericZapActionEntity is not {} action)
|
||||
if (comp.ActionEntity is not {} action)
|
||||
return;
|
||||
|
||||
var target = Comp<EntityTargetActionComponent>(action);
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
namespace Content.Server.Psionics
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed partial class PotentialPsionicComponent : Component
|
||||
{
|
||||
[DataField("chance")]
|
||||
public float Chance = 0.04f;
|
||||
|
||||
/// <summary>
|
||||
/// YORO (you only reroll once)
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public bool Rerolled = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
namespace Content.Server.Psionics
|
||||
{
|
||||
/// <summary>
|
||||
/// Will open the 'accept psionics' UI when a player attaches.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public sealed partial class PsionicAwaitingPlayerComponent : Component
|
||||
{}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
[DataField("warn")]
|
||||
public bool Warn = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<IEntityManager>();
|
||||
foreach (var (actor, mob, psionic, meta) in entMan.EntityQuery<ActorComponent, MobStateComponent, PsionicComponent, MetaDataComponent>()){
|
||||
// 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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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!;
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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<PotentialPsionicComponent, MapInitEvent>(OnStartup);
|
||||
SubscribeLocalEvent<AntiPsionicWeaponComponent, MeleeHitEvent>(OnMeleeHit);
|
||||
SubscribeLocalEvent<AntiPsionicWeaponComponent, StaminaMeleeHitEvent>(OnStamHit);
|
||||
|
||||
SubscribeLocalEvent<PsionicComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicComponent, ComponentRemove>(OnRemove);
|
||||
}
|
||||
|
||||
private void OnStartup(EntityUid uid, PotentialPsionicComponent component, MapInitEvent args)
|
||||
{
|
||||
if (HasComp<PsionicComponent>(uid))
|
||||
return;
|
||||
|
||||
_rollers.Enqueue((component, uid));
|
||||
}
|
||||
|
||||
private void OnMeleeHit(EntityUid uid, AntiPsionicWeaponComponent component, MeleeHitEvent args)
|
||||
{
|
||||
foreach (var entity in args.HitEntities)
|
||||
{
|
||||
if (HasComp<PsionicComponent>(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<MindSwappedComponent>(entity, out var swapped))
|
||||
{
|
||||
_mindSwapPowerSystem.Swap(entity, swapped.OriginalEntity, true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.Punish && HasComp<PotentialPsionicComponent>(entity) && !HasComp<PsionicComponent>(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<NpcFactionMemberComponent>(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<NpcFactionMemberComponent>(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<PsionicComponent>(stam.Entity))
|
||||
bonus = true;
|
||||
}
|
||||
|
||||
if (!bonus)
|
||||
return;
|
||||
|
||||
|
||||
args.FlatModifier += component.PsychicStaminaDamage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Makes the entity psionic if it is possible.
|
||||
/// Ignores rolling and rerolling prevention.
|
||||
/// </summary>
|
||||
public bool TryMakePsionic(Entity<PotentialPsionicComponent> ent)
|
||||
{
|
||||
if (HasComp<PsionicComponent>(ent))
|
||||
return false;
|
||||
|
||||
if (!_cfg.GetCVar(DCCVars.PsionicRollsEnabled))
|
||||
return false;
|
||||
|
||||
var warn = CompOrNull<PsionicBonusChanceComponent>(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<PsionicBonusChanceComponent>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ public sealed partial class OracleComponent : Component
|
|||
[DataField("rewardReagents", customTypeSerializer: typeof(PrototypeIdListSerializer<ReagentPrototype>))]
|
||||
public IReadOnlyList<string> RewardReagents = new[]
|
||||
{
|
||||
"LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "Wine", "Blood", "Ichor"
|
||||
"LotophagoiOil", "LotophagoiOil", "LotophagoiOil", "Ambrosia", "Nectar", "Wine", "Blood", "Ichor",
|
||||
};
|
||||
|
||||
[DataField("demandMessages")]
|
||||
|
|
|
|||
|
|
@ -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<OracleComponent> oracle, ref InteractHandEvent args)
|
||||
{
|
||||
if (!HasComp<PotentialPsionicComponent>(args.User) || HasComp<PsionicInsulationComponent>(args.User))
|
||||
if (!HasComp<PotentialPsionicComponent>(args.User)
|
||||
|| !_playerManager.TryGetSessionByEntity(args.User, out var session))
|
||||
return;
|
||||
|
||||
if (!TryComp<ActorComponent>(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<OracleComponent> ent, ref GetVerbsEvent<Verb> args)
|
||||
{
|
||||
if (!args.CanAccess || !args.CanInteract || args.Using == null)
|
||||
return;
|
||||
|
||||
if (HasComp<MobStateComponent>(args.Using))
|
||||
return;
|
||||
|
||||
if (!TryComp(args.Using, out MetaDataComponent? meta))
|
||||
return;
|
||||
|
||||
if (HasComp<BorgChassisComponent>(args.User))
|
||||
return;
|
||||
|
||||
if (meta.EntityPrototype == null)
|
||||
if (!args.CanAccess
|
||||
|| !args.CanInteract
|
||||
|| args.Using == null
|
||||
|| HasComp<MobStateComponent>(args.Using)
|
||||
|| !TryComp(args.Using, out MetaDataComponent? meta)
|
||||
|| HasComp<BorgChassisComponent>(args.User)
|
||||
|| meta.EntityPrototype == null)
|
||||
return;
|
||||
|
||||
var argsUser = args.User;
|
||||
|
|
@ -147,16 +141,10 @@ public sealed class OracleSystem : EntitySystem
|
|||
|
||||
private void DoOnInteractUsing(Entity<OracleComponent> oracle, EntityUid user, EntityUid used)
|
||||
{
|
||||
if (HasComp<MobStateComponent>(used))
|
||||
return;
|
||||
|
||||
if (!TryComp(used, out MetaDataComponent? meta))
|
||||
return;
|
||||
|
||||
if (HasComp<BorgChassisComponent>(user))
|
||||
return;
|
||||
|
||||
if (meta.EntityPrototype == null)
|
||||
if (HasComp<MobStateComponent>(used)
|
||||
|| !TryComp(used, out MetaDataComponent? meta)
|
||||
|| HasComp<BorgChassisComponent>(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)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<SophicScribeComponent>();
|
||||
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<MindSwappedComponent>(scribe, out var swapped))
|
||||
if (TryComp<MindSwappedReturnPowerComponent>(scribe, out var swapped))
|
||||
{
|
||||
speaker = swapped.OriginalEntity;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,27 +6,27 @@ public sealed partial class GlimmerEventComponent : Component
|
|||
/// <summary>
|
||||
/// Minimum glimmer value for event to be eligible. (Should be 100 at lowest.)
|
||||
/// </summary>
|
||||
[DataField("minimumGlimmer")]
|
||||
[DataField]
|
||||
public int MinimumGlimmer = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum glimmer value for event to be eligible. (Remember 1000 is max glimmer period.)
|
||||
/// </summary>
|
||||
[DataField("maximumGlimmer")]
|
||||
[DataField]
|
||||
public int MaximumGlimmer = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// Will be used for _random.Next and subtracted from glimmer.
|
||||
/// Lower bound.
|
||||
/// </summary>
|
||||
[DataField("glimmerBurnLower")]
|
||||
[DataField]
|
||||
public int GlimmerBurnLower = 25;
|
||||
|
||||
/// <summary>
|
||||
/// Will be used for _random.Next and subtracted from glimmer.
|
||||
/// Upper bound.
|
||||
/// </summary>
|
||||
[DataField("glimmerBurnUpper")]
|
||||
[DataField]
|
||||
public int GlimmerBurnUpper = 70;
|
||||
|
||||
[DataField("report")]
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ public sealed partial class MassMindSwapRuleComponent : Component
|
|||
/// <summary>
|
||||
/// The mind swap is only temporary if true.
|
||||
/// </summary>
|
||||
[DataField("isTemporary")]
|
||||
[DataField]
|
||||
public bool IsTemporary;
|
||||
|
||||
[DataField]
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// How many potential psionics should be awakened at most.
|
||||
/// </summary>
|
||||
[DataField("maxAwaken")]
|
||||
public int MaxAwaken = 3;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[DataField("baseGlimmerAddMin")]
|
||||
public int BaseGlimmerAddMin = 65;
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
[DataField("baseGlimmerAddMax")]
|
||||
public int BaseGlimmerAddMax = 85;
|
||||
|
||||
/// <summary>
|
||||
/// Multiply the EventSeverityModifier by this to determine how much extra glimmer to add.
|
||||
/// </summary>
|
||||
[DataField("glimmerSeverityCoefficient")]
|
||||
public float GlimmerSeverityCoefficient = 0.25f;
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
}
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
@ -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<GlimmerEventComponent>
|
||||
{
|
||||
public sealed class GlimmerEventSystem : StationEventSystem<GlimmerEventComponent>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<MassMindSwapRuleComponent>
|
||||
{
|
||||
[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<MassMindSwapRuleComp
|
|||
List<EntityUid> psionicActors = new();
|
||||
|
||||
var query = EntityQueryEnumerator<PotentialPsionicComponent, MobStateComponent>();
|
||||
while (query.MoveNext(out var psion, out _, out _))
|
||||
while (query.MoveNext(out var psion, out _, out var mobState))
|
||||
{
|
||||
if (_mobStateSystem.IsAlive(psion) && !HasComp<PsionicInsulationComponent>(psion))
|
||||
{
|
||||
psionicPool.Add(psion);
|
||||
if (!_mobStateSystem.IsAlive(psion, mobState) || !_psionic.CanBeTargeted(psion))
|
||||
continue;
|
||||
|
||||
if (HasComp<ActorComponent>(psion))
|
||||
{
|
||||
// This is so we don't bother mindswapping NPCs with NPCs.
|
||||
psionicActors.Add(psion);
|
||||
}
|
||||
psionicPool.Add(psion);
|
||||
|
||||
if (HasComp<ActorComponent>(psion))
|
||||
{
|
||||
// This is so we don't bother mindswapping NPCs with NPCs.
|
||||
psionicActors.Add(psion);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,13 +108,8 @@ internal sealed class MassMindSwapRule : StationEventSystem<MassMindSwapRuleComp
|
|||
// Remove this actor from the pool of swap candidates before they go.
|
||||
psionicPool.Remove(actor);
|
||||
|
||||
// Do the swap.
|
||||
_mindSwap.Swap(actor, other);
|
||||
if (!component.IsTemporary)
|
||||
{
|
||||
_mindSwap.GetTrapped(actor);
|
||||
_mindSwap.GetTrapped(other);
|
||||
}
|
||||
// Do the swap. Also ignore mindshields, because this is the big boi swap.
|
||||
_mindSwap.SwapMinds(actor, other, false, component.IsTemporary, true);
|
||||
} while (true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,131 +0,0 @@
|
|||
using Content.Shared.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Popups;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Psionics.Glimmer;
|
||||
using Content.Server.StationEvents.Components;
|
||||
using Content.Server.StationEvents.Events;
|
||||
using Content.Shared.Abilities.Psionics;
|
||||
using Content.Shared.Construction.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.GameTicking.Components;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Mobs.Components;
|
||||
using Content.Shared.Mobs.Systems;
|
||||
using Content.Shared.Psionics.Glimmer;
|
||||
using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Nyanotrasen.StationEvents.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Fries tinfoil hats and cages
|
||||
/// </summary>
|
||||
internal sealed class NoosphericFryRule : StationEventSystem<NoosphericFryRuleComponent>
|
||||
{
|
||||
[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<PsionicInsulationComponent, MobStateComponent>();
|
||||
while (query.MoveNext(out var psion, out _, out _))
|
||||
{
|
||||
if (!_mobStateSystem.IsAlive(psion))
|
||||
continue;
|
||||
|
||||
if (!_inventorySystem.TryGetSlotEntity(psion, "head", out var headItem))
|
||||
continue;
|
||||
|
||||
if (!TryComp<TinfoilHatComponent>(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<FlammableComponent>(pair.wearer, out var flammableComponent))
|
||||
{
|
||||
flammableComponent.FireStacks += 1;
|
||||
_flammableSystem.Ignite(pair.wearer, pair.wearer, flammableComponent);
|
||||
}
|
||||
} else if (_glimmerSystem.Glimmer > 750)
|
||||
{
|
||||
damage *= 3;
|
||||
if (TryComp<FlammableComponent>(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<SharedGlimmerReactiveComponent, TransformComponent, PhysicsComponent>();
|
||||
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<MapGridComponent>(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<ApcPowerReceiverComponent>(reactive, out var power))
|
||||
continue;
|
||||
|
||||
// If it's been turned off, turn it back on.
|
||||
if (power.PowerDisabled)
|
||||
_powerReceiverSystem.TogglePower(reactive, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Zaps everyone, rolling psionics and disorienting them
|
||||
/// </summary>
|
||||
internal sealed class NoosphericZapRule : StationEventSystem<NoosphericZapRuleComponent>
|
||||
{
|
||||
[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<PotentialPsionicComponent, MobStateComponent>();
|
||||
|
||||
while (query.MoveNext(out var psion, out var potentialPsionicComponent, out _))
|
||||
{
|
||||
if (!_mobStateSystem.IsAlive(psion) || HasComp<PsionicInsulationComponent>(psion))
|
||||
continue;
|
||||
|
||||
_stunSystem.TryAddParalyzeDuration(psion, TimeSpan.FromSeconds(5));
|
||||
_statusEffectsSystem.TryAddStatusEffect(psion, "Stutter", TimeSpan.FromSeconds(10), false, "StutteringAccent");
|
||||
|
||||
if (HasComp<PsionicComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Mutes everyone for a random amount of time.
|
||||
/// </summary>
|
||||
internal sealed class PsionicCatGotYourTongueRule : StationEventSystem<PsionicCatGotYourTongueRuleComponent>
|
||||
{
|
||||
[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<EntityUid> psionicList = new();
|
||||
|
||||
var query = EntityQueryEnumerator<PotentialPsionicComponent, MobStateComponent>();
|
||||
while (query.MoveNext(out var psion, out _, out _))
|
||||
{
|
||||
if (_mobStateSystem.IsAlive(psion) && !HasComp<PsionicInsulationComponent>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TagPrototype> InvalidForGlobalSpawnSpellTag = "InvalidForGlobalSpawnSpell";
|
||||
private static readonly ProtoId<TagPrototype> CannotSuicideTag = "CannotSuicide";
|
||||
|
|
@ -144,18 +146,11 @@ public sealed partial class ZombieSystem
|
|||
RemComp<ComplexInteractionComponent>(target);
|
||||
RemComp<SentienceTargetComponent>(target);
|
||||
|
||||
if (TryComp<PsionicComponent>(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<PsionicComponent>(target);
|
||||
}
|
||||
// DeltaV Start - Prevent Psionic Zombies
|
||||
RemComp<PotentialPsionicComponent>(target);
|
||||
if (HasComp<PsionicComponent>(target))
|
||||
_psionic.MindBreakEntity(target, false, true);
|
||||
// DeltaV End - Prevent Psionic Zombies
|
||||
|
||||
//funny voice
|
||||
var accentType = "zombie";
|
||||
|
|
|
|||
|
|
@ -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<MindContainerComponent> _mindContainerQuery;
|
||||
private EntityQuery<FracturedFormBodyComponent> _bodyQuery;
|
||||
private EntityQuery<SleepingComponent> _sleepingQuery;
|
||||
private EntityQuery<SSDIndicatorComponent> _ssdQuery;
|
||||
private EntityQuery<FracturedFormPowerComponent> _fracturedQuery;
|
||||
private EntityQuery<MobStateComponent> _mobStateQuery;
|
||||
private EntityQuery<ForcedSleepingStatusEffectComponent> _forcedSleepQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormPowerActionEvent>(OnPowerUsed);
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<FracturedFormPowerComponent, FracturedFormDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<FracturedFormBodyComponent, ExaminedEvent>(OnExamine);
|
||||
|
||||
_sleepingQuery = GetEntityQuery<SleepingComponent>();
|
||||
_ssdQuery = GetEntityQuery<SSDIndicatorComponent>();
|
||||
_fracturedQuery = GetEntityQuery<FracturedFormPowerComponent>();
|
||||
_mindContainerQuery = GetEntityQuery<MindContainerComponent>();
|
||||
_bodyQuery = GetEntityQuery<FracturedFormBodyComponent>();
|
||||
_mobStateQuery = GetEntityQuery<MobStateComponent>();
|
||||
_forcedSleepQuery = GetEntityQuery<ForcedSleepingStatusEffectComponent>();
|
||||
}
|
||||
|
||||
private void OnInit(Entity<FracturedFormPowerComponent> entity, ref ComponentInit args)
|
||||
{
|
||||
var component = entity.Comp;
|
||||
_actions.AddAction(entity, ref component.FracturedFormActionEntity, component.FracturedFormActionId);
|
||||
_actions.StartUseDelay(component.FracturedFormActionEntity);
|
||||
if (TryComp<PsionicComponent>(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<FracturedFormBodyComponent>(entity)) return; // Don't generate a new body if we're already part of a network.
|
||||
var bodyComp = AddComp<FracturedFormBodyComponent>(entity);
|
||||
bodyComp.ControllingForm = entity.Owner;
|
||||
component.Bodies.Add(entity);
|
||||
GenerateForm(entity);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<FracturedFormPowerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity.Owner, entity.Comp.FracturedFormActionEntity);
|
||||
|
||||
if (TryComp<PsionicComponent>(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<FracturedFormPowerComponent, MobStateComponent>();
|
||||
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<FracturedFormBodyComponent, MobStateComponent>();
|
||||
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<FracturedFormBodyComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private EntityUid GenerateForm(Entity<FracturedFormPowerComponent> 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<ProtoId<SpeciesPrototype>>();
|
||||
var speciesPrototypes = _prototype.EnumeratePrototypes<SpeciesPrototype>();
|
||||
foreach (var proto in speciesPrototypes)
|
||||
{
|
||||
var speciesEntityPrototype = _prototype.Index<EntityPrototype>(proto.Prototype);
|
||||
|
||||
if (proto.RoundStart && speciesEntityPrototype.TryGetComponent<PotentialPsionicComponent>(out var canBePsionic, Factory) && !SpeciesHiderSystem.IsHidden(proto.ID))
|
||||
{
|
||||
var chance = canBePsionic.Chance;
|
||||
|
||||
if (speciesEntityPrototype.TryGetComponent<PsionicBonusChanceComponent>(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<FracturedFormBodyComponent>(body);
|
||||
original.Comp.Bodies.Add(body);
|
||||
bodyComp.ControllingForm = original.Owner;
|
||||
return body;
|
||||
}
|
||||
|
||||
return default!;
|
||||
}
|
||||
|
||||
private bool IsValidBody(Entity<FracturedFormPowerComponent> 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<FracturedFormPowerComponent> 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<FracturedFormPowerComponent> entity)
|
||||
{
|
||||
foreach (var body in entity.Comp.Bodies)
|
||||
{
|
||||
if (IsValidBody(entity, body))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void Swap(Entity<FracturedFormPowerComponent> 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<FracturedFormPowerComponent>(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<FracturedFormPowerComponent> 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<FracturedFormPowerComponent> 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<FracturedFormPowerComponent> entity, ref FracturedFormDoAfterEvent args)
|
||||
{
|
||||
entity.Comp.DoAfter = null;
|
||||
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
_sleeping.TrySleeping(entity.Owner);
|
||||
}
|
||||
|
||||
private void OnExamine(Entity<FracturedFormBodyComponent> entity, ref ExaminedEvent args)
|
||||
{
|
||||
if (HasComp<FracturedFormPowerComponent>(entity))
|
||||
return;
|
||||
if (TryComp<FracturedFormPowerComponent>(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]");
|
||||
}
|
||||
}
|
||||
|
|
@ -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!;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A map between game rule prototypes and their results to give.
|
||||
/// </summary>
|
||||
public Dictionary<EntProtoId, PrecognitionResultComponent> Results = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
CachePrecognitionResults();
|
||||
|
||||
SubscribeLocalEvent<PrecognitionPowerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<PrecognitionPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionPowerActionEvent>(OnPowerUsed);
|
||||
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<PrecognitionPowerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_actions.AddAction(ent, ref ent.Comp.PrecognitionActionEntity, ent.Comp.PrecognitionActionId);
|
||||
_actions.StartUseDelay(ent.Comp.PrecognitionActionEntity);
|
||||
if (TryComp<PsionicComponent>(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<PsionicComponent>(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<TemporaryBlindnessComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Upon completion will send a message to the user corrosponding to the next station event to occour.
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <param name="component"></param>
|
||||
/// <param name="args"></param>
|
||||
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<ActorComponent>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precognition result message corosponding to the passed event id.
|
||||
/// </summary>
|
||||
/// <returns>message string corosponding to the event id passed</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns>The locale message id of a weighted randomly chosen precognition result</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soonest nextEvent to occur within the window.
|
||||
/// </summary>
|
||||
/// <param name="minDetectWindow"></param> The earliest reletive time that will be return a nextEvent
|
||||
/// <param name="maxDetectWindow"></param> The latest reletive latest time that will be return a nextEvent
|
||||
/// <returns>Component for the next event to occour if one exists in the window.</returns>
|
||||
private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow)
|
||||
{
|
||||
TimeSpan? earliestNextEventTime = null;
|
||||
NextEventComponent? earliestNextEvent = null;
|
||||
var query = EntityQueryEnumerator<NextEventComponent>();
|
||||
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<EntityPrototype>())
|
||||
return;
|
||||
|
||||
CachePrecognitionResults();
|
||||
}
|
||||
|
||||
private void CachePrecognitionResults()
|
||||
{
|
||||
Results.Clear();
|
||||
foreach (var prototype in _proto.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (prototype.Abstract)
|
||||
continue;
|
||||
|
||||
if (!prototype.TryGetComponent<PrecognitionResultComponent>(out var precognitionResult, _factory))
|
||||
continue;
|
||||
|
||||
Results.Add(prototype.ID, precognitionResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<PsionicEruptionPowerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, PsionicEruptionPowerActionEvent>(OnPowerUsed);
|
||||
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, DispelledEvent>(OnDispelled);
|
||||
SubscribeLocalEvent<PsionicEruptionPowerComponent, PsionicEruptionDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
private void OnInit(Entity<PsionicEruptionPowerComponent> 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<PsionicComponent>(entity, out var psionic) && psionic.PsionicAbility == null)
|
||||
{
|
||||
psionic.PsionicAbility = component.EruptionActionEntity;
|
||||
psionic.ActivePowers.Add(component);
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowWarning(Entity<PsionicEruptionPowerComponent> 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<PsionicEruptionPowerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity.Owner, entity.Comp.EruptionActionEntity);
|
||||
|
||||
if (TryComp<PsionicComponent>(entity, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(entity.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDispelled(Entity<PsionicEruptionPowerComponent> 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<PsionicEruptionPowerComponent> 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<PsionicEruptionPowerComponent>();
|
||||
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<PsionicEruptionPowerComponent> entity, ref PsionicEruptionDoAfterEvent args)
|
||||
{
|
||||
entity.Comp.DoAfter = null;
|
||||
|
||||
if (args.Cancelled || args.Handled)
|
||||
return;
|
||||
|
||||
if (!TryComp<BodyComponent>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsychokineticScreamPowerComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<PsychokineticScreamPowerComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<PsychokineticScreamPowerComponent, ShatterLightsActionEvent>(OnShatterLightsAction);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<PsychokineticScreamPowerComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_actions.AddAction(ent, ref ent.Comp.PsychokineticScreamActionEntity, ent.Comp.ShatterLightsActionId);
|
||||
_actions.StartUseDelay(ent.Comp.PsychokineticScreamActionEntity);
|
||||
|
||||
if (TryComp<PsionicComponent>(ent, out var psionic) && psionic.PsionicAbility == null)
|
||||
{
|
||||
psionic.PsionicAbility = ent.Comp.PsychokineticScreamActionEntity;
|
||||
psionic.ActivePowers.Add(ent.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<PsychokineticScreamPowerComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity.Owner, entity.Comp.PsychokineticScreamActionEntity);
|
||||
if (TryComp<PsionicComponent>(entity, out var psionic))
|
||||
{
|
||||
psionic.ActivePowers.Remove(entity.Comp);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnShatterLightsAction(Entity<PsychokineticScreamPowerComponent> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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<Entity<PoweredLightComponent>> _lightsInRange = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<ShatterLightsAbilityComponent, MapInitEvent>(OnMapInit);
|
||||
SubscribeLocalEvent<ShatterLightsAbilityComponent, ComponentShutdown>(OnShutdown);
|
||||
SubscribeLocalEvent<ShatterLightsAbilityComponent, ShatterLightsActionEvent>(OnShatterLightsAction);
|
||||
}
|
||||
|
||||
private void OnMapInit(Entity<ShatterLightsAbilityComponent> ent, ref MapInitEvent args)
|
||||
{
|
||||
_actions.AddAction(ent, ref ent.Comp.ShatterLightsActionEntity, ent.Comp.ShatterLightsActionId);
|
||||
_actions.StartUseDelay(ent.Comp.ShatterLightsActionEntity);
|
||||
}
|
||||
|
||||
private void OnShutdown(Entity<ShatterLightsAbilityComponent> entity, ref ComponentShutdown args)
|
||||
{
|
||||
_actions.RemoveAction(entity.Owner, entity.Comp.ShatterLightsActionEntity);
|
||||
}
|
||||
|
||||
private void OnShatterLightsAction(Entity<ShatterLightsAbilityComponent> 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Removes psionic abilities when at least 1u of the reagent is in the system.
|
||||
/// Removes psionic abilities.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T, TEffect}"/>
|
||||
public sealed partial class RemovePsionicAbilitiesEntityEffectSystem : EntityEffectSystem<PotentialPsionicComponent, RemovePsionicAbilities>
|
||||
public sealed partial class RemovePsionicAbilitiesEntityEffectSystem : EntityEffectSystem<PsionicComponent, RemovePsionicAbilities>
|
||||
{
|
||||
[Dependency] private readonly PsionicsSystem _psionic = default!;
|
||||
[Dependency] private readonly PsionicAbilitiesSystem _psionicAbilities = default!;
|
||||
protected override void Effect(Entity<PotentialPsionicComponent> entity, ref EntityEffectEvent<RemovePsionicAbilities> args)
|
||||
{
|
||||
if (args.Scale != 1f)
|
||||
return;
|
||||
[Dependency] private readonly PsionicSystem _psionicSystem = default!;
|
||||
|
||||
_psionicAbilities.RemovePsionics(entity);
|
||||
_psionic.GrantNewPsionicReroll(entity);
|
||||
protected override void Effect(Entity<PsionicComponent> psionic, ref EntityEffectEvent<RemovePsionicAbilities> args)
|
||||
{
|
||||
_psionicSystem.MindBreakEntity(psionic.Owner);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Rerolls psionic abilities when at least 1u of the reagent is in the system.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T, TEffect}"/>
|
||||
public sealed partial class RerollPsionicAbilitiesEntityEffectSystem : EntityEffectSystem<PotentialPsionicComponent, RerollPsionicAbilities>
|
||||
{
|
||||
[Dependency] private readonly PsionicsSystem _psionic = default!;
|
||||
protected override void Effect(Entity<PotentialPsionicComponent> entity, ref EntityEffectEvent<RerollPsionicAbilities> args)
|
||||
{
|
||||
if (args.Scale != 1f)
|
||||
return;
|
||||
|
||||
_psionic.RerollPsionics(entity, bonusMuliplier: args.Effect.BonusMultiplier);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Rolls for a new psionic power.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="EntityEffectSystem{T, TEffect}"/>
|
||||
public sealed partial class RollPsionicAbilityEntityEffectSystem : EntityEffectSystem<PotentialPsionicComponent, RollPsionicAbility>
|
||||
{
|
||||
[Dependency] private readonly PsionicSystem _psionic = default!;
|
||||
protected override void Effect(Entity<PotentialPsionicComponent> psionic, ref EntityEffectEvent<RollPsionicAbility> args)
|
||||
{
|
||||
_psionic.TryRollPsionic(psionic, args.Effect.BonusMultiplier);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
|||
/// </summary>
|
||||
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<Entity<MindComponent>> minds)
|
||||
|
|
@ -26,6 +27,6 @@ public sealed partial class ParadoxCloneRuleSystem
|
|||
{
|
||||
// guaranteed psionic power
|
||||
var psi = EnsureComp<PotentialPsionicComponent>(mob);
|
||||
_psionics.RollPsionics(mob, psi, false, 100);
|
||||
_psionic.AddRandomPsionicPower((mob, psi), false);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<BreakLightsOnSpawnComponent> entity, ref MapInitEvent args)
|
||||
{
|
||||
_shatterLights.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight);
|
||||
_psychokineticScream.ShatterLightsAround(entity.Owner, entity.Comp.Radius, entity.Comp.LineOfSight);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<GuardianComponent, DispelledEvent>(OnGuardianDispelled);
|
||||
SubscribeLocalEvent<FamiliarComponent, DispelledEvent>(OnFamiliarDispelled);
|
||||
}
|
||||
|
||||
private void OnGuardianDispelled(Entity<GuardianComponent> guardian, ref DispelledEvent args)
|
||||
{
|
||||
if (TryComp<GuardianHostComponent>(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<FamiliarComponent> familiar, ref DispelledEvent args)
|
||||
{
|
||||
if (familiar.Comp.Source != null)
|
||||
EnsureComp<SummonableRespawningComponent>(familiar.Comp.Source.Value);
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FracturedFormBodyComponent> _bodyQuery;
|
||||
private EntityQuery<SleepingComponent> _sleepingQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_bodyQuery = GetEntityQuery<FracturedFormBodyComponent>();
|
||||
_sleepingQuery = GetEntityQuery<SleepingComponent>();
|
||||
}
|
||||
|
||||
protected override void OnPowerInit(Entity<FracturedFormPowerComponent> 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<FracturedFormBodyComponent>(power))
|
||||
return;
|
||||
|
||||
// Don't make bodies if there is no body. This is solely for test fails.
|
||||
if (!HasComp<BodyComponent>(power))
|
||||
return;
|
||||
|
||||
var bodyComp = AddComp<FracturedFormBodyComponent>(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<FracturedFormPowerComponent> 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<ProtoId<SpeciesPrototype>>();
|
||||
var speciesPrototypes = _prototype.EnumeratePrototypes<SpeciesPrototype>();
|
||||
foreach (var proto in speciesPrototypes)
|
||||
{
|
||||
var speciesEntityPrototype = _prototype.Index<EntityPrototype>(proto.Prototype);
|
||||
// If they have the PotentialPsionicComponent, they can be psionic.
|
||||
if (proto.RoundStart && speciesEntityPrototype.TryGetComponent<PotentialPsionicComponent>(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<FracturedFormBodyComponent>(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<FracturedFormPowerComponent> 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<FracturedFormPowerComponent> 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<FracturedFormPowerComponent>(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<FracturedFormPowerComponent>(psionic);
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
List<Entity<FracturedFormPowerComponent>> swapTargets = [];
|
||||
|
||||
var entities = EntityQueryEnumerator<FracturedFormPowerComponent, MobStateComponent>();
|
||||
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<FracturedFormBodyComponent, MobStateComponent>();
|
||||
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<FracturedFormBodyComponent>(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicPowerDetectorComponent> _psionicDetectorQuery;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<MassSleepPowerComponent, MassSleepDoAfterEvent>(OnMassSleepDoAfter);
|
||||
_psionicDetectorQuery = GetEntityQuery<PsionicPowerDetectorComponent>();
|
||||
}
|
||||
|
||||
protected override void OnPowerUsed(Entity<MassSleepPowerComponent> 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<MassSleepPowerComponent> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<GhostAttemptHandleEvent>(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<MindSwappedReturnPowerComponent>(args.Mind.CurrentEntity, out var component)
|
||||
&& Action.GetAction(component.ActionEntity) is { } action
|
||||
&& action.Comp.AttachedEntity is not null)
|
||||
{
|
||||
args.Result = false;
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<NoosphericZapPowerComponent> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// This system lets a psionic user foretell the next event with some inconsistency.
|
||||
/// </summary>
|
||||
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!;
|
||||
|
||||
/// <summary>
|
||||
/// A map between game rule prototypes and their results to give.
|
||||
/// </summary>
|
||||
public Dictionary<EntProtoId, PrecognitionResultComponent> Results = new();
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<PrecognitionPowerComponent, PrecognitionDoAfterEvent>(OnDoAfter);
|
||||
SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
|
||||
|
||||
CachePrecognitionResults();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send a message to the user about the next station event.
|
||||
/// </summary>
|
||||
/// <param name="psionic">The source of the psionic usage.</param>
|
||||
/// <param name="args">The doAfter event.</param>
|
||||
private void OnDoAfter(Entity<PrecognitionPowerComponent> 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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the precognition result message corosponding to the passed event id.
|
||||
/// </summary>
|
||||
/// <returns>message string corosponding to the event id passed</returns>
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// </summary>
|
||||
/// <returns>The locale message id of a weighted randomly chosen precognition result</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the soonest nextEvent to occur within the window.
|
||||
/// </summary>
|
||||
/// <param name="minDetectWindow"></param> The earliest reletive time that will be return a nextEvent
|
||||
/// <param name="maxDetectWindow"></param> The latest reletive latest time that will be return a nextEvent
|
||||
/// <returns>Component for the next event to occour if one exists in the window.</returns>
|
||||
private NextEventComponent? FindEarliestNextEvent(TimeSpan minDetectWindow, TimeSpan maxDetectWindow)
|
||||
{
|
||||
TimeSpan? earliestNextEventTime = null;
|
||||
NextEventComponent? earliestNextEvent = null;
|
||||
var query = EntityQueryEnumerator<NextEventComponent>();
|
||||
|
||||
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<EntityPrototype>())
|
||||
return;
|
||||
|
||||
CachePrecognitionResults();
|
||||
}
|
||||
|
||||
private void CachePrecognitionResults()
|
||||
{
|
||||
Results.Clear();
|
||||
foreach (var prototype in _prototype.EnumeratePrototypes<EntityPrototype>())
|
||||
{
|
||||
if (prototype.Abstract
|
||||
|| !prototype.TryGetComponent<PrecognitionResultComponent>(out var precognitionResult, _factory))
|
||||
continue;
|
||||
|
||||
Results.Add(prototype.ID, precognitionResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicEruptionPowerComponent, PsionicEruptionPowerActionEvent>
|
||||
{
|
||||
[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<PsionicEruptionPowerComponent, PsionicEruptionDoAfterEvent>(OnDoAfter);
|
||||
}
|
||||
|
||||
protected override void OnPowerInit(Entity<PsionicEruptionPowerComponent> 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<PsionicEruptionPowerComponent> 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<PsionicEruptionPowerComponent>();
|
||||
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<PsionicEruptionPowerComponent> psionic, ref PsionicEruptionDoAfterEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
args.Handled = true;
|
||||
psionic.Comp.RemoveSavedDoAfterId();
|
||||
|
||||
if (args.Cancelled || !TryComp<BodyComponent>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PyrokinesisPowerComponent> psionic, ref PyrokinesisPowerActionEvent args)
|
||||
{
|
||||
if (!Psionic.CanBeTargeted(args.Target, hasAggressor: args.Performer) || !TryComp<FlammableComponent>(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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<TelegnosisPowerComponent, InhaleLocationEvent>(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<TelegnosisPowerComponent> psionic, ref TelegnosisPowerActionEvent args)
|
||||
{
|
||||
// TODO: Fix this. MindSwapSystem cannot handle popups when called from serverside while the performer is the cause.
|
||||
if (HasComp<MindShieldComponent>(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<TelegnosisPowerComponent> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PotentialPsionicComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawnComplete);
|
||||
|
||||
InitializeItems();
|
||||
}
|
||||
|
||||
private void OnPlayerSpawnComplete(Entity<PotentialPsionicComponent> potPsionic, ref PlayerSpawnCompleteEvent args)
|
||||
{
|
||||
if (RollChance(potPsionic))
|
||||
_euiManager.OpenEui(new AcceptPsionicsEui(potPsionic, this), args.Player);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PsionicallyInsulativeComponent, InventoryRelayedEvent<NoosphericFryEvent>>(OnFry);
|
||||
}
|
||||
|
||||
private void OnFry(Entity<PsionicallyInsulativeComponent> gear, ref InventoryRelayedEvent<NoosphericFryEvent> 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<FlammableComponent>(args.Owner, out var flammable))
|
||||
return;
|
||||
|
||||
_flammable.AdjustFireStacks(args.Owner, args.Args.FireStacks, flammable);
|
||||
_flammable.Ignite(args.Owner, gear, flammable);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<PotentialPsionicComponent> 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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Spawns random debris in space around a loaded grid.
|
||||
/// Requires <see cref="LoadFarGridRuleComponent"/>.
|
||||
/// Requires <see cref="_DV.StationEvents.Components.LoadFarGridRuleComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(DebrisSpawnerRule))]
|
||||
public sealed partial class DebrisSpawnerRuleComponent : Component
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Makes a GALPOL announcement and creates a report some time after an antag spawns.
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
/// <summary>
|
||||
/// Loads a grid far away from a random station.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
/// </summary>
|
||||
[RegisterComponent, Access(typeof(GlimmerMobRule))]
|
||||
public sealed partial class LockProbersRuleComponent : Component { }
|
||||
public sealed partial class LockProbersRuleComponent : Component;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The damage dealt to everyone wearing insulative gear.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public DamageSpecifier Damage = new()
|
||||
{
|
||||
DamageDict = new Dictionary<string, FixedPoint2>
|
||||
{
|
||||
{"Heat", 10},
|
||||
{"Shock", 10},
|
||||
}
|
||||
};
|
||||
|
||||
[DataField]
|
||||
public int FireStacks = 2;
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// The minimum amount of psionics that are created with each storm.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MinAwaken = 1;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of psionics that are created with each storm.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int MaxAwaken = 3;
|
||||
|
||||
///<summary>
|
||||
/// The coefficient for additional psionic awakenings.
|
||||
/// It's calculated via glimmer / coefficient, rounded down.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float AdditionalAwokenPerGlimmer = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum amount of glimmer added.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int BaseGlimmerAddMin = 65;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum amount of glimmer added.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public int BaseGlimmerAddMax = 85;
|
||||
|
||||
/// <summary>
|
||||
/// Multiply the EventSeverityModifier by this to determine how much extra glimmer to add.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public float GlimmerSeverityCoefficient = 0.25f;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
using Content.Server._DV.StationEvents.Events;
|
||||
using Content.Server._DV.StationEvents.GameRules;
|
||||
|
||||
namespace Content.Server._DV.StationEvents.Components;
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue