Refactor Damage to use Protoypes (#4262)
* Add DamageType And DamageGroup Prototypes * Remove DamageTypePrototype Field "name" as its redundant * Change I/DamageableComponent to use prototypes * Update DamageContainer, ReisistanceSet and DamageChangeData * Change Barotrauma Component to use DamageType from DamageSystem * Update AsteroidRockComponent * update some more components * update some more components * Fix m o r e c o m p o n e n t s and their damageType * all thats left is bug/missing node hunting then verification. * push changes * update submodule * Merge fixes * Revert "#3935 implemented suggestions from PR" This reverts commit a9b1c7b96333ca570067d6a9df1954481005892a. * #4219 revert of single sound removal in EmitSoundSystem * #4219 single sounds in EmitSoundSystem should work now * #4219 some small project tweaks * push DGP for example * update damagecomponent across shared and server * fix a few bugs * #4219 upgraded EmitSoundSystem to use SoundSpecifier * replacing sound (collection) names with SoundSpecifier - part 1 * #4219 pr tweaks * #4219 pr tweak (cherry picked from commit 00b80cb1df2434259ab5df45188e176be57603af) * emitsoundsystem tweak * first windoor commit * finished yaml * windoors work #Dab * sound + locked prototypes * Inserted SoundSpecifier where appropiate * added comment * removed TryGetSound + made some SoundSpecifier datafields required * changed some prototypes' sound paths * test fixes * Fix Merge issues * Made plasma grindable (#4334) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> * Automatic changelog update * Fixes invalid yaml line (#4408) * Fix potential timer exception * SetTile during extensions test (#4409) Mainly so it can actually account for accurate grid bounds when doing the test Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> * Update submodule * GasFilter appearance, EnabledAtmosDeviceVisualizer improvements. * Add Enabled property to SubFloorHideComponent, make it networked. (#4404) * Refactor IDoorCheck into entity events (#4366) * IDoorCheck refactored to events # Conflicts: # Content.Server/Atmos/TileAtmosphere.cs # Content.Server/Doors/Components/AirlockComponent.cs # Content.Server/Doors/Components/FirelockComponent.cs # Content.Server/Doors/Components/ServerDoorComponent.cs # Content.Server/Doors/IDoorCheck.cs * namespaces * Fix mapinit bug with refreshautoclose * ok i guess these just didnt feel like staging today * Automatic changelog update * AtmosDevices can optionally process in space. (#4405) Refactors some misc atmos things, too. * Fix gravity generator yeeting * Enables nullables for atmos internals * Meth (#4186) * adds an overdose metabolism with sensible default settings * adds the compoenents for the run fast part of meth * not sure what I changed here but I trust my past self to not fuck up for once * adds basic meth recipe * correctly names comething * I really should've checked my spelling before making this pr Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * adds suggestion from the maintaner, who managed to get to this pr suprisingly fast * Revert "adds suggestion from the maintaner, who managed to get to this pr suprisingly fast" This reverts commit 9b0f07402e35ed5890b3af805691b690671b950c. * tweak * tweak * git's gitting on my nerves * some small tweaks * don't need these anymore * makes some stuff required * changes the meth recipe to arbitary bullshit to get the yaml thingy to leave me alone goddamnit * extremely minor change * removes overdose, because however it's gonna be done, it's definitely not my way * i should really double check every key I press * hm * sigh, I should be more thorough with looking at error messages. * beenus * gay sex is gay * this one goes out to bingo * reviews * not sure why status lifetime description wasn't being commited * Update MovespeedModifierMetabolism.cs Co-authored-by: mirrorcult <notzombiedude@gmail.com> * adds VV to component variables * rebalances meth, and makes the completely unrealistic recipe properly work now * meth effects should go away now or something do you think I test these changes before pushing?? * ah yes, orginization * adds proper recipe and prequisite chemicals * fixes linter hopefully * Update chemicals.yml * a * starts working on prediction * thing * predmiction?? * changes thing * does it properly * uses timespan instead of timer * uses dif timers and adds a system * updates robust and tweaks a small thing * Fixes * "Fix" prediction * starts changing the timer to timespans to avoid icky on timer end * okay fixes the check thing but now meth is broken and I don't know why * fixes predicition (partially) * Delete ContentNetIDs.cs whoops * some changes advised by sloth * certified scalycode fix right here * moves resettimer to the metabolism to make it less oop stinky * moves resettimer to the metabolism to make it less oop stinky * gamin * when the is * updates mth colour to be accurate because I forgot * abc hard ok * everything should be up to date now * makes MovespeedModifierMetabolism ECS and cleans up some other stuff * does a fixy wixy * fix thing * Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth" This reverts commit 62886561098be02f9adb6352f4e858a8269d5bd5, reversing changes made to ca34fffb5b7a40f19aec7b9e4bc37bdeab914bc1. * Revert "Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth"" This reverts commit 4f550da19656abfd0be05f818fc6b7100252d5b8. * fix hopefully * updates metabolism to works with mirror's fancy new system * updates yaml + tweaks * bruh * yaml moment * :yaml moment * Revert " :yaml moment" This reverts commit 8cb51573c64db76d989de22acdbb9c50b2c6d052. * 99th commit yay, also I need to not do this * removes something that I don't need * makes system work with this and gets rid of unnesescary check * make the update only work on active components * oops * Cleanup * alphabetise this shit * Touchup * Woops stupid alloc by me * Nerf nyoom for now Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Random offset for DefaultGrid every round (#4411) * Random offset for DefaultGrid every round This is useful to make coders aware of entitycoordinates and mapcoordinates being different and to help spot problems early. It also puts the onus of fixing positioning bugs back onto the original coder rather than someone else if they happen to spot it. * Fix clickable test * Fix entitysystemextensions * Automatic changelog update * Document a few atmos classes. * Update submodule We do a little bit of debugging * Make pulling feel less crap (#4414) Something something pulling refactor someday * Automatic changelog update * Comment joint prediction * Add CVar for random grid offset, disable it by default. * Fixes not taking pressure damage in space. Fixes #4415 * Automatic changelog update * Adds an alternate jumpsuit for botany (#4419) * Adds an alternate botanists jumpsuit * Updated license information * Automatic changelog update * Added two new small areas to maintenence (#4359) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Updated meta.json to standard * Update meta.json to standard * a * Revert "Update meta.json to standard" This reverts commit4c6702f17b. * Revert "Updated meta.json to standard" This reverts commit8ea7a3ad1f. * fix rsi * proper sprites + visualizes nicely * Removes broken new-line markers from a few desc. * Fixes a localization error with solution container descriptions * construction * reviews + test * remove SetLayerVisibility, doesnt work for some reason * swept moment * leftover * Automatic changelog update * Fix can't return to body bug (#4424) * Automatic changelog update * Update submodule * Fix gas tile overlays on shuttles * Offset station on roundstart again * Fix PlaceableSurface incorrect coordinate usage. It set world position to entity coordinate positions... * Automatic changelog update * Fix pickup animation incorrect coordinate usage. Animation finalPosition was world position, while initialPosition was local position. * Automatic changelog update * Pointing coordinate fixes. * Automatic changelog update * Further pickup animation improvements. * Adds Science Windoors (#4433) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Gave CMO a Hardsuit (#4434) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fixed station engineer table spawn (#4436) * Maps in windoors (#4432) * Automatic changelog update * Losing a head doesn't mean you lose your eyes #3956 #4061 (#4225) Co-authored-by: Michael Will <will_m@outlook.de> * Automatic changelog update * Adds windoors for security and the brig. (#4441) * Automatic changelog update * Remaps Chemistry and Security, and some Mapping Fixes (#4442) * Automatic changelog update * Made the cloning pod and medical scanner constructible (#4439) * Made the cloning machine pod constructable * Added everything like I did but with the Medical Scanner Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fix fixed-point format specifier in PowerCellComponent * Update submodule * Actually update submodule The last one was a test to check if you were paying attention. * Update maps for grid collisions (#4450) * Update submodule * Fixes the windoor in science. (#4444) * Automatic changelog update * Remove IActionBlocker.CanMove (#4449) * Remove IActionBlocker.CanMove Ported the remainders over to using MovementAttemptEvent which should also help make mob movement a bit faster. * Make that check faster * Added restrictions for what can be inserted into most belts (#4367) * Belts now restrict what can be inserted into them * Bandolier now only holds shotgun shells * Tiny typo * Another tiny typo * Automatic changelog update * Fix indentation * Fix stuff * Fix gas canister * Organize gas canister prototype * Fix audio occlusion This was previously being done on every SoundComponent. * Call SoundSystem instead * Parents crowbar-red and merges RSIs * Deletes old crowbar_red.rsi * Abstracts BasePlushie and ReagentContainerBase * Adds missing suffixes to debug items and tags them all * Crematorium now makes a noise on start and during cremation (#4459) * Automatic changelog update * Refactor disposals to ECS (#4418) * ECS up disposals Also significantly reduced its CPU usage. * Make update significantly less S L O W * Start units pressurised * Client-side flush lerping * Fix powered not toggling UI * Fix flush button * InteractUsing * Minor optimisations * Fix collisions * Make visual state ECS * Almost done with shared * Most stuff moved * Optimise item sleeping * Automatic changelog update * Fix door sound dampening (#4453) * Completely fix NPC pathfinding Through rigorous investigation and hard work. * Implement Entity List Display and rework StorageComponent window (#4140) * Create EntityListDisplay * Rework ClientStorage window * Add styling * Remove unnecessary colors * Rename list * Make scrollbar push content * Change children update a bit * Add old index * Localize ClientStorageComponent * Add size return * Remove spaces * Fix usings * Fix tool sounds. Fixes #4465 * Automatic changelog update * Fix missing sounds (#4466) * Fix missing sounds * Make SoundHitSpecies fallback to SoundHit * Fix crayon YAML * Update PlaySoundBehavior YAML * Fix required * Update README.md * Fix taser physics (#4470) * Increase MoverController performance (#4448) Should be a decent amount; rest will come from removing IActionBlocker. * Remove throwing fixture error * Update submodule * Fix sound crash when making foam (#4476) * Automatic changelog update * Centers some sprites * Fix showatmos * Fix tests. Flashlight RSI state names had been changed, and some places weren't updated properly * Remove pillcomponent (#4469) * Remove PillComponent * Make food without any solution left delete and create trash * Replace PillComponent references with a Pill tag * Clean up * Add swallow message to food * Change to eatMessage override * Change FoodComponent transferAmount to nullable * Change properties to private * ItemCabinetVisualizer uses layers (#4445) * ItemCabinetVisualizer uses layers * Reviews applied * Fixes the fireaxe cabinet as well * I'm dumb * Fixes bedsheets being unpickupable (#4479) * Automatic changelog update * Adds inhands for crayons and the crayon box (#4481) * Automatic changelog update * Fixes cigarette animations and matches now have lit/unlit inhand sprites (#4480) * Automatic changelog update * Adds plant clippers and changes hydro tool sprites to be more consistent (#4482) * Adds plant clippers * Updates sycthe, hoe and spade sprites * Adds inhands for radio/walkietalkie (#4483) * Adds inhands for radio/walkietalkie and separates layers * License thingy * Fixes test * Can now equip fireaxe, claymore, spear and baseball back on your back (#4484) * Can equip spears and claymores on your back now * Adds fireaxe and baseball bat back equip * Automatic changelog update * Makes Match[sticks/box] ECS, Makes Matchsticks ignite plasma * am big dumdum pls no shed * Owner Transform to Owner in SoundSystem calls (#4460) * Owner Transform to Owner * Owner Transform to Owner minor fixes * Improve hand pickup code This way I can break EntityCoordinates' API without breaking content. BWAHAHAHAHAH! * Update submodule * Version v0.5.11 does not exist * Update submodule We don't talk about v0.5.12 either. * Update submodule For the last time * Adds duck-related content and a seclite (#4489) * Added rubber ducky * Adds ducky slippers * Adds seclite * Update Resources/Textures/Objects/Tools/seclite.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Fun/ducky.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * The duck slippers now quack as you walk Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Automatic changelog update * Adds the ability to assign equip sounds in ClothingComponent (#4485) * Initial * Works (Kinda) * Polish * Reviews * Removes vending inventory desc and adds missing vending machine descriptions (#4493) * Removes inventory descriptions * Adds missing vending machine descriptions using inventory descriptions * Woops missed a file * Automatic changelog update * Adds a ton more inhands (#4488) * Removed useless loc and added inhands for some lamps * Adds a ton of inhands to the game * Actually adds the pill inhand * Update Resources/Textures/Objects/Misc/utensils.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Tools/rcd.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Added open/close/stacklayers for Matchbox and inhands (#4496) * Added open/close/stacklayers for Matchbox * Added inhands for matchbox * What am I a clown not gonna leave no indent spaces * Adds sound to fire extinguisher safety (#4494) * Automatic changelog update * Adds a 6pack of Cola (#4499) * Adds a 6pack of Cola * Revert "Adds a 6pack of Cola" This reverts commit 99f024b94ab3c684ce62e28018bd0fe2f26a2e09. * Revert "Revert "Adds a 6pack of Cola"" This reverts commit 9e3d31e41c8ea61a962ad7d4d3c0ea7124e82653. * Automatic changelog update * Fix H-pulling? (#4425) In retrospect the answer is easy, but man this took a while. * Automatic changelog update * Makes DiceComponent ECS * Makes PlaceableSurface ECS * Added classic announcement sound to announcements (#4504) * Automatic changelog update * Refactors throw events, makes cream pies ECS (#4500) * Automatic changelog update * Port the tumbler from Box2D (#4486) * Subscribe TransformComponent events by-ref (#4478) * Refactor damageablecomponent update (#4406) * Fixing merge. I messed up part of the merge. this should fix it? * Barotrauma now uses prototypeManager As System.Runtime.CompilerServices also has a [Dependency], I think I had to use the full path [Robust.Shared.IoC.Dependency] * FlammableComponent now uses prototypeManager * SuicideCommands now use prototypeManager * Changed many files to use prototypeManager to resolve damaege prototypes Yeah.... prototype references would be very nice. maybe this was all a waste of time. * Grouping prototypeManager.Index with datafield definitions This will make it easier to eventually add prototype references * removed unused variable * Moved lines around. Lines now consistent with other TODO PROTOTYPE blocks * Grouping more prototypeManager.Index with datafield definitions * Removed unnecessary code * Added more prototypeManager indexing These ones weren't pointed out by DrSmug. But I think this is all of them? That or my regex is shit. * Remove redundant _damage field * Remove redundant _currentTemperature * Moved variables down * Added prototypeManager indexing to TemperatureComponent * WeaponComponent/System now use ProtptypeManager And as far as I can tell damageType is required, and therefore should never have been null anyway? * Make ranged weapon clumsy fire effects datafields And yes, the order in which the clumsy effects occur is very important. * Made damage on vital body part loss a datafield * Renamed several damageGroup variables to group * Capitalised DamageListToDamageGroup * Make radiation and explosion damage types datafields * Renamed _supportedDamageGroupIDs and _supportedDamageTypeIDs * Fixed mistakes Frogot to remove prototypeManager index DamageTypeTrigger, and wrong variable visibility in TemperatureComponent * Added necessary code Is something tragically wrong? * MeleeWeapon damageType is not actually required * Fixing someone else's mistakes A search comes up with nothing in the yaml files, and its not a required field. So no one uses it? Hopefully? * Changed and renamed damageTypeToDamageGroup Previously would incorrectly return the total container damage for each group, not the total in the group * renaming varitables * Renamed variable DamageClasses * Added dictionary converting functions * Added ID-keyed dictionaries * Making MedicalScanner use ID dictionaries, instead of prototype dictionaries Oh oh no. I've been able to avoid UI & networking up until now. I have no Idea what I am doing. * Fix Medical Scanner * Summary (required) The joke here is that this fixes the empty summary. * Removed DamageableComponent.GetDamageGroup/Type * Renamed "damage classes" to groups. * Update ChangeDamage description * Replaced Heal() with SettAllDamage() Heal() was just a confusing name, * More Class -> Group renaming * Replace Class with Group in yaml files DamageClassTrigger does not appear in any yaml? only in testing? DamageTypeTrigger appears only in human.yaml? HealthChangeMetabolism is Mostly in medicine.yml and one in soad.yaml Why the hell is Cola metabolizable by plants? Who is pouring cola on their plants!?!? * Fix _prototypeManager being null errors. * Changing comments Where are the prototype references * MetabolismComponent doesn't give free heals anymore. * Changes HungerComponent healing. Previously I think it would actually damage you. Only did this as I though it was causing the fast healing. Turns out that was just BREATHING. * Generalised a function in DamageableComponent and moved it to DamageGroupPrototype previously DamageTypesDictToDamageGroupDict was private to DamageableComponent, but was also quite general (nearly a static function). As this sort of function may be needed by other components using DamageGroupPrototypes in the future, I moved it there as a static function instead. * modified DamageableComponent.ChangeDamage() ignoreResistances was renamed to ignoreDamageResistances to make it clearer that it had no effect on healing. Now uses default argument for ignoreDamageResistances, so when healing you are not forced to specify an argument that does nothing. Also made some general changes to ignoreResistances() * Changed class->group and added missing damage type functionality to DamageContainerPrototypes * Added Comments to damage.yml * Misc Changes to DamageableComponent * Differentiated between group support and group applicability So far, every damage type is a member of one, and only one, damage group. So this change has no real effect so far. * Added proposed alternative to ChangeDamage() * fixed error in DamageGroupPrototype * Changes to DamageableComponent Lots of changes to comments. Some variables renamed in IDamageableComponent and DamageableComponent (also required renaming in other files) Some minor logic changes, mostly for incorrect descirptions of boolean return values. Also further differentiating between ApplicableGroups and SupportedGroups... if that will ever even matter * Generalised MedicalScannerComponent If needed, can print miscellaneous damage types now * Fixed HealthChangeMetabolism bug * Changing Comments around * More questions * Made Barotrauma default to blunt * Fix RejuvenateTest.cs * Comments * Coments and variable names * fix some master-merge issues * Removed redundant fields * Misc changes for readbility of PR diff * Consistent naming * Fixed atmos damage bug * Removed Ranting * Fixed Hunger after I broke it * Fixing Bugs * Removed stupid question * Removed more stupid questions * Fix potential null errors. * Made boolean return values consistent Also renamed several functions, to make it clear they return a bool. Docs were also updated. * Removed IoCManager.InjectDependencies() * Removed unnecessary 'suffocation' prefix * Fixed Spelling Also removed accidentally left in logger call * Fixed Medical Scanner * Apply suggestions from code review Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Changing comments and whitespaces * Made damage thresholds trigger datafields required * So many typos * Changes to DamageableComponents Changed documentation in IDamageableComponent Made testing code more readable. Relabelled groups as 'Applicable' either 'Fully Supported' * Removed function and degeneralised * Update DamageableComponent.cs Removed unused parameters Fixed Networking * Added IoCManager.Resolve * Now using alternative TryChangeDamage() * Removed function from DamageGroupPrototype * Removing comments * Remove bad if statement? * Fix damageChanged ordering * Fix hurt server command * Changed //TODO PROTOTYPE blocks Now use PrototypeManager differently. Wherever possible, only retrieve the prototype once. Also added default damage types to some more datafields * Update Content.Shared/Damage/Container/DamageContainerPrototype.cs Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * renamed _accumulatedHealth -> _accumulatedDamage and added TODOs * Another class-> group * Fix bug in generalisation of damage container prototypes * Addes Tests to make sure I dont keep adding bugs to my own code. * Changed Return values when setting * Removed unused class * Added more tests, split tests into three files * Made damage types public and VV read-write-able * Minor changes to DamageableComponent Replaced internal use of GetDamagePerType with _damageDict and removed some unnecessary fields * Fix Suicide, by adding IoC Resolve() * Fix DamageGroupTrigger bug * Fix typos in tests * Change comments./docstrings & spacing * Merge tests, use test prototypes Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Add Alt-click functionality (#4497) * Fix ItemSlot Bug * Add Alt-use Key * Fix TransferAmount window bug * Alt-click functionality * Added AltInteract verbs * Add new verbs * verb icons * Changed Comments * Change Comments * Fix disposal verbs * Changed Get...() to Get...OrNull() * Changed alt-interact combat behaviour * Update verb icons * Inventory interact event * Add Alt+E secondary binding * Add alt-z keybinding * Rename AltUse -> AltActivateItemInWorld * Automatic changelog update * Fixes chocolate/energy bar (#4503) * Bring refactor-damageablecomponent branch up-to-date with master (#4510) * Revert "#3935 implemented suggestions from PR" This reverts commit a9b1c7b96333ca570067d6a9df1954481005892a. * #4219 revert of single sound removal in EmitSoundSystem * #4219 single sounds in EmitSoundSystem should work now * #4219 some small project tweaks * #4219 upgraded EmitSoundSystem to use SoundSpecifier * replacing sound (collection) names with SoundSpecifier - part 1 * #4219 pr tweaks * #4219 pr tweak (cherry picked from commit 00b80cb1df2434259ab5df45188e176be57603af) * emitsoundsystem tweak * first windoor commit * finished yaml * windoors work #Dab * sound + locked prototypes * Inserted SoundSpecifier where appropiate * added comment * removed TryGetSound + made some SoundSpecifier datafields required * changed some prototypes' sound paths * test fixes * Made plasma grindable (#4334) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> * Automatic changelog update * Fixes invalid yaml line (#4408) * Fix potential timer exception * SetTile during extensions test (#4409) Mainly so it can actually account for accurate grid bounds when doing the test Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> * Update submodule * GasFilter appearance, EnabledAtmosDeviceVisualizer improvements. * Add Enabled property to SubFloorHideComponent, make it networked. (#4404) * Refactor IDoorCheck into entity events (#4366) * IDoorCheck refactored to events # Conflicts: # Content.Server/Atmos/TileAtmosphere.cs # Content.Server/Doors/Components/AirlockComponent.cs # Content.Server/Doors/Components/FirelockComponent.cs # Content.Server/Doors/Components/ServerDoorComponent.cs # Content.Server/Doors/IDoorCheck.cs * namespaces * Fix mapinit bug with refreshautoclose * ok i guess these just didnt feel like staging today * Automatic changelog update * AtmosDevices can optionally process in space. (#4405) Refactors some misc atmos things, too. * Fix gravity generator yeeting * Enables nullables for atmos internals * Meth (#4186) * adds an overdose metabolism with sensible default settings * adds the compoenents for the run fast part of meth * not sure what I changed here but I trust my past self to not fuck up for once * adds basic meth recipe * correctly names comething * I really should've checked my spelling before making this pr Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * adds suggestion from the maintaner, who managed to get to this pr suprisingly fast * Revert "adds suggestion from the maintaner, who managed to get to this pr suprisingly fast" This reverts commit 9b0f07402e35ed5890b3af805691b690671b950c. * tweak * tweak * git's gitting on my nerves * some small tweaks * don't need these anymore * makes some stuff required * changes the meth recipe to arbitary bullshit to get the yaml thingy to leave me alone goddamnit * extremely minor change * removes overdose, because however it's gonna be done, it's definitely not my way * i should really double check every key I press * hm * sigh, I should be more thorough with looking at error messages. * beenus * gay sex is gay * this one goes out to bingo * reviews * not sure why status lifetime description wasn't being commited * Update MovespeedModifierMetabolism.cs Co-authored-by: mirrorcult <notzombiedude@gmail.com> * adds VV to component variables * rebalances meth, and makes the completely unrealistic recipe properly work now * meth effects should go away now or something do you think I test these changes before pushing?? * ah yes, orginization * adds proper recipe and prequisite chemicals * fixes linter hopefully * Update chemicals.yml * a * starts working on prediction * thing * predmiction?? * changes thing * does it properly * uses timespan instead of timer * uses dif timers and adds a system * updates robust and tweaks a small thing * Fixes * "Fix" prediction * starts changing the timer to timespans to avoid icky on timer end * okay fixes the check thing but now meth is broken and I don't know why * fixes predicition (partially) * Delete ContentNetIDs.cs whoops * some changes advised by sloth * certified scalycode fix right here * moves resettimer to the metabolism to make it less oop stinky * moves resettimer to the metabolism to make it less oop stinky * gamin * when the is * updates mth colour to be accurate because I forgot * abc hard ok * everything should be up to date now * makes MovespeedModifierMetabolism ECS and cleans up some other stuff * does a fixy wixy * fix thing * Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth" This reverts commit 62886561098be02f9adb6352f4e858a8269d5bd5, reversing changes made to ca34fffb5b7a40f19aec7b9e4bc37bdeab914bc1. * Revert "Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth"" This reverts commit 4f550da19656abfd0be05f818fc6b7100252d5b8. * fix hopefully * updates metabolism to works with mirror's fancy new system * updates yaml + tweaks * bruh * yaml moment * :yaml moment * Revert " :yaml moment" This reverts commit 8cb51573c64db76d989de22acdbb9c50b2c6d052. * 99th commit yay, also I need to not do this * removes something that I don't need * makes system work with this and gets rid of unnesescary check * make the update only work on active components * oops * Cleanup * alphabetise this shit * Touchup * Woops stupid alloc by me * Nerf nyoom for now Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Random offset for DefaultGrid every round (#4411) * Random offset for DefaultGrid every round This is useful to make coders aware of entitycoordinates and mapcoordinates being different and to help spot problems early. It also puts the onus of fixing positioning bugs back onto the original coder rather than someone else if they happen to spot it. * Fix clickable test * Fix entitysystemextensions * Automatic changelog update * Document a few atmos classes. * Update submodule We do a little bit of debugging * Make pulling feel less crap (#4414) Something something pulling refactor someday * Automatic changelog update * Comment joint prediction * Add CVar for random grid offset, disable it by default. * Fixes not taking pressure damage in space. Fixes #4415 * Automatic changelog update * Adds an alternate jumpsuit for botany (#4419) * Adds an alternate botanists jumpsuit * Updated license information * Automatic changelog update * Added two new small areas to maintenence (#4359) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Updated meta.json to standard * Update meta.json to standard * a * Revert "Update meta.json to standard" This reverts commit4c6702f17b. * Revert "Updated meta.json to standard" This reverts commit8ea7a3ad1f. * fix rsi * proper sprites + visualizes nicely * Removes broken new-line markers from a few desc. * Fixes a localization error with solution container descriptions * construction * reviews + test * remove SetLayerVisibility, doesnt work for some reason * swept moment * leftover * Automatic changelog update * Fix can't return to body bug (#4424) * Automatic changelog update * Update submodule * Fix gas tile overlays on shuttles * Offset station on roundstart again * Fix PlaceableSurface incorrect coordinate usage. It set world position to entity coordinate positions... * Automatic changelog update * Fix pickup animation incorrect coordinate usage. Animation finalPosition was world position, while initialPosition was local position. * Automatic changelog update * Pointing coordinate fixes. * Automatic changelog update * Further pickup animation improvements. * Adds Science Windoors (#4433) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Gave CMO a Hardsuit (#4434) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fixed station engineer table spawn (#4436) * Maps in windoors (#4432) * Automatic changelog update * Losing a head doesn't mean you lose your eyes #3956 #4061 (#4225) Co-authored-by: Michael Will <will_m@outlook.de> * Automatic changelog update * Adds windoors for security and the brig. (#4441) * Automatic changelog update * Remaps Chemistry and Security, and some Mapping Fixes (#4442) * Automatic changelog update * Made the cloning pod and medical scanner constructible (#4439) * Made the cloning machine pod constructable * Added everything like I did but with the Medical Scanner Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fix fixed-point format specifier in PowerCellComponent * Update submodule * Actually update submodule The last one was a test to check if you were paying attention. * Update maps for grid collisions (#4450) * Update submodule * Fixes the windoor in science. (#4444) * Automatic changelog update * Remove IActionBlocker.CanMove (#4449) * Remove IActionBlocker.CanMove Ported the remainders over to using MovementAttemptEvent which should also help make mob movement a bit faster. * Make that check faster * Added restrictions for what can be inserted into most belts (#4367) * Belts now restrict what can be inserted into them * Bandolier now only holds shotgun shells * Tiny typo * Another tiny typo * Automatic changelog update * Fix indentation * Fix stuff * Fix gas canister * Organize gas canister prototype * Fix audio occlusion This was previously being done on every SoundComponent. * Call SoundSystem instead * Parents crowbar-red and merges RSIs * Deletes old crowbar_red.rsi * Abstracts BasePlushie and ReagentContainerBase * Adds missing suffixes to debug items and tags them all * Crematorium now makes a noise on start and during cremation (#4459) * Automatic changelog update * Refactor disposals to ECS (#4418) * ECS up disposals Also significantly reduced its CPU usage. * Make update significantly less S L O W * Start units pressurised * Client-side flush lerping * Fix powered not toggling UI * Fix flush button * InteractUsing * Minor optimisations * Fix collisions * Make visual state ECS * Almost done with shared * Most stuff moved * Optimise item sleeping * Automatic changelog update * Fix door sound dampening (#4453) * Completely fix NPC pathfinding Through rigorous investigation and hard work. * Implement Entity List Display and rework StorageComponent window (#4140) * Create EntityListDisplay * Rework ClientStorage window * Add styling * Remove unnecessary colors * Rename list * Make scrollbar push content * Change children update a bit * Add old index * Localize ClientStorageComponent * Add size return * Remove spaces * Fix usings * Fix tool sounds. Fixes #4465 * Automatic changelog update * Fix missing sounds (#4466) * Fix missing sounds * Make SoundHitSpecies fallback to SoundHit * Fix crayon YAML * Update PlaySoundBehavior YAML * Fix required * Update README.md * Fix taser physics (#4470) * Increase MoverController performance (#4448) Should be a decent amount; rest will come from removing IActionBlocker. * Remove throwing fixture error * Update submodule * Fix sound crash when making foam (#4476) * Automatic changelog update * Centers some sprites * Fix showatmos * Fix tests. Flashlight RSI state names had been changed, and some places weren't updated properly * Remove pillcomponent (#4469) * Remove PillComponent * Make food without any solution left delete and create trash * Replace PillComponent references with a Pill tag * Clean up * Add swallow message to food * Change to eatMessage override * Change FoodComponent transferAmount to nullable * Change properties to private * ItemCabinetVisualizer uses layers (#4445) * ItemCabinetVisualizer uses layers * Reviews applied * Fixes the fireaxe cabinet as well * I'm dumb * Fixes bedsheets being unpickupable (#4479) * Automatic changelog update * Adds inhands for crayons and the crayon box (#4481) * Automatic changelog update * Fixes cigarette animations and matches now have lit/unlit inhand sprites (#4480) * Automatic changelog update * Adds plant clippers and changes hydro tool sprites to be more consistent (#4482) * Adds plant clippers * Updates sycthe, hoe and spade sprites * Adds inhands for radio/walkietalkie (#4483) * Adds inhands for radio/walkietalkie and separates layers * License thingy * Fixes test * Can now equip fireaxe, claymore, spear and baseball back on your back (#4484) * Can equip spears and claymores on your back now * Adds fireaxe and baseball bat back equip * Automatic changelog update * Makes Match[sticks/box] ECS, Makes Matchsticks ignite plasma * am big dumdum pls no shed * Owner Transform to Owner in SoundSystem calls (#4460) * Owner Transform to Owner * Owner Transform to Owner minor fixes * Improve hand pickup code This way I can break EntityCoordinates' API without breaking content. BWAHAHAHAHAH! * Update submodule * Version v0.5.11 does not exist * Update submodule We don't talk about v0.5.12 either. * Update submodule For the last time * Adds duck-related content and a seclite (#4489) * Added rubber ducky * Adds ducky slippers * Adds seclite * Update Resources/Textures/Objects/Tools/seclite.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Fun/ducky.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * The duck slippers now quack as you walk Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Automatic changelog update * Adds the ability to assign equip sounds in ClothingComponent (#4485) * Initial * Works (Kinda) * Polish * Reviews * Removes vending inventory desc and adds missing vending machine descriptions (#4493) * Removes inventory descriptions * Adds missing vending machine descriptions using inventory descriptions * Woops missed a file * Automatic changelog update * Adds a ton more inhands (#4488) * Removed useless loc and added inhands for some lamps * Adds a ton of inhands to the game * Actually adds the pill inhand * Update Resources/Textures/Objects/Misc/utensils.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Tools/rcd.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Added open/close/stacklayers for Matchbox and inhands (#4496) * Added open/close/stacklayers for Matchbox * Added inhands for matchbox * What am I a clown not gonna leave no indent spaces * Adds sound to fire extinguisher safety (#4494) * Automatic changelog update * Adds a 6pack of Cola (#4499) * Adds a 6pack of Cola * Revert "Adds a 6pack of Cola" This reverts commit 99f024b94ab3c684ce62e28018bd0fe2f26a2e09. * Revert "Revert "Adds a 6pack of Cola"" This reverts commit 9e3d31e41c8ea61a962ad7d4d3c0ea7124e82653. * Automatic changelog update * Fix H-pulling? (#4425) In retrospect the answer is easy, but man this took a while. * Automatic changelog update * Makes DiceComponent ECS * Makes PlaceableSurface ECS * Added classic announcement sound to announcements (#4504) * Automatic changelog update * Refactors throw events, makes cream pies ECS (#4500) * Automatic changelog update * Port the tumbler from Box2D (#4486) * Subscribe TransformComponent events by-ref (#4478) * Add Alt-click functionality (#4497) * Fix ItemSlot Bug * Add Alt-use Key * Fix TransferAmount window bug * Alt-click functionality * Added AltInteract verbs * Add new verbs * verb icons * Changed Comments * Change Comments * Fix disposal verbs * Changed Get...() to Get...OrNull() * Changed alt-interact combat behaviour * Update verb icons * Inventory interact event * Add Alt+E secondary binding * Add alt-z keybinding * Rename AltUse -> AltActivateItemInWorld * Automatic changelog update * Fixes chocolate/energy bar (#4503) * Fix merge issues Co-authored-by: Galactic Chimp <GalacticChimpanzee@gmail.com> Co-authored-by: Kara Dinyes <lunarautomaton6@gmail.com> Co-authored-by: SethLafuente <84478872+SethLafuente@users.noreply.github.com> Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Vera Aguilera Puerto <gradientvera@outlook.com> Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: ScalyChimp <72841710+scaly-chimp@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: Jaskanbe <86671825+Jaskanbe@users.noreply.github.com> Co-authored-by: scrato <Mickaello2003@gmx.de> Co-authored-by: Michael Will <will_m@outlook.de> Co-authored-by: TimrodDX <timrod@gmail.com> Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: Ygg01 <y.laughing.man.y@gmail.com> Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: ColdAutumnRain <73938872+ColdAutumnRain@users.noreply.github.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Singularity fixes (#4383) * Singularity fixes * Fix the rest * Woops * ahh * Nerf singulo for now * Final touchups for now * Review * Automatic changelog update * Fix PA construction crash, make it clear that PA construction needs LV cables (#4506) * Automatic changelog update * Add security barriers (#4458) * Add sprites * Lock system now raises lock toggle events * Add prototype and barrier system * Toggle lock on click * Barrier blocks bullets (like a real wall) * Barrier now destroyable * Fancy visualzer and lighting. Also unlock by default * Deleted comma * Ignored components? * Update Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Resources/Prototypes/Entities/Objects/Specific/Security/barrier.yml Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Toggle Lock no longer handled * Made it much easier to move through airlocks Co-authored-by: Swept <sweptwastaken@protonmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Automatic changelog update * Updates name of LV cable coil * Admin ghosts can now interact with stuff (#4178) * Ghosts now have a bool for interacting with stuff * Wrong ghost * Simping for Swept * Merge cleanup * IT'S ODNE Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Added Basic Parts Technology (#4440) * Added parts Technology * is something they're something bad grammar * Update Parts.yml * Update technologies.yml Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> * Add basic meteor swarm (#4420) * Add basic meteor swarm * Map fixes * Dependency cache * Fix projectile crash * Last of the reviews * Automatic changelog update * Update content for PhysicsMapComponent (#4462) * Update content for PhysicsMapComponent * Fix command * Cache broadphasesystem * Update submodule * Update SS14 for grid contraction (#4452) * Update SS14 for grid contraction * Remove more dummy chunks * Update submodule * ratio's rects * Add DamageType And DamageGroup Prototypes * Remove DamageTypePrototype Field "name" as its redundant * Change I/DamageableComponent to use prototypes * Update DamageContainer, ReisistanceSet and DamageChangeData * Change Barotrauma Component to use DamageType from DamageSystem * Update AsteroidRockComponent * update some more components * update some more components * Fix m o r e c o m p o n e n t s and their damageType * all thats left is bug/missing node hunting then verification. * push changes * update submodule * Merge fixes * push DGP for example * update damagecomponent across shared and server * fix a few bugs * Fix Merge issues * Refactor damageablecomponent update (#4406) * Fixing merge. I messed up part of the merge. this should fix it? * Barotrauma now uses prototypeManager As System.Runtime.CompilerServices also has a [Dependency], I think I had to use the full path [Robust.Shared.IoC.Dependency] * FlammableComponent now uses prototypeManager * SuicideCommands now use prototypeManager * Changed many files to use prototypeManager to resolve damaege prototypes Yeah.... prototype references would be very nice. maybe this was all a waste of time. * Grouping prototypeManager.Index with datafield definitions This will make it easier to eventually add prototype references * removed unused variable * Moved lines around. Lines now consistent with other TODO PROTOTYPE blocks * Grouping more prototypeManager.Index with datafield definitions * Removed unnecessary code * Added more prototypeManager indexing These ones weren't pointed out by DrSmug. But I think this is all of them? That or my regex is shit. * Remove redundant _damage field * Remove redundant _currentTemperature * Moved variables down * Added prototypeManager indexing to TemperatureComponent * WeaponComponent/System now use ProtptypeManager And as far as I can tell damageType is required, and therefore should never have been null anyway? * Make ranged weapon clumsy fire effects datafields And yes, the order in which the clumsy effects occur is very important. * Made damage on vital body part loss a datafield * Renamed several damageGroup variables to group * Capitalised DamageListToDamageGroup * Make radiation and explosion damage types datafields * Renamed _supportedDamageGroupIDs and _supportedDamageTypeIDs * Fixed mistakes Frogot to remove prototypeManager index DamageTypeTrigger, and wrong variable visibility in TemperatureComponent * Added necessary code Is something tragically wrong? * MeleeWeapon damageType is not actually required * Fixing someone else's mistakes A search comes up with nothing in the yaml files, and its not a required field. So no one uses it? Hopefully? * Changed and renamed damageTypeToDamageGroup Previously would incorrectly return the total container damage for each group, not the total in the group * renaming varitables * Renamed variable DamageClasses * Added dictionary converting functions * Added ID-keyed dictionaries * Making MedicalScanner use ID dictionaries, instead of prototype dictionaries Oh oh no. I've been able to avoid UI & networking up until now. I have no Idea what I am doing. * Fix Medical Scanner * Summary (required) The joke here is that this fixes the empty summary. * Removed DamageableComponent.GetDamageGroup/Type * Renamed "damage classes" to groups. * Update ChangeDamage description * Replaced Heal() with SettAllDamage() Heal() was just a confusing name, * More Class -> Group renaming * Replace Class with Group in yaml files DamageClassTrigger does not appear in any yaml? only in testing? DamageTypeTrigger appears only in human.yaml? HealthChangeMetabolism is Mostly in medicine.yml and one in soad.yaml Why the hell is Cola metabolizable by plants? Who is pouring cola on their plants!?!? * Fix _prototypeManager being null errors. * Changing comments Where are the prototype references * MetabolismComponent doesn't give free heals anymore. * Changes HungerComponent healing. Previously I think it would actually damage you. Only did this as I though it was causing the fast healing. Turns out that was just BREATHING. * Generalised a function in DamageableComponent and moved it to DamageGroupPrototype previously DamageTypesDictToDamageGroupDict was private to DamageableComponent, but was also quite general (nearly a static function). As this sort of function may be needed by other components using DamageGroupPrototypes in the future, I moved it there as a static function instead. * modified DamageableComponent.ChangeDamage() ignoreResistances was renamed to ignoreDamageResistances to make it clearer that it had no effect on healing. Now uses default argument for ignoreDamageResistances, so when healing you are not forced to specify an argument that does nothing. Also made some general changes to ignoreResistances() * Changed class->group and added missing damage type functionality to DamageContainerPrototypes * Added Comments to damage.yml * Misc Changes to DamageableComponent * Differentiated between group support and group applicability So far, every damage type is a member of one, and only one, damage group. So this change has no real effect so far. * Added proposed alternative to ChangeDamage() * fixed error in DamageGroupPrototype * Changes to DamageableComponent Lots of changes to comments. Some variables renamed in IDamageableComponent and DamageableComponent (also required renaming in other files) Some minor logic changes, mostly for incorrect descirptions of boolean return values. Also further differentiating between ApplicableGroups and SupportedGroups... if that will ever even matter * Generalised MedicalScannerComponent If needed, can print miscellaneous damage types now * Fixed HealthChangeMetabolism bug * Changing Comments around * More questions * Made Barotrauma default to blunt * Fix RejuvenateTest.cs * Comments * Coments and variable names * fix some master-merge issues * Removed redundant fields * Misc changes for readbility of PR diff * Consistent naming * Fixed atmos damage bug * Removed Ranting * Fixed Hunger after I broke it * Fixing Bugs * Removed stupid question * Removed more stupid questions * Fix potential null errors. * Made boolean return values consistent Also renamed several functions, to make it clear they return a bool. Docs were also updated. * Removed IoCManager.InjectDependencies() * Removed unnecessary 'suffocation' prefix * Fixed Spelling Also removed accidentally left in logger call * Fixed Medical Scanner * Apply suggestions from code review Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Changing comments and whitespaces * Made damage thresholds trigger datafields required * So many typos * Changes to DamageableComponents Changed documentation in IDamageableComponent Made testing code more readable. Relabelled groups as 'Applicable' either 'Fully Supported' * Removed function and degeneralised * Update DamageableComponent.cs Removed unused parameters Fixed Networking * Added IoCManager.Resolve * Now using alternative TryChangeDamage() * Removed function from DamageGroupPrototype * Removing comments * Remove bad if statement? * Fix damageChanged ordering * Fix hurt server command * Changed //TODO PROTOTYPE blocks Now use PrototypeManager differently. Wherever possible, only retrieve the prototype once. Also added default damage types to some more datafields * Update Content.Shared/Damage/Container/DamageContainerPrototype.cs Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * renamed _accumulatedHealth -> _accumulatedDamage and added TODOs * Another class-> group * Fix bug in generalisation of damage container prototypes * Addes Tests to make sure I dont keep adding bugs to my own code. * Changed Return values when setting * Removed unused class * Added more tests, split tests into three files * Made damage types public and VV read-write-able * Minor changes to DamageableComponent Replaced internal use of GetDamagePerType with _damageDict and removed some unnecessary fields * Fix Suicide, by adding IoC Resolve() * Fix DamageGroupTrigger bug * Fix typos in tests * Change comments./docstrings & spacing * Merge tests, use test prototypes Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> * Bring refactor-damageablecomponent branch up-to-date with master (#4510) * Revert "#3935 implemented suggestions from PR" This reverts commit a9b1c7b96333ca570067d6a9df1954481005892a. * #4219 revert of single sound removal in EmitSoundSystem * #4219 single sounds in EmitSoundSystem should work now * #4219 some small project tweaks * #4219 upgraded EmitSoundSystem to use SoundSpecifier * replacing sound (collection) names with SoundSpecifier - part 1 * #4219 pr tweaks * #4219 pr tweak (cherry picked from commit 00b80cb1df2434259ab5df45188e176be57603af) * emitsoundsystem tweak * first windoor commit * finished yaml * windoors work #Dab * sound + locked prototypes * Inserted SoundSpecifier where appropiate * added comment * removed TryGetSound + made some SoundSpecifier datafields required * changed some prototypes' sound paths * test fixes * Made plasma grindable (#4334) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> * Automatic changelog update * Fixes invalid yaml line (#4408) * Fix potential timer exception * SetTile during extensions test (#4409) Mainly so it can actually account for accurate grid bounds when doing the test Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> * Update submodule * GasFilter appearance, EnabledAtmosDeviceVisualizer improvements. * Add Enabled property to SubFloorHideComponent, make it networked. (#4404) * Refactor IDoorCheck into entity events (#4366) * IDoorCheck refactored to events # Conflicts: # Content.Server/Atmos/TileAtmosphere.cs # Content.Server/Doors/Components/AirlockComponent.cs # Content.Server/Doors/Components/FirelockComponent.cs # Content.Server/Doors/Components/ServerDoorComponent.cs # Content.Server/Doors/IDoorCheck.cs * namespaces * Fix mapinit bug with refreshautoclose * ok i guess these just didnt feel like staging today * Automatic changelog update * AtmosDevices can optionally process in space. (#4405) Refactors some misc atmos things, too. * Fix gravity generator yeeting * Enables nullables for atmos internals * Meth (#4186) * adds an overdose metabolism with sensible default settings * adds the compoenents for the run fast part of meth * not sure what I changed here but I trust my past self to not fuck up for once * adds basic meth recipe * correctly names comething * I really should've checked my spelling before making this pr Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * adds suggestion from the maintaner, who managed to get to this pr suprisingly fast * Revert "adds suggestion from the maintaner, who managed to get to this pr suprisingly fast" This reverts commit 9b0f07402e35ed5890b3af805691b690671b950c. * tweak * tweak * git's gitting on my nerves * some small tweaks * don't need these anymore * makes some stuff required * changes the meth recipe to arbitary bullshit to get the yaml thingy to leave me alone goddamnit * extremely minor change * removes overdose, because however it's gonna be done, it's definitely not my way * i should really double check every key I press * hm * sigh, I should be more thorough with looking at error messages. * beenus * gay sex is gay * this one goes out to bingo * reviews * not sure why status lifetime description wasn't being commited * Update MovespeedModifierMetabolism.cs Co-authored-by: mirrorcult <notzombiedude@gmail.com> * adds VV to component variables * rebalances meth, and makes the completely unrealistic recipe properly work now * meth effects should go away now or something do you think I test these changes before pushing?? * ah yes, orginization * adds proper recipe and prequisite chemicals * fixes linter hopefully * Update chemicals.yml * a * starts working on prediction * thing * predmiction?? * changes thing * does it properly * uses timespan instead of timer * uses dif timers and adds a system * updates robust and tweaks a small thing * Fixes * "Fix" prediction * starts changing the timer to timespans to avoid icky on timer end * okay fixes the check thing but now meth is broken and I don't know why * fixes predicition (partially) * Delete ContentNetIDs.cs whoops * some changes advised by sloth * certified scalycode fix right here * moves resettimer to the metabolism to make it less oop stinky * moves resettimer to the metabolism to make it less oop stinky * gamin * when the is * updates mth colour to be accurate because I forgot * abc hard ok * everything should be up to date now * makes MovespeedModifierMetabolism ECS and cleans up some other stuff * does a fixy wixy * fix thing * Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth" This reverts commit 62886561098be02f9adb6352f4e858a8269d5bd5, reversing changes made to ca34fffb5b7a40f19aec7b9e4bc37bdeab914bc1. * Revert "Revert "Merge branch 'master' of https://github.com/space-wizards/space-station-14 into meth"" This reverts commit 4f550da19656abfd0be05f818fc6b7100252d5b8. * fix hopefully * updates metabolism to works with mirror's fancy new system * updates yaml + tweaks * bruh * yaml moment * :yaml moment * Revert " :yaml moment" This reverts commit 8cb51573c64db76d989de22acdbb9c50b2c6d052. * 99th commit yay, also I need to not do this * removes something that I don't need * makes system work with this and gets rid of unnesescary check * make the update only work on active components * oops * Cleanup * alphabetise this shit * Touchup * Woops stupid alloc by me * Nerf nyoom for now Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> * Automatic changelog update * Random offset for DefaultGrid every round (#4411) * Random offset for DefaultGrid every round This is useful to make coders aware of entitycoordinates and mapcoordinates being different and to help spot problems early. It also puts the onus of fixing positioning bugs back onto the original coder rather than someone else if they happen to spot it. * Fix clickable test * Fix entitysystemextensions * Automatic changelog update * Document a few atmos classes. * Update submodule We do a little bit of debugging * Make pulling feel less crap (#4414) Something something pulling refactor someday * Automatic changelog update * Comment joint prediction * Add CVar for random grid offset, disable it by default. * Fixes not taking pressure damage in space. Fixes #4415 * Automatic changelog update * Adds an alternate jumpsuit for botany (#4419) * Adds an alternate botanists jumpsuit * Updated license information * Automatic changelog update * Added two new small areas to maintenence (#4359) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Updated meta.json to standard * Update meta.json to standard * a * Revert "Update meta.json to standard" This reverts commit4c6702f17b. * Revert "Updated meta.json to standard" This reverts commit8ea7a3ad1f. * fix rsi * proper sprites + visualizes nicely * Removes broken new-line markers from a few desc. * Fixes a localization error with solution container descriptions * construction * reviews + test * remove SetLayerVisibility, doesnt work for some reason * swept moment * leftover * Automatic changelog update * Fix can't return to body bug (#4424) * Automatic changelog update * Update submodule * Fix gas tile overlays on shuttles * Offset station on roundstart again * Fix PlaceableSurface incorrect coordinate usage. It set world position to entity coordinate positions... * Automatic changelog update * Fix pickup animation incorrect coordinate usage. Animation finalPosition was world position, while initialPosition was local position. * Automatic changelog update * Pointing coordinate fixes. * Automatic changelog update * Further pickup animation improvements. * Adds Science Windoors (#4433) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Gave CMO a Hardsuit (#4434) Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fixed station engineer table spawn (#4436) * Maps in windoors (#4432) * Automatic changelog update * Losing a head doesn't mean you lose your eyes #3956 #4061 (#4225) Co-authored-by: Michael Will <will_m@outlook.de> * Automatic changelog update * Adds windoors for security and the brig. (#4441) * Automatic changelog update * Remaps Chemistry and Security, and some Mapping Fixes (#4442) * Automatic changelog update * Made the cloning pod and medical scanner constructible (#4439) * Made the cloning machine pod constructable * Added everything like I did but with the Medical Scanner Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> * Automatic changelog update * Fix fixed-point format specifier in PowerCellComponent * Update submodule * Actually update submodule The last one was a test to check if you were paying attention. * Update maps for grid collisions (#4450) * Update submodule * Fixes the windoor in science. (#4444) * Automatic changelog update * Remove IActionBlocker.CanMove (#4449) * Remove IActionBlocker.CanMove Ported the remainders over to using MovementAttemptEvent which should also help make mob movement a bit faster. * Make that check faster * Added restrictions for what can be inserted into most belts (#4367) * Belts now restrict what can be inserted into them * Bandolier now only holds shotgun shells * Tiny typo * Another tiny typo * Automatic changelog update * Fix indentation * Fix stuff * Fix gas canister * Organize gas canister prototype * Fix audio occlusion This was previously being done on every SoundComponent. * Call SoundSystem instead * Parents crowbar-red and merges RSIs * Deletes old crowbar_red.rsi * Abstracts BasePlushie and ReagentContainerBase * Adds missing suffixes to debug items and tags them all * Crematorium now makes a noise on start and during cremation (#4459) * Automatic changelog update * Refactor disposals to ECS (#4418) * ECS up disposals Also significantly reduced its CPU usage. * Make update significantly less S L O W * Start units pressurised * Client-side flush lerping * Fix powered not toggling UI * Fix flush button * InteractUsing * Minor optimisations * Fix collisions * Make visual state ECS * Almost done with shared * Most stuff moved * Optimise item sleeping * Automatic changelog update * Fix door sound dampening (#4453) * Completely fix NPC pathfinding Through rigorous investigation and hard work. * Implement Entity List Display and rework StorageComponent window (#4140) * Create EntityListDisplay * Rework ClientStorage window * Add styling * Remove unnecessary colors * Rename list * Make scrollbar push content * Change children update a bit * Add old index * Localize ClientStorageComponent * Add size return * Remove spaces * Fix usings * Fix tool sounds. Fixes #4465 * Automatic changelog update * Fix missing sounds (#4466) * Fix missing sounds * Make SoundHitSpecies fallback to SoundHit * Fix crayon YAML * Update PlaySoundBehavior YAML * Fix required * Update README.md * Fix taser physics (#4470) * Increase MoverController performance (#4448) Should be a decent amount; rest will come from removing IActionBlocker. * Remove throwing fixture error * Update submodule * Fix sound crash when making foam (#4476) * Automatic changelog update * Centers some sprites * Fix showatmos * Fix tests. Flashlight RSI state names had been changed, and some places weren't updated properly * Remove pillcomponent (#4469) * Remove PillComponent * Make food without any solution left delete and create trash * Replace PillComponent references with a Pill tag * Clean up * Add swallow message to food * Change to eatMessage override * Change FoodComponent transferAmount to nullable * Change properties to private * ItemCabinetVisualizer uses layers (#4445) * ItemCabinetVisualizer uses layers * Reviews applied * Fixes the fireaxe cabinet as well * I'm dumb * Fixes bedsheets being unpickupable (#4479) * Automatic changelog update * Adds inhands for crayons and the crayon box (#4481) * Automatic changelog update * Fixes cigarette animations and matches now have lit/unlit inhand sprites (#4480) * Automatic changelog update * Adds plant clippers and changes hydro tool sprites to be more consistent (#4482) * Adds plant clippers * Updates sycthe, hoe and spade sprites * Adds inhands for radio/walkietalkie (#4483) * Adds inhands for radio/walkietalkie and separates layers * License thingy * Fixes test * Can now equip fireaxe, claymore, spear and baseball back on your back (#4484) * Can equip spears and claymores on your back now * Adds fireaxe and baseball bat back equip * Automatic changelog update * Makes Match[sticks/box] ECS, Makes Matchsticks ignite plasma * am big dumdum pls no shed * Owner Transform to Owner in SoundSystem calls (#4460) * Owner Transform to Owner * Owner Transform to Owner minor fixes * Improve hand pickup code This way I can break EntityCoordinates' API without breaking content. BWAHAHAHAHAH! * Update submodule * Version v0.5.11 does not exist * Update submodule We don't talk about v0.5.12 either. * Update submodule For the last time * Adds duck-related content and a seclite (#4489) * Added rubber ducky * Adds ducky slippers * Adds seclite * Update Resources/Textures/Objects/Tools/seclite.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Fun/ducky.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * The duck slippers now quack as you walk Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Automatic changelog update * Adds the ability to assign equip sounds in ClothingComponent (#4485) * Initial * Works (Kinda) * Polish * Reviews * Removes vending inventory desc and adds missing vending machine descriptions (#4493) * Removes inventory descriptions * Adds missing vending machine descriptions using inventory descriptions * Woops missed a file * Automatic changelog update * Adds a ton more inhands (#4488) * Removed useless loc and added inhands for some lamps * Adds a ton of inhands to the game * Actually adds the pill inhand * Update Resources/Textures/Objects/Misc/utensils.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Update Resources/Textures/Objects/Tools/rcd.rsi/meta.json Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> * Added open/close/stacklayers for Matchbox and inhands (#4496) * Added open/close/stacklayers for Matchbox * Added inhands for matchbox * What am I a clown not gonna leave no indent spaces * Adds sound to fire extinguisher safety (#4494) * Automatic changelog update * Adds a 6pack of Cola (#4499) * Adds a 6pack of Cola * Revert "Adds a 6pack of Cola" This reverts commit 99f024b94ab3c684ce62e28018bd0fe2f26a2e09. * Revert "Revert "Adds a 6pack of Cola"" This reverts commit 9e3d31e41c8ea61a962ad7d4d3c0ea7124e82653. * Automatic changelog update * Fix H-pulling? (#4425) In retrospect the answer is easy, but man this took a while. * Automatic changelog update * Makes DiceComponent ECS * Makes PlaceableSurface ECS * Added classic announcement sound to announcements (#4504) * Automatic changelog update * Refactors throw events, makes cream pies ECS (#4500) * Automatic changelog update * Port the tumbler from Box2D (#4486) * Subscribe TransformComponent events by-ref (#4478) * Add Alt-click functionality (#4497) * Fix ItemSlot Bug * Add Alt-use Key * Fix TransferAmount window bug * Alt-click functionality * Added AltInteract verbs * Add new verbs * verb icons * Changed Comments * Change Comments * Fix disposal verbs * Changed Get...() to Get...OrNull() * Changed alt-interact combat behaviour * Update verb icons * Inventory interact event * Add Alt+E secondary binding * Add alt-z keybinding * Rename AltUse -> AltActivateItemInWorld * Automatic changelog update * Fixes chocolate/energy bar (#4503) * Fix merge issues Co-authored-by: Galactic Chimp <GalacticChimpanzee@gmail.com> Co-authored-by: Kara Dinyes <lunarautomaton6@gmail.com> Co-authored-by: SethLafuente <84478872+SethLafuente@users.noreply.github.com> Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Vera Aguilera Puerto <gradientvera@outlook.com> Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: ScalyChimp <72841710+scaly-chimp@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: Jaskanbe <86671825+Jaskanbe@users.noreply.github.com> Co-authored-by: scrato <Mickaello2003@gmx.de> Co-authored-by: Michael Will <will_m@outlook.de> Co-authored-by: TimrodDX <timrod@gmail.com> Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: Ygg01 <y.laughing.man.y@gmail.com> Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: ColdAutumnRain <73938872+ColdAutumnRain@users.noreply.github.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com> Co-authored-by: Galactic Chimp <GalacticChimpanzee@gmail.com> Co-authored-by: Kara Dinyes <lunarautomaton6@gmail.com> Co-authored-by: Leon Friedrich <leonsfriedrich@gmail.com> Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com> Co-authored-by: SethLafuente <84478872+SethLafuente@users.noreply.github.com> Co-authored-by: SETh lafuente <cetaciocascarudo@gmail.com> Co-authored-by: Swept <sweptwastaken@protonmail.com> Co-authored-by: PJBot <pieterjan.briers+bot@gmail.com> Co-authored-by: metalgearsloth <metalgearsloth@gmail.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Vera Aguilera Puerto <gradientvera@outlook.com> Co-authored-by: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com> Co-authored-by: ScalyChimp <72841710+scaly-chimp@users.noreply.github.com> Co-authored-by: mirrorcult <notzombiedude@gmail.com> Co-authored-by: Jaskanbe <86671825+Jaskanbe@users.noreply.github.com> Co-authored-by: scrato <Mickaello2003@gmx.de> Co-authored-by: Michael Will <will_m@outlook.de> Co-authored-by: TimrodDX <timrod@gmail.com> Co-authored-by: Leon Friedrich <60421075+leonsfriedrich@users.noreply.github.com> Co-authored-by: Ygg01 <y.laughing.man.y@gmail.com> Co-authored-by: Paul <ritter.paul1+git@googlemail.com> Co-authored-by: ColdAutumnRain <73938872+ColdAutumnRain@users.noreply.github.com> Co-authored-by: Paul Ritter <ritter.paul1@googlemail.com> Co-authored-by: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Co-authored-by: 20kdc <asdd2808@gmail.com> Co-authored-by: Alex Evgrashin <aevgrashin@yandex.ru>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Alert;
|
||||
using Content.Server.Pressure;
|
||||
@@ -7,6 +7,24 @@ using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
@@ -18,6 +36,32 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
public override string Name => "Barotrauma";
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly string _damageType = default!;
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")] private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Update(float airPressure)
|
||||
{
|
||||
@@ -40,11 +84,20 @@ namespace Content.Server.Atmos.Components
|
||||
// Low pressure.
|
||||
case var p when p <= Atmospherics.WarningLowPressure:
|
||||
pressure *= lowPressureMultiplier;
|
||||
|
||||
if(pressure > Atmospherics.WarningLowPressure)
|
||||
if (pressure > Atmospherics.WarningLowPressure)
|
||||
goto default;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageType.Blunt, Atmospherics.LowPressureDamage, false, Owner);
|
||||
=======
|
||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||
damageable.TryChangeDamage(DamageType, Atmospherics.LowPressureDamage,true);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (status == null) break;
|
||||
|
||||
@@ -66,7 +119,21 @@ namespace Content.Server.Atmos.Components
|
||||
|
||||
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageType.Blunt, damage, false, Owner);
|
||||
=======
|
||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), damage, false, Owner);
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||
damageable.TryChangeDamage(DamageType, damage,true);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
// Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
|
||||
damageable.TryChangeDamage(DamageType, damage,true);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (status == null) break;
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ using Robust.Shared.Localization;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
@@ -43,6 +45,18 @@ namespace Content.Server.Atmos.Components
|
||||
[DataField("canResistFire")]
|
||||
public bool CanResistFire { get; private set; } = false;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Heat"!;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
public void Extinguish()
|
||||
{
|
||||
if (!OnFire) return;
|
||||
@@ -92,7 +106,15 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
// TODO ATMOS Fire resistance from armor
|
||||
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageClass.Burn, damage, false);
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, damage, false);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, damage, false);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Body.Part;
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Body
|
||||
@@ -10,7 +10,7 @@ namespace Content.Server.Body
|
||||
}
|
||||
|
||||
// TODO BODY: Remove and pretend it never existed
|
||||
public class BodyDamageChangeParams : DamageChangeParams, IBodyHealthChangeParams
|
||||
public class BodyDamageChangeParams : IBodyHealthChangeParams
|
||||
{
|
||||
public BodyDamageChangeParams(BodyPartType part)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,8 @@ using Content.Shared.Atmos;
|
||||
using Content.Shared.Body.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Content.Shared.MobState;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -34,10 +36,22 @@ namespace Content.Server.Body.Respiratory
|
||||
private bool _isShivering;
|
||||
private bool _isSweating;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
||||
=======
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly string _damageType = default!;
|
||||
|
||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
||||
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
||||
|
||||
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
||||
@@ -94,6 +108,22 @@ namespace Content.Server.Body.Respiratory
|
||||
|
||||
[ViewVariables] public bool Suffocating { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _damage = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _damageRecovery = 1;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Asphyxiation"!;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
||||
{
|
||||
var needs = new Dictionary<Gas, float>(NeedsGases);
|
||||
@@ -349,7 +379,19 @@ namespace Content.Server.Body.Respiratory
|
||||
return;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
||||
damageable.ChangeDamage(DamageType.Asphyxiation, _suffocationDamage, false);
|
||||
=======
|
||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), _suffocationDamage, false);
|
||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, _damage, false);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, _damage, false);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
private void StopSuffocation()
|
||||
@@ -358,7 +400,19 @@ namespace Content.Server.Body.Respiratory
|
||||
|
||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master:Content.Server/Body/Respiratory/RespiratorComponent.cs
|
||||
damageable.ChangeDamage(DamageType.Asphyxiation, -_suffocationDamageRecovery, false);
|
||||
=======
|
||||
damageable.ChangeDamage(damageable.GetDamageType(_damageType), -_suffocationDamageRecovery, false);
|
||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Metabolism/MetabolismComponent.cs
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damageable.TryChangeDamage(DamageType, -_damageRecovery, false);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
|
||||
@@ -17,6 +17,7 @@ using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Chat.Commands
|
||||
{
|
||||
@@ -34,8 +35,11 @@ namespace Content.Server.Chat.Commands
|
||||
var kind = suicide.Suicide(target, chat);
|
||||
if (kind != SuicideKind.Special)
|
||||
{
|
||||
damageableComponent.SetDamage(kind switch
|
||||
var prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
damageableComponent.TrySetDamage(kind switch
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
SuicideKind.Blunt => DamageType.Blunt,
|
||||
SuicideKind.Slash => DamageType.Slash,
|
||||
SuicideKind.Piercing => DamageType.Piercing,
|
||||
@@ -47,8 +51,26 @@ namespace Content.Server.Chat.Commands
|
||||
SuicideKind.Asphyxiation => DamageType.Asphyxiation,
|
||||
SuicideKind.Bloodloss => DamageType.Bloodloss,
|
||||
_ => DamageType.Blunt
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
SuicideKind.Blunt => prototypeManager.Index<DamageTypePrototype>("Blunt"),
|
||||
SuicideKind.Slash => prototypeManager.Index<DamageTypePrototype>("Slash"),
|
||||
SuicideKind.Piercing => prototypeManager.Index<DamageTypePrototype>("Piercing"),
|
||||
SuicideKind.Heat => prototypeManager.Index<DamageTypePrototype>("Heat"),
|
||||
SuicideKind.Shock => prototypeManager.Index<DamageTypePrototype>("Shock"),
|
||||
SuicideKind.Cold => prototypeManager.Index<DamageTypePrototype>("Cold"),
|
||||
SuicideKind.Poison => prototypeManager.Index<DamageTypePrototype>("Poison"),
|
||||
SuicideKind.Radiation => prototypeManager.Index<DamageTypePrototype>("Radiation"),
|
||||
SuicideKind.Asphyxiation => prototypeManager.Index<DamageTypePrototype>("Asphyxiation"),
|
||||
SuicideKind.Bloodloss => prototypeManager.Index<DamageTypePrototype>("Bloodloss"),
|
||||
_ => prototypeManager.Index<DamageTypePrototype>("Blunt")
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
},
|
||||
200, source);
|
||||
200);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +139,15 @@ namespace Content.Server.Chat.Commands
|
||||
var selfMessage = Loc.GetString("suicide-command-default-text-self");
|
||||
owner.PopupMessage(selfMessage);
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
dmgComponent.SetDamage(DamageType.Piercing, 200, owner);
|
||||
=======
|
||||
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
dmgComponent.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Piercing"), 200);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
// Prevent the player from returning to the body.
|
||||
// Note that mind cannot be null because otherwise owner would be null.
|
||||
|
||||
@@ -5,6 +5,9 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
|
||||
namespace Content.Server.Chemistry.ReagentEffects
|
||||
{
|
||||
@@ -12,7 +15,7 @@ namespace Content.Server.Chemistry.ReagentEffects
|
||||
/// Default metabolism for medicine reagents. Attempts to find a DamageableComponent on the target,
|
||||
/// and to update its damage values.
|
||||
/// </summary>
|
||||
public class HealthChange : ReagentEffect
|
||||
public class HealthChange : ReagentEffect, ISerializationHooks
|
||||
{
|
||||
/// <summary>
|
||||
/// How much damage is changed when 1u of the reagent is metabolized.
|
||||
@@ -20,35 +23,67 @@ namespace Content.Server.Chemistry.ReagentEffects
|
||||
[DataField("healthChange")]
|
||||
public float AmountToChange { get; set; } = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// Class of damage changed, Brute, Burn, Toxin, Airloss.
|
||||
/// </summary>
|
||||
[DataField("damageClass")]
|
||||
public DamageClass DamageType { get; set; } = DamageClass.Brute;
|
||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
||||
// See also _accumulatedDamage in ThirstComponent and HungerComponent
|
||||
private float _accumulatedDamage;
|
||||
|
||||
private float _accumulatedHealth;
|
||||
/// <summary>
|
||||
/// Damage group to change.
|
||||
/// </summary>
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove ISerializationHooks, if no longer needed.
|
||||
[DataField("damageGroup", required: true)]
|
||||
private readonly string _damageGroupID = default!;
|
||||
public DamageGroupPrototype DamageGroup = default!;
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
DamageGroup = IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes damage if a DamageableComponent can be found.
|
||||
/// </summary>
|
||||
public override void Metabolize(IEntity solutionEntity, Solution.ReagentQuantity amount)
|
||||
{
|
||||
if (solutionEntity.TryGetComponent(out IDamageableComponent? health))
|
||||
if (solutionEntity.TryGetComponent(out IDamageableComponent? damageComponent))
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master:Content.Server/Chemistry/ReagentEffects/HealthChange.cs
|
||||
health.ChangeDamage(DamageType, (int)AmountToChange, true);
|
||||
=======
|
||||
damageComponent.ChangeDamage(damageComponent.GetDamageType(damageType), (int)AmountToChange, true);
|
||||
>>>>>>> Fix Merge issues
|
||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
||||
=======
|
||||
damageComponent.ChangeDamage(damageComponent.GetDamageType(damageType), (int)HealthChange, true);
|
||||
float decHealthChange = (float) (HealthChange - (int) HealthChange);
|
||||
>>>>>>> update damagecomponent across shared and server:Content.Server/Chemistry/Metabolism/HealthChangeMetabolism.cs
|
||||
_accumulatedHealth += decHealthChange;
|
||||
=======
|
||||
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
|
||||
|
||||
if (_accumulatedHealth >= 1)
|
||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
||||
_accumulatedDamage += decHealthChange;
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damageComponent.TryChangeDamage(DamageGroup, (int)AmountToChange, true);
|
||||
|
||||
float decHealthChange = (float) (AmountToChange - (int) AmountToChange);
|
||||
_accumulatedDamage += decHealthChange;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (_accumulatedDamage >= 1)
|
||||
{
|
||||
health.ChangeDamage(DamageType, 1, true);
|
||||
_accumulatedHealth -= 1;
|
||||
damageComponent.TryChangeDamage(DamageGroup, 1, true);
|
||||
_accumulatedDamage -= 1;
|
||||
}
|
||||
|
||||
else if(_accumulatedHealth <= -1)
|
||||
else if(_accumulatedDamage <= -1)
|
||||
{
|
||||
health.ChangeDamage(DamageType, -1, true);
|
||||
_accumulatedHealth += 1;
|
||||
damageComponent.TryChangeDamage(DamageGroup, -1, true);
|
||||
_accumulatedDamage += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
Content.Server/Commands/Chat/SuicideCommand.cs
Normal file
129
Content.Server/Commands/Chat/SuicideCommand.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Commands.Observer;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Players;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Enums;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
|
||||
namespace Content.Server.Commands.Chat
|
||||
{
|
||||
[AnyCommand]
|
||||
internal class SuicideCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "suicide";
|
||||
|
||||
public string Description => "Commits suicide";
|
||||
|
||||
public string Help => "The suicide command gives you a quick way out of a round while remaining in-character.\n" +
|
||||
"The method varies, first it will attempt to use the held item in your active hand.\n" +
|
||||
"If that fails, it will attempt to use an object in the environment.\n" +
|
||||
"Finally, if neither of the above worked, you will die by biting your tongue.";
|
||||
|
||||
private void DealDamage(ISuicideAct suicide, IChatManager chat, IDamageableComponent damageableComponent, IEntity source, IEntity target)
|
||||
{
|
||||
var kind = suicide.Suicide(target, chat);
|
||||
if (kind != SuicideKind.Special)
|
||||
{
|
||||
damageableComponent.SetDamage(kind switch
|
||||
{
|
||||
SuicideKind.Blunt => damageableComponent.GetDamageType("Blunt"),
|
||||
SuicideKind.Slash => damageableComponent.GetDamageType("Slash"),
|
||||
SuicideKind.Piercing => damageableComponent.GetDamageType("Piercing"),
|
||||
SuicideKind.Heat => damageableComponent.GetDamageType("Heat"),
|
||||
SuicideKind.Shock => damageableComponent.GetDamageType("Shock"),
|
||||
SuicideKind.Cold => damageableComponent.GetDamageType("Cold"),
|
||||
SuicideKind.Poison => damageableComponent.GetDamageType("Poison"),
|
||||
SuicideKind.Radiation => damageableComponent.GetDamageType("Radiation"),
|
||||
SuicideKind.Asphyxiation => damageableComponent.GetDamageType("Asphyxiation"),
|
||||
SuicideKind.Bloodloss => damageableComponent.GetDamageType("Bloodloss"),
|
||||
_ => damageableComponent.GetDamageType("Blunt")
|
||||
},
|
||||
200, source);
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
if (player == null)
|
||||
{
|
||||
shell.WriteLine("You cannot run this command from the server.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.Status != SessionStatus.InGame)
|
||||
return;
|
||||
|
||||
var chat = IoCManager.Resolve<IChatManager>();
|
||||
var owner = player.ContentData()?.Mind?.OwnedComponent?.Owner;
|
||||
|
||||
if (owner == null)
|
||||
{
|
||||
shell.WriteLine("You don't have a mind!");
|
||||
return;
|
||||
}
|
||||
|
||||
var dmgComponent = owner.GetComponent<IDamageableComponent>();
|
||||
//TODO: needs to check if the mob is actually alive
|
||||
//TODO: maybe set a suicided flag to prevent resurrection?
|
||||
|
||||
// Held item suicide
|
||||
var handsComponent = owner.GetComponent<HandsComponent>();
|
||||
var itemComponent = handsComponent.GetActiveHand;
|
||||
if (itemComponent != null)
|
||||
{
|
||||
var suicide = itemComponent.Owner.GetAllComponents<ISuicideAct>().FirstOrDefault();
|
||||
|
||||
if (suicide != null)
|
||||
{
|
||||
DealDamage(suicide, chat, dmgComponent, itemComponent.Owner, owner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Get all entities in range of the suicider
|
||||
var entities = IoCManager.Resolve<IEntityLookup>().GetEntitiesInRange(owner, 1, true).ToArray();
|
||||
|
||||
if (entities.Length > 0)
|
||||
{
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (entity.HasComponent<ItemComponent>())
|
||||
continue;
|
||||
var suicide = entity.GetAllComponents<ISuicideAct>().FirstOrDefault();
|
||||
if (suicide != null)
|
||||
{
|
||||
DealDamage(suicide, chat, dmgComponent, entity, owner);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default suicide, bite your tongue
|
||||
var othersMessage = Loc.GetString("{0:theName} is attempting to bite {0:their} own tongue!", owner);
|
||||
owner.PopupMessageOtherClients(othersMessage);
|
||||
|
||||
var selfMessage = Loc.GetString("You attempt to bite your own tongue!");
|
||||
owner.PopupMessage(selfMessage);
|
||||
|
||||
dmgComponent.SetDamage(dmgComponent.GetDamageType("Piercing"), 200, owner);
|
||||
|
||||
// Prevent the player from returning to the body. Yes, this is an ugly hack.
|
||||
var ghost = new Ghost(){CanReturn = false};
|
||||
ghost.Execute(shell, argStr, Array.Empty<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
228
Content.Server/Commands/HurtCommand.cs
Normal file
228
Content.Server/Commands/HurtCommand.cs
Normal file
@@ -0,0 +1,228 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Fun)]
|
||||
class HurtCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency]
|
||||
private readonly IPrototypeManager _prototypeManager = default!;
|
||||
public string Command => "hurt";
|
||||
public string Description => "Ouch";
|
||||
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
||||
|
||||
private string DamageTypes()
|
||||
{
|
||||
var msg = new StringBuilder();
|
||||
|
||||
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
||||
{
|
||||
msg.Append($"\n{damageGroup.ID}");
|
||||
if (damageGroup.DamageTypes.Any())
|
||||
{
|
||||
msg.Append(": ");
|
||||
msg.AppendJoin('|', damageGroup.DamageTypes);
|
||||
}
|
||||
}
|
||||
return $"Damage Types:{msg}";
|
||||
}
|
||||
|
||||
private delegate void Damage(IDamageableComponent damageable, bool ignoreResistances);
|
||||
|
||||
private bool TryParseEntity(IConsoleShell shell, IPlayerSession? player, string arg,
|
||||
[NotNullWhen(true)] out IEntity? entity)
|
||||
{
|
||||
entity = null;
|
||||
|
||||
if (arg == "_")
|
||||
{
|
||||
var playerEntity = player?.AttachedEntity;
|
||||
|
||||
if (playerEntity == null)
|
||||
{
|
||||
shell.WriteLine($"You must have a player entity to use this command without specifying an entity.\n{Help}");
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = playerEntity;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!EntityUid.TryParse(arg, out var entityUid))
|
||||
{
|
||||
shell.WriteLine($"{arg} is not a valid entity uid.\n{Help}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (!entityManager.TryGetEntity(entityUid, out var parsedEntity))
|
||||
{
|
||||
shell.WriteLine($"No entity found with uid {entityUid}");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
entity = parsedEntity;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryParseDamageArgs(
|
||||
IConsoleShell shell,
|
||||
IPlayerSession? player,
|
||||
string[] args,
|
||||
[NotNullWhen(true)] out Damage? func)
|
||||
{
|
||||
|
||||
|
||||
if (!int.TryParse(args[1], out var amount))
|
||||
{
|
||||
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
||||
|
||||
func = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageClass))
|
||||
{
|
||||
func = (damageable, ignoreResistances) =>
|
||||
{
|
||||
if (!damageable.DamageClasses.ContainsKey(damageClass))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
// Fall back to DamageType
|
||||
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
|
||||
{
|
||||
func = (damageable, ignoreResistances) =>
|
||||
{
|
||||
if (!damageable.DamageTypes.ContainsKey(damageType))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!damageable.ChangeDamage(damageType, amount, ignoreResistances))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||
|
||||
};
|
||||
return true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
shell.WriteLine($"{args[0]} is not a valid damage class or type.");
|
||||
|
||||
var types = DamageTypes();
|
||||
shell.WriteLine(types);
|
||||
|
||||
func = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
var player = shell.Player as IPlayerSession;
|
||||
bool ignoreResistances;
|
||||
IEntity entity;
|
||||
Damage? damageFunc;
|
||||
|
||||
switch (args.Length)
|
||||
{
|
||||
// Check if we have enough for the dmg types to show
|
||||
case var n when n > 0 && (args[0] == "?" || args[0] == "¿"):
|
||||
var types = DamageTypes();
|
||||
|
||||
if (args[0] == "¿")
|
||||
{
|
||||
types = types.Replace('e', 'é');
|
||||
}
|
||||
|
||||
shell.WriteLine(types);
|
||||
|
||||
return;
|
||||
// Not enough args
|
||||
case var n when n < 2:
|
||||
shell.WriteLine($"Invalid number of arguments ({args.Length}).\n{Help}");
|
||||
return;
|
||||
case var n when n >= 2 && n <= 4:
|
||||
if (!TryParseDamageArgs(shell, player, args, out damageFunc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var entityUid = n == 2 ? "_" : args[2];
|
||||
|
||||
if (!TryParseEntity(shell, player, entityUid, out var parsedEntity))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
entity = parsedEntity;
|
||||
|
||||
if (n == 4)
|
||||
{
|
||||
if (!bool.TryParse(args[3], out ignoreResistances))
|
||||
{
|
||||
shell.WriteLine($"{args[3]} is not a valid boolean value for ignoreResistances.\n{Help}");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ignoreResistances = false;
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
shell.WriteLine($"Invalid amount of arguments ({args.Length}).\n{Help}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
shell.WriteLine($"Entity {entity.Name} with id {entity.Uid} does not have a {nameof(IDamageableComponent)}.");
|
||||
return;
|
||||
}
|
||||
|
||||
damageFunc(damageable, ignoreResistances);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Content.Server.Administration;
|
||||
using Content.Shared.Administration;
|
||||
@@ -9,6 +10,7 @@ using Robust.Server.Player;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Damage.Commands
|
||||
{
|
||||
@@ -19,22 +21,24 @@ namespace Content.Server.Damage.Commands
|
||||
public string Description => "Ouch";
|
||||
public string Help => $"Usage: {Command} <type/?> <amount> (<entity uid/_>) (<ignoreResistances>)";
|
||||
|
||||
private readonly IPrototypeManager _prototypeManager = default!;
|
||||
public HurtCommand() {
|
||||
_prototypeManager = IoCManager.Resolve<IPrototypeManager>();
|
||||
}
|
||||
|
||||
private string DamageTypes()
|
||||
{
|
||||
var msg = new StringBuilder();
|
||||
foreach (var dClass in Enum.GetNames(typeof(DamageClass)))
|
||||
|
||||
foreach (var damageGroup in _prototypeManager.EnumeratePrototypes<DamageGroupPrototype>())
|
||||
{
|
||||
msg.Append($"\n{dClass}");
|
||||
|
||||
var types = Enum.Parse<DamageClass>(dClass).ToTypes();
|
||||
|
||||
if (types.Count > 0)
|
||||
msg.Append($"\n{damageGroup.ID}");
|
||||
if (damageGroup.DamageTypes.Any())
|
||||
{
|
||||
msg.Append(": ");
|
||||
msg.AppendJoin('|', types);
|
||||
msg.AppendJoin('|', damageGroup.DamageTypes);
|
||||
}
|
||||
}
|
||||
|
||||
return $"Damage Types:{msg}";
|
||||
}
|
||||
|
||||
@@ -85,6 +89,8 @@ namespace Content.Server.Damage.Commands
|
||||
string[] args,
|
||||
[NotNullWhen(true)] out Damage? func)
|
||||
{
|
||||
|
||||
|
||||
if (!int.TryParse(args[1], out var amount))
|
||||
{
|
||||
shell.WriteLine($"{args[1]} is not a valid damage integer.");
|
||||
@@ -93,42 +99,50 @@ namespace Content.Server.Damage.Commands
|
||||
return false;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
if (Enum.TryParse<DamageClass>(args[0], true, out var damageClass))
|
||||
=======
|
||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
if (_prototypeManager.TryIndex<DamageGroupPrototype>(args[0], out var damageGroup))
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
{
|
||||
func = (damageable, ignoreResistances) =>
|
||||
{
|
||||
if (!damageable.DamageClasses.ContainsKey(damageClass))
|
||||
if (!damageable.ApplicableDamageGroups.Contains(damageGroup))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageClass}");
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage group {damageGroup}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!damageable.ChangeDamage(damageClass, amount, ignoreResistances))
|
||||
if (!damageable.TryChangeDamage(damageGroup, amount, ignoreResistances))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageClass} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageGroup} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
// Fall back to DamageType
|
||||
else if (Enum.TryParse<DamageType>(args[0], true, out var damageType))
|
||||
else if (_prototypeManager.TryIndex<DamageTypePrototype>(args[0], out var damageType))
|
||||
{
|
||||
func = (damageable, ignoreResistances) =>
|
||||
{
|
||||
if (!damageable.DamageTypes.ContainsKey(damageType))
|
||||
if (!damageable.IsSupportedDamageType(damageType))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage class {damageType}");
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} can not be damaged with damage type {damageType}");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!damageable.ChangeDamage(damageType, amount, ignoreResistances))
|
||||
if (!damageable.TryChangeDamage(damageType, amount, ignoreResistances))
|
||||
{
|
||||
shell.WriteLine($"Entity {damageable.Owner.Name} with id {damageable.Owner.Uid} received no damage.");
|
||||
|
||||
@@ -136,9 +150,10 @@ namespace Content.Server.Damage.Commands
|
||||
}
|
||||
|
||||
shell.WriteLine($"Damaged entity {damageable.Owner.Name} with id {damageable.Owner.Uid} for {amount} {damageType} damage{(ignoreResistances ? ", ignoring resistances." : ".")}");
|
||||
};
|
||||
|
||||
};
|
||||
return true;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -3,6 +3,9 @@ using Content.Shared.Damage;
|
||||
using Content.Shared.Sound;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Damage.Components
|
||||
{
|
||||
@@ -14,8 +17,14 @@ namespace Content.Server.Damage.Components
|
||||
{
|
||||
public override string Name => "DamageOnHighSpeedImpact";
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("damage")]
|
||||
public DamageType Damage { get; set; } = DamageType.Blunt;
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("minimumSpeed")]
|
||||
public float MinimumSpeed { get; set; } = 20f;
|
||||
[DataField("baseDamage")]
|
||||
@@ -34,5 +43,17 @@ namespace Content.Server.Damage.Components
|
||||
public float DamageCooldown { get; set; } = 2f;
|
||||
|
||||
internal TimeSpan LastHit = TimeSpan.Zero;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,9 @@ using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Throwing;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Damage.Components
|
||||
{
|
||||
@@ -11,20 +14,59 @@ namespace Content.Server.Damage.Components
|
||||
{
|
||||
public override string Name => "DamageOnLand";
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("damageType")]
|
||||
private DamageType _damageType = DamageType.Blunt;
|
||||
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("amount")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private int _amount = 1;
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private bool _ignoreResistances;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
<<<<<<< HEAD
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
void ILand.Land(LandEventArgs eventArgs)
|
||||
{
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
||||
|
||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
||||
=======
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
void ILand.Land(LandEventArgs eventArgs)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
damageable.TryChangeDamage(DamageType, _amount, _ignoreResistances);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Damage;
|
||||
@@ -7,12 +7,16 @@ using Content.Shared.Interaction;
|
||||
using Content.Shared.Tool;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Damage.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
||||
{
|
||||
|
||||
public override string Name => "DamageOnToolInteract";
|
||||
|
||||
[DataField("damage")]
|
||||
@@ -21,6 +25,32 @@ namespace Content.Server.Damage.Components
|
||||
[DataField("tools")]
|
||||
private List<ToolQuality> _tools = new();
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace these datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("weldingDamageType")]
|
||||
private readonly string _weldingDamageTypeID = "Heat";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype WeldingDamageType = default!;
|
||||
[DataField("defaultDamageType")]
|
||||
private readonly string _defaultDamageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DefaultDamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
WeldingDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_weldingDamageTypeID);
|
||||
DefaultDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_defaultDamageTypeID);
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
||||
@@ -44,17 +74,26 @@ namespace Content.Server.Damage.Components
|
||||
|
||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
||||
{
|
||||
if (eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||
{
|
||||
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||
? DamageType.Heat
|
||||
: DamageType.Blunt,
|
||||
Damage, false, eventArgs.User);
|
||||
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||
return false;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
return true;
|
||||
}
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
damageable.TryChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||
? WeldingDamageType
|
||||
: DefaultDamageType,
|
||||
Damage);
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ using Content.Shared.Damage;
|
||||
using Robust.Shared.Analyzers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Damage.Components
|
||||
{
|
||||
@@ -11,13 +13,47 @@ namespace Content.Server.Damage.Components
|
||||
{
|
||||
public override string Name => "DamageOtherOnHit";
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("damageType")]
|
||||
public DamageType DamageType { get; } = DamageType.Blunt;
|
||||
=======
|
||||
[DataField("damageType",required: true)]
|
||||
private readonly string _damageType = default!;
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("amount")]
|
||||
public int Amount { get; } = 1;
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
public bool IgnoreResistances { get; } = false;
|
||||
=======
|
||||
private bool _ignoreResistances;
|
||||
=======
|
||||
public bool IgnoreResistances { get; } = false;
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
public bool IgnoreResistances { get; } = false;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
public DamageTypePrototype DamageType { get; set; } = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Content.Server.Damage
|
||||
if (ComponentManager.TryGetComponent(uid, out StunnableComponent? stun) && _robustRandom.Prob(component.StunChance))
|
||||
stun.Stun(component.StunSeconds);
|
||||
|
||||
damageable.ChangeDamage(component.Damage, damage, false, args.OtherFixture.Body.Owner);
|
||||
damageable.TryChangeDamage(component.DamageType, damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,15 @@ namespace Content.Server.Damage
|
||||
if (!args.Target.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances, args.User);
|
||||
=======
|
||||
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
damageable.TryChangeDamage(component.DamageType, component.Amount, component.IgnoreResistances);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.Components;
|
||||
=======
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
>>>>>>> Merge fixes
|
||||
=======
|
||||
using System.Collections.Generic;
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
using System.Collections.Generic;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Damage.Resistances;
|
||||
using Content.Shared.GameTicking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
@@ -43,8 +56,9 @@ namespace Content.Server.Damage
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
damageable.SupportedTypes.Clear();
|
||||
damageable.SupportedClasses.Clear();
|
||||
damageable.SupportedDamageTypes.Clear();
|
||||
damageable.FullySupportedDamageGroups.Clear();
|
||||
damageable.ApplicableDamageGroups.Clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -69,14 +83,33 @@ namespace Content.Server.Damage
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
if (old.SupportedTypes != null)
|
||||
if (old.SupportedDamageTypes != null)
|
||||
<<<<<<< HEAD
|
||||
{
|
||||
damageable.SupportedTypes.UnionWith(old.SupportedTypes);
|
||||
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
|
||||
}
|
||||
|
||||
if (old.SupportedClasses != null)
|
||||
if (old.SupportedDamageGroups != null)
|
||||
{
|
||||
damageable.SupportedClasses.UnionWith(old.SupportedClasses);
|
||||
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
|
||||
}
|
||||
|
||||
if (old.ApplicableDamageGroups != null)
|
||||
{
|
||||
=======
|
||||
{
|
||||
damageable.SupportedDamageTypes.UnionWith(old.SupportedDamageTypes);
|
||||
}
|
||||
|
||||
if (old.SupportedDamageGroups != null)
|
||||
{
|
||||
damageable.FullySupportedDamageGroups.UnionWith(old.SupportedDamageGroups);
|
||||
}
|
||||
|
||||
if (old.ApplicableDamageGroups != null)
|
||||
{
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
damageable.ApplicableDamageGroups.UnionWith(old.ApplicableDamageGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,8 +144,9 @@ namespace Content.Server.Damage
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
SupportedTypes = damageable.SupportedTypes.ToHashSet();
|
||||
SupportedClasses = damageable.SupportedClasses.ToHashSet();
|
||||
SupportedDamageTypes = damageable.SupportedDamageTypes.ToHashSet();
|
||||
SupportedDamageGroups = damageable.FullySupportedDamageGroups.ToHashSet();
|
||||
ApplicableDamageGroups = damageable.ApplicableDamageGroups.ToHashSet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +154,11 @@ namespace Content.Server.Damage
|
||||
|
||||
public bool MovedByPressure { get; }
|
||||
|
||||
public HashSet<DamageType>? SupportedTypes { get; }
|
||||
public HashSet<DamageTypePrototype>? SupportedDamageTypes { get; }
|
||||
|
||||
public HashSet<DamageClass>? SupportedClasses { get; }
|
||||
public HashSet<DamageGroupPrototype>? SupportedDamageGroups { get; }
|
||||
|
||||
public HashSet<DamageGroupPrototype>? ApplicableDamageGroups { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Content.Server.Damage
|
||||
{
|
||||
if (target.TryGetComponent(out IDamageableComponent? damage))
|
||||
{
|
||||
damage.Heal();
|
||||
damage.TrySetAllDamage(0);
|
||||
}
|
||||
|
||||
if (target.TryGetComponent(out IMobStateComponent? mobState))
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified class is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public class DamageGroupTrigger : IThresholdTrigger
|
||||
{
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
|
||||
// of damage types?
|
||||
[DataField("damageGroup", required: true)]
|
||||
private string _damageGroupID { get; set; } = default!;
|
||||
public DamageGroupPrototype DamageGroup => IoCManager.Resolve<IPrototypeManager>().Index<DamageGroupPrototype>(_damageGroupID);
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
if (DamageGroup == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return damageable.TryGetDamage(DamageGroup, out var damageReceived) &&
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
@@ -15,8 +15,8 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
/// </summary>
|
||||
[DataField("damage")]
|
||||
public int Damage { get; set; }
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
{
|
||||
@@ -13,20 +15,52 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
[DataDefinition]
|
||||
public class DamageTypeTrigger : IThresholdTrigger
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("type")]
|
||||
public DamageType? Type { get; set; }
|
||||
=======
|
||||
[DataField("damageType")]
|
||||
public string? DamageType { get; set; }
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// While you're at it, maybe also combine damageGroup and damage into a dictionary, and allow it to test a sum
|
||||
// of damage types?
|
||||
[DataField("damageType", required:true)]
|
||||
public string _damageTypeID { get; set; } = default!;
|
||||
public DamageTypePrototype DamageType => IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
[DataField("damage")]
|
||||
public int Damage { get; set; }
|
||||
[DataField("damage", required: true)]
|
||||
public int Damage { get; set; } = default!;
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
if (Type == null)
|
||||
if (DamageType == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
return damageable.TryGetDamage(Type.Value, out var damageReceived) &&
|
||||
=======
|
||||
return damageable.TryGetDamage(damageable.GetDamageType(DamageType), out var damageReceived) &&
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
return damageable.TryGetDamage(DamageType, out var damageReceived) &&
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Doors.Components
|
||||
{
|
||||
@@ -42,8 +44,37 @@ namespace Content.Server.Doors.Components
|
||||
[DataField("board")]
|
||||
private string? _boardPrototype;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("tryOpenDoorSound")]
|
||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
||||
=======
|
||||
=======
|
||||
[DataField("tryOpenDoorSound")]
|
||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
||||
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
[DataField("tryOpenDoorSound")]
|
||||
private SoundSpecifier _tryOpenDoorSound = new SoundPathSpecifier("/Audio/Effects/bang.ogg");
|
||||
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
public override DoorState State
|
||||
{
|
||||
@@ -536,7 +567,15 @@ namespace Content.Server.Doors.Components
|
||||
hitsomebody = true;
|
||||
CurrentlyCrushing.Add(e.Owner.Uid);
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damage.ChangeDamage(DamageType.Blunt, DoorCrushDamage, false, Owner);
|
||||
=======
|
||||
damage.TryChangeDamage(DamageType, DoorCrushDamage);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damage.TryChangeDamage(DamageType, DoorCrushDamage);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
stun.Paralyze(DoorStunTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Dependency = Robust.Shared.IoC.DependencyAttribute;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
/// <summary>
|
||||
/// Barotrauma: injury because of changes in air pressure.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class BarotraumaComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override string Name => "Barotrauma";
|
||||
|
||||
[DataField("damageType",required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Update(float airPressure)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
||||
|
||||
var status = Owner.GetComponentOrNull<ServerAlertsComponent>();
|
||||
var highPressureMultiplier = 1f;
|
||||
var lowPressureMultiplier = 1f;
|
||||
|
||||
foreach (var protection in Owner.GetAllComponents<IPressureProtection>())
|
||||
{
|
||||
highPressureMultiplier *= protection.HighPressureMultiplier;
|
||||
lowPressureMultiplier *= protection.LowPressureMultiplier;
|
||||
}
|
||||
|
||||
var pressure = MathF.Max(airPressure, 1f);
|
||||
|
||||
switch (pressure)
|
||||
{
|
||||
// Low pressure.
|
||||
case var p when p <= Atmospherics.WarningLowPressure:
|
||||
pressure *= lowPressureMultiplier;
|
||||
|
||||
if(pressure > Atmospherics.WarningLowPressure)
|
||||
goto default;
|
||||
|
||||
damageable.ChangeDamage(damageable.GetDamageType("Blunt"), Atmospherics.LowPressureDamage, false, Owner);
|
||||
|
||||
if (status == null) break;
|
||||
|
||||
if (pressure <= Atmospherics.HazardLowPressure)
|
||||
{
|
||||
status.ShowAlert(AlertType.LowPressure, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
status.ShowAlert(AlertType.LowPressure, 1);
|
||||
break;
|
||||
|
||||
// High pressure.
|
||||
case var p when p >= Atmospherics.WarningHighPressure:
|
||||
pressure *= highPressureMultiplier;
|
||||
|
||||
if(pressure < Atmospherics.WarningHighPressure)
|
||||
goto default;
|
||||
|
||||
var damage = (int) MathF.Min((pressure / Atmospherics.HazardHighPressure) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
|
||||
|
||||
damageable.ChangeDamage(_damageType, damage, false, Owner);
|
||||
|
||||
if (status == null) break;
|
||||
|
||||
if (pressure >= Atmospherics.HazardHighPressure)
|
||||
{
|
||||
status.ShowAlert(AlertType.HighPressure, 2);
|
||||
break;
|
||||
}
|
||||
|
||||
status.ShowAlert(AlertType.HighPressure, 1);
|
||||
break;
|
||||
|
||||
// Normal pressure.
|
||||
default:
|
||||
status?.ClearAlertCategory(AlertCategory.Pressure);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Temperature;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Chemistry;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Atmos;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Atmos
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class FlammableComponent : SharedFlammableComponent, IStartCollide, IFireAct, IInteractUsing
|
||||
{
|
||||
private bool _resisting = false;
|
||||
private readonly List<EntityUid> _collided = new();
|
||||
|
||||
[DataField("damageType", required:true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool OnFire { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float FireStacks { get; private set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("fireSpread")]
|
||||
public bool FireSpread { get; private set; } = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("canResistFire")]
|
||||
public bool CanResistFire { get; private set; } = false;
|
||||
|
||||
public void Ignite()
|
||||
{
|
||||
if (FireStacks > 0 && !OnFire)
|
||||
{
|
||||
OnFire = true;
|
||||
|
||||
}
|
||||
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void Extinguish()
|
||||
{
|
||||
if (!OnFire) return;
|
||||
OnFire = false;
|
||||
FireStacks = 0;
|
||||
|
||||
_collided.Clear();
|
||||
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void AdjustFireStacks(float relativeFireStacks)
|
||||
{
|
||||
FireStacks = MathF.Min(MathF.Max(-10f, FireStacks + relativeFireStacks), 20f);
|
||||
if (OnFire && FireStacks <= 0)
|
||||
Extinguish();
|
||||
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void Update(TileAtmosphere tile)
|
||||
{
|
||||
// Slowly dry ourselves off if wet.
|
||||
if (FireStacks < 0)
|
||||
{
|
||||
FireStacks = MathF.Min(0, FireStacks + 1);
|
||||
}
|
||||
|
||||
Owner.TryGetComponent(out ServerAlertsComponent? status);
|
||||
|
||||
if (!OnFire)
|
||||
{
|
||||
status?.ClearAlert(AlertType.Fire);
|
||||
return;
|
||||
}
|
||||
|
||||
status?.ShowAlert(AlertType.Fire);
|
||||
|
||||
if (FireStacks > 0)
|
||||
{
|
||||
if (Owner.TryGetComponent(out TemperatureComponent? temp))
|
||||
{
|
||||
temp.ReceiveHeat(200 * FireStacks);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
// TODO ATMOS Fire resistance from armor
|
||||
var damage = Math.Min((int) (FireStacks * 2.5f), 10);
|
||||
damageable.ChangeDamage(_damageType, damage, false);
|
||||
}
|
||||
|
||||
AdjustFireStacks(-0.1f * (_resisting ? 10f : 1f));
|
||||
}
|
||||
else
|
||||
{
|
||||
Extinguish();
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're in an oxygenless environment, put the fire out.
|
||||
if (tile.Air?.GetMoles(Gas.Oxygen) < 1f)
|
||||
{
|
||||
Extinguish();
|
||||
return;
|
||||
}
|
||||
|
||||
tile.HotspotExpose(700, 50, true);
|
||||
|
||||
var physics = Owner.GetComponent<IPhysBody>();
|
||||
|
||||
foreach (var uid in _collided.ToArray())
|
||||
{
|
||||
if (!uid.IsValid() || !Owner.EntityManager.EntityExists(uid))
|
||||
{
|
||||
_collided.Remove(uid);
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = Owner.EntityManager.GetEntity(uid);
|
||||
var otherPhysics = entity.GetComponent<IPhysBody>();
|
||||
|
||||
if (!physics.GetWorldAABB().Intersects(otherPhysics.GetWorldAABB()))
|
||||
{
|
||||
_collided.Remove(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
||||
{
|
||||
if (!otherFixture.Body.Owner.TryGetComponent(out FlammableComponent? otherFlammable))
|
||||
return;
|
||||
|
||||
if (!FireSpread || !otherFlammable.FireSpread)
|
||||
return;
|
||||
|
||||
if (OnFire)
|
||||
{
|
||||
if (otherFlammable.OnFire)
|
||||
{
|
||||
var fireSplit = (FireStacks + otherFlammable.FireStacks) / 2;
|
||||
FireStacks = fireSplit;
|
||||
otherFlammable.FireStacks = fireSplit;
|
||||
}
|
||||
else
|
||||
{
|
||||
FireStacks /= 2;
|
||||
otherFlammable.FireStacks += FireStacks;
|
||||
otherFlammable.Ignite();
|
||||
}
|
||||
} else if (otherFlammable.OnFire)
|
||||
{
|
||||
otherFlammable.FireStacks /= 2;
|
||||
FireStacks += otherFlammable.FireStacks;
|
||||
Ignite();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
if (Owner.Deleted || !Owner.TryGetComponent(out AppearanceComponent? appearanceComponent)) return;
|
||||
appearanceComponent.SetData(FireVisuals.OnFire, OnFire);
|
||||
appearanceComponent.SetData(FireVisuals.FireStacks, FireStacks);
|
||||
}
|
||||
|
||||
public void FireAct(float temperature, float volume)
|
||||
{
|
||||
AdjustFireStacks(3);
|
||||
Ignite();
|
||||
}
|
||||
|
||||
// This needs some improvements...
|
||||
public void Resist()
|
||||
{
|
||||
if (!OnFire || !ActionBlockerSystem.CanInteract(Owner) || _resisting || !Owner.TryGetComponent(out StunnableComponent? stunnable)) return;
|
||||
|
||||
_resisting = true;
|
||||
|
||||
Owner.PopupMessage(Loc.GetString("You stop, drop, and roll!"));
|
||||
stunnable.Paralyze(2f);
|
||||
|
||||
Owner.SpawnTimer(2000, () =>
|
||||
{
|
||||
_resisting = false;
|
||||
FireStacks -= 3f;
|
||||
UpdateAppearance();
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
foreach (var hotItem in eventArgs.Using.GetAllComponents<IHotItem>())
|
||||
{
|
||||
if (hotItem.IsCurrentlyHot())
|
||||
{
|
||||
Ignite();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DamageOnHighSpeedImpactComponent : Component, IStartCollide
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override string Name => "DamageOnHighSpeedImpact";
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
public DamageTypePrototype DamageType { get; set; } = default!;
|
||||
[DataField("minimumSpeed")]
|
||||
public float MinimumSpeed { get; set; } = 20f;
|
||||
[DataField("baseDamage")]
|
||||
public int BaseDamage { get; set; } = 5;
|
||||
[DataField("factor")]
|
||||
public float Factor { get; set; } = 1f;
|
||||
[DataField("soundHit")]
|
||||
public string SoundHit { get; set; } = "";
|
||||
[DataField("stunChance")]
|
||||
public float StunChance { get; set; } = 0.25f;
|
||||
[DataField("stunMinimumDamage")]
|
||||
public int StunMinimumDamage { get; set; } = 10;
|
||||
[DataField("stunSeconds")]
|
||||
public float StunSeconds { get; set; } = 1f;
|
||||
[DataField("damageCooldown")]
|
||||
public float DamageCooldown { get; set; } = 2f;
|
||||
private TimeSpan _lastHit = TimeSpan.Zero;
|
||||
|
||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable)) return;
|
||||
|
||||
var speed = ourFixture.Body.LinearVelocity.Length;
|
||||
|
||||
if (speed < MinimumSpeed) return;
|
||||
|
||||
if (!string.IsNullOrEmpty(SoundHit))
|
||||
SoundSystem.Play(Filter.Pvs(otherFixture.Body.Owner), SoundHit, otherFixture.Body.Owner, AudioHelpers.WithVariation(0.125f).WithVolume(-0.125f));
|
||||
|
||||
if ((_gameTiming.CurTime - _lastHit).TotalSeconds < DamageCooldown)
|
||||
return;
|
||||
|
||||
_lastHit = _gameTiming.CurTime;
|
||||
|
||||
var damage = (int) (BaseDamage * (speed / MinimumSpeed) * Factor);
|
||||
|
||||
if (Owner.TryGetComponent(out StunnableComponent? stun) && _robustRandom.Prob(StunChance))
|
||||
stun.Stun(StunSeconds);
|
||||
|
||||
damageable.ChangeDamage(DamageType, damage, false, otherFixture.Body.Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DamageOnLandComponent : Component, ILand
|
||||
{
|
||||
public override string Name => "DamageOnLand";
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private DamageTypePrototype _damageType = default!;
|
||||
|
||||
[DataField("amount")]
|
||||
private int _amount = 1;
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
private bool _ignoreResistances;
|
||||
|
||||
void ILand.Land(LandEventArgs eventArgs)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DamageOnToolInteractComponent : Component, IInteractUsing
|
||||
{
|
||||
public override string Name => "DamageOnToolInteract";
|
||||
|
||||
[DataField("damage")]
|
||||
protected int Damage;
|
||||
|
||||
[DataField("tools")]
|
||||
private List<ToolQuality> _tools = new();
|
||||
|
||||
[DataField("weldingDamageType",required: true)]
|
||||
private readonly DamageTypePrototype _weldingDamageType = default!;
|
||||
|
||||
[DataField("defaultDamageType",required: true)]
|
||||
private readonly DamageTypePrototype _defaultDamageType = default!;
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
||||
{
|
||||
foreach (var toolQuality in _tools)
|
||||
{
|
||||
if (tool.HasQuality(ToolQuality.Welding) && toolQuality == ToolQuality.Welding)
|
||||
{
|
||||
if (eventArgs.Using.TryGetComponent(out WelderComponent? welder))
|
||||
{
|
||||
if (welder.WelderLit) return CallDamage(eventArgs, tool);
|
||||
}
|
||||
break; //If the tool quality is welding and its not lit or its not actually a welder that can be lit then its pointless to continue.
|
||||
}
|
||||
|
||||
if (tool.HasQuality(toolQuality)) return CallDamage(eventArgs, tool);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected bool CallDamage(InteractUsingEventArgs eventArgs, ToolComponent tool)
|
||||
{
|
||||
if (!eventArgs.Target.TryGetComponent<IDamageableComponent>(out var damageable))
|
||||
return false;
|
||||
|
||||
damageable.ChangeDamage(tool.HasQuality(ToolQuality.Welding)
|
||||
? _weldingDamageType
|
||||
: _defaultDamageType,
|
||||
Damage, false, eventArgs.User);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Damage
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class DamageOtherOnHitComponent : Component, IThrowCollide
|
||||
{
|
||||
public override string Name => "DamageOtherOnHit";
|
||||
|
||||
[DataField("damageType",required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
[DataField("amount")]
|
||||
private int _amount = 1;
|
||||
|
||||
[DataField("ignoreResistances")]
|
||||
private bool _ignoreResistances;
|
||||
|
||||
void IThrowCollide.DoHit(ThrowCollideEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
damageable.ChangeDamage(_damageType, _amount, _ignoreResistances, eventArgs.User);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
using System;
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
@@ -17,7 +19,7 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
/// The class to check the damage of.
|
||||
/// </summary>
|
||||
[DataField("class")]
|
||||
public DamageClass? Class { get; set; }
|
||||
public DamageGroupPrototype? Group { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The amount of damage at which this threshold will trigger.
|
||||
@@ -27,12 +29,12 @@ namespace Content.Server.Destructible.Thresholds.Triggers
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
if (Class == null)
|
||||
if (Group == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return damageable.TryGetDamage(Class.Value, out var damageReceived) &&
|
||||
return damageable.TryGetDamage(Group, out var damageReceived) &&
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Destructible.Thresholds.Triggers
|
||||
{
|
||||
/// <summary>
|
||||
/// A trigger that will activate when the amount of damage received
|
||||
/// of the specified type is above the specified threshold.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
[DataDefinition]
|
||||
public class DamageTypeTrigger : IThresholdTrigger
|
||||
{
|
||||
[DataField("type")]
|
||||
public DamageTypePrototype? Type { get; set; }
|
||||
|
||||
[DataField("damage")]
|
||||
public int Damage { get; set; }
|
||||
|
||||
public bool Reached(IDamageableComponent damageable, DestructibleSystem system)
|
||||
{
|
||||
if (Type == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return damageable.TryGetDamage(Type, out var damageReceived) &&
|
||||
damageReceived >= Damage;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,679 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Access;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Interactable;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Interfaces.GameObjects.Components.Doors;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Doors;
|
||||
using Content.Shared.GameObjects.Components.Interactable;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
using Content.Server.GameObjects.Components.Construction;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Doors
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(SharedDoorComponent))]
|
||||
public class ServerDoorComponent : SharedDoorComponent, IActivate, IStartCollide, IInteractUsing, IMapInit
|
||||
{
|
||||
[ComponentDependency]
|
||||
private readonly IDoorCheck? _doorCheck = null;
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("board")]
|
||||
private string? _boardPrototype;
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
public override DoorState State
|
||||
{
|
||||
get => base.State;
|
||||
protected set
|
||||
{
|
||||
if (State == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.State = value;
|
||||
|
||||
StateChangeStartTime = State switch
|
||||
{
|
||||
DoorState.Open or DoorState.Closed => null,
|
||||
DoorState.Opening or DoorState.Closing => GameTiming.CurTime,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
if (_doorCheck != null)
|
||||
{
|
||||
_doorCheck.OnStateChange(State);
|
||||
RefreshAutoClose();
|
||||
}
|
||||
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly TimeSpan AutoCloseDelay = TimeSpan.FromSeconds(5);
|
||||
|
||||
private CancellationTokenSource? _stateChangeCancelTokenSource;
|
||||
private CancellationTokenSource? _autoCloseCancelTokenSource;
|
||||
|
||||
private const int DoorCrushDamage = 15;
|
||||
private const float DoorStunTime = 5f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door will ever crush.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("inhibitCrush")]
|
||||
private bool _inhibitCrush;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door blocks light.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("occludes")]
|
||||
private bool _occludes = true;
|
||||
public bool Occludes => _occludes;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door will open when it is bumped into.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("bumpOpen")]
|
||||
private bool _bumpOpen = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door starts open when it's first loaded from prototype. A door won't start open if its prototype is also welded shut.
|
||||
/// Handled in Startup().
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("startOpen")]
|
||||
private bool _startOpen;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the airlock is welded shut. Can be set by the prototype, although this will fail if the door isn't weldable.
|
||||
/// When set by prototype, handled in Startup().
|
||||
/// </summary>
|
||||
[DataField("welded")]
|
||||
private bool _isWeldedShut;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the airlock is welded shut.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool IsWeldedShut
|
||||
{
|
||||
get => _isWeldedShut;
|
||||
set
|
||||
{
|
||||
if (_isWeldedShut == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_isWeldedShut = value;
|
||||
SetAppearance(_isWeldedShut ? DoorVisualState.Welded : DoorVisualState.Closed);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door can ever be welded shut.
|
||||
/// </summary>
|
||||
[DataField("weldable")]
|
||||
private bool _weldable = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door can currently be welded.
|
||||
/// </summary>
|
||||
private bool CanWeldShut => _weldable && State == DoorState.Closed;
|
||||
|
||||
/// <summary>
|
||||
/// Whether something is currently using a welder on this so DoAfter isn't spammed.
|
||||
/// </summary>
|
||||
private bool _beingWelded;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("canCrush")]
|
||||
private bool _canCrush = true;
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
if (IsWeldedShut)
|
||||
{
|
||||
if (!CanWeldShut)
|
||||
{
|
||||
Logger.Warning("{0} prototype loaded with incompatible flags: 'welded' is true, but door cannot be welded.", Owner.Name);
|
||||
return;
|
||||
}
|
||||
SetAppearance(DoorVisualState.Welded);
|
||||
}
|
||||
|
||||
CreateDoorElectronicsBoard();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
_stateChangeCancelTokenSource?.Cancel();
|
||||
_autoCloseCancelTokenSource?.Cancel();
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
if (_startOpen)
|
||||
{
|
||||
if (IsWeldedShut)
|
||||
{
|
||||
Logger.Warning("{0} prototype loaded with incompatible flags: 'welded' and 'startOpen' are both true.", Owner.Name);
|
||||
return;
|
||||
}
|
||||
QuickOpen();
|
||||
}
|
||||
|
||||
CreateDoorElectronicsBoard();
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (_doorCheck != null && _doorCheck.BlockActivate(eventArgs))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (State == DoorState.Open)
|
||||
{
|
||||
TryClose(eventArgs.User);
|
||||
}
|
||||
else if (State == DoorState.Closed)
|
||||
{
|
||||
TryOpen(eventArgs.User);
|
||||
}
|
||||
}
|
||||
|
||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
||||
{
|
||||
if (State != DoorState.Closed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_bumpOpen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Disabled because it makes it suck hard to walk through double doors.
|
||||
|
||||
TryOpen(otherFixture.Body.Owner);
|
||||
|
||||
}
|
||||
|
||||
#region Opening
|
||||
|
||||
public void TryOpen(IEntity user)
|
||||
{
|
||||
if (CanOpenByEntity(user))
|
||||
{
|
||||
Open();
|
||||
|
||||
if (user.TryGetComponent(out HandsComponent? hands) && hands.Count == 0)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/bang.ogg", Owner,
|
||||
AudioParams.Default.WithVolume(-2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Deny();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanOpenByEntity(IEntity user)
|
||||
{
|
||||
if(!CanOpenGeneric())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out AccessReader? access))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var doorSystem = EntitySystem.Get<ServerDoorSystem>();
|
||||
var isAirlockExternal = HasAccessType("External");
|
||||
|
||||
return doorSystem.AccessType switch
|
||||
{
|
||||
ServerDoorSystem.AccessTypes.AllowAll => true,
|
||||
ServerDoorSystem.AccessTypes.AllowAllIdExternal => isAirlockExternal || access.IsAllowed(user),
|
||||
ServerDoorSystem.AccessTypes.AllowAllNoExternal => !isAirlockExternal,
|
||||
_ => access.IsAllowed(user)
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether a door has a certain access type. For example, maintenance doors will have access type
|
||||
/// "Maintenance" in their AccessReader.
|
||||
/// </summary>
|
||||
private bool HasAccessType(string accessType)
|
||||
{
|
||||
if (Owner.TryGetComponent(out AccessReader? access))
|
||||
{
|
||||
return access.AccessLists.Any(list => list.Contains(accessType));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if we can open at all, for anyone or anything. Will return false if inhibited by an IDoorCheck component.
|
||||
/// </summary>
|
||||
/// <returns>Boolean describing whether this door can open.</returns>
|
||||
public bool CanOpenGeneric()
|
||||
{
|
||||
// note the welded check -- CanCloseGeneric does not have this
|
||||
if (IsWeldedShut)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(_doorCheck != null)
|
||||
{
|
||||
return _doorCheck.OpenCheck();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the door. Does not check if this is possible.
|
||||
/// </summary>
|
||||
public void Open()
|
||||
{
|
||||
State = DoorState.Opening;
|
||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
||||
{
|
||||
occluder.Enabled = false;
|
||||
}
|
||||
|
||||
_stateChangeCancelTokenSource?.Cancel();
|
||||
_stateChangeCancelTokenSource = new();
|
||||
|
||||
Owner.SpawnTimer(OpenTimeOne, async () =>
|
||||
{
|
||||
OnPartialOpen();
|
||||
await Timer.Delay(OpenTimeTwo, _stateChangeCancelTokenSource.Token);
|
||||
|
||||
State = DoorState.Open;
|
||||
}, _stateChangeCancelTokenSource.Token);
|
||||
}
|
||||
|
||||
protected override void OnPartialOpen()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AirtightComponent? airtight))
|
||||
{
|
||||
airtight.AirBlocked = false;
|
||||
}
|
||||
base.OnPartialOpen();
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner, false));
|
||||
}
|
||||
|
||||
private void QuickOpen()
|
||||
{
|
||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
||||
{
|
||||
occluder.Enabled = false;
|
||||
}
|
||||
OnPartialOpen();
|
||||
State = DoorState.Open;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Closing
|
||||
|
||||
public void TryClose(IEntity user)
|
||||
{
|
||||
if (!CanCloseByEntity(user))
|
||||
{
|
||||
Deny();
|
||||
return;
|
||||
}
|
||||
|
||||
Close();
|
||||
}
|
||||
|
||||
public bool CanCloseByEntity(IEntity user)
|
||||
{
|
||||
if (!CanCloseGeneric())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out AccessReader? access))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return access.IsAllowed(user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if we can close at all, for anyone or anything. Will return false if inhibited by an IDoorCheck component or if we are colliding with somebody while our Safety is on.
|
||||
/// </summary>
|
||||
/// <returns>Boolean describing whether this door can close.</returns>
|
||||
public bool CanCloseGeneric()
|
||||
{
|
||||
if (_doorCheck != null && !_doorCheck.CloseCheck())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return !IsSafetyColliding();
|
||||
}
|
||||
|
||||
private bool SafetyCheck()
|
||||
{
|
||||
return (_doorCheck != null && _doorCheck.SafetyCheck()) || _inhibitCrush;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if we care about safety, and if so, if something is colliding with it; ignores the CanCollide of the door's PhysicsComponent.
|
||||
/// </summary>
|
||||
/// <returns>True if something is colliding with us and we shouldn't crush things, false otherwise.</returns>
|
||||
private bool IsSafetyColliding()
|
||||
{
|
||||
var safety = SafetyCheck();
|
||||
|
||||
if (safety && Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
|
||||
{
|
||||
var broadPhaseSystem = EntitySystem.Get<SharedBroadPhaseSystem>();
|
||||
|
||||
// Use this version so we can ignore the CanCollide being false
|
||||
foreach(var e in broadPhaseSystem.GetCollidingEntities(physicsComponent.Owner.Transform.MapID, physicsComponent.GetWorldAABB()))
|
||||
{
|
||||
if ((physicsComponent.CollisionMask & e.CollisionLayer) != 0 && broadPhaseSystem.IntersectionPercent(physicsComponent, e) > 0.01f) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the door. Does not check if this is possible.
|
||||
/// </summary>
|
||||
public void Close()
|
||||
{
|
||||
State = DoorState.Closing;
|
||||
|
||||
// no more autoclose; we ARE closed
|
||||
_autoCloseCancelTokenSource?.Cancel();
|
||||
|
||||
_stateChangeCancelTokenSource?.Cancel();
|
||||
_stateChangeCancelTokenSource = new();
|
||||
Owner.SpawnTimer(CloseTimeOne, async () =>
|
||||
{
|
||||
// if somebody walked into the door as it was closing, and we don't crush things
|
||||
if (IsSafetyColliding())
|
||||
{
|
||||
Open();
|
||||
return;
|
||||
}
|
||||
|
||||
OnPartialClose();
|
||||
await Timer.Delay(CloseTimeTwo, _stateChangeCancelTokenSource.Token);
|
||||
|
||||
if (Occludes && Owner.TryGetComponent(out OccluderComponent? occluder))
|
||||
{
|
||||
occluder.Enabled = true;
|
||||
}
|
||||
|
||||
State = DoorState.Closed;
|
||||
}, _stateChangeCancelTokenSource.Token);
|
||||
}
|
||||
|
||||
protected override void OnPartialClose()
|
||||
{
|
||||
base.OnPartialClose();
|
||||
|
||||
// if safety is off, crushes people inside of the door, temporarily turning off collisions with them while doing so.
|
||||
var becomeairtight = SafetyCheck() || !TryCrush();
|
||||
|
||||
if (becomeairtight && Owner.TryGetComponent(out AirtightComponent? airtight))
|
||||
{
|
||||
airtight.AirBlocked = true;
|
||||
}
|
||||
|
||||
Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crushes everyone colliding with us by more than 10%.
|
||||
/// </summary>
|
||||
/// <returns>True if we crushed somebody, false if we did not.</returns>
|
||||
private bool TryCrush()
|
||||
{
|
||||
if (PhysicsComponent == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var collidingentities = PhysicsComponent.GetCollidingEntities(Vector2.Zero, false);
|
||||
|
||||
if (!collidingentities.Any())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var doorAABB = PhysicsComponent.GetWorldAABB();
|
||||
var hitsomebody = false;
|
||||
|
||||
// Crush
|
||||
foreach (var e in collidingentities)
|
||||
{
|
||||
if (!e.Owner.TryGetComponent(out StunnableComponent? stun)
|
||||
|| !e.Owner.TryGetComponent(out IDamageableComponent? damage))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var percentage = e.GetWorldAABB().IntersectPercentage(doorAABB);
|
||||
|
||||
if (percentage < 0.1f)
|
||||
continue;
|
||||
|
||||
hitsomebody = true;
|
||||
CurrentlyCrushing.Add(e.Owner.Uid);
|
||||
|
||||
damage.ChangeDamage(_damageType, DoorCrushDamage, false, Owner);
|
||||
stun.Paralyze(DoorStunTime);
|
||||
}
|
||||
|
||||
// If we hit someone, open up after stun (opens right when stun ends)
|
||||
if (hitsomebody)
|
||||
{
|
||||
Owner.SpawnTimer(TimeSpan.FromSeconds(DoorStunTime) - OpenTimeOne - OpenTimeTwo, Open);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void Deny()
|
||||
{
|
||||
if (_doorCheck != null && !_doorCheck.DenyCheck())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (State == DoorState.Open || IsWeldedShut)
|
||||
return;
|
||||
|
||||
_stateChangeCancelTokenSource?.Cancel();
|
||||
_stateChangeCancelTokenSource = new();
|
||||
SetAppearance(DoorVisualState.Deny);
|
||||
Owner.SpawnTimer(DenyTime, () =>
|
||||
{
|
||||
SetAppearance(DoorVisualState.Closed);
|
||||
}, _stateChangeCancelTokenSource.Token);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stops the current auto-close timer if there is one. Starts a new one if this is appropriate (i.e. entity has an IDoorCheck component that allows auto-closing).
|
||||
/// </summary>
|
||||
public void RefreshAutoClose()
|
||||
{
|
||||
_autoCloseCancelTokenSource?.Cancel();
|
||||
|
||||
if (State != DoorState.Open || _doorCheck == null || !_doorCheck.AutoCloseCheck())
|
||||
{
|
||||
return;
|
||||
}
|
||||
_autoCloseCancelTokenSource = new();
|
||||
|
||||
var realCloseTime = _doorCheck.GetCloseSpeed() ?? AutoCloseDelay;
|
||||
|
||||
Owner.SpawnRepeatingTimer(realCloseTime, async () =>
|
||||
{
|
||||
if (CanCloseGeneric())
|
||||
{
|
||||
// Close() cancels _autoCloseCancellationTokenSource, so we're fine.
|
||||
Close();
|
||||
}
|
||||
}, _autoCloseCancelTokenSource.Token);
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if(!eventArgs.Using.TryGetComponent(out ToolComponent? tool))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// for prying doors
|
||||
if (tool.HasQuality(ToolQuality.Prying) && !IsWeldedShut)
|
||||
{
|
||||
var successfulPry = false;
|
||||
|
||||
if (_doorCheck != null)
|
||||
{
|
||||
_doorCheck.OnStartPry(eventArgs);
|
||||
successfulPry = await tool.UseTool(eventArgs.User, Owner,
|
||||
_doorCheck.GetPryTime() ?? 0.5f, ToolQuality.Prying, () => _doorCheck.CanPryCheck(eventArgs));
|
||||
}
|
||||
else
|
||||
{
|
||||
successfulPry = await tool.UseTool(eventArgs.User, Owner, 0.5f, ToolQuality.Prying);
|
||||
}
|
||||
|
||||
if (successfulPry && !IsWeldedShut)
|
||||
{
|
||||
if (State == DoorState.Closed)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
else if (State == DoorState.Open)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// for welding doors
|
||||
if (CanWeldShut && tool.Owner.TryGetComponent(out WelderComponent? welder) && welder.WelderLit)
|
||||
{
|
||||
if(!_beingWelded)
|
||||
{
|
||||
_beingWelded = true;
|
||||
if(await welder.UseTool(eventArgs.User, Owner, 3f, ToolQuality.Welding, 3f, () => CanWeldShut))
|
||||
{
|
||||
// just in case
|
||||
if (!CanWeldShut)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_beingWelded = false;
|
||||
IsWeldedShut = !IsWeldedShut;
|
||||
return true;
|
||||
}
|
||||
_beingWelded = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_beingWelded = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates the corresponding door electronics board on the door.
|
||||
/// This exists so when you deconstruct doors that were serialized with the map,
|
||||
/// you can retrieve the door electronics board.
|
||||
/// </summary>
|
||||
private void CreateDoorElectronicsBoard()
|
||||
{
|
||||
// Ensure that the construction component is aware of the board container.
|
||||
if (Owner.TryGetComponent(out ConstructionComponent? construction))
|
||||
construction.AddContainer("board");
|
||||
|
||||
// We don't do anything if this is null or empty.
|
||||
if (string.IsNullOrEmpty(_boardPrototype))
|
||||
return;
|
||||
|
||||
var container = Owner.EnsureContainer<Container>("board", out var existed);
|
||||
|
||||
return;
|
||||
/* // TODO ShadowCommander: Re-enable when access is added to boards. Requires map update.
|
||||
if (existed)
|
||||
{
|
||||
// We already contain a board. Note: We don't check if it's the right one!
|
||||
if (container.ContainedEntities.Count != 0)
|
||||
return;
|
||||
}
|
||||
|
||||
var board = Owner.EntityManager.SpawnEntity(_boardPrototype, Owner.Transform.Coordinates);
|
||||
|
||||
if(!container.Insert(board))
|
||||
Logger.Warning($"Couldn't insert board {board} into door {Owner}!");
|
||||
*/
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new DoorComponentState(State, StateChangeStartTime, CurrentlyCrushing, GameTiming.CurTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Stack;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Utility;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class HealingComponent : Component, IAfterInteract
|
||||
{
|
||||
public override string Name => "Healing";
|
||||
|
||||
[DataField("heal")] public Dictionary<DamageTypePrototype, int> Heal { get; private set; } = new();
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.Target == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!eventArgs.Target.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ActionBlockerSystem.CanInteract(eventArgs.User))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eventArgs.User != eventArgs.Target &&
|
||||
!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out StackComponent? stack) &&
|
||||
!stack.Use(1))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var (type, amount) in Heal)
|
||||
{
|
||||
damageable.ChangeDamage(type, -amount, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Power.ApcNetComponents;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Server.Utility;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Medical;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Medical
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[ComponentReference(typeof(SharedMedicalScannerComponent))]
|
||||
public class MedicalScannerComponent : SharedMedicalScannerComponent, IActivate, IDestroyAct
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5);
|
||||
private TimeSpan _lastInternalOpenAttempt;
|
||||
|
||||
private ContainerSlot _bodyContainer = default!;
|
||||
private readonly Vector2 _ejectOffset = new(0f, 0f);
|
||||
|
||||
[ViewVariables]
|
||||
private bool Powered => !Owner.TryGetComponent(out PowerReceiverComponent? receiver) || receiver.Powered;
|
||||
[ViewVariables]
|
||||
private BoundUserInterface? UserInterface => Owner.GetUIOrNull(MedicalScannerUiKey.Key);
|
||||
|
||||
public bool IsOccupied => _bodyContainer.ContainedEntity != null;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += OnUiReceiveMessage;
|
||||
}
|
||||
|
||||
_bodyContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-bodyContainer");
|
||||
|
||||
// TODO: write this so that it checks for a change in power events and acts accordingly.
|
||||
var newState = GetUserInterfaceState();
|
||||
UserInterface?.SetState(newState);
|
||||
|
||||
UpdateUserInterface();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case RelayMovementEntityMessage msg:
|
||||
{
|
||||
if (ActionBlockerSystem.CanInteract(msg.Entity))
|
||||
{
|
||||
if (_gameTiming.CurTime <
|
||||
_lastInternalOpenAttempt + InternalOpenAttemptDelay)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
_lastInternalOpenAttempt = _gameTiming.CurTime;
|
||||
EjectBody();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
||||
new(
|
||||
null,
|
||||
new Dictionary<DamageGroupPrototype, int>(),
|
||||
new Dictionary<DamageTypePrototype, int>(),
|
||||
false);
|
||||
|
||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
||||
{
|
||||
var body = _bodyContainer.ContainedEntity;
|
||||
if (body == null)
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance?.SetData(MedicalScannerVisuals.Status, MedicalScannerStatus.Open);
|
||||
}
|
||||
|
||||
return EmptyUIState;
|
||||
}
|
||||
|
||||
if (!body.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
return EmptyUIState;
|
||||
}
|
||||
|
||||
var classes = new Dictionary<DamageGroupPrototype, int>(damageable.DamageClasses);
|
||||
var types = new Dictionary<DamageTypePrototype, int>(damageable.DamageTypes);
|
||||
|
||||
if (_bodyContainer.ContainedEntity?.Uid == null)
|
||||
{
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, true);
|
||||
}
|
||||
|
||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
||||
var scanned = _bodyContainer.ContainedEntity.TryGetComponent(out MindComponent? mindComponent) &&
|
||||
mindComponent.Mind != null &&
|
||||
cloningSystem.HasDnaScan(mindComponent.Mind);
|
||||
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
{
|
||||
if (!Powered)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var newState = GetUserInterfaceState();
|
||||
UserInterface?.SetState(newState);
|
||||
}
|
||||
|
||||
private MedicalScannerStatus GetStatusFromDamageState(IMobStateComponent state)
|
||||
{
|
||||
if (state.IsAlive())
|
||||
{
|
||||
return MedicalScannerStatus.Green;
|
||||
}
|
||||
else if (state.IsCritical())
|
||||
{
|
||||
return MedicalScannerStatus.Red;
|
||||
}
|
||||
else if (state.IsDead())
|
||||
{
|
||||
return MedicalScannerStatus.Death;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MedicalScannerStatus.Yellow;
|
||||
}
|
||||
}
|
||||
|
||||
private MedicalScannerStatus GetStatus()
|
||||
{
|
||||
if (Powered)
|
||||
{
|
||||
var body = _bodyContainer.ContainedEntity;
|
||||
var state = body?.GetComponentOrNull<IMobStateComponent>();
|
||||
|
||||
return state == null
|
||||
? MedicalScannerStatus.Open
|
||||
: GetStatusFromDamageState(state);
|
||||
}
|
||||
|
||||
return MedicalScannerStatus.Off;
|
||||
}
|
||||
|
||||
private void UpdateAppearance()
|
||||
{
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(MedicalScannerVisuals.Status, GetStatus());
|
||||
}
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out IActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Powered)
|
||||
return;
|
||||
|
||||
UserInterface?.Open(actor.playerSession);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EnterVerb : Verb<MedicalScannerComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Enter");
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Invisible : VerbVisibility.Visible;
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
||||
{
|
||||
component.InsertBody(user);
|
||||
}
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectVerb : Verb<MedicalScannerComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, MedicalScannerComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user))
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
data.Text = Loc.GetString("Eject");
|
||||
data.Visibility = component.IsOccupied ? VerbVisibility.Visible : VerbVisibility.Invisible;
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, MedicalScannerComponent component)
|
||||
{
|
||||
component.EjectBody();
|
||||
}
|
||||
}
|
||||
|
||||
public void InsertBody(IEntity user)
|
||||
{
|
||||
_bodyContainer.Insert(user);
|
||||
UpdateUserInterface();
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void EjectBody()
|
||||
{
|
||||
var containedEntity = _bodyContainer.ContainedEntity;
|
||||
if (containedEntity == null) return;
|
||||
_bodyContainer.Remove(containedEntity);
|
||||
containedEntity.Transform.WorldPosition += _ejectOffset;
|
||||
UpdateUserInterface();
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
UpdateUserInterface();
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
private void OnUiReceiveMessage(ServerBoundUserInterfaceMessage obj)
|
||||
{
|
||||
if (obj.Message is not UiButtonPressedMessage message) return;
|
||||
|
||||
switch (message.Button)
|
||||
{
|
||||
case UiButton.ScanDNA:
|
||||
if (_bodyContainer.ContainedEntity != null)
|
||||
{
|
||||
//TODO: Show a 'ERROR: Body is completely devoid of soul' if no Mind owns the entity.
|
||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
||||
|
||||
if (!_bodyContainer.ContainedEntity.TryGetComponent(out MindComponent? mind) || mind.Mind == null)
|
||||
break;
|
||||
|
||||
cloningSystem.AddToDnaScans(mind.Mind);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
public override bool DragDropOn(DragDropEventArgs eventArgs)
|
||||
{
|
||||
_bodyContainer.Insert(eventArgs.Dragged);
|
||||
return true;
|
||||
}
|
||||
|
||||
void IDestroyAct.OnDestroy(DestructionEventArgs eventArgs)
|
||||
{
|
||||
EjectBody();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,405 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.Body.Behavior;
|
||||
using Content.Server.GameObjects.Components.Body.Circulatory;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Temperature;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Body;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.Interfaces;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Metabolism
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MetabolismComponent : Component
|
||||
{
|
||||
[ComponentDependency] private readonly IBody? _body = default!;
|
||||
|
||||
public override string Name => "Metabolism";
|
||||
|
||||
private float _accumulatedFrameTime;
|
||||
|
||||
private bool _isShivering;
|
||||
private bool _isSweating;
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamage")] private int _suffocationDamage = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("suffocationDamageRecovery")] private int _suffocationDamageRecovery = 1;
|
||||
|
||||
[ViewVariables] [DataField("needsGases")] public Dictionary<Gas, float> NeedsGases { get; set; } = new();
|
||||
|
||||
[ViewVariables] [DataField("producesGases")] public Dictionary<Gas, float> ProducesGases { get; set; } = new();
|
||||
|
||||
[ViewVariables] [DataField("deficitGases")] public Dictionary<Gas, float> DeficitGases { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Heat generated due to metabolism. It's generated via metabolism
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("metabolismHeat")]
|
||||
public float MetabolismHeat { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Heat output via radiation.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("radiatedHeat")]
|
||||
public float RadiatedHeat { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum heat regulated via sweat
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("sweatHeatRegulation")]
|
||||
public float SweatHeatRegulation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Maximum heat regulated via shivering
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("shiveringHeatRegulation")]
|
||||
public float ShiveringHeatRegulation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Amount of heat regulation that represents thermal regulation processes not
|
||||
/// explicitly coded.
|
||||
/// </summary>
|
||||
[DataField("implicitHeatRegulation")]
|
||||
public float ImplicitHeatRegulation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Normal body temperature
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
[DataField("normalBodyTemperature")]
|
||||
public float NormalBodyTemperature { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Deviation from normal temperature for body to start thermal regulation
|
||||
/// </summary>
|
||||
[DataField("thermalRegulationTemperatureThreshold")]
|
||||
public float ThermalRegulationTemperatureThreshold { get; private set; }
|
||||
|
||||
[ViewVariables] public bool Suffocating { get; private set; }
|
||||
|
||||
private Dictionary<Gas, float> NeedsAndDeficit(float frameTime)
|
||||
{
|
||||
var needs = new Dictionary<Gas, float>(NeedsGases);
|
||||
foreach (var (gas, amount) in DeficitGases)
|
||||
{
|
||||
var newAmount = (needs.GetValueOrDefault(gas) + amount) * frameTime;
|
||||
needs[gas] = newAmount;
|
||||
}
|
||||
|
||||
return needs;
|
||||
}
|
||||
|
||||
private void ClampDeficit()
|
||||
{
|
||||
var deficitGases = new Dictionary<Gas, float>(DeficitGases);
|
||||
|
||||
foreach (var (gas, deficit) in deficitGases)
|
||||
{
|
||||
if (!NeedsGases.TryGetValue(gas, out var need))
|
||||
{
|
||||
DeficitGases.Remove(gas);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deficit > need)
|
||||
{
|
||||
DeficitGases[gas] = need;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float SuffocatingPercentage()
|
||||
{
|
||||
var total = 0f;
|
||||
|
||||
foreach (var (gas, deficit) in DeficitGases)
|
||||
{
|
||||
var lack = 1f;
|
||||
if (NeedsGases.TryGetValue(gas, out var needed))
|
||||
{
|
||||
lack = deficit / needed;
|
||||
}
|
||||
|
||||
total += lack / Atmospherics.TotalNumberOfGases;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
private float GasProducedMultiplier(Gas gas, float usedAverage)
|
||||
{
|
||||
if (!ProducesGases.TryGetValue(gas, out var produces))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!NeedsGases.TryGetValue(gas, out var needs))
|
||||
{
|
||||
needs = 1;
|
||||
}
|
||||
|
||||
return needs * produces * usedAverage;
|
||||
}
|
||||
|
||||
private Dictionary<Gas, float> GasProduced(float usedAverage)
|
||||
{
|
||||
return ProducesGases.ToDictionary(pair => pair.Key, pair => GasProducedMultiplier(pair.Key, usedAverage));
|
||||
}
|
||||
|
||||
private void ProcessGases(float frameTime)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out BloodstreamComponent? bloodstream))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_body == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var lungs = _body.GetMechanismBehaviors<LungBehavior>().ToArray();
|
||||
|
||||
var needs = NeedsAndDeficit(frameTime);
|
||||
var used = 0f;
|
||||
foreach (var (gas, amountNeeded) in needs)
|
||||
{
|
||||
var bloodstreamAmount = bloodstream.Air.GetMoles(gas);
|
||||
var deficit = 0f;
|
||||
|
||||
if (bloodstreamAmount < amountNeeded)
|
||||
{
|
||||
if (!Owner.GetComponent<IMobStateComponent>().IsCritical())
|
||||
{
|
||||
// Panic inhale
|
||||
foreach (var lung in lungs)
|
||||
{
|
||||
lung.Gasp();
|
||||
}
|
||||
}
|
||||
|
||||
bloodstreamAmount = bloodstream.Air.GetMoles(gas);
|
||||
|
||||
deficit = Math.Max(0, amountNeeded - bloodstreamAmount);
|
||||
|
||||
if (deficit > 0)
|
||||
{
|
||||
bloodstream.Air.SetMoles(gas, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
bloodstream.Air.AdjustMoles(gas, -amountNeeded);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bloodstream.Air.AdjustMoles(gas, -amountNeeded);
|
||||
}
|
||||
|
||||
DeficitGases[gas] = deficit;
|
||||
|
||||
|
||||
used += (amountNeeded - deficit) / amountNeeded;
|
||||
}
|
||||
|
||||
var produced = GasProduced(used / needs.Count);
|
||||
|
||||
foreach (var (gas, amountProduced) in produced)
|
||||
{
|
||||
bloodstream.Air.AdjustMoles(gas, amountProduced);
|
||||
}
|
||||
|
||||
ClampDeficit();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Process thermal regulation
|
||||
/// </summary>
|
||||
/// <param name="frameTime"></param>
|
||||
private void ProcessThermalRegulation(float frameTime)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out TemperatureComponent? temperatureComponent)) return;
|
||||
temperatureComponent.ReceiveHeat(MetabolismHeat);
|
||||
temperatureComponent.RemoveHeat(RadiatedHeat);
|
||||
|
||||
// implicit heat regulation
|
||||
var tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
|
||||
var targetHeat = tempDiff * temperatureComponent.HeatCapacity;
|
||||
if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
|
||||
{
|
||||
temperatureComponent.RemoveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
|
||||
}
|
||||
else
|
||||
{
|
||||
temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ImplicitHeatRegulation));
|
||||
}
|
||||
|
||||
// recalc difference and target heat
|
||||
tempDiff = Math.Abs(temperatureComponent.CurrentTemperature - NormalBodyTemperature);
|
||||
targetHeat = tempDiff * temperatureComponent.HeatCapacity;
|
||||
|
||||
// if body temperature is not within comfortable, thermal regulation
|
||||
// processes starts
|
||||
if (tempDiff < ThermalRegulationTemperatureThreshold)
|
||||
{
|
||||
if (_isShivering || _isSweating)
|
||||
{
|
||||
Owner.PopupMessage(Loc.GetString("You feel comfortable"));
|
||||
}
|
||||
|
||||
_isShivering = false;
|
||||
_isSweating = false;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (temperatureComponent.CurrentTemperature > NormalBodyTemperature)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanSweat(Owner)) return;
|
||||
if (!_isSweating)
|
||||
{
|
||||
Owner.PopupMessage(Loc.GetString("You are sweating"));
|
||||
_isSweating = true;
|
||||
}
|
||||
|
||||
// creadth: sweating does not help in airless environment
|
||||
if (Owner.Transform.Coordinates.TryGetTileAir(out _, Owner.EntityManager))
|
||||
{
|
||||
temperatureComponent.RemoveHeat(Math.Min(targetHeat, SweatHeatRegulation));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!ActionBlockerSystem.CanShiver(Owner)) return;
|
||||
if (!_isShivering)
|
||||
{
|
||||
Owner.PopupMessage(Loc.GetString("You are shivering"));
|
||||
_isShivering = true;
|
||||
}
|
||||
|
||||
temperatureComponent.ReceiveHeat(Math.Min(targetHeat, ShiveringHeatRegulation));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes gases in the bloodstream.
|
||||
/// </summary>
|
||||
/// <param name="frameTime">
|
||||
/// The time since the last metabolism tick in seconds.
|
||||
/// </param>
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (!Owner.TryGetComponent<IMobStateComponent>(out var state) ||
|
||||
state.IsDead())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_accumulatedFrameTime += frameTime;
|
||||
|
||||
if (_accumulatedFrameTime < 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessGases(_accumulatedFrameTime);
|
||||
ProcessThermalRegulation(_accumulatedFrameTime);
|
||||
|
||||
_accumulatedFrameTime -= 1;
|
||||
|
||||
if (SuffocatingPercentage() > 0)
|
||||
{
|
||||
TakeSuffocationDamage();
|
||||
return;
|
||||
}
|
||||
|
||||
StopSuffocation();
|
||||
}
|
||||
|
||||
private void TakeSuffocationDamage()
|
||||
{
|
||||
Suffocating = true;
|
||||
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
alertsComponent.ShowAlert(AlertType.LowOxygen);
|
||||
}
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
damageable.ChangeDamage(_damageType, _suffocationDamage, false);
|
||||
}
|
||||
|
||||
private void StopSuffocation()
|
||||
{
|
||||
Suffocating = false;
|
||||
|
||||
if (Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
damageable.ChangeDamage(_damageType, -_suffocationDamageRecovery, false);
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent))
|
||||
{
|
||||
alertsComponent.ClearAlert(AlertType.LowOxygen);
|
||||
}
|
||||
}
|
||||
|
||||
public GasMixture Clean(BloodstreamComponent bloodstream)
|
||||
{
|
||||
var gasMixture = new GasMixture(bloodstream.Air.Volume)
|
||||
{
|
||||
Temperature = bloodstream.Air.Temperature
|
||||
};
|
||||
|
||||
for (Gas gas = 0; gas < (Gas) Atmospherics.TotalNumberOfGases; gas++)
|
||||
{
|
||||
float amount;
|
||||
var molesInBlood = bloodstream.Air.GetMoles(gas);
|
||||
|
||||
if (!NeedsGases.TryGetValue(gas, out var needed))
|
||||
{
|
||||
amount = molesInBlood;
|
||||
}
|
||||
else
|
||||
{
|
||||
var overflowThreshold = needed * 5f;
|
||||
|
||||
amount = molesInBlood > overflowThreshold
|
||||
? molesInBlood - overflowThreshold
|
||||
: 0;
|
||||
}
|
||||
|
||||
gasMixture.AdjustMoles(gas, amount);
|
||||
bloodstream.Air.AdjustMoles(gas, -amount);
|
||||
}
|
||||
|
||||
return gasMixture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.Weapon.Melee;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mining;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Mining
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class AsteroidRockComponent : Component, IInteractUsing
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
public override string Name => "AsteroidRock";
|
||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
||||
|
||||
[DataField("damageType",required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
||||
}
|
||||
}
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
var item = eventArgs.Using;
|
||||
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent)) return false;
|
||||
|
||||
var DamageableComponent = Owner.GetComponent<IDamageableComponent>().ChangeDamage(
|
||||
_damageType, meleeWeaponComponent.Damage, false, item);
|
||||
|
||||
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent)) return true;
|
||||
if (!string.IsNullOrWhiteSpace(pickaxeComponent.MiningSound))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), pickaxeComponent.MiningSound, Owner, AudioParams.Default);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Nutrition
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class HungerComponent : SharedHungerComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
[DataField("base_decay_rate")]
|
||||
private float _baseDecayRate = 0.1f;
|
||||
|
||||
[DataField("damageType",required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
private float _actualDecayRate;
|
||||
private float _currentHunger;
|
||||
private HungerThreshold _currentHungerThreshold;
|
||||
private HungerThreshold _lastHungerThreshold;
|
||||
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
||||
{
|
||||
{HungerThreshold.Overfed, 600.0f},
|
||||
{HungerThreshold.Okay, 450.0f},
|
||||
{HungerThreshold.Peckish, 300.0f},
|
||||
{HungerThreshold.Starving, 150.0f},
|
||||
{HungerThreshold.Dead, 0.0f},
|
||||
};
|
||||
|
||||
// Base stuff
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseDecayRate
|
||||
{
|
||||
get => _baseDecayRate;
|
||||
set => _baseDecayRate = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ActualDecayRate
|
||||
{
|
||||
get => _actualDecayRate;
|
||||
set => _actualDecayRate = value;
|
||||
}
|
||||
|
||||
// Hunger
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public override HungerThreshold CurrentHungerThreshold => _currentHungerThreshold;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentHunger
|
||||
{
|
||||
get => _currentHunger;
|
||||
set => _currentHunger = value;
|
||||
}
|
||||
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
||||
|
||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
||||
{
|
||||
{ HungerThreshold.Overfed, AlertType.Overfed },
|
||||
{ HungerThreshold.Peckish, AlertType.Peckish },
|
||||
{ HungerThreshold.Starving, AlertType.Starving },
|
||||
};
|
||||
|
||||
public void HungerThresholdEffect(bool force = false)
|
||||
{
|
||||
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
||||
{
|
||||
// Revert slow speed if required
|
||||
if (_lastHungerThreshold == HungerThreshold.Starving && _currentHungerThreshold != HungerThreshold.Dead &&
|
||||
Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent))
|
||||
{
|
||||
movementSlowdownComponent.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
|
||||
// Update UI
|
||||
Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent);
|
||||
|
||||
if (HungerThresholdAlertTypes.TryGetValue(_currentHungerThreshold, out var alertId))
|
||||
{
|
||||
alertsComponent?.ShowAlert(alertId);
|
||||
}
|
||||
else
|
||||
{
|
||||
alertsComponent?.ClearAlertCategory(AlertCategory.Hunger);
|
||||
}
|
||||
|
||||
switch (_currentHungerThreshold)
|
||||
{
|
||||
case HungerThreshold.Overfed:
|
||||
_lastHungerThreshold = _currentHungerThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 1.2f;
|
||||
return;
|
||||
|
||||
case HungerThreshold.Okay:
|
||||
_lastHungerThreshold = _currentHungerThreshold;
|
||||
_actualDecayRate = _baseDecayRate;
|
||||
return;
|
||||
|
||||
case HungerThreshold.Peckish:
|
||||
// Same as okay except with UI icon saying eat soon.
|
||||
_lastHungerThreshold = _currentHungerThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 0.8f;
|
||||
return;
|
||||
|
||||
case HungerThreshold.Starving:
|
||||
// TODO: If something else bumps this could cause mega-speed.
|
||||
// If some form of speed update system if multiple things are touching it use that.
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent1))
|
||||
{
|
||||
movementSlowdownComponent1.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
_lastHungerThreshold = _currentHungerThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 0.6f;
|
||||
return;
|
||||
|
||||
case HungerThreshold.Dead:
|
||||
return;
|
||||
default:
|
||||
Logger.ErrorS("hunger", $"No hunger threshold found for {_currentHungerThreshold}");
|
||||
throw new ArgumentOutOfRangeException($"No hunger threshold found for {_currentHungerThreshold}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
// Similar functionality to SS13. Should also stagger people going to the chef.
|
||||
_currentHunger = _random.Next(
|
||||
(int)_hungerThresholds[HungerThreshold.Peckish] + 10,
|
||||
(int)_hungerThresholds[HungerThreshold.Okay] - 1);
|
||||
_currentHungerThreshold = GetHungerThreshold(_currentHunger);
|
||||
_lastHungerThreshold = HungerThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
|
||||
HungerThresholdEffect(true);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public HungerThreshold GetHungerThreshold(float food)
|
||||
{
|
||||
HungerThreshold result = HungerThreshold.Dead;
|
||||
var value = HungerThresholds[HungerThreshold.Overfed];
|
||||
foreach (var threshold in _hungerThresholds)
|
||||
{
|
||||
if (threshold.Value <= value && threshold.Value >= food)
|
||||
{
|
||||
result = threshold.Key;
|
||||
value = threshold.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateFood(float amount)
|
||||
{
|
||||
_currentHunger = Math.Min(_currentHunger + amount, HungerThresholds[HungerThreshold.Overfed]);
|
||||
}
|
||||
|
||||
// TODO: If mob is moving increase rate of consumption?
|
||||
// Should use a multiplier as something like a disease would overwrite decay rate.
|
||||
public void OnUpdate(float frametime)
|
||||
{
|
||||
_currentHunger -= frametime * ActualDecayRate;
|
||||
UpdateCurrentThreshold();
|
||||
|
||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
||||
return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
|
||||
return;
|
||||
|
||||
if (!mobState.IsDead())
|
||||
{
|
||||
damageable.ChangeDamage(_damageType, 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCurrentThreshold()
|
||||
{
|
||||
var calculatedHungerThreshold = GetHungerThreshold(_currentHunger);
|
||||
// _trySound(calculatedThreshold);
|
||||
if (calculatedHungerThreshold != _currentHungerThreshold)
|
||||
{
|
||||
_currentHungerThreshold = calculatedHungerThreshold;
|
||||
HungerThresholdEffect();
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetFood()
|
||||
{
|
||||
_currentHunger = HungerThresholds[HungerThreshold.Okay];
|
||||
UpdateCurrentThreshold();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new HungerComponentState(_currentHungerThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.GameObjects.Components.Movement;
|
||||
using Content.Shared.GameObjects.Components.Nutrition;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Nutrition
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ThirstComponent : SharedThirstComponent
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
// Base stuff
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseDecayRate
|
||||
{
|
||||
get => _baseDecayRate;
|
||||
set => _baseDecayRate = value;
|
||||
}
|
||||
[DataField("base_decay_rate")]
|
||||
private float _baseDecayRate = 0.1f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float ActualDecayRate
|
||||
{
|
||||
get => _actualDecayRate;
|
||||
set => _actualDecayRate = value;
|
||||
}
|
||||
private float _actualDecayRate;
|
||||
|
||||
// Thirst
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public override ThirstThreshold CurrentThirstThreshold => _currentThirstThreshold;
|
||||
private ThirstThreshold _currentThirstThreshold;
|
||||
|
||||
private ThirstThreshold _lastThirstThreshold;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentThirst
|
||||
{
|
||||
get => _currentThirst;
|
||||
set => _currentThirst = value;
|
||||
}
|
||||
private float _currentThirst;
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public Dictionary<ThirstThreshold, float> ThirstThresholds { get; } = new()
|
||||
{
|
||||
{ThirstThreshold.OverHydrated, 600.0f},
|
||||
{ThirstThreshold.Okay, 450.0f},
|
||||
{ThirstThreshold.Thirsty, 300.0f},
|
||||
{ThirstThreshold.Parched, 150.0f},
|
||||
{ThirstThreshold.Dead, 0.0f},
|
||||
};
|
||||
|
||||
public static readonly Dictionary<ThirstThreshold, AlertType> ThirstThresholdAlertTypes = new()
|
||||
{
|
||||
{ThirstThreshold.OverHydrated, AlertType.Overhydrated},
|
||||
{ThirstThreshold.Thirsty, AlertType.Thirsty},
|
||||
{ThirstThreshold.Parched, AlertType.Parched},
|
||||
};
|
||||
|
||||
public void ThirstThresholdEffect(bool force = false)
|
||||
{
|
||||
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
||||
{
|
||||
// Revert slow speed if required
|
||||
if (_lastThirstThreshold == ThirstThreshold.Parched && _currentThirstThreshold != ThirstThreshold.Dead &&
|
||||
Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent))
|
||||
{
|
||||
movementSlowdownComponent.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
|
||||
// Update UI
|
||||
Owner.TryGetComponent(out ServerAlertsComponent? alertsComponent);
|
||||
|
||||
if (ThirstThresholdAlertTypes.TryGetValue(_currentThirstThreshold, out var alertId))
|
||||
{
|
||||
alertsComponent?.ShowAlert(alertId);
|
||||
}
|
||||
else
|
||||
{
|
||||
alertsComponent?.ClearAlertCategory(AlertCategory.Thirst);
|
||||
}
|
||||
|
||||
switch (_currentThirstThreshold)
|
||||
{
|
||||
case ThirstThreshold.OverHydrated:
|
||||
_lastThirstThreshold = _currentThirstThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 1.2f;
|
||||
return;
|
||||
|
||||
case ThirstThreshold.Okay:
|
||||
_lastThirstThreshold = _currentThirstThreshold;
|
||||
_actualDecayRate = _baseDecayRate;
|
||||
return;
|
||||
|
||||
case ThirstThreshold.Thirsty:
|
||||
// Same as okay except with UI icon saying drink soon.
|
||||
_lastThirstThreshold = _currentThirstThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 0.8f;
|
||||
return;
|
||||
|
||||
case ThirstThreshold.Parched:
|
||||
if (Owner.TryGetComponent(out MovementSpeedModifierComponent? movementSlowdownComponent1))
|
||||
{
|
||||
movementSlowdownComponent1.RefreshMovementSpeedModifiers();
|
||||
}
|
||||
_lastThirstThreshold = _currentThirstThreshold;
|
||||
_actualDecayRate = _baseDecayRate * 0.6f;
|
||||
return;
|
||||
|
||||
case ThirstThreshold.Dead:
|
||||
return;
|
||||
default:
|
||||
Logger.ErrorS("thirst", $"No thirst threshold found for {_currentThirstThreshold}");
|
||||
throw new ArgumentOutOfRangeException($"No thirst threshold found for {_currentThirstThreshold}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
_currentThirst = _random.Next(
|
||||
(int)ThirstThresholds[ThirstThreshold.Thirsty] + 10,
|
||||
(int)ThirstThresholds[ThirstThreshold.Okay] - 1);
|
||||
_currentThirstThreshold = GetThirstThreshold(_currentThirst);
|
||||
_lastThirstThreshold = ThirstThreshold.Okay; // TODO: Potentially change this -> Used Okay because no effects.
|
||||
// TODO: Check all thresholds make sense and throw if they don't.
|
||||
ThirstThresholdEffect(true);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public ThirstThreshold GetThirstThreshold(float drink)
|
||||
{
|
||||
ThirstThreshold result = ThirstThreshold.Dead;
|
||||
var value = ThirstThresholds[ThirstThreshold.OverHydrated];
|
||||
foreach (var threshold in ThirstThresholds)
|
||||
{
|
||||
if (threshold.Value <= value && threshold.Value >= drink)
|
||||
{
|
||||
result = threshold.Key;
|
||||
value = threshold.Value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void UpdateThirst(float amount)
|
||||
{
|
||||
_currentThirst = Math.Min(_currentThirst + amount, ThirstThresholds[ThirstThreshold.OverHydrated]);
|
||||
}
|
||||
|
||||
// TODO: If mob is moving increase rate of consumption.
|
||||
// Should use a multiplier as something like a disease would overwrite decay rate.
|
||||
public void OnUpdate(float frametime)
|
||||
{
|
||||
_currentThirst -= frametime * ActualDecayRate;
|
||||
UpdateCurrentThreshold();
|
||||
|
||||
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
||||
return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IMobStateComponent? mobState))
|
||||
return;
|
||||
|
||||
if (!mobState.IsDead())
|
||||
{
|
||||
damageable.ChangeDamage(_damageType, 2, true);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCurrentThreshold()
|
||||
{
|
||||
var calculatedThirstThreshold = GetThirstThreshold(_currentThirst);
|
||||
// _trySound(calculatedThreshold);
|
||||
if (calculatedThirstThreshold != _currentThirstThreshold)
|
||||
{
|
||||
_currentThirstThreshold = calculatedThirstThreshold;
|
||||
ThirstThresholdEffect();
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetThirst()
|
||||
{
|
||||
_currentThirst = ThirstThresholds[ThirstThreshold.Okay];
|
||||
UpdateCurrentThreshold();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new ThirstComponentState(_currentThirstThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,341 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.MachineLinking;
|
||||
using Content.Server.GameObjects.Components.MachineLinking.Signals;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power.ApcNetComponents.PowerReceiverUsers
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver<bool>, ISignalReceiver<ToggleSignal>, IGhostBooAffected
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override string Name => "PoweredLight";
|
||||
|
||||
private static readonly TimeSpan _thunkDelay = TimeSpan.FromSeconds(2);
|
||||
// time to blink light when ghost made boo nearby
|
||||
private static readonly TimeSpan ghostBlinkingTime = TimeSpan.FromSeconds(10);
|
||||
private static readonly TimeSpan ghostBlinkingCooldown = TimeSpan.FromSeconds(60);
|
||||
|
||||
[ComponentDependency]
|
||||
private readonly AppearanceComponent? _appearance;
|
||||
|
||||
private TimeSpan _lastThunk;
|
||||
private TimeSpan? _lastGhostBlink;
|
||||
|
||||
[DataField("hasLampOnSpawn")]
|
||||
private bool _hasLampOnSpawn = true;
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private readonly DamageTypePrototype _damageType = default!;
|
||||
|
||||
[ViewVariables] [DataField("on")]
|
||||
private bool _on = true;
|
||||
|
||||
[ViewVariables]
|
||||
private bool _currentLit;
|
||||
|
||||
[ViewVariables]
|
||||
private bool _isBlinking;
|
||||
|
||||
[ViewVariables] [DataField("ignoreGhostsBoo")]
|
||||
private bool _ignoreGhostsBoo;
|
||||
|
||||
[DataField("bulb")] private LightBulbType _bulbType = LightBulbType.Tube;
|
||||
public LightBulbType BulbType => _bulbType;
|
||||
|
||||
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public LightBulbComponent? LightBulb
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_lightBulbContainer.ContainedEntity == null) return null;
|
||||
|
||||
_lightBulbContainer.ContainedEntity.TryGetComponent(out LightBulbComponent? bulb);
|
||||
|
||||
return bulb;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO CONSTRUCTION make this use a construction graph
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
return InsertBulb(eventArgs.Using);
|
||||
}
|
||||
|
||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out IDamageableComponent? damageableComponent))
|
||||
{
|
||||
Eject();
|
||||
return false;
|
||||
}
|
||||
if(eventArgs.User.TryGetComponent(out HeatResistanceComponent? heatResistanceComponent))
|
||||
{
|
||||
if(CanBurn(heatResistanceComponent.GetHeatResistance()))
|
||||
{
|
||||
Burn();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Eject();
|
||||
return true;
|
||||
|
||||
bool CanBurn(int heatResistance)
|
||||
{
|
||||
if (LightBulb == null)
|
||||
return false;
|
||||
|
||||
return _currentLit && heatResistance < LightBulb.BurningTemperature;
|
||||
}
|
||||
|
||||
void Burn()
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("You burn your hand!"));
|
||||
damageableComponent.ChangeDamage(_damageType, 20, false, Owner);
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/lightburn.ogg", Owner);
|
||||
}
|
||||
|
||||
void Eject()
|
||||
{
|
||||
EjectBulb(eventArgs.User);
|
||||
UpdateLight();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Try to replace current bulb with a new one
|
||||
/// </summary>
|
||||
public bool ReplaceBulb(IEntity bulb)
|
||||
{
|
||||
EjectBulb();
|
||||
return InsertBulb(bulb);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts the bulb if possible.
|
||||
/// </summary>
|
||||
/// <returns>True if it could insert it, false if it couldn't.</returns>
|
||||
private bool InsertBulb(IEntity bulb)
|
||||
{
|
||||
if (LightBulb != null) return false;
|
||||
if (!bulb.TryGetComponent(out LightBulbComponent? lightBulb)) return false;
|
||||
if (lightBulb.Type != _bulbType) return false;
|
||||
|
||||
var inserted = _lightBulbContainer.Insert(bulb);
|
||||
|
||||
lightBulb.OnLightBulbStateChange += UpdateLight;
|
||||
lightBulb.OnLightColorChange += UpdateLight;
|
||||
|
||||
UpdateLight();
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ejects the bulb to a mob's hand if possible.
|
||||
/// </summary>
|
||||
private void EjectBulb(IEntity? user = null)
|
||||
{
|
||||
if (LightBulb == null) return;
|
||||
|
||||
var bulb = LightBulb;
|
||||
|
||||
bulb.OnLightBulbStateChange -= UpdateLight;
|
||||
bulb.OnLightColorChange -= UpdateLight;
|
||||
|
||||
if (!_lightBulbContainer.Remove(bulb.Owner)) return;
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (!user.TryGetComponent(out HandsComponent? hands)
|
||||
|| !hands.PutInHand(bulb.Owner.GetComponent<ItemComponent>()))
|
||||
bulb.Owner.Transform.Coordinates = user.Transform.Coordinates;
|
||||
}
|
||||
else
|
||||
{
|
||||
bulb.Owner.Transform.Coordinates = Owner.Transform.Coordinates;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For attaching UpdateLight() to events.
|
||||
/// </summary>
|
||||
public void UpdateLight(object? sender, EventArgs? e)
|
||||
{
|
||||
UpdateLight();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the light's power drain, sprite and actual light state.
|
||||
/// </summary>
|
||||
public void UpdateLight()
|
||||
{
|
||||
var powerReceiver = Owner.GetComponent<PowerReceiverComponent>();
|
||||
|
||||
if (LightBulb == null) // No light bulb.
|
||||
{
|
||||
_currentLit = false;
|
||||
powerReceiver.Load = 0;
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Empty);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (LightBulb.State)
|
||||
{
|
||||
case LightBulbState.Normal:
|
||||
if (powerReceiver.Powered && _on)
|
||||
{
|
||||
_currentLit = true;
|
||||
powerReceiver.Load = LightBulb.PowerUse;
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.On);
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbColor, LightBulb.Color);
|
||||
var time = _gameTiming.CurTime;
|
||||
if (time > _lastThunk + _thunkDelay)
|
||||
{
|
||||
_lastThunk = time;
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Machines/light_tube_on.ogg", Owner, AudioParams.Default.WithVolume(-10f));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_currentLit = false;
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Off);
|
||||
}
|
||||
break;
|
||||
case LightBulbState.Broken:
|
||||
_currentLit = false;
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Broken);
|
||||
break;
|
||||
case LightBulbState.Burned:
|
||||
_currentLit = false;
|
||||
_appearance?.SetData(PoweredLightVisuals.BulbState, PoweredLightState.Burned);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
switch (message)
|
||||
{
|
||||
case PowerChangedMessage:
|
||||
UpdateLight();
|
||||
break;
|
||||
case DamageChangedMessage msg:
|
||||
TryDestroyBulb(msg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDestroyBulb(DamageChangedMessage msg)
|
||||
{
|
||||
if (!msg.TookDamage)
|
||||
return;
|
||||
|
||||
if (LightBulb == null || LightBulb.State == LightBulbState.Broken)
|
||||
return;
|
||||
|
||||
LightBulb.State = LightBulbState.Broken;
|
||||
LightBulb.PlayBreakSound();
|
||||
UpdateLight();
|
||||
}
|
||||
|
||||
void IMapInit.MapInit()
|
||||
{
|
||||
if (_hasLampOnSpawn)
|
||||
{
|
||||
var prototype = _bulbType switch
|
||||
{
|
||||
LightBulbType.Bulb => "LightBulb",
|
||||
LightBulbType.Tube => "LightTube",
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
|
||||
var entity = Owner.EntityManager.SpawnEntity(prototype, Owner.Transform.Coordinates);
|
||||
_lightBulbContainer.Insert(entity);
|
||||
}
|
||||
|
||||
// need this to update visualizers
|
||||
UpdateLight();
|
||||
}
|
||||
|
||||
public void TriggerSignal(bool signal)
|
||||
{
|
||||
_on = signal;
|
||||
UpdateLight();
|
||||
}
|
||||
|
||||
public void TriggerSignal(ToggleSignal signal)
|
||||
{
|
||||
_on = !_on;
|
||||
UpdateLight();
|
||||
}
|
||||
|
||||
public void ToggleBlinkingLight(bool isNowBlinking)
|
||||
{
|
||||
if (_isBlinking == isNowBlinking)
|
||||
return;
|
||||
|
||||
_isBlinking = isNowBlinking;
|
||||
_appearance?.SetData(PoweredLightVisuals.Blinking, _isBlinking);
|
||||
}
|
||||
|
||||
public bool AffectedByGhostBoo(InstantActionEventArgs args)
|
||||
{
|
||||
if (_ignoreGhostsBoo)
|
||||
return false;
|
||||
|
||||
// check cooldown first to prevent abuse
|
||||
var time = _gameTiming.CurTime;
|
||||
if (_lastGhostBlink != null)
|
||||
{
|
||||
if (time <= _lastGhostBlink + ghostBlinkingCooldown)
|
||||
return false;
|
||||
}
|
||||
_lastGhostBlink = time;
|
||||
|
||||
ToggleBlinkingLight(true);
|
||||
Owner.SpawnTimer(ghostBlinkingTime, () => {
|
||||
ToggleBlinkingLight(false);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
using System;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Projectiles
|
||||
{
|
||||
/// <summary>
|
||||
/// Lasers etc.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class HitscanComponent : Component
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private TimeSpan _startTime;
|
||||
private TimeSpan _deathTime;
|
||||
|
||||
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
||||
private int _collisionMask = (int) CollisionGroup.Opaque;
|
||||
[DataField("damage")]
|
||||
private float _damage = 10f;
|
||||
|
||||
[DataField("damageType", required: true)]
|
||||
private DamageTypePrototype _damageType { get; set; } = default!;
|
||||
|
||||
[DataField("muzzleFlash")]
|
||||
private string? _muzzleFlash;
|
||||
[DataField("impactFlash")]
|
||||
private string? _impactFlash;
|
||||
[DataField("soundHitWall")]
|
||||
private string _soundHitWall = "/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg";
|
||||
[DataField("spriteName")]
|
||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
||||
|
||||
public override string Name => "Hitscan";
|
||||
public DamageTypePrototype DamageType => _damageType;
|
||||
public float MaxLength => 20.0f;
|
||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
||||
public float ColorModifier { get; set; } = 1.0f;
|
||||
public float Damage
|
||||
{
|
||||
get => _damage;
|
||||
set => _damage = value;
|
||||
}
|
||||
|
||||
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
||||
{
|
||||
var effectSystem = EntitySystem.Get<EffectSystem>();
|
||||
_startTime = _gameTiming.CurTime;
|
||||
_deathTime = _startTime + TimeSpan.FromSeconds(1);
|
||||
|
||||
var afterEffect = AfterEffects(user.Transform.Coordinates, angle, distance, 1.0f);
|
||||
if (afterEffect != null)
|
||||
{
|
||||
effectSystem.CreateParticle(afterEffect);
|
||||
}
|
||||
|
||||
// if we're too close we'll stop the impact and muzzle / impact sprites from clipping
|
||||
if (distance > 1.0f)
|
||||
{
|
||||
var impactEffect = ImpactFlash(distance, angle);
|
||||
if (impactEffect != null)
|
||||
{
|
||||
effectSystem.CreateParticle(impactEffect);
|
||||
}
|
||||
|
||||
var muzzleEffect = MuzzleFlash(user.Transform.Coordinates, angle);
|
||||
if (muzzleEffect != null)
|
||||
{
|
||||
effectSystem.CreateParticle(muzzleEffect);
|
||||
}
|
||||
}
|
||||
|
||||
if (hitEntity != null && _soundHitWall != null)
|
||||
{
|
||||
// TODO: No wall component so ?
|
||||
var offset = angle.ToVec().Normalized / 2;
|
||||
var coordinates = user.Transform.Coordinates.Offset(offset);
|
||||
SoundSystem.Play(Filter.Pvs(coordinates), _soundHitWall, coordinates);
|
||||
}
|
||||
|
||||
Owner.SpawnTimer((int) _deathTime.TotalMilliseconds, () =>
|
||||
{
|
||||
if (!Owner.Deleted)
|
||||
{
|
||||
Owner.Delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private EffectSystemMessage? MuzzleFlash(EntityCoordinates grid, Angle angle)
|
||||
{
|
||||
if (_muzzleFlash == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var offset = angle.ToVec().Normalized / 2;
|
||||
|
||||
var message = new EffectSystemMessage
|
||||
{
|
||||
EffectSprite = _muzzleFlash,
|
||||
Born = _startTime,
|
||||
DeathTime = _deathTime,
|
||||
Coordinates = grid.Offset(offset),
|
||||
//Rotated from east facing
|
||||
Rotation = (float) angle.Theta,
|
||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||
Shaded = false
|
||||
};
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private EffectSystemMessage AfterEffects(EntityCoordinates origin, Angle angle, float distance, float offset = 0.0f)
|
||||
{
|
||||
var midPointOffset = angle.ToVec() * distance / 2;
|
||||
var message = new EffectSystemMessage
|
||||
{
|
||||
EffectSprite = _spriteName,
|
||||
Born = _startTime,
|
||||
DeathTime = _deathTime,
|
||||
Size = new Vector2(distance - offset, 1f),
|
||||
Coordinates = origin.Offset(midPointOffset),
|
||||
//Rotated from east facing
|
||||
Rotation = (float) angle.Theta,
|
||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||
|
||||
Shaded = false
|
||||
};
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
private EffectSystemMessage? ImpactFlash(float distance, Angle angle)
|
||||
{
|
||||
if (_impactFlash == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var message = new EffectSystemMessage
|
||||
{
|
||||
EffectSprite = _impactFlash,
|
||||
Born = _startTime,
|
||||
DeathTime = _deathTime,
|
||||
Coordinates = Owner.Transform.Coordinates.Offset(angle.ToVec() * distance),
|
||||
//Rotated from east facing
|
||||
Rotation = (float) angle.FlipPositive(),
|
||||
Color = Vector4.Multiply(new Vector4(255, 255, 255, 750), ColorModifier),
|
||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||
Shaded = false
|
||||
};
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Projectiles;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics.Collision;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Projectiles
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ProjectileComponent : SharedProjectileComponent, IStartCollide
|
||||
{
|
||||
protected override EntityUid Shooter => _shooter;
|
||||
|
||||
private EntityUid _shooter = EntityUid.Invalid;
|
||||
|
||||
[DataField("damageTypes")]
|
||||
private Dictionary<DamageTypePrototype, int> _damageTypes = new();
|
||||
|
||||
[ViewVariables]
|
||||
public Dictionary<DamageTypePrototype, int> Damages
|
||||
{
|
||||
get => _damageTypes;
|
||||
set => _damageTypes = value;
|
||||
}
|
||||
|
||||
[DataField("deleteOnCollide")]
|
||||
public bool DeleteOnCollide { get; } = true;
|
||||
|
||||
// Get that juicy FPS hit sound
|
||||
[DataField("soundHit")]
|
||||
private string? _soundHit = default;
|
||||
[DataField("soundHitSpecies")]
|
||||
private string? _soundHitSpecies = default;
|
||||
|
||||
private bool _damagedEntity;
|
||||
|
||||
public float TimeLeft { get; set; } = 10;
|
||||
|
||||
/// <summary>
|
||||
/// Function that makes the collision of this object ignore a specific entity so we don't collide with ourselves
|
||||
/// </summary>
|
||||
/// <param name="shooter"></param>
|
||||
public void IgnoreEntity(IEntity shooter)
|
||||
{
|
||||
_shooter = shooter.Uid;
|
||||
Dirty();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the damage when our projectile collides with its victim
|
||||
/// </summary>
|
||||
void IStartCollide.CollideWith(Fixture ourFixture, Fixture otherFixture, in Manifold manifold)
|
||||
{
|
||||
// This is so entities that shouldn't get a collision are ignored.
|
||||
if (!otherFixture.Hard || _damagedEntity)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var coordinates = otherFixture.Body.Owner.Transform.Coordinates;
|
||||
var playerFilter = Filter.Pvs(coordinates);
|
||||
if (otherFixture.Body.Owner.TryGetComponent(out IDamageableComponent? damage) && _soundHitSpecies != null)
|
||||
{
|
||||
SoundSystem.Play(playerFilter, _soundHitSpecies, coordinates);
|
||||
}
|
||||
else if (_soundHit != null)
|
||||
{
|
||||
SoundSystem.Play(playerFilter, _soundHit, coordinates);
|
||||
}
|
||||
|
||||
if (damage != null)
|
||||
{
|
||||
Owner.EntityManager.TryGetEntity(_shooter, out var shooter);
|
||||
|
||||
foreach (var (damageType, amount) in _damageTypes)
|
||||
{
|
||||
damage.ChangeDamage(damageType, amount, false, shooter);
|
||||
}
|
||||
|
||||
_damagedEntity = true;
|
||||
}
|
||||
|
||||
// Damaging it can delete it
|
||||
if (!otherFixture.Body.Deleted && otherFixture.Body.Owner.TryGetComponent(out CameraRecoilComponent? recoilComponent))
|
||||
{
|
||||
var direction = ourFixture.Body.LinearVelocity.Normalized;
|
||||
recoilComponent.Kick(direction);
|
||||
}
|
||||
|
||||
if(DeleteOnCollide)
|
||||
Owner.Delete();
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new ProjectileComponentState(NetID!.Value, _shooter, IgnoreShooter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using System;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Temperature
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles changing temperature,
|
||||
/// informing others of the current temperature,
|
||||
/// and taking fire damage from high temperature.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class TemperatureComponent : Component
|
||||
{
|
||||
|
||||
[DataField("coldDamageType",required: true)]
|
||||
private readonly DamageTypePrototype coldDamageType = default!;
|
||||
|
||||
[DataField("hotDamageType",required: true)]
|
||||
private readonly DamageTypePrototype hotDamageType = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Temperature";
|
||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
||||
|
||||
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
||||
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
||||
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
||||
[ViewVariables] public float HeatCapacity {
|
||||
get
|
||||
{
|
||||
if (Owner.TryGetComponent<IPhysBody>(out var physics))
|
||||
{
|
||||
return SpecificHeat * physics.Mass;
|
||||
}
|
||||
|
||||
return Atmospherics.MinimumHeatCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
||||
|
||||
[DataField("heatDamageThreshold")]
|
||||
private float _heatDamageThreshold = default;
|
||||
[DataField("coldDamageThreshold")]
|
||||
private float _coldDamageThreshold = default;
|
||||
[DataField("tempDamageCoefficient")]
|
||||
private float _tempDamageCoefficient = 1;
|
||||
[DataField("currentTemperature")]
|
||||
private float _currentTemperature = Atmospherics.T20C;
|
||||
[DataField("specificHeat")]
|
||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var tempDamage = 0;
|
||||
DamageTypePrototype? damageType = null;
|
||||
if (CurrentTemperature >= _heatDamageThreshold)
|
||||
{
|
||||
tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
||||
damageType = hotDamageType;
|
||||
}
|
||||
else if (CurrentTemperature <= _coldDamageThreshold)
|
||||
{
|
||||
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
||||
damageType = coldDamageType;
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||
{
|
||||
switch (CurrentTemperature)
|
||||
{
|
||||
// Cold strong.
|
||||
case var t when t <= 260:
|
||||
status.ShowAlert(AlertType.Cold, 3);
|
||||
break;
|
||||
|
||||
// Cold mild.
|
||||
case var t when t <= 280 && t > 260:
|
||||
status.ShowAlert(AlertType.Cold, 2);
|
||||
break;
|
||||
|
||||
// Cold weak.
|
||||
case var t when t <= 292 && t > 280:
|
||||
status.ShowAlert(AlertType.Cold, 1);
|
||||
break;
|
||||
|
||||
// Safe.
|
||||
case var t when t <= 327 && t > 292:
|
||||
status.ClearAlertCategory(AlertCategory.Temperature);
|
||||
break;
|
||||
|
||||
// Heat weak.
|
||||
case var t when t <= 335 && t > 327:
|
||||
status.ShowAlert(AlertType.Hot, 1);
|
||||
break;
|
||||
|
||||
// Heat mild.
|
||||
case var t when t <= 345 && t > 335:
|
||||
status.ShowAlert(AlertType.Hot, 2);
|
||||
break;
|
||||
|
||||
// Heat strong.
|
||||
case var t when t > 345:
|
||||
status.ShowAlert(AlertType.Hot, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (damageType is null) return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||
component.ChangeDamage(damageType, tempDamage, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forcefully give heat to this component
|
||||
/// </summary>
|
||||
/// <param name="heatAmount"></param>
|
||||
public void ReceiveHeat(float heatAmount)
|
||||
{
|
||||
CurrentTemperature += heatAmount / HeatCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forcefully remove heat from this component
|
||||
/// </summary>
|
||||
/// <param name="heatAmount"></param>
|
||||
public void RemoveHeat(float heatAmount)
|
||||
{
|
||||
CurrentTemperature -= heatAmount / HeatCapacity;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Items;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Content.Shared.Physics;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Broadphase;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Melee
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class MeleeWeaponComponent : Component, IAttack, IHandSelected
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override string Name => "MeleeWeapon";
|
||||
private TimeSpan _lastAttackTime;
|
||||
private TimeSpan _cooldownEnd;
|
||||
|
||||
[DataField("hitSound")]
|
||||
private string _hitSound = "/Audio/Weapons/genhit1.ogg";
|
||||
|
||||
[DataField("missSound")]
|
||||
private string _missSound = "/Audio/Weapons/punchmiss.ogg";
|
||||
|
||||
[DataField("arcCooldownTime")]
|
||||
public float ArcCooldownTime { get; private set; } = 1f;
|
||||
|
||||
[DataField("cooldownTime")]
|
||||
public float CooldownTime { get; private set; } = 1f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("clickArc")]
|
||||
public string ClickArc { get; set; } = "punch";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("arc")]
|
||||
public string Arc { get; set; } = "default";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("arcwidth")] public float ArcWidth { get; set; } = 90;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("range")]
|
||||
public float Range { get; set; } = 1;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("damage")]
|
||||
public int Damage { get; set; } = 5;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("damageType", required: true)]
|
||||
public DamageTypePrototype DamageType { get; set; } = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("clickAttackEffect")] public bool ClickAttackEffect { get; set; } = true;
|
||||
|
||||
protected virtual bool OnHitEntities(IReadOnlyList<IEntity> entities, AttackEventArgs eventArgs)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IAttack.WideAttack(AttackEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.WideAttack) return true;
|
||||
|
||||
var curTime = _gameTiming.CurTime;
|
||||
|
||||
if (curTime < _cooldownEnd)
|
||||
return true;
|
||||
|
||||
var location = eventArgs.User.Transform.Coordinates;
|
||||
var diff = eventArgs.ClickLocation.ToMapPos(Owner.EntityManager) - location.ToMapPos(Owner.EntityManager);
|
||||
var angle = Angle.FromWorldVec(diff);
|
||||
|
||||
// This should really be improved. GetEntitiesInArc uses pos instead of bounding boxes.
|
||||
var entities = ArcRayCast(eventArgs.User.Transform.WorldPosition, angle, eventArgs.User);
|
||||
|
||||
if (entities.Count != 0)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _hitSound, entities.First());
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _missSound, eventArgs.User);
|
||||
}
|
||||
|
||||
var hitEntities = new List<IEntity>();
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
if (!entity.Transform.IsMapTransform || entity == eventArgs.User)
|
||||
continue;
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageComponent))
|
||||
{
|
||||
damageComponent.ChangeDamage(DamageType, Damage, false, Owner);
|
||||
hitEntities.Add(entity);
|
||||
}
|
||||
}
|
||||
SendMessage(new MeleeHitMessage(hitEntities));
|
||||
|
||||
if (!OnHitEntities(hitEntities, eventArgs)) return false;
|
||||
|
||||
if (Arc != null)
|
||||
{
|
||||
var sys = EntitySystem.Get<MeleeWeaponSystem>();
|
||||
sys.SendAnimation(Arc, angle, eventArgs.User, Owner, hitEntities);
|
||||
}
|
||||
|
||||
_lastAttackTime = curTime;
|
||||
_cooldownEnd = _lastAttackTime + TimeSpan.FromSeconds(ArcCooldownTime);
|
||||
|
||||
RefreshItemCooldown();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IAttack.ClickAttack(AttackEventArgs eventArgs)
|
||||
{
|
||||
if (eventArgs.WideAttack) return false;
|
||||
|
||||
var curTime = _gameTiming.CurTime;
|
||||
|
||||
if (curTime < _cooldownEnd || !eventArgs.Target.IsValid())
|
||||
return true;
|
||||
|
||||
var target = eventArgs.TargetEntity;
|
||||
|
||||
var location = eventArgs.User.Transform.Coordinates;
|
||||
var diff = eventArgs.ClickLocation.ToMapPos(Owner.EntityManager) - location.ToMapPos(Owner.EntityManager);
|
||||
var angle = Angle.FromWorldVec(diff);
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _hitSound, target);
|
||||
}
|
||||
else
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _missSound, eventArgs.User);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (target.TryGetComponent(out IDamageableComponent? damageComponent))
|
||||
{
|
||||
damageComponent.ChangeDamage(DamageType, Damage, false, Owner);
|
||||
}
|
||||
SendMessage(new MeleeHitMessage(new List<IEntity> { target }));
|
||||
|
||||
var targets = new[] { target };
|
||||
|
||||
if (!OnHitEntities(targets, eventArgs))
|
||||
return false;
|
||||
|
||||
if (ClickArc != null)
|
||||
{
|
||||
var sys = EntitySystem.Get<MeleeWeaponSystem>();
|
||||
sys.SendAnimation(ClickArc, angle, eventArgs.User, Owner, targets, ClickAttackEffect, false);
|
||||
}
|
||||
|
||||
_lastAttackTime = curTime;
|
||||
_cooldownEnd = _lastAttackTime + TimeSpan.FromSeconds(CooldownTime);
|
||||
|
||||
RefreshItemCooldown();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private HashSet<IEntity> ArcRayCast(Vector2 position, Angle angle, IEntity ignore)
|
||||
{
|
||||
var widthRad = Angle.FromDegrees(ArcWidth);
|
||||
var increments = 1 + 35 * (int) Math.Ceiling(widthRad / (2 * Math.PI));
|
||||
var increment = widthRad / increments;
|
||||
var baseAngle = angle - widthRad / 2;
|
||||
|
||||
var resSet = new HashSet<IEntity>();
|
||||
|
||||
var mapId = Owner.Transform.MapID;
|
||||
for (var i = 0; i < increments; i++)
|
||||
{
|
||||
var castAngle = new Angle(baseAngle + increment * i);
|
||||
var res = EntitySystem.Get<SharedBroadPhaseSystem>().IntersectRay(mapId,
|
||||
new CollisionRay(position, castAngle.ToWorldVec(),
|
||||
(int) (CollisionGroup.Impassable | CollisionGroup.MobImpassable)), Range, ignore).ToList();
|
||||
|
||||
if (res.Count != 0)
|
||||
{
|
||||
resSet.Add(res[0].HitEntity);
|
||||
}
|
||||
}
|
||||
|
||||
return resSet;
|
||||
}
|
||||
|
||||
void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs)
|
||||
{
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var cool = TimeSpan.FromSeconds(CooldownTime * 0.5f);
|
||||
|
||||
if (curTime < _cooldownEnd)
|
||||
{
|
||||
if (_cooldownEnd - curTime < cool)
|
||||
{
|
||||
_lastAttackTime = curTime;
|
||||
_cooldownEnd += cool;
|
||||
}
|
||||
else
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_lastAttackTime = curTime;
|
||||
_cooldownEnd = curTime + cool;
|
||||
}
|
||||
|
||||
RefreshItemCooldown();
|
||||
}
|
||||
|
||||
private void RefreshItemCooldown()
|
||||
{
|
||||
if (Owner.TryGetComponent(out ItemCooldownComponent? cooldown))
|
||||
{
|
||||
cooldown.CooldownStart = _lastAttackTime;
|
||||
cooldown.CooldownEnd = _cooldownEnd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class MeleeHitMessage : ComponentMessage
|
||||
{
|
||||
public readonly List<IEntity> HitEntities;
|
||||
|
||||
public MeleeHitMessage(List<IEntity> hitEntities)
|
||||
{
|
||||
HitEntities = hitEntities;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.GameObjects.Components.Projectiles;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.GameObjects.Verbs;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ServerBatteryBarrelComponent : ServerRangedBarrelComponent
|
||||
{
|
||||
public override string Name => "BatteryBarrel";
|
||||
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
|
||||
|
||||
// The minimum change we need before we can fire
|
||||
[DataField("lowerChargeLimit")]
|
||||
[ViewVariables] private float _lowerChargeLimit = 10;
|
||||
[DataField("fireCost")]
|
||||
[ViewVariables] private int _baseFireCost = 300;
|
||||
// What gets fired
|
||||
[DataField("ammoPrototype")]
|
||||
[ViewVariables] private string? _ammoPrototype;
|
||||
|
||||
[ViewVariables] public IEntity? PowerCellEntity => _powerCellContainer.ContainedEntity;
|
||||
public BatteryComponent? PowerCell => _powerCellContainer.ContainedEntity?.GetComponentOrNull<BatteryComponent>();
|
||||
private ContainerSlot _powerCellContainer = default!;
|
||||
private ContainerSlot _ammoContainer = default!;
|
||||
[DataField("powerCellPrototype")]
|
||||
private string? _powerCellPrototype = default;
|
||||
[DataField("powerCellRemovable")]
|
||||
[ViewVariables] private bool _powerCellRemovable = default;
|
||||
|
||||
public override int ShotsLeft
|
||||
{
|
||||
get
|
||||
{
|
||||
var powerCell = _powerCellContainer.ContainedEntity;
|
||||
|
||||
if (powerCell == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) Math.Ceiling(powerCell.GetComponent<BatteryComponent>().CurrentCharge / _baseFireCost);
|
||||
}
|
||||
}
|
||||
|
||||
public override int Capacity
|
||||
{
|
||||
get
|
||||
{
|
||||
var powerCell = _powerCellContainer.ContainedEntity;
|
||||
|
||||
if (powerCell == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) Math.Ceiling((float) (powerCell.GetComponent<BatteryComponent>().MaxCharge / _baseFireCost));
|
||||
}
|
||||
}
|
||||
|
||||
private AppearanceComponent? _appearanceComponent;
|
||||
|
||||
// Sounds
|
||||
[DataField("soundPowerCellInsert")]
|
||||
private string? _soundPowerCellInsert = default;
|
||||
[DataField("soundPowerCellEject")]
|
||||
private string? _soundPowerCellEject = default;
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
(int, int)? count = (ShotsLeft, Capacity);
|
||||
|
||||
return new BatteryBarrelComponentState(
|
||||
FireRateSelector,
|
||||
count);
|
||||
}
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
_powerCellContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powercell-container", out var existing);
|
||||
if (!existing && _powerCellPrototype != null)
|
||||
{
|
||||
var powerCellEntity = Owner.EntityManager.SpawnEntity(_powerCellPrototype, Owner.Transform.Coordinates);
|
||||
_powerCellContainer.Insert(powerCellEntity);
|
||||
}
|
||||
|
||||
if (_ammoPrototype != null)
|
||||
{
|
||||
_ammoContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-ammo-container");
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearanceComponent))
|
||||
{
|
||||
_appearanceComponent = appearanceComponent;
|
||||
}
|
||||
Dirty();
|
||||
}
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
UpdateAppearance();
|
||||
}
|
||||
|
||||
public void UpdateAppearance()
|
||||
{
|
||||
_appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, _powerCellContainer.ContainedEntity != null);
|
||||
_appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft);
|
||||
_appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity);
|
||||
Dirty();
|
||||
}
|
||||
|
||||
public override IEntity PeekAmmo()
|
||||
{
|
||||
// Spawn a dummy entity because it's easier to work with I guess
|
||||
// This will get re-used for the projectile
|
||||
var ammo = _ammoContainer.ContainedEntity;
|
||||
if (ammo == null)
|
||||
{
|
||||
ammo = Owner.EntityManager.SpawnEntity(_ammoPrototype, Owner.Transform.Coordinates);
|
||||
_ammoContainer.Insert(ammo);
|
||||
}
|
||||
|
||||
return ammo;
|
||||
}
|
||||
|
||||
public override IEntity? TakeProjectile(EntityCoordinates spawnAt)
|
||||
{
|
||||
var powerCellEntity = _powerCellContainer.ContainedEntity;
|
||||
|
||||
if (powerCellEntity == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var capacitor = powerCellEntity.GetComponent<BatteryComponent>();
|
||||
if (capacitor.CurrentCharge < _lowerChargeLimit)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// Can fire confirmed
|
||||
// Multiply the entity's damage / whatever by the percentage of charge the shot has.
|
||||
IEntity entity;
|
||||
var chargeChange = Math.Min(capacitor.CurrentCharge, _baseFireCost);
|
||||
if (capacitor.UseCharge(chargeChange) < _lowerChargeLimit)
|
||||
{
|
||||
// Handling of funny exploding cells.
|
||||
return null;
|
||||
}
|
||||
var energyRatio = chargeChange / _baseFireCost;
|
||||
|
||||
if (_ammoContainer.ContainedEntity != null)
|
||||
{
|
||||
entity = _ammoContainer.ContainedEntity;
|
||||
_ammoContainer.Remove(entity);
|
||||
entity.Transform.Coordinates = spawnAt;
|
||||
}
|
||||
else
|
||||
{
|
||||
entity = Owner.EntityManager.SpawnEntity(_ammoPrototype, spawnAt);
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out ProjectileComponent? projectileComponent))
|
||||
{
|
||||
if (energyRatio < 1.0)
|
||||
{
|
||||
var newDamages = new Dictionary<DamageTypePrototype, int>(projectileComponent.Damages.Count);
|
||||
foreach (var (damageType, damage) in projectileComponent.Damages)
|
||||
{
|
||||
newDamages.Add(damageType, (int) (damage * energyRatio));
|
||||
}
|
||||
|
||||
projectileComponent.Damages = newDamages;
|
||||
}
|
||||
} else if (entity.TryGetComponent(out HitscanComponent? hitscanComponent))
|
||||
{
|
||||
hitscanComponent.Damage *= energyRatio;
|
||||
hitscanComponent.ColorModifier = energyRatio;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("Ammo doesn't have hitscan or projectile?");
|
||||
}
|
||||
|
||||
Dirty();
|
||||
UpdateAppearance();
|
||||
return entity;
|
||||
}
|
||||
|
||||
public bool TryInsertPowerCell(IEntity entity)
|
||||
{
|
||||
if (_powerCellContainer.ContainedEntity != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!entity.HasComponent<BatteryComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_soundPowerCellInsert != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellInsert, Owner.Transform.Coordinates, AudioParams.Default.WithVolume(-2));
|
||||
}
|
||||
|
||||
_powerCellContainer.Insert(entity);
|
||||
|
||||
Dirty();
|
||||
UpdateAppearance();
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool UseEntity(UseEntityEventArgs eventArgs)
|
||||
{
|
||||
if (!_powerCellRemovable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (PowerCellEntity == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryEjectCell(eventArgs.User);
|
||||
}
|
||||
|
||||
private bool TryEjectCell(IEntity user)
|
||||
{
|
||||
if (PowerCell == null || !_powerCellRemovable)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!user.TryGetComponent(out HandsComponent? hands))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var cell = PowerCell;
|
||||
if (!_powerCellContainer.Remove(cell.Owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Dirty();
|
||||
UpdateAppearance();
|
||||
|
||||
if (!hands.PutInHand(cell.Owner.GetComponent<ItemComponent>()))
|
||||
{
|
||||
cell.Owner.Transform.Coordinates = user.Transform.Coordinates;
|
||||
}
|
||||
|
||||
if (_soundPowerCellEject != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _soundPowerCellEject, Owner.Transform.Coordinates, AudioParams.Default.WithVolume(-2));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.Using.HasComponent<BatteryComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return TryInsertPowerCell(eventArgs.Using);
|
||||
}
|
||||
|
||||
[Verb]
|
||||
public sealed class EjectCellVerb : Verb<ServerBatteryBarrelComponent>
|
||||
{
|
||||
protected override void GetData(IEntity user, ServerBatteryBarrelComponent component, VerbData data)
|
||||
{
|
||||
if (!ActionBlockerSystem.CanInteract(user) || !component._powerCellRemovable)
|
||||
{
|
||||
data.Visibility = VerbVisibility.Invisible;
|
||||
return;
|
||||
}
|
||||
|
||||
if (component.PowerCell == null)
|
||||
{
|
||||
data.Text = Loc.GetString("No cell");
|
||||
data.Visibility = VerbVisibility.Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.Text = Loc.GetString("Eject cell");
|
||||
data.IconTexture = "/Textures/Interface/VerbIcons/eject.svg.192dpi.png";
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Activate(IEntity user, ServerBatteryBarrelComponent component)
|
||||
{
|
||||
component.TryEjectCell(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Mobs;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Barrels;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Weapons.Ranged;
|
||||
using Content.Shared.GameObjects.EntitySystems.ActionBlocker;
|
||||
using Content.Shared.Interfaces;
|
||||
using Content.Shared.Interfaces.GameObjects.Components;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Content.Server.Atmos;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ServerRangedWeaponComponent : SharedRangedWeaponComponent, IHandSelected
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private TimeSpan _lastFireTime;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("clumsyCheck")]
|
||||
public bool ClumsyCheck { get; set; } = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("clumsyExplodeChance")]
|
||||
public float ClumsyExplodeChance { get; set; } = 0.5f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("canHotspot")]
|
||||
private bool _canHotspot = true;
|
||||
|
||||
public Func<bool>? WeaponCanFireHandler;
|
||||
public Func<IEntity, bool>? UserCanFireHandler;
|
||||
public Action<IEntity, Vector2>? FireHandler;
|
||||
|
||||
public ServerRangedBarrelComponent? Barrel
|
||||
{
|
||||
get => _barrel;
|
||||
set
|
||||
{
|
||||
if (_barrel != null && value != null)
|
||||
{
|
||||
Logger.Error("Tried setting Barrel on RangedWeapon that already has one");
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
_barrel = value;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
private ServerRangedBarrelComponent? _barrel;
|
||||
|
||||
private FireRateSelector FireRateSelector => _barrel?.FireRateSelector ?? FireRateSelector.Safety;
|
||||
|
||||
private bool WeaponCanFire()
|
||||
{
|
||||
return WeaponCanFireHandler == null || WeaponCanFireHandler();
|
||||
}
|
||||
|
||||
private bool UserCanFire(IEntity user)
|
||||
{
|
||||
return (UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession? session = null)
|
||||
{
|
||||
base.HandleNetworkMessage(message, channel, session);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(session));
|
||||
}
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case FirePosComponentMessage msg:
|
||||
var user = session.AttachedEntity;
|
||||
if (user == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg.TargetGrid != GridId.Invalid)
|
||||
{
|
||||
// grid pos
|
||||
if (!_mapManager.TryGetGrid(msg.TargetGrid, out var grid))
|
||||
{
|
||||
// Client sent us a message with an invalid grid.
|
||||
break;
|
||||
}
|
||||
|
||||
var targetPos = grid.LocalToWorld(msg.TargetPosition);
|
||||
TryFire(user, targetPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
// map pos
|
||||
TryFire(user, msg.TargetPosition);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState(ICommonSession player)
|
||||
{
|
||||
return new RangedWeaponComponentState(FireRateSelector);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to fire a round of ammo out of the weapon.
|
||||
/// </summary>
|
||||
/// <param name="user">Entity that is operating the weapon, usually the player.</param>
|
||||
/// <param name="targetPos">Target position on the map to shoot at.</param>
|
||||
private void TryFire(IEntity user, Vector2 targetPos)
|
||||
{
|
||||
if (!user.TryGetComponent(out HandsComponent? hands) || hands.GetActiveHand?.Owner != Owner)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!user.TryGetComponent(out CombatModeComponent? combat) || !combat.IsInCombatMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!UserCanFire(user) || !WeaponCanFire())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var curTime = _gameTiming.CurTime;
|
||||
var span = curTime - _lastFireTime;
|
||||
if (span.TotalSeconds < 1 / _barrel?.FireRate)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_lastFireTime = curTime;
|
||||
|
||||
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
||||
{
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Items/bikehorn.ogg",
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Weapons/Guns/Gunshots/bang.ogg",
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
|
||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
||||
{
|
||||
health.ChangeDamage(health.GetDamageType("Blunt"), 10, false, user);
|
||||
health.ChangeDamage(health.GetDamageType("Heat"), 5, false, user);
|
||||
}
|
||||
|
||||
if (user.TryGetComponent(out StunnableComponent? stun))
|
||||
{
|
||||
stun.Paralyze(3f);
|
||||
}
|
||||
|
||||
user.PopupMessage(Loc.GetString("The gun blows up in your face!"));
|
||||
|
||||
Owner.Delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_canHotspot && user.Transform.Coordinates.TryGetTileAtmosphere(out var tile))
|
||||
{
|
||||
tile.HotspotExpose(700, 50);
|
||||
}
|
||||
FireHandler?.Invoke(user, targetPos);
|
||||
}
|
||||
|
||||
// Probably a better way to do this.
|
||||
void IHandSelected.HandSelected(HandSelectedEventArgs eventArgs)
|
||||
{
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
100
Content.Server/GameObjects/EntitySystems/GodmodeSystem.cs
Normal file
100
Content.Server/GameObjects/EntitySystems/GodmodeSystem.cs
Normal file
@@ -0,0 +1,100 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameTicking;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GodmodeSystem : EntitySystem, IResettingEntitySystem
|
||||
{
|
||||
private readonly Dictionary<IEntity, OldEntityInformation> _entities = new();
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_entities.Clear();
|
||||
}
|
||||
|
||||
public bool EnableGodmode(IEntity entity)
|
||||
{
|
||||
if (_entities.ContainsKey(entity))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_entities[entity] = new OldEntityInformation(entity);
|
||||
|
||||
if (entity.TryGetComponent(out MovedByPressureComponent? moved))
|
||||
{
|
||||
moved.Enabled = false;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
//TODO use resistanceSet instead?
|
||||
damageable.Godmode = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool HasGodmode(IEntity entity)
|
||||
{
|
||||
return _entities.ContainsKey(entity);
|
||||
}
|
||||
|
||||
public bool DisableGodmode(IEntity entity)
|
||||
{
|
||||
if (!_entities.Remove(entity, out var old))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out MovedByPressureComponent? moved))
|
||||
{
|
||||
moved.Enabled = old.MovedByPressure;
|
||||
}
|
||||
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
damageable.Godmode = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Toggles godmode for a given entity.
|
||||
/// </summary>
|
||||
/// <param name="entity">The entity to toggle godmode for.</param>
|
||||
/// <returns>true if enabled, false if disabled.</returns>
|
||||
public bool ToggleGodmode(IEntity entity)
|
||||
{
|
||||
if (HasGodmode(entity))
|
||||
{
|
||||
DisableGodmode(entity);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
EnableGodmode(entity);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public class OldEntityInformation
|
||||
{
|
||||
public OldEntityInformation(IEntity entity)
|
||||
{
|
||||
Entity = entity;
|
||||
MovedByPressure = entity.IsMovedByPressure();
|
||||
}
|
||||
|
||||
public IEntity Entity { get; }
|
||||
public bool MovedByPressure { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
94
Content.Server/GameTicking/GamePreset.cs
Normal file
94
Content.Server/GameTicking/GamePreset.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
#nullable enable annotations
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects.Components.Observer;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Server.Mobs;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
|
||||
namespace Content.Server.GameTicking
|
||||
{
|
||||
/// <summary>
|
||||
/// A round-start setup preset, such as which antagonists to spawn.
|
||||
/// </summary>
|
||||
public abstract class GamePreset
|
||||
{
|
||||
|
||||
|
||||
public abstract bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false);
|
||||
public virtual string ModeTitle => "Sandbox";
|
||||
public virtual string Description => "Secret!";
|
||||
public virtual bool DisallowLateJoin => false;
|
||||
public Dictionary<NetUserId, HumanoidCharacterProfile> ReadyProfiles = new();
|
||||
|
||||
public virtual void OnGameStarted() { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player is spawned in (this includes, but is not limited to, before Start)
|
||||
/// </summary>
|
||||
public virtual void OnSpawnPlayerCompleted(IPlayerSession session, IEntity mob, bool lateJoin) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called when a player attempts to ghost.
|
||||
/// </summary>
|
||||
public virtual bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
|
||||
{
|
||||
var playerEntity = mind.OwnedEntity;
|
||||
|
||||
if (playerEntity != null && playerEntity.HasComponent<GhostComponent>())
|
||||
return false;
|
||||
|
||||
if (mind.VisitingEntity != null)
|
||||
{
|
||||
mind.UnVisit();
|
||||
}
|
||||
|
||||
var position = playerEntity?.Transform.Coordinates ?? IoCManager.Resolve<IGameTicker>().GetObserverSpawnPoint();
|
||||
var canReturn = false;
|
||||
|
||||
if (playerEntity != null && canReturnGlobal && playerEntity.TryGetComponent(out IMobStateComponent? mobState))
|
||||
{
|
||||
if (mobState.IsDead())
|
||||
{
|
||||
canReturn = true;
|
||||
}
|
||||
else if (mobState.IsCritical())
|
||||
{
|
||||
canReturn = true;
|
||||
|
||||
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
//todo: what if they dont breathe lol
|
||||
//cry deeply
|
||||
damageable.SetDamage(damageable.GetDamageType("Asphyxiation"), 200, playerEntity);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
canReturn = false;
|
||||
}
|
||||
}
|
||||
|
||||
var entityManager = IoCManager.Resolve<IEntityManager>();
|
||||
var ghost = entityManager.SpawnEntity("MobObserver", position);
|
||||
ghost.Name = mind.CharacterName ?? string.Empty;
|
||||
|
||||
var ghostComponent = ghost.GetComponent<GhostComponent>();
|
||||
ghostComponent.CanReturnToBody = canReturn;
|
||||
|
||||
if (canReturn)
|
||||
mind.Visit(ghost);
|
||||
else
|
||||
mind.TransferTo(ghost);
|
||||
return true;
|
||||
}
|
||||
|
||||
public virtual string GetRoundEndDescription() => "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.GameObjects.Components.GUI;
|
||||
using Content.Server.GameObjects.Components.Items.Storage;
|
||||
using Content.Server.GameObjects.Components.Markers;
|
||||
using Content.Server.GameObjects.Components.PDA;
|
||||
using Content.Server.GameObjects.Components.TraitorDeathMatch;
|
||||
using Content.Server.GameTicking.GameRules;
|
||||
using Content.Server.Interfaces.Chat;
|
||||
using Content.Server.Interfaces.GameTicking;
|
||||
using Content.Server.Mobs;
|
||||
using Content.Server.Mobs.Roles.Traitor;
|
||||
using Content.Server.Players;
|
||||
using Content.Shared;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.GameObjects.Components.Damage;
|
||||
using Content.Shared.GameObjects.Components.Inventory;
|
||||
using Content.Shared.GameObjects.Components.Mobs.State;
|
||||
using Content.Shared.GameObjects.Components.PDA;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.GameTicking.GamePresets
|
||||
{
|
||||
[GamePreset("traitordm", "traitordeathmatch")]
|
||||
public sealed class PresetTraitorDeathMatch : GamePreset
|
||||
{
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||
[Dependency] private readonly IGameTicker _gameTicker = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
public string PDAPrototypeName => "CaptainPDA";
|
||||
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
||||
public string BackpackPrototypeName => "ClothingBackpackFilled";
|
||||
|
||||
private RuleMaxTimeRestart _restarter = default!;
|
||||
private bool _safeToEndRound = false;
|
||||
|
||||
private Dictionary<UplinkAccount, string> _allOriginalNames = new();
|
||||
|
||||
public override bool Start(IReadOnlyList<IPlayerSession> readyPlayers, bool force = false)
|
||||
{
|
||||
_gameTicker.AddGameRule<RuleTraitorDeathMatch>();
|
||||
_restarter = _gameTicker.AddGameRule<RuleMaxTimeRestart>();
|
||||
_restarter.RoundMaxTime = TimeSpan.FromMinutes(30);
|
||||
_restarter.RestartTimer();
|
||||
_safeToEndRound = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnSpawnPlayerCompleted(IPlayerSession session, IEntity mob, bool lateJoin)
|
||||
{
|
||||
var startingBalance = _cfg.GetCVar(CCVars.TraitorDeathMatchStartingBalance);
|
||||
|
||||
// Yup, they're a traitor
|
||||
var mind = session.Data.ContentData()?.Mind;
|
||||
if (mind == null)
|
||||
{
|
||||
Logger.ErrorS("preset", "Failed getting mind for TDM player.");
|
||||
return;
|
||||
}
|
||||
|
||||
var traitorRole = new TraitorRole(mind);
|
||||
mind.AddRole(traitorRole);
|
||||
|
||||
// Delete anything that may contain "dangerous" role-specific items.
|
||||
// (This includes the PDA, as everybody gets the captain PDA in this mode for true-all-access reasons.)
|
||||
if (mind.OwnedEntity != null && mind.OwnedEntity.TryGetComponent(out InventoryComponent? inventory))
|
||||
{
|
||||
var victimSlots = new[] {EquipmentSlotDefines.Slots.IDCARD, EquipmentSlotDefines.Slots.BELT, EquipmentSlotDefines.Slots.BACKPACK};
|
||||
foreach (var slot in victimSlots)
|
||||
{
|
||||
if (inventory.TryGetSlotItem(slot, out ItemComponent? vItem))
|
||||
vItem.Owner.Delete();
|
||||
}
|
||||
|
||||
// Replace their items:
|
||||
|
||||
// pda
|
||||
var newPDA = _entityManager.SpawnEntity(PDAPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
||||
inventory.Equip(EquipmentSlotDefines.Slots.IDCARD, newPDA.GetComponent<ItemComponent>());
|
||||
|
||||
// belt
|
||||
var newTmp = _entityManager.SpawnEntity(BeltPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
||||
inventory.Equip(EquipmentSlotDefines.Slots.BELT, newTmp.GetComponent<ItemComponent>());
|
||||
|
||||
// backpack
|
||||
newTmp = _entityManager.SpawnEntity(BackpackPrototypeName, mind.OwnedEntity.Transform.Coordinates);
|
||||
inventory.Equip(EquipmentSlotDefines.Slots.BACKPACK, newTmp.GetComponent<ItemComponent>());
|
||||
|
||||
// Like normal traitors, they need access to a traitor account.
|
||||
var uplinkAccount = new UplinkAccount(mind.OwnedEntity.Uid, startingBalance);
|
||||
var pdaComponent = newPDA.GetComponent<PDAComponent>();
|
||||
pdaComponent.InitUplinkAccount(uplinkAccount);
|
||||
_allOriginalNames[uplinkAccount] = mind.OwnedEntity.Name;
|
||||
|
||||
// The PDA needs to be marked with the correct owner.
|
||||
pdaComponent.SetPDAOwner(mind.OwnedEntity.Name);
|
||||
newPDA.AddComponent<TraitorDeathMatchReliableOwnerTagComponent>().UserId = mind.UserId;
|
||||
}
|
||||
|
||||
// Finally, it would be preferrable if they spawned as far away from other players as reasonably possible.
|
||||
if (mind.OwnedEntity != null && FindAnyIsolatedSpawnLocation(mind, out var bestTarget))
|
||||
{
|
||||
mind.OwnedEntity.Transform.Coordinates = bestTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The station is too drained of air to safely continue.
|
||||
if (_safeToEndRound)
|
||||
{
|
||||
_chatManager.DispatchServerAnnouncement(Loc.GetString("The station is too unsafe to continue. You have one minute."));
|
||||
_restarter.RoundMaxTime = TimeSpan.FromMinutes(1);
|
||||
_restarter.RestartTimer();
|
||||
_safeToEndRound = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// It would be nice if this function were moved to some generic helpers class.
|
||||
private bool FindAnyIsolatedSpawnLocation(Mind ignoreMe, out EntityCoordinates bestTarget)
|
||||
{
|
||||
// Collate people to avoid...
|
||||
var existingPlayerPoints = new List<EntityCoordinates>();
|
||||
foreach (var player in _playerManager.GetAllPlayers())
|
||||
{
|
||||
var avoidMeMind = player.Data.ContentData()?.Mind;
|
||||
if ((avoidMeMind == null) || (avoidMeMind == ignoreMe))
|
||||
continue;
|
||||
var avoidMeEntity = avoidMeMind.OwnedEntity;
|
||||
if (avoidMeEntity == null)
|
||||
continue;
|
||||
if (avoidMeEntity.TryGetComponent(out IMobStateComponent? mobState))
|
||||
{
|
||||
// Does have mob state component; if critical or dead, they don't really matter for spawn checks
|
||||
if (mobState.IsCritical() || mobState.IsDead())
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Doesn't have mob state component. Assume something interesting is going on and don't count this as someone to avoid.
|
||||
continue;
|
||||
}
|
||||
existingPlayerPoints.Add(avoidMeEntity.Transform.Coordinates);
|
||||
}
|
||||
|
||||
// Iterate over each possible spawn point, comparing to the existing player points.
|
||||
// On failure, the returned target is the location that we're already at.
|
||||
var bestTargetDistanceFromNearest = -1.0f;
|
||||
// Need the random shuffle or it stuffs the first person into Atmospherics pretty reliably
|
||||
var ents = new List<IEntity>(_entityManager.GetEntities(new TypeEntityQuery(typeof(SpawnPointComponent))));
|
||||
_robustRandom.Shuffle(ents);
|
||||
var foundATarget = false;
|
||||
bestTarget = EntityCoordinates.Invalid;
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
if (!entity.Transform.Coordinates.IsTileAirProbablySafe())
|
||||
continue;
|
||||
var distanceFromNearest = float.PositiveInfinity;
|
||||
foreach (var existing in existingPlayerPoints)
|
||||
{
|
||||
if (entity.Transform.Coordinates.TryDistance(_entityManager, existing, out var dist))
|
||||
distanceFromNearest = Math.Min(distanceFromNearest, dist);
|
||||
}
|
||||
if (bestTargetDistanceFromNearest < distanceFromNearest)
|
||||
{
|
||||
bestTarget = entity.Transform.Coordinates;
|
||||
bestTargetDistanceFromNearest = distanceFromNearest;
|
||||
foundATarget = true;
|
||||
}
|
||||
}
|
||||
return foundATarget;
|
||||
}
|
||||
|
||||
public override bool OnGhostAttempt(Mind mind, bool canReturnGlobal)
|
||||
{
|
||||
var entity = mind.OwnedEntity;
|
||||
if ((entity != null) && (entity.TryGetComponent(out IMobStateComponent? mobState)))
|
||||
{
|
||||
if (mobState.IsCritical())
|
||||
{
|
||||
// TODO: This is copy/pasted from ghost code. Really, IDamagableComponent needs a method to reliably kill the target.
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
//todo: what if they dont breathe lol
|
||||
damageable.ChangeDamage(damageable.GetDamageType("Asphyxiation"), 100, true);
|
||||
}
|
||||
}
|
||||
else if (!mobState.IsDead())
|
||||
{
|
||||
if (entity.HasComponent<HandsComponent>())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
var session = mind.Session;
|
||||
if (session == null)
|
||||
return false;
|
||||
_gameTicker.Respawn(session);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override string GetRoundEndDescription()
|
||||
{
|
||||
var lines = new List<string>();
|
||||
lines.Add("The PDAs recovered afterwards...");
|
||||
foreach (var entity in _entityManager.GetEntities(new TypeEntityQuery(typeof(PDAComponent))))
|
||||
{
|
||||
var pda = entity.GetComponent<PDAComponent>();
|
||||
var uplink = pda.SyndicateUplinkAccount;
|
||||
if ((uplink != null) && _allOriginalNames.ContainsKey(uplink))
|
||||
{
|
||||
lines.Add(Loc.GetString("{0}'s PDA, with {1} TC", _allOriginalNames[uplink], uplink.Balance));
|
||||
}
|
||||
}
|
||||
return string.Join('\n', lines);
|
||||
}
|
||||
|
||||
public override string ModeTitle => "Traitor Deathmatch";
|
||||
public override string Description => Loc.GetString("Everyone's a traitor. Everyone wants each other dead.");
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ using Robust.Server.Player;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Network;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.GameTicking.Presets
|
||||
{
|
||||
@@ -66,7 +67,17 @@ namespace Content.Server.GameTicking.Presets
|
||||
if (playerEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
//todo: what if they dont breathe lol
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.SetDamage(DamageType.Asphyxiation, 200, playerEntity);
|
||||
=======
|
||||
//cry deeply
|
||||
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
//cry deeply
|
||||
damageable.TrySetDamage(IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>("Asphyxiation"), 200);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
@@ -38,6 +39,7 @@ namespace Content.Server.GameTicking.Presets
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IChatManager _chatManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public string PDAPrototypeName => "CaptainPDA";
|
||||
public string BeltPrototypeName => "ClothingBeltJanitorFilled";
|
||||
@@ -192,11 +194,19 @@ namespace Content.Server.GameTicking.Presets
|
||||
{
|
||||
if (mobState.IsCritical())
|
||||
{
|
||||
// TODO: This is copy/pasted from ghost code. Really, IDamagableComponent needs a method to reliably kill the target.
|
||||
// TODO: This is copy/pasted from ghost code. Really, IDamageableComponent needs a method to reliably kill the target.
|
||||
if (entity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
{
|
||||
//todo: what if they dont breathe lol
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageType.Asphyxiation, 100, true);
|
||||
=======
|
||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>("Asphyxiation"), 100, true);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
else if (!mobState.IsDead())
|
||||
|
||||
@@ -20,6 +20,7 @@ using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
@@ -58,8 +59,22 @@ namespace Content.Server.Light.Components
|
||||
[DataField("hasLampOnSpawn")]
|
||||
private bool _hasLampOnSpawn = true;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[ViewVariables]
|
||||
[DataField("on")]
|
||||
=======
|
||||
[ViewVariables] [DataField("on")]
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
[ViewVariables]
|
||||
[DataField("on")]
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
[ViewVariables]
|
||||
[DataField("on")]
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
private bool _on = true;
|
||||
|
||||
[ViewVariables]
|
||||
@@ -77,6 +92,19 @@ namespace Content.Server.Light.Components
|
||||
|
||||
[ViewVariables] private ContainerSlot _lightBulbContainer = default!;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Heat";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public LightBulbComponent? LightBulb
|
||||
{
|
||||
@@ -126,8 +154,22 @@ namespace Content.Server.Light.Components
|
||||
void Burn()
|
||||
{
|
||||
Owner.PopupMessage(eventArgs.User, Loc.GetString("powered-light-component-burn-hand"));
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageableComponent.ChangeDamage(DamageType.Heat, 20, false, Owner);
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
||||
=======
|
||||
damageableComponent.TryChangeDamage(DamageType, 20);
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Effects/lightburn.ogg", Owner);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
damageableComponent.TryChangeDamage(DamageType, 20);
|
||||
SoundSystem.Play(Filter.Pvs(Owner), _burnHandSound.GetSound(), Owner);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
void Eject()
|
||||
@@ -249,13 +291,6 @@ namespace Content.Server.Light.Components
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
_lightBulbContainer = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, "light_bulb");
|
||||
}
|
||||
|
||||
public override void HandleMessage(ComponentMessage message, IComponent? component)
|
||||
{
|
||||
base.HandleMessage(message, component);
|
||||
|
||||
@@ -20,7 +20,13 @@ namespace Content.Server.Storage.Components
|
||||
public override string Name => "Lock";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("locked")] public bool Locked { get; set; } = true;
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockOnClick")] public bool LockOnClick { get; set; } = false;
|
||||
=======
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("unlockingSound")] public SoundSpecifier UnlockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("lockingSound")] public SoundSpecifier LockSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/door_lock_off.ogg");
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Damage.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Events;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Content.Shared.Stacks;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Medical.Components
|
||||
{
|
||||
@@ -18,7 +20,27 @@ namespace Content.Server.Medical.Components
|
||||
{
|
||||
public override string Name => "Healing";
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("heal")] public Dictionary<DamageType, int> Heal { get; private set; } = new();
|
||||
=======
|
||||
[DataField("heal", required: true )]
|
||||
public Dictionary<string, int> Heal { get; private set; } = new();
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// This also requires changing the dictionary type, and removing a _prototypeManager.Index() call.
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[DataField("heal", required: true )]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Dictionary<string, int> Heal = new();
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
@@ -48,9 +70,9 @@ namespace Content.Server.Medical.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach (var (type, amount) in Heal)
|
||||
foreach (var (damageTypeID, amount) in Heal)
|
||||
{
|
||||
damageable.ChangeDamage(type, -amount, true);
|
||||
damageable.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), -amount, true);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -99,8 +99,18 @@ namespace Content.Server.Medical.Components
|
||||
private static readonly MedicalScannerBoundUserInterfaceState EmptyUIState =
|
||||
new(
|
||||
null,
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
new Dictionary<DamageClass, int>(),
|
||||
new Dictionary<DamageType, int>(),
|
||||
=======
|
||||
new Dictionary<string, int>(),
|
||||
new Dictionary<string, int>(),
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
new Dictionary<string, int>(),
|
||||
new Dictionary<string, int>(),
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
false);
|
||||
|
||||
private MedicalScannerBoundUserInterfaceState GetUserInterfaceState()
|
||||
@@ -121,12 +131,24 @@ namespace Content.Server.Medical.Components
|
||||
return EmptyUIState;
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
var classes = new Dictionary<DamageClass, int>(damageable.DamageClasses);
|
||||
var types = new Dictionary<DamageType, int>(damageable.DamageTypes);
|
||||
=======
|
||||
// Get dictionaries of damage, by fully supported damage groups and types
|
||||
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
|
||||
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
// Get dictionaries of damage, by fully supported damage groups and types
|
||||
var groups = new Dictionary<string, int>(damageable.GetDamagePerFullySupportedGroupIDs);
|
||||
var types = new Dictionary<string, int>(damageable.GetDamagePerTypeIDs);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (_bodyContainer.ContainedEntity?.Uid == null)
|
||||
{
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, true);
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, true);
|
||||
}
|
||||
|
||||
var cloningSystem = EntitySystem.Get<CloningSystem>();
|
||||
@@ -134,7 +156,7 @@ namespace Content.Server.Medical.Components
|
||||
mindComponent.Mind != null &&
|
||||
cloningSystem.HasDnaScan(mindComponent.Mind);
|
||||
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, classes, types, scanned);
|
||||
return new MedicalScannerBoundUserInterfaceState(body.Uid, groups, types, scanned);
|
||||
}
|
||||
|
||||
private void UpdateUserInterface()
|
||||
|
||||
@@ -7,9 +7,20 @@ using Content.Shared.Mining;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
namespace Content.Server.Mining.Components
|
||||
{
|
||||
@@ -21,10 +32,25 @@ namespace Content.Server.Mining.Components
|
||||
public override string Name => "AsteroidRock";
|
||||
private static readonly string[] SpriteStates = {"0", "1", "2", "3", "4"};
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt"!;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(AsteroidRockVisuals.State, _random.Pick(SpriteStates));
|
||||
@@ -37,7 +63,15 @@ namespace Content.Server.Mining.Components
|
||||
if (!item.TryGetComponent(out MeleeWeaponComponent? meleeWeaponComponent))
|
||||
return false;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
Owner.GetComponent<IDamageableComponent>().ChangeDamage(DamageType.Blunt, meleeWeaponComponent.Damage, false, item);
|
||||
=======
|
||||
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
Owner.GetComponent<IDamageableComponent>().TryChangeDamage(DamageType, meleeWeaponComponent.Damage);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (!item.TryGetComponent(out PickaxeComponent? pickaxeComponent))
|
||||
return true;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Alert;
|
||||
using Content.Shared.Alert;
|
||||
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
@@ -22,6 +23,19 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
||||
// See also _accumulatedDamage in ThirstComponent and HealthChange.
|
||||
private float _accumulatedDamage;
|
||||
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// Base stuff
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseDecayRate
|
||||
@@ -29,7 +43,15 @@ namespace Content.Server.Nutrition.Components
|
||||
get => _baseDecayRate;
|
||||
set => _baseDecayRate = value;
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("base_decay_rate")]
|
||||
=======
|
||||
[DataField("baseDecayRate")]
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
[DataField("baseDecayRate")]
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
private float _baseDecayRate = 0.1f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -59,11 +81,25 @@ namespace Content.Server.Nutrition.Components
|
||||
public Dictionary<HungerThreshold, float> HungerThresholds => _hungerThresholds;
|
||||
private readonly Dictionary<HungerThreshold, float> _hungerThresholds = new()
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
{HungerThreshold.Overfed, 600.0f},
|
||||
{HungerThreshold.Okay, 450.0f},
|
||||
{HungerThreshold.Peckish, 300.0f},
|
||||
{HungerThreshold.Starving, 150.0f},
|
||||
{HungerThreshold.Dead, 0.0f},
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
{ HungerThreshold.Overfed, 600.0f },
|
||||
{ HungerThreshold.Okay, 450.0f },
|
||||
{ HungerThreshold.Peckish, 300.0f },
|
||||
{ HungerThreshold.Starving, 150.0f },
|
||||
{ HungerThreshold.Dead, 0.0f },
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
};
|
||||
|
||||
public static readonly Dictionary<HungerThreshold, AlertType> HungerThresholdAlertTypes = new()
|
||||
@@ -73,6 +109,18 @@ namespace Content.Server.Nutrition.Components
|
||||
{ HungerThreshold.Starving, AlertType.Starving },
|
||||
};
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt"!;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
public void HungerThresholdEffect(bool force = false)
|
||||
{
|
||||
if (_currentHungerThreshold != _lastHungerThreshold || force)
|
||||
@@ -177,6 +225,7 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
if (_currentHungerThreshold != HungerThreshold.Dead)
|
||||
return;
|
||||
// --> Current Hunger is below dead threshold
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
@@ -186,7 +235,24 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
if (!mobState.IsDead())
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// --> But they are not dead yet.
|
||||
var damage = 2 * frametime;
|
||||
_accumulatedDamage += damage - ((int) damage);
|
||||
damageable.TryChangeDamage(DamageType, (int) damage);
|
||||
if (_accumulatedDamage >= 1) {
|
||||
_accumulatedDamage -= 1;
|
||||
damageable.TryChangeDamage(DamageType, 1, true);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Alert;
|
||||
using Content.Shared.Alert;
|
||||
@@ -14,6 +14,7 @@ using Robust.Shared.Players;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
@@ -22,6 +23,19 @@ namespace Content.Server.Nutrition.Components
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO DAMAGE UNITS When damage units support decimals, get rid of this.
|
||||
// See also _accumulatedDamage in HungerComponent and HealthChange.
|
||||
private float _accumulatedDamage;
|
||||
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// Base stuff
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float BaseDecayRate
|
||||
@@ -29,7 +43,7 @@ namespace Content.Server.Nutrition.Components
|
||||
get => _baseDecayRate;
|
||||
set => _baseDecayRate = value;
|
||||
}
|
||||
[DataField("base_decay_rate")]
|
||||
[DataField("baseDecayRate")]
|
||||
private float _baseDecayRate = 0.1f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
@@ -72,6 +86,18 @@ namespace Content.Server.Nutrition.Components
|
||||
{ThirstThreshold.Parched, AlertType.Parched},
|
||||
};
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
|
||||
public void ThirstThresholdEffect(bool force = false)
|
||||
{
|
||||
if (_currentThirstThreshold != _lastThirstThreshold || force)
|
||||
@@ -174,6 +200,7 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
if (_currentThirstThreshold != ThirstThreshold.Dead)
|
||||
return;
|
||||
// --> Current Hunger is below dead threshold
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
@@ -183,7 +210,25 @@ namespace Content.Server.Nutrition.Components
|
||||
|
||||
if (!mobState.IsDead())
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
damageable.ChangeDamage(DamageType.Blunt, 2, true);
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// --> But they are not dead yet.
|
||||
var damage = 2 * frametime;
|
||||
_accumulatedDamage += damage - ((int) damage);
|
||||
damageable.TryChangeDamage(DamageType, (int) damage);
|
||||
if (_accumulatedDamage >= 1)
|
||||
{
|
||||
_accumulatedDamage -= 1;
|
||||
damageable.TryChangeDamage(DamageType, 1, true);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
{
|
||||
comp.OnUpdate(_accumulatedFrameTime);
|
||||
}
|
||||
_accumulatedFrameTime -= 1;
|
||||
_accumulatedFrameTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +354,19 @@ namespace Content.Server.Physics
|
||||
// Box2D has this as 800 which is jesus christo.
|
||||
// Wouldn't recommend higher than 100 in debug and higher than 300 on release unless
|
||||
// you really want a profile.
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
var count = 200;
|
||||
=======
|
||||
var count = 50;
|
||||
|
||||
EntitySystem.Get<SharedPhysicsSystem>().Maps[mapId].Gravity = new Vector2(0f, -9.8f);
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
var count = 50;
|
||||
|
||||
EntitySystem.Get<SharedPhysicsSystem>().Maps[mapId].Gravity = new Vector2(0f, -9.8f);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
var mapManager = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
|
||||
@@ -9,8 +9,10 @@ using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Projectiles.Components
|
||||
{
|
||||
@@ -25,33 +27,96 @@ namespace Content.Server.Projectiles.Components
|
||||
public override string Name => "Hitscan";
|
||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
public override string Name => "Hitscan";
|
||||
public CollisionGroup CollisionMask => (CollisionGroup) _collisionMask;
|
||||
=======
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
[DataField("layers")] //todo WithFormat.Flags<CollisionLayer>()
|
||||
private int _collisionMask = (int) CollisionGroup.Opaque;
|
||||
|
||||
public float Damage
|
||||
{
|
||||
get => _damage;
|
||||
set => _damage = value;
|
||||
}
|
||||
[DataField("damage")]
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
private float _damage = 10f;
|
||||
public DamageType DamageType => _damageType;
|
||||
[DataField("damageType")]
|
||||
private DamageType _damageType = DamageType.Heat;
|
||||
public float MaxLength => 20.0f;
|
||||
=======
|
||||
public float Damage { get; set; } = 10f;
|
||||
public float MaxLength => 20.0f;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
private TimeSpan _startTime;
|
||||
private TimeSpan _deathTime;
|
||||
=======
|
||||
[DataField("damageType", required: true)]
|
||||
private string _damageTypeID = default!;
|
||||
|
||||
private DamageTypePrototype _damageType => _prototypeManager.Index<DamageTypePrototype>(_damageTypeID);
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
public float Damage { get; set; } = 10f;
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
|
||||
public float ColorModifier { get; set; } = 1.0f;
|
||||
<<<<<<< HEAD
|
||||
[DataField("spriteName")]
|
||||
=======
|
||||
public float MaxLength => 20.0f;
|
||||
|
||||
private TimeSpan _startTime;
|
||||
private TimeSpan _deathTime;
|
||||
|
||||
public float ColorModifier { get; set; } = 1.0f;
|
||||
[DataField("spriteName")]
|
||||
[DataField("spriteName")]
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
[DataField("spriteName")]
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
||||
[DataField("muzzleFlash")]
|
||||
private string? _muzzleFlash;
|
||||
[DataField("impactFlash")]
|
||||
private string? _impactFlash;
|
||||
[DataField("soundHitWall")]
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
||||
=======
|
||||
private string _soundHitWall = "/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg";
|
||||
[DataField("spriteName")]
|
||||
private string _spriteName = "Objects/Weapons/Guns/Projectiles/laser.png";
|
||||
=======
|
||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
private SoundSpecifier _soundHitWall = new SoundPathSpecifier("/Audio/Weapons/Guns/Hits/laser_sear_wall.ogg");
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Piercing";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
public void FireEffects(IEntity user, float distance, Angle angle, IEntity? hitEntity = null)
|
||||
{
|
||||
|
||||
@@ -13,6 +13,8 @@ namespace Content.Server.Projectiles.Components
|
||||
[ComponentReference(typeof(SharedProjectileComponent))]
|
||||
public class ProjectileComponent : SharedProjectileComponent
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("damages")] private Dictionary<DamageType, int> _damages = new();
|
||||
|
||||
[ViewVariables]
|
||||
@@ -21,12 +23,33 @@ namespace Content.Server.Projectiles.Components
|
||||
get => _damages;
|
||||
set => _damages = value;
|
||||
}
|
||||
=======
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// This also requires changing the dictionary type and modifying ProjectileSystem.cs, which uses it.
|
||||
// While thats being done, also replace "damages" -> "damageTypes" For consistency.
|
||||
[DataField("damages")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Dictionary<string, int> Damages { get; set; } = new();
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
[DataField("deleteOnCollide")]
|
||||
public bool DeleteOnCollide { get; } = true;
|
||||
|
||||
// Get that juicy FPS hit sound
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
[DataField("soundHit", required: true)] public SoundSpecifier? SoundHit = default!;
|
||||
=======
|
||||
[DataField("soundHit", required: true)] public SoundSpecifier SoundHit = default!;
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
[DataField("soundHit", required: true)] public SoundSpecifier SoundHit = default!;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("soundHitSpecies")] public SoundSpecifier? SoundHitSpecies = null;
|
||||
|
||||
public bool DamagedEntity;
|
||||
|
||||
@@ -7,12 +7,17 @@ using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.IoC;
|
||||
using Content.Shared.Damage;
|
||||
|
||||
namespace Content.Server.Projectiles
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ProjectileSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -39,19 +44,27 @@ namespace Content.Server.Projectiles
|
||||
}
|
||||
else
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
var soundHit = component.SoundHit?.GetSound();
|
||||
|
||||
if (!string.IsNullOrEmpty(soundHit))
|
||||
SoundSystem.Play(playerFilter, soundHit, coordinates);
|
||||
=======
|
||||
SoundSystem.Play(playerFilter, component.SoundHit.GetSound(), coordinates);
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
SoundSystem.Play(playerFilter, component.SoundHit.GetSound(), coordinates);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
if (!otherEntity.Deleted && otherEntity.TryGetComponent(out IDamageableComponent? damage))
|
||||
{
|
||||
EntityManager.TryGetEntity(component.Shooter, out var shooter);
|
||||
|
||||
foreach (var (damageType, amount) in component.Damages)
|
||||
foreach (var (damageTypeID, amount) in component.Damages)
|
||||
{
|
||||
damage.ChangeDamage(damageType, amount, false, shooter);
|
||||
damage.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damageTypeID), amount);
|
||||
}
|
||||
|
||||
component.DamagedEntity = true;
|
||||
|
||||
@@ -13,8 +13,35 @@ namespace Content.Server.Radiation
|
||||
{
|
||||
[Dependency] private readonly IEntityLookup _lookup = default!;
|
||||
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
private const float RadiationCooldown = 0.5f;
|
||||
private float _accumulator;
|
||||
=======
|
||||
public IEntity RadiationPulse(
|
||||
EntityCoordinates coordinates,
|
||||
float range,
|
||||
int dps,
|
||||
bool decay = true,
|
||||
float minPulseLifespan = 0.8f,
|
||||
float maxPulseLifespan = 2.5f,
|
||||
SoundSpecifier sound = default!)
|
||||
{
|
||||
var radiationEntity = EntityManager.SpawnEntity(RadiationPrototype, coordinates);
|
||||
var radiation = radiationEntity.GetComponent<RadiationPulseComponent>();
|
||||
|
||||
radiation.Range = range;
|
||||
radiation.RadsPerSecond = dps;
|
||||
radiation.Draw = false;
|
||||
radiation.Decay = decay;
|
||||
radiation.MinPulseLifespan = minPulseLifespan;
|
||||
radiation.MaxPulseLifespan = maxPulseLifespan;
|
||||
radiation.Sound = sound;
|
||||
|
||||
radiation.DoPulse();
|
||||
|
||||
return radiationEntity;
|
||||
}
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Content.Server.Repairable
|
||||
{
|
||||
if (!await welder.UseTool(eventArgs.User, Owner, _doAfterDelay, ToolQuality.Welding, _fuelCost))
|
||||
return false;
|
||||
damageable.Heal();
|
||||
damageable.TrySetAllDamage(0);
|
||||
|
||||
Owner.PopupMessage(eventArgs.User,
|
||||
Loc.GetString("comp-repairable-repair",
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace Content.Server.Spawners.Components
|
||||
[DataField("job_id")]
|
||||
private string? _jobId;
|
||||
|
||||
[field: ViewVariables(VVAccess.ReadWrite)]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("spawn_type")]
|
||||
public SpawnPointType SpawnType { get; } = SpawnPointType.Unset;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using Content.Server.Alert;
|
||||
using Content.Shared.Alert;
|
||||
using Content.Shared.Atmos;
|
||||
@@ -8,6 +8,8 @@ using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Temperature.Components
|
||||
{
|
||||
@@ -22,11 +24,38 @@ namespace Content.Server.Temperature.Components
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Temperature";
|
||||
|
||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
[DataField("coldDamageType",required: true)]
|
||||
private readonly string coldDamageType = default!;
|
||||
[DataField("hotDamageType",required: true)]
|
||||
private readonly string hotDamageType = default!;
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("heatDamageThreshold")]
|
||||
private float _heatDamageThreshold = default;
|
||||
[DataField("coldDamageThreshold")]
|
||||
private float _coldDamageThreshold = default;
|
||||
[DataField("tempDamageCoefficient")]
|
||||
private float _tempDamageCoefficient = 1;
|
||||
[DataField("currentTemperature")]
|
||||
public float CurrentTemperature { get; set; } = Atmospherics.T20C;
|
||||
[DataField("specificHeat")]
|
||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
||||
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
[ViewVariables] public float CurrentTemperature { get => _currentTemperature; set => _currentTemperature = value; }
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
[ViewVariables] public float HeatDamageThreshold => _heatDamageThreshold;
|
||||
[ViewVariables] public float ColdDamageThreshold => _coldDamageThreshold;
|
||||
[ViewVariables] public float TempDamageCoefficient => _tempDamageCoefficient;
|
||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
||||
[ViewVariables] public float HeatCapacity {
|
||||
get
|
||||
{
|
||||
@@ -39,22 +68,29 @@ namespace Content.Server.Temperature.Components
|
||||
}
|
||||
}
|
||||
|
||||
[ViewVariables] public float SpecificHeat => _specificHeat;
|
||||
|
||||
[DataField("heatDamageThreshold")]
|
||||
private float _heatDamageThreshold = default;
|
||||
[DataField("coldDamageThreshold")]
|
||||
private float _coldDamageThreshold = default;
|
||||
[DataField("tempDamageCoefficient")]
|
||||
private float _tempDamageCoefficient = 1;
|
||||
[DataField("currentTemperature")]
|
||||
private float _currentTemperature = Atmospherics.T20C;
|
||||
[DataField("specificHeat")]
|
||||
private float _specificHeat = Atmospherics.MinimumHeatCapacity;
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("coldDamageType")]
|
||||
private readonly string _coldDamageTypeID = "Cold";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype ColdDamageType = default!;
|
||||
[DataField("hotDamageType")]
|
||||
private readonly string _hotDamageTypeID = "Heat";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype HotDamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
ColdDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_coldDamageTypeID);
|
||||
HotDamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_hotDamageTypeID);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
var tempDamage = 0;
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
DamageType? damageType = null;
|
||||
if (CurrentTemperature >= _heatDamageThreshold)
|
||||
{
|
||||
@@ -66,6 +102,13 @@ namespace Content.Server.Temperature.Components
|
||||
tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
||||
damageType = DamageType.Cold;
|
||||
}
|
||||
=======
|
||||
DamageTypePrototype? damageType = null;
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (Owner.TryGetComponent(out ServerAlertsComponent? status))
|
||||
{
|
||||
@@ -108,10 +151,40 @@ namespace Content.Server.Temperature.Components
|
||||
}
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
if (!damageType.HasValue) return;
|
||||
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||
component.ChangeDamage(damageType.Value, tempDamage, false);
|
||||
=======
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||
=======
|
||||
if (!Owner.TryGetComponent(out IDamageableComponent? component)) return;
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
if (CurrentTemperature >= _heatDamageThreshold)
|
||||
{
|
||||
int tempDamage = (int) Math.Floor((CurrentTemperature - _heatDamageThreshold) * _tempDamageCoefficient);
|
||||
component.TryChangeDamage(HotDamageType, tempDamage, false);
|
||||
}
|
||||
else if (CurrentTemperature <= _coldDamageThreshold)
|
||||
{
|
||||
int tempDamage = (int) Math.Floor((_coldDamageThreshold - CurrentTemperature) * _tempDamageCoefficient);
|
||||
component.TryChangeDamage(ColdDamageType, tempDamage, false);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
|
||||
if (damageType is null) return;
|
||||
component.ChangeDamage(damageType, tempDamage, false);
|
||||
>>>>>>> update damagecomponent across shared and server
|
||||
=======
|
||||
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -4,6 +4,8 @@ using Content.Shared.Sound;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Server.Weapon.Melee.Components
|
||||
{
|
||||
@@ -48,15 +50,23 @@ namespace Content.Server.Weapon.Melee.Components
|
||||
[DataField("damage")]
|
||||
public int Damage { get; set; } = 5;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("damageType")]
|
||||
public DamageType DamageType { get; set; } = DamageType.Blunt;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("clickAttackEffect")]
|
||||
public bool ClickAttackEffect { get; set; } = true;
|
||||
|
||||
public TimeSpan LastAttackTime;
|
||||
public TimeSpan CooldownEnd;
|
||||
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// Also remove Initialize override, if no longer needed.
|
||||
[DataField("damageType")]
|
||||
private readonly string _damageTypeID = "Blunt";
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public DamageTypePrototype DamageType = default!;
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
DamageType = IoCManager.Resolve<IPrototypeManager>().Index<DamageTypePrototype>(_damageTypeID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ namespace Content.Server.Weapon.Melee
|
||||
public sealed class MeleeWeaponSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -88,7 +90,7 @@ namespace Content.Server.Weapon.Melee
|
||||
|
||||
if (target.TryGetComponent(out IDamageableComponent? damageableComponent))
|
||||
{
|
||||
damageableComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
||||
damageableComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
||||
}
|
||||
|
||||
SoundSystem.Play(Filter.Pvs(owner), comp.HitSound.GetSound(), target);
|
||||
@@ -157,7 +159,7 @@ namespace Content.Server.Weapon.Melee
|
||||
{
|
||||
if (entity.TryGetComponent<IDamageableComponent>(out var damageComponent))
|
||||
{
|
||||
damageComponent.ChangeDamage(comp.DamageType, comp.Damage, false, owner);
|
||||
damageComponent.TryChangeDamage(comp.DamageType, comp.Damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +188,15 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
||||
{
|
||||
if (energyRatio < 1.0)
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
var newDamages = new Dictionary<DamageType, int>(projectileComponent.Damages.Count);
|
||||
=======
|
||||
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
var newDamages = new Dictionary<string, int>(projectileComponent.Damages.Count);
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
foreach (var (damageType, damage) in projectileComponent.Damages)
|
||||
{
|
||||
newDamages.Add(damageType, (int) (damage * energyRatio));
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace Content.Server.Weapon.Ranged.Barrels.Components
|
||||
if (!result.HitEntity.TryGetComponent(out IDamageableComponent? damageable))
|
||||
return;
|
||||
|
||||
damageable.ChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero), false, Owner);
|
||||
damageable.TryChangeDamage(hitscan.DamageType, (int)Math.Round(hitscan.Damage, MidpointRounding.AwayFromZero));
|
||||
//I used Math.Round over Convert.toInt32, as toInt32 always rounds to
|
||||
//even numbers if halfway between two numbers, rather than rounding to nearest
|
||||
}
|
||||
|
||||
@@ -27,6 +27,8 @@ using Robust.Shared.Players;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.Weapon.Ranged
|
||||
{
|
||||
@@ -50,11 +52,41 @@ namespace Content.Server.Weapon.Ranged
|
||||
[DataField("canHotspot")]
|
||||
private bool _canHotspot = true;
|
||||
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
[DataField("clumsyWeaponHandlingSound")]
|
||||
private SoundSpecifier _clumsyWeaponHandlingSound = new SoundPathSpecifier("/Audio/Items/bikehorn.ogg");
|
||||
|
||||
[DataField("clumsyWeaponShotSound")]
|
||||
private SoundSpecifier _clumsyWeaponShotSound = new SoundPathSpecifier("/Audio/Weapons/Guns/Gunshots/bang.ogg");
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
=======
|
||||
=======
|
||||
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
=======
|
||||
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
// TODO PROTOTYPE Replace this datafield variable with prototype references, once they are supported.
|
||||
// This also requires changing the dictionary type and modifying TryFire(), which uses it.
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("clumsyDamage")]
|
||||
public Dictionary<string, int> ClumsyDamage { get; set; } = new()
|
||||
{
|
||||
{ "Blunt", 10 },
|
||||
{ "Heat", 5 }
|
||||
};
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
|
||||
public Func<bool>? WeaponCanFireHandler;
|
||||
public Func<IEntity, bool>? UserCanFireHandler;
|
||||
@@ -168,6 +200,9 @@ namespace Content.Server.Weapon.Ranged
|
||||
|
||||
if (ClumsyCheck && ClumsyComponent.TryRollClumsy(user, ClumsyExplodeChance))
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
SoundSystem.Play(
|
||||
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
@@ -175,18 +210,48 @@ namespace Content.Server.Weapon.Ranged
|
||||
SoundSystem.Play(
|
||||
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
=======
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
|
||||
=======
|
||||
>>>>>>> Bring refactor-damageablecomponent branch up-to-date with master (#4510)
|
||||
//Wound them
|
||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
||||
{
|
||||
<<<<<<< refs/remotes/origin/master
|
||||
health.ChangeDamage(DamageType.Blunt, 10, false, user);
|
||||
health.ChangeDamage(DamageType.Heat, 5, false, user);
|
||||
=======
|
||||
=======
|
||||
//Wound them
|
||||
if (user.TryGetComponent(out IDamageableComponent? health))
|
||||
{
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
foreach (KeyValuePair<string, int> damage in ClumsyDamage)
|
||||
{
|
||||
health.TryChangeDamage(_prototypeManager.Index<DamageTypePrototype>(damage.Key), damage.Value);
|
||||
}
|
||||
<<<<<<< HEAD
|
||||
>>>>>>> Refactor damageablecomponent update (#4406)
|
||||
=======
|
||||
>>>>>>> refactor-damageablecomponent
|
||||
}
|
||||
|
||||
// Knock them down
|
||||
if (user.TryGetComponent(out StunnableComponent? stun))
|
||||
{
|
||||
stun.Paralyze(3f);
|
||||
}
|
||||
|
||||
// Apply salt to the wound ("Honk!")
|
||||
SoundSystem.Play(
|
||||
Filter.Pvs(Owner), _clumsyWeaponHandlingSound.GetSound(),
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
|
||||
SoundSystem.Play(
|
||||
Filter.Pvs(Owner), _clumsyWeaponShotSound.GetSound(),
|
||||
Owner.Transform.Coordinates, AudioParams.Default.WithMaxDistance(5));
|
||||
|
||||
user.PopupMessage(Loc.GetString("server-ranged-weapon-component-try-fire-clumsy"));
|
||||
|
||||
Owner.Delete();
|
||||
|
||||
Reference in New Issue
Block a user