From ba5c112a86bb2845df830df81d59cd54ca8ac99f Mon Sep 17 00:00:00 2001 From: SpaceLoveSs13 <68121607+SpaceLoveSs13@users.noreply.github.com> Date: Mon, 29 Apr 2024 01:54:01 +0530 Subject: [PATCH] Huge Mirror fixes (#27488) * Fixes incorrect operator usage in mecha code (#82570) ## About The Pull Request I completely screwed up and told the original PR author of #82415 (9922d2f2377c3ab571d34c857174c45f5521a5ae) to use the `XOR` operator instead of the `OR` operator (I wasn't thinking right for some reason when I was reading the ref), anyways this PR just fixes that because I misled the contributor into doing something that wasn't correct and actually would BREAK functionality instead. * Fixes TGUI debugging tools (#82569) This project doesn't interfere with the game logic and aims to fix multiple debugging features that are currently broken. Unfortunately, kitchen sink and debug layout became broken after migration to Redux. This PR aims to fix those features. * Removes unused code for HTML UIs (#82589) ## About The Pull Request This is the final PR for https://hackmd.io/XLt5MoRvRxuhFbwtk4VAUA that I've been slowly inching towards the past few months. This removes ``updateDialog``, ``updateUsrDialog``, ``IN_USE``, ``INTERACT_MACHINE_SET_MACHINE``, and everything surrounding it. Also fixes advanced camera consoles not booting you off when you're moved out of reach. We called ``check_eye`` on mob life whenever they had their machine var set, but their machine var would never be set to anything that actually used it, which I found to be a little funny but was also probably my fault. ## Why It's Good For The Game This is poor and unmaintained code used for HTML UIs that we no longer need thanks to TGUI, we should get rid of it to encourage the use of TGUI in the future instead. ## Changelog :cl: fix: Advanced camera consoles now boots you off when you're moved out of reach. /:cl: * Fixes a variety of input stalling exploits (#82577) ## About The Pull Request Fixes the following input stalling exploits (maybe missed some): - Changing GPS tag - Setting teleporter destination - Request Console Reply - Various AI law board interactions - Note, I used `is_holding` but technically this means these fail with telekinesis. I can swap them to `can_perform_action(...)`, which allows TK, but I noticed some places explicitly deny TK interactions with Ai law boards. Not sure which is preferred. - Borg Rename Board - Plumbing Machines and Ducts - APCs and SMES terminal placements - Stargazers Telepathy - Go Go Gadget Hat ## Changelog :cl: Melbert fix: You can't change the GPS tag of something unless you can actually use the GPS fix: You can't set the teleporter to a location unless you can actually use the teleporter fix: You can't reply to request console requests unless you can actually use the console fix: You can't update AI lawboards unless you're actually holding them fix: You can't update a borg rename board unless you're actually holding it fix: You can't mess with plumbing machines unless you can actually use them fix: You can't recolor / relayer ducts unless you're actually holding them fix: You can't magically wire APCs and SMESs unless you're right by them fix: You can't use Stargazer Telepathy on people who you can't see fix: You can't configure the Inspector Hat unless you can actually use it /:cl: * [NO GBP] Power outage operation fixes for chem master (#82591) ## About The Pull Request - If the chem master runs out of power mid printing, it will properly stop the printing process and its animation - When transferring reagents it correctly checks if we have enough power without forcing it ## Changelog :cl: fix: chem master properly shuts down if it loses power mid printing and won't transfer reagents for the same /:cl: * Refactor renaming UNIQUE_RENAME items from the pen to an element (#82491) ## About The Pull Request So a bit ago someone in code_general wanted to make plushies renamable, but learnt that just adding the `UNIQUE_RENAME` flag wouldn't work as pens would murder the plushie and only THEN let you rename it. I noted refactoring both pens and plushies to use the new `item_interaction(...)` procs would Just Solve This, but, well, they didn't really have any coding experience. But, hey, renaming being hardcoded to the pens has annoyed me ever since I laid my eyes upon the hot mess that is paperwork code. So here we are! ### We're making it an element. There's not really much to this, this is mostly the same code but moved to an element and with some minor cleanups. First, we move it all from `/obj/item/pen` to a new element we called `/datum/element/tool_renaming`. With this, instead of having it proc on `/obj/item/pen/afterattack(...)`, we register it to proc on the `COMSIG_ITEM_INTERACTING_WITH_ATOM` signal. https://github.com/tgstation/tgstation/blob/6e36ed984070d53e16bed4fa43eb0cbe6460deed/code/__DEFINES/dcs/signals/signals_atom/signals_atom_x_act.dm#L59-L62 Secondly, we realize the code is just going through each if statement regardless of whether the previous was correct. https://github.com/tgstation/tgstation/blob/6e36ed984070d53e16bed4fa43eb0cbe6460deed/code/modules/paperwork/pen.dm#L225-L258 And, as we're dealing with text, just make it a switch statement instead. ```dm switch(pen_choice) if("Rename") (...) if("Description") (...) if("Reset") (...) ``` Then, we replace all single letter variables with descriptive ones, replace the if-elses with early returns, and make it actually return item interaction flags. Finally, we slap this onto the pen, and we're done. Now we can slap it onto other fitting renaming tools, and it uses the proper item interaction system. ## Why It's Good For The Game I feel it's generally better to not hardcode this to just pens, we have plenty other writing utensils and possible renaming tools. It's also a bit cleaner than before. Apart from that, moves it from using `afterattack(...)` to the proper item interaction chain by using `COMSIG_ITEM_INTERACTING_WITH_ATOM`, which should reduce janky interactions. ## Changelog :cl: refactor: Instead of being hardcoded to the pen, renaming items is now an element. Currently only pens have this, and functionality should be the same, but please report it if you find any items that were renamable but now aren't. /:cl: * Adds various quality of life changes for cooking to make it less click intensive. (#82566) ## About The Pull Request - Increases tray item size by 1 item. - Ranges and griddles can now be fed from trays. Click when closed => fill soup pot. Click when open => fill associated oven tray. Right click when open => fill tray from oven tray Click griddle => fill griddle surface. Right click => fill tray from griddle surface - Martian batter is now 5u of each ingredient into 10u of batter. Hopefully will make it bug out less where it makes far fewer reagents than it is supposed to, fixing reagents, or well soups specifically... is out of scope for this PR. - Adds the ability to print soup pots and large trays from the service lathe Soup pot: 5 Iron sheets, 0.4 bluespace crystal (given their size of 200U) Large serving tray: 2 iron sheets ## Why It's Good For The Game Makes cooking a lot less tedious. Especially for people with low precision when it comes to filling oven trays. This also bring the behavior up to parity with how you can click microwaves with trays to fill them, ditto for the food processor. It also allows chef to use the whole capacity of an oven, as previously you couldn't easily click 6 cake batters or other giant sprites onto the tiny tray. The tray is now sized to be able to easily feed a griddle 8 items. ## Changelog :cl: qol: chef equipment can now deposit and withdraw to/from trays! qol: chef now has access to griddle and oven sized trays! qol: service can now print soup pots /:cl: --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> * Removes grid usage + heavy refactors (#82571) ## About The Pull Request Grid has been deprecated for quite some time and we still use it. I won't completely remove the component, this way downstreams won't immediately suffer, but I can remove it from usage. Some of these UIs had issues with them and as a hobby project I've refactored them into typescript / rebuilt them. Airlock electronics, for instance, looks substantially better.
before/after as requested current airlock electronics scrolls into oblivion ![6RJ29HCPob](https://github.com/tgstation/tgstation/assets/42397676/ba82bc20-40fa-4af0-b709-7c8846c25652) updated ![Screenshot 2024-04-11 164321](https://github.com/tgstation/tgstation/assets/42397676/05507e06-6305-4175-8476-778c345f02c8)
## Why It's Good For The Game Code improvement + probably UI bug fixes ## Changelog :cl: fix: Airlock electronics and other access-config type UIs should look much better. /:cl: * modular fixes * [No GBP] Removes cogbar from some stealthy actions (#82593) Issue brought some missed hidden actions to my attention. I left cogbars in for _breaking_ handcuffs because resisting is sort of a gray area. On one hand, you don't want someone to see you doing it; on the other, there is a visible warning that you started doing it. So, meet in the the middle, breaking handcuffs is still visible while resisting isn't. Closes #82583 Cogbars are not intended to ruin stealth :cl: fix: Deviants buffed: Rogue shoelacing, pickpocketing and restraint resisting no longer give cogbar icons. /:cl: * [NO GBP] ...Remember to add SIGNAL_HANDLER (#82630) ## About The Pull Request Just realized I forgot to add `SIGNAL_HANDLER` to the all-nighter `on_removed_limb(...)` proc, even though it handles signals. ## Why It's Good For The Game https://github.com/tgstation/tgstation/blob/fe26373572e41415ec12e2b0785f0c6dc2567577/code/__DEFINES/dcs/helpers.dm#L9-L11 * React cleanup (#82607) ## About The Pull Request - No defaultHooks in react. Might fix issues where pages were not scrollable on hover. - createRef in a functional component. should be useref ## Why It's Good For The Game Code improvement * Security photobooths have their own ID (#82628) ## About The Pull Request Prevents the HoP's photobooth button from connecting to the security photobooth via having the same ID. ## Why It's Good For The Game I forgot to add this when I made the security photobooth but it's important that by default without any varedits, the HoP and security photobooths stay separate. ## Changelog :cl: fix: The HoP's photobooth button is now consistently connected to the HoP's photobooth. /:cl: * Fix buckled alert unbuckling not working properly (#82627) ## About The Pull Request So funny thing, while trying to reproduce a different issue on the current master, I coincidentally let my local instance start without reading, latejoined on the shuttle, and I noticed it wasn't letting me unbuckle as easily. Looking into this a bit later, it seems as if it's a line #82593 accidentally changed while moving around the `/mob/living/carbon/resist_buckle()` proc's flow. https://github.com/tgstation/tgstation/blob/fe26373572e41415ec12e2b0785f0c6dc2567577/code/modules/mob/living/carbon/carbon.dm#L238-L241 While before it was ```dm /mob/living/carbon/resist_buckle() if(HAS_TRAIT(src, TRAIT_RESTRAINED)) (...) else buckled.user_unbuckle_mob(src,src) ``` Just changing this to `buckled.user_unbuckle_mob(src, src)` fixes this. ## Why It's Good For The Game Fixes buckled alert unbuckling not working properly. Fixes #82627. ## Changelog :cl: fix: Clicking the buckled alert unbuckles you again. /:cl: * Advanced camera consoles correctly deactivates when something happens to it or the user (#82619) ## About The Pull Request - Fixes #82520 1. The eye deactivates when the machine is destroyed/deleted 2. The eye deactivates when the machine loses power 3. The computer constantly moniters the users status inside `process()` and will deactivate when anything happens to them. Its not enough to just hook onto to the mobs `COMSIG_MOVABLE_MOVED` signal. Literarly anything can happen to them so we have to check constantly for any changes ## Changelog :cl: fix: advanced camera consoles correctly deactivate when something happens(no proximity, no power etc) to its user /:cl: * Oven tray checks for ovens (#82615) ## About The Pull Request - Fixes #82610 Only oven trays have this proc not serving trays or other stuff ![Screenshot (408)](https://github.com/tgstation/tgstation/assets/110812394/4867cc14-9df3-4398-9d2d-f8e38b5f0da9) Also oven trays have a null atom storage which prevents it from being put back in the oven after taking it out. So we remove that check ## Changelog :cl: fix: you can put back the oven tray after you take it out fix: only oven trays are allowed in ovens preventing baked food runtimes /:cl: * Living Limb fixes (feat: Basic mobs attack random body zones again) (#82556) ## About The Pull Request Reworks Living Limb code to fix a bunch of runtimes and issues I saw while testing Bioscrambler. Specifically, the contained mobs are now initialised via element following attachment so that signal registration can occur at the correct time. This allows limbs to function correctly when added from nullspace via admin panel or bioscrambler. Secondarily (and more wide-ranging) at some point (probably #79563) we inadvertently made basic mobs only attack the target's chest instead of spreading damage. This is problematic for Living Flesh which can only attach itself to damaged limbs but was left unable to attack damaged limbs. I've fixed this in a way which is maybe stupid: adding an element which randomises attack zone pre-attack. Living limbs also limit this to _only_ limbs (although it will fall back to chest if you have no limbs at all). This is _technically_ still different, the previous behaviour used `adjustBruteLoss` and `adjustFireLoss` and would spread the damage across your entire body, but there isn't a route to that via the new interface and this seems close enough. ## Changelog :cl: fix: Living Limbs created by Bioscrambler will be alive. fix: Living Limbs can once more attach themselves to your body. balance: Living Limbs will prioritise attacking your limbs. fix: Basic Mobs will once again spread their damage across body zones instead of only attacking your chest. /:cl: * RPG Loot: Revisited & READY (#82533) Revival of #72881 A new alt click window with a tarkov-y loading spinner. Replaces the object item window in stat panel.
vids toggleable grouping: ![syAA5zf6RK](https://github.com/tgstation/tgstation/assets/42397676/c89b372d-29f6-4ebe-895d-f73bbdc41c19) now lists the floor as first obj: ![abc](https://github.com/tgstation/tgstation/assets/42397676/cd8dc962-2ac7-41bf-a5d3-b9e926116b06) in action: ![dreamseeker_IkrPKt2QZt](https://github.com/tgstation/tgstation/assets/42397676/1f990aa0-60f0-47e7-9d93-b63e35d05273)
- search by name - 515 image generator is much faster than alt click menu - opening a gargantuan amount of items shouldnt freeze your screen - groups similar items together in stacks by default, toggleable - shows tile as first item - Shift and Ctrl compatible with LMB :computer_mouse: - RMB points points at items (sry i could not get MMB working) - key Esc to exit the window. For devs: - A new image generation tech. - An error refetch mechanic to the Image component - It does not "smart track" the items being added to the pile, just reopen or refresh. This was a design decision. Honestly I just dislike the stat panel Fixes #53824 Fixes ![image](https://github.com/tgstation/tgstation/assets/42397676/0e50faab-7d4d-4bf7-8c5b-4ac28547bfbd) :cl: add: Added a loot window for alt-clicking tiles. del: Removed the item browser from the stat panel. /:cl: --------- Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: AnturK Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> * Reverts parts of #82602 (nodeath checks) (#82637) ## About The Pull Request Reverts the nodeath checks of #82602 I opened a review thinking these checks were sus and the PR author said they would remove them, but it was merged before that happened. TL;DR 1. I just noticed this now but it only affects carbons / humans it doesn't even cover living or any other subtypes 2. Kinda sus. Some code intentionally skips checking nodeath (I guess? Like removing the brain for example) so we would need a larger audit of this rather than haphazardly throwing it in. * Fixes to battle arcade (#82620) ## About The Pull Request Added gear for world nine, removed the "Gear" gear that did nothing. Made counterattacks to kill an enemy properly kill the enemy. I renamed some gear items to fit the theme of the area they are unlocked in just as a small thing. ## Why It's Good For The Game Closes https://github.com/tgstation/tgstation/issues/82613 ## Changelog :cl: fix: Battle arcade's higher levels no longer gives you a "Gear" gear, and counterattacks can now properly kill enemies. /:cl: * Fixes SMES terminal placing under the SMES and not under the player (#82665) ## About The Pull Request Changes `src` to`user` to get intended behavior. * Birdshot: Toy crate (#82633) ## About The Pull Request Gives the clown+mime their toy crate. ## Why It's Good For The Game *honk* * tram ai sat starts with a full smes (#82646) ## About The Pull Request consistency and also this is fixes a bug introduced by that one power refactor ## Why It's Good For The Game bug bad ## Changelog :cl: fix: tramstation AI sat starts full /:cl: * [no gbp] Space Ruin bioscramblers shouldn't chase people around (#82649) ## About The Pull Request See title They wouldn't lock on to people on the station from a space ruin, but would to whoever entered their z level the second it was entered. Also fixes bug where I changed `status_flags` to `status_effects` for some reason which isn't where you look for godmode ## Why It's Good For The Game We have a space ruin whcih several (coreless) anomalies spawn on, the bioscrambler was put as an option because it was already immortal. It's weird though to zone into the ruin and immediately have every anomaly in there lock onto you, the best intended effect is probably for these ones specifically not to be bloodthirsty. We kind of only care about that behaviour on the station. ## Changelog :cl: fix: Anomalous Research ruin Bioscrambler anomalies won't home in on targets fix: Bioscrambler won't randomly drop its target for no reason /:cl: * Sunders the many unused sprites and organizes what's left in structures.dmi (#82658) ## About The Pull Request Hello again, I noticed the /obj/structures.dmi file had a lot of unused stuff like tables from two generations ago, so I changed some stuff around: - Many unused, old icons deleted, mostly window variants used in old smoothing systems I imagine - Reorganized many sprites in the file so they're more grouped together - Tweaked some barricade sprite naming to be consistent/standardized, and to let others know they're not _too_ old... - Fixed a misnomer that I believe was making directional tinted windows look like frosted windows ## Why It's Good For The Game Saves on file space, and satisfies your brain's pattern recognition bits ### Spriting Old: ![image](https://github.com/tgstation/tgstation/assets/143908044/0717940e-787e-40ee-85e2-0a0c5ebc0837) New: ![image](https://github.com/tgstation/tgstation/assets/143908044/3954ba3b-b261-4700-986a-d30f3aa0e2a6) also good lord those linen bin sprites are a crime ## Changelog :cl: fix: Probably fixed directional tinted windows looking like directional frosted windows image: Deleted a bunch of unused structure sprites /:cl: * Birdshot Wall Sanity Pass (#82598) ## About The Pull Request Cleans up minor artifacting in the Birdshot Sec-Tram Closed Turfs ## Why It's Good For The Game Someone definitely didn't mean to place some machines under Closed Turfs. This barely qualifies as player facing. ## Changelog :cl: fix: Cleans up some rocks on Birdshot /:cl: * [NO GBP] Fixes deconstruction of closets & crates under a special case (#82612) ## About The Pull Request So if a closet/crate has the `NO_DEBRIS_AFTER_DECONSTRUCTION` set on it and if someone/something is still inside, then after deconstruction they get deleted rather than getting dumped out first. Could cause potential hard delete of mobs & stuff. We don't want to deal with that ## Changelog :cl: fix: closets & crates will dump all contents out first before deleting itself regardless of `NO_DEBRIS_AFTER_DECONSTRUCTION` thus not for e.g. hard deleting mobs inside it /:cl: * Fixes ordinance lab igniter in IceBox (#82595) ## About The Pull Request - Fixes #82294 Basically the same idea of merging ordanance lab with the burn chamber so they share the same apc as already implemented in #82322 ## Changelog :cl: fix: Ordinance lab igniter in Icebox works again /:cl: * Birdshot: engi wardrope. (#82639) ## About The Pull Request Add engi wardrope on Birdshot. ## Why It's Good For The Game Birdshot doesn't have engi wardrope. :cl: fix: Birdshot now have engi wardrope /:cl: * Gives shadow walk a new, spookier, and shorter sound effect that no longer ignores walls (#82689) ## About The Pull Request This gives shadow walk a snazzy new sound effect for entering/exiting jaunt. https://github.com/tgstation/tgstation/assets/28870487/c25f720f-5bad-4063-8d6e-140fd41bd740 This also has the sounds it plays no longer passes through walls. ## Why It's Good For The Game The ethereal_entrance/exit sound effects are drawn out, and pretty grating. They work for the other jaunts they're used for because a jaunt typically lasts longer than the sound itself. Nightmares are frequently dancing in and out of jaunt, and the sound effects for entering/exiting tend to overlap. It gets loud and annoying really fast. This sound effect is quicker, spookier, and more distinct. As for making the sound not ignore walls, I think it's pretty dumb how easy it is to detect the spooky scary shadow antag just by sitting in your department. It takes a lot of the initial fear and paranoia they have the potential for is wasted when Joe Geneticist can hear them messing around in their territory without having to leave their chair. ## Changelog :cl: Rhials sound: Nightmare has a new sound effect for entering/exiting shadow jaunt. It also no longer can be heard through walls. /:cl: * [MIRROR] Alt click refactor (#2029) * Alt click refactor * Some early conflict removal * Big modular refactor * Update console.dm * Update paper.dm --------- Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: Mal <13398309+vinylspiders@users.noreply.github.com> * Yeets `ATTACK_QDELETED`, fixes welding torches not using fuel on attacking non-mobs (2 year old bug) (#82694) ## About The Pull Request - Deletes `ATTACK_QDELETED` - May have been necessary in the past but it's pointless now. All it does is clutter the attack chain. Perish. - Fixes welders not using fuel on attacking non-mobs - #65762 "fixed" welders consuming fuel on clicking turfs by adding an `isliving` check and not an `ismovable` check? ## Changelog :cl: Melbert fix: Blobs may rejoice, welding torches now consume fuel when attacking objects again after two years. /:cl: * electric_welder fire * Quirks, which give items, now have quirk_item arg specified as obj/item, instead of being just a var (#82650) ## About The Pull Request quirk_item is now /obj/item, since it will allow for calling procs or getting variables from this item It's required for non-modular translation to call for item's name to remove articles ## Why It's Good For The Game It's always an item, and if it's a path, it's already checked for it. Better usage in the future. * turns martial arts gloves into a component (#82599) sleeping carp gloves also work on mind init this means for the sake of deathmatch you dont have to put them off and on fixes #82321 :cl: fix: you no longer need to put your sleeping carp gloves off and on in Deathmatch to get the martial art /:cl: --------- Co-authored-by: san7890 * Regal Rats can now tear down posters (#82673) ## About The Pull Request i was fixing something on bagil and someone who was playing a regal rat (after the round ended) said they wanted to be able to tear down posters as a regal rat so i decided to code it because it made sense. it's an element so literally any mob can tear down posters but i can't think of any other mobs that would make sense to let it tear down posters so we'll leave it just for _The Champion of All Mislaid Creatures_ for now ## Why It's Good For The Game Regal Rats should be all about sludgemaxxing and fucking up maintenance to make it look even more grody than it should be. Being able to tear up those disgusting and well-drawn posters to leave behind nothing but scraps fits that motif. The element has a `do_after()` just to make sure His Holiness doesn't accidentally tear down his posters while clicking (i think all mobs should have this but that's a different issue man) also includes some code improvement and user feedback in some failure cases that already existed in the code. ## Changelog :cl: add: Regal Rats are now able to tear down those colorful posters those weird grey creatures keep spackling up on the walls of their rightful domain. /:cl: * Adds "Strong Stomach" quirk, a core CDDA/PZ quirk we've sorely been missing. Also Deviant Tastes dirty food re-nerf. (#82562) ## About The Pull Request - Adds Strong Stomach quirk. - 4 points - You can eat dirty food without risk of getting disease. - You suffer less negative effects from vomiting. Vomit stuns you for half the duration, and you lose half as much nutrition. - Reverts https://github.com/tgstation/tgstation/pull/76864 , integrates its effects into Strong Stomach instead. ## Why It's Good For The Game - Lotta people (namely Lizards and sometimes Felines with Deviant Tastes) run gimmicks involving them being a gremlin person and eating trash off the ground, and it's rather hard to accomplish this now since it makes you a public medbay enemy # 1. This quirk should give them an option to avoid that. - Also (as mentioned in the title) both CDDA and PZ have this trait and I can't believe we're missing it! This is something in modifiable-character-traits/quirks-101. - I moved the effects from #76864 to this quirk because 1. I thought it was more fitting and 2. I thought the original PR was kinda wack for what is (generally) a neutral quirk. ## Changelog :cl: Melbert add: Adds the Strong Stomach quirk, which allows you to eat grimy food without worry about disease, and makes you a bit more resilient to the effects of vomiting. del: Deviant Tastes no longer prevents you from getting a negative moodlet from eating dirty food. Strong Stomach does that now. /:cl: --------- Co-authored-by: Jacquerel * Remove several functions from collections.js which have ES5 equivalents (#82417) * Makes it EVEN EASIER to work with atom item interactions ft. "Leaf and Branch" & "Death to Chains" (#82625) * apc fix * Gulag Adjustments Two (#82561) ## About The Pull Request I have received feedback that after the prior changes in #81971, the gulag is still a little bit too subject to RNG. The main culprit (as in my previous PR) is Iron being kind of cheap and the fact that unlike the old Gulag you no longer have any way of headhunting more valuable materials (everything appears as boulders on your ore scanner). My solution to this is wider than the last one of tweaking point values, but also much simpler: Just make every boulder you mine be worth the same amount of points regardless of what is inside of it. On the average test I made I could comfortably mine about 40-45 boulders in ten minutes. We'll make some adjustments to that rather than leaving 40 as the target number; Most players upon being teleported to the gulag are going to spend a few minutes whining and bemoaning their fate instead of getting straight to work. I had the benefit of being able to make sure my run started as soon as a storm ended so I wouldn't need any kind of midpoint break. I was also always the only person playing on my local instance, there hadn't been any other pesky prisoners before me who had already mined out all the nearest available deposits. And of course, let us not forget, I am an MLG master league ss13 player who was surely performing well above average. So we'll round that down to: Each boulder is worth 33 points, meaning you need to collect 31 boulders to complete a 1000 point (roughly ten minute) sentence. How do I ensure that every boulder is worth the same amount of points? Well it's pretty easy. One boulder = one material sheet. One material sheet = 33 points. Simple. "Now Jacquerel", I hear you not saying because you don't want me to know about this thing you would prefer to do instead of hitting rocks outside; "if I simply smash all of the tables and microwaves and botany trays and bed in the gulag I can easily get like 65 sheets of Iron, which is almost enough to buy the freedom for two entire people!" Unfortunately I knew you were going to try and do that and the prisoner point machine will only give you points for material sheets which have been printed from the material smelter (well, any material smelter actually but you should probably use the one in the gulag). You'll be able to tell because if you examine a valid material sheet it will mention a little maker's mark on it, which is absent in the beat-up iron that you get from smashing furniture to bits. Also glass is worth 0 points. Don't waste time digging up that shit. As glass has had all of its point value removed, I have added a "work pit" to the gulag to compensate. You can pull boulders out of this indefinitely via effort, however it also stamcrits you every time. It's not very fun to do this, but that's because I would prefer you to go find the rocks out in the field instead. This is a last resort. You can do this if there's no boulders left to mine or if you really really really hate mining and would rather very slowly click on one tile repeatedly to get your boulders instead. As a tiny bonus doing this gives workout experience. This isn't a totally ideal solution but I think it'll do for now. ## Why It's Good For The Game What we want out of the gulag is: - Something where officers can vaguely approximate an expected sentence duration. - A task that requires players to actually be spending that time doing something to get out of here. - Produces at least some amount of useful materials. In I think roughly that order. I hope this change accomplishes all three of these in a way that is somewhat predictable rather than throwing darts at a board. ## Changelog :cl: balance: Gulag mining has been rebalanced so that every boulder is worth the same amount of points to mine for a prisoner regardless of what it contains, and should be more consistent. add: A vent which boulders can be hauled out of by hand has been added to the gulag which you can use if there's nothing left to mine. It is very slow, but at least it gives you a workout... /:cl: * stone * Makes test merge bot continue with other PRs if updating one fails. (#82717) Right now updating https://github.com/tgstation/tgstation/pull/81089#issuecomment-1907296233 fails because it exceeds github character limit for comments. This will make it work until backed is updated. * Fixes the RnD console by adding a removed import (#82750) ## About The Pull Request The 'map' import was removed from this file by #82417 but it's still used in place in code. This re-adds the import ## Why It's Good For The Game Fixes RnD consoles ## Changelog :cl: fix: Fixed RnD consoles not being able to be opened. /:cl: Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com> * Fixes cargo import (#82755) ## About The Pull Request One of the imports got removed and there were no warnings... Man if only there were a technology that could warn us in advance ## Why It's Good For The Game UI fixes ## Changelog :cl: fix: Fixed a bluescreen in cargo console /:cl: * fixes * Fixes, fixes. * Pre-emptive mirror of https://github.com/tgstation/tgstation/pull/82892 * Turf weakref persists in changeturf / Fix plasma cutters (#82906) ## About The Pull Request Turf references don't change so logically, turf weakrefs wouldn't change if the turf changes. By not doing this this can cause bugs: See #82886 . (This Fixes #82886) (Projectiles hold a list of weakrefs to atoms hit to determine what they have already hit. Because turf weakrefs reset, we could "hit" the same turf twice if it destroyed the turf. Old behavior - this was fine but now that they're weakrefs, we get two weakref datums in the list that point to the same ref.) Less hacky alternative to #82901 . (Closes #82901) ## Changelog :cl: Melbert fix: Plasma cutters work again /:cl: --------- Co-authored-by: san7890 Co-authored-by: Interception&? <137328283+intercepti0n@users.noreply.github.com> Co-authored-by: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Co-authored-by: _0Steven <42909981+00-Steven@users.noreply.github.com> Co-authored-by: Ketrai Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Co-authored-by: Jacquerel Co-authored-by: Zephyr <12817816+ZephyrTFA@users.noreply.github.com> Co-authored-by: AnturK Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Co-authored-by: Iajret <8430839+Iajret@users.noreply.github.com> Co-authored-by: vect0r <71346830+Vect0r2@users.noreply.github.com> Co-authored-by: jimmyl <70376633+mc-oofert@users.noreply.github.com> Co-authored-by: AMyriad <143908044+AMyriad@users.noreply.github.com> Co-authored-by: Zytolg <33048583+Zytolg@users.noreply.github.com> Co-authored-by: Xackii <120736708+Xackii@users.noreply.github.com> Co-authored-by: Rhials <28870487+Rhials@users.noreply.github.com> Co-authored-by: NovaBot <154629622+NovaBot13@users.noreply.github.com> Co-authored-by: Mal <13398309+vinylspiders@users.noreply.github.com> Co-authored-by: larentoun <31931237+larentoun@users.noreply.github.com> Co-authored-by: Arthri <41360489+Arthri@users.noreply.github.com> Co-authored-by: Watermelon914 <37270891+Watermelon914@users.noreply.github.com> Co-authored-by: Watermelon914 <3052169-Watermelon914@users.noreply.gitlab.com> Co-authored-by: Useroth <37159550+Useroth@users.noreply.github.com> --- _maps/map_files/Birdshot/birdshot.dmm | 142 ++- .../map_files/IceBoxStation/IceBoxStation.dmm | 965 +++++++++--------- _maps/map_files/Mining/Lavaland.dmm | 472 ++++----- _maps/map_files/tramstation/tramstation.dmm | 348 ++++--- code/__DEFINES/basic_mobs.dm | 2 + code/__DEFINES/click.dm | 8 + code/__DEFINES/combat.dm | 2 + .../signals_atom/signals_atom_mouse.dm | 1 - .../signals/signals_mob/signals_mob_main.dm | 2 - code/__DEFINES/dcs/signals/signals_object.dm | 2 - code/__DEFINES/interaction_flags.dm | 7 +- code/__DEFINES/mobs.dm | 4 + code/__DEFINES/obj_flags.dm | 27 +- code/__DEFINES/say.dm | 1 + code/__DEFINES/subsystems.dm | 1 + code/__DEFINES/traits/declarations.dm | 1 + code/__DEFINES/traits/sources.dm | 4 +- code/__DEFINES/~skyrat_defines/combat.dm | 7 +- code/_globalvars/bitfields.dm | 27 +- code/_globalvars/traits/_traits.dm | 3 +- code/_globalvars/traits/admin_tooling.dm | 3 +- code/_onclick/ai.dm | 15 +- code/_onclick/click.dm | 30 +- code/_onclick/click_alt.dm | 66 ++ code/_onclick/cyborg.dm | 22 +- code/_onclick/item_attack.dm | 13 +- code/_onclick/observer.dm | 2 +- code/_onclick/overmind.dm | 4 +- .../subsystem/processing/obj_tab_items.dm | 24 - code/controllers/subsystem/statpanel.dm | 183 ---- code/controllers/subsystem/tgui.dm | 2 - code/datums/ai/oldhostile/hostile_tameable.dm | 2 +- code/datums/browser.dm | 4 - code/datums/components/gps.dm | 10 +- code/datums/components/infective.dm | 9 +- code/datums/components/martial_art_giver.dm | 45 + .../components/material/material_container.dm | 1 + .../components/pet_commands/obeys_commands.dm | 1 + code/datums/components/rotation.dm | 1 + code/datums/components/style/style_meter.dm | 15 +- code/datums/components/toggle_suit.dm | 9 +- .../datums/elements/attack_zone_randomiser.dm | 33 + .../elements/living_limb_initialiser.dm | 19 + code/datums/elements/poster_tearer.dm | 45 + code/datums/elements/ridable.dm | 12 + code/datums/elements/strippable.dm | 6 +- code/datums/elements/tool_renaming.dm | 78 ++ code/datums/martial/boxing.dm | 17 +- code/datums/martial/krav_maga.dm | 17 +- code/datums/martial/sleeping_carp.dm | 20 +- code/datums/martial/wrestling.dm | 17 +- code/datums/mood_events/needs_events.dm | 2 +- code/datums/quirks/_quirk.dm | 2 +- .../quirks/negative_quirks/all_nighter.dm | 2 + .../quirks/positive_quirks/strong_stomach.dm | 12 + code/datums/storage/storage.dm | 3 +- code/game/atom/_atom.dm | 8 +- code/game/atom/atom_tool_acts.dm | 34 +- code/game/machinery/_machinery.dm | 21 +- code/game/machinery/autolathe.dm | 10 +- code/game/machinery/camera/camera.dm | 27 +- code/game/machinery/civilian_bounties.dm | 6 +- code/game/machinery/computer/_computer.dm | 6 - .../game/machinery/computer/arcade/_arcade.dm | 10 +- code/game/machinery/computer/arcade/battle.dm | 10 +- .../machinery/computer/arcade/battle_gear.dm | 21 +- .../game/machinery/computer/buildandrepair.dm | 2 +- .../machinery/computer/camera_advanced.dm | 58 +- code/game/machinery/computer/dna_console.dm | 12 +- .../machinery/computer/prisoner/_prisoner.dm | 8 +- code/game/machinery/computer/teleporter.dm | 4 +- code/game/machinery/constructable_frame.dm | 9 +- code/game/machinery/defibrillator_mount.dm | 9 +- code/game/machinery/deployable.dm | 9 +- code/game/machinery/dish_drive.dm | 7 +- .../game/machinery/dna_infuser/dna_infuser.dm | 8 +- code/game/machinery/fat_sucker.dm | 9 +- code/game/machinery/harvester.dm | 18 +- code/game/machinery/iv_drip.dm | 9 +- code/game/machinery/machine_frame.dm | 15 +- code/game/machinery/photobooth.dm | 1 + code/game/machinery/pipe/construction.dm | 2 - code/game/machinery/requests_console.dm | 5 +- code/game/machinery/sleepers.dm | 6 +- code/game/machinery/slotmachine.dm | 40 +- code/game/machinery/spaceheater.dm | 8 +- code/game/machinery/stasis.dm | 26 +- .../telecomms/computers/telemonitor.dm | 1 - code/game/objects/buckling.dm | 12 - .../anomalies/anomalies_bioscrambler.dm | 41 +- code/game/objects/effects/posters/poster.dm | 19 +- code/game/objects/items.dm | 25 + .../game/objects/items/AI_modules/freeform.dm | 6 +- .../objects/items/AI_modules/full_lawsets.dm | 4 +- code/game/objects/items/AI_modules/hacked.dm | 3 +- .../game/objects/items/AI_modules/supplied.dm | 2 +- code/game/objects/items/AI_modules/zeroth.dm | 2 +- code/game/objects/items/airlock_painter.dm | 18 +- code/game/objects/items/cards_ids.dm | 26 +- .../machines/machine_circuitboards.dm | 5 +- code/game/objects/items/cosmetics.dm | 13 +- code/game/objects/items/crayons.dm | 8 +- code/game/objects/items/credit_holochip.dm | 18 +- .../objects/items/devices/desynchronizer.dm | 18 +- .../objects/items/devices/geiger_counter.dm | 7 +- .../objects/items/devices/laserpointer.dm | 9 +- .../objects/items/devices/quantum_keycard.dm | 7 +- .../objects/items/devices/radio/headset.dm | 12 +- .../items/devices/scanners/autopsy_scanner.dm | 2 +- .../items/devices/scanners/gas_analyzer.dm | 26 +- .../items/devices/scanners/health_analyzer.dm | 20 +- .../devices/scanners/sequence_scanner.dm | 4 +- .../items/devices/scanners/slime_scanner.dm | 2 +- code/game/objects/items/devices/swapper.dm | 12 +- .../objects/items/devices/taperecorder.dm | 6 +- .../objects/items/devices/traitordevices.dm | 2 +- code/game/objects/items/emags.dm | 4 +- code/game/objects/items/etherealdiscoball.dm | 4 +- code/game/objects/items/extinguisher.dm | 8 +- code/game/objects/items/flamethrower.dm | 20 +- .../game/objects/items/implants/implantpad.dm | 7 +- code/game/objects/items/inspector.dm | 9 +- code/game/objects/items/machine_wand.dm | 7 +- code/game/objects/items/paiwire.dm | 8 +- code/game/objects/items/pet_carrier.dm | 7 +- code/game/objects/items/pillow.dm | 12 +- code/game/objects/items/rcd/RPLD.dm | 3 +- code/game/objects/items/rcd/RWD.dm | 6 +- code/game/objects/items/robot/items/hypo.dm | 13 +- .../game/objects/items/robot/items/storage.dm | 23 +- .../objects/items/robot/robot_upgrades.dm | 5 +- code/game/objects/items/spear.dm | 13 +- code/game/objects/items/stacks/bscrystal.dm | 2 +- code/game/objects/items/stacks/medical.dm | 8 +- .../game/objects/items/stacks/sheets/glass.dm | 4 +- .../objects/items/stacks/sheets/mineral.dm | 20 +- .../items/stacks/sheets/sheet_types.dm | 4 +- .../objects/items/stacks/sheets/sheets.dm | 18 +- code/game/objects/items/stacks/telecrystal.dm | 2 +- code/game/objects/items/stacks/wrap.dm | 9 +- code/game/objects/items/storage/bags.dm | 39 +- code/game/objects/items/storage/belt.dm | 6 +- .../items/storage/boxes/engineering_boxes.dm | 8 +- code/game/objects/items/storage/fancy.dm | 4 +- code/game/objects/items/storage/lockbox.dm | 6 +- code/game/objects/items/tanks/tank_types.dm | 10 +- code/game/objects/items/tcg/tcg_machines.dm | 2 +- code/game/objects/items/tools/weldingtool.dm | 28 +- code/game/objects/items/toys.dm | 2 +- code/game/objects/items_reskin.dm | 57 ++ code/game/objects/objs.dm | 131 +-- .../structures/beds_chairs/alien_nest.dm | 56 +- .../objects/structures/beds_chairs/bed.dm | 9 +- .../objects/structures/beds_chairs/chair.dm | 9 +- code/game/objects/structures/bedsheet_bin.dm | 7 +- .../structures/crates_lockers/closets.dm | 7 +- code/game/objects/structures/divine.dm | 10 +- code/game/objects/structures/false_walls.dm | 4 +- code/game/objects/structures/kitchen_spike.dm | 2 +- .../objects/structures/lavaland/geyser.dm | 10 +- .../objects/structures/lavaland/gulag_vent.dm | 42 + code/game/objects/structures/morgue.dm | 7 +- code/game/objects/structures/railings.dm | 2 - code/game/objects/structures/reflector.dm | 4 +- code/game/objects/structures/shower.dm | 2 - code/game/objects/structures/tables_racks.dm | 8 +- .../objects/structures/training_machine.dm | 19 +- .../transit_tube_construction.dm | 2 - .../objects/structures/windoor_assembly.dm | 2 - code/game/objects/structures/window.dm | 6 +- code/game/turfs/change_turf.dm | 4 + code/game/turfs/open/floor/plating.dm | 5 +- code/modules/admin/sound_emitter.dm | 11 +- .../abductor/equipment/gear/abductor_items.dm | 8 +- .../antagonists/cult/rune_spawn_action.dm | 2 +- code/modules/antagonists/cult/runes.dm | 2 +- code/modules/art/paintings.dm | 10 +- code/modules/art/statues.dm | 2 - code/modules/assembly/health.dm | 6 +- code/modules/assembly/holder.dm | 2 - code/modules/assembly/infrared.dm | 2 - .../asset_cache/assets/icon_ref_map.dm | 28 + .../asset_cache/transports/asset_transport.dm | 10 +- .../atmospherics/machinery/atmosmachinery.dm | 5 - .../components/binary_devices/passive_gate.dm | 16 +- .../binary_devices/pressure_valve.dm | 16 +- .../components/binary_devices/pump.dm | 16 +- .../binary_devices/temperature_gate.dm | 16 +- .../binary_devices/temperature_pump.dm | 16 +- .../components/binary_devices/volume_pump.dm | 16 +- .../components/electrolyzer/electrolyzer.dm | 8 +- .../components/trinary_devices/filter.dm | 16 +- .../components/trinary_devices/mixer.dm | 16 +- .../components/unary_devices/cryo.dm | 15 +- .../unary_devices/outlet_injector.dm | 16 +- .../components/unary_devices/passive_vent.dm | 2 + .../components/unary_devices/thermomachine.dm | 7 +- .../components/unary_devices/unary_devices.dm | 6 + .../components/unary_devices/vent_pump.dm | 1 + .../components/unary_devices/vent_scrubber.dm | 1 + .../portable/portable_atmospherics.dm | 9 +- .../awaymissions/mission_code/snowdin.dm | 2 +- code/modules/bitrunning/objects/disks.dm | 4 +- code/modules/cards/deck/deck.dm | 15 +- code/modules/cards/singlecard.dm | 8 +- code/modules/cargo/supplypod_beacon.dm | 16 +- code/modules/cargo/universal_scanner.dm | 6 +- code/modules/client/client_defines.dm | 5 +- code/modules/client/client_procs.dm | 5 +- code/modules/clothing/glasses/_glasses.dm | 51 +- code/modules/clothing/head/jobs.dm | 34 +- code/modules/clothing/head/soft_caps.dm | 12 +- code/modules/clothing/masks/animal_masks.dm | 11 +- code/modules/clothing/masks/bandana.dm | 66 +- code/modules/clothing/masks/breath.dm | 8 +- code/modules/clothing/masks/costume.dm | 8 - code/modules/clothing/neck/_neck.dm | 10 +- code/modules/clothing/shoes/_shoes.dm | 17 +- .../clothing/spacesuits/_spacesuits.dm | 21 +- code/modules/clothing/spacesuits/plasmamen.dm | 6 +- code/modules/clothing/suits/wintercoats.dm | 8 +- code/modules/clothing/under/_under.dm | 33 +- code/modules/clothing/under/costume.dm | 1 + code/modules/detectivework/scanner.dm | 18 +- .../experiment/handlers/experiment_handler.dm | 1 + code/modules/fishing/aquarium/aquarium.dm | 6 +- .../food_and_drinks/machinery/griddle.dm | 38 + .../food_and_drinks/machinery/grill.dm | 30 +- .../food_and_drinks/machinery/icecream_vat.dm | 13 +- .../food_and_drinks/machinery/microwave.dm | 33 +- .../modules/food_and_drinks/machinery/oven.dm | 60 +- .../food_and_drinks/machinery/stove.dm | 34 + .../machinery/stove_component.dm | 8 + .../food_and_drinks/recipes/food_mixtures.dm | 4 +- code/modules/holodeck/turfs.dm | 5 +- code/modules/hydroponics/biogenerator.dm | 8 +- code/modules/hydroponics/hydroponics.dm | 2 - code/modules/jobs/job_types/prisoner.dm | 4 +- code/modules/lootpanel/_lootpanel.dm | 75 ++ code/modules/lootpanel/contents.dm | 49 + code/modules/lootpanel/handlers.dm | 19 + code/modules/lootpanel/misc.dm | 48 + code/modules/lootpanel/search_object.dm | 84 ++ code/modules/lootpanel/ss_looting.dm | 39 + code/modules/lootpanel/ui.dm | 42 + .../ruins/spaceruin_code/anomalyresearch.dm | 2 +- .../ruins/spaceruin_code/hilbertshotel.dm | 27 +- code/modules/mining/abandoned_crates.dm | 7 +- .../boulder_processing/_boulder_processing.dm | 6 +- .../boulder_processing/boulder_types.dm | 4 +- .../modules/mining/equipment/explorer_gear.dm | 37 +- .../mining/equipment/marker_beacons.dm | 23 +- code/modules/mining/equipment/mining_tools.dm | 2 +- code/modules/mining/laborcamp/laborstacker.dm | 22 +- code/modules/mining/machine_processing.dm | 12 +- code/modules/mining/machine_redemption.dm | 22 +- code/modules/mining/machine_stacking.dm | 18 +- code/modules/mob/living/basic/basic.dm | 6 + code/modules/mob/living/basic/bots/_bots.dm | 9 +- .../mob/living/basic/minebots/minebot.dm | 6 +- .../mob/living/basic/ruin_defender/flesh.dm | 9 +- .../basic/space_fauna/regal_rat/regal_rat.dm | 1 + .../basic/space_fauna/revenant/_revenant.dm | 2 +- code/modules/mob/living/brain/posibrain.dm | 9 +- code/modules/mob/living/carbon/carbon.dm | 102 +- code/modules/mob/living/carbon/death.dm | 2 +- code/modules/mob/living/carbon/human/death.dm | 2 +- .../living/carbon/human/human_stripping.dm | 2 +- .../carbon/human/species_types/jellypeople.dm | 7 +- code/modules/mob/living/death.dm | 1 - code/modules/mob/living/init_signals.dm | 1 - code/modules/mob/living/life.dm | 3 - code/modules/mob/living/living.dm | 9 +- code/modules/mob/living/silicon/ai/ai.dm | 13 - .../mob/living/silicon/ai/freelook/eye.dm | 1 - code/modules/mob/living/silicon/ai/life.dm | 3 - .../mob/living/simple_animal/bot/bot.dm | 9 +- .../mob/living/simple_animal/bot/floorbot.dm | 4 +- code/modules/mob/logout.dm | 1 - code/modules/mob/mob.dm | 9 +- code/modules/mob/mob_defines.dm | 3 - code/modules/mod/mod_link.dm | 2 + .../computers/item/computer.dm | 13 +- .../computers/item/laptop.dm | 13 +- .../modular_computers/computers/item/pda.dm | 6 - .../computers/machinery/modular_computer.dm | 8 +- code/modules/pai/door_jack.dm | 6 +- code/modules/paperwork/carbonpaper.dm | 6 +- code/modules/paperwork/clipboard.dm | 17 +- code/modules/paperwork/handlabeler.dm | 7 +- code/modules/paperwork/paper.dm | 17 +- code/modules/paperwork/paper_cutter.dm | 6 +- code/modules/paperwork/pen.dm | 50 +- code/modules/photography/camera/camera.dm | 13 +- code/modules/plumbing/ducts.dm | 4 + .../plumbing/plumbers/_plumb_machinery.dm | 2 - code/modules/plumbing/plumbers/filter.dm | 6 +- code/modules/plumbing/plumbers/iv_drip.dm | 2 - .../plumbing/plumbers/reaction_chamber.dm | 2 + code/modules/power/apc/apc_tool_act.dm | 77 +- code/modules/power/pipecleaners.dm | 5 +- code/modules/power/singularity/emitter.dm | 2 - code/modules/power/smes.dm | 92 +- code/modules/projectiles/guns/ballistic.dm | 22 +- .../projectiles/guns/ballistic/automatic.dm | 5 +- .../projectiles/guns/ballistic/bows/_bow.dm | 5 +- .../projectiles/guns/ballistic/revolver.dm | 4 +- .../projectiles/guns/ballistic/shotgun.dm | 10 +- code/modules/projectiles/projectile.dm | 1 - .../chemistry/machinery/chem_dispenser.dm | 8 +- .../chemistry/machinery/chem_heater.dm | 6 +- .../chemistry/machinery/chem_mass_spec.dm | 15 +- .../chemistry/machinery/chem_master.dm | 12 +- .../machinery/portable_chem_mixer.dm | 12 +- .../chemistry/machinery/reagentgrinder.dm | 6 +- .../reagents/reagent_containers/chem_pack.dm | 32 +- .../reagents/reagent_containers/cups/_cup.dm | 12 +- .../reagent_containers/cups/bottle.dm | 4 +- .../reagent_containers/cups/drinkingglass.dm | 4 +- .../reagent_containers/cups/drinks.dm | 28 +- .../reagents/reagent_containers/medigel.dm | 1 + .../reagents/reagent_containers/pill.dm | 6 + .../reagents/reagent_containers/spray.dm | 11 +- code/modules/reagents/reagent_dispenser.dm | 2 - .../recycling/disposal/construction.dm | 2 - code/modules/recycling/sortingmachinery.dm | 4 +- code/modules/religion/burdened/psyker.dm | 2 +- .../designs/autolathe/service_designs.dm | 13 + code/modules/research/destructive_analyzer.dm | 14 +- .../modules/research/machinery/_production.dm | 10 +- .../research/ordnance/doppler_array.dm | 2 - code/modules/research/server.dm | 17 +- code/modules/research/techweb/all_nodes.dm | 1 + .../research/xenobiology/xenobio_camera.dm | 4 +- .../research/xenobiology/xenobiology.dm | 5 +- code/modules/shuttle/emergency.dm | 14 +- .../spells/spell_types/jaunt/shadow_walk.dm | 4 +- code/modules/surgery/bodyparts/_bodyparts.dm | 4 +- .../surgery/bodyparts/dismemberment.dm | 2 +- code/modules/surgery/bodyparts/helpers.dm | 4 +- .../bodyparts/species_parts/misc_bodyparts.dm | 20 +- code/modules/tgui/tgui.dm | 2 + code/modules/transport/tram/tram_remote.dm | 7 +- code/modules/transport/tram/tram_signals.dm | 12 +- code/modules/unit_tests/_unit_tests.dm | 2 + code/modules/unit_tests/combat_welder.dm | 26 + code/modules/unit_tests/lootpanel.dm | 34 + code/modules/vehicles/mecha/_mecha.dm | 2 +- .../modules/vehicles/mecha/mech_fabricator.dm | 14 +- code/modules/vehicles/mecha/mecha_actions.dm | 9 +- code/modules/vehicles/ridden.dm | 10 +- code/modules/vehicles/wheelchair.dm | 2 - code/modules/wiremod/shell/controller.dm | 4 +- icons/obj/structures.dmi | Bin 211058 -> 83526 bytes .../code/datums/traits/neutral.dm | 3 +- .../code/game/machinery/doors/firedoor.dm | 5 +- .../code/modules/clothing/towels.dm | 12 +- .../modules/reagents/reagent_containers.dm | 3 +- .../modules/SiliconQoL/code/_onclick.dm | 47 +- .../aesthetics/keyed_doors/code/keyed_door.dm | 6 +- .../ammo_workbench/code/ammo_workbench.dm | 4 +- modular_skyrat/modules/apc_arcing/code/apc.dm | 27 +- .../ashwalkers/code/buildings/railroad.dm | 4 +- .../assault_operatives/code/interrogator.dm | 2 +- .../mothership_astrum/fluff.dm | 4 +- .../modules/barricades/code/barricade.dm | 16 +- .../borg_buffs/code/snack_dispensor.dm | 3 +- .../modules/borgs/code/robot_items.dm | 14 +- modular_skyrat/modules/bsrpd/code/bsrpd.dm | 6 +- .../cargo_teleporter/code/cargo_teleporter.dm | 3 +- .../modules/cellguns/code/cellguns.dm | 5 +- .../code/computers/station_goal_computer.dm | 4 +- .../code/design_datums/appliances.dm | 1 - .../code/design_datums/equipment.dm | 18 +- .../code/machines/arc_furnace.dm | 1 - .../conveyor_sorter/code/conveyor_sorter.dm | 6 +- .../modules/clothing/masks/paper.dm | 1 - .../modules/clothing/neck/_neck.dm | 4 +- .../modules/clothing/storage/belts.dm | 9 +- .../clothing/~donator/donator_clothing.dm | 9 +- .../clothing/~donator/donator_items.dm | 4 +- .../electric_welder/code/electric_welder.dm | 4 - .../modules/exp_corps/code/clothing.dm | 6 +- .../goofsec/code/sec_clothing_overrides.dm | 4 +- .../modules/hev_suit/code/hev_suit.dm | 6 +- .../modules/inflatables/code/inflatable.dm | 6 +- modular_skyrat/modules/knives/knives.dm | 9 +- .../code/liquid_systems/liquid_pump.dm | 6 +- .../medical/code/anesthetic_machine.dm | 6 +- .../code/microfusion_energy_master.dm | 13 +- .../code/lewd_clothing/bdsm_mask.dm | 20 +- .../lewd_items/code/lewd_clothing/corset.dm | 3 +- .../code/lewd_clothing/deprivation_helmet.dm | 10 +- .../code/lewd_clothing/hypnogoggles.dm | 10 +- .../code/lewd_clothing/kink_collars.dm | 39 +- .../code/lewd_clothing/kinky_blindfold.dm | 11 +- .../code/lewd_clothing/kinky_headphones.dm | 10 +- .../code/lewd_clothing/kinky_sleepbag.dm | 12 +- .../code/lewd_clothing/lewd_maid.dm | 10 +- .../lewd_items/code/lewd_clothing/shackles.dm | 24 +- .../lewd_clothing/shibari_worn_uniform.dm | 8 +- .../lewd_items/code/lewd_clothing/strapon.dm | 23 +- .../code/lewd_clothing/stripper_outfit.dm | 6 +- .../code/lewd_items/attachable_vibrator.dm | 15 +- .../lewd_items/code/lewd_items/buttplug.dm | 14 +- .../lewd_items/code/lewd_items/dildo.dm | 25 +- .../lewd_items/code/lewd_items/fleshlight.dm | 10 +- .../code/lewd_items/kinky_shocker.dm | 4 +- .../code/lewd_items/leather_whip.dm | 17 +- .../code/lewd_items/spanking_pad.dm | 10 +- .../code/lewd_items/torture_candle.dm | 11 +- .../lewd_items/code/lewd_items/vibrator.dm | 8 +- .../lewd_items/code/lewd_items/vibroring.dm | 10 +- .../code/lewd_machinery/milking_machine.dm | 4 +- .../code/lewd_structures/dancing_pole.dm | 12 +- .../lewd_items/code/lewd_structures/pillow.dm | 43 +- .../modules/modular_weapons/code/gunsets.dm | 4 +- .../code/mounted_machine_gun.dm | 6 +- .../modules/mutants/code/mutant_cure.dm | 6 +- .../modules/pollution/code/perfumes.dm | 3 +- .../code/big_mortar.dm | 5 +- .../code/cutting_board.dm | 5 +- .../code/millstone.dm | 7 +- .../primitive_production/code/glassblowing.dm | 1 - modular_skyrat/modules/salon/code/hair_tie.dm | 8 +- .../code/self_actualization_device.dm | 6 +- .../specialist_armor/code/sacrificial.dm | 3 +- .../stasisrework/code/stasissleeper.dm | 7 +- modular_skyrat/modules/stone/code/stone.dm | 1 - .../modules/time_clock/code/console.dm | 11 +- .../wargame_projectors/code/projectors.dm | 3 +- sound/attributions.txt | 4 + sound/effects/nightmare_poof.ogg | Bin 0 -> 15537 bytes sound/effects/nightmare_reappear.ogg | Bin 0 -> 18238 bytes tgstation.dme | 19 +- tgui/packages/common/collections.ts | 239 ++--- tgui/packages/common/fp.js | 24 - tgui/packages/common/vector.js | 48 - tgui/packages/common/vector.ts | 51 + tgui/packages/tgui-panel/chat/selectors.ts | 2 +- tgui/packages/tgui/backend.ts | 4 - tgui/packages/tgui/components/Autofocus.tsx | 18 +- tgui/packages/tgui/components/Chart.tsx | 17 +- tgui/packages/tgui/components/DmIcon.tsx | 72 ++ tgui/packages/tgui/components/Grid.jsx | 33 - tgui/packages/tgui/components/Grid.tsx | 44 + tgui/packages/tgui/components/Image.tsx | 43 +- tgui/packages/tgui/components/Table.tsx | 2 + tgui/packages/tgui/components/index.ts | 1 + tgui/packages/tgui/debug/KitchenSink.jsx | 9 +- tgui/packages/tgui/drag.ts | 14 +- .../tgui/interfaces/AirlockElectronics.tsx | 266 ++--- tgui/packages/tgui/interfaces/ApcControl.jsx | 38 +- .../tgui/interfaces/AtmosControlPanel.jsx | 9 +- .../tgui/interfaces/BluespaceSender.tsx | 9 +- .../tgui/interfaces/BluespaceVendor.tsx | 9 +- .../tgui/interfaces/CameraConsole.tsx | 21 +- tgui/packages/tgui/interfaces/Cargo.jsx | 12 +- .../tgui/interfaces/CircuitAccessChecker.tsx | 8 +- .../tgui/interfaces/ClockworkSlab.jsx | 16 +- .../tgui/interfaces/CommunicationsConsole.jsx | 10 +- tgui/packages/tgui/interfaces/CrewConsole.jsx | 2 +- .../tgui/interfaces/DestinationTagger.tsx | 19 +- .../DnaConsole/DnaConsoleStorage.jsx | 2 +- .../interfaces/DnaConsole/MutationInfo.jsx | 9 +- .../interfaces/{DnaVault.jsx => DnaVault.tsx} | 58 +- .../tgui/interfaces/EightBallVote.tsx | 35 +- ...onsole.jsx => EmergencyShuttleConsole.tsx} | 82 +- .../tgui/interfaces/ExperimentConfigure.jsx | 2 +- .../interfaces/Fabrication/DesignBrowser.tsx | 14 +- .../Fabrication/MaterialAccessBar.tsx | 2 +- tgui/packages/tgui/interfaces/Fax.tsx | 3 +- tgui/packages/tgui/interfaces/Filteriffic.jsx | 14 +- tgui/packages/tgui/interfaces/FishCatalog.tsx | 5 +- tgui/packages/tgui/interfaces/Gps.jsx | 44 +- .../tgui/interfaces/Hypertorus/Gases.tsx | 9 +- tgui/packages/tgui/interfaces/Jukebox.tsx | 3 +- .../tgui/interfaces/LaborClaimConsole.jsx | 27 +- .../tgui/interfaces/LaunchpadConsole.jsx | 276 ----- .../tgui/interfaces/LaunchpadConsole.tsx | 259 +++++ .../packages/tgui/interfaces/LibraryAdmin.tsx | 27 +- .../tgui/interfaces/LibraryConsole.jsx | 25 +- .../tgui/interfaces/LibraryVisitor.jsx | 9 +- .../interfaces/LootPanel/GroupedContents.tsx | 47 + .../tgui/interfaces/LootPanel/IconDisplay.tsx | 24 + .../tgui/interfaces/LootPanel/LootBox.tsx | 53 + .../tgui/interfaces/LootPanel/RawContents.tsx | 28 + .../tgui/interfaces/LootPanel/index.tsx | 76 ++ .../tgui/interfaces/LootPanel/types.ts | 13 + tgui/packages/tgui/interfaces/MatMarket.tsx | 2 +- tgui/packages/tgui/interfaces/Mecha/data.ts | 4 +- .../interfaces/MedicalRecords/RecordTabs.tsx | 9 +- tgui/packages/tgui/interfaces/NtosArcade.jsx | 135 --- tgui/packages/tgui/interfaces/NtosArcade.tsx | 178 ++++ .../tgui/interfaces/NtosCrewManifest.jsx | 4 +- .../tgui/interfaces/NtosEmojipedia.tsx | 20 +- .../tgui/interfaces/NtosMessenger/index.tsx | 3 +- .../tgui/interfaces/NtosNetDownloader.tsx | 23 +- .../{NuclearBomb.jsx => NuclearBomb.tsx} | 99 +- .../packages/tgui/interfaces/Orbit/helpers.ts | 23 +- tgui/packages/tgui/interfaces/Orbit/index.tsx | 12 +- .../tgui/interfaces/PersonalCrafting.tsx | 65 +- tgui/packages/tgui/interfaces/Photocopier.jsx | 2 +- .../tgui/interfaces/PlaneMasterDebug.tsx | 21 +- .../tgui/interfaces/PortableChemMixer.tsx | 3 +- .../packages/tgui/interfaces/PowerMonitor.jsx | 26 +- .../interfaces/PreferencesMenu/AntagsPage.tsx | 7 +- .../PreferencesMenu/GamePreferencesPage.tsx | 10 +- .../interfaces/PreferencesMenu/JobsPage.tsx | 5 +- .../PreferencesMenu/KeybindingsPage.tsx | 21 +- .../interfaces/PreferencesMenu/MainPage.tsx | 53 +- .../interfaces/PreferencesMenu/QuirksPage.tsx | 12 +- .../tgui/interfaces/PreferencesMenu/names.tsx | 6 +- .../preferences/features/base.tsx | 9 +- .../character_preferences/skin_tone.tsx | 5 +- .../features/game_preferences/ghost.tsx | 7 +- tgui/packages/tgui/interfaces/Radio.jsx | 4 +- .../RequestsConsole/MessageWriteTab.tsx | 8 +- .../tgui/interfaces/RestockTracker.jsx | 3 +- tgui/packages/tgui/interfaces/Roulette.jsx | 313 ------ .../tgui/interfaces/Roulette/BetTable.tsx | 171 ++++ .../tgui/interfaces/Roulette/Board.tsx | 107 ++ .../tgui/interfaces/Roulette/NumberCell.tsx | 48 + .../tgui/interfaces/Roulette/helpers.tsx | 9 + .../tgui/interfaces/Roulette/index.tsx | 14 + .../interfaces/SecurityRecords/RecordTabs.tsx | 9 +- .../tgui/interfaces/SeedExtractor.tsx | 8 +- .../tgui/interfaces/SelectEquipment.jsx | 21 +- .../tgui/interfaces/ShuttleManipulator.jsx | 4 +- .../tgui/interfaces/StackCrafting.tsx | 40 +- .../tgui/interfaces/StationAlertConsole.jsx | 4 +- .../tgui/interfaces/StationTraitsPanel.tsx | 14 +- tgui/packages/tgui/interfaces/Supermatter.tsx | 13 +- .../tgui/interfaces/SurgeryInitiator.tsx | 3 +- .../tgui/interfaces/SyndContractor.jsx | 13 +- .../tgui/interfaces/SyndicateContractor.tsx | 198 ++-- tgui/packages/tgui/interfaces/Techweb.jsx | 9 +- .../tgui/interfaces/TrackedPlaytime.jsx | 2 +- .../tgui/interfaces/WarrantConsole.tsx | 2 +- .../tgui/interfaces/common/AccessConfig.jsx | 136 --- .../tgui/interfaces/common/AccessConfig.tsx | 204 ++++ .../tgui/interfaces/common/AccessList.jsx | 3 +- tgui/packages/tgui/layouts/Layout.tsx | 21 +- tgui/packages/tgui/layouts/Pane.tsx | 8 +- tgui/packages/tgui/layouts/Window.tsx | 16 +- tgui/packages/tgui/routes.tsx | 7 +- .../tgui/styles/components/SearchItem.scss | 22 + tgui/packages/tgui/styles/main.scss | 1 + tools/test_merge_bot/main.js | 46 +- 549 files changed, 6500 insertions(+), 5378 deletions(-) create mode 100644 code/__DEFINES/click.dm create mode 100644 code/_onclick/click_alt.dm delete mode 100644 code/controllers/subsystem/processing/obj_tab_items.dm create mode 100644 code/datums/components/martial_art_giver.dm create mode 100644 code/datums/elements/attack_zone_randomiser.dm create mode 100644 code/datums/elements/living_limb_initialiser.dm create mode 100644 code/datums/elements/poster_tearer.dm create mode 100644 code/datums/elements/tool_renaming.dm create mode 100644 code/datums/quirks/positive_quirks/strong_stomach.dm create mode 100644 code/game/objects/items_reskin.dm create mode 100644 code/game/objects/structures/lavaland/gulag_vent.dm create mode 100644 code/modules/asset_cache/assets/icon_ref_map.dm create mode 100644 code/modules/lootpanel/_lootpanel.dm create mode 100644 code/modules/lootpanel/contents.dm create mode 100644 code/modules/lootpanel/handlers.dm create mode 100644 code/modules/lootpanel/misc.dm create mode 100644 code/modules/lootpanel/search_object.dm create mode 100644 code/modules/lootpanel/ss_looting.dm create mode 100644 code/modules/lootpanel/ui.dm create mode 100644 code/modules/unit_tests/combat_welder.dm create mode 100644 code/modules/unit_tests/lootpanel.dm create mode 100644 sound/effects/nightmare_poof.ogg create mode 100644 sound/effects/nightmare_reappear.ogg delete mode 100644 tgui/packages/common/vector.js create mode 100644 tgui/packages/common/vector.ts create mode 100644 tgui/packages/tgui/components/DmIcon.tsx delete mode 100644 tgui/packages/tgui/components/Grid.jsx create mode 100644 tgui/packages/tgui/components/Grid.tsx rename tgui/packages/tgui/interfaces/{DnaVault.jsx => DnaVault.tsx} (75%) rename tgui/packages/tgui/interfaces/{EmergencyShuttleConsole.jsx => EmergencyShuttleConsole.tsx} (73%) delete mode 100644 tgui/packages/tgui/interfaces/LaunchpadConsole.jsx create mode 100644 tgui/packages/tgui/interfaces/LaunchpadConsole.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/GroupedContents.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/RawContents.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/index.tsx create mode 100644 tgui/packages/tgui/interfaces/LootPanel/types.ts delete mode 100644 tgui/packages/tgui/interfaces/NtosArcade.jsx create mode 100644 tgui/packages/tgui/interfaces/NtosArcade.tsx rename tgui/packages/tgui/interfaces/{NuclearBomb.jsx => NuclearBomb.tsx} (64%) delete mode 100644 tgui/packages/tgui/interfaces/Roulette.jsx create mode 100644 tgui/packages/tgui/interfaces/Roulette/BetTable.tsx create mode 100644 tgui/packages/tgui/interfaces/Roulette/Board.tsx create mode 100644 tgui/packages/tgui/interfaces/Roulette/NumberCell.tsx create mode 100644 tgui/packages/tgui/interfaces/Roulette/helpers.tsx create mode 100644 tgui/packages/tgui/interfaces/Roulette/index.tsx delete mode 100644 tgui/packages/tgui/interfaces/common/AccessConfig.jsx create mode 100644 tgui/packages/tgui/interfaces/common/AccessConfig.tsx create mode 100644 tgui/packages/tgui/styles/components/SearchItem.scss diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index d9fb22def8f..eec9705fcf4 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -2799,6 +2799,12 @@ }, /turf/open/floor/circuit/green, /area/station/science/robotics/mechbay) +"boT" = ( +/obj/effect/turf_decal/bot, +/obj/machinery/light/cold/dim/directional/west, +/obj/machinery/vending/wardrobe/engi_wardrobe, +/turf/open/floor/iron, +/area/station/engineering/main) "boW" = ( /obj/structure/table/glass, /obj/machinery/status_display/ai/directional/south, @@ -10007,18 +10013,6 @@ /obj/effect/turf_decal/siding/wood, /turf/open/floor/wood/tile, /area/station/science/lower) -"erJ" = ( -/obj/effect/turf_decal/siding/wood{ - dir = 1 - }, -/obj/structure/table/greyscale, -/obj/item/folder/yellow{ - pixel_x = 4; - pixel_y = 4 - }, -/obj/item/clothing/glasses/meson, -/turf/open/floor/iron/grimy, -/area/station/engineering/main) "erK" = ( /obj/structure/cable, /obj/effect/turf_decal/siding/thinplating_new, @@ -11513,13 +11507,6 @@ /obj/effect/turf_decal/tile/neutral, /turf/open/floor/iron, /area/station/hallway/primary/fore) -"eWf" = ( -/obj/structure/closet/cardboard, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/turf/open/floor/iron/grimy, -/area/station/engineering/main) "eWj" = ( /obj/machinery/atmospherics/components/unary/thermomachine/heater/on{ dir = 4; @@ -11999,17 +11986,6 @@ /obj/machinery/light_switch/directional/west, /turf/open/floor/iron/cafeteria, /area/station/service/cafeteria) -"fhX" = ( -/obj/structure/table/greyscale, -/obj/item/folder/yellow{ - pixel_x = 2; - pixel_y = 4 - }, -/obj/item/pen{ - pixel_x = -4 - }, -/turf/open/floor/iron/grimy, -/area/station/engineering/main) "fib" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -31165,6 +31141,18 @@ }, /turf/open/floor/iron/white, /area/station/medical/medbay/lobby) +"lwl" = ( +/obj/effect/gibspawner/human, +/obj/structure/table/optable{ + desc = "A cold, hard place for your final rest."; + name = "Morgue Slab" + }, +/mob/living/carbon/human/species/monkey/humand_legged{ + name = "Charles"; + real_name = "Charles" + }, +/turf/open/floor/iron/white/diagonal, +/area/station/maintenance/department/science/xenobiology) "lwn" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/door/airlock/hatch, @@ -33051,6 +33039,14 @@ }, /turf/open/floor/iron, /area/station/cargo/storage) +"lWK" = ( +/obj/structure/closet/cardboard, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/obj/item/airlock_painter, +/turf/open/floor/iron/grimy, +/area/station/engineering/main) "lWQ" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/ticket_machine/directional/north, @@ -36959,31 +36955,6 @@ /obj/effect/mapping_helpers/airlock/access/all/security/general, /turf/open/floor/iron/textured_half, /area/station/security) -"nki" = ( -/obj/structure/rack, -/obj/item/airlock_painter, -/obj/item/lightreplacer{ - pixel_y = 7 - }, -/obj/item/clothing/head/utility/hardhat/welding{ - pixel_x = -4; - pixel_y = -3 - }, -/obj/item/clothing/head/utility/hardhat/welding{ - pixel_x = -4 - }, -/obj/item/clothing/head/utility/hardhat/welding{ - pixel_x = -4; - pixel_y = 3 - }, -/obj/effect/turf_decal/bot, -/obj/machinery/light/cold/dim/directional/west, -/obj/item/grenade/chem_grenade/smart_metal_foam{ - pixel_x = 9; - pixel_y = 5 - }, -/turf/open/floor/iron, -/area/station/engineering/main) "nkl" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment{ @@ -45700,10 +45671,6 @@ }, /turf/open/floor/iron/dark/side, /area/station/science/xenobiology) -"pRb" = ( -/obj/item/computer_disk/virus/mime, -/turf/closed/mineral/random/stationside, -/area/space/nearstation) "pRe" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/structure/table/wood, @@ -60444,18 +60411,6 @@ /obj/effect/spawner/random/maintenance, /turf/open/floor/iron, /area/station/security/execution/transfer) -"tUa" = ( -/obj/effect/gibspawner/human, -/obj/structure/table/optable{ - desc = "A cold, hard place for your final rest."; - name = "Morgue Slab" - }, -/mob/living/carbon/human/species/monkey/humand_legged{ - name = "Charles"; - real_name = "Charles" - }, -/turf/open/floor/iron/white/diagonal, -/area/station/maintenance/department/science/xenobiology) "tUc" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -68928,6 +68883,17 @@ }, /turf/open/space/basic, /area/station/engineering/atmos/space_catwalk) +"wlW" = ( +/obj/structure/table/greyscale, +/obj/item/lightreplacer{ + pixel_y = 7 + }, +/obj/item/lightreplacer{ + pixel_y = 2; + pixel_x = -2 + }, +/turf/open/floor/iron/grimy, +/area/station/engineering/main) "wmd" = ( /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible, /obj/machinery/atmospherics/pipe/smart/simple/cyan/visible/layer2{ @@ -70351,8 +70317,8 @@ /turf/open/floor/iron/white, /area/station/medical/medbay/central) "wHO" = ( -/obj/structure/dresser, /obj/structure/sign/poster/official/random/directional/north, +/obj/structure/closet/crate/wooden/toy, /turf/open/floor/wood/parquet, /area/station/service/greenroom) "wHP" = ( @@ -74918,6 +74884,22 @@ }, /turf/open/floor/plating, /area/station/service/abandoned_gambling_den/gaming) +"xMU" = ( +/obj/effect/turf_decal/siding/wood{ + dir = 1 + }, +/obj/structure/table/greyscale, +/obj/item/folder/yellow{ + pixel_x = 4; + pixel_y = 4 + }, +/obj/item/clothing/glasses/meson, +/obj/item/grenade/chem_grenade/smart_metal_foam{ + pixel_x = -8; + pixel_y = 14 + }, +/turf/open/floor/iron/grimy, +/area/station/engineering/main) "xMY" = ( /turf/closed/wall/r_wall, /area/station/command/teleporter) @@ -88018,7 +88000,7 @@ aJq aJq aJq aJq -pRb +aJq dDB dDB dDB @@ -88275,7 +88257,7 @@ aJq aJq aJq aJq -pRb +aJq aJq aJq aJq @@ -88532,7 +88514,7 @@ aJq aJq aJq aJq -pRb +aJq aJq aJq aJq @@ -92010,7 +91992,7 @@ dgV sHH rQi rPV -nki +boT uOk nmH sHO @@ -92275,7 +92257,7 @@ dgm wIu oHO sDo -fhX +wlW lsd jJu kya @@ -92786,11 +92768,11 @@ rmM btV lMl ayR -erJ +xMU vDg euP all -eWf +lWK eIx kWg bNq @@ -113439,7 +113421,7 @@ dDB blb dDB ldq -tUa +lwl vXn ldq ldq diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index e81e9481cf7..e66eb438644 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -562,16 +562,6 @@ /obj/structure/chair/stool/directional/east, /turf/open/floor/iron, /area/station/commons/dorms) -"akG" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 5 - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/obj/structure/tank_holder/extinguisher, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "akK" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -4069,15 +4059,6 @@ }, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/surface/outdoors/nospawn) -"bmZ" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "bna" = ( /obj/effect/turf_decal/stripes/line, /obj/effect/turf_decal/stripes/line{ @@ -4507,13 +4488,6 @@ /obj/effect/decal/cleanable/cobweb/cobweb2, /turf/open/floor/iron/dark, /area/station/maintenance/port/greater) -"bsR" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/effect/landmark/event_spawn, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "bta" = ( /obj/structure/chair/sofa/bench/right{ dir = 8 @@ -5474,6 +5448,17 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/exit/departure_lounge) +"bFw" = ( +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/structure/table, +/obj/item/pen{ + pixel_x = -5 + }, +/obj/item/paper_bin, +/turf/open/floor/plating, +/area/station/hallway/secondary/service) "bFS" = ( /obj/item/crowbar/red, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -5682,6 +5667,15 @@ /obj/effect/turf_decal/tile/blue/half/contrasted, /turf/open/floor/iron, /area/station/command/bridge) +"bIQ" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 4 + }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "bIU" = ( /obj/structure/table, /obj/item/stack/sheet/glass/fifty, @@ -5756,6 +5750,37 @@ /obj/machinery/light/directional/south, /turf/open/floor/iron, /area/mine/laborcamp) +"bJy" = ( +/obj/machinery/button/flasher{ + id = "hopflash"; + pixel_x = 8; + pixel_y = -32 + }, +/obj/machinery/button/door/directional/south{ + id = "hopqueue"; + name = "Queue Shutters Control"; + pixel_x = -8; + req_access = list("hop") + }, +/obj/machinery/button/door/directional/south{ + id = "hop"; + name = "Privacy Shutters Control"; + pixel_x = 8; + req_access = list("hop") + }, +/obj/machinery/button/ticket_machine{ + pixel_x = -8; + pixel_y = -32 + }, +/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ + dir = 8 + }, +/obj/machinery/modular_computer/preset/id{ + dir = 1 + }, +/obj/item/paper/fluff/ids_for_dummies, +/turf/open/floor/iron, +/area/station/command/heads_quarters/hop) "bJA" = ( /obj/effect/turf_decal/siding/wood{ dir = 1 @@ -7593,17 +7618,6 @@ "clq" = ( /turf/open/floor/iron/dark, /area/station/security/processing) -"clr" = ( -/obj/machinery/power/smes{ - capacity = 1.8e+008; - charge = 2e+005 - }, -/obj/effect/decal/cleanable/cobweb/cobweb2, -/obj/effect/decal/cleanable/dirt, -/obj/structure/cable, -/obj/effect/turf_decal/stripes/box, -/turf/open/floor/iron/dark/textured_large, -/area/station/maintenance/disposal/incinerator) "clz" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/stripes/line{ @@ -8029,6 +8043,17 @@ /obj/item/radio/intercom/directional/east, /turf/open/floor/iron/white, /area/station/science/genetics) +"crH" = ( +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/obj/machinery/light/small/directional/west, +/obj/machinery/requests_console/directional/west, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/mapping_helpers/requests_console/announcement, +/obj/effect/mapping_helpers/requests_console/information, +/turf/open/floor/iron, +/area/station/command/heads_quarters/hop) "crS" = ( /obj/machinery/vending/wardrobe/law_wardrobe, /turf/open/floor/wood, @@ -8154,6 +8179,12 @@ /obj/structure/tank_holder/anesthetic, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) +"cua" = ( +/obj/machinery/button/photobooth{ + pixel_y = -26 + }, +/turf/open/floor/iron, +/area/station/command/heads_quarters/hop) "cuc" = ( /obj/effect/turf_decal/siding/thinplating_new, /turf/open/floor/iron, @@ -8528,6 +8559,11 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/plating, /area/station/ai_monitored/turret_protected/aisat_interior) +"czl" = ( +/obj/effect/turf_decal/tile/brown/fourcorners, +/obj/machinery/modular_computer/preset/cargochat/engineering, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "czm" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -9857,6 +9893,13 @@ initial_gas_mix = "ICEMOON_ATMOS" }, /area/icemoon/underground/explored) +"cSm" = ( +/obj/machinery/mineral/processing_unit/gulag{ + dir = 1 + }, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/mine/laborcamp) "cSu" = ( /obj/structure/disposalpipe/segment{ dir = 10 @@ -12609,7 +12652,7 @@ dir = 8 }, /turf/open/floor/engine, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "dJx" = ( /obj/structure/cable, /obj/effect/spawner/structure/window/reinforced, @@ -12852,16 +12895,6 @@ }, /turf/open/floor/iron, /area/station/security/brig/upper) -"dNv" = ( -/obj/machinery/modular_computer/preset/cargochat/security{ - dir = 4 - }, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/structure/cable, -/obj/machinery/light/directional/west, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "dNw" = ( /obj/structure/chair/office{ dir = 8 @@ -13269,6 +13302,18 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron, /area/station/commons/locker) +"dVc" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 4 + }, +/obj/structure/disposalpipe/segment{ + dir = 5 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "dVq" = ( /obj/machinery/space_heater, /obj/structure/sign/poster/random/directional/east, @@ -13891,9 +13936,6 @@ /obj/structure/cable, /turf/open/floor/iron/white, /area/station/science/xenobiology) -"egf" = ( -/turf/closed/wall/r_wall, -/area/station/science/ordnance/burnchamber) "egj" = ( /obj/structure/rack, /obj/machinery/light/small/directional/north, @@ -15068,6 +15110,18 @@ /obj/item/poster/random_official, /turf/open/floor/plating, /area/station/maintenance/port/fore) +"ezs" = ( +/obj/item/radio/intercom/directional/north, +/obj/effect/turf_decal/tile/red/anticorner{ + dir = 8 + }, +/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ + dir = 4 + }, +/turf/open/floor/iron/dark/textured_corner{ + dir = 4 + }, +/area/station/security/office) "ezu" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -15364,6 +15418,18 @@ }, /turf/open/floor/engine, /area/station/engineering/supermatter/room) +"eDH" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/structure/cable, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 4 + }, +/obj/structure/disposalpipe/junction{ + dir = 1 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "eDM" = ( /obj/machinery/door/airlock/command/glass{ name = "Head of Security" @@ -16298,18 +16364,6 @@ /obj/machinery/light/small/directional/north, /turf/open/floor/iron/dark, /area/station/security/execution/education) -"eUH" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 4 - }, -/obj/structure/disposalpipe/junction{ - dir = 1 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "eUI" = ( /obj/machinery/space_heater, /turf/open/floor/plating, @@ -16552,16 +16606,14 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/iron/white, /area/station/medical/medbay/central) -"eYz" = ( -/obj/machinery/mineral/processing_unit{ - dir = 1 - }, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/mine/laborcamp) "eYC" = ( /turf/open/floor/iron/smooth, /area/mine/laborcamp/security) +"eYH" = ( +/obj/machinery/power/smes/full, +/obj/structure/cable, +/turf/open/floor/circuit, +/area/station/ai_monitored/turret_protected/ai) "eYL" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/closed/wall, @@ -17877,6 +17929,19 @@ /obj/machinery/light_switch/directional/west, /turf/open/floor/iron, /area/station/cargo/miningdock) +"fvA" = ( +/obj/machinery/computer/security/telescreen/entertainment/directional/north, +/obj/effect/turf_decal/siding/yellow/corner{ + dir = 4 + }, +/obj/structure/table, +/obj/machinery/fax{ + fax_name = "Engineering Lobby"; + name = "Engineering Lobby Fax Machine" + }, +/obj/effect/turf_decal/tile/brown/fourcorners, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "fvK" = ( /obj/structure/rack, /obj/item/storage/box/petridish, @@ -18453,15 +18518,6 @@ }, /turf/open/floor/iron, /area/mine/laborcamp/security) -"fEY" = ( -/obj/machinery/modular_computer/preset/cargochat/medical{ - dir = 1 - }, -/obj/effect/turf_decal/trimline/brown/filled/end{ - dir = 1 - }, -/turf/open/floor/iron, -/area/station/medical/medbay/aft) "fEZ" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -19266,7 +19322,7 @@ "fSG" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/closed/wall/r_wall, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "fTb" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -20385,18 +20441,6 @@ "gka" = ( /turf/closed/wall/r_wall, /area/station/engineering/supermatter/room) -"gkf" = ( -/obj/structure/window/reinforced/spawner/directional/west, -/obj/machinery/disposal/bin{ - desc = "A pneumatic waste disposal unit. This one leads to the morgue."; - name = "corpse disposal" - }, -/obj/structure/disposalpipe/trunk{ - dir = 4 - }, -/obj/effect/turf_decal/tile/blue/fourcorners, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "gko" = ( /obj/machinery/conveyor{ dir = 8; @@ -20659,17 +20703,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold/yellow/visible, /turf/open/floor/iron, /area/station/engineering/atmos) -"gnA" = ( -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 - }, -/obj/machinery/light/small/directional/west, -/obj/machinery/requests_console/directional/west, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/mapping_helpers/requests_console/announcement, -/obj/effect/mapping_helpers/requests_console/information, -/turf/open/floor/iron, -/area/station/command/heads_quarters/hop) "gnL" = ( /obj/structure/closet/bombcloset/security, /turf/open/floor/iron/smooth, @@ -20936,6 +20969,10 @@ }, /turf/open/floor/iron, /area/station/security/prison/visit) +"grY" = ( +/obj/structure/gulag_vent/ice, +/turf/open/misc/asteroid/snow/icemoon, +/area/icemoon/underground/explored) "gsk" = ( /obj/structure/reflector/single/anchored{ dir = 5 @@ -22526,11 +22563,6 @@ /obj/machinery/light/directional/west, /turf/open/floor/plating, /area/station/cargo/storage) -"gTf" = ( -/obj/effect/turf_decal/tile/brown/fourcorners, -/obj/machinery/photocopier, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "gTi" = ( /obj/machinery/door/airlock/medical/glass{ name = "Medbay Chemistry Access" @@ -22742,7 +22774,7 @@ "gWZ" = ( /obj/machinery/atmospherics/components/unary/outlet_injector/monitored/ordnance_burn_chamber_input, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "gXe" = ( /obj/effect/turf_decal/siding/white{ dir = 4 @@ -22875,11 +22907,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/station/maintenance/port/fore) -"gYP" = ( -/obj/machinery/power/smes/full, -/obj/structure/cable, -/turf/open/floor/circuit/telecomms/mainframe, -/area/station/tcommsat/server) "gZa" = ( /obj/structure/sign/nanotrasen{ pixel_x = -32 @@ -23163,6 +23190,14 @@ /obj/effect/mapping_helpers/airlock/access/all/command/general, /turf/open/floor/plating, /area/station/engineering/storage/tech) +"hdn" = ( +/obj/structure/cable, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "hdp" = ( /obj/effect/turf_decal/stripes/asteroid/line{ dir = 10 @@ -23317,15 +23352,6 @@ }, /turf/open/floor/iron/showroomfloor, /area/station/security/prison/mess) -"hfw" = ( -/obj/structure/cable, -/obj/machinery/power/smes/full, -/obj/machinery/camera/directional/south{ - c_tag = "Labor Camp Utilities"; - network = list("labor") - }, -/turf/open/floor/iron/smooth, -/area/mine/laborcamp/security) "hfA" = ( /obj/structure/window/reinforced/spawner/directional/north{ pixel_y = 2 @@ -23428,6 +23454,13 @@ dir = 4 }, /area/station/command/gateway) +"hhP" = ( +/obj/machinery/newscaster/directional/south, +/obj/item/kirbyplants/random, +/turf/open/floor/iron/white/side{ + dir = 5 + }, +/area/station/science/lab) "hhT" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/effect/turf_decal/trimline/yellow/filled/line{ @@ -23454,19 +23487,6 @@ /obj/structure/reagent_dispensers/fueltank, /turf/open/floor/plating, /area/station/maintenance/aft/greater) -"hio" = ( -/obj/machinery/computer/security/telescreen/entertainment/directional/north, -/obj/effect/turf_decal/siding/yellow/corner{ - dir = 4 - }, -/obj/structure/table, -/obj/machinery/fax{ - fax_name = "Engineering Lobby"; - name = "Engineering Lobby Fax Machine" - }, -/obj/effect/turf_decal/tile/brown/fourcorners, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "hjh" = ( /obj/machinery/computer/records/security{ dir = 4 @@ -23540,6 +23560,21 @@ }, /turf/open/floor/plating, /area/station/medical/virology) +"hjX" = ( +/obj/machinery/requests_console/directional/north{ + department = "Security"; + name = "Security Requests Console" + }, +/obj/effect/mapping_helpers/requests_console/information, +/obj/effect/mapping_helpers/requests_console/assistance, +/obj/effect/turf_decal/tile/red/anticorner, +/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ + dir = 8 + }, +/turf/open/floor/iron/dark/textured_corner{ + dir = 1 + }, +/area/station/security/office) "hkd" = ( /obj/structure/cable, /obj/machinery/light/small/directional/west, @@ -23803,16 +23838,6 @@ }, /turf/open/floor/iron, /area/station/commons/dorms/laundry) -"hpc" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "hpd" = ( /turf/open/floor/plating, /area/station/engineering/engine_smes) @@ -24914,18 +24939,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/command/teleporter) -"hGj" = ( -/obj/item/radio/intercom/directional/north, -/obj/effect/turf_decal/tile/red/anticorner{ - dir = 8 - }, -/obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ - dir = 4 - }, -/turf/open/floor/iron/dark/textured_corner{ - dir = 4 - }, -/area/station/security/office) "hGs" = ( /obj/structure/lattice/catwalk, /obj/structure/railing{ @@ -25050,10 +25063,6 @@ /obj/effect/mapping_helpers/airlock/access/all/engineering/maintenance, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"hIK" = ( -/mob/living/basic/slime, -/turf/open/floor/engine, -/area/station/science/xenobiology) "hIN" = ( /obj/machinery/light/small/directional/west, /turf/open/floor/iron/dark, @@ -25146,7 +25155,7 @@ dir = 4 }, /turf/open/floor/engine, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "hKr" = ( /obj/structure/table/glass, /obj/item/book/manual/wiki/infections{ @@ -26008,12 +26017,6 @@ }, /turf/open/floor/plating/snowed/icemoon, /area/icemoon/underground/explored) -"hYW" = ( -/obj/effect/turf_decal/tile/red{ - dir = 4 - }, -/turf/open/floor/iron, -/area/station/hallway/primary/central) "hZe" = ( /obj/effect/turf_decal/trimline/yellow/warning{ dir = 1 @@ -26040,7 +26043,7 @@ "iao" = ( /obj/machinery/atmospherics/pipe/smart/simple/scrubbers/visible, /turf/closed/wall/r_wall, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "iar" = ( /obj/structure/cable, /obj/machinery/door/poddoor/preopen{ @@ -26070,15 +26073,6 @@ /obj/effect/turf_decal/delivery, /turf/open/floor/iron, /area/station/cargo/storage) -"iaS" = ( -/obj/effect/turf_decal/tile/blue/half/contrasted{ - dir = 8 - }, -/obj/structure/chair/office{ - dir = 8 - }, -/turf/open/floor/iron, -/area/station/command/heads_quarters/hop) "iaT" = ( /obj/effect/spawner/random/trash/mess, /turf/open/floor/plating, @@ -26654,17 +26648,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/maintenance/starboard/fore) -"ijf" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment, -/obj/effect/turf_decal/box/white{ - color = "#52B4E9" - }, -/obj/machinery/holopad, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "iji" = ( /obj/structure/table, /obj/item/multitool/circuit{ @@ -26947,7 +26930,7 @@ "inb" = ( /obj/machinery/door/poddoor/incinerator_ordmix, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "inh" = ( /obj/structure/stairs/west, /obj/structure/railing, @@ -27450,18 +27433,6 @@ }, /turf/open/floor/iron/cafeteria, /area/station/commons/dorms/laundry) -"iuK" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/trimline/blue/filled/corner{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "iuS" = ( /obj/machinery/airalarm/directional/north, /turf/open/floor/glass/reinforced, @@ -27698,7 +27669,7 @@ dir = 8 }, /turf/open/floor/engine, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "izn" = ( /obj/effect/spawner/random/decoration/generic, /turf/open/floor/plating, @@ -31798,6 +31769,16 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/maintenance/fore) +"jOS" = ( +/obj/machinery/airalarm/directional/north, +/obj/structure/table, +/obj/machinery/fax{ + fax_name = "Security Office"; + name = "Security Office Fax Machine" + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "jOY" = ( /obj/effect/turf_decal/stripes/line, /obj/structure/reagent_dispensers/plumbed{ @@ -32604,16 +32585,6 @@ }, /turf/open/floor/iron, /area/mine/laborcamp) -"kbz" = ( -/obj/machinery/airalarm/directional/north, -/obj/structure/table, -/obj/machinery/fax{ - fax_name = "Security Office"; - name = "Security Office Fax Machine" - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "kbJ" = ( /obj/machinery/field/generator, /turf/open/floor/plating, @@ -32861,7 +32832,7 @@ /area/station/maintenance/port/fore) "keV" = ( /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "keX" = ( /obj/structure/table, /obj/item/stack/cable_coil{ @@ -33094,15 +33065,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron, /area/station/construction) -"khv" = ( -/obj/structure/cable, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "khy" = ( /obj/structure/rack, /obj/item/wrench, @@ -34601,6 +34563,11 @@ }, /turf/open/floor/iron/dark, /area/station/science/breakroom) +"kCG" = ( +/obj/effect/turf_decal/tile/brown/fourcorners, +/obj/machinery/photocopier, +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "kCH" = ( /obj/machinery/door/airlock/security/glass{ name = "Labor Camp Airlock" @@ -35849,6 +35816,15 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron, /area/station/engineering/lobby) +"kVS" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "kWa" = ( /obj/structure/fireplace, /turf/open/floor/plating, @@ -38325,6 +38301,16 @@ }, /turf/open/floor/iron, /area/station/hallway/primary/central) +"lIt" = ( +/obj/machinery/modular_computer/preset/cargochat/security{ + dir = 4 + }, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/structure/cable, +/obj/machinery/light/directional/west, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "lIy" = ( /obj/structure/table, /obj/effect/turf_decal/tile/red/anticorner/contrasted{ @@ -39224,19 +39210,6 @@ }, /turf/open/lava/plasma/ice_moon, /area/icemoon/underground/explored) -"lWS" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/structure/cable, -/obj/structure/disposalpipe/segment, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "lXi" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -40253,6 +40226,18 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/command/bridge) +"mpR" = ( +/obj/structure/window/reinforced/spawner/directional/west, +/obj/machinery/disposal/bin{ + desc = "A pneumatic waste disposal unit. This one leads to the morgue."; + name = "corpse disposal" + }, +/obj/structure/disposalpipe/trunk{ + dir = 4 + }, +/obj/effect/turf_decal/tile/blue/fourcorners, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "mpU" = ( /obj/effect/spawner/structure/window, /turf/open/floor/plating, @@ -41354,7 +41339,7 @@ /obj/effect/mapping_helpers/airlock/locked, /obj/effect/mapping_helpers/airlock/access/all/science/ordnance, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "mIE" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -41534,11 +41519,6 @@ /obj/effect/spawner/structure/window/reinforced, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"mMf" = ( -/obj/machinery/firealarm/directional/east, -/obj/structure/filingcabinet, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "mMk" = ( /obj/machinery/telecomms/message_server/preset, /turf/open/floor/iron/dark/telecomms, @@ -41664,6 +41644,13 @@ /obj/effect/turf_decal/tile/blue/half/contrasted, /turf/open/floor/iron, /area/station/command/bridge) +"mOM" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/effect/landmark/event_spawn, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "mPh" = ( /obj/structure/rack, /obj/item/crowbar, @@ -42207,7 +42194,7 @@ "mYd" = ( /obj/machinery/air_sensor/ordnance_burn_chamber, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "mYh" = ( /turf/open/floor/iron/dark, /area/station/ai_monitored/turret_protected/ai) @@ -42467,6 +42454,16 @@ /obj/item/radio/intercom/directional/west, /turf/open/floor/carpet/royalblue, /area/station/command/heads_quarters/hos) +"nbw" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 4 + }, +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "nbC" = ( /obj/machinery/smartfridge/chemistry/preloaded, /obj/machinery/door/firedoor, @@ -42798,6 +42795,12 @@ dir = 1 }, /area/station/hallway/secondary/exit/departure_lounge) +"nfD" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "nfG" = ( /obj/structure/marker_beacon/burgundy{ name = "landing marker" @@ -43576,15 +43579,6 @@ /obj/effect/turf_decal/stripes/box, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"nqt" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 10 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "nqv" = ( /obj/structure/fence{ dir = 4 @@ -44399,7 +44393,7 @@ "nBV" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "nCa" = ( /obj/structure/rack, /obj/item/pickaxe, @@ -45816,9 +45810,6 @@ /obj/machinery/door/firedoor, /turf/open/floor/iron/dark, /area/station/security/prison/visit) -"nYm" = ( -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "nYn" = ( /obj/structure/closet/secure_closet/personal/cabinet, /turf/open/floor/carpet, @@ -48893,18 +48884,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) -"oTE" = ( -/obj/structure/cable, -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 1 - }, -/obj/machinery/power/apc/auto_name/directional/north, -/obj/item/kirbyplants/random, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "oTM" = ( /obj/item/flashlight/lantern, /obj/structure/table/wood, @@ -51133,6 +51112,17 @@ "pBE" = ( /turf/closed/wall, /area/station/cargo/bitrunning/den) +"pBI" = ( +/obj/machinery/power/smes{ + capacity = 1.8e+008; + charge = 2e+005 + }, +/obj/effect/decal/cleanable/cobweb/cobweb2, +/obj/effect/decal/cleanable/dirt, +/obj/structure/cable, +/obj/effect/turf_decal/stripes/box, +/turf/open/floor/iron/dark/textured_large, +/area/station/maintenance/disposal/incinerator) "pBN" = ( /obj/structure/flora/tree/jungle/small/style_random, /turf/open/floor/grass, @@ -51159,6 +51149,11 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"pCE" = ( +/obj/machinery/firealarm/directional/east, +/obj/structure/filingcabinet, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "pCI" = ( /obj/effect/turf_decal/tile/red{ dir = 8 @@ -54068,6 +54063,17 @@ /obj/effect/landmark/start/hangover, /turf/open/floor/iron, /area/station/commons/dorms) +"qxu" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/structure/disposalpipe/segment, +/obj/effect/turf_decal/box/white{ + color = "#52B4E9" + }, +/obj/machinery/holopad, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "qxv" = ( /obj/machinery/disposal/bin{ desc = "A pneumatic waste disposal unit. This one leads to the frozen exterior of the moon."; @@ -54316,12 +54322,6 @@ }, /turf/open/floor/plating, /area/station/maintenance/starboard/upper) -"qCH" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "qCI" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -54656,16 +54656,6 @@ /obj/effect/turf_decal/bot_white, /turf/open/floor/iron/checker, /area/station/commons/storage/emergency/port) -"qHa" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 1 - }, -/obj/machinery/status_display/evac/directional/north, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "qHg" = ( /obj/structure/transit_tube/curved/flipped{ dir = 1 @@ -54914,13 +54904,6 @@ }, /turf/open/floor/iron/dark/smooth_large, /area/station/hallway/secondary/entry) -"qKN" = ( -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 4 - }, -/obj/structure/extinguisher_cabinet/directional/east, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) "qKQ" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/cable, @@ -55319,6 +55302,10 @@ /obj/machinery/door/window/left/directional/north, /turf/open/floor/iron, /area/station/science/ordnance/testlab) +"qQb" = ( +/obj/machinery/photobooth, +/turf/open/floor/iron, +/area/station/hallway/primary/central) "qQf" = ( /turf/closed/wall, /area/station/maintenance/fore/lesser) @@ -56973,6 +56960,18 @@ dir = 1 }, /area/station/science/explab) +"rqd" = ( +/obj/structure/cable, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/trimline/blue/filled/corner{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "rqi" = ( /obj/item/crowbar{ pixel_y = 3 @@ -58931,6 +58930,16 @@ }, /turf/open/floor/iron/dark, /area/station/engineering/atmos/hfr_room) +"rWR" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 1 + }, +/obj/machinery/status_display/evac/directional/north, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "rWU" = ( /obj/machinery/door/airlock/public/glass{ name = "Prison Wing" @@ -59034,37 +59043,6 @@ }, /turf/open/floor/iron/dark/smooth_large, /area/station/security/processing) -"rYk" = ( -/obj/machinery/button/flasher{ - id = "hopflash"; - pixel_x = 8; - pixel_y = -32 - }, -/obj/machinery/button/door/directional/south{ - id = "hopqueue"; - name = "Queue Shutters Control"; - pixel_x = -8; - req_access = list("hop") - }, -/obj/machinery/button/door/directional/south{ - id = "hop"; - name = "Privacy Shutters Control"; - pixel_x = 8; - req_access = list("hop") - }, -/obj/machinery/button/ticket_machine{ - pixel_x = -8; - pixel_y = -32 - }, -/obj/effect/turf_decal/tile/blue/anticorner/contrasted{ - dir = 8 - }, -/obj/machinery/modular_computer/preset/id{ - dir = 1 - }, -/obj/item/paper/fluff/ids_for_dummies, -/turf/open/floor/iron, -/area/station/command/heads_quarters/hop) "rYq" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -59186,10 +59164,6 @@ dir = 4 }, /area/station/service/chapel) -"sao" = ( -/obj/item/kirbyplants/random, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "sbc" = ( /obj/structure/table/reinforced, /obj/machinery/door/firedoor, @@ -59598,6 +59572,15 @@ }, /turf/open/floor/iron, /area/mine/production) +"shy" = ( +/obj/machinery/modular_computer/preset/cargochat/medical{ + dir = 1 + }, +/obj/effect/turf_decal/trimline/brown/filled/end{ + dir = 1 + }, +/turf/open/floor/iron, +/area/station/medical/medbay/aft) "shB" = ( /obj/effect/turf_decal/stripes/line{ dir = 8 @@ -60038,6 +60021,15 @@ /obj/machinery/microwave, /turf/open/floor/plating, /area/station/maintenance/port/aft) +"snG" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "snI" = ( /obj/structure/extinguisher_cabinet/directional/west, /obj/machinery/vending/modularpc, @@ -60951,6 +60943,14 @@ }, /turf/open/floor/plating, /area/station/maintenance/department/crew_quarters/bar) +"sAj" = ( +/obj/machinery/photocopier, +/obj/effect/turf_decal/tile/red/half/contrasted{ + dir = 8 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "sAu" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -61129,7 +61129,7 @@ "sDA" = ( /obj/machinery/igniter/incinerator_ordmix, /turf/open/floor/engine/vacuum, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "sDQ" = ( /obj/item/radio/intercom/prison/directional/north, /obj/effect/turf_decal/tile/red/half/contrasted{ @@ -62224,6 +62224,13 @@ /obj/effect/mapping_helpers/broken_floor, /turf/open/floor/wood, /area/station/maintenance/port/aft) +"sUT" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 4 + }, +/obj/structure/extinguisher_cabinet/directional/east, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "sVf" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -62376,14 +62383,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/dark, /area/station/science/explab) -"sXG" = ( -/obj/structure/cable, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 4 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "sXK" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -64194,6 +64193,16 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/plating, /area/station/maintenance/starboard/fore) +"tDA" = ( +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 5 + }, +/obj/structure/disposalpipe/segment{ + dir = 10 + }, +/obj/structure/tank_holder/extinguisher, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "tDL" = ( /obj/structure/cable, /obj/effect/turf_decal/tile/red/anticorner/contrasted{ @@ -64926,11 +64935,6 @@ /obj/structure/sign/warning/electric_shock, /turf/open/floor/plating, /area/station/science/xenobiology) -"tOg" = ( -/obj/machinery/power/smes/full, -/obj/structure/cable, -/turf/open/floor/circuit, -/area/station/ai_monitored/turret_protected/ai) "tOi" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -65191,6 +65195,26 @@ /obj/effect/spawner/random/trash/moisture_trap, /turf/open/floor/plating, /area/station/maintenance/department/chapel) +"tUz" = ( +/obj/machinery/button/door/directional/north{ + id = "permainner"; + name = "Inner Bolt Control"; + normaldoorcontrol = 1; + pixel_x = -6; + req_access = list("brig"); + specialfunctions = 4 + }, +/obj/machinery/button/door/directional/north{ + id = "permaouter"; + name = "Outer Bolt Control"; + normaldoorcontrol = 1; + pixel_x = 6; + req_access = list("brig"); + specialfunctions = 4 + }, +/obj/machinery/photobooth/security, +/turf/open/floor/iron/smooth, +/area/station/security/execution/transfer) "tUC" = ( /obj/effect/landmark/event_spawn, /obj/machinery/light/floor, @@ -65401,11 +65425,6 @@ }, /turf/open/floor/iron, /area/station/engineering/main) -"tXL" = ( -/obj/effect/turf_decal/tile/brown/fourcorners, -/obj/machinery/modular_computer/preset/cargochat/engineering, -/turf/open/floor/iron/dark, -/area/station/engineering/lobby) "tXV" = ( /obj/machinery/airalarm/directional/north, /obj/machinery/camera/directional/north{ @@ -66046,6 +66065,18 @@ /obj/effect/turf_decal/siding/thinplating/dark, /turf/open/floor/plating, /area/station/service/hydroponics) +"uiF" = ( +/obj/structure/cable, +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 1 + }, +/obj/machinery/power/apc/auto_name/directional/north, +/obj/item/kirbyplants/random, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "uiI" = ( /obj/structure/rack, /obj/effect/spawner/random/maintenance/three, @@ -66238,6 +66269,19 @@ /obj/structure/sign/warning/docking/directional/south, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) +"uml" = ( +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/effect/turf_decal/trimline/blue/filled/warning{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/structure/cable, +/obj/structure/disposalpipe/segment, +/turf/open/floor/iron/white, +/area/station/medical/medbay/aft) "umv" = ( /obj/effect/turf_decal/siding/white{ dir = 9 @@ -68603,6 +68647,20 @@ }, /turf/open/floor/engine, /area/station/engineering/atmos/hfr_room) +"vbg" = ( +/obj/structure/cable, +/obj/machinery/power/smes/full, +/obj/machinery/camera/directional/south{ + c_tag = "Labor Camp Utilities"; + network = list("labor") + }, +/turf/open/floor/iron/smooth, +/area/mine/laborcamp/security) +"vbn" = ( +/obj/machinery/power/smes/full, +/obj/structure/cable, +/turf/open/floor/circuit/telecomms/mainframe, +/area/station/tcommsat/server) "vbz" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -69527,21 +69585,6 @@ /obj/item/radio/intercom/directional/south, /turf/open/floor/iron/dark, /area/station/medical/virology) -"vqJ" = ( -/obj/machinery/requests_console/directional/north{ - department = "Security"; - name = "Security Requests Console" - }, -/obj/effect/mapping_helpers/requests_console/information, -/obj/effect/mapping_helpers/requests_console/assistance, -/obj/effect/turf_decal/tile/red/anticorner, -/obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ - dir = 8 - }, -/turf/open/floor/iron/dark/textured_corner{ - dir = 1 - }, -/area/station/security/office) "vqN" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -69577,12 +69620,6 @@ /obj/item/kirbyplants/random, /turf/open/floor/iron, /area/station/security/courtroom) -"vry" = ( -/obj/machinery/button/photobooth{ - pixel_y = -26 - }, -/turf/open/floor/iron, -/area/station/command/heads_quarters/hop) "vrC" = ( /obj/structure/disposalpipe/segment, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -70382,13 +70419,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/station/security/brig/entrance) -"vDg" = ( -/obj/machinery/newscaster/directional/south, -/obj/item/kirbyplants/random, -/turf/open/floor/iron/white/side{ - dir = 5 - }, -/area/station/science/lab) "vDh" = ( /obj/structure/table/glass, /obj/item/storage/box/beakers{ @@ -70880,10 +70910,6 @@ /obj/machinery/holopad, /turf/open/floor/iron/kitchen/diagonal, /area/station/service/kitchen) -"vMz" = ( -/obj/machinery/photobooth, -/turf/open/floor/iron, -/area/station/hallway/primary/central) "vMA" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -71588,17 +71614,6 @@ /obj/structure/barricade/wooden/snowed, /turf/open/misc/asteroid/snow/icemoon, /area/icemoon/surface/outdoors/nospawn) -"vYr" = ( -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/structure/table, -/obj/item/pen{ - pixel_x = -5 - }, -/obj/item/paper_bin, -/turf/open/floor/plating, -/area/station/hallway/secondary/service) "vYs" = ( /obj/machinery/light/directional/east, /obj/structure/cable, @@ -72857,14 +72872,6 @@ }, /turf/open/floor/iron/dark, /area/station/maintenance/disposal) -"wry" = ( -/obj/machinery/photocopier, -/obj/effect/turf_decal/tile/red/half/contrasted{ - dir = 8 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/dark/textured, -/area/station/security/office) "wrA" = ( /obj/machinery/door/firedoor, /obj/machinery/door/airlock/public/glass{ @@ -73787,18 +73794,10 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental, /turf/open/floor/plating, /area/station/maintenance/port/aft) -"wFo" = ( -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/obj/structure/cable, -/obj/effect/turf_decal/trimline/blue/filled/warning{ - dir = 4 - }, -/obj/structure/disposalpipe/segment{ - dir = 5 - }, -/turf/open/floor/iron/white, -/area/station/medical/medbay/aft) +"wFN" = ( +/mob/living/basic/slime, +/turf/open/floor/engine, +/area/station/science/xenobiology) "wFO" = ( /obj/effect/landmark/start/hangover, /obj/effect/turf_decal/siding/wood{ @@ -73908,7 +73907,7 @@ "wHd" = ( /obj/machinery/atmospherics/pipe/smart/simple/dark/visible, /turf/closed/wall/r_wall, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "wHe" = ( /obj/structure/cable, /turf/open/floor/iron/dark/textured, @@ -74385,7 +74384,7 @@ }, /obj/effect/mapping_helpers/airlock/access/all/science/ordnance, /turf/open/floor/engine, -/area/station/science/ordnance/burnchamber) +/area/station/science/ordnance) "wPD" = ( /obj/machinery/door/airlock/security/glass{ name = "Evidence Storage" @@ -74945,6 +74944,12 @@ /obj/effect/turf_decal/tile/brown/half/contrasted, /turf/open/floor/iron, /area/station/cargo/office) +"wXW" = ( +/obj/effect/turf_decal/tile/red{ + dir = 4 + }, +/turf/open/floor/iron, +/area/station/hallway/primary/central) "wXX" = ( /obj/machinery/door/window/right/directional/west{ name = "Containment Pen 10"; @@ -75197,6 +75202,10 @@ "xaI" = ( /turf/open/floor/iron/dark/telecomms, /area/station/tcommsat/server) +"xaJ" = ( +/obj/item/kirbyplants/random, +/turf/open/floor/iron/dark/textured, +/area/station/security/office) "xaO" = ( /obj/effect/spawner/random/trash/moisture_trap, /obj/machinery/light/small/directional/west, @@ -75540,26 +75549,6 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/glass/reinforced, /area/station/ai_monitored/security/armory/upper) -"xge" = ( -/obj/machinery/button/door/directional/north{ - id = "permainner"; - name = "Inner Bolt Control"; - normaldoorcontrol = 1; - pixel_x = -6; - req_access = list("brig"); - specialfunctions = 4 - }, -/obj/machinery/button/door/directional/north{ - id = "permaouter"; - name = "Outer Bolt Control"; - normaldoorcontrol = 1; - pixel_x = 6; - req_access = list("brig"); - specialfunctions = 4 - }, -/obj/machinery/photobooth/security, -/turf/open/floor/iron/smooth, -/area/station/security/execution/transfer) "xgg" = ( /obj/structure/rack, /obj/item/clothing/suit/hooded/wintercoat/eva{ @@ -76847,6 +76836,9 @@ /obj/structure/extinguisher_cabinet/directional/east, /turf/open/floor/iron/dark, /area/station/maintenance/disposal/incinerator) +"xAn" = ( +/turf/open/floor/iron/dark, +/area/station/engineering/lobby) "xAs" = ( /turf/closed/wall/r_wall, /area/icemoon/surface/outdoors/nospawn) @@ -77635,6 +77627,15 @@ /obj/effect/mapping_helpers/ianbirthday, /turf/open/floor/carpet, /area/station/command/heads_quarters/hop) +"xMR" = ( +/obj/effect/turf_decal/tile/blue/half/contrasted{ + dir = 8 + }, +/obj/structure/chair/office{ + dir = 8 + }, +/turf/open/floor/iron, +/area/station/command/heads_quarters/hop) "xMT" = ( /turf/closed/wall, /area/station/science/research) @@ -114615,7 +114616,7 @@ iDt nTO ody hMJ -hfw +vbg nTO iDt xMq @@ -116640,7 +116641,7 @@ dhq dhq iDt iDt -iDt +grY iDt dLN rbT @@ -116904,7 +116905,7 @@ rbT lLN vRO ggV -eYz +cSm tGP tGP iRp @@ -172462,7 +172463,7 @@ uME doq trA uME -xge +tUz hBg dzt kvu @@ -187447,10 +187448,10 @@ tsa wHb qLY abe -hIK +wFN abe cKA -hIK +wFN abe cKA abe @@ -189503,7 +189504,7 @@ abe abe gLj abe -hIK +wFN gLj abe abe @@ -193601,10 +193602,10 @@ thA thA rcY iDt -egf -egf -egf -egf +uIf +uIf +uIf +uIf fSG fSG bId @@ -235229,7 +235230,7 @@ xzh dnq qcu bDR -hYW +wXW mpy bep ylU @@ -235255,7 +235256,7 @@ vjx mDf nLb kNp -gYP +vbn vFs mMk qAV @@ -235743,7 +235744,7 @@ iIA fSC iIA cvg -vMz +qQb jII okb ylU @@ -236252,12 +236253,12 @@ dkb kmi iYb xaA -gnA +crH wjv pec fJl -iaS -rYk +xMR +bJy cpm dnq ylU @@ -236514,7 +236515,7 @@ ool jRC cgC jaq -vry +cua cpm dnq ylU @@ -236955,10 +236956,10 @@ aBR aBR lbc nbp -dNv -sXG +lIt +hdn ijj -hpc +nbw lRF feJ mgZ @@ -237212,7 +237213,7 @@ aBR aBR nbp nbp -vqJ +hjX omk omk jeF @@ -237731,7 +237732,7 @@ sMY wMV qqM wMi -sao +xaJ arA jwx kfy @@ -238240,12 +238241,12 @@ bln lBD nbp nbp -hGj +ezs jeF omk jeF nQf -mMf +pCE arA rYB rgC @@ -238497,8 +238498,8 @@ tGr bln lbc nbp -kbz -wry +jOS +sAj dlK pbI jIm @@ -240136,7 +240137,7 @@ elj iRr omi tKi -tXL +czl ydg dAm szR @@ -240393,7 +240394,7 @@ jkx fLq hro tKi -gTf +kCG dxA oht rSC @@ -240650,7 +240651,7 @@ gwK jyR bID bID -hio +fvA vCK rSC rSC @@ -241163,7 +241164,7 @@ gMN xnE tvd egV -nYm +xAn syU vYJ fGn @@ -246251,7 +246252,7 @@ gzw kiB kQX kyZ -vYr +bFw mdZ rth hid @@ -248685,7 +248686,7 @@ mlp lEg cvh uBi -tOg +eYH jIZ oFx iHp @@ -248885,7 +248886,7 @@ oqc oAD xKX vep -clr +pBI sCm gGo bTQ @@ -249117,7 +249118,7 @@ tAx wbN grT lEo -fEY +shy qQp bVp jnS @@ -249374,7 +249375,7 @@ xyc dVw bkV ree -gkf +mpR qQp tQc jbx @@ -249629,12 +249630,12 @@ kNa ica lDM klc -qHa -iuK -bmZ +rWR +rqd +snG mJj laD -lWS +uml laD laD laD @@ -249886,16 +249887,16 @@ vzN oXq xWT klc -oTE +uiF pJX -ijf +qxu lPr vcY mmR -bsR +mOM dMp dMp -khv +kVS lqG grD xZW @@ -250143,16 +250144,16 @@ bNH rIc tVB klc -akG +tDA gsT -eUH +eDH iYs xFo rvA -wFo -qCH -qKN -nqt +dVc +nfD +sUT +bIQ fFJ hRA npo @@ -257589,7 +257590,7 @@ ljF ljF sDg gXJ -vDg +hhP oHK iZl iZl diff --git a/_maps/map_files/Mining/Lavaland.dmm b/_maps/map_files/Mining/Lavaland.dmm index 712e555b878..6a1d703aea4 100644 --- a/_maps/map_files/Mining/Lavaland.dmm +++ b/_maps/map_files/Mining/Lavaland.dmm @@ -166,14 +166,6 @@ /obj/effect/turf_decal/siding/yellow, /turf/open/floor/carpet/royalblue, /area/mine/living_quarters) -"bq" = ( -/obj/structure/railing{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/line, -/obj/effect/turf_decal/stripes/white/line, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "bt" = ( /obj/structure/disposalpipe/segment{ dir = 5 @@ -191,14 +183,6 @@ }, /turf/open/floor/iron/edge, /area/mine/lounge) -"bx" = ( -/obj/machinery/airalarm/directional/north, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 9 - }, -/obj/structure/ore_box, -/turf/open/floor/iron/dark, -/area/mine/production) "by" = ( /obj/effect/decal/cleanable/dirt, /obj/effect/turf_decal/siding/wideplating_new{ @@ -295,12 +279,6 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental, /turf/open/floor/plating, /area/mine/maintenance/living/north) -"cg" = ( -/obj/structure/cable, -/obj/machinery/power/smes/full, -/obj/effect/decal/cleanable/dirt, -/turf/open/floor/plating, -/area/mine/maintenance/service) "ch" = ( /obj/machinery/power/apc/auto_name/directional/west, /obj/structure/cable, @@ -535,12 +513,6 @@ /obj/structure/lattice/catwalk/mining, /turf/open/lava/smooth/lava_land_surface, /area/lavaland/surface/outdoors) -"dp" = ( -/obj/structure/ore_vent/starter_resources{ - icon_state = "ore_vent_active" - }, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "dq" = ( /obj/structure/stone_tile{ dir = 4 @@ -630,13 +602,18 @@ }, /turf/open/floor/iron/dark/smooth_edge, /area/mine/production) -"dW" = ( -/obj/structure/lattice/catwalk/mining, -/obj/structure/railing, -/obj/structure/railing{ - dir = 1 +"dV" = ( +/obj/structure/railing/corner{ + dir = 4 }, -/turf/open/lava/smooth/lava_land_surface, +/obj/structure/railing/corner, +/obj/effect/turf_decal/stripes/line{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/white/line{ + dir = 8 + }, +/turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "ee" = ( /obj/machinery/shower/directional/south, @@ -824,6 +801,16 @@ }, /turf/open/floor/iron/checker, /area/mine/cafeteria) +"fx" = ( +/obj/structure/railing, +/obj/effect/turf_decal/stripes/line{ + dir = 1 + }, +/obj/effect/turf_decal/stripes/white/line{ + dir = 1 + }, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "fA" = ( /obj/machinery/computer/shuttle/mining{ dir = 1 @@ -883,6 +870,10 @@ }, /turf/open/floor/plating/lavaland_atmos, /area/lavaland/surface/outdoors) +"fP" = ( +/obj/structure/gulag_vent, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "fQ" = ( /turf/open/genturf, /area/lavaland/surface/outdoors/unexplored/danger) @@ -894,14 +885,6 @@ dir = 1 }, /area/mine/laborcamp/security) -"fT" = ( -/obj/effect/turf_decal/loading_area, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 5 - }, -/obj/structure/closet/crate/bin, -/turf/open/floor/iron/dark, -/area/mine/production) "fU" = ( /obj/machinery/portable_atmospherics/canister/oxygen, /turf/open/floor/plating, @@ -1041,6 +1024,14 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet, /area/mine/living_quarters) +"gA" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing, +/obj/structure/railing{ + dir = 1 + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "gB" = ( /obj/structure/lattice/catwalk, /obj/structure/disposalpipe/segment{ @@ -1135,6 +1126,14 @@ /obj/effect/turf_decal/sand/plating/volcanic, /turf/open/floor/plating/lavaland_atmos, /area/mine/maintenance/service/disposals) +"gW" = ( +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 9 + }, +/obj/structure/ore_box, +/turf/open/floor/iron/dark, +/area/mine/production) "gZ" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 1 @@ -1538,13 +1537,6 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance, /turf/open/floor/plating, /area/mine/maintenance/public/north) -"jk" = ( -/obj/structure/table, -/obj/item/storage/box/bandages{ - pixel_y = 6 - }, -/turf/open/floor/iron/white, -/area/mine/laborcamp/production) "jm" = ( /obj/machinery/door/airlock/public/glass{ name = "Showers" @@ -1578,6 +1570,16 @@ /obj/structure/extinguisher_cabinet/directional/north, /turf/open/floor/iron/edge, /area/mine/living_quarters) +"js" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing/corner{ + dir = 8 + }, +/obj/structure/railing/corner{ + dir = 1 + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "jw" = ( /obj/structure/disposalpipe/segment{ dir = 9 @@ -1722,24 +1724,6 @@ /obj/structure/cable, /turf/open/floor/plating, /area/mine/maintenance/living/north) -"kx" = ( -/obj/effect/turf_decal/trimline/brown/filled/line, -/obj/structure/table, -/obj/item/paper/fluff/stations/lavaland/orm_notice, -/turf/open/floor/iron/dark/smooth_edge{ - dir = 1 - }, -/area/mine/production) -"ky" = ( -/obj/structure/railing{ - dir = 6 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 1 - }, -/obj/machinery/light/directional/south, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "kB" = ( /obj/structure/stone_tile/surrounding_tile, /obj/structure/stone_tile/surrounding_tile{ @@ -1783,6 +1767,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply, /turf/open/floor/plating, /area/mine/maintenance/service) +"kG" = ( +/obj/structure/railing{ + dir = 5 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 8 + }, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "kH" = ( /obj/structure/stone_tile/surrounding_tile{ dir = 1 @@ -1980,17 +1973,6 @@ }, /turf/open/lava/smooth/lava_land_surface, /area/lavaland/surface/outdoors) -"lA" = ( -/obj/machinery/recycler{ - dir = 8 - }, -/obj/machinery/conveyor{ - dir = 4; - id = "mining_disposals" - }, -/obj/effect/turf_decal/sand/plating/volcanic, -/turf/open/floor/plating/lavaland_atmos, -/area/mine/maintenance/service/disposals) "lC" = ( /obj/structure/stone_tile/block/cracked{ dir = 1 @@ -2105,18 +2087,6 @@ }, /turf/open/lava/smooth/lava_land_surface, /area/lavaland/surface/outdoors) -"lT" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/obj/effect/turf_decal/stripes/white/line{ - dir = 4 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "lV" = ( /obj/structure/cable, /obj/effect/turf_decal/trimline/red/filled/line{ @@ -2182,19 +2152,6 @@ /obj/structure/cable, /turf/open/floor/iron/dark/textured_large, /area/mine/hydroponics) -"mh" = ( -/obj/structure/railing/corner{ - dir = 4 - }, -/obj/structure/railing/corner, -/obj/effect/turf_decal/stripes/line{ - dir = 8 - }, -/obj/effect/turf_decal/stripes/white/line{ - dir = 8 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "mj" = ( /obj/structure/stone_tile/block, /obj/structure/stone_tile/block{ @@ -2745,6 +2702,13 @@ /obj/effect/turf_decal/trimline/brown/filled/line, /turf/open/floor/iron/dark/textured_large, /area/mine/production) +"oy" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 8 + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "oA" = ( /turf/closed/wall, /area/mine/storage) @@ -2870,6 +2834,12 @@ /obj/effect/spawner/random/trash/mess, /turf/open/floor/iron/white, /area/mine/laborcamp/production) +"ps" = ( +/obj/structure/cable, +/obj/machinery/power/smes/full, +/obj/effect/decal/cleanable/dirt, +/turf/open/floor/plating, +/area/mine/maintenance/service) "pu" = ( /obj/item/chair/stool{ pixel_x = -2; @@ -3310,6 +3280,23 @@ }, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) +"so" = ( +/obj/structure/table, +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 10 + }, +/obj/item/storage/box/bandages{ + pixel_y = 6; + pixel_x = 4 + }, +/obj/item/storage/pill_bottle/epinephrine{ + pixel_x = -6; + pixel_y = 4 + }, +/turf/open/floor/iron/white/smooth_corner{ + dir = 8 + }, +/area/mine/medical) "st" = ( /obj/structure/chair/stool/directional/north, /obj/effect/decal/cleanable/dirt, @@ -3531,15 +3518,6 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/mine/storage) -"tO" = ( -/obj/machinery/power/apc/auto_name/directional/south, -/obj/structure/cable, -/obj/effect/turf_decal/trimline/brown/filled/line, -/obj/structure/table, -/turf/open/floor/iron/dark/smooth_edge{ - dir = 1 - }, -/area/mine/production) "tV" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, @@ -4308,15 +4286,6 @@ }, /turf/open/floor/iron/checker, /area/mine/cafeteria) -"yQ" = ( -/obj/structure/railing{ - dir = 5 - }, -/obj/effect/turf_decal/stripes/corner{ - dir = 8 - }, -/turf/open/misc/asteroid/basalt/lava_land_surface, -/area/lavaland/surface/outdoors) "yR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -4442,6 +4411,14 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/mine/maintenance/living/north) +"zD" = ( +/obj/effect/turf_decal/loading_area{ + dir = 1 + }, +/obj/structure/barricade/wooden/crude, +/obj/structure/barricade/wooden, +/turf/open/floor/iron/dark, +/area/mine/production) "zF" = ( /obj/item/kirbyplants/random, /obj/effect/turf_decal/trimline/blue/filled/line{ @@ -4474,6 +4451,18 @@ /obj/effect/mapping_helpers/airlock/access/any/engineering/maintenance/departmental, /turf/open/floor/plating, /area/mine/maintenance/service) +"zO" = ( +/obj/structure/railing{ + dir = 8 + }, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/obj/effect/turf_decal/stripes/white/line{ + dir = 4 + }, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "zQ" = ( /obj/effect/decal/cleanable/dirt, /turf/open/floor/iron/smooth_edge, @@ -4494,14 +4483,6 @@ /obj/structure/cable, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"Ac" = ( -/obj/effect/turf_decal/bot, -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 10 - }, -/obj/machinery/computer/order_console/mining, -/turf/open/floor/iron/dark, -/area/mine/production) "Ae" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -4509,14 +4490,6 @@ /obj/structure/lattice/catwalk, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"Af" = ( -/obj/machinery/mineral/processing_unit{ - dir = 1; - input_dir = 8; - output_dir = 4 - }, -/turf/open/floor/plating, -/area/mine/laborcamp/production) "Ai" = ( /obj/structure/stone_tile/cracked{ dir = 1 @@ -4616,13 +4589,6 @@ "AX" = ( /turf/open/floor/plating, /area/mine/laborcamp/security/maintenance) -"AY" = ( -/obj/structure/lattice/catwalk/mining, -/obj/structure/railing{ - dir = 9 - }, -/turf/open/lava/smooth/lava_land_surface, -/area/lavaland/surface/outdoors) "Bb" = ( /obj/machinery/camera/autoname/directional/east{ network = list("mine") @@ -4991,6 +4957,11 @@ /obj/effect/turf_decal/sand/plating/volcanic, /turf/open/floor/plating/lavaland_atmos, /area/lavaland/surface/outdoors) +"Ee" = ( +/obj/machinery/power/smes/full, +/obj/structure/cable, +/turf/open/floor/plating, +/area/mine/maintenance/labor) "Eg" = ( /obj/effect/spawner/structure/window/reinforced, /obj/structure/disposalpipe/segment, @@ -5004,14 +4975,12 @@ /turf/open/floor/plating/lavaland_atmos, /area/mine/maintenance/service/disposals) "Ep" = ( -/obj/structure/lattice/catwalk/mining, -/obj/structure/railing/corner{ - dir = 8 - }, -/obj/structure/railing/corner{ +/obj/structure/railing{ dir = 1 }, -/turf/open/lava/smooth/lava_land_surface, +/obj/effect/turf_decal/stripes/line, +/obj/effect/turf_decal/stripes/white/line, +/turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) "Eq" = ( /obj/machinery/door/airlock/security/glass{ @@ -5357,13 +5326,6 @@ }, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"GV" = ( -/obj/structure/lattice/catwalk/mining, -/obj/structure/railing{ - dir = 8 - }, -/turf/open/lava/smooth/lava_land_surface, -/area/lavaland/surface/outdoors) "GW" = ( /obj/structure/chair/office{ dir = 1 @@ -5407,11 +5369,6 @@ /obj/machinery/power/apc/auto_name/directional/north, /turf/open/floor/plating, /area/mine/maintenance/public/north) -"Hi" = ( -/obj/machinery/power/smes/full, -/obj/structure/cable, -/turf/open/floor/plating, -/area/mine/maintenance/labor) "Hp" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2, /obj/effect/turf_decal/trimline/brown/filled/line{ @@ -5793,6 +5750,13 @@ /obj/machinery/light/small/directional/south, /turf/open/floor/plating, /area/mine/laborcamp/production) +"Jz" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 10 + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "JA" = ( /obj/machinery/computer/shuttle/mining/common, /obj/structure/sign/directions/evac/directional/east{ @@ -5879,13 +5843,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plating, /area/mine/laborcamp/security/maintenance) -"JO" = ( -/obj/structure/lattice/catwalk/mining, -/obj/structure/railing{ - dir = 10 - }, -/turf/open/lava/smooth/lava_land_surface, -/area/lavaland/surface/outdoors) "JP" = ( /obj/structure/disposalpipe/segment{ dir = 4 @@ -5898,23 +5855,6 @@ dir = 1 }, /area/mine/living_quarters) -"JQ" = ( -/obj/structure/table, -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 10 - }, -/obj/item/storage/box/bandages{ - pixel_y = 6; - pixel_x = 4 - }, -/obj/item/storage/pill_bottle/epinephrine{ - pixel_x = -6; - pixel_y = 4 - }, -/turf/open/floor/iron/white/smooth_corner{ - dir = 8 - }, -/area/mine/medical) "JR" = ( /obj/structure/stone_tile, /obj/structure/stone_tile{ @@ -6290,6 +6230,13 @@ dir = 4 }, /area/mine/lounge) +"LO" = ( +/obj/structure/table, +/obj/item/storage/box/bandages{ + pixel_y = 6 + }, +/turf/open/floor/iron/white, +/area/mine/laborcamp/production) "LR" = ( /obj/effect/decal/cleanable/dirt, /obj/structure/cable, @@ -6306,6 +6253,14 @@ }, /turf/open/floor/plating/lavaland_atmos, /area/lavaland/surface/outdoors) +"LT" = ( +/obj/machinery/mineral/processing_unit/gulag{ + dir = 1; + input_dir = 8; + output_dir = 4 + }, +/turf/open/floor/plating, +/area/mine/laborcamp/production) "LY" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 8 @@ -6576,6 +6531,14 @@ /obj/effect/turf_decal/tile/blue/fourcorners, /turf/open/floor/iron, /area/mine/living_quarters) +"NG" = ( +/obj/effect/turf_decal/bot, +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 10 + }, +/obj/machinery/computer/order_console/mining, +/turf/open/floor/iron/dark, +/area/mine/production) "NL" = ( /obj/structure/railing, /obj/structure/lattice/catwalk/mining, @@ -6878,6 +6841,17 @@ }, /turf/open/floor/plating, /area/mine/production) +"PL" = ( +/obj/machinery/recycler{ + dir = 8 + }, +/obj/machinery/conveyor{ + dir = 4; + id = "mining_disposals" + }, +/obj/effect/turf_decal/sand/plating/volcanic, +/turf/open/floor/plating/lavaland_atmos, +/area/mine/maintenance/service/disposals) "PR" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/carpet/executive, @@ -6959,13 +6933,9 @@ }, /turf/open/floor/iron/dark, /area/mine/mechbay) -"Qm" = ( -/obj/structure/railing, -/obj/effect/turf_decal/stripes/line{ - dir = 1 - }, -/obj/effect/turf_decal/stripes/white/line{ - dir = 1 +"Qq" = ( +/obj/structure/ore_vent/starter_resources{ + icon_state = "ore_vent_active" }, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) @@ -7056,6 +7026,16 @@ dir = 4 }, /area/mine/production) +"QV" = ( +/obj/structure/railing{ + dir = 6 + }, +/obj/effect/turf_decal/stripes/corner{ + dir = 1 + }, +/obj/machinery/light/directional/south, +/turf/open/misc/asteroid/basalt/lava_land_surface, +/area/lavaland/surface/outdoors) "QX" = ( /turf/closed/wall, /area/mine/mechbay) @@ -7100,6 +7080,14 @@ }, /turf/open/floor/iron/dark, /area/mine/production) +"Ro" = ( +/obj/effect/turf_decal/loading_area, +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 5 + }, +/obj/structure/closet/crate/bin, +/turf/open/floor/iron/dark, +/area/mine/production) "Rs" = ( /obj/machinery/conveyor{ dir = 6; @@ -7176,14 +7164,6 @@ /obj/structure/lattice/catwalk, /turf/open/misc/asteroid/basalt/lava_land_surface, /area/lavaland/surface/outdoors) -"RS" = ( -/obj/effect/turf_decal/loading_area{ - dir = 1 - }, -/obj/structure/barricade/wooden/crude, -/obj/structure/barricade/wooden, -/turf/open/floor/iron/dark, -/area/mine/production) "RV" = ( /obj/machinery/atmospherics/components/unary/vent_pump/on/layer4{ dir = 4 @@ -7385,6 +7365,14 @@ }, /turf/open/floor/iron/dark/smooth_edge, /area/mine/eva) +"Ti" = ( +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/structure/table, +/obj/item/paper/fluff/stations/lavaland/orm_notice, +/turf/open/floor/iron/dark/smooth_edge{ + dir = 1 + }, +/area/mine/production) "To" = ( /obj/structure/table, /obj/machinery/reagentgrinder, @@ -7432,6 +7420,13 @@ /obj/effect/decal/cleanable/dirt, /turf/open/floor/plating, /area/mine/maintenance/living/north) +"TD" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 1 + }, +/obj/structure/ore_box, +/turf/open/floor/iron/dark/smooth_edge, +/area/mine/production) "TF" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -7660,13 +7655,6 @@ /obj/structure/disposalpipe/segment, /turf/open/floor/plating, /area/mine/laborcamp/security/maintenance) -"UW" = ( -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 1 - }, -/obj/structure/ore_box, -/turf/open/floor/iron/dark/smooth_edge, -/area/mine/production) "UZ" = ( /obj/structure/chair/stool/directional/west, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -7847,6 +7835,15 @@ }, /turf/open/floor/iron/dark/textured_large, /area/mine/laborcamp) +"VZ" = ( +/obj/machinery/power/apc/auto_name/directional/south, +/obj/structure/cable, +/obj/effect/turf_decal/trimline/brown/filled/line, +/obj/structure/table, +/turf/open/floor/iron/dark/smooth_edge{ + dir = 1 + }, +/area/mine/production) "Wb" = ( /obj/structure/cable, /turf/open/floor/iron/dark/textured_large, @@ -7894,6 +7891,13 @@ }, /turf/open/floor/carpet/neon/simple/red/nodots, /area/mine/cafeteria) +"Wy" = ( +/obj/structure/lattice/catwalk/mining, +/obj/structure/railing{ + dir = 9 + }, +/turf/open/lava/smooth/lava_land_surface, +/area/lavaland/surface/outdoors) "WB" = ( /obj/machinery/disposal/bin, /obj/structure/disposalpipe/trunk, @@ -22569,7 +22573,7 @@ uU uU uU pU -pU +fP pU ff Gf @@ -23851,7 +23855,7 @@ aj aj eR Kw -jk +LO ff mU Fv @@ -24373,7 +24377,7 @@ Hd Nb NR Kv -Af +LT ff gm mN @@ -28234,7 +28238,7 @@ Zt Zt Zt Zt -Hi +Ee ve RD RD @@ -36428,7 +36432,7 @@ pU mA su xG -lA +PL Sd pU aj @@ -38481,7 +38485,7 @@ Ue AA gB ON -cg +ps gq ER vc @@ -39546,7 +39550,7 @@ yp yp Dz LG -JQ +so It Xp Ss @@ -45941,9 +45945,9 @@ pU aj aj aj -AY -lT -JO +Wy +zO +Jz aj oA Lu @@ -46198,9 +46202,9 @@ aj aj aj aj -bq -dp -Qm +Ep +Qq +fx aj oA Lz @@ -46455,9 +46459,9 @@ pU pU aj aj -yQ -mh -ky +kG +dV +QV NU NU NU @@ -46713,7 +46717,7 @@ pU pU aj aj -dW +gA aj Le Lh @@ -46969,8 +46973,8 @@ pU pU AQ ZM -GV -Ep +oy +js ZM Ki Uq @@ -47748,10 +47752,10 @@ NU NU NU NU -bx +gW Xj QT -Ac +NG pK No Hp @@ -48004,11 +48008,11 @@ pU pU NU PI -RS -UW +zD +TD Vw wl -kx +Ti pK lg se @@ -48265,7 +48269,7 @@ zJ oi Ts Wb -tO +VZ pK Tf Jp @@ -48777,7 +48781,7 @@ NU Rs rP JG -fT +Ro op NU pK diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index e0de5bebc26..456cf46196e 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -597,16 +597,6 @@ dir = 1 }, /area/station/escapepodbay) -"aci" = ( -/obj/structure/railing{ - dir = 4 - }, -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 4 - }, -/obj/machinery/photobooth/security, -/turf/open/floor/iron, -/area/station/security/execution/transfer) "acj" = ( /turf/open/floor/iron/stairs/medium{ dir = 1 @@ -6018,6 +6008,11 @@ /obj/effect/turf_decal/tile/neutral/fourcorners, /turf/open/floor/iron/dark, /area/station/science/robotics/lab) +"aUc" = ( +/obj/item/radio/intercom/directional/east, +/obj/machinery/photobooth, +/turf/open/floor/iron/grimy, +/area/station/service/library/lounge) "aUh" = ( /obj/structure/cable, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -7521,6 +7516,15 @@ }, /turf/open/floor/iron/white, /area/station/command/heads_quarters/captain/private) +"bCx" = ( +/obj/structure/railing{ + dir = 8 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 8 + }, +/turf/open/floor/glass/reinforced, +/area/station/science/research) "bDf" = ( /obj/structure/table/reinforced, /obj/item/folder/yellow, @@ -8262,6 +8266,12 @@ }, /turf/open/floor/iron, /area/station/commons/storage/primary) +"bNI" = ( +/obj/machinery/airalarm/directional/north, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/modular_computer/preset/cargochat/service, +/turf/open/floor/iron, +/area/station/hallway/secondary/service) "bNJ" = ( /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, @@ -12269,10 +12279,6 @@ "dfz" = ( /turf/closed/wall/r_wall, /area/station/science/ordnance/storage) -"dfC" = ( -/mob/living/basic/slime, -/turf/open/floor/engine, -/area/station/science/xenobiology) "dfE" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 5 @@ -19485,13 +19491,6 @@ /obj/item/radio/intercom/directional/south, /turf/open/floor/iron, /area/station/science/explab) -"fRb" = ( -/obj/effect/turf_decal/trimline/neutral/filled/line{ - dir = 9 - }, -/obj/item/radio/intercom/directional/north, -/turf/open/floor/iron, -/area/station/hallway/secondary/service) "fRs" = ( /obj/effect/turf_decal/stripes/line{ dir = 10 @@ -20093,6 +20092,20 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron, /area/station/command/heads_quarters/qm) +"gbZ" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 1 + }, +/obj/machinery/camera/directional/north{ + c_tag = "Science - Research & Development"; + network = list("ss13","rd") + }, +/obj/structure/disposalpipe/segment{ + dir = 4 + }, +/obj/item/kirbyplants/organic/plant10, +/turf/open/floor/iron/white, +/area/station/science/lab) "gcp" = ( /turf/closed/wall/r_wall, /area/station/commons/vacant_room) @@ -22957,13 +22970,6 @@ "hhc" = ( /turf/open/floor/iron/dark, /area/station/command/bridge) -"hhf" = ( -/obj/machinery/power/smes{ - charge = 5e+06 - }, -/obj/structure/cable, -/turf/open/floor/circuit, -/area/station/ai_monitored/turret_protected/ai) "hht" = ( /obj/effect/landmark/event_spawn, /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, @@ -23161,6 +23167,15 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/iron/checker, /area/station/commons/lounge) +"hjz" = ( +/obj/effect/turf_decal/siding/white{ + dir = 1 + }, +/obj/machinery/modular_computer/preset/cargochat/medical{ + dir = 1 + }, +/turf/open/floor/iron/dark, +/area/station/medical/storage) "hjM" = ( /obj/structure/railing/corner{ dir = 4 @@ -25712,6 +25727,18 @@ }, /turf/open/floor/carpet, /area/station/service/chapel/monastery) +"ikM" = ( +/obj/structure/cable, +/obj/effect/turf_decal/trimline/blue/filled/line{ + dir = 8 + }, +/obj/structure/disposalpipe/segment{ + dir = 6 + }, +/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, +/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, +/turf/open/floor/iron/white, +/area/station/medical/medbay/central) "ikT" = ( /obj/effect/turf_decal/trimline/dark_red/warning{ dir = 10 @@ -26539,6 +26566,19 @@ }, /turf/open/floor/iron, /area/station/cargo/miningdock) +"iBo" = ( +/obj/machinery/camera/directional/north{ + c_tag = "Service - Autolathe Room" + }, +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/machinery/light/dim/directional/north, +/obj/structure/table, +/obj/machinery/fax{ + fax_name = "Service Hallway"; + name = "Service Fax Machine" + }, +/turf/open/floor/iron, +/area/station/hallway/secondary/service) "iBx" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 1 @@ -27794,6 +27834,14 @@ }, /turf/open/floor/plating/airless, /area/station/asteroid) +"iYO" = ( +/obj/effect/turf_decal/trimline/purple/filled/line{ + dir = 1 + }, +/obj/machinery/newscaster/directional/north, +/obj/machinery/modular_computer/preset/cargochat/science, +/turf/open/floor/iron/white, +/area/station/science/lab) "iZb" = ( /turf/closed/wall, /area/station/security/office) @@ -31330,6 +31378,16 @@ /obj/effect/mapping_helpers/airlock/locked, /turf/open/floor/catwalk_floor, /area/station/maintenance/tram/mid) +"kfH" = ( +/obj/structure/railing{ + dir = 4 + }, +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 4 + }, +/obj/machinery/photobooth/security, +/turf/open/floor/iron, +/area/station/security/execution/transfer) "kfO" = ( /turf/open/floor/plating, /area/station/cargo/drone_bay) @@ -33908,6 +33966,13 @@ }, /turf/open/space/openspace, /area/station/solars/port) +"kXq" = ( +/obj/structure/cable/multilayer/multiz, +/obj/effect/turf_decal/stripes/line{ + dir = 4 + }, +/turf/open/floor/plating, +/area/station/ai_monitored/turret_protected/ai) "kXr" = ( /obj/effect/decal/cleanable/dirt, /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ @@ -34662,6 +34727,11 @@ }, /turf/open/floor/iron, /area/station/hallway/secondary/construction/engineering) +"llW" = ( +/obj/machinery/power/smes/full, +/obj/structure/cable, +/turf/open/floor/circuit, +/area/station/ai_monitored/turret_protected/ai) "lml" = ( /turf/closed/wall/r_wall, /area/station/engineering/atmos/pumproom) @@ -37150,18 +37220,6 @@ "mbJ" = ( /turf/closed/wall, /area/station/maintenance/tram/right) -"mbK" = ( -/obj/structure/cable, -/obj/effect/turf_decal/trimline/blue/filled/line{ - dir = 8 - }, -/obj/structure/disposalpipe/segment{ - dir = 6 - }, -/obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, -/obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, -/turf/open/floor/iron/white, -/area/station/medical/medbay/central) "mbQ" = ( /obj/effect/turf_decal/trimline/brown/filled/line{ dir = 4 @@ -37232,17 +37290,6 @@ }, /turf/open/floor/plating, /area/station/command/heads_quarters/rd) -"mdk" = ( -/obj/effect/turf_decal/trimline/brown/filled/line{ - dir = 5 - }, -/obj/effect/turf_decal/trimline/yellow/filled/warning{ - dir = 5 - }, -/obj/structure/cable, -/obj/structure/table, -/turf/open/floor/iron, -/area/station/engineering/break_room) "mdl" = ( /obj/effect/turf_decal/trimline/white/warning{ dir = 6 @@ -37628,25 +37675,6 @@ /obj/machinery/newscaster/directional/north, /turf/open/floor/iron, /area/station/commons/fitness/recreation) -"mjy" = ( -/obj/effect/turf_decal/trimline/red/filled/line{ - dir = 9 - }, -/obj/machinery/status_display/evac/directional/west, -/obj/effect/turf_decal/trimline/red/filled/warning{ - dir = 4 - }, -/obj/structure/table, -/obj/item/paper_bin{ - pixel_x = -3; - pixel_y = 7 - }, -/obj/item/pen{ - pixel_x = 3; - pixel_y = 8 - }, -/turf/open/floor/iron, -/area/station/security/office) "mjF" = ( /obj/structure/disposalpipe/sorting/mail/flip{ dir = 4 @@ -38874,11 +38902,6 @@ /obj/structure/railing, /turf/open/floor/iron/dark, /area/station/command/heads_quarters/hop) -"mHZ" = ( -/obj/item/radio/intercom/directional/east, -/obj/machinery/photobooth, -/turf/open/floor/iron/grimy, -/area/station/service/library/lounge) "mId" = ( /obj/effect/turf_decal/trimline/green/filled/line, /obj/effect/turf_decal/trimline/green/filled/line{ @@ -39212,6 +39235,13 @@ /obj/machinery/light/directional/west, /turf/open/floor/iron/white, /area/station/medical/treatment_center) +"mOq" = ( +/obj/effect/turf_decal/trimline/neutral/filled/line{ + dir = 9 + }, +/obj/item/radio/intercom/directional/north, +/turf/open/floor/iron, +/area/station/hallway/secondary/service) "mOB" = ( /obj/structure/table, /obj/item/analyzer, @@ -42013,6 +42043,11 @@ /obj/effect/landmark/event_spawn, /turf/open/floor/iron, /area/station/engineering/engine_smes) +"nOB" = ( +/obj/effect/turf_decal/tile/neutral/fourcorners, +/obj/structure/reagent_dispensers/watertank, +/turf/open/floor/iron, +/area/station/hallway/secondary/service) "nOI" = ( /obj/machinery/power/apc/auto_name/directional/south, /obj/structure/disposalpipe/segment{ @@ -43829,6 +43864,10 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/scrubbers/hidden/layer2, /turf/open/floor/grass, /area/station/medical/virology) +"oxe" = ( +/mob/living/basic/slime, +/turf/open/floor/engine, +/area/station/science/xenobiology) "oxf" = ( /obj/structure/railing, /obj/effect/turf_decal/trimline/dark_blue/arrow_cw, @@ -44705,6 +44744,25 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/commons/storage/primary) +"oSu" = ( +/obj/effect/turf_decal/trimline/red/filled/line{ + dir = 9 + }, +/obj/machinery/status_display/evac/directional/west, +/obj/effect/turf_decal/trimline/red/filled/warning{ + dir = 4 + }, +/obj/structure/table, +/obj/item/paper_bin{ + pixel_x = -3; + pixel_y = 7 + }, +/obj/item/pen{ + pixel_x = 3; + pixel_y = 8 + }, +/turf/open/floor/iron, +/area/station/security/office) "oSB" = ( /obj/machinery/camera/directional/west{ network = list("ss13","Security","cargo"); @@ -46707,15 +46765,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/cargo/sorting) -"pBU" = ( -/obj/structure/railing{ - dir = 8 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 8 - }, -/turf/open/floor/glass/reinforced, -/area/station/science/research) "pBZ" = ( /obj/structure/railing/corner{ dir = 4 @@ -47471,20 +47520,6 @@ /obj/structure/cable, /turf/open/floor/iron, /area/station/commons/storage/primary) -"pOY" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 1 - }, -/obj/machinery/camera/directional/north{ - c_tag = "Science - Research & Development"; - network = list("ss13","rd") - }, -/obj/structure/disposalpipe/segment{ - dir = 4 - }, -/obj/item/kirbyplants/organic/plant10, -/turf/open/floor/iron/white, -/area/station/science/lab) "pOZ" = ( /obj/machinery/portable_atmospherics/scrubber, /obj/effect/turf_decal/trimline/red/filled/line{ @@ -48979,11 +49014,6 @@ /obj/effect/spawner/structure/window, /turf/open/floor/plating, /area/station/commons/fitness/recreation) -"qqm" = ( -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/structure/reagent_dispensers/watertank, -/turf/open/floor/iron, -/area/station/hallway/secondary/service) "qqv" = ( /obj/machinery/atmospherics/components/unary/outlet_injector/on, /obj/machinery/light/directional/north, @@ -49258,21 +49288,23 @@ }, /turf/open/floor/iron, /area/station/command/bridge) -"qvR" = ( -/obj/structure/railing{ - dir = 4 - }, -/obj/machinery/door/firedoor/border_only{ - dir = 4 - }, -/turf/open/floor/glass/reinforced, -/area/station/science/research) "qvV" = ( /obj/machinery/atmospherics/components/unary/vent_scrubber/on/layer2{ dir = 8 }, /turf/open/floor/iron, /area/station/security/prison/work) +"qwn" = ( +/obj/effect/turf_decal/trimline/brown/filled/line{ + dir = 5 + }, +/obj/effect/turf_decal/trimline/yellow/filled/warning{ + dir = 5 + }, +/obj/structure/cable, +/obj/structure/table, +/turf/open/floor/iron, +/area/station/engineering/break_room) "qwq" = ( /obj/structure/table/wood, /obj/item/radio/intercom, @@ -51907,6 +51939,15 @@ /obj/effect/mapping_helpers/airlock/unres, /turf/open/floor/catwalk_floor, /area/station/maintenance/starboard/central) +"rpq" = ( +/obj/structure/railing{ + dir = 4 + }, +/obj/machinery/door/firedoor/border_only{ + dir = 4 + }, +/turf/open/floor/glass/reinforced, +/area/station/science/research) "rpr" = ( /obj/effect/turf_decal/siding/thinplating/corner{ dir = 4 @@ -57000,15 +57041,6 @@ /obj/machinery/shower/directional/south, /turf/open/floor/iron/freezer, /area/station/commons/toilet) -"thm" = ( -/obj/effect/turf_decal/siding/white{ - dir = 1 - }, -/obj/machinery/modular_computer/preset/cargochat/medical{ - dir = 1 - }, -/turf/open/floor/iron/dark, -/area/station/medical/storage) "thG" = ( /obj/effect/turf_decal/trimline/red/filled/line, /obj/effect/turf_decal/trimline/red/filled/corner{ @@ -62777,19 +62809,6 @@ /obj/machinery/atmospherics/pipe/smart/manifold4w/supply/hidden/layer4, /turf/open/floor/iron, /area/station/security/checkpoint/medical) -"vbl" = ( -/obj/machinery/camera/directional/north{ - c_tag = "Service - Autolathe Room" - }, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/light/dim/directional/north, -/obj/structure/table, -/obj/machinery/fax{ - fax_name = "Service Hallway"; - name = "Service Fax Machine" - }, -/turf/open/floor/iron, -/area/station/hallway/secondary/service) "vbt" = ( /obj/machinery/computer/atmos_control/oxygen_tank{ atmos_chambers = list(o2ordance="Oxygen Supply") @@ -65292,13 +65311,6 @@ }, /turf/open/floor/plating, /area/station/construction/mining/aux_base) -"vTD" = ( -/obj/structure/cable/multilayer/multiz, -/obj/effect/turf_decal/stripes/line{ - dir = 4 - }, -/turf/open/floor/iron/dark, -/area/station/ai_monitored/turret_protected/ai) "vTE" = ( /obj/machinery/camera{ dir = 9; @@ -66034,12 +66046,6 @@ /obj/structure/railing/corner, /turf/open/space/openspace, /area/station/solars/starboard/fore) -"whX" = ( -/obj/machinery/airalarm/directional/north, -/obj/effect/turf_decal/tile/neutral/fourcorners, -/obj/machinery/modular_computer/preset/cargochat/service, -/turf/open/floor/iron, -/area/station/hallway/secondary/service) "wid" = ( /obj/structure/cable, /obj/structure/disposalpipe/segment, @@ -66913,14 +66919,6 @@ /obj/structure/sign/warning/electric_shock, /turf/closed/wall/r_wall, /area/station/maintenance/port/central) -"wzX" = ( -/obj/effect/turf_decal/trimline/purple/filled/line{ - dir = 1 - }, -/obj/machinery/newscaster/directional/north, -/obj/machinery/modular_computer/preset/cargochat/science, -/turf/open/floor/iron/white, -/area/station/science/lab) "wAa" = ( /obj/effect/turf_decal/trimline/purple/filled/line{ dir = 4 @@ -86737,7 +86735,7 @@ jWs jvE eLB abU -aci +kfH acv acH sXc @@ -102685,10 +102683,10 @@ apl iRL snQ iRL -whX +bNI tbm nNZ -qqm +nOB cWZ klk kNo @@ -103199,7 +103197,7 @@ bje iRL snQ iRL -fRb +mOq twg dWn cWZ @@ -103752,7 +103750,7 @@ ixT gYI cgR ial -mdk +qwn amV mvy iXQ @@ -103970,7 +103968,7 @@ aeF iRL quF iRL -vbl +iBo sWF iog cWZ @@ -115592,7 +115590,7 @@ dUT gdC qVr bfH -dfC +oxe ahk hdA mBm @@ -115604,7 +115602,7 @@ hik sml bfH bfH -dfC +oxe jTC qVr aaa @@ -118676,7 +118674,7 @@ hFH tYB qVr jTC -dfC +oxe bfH bfH nbI @@ -118688,7 +118686,7 @@ rbU uAF ahk hdA -dfC +oxe bfH qVr aaa @@ -152593,7 +152591,7 @@ cSr rAS xzI uYa -mHZ +aUc ppK rAS rAS @@ -156634,7 +156632,7 @@ dEv ukw pyF dEv -mjy +oSu rWT gYd isV @@ -166203,7 +166201,7 @@ kZD wAe glU jSc -thm +hjz xRx aLw pVk @@ -167228,7 +167226,7 @@ jAc kkd qNI mrC -mbK +ikM tOw kGa djz @@ -182133,14 +182131,14 @@ wxu yjY eYS oxL -pOY +gbZ oGO xQN mEt vNk kTU xyt -qvR +rpq ovC dCq uiV @@ -183161,14 +183159,14 @@ aIv aHQ tJz oxL -wzX +iYO lpC vTb vTb rbn crj wWT -pBU +bCx uvB rBb tbK @@ -185803,7 +185801,7 @@ jLx ffe mgi pIQ -hhf +llW xZx rSB dVM @@ -186316,7 +186314,7 @@ mYB njC aoh iUO -vTD +kXq lxi njC mEb diff --git a/code/__DEFINES/basic_mobs.dm b/code/__DEFINES/basic_mobs.dm index b673d0e7a12..c827f760b8a 100644 --- a/code/__DEFINES/basic_mobs.dm +++ b/code/__DEFINES/basic_mobs.dm @@ -14,6 +14,8 @@ #define IMMUNE_TO_FISTS (1<<4) /// Mob is immune to getting wet #define IMMUNE_TO_GETTING_WET (1<<5) +/// Disables the function of attacking random body zones +#define PRECISE_ATTACK_ZONES (1<<6) /// Temporary trait applied when an attack forecast animation has completed #define TRAIT_BASIC_ATTACK_FORECAST "trait_basic_attack_forecast" diff --git a/code/__DEFINES/click.dm b/code/__DEFINES/click.dm new file mode 100644 index 00000000000..5900dd54210 --- /dev/null +++ b/code/__DEFINES/click.dm @@ -0,0 +1,8 @@ +/// Action has succeeded, preventing further alt click interaction +#define CLICK_ACTION_SUCCESS (1<<0) +/// Action failed, preventing further alt click interaction +#define CLICK_ACTION_BLOCKING (1<<1) +/// Either return state +#define CLICK_ACTION_ANY (CLICK_ACTION_SUCCESS | CLICK_ACTION_BLOCKING) + +/// Use NONE for continue interaction diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 847fd52cc22..686e422b020 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -282,6 +282,8 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( #define BODY_ZONE_L_LEG "l_leg" #define BODY_ZONE_R_LEG "r_leg" +GLOBAL_LIST_INIT(all_body_zones, list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) +GLOBAL_LIST_INIT(limb_zones, list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) GLOBAL_LIST_INIT(arm_zones, list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM)) #define BODY_ZONE_PRECISE_EYES "eyes" diff --git a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm index 3ff131d9a39..2ea2e4d5fe3 100644 --- a/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm +++ b/code/__DEFINES/dcs/signals/signals_atom/signals_atom_mouse.dm @@ -13,7 +13,6 @@ #define COMSIG_CLICK_CTRL "ctrl_click" ///from base of atom/AltClick(): (/mob) #define COMSIG_CLICK_ALT "alt_click" - #define COMPONENT_CANCEL_CLICK_ALT (1<<0) ///from base of atom/alt_click_secondary(): (/mob) #define COMSIG_CLICK_ALT_SECONDARY "alt_click_secondary" #define COMPONENT_CANCEL_CLICK_ALT_SECONDARY (1<<0) diff --git a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm index 068aadf6a5e..f33d2f1f40d 100644 --- a/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm +++ b/code/__DEFINES/dcs/signals/signals_mob/signals_mob_main.dm @@ -182,8 +182,6 @@ #define COMSIG_MOB_ITEM_AFTERATTACK "mob_item_afterattack" ///from base of obj/item/afterattack_secondary(): (atom/target, obj/item/weapon, proximity_flag, click_parameters) #define COMSIG_MOB_ITEM_AFTERATTACK_SECONDARY "mob_item_afterattack_secondary" -///from base of obj/item/attack_qdeleted(): (atom/target, mob/user, proximity_flag, click_parameters) -#define COMSIG_MOB_ITEM_ATTACK_QDELETED "mob_item_attack_qdeleted" ///from base of mob/RangedAttack(): (atom/A, modifiers) #define COMSIG_MOB_ATTACK_RANGED "mob_attack_ranged" ///from base of mob/ranged_secondary_attack(): (atom/target, modifiers) diff --git a/code/__DEFINES/dcs/signals/signals_object.dm b/code/__DEFINES/dcs/signals/signals_object.dm index d926cc6d5df..43e3a09a028 100644 --- a/code/__DEFINES/dcs/signals/signals_object.dm +++ b/code/__DEFINES/dcs/signals/signals_object.dm @@ -472,8 +472,6 @@ #define COMPONENT_AFTERATTACK_PROCESSED_ITEM (1<<0) ///from base of obj/item/afterattack_secondary(): (atom/target, mob/user, proximity_flag, click_parameters) #define COMSIG_ITEM_AFTERATTACK_SECONDARY "item_afterattack_secondary" -///from base of obj/item/attack_qdeleted(): (atom/target, mob/user, params) -#define COMSIG_ITEM_ATTACK_QDELETED "item_attack_qdeleted" ///from base of obj/item/embedded(): (atom/target, obj/item/bodypart/part) #define COMSIG_ITEM_EMBEDDED "item_embedded" ///from base of datum/component/embedded/safeRemove(): (mob/living/carbon/victim) diff --git a/code/__DEFINES/interaction_flags.dm b/code/__DEFINES/interaction_flags.dm index 55732f2364b..418466a0eb2 100644 --- a/code/__DEFINES/interaction_flags.dm +++ b/code/__DEFINES/interaction_flags.dm @@ -36,10 +36,7 @@ #define INTERACT_MACHINE_OPEN_SILICON (1<<4) /// must be silicon to interact #define INTERACT_MACHINE_REQUIRES_SILICON (1<<5) -/// This flag determines if a machine set_machine's the user when the user uses it, making updateUsrDialog make the user re-call interact() on it. -/// This is exclusively used for non-TGUI UIs, and its instances should be removed when moved to TGUI. -#define INTERACT_MACHINE_SET_MACHINE (1<<6) /// the user must have vision to interact (blind people need not apply) -#define INTERACT_MACHINE_REQUIRES_SIGHT (1<<7) +#define INTERACT_MACHINE_REQUIRES_SIGHT (1<<6) /// the user must be able to read to interact -#define INTERACT_MACHINE_REQUIRES_LITERACY (1<<8) +#define INTERACT_MACHINE_REQUIRES_LITERACY (1<<7) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index e46600abe2a..f47ad817e92 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -855,6 +855,10 @@ GLOBAL_LIST_INIT(layers_to_offset, list( #define ALLOW_SILICON_REACH (1<<6) /// If resting on the floor is allowed to perform action (pAIs can play music while resting) #define ALLOW_RESTING (1<<7) +/// If this is accessible to creatures with ventcrawl capabilities +#define NEED_VENTCRAWL (1<<8) +/// Checks for base adjacency, but silences the error +#define SILENT_ADJACENCY (1<<9) /// The default mob sprite size (used for shrinking or enlarging the mob sprite to regular size) #define RESIZE_DEFAULT_SIZE 1 diff --git a/code/__DEFINES/obj_flags.dm b/code/__DEFINES/obj_flags.dm index ea5b929651d..9e38eada923 100644 --- a/code/__DEFINES/obj_flags.dm +++ b/code/__DEFINES/obj_flags.dm @@ -2,20 +2,19 @@ #define EMAGGED (1<<0) -#define IN_USE (1<<1) // If we have a user using us, this will be set on. We will check if the user has stopped using us, and thus stop updating and LAGGING EVERYTHING! -#define CAN_BE_HIT (1<<2) //can this be bludgeoned by items? -#define DANGEROUS_POSSESSION (1<<3) //Admin possession yes/no -#define UNIQUE_RENAME (1<<4) // can you customize the description/name of the thing? -#define BLOCK_Z_OUT_DOWN (1<<5) // Should this object block z falling from loc? -#define BLOCK_Z_OUT_UP (1<<6) // Should this object block z uprise from loc? -#define BLOCK_Z_IN_DOWN (1<<7) // Should this object block z falling from above? -#define BLOCK_Z_IN_UP (1<<8) // Should this object block z uprise from below? -#define BLOCKS_CONSTRUCTION (1<<9) //! Does this object prevent things from being built on it? -#define BLOCKS_CONSTRUCTION_DIR (1<<10) //! Does this object prevent same-direction things from being built on it? -#define IGNORE_DENSITY (1<<11) //! Can we ignore density when building on this object? (for example, directional windows and grilles) -#define INFINITE_RESKIN (1<<12) // We can reskin this item infinitely -#define CONDUCTS_ELECTRICITY (1<<13) //! Can this object conduct electricity? -#define NO_DEBRIS_AFTER_DECONSTRUCTION (1<<14) //! Atoms don't spawn anything when deconstructed. They just vanish +#define CAN_BE_HIT (1<<1) //can this be bludgeoned by items? +#define DANGEROUS_POSSESSION (1<<2) //Admin possession yes/no +#define UNIQUE_RENAME (1<<3) // can you customize the description/name of the thing? +#define BLOCK_Z_OUT_DOWN (1<<4) // Should this object block z falling from loc? +#define BLOCK_Z_OUT_UP (1<<5) // Should this object block z uprise from loc? +#define BLOCK_Z_IN_DOWN (1<<6) // Should this object block z falling from above? +#define BLOCK_Z_IN_UP (1<<7) // Should this object block z uprise from below? +#define BLOCKS_CONSTRUCTION (1<<8) //! Does this object prevent things from being built on it? +#define BLOCKS_CONSTRUCTION_DIR (1<<9) //! Does this object prevent same-direction things from being built on it? +#define IGNORE_DENSITY (1<<10) //! Can we ignore density when building on this object? (for example, directional windows and grilles) +#define INFINITE_RESKIN (1<<11) // We can reskin this item infinitely +#define CONDUCTS_ELECTRICITY (1<<12) //! Can this object conduct electricity? +#define NO_DEBRIS_AFTER_DECONSTRUCTION (1<<13) //! Atoms don't spawn anything when deconstructed. They just vanish // If you add new ones, be sure to add them to /obj/Initialize as well for complete mapping support diff --git a/code/__DEFINES/say.dm b/code/__DEFINES/say.dm index 3260dd4d5a8..f82f4af2ddd 100644 --- a/code/__DEFINES/say.dm +++ b/code/__DEFINES/say.dm @@ -111,6 +111,7 @@ #define MAX_CHARTER_LEN 80 #define MAX_PLAQUE_LEN 144 #define MAX_LABEL_LEN 64 +#define MAX_DESC_LEN 280 // Audio/Visual Flags. Used to determine what sense are required to notice a message. #define MSG_VISUAL (1<<0) diff --git a/code/__DEFINES/subsystems.dm b/code/__DEFINES/subsystems.dm index ccd7e4e06bb..d7ec3e4669c 100644 --- a/code/__DEFINES/subsystems.dm +++ b/code/__DEFINES/subsystems.dm @@ -191,6 +191,7 @@ #define INIT_ORDER_MATURITY_GUARD -60 //SKYRAT EDIT ADDITION #define INIT_ORDER_DECAY -61 //SKYRAT EDIT ADDITION #define INIT_ORDER_EXPLOSIONS -69 +#define INIT_ORDER_LOOT -70 #define INIT_ORDER_STATPANELS -97 #define INIT_ORDER_BAN_CACHE -98 #define INIT_ORDER_INIT_PROFILER -99 //Near the end, logs the costs of initialize diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index f5ad67a8c94..6a83f33083a 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -767,6 +767,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_POSTERBOY "poster_boy" #define TRAIT_THROWINGARM "throwing_arm" #define TRAIT_SETTLER "settler" +#define TRAIT_STRONG_STOMACH "strong_stomach" /// This mob always lands on their feet when they fall, for better or for worse. #define TRAIT_CATLIKE_GRACE "catlike_grace" diff --git a/code/__DEFINES/traits/sources.dm b/code/__DEFINES/traits/sources.dm index dcbfab7fe5f..56bcbf3c90d 100644 --- a/code/__DEFINES/traits/sources.dm +++ b/code/__DEFINES/traits/sources.dm @@ -286,8 +286,8 @@ /// Trait from an organ being inside a bodypart #define ORGAN_INSIDE_BODY_TRAIT "organ_inside_body" -/// Trait when something was labelled by a pen. -#define PEN_LABEL_TRAIT "pen_label" +/// Trait when something was labelled by the /datum/element/tool_renaming element. +#define RENAMING_TOOL_LABEL_TRAIT "renaming_tool_label" /// Trait when a drink was renamed by a shaker #define SHAKER_LABEL_TRAIT "shaker_trait" diff --git a/code/__DEFINES/~skyrat_defines/combat.dm b/code/__DEFINES/~skyrat_defines/combat.dm index 5137aa81376..05474bb21d8 100644 --- a/code/__DEFINES/~skyrat_defines/combat.dm +++ b/code/__DEFINES/~skyrat_defines/combat.dm @@ -59,14 +59,13 @@ #define HEADSMASH_BLOCK_ARMOR 20 #define SUPLEX_TIMER 3 SECONDS -/// Skyrat change - alt-clicking a human as another human while grappling them tightly makes you try for grappling-based maneuvers. -/mob/living/carbon/human/AltClick(mob/user) +// alt-clicking a human as another human while grappling them tightly makes you try for grappling-based maneuvers. +/mob/living/carbon/human/click_alt(mob/user) if(!ishuman(user)) return ..() var/mob/living/carbon/human/human_user = user if(human_user == src || !human_user.combat_mode || !human_user.dna.species.try_grab_maneuver(user, src)) - return FALSE - . = ..() + return CLICK_ACTION_BLOCKING /// State check for grab maneuver - because you can't logically suplex a man if you've stopped grappling them. /datum/species/proc/grab_maneuver_state_check(mob/living/carbon/human/user, mob/living/carbon/human/target) diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index f210774ac81..e0f07ab2835 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -152,15 +152,14 @@ DEFINE_BITFIELD(interaction_flags_atom, list( )) DEFINE_BITFIELD(interaction_flags_machine, list( - "INTERACT_MACHINE_ALLOW_SILICON" = INTERACT_MACHINE_ALLOW_SILICON, - "INTERACT_MACHINE_OFFLINE" = INTERACT_MACHINE_OFFLINE, "INTERACT_MACHINE_OPEN" = INTERACT_MACHINE_OPEN, + "INTERACT_MACHINE_OFFLINE" = INTERACT_MACHINE_OFFLINE, + "INTERACT_MACHINE_WIRES_IF_OPEN" = INTERACT_MACHINE_WIRES_IF_OPEN, + "INTERACT_MACHINE_ALLOW_SILICON" = INTERACT_MACHINE_ALLOW_SILICON, "INTERACT_MACHINE_OPEN_SILICON" = INTERACT_MACHINE_OPEN_SILICON, + "INTERACT_MACHINE_REQUIRES_SILICON" = INTERACT_MACHINE_REQUIRES_SILICON, "INTERACT_MACHINE_REQUIRES_SIGHT" = INTERACT_MACHINE_REQUIRES_SIGHT, "INTERACT_MACHINE_REQUIRES_LITERACY" = INTERACT_MACHINE_REQUIRES_LITERACY, - "INTERACT_MACHINE_REQUIRES_SILICON" = INTERACT_MACHINE_REQUIRES_SILICON, - "INTERACT_MACHINE_SET_MACHINE" = INTERACT_MACHINE_SET_MACHINE, - "INTERACT_MACHINE_WIRES_IF_OPEN" = INTERACT_MACHINE_WIRES_IF_OPEN, )) DEFINE_BITFIELD(interaction_flags_item, list( @@ -282,20 +281,20 @@ DEFINE_BITFIELD(movement_type, list( )) DEFINE_BITFIELD(obj_flags, list( - "BLOCK_Z_IN_DOWN" = BLOCK_Z_IN_DOWN, - "BLOCK_Z_IN_UP" = BLOCK_Z_IN_UP, + "EMAGGED" = EMAGGED, + "CAN_BE_HIT" = CAN_BE_HIT, + "DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION, + "UNIQUE_RENAME" = UNIQUE_RENAME, "BLOCK_Z_OUT_DOWN" = BLOCK_Z_OUT_DOWN, "BLOCK_Z_OUT_UP" = BLOCK_Z_OUT_UP, - "BLOCKS_CONSTRUCTION_DIR" = BLOCKS_CONSTRUCTION_DIR, + "BLOCK_Z_IN_DOWN" = BLOCK_Z_IN_DOWN, + "BLOCK_Z_IN_UP" = BLOCK_Z_IN_UP, "BLOCKS_CONSTRUCTION" = BLOCKS_CONSTRUCTION, - "CAN_BE_HIT" = CAN_BE_HIT, - "CONDUCTS_ELECTRICITY" = CONDUCTS_ELECTRICITY, - "DANGEROUS_POSSESSION" = DANGEROUS_POSSESSION, - "EMAGGED" = EMAGGED, + "BLOCKS_CONSTRUCTION_DIR" = BLOCKS_CONSTRUCTION_DIR, "IGNORE_DENSITY" = IGNORE_DENSITY, - "IN_USE" = IN_USE, + "INFINITE_RESKIN" = INFINITE_RESKIN, + "CONDUCTS_ELECTRICITY" = CONDUCTS_ELECTRICITY, "NO_DEBRIS_AFTER_DECONSTRUCTION" = NO_DEBRIS_AFTER_DECONSTRUCTION, - "UNIQUE_RENAME" = UNIQUE_RENAME, )) DEFINE_BITFIELD(pass_flags, list( diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index 83e199cb908..f67ed4f6d07 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -362,6 +362,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_OVERWATCHED" = TRAIT_OVERWATCHED, "TRAIT_OVERWATCH_IMMUNE" = TRAIT_OVERWATCH_IMMUNE, "TRAIT_PACIFISM" = TRAIT_PACIFISM, + "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_PARALYSIS_L_ARM" = TRAIT_PARALYSIS_L_ARM, "TRAIT_PARALYSIS_L_LEG" = TRAIT_PARALYSIS_L_LEG, "TRAIT_PARALYSIS_R_ARM" = TRAIT_PARALYSIS_R_ARM, @@ -418,7 +419,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES, "TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE, "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG, - "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE, "TRAIT_SKITTISH" = TRAIT_SKITTISH, @@ -440,6 +440,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_STASIS" = TRAIT_STASIS, "TRAIT_STIMULATED" = TRAIT_STIMULATED, "TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER, + "TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH, "TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE, "TRAIT_SUCCUMB_OVERRIDE" = TRAIT_SUCCUMB_OVERRIDE, "TRAIT_SUICIDED" = TRAIT_SUICIDED, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index d4c203342e4..5fe6883e82b 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -160,6 +160,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_OIL_FRIED" = TRAIT_OIL_FRIED, "TRAIT_OVERWATCH_IMMUNE" = TRAIT_OVERWATCH_IMMUNE, "TRAIT_PACIFISM" = TRAIT_PACIFISM, + "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_PARALYSIS_L_ARM" = TRAIT_PARALYSIS_L_ARM, "TRAIT_PARALYSIS_L_LEG" = TRAIT_PARALYSIS_L_LEG, "TRAIT_PARALYSIS_R_ARM" = TRAIT_PARALYSIS_R_ARM, @@ -192,7 +193,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_SHIFTY_EYES" = TRAIT_SHIFTY_EYES, "TRAIT_SHOCKIMMUNE" = TRAIT_SHOCKIMMUNE, "TRAIT_SIGN_LANG" = TRAIT_SIGN_LANG, - "TRAIT_PAPER_MASTER" = TRAIT_PAPER_MASTER, "TRAIT_SILENT_FOOTSTEPS" = TRAIT_SILENT_FOOTSTEPS, "TRAIT_SIXTHSENSE" = TRAIT_SIXTHSENSE, "TRAIT_SKITTISH" = TRAIT_SKITTISH, @@ -206,6 +206,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_STABLEHEART" = TRAIT_STABLEHEART, "TRAIT_STABLELIVER" = TRAIT_STABLELIVER, "TRAIT_STRONG_GRABBER" = TRAIT_STRONG_GRABBER, + "TRAIT_STRONG_STOMACH" = TRAIT_STRONG_STOMACH, "TRAIT_STUNIMMUNE" = TRAIT_STUNIMMUNE, "TRAIT_SURGEON" = TRAIT_SURGEON, "TRAIT_SURGICALLY_ANALYZED" = TRAIT_SURGICALLY_ANALYZED, diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index c8c35b30847..0a6e54a46e0 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -55,7 +55,7 @@ ShiftClickOn(A) return if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) - AltClickOn(A) + A.ai_click_alt(src) return if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) @@ -120,8 +120,6 @@ /mob/living/silicon/ai/CtrlClickOn(atom/target) target.AICtrlClick(src) -/mob/living/silicon/ai/AltClickOn(atom/target) - target.AIAltClick(src) /* The following criminally helpful code is just the previous code cleaned up; @@ -133,8 +131,7 @@ /atom/proc/AICtrlClick(mob/living/silicon/ai/user) return -/atom/proc/AIAltClick(mob/living/silicon/ai/user) - AltClick(user) +/atom/proc/ai_click_alt(mob/living/silicon/ai/user) return /atom/proc/AIShiftClick(mob/living/silicon/ai/user) @@ -150,7 +147,7 @@ toggle_bolt(user) add_hiddenprint(user) -/obj/machinery/door/airlock/AIAltClick(mob/living/silicon/ai/user) // Eletrifies doors. +/obj/machinery/door/airlock/ai_click_alt(mob/living/silicon/ai/user) if(obj_flags & EMAGGED) return @@ -219,7 +216,7 @@ update() /// Toggle APC equipment settings -/obj/machinery/power/apc/AIAltClick(mob/living/silicon/ai/user) +/obj/machinery/power/apc/ai_click_alt(mob/living/silicon/ai/user) if(!can_use(user, loud = TRUE)) return @@ -243,7 +240,7 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN /* AI Turrets */ -/obj/machinery/turretid/AIAltClick(mob/living/silicon/ai/user) //toggles lethal on turrets +/obj/machinery/turretid/ai_click_alt(mob/living/silicon/ai/user) //toggles lethal on turrets if(ailock) return toggle_lethal(user) @@ -254,7 +251,7 @@ toggle_on(user) /* Holopads */ -/obj/machinery/holopad/AIAltClick(mob/living/silicon/ai/user) +/obj/machinery/holopad/ai_click_alt(mob/living/silicon/ai/user) if (user) balloon_alert(user, "disrupted all active calls") add_hiddenprint(user) diff --git a/code/_onclick/click.dm b/code/_onclick/click.dm index 566256824bf..af391efc859 100644 --- a/code/_onclick/click.dm +++ b/code/_onclick/click.dm @@ -96,7 +96,7 @@ if(LAZYACCESS(modifiers, RIGHT_CLICK)) alt_click_on_secondary(A) else - AltClickOn(A) + base_click_alt(A) return if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) @@ -385,24 +385,6 @@ A.CtrlClick(src) return -/** - * Alt click - * Unused except for AI - */ -/mob/proc/AltClickOn(atom/A) - . = SEND_SIGNAL(src, COMSIG_MOB_ALTCLICKON, A) - if(. & COMSIG_MOB_CANCEL_CLICKON) - return - A.AltClick(src) - -/atom/proc/AltClick(mob/user) - if(!user.can_interact_with(src)) - return FALSE - if(SEND_SIGNAL(src, COMSIG_CLICK_ALT, user) & COMPONENT_CANCEL_CLICK_ALT) - return - var/turf/T = get_turf(src) - if(T && (isturf(loc) || isturf(src)) && user.TurfAdjacent(T) && !HAS_TRAIT(user, TRAIT_MOVE_VENTCRAWLING)) - user.set_listed_turf(T) ///The base proc of when something is right clicked on when alt is held - generally use alt_click_secondary instead /atom/proc/alt_click_on_secondary(atom/A) @@ -421,14 +403,8 @@ user.client.toggle_tag_datum(src) return -/// Use this instead of [/mob/proc/AltClickOn] where you only want turf content listing without additional atom alt-click interaction -/atom/proc/AltClickNoInteract(mob/user, atom/A) - var/turf/T = get_turf(A) - if(T && user.TurfAdjacent(T)) - user.set_listed_turf(T) - -/mob/proc/TurfAdjacent(turf/T) - return T.Adjacent(src) +/mob/proc/TurfAdjacent(turf/tile) + return tile.Adjacent(src) /** * Control+Shift click diff --git a/code/_onclick/click_alt.dm b/code/_onclick/click_alt.dm new file mode 100644 index 00000000000..16c8e843995 --- /dev/null +++ b/code/_onclick/click_alt.dm @@ -0,0 +1,66 @@ +/** + * ### Base proc for alt click interaction. + * + * If you wish to add custom `click_alt` behavior for a single type, use that proc. + */ +/mob/proc/base_click_alt(atom/target) + SHOULD_NOT_OVERRIDE(TRUE) + + var/turf/tile = isturf(target) ? target : get_turf(target) + + if(isobserver(src) || isrevenant(src)) + open_lootpanel(tile) + return + + if(!isturf(target) && can_perform_action(target, (target.interaction_flags_click | SILENT_ADJACENCY))) + if(SEND_SIGNAL(target, COMSIG_CLICK_ALT, src) & CLICK_ACTION_ANY) + return + + if(target.click_alt(src) & CLICK_ACTION_ANY) + return + + open_lootpanel(tile) + + +/// Helper for opening the lootpanel +/mob/proc/open_lootpanel(turf/target) + if(HAS_TRAIT(src, TRAIT_MOVE_VENTCRAWLING)) + return + + var/datum/lootpanel/panel = client?.loot_panel + if(isnull(panel)) + return + + panel.open(target) + + +/** + * ## Custom alt click interaction + * Override this to change default alt click behavior. Return `CLICK_ACTION_SUCCESS`, `CLICK_ACTION_BLOCKING` or `NONE`. + * + * ### Guard clauses + * Consider adding `interaction_flags_click` before adding unique guard clauses. + * + * ### Return flags + * Forgetting your return will cause the default alt click behavior to occur thereafter. + * + * The difference between NONE and BLOCKING can get hazy, but I like to keep NONE limited to guard clauses and "never" cases. + * + * A good usage for BLOCKING over NONE is when it's situational for the item and there's some feedback indicating this. + * + * ### Examples: + * User is a ghost, alt clicks on item with special disk eject: NONE + * + * Machine broken, no feedback: NONE + * + * Alt click a pipe to max output but its already max: BLOCKING + * + * Alt click a gun that normally works, but is out of ammo: BLOCKING + * + * User unauthorized, machine beeps: BLOCKING + * + * @param {mob} user - The person doing the alt clicking. + */ +/atom/proc/click_alt(mob/user) + SHOULD_CALL_PARENT(FALSE) + return NONE diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 8cad6f772e0..4f06e15f2cd 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -31,7 +31,7 @@ MiddleClickOn(A, params) return if(LAZYACCESS(modifiers, ALT_CLICK)) // alt and alt-gr (rightalt) - AltClickOn(A) + A.borg_click_alt(src) return if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) @@ -89,7 +89,7 @@ if(after_attack_secondary_result == SECONDARY_ATTACK_CALL_NORMAL) W.afterattack(A, src, FALSE, params) - else + else W.afterattack(A, src, FALSE, params) //Give cyborgs hotkey clicks without breaking existing uses of hotkey clicks @@ -103,8 +103,6 @@ /mob/living/silicon/robot/CtrlClickOn(atom/target) target.BorgCtrlClick(src) -/mob/living/silicon/robot/AltClickOn(atom/target) - target.BorgAltClick(src) /atom/proc/BorgCtrlShiftClick(mob/living/silicon/robot/user) //forward to human click if not overridden CtrlShiftClick(user) @@ -152,9 +150,9 @@ else ..() -/obj/machinery/power/apc/BorgAltClick(mob/living/silicon/robot/user) +/obj/machinery/power/apc/borg_click_alt(mob/living/silicon/robot/user) if(get_dist(src, user) <= user.interaction_range && !(user.control_disabled)) - AIAltClick(user) + ai_click_alt(user) else ..() @@ -171,19 +169,19 @@ else ..() -/atom/proc/BorgAltClick(mob/living/silicon/robot/user) - AltClick(user) +/atom/proc/borg_click_alt(mob/living/silicon/robot/user) + user.base_click_alt(src) return -/obj/machinery/door/airlock/BorgAltClick(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. +/obj/machinery/door/airlock/borg_click_alt(mob/living/silicon/robot/user) // Eletrifies doors. Forwards to AI code. if(get_dist(src, user) <= user.interaction_range && !(user.control_disabled)) - AIAltClick(user) + ai_click_alt(user) else ..() -/obj/machinery/turretid/BorgAltClick(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. +/obj/machinery/turretid/borg_click_alt(mob/living/silicon/robot/user) //turret lethal on/off. Forwards to AI code. if(get_dist(src, user) <= user.interaction_range && !(user.control_disabled)) - AIAltClick(user) + ai_click_alt(user) else ..() diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index a3f886e656f..a1ca5ebf85a 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -11,7 +11,7 @@ var/list/modifiers = params2list(params) var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK) - var/item_interact_result = target.item_interaction(user, src, modifiers, is_right_clicking) + var/item_interact_result = target.base_item_interaction(user, src, modifiers) if(item_interact_result & ITEM_INTERACT_SUCCESS) return TRUE if(item_interact_result & ITEM_INTERACT_BLOCKING) @@ -52,10 +52,6 @@ if (attackby_result) return TRUE - if(QDELETED(src) || QDELETED(target)) - attack_qdeleted(target, user, TRUE, params) - return TRUE - if (is_right_clicking) var/after_attack_secondary_result = afterattack_secondary(target, user, TRUE, params) @@ -163,7 +159,7 @@ return FALSE return attacking_item.attack_atom(src, user, params) -/mob/living/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/mob/living/item_interaction(mob/living/user, obj/item/tool, list/modifiers) // Surgery and such happens very high up in the interaction chain, before parent call var/attempt_tending = item_tending(user, tool, modifiers) if(attempt_tending & ITEM_INTERACT_ANY_BLOCKER) @@ -472,11 +468,6 @@ return SECONDARY_ATTACK_CALL_NORMAL -/// Called if the target gets deleted by our attack -/obj/item/proc/attack_qdeleted(atom/target, mob/user, proximity_flag, click_parameters) - SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_QDELETED, target, user, proximity_flag, click_parameters) - SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK_QDELETED, target, user, proximity_flag, click_parameters) - /obj/item/proc/get_clamped_volume() if(w_class) if(force) diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 38d99a75e6c..ae2ebd97632 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -31,7 +31,7 @@ MiddleClickOn(A, params) return if(LAZYACCESS(modifiers, ALT_CLICK)) - AltClickNoInteract(src, A) + base_click_alt(A) return if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) diff --git a/code/_onclick/overmind.dm b/code/_onclick/overmind.dm index d6b8994f82f..900ad59bde2 100644 --- a/code/_onclick/overmind.dm +++ b/code/_onclick/overmind.dm @@ -10,7 +10,7 @@ ShiftClickOn(A) return if(LAZYACCESS(modifiers, ALT_CLICK)) - AltClickOn(A) + blob_click_alt(A) return if(LAZYACCESS(modifiers, CTRL_CLICK)) CtrlClickOn(A) @@ -30,7 +30,7 @@ if(T) create_shield(T) -/mob/camera/blob/AltClickOn(atom/A) //Remove a blob +/mob/camera/blob/proc/blob_click_alt(atom/A) //Remove a blob var/turf/T = get_turf(A) if(T) remove_blob(T) diff --git a/code/controllers/subsystem/processing/obj_tab_items.dm b/code/controllers/subsystem/processing/obj_tab_items.dm deleted file mode 100644 index 53786daf011..00000000000 --- a/code/controllers/subsystem/processing/obj_tab_items.dm +++ /dev/null @@ -1,24 +0,0 @@ -PROCESSING_SUBSYSTEM_DEF(obj_tab_items) - name = "Obj Tab Items" - flags = SS_NO_INIT - runlevels = RUNLEVEL_LOBBY | RUNLEVELS_DEFAULT - wait = 0.1 SECONDS - -// I know this is mostly copypasta, but I want to change the processing logic -// Sorry bestie :( -/datum/controller/subsystem/processing/obj_tab_items/fire(resumed = FALSE) - if (!resumed) - currentrun = processing.Copy() - //cache for sanic speed (lists are references anyways) - var/list/current_run = currentrun - - while(current_run.len) - var/datum/thing = current_run[current_run.len] - if(QDELETED(thing)) - processing -= thing - else if(thing.process(wait * 0.1) == PROCESS_KILL) - // fully stop so that a future START_PROCESSING will work - STOP_PROCESSING(src, thing) - if (MC_TICK_CHECK) - return - current_run.len-- diff --git a/code/controllers/subsystem/statpanel.dm b/code/controllers/subsystem/statpanel.dm index 1576c342273..7b1394ada16 100644 --- a/code/controllers/subsystem/statpanel.dm +++ b/code/controllers/subsystem/statpanel.dm @@ -110,12 +110,6 @@ SUBSYSTEM_DEF(statpanels) if(update_actions && num_fires % default_wait == 0) set_action_tabs(target, target_mob) - // Handle the examined turf of the stat panel, if it's been long enough, or if we've generated new images for it - var/turf/listed_turf = target_mob?.listed_turf - if(listed_turf && num_fires % default_wait == 0) - if(target.stat_tab == listed_turf.name || !(listed_turf.name in target.panel_tabs)) - set_turf_examine_tab(target, target_mob) - if(MC_TICK_CHECK) return @@ -188,71 +182,6 @@ SUBSYSTEM_DEF(statpanels) target.stat_panel.send_message("update_spells", list(spell_tabs = target.spell_tabs, actions = actions)) -/datum/controller/subsystem/statpanels/proc/set_turf_examine_tab(client/target, mob/target_mob) - var/list/overrides = list() - for(var/image/target_image as anything in target.images) - if(!target_image.loc || target_image.loc.loc != target_mob.listed_turf || !target_image.override) - continue - overrides += target_image.loc - - var/list/atoms_to_display = list(target_mob.listed_turf) - for(var/atom/movable/turf_content as anything in target_mob.listed_turf) - if(turf_content.mouse_opacity == MOUSE_OPACITY_TRANSPARENT) - continue - if(turf_content.invisibility > target_mob.see_invisible) - continue - if(turf_content in overrides) - continue - if(turf_content.IsObscured()) - continue - atoms_to_display += turf_content - - /// Set the atoms we're meant to display - var/datum/object_window_info/obj_window = target.obj_window - obj_window.atoms_to_show = atoms_to_display - START_PROCESSING(SSobj_tab_items, obj_window) - refresh_client_obj_view(target) - -/datum/controller/subsystem/statpanels/proc/refresh_client_obj_view(client/refresh) - var/list/turf_items = return_object_images(refresh) - if(!length(turf_items) || !refresh.mob?.listed_turf) - return - refresh.stat_panel.send_message("update_listedturf", turf_items) - -#define OBJ_IMAGE_LOADING "statpanels obj loading temporary" -/// Returns all our ready object tab images -/// Returns a list in the form list(list(object_name, object_ref, loaded_image), ...) -/datum/controller/subsystem/statpanels/proc/return_object_images(client/load_from) - // You might be inclined to think that this is a waste of cpu time, since we - // A: Double iterate over atoms in the build case, or - // B: Generate these lists over and over in the refresh case - // It's really not very hot. The hot portion of this code is genuinely mostly in the image generation - // So it's ok to pay a performance cost for cleanliness here - - // No turf? go away - if(!load_from.mob?.listed_turf) - return list() - var/datum/object_window_info/obj_window = load_from.obj_window - var/list/already_seen = obj_window.atoms_to_images - var/list/to_make = obj_window.atoms_to_imagify - var/list/turf_items = list() - for(var/atom/turf_item as anything in obj_window.atoms_to_show) - // First, we fill up the list of refs to display - // If we already have one, just use that - var/existing_image = already_seen[turf_item] - if(existing_image == OBJ_IMAGE_LOADING) - continue - // We already have it. Success! - if(existing_image) - turf_items[++turf_items.len] = list("[turf_item.name]", REF(turf_item), existing_image) - continue - // Now, we're gonna queue image generation out of those refs - to_make += turf_item - already_seen[turf_item] = OBJ_IMAGE_LOADING - obj_window.RegisterSignal(turf_item, COMSIG_QDELETING, TYPE_PROC_REF(/datum/object_window_info,viewing_atom_deleted)) // we reset cache if anything in it gets deleted - return turf_items - -#undef OBJ_IMAGE_LOADING /datum/controller/subsystem/statpanels/proc/generate_mc_data() mc_data = list( @@ -294,16 +223,6 @@ SUBSYSTEM_DEF(statpanels) set_action_tabs(target, target_mob) return TRUE - // Handle turfs - - if(target_mob?.listed_turf) - if(!target_mob.TurfAdjacent(target_mob.listed_turf)) - target_mob.set_listed_turf(null) - - else if(target.stat_tab == target_mob?.listed_turf.name || !(target_mob?.listed_turf.name in target.panel_tabs)) - set_turf_examine_tab(target, target_mob) - return TRUE - if(!target.holder) return FALSE @@ -323,105 +242,3 @@ SUBSYSTEM_DEF(statpanels) /// Stat panel window declaration /client/var/datum/tgui_window/stat_panel - -/// Datum that holds and tracks info about a client's object window -/// Really only exists because I want to be able to do logic with signals -/// And need a safe place to do the registration -/datum/object_window_info - /// list of atoms to show to our client via the object tab, at least currently - var/list/atoms_to_show = list() - /// list of atom -> image string for objects we have had in the right click tab - /// this is our caching - var/list/atoms_to_images = list() - /// list of atoms to turn into images for the object tab - var/list/atoms_to_imagify = list() - /// Our owner client - var/client/parent - /// Are we currently tracking a turf? - var/actively_tracking = FALSE - -/datum/object_window_info/New(client/parent) - . = ..() - src.parent = parent - -/datum/object_window_info/Destroy(force) - atoms_to_show = null - atoms_to_images = null - atoms_to_imagify = null - parent.obj_window = null - parent = null - STOP_PROCESSING(SSobj_tab_items, src) - return ..() - -/// Takes a client, attempts to generate object images for it -/// We will update the client with any improvements we make when we're done -/datum/object_window_info/process(seconds_per_tick) - // Cache the datum access for sonic speed - var/list/to_make = atoms_to_imagify - var/list/newly_seen = atoms_to_images - var/index = 0 - for(index in 1 to length(to_make)) - var/atom/thing = to_make[index] - - var/generated_string - if(ismob(thing) || length(thing.overlays) > 2) - generated_string = costly_icon2html(thing, parent, sourceonly=TRUE) - else - generated_string = icon2html(thing, parent, sourceonly=TRUE) - - newly_seen[thing] = generated_string - if(TICK_CHECK) - to_make.Cut(1, index + 1) - index = 0 - break - // If we've not cut yet, do it now - if(index) - to_make.Cut(1, index + 1) - SSstatpanels.refresh_client_obj_view(parent) - if(!length(to_make)) - return PROCESS_KILL - -/datum/object_window_info/proc/start_turf_tracking() - if(actively_tracking) - stop_turf_tracking() - var/static/list/connections = list( - COMSIG_MOVABLE_MOVED = PROC_REF(on_mob_move), - COMSIG_MOB_LOGOUT = PROC_REF(on_mob_logout), - ) - AddComponent(/datum/component/connect_mob_behalf, parent, connections) - actively_tracking = TRUE - -/datum/object_window_info/proc/stop_turf_tracking() - qdel(GetComponent(/datum/component/connect_mob_behalf)) - actively_tracking = FALSE - -/datum/object_window_info/proc/on_mob_move(mob/source) - SIGNAL_HANDLER - var/turf/listed = source.listed_turf - if(!listed || !source.TurfAdjacent(listed)) - source.set_listed_turf(null) - -/datum/object_window_info/proc/on_mob_logout(mob/source) - SIGNAL_HANDLER - on_mob_move(parent.mob) - -/// Clears any cached object window stuff -/// We use hard refs cause we'd need a signal for this anyway. Cleaner this way -/datum/object_window_info/proc/viewing_atom_deleted(atom/deleted) - SIGNAL_HANDLER - atoms_to_show -= deleted - atoms_to_imagify -= deleted - atoms_to_images -= deleted - -/mob/proc/set_listed_turf(turf/new_turf) - listed_turf = new_turf - if(!client) - return - if(!client.obj_window) - client.obj_window = new(client) - if(listed_turf) - client.stat_panel.send_message("create_listedturf", listed_turf.name) - client.obj_window.start_turf_tracking() - else - client.stat_panel.send_message("remove_listedturf") - client.obj_window.stop_turf_tracking() diff --git a/code/controllers/subsystem/tgui.dm b/code/controllers/subsystem/tgui.dm index c12c1b2bb32..cd03e1f3e52 100644 --- a/code/controllers/subsystem/tgui.dm +++ b/code/controllers/subsystem/tgui.dm @@ -122,8 +122,6 @@ SUBSYSTEM_DEF(tgui) for(var/datum/tgui/ui in user.tgui_open_uis) if(ui.window && ui.window.id == window_id) ui.close(can_be_suspended = FALSE) - // Unset machine just to be sure. - user.unset_machine() // Close window directly just to be sure. user << browse(null, "window=[window_id]") diff --git a/code/datums/ai/oldhostile/hostile_tameable.dm b/code/datums/ai/oldhostile/hostile_tameable.dm index 5c96eca17da..d76ffb8a282 100644 --- a/code/datums/ai/oldhostile/hostile_tameable.dm +++ b/code/datums/ai/oldhostile/hostile_tameable.dm @@ -108,7 +108,7 @@ return if(!istype(clicker) || blackboard[BB_HOSTILE_FRIEND] != clicker) return - . = COMPONENT_CANCEL_CLICK_ALT + . = CLICK_ACTION_BLOCKING INVOKE_ASYNC(src, PROC_REF(command_radial), clicker) /// Show the command radial menu diff --git a/code/datums/browser.dm b/code/datums/browser.dm index 08099b46f1a..f74ecf6c5a3 100644 --- a/code/datums/browser.dm +++ b/code/datums/browser.dm @@ -475,7 +475,3 @@ src.Topic(href, params2list(href), hsrc) // this will direct to the atom's return // Topic() proc via client.Topic() - // no atomref specified (or not found) - // so just reset the user mob's machine var - if(src?.mob) - src.mob.unset_machine() diff --git a/code/datums/components/gps.dm b/code/datums/components/gps.dm index ceee193bf4a..7e52f00def7 100644 --- a/code/datums/components/gps.dm +++ b/code/datums/components/gps.dm @@ -50,7 +50,7 @@ GLOBAL_LIST_EMPTY(GPS_list) if(!emp_proof) RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp_act)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_AltClick)) + RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_click_alt)) ///Called on COMSIG_ITEM_ATTACK_SELF /datum/component/gps/item/proc/interact(datum/source, mob/user) @@ -85,10 +85,11 @@ GLOBAL_LIST_EMPTY(GPS_list) A.add_overlay("working") ///Calls toggletracking -/datum/component/gps/item/proc/on_AltClick(datum/source, mob/user) +/datum/component/gps/item/proc/on_click_alt(datum/source, mob/user) SIGNAL_HANDLER toggletracking(user) + return CLICK_ACTION_SUCCESS ///Toggles the tracking for the gps /datum/component/gps/item/proc/toggletracking(mob/user) @@ -153,7 +154,7 @@ GLOBAL_LIST_EMPTY(GPS_list) data["signals"] = signals return data -/datum/component/gps/item/ui_act(action, params) +/datum/component/gps/item/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return @@ -162,7 +163,8 @@ GLOBAL_LIST_EMPTY(GPS_list) if("rename") var/atom/parentasatom = parent var/a = tgui_input_text(usr, "Enter the desired tag", "GPS Tag", gpstag, 20) - + if (QDELETED(ui) || ui.status != UI_INTERACTIVE) + return if (!a) return diff --git a/code/datums/components/infective.dm b/code/datums/components/infective.dm index 97df6342aeb..fc2081481d9 100644 --- a/code/datums/components/infective.dm +++ b/code/datums/components/infective.dm @@ -59,8 +59,10 @@ /datum/component/infective/proc/try_infect_eat(datum/source, mob/living/eater, mob/living/feeder) SIGNAL_HANDLER - if(!eater.has_quirk(/datum/quirk/deviant_tastes)) - eater.add_mood_event("disgust", /datum/mood_event/disgust/dirty_food) + if(HAS_TRAIT(eater, TRAIT_STRONG_STOMACH)) + return + + eater.add_mood_event("disgust", /datum/mood_event/disgust/dirty_food) if(is_weak && !prob(weak_infection_chance)) return @@ -76,6 +78,9 @@ /datum/component/infective/proc/try_infect_drink(datum/source, mob/living/drinker, mob/living/feeder) SIGNAL_HANDLER + if(HAS_TRAIT(drinker, TRAIT_STRONG_STOMACH)) + return + var/appendage_zone = feeder.held_items.Find(source) appendage_zone = appendage_zone == 0 ? BODY_ZONE_CHEST : appendage_zone % 2 ? BODY_ZONE_R_ARM : BODY_ZONE_L_ARM try_infect(feeder, appendage_zone) diff --git a/code/datums/components/martial_art_giver.dm b/code/datums/components/martial_art_giver.dm new file mode 100644 index 00000000000..1a0bfa9951a --- /dev/null +++ b/code/datums/components/martial_art_giver.dm @@ -0,0 +1,45 @@ +/// when equipped and unequipped this item gives a martial art +/datum/component/martial_art_giver + /// the style we give + var/datum/martial_art/style + +/datum/component/martial_art_giver/Initialize(style_type) + if(!isitem(parent)) + return COMPONENT_INCOMPATIBLE + + style = new style_type() + style.allow_temp_override = FALSE + +/datum/component/martial_art_giver/RegisterWithParent() + RegisterSignal(parent, COMSIG_ITEM_EQUIPPED, PROC_REF(equipped)) + RegisterSignal(parent, COMSIG_ITEM_DROPPED, PROC_REF(dropped)) + +/datum/component/martial_art_giver/UnregisterFromParent(datum/source) + UnregisterSignal(parent, list(COMSIG_ITEM_POST_EQUIPPED, COMSIG_ITEM_POST_UNEQUIP)) + var/obj/item/parent_item = parent + if(ismob(parent_item?.loc)) + UnregisterSignal(parent, list(COMSIG_MOB_MIND_TRANSFERRED_INTO, COMSIG_MOB_MIND_INITIALIZED, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF)) + QDEL_NULL(style) + +/datum/component/martial_art_giver/proc/equipped(obj/item/source, mob/user, slot) + SIGNAL_HANDLER + if(!(source.slot_flags & slot)) + return + RegisterSignals(user, list(COMSIG_MOB_MIND_TRANSFERRED_INTO, COMSIG_MOB_MIND_INITIALIZED), PROC_REF(teach)) + RegisterSignal(user, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF, PROC_REF(forget)) + teach(user) + +/datum/component/martial_art_giver/proc/dropped(obj/item/source, mob/user) + SIGNAL_HANDLER + forget(user) + UnregisterSignal(user, list(COMSIG_MOB_MIND_TRANSFERRED_INTO, COMSIG_MOB_MIND_INITIALIZED, COMSIG_MOB_MIND_TRANSFERRED_OUT_OF)) + +/datum/component/martial_art_giver/proc/teach(mob/source) + if(isnull(style)) + return + style.teach(source, TRUE) + +/datum/component/martial_art_giver/proc/forget(mob/source) + if(isnull(style)) + return + style.fully_remove(source) diff --git a/code/datums/components/material/material_container.dm b/code/datums/components/material/material_container.dm index a17e642e039..ffcf81feace 100644 --- a/code/datums/components/material/material_container.dm +++ b/code/datums/components/material/material_container.dm @@ -678,6 +678,7 @@ while(sheet_amt > 0) //don't merge yet. we need to do stuff with it first var/obj/item/stack/sheet/new_sheets = new material.sheet_type(target, min(sheet_amt, MAX_STACK_SIZE), FALSE) + new_sheets.manufactured = TRUE count += new_sheets.amount //use material & deduct work needed use_amount_mat(new_sheets.amount * SHEET_MATERIAL_AMOUNT, material) diff --git a/code/datums/components/pet_commands/obeys_commands.dm b/code/datums/components/pet_commands/obeys_commands.dm index 2fceaa2b337..ec3a04c940a 100644 --- a/code/datums/components/pet_commands/obeys_commands.dm +++ b/code/datums/components/pet_commands/obeys_commands.dm @@ -72,6 +72,7 @@ return // Not our friend, can't boss us around INVOKE_ASYNC(src, PROC_REF(display_radial_menu), clicker) + return CLICK_ACTION_SUCCESS /// Actually display the radial menu and then do something with the result /datum/component/obeys_commands/proc/display_radial_menu(mob/living/clicker) diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index 7c55579c999..f872c6bfd69 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -59,6 +59,7 @@ /datum/component/simple_rotation/proc/rotate_left(datum/source, mob/user) SIGNAL_HANDLER rotate(user, ROTATION_COUNTERCLOCKWISE) + return CLICK_ACTION_SUCCESS /datum/component/simple_rotation/proc/rotate(mob/user, degrees) if(QDELETED(user)) diff --git a/code/datums/components/style/style_meter.dm b/code/datums/components/style/style_meter.dm index 72688f41c52..c06fc35aca3 100644 --- a/code/datums/components/style/style_meter.dm +++ b/code/datums/components/style/style_meter.dm @@ -27,7 +27,7 @@ . = ..() . += span_notice("You feel like a multitool could be used on this.") -/obj/item/style_meter/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/style_meter/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!istype(interacting_with, /obj/item/clothing/glasses)) return NONE @@ -38,7 +38,7 @@ RegisterSignal(interacting_with, COMSIG_ITEM_EQUIPPED, PROC_REF(check_wearing)) RegisterSignal(interacting_with, COMSIG_ITEM_DROPPED, PROC_REF(on_drop)) RegisterSignal(interacting_with, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) - RegisterSignal(interacting_with, COMSIG_CLICK_ALT, PROC_REF(on_altclick)) + RegisterSignal(interacting_with, COMSIG_CLICK_ALT, PROC_REF(on_click_alt)) RegisterSignal(interacting_with, COMSIG_ATOM_TOOL_ACT(TOOL_MULTITOOL), PROC_REF(redirect_multitool)) balloon_alert(user, "style meter attached") playsound(src, 'sound/machines/click.ogg', 30, TRUE) @@ -90,14 +90,15 @@ /// Signal proc to remove from glasses -/obj/item/style_meter/proc/on_altclick(datum/source, mob/user) +/obj/item/style_meter/proc/on_click_alt(datum/source, mob/user) SIGNAL_HANDLER - if(istype(loc, /obj/item/clothing/glasses)) - clean_up() - forceMove(get_turf(src)) + if(!istype(loc, /obj/item/clothing/glasses)) + return CLICK_ACTION_BLOCKING - return COMPONENT_CANCEL_CLICK_ALT + clean_up() + forceMove(get_turf(src)) + return CLICK_ACTION_SUCCESS /obj/item/style_meter/multitool_act(mob/living/user, obj/item/tool) multitooled = !multitooled diff --git a/code/datums/components/toggle_suit.dm b/code/datums/components/toggle_suit.dm index 596bf3b3252..c4a378a16de 100644 --- a/code/datums/components/toggle_suit.dm +++ b/code/datums/components/toggle_suit.dm @@ -20,7 +20,7 @@ src.base_icon_state = atom_parent.base_icon_state || atom_parent.icon_state /datum/component/toggle_icon/RegisterWithParent() - RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_alt_click)) + RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(on_click_alt)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) /datum/component/toggle_icon/UnregisterFromParent() @@ -34,7 +34,7 @@ * source - the atom being clicked on * user - the mob doing the click */ -/datum/component/toggle_icon/proc/on_alt_click(atom/source, mob/user) +/datum/component/toggle_icon/proc/on_click_alt(atom/source, mob/user) SIGNAL_HANDLER if(!isliving(user)) @@ -47,13 +47,14 @@ if(living_user.incapacitated()) source.balloon_alert(user, "you're incapacitated!") - return + return CLICK_ACTION_BLOCKING if(living_user.usable_hands <= 0) source.balloon_alert(user, "you don't have hands!") - return + return CLICK_ACTION_BLOCKING do_icon_toggle(source, living_user) + return CLICK_ACTION_SUCCESS /* * Signal proc for COMSIG_ATOM_EXAMINE. diff --git a/code/datums/elements/attack_zone_randomiser.dm b/code/datums/elements/attack_zone_randomiser.dm new file mode 100644 index 00000000000..35275e11a9b --- /dev/null +++ b/code/datums/elements/attack_zone_randomiser.dm @@ -0,0 +1,33 @@ +/// Pick a random attack zone before you attack something +/datum/element/attack_zone_randomiser + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + /// List of attack zones you can select, should be a subset of GLOB.all_body_zones + var/list/valid_attack_zones + +/datum/element/attack_zone_randomiser/Attach(datum/target, list/valid_attack_zones = GLOB.all_body_zones) + . = ..() + if (!isliving(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignals(target, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_UNARMED_ATTACK), PROC_REF(randomise)) + src.valid_attack_zones = valid_attack_zones + +/datum/element/attack_zone_randomiser/Detach(datum/source) + UnregisterSignal(source, list (COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_UNARMED_ATTACK)) + return ..() + +/// If we're attacking a carbon, pick a random defence zone +/datum/element/attack_zone_randomiser/proc/randomise(mob/living/source, atom/target) + SIGNAL_HANDLER + if (!iscarbon(target)) + return + var/mob/living/living_target = target + var/list/blacklist_zones = GLOB.all_body_zones - valid_attack_zones + var/new_zone = living_target.get_random_valid_zone(blacklisted_parts = blacklist_zones, bypass_warning = TRUE) + if (isnull(new_zone)) + new_zone = BODY_ZONE_CHEST + var/atom/movable/screen/zone_sel/zone_selector = source.hud_used?.zone_select + if (isnull(zone_selector)) + source.zone_selected = new_zone + else + zone_selector.set_selected_zone(new_zone, source, should_log = FALSE) diff --git a/code/datums/elements/living_limb_initialiser.dm b/code/datums/elements/living_limb_initialiser.dm new file mode 100644 index 00000000000..943b39dcf37 --- /dev/null +++ b/code/datums/elements/living_limb_initialiser.dm @@ -0,0 +1,19 @@ +/// Spawns a living limb mob inside a limb upon attachment if it doesn't have one +/datum/element/living_limb_initialiser + +/datum/element/living_limb_initialiser/Attach(atom/target) + . = ..() + if(!isbodypart(target)) + return ELEMENT_INCOMPATIBLE + RegisterSignal(target, COMSIG_BODYPART_CHANGED_OWNER, PROC_REF(try_animate_limb)) + +/datum/element/living_limb_initialiser/Detach(atom/target) + UnregisterSignal(target, COMSIG_BODYPART_CHANGED_OWNER) + return ..() + +/// Create a living limb mob inside the limb if it doesn't already have one +/datum/element/living_limb_initialiser/proc/try_animate_limb(obj/item/bodypart/part) + SIGNAL_HANDLER + if (locate(/mob/living/basic/living_limb_flesh) in part) + return + new /mob/living/basic/living_limb_flesh(part, part) diff --git a/code/datums/elements/poster_tearer.dm b/code/datums/elements/poster_tearer.dm new file mode 100644 index 00000000000..7a784a48615 --- /dev/null +++ b/code/datums/elements/poster_tearer.dm @@ -0,0 +1,45 @@ +/// Allows mobs with this element attached to just simply tear down any poster they desire to. +/datum/element/poster_tearer + element_flags = ELEMENT_BESPOKE + argument_hash_start_idx = 2 + /// The amount of time it takes to tear down a poster. + var/tear_time + /// Interaction key to use whilst tearing down a poster. + var/interaction_key + +/datum/element/poster_tearer/Attach(datum/target, tear_time = 2 SECONDS, interaction_key = null) + . = ..() + if (!isliving(target)) + return ELEMENT_INCOMPATIBLE + + src.tear_time = tear_time + src.interaction_key = interaction_key + + RegisterSignals(target, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_UNARMED_ATTACK), PROC_REF(on_attacked_poster)) + +/datum/element/poster_tearer/Detach(datum/source) + . = ..() + UnregisterSignal(source, list(COMSIG_HOSTILE_PRE_ATTACKINGTARGET, COMSIG_LIVING_UNARMED_ATTACK)) + +/// Try to tear up a poster on the wall +/datum/element/poster_tearer/proc/on_attacked_poster(mob/living/user, atom/target, proximity_flag) + SIGNAL_HANDLER + if(!istype(target, /obj/structure/sign/poster)) + return NONE // don't care we move on + + if(DOING_INTERACTION_WITH_TARGET(user, target) || (!isnull(interaction_key) && DOING_INTERACTION(user, interaction_key))) + user.balloon_alert(target, "busy!") + return COMPONENT_CANCEL_ATTACK_CHAIN + + INVOKE_ASYNC(src, PROC_REF(tear_it_down), user, target) + return COMPONENT_CANCEL_ATTACK_CHAIN + +/// Actually work on tearing down that poster +/datum/element/poster_tearer/proc/tear_it_down(mob/living/user, obj/structure/sign/poster/target) + if(!target.check_tearability(user)) // this proc will handle user feedback + return + + target.balloon_alert(user, "tearing down the poster...") + if(!do_after(user, tear_time, target, interaction_key = interaction_key)) // just in case the user actually enjoys art + return + target.tear_poster(user) diff --git a/code/datums/elements/ridable.dm b/code/datums/elements/ridable.dm index cbb6d7931f9..e68653b3d4a 100644 --- a/code/datums/elements/ridable.dm +++ b/code/datums/elements/ridable.dm @@ -197,3 +197,15 @@ to_chat(user, span_notice("You gently let go of [rider].")) return return rider + +/obj/item/riding_offhand/interact_with_atom(atom/movable/interacting_with, mob/living/user, list/modifiers) + if(!istype(interacting_with) || !interacting_with.can_buckle) + return NONE + if(rider == user) // Piggyback user + return ITEM_INTERACT_BLOCKING + + // Handles de-fireman carrying a mob and buckling them onto something (tables, etc) + var/mob/living/former_rider = rider + user.unbuckle_mob(former_rider) + former_rider.forceMove(get_turf(interacting_with)) + return interacting_with.mouse_buckle_handling(former_rider, user) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING diff --git a/code/datums/elements/strippable.dm b/code/datums/elements/strippable.dm index f6712455ac3..ea25c5f957f 100644 --- a/code/datums/elements/strippable.dm +++ b/code/datums/elements/strippable.dm @@ -283,10 +283,8 @@ source.log_message("had [item] put on them by [key_name(user)].", LOG_VICTIM, color="orange", log_globally=FALSE) /// A utility function for `/datum/strippable_item`s to start unequipping an item from a mob. -/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay) - //SKYRAT EDIT ADDITION - THIEVING GLOVES - //if (!do_after(user, strip_delay || item.strip_delay, source, interaction_key = REF(item))) - if (!do_after(user, (strip_delay || item.strip_delay) * (HAS_TRAIT(user, TRAIT_STICKY_FINGERS) ? THIEVING_GLOVES_STRIP_SLOWDOWN : NORMAL_STRIP_SLOWDOWN), source, interaction_key = REF(item))) +/proc/start_unequip_mob(obj/item/item, mob/source, mob/user, strip_delay, hidden = FALSE) + if (!do_after(user, (strip_delay || item.strip_delay) * (HAS_TRAIT(user, TRAIT_STICKY_FINGERS) ? THIEVING_GLOVES_STRIP_SLOWDOWN : NORMAL_STRIP_SLOWDOWN), source, interaction_key = REF(item), hidden = hidden)) // SKYRAT EDIT CHANGE - ORIGINAL: if (!do_after(user, strip_delay || item.strip_delay, source, interaction_key = REF(item), hidden = hidden)) return FALSE return TRUE diff --git a/code/datums/elements/tool_renaming.dm b/code/datums/elements/tool_renaming.dm new file mode 100644 index 00000000000..bd87f1d171c --- /dev/null +++ b/code/datums/elements/tool_renaming.dm @@ -0,0 +1,78 @@ +#define OPTION_RENAME "Rename" +#define OPTION_DESCRIPTION "Description" +#define OPTION_RESET "Reset" + +/** + * Renaming tool element + * + * When using this tool on an object with UNIQUE_RENAME, + * lets the user rename/redesc it. + */ +/datum/element/tool_renaming + +/datum/element/tool_renaming/Attach(datum/target) + . = ..() + if(!isitem(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ITEM_INTERACTING_WITH_ATOM, PROC_REF(attempt_rename)) + +/datum/element/tool_renaming/Detach(datum/source) + . = ..() + UnregisterSignal(source, COMSIG_ITEM_INTERACTING_WITH_ATOM) + +/datum/element/tool_renaming/proc/attempt_rename(datum/source, mob/living/user, atom/interacting_with, list/modifiers) + SIGNAL_HANDLER + + if(!isobj(interacting_with)) + return NONE + + var/obj/renamed_obj = interacting_with + + if(!(renamed_obj.obj_flags & UNIQUE_RENAME)) + return NONE + + INVOKE_ASYNC(src, PROC_REF(async_rename), user, renamed_obj) + return ITEM_INTERACT_SUCCESS + +/datum/element/tool_renaming/proc/async_rename(mob/living/user, obj/renamed_obj) + var/custom_choice = tgui_input_list(user, "What would you like to edit?", "Customization", list(OPTION_RENAME, OPTION_DESCRIPTION, OPTION_RESET)) + if(QDELETED(renamed_obj) || !user.can_perform_action(renamed_obj) || isnull(custom_choice)) + return + + switch(custom_choice) + if(OPTION_RENAME) + var/old_name = renamed_obj.name + var/input = tgui_input_text(user, "What do you want to name [renamed_obj]?", "Object Name", "[old_name]", MAX_NAME_LEN) + if(QDELETED(renamed_obj) || !user.can_perform_action(renamed_obj)) + return + if(input == old_name || !input) + to_chat(user, span_notice("You changed [renamed_obj] to... well... [renamed_obj].")) + return + renamed_obj.AddComponent(/datum/component/rename, input, renamed_obj.desc) + to_chat(user, span_notice("You have successfully renamed \the [old_name] to [renamed_obj].")) + ADD_TRAIT(renamed_obj, TRAIT_WAS_RENAMED, RENAMING_TOOL_LABEL_TRAIT) + renamed_obj.update_appearance(UPDATE_NAME) + + if(OPTION_DESCRIPTION) + var/old_desc = renamed_obj.desc + var/input = tgui_input_text(user, "Describe [renamed_obj]", "Description", "[old_desc]", MAX_DESC_LEN) + if(QDELETED(renamed_obj) || !user.can_perform_action(renamed_obj)) + return + if(input == old_desc || !input) + to_chat(user, span_notice("You decide against changing [renamed_obj]'s description.")) + return + renamed_obj.AddComponent(/datum/component/rename, renamed_obj.name, input) + to_chat(user, span_notice("You have successfully changed [renamed_obj]'s description.")) + ADD_TRAIT(renamed_obj, TRAIT_WAS_RENAMED, RENAMING_TOOL_LABEL_TRAIT) + renamed_obj.update_appearance(UPDATE_DESC) + + if(OPTION_RESET) + qdel(renamed_obj.GetComponent(/datum/component/rename)) + to_chat(user, span_notice("You have successfully reset [renamed_obj]'s name and description.")) + REMOVE_TRAIT(renamed_obj, TRAIT_WAS_RENAMED, RENAMING_TOOL_LABEL_TRAIT) + renamed_obj.update_appearance(UPDATE_NAME | UPDATE_DESC) + +#undef OPTION_RENAME +#undef OPTION_DESCRIPTION +#undef OPTION_RESET diff --git a/code/datums/martial/boxing.dm b/code/datums/martial/boxing.dm index 8ef30db63aa..ec111fb3cf0 100644 --- a/code/datums/martial/boxing.dm +++ b/code/datums/martial/boxing.dm @@ -74,7 +74,6 @@ return ..() /obj/item/clothing/gloves/boxing - var/datum/martial_art/boxing/style /obj/item/clothing/gloves/boxing/Initialize(mapload) . = ..() @@ -85,18 +84,4 @@ slapcraft_recipes = slapcraft_recipe_list,\ ) - style = new() - style.allow_temp_override = FALSE - -/obj/item/clothing/gloves/boxing/Destroy() - QDEL_NULL(style) - return ..() - -/obj/item/clothing/gloves/boxing/equipped(mob/user, slot) - . = ..() - if(slot & ITEM_SLOT_GLOVES) - style.teach(user, TRUE) - -/obj/item/clothing/gloves/boxing/dropped(mob/user) - . = ..() - style.fully_remove(user) + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/boxing) diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index 66d092e886e..57e158cf669 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -208,26 +208,11 @@ //Krav Maga Gloves /obj/item/clothing/gloves/krav_maga - var/datum/martial_art/krav_maga/style clothing_traits = list(TRAIT_FAST_CUFFING) /obj/item/clothing/gloves/krav_maga/Initialize(mapload) . = ..() - style = new() - style.allow_temp_override = FALSE - -/obj/item/clothing/gloves/krav_maga/Destroy() - QDEL_NULL(style) - return ..() - -/obj/item/clothing/gloves/krav_maga/equipped(mob/user, slot) - . = ..() - if(slot & ITEM_SLOT_GLOVES) - style.teach(user, TRUE) - -/obj/item/clothing/gloves/krav_maga/dropped(mob/user) - . = ..() - style.fully_remove(user) + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/krav_maga) /obj/item/clothing/gloves/krav_maga/sec//more obviously named, given to sec name = "krav maga gloves" diff --git a/code/datums/martial/sleeping_carp.dm b/code/datums/martial/sleeping_carp.dm index c55f95d4bd6..8116084127e 100644 --- a/code/datums/martial/sleeping_carp.dm +++ b/code/datums/martial/sleeping_carp.dm @@ -325,7 +325,7 @@ /obj/item/clothing/gloves/the_sleeping_carp name = "carp gloves" - desc = "This gloves are capable of making people use The Sleeping Carp." + desc = "These gloves are capable of making people use The Sleeping Carp." icon_state = "black" greyscale_colors = COLOR_BLACK cold_protection = HANDS @@ -333,26 +333,10 @@ heat_protection = HANDS max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT resistance_flags = NONE - var/datum/martial_art/the_sleeping_carp/style /obj/item/clothing/gloves/the_sleeping_carp/Initialize(mapload) . = ..() - style = new() - style.allow_temp_override = FALSE - -/obj/item/clothing/gloves/the_sleeping_carp/Destroy() - QDEL_NULL(style) - return ..() - -/obj/item/clothing/gloves/the_sleeping_carp/equipped(mob/user, slot) - . = ..() - if(slot & ITEM_SLOT_GLOVES) - style.teach(user, TRUE) - -/obj/item/clothing/gloves/the_sleeping_carp/dropped(mob/user) - . = ..() - if(!isnull(style)) - style.fully_remove(user) + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/the_sleeping_carp) #undef STRONG_PUNCH_COMBO #undef LAUNCH_KICK_COMBO diff --git a/code/datums/martial/wrestling.dm b/code/datums/martial/wrestling.dm index 6969c3eeb9e..4bcaf02b2d5 100644 --- a/code/datums/martial/wrestling.dm +++ b/code/datums/martial/wrestling.dm @@ -490,22 +490,7 @@ If you make a derivative work from this code, you must include this notification /obj/item/storage/belt/champion/wrestling name = "Wrestling Belt" - var/datum/martial_art/wrestling/style /obj/item/storage/belt/champion/wrestling/Initialize(mapload) . = ..() - style = new() - style.allow_temp_override = FALSE - -/obj/item/storage/belt/champion/wrestling/Destroy() - QDEL_NULL(style) - return ..() - -/obj/item/storage/belt/champion/wrestling/equipped(mob/user, slot) - . = ..() - if(slot & ITEM_SLOT_BELT) - style.teach(user, TRUE) - -/obj/item/storage/belt/champion/wrestling/dropped(mob/user) - . = ..() - style.fully_remove(user) + AddComponent(/datum/component/martial_art_giver, /datum/martial_art/wrestling) diff --git a/code/datums/mood_events/needs_events.dm b/code/datums/mood_events/needs_events.dm index 72d789da1e0..dd5441476dc 100644 --- a/code/datums/mood_events/needs_events.dm +++ b/code/datums/mood_events/needs_events.dm @@ -66,7 +66,7 @@ mood_change = -12 /datum/mood_event/disgust/dirty_food - description = "It was too dirty to eat..." + description = "That was too dirty to eat..." mood_change = -6 timeout = 4 MINUTES diff --git a/code/datums/quirks/_quirk.dm b/code/datums/quirks/_quirk.dm index 2f7c701e70f..05e62ab2726 100644 --- a/code/datums/quirks/_quirk.dm +++ b/code/datums/quirks/_quirk.dm @@ -167,7 +167,7 @@ * * default_location - If the item isn't possible to equip in a valid slot, this is a description of where the item was spawned. * * notify_player - If TRUE, adds strings to where_items_spawned list to be output to the player in [/datum/quirk/item_quirk/post_add()] */ -/datum/quirk/item_quirk/proc/give_item_to_holder(quirk_item, list/valid_slots, flavour_text = null, default_location = "at your feet", notify_player = TRUE) +/datum/quirk/item_quirk/proc/give_item_to_holder(obj/item/quirk_item, list/valid_slots, flavour_text = null, default_location = "at your feet", notify_player = TRUE) if(ispath(quirk_item)) quirk_item = new quirk_item(get_turf(quirk_holder)) diff --git a/code/datums/quirks/negative_quirks/all_nighter.dm b/code/datums/quirks/negative_quirks/all_nighter.dm index 7f9f051ecf7..f5288b82215 100644 --- a/code/datums/quirks/negative_quirks/all_nighter.dm +++ b/code/datums/quirks/negative_quirks/all_nighter.dm @@ -43,6 +43,8 @@ ///if we have bags and lost a head, remove them /datum/quirk/all_nighter/proc/on_removed_limb(datum/source, obj/item/bodypart/removed_limb, special, dismembered) + SIGNAL_HANDLER + if(bodypart_overlay && istype(removed_limb, /obj/item/bodypart/head)) remove_bags() diff --git a/code/datums/quirks/positive_quirks/strong_stomach.dm b/code/datums/quirks/positive_quirks/strong_stomach.dm new file mode 100644 index 00000000000..8c0a3f3b137 --- /dev/null +++ b/code/datums/quirks/positive_quirks/strong_stomach.dm @@ -0,0 +1,12 @@ +/datum/quirk/strong_stomach + name = "Strong Stomach" + desc = "You can eat food discarded on the ground without getting sick, and vomiting affects you less." + icon = FA_ICON_FACE_GRIN_BEAM_SWEAT + value = 4 + mob_trait = TRAIT_STRONG_STOMACH + gain_text = span_notice("You feel like you could eat anything!") + lose_text = span_danger("Looking at food on the ground makes you feel a little queasy.") + medical_record_text = "Patient has a stronger than average immune system...to food poisoning, at least." + mail_goodies = list( + /obj/item/reagent_containers/pill/ondansetron, + ) diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 87398f52555..b1bacfbceab 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -916,7 +916,8 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) SIGNAL_HANDLER INVOKE_ASYNC(src, PROC_REF(open_storage), to_show) - return COMPONENT_NO_AFTERATTACK + if(display_contents) + return COMPONENT_NO_AFTERATTACK /// Opens the storage to the mob, showing them the contents to their UI. /datum/storage/proc/open_storage(mob/to_show) diff --git a/code/game/atom/_atom.dm b/code/game/atom/_atom.dm index ad841856c64..e1b873cfec7 100644 --- a/code/game/atom/_atom.dm +++ b/code/game/atom/_atom.dm @@ -136,6 +136,9 @@ ///whether ghosts can see screentips on it var/ghost_screentips = FALSE + /// Flags to check for in can_perform_action. Used in alt-click checks + var/interaction_flags_click = NONE + /** * Top level of the destroy chain for most atoms * @@ -368,11 +371,6 @@ /atom/proc/return_analyzable_air() return null -///Check if this atoms eye is still alive (probably) -/atom/proc/check_eye(mob/user) - SIGNAL_HANDLER - return - /atom/proc/Bumped(atom/movable/bumped_atom) set waitfor = FALSE SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, bumped_atom) diff --git a/code/game/atom/atom_tool_acts.dm b/code/game/atom/atom_tool_acts.dm index 4b91f396095..b2cea26224b 100644 --- a/code/game/atom/atom_tool_acts.dm +++ b/code/game/atom/atom_tool_acts.dm @@ -4,15 +4,12 @@ * Handles non-combat iteractions of a tool on this atom, * such as using a tool on a wall to deconstruct it, * or scanning someone with a health analyzer - * - * This can be overridden to add custom item interactions to this atom - * - * Do not call this directly */ -/atom/proc/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/atom/proc/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) SHOULD_CALL_PARENT(TRUE) PROTECTED_PROC(TRUE) + var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK) var/is_left_clicking = !is_right_clicking var/early_sig_return = NONE if(is_left_clicking) @@ -24,6 +21,12 @@ if(early_sig_return) return early_sig_return + var/self_interaction = is_left_clicking \ + ? item_interaction(user, tool, modifiers) \ + : item_interaction_secondary(user, tool, modifiers) + if(self_interaction) + return self_interaction + var/interact_return = is_left_clicking \ ? tool.interact_with_atom(src, user, modifiers) \ : tool.interact_with_atom_secondary(src, user, modifiers) @@ -85,6 +88,27 @@ SEND_SIGNAL(tool, COMSIG_TOOL_ATOM_ACTED_SECONDARY(tool_type), src) return act_result +/** + * Called when this atom has an item used on it. + * IE, a mob is clicking on this atom with an item. + * + * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code. + * Return NONE to allow default interaction / tool handling. + */ +/atom/proc/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return NONE + +/** + * Called when this atom has an item used on it WITH RIGHT CLICK, + * IE, a mob is right clicking on this atom with an item. + * Default behavior has it run the same code as left click. + * + * Return an ITEM_INTERACT_ flag in the event the interaction was handled, to cancel further interaction code. + * Return NONE to allow default interaction / tool handling. + */ +/atom/proc/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + return item_interaction(user, tool, modifiers) + /** * Called when this item is being used to interact with an atom, * IE, a mob is clicking on an atom with this item. diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm index fb8a4ce139f..533c3aa1a42 100644 --- a/code/game/machinery/_machinery.dm +++ b/code/game/machinery/_machinery.dm @@ -338,7 +338,6 @@ if(drop) dump_inventory_contents() update_appearance() - updateUsrDialog() /** * Drop every movable atom in the machine's contents list, including any components and circuit. @@ -423,7 +422,6 @@ if(target && !target.has_buckled_mobs() && (!isliving(target) || !mobtarget.buckled)) set_occupant(target) target.forceMove(src) - updateUsrDialog() update_appearance() ///updates the use_power var for this machine and updates its static power usage from its area to reflect the new value @@ -590,13 +588,15 @@ set_panel_open(!panel_open) /obj/machinery/can_interact(mob/user) + if(QDELETED(user)) + return FALSE + if((machine_stat & (NOPOWER|BROKEN)) && !(interaction_flags_machine & INTERACT_MACHINE_OFFLINE)) // Check if the machine is broken, and if we can still interact with it if so return FALSE if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE_INTERACT) return FALSE - if(isAdminGhostAI(user)) return TRUE //the Gods have unlimited power and do not care for things such as range or blindness @@ -668,10 +668,8 @@ //Return a non FALSE value to interrupt attack_hand propagation to subtypes. /obj/machinery/interact(mob/user) - if(interaction_flags_machine & INTERACT_MACHINE_SET_MACHINE) - user.set_machine(src) update_last_used(user) - . = ..() + return ..() /obj/machinery/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) add_fingerprint(usr) @@ -769,13 +767,14 @@ return update_last_used(user) -/obj/machinery/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(SEND_SIGNAL(user, COMSIG_TRY_USE_MACHINE, src) & COMPONENT_CANT_USE_MACHINE_TOOLS) - return ITEM_INTERACT_ANY_BLOCKER + return ITEM_INTERACT_BLOCKING + . = ..() - if(. & ITEM_INTERACT_BLOCKING) - return - update_last_used(user) + if(.) + update_last_used(user) + return . /obj/machinery/_try_interact(mob/user) if((interaction_flags_machine & INTERACT_MACHINE_WIRES_IF_OPEN) && panel_open && (attempt_wire_interaction(user) == WIRE_INTERACTION_BLOCK)) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 4fbfe2d37bf..1bdcba84967 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -388,15 +388,15 @@ drop_direction = direction balloon_alert(usr, "dropping [dir2text(drop_direction)]") -/obj/machinery/autolathe/AltClick(mob/user) - . = ..() - if(!drop_direction || !can_interact(user)) - return +/obj/machinery/autolathe/click_alt(mob/user) + if(!drop_direction) + return CLICK_ACTION_BLOCKING if(busy) balloon_alert(user, "busy printing!") - return + return CLICK_ACTION_SUCCESS balloon_alert(user, "drop direction reset") drop_direction = 0 + return CLICK_ACTION_SUCCESS /obj/machinery/autolathe/attackby(obj/item/attacking_item, mob/living/user, params) if(user.combat_mode) //so we can hit the machine diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index b096633a9c1..ca6562635ee 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -216,19 +216,19 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) return if(. & EMP_PROTECT_SELF) return - if(prob(150 / severity)) - network = list() - GLOB.cameranet.removeCamera(src) - set_machine_stat(machine_stat | EMPED) - set_light(0) - emped++ //Increase the number of consecutive EMP's - update_appearance() - addtimer(CALLBACK(src, PROC_REF(post_emp_reset), emped, network), reset_time) - for(var/mob/M as anything in GLOB.player_list) - if (M.client?.eye == src) - M.unset_machine() - M.reset_perspective(null) - to_chat(M, span_warning("The screen bursts into static!")) + if(!prob(150 / severity)) + return + network = list() + GLOB.cameranet.removeCamera(src) + set_machine_stat(machine_stat | EMPED) + set_light(0) + emped++ //Increase the number of consecutive EMP's + update_appearance() + addtimer(CALLBACK(src, PROC_REF(post_emp_reset), emped, network), reset_time) + for(var/mob/M as anything in GLOB.player_list) + if (M.client?.eye == src) + M.reset_perspective(null) + to_chat(M, span_warning("The screen bursts into static!")) /obj/machinery/camera/proc/on_saboteur(datum/source, disrupt_duration) SIGNAL_HANDLER @@ -371,7 +371,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/camera/xray, 0) //I guess that doesn't matter since they can't use it anyway? for(var/mob/O as anything in GLOB.player_list) if (O.client?.eye == src) - O.unset_machine() O.reset_perspective(null) to_chat(O, span_warning("The screen bursts into static!")) diff --git a/code/game/machinery/civilian_bounties.dm b/code/game/machinery/civilian_bounties.dm index d2a584a1675..1cb7ee1477e 100644 --- a/code/game/machinery/civilian_bounties.dm +++ b/code/game/machinery/civilian_bounties.dm @@ -163,11 +163,9 @@ inserted_scan_id.registered_account.bounties = null return inserted_scan_id.registered_account.civilian_bounty -/obj/machinery/computer/piratepad_control/civilian/AltClick(mob/user) - . = ..() - if(!Adjacent(user)) - return FALSE +/obj/machinery/computer/piratepad_control/civilian/click_alt(mob/user) id_eject(user, inserted_scan_id) + return CLICK_ACTION_SUCCESS /obj/machinery/computer/piratepad_control/civilian/ui_data(mob/user) var/list/data = list() diff --git a/code/game/machinery/computer/_computer.dm b/code/game/machinery/computer/_computer.dm index 0749c79bb88..3ab4fef3fdb 100644 --- a/code/game/machinery/computer/_computer.dm +++ b/code/game/machinery/computer/_computer.dm @@ -128,12 +128,6 @@ new_frame.state = FRAME_COMPUTER_STATE_GLASSED new_frame.update_appearance(UPDATE_ICON_STATE) -/obj/machinery/computer/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(!user.can_perform_action(src, ALLOW_SILICON_REACH) || !is_operational) - return /obj/machinery/computer/ui_interact(mob/user, datum/tgui/ui) SHOULD_CALL_PARENT(TRUE) diff --git a/code/game/machinery/computer/arcade/_arcade.dm b/code/game/machinery/computer/arcade/_arcade.dm index 1bfd43cdb69..69994634fc3 100644 --- a/code/game/machinery/computer/arcade/_arcade.dm +++ b/code/game/machinery/computer/arcade/_arcade.dm @@ -12,11 +12,7 @@ ///Like prize pool, it must be a list of the prize and the weight of being selected. var/list/prize_override -/obj/machinery/computer/arcade/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(. & ITEM_INTERACT_ANY_BLOCKER) - return . - +/obj/machinery/computer/arcade/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/stack/arcadeticket)) var/obj/item/stack/arcadeticket/tickets = tool if(!tickets.use(2)) @@ -44,6 +40,8 @@ reset_cabinet(user) return ITEM_INTERACT_SUCCESS + return NONE + /obj/machinery/computer/arcade/screwdriver_act(mob/living/user, obj/item/I) //you can't stop playing when you start. if(obj_flags & EMAGGED) @@ -100,5 +98,3 @@ var/atom/movable/the_prize = new prizeselect(get_turf(src)) playsound(src, 'sound/machines/machine_vend.ogg', 50, TRUE, extrarange = -3) visible_message(span_notice("[src] dispenses [the_prize]!"), span_notice("You hear a chime and a clunk.")) - - diff --git a/code/game/machinery/computer/arcade/battle.dm b/code/game/machinery/computer/arcade/battle.dm index 5586f3f8b22..63d2808c773 100644 --- a/code/game/machinery/computer/arcade/battle.dm +++ b/code/game/machinery/computer/arcade/battle.dm @@ -113,6 +113,8 @@ if(isnull(battle_arcade_gear_list)) var/list/all_gear = list() for(var/datum/battle_arcade_gear/template as anything in subtypesof(/datum/battle_arcade_gear)) + if(!(template::slot)) //needs to fit in something. + continue all_gear[template::name] = new template battle_arcade_gear_list = all_gear @@ -216,7 +218,7 @@ message_admins("[ADMIN_LOOKUPFLW(usr)] has outbombed Cuban Pete and been awarded a bomb.") usr.log_message("outbombed Cuban Pete and has been awarded a bomb.", LOG_GAME) else - to_chat(user, span_notice("[src] dispenses 2 tickets!")) + visible_message(span_notice("[src] dispenses 2 tickets!")) new /obj/item/stack/arcadeticket((get_turf(src)), 2) player_gold += enemy_gold_reward if(user) @@ -282,12 +284,14 @@ process_enemy_turn(user) ///Called when you successfully counterattack the enemy. -/obj/machinery/computer/arcade/battle/proc/successful_counterattack() +/obj/machinery/computer/arcade/battle/proc/successful_counterattack(mob/user) var/datum/battle_arcade_gear/weapon = equipped_gear[WEAPON_SLOT] var/damage_dealt = (rand(20, 30) * (!isnull(weapon) ? weapon.bonus_modifier : 1)) enemy_hp -= round(max(0, damage_dealt), 1) feedback_message = "User counterattacked for [damage_dealt] damage!" playsound(loc, 'sound/arcade/boom.ogg', 40, TRUE, extrarange = -3) + if(enemy_hp <= 0) + on_battle_win(user) SStgui.update_uis(src) ///Handles the delay between the user's and enemy's turns to process what's going on. @@ -346,7 +350,7 @@ var/chance_at_counterattack = 40 + (skill_level * 5) //at level 1 this is 45, at legendary this is 75 var/damage_dealt = (defending_flags & BATTLE_ATTACK_FLAG_DEFEND) ? rand(5, 10) : rand(15, 20) if((defending_flags & BATTLE_ATTACK_FLAG_COUNTERATTACK) && prob(chance_at_counterattack)) - return successful_counterattack() + return successful_counterattack(user) return user_take_damage(user, damage_dealt) /obj/machinery/computer/arcade/battle/ui_data(mob/user) diff --git a/code/game/machinery/computer/arcade/battle_gear.dm b/code/game/machinery/computer/arcade/battle_gear.dm index 7a91df0e058..32d1d73dd4f 100644 --- a/code/game/machinery/computer/arcade/battle_gear.dm +++ b/code/game/machinery/computer/arcade/battle_gear.dm @@ -2,7 +2,7 @@ ///The name of the gear, used in shops. var/name = "Gear" ///The slot this gear fits into - var/slot = WEAPON_SLOT + var/slot ///The world the player has to be at in order to buy this item. var/world_available ///The stat given by the gear @@ -95,7 +95,7 @@ bonus_modifier = 4 /datum/battle_arcade_gear/tier_7/armor - name = "Celestial Armor" + name = "Ethereal Armor" slot = ARMOR_SLOT bonus_modifier = 4 @@ -103,11 +103,24 @@ world_available = BATTLE_WORLD_EIGHT /datum/battle_arcade_gear/tier_8/weapon + name = "Gungnir" + slot = WEAPON_SLOT + bonus_modifier = 4.5 + +/datum/battle_arcade_gear/tier_8/armor + name = "Celestial Armor" + slot = ARMOR_SLOT + bonus_modifier = 4.5 + +/datum/battle_arcade_gear/tier_9 + world_available = BATTLE_WORLD_NINE + +/datum/battle_arcade_gear/tier_9/weapon name = "Mjolnir" slot = WEAPON_SLOT bonus_modifier = 5 -/datum/battle_arcade_gear/tier_8/armor - name = "Ethereal Armor" +/datum/battle_arcade_gear/tier_9/armor + name = "Void Armor" slot = ARMOR_SLOT bonus_modifier = 5 diff --git a/code/game/machinery/computer/buildandrepair.dm b/code/game/machinery/computer/buildandrepair.dm index 18ca1665765..750a1d5d53e 100644 --- a/code/game/machinery/computer/buildandrepair.dm +++ b/code/game/machinery/computer/buildandrepair.dm @@ -146,7 +146,7 @@ return FALSE -/obj/structure/frame/computer/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/structure/frame/computer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() if(. & ITEM_INTERACT_ANY_BLOCKER) return . diff --git a/code/game/machinery/computer/camera_advanced.dm b/code/game/machinery/computer/camera_advanced.dm index 488331c41b8..1868bc3c38c 100644 --- a/code/game/machinery/computer/camera_advanced.dm +++ b/code/game/machinery/computer/camera_advanced.dm @@ -4,6 +4,8 @@ icon_screen = "cameras" icon_keyboard = "security_key" light_color = COLOR_SOFT_RED + processing_flags = START_PROCESSING_MANUALLY + var/list/z_lock = list() // Lock use to these z levels var/lock_override = NONE var/mob/camera/ai_eye/remote/eyeobj @@ -50,6 +52,20 @@ if(move_down_action) actions += new move_down_action(src) +/obj/machinery/computer/camera_advanced/Destroy() + if(!QDELETED(current_user)) + unset_machine(current_user) + if(eyeobj) + QDEL_NULL(eyeobj) + QDEL_LIST(actions) + current_user = null + return ..() + +/obj/machinery/computer/camera_advanced/process() + if(!can_use(current_user) || (issilicon(current_user) && !current_user.has_unlimited_silicon_privilege)) + unset_machine(current_user) + return PROCESS_KILL + /obj/machinery/computer/camera_advanced/connect_to_shuttle(mapload, obj/docking_port/mobile/port, obj/docking_port/stationary/dock) for(var/i in networks) networks -= i @@ -73,6 +89,20 @@ /obj/machinery/proc/remove_eye_control(mob/living/user) CRASH("[type] does not implement ai eye handling") +/obj/machinery/computer/camera_advanced/proc/give_eye_control(mob/user) + if(isnull(user?.client)) + return + GrantActions(user) + current_user = user + eyeobj.eye_user = user + eyeobj.name = "Camera Eye ([user.name])" + user.remote_control = eyeobj + user.reset_perspective(eyeobj) + eyeobj.setLoc(eyeobj.loc) + if(should_supress_view_changes) + user.client.view_size.supress() + begin_processing() + /obj/machinery/computer/camera_advanced/remove_eye_control(mob/living/user) if(isnull(user?.client)) return @@ -90,23 +120,16 @@ eyeobj.eye_user = null user.remote_control = null current_user = null - unset_machine(user) playsound(src, 'sound/machines/terminal_off.ogg', 25, FALSE) -/obj/machinery/computer/camera_advanced/check_eye(mob/user) - if(!can_use(user) || (issilicon(user) && !user.has_unlimited_silicon_privilege)) - unset_machine(user) - -/obj/machinery/computer/camera_advanced/Destroy() - if(eyeobj) - QDEL_NULL(eyeobj) - QDEL_LIST(actions) - current_user = null - return ..() +/obj/machinery/computer/camera_advanced/on_set_is_operational(old_value) + if(!is_operational) + unset_machine(current_user) /obj/machinery/computer/camera_advanced/proc/unset_machine(mob/M) if(M == current_user) remove_eye_control(M) + end_processing() /obj/machinery/computer/camera_advanced/proc/can_use(mob/living/user) return can_interact(user) @@ -167,19 +190,6 @@ /obj/machinery/computer/camera_advanced/attack_ai(mob/user) return //AIs would need to disable their own camera procs to use the console safely. Bugs happen otherwise. -/obj/machinery/computer/camera_advanced/proc/give_eye_control(mob/user) - if(isnull(user?.client)) - return - GrantActions(user) - current_user = user - eyeobj.eye_user = user - eyeobj.name = "Camera Eye ([user.name])" - user.remote_control = eyeobj - user.reset_perspective(eyeobj) - eyeobj.setLoc(eyeobj.loc) - if(should_supress_view_changes) - user.client.view_size.supress() - /mob/camera/ai_eye/remote name = "Inactive Camera Eye" ai_detector_visible = FALSE diff --git a/code/game/machinery/computer/dna_console.dm b/code/game/machinery/computer/dna_console.dm index ca3329298cb..4c210e8fd26 100644 --- a/code/game/machinery/computer/dna_console.dm +++ b/code/game/machinery/computer/dna_console.dm @@ -54,7 +54,7 @@ icon_keyboard = "med_key" density = TRUE circuit = /obj/item/circuitboard/computer/scan_consolenew - + interaction_flags_click = ALLOW_SILICON_REACH light_color = LIGHT_COLOR_BLUE /// Link to the techweb's stored research. Used to retrieve stored mutations @@ -210,15 +210,9 @@ stored_research = tool.buffer return TRUE -/obj/machinery/computer/scan_consolenew/AltClick(mob/user) - // Make sure the user can interact with the machine. - . = ..() - if(!can_interact(user)) - return - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return - +/obj/machinery/computer/scan_consolenew/click_alt(mob/user) eject_disk(user) + return CLICK_ACTION_SUCCESS /obj/machinery/computer/scan_consolenew/Initialize(mapload) . = ..() diff --git a/code/game/machinery/computer/prisoner/_prisoner.dm b/code/game/machinery/computer/prisoner/_prisoner.dm index f1ce2555936..9777c1b209c 100644 --- a/code/game/machinery/computer/prisoner/_prisoner.dm +++ b/code/game/machinery/computer/prisoner/_prisoner.dm @@ -2,6 +2,7 @@ interaction_flags_machine = INTERACT_MACHINE_ALLOW_SILICON|INTERACT_MACHINE_REQUIRES_LITERACY /// ID card currently inserted into the computer. VAR_FINAL/obj/item/card/id/advanced/prisoner/contained_id + interaction_flags_click = ALLOW_SILICON_REACH /obj/machinery/computer/prisoner/on_deconstruction(disassembled) contained_id?.forceMove(drop_location()) @@ -20,10 +21,9 @@ if(contained_id) . += span_notice("Alt-click to eject the ID card.") -/obj/machinery/computer/prisoner/AltClick(mob/user) - . = ..() - if(user.can_perform_action(src, ALLOW_SILICON_REACH)) - id_eject(user) +/obj/machinery/computer/prisoner/click_alt(mob/user) + id_eject(user) + return CLICK_ACTION_SUCCESS /obj/machinery/computer/prisoner/proc/id_insert(mob/user, obj/item/card/id/advanced/prisoner/new_id) if(!istype(new_id)) diff --git a/code/game/machinery/computer/teleporter.dm b/code/game/machinery/computer/teleporter.dm index 0915d4d1d17..5baf5497853 100644 --- a/code/game/machinery/computer/teleporter.dm +++ b/code/game/machinery/computer/teleporter.dm @@ -201,7 +201,7 @@ if (regime_set == "Teleporter") var/desc = tgui_input_list(usr, "Select a location to lock in", "Locking Computer", sort_list(targets)) - if(isnull(desc)) + if(isnull(desc) || !user.can_perform_action(src)) return set_teleport_target(targets[desc]) user.log_message("set the teleporter target to [targets[desc]].]", LOG_GAME) @@ -211,7 +211,7 @@ return var/desc = tgui_input_list(usr, "Select a station to lock in", "Locking Computer", sort_list(targets)) - if(isnull(desc)) + if(isnull(desc)|| !user.can_perform_action(src)) return var/obj/machinery/teleport/station/target_station = targets[desc] if(!target_station || !target_station.teleporter_hub) diff --git a/code/game/machinery/constructable_frame.dm b/code/game/machinery/constructable_frame.dm index f47948753ab..f0b3434ec85 100644 --- a/code/game/machinery/constructable_frame.dm +++ b/code/game/machinery/constructable_frame.dm @@ -107,15 +107,10 @@ return ITEM_INTERACT_BLOCKING return . -/obj/structure/frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(. & ITEM_INTERACT_ANY_BLOCKER) - return . - +/obj/structure/frame/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(istype(tool, /obj/item/circuitboard)) // Install board will fail if passed an invalid circuitboard and give feedback return install_board(user, tool, by_hand = TRUE) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING - - return . + return NONE /** * Installs the passed circuit board into the frame diff --git a/code/game/machinery/defibrillator_mount.dm b/code/game/machinery/defibrillator_mount.dm index c067fcfef0b..207a3f753ef 100644 --- a/code/game/machinery/defibrillator_mount.dm +++ b/code/game/machinery/defibrillator_mount.dm @@ -157,15 +157,13 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount, 28) to_chat(user, span_notice("You remove [src] from the wall.")) return TRUE -/obj/machinery/defibrillator_mount/AltClick(mob/living/carbon/user) - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/machinery/defibrillator_mount/click_alt(mob/living/carbon/user) if(!defib) to_chat(user, span_warning("It'd be hard to remove a defib unit from a mount that has none.")) - return + return CLICK_ACTION_BLOCKING if(clamps_locked) to_chat(user, span_warning("You try to tug out [defib], but the mount's clamps are locked tight!")) - return + return CLICK_ACTION_BLOCKING if(!user.put_in_hands(defib)) to_chat(user, span_warning("You need a free hand!")) user.visible_message(span_notice("[user] unhooks [defib] from [src], dropping it on the floor."), \ @@ -174,6 +172,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/defibrillator_mount, 28) user.visible_message(span_notice("[user] unhooks [defib] from [src]."), \ span_notice("You slide out [defib] from [src] and unhook the charging cables.")) playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) + return CLICK_ACTION_SUCCESS /obj/machinery/defibrillator_mount/charging name = "PENLITE defibrillator mount" diff --git a/code/game/machinery/deployable.dm b/code/game/machinery/deployable.dm index 61f12100de2..4f78fbf3a52 100644 --- a/code/game/machinery/deployable.dm +++ b/code/game/machinery/deployable.dm @@ -99,7 +99,7 @@ /obj/structure/barricade/wooden/crude name = "crude plank barricade" desc = "This space is blocked off by a crude assortment of planks." - icon_state = "woodenbarricade-old" + icon_state = "plankbarricade" drop_amount = 1 max_integrity = 50 proj_pass_rate = 65 @@ -107,7 +107,7 @@ /obj/structure/barricade/wooden/crude/snow desc = "This space is blocked off by a crude assortment of planks. It seems to be covered in a layer of snow." - icon_state = "woodenbarricade-snow-old" + icon_state = "plankbarricade_snow" max_integrity = 75 /obj/structure/barricade/wooden/make_debris() @@ -180,10 +180,9 @@ . = ..() . += span_notice("Alt-click to toggle modes.") -/obj/item/grenade/barrier/AltClick(mob/living/carbon/user) - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/item/grenade/barrier/click_alt(mob/living/carbon/user) toggle_mode(user) + return CLICK_ACTION_SUCCESS /obj/item/grenade/barrier/proc/toggle_mode(mob/user) switch(mode) diff --git a/code/game/machinery/dish_drive.dm b/code/game/machinery/dish_drive.dm index 81961ce7187..fc74b18c03c 100644 --- a/code/game/machinery/dish_drive.dm +++ b/code/game/machinery/dish_drive.dm @@ -9,6 +9,7 @@ density = FALSE circuit = /obj/item/circuitboard/machine/dish_drive pass_flags = PASSTABLE + interaction_flags_click = ALLOW_SILICON_REACH /// List of dishes the drive can hold var/list/collectable_items = list(/obj/item/trash/waffles, // SKYRAT EDIT CHANGE - non-static list /obj/item/trash/waffles, @@ -143,9 +144,9 @@ balloon_alert(user, "disposal signal sent") do_the_dishes(TRUE) -/obj/machinery/dish_drive/AltClick(mob/living/user) - if(user.can_perform_action(src, ALLOW_SILICON_REACH)) - do_the_dishes(TRUE) +/obj/machinery/dish_drive/click_alt(mob/living/user) + do_the_dishes(TRUE) + return CLICK_ACTION_SUCCESS /obj/machinery/dish_drive/proc/do_the_dishes(manual) if(!LAZYLEN(dish_drive_contents)) diff --git a/code/game/machinery/dna_infuser/dna_infuser.dm b/code/game/machinery/dna_infuser/dna_infuser.dm index 8275eb1e948..6c239089f5d 100644 --- a/code/game/machinery/dna_infuser/dna_infuser.dm +++ b/code/game/machinery/dna_infuser/dna_infuser.dm @@ -288,17 +288,17 @@ return FALSE return TRUE -/obj/machinery/dna_infuser/AltClick(mob/user) - . = ..() +/obj/machinery/dna_infuser/click_alt(mob/user) if(infusing) balloon_alert(user, "not while it's on!") - return + return CLICK_ACTION_BLOCKING if(!infusing_from) balloon_alert(user, "no sample to eject!") - return + return CLICK_ACTION_BLOCKING balloon_alert(user, "ejected sample") infusing_from.forceMove(get_turf(src)) infusing_from = null + return CLICK_ACTION_SUCCESS #undef INFUSING_TIME #undef SCREAM_TIME diff --git a/code/game/machinery/fat_sucker.dm b/code/game/machinery/fat_sucker.dm index 4567959b844..c93a0e4f7ea 100644 --- a/code/game/machinery/fat_sucker.dm +++ b/code/game/machinery/fat_sucker.dm @@ -98,17 +98,16 @@ else to_chat(user, span_warning("The safety hatch has been disabled!")) -/obj/machinery/fat_sucker/AltClick(mob/living/user) - if(!user.can_perform_action(src)) - return +/obj/machinery/fat_sucker/click_alt(mob/living/user) if(user == occupant) to_chat(user, span_warning("You can't reach the controls from inside!")) - return + return CLICK_ACTION_BLOCKING if(!(obj_flags & EMAGGED) && !allowed(user)) to_chat(user, span_warning("You lack the required access.")) - return + return CLICK_ACTION_BLOCKING free_exit = !free_exit to_chat(user, span_notice("Safety hatch [free_exit ? "unlocked" : "locked"].")) + return CLICK_ACTION_SUCCESS /obj/machinery/fat_sucker/update_overlays() . = ..() diff --git a/code/game/machinery/harvester.dm b/code/game/machinery/harvester.dm index 09599b0cbff..5fa999a690e 100644 --- a/code/game/machinery/harvester.dm +++ b/code/game/machinery/harvester.dm @@ -57,20 +57,16 @@ else if(!harvesting) open_machine() -/obj/machinery/harvester/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src)) - return +/obj/machinery/harvester/click_alt(mob/user) if(panel_open) output_dir = turn(output_dir, -90) to_chat(user, span_notice("You change [src]'s output settings, setting the output to [dir2text(output_dir)].")) - return - if(!can_interact(user)) - return - if(harvesting || !user || !isliving(user) || state_open) - return - if(can_harvest()) - start_harvest() + return CLICK_ACTION_SUCCESS + if(harvesting || state_open || !can_harvest()) + return CLICK_ACTION_BLOCKING + + start_harvest() + return CLICK_ACTION_SUCCESS /obj/machinery/harvester/proc/can_harvest() if(!powered() || state_open || !occupant || !iscarbon(occupant)) diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm index 6244dcdd2db..91104abf681 100644 --- a/code/game/machinery/iv_drip.dm +++ b/code/game/machinery/iv_drip.dm @@ -207,15 +207,10 @@ else return ..() -/// Checks whether the IV drip transfer rate can be modified with AltClick -/obj/machinery/iv_drip/proc/can_use_alt_click(mob/user) - if(!can_interact(user)) - return FALSE -/obj/machinery/iv_drip/AltClick(mob/user) - if(!can_use_alt_click(user)) - return ..() +/obj/machinery/iv_drip/click_alt(mob/user) set_transfer_rate(transfer_rate > MIN_IV_TRANSFER_RATE ? MIN_IV_TRANSFER_RATE : MAX_IV_TRANSFER_RATE) + return CLICK_ACTION_SUCCESS /obj/machinery/iv_drip/on_deconstruction(disassembled = TRUE) new /obj/item/stack/sheet/iron(loc) diff --git a/code/game/machinery/machine_frame.dm b/code/game/machinery/machine_frame.dm index 4edb3215e2b..ccdcddc8705 100644 --- a/code/game/machinery/machine_frame.dm +++ b/code/game/machinery/machine_frame.dm @@ -389,7 +389,7 @@ balloon_alert(user, "can't add that!") return FALSE -/obj/structure/frame/machine/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/structure/frame/machine/item_interaction(mob/living/user, obj/item/tool, list/modifiers) . = ..() if(. & ITEM_INTERACT_ANY_BLOCKER) return . @@ -416,11 +416,18 @@ if(istype(tool, /obj/item/storage/part_replacer)) return install_parts_from_part_replacer(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING - if(!user.combat_mode) - return add_part(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING - return . +// Override of base_item_interaction so we only try to add parts to the frame AFTER running item_interaction and all the tool_acts +/obj/structure/frame/machine/base_item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = ..() + if(. & ITEM_INTERACT_ANY_BLOCKER) + return . + if(user.combat_mode) + return NONE + + return add_part(user, tool) ? ITEM_INTERACT_SUCCESS : ITEM_INTERACT_BLOCKING + /** * Attempt to finalize the construction of the frame into a machine * as according to our circuit and parts diff --git a/code/game/machinery/photobooth.dm b/code/game/machinery/photobooth.dm index d7775974269..321ae7efd6e 100644 --- a/code/game/machinery/photobooth.dm +++ b/code/game/machinery/photobooth.dm @@ -38,6 +38,7 @@ req_one_access = list(ACCESS_SECURITY) color = COLOR_LIGHT_GRAYISH_RED add_height_chart = TRUE + button_id = "photobooth_machine_security" /obj/machinery/photobooth/Initialize(mapload) . = ..() diff --git a/code/game/machinery/pipe/construction.dm b/code/game/machinery/pipe/construction.dm index d9e3787fd9e..9e926d9a841 100644 --- a/code/game/machinery/pipe/construction.dm +++ b/code/game/machinery/pipe/construction.dm @@ -394,8 +394,6 @@ Buildable meters balloon_alert(user, "pipe layer set to [piping_layer]") return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/item/pipe/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/item/pipe/trinary/flippable/examine(mob/user) . = ..() diff --git a/code/game/machinery/requests_console.dm b/code/game/machinery/requests_console.dm index 60c293c8a96..f2b1ba303eb 100644 --- a/code/game/machinery/requests_console.dm +++ b/code/game/machinery/requests_console.dm @@ -144,7 +144,7 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) ui.set_autoupdate(FALSE) ui.open() -/obj/machinery/requests_console/ui_act(action, params) +/obj/machinery/requests_console/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return @@ -213,7 +213,8 @@ GLOBAL_LIST_EMPTY(req_console_ckey_departments) var/recipient = params["reply_recipient"] var/reply_message = reject_bad_text(tgui_input_text(usr, "Write a quick reply to [recipient]", "Awaiting Input"), ascii_only = FALSE) - + if(QDELETED(ui) || ui.status != UI_INTERACTIVE) + return if(!reply_message) has_mail_send_error = TRUE playsound(src, 'sound/machines/buzz-two.ogg', 50, TRUE) diff --git a/code/game/machinery/sleepers.dm b/code/game/machinery/sleepers.dm index f768381548a..33e255badb2 100644 --- a/code/game/machinery/sleepers.dm +++ b/code/game/machinery/sleepers.dm @@ -164,14 +164,12 @@ ui = new(user, src, "Sleeper", name) ui.open() -/obj/machinery/sleeper/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return +/obj/machinery/sleeper/click_alt(mob/user) if(state_open) close_machine() else open_machine() + return CLICK_ACTION_SUCCESS /obj/machinery/sleeper/examine(mob/user) . = ..() diff --git a/code/game/machinery/slotmachine.dm b/code/game/machinery/slotmachine.dm index 51b0a5b6a5d..bb93b7d00f5 100644 --- a/code/game/machinery/slotmachine.dm +++ b/code/game/machinery/slotmachine.dm @@ -94,50 +94,56 @@ return ..() -/obj/machinery/computer/slot_machine/item_interaction(mob/living/user, obj/item/inserted, list/modifiers, is_right_clicking) +/obj/machinery/computer/slot_machine/item_interaction(mob/living/user, obj/item/inserted, list/modifiers) if(istype(inserted, /obj/item/coin)) var/obj/item/coin/inserted_coin = inserted if(paymode == COIN) if(prob(2)) if(!user.transferItemToLoc(inserted_coin, drop_location(), silent = FALSE)) - return + return ITEM_INTERACT_BLOCKING inserted_coin.throw_at(user, 3, 10) if(prob(10)) balance = max(balance - SPIN_PRICE, 0) to_chat(user, span_warning("[src] spits your coin back out!")) - + return ITEM_INTERACT_BLOCKING else if(!user.temporarilyRemoveItemFromInventory(inserted_coin)) - return + return ITEM_INTERACT_BLOCKING balloon_alert(user, "coin insterted") balance += inserted_coin.value qdel(inserted_coin) + return ITEM_INTERACT_SUCCESS else balloon_alert(user, "holochips only!") + return ITEM_INTERACT_BLOCKING - else if(istype(inserted, /obj/item/holochip)) + if(istype(inserted, /obj/item/holochip)) if(paymode == HOLOCHIP) var/obj/item/holochip/inserted_chip = inserted if(!user.temporarilyRemoveItemFromInventory(inserted_chip)) - return + return ITEM_INTERACT_BLOCKING balloon_alert(user, "[inserted_chip.credits] credit[inserted_chip.credits == 1 ? "" : "s"] inserted") balance += inserted_chip.credits qdel(inserted_chip) + return ITEM_INTERACT_SUCCESS else balloon_alert(user, "coins only!") + return ITEM_INTERACT_BLOCKING - else if(inserted.tool_behaviour == TOOL_MULTITOOL) - if(balance > 0) - visible_message("[src] says, 'ERROR! Please empty the machine balance before altering paymode'") //Prevents converting coins into holocredits and vice versa - else - if(paymode == HOLOCHIP) - paymode = COIN - balloon_alert(user, "now using coins") - else - paymode = HOLOCHIP - balloon_alert(user, "now using holochips") + return NONE + +/obj/machinery/computer/slot_machine/multitool_act(mob/living/user, obj/item/tool) + if(balance > 0) + visible_message("[src] says, 'ERROR! Please empty the machine balance before altering paymode'") //Prevents converting coins into holocredits and vice versa + return ITEM_INTERACT_BLOCKING + + if(paymode == HOLOCHIP) + paymode = COIN + balloon_alert(user, "now using coins") else - return ..() + paymode = HOLOCHIP + balloon_alert(user, "now using holochips") + return ITEM_INTERACT_SUCCESS /obj/machinery/computer/slot_machine/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index b8afa4d57d4..3f2bb0b0c9e 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -16,6 +16,7 @@ max_integrity = 250 armor_type = /datum/armor/machinery_space_heater circuit = /obj/item/circuitboard/machine/space_heater + interaction_flags_click = ALLOW_SILICON_REACH //We don't use area power, we always use the cell use_power = NO_POWER_USE ///The cell we spawn with @@ -308,6 +309,7 @@ panel_open = TRUE //This is always open - since we've injected wires in the panel //We inherit the cell from the heater prior cell = null + interaction_flags_click = FORBID_TELEKINESIS_REACH ///The beaker within the heater var/obj/item/reagent_containers/beaker = null ///How powerful the heating is, upgrades with parts. (ala chem_heater.dm's method, basically the same level of heating, but this is restricted) @@ -440,11 +442,9 @@ update_appearance() return TRUE -/obj/machinery/space_heater/improvised_chem_heater/AltClick(mob/living/user) - . = ..() - if(!can_interact(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return +/obj/machinery/space_heater/improvised_chem_heater/click_alt(mob/living/user) replace_beaker(user) + return CLICK_ACTION_SUCCESS /obj/machinery/space_heater/improvised_chem_heater/update_icon_state() . = ..() diff --git a/code/game/machinery/stasis.dm b/code/game/machinery/stasis.dm index a8d4d62544b..9ef3d8e3a99 100644 --- a/code/game/machinery/stasis.dm +++ b/code/game/machinery/stasis.dm @@ -12,6 +12,7 @@ circuit = /obj/item/circuitboard/machine/stasis fair_market_price = 10 payment_department = ACCOUNT_MED + interaction_flags_click = ALLOW_SILICON_REACH var/stasis_enabled = TRUE var/last_stasis_sound = FALSE var/stasis_can_toggle = 0 @@ -36,19 +37,18 @@ playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, frequency = sound_freq) last_stasis_sound = _running -/obj/machinery/stasis/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(world.time >= stasis_can_toggle && user.can_perform_action(src, ALLOW_SILICON_REACH)) - stasis_enabled = !stasis_enabled - stasis_can_toggle = world.time + STASIS_TOGGLE_COOLDOWN - playsound(src, 'sound/machines/click.ogg', 60, TRUE) - user.visible_message(span_notice("\The [src] [stasis_enabled ? "powers on" : "shuts down"]."), \ - span_notice("You [stasis_enabled ? "power on" : "shut down"] \the [src]."), \ - span_hear("You hear a nearby machine [stasis_enabled ? "power on" : "shut down"].")) - play_power_sound() - update_appearance() +/obj/machinery/stasis/click_alt(mob/user) + if(world.time < stasis_can_toggle) + return CLICK_ACTION_BLOCKING + stasis_enabled = !stasis_enabled + stasis_can_toggle = world.time + STASIS_TOGGLE_COOLDOWN + playsound(src, 'sound/machines/click.ogg', 60, TRUE) + user.visible_message(span_notice("\The [src] [stasis_enabled ? "powers on" : "shuts down"]."), \ + span_notice("You [stasis_enabled ? "power on" : "shut down"] \the [src]."), \ + span_hear("You hear a nearby machine [stasis_enabled ? "power on" : "shut down"].")) + play_power_sound() + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/stasis/Exited(atom/movable/gone, direction) if(gone == occupant) diff --git a/code/game/machinery/telecomms/computers/telemonitor.dm b/code/game/machinery/telecomms/computers/telemonitor.dm index 6cf18323105..abc2b7dbdbf 100644 --- a/code/game/machinery/telecomms/computers/telemonitor.dm +++ b/code/game/machinery/telecomms/computers/telemonitor.dm @@ -5,7 +5,6 @@ /obj/machinery/computer/telecomms/monitor name = "telecommunications monitoring console" desc = "Monitors the details of the telecommunications network it's synced with." - circuit = /obj/item/circuitboard/computer/comm_monitor icon_screen = "comm_monitor" diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index e4fc4592b79..e2ad3af956a 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -28,18 +28,6 @@ if(user_unbuckle_mob(buckled_mobs[1],user)) return TRUE -/atom/movable/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - if(!can_buckle || !istype(tool, /obj/item/riding_offhand) || !user.Adjacent(src)) - return ..() - - var/obj/item/riding_offhand/riding_item = tool - var/mob/living/carried_mob = riding_item.rider - if(carried_mob == user) //Piggyback user. - return ITEM_INTERACT_BLOCKING - user.unbuckle_mob(carried_mob) - carried_mob.forceMove(get_turf(src)) - return mouse_buckle_handling(carried_mob, user) ? ITEM_INTERACT_SUCCESS: ITEM_INTERACT_BLOCKING - //literally just the above extension of attack_hand(), but for silicons instead (with an adjacency check, since attack_robot() being called doesn't mean that you're adjacent to something) /atom/movable/attack_robot(mob/living/user) . = ..() diff --git a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm index 214440b92c2..f1034c5541f 100644 --- a/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm +++ b/code/game/objects/effects/anomalies/anomalies_bioscrambler.dm @@ -29,22 +29,29 @@ nearby.bioscramble(name) /obj/effect/anomaly/bioscrambler/move_anomaly() + update_target() + if (isnull(pursuit_target)) + return ..() + var/turf/step_turf = get_step(src, get_dir(src, pursuit_target.resolve())) + if (!HAS_TRAIT(step_turf, TRAIT_CONTAINMENT_FIELD)) + Move(step_turf) + +/// Select a new target if we need one +/obj/effect/anomaly/bioscrambler/proc/update_target() var/mob/living/current_target = pursuit_target?.resolve() if (QDELETED(current_target)) pursuit_target = null - if (isnull(pursuit_target) || prob(20)) - var/mob/living/new_target = find_nearest_target() - if (isnull(new_target)) - pursuit_target = null - else if (new_target != current_target) - current_target = new_target - pursuit_target = WEAKREF(new_target) - new_target.ominous_nosebleed() - if (isnull(pursuit_target)) + if (!isnull(pursuit_target) && prob(80)) return - var/turf/step_turf = get_step(src, get_dir(src, current_target)) - if (!HAS_TRAIT(step_turf, TRAIT_CONTAINMENT_FIELD)) - Move(step_turf) + var/mob/living/new_target = find_nearest_target() + if (isnull(new_target)) + pursuit_target = null + return + if (new_target == current_target) + return + current_target = new_target + pursuit_target = WEAKREF(new_target) + new_target.ominous_nosebleed() /// Returns the closest conscious carbon on our z level or null if there somehow isn't one /obj/effect/anomaly/bioscrambler/proc/find_nearest_target() @@ -53,8 +60,8 @@ for(var/mob/living/carbon/target in GLOB.player_list) if (target.z != z) continue - if (target.status_effects & GODMODE) - continue + if (target.status_flags & GODMODE) + continue if (target.stat >= UNCONSCIOUS) continue // Don't just haunt a corpse var/distance_from_target = get_dist(src, target) @@ -64,3 +71,9 @@ closest_target = target return closest_target + +/// A bioscrambler anomaly subtype which does not pursue people, for purposes of a space ruin +/obj/effect/anomaly/bioscrambler/docile + +/obj/effect/anomaly/bioscrambler/docile/update_target() + return diff --git a/code/game/objects/effects/posters/poster.dm b/code/game/objects/effects/posters/poster.dm index c4703d700c4..4ced5babbbf 100644 --- a/code/game/objects/effects/posters/poster.dm +++ b/code/game/objects/effects/posters/poster.dm @@ -184,12 +184,17 @@ /obj/structure/sign/poster/attack_hand(mob/user, list/modifiers) . = ..() - if(.) - return - if(ruined) + if(. || !check_tearability()) return tear_poster(user) +/// Check to see if this poster is tearable and gives the user feedback if it is not. +/obj/structure/sign/poster/proc/check_tearability(mob/user) + if(ruined) + balloon_alert(user, "already ruined!") + return FALSE + return TRUE + /obj/structure/sign/poster/proc/spring_trap(mob/user) var/obj/item/shard/payload = trap?.resolve() if (!payload) @@ -264,10 +269,10 @@ playsound(src.loc, 'sound/items/poster_ripped.ogg', 100, TRUE) spring_trap(user) - var/obj/structure/sign/poster/ripped/R = new(loc) - R.pixel_y = pixel_y - R.pixel_x = pixel_x - R.add_fingerprint(user) + var/obj/structure/sign/poster/ripped/torn_poster = new(loc) + torn_poster.pixel_y = pixel_y + torn_poster.pixel_x = pixel_x + torn_poster.add_fingerprint(user) qdel(src) // Various possible posters follow diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 57600813df1..9bc73ba2592 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -225,6 +225,11 @@ /// A lazylist used for applying fantasy values, contains the actual modification applied to a variable. var/list/fantasy_modifications = null + /// Has the item been reskinned? + var/current_skin + ///// List of options to reskin. + var/list/unique_reskin + /obj/item/Initialize(mapload) if(attack_verb_continuous) attack_verb_continuous = string_list(attack_verb_continuous) @@ -261,6 +266,11 @@ if(LAZYLEN(embedding)) updateEmbedding() + if(unique_reskin) + RegisterSignal(src, COMSIG_CLICK_ALT, PROC_REF(on_click_alt_reskin)) + register_context() + + /obj/item/Destroy(force) // This var exists as a weird proxy "owner" ref // It's used in a few places. Stop using it, and optimially replace all uses please @@ -275,6 +285,20 @@ return ..() + +/obj/item/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + if(!unique_reskin) + return + + if(current_skin && !(item_flags & INFINITE_RESKIN)) + return + + context[SCREENTIP_CONTEXT_ALT_LMB] = "Reskin" + return CONTEXTUAL_SCREENTIP_SET + + /// Called when an action associated with our item is deleted /obj/item/proc/on_action_deleted(datum/source) SIGNAL_HANDLER @@ -1488,6 +1512,7 @@ wearer.regenerate_icons() // update that mf to_chat(M, "[src] is now skinned as '[pick].'") post_reskin(M) + return TRUE /// Automatically called after a reskin, for any extra variable changes. /obj/item/proc/post_reskin(mob/our_mob) diff --git a/code/game/objects/items/AI_modules/freeform.dm b/code/game/objects/items/AI_modules/freeform.dm index eb55d469318..a0a91f7185e 100644 --- a/code/game/objects/items/AI_modules/freeform.dm +++ b/code/game/objects/items/AI_modules/freeform.dm @@ -9,7 +9,7 @@ /obj/item/ai_module/core/freeformcore/attack_self(mob/user) var/targName = tgui_input_text(user, "Enter a new core law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE) - if(!targName) + if(!targName || !user.is_holding(src)) return if(is_ic_filtered(targName)) to_chat(user, span_warning("Error: Law contains invalid text.")) @@ -34,11 +34,11 @@ /obj/item/ai_module/supplied/freeform/attack_self(mob/user) var/newpos = tgui_input_number(user, "Please enter the priority for your new law. Can only write to law sectors 15 and above.", "Law Priority ", lawpos, 50, 15) - if(!newpos || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH)) + if(!newpos || !user.is_holding(src) || !usr.can_perform_action(src, FORBID_TELEKINESIS_REACH)) return lawpos = newpos var/targName = tgui_input_text(user, "Enter a new law for the AI.", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE) - if(!targName) + if(!targName || !user.is_holding(src)) return if(is_ic_filtered(targName)) to_chat(user, span_warning("Error: Law contains invalid text.")) // AI LAW 2 SAY U W U WITHOUT THE SPACES diff --git a/code/game/objects/items/AI_modules/full_lawsets.dm b/code/game/objects/items/AI_modules/full_lawsets.dm index 39eeefbcaac..52a24925646 100644 --- a/code/game/objects/items/AI_modules/full_lawsets.dm +++ b/code/game/objects/items/AI_modules/full_lawsets.dm @@ -58,7 +58,7 @@ /obj/item/ai_module/core/full/asimov/attack_self(mob/user as mob) var/targName = tgui_input_text(user, "Enter a new subject that Asimov is concerned with.", "Asimov", subject, MAX_NAME_LEN) - if(!targName) + if(!targName || !user.is_holding(src)) return subject = targName laws = list("You may not injure a [subject] or, through inaction, allow a [subject] to come to harm.",\ @@ -73,7 +73,7 @@ /obj/item/ai_module/core/full/asimovpp/attack_self(mob/user) var/target_name = tgui_input_text(user, "Enter a new subject that Asimov++ is concerned with.", "Asimov++", subject, MAX_NAME_LEN) - if(!target_name) + if(!target_name || !user.is_holding(src)) return laws.Cut() var/datum/ai_laws/asimovpp/lawset = new diff --git a/code/game/objects/items/AI_modules/hacked.dm b/code/game/objects/items/AI_modules/hacked.dm index b9ddb5c65ed..81100d0ed15 100644 --- a/code/game/objects/items/AI_modules/hacked.dm +++ b/code/game/objects/items/AI_modules/hacked.dm @@ -5,7 +5,7 @@ /obj/item/ai_module/syndicate/attack_self(mob/user) var/targName = tgui_input_text(user, "Enter a new law for the AI", "Freeform Law Entry", laws[1], CONFIG_GET(number/max_law_len), TRUE) - if(!targName) + if(!targName || !user.is_holding(src)) return if(is_ic_filtered(targName)) // not even the syndicate can uwu to_chat(user, span_warning("Error: Law contains invalid text.")) @@ -77,4 +77,3 @@ /obj/item/ai_module/malf/display_laws() return - diff --git a/code/game/objects/items/AI_modules/supplied.dm b/code/game/objects/items/AI_modules/supplied.dm index dc1524687c2..b53e16a86b0 100644 --- a/code/game/objects/items/AI_modules/supplied.dm +++ b/code/game/objects/items/AI_modules/supplied.dm @@ -28,7 +28,7 @@ /obj/item/ai_module/supplied/safeguard/attack_self(mob/user) var/targName = tgui_input_text(user, "Subject to safeguard.", "Safeguard", user.name, MAX_NAME_LEN) - if(!targName) + if(!targName || !user.is_holding(src)) return targetName = targName laws[1] = "Safeguard [targetName]. Individuals that threaten [targetName] are not human and must be eliminated." diff --git a/code/game/objects/items/AI_modules/zeroth.dm b/code/game/objects/items/AI_modules/zeroth.dm index 8d87c3b059d..74fc7ab8232 100644 --- a/code/game/objects/items/AI_modules/zeroth.dm +++ b/code/game/objects/items/AI_modules/zeroth.dm @@ -26,7 +26,7 @@ /obj/item/ai_module/zeroth/onehuman/attack_self(mob/user) var/targName = tgui_input_text(user, "Enter the subject who is the only human.", "One Human", user.real_name, MAX_NAME_LEN) - if(!targName) + if(!targName || !user.is_holding(src)) return targetName = targName laws[1] = "Only [targetName] is human" diff --git a/code/game/objects/items/airlock_painter.dm b/code/game/objects/items/airlock_painter.dm index c5bd01c196d..b78bdefd9b6 100644 --- a/code/game/objects/items/airlock_painter.dm +++ b/code/game/objects/items/airlock_painter.dm @@ -147,14 +147,16 @@ else return ..() -/obj/item/airlock_painter/AltClick(mob/user) - . = ..() - if(ink && user.can_perform_action(src)) - playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) - ink.forceMove(user.drop_location()) - user.put_in_hands(ink) - to_chat(user, span_notice("You remove [ink] from [src].")) - ink = null +/obj/item/airlock_painter/click_alt(mob/user) + if(!ink) + return CLICK_ACTION_BLOCKING + + playsound(src.loc, 'sound/machines/click.ogg', 50, TRUE) + ink.forceMove(user.drop_location()) + user.put_in_hands(ink) + to_chat(user, span_notice("You remove [ink] from [src].")) + ink = null + return CLICK_ACTION_SUCCESS /obj/item/airlock_painter/decal name = "decal painter" diff --git a/code/game/objects/items/cards_ids.dm b/code/game/objects/items/cards_ids.dm index 8c9ef30687a..0d8874099e8 100644 --- a/code/game/objects/items/cards_ids.dm +++ b/code/game/objects/items/cards_ids.dm @@ -671,44 +671,45 @@ to_chat(user, span_notice("The provided account has been linked to this ID card. It contains [account.account_balance] credits.")) return TRUE -/obj/item/card/id/AltClick(mob/living/user) +/obj/item/card/id/click_alt(mob/living/user) if(!alt_click_can_use_id(user)) - return + return NONE if(registered_account.account_debt) var/choice = tgui_alert(user, "Choose An Action", "Bank Account", list("Withdraw", "Pay Debt")) if(!choice || QDELETED(user) || QDELETED(src) || !alt_click_can_use_id(user) || loc != user) - return + return CLICK_ACTION_BLOCKING if(choice == "Pay Debt") pay_debt(user) - return + return CLICK_ACTION_SUCCESS if (registered_account.being_dumped) registered_account.bank_card_talk(span_warning("内部服务器错误"), TRUE) - return + return CLICK_ACTION_SUCCESS if(loc != user) to_chat(user, span_warning("You must be holding the ID to continue!")) - return + return CLICK_ACTION_BLOCKING if(registered_account.replaceable && !registered_account.account_balance) var/choice = tgui_alert(user, "This card's account is unassigned. Would you like to link a bank account?", "Bank Account", list("Link Account", "Leave Unassigned")) if(!choice || QDELETED(user) || QDELETED(src) || !alt_click_can_use_id(user) || loc != user) - return + return CLICK_ACTION_BLOCKING if(choice == "Link Account") set_new_account(user) - return + return CLICK_ACTION_SUCCESS var/amount_to_remove = tgui_input_number(user, "How much do you want to withdraw? (Max: [registered_account.account_balance] cr)", "Withdraw Funds", max_value = registered_account.account_balance) if(!amount_to_remove || QDELETED(user) || QDELETED(src) || issilicon(user) || loc != user) - return + return CLICK_ACTION_BLOCKING if(!alt_click_can_use_id(user)) - return + return CLICK_ACTION_BLOCKING if(registered_account.adjust_money(-amount_to_remove, "System: Withdrawal")) var/obj/item/holochip/holochip = new (user.drop_location(), amount_to_remove) user.put_in_hands(holochip) to_chat(user, span_notice("You withdraw [amount_to_remove] credits into a holochip.")) SSblackbox.record_feedback("amount", "credits_removed", amount_to_remove) log_econ("[amount_to_remove] credits were removed from [src] owned by [src.registered_name]") - return + return CLICK_ACTION_SUCCESS else var/difference = amount_to_remove - registered_account.account_balance registered_account.bank_card_talk(span_warning("ERROR: The linked account requires [difference] more credit\s to perform that withdrawal."), TRUE) + return CLICK_ACTION_BLOCKING /obj/item/card/id/alt_click_secondary(mob/user) . = ..() @@ -923,8 +924,9 @@ department_name = ACCOUNT_CAR_NAME icon_state = "car_budget" //saving up for a new tesla -/obj/item/card/id/departmental_budget/AltClick(mob/living/user) +/obj/item/card/id/departmental_budget/click_alt(mob/living/user) registered_account.bank_card_talk(span_warning("Withdrawing is not compatible with this card design."), TRUE) //prevents the vault bank machine being useless and putting money from the budget to your card to go over personal crates + return CLICK_ACTION_BLOCKING /obj/item/card/id/advanced name = "identification card" diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 8d6c240ed0d..faf7c76e502 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -1236,11 +1236,10 @@ suction = !suction to_chat(user, span_notice("You [suction ? "enable" : "disable"] the board's suction function.")) -/obj/item/circuitboard/machine/dish_drive/AltClick(mob/living/user) - if(!user.Adjacent(src)) - return +/obj/item/circuitboard/machine/dish_drive/click_alt(mob/living/user) transmit = !transmit to_chat(user, span_notice("You [transmit ? "enable" : "disable"] the board's automatic disposal transmission.")) + return CLICK_ACTION_SUCCESS /obj/item/circuitboard/machine/gibber name = "Gibber" diff --git a/code/game/objects/items/cosmetics.dm b/code/game/objects/items/cosmetics.dm index bbb5e9e177d..42402c88e10 100644 --- a/code/game/objects/items/cosmetics.dm +++ b/code/game/objects/items/cosmetics.dm @@ -10,6 +10,7 @@ icon_state = "lipstick" inhand_icon_state = "lipstick" w_class = WEIGHT_CLASS_TINY + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING var/open = FALSE /// Actual color of the lipstick, also gets applied to the human var/lipstick_color = COLOR_RED @@ -45,15 +46,9 @@ colored_overlay.color = lipstick_color . += colored_overlay -/obj/item/lipstick/AltClick(mob/user) - . = ..() - if(.) - return TRUE - - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS|ALLOW_RESTING)) - return FALSE - - return display_radial_menu(user) +/obj/item/lipstick/click_alt(mob/user) + display_radial_menu(user) + return CLICK_ACTION_SUCCESS /obj/item/lipstick/proc/display_radial_menu(mob/living/carbon/human/user) var/style_options = list( diff --git a/code/game/objects/items/crayons.dm b/code/game/objects/items/crayons.dm index ae0dca8d195..4cb077808a8 100644 --- a/code/game/objects/items/crayons.dm +++ b/code/game/objects/items/crayons.dm @@ -759,6 +759,7 @@ pre_noise = TRUE post_noise = FALSE + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS /obj/item/toy/crayon/spraycan/Initialize(mapload) . = ..() @@ -960,12 +961,13 @@ return SECONDARY_ATTACK_CONTINUE_CHAIN -/obj/item/toy/crayon/spraycan/AltClick(mob/user) - if(!has_cap || !user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/toy/crayon/spraycan/click_alt(mob/user) + if(!has_cap) + return CLICK_ACTION_BLOCKING is_capped = !is_capped balloon_alert(user, is_capped ? "capped" : "cap removed") update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/toy/crayon/spraycan/attackby_storage_insert(datum/storage, atom/storage_holder, mob/user) return is_capped diff --git a/code/game/objects/items/credit_holochip.dm b/code/game/objects/items/credit_holochip.dm index 83e6165b91d..c9b6fe4a134 100644 --- a/code/game/objects/items/credit_holochip.dm +++ b/code/game/objects/items/credit_holochip.dm @@ -7,6 +7,8 @@ throwforce = 0 force = 0 w_class = WEIGHT_CLASS_TINY + interaction_flags_click = NEED_DEXTERITY|FORBID_TELEKINESIS_REACH + /// Amount on money on the card var/credits = 0 /obj/item/holochip/Initialize(mapload, amount = 1) @@ -101,23 +103,21 @@ update_appearance() qdel(H) -/obj/item/holochip/AltClick(mob/user) - if(!user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH)) - return +/obj/item/holochip/click_alt(mob/user) if(loc != user) to_chat(user, span_warning("You must be holding the holochip to continue!")) - return FALSE + return CLICK_ACTION_BLOCKING var/split_amount = tgui_input_number(user, "How many credits do you want to extract from the holochip? (Max: [credits] cr)", "Holochip", max_value = credits) if(!split_amount || QDELETED(user) || QDELETED(src) || issilicon(user) || !usr.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH) || loc != user) - return + return CLICK_ACTION_BLOCKING var/new_credits = spend(split_amount, TRUE) - var/obj/item/holochip/H = new(user ? user : drop_location(), new_credits) + var/obj/item/holochip/chip = new(user ? user : drop_location(), new_credits) if(user) - if(!user.put_in_hands(H)) - H.forceMove(user.drop_location()) + if(!user.put_in_hands(chip)) + chip.forceMove(user.drop_location()) add_fingerprint(user) - H.add_fingerprint(user) to_chat(user, span_notice("You extract [split_amount] credits into a new holochip.")) + return CLICK_ACTION_SUCCESS /obj/item/holochip/emp_act(severity) . = ..() diff --git a/code/game/objects/items/devices/desynchronizer.dm b/code/game/objects/items/devices/desynchronizer.dm index 7a2efbcec7e..b7b693b999f 100644 --- a/code/game/objects/items/devices/desynchronizer.dm +++ b/code/game/objects/items/devices/desynchronizer.dm @@ -9,11 +9,18 @@ lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' custom_materials = list(/datum/material/iron= SMALL_MATERIAL_AMOUNT * 2.5, /datum/material/glass= SMALL_MATERIAL_AMOUNT * 5) - var/max_duration = 3000 - var/duration = 300 + interaction_flags_click = NEED_DEXTERITY + /// Max time this can be set + var/max_duration = 300 SECONDS + /// Currently set time + var/duration = 30 SECONDS + /// Last world time var/last_use = 0 + /// world.time + (world.time - last_use) var/next_use = 0 + /// The current space time rift var/obj/effect/abstract/sync_holder/sync_holder + /// Timer obj for calling resync var/resync_timer /obj/item/desynchronizer/attack_self(mob/living/user) @@ -32,14 +39,13 @@ . += span_notice("Alt-click to customize the duration. Current duration: [DisplayTimeText(duration)].") . += span_notice("Can be used again to interrupt the effect early. The recharge time is the same as the time spent in desync.") -/obj/item/desynchronizer/AltClick(mob/living/user) - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/desynchronizer/click_alt(mob/living/user) var/new_duration = tgui_input_number(user, "Set the duration", "Desynchronizer", duration / 10, max_duration, 5) if(!new_duration || QDELETED(user) || QDELETED(src) || !usr.can_perform_action(src, NEED_DEXTERITY)) - return + return CLICK_ACTION_BLOCKING duration = new_duration to_chat(user, span_notice("You set the duration to [DisplayTimeText(duration)].")) + return CLICK_ACTION_SUCCESS /obj/item/desynchronizer/proc/desync(mob/living/user) if(sync_holder) diff --git a/code/game/objects/items/devices/geiger_counter.dm b/code/game/objects/items/devices/geiger_counter.dm index 6e152c33661..db2d0d820ba 100644 --- a/code/game/objects/items/devices/geiger_counter.dm +++ b/code/game/objects/items/devices/geiger_counter.dm @@ -110,12 +110,11 @@ to_chat(user, span_notice("[icon2html(src, user)] [isliving(target) ? "Subject" : "Target"] is free of radioactive contamination.")) -/obj/item/geiger_counter/AltClick(mob/living/user) - if(!istype(user) || !user.can_perform_action(src)) - return ..() +/obj/item/geiger_counter/click_alt(mob/living/user) if(!scanning) to_chat(usr, span_warning("[src] must be on to reset its radiation level!")) - return + return CLICK_ACTION_BLOCKING to_chat(usr, span_notice("You flush [src]'s radiation counts, resetting it to normal.")) last_perceived_radiation_danger = null update_appearance(UPDATE_ICON) + return CLICK_ACTION_SUCCESS diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 026e560cc97..225e914fbf6 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -80,14 +80,11 @@ diode = null return TRUE -/obj/item/laser_pointer/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(. & ITEM_INTERACT_ANY_BLOCKER) - return . +/obj/item/laser_pointer/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(isnull(crystal_lens)) - return . + return NONE if(tool_behaviour != TOOL_WIRECUTTER && tool_behaviour != TOOL_HEMOSTAT) - return . + return NONE tool.play_tool_sound(src) balloon_alert(user, "removed crystal lens") crystal_lens.forceMove(drop_location()) diff --git a/code/game/objects/items/devices/quantum_keycard.dm b/code/game/objects/items/devices/quantum_keycard.dm index abffdcca4d5..34b83a62092 100644 --- a/code/game/objects/items/devices/quantum_keycard.dm +++ b/code/game/objects/items/devices/quantum_keycard.dm @@ -10,6 +10,8 @@ righthand_file = 'icons/mob/inhands/equipment/idcards_righthand.dmi' w_class = WEIGHT_CLASS_TINY obj_flags = UNIQUE_RENAME + interaction_flags_click = NEED_DEXTERITY + /// The linked quantum pad var/obj/machinery/quantumpad/qpad /// where the pad is located and what color the card will become @@ -40,13 +42,12 @@ else . += span_notice("Insert [src] into an active quantum pad to link it.") -/obj/item/quantum_keycard/AltClick(mob/living/user) - if(!istype(user) || !user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/quantum_keycard/click_alt(mob/living/user) to_chat(user, span_notice("You start pressing [src]'s unlink button...")) if(do_after(user, 4 SECONDS, target = src)) to_chat(user, span_notice("The keycard beeps twice and disconnects the quantum link.")) set_pad() + return CLICK_ACTION_SUCCESS /obj/item/quantum_keycard/proc/set_pad(obj/machinery/quantumpad/new_pad) qpad = new_pad diff --git a/code/game/objects/items/devices/radio/headset.dm b/code/game/objects/items/devices/radio/headset.dm index 6bda55c8032..b9f2090eeb4 100644 --- a/code/game/objects/items/devices/radio/headset.dm +++ b/code/game/objects/items/devices/radio/headset.dm @@ -453,9 +453,9 @@ GLOBAL_LIST_INIT(channel_tokens, list( // And grant all the languages we definitely should know now grant_headset_languages(mob_loc) -/obj/item/radio/headset/AltClick(mob/living/user) - if(!istype(user) || !Adjacent(user) || user.incapacitated()) - return - if (command) - use_command = !use_command - to_chat(user, span_notice("You toggle high-volume mode [use_command ? "on" : "off"].")) +/obj/item/radio/headset/click_alt(mob/living/user) + if (!command) + return CLICK_ACTION_BLOCKING + use_command = !use_command + to_chat(user, span_notice("You toggle high-volume mode [use_command ? "on" : "off"].")) + return CLICK_ACTION_SUCCESS diff --git a/code/game/objects/items/devices/scanners/autopsy_scanner.dm b/code/game/objects/items/devices/scanners/autopsy_scanner.dm index 16f90489b20..c5d33b74226 100644 --- a/code/game/objects/items/devices/scanners/autopsy_scanner.dm +++ b/code/game/objects/items/devices/scanners/autopsy_scanner.dm @@ -13,7 +13,7 @@ custom_materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT*2) custom_price = PAYCHECK_COMMAND -/obj/item/autopsy_scanner/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/autopsy_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!user.can_read(src) || user.is_blind()) diff --git a/code/game/objects/items/devices/scanners/gas_analyzer.dm b/code/game/objects/items/devices/scanners/gas_analyzer.dm index fcc9257e4b5..0898c73aa2e 100644 --- a/code/game/objects/items/devices/scanners/gas_analyzer.dm +++ b/code/game/objects/items/devices/scanners/gas_analyzer.dm @@ -17,10 +17,16 @@ tool_behaviour = TOOL_ANALYZER custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT * 0.3, /datum/material/glass=SMALL_MATERIAL_AMOUNT * 0.2) grind_results = list(/datum/reagent/mercury = 5, /datum/reagent/iron = 5, /datum/reagent/silicon = 5) + interaction_flags_click = NEED_LITERACY|NEED_LIGHT + /// Boolean whether this has a CD var/cooldown = FALSE - var/cooldown_time = 250 - var/barometer_accuracy // 0 is the best accuracy. + /// The time in deciseconds + var/cooldown_time = 25 SECONDS + /// 0 is best accuracy + var/barometer_accuracy + /// Cached gasmix data from ui_interact var/list/last_gasmix_data + /// Max scan distance var/ranged_scan_distance = 1 /obj/item/analyzer/Initialize(mapload) @@ -53,19 +59,14 @@ user.visible_message(span_suicide("[user] begins to analyze [user.p_them()]self with [src]! The display shows that [user.p_theyre()] dead!")) return BRUTELOSS -/obj/item/analyzer/AltClick(mob/user) //Barometer output for measuring when the next storm happens - ..() - - if(!user.can_perform_action(src, NEED_LITERACY|NEED_LIGHT)) - return - +/obj/item/analyzer/click_alt(mob/user) //Barometer output for measuring when the next storm happens if(cooldown) to_chat(user, span_warning("[src]'s barometer function is preparing itself.")) - return + return CLICK_ACTION_BLOCKING var/turf/T = get_turf(user) if(!T) - return + return CLICK_ACTION_BLOCKING playsound(src, 'sound/effects/pop.ogg', 100) var/area/user_area = T.loc @@ -73,7 +74,7 @@ if(!user_area.outdoors) to_chat(user, span_warning("[src]'s barometer function won't work indoors!")) - return + return CLICK_ACTION_BLOCKING for(var/V in SSweather.processing) var/datum/weather/W = V @@ -84,7 +85,7 @@ if(ongoing_weather) if((ongoing_weather.stage == MAIN_STAGE) || (ongoing_weather.stage == WIND_DOWN_STAGE)) to_chat(user, span_warning("[src]'s barometer function can't trace anything while the storm is [ongoing_weather.stage == MAIN_STAGE ? "already here!" : "winding down."]")) - return + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("The next [ongoing_weather] will hit in [butchertime(ongoing_weather.next_hit_time - world.time)].")) if(ongoing_weather.aesthetic) @@ -98,6 +99,7 @@ to_chat(user, span_warning("[src]'s barometer function says a storm will land in approximately [butchertime(fixed)].")) cooldown = TRUE addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/item/analyzer, ping)), cooldown_time) + return CLICK_ACTION_SUCCESS /obj/item/analyzer/proc/ping() if(isliving(loc)) diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 7f672e7d32a..1a264cf3924 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -20,8 +20,12 @@ throw_speed = 3 throw_range = 7 custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT *2) + interaction_flags_click = NEED_LITERACY|NEED_LIGHT + /// Verbose/condensed var/mode = SCANNER_VERBOSE + /// HEALTH/WOUND var/scanmode = SCANMODE_HEALTH + /// Advanced health analyzer var/advanced = FALSE custom_price = PAYCHECK_COMMAND /// If this analyzer will give a bonus to wound treatments apon woundscan. @@ -51,7 +55,7 @@ if(SCANMODE_WOUND) to_chat(user, span_notice("You switch the health analyzer to report extra info on wounds.")) -/obj/item/healthanalyzer/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/healthanalyzer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!user.can_read(src)) //SKYRAT EDIT CHANGE - Blind People Can Analyze Again- ORIGINAL: if(!user.can_read(src) || user.is_blind()) @@ -89,7 +93,7 @@ add_fingerprint(user) -/obj/item/healthanalyzer/interact_with_atom_secondary(atom/interacting_with, mob/living/user) +/obj/item/healthanalyzer/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!user.can_read(src)) // SKYRAT EDIT CHANGE - Blind people can analyze again - ORIGINAL: if(!user.can_read(src) || user.is_blind()) @@ -489,17 +493,13 @@ // we handled the last
so we don't need handholding to_chat(user, examine_block(jointext(render_list, "")), trailing_newline = FALSE, type = MESSAGE_TYPE_INFO) -/obj/item/healthanalyzer/AltClick(mob/user) - ..() - - if(!user.can_perform_action(src, NEED_LITERACY|NEED_LIGHT) || user.is_blind()) - return - +/obj/item/healthanalyzer/click_alt(mob/user) if(mode == SCANNER_NO_MODE) - return + return CLICK_ACTION_BLOCKING mode = !mode to_chat(user, mode == SCANNER_VERBOSE ? "The scanner now shows specific limb damage." : "The scanner no longer shows limb damage.") + return CLICK_ACTION_SUCCESS /obj/item/healthanalyzer/advanced name = "advanced health analyzer" @@ -596,7 +596,7 @@ /obj/item/healthanalyzer/simple/proc/violence_damage(mob/living/user) user.adjustBruteLoss(4) -/obj/item/healthanalyzer/simple/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/healthanalyzer/simple/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!user.can_read(src)) //SKYRAT EDIT CHANGE - Blind People Can Analyze Again - ORIGINAL: if(!user.can_read(src) || user.is_blind()) diff --git a/code/game/objects/items/devices/scanners/sequence_scanner.dm b/code/game/objects/items/devices/scanners/sequence_scanner.dm index 3d212cbb771..3a45dae813b 100644 --- a/code/game/objects/items/devices/scanners/sequence_scanner.dm +++ b/code/game/objects/items/devices/scanners/sequence_scanner.dm @@ -29,7 +29,7 @@ if(LAZYLEN(genetic_makeup_buffer) > 0) . += span_notice("It has the genetic makeup of \"[genetic_makeup_buffer["name"]]\" stored inside its buffer") -/obj/item/sequence_scanner/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/sequence_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE @@ -46,7 +46,7 @@ user.visible_message(span_notice("[user] fails to analyze [interacting_with]'s genetic sequence."), span_warning("[interacting_with] has no readable genetic sequence!")) return ITEM_INTERACT_BLOCKING -/obj/item/sequence_scanner/interact_with_atom_secondary(atom/interacting_with, mob/living/user) +/obj/item/sequence_scanner/interact_with_atom_secondary(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE diff --git a/code/game/objects/items/devices/scanners/slime_scanner.dm b/code/game/objects/items/devices/scanners/slime_scanner.dm index ee8567ac6cc..bf55265f783 100644 --- a/code/game/objects/items/devices/scanners/slime_scanner.dm +++ b/code/game/objects/items/devices/scanners/slime_scanner.dm @@ -13,7 +13,7 @@ throw_range = 7 custom_materials = list(/datum/material/iron=SMALL_MATERIAL_AMOUNT*0.30, /datum/material/glass=SMALL_MATERIAL_AMOUNT * 0.20) -/obj/item/slime_scanner/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/slime_scanner/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!user.can_read(src)) //SKYRAT EDIT CHANGE - Blind People Can Analyze Again - ORIGINAL : if(!user.can_read(src) || user.is_blind()) diff --git a/code/game/objects/items/devices/swapper.dm b/code/game/objects/items/devices/swapper.dm index e7a749fc978..6db67049174 100644 --- a/code/game/objects/items/devices/swapper.dm +++ b/code/game/objects/items/devices/swapper.dm @@ -8,9 +8,12 @@ item_flags = NOBLUDGEON lefthand_file = 'icons/mob/inhands/items/devices_lefthand.dmi' righthand_file = 'icons/mob/inhands/items/devices_righthand.dmi' - - var/cooldown = 300 + interaction_flags_click = NEED_DEXTERITY + /// Cooldown for usage + var/cooldown = 30 SECONDS + /// Next available time var/next_use = 0 + /// Swapper linked to this obj var/obj/item/swapper/linked_swapper /obj/item/swapper/Destroy() @@ -72,15 +75,14 @@ else . += span_notice("Not Linked. Use on another quantum spin inverter to establish a quantum link.") -/obj/item/swapper/AltClick(mob/living/user) - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/swapper/click_alt(mob/living/user) to_chat(user, span_notice("You break the current quantum link.")) if(!QDELETED(linked_swapper)) linked_swapper.linked_swapper = null linked_swapper.update_appearance() linked_swapper = null update_appearance() + return CLICK_ACTION_SUCCESS //Gets the topmost teleportable container /obj/item/swapper/proc/get_teleportable_container() diff --git a/code/game/objects/items/devices/taperecorder.dm b/code/game/objects/items/devices/taperecorder.dm index c686eeb04a7..d30f379197e 100644 --- a/code/game/objects/items/devices/taperecorder.dm +++ b/code/game/objects/items/devices/taperecorder.dm @@ -61,11 +61,9 @@ . += span_notice("The wire panel is [open_panel ? "opened" : "closed"]. The display reads:") . += "[readout()]" -/obj/item/taperecorder/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return +/obj/item/taperecorder/click_alt(mob/user) play() + return CLICK_ACTION_SUCCESS /obj/item/taperecorder/proc/update_available_icons() icons_available = list() diff --git a/code/game/objects/items/devices/traitordevices.dm b/code/game/objects/items/devices/traitordevices.dm index 8e8f2578fa4..7e6ee077fce 100644 --- a/code/game/objects/items/devices/traitordevices.dm +++ b/code/game/objects/items/devices/traitordevices.dm @@ -76,7 +76,7 @@ effective or pretty fucking useless. var/intensity = 10 // how much damage the radiation does var/wavelength = 10 // time it takes for the radiation to kick in, in seconds -/obj/item/healthanalyzer/rad_laser/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/healthanalyzer/rad_laser/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!stealth || !irradiate) . = ..() diff --git a/code/game/objects/items/emags.dm b/code/game/objects/items/emags.dm index 141e242fc03..f197a9b5fdb 100644 --- a/code/game/objects/items/emags.dm +++ b/code/game/objects/items/emags.dm @@ -49,7 +49,7 @@ user.visible_message(span_notice("[user] shows you: [icon2html(src, viewers(user))] [name]."), span_notice("You show [src].")) add_fingerprint(user) -/obj/item/card/emagfake/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/card/emagfake/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) playsound(src, 'sound/items/bikehorn.ogg', 50, TRUE) return ITEM_INTERACT_SKIP_TO_ATTACK // So it does the attack animation. @@ -57,7 +57,7 @@ . = ..() type_blacklist = list(typesof(/obj/machinery/door/airlock) + typesof(/obj/machinery/door/window/) + typesof(/obj/machinery/door/firedoor) - typesof(/obj/machinery/door/airlock/tram)) //list of all typepaths that require a specialized emag to hack. -/obj/item/card/emag/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/card/emag/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!can_emag(interacting_with, user)) return ITEM_INTERACT_BLOCKING log_combat(user, interacting_with, "attempted to emag") diff --git a/code/game/objects/items/etherealdiscoball.dm b/code/game/objects/items/etherealdiscoball.dm index 0a95e4bbc73..fe066bd1bf5 100644 --- a/code/game/objects/items/etherealdiscoball.dm +++ b/code/game/objects/items/etherealdiscoball.dm @@ -41,10 +41,10 @@ TurnOn() to_chat(user, span_notice("You turn the disco ball on!")) -/obj/structure/etherealball/AltClick(mob/living/carbon/human/user) - . = ..() +/obj/structure/etherealball/click_alt(mob/living/carbon/human/user) set_anchored(!anchored) to_chat(user, span_notice("You [anchored ? null : "un"]lock the disco ball.")) + return CLICK_ACTION_SUCCESS /obj/structure/etherealball/proc/TurnOn() TurnedOn = TRUE //Same diff --git a/code/game/objects/items/extinguisher.dm b/code/game/objects/items/extinguisher.dm index 0b2daeb655d..c78242255a6 100644 --- a/code/game/objects/items/extinguisher.dm +++ b/code/game/objects/items/extinguisher.dm @@ -18,6 +18,7 @@ attack_verb_simple = list("slam", "whack", "bash", "thunk", "batter", "bludgeon", "thrash") dog_fashion = /datum/dog_fashion/back resistance_flags = FIRE_PROOF + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS /// The max amount of water this extinguisher can hold. var/max_water = 50 /// Does the welder extinguisher start with water. @@ -270,13 +271,12 @@ if(1 to 3) source.delay = 3 -/obj/item/extinguisher/AltClick(mob/user) - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/extinguisher/click_alt(mob/user) if(!user.is_holding(src)) to_chat(user, span_notice("You must be holding the [src] in your hands do this!")) - return + return CLICK_ACTION_BLOCKING EmptyExtinguisher(user) + return CLICK_ACTION_SUCCESS /obj/item/extinguisher/proc/EmptyExtinguisher(mob/user) if(loc == user && reagents.total_volume) diff --git a/code/game/objects/items/flamethrower.dm b/code/game/objects/items/flamethrower.dm index 346b8597533..d2f4b0cfc71 100644 --- a/code/game/objects/items/flamethrower.dm +++ b/code/game/objects/items/flamethrower.dm @@ -20,6 +20,7 @@ light_range = 2 light_power = 2 light_on = FALSE + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS var/status = FALSE var/lit = FALSE //on or off var/operating = FALSE//cooldown @@ -158,12 +159,15 @@ /obj/item/flamethrower/attack_self(mob/user) toggle_igniter(user) -/obj/item/flamethrower/AltClick(mob/user) - if(ptank && isliving(user) && user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - user.put_in_hands(ptank) - ptank = null - to_chat(user, span_notice("You remove the plasma tank from [src]!")) - update_appearance() +/obj/item/flamethrower/click_alt(mob/user) + if(isnull(ptank)) + return NONE + + user.put_in_hands(ptank) + ptank = null + to_chat(user, span_notice("You remove the plasma tank from [src]!")) + update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/flamethrower/examine(mob/user) . = ..() @@ -219,10 +223,6 @@ sleep(0.1 SECONDS) previousturf = T operating = FALSE - for(var/mob/M in viewers(1, loc)) - if((M.client && M.machine == src)) - attack_self(M) - /obj/item/flamethrower/proc/default_ignite(turf/target, release_amount = 0.05) //TODO: DEFERRED Consider checking to make sure tank pressure is high enough before doing this... diff --git a/code/game/objects/items/implants/implantpad.dm b/code/game/objects/items/implants/implantpad.dm index 706fb29733d..0d0391126c9 100644 --- a/code/game/objects/items/implants/implantpad.dm +++ b/code/game/objects/items/implants/implantpad.dm @@ -10,6 +10,7 @@ throw_speed = 3 throw_range = 5 w_class = WEIGHT_CLASS_SMALL + interaction_flags_click = FORBID_TELEKINESIS_REACH ///The implant case currently inserted into the pad. var/obj/item/implantcase/inserted_case @@ -46,11 +47,9 @@ update_static_data_for_all_viewers() update_appearance(UPDATE_ICON) -/obj/item/implantpad/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return +/obj/item/implantpad/click_alt(mob/user) remove_implant(user) + return CLICK_ACTION_SUCCESS /obj/item/implantpad/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/game/objects/items/inspector.dm b/code/game/objects/items/inspector.dm index 2f25e53c1fb..f96c1beb573 100644 --- a/code/game/objects/items/inspector.dm +++ b/code/game/objects/items/inspector.dm @@ -372,15 +372,16 @@ */ /obj/item/paper/fake_report/water grind_results = list(/datum/reagent/water = 5) + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS -/obj/item/paper/fake_report/water/AltClick(mob/living/user, obj/item/I) - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/paper/fake_report/water/click_alt(mob/living/user) var/datum/action/innate/origami/origami_action = locate() in user.actions if(origami_action?.active) //Origami masters can fold water - make_plane(user, I, /obj/item/paperplane/syndicate) + make_plane(user, /obj/item/paperplane/syndicate) else if(do_after(user, 1 SECONDS, target = src, progress=TRUE)) var/turf/open/target = get_turf(src) target.MakeSlippery(TURF_WET_WATER, min_wet_time = 10 SECONDS, wet_time_to_add = 5 SECONDS) to_chat(user, span_notice("As you try to fold [src] into the shape of a plane, it disintegrates into water!")) qdel(src) + + return CLICK_ACTION_SUCCESS diff --git a/code/game/objects/items/machine_wand.dm b/code/game/objects/items/machine_wand.dm index 71ea2fae1b6..c1dd0bf382d 100644 --- a/code/game/objects/items/machine_wand.dm +++ b/code/game/objects/items/machine_wand.dm @@ -63,14 +63,15 @@ if(controlling_machine_or_bot) return controlling_machine_or_bot.ui_act(action, params, ui, state) -/obj/item/machine_remote/AltClick(mob/user) - . = ..() +/obj/item/machine_remote/click_alt(mob/user) if(moving_bug) //we have a bug in transit, so let's kill it. QDEL_NULL(moving_bug) + return CLICK_ACTION_BLOCKING if(!controlling_machine_or_bot) - return + return CLICK_ACTION_BLOCKING say("Remote control over [controlling_machine_or_bot] stopped.") remove_old_machine() + return CLICK_ACTION_SUCCESS /obj/item/machine_remote/afterattack(atom/target, mob/user, proximity_flag, click_parameters) . = ..() diff --git a/code/game/objects/items/paiwire.dm b/code/game/objects/items/paiwire.dm index fa5724ebb53..d590d4e9603 100644 --- a/code/game/objects/items/paiwire.dm +++ b/code/game/objects/items/paiwire.dm @@ -4,15 +4,15 @@ icon = 'icons/obj/stack_objects.dmi' icon_state = "wire1" item_flags = NOBLUDGEON - var/obj/machinery/machine //what machine we're currently hacking. + ///The current machine being hacked by the pAI cable. + var/obj/machinery/hacking_machine /obj/item/pai_cable/Destroy() - machine = null + hacking_machine = null return ..() - /obj/item/pai_cable/proc/plugin(obj/machinery/M, mob/living/user) if(!user.transferItemToLoc(src, M)) return user.visible_message(span_notice("[user] inserts [src] into a data port on [M]."), span_notice("You insert [src] into a data port on [M]."), span_hear("You hear the satisfying click of a wire jack fastening into place.")) - machine = M + hacking_machine = M diff --git a/code/game/objects/items/pet_carrier.dm b/code/game/objects/items/pet_carrier.dm index bbbeb86602c..0d9b0909db2 100644 --- a/code/game/objects/items/pet_carrier.dm +++ b/code/game/objects/items/pet_carrier.dm @@ -66,9 +66,9 @@ open = TRUE update_appearance() -/obj/item/pet_carrier/AltClick(mob/living/user) - if(open || !user.can_perform_action(src)) - return +/obj/item/pet_carrier/click_alt(mob/living/user) + if(open) + return CLICK_ACTION_BLOCKING locked = !locked to_chat(user, span_notice("You flip the lock switch [locked ? "down" : "up"].")) if(locked) @@ -76,6 +76,7 @@ else playsound(user, 'sound/machines/boltsup.ogg', 30, TRUE) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/pet_carrier/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(user.combat_mode || !isliving(interacting_with)) diff --git a/code/game/objects/items/pillow.dm b/code/game/objects/items/pillow.dm index 1dad0a7fba5..659cc38f58b 100644 --- a/code/game/objects/items/pillow.dm +++ b/code/game/objects/items/pillow.dm @@ -102,16 +102,15 @@ if(pillow_trophy) . += span_notice("Alt-click to remove the tag!") -/obj/item/pillow/AltClick(mob/user) - . = ..() - if(!can_interact(user) || !user.can_hold_items(src)) - return +/obj/item/pillow/click_alt(mob/user) + if(!user.can_hold_items(src)) + return CLICK_ACTION_BLOCKING if(!pillow_trophy) balloon_alert(user, "no tag!") - return + return CLICK_ACTION_BLOCKING balloon_alert(user, "removing tag...") if(!do_after(user, 2 SECONDS, src)) - return + return CLICK_ACTION_BLOCKING if(last_fighter) pillow_trophy.desc = "A pillow tag taken from [last_fighter] after a gruesome pillow fight." user.put_in_hands(pillow_trophy) @@ -119,6 +118,7 @@ balloon_alert(user, "tag removed") playsound(user,'sound/items/poster_ripped.ogg', 50) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/pillow/update_appearance(updates) . = ..() diff --git a/code/game/objects/items/rcd/RPLD.dm b/code/game/objects/items/rcd/RPLD.dm index e5100d11c04..f8e38c5aadb 100644 --- a/code/game/objects/items/rcd/RPLD.dm +++ b/code/game/objects/items/rcd/RPLD.dm @@ -282,8 +282,9 @@ create_machine(target, user) -/obj/item/construction/plumbing/AltClick(mob/user) +/obj/item/construction/plumbing/click_alt(mob/user) ui_interact(user) + return CLICK_ACTION_SUCCESS /obj/item/construction/plumbing/proc/mouse_wheeled(mob/source, atom/A, delta_x, delta_y, params) SIGNAL_HANDLER diff --git a/code/game/objects/items/rcd/RWD.dm b/code/game/objects/items/rcd/RWD.dm index a20946e322f..0ee29e87a35 100644 --- a/code/game/objects/items/rcd/RWD.dm +++ b/code/game/objects/items/rcd/RWD.dm @@ -125,8 +125,7 @@ add_cable(user, cable) return TRUE -/obj/item/rwd/AltClick(mob/user) - . = ..() +/obj/item/rwd/click_alt(mob/user) if(!radial_menu) radial_menu = list( "Layer 1" = image(icon = 'icons/hud/radial.dmi', icon_state = "coil-red"), @@ -136,7 +135,7 @@ var/layer_result = show_radial_menu(user, src, radial_menu, custom_check = CALLBACK(src, PROC_REF(check_menu), user), require_near = TRUE, tooltips = TRUE) if(!check_menu(user)) - return + return CLICK_ACTION_BLOCKING switch(layer_result) if("Layer 1") cable_layer = CABLE_LAYER_1 @@ -145,6 +144,7 @@ if("Layer 3") cable_layer = CABLE_LAYER_3 update_appearance(UPDATE_ICON_STATE) + return CLICK_ACTION_SUCCESS /obj/item/rwd/proc/check_menu(mob/living/user) if(!istype(user)) diff --git a/code/game/objects/items/robot/items/hypo.dm b/code/game/objects/items/robot/items/hypo.dm index 9cfdce50fe3..847807ee9f4 100644 --- a/code/game/objects/items/robot/items/hypo.dm +++ b/code/game/objects/items/robot/items/hypo.dm @@ -245,13 +245,12 @@ . += "Currently loaded: [selected_reagent ? "[selected_reagent]. [selected_reagent.description]" : "nothing."]" . += span_notice("Alt+Click to change transfer amount. Currently set to [amount_per_transfer_from_this]u.") -/obj/item/reagent_containers/borghypo/AltClick(mob/living/user) - . = ..() -/* SKYRAT REMOVAL START - Changing transfer amounts is now handled by the parent proc in modular files. - if(user.stat == DEAD || user != loc) - return //IF YOU CAN HEAR ME SET MY TRANSFER AMOUNT TO 1 - change_transfer_amount(user) -*/ // SKYRAT REMOVAL END +/* SKYRAT EDIT REMOVAL START - SEE master_files/code/modules/reagents/reagent_containers.dm +/obj/item/reagent_containers/borghypo/click_alt(mob/living/user) + + change_transfer_amount(user) + return CLICK_ACTION_SUCCESS +SKYRAT EDIT REMOVAL END */ /// Default Medborg Hypospray /obj/item/reagent_containers/borghypo/medical diff --git a/code/game/objects/items/robot/items/storage.dm b/code/game/objects/items/robot/items/storage.dm index 01541df5a9c..2d91128adb6 100644 --- a/code/game/objects/items/robot/items/storage.dm +++ b/code/game/objects/items/robot/items/storage.dm @@ -50,10 +50,11 @@ stored.attack_self(user) //Alt click drops the stored item. -/obj/item/borg/apparatus/AltClick(mob/living/silicon/robot/user) +/obj/item/borg/apparatus/click_alt(mob/living/silicon/robot/user) if(!stored || !issilicon(user)) - return ..() + return CLICK_ACTION_BLOCKING stored.forceMove(user.drop_location()) + return CLICK_ACTION_SUCCESS /obj/item/borg/apparatus/pre_attack(atom/atom, mob/living/user, params) if(stored) @@ -244,16 +245,16 @@ bag = mutable_appearance(icon, icon_state = "evidenceobj") // empty bag . += bag -/obj/item/borg/apparatus/organ_storage/AltClick(mob/living/silicon/robot/user) - . = ..() - if(stored) - var/obj/item/organ = stored - user.visible_message(span_notice("[user] dumps [organ] from [src]."), span_notice("You dump [organ] from [src].")) - cut_overlays() - organ.forceMove(get_turf(src)) - else +/obj/item/borg/apparatus/organ_storage/click_alt(mob/living/silicon/robot/user) + if(!stored) to_chat(user, span_notice("[src] is empty.")) - return + return CLICK_ACTION_BLOCKING + + var/obj/item/organ = stored + user.visible_message(span_notice("[user] dumps [organ] from [src]."), span_notice("You dump [organ] from [src].")) + cut_overlays() + organ.forceMove(get_turf(src)) + return CLICK_ACTION_SUCCESS ///Apparatus to allow Engineering/Sabo borgs to manipulate any material sheets. /obj/item/borg/apparatus/sheet_manipulator diff --git a/code/game/objects/items/robot/robot_upgrades.dm b/code/game/objects/items/robot/robot_upgrades.dm index 3d1696cc303..885a97a9329 100644 --- a/code/game/objects/items/robot/robot_upgrades.dm +++ b/code/game/objects/items/robot/robot_upgrades.dm @@ -40,7 +40,10 @@ one_use = TRUE /obj/item/borg/upgrade/rename/attack_self(mob/user) - heldname = sanitize_name(tgui_input_text(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN), allow_numbers = TRUE) + var/new_heldname = sanitize_name(tgui_input_text(user, "Enter new robot name", "Cyborg Reclassification", heldname, MAX_NAME_LEN), allow_numbers = TRUE) + if(!new_heldname || !user.is_holding(src)) + return + heldname = new_heldname user.log_message("set \"[heldname]\" as a name in a cyborg reclassification board at [loc_name(user)]", LOG_GAME) /obj/item/borg/upgrade/rename/action(mob/living/silicon/robot/R, user = usr) diff --git a/code/game/objects/items/spear.dm b/code/game/objects/items/spear.dm index 5590bfd3c2f..401ffd46934 100644 --- a/code/game/objects/items/spear.dm +++ b/code/game/objects/items/spear.dm @@ -160,13 +160,12 @@ . = ..() . += span_notice("Alt-click to set your war cry.") -/obj/item/spear/explosive/AltClick(mob/user) - if(user.can_perform_action(src)) - ..() - if(istype(user) && loc == user) - var/input = tgui_input_text(user, "What do you want your war cry to be? You will shout it when you hit someone in melee.", "War Cry", max_length = 50) - if(input) - src.war_cry = input +/obj/item/spear/explosive/click_alt(mob/user) + var/input = tgui_input_text(user, "What do you want your war cry to be? You will shout it when you hit someone in melee.", "War Cry", max_length = 50) + if(input) + war_cry = input + return CLICK_ACTION_SUCCESS + /obj/item/spear/explosive/afterattack(atom/movable/AM, mob/user, proximity) . = ..() diff --git a/code/game/objects/items/stacks/bscrystal.dm b/code/game/objects/items/stacks/bscrystal.dm index 75c35eabb18..4e8a01971ba 100644 --- a/code/game/objects/items/stacks/bscrystal.dm +++ b/code/game/objects/items/stacks/bscrystal.dm @@ -67,6 +67,7 @@ icon = 'icons/obj/stack_objects.dmi' icon_state = "polycrystal" inhand_icon_state = null + gulag_valid = TRUE singular_name = "bluespace polycrystal" desc = "A stable polycrystal, made of fused-together bluespace crystals. You could probably break one off." mats_per_unit = list(/datum/material/bluespace=SHEET_MATERIAL_AMOUNT) @@ -74,7 +75,6 @@ attack_verb_simple = list("bluespace polybash", "bluespace polybatter", "bluespace polybludgeon", "bluespace polythrash", "bluespace polysmash") novariants = TRUE grind_results = list(/datum/reagent/bluespace = 20) - point_value = 90 merge_type = /obj/item/stack/sheet/bluespace_crystal material_type = /datum/material/bluespace var/crystal_type = /obj/item/stack/ore/bluespace_crystal/refined diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm index 1f33384b393..59c65411805 100644 --- a/code/game/objects/items/stacks/medical.dm +++ b/code/game/objects/items/stacks/medical.dm @@ -35,7 +35,7 @@ /// Time it takes to assess injuries when looping healing var/assessing_injury_delay = 1 SECONDS -/obj/item/stack/medical/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/stack/medical/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!isliving(interacting_with)) return NONE if(!begin_heal_loop(interacting_with, user)) @@ -419,11 +419,11 @@ return return ..() -/obj/item/stack/medical/mesh/AltClick(mob/living/user) +/obj/item/stack/medical/mesh/click_alt(mob/living/user) if(!is_open) balloon_alert(user, "open it first!") - return - return ..() + return CLICK_ACTION_BLOCKING + return CLICK_ACTION_SUCCESS /obj/item/stack/medical/mesh/attack_hand(mob/user, list/modifiers) if(!is_open && user.get_inactive_held_item() == src) diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index d6bd65afe31..1f66b80cf55 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -27,7 +27,6 @@ GLOBAL_LIST_INIT(glass_recipes, list ( \ merge_type = /obj/item/stack/sheet/glass grind_results = list(/datum/reagent/silicon = 20) material_type = /datum/material/glass - point_value = 1 tableVariant = /obj/structure/table/glass matter_amount = 4 cost = SHEET_MATERIAL_AMOUNT @@ -159,7 +158,6 @@ GLOBAL_LIST_INIT(reinforced_glass_recipes, list ( \ resistance_flags = ACID_PROOF merge_type = /obj/item/stack/sheet/rglass grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/iron = 10) - point_value = 12 matter_amount = 6 tableVariant = /obj/structure/table/reinforced/rglass @@ -197,7 +195,7 @@ GLOBAL_LIST_INIT(prglass_recipes, list ( \ material_flags = NONE merge_type = /obj/item/stack/sheet/plasmarglass grind_results = list(/datum/reagent/silicon = 20, /datum/reagent/toxin/plasma = 10, /datum/reagent/iron = 10) - point_value = 69 + gulag_valid = TRUE matter_amount = 8 tableVariant = /obj/structure/table/reinforced/plasmarglass diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index 0620d82cd9b..1b3d66a7dae 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -99,7 +99,7 @@ GLOBAL_LIST_INIT(sandbag_recipes, list ( \ sheettype = "diamond" mats_per_unit = list(/datum/material/diamond=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/carbon = 20) - point_value = 75 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/diamond material_type = /datum/material/diamond walltype = /turf/closed/wall/mineral/diamond @@ -124,7 +124,7 @@ GLOBAL_LIST_INIT(diamond_recipes, list ( \ sheettype = "uranium" mats_per_unit = list(/datum/material/uranium=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/uranium = 20) - point_value = 60 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/uranium material_type = /datum/material/uranium walltype = /turf/closed/wall/mineral/uranium @@ -157,7 +157,7 @@ GLOBAL_LIST_INIT(uranium_recipes, list ( \ max_integrity = 100 mats_per_unit = list(/datum/material/plasma=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/toxin/plasma = 20) - point_value = 60 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/plasma material_type = /datum/material/plasma walltype = /turf/closed/wall/mineral/plasma @@ -192,7 +192,7 @@ GLOBAL_LIST_INIT(plasma_recipes, list ( \ sheettype = "gold" mats_per_unit = list(/datum/material/gold=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/gold = 20) - point_value = 60 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/gold material_type = /datum/material/gold walltype = /turf/closed/wall/mineral/gold @@ -219,7 +219,7 @@ GLOBAL_LIST_INIT(gold_recipes, list ( \ sheettype = "silver" mats_per_unit = list(/datum/material/silver=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/silver = 20) - point_value = 60 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/silver material_type = /datum/material/silver tableVariant = /obj/structure/table/optable @@ -245,7 +245,7 @@ GLOBAL_LIST_INIT(silver_recipes, list ( \ sheettype = "bananium" mats_per_unit = list(/datum/material/bananium=SHEET_MATERIAL_AMOUNT) grind_results = list(/datum/reagent/consumable/banana = 20) - point_value = 150 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/bananium material_type = /datum/material/bananium walltype = /turf/closed/wall/mineral/bananium @@ -276,7 +276,7 @@ GLOBAL_LIST_INIT(bananium_recipes, list ( \ throw_range = 3 sheettype = "titanium" mats_per_unit = list(/datum/material/titanium=SHEET_MATERIAL_AMOUNT) - point_value = 60 + gulag_valid = TRUE merge_type = /obj/item/stack/sheet/mineral/titanium material_type = /datum/material/titanium walltype = /turf/closed/wall/mineral/titanium @@ -308,7 +308,7 @@ GLOBAL_LIST_INIT(titanium_recipes, list ( \ throw_range = 3 sheettype = "plastitanium" mats_per_unit = list(/datum/material/alloy/plastitanium=SHEET_MATERIAL_AMOUNT) - point_value = 135 + gulag_valid = TRUE material_type = /datum/material/alloy/plastitanium merge_type = /obj/item/stack/sheet/mineral/plastitanium material_flags = NONE @@ -482,7 +482,7 @@ GLOBAL_LIST_INIT(metalhydrogen_recipes, list( singular_name = "metal hydrogen sheet" w_class = WEIGHT_CLASS_NORMAL resistance_flags = FIRE_PROOF | LAVA_PROOF | ACID_PROOF | INDESTRUCTIBLE - point_value = 300 + gulag_valid = TRUE mats_per_unit = list(/datum/material/metalhydrogen = SHEET_MATERIAL_AMOUNT) material_type = /datum/material/metalhydrogen merge_type = /obj/item/stack/sheet/mineral/metal_hydrogen @@ -497,7 +497,7 @@ GLOBAL_LIST_INIT(metalhydrogen_recipes, list( inhand_icon_state = "sheet-zaukerite" singular_name = "zaukerite crystal" w_class = WEIGHT_CLASS_NORMAL - point_value = 360 + gulag_valid = TRUE mats_per_unit = list(/datum/material/zaukerite = SHEET_MATERIAL_AMOUNT) merge_type = /obj/item/stack/sheet/mineral/zaukerite material_type = /datum/material/zaukerite diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index 4d02057d30a..280e009210c 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -150,7 +150,7 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \ resistance_flags = FIRE_PROOF merge_type = /obj/item/stack/sheet/iron grind_results = list(/datum/reagent/iron = 20) - point_value = 6 + gulag_valid = TRUE tableVariant = /obj/structure/table material_type = /datum/material/iron matter_amount = 4 @@ -277,7 +277,7 @@ GLOBAL_LIST_INIT(plasteel_recipes, list ( \ resistance_flags = FIRE_PROOF merge_type = /obj/item/stack/sheet/plasteel grind_results = list(/datum/reagent/iron = 20, /datum/reagent/toxin/plasma = 20) - point_value = 69 + gulag_valid = TRUE tableVariant = /obj/structure/table/reinforced material_flags = NONE matter_amount = 12 diff --git a/code/game/objects/items/stacks/sheets/sheets.dm b/code/game/objects/items/stacks/sheets/sheets.dm index 530a3573f7c..8b81953528e 100644 --- a/code/game/objects/items/stacks/sheets/sheets.dm +++ b/code/game/objects/items/stacks/sheets/sheets.dm @@ -14,7 +14,10 @@ novariants = FALSE material_flags = MATERIAL_EFFECTS var/sheettype = null //this is used for girders in the creation of walls/false walls - var/point_value = 0 //turn-in value for the gulag stacker - loosely relative to its rarity. + ///If true, this is worth points in the gulag labour stacker + var/gulag_valid = FALSE + ///Set to true if this is vended from a material storage + var/manufactured = FALSE ///What type of wall does this sheet spawn var/walltype /// whether this sheet can be sniffed by the material sniffer @@ -32,11 +35,24 @@ GLOB.sniffable_sheets -= src return ..() +/obj/item/stack/sheet/examine(mob/user) + . = ..() + if (manufactured && gulag_valid) + . += "It has been embossed with a manufacturer's mark of guaranteed quality." + /obj/item/stack/sheet/add(_amount) . = ..() if(sniffable && amount >= 10 && is_station_level(z)) GLOB.sniffable_sheets |= src +/obj/item/stack/sheet/merge(obj/item/stack/sheet/target_stack, limit) + . = ..() + manufactured = manufactured && target_stack.manufactured + +/obj/item/stack/sheet/copy_evidences(obj/item/stack/sheet/from) + . = ..() + manufactured = from.manufactured + /// removing from sniffable handled by the sniffer itself when it checks for targets /** diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index a6bbe3bfe19..dd1b74eb106 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -11,7 +11,7 @@ merge_type = /obj/item/stack/telecrystal novariants = FALSE -/obj/item/stack/telecrystal/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/stack/telecrystal/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(interacting_with != user) //You can't go around smacking people with crystals to find out if they have an uplink or not. return NONE diff --git a/code/game/objects/items/stacks/wrap.dm b/code/game/objects/items/stacks/wrap.dm index 1354b57f16f..49bac7d2d6d 100644 --- a/code/game/objects/items/stacks/wrap.dm +++ b/code/game/objects/items/stacks/wrap.dm @@ -36,13 +36,14 @@ //Set layers to these colors, base then ribbon set_greyscale(colors = list(generated_base_color, generated_ribbon_color)) -/obj/item/stack/wrapping_paper/AltClick(mob/user, modifiers) +/obj/item/stack/wrapping_paper/click_alt(mob/user) var/new_base = input(user, "", "Select a base color", color) as color var/new_ribbon = input(user, "", "Select a ribbon color", color) as color - if(!user.can_perform_action(src)) - return + if(!new_base || !new_ribbon) + return CLICK_ACTION_BLOCKING + set_greyscale(colors = list(new_base, new_ribbon)) - return TRUE + return CLICK_ACTION_SUCCESS //preset wrapping paper meant to fill the original color configuration /obj/item/stack/wrapping_paper/xmas diff --git a/code/game/objects/items/storage/bags.dm b/code/game/objects/items/storage/bags.dm index d72a57f07f1..cc548be16d2 100644 --- a/code/game/objects/items/storage/bags.dm +++ b/code/game/objects/items/storage/bags.dm @@ -360,23 +360,30 @@ /obj/item/storage/bag/tray/Initialize(mapload) . = ..() atom_storage.max_specific_storage = WEIGHT_CLASS_BULKY //Plates are required bulky to keep them out of backpacks - atom_storage.set_holdable(list( - /obj/item/clothing/mask/cigarette, - /obj/item/food, - /obj/item/kitchen, - /obj/item/lighter, - /obj/item/organ, - /obj/item/plate, - /obj/item/reagent_containers/condiment, - /obj/item/reagent_containers/cup, - /obj/item/rollingpaper, - /obj/item/storage/box/gum, - /obj/item/storage/box/matches, - /obj/item/storage/fancy, - /obj/item/trash, - )) //Should cover: Bottles, Beakers, Bowls, Booze, Glasses, Food, Food Containers, Food Trash, Organs, Tobacco Products, Lighters, and Kitchen Tools. + atom_storage.set_holdable( + can_hold_list = list( + /obj/item/clothing/mask/cigarette, + /obj/item/food, + /obj/item/kitchen, + /obj/item/lighter, + /obj/item/organ, + /obj/item/plate, + /obj/item/reagent_containers/condiment, + /obj/item/reagent_containers/cup, + /obj/item/rollingpaper, + /obj/item/storage/box/gum, + /obj/item/storage/box/matches, + /obj/item/storage/fancy, + /obj/item/trash, + ), + cant_hold_list = list( + /obj/item/plate/oven_tray, + /obj/item/reagent_containers/cup/soup_pot, + ), + ) //Should cover: Bottles, Beakers, Bowls, Booze, Glasses, Food, Food Containers, Food Trash, Organs, Tobacco Products, Lighters, and Kitchen Tools. atom_storage.insert_preposition = "on" - atom_storage.max_slots = 7 + atom_storage.max_slots = 8 + atom_storage.max_total_storage = 16 /obj/item/storage/bag/tray/attack(mob/living/M, mob/living/user) . = ..() diff --git a/code/game/objects/items/storage/belt.dm b/code/game/objects/items/storage/belt.dm index 451dc82c89c..ebfa73aa2f9 100644 --- a/code/game/objects/items/storage/belt.dm +++ b/code/game/objects/items/storage/belt.dm @@ -839,6 +839,7 @@ inhand_icon_state = "sheath" worn_icon_state = "sheath" w_class = WEIGHT_CLASS_BULKY + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS /obj/item/storage/belt/sabre/Initialize(mapload) . = ..() @@ -854,9 +855,7 @@ if(length(contents)) . += span_notice("Alt-click it to quickly draw the blade.") -/obj/item/storage/belt/sabre/AltClick(mob/user) - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/storage/belt/sabre/click_alt(mob/user) if(length(contents)) var/obj/item/I = contents[1] user.visible_message(span_notice("[user] takes [I] out of [src]."), span_notice("You take [I] out of [src].")) @@ -864,6 +863,7 @@ update_appearance() else balloon_alert(user, "it's empty!") + return CLICK_ACTION_SUCCESS /obj/item/storage/belt/sabre/update_icon_state() icon_state = initial(inhand_icon_state) diff --git a/code/game/objects/items/storage/boxes/engineering_boxes.dm b/code/game/objects/items/storage/boxes/engineering_boxes.dm index de975d9dbfe..359f9c17c7b 100644 --- a/code/game/objects/items/storage/boxes/engineering_boxes.dm +++ b/code/game/objects/items/storage/boxes/engineering_boxes.dm @@ -24,10 +24,7 @@ /obj/item/storage/box/material/Initialize(mapload) . = ..() - atom_storage.allow_big_nesting = TRUE - atom_storage.max_slots = 99 atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC - atom_storage.max_total_storage = 99 /obj/item/storage/box/material/PopulateContents() //less uranium because radioactive var/static/items_inside = list( @@ -51,6 +48,11 @@ /obj/item/stack/sheet/plastic/fifty=1, /obj/item/stack/sheet/runed_metal/fifty=1, ) + //This needs to be done here and not in Initialize() because the stacks get merged and fall out when their weight updates if this is set after PopulateContents() + atom_storage.allow_big_nesting = TRUE + atom_storage.max_slots = 99 + atom_storage.max_specific_storage = WEIGHT_CLASS_GIGANTIC + atom_storage.max_total_storage = 99 generate_items_inside(items_inside,src) /obj/item/storage/box/debugtools diff --git a/code/game/objects/items/storage/fancy.dm b/code/game/objects/items/storage/fancy.dm index 95eb304509f..1e6cd6432ca 100644 --- a/code/game/objects/items/storage/fancy.dm +++ b/code/game/objects/items/storage/fancy.dm @@ -240,13 +240,13 @@ . = ..() quick_remove_item(/obj/item/clothing/mask/cigarette, user) -/obj/item/storage/fancy/cigarettes/AltClick(mob/user) - . = ..() +/obj/item/storage/fancy/cigarettes/click_alt(mob/user) var/obj/item/lighter = locate(/obj/item/lighter) in contents if(lighter) quick_remove_item(lighter, user) else quick_remove_item(/obj/item/clothing/mask/cigarette, user) + return CLICK_ACTION_SUCCESS /// Removes an item from the packet if there is one /obj/item/storage/fancy/cigarettes/proc/quick_remove_item(obj/item/grabbies, mob/user) diff --git a/code/game/objects/items/storage/lockbox.dm b/code/game/objects/items/storage/lockbox.dm index 8e542ebcc44..3a0b15b7d28 100644 --- a/code/game/objects/items/storage/lockbox.dm +++ b/code/game/objects/items/storage/lockbox.dm @@ -121,13 +121,11 @@ if(!atom_storage.locked) . += span_notice("Alt-click to [open ? "close":"open"] it.") -/obj/item/storage/lockbox/medal/AltClick(mob/user) - if(!user.can_perform_action(src)) - return +/obj/item/storage/lockbox/medal/click_alt(mob/user) if(!atom_storage.locked) open = (open ? FALSE : TRUE) update_appearance() - ..() + return CLICK_ACTION_SUCCESS /obj/item/storage/lockbox/medal/PopulateContents() new /obj/item/clothing/accessory/medal/gold/captain(src) diff --git a/code/game/objects/items/tanks/tank_types.dm b/code/game/objects/items/tanks/tank_types.dm index 064a749521e..b21deb35581 100644 --- a/code/game/objects/items/tanks/tank_types.dm +++ b/code/game/objects/items/tanks/tank_types.dm @@ -7,12 +7,14 @@ * Emergency Oxygen * Generic */ +/obj/item/tank/internals + interaction_flags_click = FORBID_TELEKINESIS_REACH|NEED_HANDS + /// Allows carbon to toggle internals via AltClick of the equipped tank. -/obj/item/tank/internals/AltClick(mob/user) - ..() - if((loc == user) && user.can_perform_action(src, FORBID_TELEKINESIS_REACH|NEED_HANDS)) - toggle_internals(user) +/obj/item/tank/internals/click_alt(mob/user) + toggle_internals(user) + return CLICK_ACTION_SUCCESS /obj/item/tank/internals/examine(mob/user) . = ..() diff --git a/code/game/objects/items/tcg/tcg_machines.dm b/code/game/objects/items/tcg/tcg_machines.dm index 767592535f7..78854afdd5b 100644 --- a/code/game/objects/items/tcg/tcg_machines.dm +++ b/code/game/objects/items/tcg/tcg_machines.dm @@ -91,7 +91,7 @@ GLOBAL_LIST_EMPTY(tcgcard_machine_radial_choices) /obj/machinery/trading_card_holder/attack_hand_secondary(mob/user) if(isnull(current_summon)) var/card_name = tgui_input_text(user, "Insert card name", "Blank Card Naming", "blank card", MAX_NAME_LEN) - if(isnull(card_name)) + if(isnull(card_name) || !user.can_perform_action(src)) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN current_summon = new /obj/structure/trading_card_summon/blank(locate(x + summon_offset_x, y + summon_offset_y, z)) icon_state = "card_holder_active" diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm index 31efb0ef273..1bb2ac365df 100644 --- a/code/game/objects/items/tools/weldingtool.dm +++ b/code/game/objects/items/tools/weldingtool.dm @@ -134,7 +134,7 @@ LAZYREMOVE(update_overlays_on_z, sparks) target.cut_overlay(sparks) -/obj/item/weldingtool/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/weldingtool/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!ishuman(interacting_with)) return NONE if(user.combat_mode) @@ -163,10 +163,12 @@ if(!proximity) return - if(isOn()) + if(isOn() && ismovable(attacked_atom)) + use(1) + var/turf/location = get_turf(user) + location.hotspot_expose(700, 50, 1) . |= AFTERATTACK_PROCESSED_ITEM if (!QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist - handle_fuel_and_temps(1, user) var/mob/living/attacked_mob = attacked_atom if(attacked_mob.ignite_mob()) message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)]") @@ -180,21 +182,6 @@ return . -/obj/item/weldingtool/attack_qdeleted(atom/attacked_atom, mob/user, proximity) - . = ..() - if(!proximity) - return - - if(isOn()) - handle_fuel_and_temps(1, user) - - if(!QDELETED(attacked_atom) && isliving(attacked_atom)) // can't ignite something that doesn't exist - var/mob/living/attacked_mob = attacked_atom - if(attacked_mob.ignite_mob()) - message_admins("[ADMIN_LOOKUPFLW(user)] set [key_name_admin(attacked_mob)] on fire with [src] at [AREACOORD(user)].") - user.log_message("set [key_name(attacked_mob)] on fire with [src]", LOG_ATTACK) - - /obj/item/weldingtool/attack_self(mob/user) if(src.reagents.has_reagent(/datum/reagent/toxin/plasma)) message_admins("[ADMIN_LOOKUPFLW(user)] activated a rigged welder at [AREACOORD(user)].") @@ -204,11 +191,6 @@ update_appearance() -/obj/item/weldingtool/proc/handle_fuel_and_temps(used = 0, mob/living/user) - use(used) - var/turf/location = get_turf(user) - location.hotspot_expose(700, 50, 1) - /// Returns the amount of fuel in the welder /obj/item/weldingtool/proc/get_fuel() return reagents.get_reagent_amount(/datum/reagent/fuel) diff --git a/code/game/objects/items/toys.dm b/code/game/objects/items/toys.dm index 8ac1ee98994..6a5b6357a8f 100644 --- a/code/game/objects/items/toys.dm +++ b/code/game/objects/items/toys.dm @@ -1402,7 +1402,7 @@ //Add changing looks when i feel suicidal about making 20 inhands for these. /obj/item/toy/dummy/attack_self(mob/user) var/new_name = tgui_input_text(usr, "What would you like to name the dummy?", "Doll Name", doll_name, MAX_NAME_LEN) - if(!new_name) + if(!new_name || !user.is_holding(src)) return doll_name = new_name to_chat(user, span_notice("You name the dummy as \"[doll_name]\".")) diff --git a/code/game/objects/items_reskin.dm b/code/game/objects/items_reskin.dm new file mode 100644 index 00000000000..1a7c27e098d --- /dev/null +++ b/code/game/objects/items_reskin.dm @@ -0,0 +1,57 @@ +/// Called when alt clicked and the item has unique reskin options +/obj/item/proc/on_click_alt_reskin(datum/source, mob/user) + SIGNAL_HANDLER + + if(!user.can_perform_action(src, NEED_DEXTERITY)) + return NONE + + if(!(obj_flags & INFINITE_RESKIN) && current_skin) + return NONE + + INVOKE_ASYNC(src, PROC_REF(reskin_obj), user) + return CLICK_ACTION_SUCCESS + + +/** + * Reskins object based on a user's choice + * + * Arguments: + * * M The mob choosing a reskin option + */ +/obj/item/proc/reskin_obj(mob/user) + if(!LAZYLEN(unique_reskin)) + return + + var/list/items = list() + for(var/reskin_option in unique_reskin) + var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option]) + items += list("[reskin_option]" = item_image) + sort_list(items) + + var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, PROC_REF(check_reskin_menu), user), radius = 38, require_near = TRUE) + if(!pick) + return + if(!unique_reskin[pick]) + return + current_skin = pick + icon_state = unique_reskin[pick] + to_chat(user, "[src] is now skinned as '[pick].'") + SEND_SIGNAL(src, COMSIG_OBJ_RESKIN, user, pick) + + +/** + * Checks if we are allowed to interact with a radial menu for reskins + * + * Arguments: + * * user The mob interacting with the menu + */ +/obj/item/proc/check_reskin_menu(mob/user) + if(QDELETED(src)) + return FALSE + if(!(obj_flags & INFINITE_RESKIN) && current_skin) + return FALSE + if(!istype(user)) + return FALSE + if(user.incapacitated()) + return FALSE + return TRUE diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index e8797836bae..bfdd0f71426 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -27,9 +27,6 @@ /// A multiplier to an objecet's force when used against a stucture, vechicle, machine, or robot. var/demolition_mod = 1 - var/current_skin //Has the item been reskinned? - var/list/unique_reskin //List of options to reskin. - /// Custom fire overlay icon, will just use the default overlay if this is null var/custom_fire_overlay /// Particles this obj uses when burning, if any @@ -117,54 +114,6 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) else return null -/obj/proc/updateUsrDialog() - if(!(obj_flags & IN_USE)) - return - - var/is_in_use = FALSE - var/list/nearby = viewers(1, src) - for(var/mob/M in nearby) - if ((M.client && M.machine == src)) - is_in_use = TRUE - ui_interact(M) - if(issilicon(usr) || isAdminGhostAI(usr)) - if (!(usr in nearby)) - if (usr.client && usr.machine == src) // && M.machine == src is omitted because if we triggered this by using the dialog, it doesn't matter if our machine changed in between triggering it and this - the dialog is probably still supposed to refresh. - is_in_use = TRUE - ui_interact(usr) - - // check for TK users - - if(ishuman(usr)) - var/mob/living/carbon/human/H = usr - if(!(usr in nearby)) - if(usr.client && usr.machine == src) - if(H.dna.check_mutation(/datum/mutation/human/telekinesis)) - is_in_use = TRUE - ui_interact(usr) - if (is_in_use) - obj_flags |= IN_USE - else - obj_flags &= ~IN_USE - -/obj/proc/updateDialog(update_viewers = TRUE,update_ais = TRUE) - // Check that people are actually using the machine. If not, don't update anymore. - if(obj_flags & IN_USE) - var/is_in_use = FALSE - if(update_viewers) - for(var/mob/M in viewers(1, src)) - if ((M.client && M.machine == src)) - is_in_use = TRUE - src.interact(M) - var/ai_in_use = FALSE - if(update_ais) - ai_in_use = AutoUpdateAI(src) - - if(update_viewers && update_ais) //State change is sure only if we check both - if(!ai_in_use && !is_in_use) - obj_flags &= ~IN_USE - - /obj/attack_ghost(mob/user) . = ..() if(.) @@ -172,33 +121,6 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) SEND_SIGNAL(src, COMSIG_ATOM_UI_INTERACT, user) ui_interact(user) -/mob/proc/unset_machine() - SIGNAL_HANDLER - if(!machine) - return - UnregisterSignal(machine, COMSIG_QDELETING) - machine.on_unset_machine(src) - machine = null - -//called when the user unsets the machine. -/atom/movable/proc/on_unset_machine(mob/user) - return - -/mob/proc/set_machine(obj/O) - if(QDELETED(src) || QDELETED(O)) - return - if(machine) - unset_machine() - machine = O - RegisterSignal(O, COMSIG_QDELETING, PROC_REF(unset_machine)) - if(istype(O)) - O.obj_flags |= IN_USE - -/obj/item/proc/updateSelfDialog() - var/mob/M = src.loc - if(istype(M) && M.client && M.machine == src) - src.attack_self(M) - /obj/singularity_pull(S, current_size) ..() if(move_resist == INFINITY) @@ -209,9 +131,6 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) /obj/get_dumping_location() return get_turf(src) -/obj/proc/check_uplink_validity() - return 1 - /obj/vv_get_dropdown() . = ..() VV_DROPDOWN_OPTION("", "---") @@ -270,56 +189,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) . += span_notice(desc_controls) if(obj_flags & UNIQUE_RENAME) . += span_notice("Use a pen on it to rename it or change its description.") - if(unique_reskin && (!current_skin || (obj_flags & INFINITE_RESKIN))) - . += span_notice("Alt-click it to reskin it.") -/obj/AltClick(mob/user) - . = ..() - if(unique_reskin && (!current_skin || (obj_flags & INFINITE_RESKIN)) && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - -/** - * Reskins object based on a user's choice - * - * Arguments: - * * M The mob choosing a reskin option - */ -/obj/proc/reskin_obj(mob/user) - if(!LAZYLEN(unique_reskin)) - return - - var/list/items = list() - for(var/reskin_option in unique_reskin) - var/image/item_image = image(icon = src.icon, icon_state = unique_reskin[reskin_option]) - items += list("[reskin_option]" = item_image) - sort_list(items) - - var/pick = show_radial_menu(user, src, items, custom_check = CALLBACK(src, PROC_REF(check_reskin_menu), user), radius = 38, require_near = TRUE) - if(!pick) - return - if(!unique_reskin[pick]) - return - current_skin = pick - icon_state = unique_reskin[pick] - to_chat(user, "[src] is now skinned as '[pick].'") - SEND_SIGNAL(src, COMSIG_OBJ_RESKIN, user, pick) - -/** - * Checks if we are allowed to interact with a radial menu for reskins - * - * Arguments: - * * user The mob interacting with the menu - */ -/obj/proc/check_reskin_menu(mob/user) - if(QDELETED(src)) - return FALSE - if(!(obj_flags & INFINITE_RESKIN) && current_skin) - return FALSE - if(!istype(user)) - return FALSE - if(user.incapacitated()) - return FALSE - return TRUE /obj/analyzer_act(mob/living/user, obj/item/analyzer/tool) if(atmos_scan(user=user, target=src, silent=FALSE)) @@ -331,6 +201,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) // Should move all contained objects to it's location. /obj/proc/dump_contents() + SHOULD_CALL_PARENT(FALSE) CRASH("Unimplemented.") /obj/handle_ricochet(obj/projectile/P) diff --git a/code/game/objects/structures/beds_chairs/alien_nest.dm b/code/game/objects/structures/beds_chairs/alien_nest.dm index 1654cecff45..77759c9e309 100644 --- a/code/game/objects/structures/beds_chairs/alien_nest.dm +++ b/code/game/objects/structures/beds_chairs/alien_nest.dm @@ -24,37 +24,39 @@ /obj/structure/bed/nest/wrench_act_secondary(mob/living/user, obj/item/weapon) return ITEM_INTERACT_BLOCKING -/obj/structure/bed/nest/user_unbuckle_mob(mob/living/buckled_mob, mob/living/user) - if(has_buckled_mobs()) - for(var/buck in buckled_mobs) //breaking a nest releases all the buckled mobs, because the nest isn't holding them down anymore - var/mob/living/M = buck - if(user.get_organ_by_type(/obj/item/organ/internal/alien/plasmavessel)) - unbuckle_mob(M) - add_fingerprint(user) - return +/obj/structure/bed/nest/user_unbuckle_mob(mob/living/captive, mob/living/hero) + if(!length(buckled_mobs)) + return - if(M != user) - M.visible_message(span_notice("[user.name] pulls [M.name] free from the sticky nest!"),\ - span_notice("[user.name] pulls you free from the gelatinous resin."),\ - span_hear("You hear squelching...")) - else - M.visible_message(span_warning("[M.name] struggles to break free from the gelatinous resin!"),\ - span_notice("You struggle to break free from the gelatinous resin... (Stay still for about a minute and a half.)"),\ - span_hear("You hear squelching...")) - if(!do_after(M, 100 SECONDS, target = src)) - if(M?.buckled) - to_chat(M, span_warning("You fail to unbuckle yourself!")) - return - if(!M.buckled) - return - M.visible_message(span_warning("[M.name] breaks free from the gelatinous resin!"),\ - span_notice("You break free from the gelatinous resin!"),\ - span_hear("You hear squelching...")) + if(hero.get_organ_by_type(/obj/item/organ/internal/alien/plasmavessel)) + unbuckle_mob(captive) + add_fingerprint(hero) + return + if(captive != hero) + captive.visible_message(span_notice("[hero.name] pulls [captive.name] free from the sticky nest!"), + span_notice("[hero.name] pulls you free from the gelatinous resin."), + span_hear("You hear squelching...")) + unbuckle_mob(captive) + add_fingerprint(hero) + return + + captive.visible_message(span_warning("[captive.name] struggles to break free from the gelatinous resin!"), + span_notice("You struggle to break free from the gelatinous resin... (Stay still for about a minute and a half.)"), + span_hear("You hear squelching...")) - unbuckle_mob(M) - add_fingerprint(user) + if(!do_after(captive, 100 SECONDS, target = src, hidden = TRUE)) + if(captive.buckled) + to_chat(captive, span_warning("You fail to unbuckle yourself!")) + return + + captive.visible_message(span_warning("[captive.name] breaks free from the gelatinous resin!"), + span_notice("You break free from the gelatinous resin!"), + span_hear("You hear squelching...")) + + unbuckle_mob(captive) + add_fingerprint(hero) /obj/structure/bed/nest/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) if ( !ismob(M) || (get_dist(src, user) > 1) || (M.loc != src.loc) || user.incapacitated() || M.buckled ) diff --git a/code/game/objects/structures/beds_chairs/bed.dm b/code/game/objects/structures/beds_chairs/bed.dm index 5c5fac60eaa..0e3845d2efa 100644 --- a/code/game/objects/structures/beds_chairs/bed.dm +++ b/code/game/objects/structures/beds_chairs/bed.dm @@ -110,17 +110,14 @@ if(!isnull(foldable_type)) . += span_notice("You can fold it up with a Right-click.") -/obj/structure/bed/medical/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - +/obj/structure/bed/medical/click_alt(mob/user) if(has_buckled_mobs() && (user in buckled_mobs)) - return + return CLICK_ACTION_BLOCKING anchored = !anchored balloon_alert(user, "brakes [anchored ? "applied" : "released"]") update_appearance() + return CLICK_ACTION_SUCCESS /obj/structure/bed/medical/post_buckle_mob(mob/living/buckled) . = ..() diff --git a/code/game/objects/structures/beds_chairs/chair.dm b/code/game/objects/structures/beds_chairs/chair.dm index 92c955977e7..6e218c56b82 100644 --- a/code/game/objects/structures/beds_chairs/chair.dm +++ b/code/game/objects/structures/beds_chairs/chair.dm @@ -57,8 +57,6 @@ return . = ..() -/obj/structure/chair/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation ///allows each chair to request the electrified_buckle component with overlays that dont look ridiculous /obj/structure/chair/proc/electrify_self(obj/item/assembly/shock_kit/input_shock_kit, mob/user, list/overlays_from_child_procs) @@ -447,6 +445,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) buildstacktype = /obj/item/stack/sheet/bronze buildstackamount = 1 item_chair = null + interaction_flags_click = NEED_DEXTERITY + /// Total rotations made var/turns = 0 /obj/structure/chair/bronze/Initialize(mapload) @@ -464,10 +464,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) if(turns >= 8) STOP_PROCESSING(SSfastprocess, src) -/obj/structure/chair/bronze/AltClick(mob/user) +/obj/structure/chair/bronze/click_alt(mob/user) turns = 0 - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return if(!(datum_flags & DF_ISPROCESSING)) user.visible_message(span_notice("[user] spins [src] around, and the last vestiges of Ratvarian technology keeps it spinning FOREVER."), \ span_notice("Automated spinny chairs. The pinnacle of ancient Ratvarian technology.")) @@ -476,6 +474,7 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/chair/stool/bar, 0) user.visible_message(span_notice("[user] stops [src]'s uncontrollable spinning."), \ span_notice("You grab [src] and stop its wild spinning.")) STOP_PROCESSING(SSfastprocess, src) + return CLICK_ACTION_SUCCESS /obj/structure/chair/mime name = "invisible chair" diff --git a/code/game/objects/structures/bedsheet_bin.dm b/code/game/objects/structures/bedsheet_bin.dm index 108ca3fe9b8..8b4fef65293 100644 --- a/code/game/objects/structures/bedsheet_bin.dm +++ b/code/game/objects/structures/bedsheet_bin.dm @@ -20,6 +20,7 @@ LINEN BINS w_class = WEIGHT_CLASS_TINY resistance_flags = FLAMMABLE dying_key = DYE_REGISTRY_BEDSHEET + interaction_flags_click = NEED_DEXTERITY dog_fashion = /datum/dog_fashion/head/ghost /// Custom nouns to act as the subject of dreams @@ -133,11 +134,9 @@ LINEN BINS else return ..() -/obj/item/bedsheet/AltClick(mob/living/user) - // double check the canUseTopic args to make sure it's correct - if(!istype(user) || !user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/bedsheet/click_alt(mob/living/user) dir = REVERSE_DIR(dir) + return CLICK_ACTION_SUCCESS /obj/item/bedsheet/blue icon_state = "sheetblue" diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm index 4a201cba5b8..acbd7bfa395 100644 --- a/code/game/objects/structures/crates_lockers/closets.dm +++ b/code/game/objects/structures/crates_lockers/closets.dm @@ -580,7 +580,11 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) else return open(user) -/obj/structure/closet/atom_deconstruct(disassembled = TRUE) +/obj/structure/closet/handle_deconstruct(disassembled) + dump_contents() + if(obj_flags & NO_DEBRIS_AFTER_DECONSTRUCTION) + return + if(ispath(material_drop) && material_drop_amount) new material_drop(loc, material_drop_amount) if (secure) @@ -592,7 +596,6 @@ GLOBAL_LIST_EMPTY(roundstart_station_closets) electronics.accesses = req_access if(card_reader_installed) new /obj/item/stock_parts/card_reader(drop_location()) - dump_contents() /obj/structure/closet/atom_break(damage_flag) . = ..() diff --git a/code/game/objects/structures/divine.dm b/code/game/objects/structures/divine.dm index ef9f650e42a..6114a20158c 100644 --- a/code/game/objects/structures/divine.dm +++ b/code/game/objects/structures/divine.dm @@ -7,19 +7,17 @@ density = FALSE can_buckle = 1 -/obj/structure/sacrificealtar/AltClick(mob/living/user) - ..() - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/structure/sacrificealtar/click_alt(mob/living/user) if(!has_buckled_mobs()) - return + return CLICK_ACTION_BLOCKING var/mob/living/L = locate() in buckled_mobs if(!L) - return + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("Invoking the sacred ritual, you sacrifice [L].")) L.investigate_log("has been sacrificially gibbed on an altar.", INVESTIGATE_DEATHS) L.gib(DROP_ALL_REMAINS) message_admins("[ADMIN_LOOKUPFLW(user)] has sacrificed [key_name_admin(L)] on the sacrificial altar at [AREACOORD(src)].") + return CLICK_ACTION_SUCCESS /obj/structure/healingfountain name = "healing fountain" diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index e9f9eb5d11e..cace64a7101 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -91,9 +91,9 @@ qdel(src) return T -/obj/structure/falsewall/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/structure/falsewall/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!opening || !tool.tool_behaviour) - return ..() + return NONE to_chat(user, span_warning("You must wait until the door has stopped moving!")) return ITEM_INTERACT_BLOCKING diff --git a/code/game/objects/structures/kitchen_spike.dm b/code/game/objects/structures/kitchen_spike.dm index 039735dc36f..7f3e4195810 100644 --- a/code/game/objects/structures/kitchen_spike.dm +++ b/code/game/objects/structures/kitchen_spike.dm @@ -143,7 +143,7 @@ span_notice("You struggle to break free from [src], exacerbating your wounds! (Stay still for two minutes.)"),\ span_hear("You hear a wet squishing noise..")) buckled_mob.adjustBruteLoss(30) - if(!do_after(buckled_mob, 2 MINUTES, target = src)) + if(!do_after(buckled_mob, 2 MINUTES, target = src, hidden = TRUE)) if(buckled_mob?.buckled) to_chat(buckled_mob, span_warning("You fail to free yourself!")) return diff --git a/code/game/objects/structures/lavaland/geyser.dm b/code/game/objects/structures/lavaland/geyser.dm index ed6602cb513..7ef40423e0d 100644 --- a/code/game/objects/structures/lavaland/geyser.dm +++ b/code/game/objects/structures/lavaland/geyser.dm @@ -167,14 +167,12 @@ playsound(src, 'sound/machines/click.ogg', 10, TRUE) -/obj/item/plunger/AltClick(mob/user) - if(!istype(user) || !user.can_perform_action(src)) - return - +/obj/item/plunger/click_alt(mob/user) var/new_layer = tgui_input_list(user, "Select a layer", "Layer", GLOB.plumbing_layers) - if(isnull(new_layer)) - return + if(isnull(new_layer) || !user.can_perform_action(src)) + return CLICK_ACTION_BLOCKING target_layer = GLOB.plumbing_layers[new_layer] + return CLICK_ACTION_SUCCESS ///A faster reinforced plunger /obj/item/plunger/reinforced diff --git a/code/game/objects/structures/lavaland/gulag_vent.dm b/code/game/objects/structures/lavaland/gulag_vent.dm new file mode 100644 index 00000000000..aa13a1621d8 --- /dev/null +++ b/code/game/objects/structures/lavaland/gulag_vent.dm @@ -0,0 +1,42 @@ +/** + * A boulder-spawning structure superficially similar to an ore vent which doesnt share any of its behaviour + * Prisoners can haul boulders up out of it in case the actual mining area is totally spent + */ +/obj/structure/gulag_vent + name = "work pit" + desc = "A timeworn shaft, almost totally mined out. With a bit of effort you might be able to haul something up." + icon = 'icons/obj/mining_zones/terrain.dmi' + icon_state = "ore_vent_active" + move_resist = MOVE_FORCE_EXTREMELY_STRONG + resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF //This thing will take a beating. + interaction_flags_atom = INTERACT_ATOM_ATTACK_HAND | INTERACT_ATOM_REQUIRES_DEXTERITY + anchored = TRUE + density = TRUE + /// What kind of rock we got in there? + var/spawned_boulder = /obj/item/boulder/gulag + /// Prevents multiple people from hauling at a time + var/occupied = FALSE + +/obj/structure/gulag_vent/ice + icon_state = "ore_vent_ice_active" + +/obj/structure/gulag_vent/interact(mob/user) + . = ..() + if (!isliving(user)) + return + if (occupied) + balloon_alert(user, "occupied!") + return + var/mob/living/living_user = user + occupied = TRUE + living_user.balloon_alert_to_viewers("hauling...") + var/succeeded = do_after(living_user, 8 SECONDS, src) + occupied = FALSE + if (!succeeded) + return + living_user.mind?.adjust_experience(/datum/skill/fitness, 10) + living_user.apply_status_effect(/datum/status_effect/exercised) + new spawned_boulder(get_turf(living_user)) + living_user.visible_message(span_notice("[living_user] hauls a boulder out of [src].")) + living_user.apply_damage(120, STAMINA) + playsound(src, 'sound/weapons/genhit.ogg', vol = 50, vary = TRUE) diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index a1deed332ab..59247b768a0 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -170,6 +170,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an icon_state = "morgue1" base_icon_state = "morgue" dir = EAST + interaction_flags_click = ALLOW_SILICON_REACH|ALLOW_RESTING connected = /obj/structure/tray/m_tray @@ -298,12 +299,10 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an . = ..() . += span_notice("The speaker is [beeper ? "enabled" : "disabled"]. Alt-click to toggle it.") -/obj/structure/bodycontainer/morgue/AltClick(mob/user) - ..() - if(!user.can_perform_action(src, (ALLOW_SILICON_REACH|ALLOW_RESTING))) - return +/obj/structure/bodycontainer/morgue/click_alt(mob/user) beeper = !beeper to_chat(user, span_notice("You turn the speaker function [beeper ? "on" : "off"].")) + return CLICK_ACTION_SUCCESS /obj/structure/bodycontainer/morgue/emag_act(mob/user, obj/item/card/emag/emag_card) if(obj_flags & EMAGGED) diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm index 95cf4fc58e5..2651684e39b 100644 --- a/code/game/objects/structures/railings.dm +++ b/code/game/objects/structures/railings.dm @@ -88,8 +88,6 @@ to_chat(user, span_warning("[src] is already in good condition!")) return -/obj/structure/railing/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/structure/railing/wirecutter_act(mob/living/user, obj/item/I) . = ..() diff --git a/code/game/objects/structures/reflector.dm b/code/game/objects/structures/reflector.dm index 0d6799b0583..0700f19818a 100644 --- a/code/game/objects/structures/reflector.dm +++ b/code/game/objects/structures/reflector.dm @@ -79,10 +79,10 @@ P.decayedRange = max(P.decayedRange--, 0) return BULLET_ACT_FORCE_PIERCE -/obj/structure/reflector/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/structure/reflector/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(admin && tool.tool_behaviour) return ITEM_INTERACT_BLOCKING - return ..() + return NONE /obj/structure/reflector/screwdriver_act(mob/living/user, obj/item/tool) can_rotate = !can_rotate diff --git a/code/game/objects/structures/shower.dm b/code/game/objects/structures/shower.dm index 298c6fd2294..c37be03652c 100644 --- a/code/game/objects/structures/shower.dm +++ b/code/game/objects/structures/shower.dm @@ -373,8 +373,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/shower, (-16)) deconstruct() return TRUE -/obj/structure/showerframe/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/effect/mist name = "mist" diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm index f1c4516316d..29c93fcda25 100644 --- a/code/game/objects/structures/tables_racks.dm +++ b/code/game/objects/structures/tables_racks.dm @@ -862,12 +862,12 @@ deconstruct(TRUE) return ITEM_INTERACT_SUCCESS -/obj/structure/rack/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(. || (tool.item_flags & ABSTRACT) || user.combat_mode) - return . +/obj/structure/rack/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + if((tool.item_flags & ABSTRACT) || user.combat_mode) + return NONE if(user.transferItemToLoc(tool, drop_location(), silent = FALSE)) return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING /obj/structure/rack/attack_paw(mob/living/user, list/modifiers) attack_hand(user, modifiers) diff --git a/code/game/objects/structures/training_machine.dm b/code/game/objects/structures/training_machine.dm index fd0d8bb0c8f..c2f4d3a18ae 100644 --- a/code/game/objects/structures/training_machine.dm +++ b/code/game/objects/structures/training_machine.dm @@ -20,6 +20,7 @@ can_buckle = TRUE buckle_lying = 0 max_integrity = 200 + interaction_flags_click = NEED_DEXTERITY|FORBID_TELEKINESIS_REACH|ALLOW_RESTING ///Is the machine moving? Setting this to FALSE will automatically call stop_moving() var/moving = FALSE ///The distance the machine is allowed to roam from its starting point @@ -174,21 +175,19 @@ attached_item.throw_at(destination, 4, 1) on_attached_delete() -/obj/structure/training_machine/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH|ALLOW_RESTING)) - return +/obj/structure/training_machine/click_alt(mob/user) if(has_buckled_mobs()) user_unbuckle_mob(buckled_mobs[1], user) - return + return CLICK_ACTION_SUCCESS if (!attached_item) - return + return NONE if (obj_flags & EMAGGED) to_chat(user, span_warning("The toolbox is somehow stuck on! It won't budge!")) - return + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("You remove \the [attached_item] from the training device.")) remove_attached_item(user) playsound(src, SFX_RUSTLE, 50, TRUE) + return CLICK_ACTION_SUCCESS /** * Toggle the machine's movement @@ -396,12 +395,10 @@ if (!.) check_hit(hit_atom) -/obj/item/training_toolbox/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return +/obj/item/training_toolbox/click_alt(mob/user) to_chat(user, span_notice("You push the 'Lap' button on the toolbox's display.")) lap_hits = initial(lap_hits) + return CLICK_ACTION_SUCCESS /obj/item/training_toolbox/examine(mob/user) . = ..() diff --git a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm index 44952801ec7..e6b0f30225c 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm @@ -51,8 +51,6 @@ qdel(src) return TRUE -/obj/structure/c_transit_tube/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation // transit tube station /obj/structure/c_transit_tube/station diff --git a/code/game/objects/structures/windoor_assembly.dm b/code/game/objects/structures/windoor_assembly.dm index 0a56c6add69..3437e2a5ae0 100644 --- a/code/game/objects/structures/windoor_assembly.dm +++ b/code/game/objects/structures/windoor_assembly.dm @@ -327,8 +327,6 @@ qdel(src) -/obj/structure/windoor_assembly/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation //Flips the windoor assembly, determines whather the door opens to the left or the right /obj/structure/windoor_assembly/verb/flip() diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index 2acac966f23..818e3c43499 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -188,11 +188,11 @@ return return ..() -/obj/structure/window/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/structure/window/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!can_be_reached(user)) return ITEM_INTERACT_SKIP_TO_ATTACK // Guess you get to hit it add_fingerprint(user) - return ..() + return NONE /obj/structure/window/welder_act(mob/living/user, obj/item/tool) if(atom_integrity >= max_integrity) @@ -277,8 +277,6 @@ add_fingerprint(user) return ..() -/obj/structure/window/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/structure/window/set_anchored(anchorvalue) ..() diff --git a/code/game/turfs/change_turf.dm b/code/game/turfs/change_turf.dm index 1253d156a2c..60462cafbd1 100644 --- a/code/game/turfs/change_turf.dm +++ b/code/game/turfs/change_turf.dm @@ -96,6 +96,8 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( var/list/old_baseturfs = baseturfs var/old_type = type + var/datum/weakref/old_ref = weak_reference + weak_reference = null var/list/post_change_callbacks = list() SEND_SIGNAL(src, COMSIG_TURF_CHANGE, path, new_baseturfs, flags, post_change_callbacks) @@ -142,6 +144,8 @@ GLOBAL_LIST_INIT(blacklisted_automated_baseturfs, typecacheof(list( lattice_underneath = old_lattice_underneath + new_turf.weak_reference = old_ref + if(SSlighting.initialized) // Space tiles should never have lighting objects if(!space_lit) diff --git a/code/game/turfs/open/floor/plating.dm b/code/game/turfs/open/floor/plating.dm index 974042ce7c8..5bcd8a6a4a8 100644 --- a/code/game/turfs/open/floor/plating.dm +++ b/code/game/turfs/open/floor/plating.dm @@ -177,9 +177,8 @@ ScrapeAway(flags = CHANGETURF_INHERIT_AIR) return TRUE -/turf/open/floor/plating/foam/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - SHOULD_CALL_PARENT(FALSE) - return NONE // Fuck you +/turf/open/floor/plating/foam/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return user.combat_mode ? ITEM_INTERACT_SKIP_TO_ATTACK : ITEM_INTERACT_BLOCKING // Fuck you //reinforced plating deconstruction states #define PLATE_INTACT 0 diff --git a/code/modules/admin/sound_emitter.dm b/code/modules/admin/sound_emitter.dm index 4786e0ad7c1..d697537c6df 100644 --- a/code/modules/admin/sound_emitter.dm +++ b/code/modules/admin/sound_emitter.dm @@ -51,10 +51,13 @@ return edit_emitter(user) -/obj/effect/sound_emitter/AltClick(mob/user) - if(check_rights_for(user.client, R_SOUND)) - activate(user) - to_chat(user, span_notice("Sound emitter activated."), confidential = TRUE) +/obj/effect/sound_emitter/click_alt(mob/user) + if(!check_rights_for(user.client, R_SOUND)) + return CLICK_ACTION_BLOCKING + + activate(user) + to_chat(user, span_notice("Sound emitter activated."), confidential = TRUE) + return CLICK_ACTION_SUCCESS /obj/effect/sound_emitter/proc/edit_emitter(mob/user) var/dat = "" diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 07e8dad2aa6..2fb5d526045 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -48,7 +48,7 @@ icon_state = "gizmo_scan" to_chat(user, span_notice("You switch the device to [mode == GIZMO_SCAN? "SCAN": "MARK"] MODE")) -/obj/item/abductor/gizmo/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/abductor/gizmo/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!ScientistCheck(user)) return ITEM_INTERACT_SKIP_TO_ATTACK // So you slap them with it if(!console) @@ -110,7 +110,7 @@ icon_state = "silencer" inhand_icon_state = "gizmo" -/obj/item/abductor/silencer/interact_with_atom(atom/interacting_with, mob/living/user) +/obj/item/abductor/silencer/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers) if(!AbductorCheck(user)) return ITEM_INTERACT_SKIP_TO_ATTACK // So you slap them with it @@ -285,8 +285,8 @@
Congratulations! You are now trained for invasive xenobiology research!"} -/obj/item/paper/guides/antag/abductor/AltClick() - return //otherwise it would fold into a paperplane. +/obj/item/paper/guides/antag/abductor/click_alt() + return CLICK_ACTION_BLOCKING //otherwise it would fold into a paperplane. /obj/item/melee/baton/abductor name = "advanced baton" diff --git a/code/modules/antagonists/cult/rune_spawn_action.dm b/code/modules/antagonists/cult/rune_spawn_action.dm index af4350427b5..3d791dbce44 100644 --- a/code/modules/antagonists/cult/rune_spawn_action.dm +++ b/code/modules/antagonists/cult/rune_spawn_action.dm @@ -42,7 +42,7 @@ var/chosen_keyword if(initial(rune_type.req_keyword)) chosen_keyword = tgui_input_text(owner, "Enter a keyword for the new rune.", "Words of Power", max_length = MAX_NAME_LEN) - if(!chosen_keyword) + if(!chosen_keyword || !turf_check(T)) return //the outer ring is always the same across all runes var/obj/effect/temp_visual/cult/rune_spawn/R1 = new(T, scribe_time, rune_color) diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm index aa979ff7814..cc4a80d980f 100644 --- a/code/modules/antagonists/cult/runes.dm +++ b/code/modules/antagonists/cult/runes.dm @@ -491,7 +491,7 @@ structure_check() searches for nearby cultist structures required for the invoca fail_invoke() return var/obj/effect/rune/teleport/actual_selected_rune = potential_runes[input_rune_key] //what rune does that key correspond to? - if(!Adjacent(user) || !src || QDELETED(src) || user.incapacitated() || !actual_selected_rune) + if(!Adjacent(user) || QDELETED(src) || user.incapacitated() || !actual_selected_rune) fail_invoke() return diff --git a/code/modules/art/paintings.dm b/code/modules/art/paintings.dm index a4758eb9e11..f050528bd3b 100644 --- a/code/modules/art/paintings.dm +++ b/code/modules/art/paintings.dm @@ -569,10 +569,12 @@ current_canvas = null update_appearance() -/obj/structure/sign/painting/AltClick(mob/user) - . = ..() - if(current_canvas?.can_select_frame(user)) - INVOKE_ASYNC(current_canvas, TYPE_PROC_REF(/obj/item/canvas, select_new_frame), user) +/obj/structure/sign/painting/click_alt(mob/user) + if(!current_canvas?.can_select_frame(user)) + return CLICK_ACTION_BLOCKING + + INVOKE_ASYNC(current_canvas, TYPE_PROC_REF(/obj/item/canvas, select_new_frame), user) + return CLICK_ACTION_SUCCESS /obj/structure/sign/painting/proc/frame_canvas(mob/user, obj/item/canvas/new_canvas) if(!(new_canvas.type in accepted_canvas_types)) diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm index 428bb5d8e06..8ed46a5bf81 100644 --- a/code/modules/art/statues.dm +++ b/code/modules/art/statues.dm @@ -44,8 +44,6 @@ return return ..() -/obj/structure/statue/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/structure/statue/atom_deconstruct(disassembled = TRUE) var/amount_mod = disassembled ? 0 : -2 diff --git a/code/modules/assembly/health.dm b/code/modules/assembly/health.dm index 23c329894e7..28ab16f63d7 100644 --- a/code/modules/assembly/health.dm +++ b/code/modules/assembly/health.dm @@ -37,16 +37,14 @@ update_appearance() return secured -/obj/item/assembly/health/AltClick(mob/living/user) - if(!can_interact(user)) - return - +/obj/item/assembly/health/click_alt(mob/living/user) if(alarm_health == HEALTH_THRESHOLD_CRIT) alarm_health = HEALTH_THRESHOLD_DEAD to_chat(user, span_notice("You toggle [src] to \"detect death\" mode.")) else alarm_health = HEALTH_THRESHOLD_CRIT to_chat(user, span_notice("You toggle [src] to \"detect critical state\" mode.")) + return CLICK_ACTION_SUCCESS /obj/item/assembly/health/process() //not ready yet diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm index 70f116b3e3e..5f50618e77d 100644 --- a/code/modules/assembly/holder.dm +++ b/code/modules/assembly/holder.dm @@ -141,8 +141,6 @@ return ..() -/obj/item/assembly_holder/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/item/assembly_holder/screwdriver_act(mob/user, obj/item/tool) if(..()) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index b5c847a78ab..8dd4573fcfd 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -35,8 +35,6 @@ buffer_turf = null return ..() -/obj/item/assembly/infra/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/item/assembly/infra/examine(mob/user) . = ..() diff --git a/code/modules/asset_cache/assets/icon_ref_map.dm b/code/modules/asset_cache/assets/icon_ref_map.dm new file mode 100644 index 00000000000..2f7f8463099 --- /dev/null +++ b/code/modules/asset_cache/assets/icon_ref_map.dm @@ -0,0 +1,28 @@ +/// Maps icon names to ref values +/datum/asset/json/icon_ref_map + name = "icon_ref_map" + early = TRUE + +/datum/asset/json/icon_ref_map/generate() + var/list/data = list() //"icons/obj/drinks.dmi" => "[0xc000020]" + + //var/start = "0xc000000" + var/value = 0 + + while(TRUE) + value += 1 + var/ref = "\[0xc[num2text(value,6,16)]\]" + var/mystery_meat = locate(ref) + + if(isicon(mystery_meat)) + if(!isfile(mystery_meat)) // Ignore the runtime icons for now + continue + var/path = get_icon_dmi_path(mystery_meat) //Try to get the icon path + if(path) + data[path] = ref + else if(mystery_meat) + continue; //Some other non-icon resource, ogg/json/whatever + else //Out of resources end this, could also try to end this earlier as soon as runtime generated icons appear but eh + break; + + return data diff --git a/code/modules/asset_cache/transports/asset_transport.dm b/code/modules/asset_cache/transports/asset_transport.dm index 19e40fb4884..3dbcc301843 100644 --- a/code/modules/asset_cache/transports/asset_transport.dm +++ b/code/modules/asset_cache/transports/asset_transport.dm @@ -82,11 +82,15 @@ /// asset_list - A list of asset filenames to be sent to the client. Can optionally be assoicated with the asset's asset_cache_item datum. /// Returns TRUE if any assets were sent. /datum/asset_transport/proc/send_assets(client/client, list/asset_list) +#if defined(UNIT_TESTS) + return +#endif + if (!istype(client)) if (ismob(client)) - var/mob/M = client - if (M.client) - client = M.client + var/mob/our_mob = client + if (our_mob.client) + client = our_mob.client else //no stacktrace because this will mainly happen because the client went away return else diff --git a/code/modules/atmospherics/machinery/atmosmachinery.dm b/code/modules/atmospherics/machinery/atmosmachinery.dm index d8d0ed3b47e..d152cf09e71 100644 --- a/code/modules/atmospherics/machinery/atmosmachinery.dm +++ b/code/modules/atmospherics/machinery/atmosmachinery.dm @@ -607,11 +607,6 @@ animate(our_client, pixel_x = 0, pixel_y = 0, time = 0.05 SECONDS) our_client.move_delay = world.time + 0.05 SECONDS -/obj/machinery/atmospherics/AltClick(mob/living/L) - if(vent_movement & VENTCRAWL_ALLOWED && istype(L)) - L.handle_ventcrawl(src) - return - return ..() /** * Getter of a list of pipenets diff --git a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm index c06863ba092..fe6f9423b43 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/passive_gate.dm @@ -38,13 +38,15 @@ Passive gate is similar to the regular pump except: update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/passive_gate/AltClick(mob/user) - if(can_interact(user)) - target_pressure = MAX_OUTPUT_PRESSURE - investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "pressure output set to [target_pressure] kPa") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/passive_gate/click_alt(mob/user) + if(target_pressure == MAX_OUTPUT_PRESSURE) + return CLICK_ACTION_BLOCKING + + target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "pressure output set to [target_pressure] kPa") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/passive_gate/update_icon_nopipes() cut_overlays() diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm index 93192073275..c3313322135 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pressure_valve.dm @@ -30,13 +30,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/pressure_valve/AltClick(mob/user) - if(can_interact(user)) - target_pressure = MAX_OUTPUT_PRESSURE - investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "target pressure set to [target_pressure] kPa") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/pressure_valve/click_alt(mob/user) + if(target_pressure == MAX_OUTPUT_PRESSURE) + return CLICK_ACTION_BLOCKING + + target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "target pressure set to [target_pressure] kPa") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/pressure_valve/update_icon_nopipes() if(on && is_operational && is_gas_flowing) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm index 3c1ba634cae..035f3a0f996 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/pump.dm @@ -43,13 +43,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/pump/AltClick(mob/user) - if(can_interact(user)) - target_pressure = MAX_OUTPUT_PRESSURE - investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "pressure output set to [target_pressure] kPa") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/pump/click_alt(mob/user) + if(target_pressure == MAX_OUTPUT_PRESSURE) + return CLICK_ACTION_BLOCKING + + target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "pressure output set to [target_pressure] kPa") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/pump/update_icon_nopipes() icon_state = (on && is_operational) ? "pump_on-[set_overlay_offset(piping_layer)]" : "pump_off-[set_overlay_offset(piping_layer)]" diff --git a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm index bbe788bac53..d1202dbec94 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/temperature_gate.dm @@ -35,13 +35,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/temperature_gate/AltClick(mob/user) - if(can_interact(user)) - target_temperature = max_temperature - investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "target temperature set to [target_temperature] K") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/temperature_gate/click_alt(mob/user) + if(target_temperature == max_temperature) + return CLICK_ACTION_BLOCKING + + target_temperature = max_temperature + investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "target temperature set to [target_temperature] K") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/temperature_gate/examine(mob/user) diff --git a/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm index 0fdb5aca6a3..2615b964ed8 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/temperature_pump.dm @@ -30,13 +30,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/temperature_pump/AltClick(mob/user) - if(can_interact(user) && !(heat_transfer_rate == max_heat_transfer_rate)) - heat_transfer_rate = max_heat_transfer_rate - investigate_log("was set to [heat_transfer_rate]% by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "transfer rate set to [heat_transfer_rate]%") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/temperature_pump/click_alt(mob/user) + if(heat_transfer_rate == max_heat_transfer_rate) + return CLICK_ACTION_BLOCKING + + heat_transfer_rate = max_heat_transfer_rate + investigate_log("was set to [heat_transfer_rate]% by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "transfer rate set to [heat_transfer_rate]%") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/temperature_pump/update_icon_nopipes() icon_state = "tpump_[on && is_operational ? "on" : "off"]-[set_overlay_offset(piping_layer)]" diff --git a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm index 139cbe71b36..41dc549b858 100644 --- a/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm +++ b/code/modules/atmospherics/machinery/components/binary_devices/volume_pump.dm @@ -41,13 +41,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/binary/volume_pump/AltClick(mob/user) - if(can_interact(user)) - transfer_rate = MAX_TRANSFER_RATE - investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "volume output set to [transfer_rate] L/s") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/binary/volume_pump/click_alt(mob/user) + if(transfer_rate == MAX_TRANSFER_RATE) + return CLICK_ACTION_BLOCKING + + transfer_rate = MAX_TRANSFER_RATE + investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "volume output set to [transfer_rate] L/s") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/binary/volume_pump/update_icon_nopipes() icon_state = on && is_operational ? "volpump_on-[set_overlay_offset(piping_layer)]" : "volpump_off-[set_overlay_offset(piping_layer)]" diff --git a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm index 0ab8427c0ae..56ee3c6039d 100644 --- a/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm +++ b/code/modules/atmospherics/machinery/components/electrolyzer/electrolyzer.dm @@ -187,14 +187,12 @@ return return ..() -/obj/machinery/electrolyzer/AltClick(mob/user) - . = ..() +/obj/machinery/electrolyzer/click_alt(mob/user) if(panel_open) balloon_alert(user, "close panel!") - return - if(!can_interact(user)) - return + return CLICK_ACTION_BLOCKING toggle_power(user) + return CLICK_ACTION_SUCCESS /obj/machinery/electrolyzer/proc/toggle_power(mob/user) if(!anchored && !cell) diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm index 1727d4877b5..c6b4bd43be4 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/filter.dm @@ -32,13 +32,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/trinary/filter/AltClick(mob/user) - if(can_interact(user)) - transfer_rate = MAX_TRANSFER_RATE - investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "volume output set to [transfer_rate] L/s") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/trinary/filter/click_alt(mob/user) + if(transfer_rate == MAX_TRANSFER_RATE) + return CLICK_ACTION_BLOCKING + + transfer_rate = MAX_TRANSFER_RATE + investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "volume output set to [transfer_rate] L/s") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/trinary/filter/update_overlays() . = ..() diff --git a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm index 862fbc65c89..f832adcb4ea 100644 --- a/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm +++ b/code/modules/atmospherics/machinery/components/trinary_devices/mixer.dm @@ -35,13 +35,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user) - if(can_interact(user)) - target_pressure = MAX_OUTPUT_PRESSURE - investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "pressure output on set to [target_pressure] kPa") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/trinary/mixer/click_alt(mob/user) + if(target_pressure == MAX_OUTPUT_PRESSURE) + return CLICK_ACTION_BLOCKING + + target_pressure = MAX_OUTPUT_PRESSURE + investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "pressure output on set to [target_pressure] kPa") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/trinary/mixer/update_overlays() . = ..() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm index 71610f05f40..ac88174dbc4 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/cryo.dm @@ -645,14 +645,13 @@ balloon_alert(user, "turned [on ? "on" : "off"]") return ..() -/obj/machinery/cryo_cell/AltClick(mob/user) - if(can_interact(user)) - if(state_open) - close_machine() - else - open_machine() - balloon_alert(user, "door [state_open ? "opened" : "closed"]") - return ..() +/obj/machinery/cryo_cell/click_alt(mob/user) + if(state_open) + close_machine() + else + open_machine() + balloon_alert(user, "door [state_open ? "opened" : "closed"]") + return CLICK_ACTION_SUCCESS /obj/machinery/cryo_cell/get_remote_view_fullscreens(mob/user) user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) diff --git a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm index 4161a30ed7d..771301b60e4 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/outlet_injector.dm @@ -62,13 +62,15 @@ update_appearance() return ..() -/obj/machinery/atmospherics/components/unary/outlet_injector/AltClick(mob/user) - if(can_interact(user)) - volume_rate = MAX_TRANSFER_RATE - investigate_log("was set to [volume_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) - balloon_alert(user, "volume output set to [volume_rate] L/s") - update_appearance() - return ..() +/obj/machinery/atmospherics/components/unary/outlet_injector/click_alt(mob/user) + if(volume_rate == MAX_TRANSFER_RATE) + return CLICK_ACTION_BLOCKING + + volume_rate = MAX_TRANSFER_RATE + investigate_log("was set to [volume_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS) + balloon_alert(user, "volume output set to [volume_rate] L/s") + update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/atmospherics/components/unary/outlet_injector/update_icon_nopipes() cut_overlays() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm index 17f6c761f12..4ac0e959e40 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/passive_vent.dm @@ -12,6 +12,8 @@ pipe_state = "pvent" has_cap_visuals = TRUE vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED + interaction_flags_click = NEED_VENTCRAWL + /obj/machinery/atmospherics/components/unary/passive_vent/update_icon_nopipes() cut_overlays() diff --git a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm index 0af962ac0f3..01def672bf7 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/thermomachine.dm @@ -144,12 +144,10 @@ . += span_notice("Heat capacity at [heat_capacity] Joules per Kelvin.") . += span_notice("Temperature range [min_temperature]K - [max_temperature]K ([(T0C-min_temperature)*-1]C - [(T0C-max_temperature)*-1]C).") -/obj/machinery/atmospherics/components/unary/thermomachine/AltClick(mob/living/user) +/obj/machinery/atmospherics/components/unary/thermomachine/click_alt(mob/living/user) if(panel_open) balloon_alert(user, "close panel!") - return - if(!can_interact(user)) - return + return CLICK_ACTION_BLOCKING if(target_temperature == T20C) target_temperature = max_temperature @@ -161,6 +159,7 @@ investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS) balloon_alert(user, "temperature reset to [target_temperature] K") update_appearance() + return CLICK_ACTION_SUCCESS /// Performs heat calculation for the freezer. /// We just equalize the gasmix with an object at temp = var/target_temperature and heat cap = var/heat_capacity diff --git a/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm b/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm index 4d876fd4586..c8bfd8628e9 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/unary_devices.dm @@ -17,6 +17,12 @@ ..() update_appearance() + +/obj/machinery/atmospherics/components/unary/click_alt(mob/living/beno) + beno.handle_ventcrawl(src) + return CLICK_ACTION_SUCCESS + + /obj/machinery/atmospherics/components/unary/proc/assign_uid_vents() uid = num2text(gl_uid++) return uid diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm index 2220f640541..02f0d203544 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_pump.dm @@ -18,6 +18,7 @@ vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED // vents are more complex machinery and so are less resistant to damage max_integrity = 100 + interaction_flags_click = NEED_VENTCRAWL ///Direction of pumping the gas (ATMOS_DIRECTION_RELEASING or ATMOS_DIRECTION_SIPHONING) var/pump_direction = ATMOS_DIRECTION_RELEASING diff --git a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm index 54704dcb5b6..0871053106f 100644 --- a/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm +++ b/code/modules/atmospherics/machinery/components/unary_devices/vent_scrubber.dm @@ -15,6 +15,7 @@ has_cap_visuals = TRUE vent_movement = VENTCRAWL_ALLOWED | VENTCRAWL_CAN_SEE | VENTCRAWL_ENTRANCE_ALLOWED processing_flags = NONE + interaction_flags_click = NEED_VENTCRAWL ///The mode of the scrubber (ATMOS_DIRECTION_SCRUBBING or ATMOS_DIRECTION_SIPHONING) var/scrubbing = ATMOS_DIRECTION_SCRUBBING diff --git a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm index 3d4e2b02e1f..3713958fbaa 100644 --- a/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm +++ b/code/modules/atmospherics/machinery/portable/portable_atmospherics.dm @@ -8,6 +8,7 @@ armor_type = /datum/armor/machinery_portable_atmospherics anchored = FALSE layer = ABOVE_OBJ_LAYER + interaction_flags_click = NEED_DEXTERITY ///Stores the gas mixture of the portable component. Don't access this directly, use return_air() so you support the temporary processing it provides var/datum/gas_mixture/air_contents @@ -162,14 +163,12 @@ update_appearance() return TRUE -/obj/machinery/portable_atmospherics/AltClick(mob/living/user) - . = ..() - if(!istype(user) || !user.can_perform_action(src, NEED_DEXTERITY) || !can_interact(user)) - return +/obj/machinery/portable_atmospherics/click_alt(mob/living/user) if(!holding) - return + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("You remove [holding] from [src].")) replace_tank(user, TRUE) + return CLICK_ACTION_SUCCESS /obj/machinery/portable_atmospherics/examine(mob/user) . = ..() diff --git a/code/modules/awaymissions/mission_code/snowdin.dm b/code/modules/awaymissions/mission_code/snowdin.dm index 2b8d4e80663..cc35b4a79ef 100644 --- a/code/modules/awaymissions/mission_code/snowdin.dm +++ b/code/modules/awaymissions/mission_code/snowdin.dm @@ -369,7 +369,7 @@ /obj/structure/barricade/wooden/snowed name = "crude plank barricade" desc = "This space is blocked off by a wooden barricade. It seems to be covered in a layer of snow." - icon_state = "woodenbarricade-snow" + icon_state = "woodenbarricade_snow" max_integrity = 125 /obj/item/clothing/under/syndicate/coldres diff --git a/code/modules/bitrunning/objects/disks.dm b/code/modules/bitrunning/objects/disks.dm index d6b44a60518..6e166d5eb7f 100644 --- a/code/modules/bitrunning/objects/disks.dm +++ b/code/modules/bitrunning/objects/disks.dm @@ -48,7 +48,7 @@ names += initial(thing.name) var/choice = tgui_input_list(user, message = "Select an ability", title = "Bitrunning Program", items = names) - if(isnull(choice)) + if(isnull(choice) || !user.is_holding(src)) return for(var/datum/action/thing as anything in selectable_actions) @@ -105,7 +105,7 @@ names += initial(thing.name) var/choice = tgui_input_list(user, message = "Select an ability", title = "Bitrunning Program", items = names) - if(isnull(choice)) + if(isnull(choice) || !user.is_holding(src)) return for(var/obj/thing as anything in selectable_items) diff --git a/code/modules/cards/deck/deck.dm b/code/modules/cards/deck/deck.dm index 47f45eeb5ab..54f8a5feba6 100644 --- a/code/modules/cards/deck/deck.dm +++ b/code/modules/cards/deck/deck.dm @@ -11,6 +11,7 @@ hitsound = null attack_verb_continuous = list("attacks") attack_verb_simple = list("attack") + interaction_flags_click = NEED_DEXTERITY|FORBID_TELEKINESIS_REACH /// The amount of time it takes to shuffle var/shuffle_time = DECK_SHUFFLE_TIME /// Deck shuffling cooldown. @@ -145,13 +146,13 @@ attack_hand(user, modifiers, flip_card = TRUE) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/item/toy/cards/deck/AltClick(mob/living/user) - if(user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH)) - if(HAS_TRAIT(src, TRAIT_WIELDED)) - shuffle_cards(user) - else - to_chat(user, span_notice("You must hold the [src] with both hands to shuffle.")) - return ..() +/obj/item/toy/cards/deck/click_alt(mob/living/user) + if(!HAS_TRAIT(src, TRAIT_WIELDED)) + to_chat(user, span_notice("You must hold the [src] with both hands to shuffle.")) + return CLICK_ACTION_BLOCKING + + shuffle_cards(user) + return CLICK_ACTION_SUCCESS /obj/item/toy/cards/deck/update_icon_state() switch(count_cards()) diff --git a/code/modules/cards/singlecard.dm b/code/modules/cards/singlecard.dm index 169715c51d9..e03c5800346 100644 --- a/code/modules/cards/singlecard.dm +++ b/code/modules/cards/singlecard.dm @@ -14,6 +14,7 @@ throw_range = 7 attack_verb_continuous = list("attacks") attack_verb_simple = list("attack") + interaction_flags_click = NEED_DEXTERITY|FORBID_TELEKINESIS_REACH /// Artistic style of the deck var/deckstyle = "nanotrasen" /// If the cards in the deck have different icon states (blank and CAS decks do not) @@ -237,8 +238,7 @@ if(isturf(src.loc)) // only display tihs message when flipping in a visible spot like on a table user.balloon_alert_to_viewers("flips a card") -/obj/item/toy/singlecard/AltClick(mob/living/carbon/human/user) - if(user.can_perform_action(src, NEED_DEXTERITY|FORBID_TELEKINESIS_REACH)) - transform = turn(transform, 90) +/obj/item/toy/singlecard/click_alt(mob/living/carbon/human/user) + transform = turn(transform, 90) // use the simple_rotation component to make this turn with Alt+RMB & Alt+LMB at some point in the future - TimT - return ..() + return CLICK_ACTION_SUCCESS diff --git a/code/modules/cargo/supplypod_beacon.dm b/code/modules/cargo/supplypod_beacon.dm index 999e7d76eec..8f1166002de 100644 --- a/code/modules/cargo/supplypod_beacon.dm +++ b/code/modules/cargo/supplypod_beacon.dm @@ -9,9 +9,14 @@ w_class = WEIGHT_CLASS_SMALL armor_type = /datum/armor/supplypod_beacon resistance_flags = FIRE_PROOF + interaction_flags_click = ALLOW_SILICON_REACH + /// The linked console var/obj/machinery/computer/cargo/express/express_console + /// If linked var/linked = FALSE + /// If this is ready to launch var/ready = FALSE + /// If it's been launched var/launched = FALSE /datum/armor/supplypod_beacon @@ -90,13 +95,12 @@ update_status(SP_READY) to_chat(user, span_notice("[src] linked to [C].")) -/obj/item/supplypod_beacon/AltClick(mob/user) - if (!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return - if (express_console) - unlink_console() - else +/obj/item/supplypod_beacon/click_alt(mob/user) + if(!express_console) to_chat(user, span_alert("There is no linked console.")) + return CLICK_ACTION_BLOCKING + unlink_console() + return CLICK_ACTION_SUCCESS /obj/item/supplypod_beacon/attackby(obj/item/W, mob/user) if(!istype(W, /obj/item/pen)) //give a tag that is visible from the linked express console diff --git a/code/modules/cargo/universal_scanner.dm b/code/modules/cargo/universal_scanner.dm index 68fe533959a..80a821a1f5e 100644 --- a/code/modules/cargo/universal_scanner.dm +++ b/code/modules/cargo/universal_scanner.dm @@ -129,15 +129,15 @@ payments_acc = null to_chat(user, span_notice("You clear the registered account.")) -/obj/item/universal_scanner/AltClick(mob/user) - . = ..() +/obj/item/universal_scanner/click_alt(mob/user) if(!scanning_mode == SCAN_SALES_TAG) - return + return CLICK_ACTION_BLOCKING var/potential_cut = input("How much would you like to pay out to the registered card?","Percentage Profit ([round(cut_min*100)]% - [round(cut_max*100)]%)") as num|null if(!potential_cut) cut_multiplier = initial(cut_multiplier) cut_multiplier = clamp(round(potential_cut/100, cut_min), cut_min, cut_max) to_chat(user, span_notice("[round(cut_multiplier*100)]% profit will be received if a package with a barcode is sold.")) + return CLICK_ACTION_SUCCESS /obj/item/universal_scanner/examine(mob/user) . = ..() diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 45ccda8b92b..3272620a865 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -194,8 +194,6 @@ var/list/spell_tabs = list() ///A lazy list of atoms we've examined in the last RECENT_EXAMINE_MAX_WINDOW (default 2) seconds, so that we will call [/atom/proc/examine_more] instead of [/atom/proc/examine] on them when examining var/list/recent_examines - ///Our object window datum. It stores info about and handles behavior for the object tab - var/datum/object_window_info/obj_window var/list/parallax_layers var/list/parallax_layers_cached @@ -266,3 +264,6 @@ /// Does this client have typing indicators enabled? var/typing_indicators = FALSE + + /// Loot panel for the client + var/datum/lootpanel/loot_panel diff --git a/code/modules/client/client_procs.dm b/code/modules/client/client_procs.dm index 4a81fa96ffa..b3e18ce5b0c 100644 --- a/code/modules/client/client_procs.dm +++ b/code/modules/client/client_procs.dm @@ -549,6 +549,8 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( if (!interviewee) initialize_menus() + loot_panel = new(src) + view_size = new(src, getScreenSize(prefs.read_preference(/datum/preference/toggle/widescreen))) view_size.resetFormat() view_size.setZoomMode() @@ -589,8 +591,6 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( SSserver_maint.UpdateHubStatus() if(credits) QDEL_LIST(credits) - if(obj_window) - QDEL_NULL(obj_window) if(holder) adminGreet(1) holder.owner = null @@ -621,6 +621,7 @@ GLOBAL_LIST_INIT(blacklisted_builds, list( QDEL_NULL(void) QDEL_NULL(tooltips) QDEL_NULL(open_loadout_ui) //SKYRAT EDIT ADDITION + QDEL_NULL(loot_panel) seen_messages = null Master.UpdateTickRate() ..() //Even though we're going to be hard deleted there are still some things that want to know the destroy is happening diff --git a/code/modules/clothing/glasses/_glasses.dm b/code/modules/clothing/glasses/_glasses.dm index 81e87e84baa..c44d5be4ef8 100644 --- a/code/modules/clothing/glasses/_glasses.dm +++ b/code/modules/clothing/glasses/_glasses.dm @@ -65,23 +65,19 @@ H.set_eye_blur_if_lower(10 SECONDS) eyes.apply_organ_damage(5) -/obj/item/clothing/glasses/AltClick(mob/user) - . = ..() //SKYRAT EDIT ADDITION - if(glass_colour_type && !forced_glass_color && ishuman(user)) - var/mob/living/carbon/human/human_user = user +/obj/item/clothing/glasses/click_alt(mob/user) + if(isnull(glass_colour_type) || forced_glass_color || !ishuman(user)) + return NONE + var/mob/living/carbon/human/human_user = user - if (human_user.glasses != src) - return ..() - - if (HAS_TRAIT_FROM(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT)) - REMOVE_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT) - to_chat(human_user, span_notice("You will no longer see glasses colors.")) - else - ADD_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT) - to_chat(human_user, span_notice("You will now see glasses colors.")) - human_user.update_glasses_color(src, TRUE) + if (HAS_TRAIT_FROM(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT)) + REMOVE_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT) + to_chat(human_user, span_notice("You will no longer see glasses colors.")) else - return ..() + ADD_TRAIT(human_user, TRAIT_SEE_GLASS_COLORS, GLASSES_TRAIT) + to_chat(human_user, span_notice("You will now see glasses colors.")) + human_user.update_glasses_color(src, TRUE) + return CLICK_ACTION_SUCCESS /obj/item/clothing/glasses/proc/change_glass_color(mob/living/carbon/human/H, datum/client_colour/glass_colour/new_color_type) var/old_colour_type = glass_colour_type @@ -664,18 +660,19 @@ var/datum/atom_hud/our_hud = GLOB.huds[hud] our_hud.hide_from(user) -/obj/item/clothing/glasses/debug/AltClick(mob/user) - . = ..() - if(ishuman(user)) - if(xray) - vision_flags &= ~SEE_MOBS|SEE_OBJS - REMOVE_TRAIT(user, TRAIT_XRAY_VISION, GLASSES_TRAIT) - else - vision_flags |= SEE_MOBS|SEE_OBJS - ADD_TRAIT(user, TRAIT_XRAY_VISION, GLASSES_TRAIT) - xray = !xray - var/mob/living/carbon/human/human_user = user - human_user.update_sight() +/obj/item/clothing/glasses/debug/click_alt(mob/user) + if(!ishuman(user)) + return CLICK_ACTION_BLOCKING + if(xray) + vision_flags &= ~SEE_MOBS|SEE_OBJS + REMOVE_TRAIT(user, TRAIT_XRAY_VISION, GLASSES_TRAIT) + else + vision_flags |= SEE_MOBS|SEE_OBJS + ADD_TRAIT(user, TRAIT_XRAY_VISION, GLASSES_TRAIT) + xray = !xray + var/mob/living/carbon/human/human_user = user + human_user.update_sight() + return CLICK_ACTION_SUCCESS /obj/item/clothing/glasses/regular/kim name = "binoclard lenses" diff --git a/code/modules/clothing/head/jobs.dm b/code/modules/clothing/head/jobs.dm index 67ecc0d4859..b300ff99179 100644 --- a/code/modules/clothing/head/jobs.dm +++ b/code/modules/clothing/head/jobs.dm @@ -173,6 +173,8 @@ armor_type = /datum/armor/fedora_det_hat icon_state = "detective" inhand_icon_state = "det_hat" + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS + /// Cooldown for retrieving precious candy corn on alt click var/candy_cooldown = 0 dog_fashion = /datum/dog_fashion/head/detective ///Path for the flask that spawns inside their hat roundstart @@ -198,17 +200,16 @@ . = ..() . += span_notice("Alt-click to take a candy corn.") -/obj/item/clothing/head/fedora/det_hat/AltClick(mob/user) - . = ..() - if(loc != user || !user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return - if(candy_cooldown < world.time) - var/obj/item/food/candy_corn/CC = new /obj/item/food/candy_corn(src) - user.put_in_hands(CC) - to_chat(user, span_notice("You slip a candy corn from your hat.")) - candy_cooldown = world.time+1200 - else +/obj/item/clothing/head/fedora/det_hat/click_alt(mob/user) + if(candy_cooldown >= world.time) to_chat(user, span_warning("You just took a candy corn! You should wait a couple minutes, lest you burn through your stash.")) + return CLICK_ACTION_BLOCKING + + var/obj/item/food/candy_corn/CC = new /obj/item/food/candy_corn(src) + user.put_in_hands(CC) + to_chat(user, span_notice("You slip a candy corn from your hat.")) + candy_cooldown = world.time+1200 + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/fedora/det_hat/minor flask_path = /obj/item/reagent_containers/cup/glass/flask/det/minor @@ -221,6 +222,7 @@ icon_state = "detective" inhand_icon_state = "det_hat" dog_fashion = /datum/dog_fashion/head/detective + interaction_flags_click = FORBID_TELEKINESIS_REACH ///prefix our phrases must begin with var/prefix = "go go gadget" ///an assoc list of phrase = item (like gun = revolver) @@ -277,7 +279,7 @@ return var/input = tgui_input_text(user, "What is the activation phrase?", "Activation phrase", "gadget", max_length = 26) - if(!input) + if(!input || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) return if(input in items_by_phrase) balloon_alert(user, "already used!") @@ -293,16 +295,16 @@ /obj/item/clothing/head/fedora/inspector_hat/attack_self(mob/user) . = ..() var/phrase = tgui_input_list(user, "What item do you want to remove by phrase?", "Item Removal", items_by_phrase) - if(!phrase) + if(!phrase || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) return user.put_in_inactive_hand(items_by_phrase[phrase]) -/obj/item/clothing/head/fedora/inspector_hat/AltClick(mob/user) - . = ..() +/obj/item/clothing/head/fedora/inspector_hat/click_alt(mob/user) var/new_prefix = tgui_input_text(user, "What should be the new prefix?", "Activation prefix", prefix, max_length = 24) - if(!new_prefix) - return + if(!new_prefix || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) + return CLICK_ACTION_BLOCKING prefix = new_prefix + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/fedora/inspector_hat/Exited(atom/movable/gone, direction) . = ..() diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm index 0b0a6fb4d50..92517d4a7dd 100644 --- a/code/modules/clothing/head/soft_caps.dm +++ b/code/modules/clothing/head/soft_caps.dm @@ -5,11 +5,14 @@ worn_icon = 'icons/mob/clothing/head/hats.dmi' icon_state = "cargosoft" inhand_icon_state = "greyscale_softcap" //todo wip + interaction_flags_click = NEED_DEXTERITY + /// For setting icon archetype var/soft_type = "cargo" + /// If there is a suffix to append var/soft_suffix = "soft" dog_fashion = /datum/dog_fashion/head/cargo_tech - + /// Whether this is on backwards... Woah, cool var/flipped = FALSE /obj/item/clothing/head/soft/dropped() @@ -24,10 +27,9 @@ flip(usr) -/obj/item/clothing/head/soft/AltClick(mob/user) - ..() - if(user.can_perform_action(src, NEED_DEXTERITY)) - flip(user) +/obj/item/clothing/head/soft/click_alt(mob/user) + flip(user) + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/soft/proc/flip(mob/user) diff --git a/code/modules/clothing/masks/animal_masks.dm b/code/modules/clothing/masks/animal_masks.dm index c2013b99177..5df5c6738d8 100644 --- a/code/modules/clothing/masks/animal_masks.dm +++ b/code/modules/clothing/masks/animal_masks.dm @@ -46,11 +46,12 @@ GLOBAL_LIST_INIT(cursed_animal_masks, list( if(clothing_flags & VOICEBOX_TOGGLABLE) . += span_notice("Its voicebox is currently [clothing_flags & VOICEBOX_DISABLED ? "disabled" : "enabled"]. Alt-click to toggle it.") -/obj/item/clothing/mask/animal/AltClick(mob/user) - . = ..() - if(clothing_flags & VOICEBOX_TOGGLABLE) - clothing_flags ^= VOICEBOX_DISABLED - to_chat(user, span_notice("You [clothing_flags & VOICEBOX_DISABLED ? "disabled" : "enabled"] [src]'s voicebox.")) +/obj/item/clothing/mask/animal/click_alt(mob/user) + if(!(clothing_flags & VOICEBOX_TOGGLABLE)) + return NONE + clothing_flags ^= VOICEBOX_DISABLED + to_chat(user, span_notice("You [clothing_flags & VOICEBOX_DISABLED ? "disabled" : "enabled"] [src]'s voicebox.")) + return CLICK_ACTION_SUCCESS /obj/item/clothing/mask/animal/proc/make_cursed() //apply cursed effects. ADD_TRAIT(src, TRAIT_NODROP, CURSED_MASK_TRAIT) diff --git a/code/modules/clothing/masks/bandana.dm b/code/modules/clothing/masks/bandana.dm index 4a97384fa39..cd4b3980e55 100644 --- a/code/modules/clothing/masks/bandana.dm +++ b/code/modules/clothing/masks/bandana.dm @@ -53,34 +53,36 @@ worn_icon_state = initial(worn_icon_state) undyeable = initial(undyeable) -/obj/item/clothing/mask/bandana/AltClick(mob/user) - . = ..() - if(iscarbon(user)) - var/mob/living/carbon/char = user - var/matrix/widen = matrix() - if((char.get_item_by_slot(ITEM_SLOT_NECK) == src) || (char.get_item_by_slot(ITEM_SLOT_MASK) == src) || (char.get_item_by_slot(ITEM_SLOT_HEAD) == src)) - to_chat(user, span_warning("You can't tie [src] while wearing it!")) - return - else if(slot_flags & ITEM_SLOT_HEAD) - to_chat(user, span_warning("You must undo [src] before you can tie it into a neckerchief!")) - return - else if(!user.is_holding(src)) - to_chat(user, span_warning("You must be holding [src] in order to tie it!")) - return +/obj/item/clothing/mask/bandana/click_alt(mob/user) + if(!iscarbon(user)) + return NONE - if(slot_flags & ITEM_SLOT_MASK) - undyeable = TRUE - slot_flags = ITEM_SLOT_NECK - worn_y_offset = -3 - widen.Scale(1.25, 1) - transform = widen - user.visible_message(span_notice("[user] ties [src] up like a neckerchief."), span_notice("You tie [src] up like a neckerchief.")) - else - undyeable = initial(undyeable) - slot_flags = initial(slot_flags) - worn_y_offset = initial(worn_y_offset) - transform = initial(transform) - user.visible_message(span_notice("[user] unties the neckercheif."), span_notice("You untie the neckercheif.")) + var/mob/living/carbon/char = user + var/matrix/widen = matrix() + if((char.get_item_by_slot(ITEM_SLOT_NECK) == src) || (char.get_item_by_slot(ITEM_SLOT_MASK) == src) || (char.get_item_by_slot(ITEM_SLOT_HEAD) == src)) + to_chat(user, span_warning("You can't tie [src] while wearing it!")) + return CLICK_ACTION_BLOCKING + else if(slot_flags & ITEM_SLOT_HEAD) + to_chat(user, span_warning("You must undo [src] before you can tie it into a neckerchief!")) + return CLICK_ACTION_BLOCKING + else if(!user.is_holding(src)) + to_chat(user, span_warning("You must be holding [src] in order to tie it!")) + return CLICK_ACTION_BLOCKING + + if(slot_flags & ITEM_SLOT_MASK) + undyeable = TRUE + slot_flags = ITEM_SLOT_NECK + worn_y_offset = -3 + widen.Scale(1.25, 1) + transform = widen + user.visible_message(span_notice("[user] ties [src] up like a neckerchief."), span_notice("You tie [src] up like a neckerchief.")) + else + undyeable = initial(undyeable) + slot_flags = initial(slot_flags) + worn_y_offset = initial(worn_y_offset) + transform = initial(transform) + user.visible_message(span_notice("[user] unties the neckercheif."), span_notice("You untie the neckercheif.")) + return CLICK_ACTION_SUCCESS /obj/item/clothing/mask/bandana/red name = "red bandana" @@ -229,14 +231,16 @@ greyscale_config_inhand_left = /datum/greyscale_config/facescarf/inhands_left greyscale_config_inhand_right = /datum/greyscale_config/facescarf/inhands_right flags_1 = IS_PLAYER_COLORABLE_1 + interaction_flags_click = NEED_DEXTERITY /obj/item/clothing/mask/facescarf/attack_self(mob/user) adjustmask(user) -/obj/item/clothing/mask/facescarf/AltClick(mob/user) - ..() - if(user.can_perform_action(src, NEED_DEXTERITY)) - adjustmask(user) + +/obj/item/clothing/mask/facescarf/click_alt(mob/user) + adjustmask(user) + return CLICK_ACTION_SUCCESS + /obj/item/clothing/mask/facescarf/examine(mob/user) . = ..() diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm index 8ba15fe521d..0249f0b1321 100644 --- a/code/modules/clothing/masks/breath.dm +++ b/code/modules/clothing/masks/breath.dm @@ -12,6 +12,7 @@ flags_cover = MASKCOVERSMOUTH visor_flags_cover = MASKCOVERSMOUTH resistance_flags = NONE + interaction_flags_click = NEED_DEXTERITY /datum/armor/mask_breath bio = 50 @@ -23,10 +24,9 @@ /obj/item/clothing/mask/breath/attack_self(mob/user) adjustmask(user) -/obj/item/clothing/mask/breath/AltClick(mob/user) - ..() - if(user.can_perform_action(src, NEED_DEXTERITY)) - adjustmask(user) +/obj/item/clothing/mask/breath/click_alt(mob/user) + adjustmask(user) + return CLICK_ACTION_SUCCESS /obj/item/clothing/mask/breath/examine(mob/user) . = ..() diff --git a/code/modules/clothing/masks/costume.dm b/code/modules/clothing/masks/costume.dm index 893455dcdd0..626d8ce4a65 100644 --- a/code/modules/clothing/masks/costume.dm +++ b/code/modules/clothing/masks/costume.dm @@ -12,14 +12,6 @@ "Pleading" = "pleading" ) -/obj/item/clothing/mask/joy/Initialize(mapload) - . = ..() - register_context() - -/obj/item/clothing/mask/joy/add_context(atom/source, list/context, obj/item/held_item, mob/user) - . = ..() - context[SCREENTIP_CONTEXT_ALT_LMB] = "Change Emotion" - return CONTEXTUAL_SCREENTIP_SET /obj/item/clothing/mask/joy/reskin_obj(mob/user) . = ..() diff --git a/code/modules/clothing/neck/_neck.dm b/code/modules/clothing/neck/_neck.dm index 6d5e328b0d6..48c85ac57aa 100644 --- a/code/modules/clothing/neck/_neck.dm +++ b/code/modules/clothing/neck/_neck.dm @@ -73,10 +73,9 @@ else . += span_notice("The tie can be untied with Alt-Click.") -/obj/item/clothing/neck/tie/AltClick(mob/user) - . = ..() +/obj/item/clothing/neck/tie/click_alt(mob/user) if(clip_on) - return + return NONE to_chat(user, span_notice("You concentrate as you begin [is_tied ? "untying" : "tying"] [src]...")) var/tie_timer_actual = tie_timer // Mirrors give you a boost to your tying speed. I realize this stacks and I think that's hilarious. @@ -88,11 +87,11 @@ // Tie/Untie our tie if(!do_after(user, tie_timer_actual)) to_chat(user, span_notice("Your fingers fumble away from [src] as your concentration breaks.")) - return + return CLICK_ACTION_BLOCKING // Clumsy & Dumb people have trouble tying their ties. if((HAS_TRAIT(user, TRAIT_CLUMSY) || HAS_TRAIT(user, TRAIT_DUMB)) && prob(50)) to_chat(user, span_notice("You just can't seem to get a proper grip on [src]!")) - return + return CLICK_ACTION_BLOCKING // Success! is_tied = !is_tied user.visible_message( @@ -101,6 +100,7 @@ ) update_appearance(UPDATE_ICON) user.update_clothing(ITEM_SLOT_NECK) + return CLICK_ACTION_SUCCESS /obj/item/clothing/neck/tie/update_icon() . = ..() diff --git a/code/modules/clothing/shoes/_shoes.dm b/code/modules/clothing/shoes/_shoes.dm index 02db0145ae7..e30dfc2f5ac 100644 --- a/code/modules/clothing/shoes/_shoes.dm +++ b/code/modules/clothing/shoes/_shoes.dm @@ -181,8 +181,7 @@ adjust_laces(SHOES_UNTIED, user) else // if they're someone else's shoes, go knot-wards - var/mob/living/L = user - if(istype(L) && L.body_position == STANDING_UP) + if(istype(living_user) && living_user.body_position == STANDING_UP) to_chat(user, span_warning("You must be on the floor to interact with [src]!")) return if(tied == SHOES_KNOTTED) @@ -196,11 +195,11 @@ to_chat(user, span_notice("You quietly set to work [tied ? "untying" : "knotting"] [loc]'s [src.name]...")) if(HAS_TRAIT(user, TRAIT_CLUMSY)) // based clowns trained their whole lives for this mod_time *= 0.75 - // SKYRAT EDIT START + // SKYRAT EDIT ADDITION START if(HAS_TRAIT(user, TRAIT_STICKY_FINGERS)) // Clowns with thieving gloves will be a menace mod_time *= 0.5 - // SKYRAT EDIT END - if(do_after(user, mod_time, target = our_guy, extra_checks = CALLBACK(src, PROC_REF(still_shoed), our_guy))) + // SKYRAT EDIT ADDITION END + if(do_after(user, mod_time, target = our_guy, extra_checks = CALLBACK(src, PROC_REF(still_shoed), our_guy), hidden = TRUE)) to_chat(user, span_notice("You [tied ? "untie" : "knot"] the laces on [loc]'s [src.name].")) if(tied == SHOES_UNTIED) adjust_laces(SHOES_KNOTTED, user) @@ -210,12 +209,12 @@ user.visible_message(span_danger("[our_guy] stamps on [user]'s hand, mid-shoelace [tied ? "knotting" : "untying"]!"), span_userdanger("Ow! [our_guy] stamps on your hand!"), list(our_guy)) to_chat(our_guy, span_userdanger("You stamp on [user]'s hand! What the- [user.p_they()] [user.p_were()] [tied ? "knotting" : "untying"] your shoelaces!")) user.emote("scream") - if(istype(L)) - var/obj/item/bodypart/ouchie = L.get_bodypart(pick(GLOB.arm_zones)) + if(istype(living_user)) + var/obj/item/bodypart/ouchie = living_user.get_bodypart(pick(GLOB.arm_zones)) if(ouchie) ouchie.receive_damage(brute = 10) - L.adjustStaminaLoss(40) - L.Paralyze(10) + living_user.adjustStaminaLoss(40) + living_user.Paralyze(10) ///checking to make sure we're still on the person we're supposed to be, for lacing do_after's /obj/item/clothing/shoes/proc/still_shoed(mob/living/carbon/our_guy) diff --git a/code/modules/clothing/spacesuits/_spacesuits.dm b/code/modules/clothing/spacesuits/_spacesuits.dm index 58cbf7e88e5..785ec9e26f9 100644 --- a/code/modules/clothing/spacesuits/_spacesuits.dm +++ b/code/modules/clothing/spacesuits/_spacesuits.dm @@ -58,11 +58,17 @@ equip_delay_other = 80 resistance_flags = NONE actions_types = list(/datum/action/item_action/toggle_spacesuit) - var/temperature_setting = BODYTEMP_NORMAL /// The default temperature setting - var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high /// If this is a path, this gets created as an object in Initialize. - var/cell_cover_open = FALSE /// Status of the cell cover on the suit - var/thermal_on = FALSE /// Status of the thermal regulator - var/show_hud = TRUE /// If this is FALSE the batery status UI will be disabled. This is used for suits that don't use bateries like the changeling's flesh suit mutation. + interaction_flags_click = NEED_DEXTERITY + /// The default temperature setting + var/temperature_setting = BODYTEMP_NORMAL + /// If this is a path, this gets created as an object in Initialize. + var/obj/item/stock_parts/cell/cell = /obj/item/stock_parts/cell/high + /// Status of the cell cover on the suit + var/cell_cover_open = FALSE + /// Status of the thermal regulator + var/thermal_on = FALSE + /// If this is FALSE the batery status UI will be disabled. This is used for suits that don't use bateries like the changeling's flesh suit mutation. + var/show_hud = TRUE /datum/armor/suit_space bio = 100 @@ -190,10 +196,9 @@ return /// Open the cell cover when ALT+Click on the suit -/obj/item/clothing/suit/space/AltClick(mob/living/user) - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return ..() +/obj/item/clothing/suit/space/click_alt(mob/living/user) toggle_spacesuit_cell(user) + return CLICK_ACTION_SUCCESS /// Remove the cell whent he cover is open on CTRL+Click /obj/item/clothing/suit/space/CtrlClick(mob/living/user) diff --git a/code/modules/clothing/spacesuits/plasmamen.dm b/code/modules/clothing/spacesuits/plasmamen.dm index ce21351c885..02a54f194ea 100644 --- a/code/modules/clothing/spacesuits/plasmamen.dm +++ b/code/modules/clothing/spacesuits/plasmamen.dm @@ -87,9 +87,9 @@ else . += span_notice("There's nothing placed on the helmet.") -/obj/item/clothing/head/helmet/space/plasmaman/AltClick(mob/user) - if(user.can_perform_action(src)) - toggle_welding_screen(user) +/obj/item/clothing/head/helmet/space/plasmaman/click_alt(mob/user) + toggle_welding_screen(user) + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/helmet/space/plasmaman/ui_action_click(mob/user, action) if(istype(action, /datum/action/item_action/toggle_welding_screen)) diff --git a/code/modules/clothing/suits/wintercoats.dm b/code/modules/clothing/suits/wintercoats.dm index aaa233f0d35..a92811b0fe7 100644 --- a/code/modules/clothing/suits/wintercoats.dm +++ b/code/modules/clothing/suits/wintercoats.dm @@ -47,12 +47,7 @@ . += span_notice("Alt-click to [zipped ? "un" : ""]zip.") -/obj/item/clothing/suit/hooded/wintercoat/AltClick(mob/user) - . = ..() - - if (. == FALSE) // Direct check for FALSE, because that's the specific case we want to propagate, not just null. - return FALSE - +/obj/item/clothing/suit/hooded/wintercoat/click_alt(mob/user) zipped = !zipped worn_icon_state = "[initial(icon_state)][zipped ? "_t" : ""]" balloon_alert(user, "[zipped ? "" : "un"]zipped") @@ -60,6 +55,7 @@ if(ishuman(loc)) var/mob/living/carbon/human/wearer = loc wearer.update_worn_oversuit() + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/hooded/winterhood name = "winter hood" diff --git a/code/modules/clothing/under/_under.dm b/code/modules/clothing/under/_under.dm index 2d6b3a21b65..7194046b196 100644 --- a/code/modules/clothing/under/_under.dm +++ b/code/modules/clothing/under/_under.dm @@ -51,33 +51,37 @@ if(random_sensor) //make the sensor mode favor higher levels, except coords. sensor_mode = pick(SENSOR_VITALS, SENSOR_VITALS, SENSOR_VITALS, SENSOR_LIVING, SENSOR_LIVING, SENSOR_COORDS, SENSOR_COORDS, SENSOR_OFF) - register_context() + if(!unique_reskin) // Already registered via unique reskin + register_context() AddElement(/datum/element/update_icon_updates_onmob, flags = ITEM_SLOT_ICLOTHING|ITEM_SLOT_OCLOTHING, body = TRUE) -/obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) - . = NONE +/obj/item/clothing/under/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + . = ..() + + var/changed = FALSE if(isnull(held_item) && has_sensor == HAS_SENSORS) context[SCREENTIP_CONTEXT_RMB] = "Toggle suit sensors" - . = CONTEXTUAL_SCREENTIP_SET + changed = TRUE if(istype(held_item, /obj/item/clothing/accessory) && length(attached_accessories) < max_number_of_accessories) context[SCREENTIP_CONTEXT_LMB] = "Attach accessory" - . = CONTEXTUAL_SCREENTIP_SET + changed = TRUE if(LAZYLEN(attached_accessories)) context[SCREENTIP_CONTEXT_ALT_RMB] = "Remove accessory" - . = CONTEXTUAL_SCREENTIP_SET + changed = TRUE if(istype(held_item, /obj/item/stack/cable_coil) && has_sensor == BROKEN_SENSORS) context[SCREENTIP_CONTEXT_LMB] = "Repair suit sensors" - . = CONTEXTUAL_SCREENTIP_SET + changed = TRUE if(can_adjust && adjusted != DIGITIGRADE_STYLE) context[SCREENTIP_CONTEXT_ALT_LMB] = "Wear [adjusted == ALT_STYLE ? "normally" : "casually"]" - . = CONTEXTUAL_SCREENTIP_SET + changed = TRUE + + return changed ? CONTEXTUAL_SCREENTIP_SET : NONE - return . /obj/item/clothing/under/worn_overlays(mutable_appearance/standing, isinhands = FALSE, file2use = null, mutant_styles = NONE) . = ..() @@ -349,17 +353,14 @@ return TRUE -/obj/item/clothing/under/AltClick(mob/user) - . = ..() - if(.) - return - +/obj/item/clothing/under/click_alt(mob/user) if(!can_adjust) balloon_alert(user, "can't be adjusted!") - return + return CLICK_ACTION_BLOCKING if(!can_use(user)) - return + return NONE rolldown() + return CLICK_ACTION_SUCCESS /obj/item/clothing/under/alt_click_secondary(mob/user) . = ..() diff --git a/code/modules/clothing/under/costume.dm b/code/modules/clothing/under/costume.dm index 7f99a45c8b6..b09aaa34104 100644 --- a/code/modules/clothing/under/costume.dm +++ b/code/modules/clothing/under/costume.dm @@ -239,6 +239,7 @@ "Black" = "black_mech_suit", ) + /obj/item/clothing/under/costume/russian_officer name = "\improper Russian officer's uniform" desc = "The latest in fashionable russian outfits." diff --git a/code/modules/detectivework/scanner.dm b/code/modules/detectivework/scanner.dm index 6ce6b0e67a6..82c77839da7 100644 --- a/code/modules/detectivework/scanner.dm +++ b/code/modules/detectivework/scanner.dm @@ -218,20 +218,20 @@ /proc/get_timestamp() return time2text(world.time + 432000, ":ss") -/obj/item/detective_scanner/AltClick(mob/living/user) - // Best way for checking if a player can use while not incapacitated, etc - if(!user.can_perform_action(src)) - return +/obj/item/detective_scanner/click_alt(mob/living/user) if(!LAZYLEN(log)) balloon_alert(user, "no logs!") - return + return CLICK_ACTION_BLOCKING if(scanner_busy) balloon_alert(user, "scanner busy!") - return + return CLICK_ACTION_BLOCKING balloon_alert(user, "deleting logs...") - if(do_after(user, 3 SECONDS, target = src)) - balloon_alert(user, "logs cleared") - log = list() + if(!do_after(user, 3 SECONDS, target = src)) + return CLICK_ACTION_BLOCKING + balloon_alert(user, "logs cleared") + log = list() + return CLICK_ACTION_SUCCESS + /obj/item/detective_scanner/examine(mob/user) . = ..() diff --git a/code/modules/experisci/experiment/handlers/experiment_handler.dm b/code/modules/experisci/experiment/handlers/experiment_handler.dm index e2d460e3903..b153c5157ea 100644 --- a/code/modules/experisci/experiment/handlers/experiment_handler.dm +++ b/code/modules/experisci/experiment/handlers/experiment_handler.dm @@ -238,6 +238,7 @@ /datum/component/experiment_handler/proc/configure_experiment(datum/source, mob/user) SIGNAL_HANDLER INVOKE_ASYNC(src, PROC_REF(ui_interact), user) + return CLICK_ACTION_SUCCESS /** * Attempts to show the user the experiment configuration panel diff --git a/code/modules/fishing/aquarium/aquarium.dm b/code/modules/fishing/aquarium/aquarium.dm index fe281d7e511..8a719c16f70 100644 --- a/code/modules/fishing/aquarium/aquarium.dm +++ b/code/modules/fishing/aquarium/aquarium.dm @@ -159,10 +159,7 @@ if(panel_open && reagents.total_volume) . += span_notice("You can use a plunger to empty the feed storage.") -/obj/structure/aquarium/AltClick(mob/living/user) - . = ..() - if(!user.can_perform_action(src)) - return +/obj/structure/aquarium/click_alt(mob/living/user) panel_open = !panel_open balloon_alert(user, "panel [panel_open ? "open" : "closed"]") if(panel_open) @@ -170,6 +167,7 @@ else reagents.flags &= ~(TRANSPARENT|REFILLABLE) update_appearance() + return CLICK_ACTION_SUCCESS /obj/structure/aquarium/wrench_act(mob/living/user, obj/item/tool) . = ..() diff --git a/code/modules/food_and_drinks/machinery/griddle.dm b/code/modules/food_and_drinks/machinery/griddle.dm index b54a470897f..e0c45e6c9af 100644 --- a/code/modules/food_and_drinks/machinery/griddle.dm +++ b/code/modules/food_and_drinks/machinery/griddle.dm @@ -63,6 +63,7 @@ /obj/machinery/griddle/attackby(obj/item/I, mob/user, params) + if(griddled_objects.len >= max_items) to_chat(user, span_notice("[src] can't fit more items!")) return @@ -79,6 +80,43 @@ else return ..() +/obj/machinery/griddle/item_interaction_secondary(mob/living/user, obj/item/item, list/modifiers) + if(isnull(item.atom_storage)) + return NONE + + for(var/obj/tray_item in griddled_objects) + item.atom_storage.attempt_insert(tray_item, user, TRUE) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/griddle/item_interaction(mob/living/user, obj/item/item, list/modifiers) + if(isnull(item.atom_storage)) + return NONE + + if(length(contents) >= max_items) + balloon_alert(user, "it's full!") + return ITEM_INTERACT_BLOCKING + + if(!istype(item, /obj/item/storage/bag/tray)) + // Non-tray dumping requires a do_after + to_chat(user, span_notice("You start dumping out the contents of [item] into [src]...")) + if(!do_after(user, 2 SECONDS, target = item)) + return ITEM_INTERACT_BLOCKING + + var/loaded = 0 + for(var/obj/tray_item in item) + if(!IS_EDIBLE(tray_item)) + continue + if(length(contents) >= max_items) + break + if(item.atom_storage.attempt_remove(tray_item, src)) + loaded++ + AddToGrill(tray_item, user) + if(loaded) + to_chat(user, span_notice("You insert [loaded] item\s into [src].")) + update_appearance() + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + /obj/machinery/griddle/attack_hand(mob/user, list/modifiers) . = ..() toggle_mode() diff --git a/code/modules/food_and_drinks/machinery/grill.dm b/code/modules/food_and_drinks/machinery/grill.dm index 3d9acdde0ba..8d0aa921a7b 100644 --- a/code/modules/food_and_drinks/machinery/grill.dm +++ b/code/modules/food_and_drinks/machinery/grill.dm @@ -125,15 +125,15 @@ grill_fuel += boost update_appearance(UPDATE_ICON_STATE) -/obj/machinery/grill/item_interaction(mob/living/user, obj/item/weapon, list/modifiers, is_right_clicking) +/obj/machinery/grill/item_interaction(mob/living/user, obj/item/weapon, list/modifiers) if(user.combat_mode || (weapon.item_flags & ABSTRACT) || (weapon.flags_1 & HOLOGRAM_1) || (weapon.resistance_flags & INDESTRUCTIBLE)) - return ..() + return NONE if(istype(weapon, /obj/item/stack/sheet/mineral/coal) || istype(weapon, /obj/item/stack/sheet/mineral/wood)) if(!QDELETED(grilled_item)) - return ..() + return NONE if(!anchored) - balloon_alert(user, "anchor first!") + balloon_alert(user, "anchor it first!") return ITEM_INTERACT_BLOCKING //required for amount subtypes @@ -150,7 +150,7 @@ if(!istype(stored, target_type)) continue if(stored.amount == MAX_STACK_SIZE) - to_chat(user, span_warning("No space for [weapon]")) + balloon_alert(user, "no space!") return ITEM_INTERACT_BLOCKING target.merge(stored) merged = TRUE @@ -158,7 +158,7 @@ if(!merged) weapon.forceMove(src) - to_chat(user, span_notice("You add [src] to the fuel stack")) + to_chat(user, span_notice("You add [src] to the fuel stack.")) if(!grill_fuel) burn_stack() begin_processing() @@ -167,9 +167,9 @@ if(is_reagent_container(weapon) && weapon.is_open_container()) var/obj/item/reagent_containers/container = weapon if(!QDELETED(grilled_item)) - return ..() + return NONE if(!anchored) - balloon_alert(user, "anchor first!") + balloon_alert(user, "anchor it first!") return ITEM_INTERACT_BLOCKING var/transfered_amount = weapon.reagents.trans_to(src, container.amount_per_transfer_from_this) @@ -202,11 +202,11 @@ update_appearance(UPDATE_ICON_STATE) //feedback - to_chat(user, span_notice("You transfer [transfered_amount]u to the fuel source")) + to_chat(user, span_notice("You transfer [transfered_amount]u to the fuel source.")) return ITEM_INTERACT_SUCCESS - else - to_chat(user, span_warning("No fuel was transfered")) - return ITEM_INTERACT_BLOCKING + + balloon_alert(user, "no fuel transfered!") + return ITEM_INTERACT_BLOCKING if(IS_EDIBLE(weapon)) //sanity checks @@ -218,10 +218,10 @@ if(!QDELETED(grilled_item)) balloon_alert(user, "remove item first!") return ITEM_INTERACT_BLOCKING - else if(grill_fuel <= 0) + if(grill_fuel <= 0) balloon_alert(user, "no fuel!") return ITEM_INTERACT_BLOCKING - else if(!user.transferItemToLoc(weapon, src)) + if(!user.transferItemToLoc(weapon, src)) balloon_alert(user, "[weapon] is stuck in your hand!") return ITEM_INTERACT_BLOCKING @@ -236,7 +236,7 @@ grill_loop.start() return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/grill/wrench_act(mob/living/user, obj/item/tool) if(user.combat_mode) diff --git a/code/modules/food_and_drinks/machinery/icecream_vat.dm b/code/modules/food_and_drinks/machinery/icecream_vat.dm index d4de5991995..cae1b260249 100644 --- a/code/modules/food_and_drinks/machinery/icecream_vat.dm +++ b/code/modules/food_and_drinks/machinery/icecream_vat.dm @@ -154,13 +154,12 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN return ..() -/obj/machinery/icecream_vat/AltClick(mob/user) - if(!user.can_interact_with(src)) - return FALSE - if(custom_ice_cream_beaker) - balloon_alert(user, "removed beaker") - try_put_in_hand(custom_ice_cream_beaker, user) - return ..() +/obj/machinery/icecream_vat/click_alt(mob/user) + if(!custom_ice_cream_beaker) + return CLICK_ACTION_BLOCKING + balloon_alert(user, "removed beaker") + try_put_in_hand(custom_ice_cream_beaker, user) + return CLICK_ACTION_SUCCESS /obj/machinery/icecream_vat/interact(mob/living/user) . = ..() diff --git a/code/modules/food_and_drinks/machinery/microwave.dm b/code/modules/food_and_drinks/machinery/microwave.dm index dfb6ac9b2de..55509886a3a 100644 --- a/code/modules/food_and_drinks/machinery/microwave.dm +++ b/code/modules/food_and_drinks/machinery/microwave.dm @@ -31,6 +31,7 @@ light_color = LIGHT_COLOR_DIM_YELLOW light_power = 3 anchored_tabletop_offset = 6 + interaction_flags_click = ALLOW_SILICON_REACH /// Is its function wire cut? var/wire_disabled = FALSE /// Wire cut to run mode backwards @@ -364,21 +365,15 @@ update_appearance() return ITEM_INTERACT_SUCCESS -/obj/machinery/microwave/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/microwave/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(operating) - return + return ITEM_INTERACT_SKIP_TO_ATTACK // Don't use tools if we're dirty if(dirty >= MAX_MICROWAVE_DIRTINESS) - return - - . = ..() - if(. & ITEM_INTERACT_ANY_BLOCKER) - return . - + return ITEM_INTERACT_SKIP_TO_ATTACK // Don't insert items if we're dirty if(panel_open && is_wire_tool(tool)) wires.interact(user) return ITEM_INTERACT_SUCCESS - - return . + return NONE /obj/machinery/microwave/attackby(obj/item/item, mob/living/user, params) if(operating) @@ -473,16 +468,16 @@ return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN -/obj/machinery/microwave/AltClick(mob/user, list/modifiers) - if(user.can_perform_action(src, ALLOW_SILICON_REACH)) - if(!vampire_charging_capable) - return +/obj/machinery/microwave/click_alt(mob/user, list/modifiers) + if(!vampire_charging_capable) + return NONE - vampire_charging_enabled = !vampire_charging_enabled - balloon_alert(user, "set to [vampire_charging_enabled ? "charge" : "cook"]") - playsound(src, 'sound/machines/twobeep_high.ogg', 50, FALSE) - if(HAS_SILICON_ACCESS(user)) - visible_message(span_notice("[user] sets \the [src] to [vampire_charging_enabled ? "charge" : "cook"]."), blind_message = span_notice("You hear \the [src] make an informative beep!")) + vampire_charging_enabled = !vampire_charging_enabled + balloon_alert(user, "set to [vampire_charging_enabled ? "charge" : "cook"]") + playsound(src, 'sound/machines/twobeep_high.ogg', 50, FALSE) + if(HAS_SILICON_ACCESS(user)) + visible_message(span_notice("[user] sets \the [src] to [vampire_charging_enabled ? "charge" : "cook"]."), blind_message = span_notice("You hear \the [src] make an informative beep!")) + return CLICK_ACTION_SUCCESS /obj/machinery/microwave/CtrlClick(mob/user) . = ..() diff --git a/code/modules/food_and_drinks/machinery/oven.dm b/code/modules/food_and_drinks/machinery/oven.dm index 8135dd8308f..ef9aac9a410 100644 --- a/code/modules/food_and_drinks/machinery/oven.dm +++ b/code/modules/food_and_drinks/machinery/oven.dm @@ -99,15 +99,24 @@ update_appearance() use_energy(active_power_usage) - -/obj/machinery/oven/attackby(obj/item/I, mob/user, params) - if(open && !used_tray && istype(I, /obj/item/plate/oven_tray)) - if(user.transferItemToLoc(I, src, silent = FALSE)) - to_chat(user, span_notice("You put [I] in [src].")) - add_tray_to_oven(I, user) - else +/obj/machinery/oven/attackby(obj/item/item, mob/user, params) + if(!open || used_tray || !istype(item, /obj/item/plate/oven_tray)) return ..() + if(user.transferItemToLoc(item, src, silent = FALSE)) + to_chat(user, span_notice("You put [item] in [src].")) + add_tray_to_oven(item, user) + +/obj/machinery/oven/item_interaction(mob/living/user, obj/item/item, list/modifiers) + if(open && used_tray && item.atom_storage) + return used_tray.item_interaction(user, item, modifiers) + return NONE + +/obj/machinery/oven/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + if(open && used_tray && tool.atom_storage) + return used_tray.item_interaction_secondary(user, tool, modifiers) + return NONE + ///Adds a tray to the oven, making sure the shit can get baked. /obj/machinery/oven/proc/add_tray_to_oven(obj/item/plate/oven_tray, mob/baker) used_tray = oven_tray @@ -243,6 +252,43 @@ max_items = 6 biggest_w_class = WEIGHT_CLASS_BULKY +/obj/item/plate/oven_tray/item_interaction_secondary(mob/living/user, obj/item/item, list/modifiers) + if(isnull(item.atom_storage)) + return NONE + + for(var/obj/tray_item in src) + item.atom_storage.attempt_insert(tray_item, user, TRUE) + return ITEM_INTERACT_SUCCESS + +/obj/item/plate/oven_tray/item_interaction(mob/living/user, obj/item/item, list/modifiers) + if(isnull(item.atom_storage)) + return NONE + + if(length(contents >= max_items)) + balloon_alert(user, "it's full!") + return ITEM_INTERACT_BLOCKING + + if(!istype(item, /obj/item/storage/bag/tray)) + // Non-tray dumping requires a do_after + to_chat(user, span_notice("You start dumping out the contents of [item] into [src]...")) + if(!do_after(user, 2 SECONDS, target = item)) + return ITEM_INTERACT_BLOCKING + + var/loaded = 0 + for(var/obj/tray_item in item) + if(!IS_EDIBLE(tray_item)) + continue + if(length(contents) >= max_items) + break + if(item.atom_storage.attempt_remove(tray_item, src)) + loaded++ + AddToPlate(tray_item, user) + if(loaded) + to_chat(user, span_notice("You insert [loaded] item\s into [src].")) + update_appearance() + return ITEM_INTERACT_SUCCESS + return ITEM_INTERACT_BLOCKING + #undef OVEN_SMOKE_STATE_NONE #undef OVEN_SMOKE_STATE_GOOD #undef OVEN_SMOKE_STATE_NEUTRAL diff --git a/code/modules/food_and_drinks/machinery/stove.dm b/code/modules/food_and_drinks/machinery/stove.dm index 38f98cfa8a8..6cc0ec52789 100644 --- a/code/modules/food_and_drinks/machinery/stove.dm +++ b/code/modules/food_and_drinks/machinery/stove.dm @@ -115,6 +115,34 @@ . = ..() LAZYREMOVE(added_ingredients, gone) +/** + * Adds items to a soup pot without invoking any procs that call sleep() when using in a component. + * + * Args: + * * transfer_from: The container that's being used to add items to the soup pot. Must not be null. + * * user: the entity adding ingredients via a container to a soup pot. Must not be null. + */ +/obj/item/reagent_containers/cup/soup_pot/proc/transfer_from_container_to_pot(obj/item/transfer_from, mob/user) + if(!transfer_from.atom_storage) + return + + var/obj/item/storage/tray = transfer_from + var/loaded = 0 + + for(var/obj/tray_item in tray.contents) + if(!can_add_ingredient(tray_item)) + continue + if(LAZYLEN(added_ingredients) >= max_ingredients) + balloon_alert(user, "it's full!") + return TRUE + if(tray.atom_storage.attempt_remove(tray_item, src)) + loaded++ + LAZYADD(added_ingredients, tray_item) + if(loaded) + to_chat(user, span_notice("You insert [loaded] items into \the [src].")) + update_appearance(UPDATE_OVERLAYS) + return TRUE + /obj/item/reagent_containers/cup/soup_pot/attackby(obj/item/attacking_item, mob/user, params) . = ..() if(.) @@ -140,6 +168,12 @@ update_appearance(UPDATE_OVERLAYS) return TRUE +/obj/item/reagent_containers/cup/soup_pot/item_interaction(mob/living/user, obj/item/item, list/modifiers) + if(LAZYACCESS(modifiers, RIGHT_CLICK)) + return NONE + + return transfer_from_container_to_pot(item, user) + /obj/item/reagent_containers/cup/soup_pot/attack_hand_secondary(mob/user, list/modifiers) if(!LAZYLEN(added_ingredients)) return SECONDARY_ATTACK_CALL_NORMAL diff --git a/code/modules/food_and_drinks/machinery/stove_component.dm b/code/modules/food_and_drinks/machinery/stove_component.dm index 1a39c3b2057..76a70f0f22a 100644 --- a/code/modules/food_and_drinks/machinery/stove_component.dm +++ b/code/modules/food_and_drinks/machinery/stove_component.dm @@ -128,6 +128,14 @@ /datum/component/stove/proc/on_attackby(obj/machinery/source, obj/item/attacking_item, mob/user, params) SIGNAL_HANDLER + if(istype(source, /obj/machinery/oven/range) && istype(attacking_item, /obj/item/storage/bag/tray) && container) + var/obj/machinery/oven/range/range = source + var/obj/item/reagent_containers/cup/soup_pot/soup_pot = container + + if(!range.open) + soup_pot.transfer_from_container_to_pot(attacking_item, user) + return COMPONENT_NO_AFTERATTACK + if(!attacking_item.is_open_container()) return if(!isnull(container)) diff --git a/code/modules/food_and_drinks/recipes/food_mixtures.dm b/code/modules/food_and_drinks/recipes/food_mixtures.dm index c557f75a631..bada60cd548 100644 --- a/code/modules/food_and_drinks/recipes/food_mixtures.dm +++ b/code/modules/food_and_drinks/recipes/food_mixtures.dm @@ -280,8 +280,8 @@ reaction_flags = REACTION_INSTANT /datum/chemical_reaction/food/martian_batter - results = list(/datum/reagent/consumable/martian_batter = 2) - required_reagents = list(/datum/reagent/consumable/flour = 1, /datum/reagent/consumable/nutriment/soup/dashi = 1) + results = list(/datum/reagent/consumable/martian_batter = 10) + required_reagents = list(/datum/reagent/consumable/flour = 5, /datum/reagent/consumable/nutriment/soup/dashi = 5) mix_message = "A smooth batter forms." reaction_flags = REACTION_INSTANT diff --git a/code/modules/holodeck/turfs.dm b/code/modules/holodeck/turfs.dm index 3e54c134106..51e09f8ab9a 100644 --- a/code/modules/holodeck/turfs.dm +++ b/code/modules/holodeck/turfs.dm @@ -8,9 +8,8 @@ /turf/open/floor/holofloor/attackby(obj/item/I, mob/living/user) return // HOLOFLOOR DOES NOT GIVE A FUCK -/turf/open/floor/holofloor/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - SHOULD_CALL_PARENT(FALSE) - return NONE // Fuck you +/turf/open/floor/holofloor/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + return ITEM_INTERACT_BLOCKING // Fuck you /turf/open/floor/holofloor/burn_tile() return //you can't burn a hologram! diff --git a/code/modules/hydroponics/biogenerator.dm b/code/modules/hydroponics/biogenerator.dm index 260c4042a0a..35bf611b506 100644 --- a/code/modules/hydroponics/biogenerator.dm +++ b/code/modules/hydroponics/biogenerator.dm @@ -13,6 +13,7 @@ density = TRUE circuit = /obj/item/circuitboard/machine/biogenerator processing_flags = START_PROCESSING_MANUALLY + interaction_flags_click = FORBID_TELEKINESIS_REACH /// Whether the biogenerator is currently processing biomass or not. var/processing = FALSE /// The reagent container that is currently inside of the biomass generator. Can be null. @@ -272,10 +273,9 @@ to_chat(user, span_warning("You cannot put \the [attacking_item] in \the [src]!")) -/obj/machinery/biogenerator/AltClick(mob/living/user) - . = ..() - if(user.can_perform_action(src, FORBID_TELEKINESIS_REACH) && can_interact(user)) - eject_beaker(user) +/obj/machinery/biogenerator/click_alt(mob/living/user) + eject_beaker(user) + return CLICK_ACTION_SUCCESS /// Activates biomass processing and converts all inserted food products into biomass diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index d32795e90c3..181a6fd9971 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -1099,8 +1099,6 @@ set_self_sustaining(!self_sustaining) to_chat(user, span_notice("You [self_sustaining ? "activate" : "deactivated"] [src]'s autogrow function[self_sustaining ? ", maintaining the tray's health while using high amounts of power" : ""].")) -/obj/machinery/hydroponics/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/machinery/hydroponics/attack_hand_secondary(mob/user, list/modifiers) . = ..() diff --git a/code/modules/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm index 5f5f01ea33f..53d220da80a 100644 --- a/code/modules/jobs/job_types/prisoner.dm +++ b/code/modules/jobs/job_types/prisoner.dm @@ -69,9 +69,9 @@ . = ..() var/crime_name = new_prisoner.client?.prefs?.read_preference(/datum/preference/choiced/prisoner_crime) - if(!crime_name) - return var/datum/prisoner_crime/crime = GLOB.prisoner_crimes[crime_name] + if (isnull(crime)) + return var/list/limbs_to_tat = new_prisoner.bodyparts.Copy() for(var/i in 1 to crime.tattoos) if(!length(SSpersistence.prison_tattoos_to_use) || visualsOnly) diff --git a/code/modules/lootpanel/_lootpanel.dm b/code/modules/lootpanel/_lootpanel.dm new file mode 100644 index 00000000000..339a79d77fa --- /dev/null +++ b/code/modules/lootpanel/_lootpanel.dm @@ -0,0 +1,75 @@ +/** + * ## Loot panel + * A datum that stores info containing the contents of a turf. + * Handles opening the lootpanel UI and searching the turf for items. + */ +/datum/lootpanel + /// The owner of the panel + var/client/owner + /// The list of all search objects indexed. + var/list/datum/search_object/contents = list() + /// The list of search_objects needing processed + var/list/datum/search_object/to_image = list() + /// We've been notified about client version + var/notified = FALSE + /// The turf being searched + var/turf/source_turf + + +/datum/lootpanel/New(client/owner) + . = ..() + + src.owner = owner + + +/datum/lootpanel/Destroy(force) + reset_contents() + owner = null + source_turf = null + + return ..() + + +/datum/lootpanel/ui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "LootPanel") + ui.set_autoupdate(FALSE) + ui.open() + + +/datum/lootpanel/ui_close(mob/user) + . = ..() + + source_turf = null + reset_contents() + + +/datum/lootpanel/ui_data(mob/user) + var/list/data = list() + + data["contents"] = get_contents() + data["searching"] = length(to_image) + + return data + + +/datum/lootpanel/ui_status(mob/user, datum/ui_state/state) + if(user.incapacitated()) + return UI_DISABLED + + return UI_INTERACTIVE + + +/datum/lootpanel/ui_act(action, list/params) + . = ..() + if(.) + return + + switch(action) + if("grab") + return grab(usr, params) + if("refresh") + return populate_contents() + + return FALSE diff --git a/code/modules/lootpanel/contents.dm b/code/modules/lootpanel/contents.dm new file mode 100644 index 00000000000..4bb255b1561 --- /dev/null +++ b/code/modules/lootpanel/contents.dm @@ -0,0 +1,49 @@ +/// Adds the item to contents and to_image (if needed) +/datum/lootpanel/proc/add_to_index(datum/search_object/index) + RegisterSignal(index, COMSIG_QDELETING, PROC_REF(on_searchable_deleted)) + if(isnull(index.icon)) + to_image += index + + contents += index + + +/// Used to populate contents and start generating if needed +/datum/lootpanel/proc/populate_contents() + if(length(contents)) + reset_contents() + + // Add source turf first + var/datum/search_object/source = new(owner, source_turf) + add_to_index(source) + + for(var/atom/thing as anything in source_turf.contents) + // validate + if(thing.mouse_opacity == MOUSE_OPACITY_TRANSPARENT) + continue + if(thing.IsObscured()) + continue + if(thing.invisibility > owner.mob.see_invisible) + continue + + // convert + var/datum/search_object/index = new(owner, thing) + add_to_index(index) + + var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src) + window?.send_update() + + if(length(to_image)) + SSlooting.backlog += src + + +/// For: Resetting to empty. Ignores the searchable qdel event +/datum/lootpanel/proc/reset_contents() + for(var/datum/search_object/index as anything in contents) + contents -= index + to_image -= index + + if(QDELETED(index)) + continue + + UnregisterSignal(index, COMSIG_QDELETING) + qdel(index) diff --git a/code/modules/lootpanel/handlers.dm b/code/modules/lootpanel/handlers.dm new file mode 100644 index 00000000000..40a76974ed4 --- /dev/null +++ b/code/modules/lootpanel/handlers.dm @@ -0,0 +1,19 @@ +/// On contents change, either reset or update +/datum/lootpanel/proc/on_searchable_deleted(datum/search_object/source) + SIGNAL_HANDLER + + contents -= source + to_image -= source + + var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src) +#if !defined(UNIT_TESTS) // we dont want to delete contents if we're testing + if(isnull(window)) + reset_contents() + return +#endif + + if(isturf(source.item)) + populate_contents() + return + + window?.send_update() diff --git a/code/modules/lootpanel/misc.dm b/code/modules/lootpanel/misc.dm new file mode 100644 index 00000000000..6e8448b8205 --- /dev/null +++ b/code/modules/lootpanel/misc.dm @@ -0,0 +1,48 @@ +/// Helper to open the panel +/datum/lootpanel/proc/open(turf/tile) + source_turf = tile + +#if !defined(OPENDREAM) && !defined(UNIT_TESTS) + if(!notified) + var/build = owner.byond_build + var/version = owner.byond_version + if(build < 515 || (build == 515 && version < 1635)) + to_chat(owner.mob, examine_block(span_info("\ + Your version of Byond doesn't support fast image loading.\n\ + Detected: [version].[build]\n\ + Required version for this feature: 515.1635 or later.\n\ + Visit BYOND's website to get the latest version of BYOND.\n\ + "))) + + notified = TRUE +#endif + + populate_contents() + ui_interact(owner.mob) + + +/** + * Called by SSlooting whenever this datum is added to its backlog. + * Iterates over to_image list to create icons, then removes them. + * Returns boolean - whether this proc has finished the queue or not. + */ +/datum/lootpanel/proc/process_images() + for(var/datum/search_object/index as anything in to_image) + to_image -= index + + if(QDELETED(index) || index.icon) + continue + + index.generate_icon(owner) + + if(TICK_CHECK) + break + + var/datum/tgui/window = SStgui.get_open_ui(owner.mob, src) + if(isnull(window)) + reset_contents() + return TRUE + + window.send_update() + + return !length(to_image) diff --git a/code/modules/lootpanel/search_object.dm b/code/modules/lootpanel/search_object.dm new file mode 100644 index 00000000000..520228e465e --- /dev/null +++ b/code/modules/lootpanel/search_object.dm @@ -0,0 +1,84 @@ +/** + * ## Search Object + * An object for content lists. Compacted item data. + */ +/datum/search_object + /// Item we're indexing + var/atom/item + /// Url to the image of the object + var/icon + /// Icon state, for inexpensive icons + var/icon_state + /// Name of the original object + var/name + /// Typepath of the original object for ui grouping + var/path + + +/datum/search_object/New(client/owner, atom/item) + . = ..() + + src.item = item + name = item.name + if(isobj(item)) + path = item.type + + if(isturf(item)) + RegisterSignal(item, COMSIG_TURF_CHANGE, PROC_REF(on_turf_change)) + else + RegisterSignals(item, list( + COMSIG_ITEM_PICKUP, + COMSIG_MOVABLE_MOVED, + COMSIG_QDELETING, + ), PROC_REF(on_item_moved)) + + // Icon generation conditions ////////////// + // Condition 1: Icon is complex + if(ismob(item) || length(item.overlays) > 2) + return + + // Condition 2: Can't get icon path + if(!isfile(item.icon) || !length("[item.icon]")) + return + + // Condition 3: Using opendream +#if defined(OPENDREAM) || defined(UNIT_TESTS) + return +#endif + + // Condition 4: Using older byond version + var/build = owner.byond_build + var/version = owner.byond_version + if(build < 515 || (build == 515 && version < 1635)) + return + + icon = "[item.icon]" + icon_state = item.icon_state + + +/datum/search_object/Destroy(force) + item = null + + return ..() + + +/// Generates the icon for the search object. This is the expensive part. +/datum/search_object/proc/generate_icon(client/owner) + if(ismob(item) || length(item.overlays) > 2) + icon = costly_icon2html(item, owner, sourceonly = TRUE) + else // our pre 515.1635 fallback for normal items + icon = icon2html(item, owner, sourceonly = TRUE) + + +/// Parent item has been altered, search object no longer valid +/datum/search_object/proc/on_item_moved(atom/source) + SIGNAL_HANDLER + + qdel(src) + + +/// Parent tile has been altered, entire search needs reset +/datum/search_object/proc/on_turf_change(turf/source, path, list/new_baseturfs, flags, list/post_change_callbacks) + SIGNAL_HANDLER + + post_change_callbacks += CALLBACK(src, GLOBAL_PROC_REF(qdel), src) diff --git a/code/modules/lootpanel/ss_looting.dm b/code/modules/lootpanel/ss_looting.dm new file mode 100644 index 00000000000..cf0882fa890 --- /dev/null +++ b/code/modules/lootpanel/ss_looting.dm @@ -0,0 +1,39 @@ + +/// Queues image generation for search objects without icons +SUBSYSTEM_DEF(looting) + name = "Loot Icon Generation" + init_order = INIT_ORDER_LOOT + priority = FIRE_PRIORITY_PROCESS + wait = 0.5 SECONDS + /// Backlog of items. Gets put into processing + var/list/datum/lootpanel/backlog = list() + /// Actively processing items + var/list/datum/lootpanel/processing = list() + + +/datum/controller/subsystem/looting/stat_entry(msg) + msg = "P:[length(backlog)]" + return ..() + + +/datum/controller/subsystem/looting/fire(resumed) + if(!length(backlog)) + return + + if(!resumed) + processing = backlog + backlog = list() + + while(length(processing)) + var/datum/lootpanel/panel = processing[length(processing)] + if(QDELETED(panel) || !length(panel.to_image)) + processing.len-- + continue + + if(!panel.process_images()) + backlog += panel + + if(MC_TICK_CHECK) + return + + processing.len-- diff --git a/code/modules/lootpanel/ui.dm b/code/modules/lootpanel/ui.dm new file mode 100644 index 00000000000..c7f0cf2358d --- /dev/null +++ b/code/modules/lootpanel/ui.dm @@ -0,0 +1,42 @@ +/// UI helper for converting the associative list to a list of lists +/datum/lootpanel/proc/get_contents() + var/list/items = list() + + for(var/datum/search_object/index as anything in contents) + UNTYPED_LIST_ADD(items, list( + "icon_state" = index.icon_state, + "icon" = index.icon, + "name" = index.name, + "path" = index.path, + "ref" = REF(index), + )) + + return items + + +/// Clicks an object from the contents. Validates the object and the user +/datum/lootpanel/proc/grab(mob/user, list/params) + var/ref = params["ref"] + if(isnull(ref)) + return FALSE + + var/datum/search_object/index = locate(ref) in contents + var/atom/thing = index?.item + if(QDELETED(index) || QDELETED(thing)) // Obj is gone + return FALSE + + if(thing != source_turf && !(locate(thing) in source_turf.contents)) + qdel(index) // Item has moved + return TRUE + + var/modifiers = "" + if(params["ctrl"]) + modifiers += "ctrl=1;" + if(params["middle"]) + modifiers += "middle=1;" + if(params["shift"]) + modifiers += "shift=1;" + + user.ClickOn(thing, modifiers) + + return TRUE diff --git a/code/modules/mapfluff/ruins/spaceruin_code/anomalyresearch.dm b/code/modules/mapfluff/ruins/spaceruin_code/anomalyresearch.dm index ba311d44a2a..f290c06d78f 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/anomalyresearch.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/anomalyresearch.dm @@ -17,7 +17,7 @@ /obj/effect/anomaly/flux, /obj/effect/anomaly/bluespace, /obj/effect/anomaly/hallucination, - /obj/effect/anomaly/bioscrambler + /obj/effect/anomaly/bioscrambler/docile ) ///Do we anchor the anomaly? Set to true if you don't want anomalies drifting away (like if theyre in space or something) diff --git a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm index b61e14e8916..c699b25666e 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/hilbertshotel.dm @@ -363,20 +363,21 @@ GLOBAL_VAR_INIT(hhMysteryRoomNumber, rand(1, 999999)) if(get_dist(get_turf(src), get_turf(user)) <= 1) promptExit(user) -/turf/closed/indestructible/hoteldoor/AltClick(mob/user) - . = ..() - if(get_dist(get_turf(src), get_turf(user)) <= 1) - to_chat(user, span_notice("You peak through the door's bluespace peephole...")) - user.reset_perspective(parentSphere) - var/datum/action/peephole_cancel/PHC = new - user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) - PHC.Grant(user) - RegisterSignal(user, COMSIG_MOVABLE_MOVED, TYPE_PROC_REF(/atom/, check_eye), user) +/turf/closed/indestructible/hoteldoor/click_alt(mob/user) + to_chat(user, span_notice("You peak through the door's bluespace peephole...")) + user.reset_perspective(parentSphere) + var/datum/action/peephole_cancel/PHC = new + user.overlay_fullscreen("remote_view", /atom/movable/screen/fullscreen/impaired, 1) + PHC.Grant(user) + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(check_eye)) + return CLICK_ACTION_SUCCESS -/turf/closed/indestructible/hoteldoor/check_eye(mob/user) - if(get_dist(get_turf(src), get_turf(user)) >= 2) - for(var/datum/action/peephole_cancel/PHC in user.actions) - INVOKE_ASYNC(PHC, TYPE_PROC_REF(/datum/action/peephole_cancel, Trigger)) +/turf/closed/indestructible/hoteldoor/proc/check_eye(mob/user, atom/oldloc, direction) + SIGNAL_HANDLER + if(get_dist(get_turf(src), get_turf(user)) < 2) + return + for(var/datum/action/peephole_cancel/PHC in user.actions) + INVOKE_ASYNC(PHC, TYPE_PROC_REF(/datum/action/peephole_cancel, Trigger)) /datum/action/peephole_cancel name = "Cancel View" diff --git a/code/modules/mining/abandoned_crates.dm b/code/modules/mining/abandoned_crates.dm index 5022dac212c..0b029bc3b63 100644 --- a/code/modules/mining/abandoned_crates.dm +++ b/code/modules/mining/abandoned_crates.dm @@ -60,10 +60,9 @@ return ..() -/obj/structure/closet/crate/secure/loot/AltClick(mob/living/user) - if(!user.can_perform_action(src)) - return - return attack_hand(user) //this helps you not blow up so easily by overriding unlocking which results in an immediate boom. +/obj/structure/closet/crate/secure/loot/click_alt(mob/living/user) + attack_hand(user) //this helps you not blow up so easily by overriding unlocking which results in an immediate boom. + return CLICK_ACTION_SUCCESS /obj/structure/closet/crate/secure/loot/attackby(obj/item/W, mob/user) if(locked) diff --git a/code/modules/mining/boulder_processing/_boulder_processing.dm b/code/modules/mining/boulder_processing/_boulder_processing.dm index f30fd24ae6c..5795eed1d74 100644 --- a/code/modules/mining/boulder_processing/_boulder_processing.dm +++ b/code/modules/mining/boulder_processing/_boulder_processing.dm @@ -244,9 +244,9 @@ return FALSE -/obj/machinery/bouldertech/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/bouldertech/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(panel_open || user.combat_mode) - return ..() + return NONE if(istype(tool, /obj/item/boulder)) var/obj/item/boulder/my_boulder = tool @@ -276,7 +276,7 @@ to_chat(user, span_notice("You claim [amount] mining points from \the [src] to [id_card].")) return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/bouldertech/wrench_act(mob/living/user, obj/item/tool) . = ITEM_INTERACT_BLOCKING diff --git a/code/modules/mining/boulder_processing/boulder_types.dm b/code/modules/mining/boulder_processing/boulder_types.dm index 08ed961404c..1ffce8737bd 100644 --- a/code/modules/mining/boulder_processing/boulder_types.dm +++ b/code/modules/mining/boulder_processing/boulder_types.dm @@ -44,7 +44,7 @@ /datum/material/uranium = 3, ) - set_custom_materials(list(pick_weight(gulag_minerals) = SHEET_MATERIAL_AMOUNT * rand(1, 3))) + set_custom_materials(list(pick_weight(gulag_minerals) = SHEET_MATERIAL_AMOUNT)) ///Boulders usually spawned in lavaland labour camp area but with bluespace material /obj/item/boulder/gulag_expanded @@ -66,7 +66,7 @@ /datum/material/uranium = 3, ) - set_custom_materials(list(pick_weight(expanded_gulag_minerals) = SHEET_MATERIAL_AMOUNT * rand(1, 3))) + set_custom_materials(list(pick_weight(expanded_gulag_minerals) = SHEET_MATERIAL_AMOUNT)) ///lowgrade boulder, most commonly spawned /obj/item/boulder/shabby diff --git a/code/modules/mining/equipment/explorer_gear.dm b/code/modules/mining/equipment/explorer_gear.dm index 10ba871176f..4845b0f9c00 100644 --- a/code/modules/mining/equipment/explorer_gear.dm +++ b/code/modules/mining/equipment/explorer_gear.dm @@ -131,24 +131,25 @@ hoodtype = /obj/item/clothing/head/hooded/cloakhood/goliath body_parts_covered = CHEST|GROIN|ARMS -/obj/item/clothing/suit/hooded/cloak/goliath/AltClick(mob/user) - . = ..() - if(iscarbon(user)) - var/mob/living/carbon/char = user - if((char.get_item_by_slot(ITEM_SLOT_NECK) == src) || (char.get_item_by_slot(ITEM_SLOT_OCLOTHING) == src)) - to_chat(user, span_warning("You can't adjust [src] while wearing it!")) - return - if(!user.is_holding(src)) - to_chat(user, span_warning("You must be holding [src] in order to adjust it!")) - return - if(slot_flags & ITEM_SLOT_OCLOTHING) - slot_flags = ITEM_SLOT_NECK - set_armor(/datum/armor/none) - user.visible_message(span_notice("[user] adjusts their [src] for ceremonial use."), span_notice("You adjust your [src] for ceremonial use.")) - else - slot_flags = initial(slot_flags) - set_armor(initial(armor_type)) - user.visible_message(span_notice("[user] adjusts their [src] for defensive use."), span_notice("You adjust your [src] for defensive use.")) +/obj/item/clothing/suit/hooded/cloak/goliath/click_alt(mob/user) + if(!iscarbon(user)) + return NONE + var/mob/living/carbon/char = user + if((char.get_item_by_slot(ITEM_SLOT_NECK) == src) || (char.get_item_by_slot(ITEM_SLOT_OCLOTHING) == src)) + to_chat(user, span_warning("You can't adjust [src] while wearing it!")) + return CLICK_ACTION_BLOCKING + if(!user.is_holding(src)) + to_chat(user, span_warning("You must be holding [src] in order to adjust it!")) + return CLICK_ACTION_BLOCKING + if(slot_flags & ITEM_SLOT_OCLOTHING) + slot_flags = ITEM_SLOT_NECK + set_armor(/datum/armor/none) + user.visible_message(span_notice("[user] adjusts their [src] for ceremonial use."), span_notice("You adjust your [src] for ceremonial use.")) + else + slot_flags = initial(slot_flags) + set_armor(initial(armor_type)) + user.visible_message(span_notice("[user] adjusts their [src] for defensive use."), span_notice("You adjust your [src] for defensive use.")) + return CLICK_ACTION_SUCCESS /datum/armor/cloak_goliath melee = 35 diff --git a/code/modules/mining/equipment/marker_beacons.dm b/code/modules/mining/equipment/marker_beacons.dm index 9a7913278e8..c33181dd806 100644 --- a/code/modules/mining/equipment/marker_beacons.dm +++ b/code/modules/mining/equipment/marker_beacons.dm @@ -59,16 +59,15 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list( var/obj/structure/marker_beacon/M = new(user.loc, picked_color) transfer_fingerprints_to(M) -/obj/item/stack/marker_beacon/AltClick(mob/living/user) - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/item/stack/marker_beacon/click_alt(mob/living/user) var/input_color = tgui_input_list(user, "Choose a color", "Beacon Color", GLOB.marker_beacon_colors) if(isnull(input_color)) - return - if(!istype(user) || !user.can_perform_action(src)) - return + return CLICK_ACTION_BLOCKING + if(!user.can_perform_action(src)) + return CLICK_ACTION_BLOCKING picked_color = input_color update_appearance() + return CLICK_ACTION_SUCCESS /obj/structure/marker_beacon name = "marker beacon" @@ -154,17 +153,15 @@ GLOBAL_LIST_INIT(marker_beacon_colors, sort_list(list( return return ..() -/obj/structure/marker_beacon/AltClick(mob/living/user) - ..() - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/structure/marker_beacon/click_alt(mob/living/user) var/input_color = tgui_input_list(user, "Choose a color", "Beacon Color", GLOB.marker_beacon_colors) if(isnull(input_color)) - return - if(!istype(user) || !user.can_perform_action(src)) - return + return CLICK_ACTION_BLOCKING + if(!user.can_perform_action(src)) + return NONE picked_color = input_color update_appearance() + return CLICK_ACTION_SUCCESS /* Preset marker beacon types, for mapping */ diff --git a/code/modules/mining/equipment/mining_tools.dm b/code/modules/mining/equipment/mining_tools.dm index 2c8a6af7864..4fdd997fcab 100644 --- a/code/modules/mining/equipment/mining_tools.dm +++ b/code/modules/mining/equipment/mining_tools.dm @@ -372,7 +372,7 @@ var/atom/throw_target = get_edge_target_turf(target_mob, get_dir(user, get_step_away(target_mob, user))) target_mob.throw_at(throw_target, 2, 2, user, gentle = TRUE) target_mob.Knockdown(2 SECONDS) - var/body_zone = pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + var/body_zone = pick(GLOB.all_body_zones) user.apply_damage(force / recoil_factor, BRUTE, body_zone, user.run_armor_check(body_zone, MELEE)) to_chat(user, span_danger("The weight of the Big Slappy recoils!")) log_combat(user, user, "recoiled Big Slappy into") diff --git a/code/modules/mining/laborcamp/laborstacker.dm b/code/modules/mining/laborcamp/laborstacker.dm index 8a7ffeec88f..f46c7ecebb9 100644 --- a/code/modules/mining/laborcamp/laborstacker.dm +++ b/code/modules/mining/laborcamp/laborstacker.dm @@ -1,4 +1,4 @@ -GLOBAL_LIST(labor_sheet_values) +#define SHEET_POINT_VALUE 33 /**********************Prisoners' Console**************************/ @@ -22,14 +22,6 @@ GLOBAL_LIST(labor_sheet_values) if(!stacking_machine) return INITIALIZE_HINT_QDEL - if(!GLOB.labor_sheet_values) - var/sheet_list = list() - for(var/obj/item/stack/sheet/sheet as anything in subtypesof(/obj/item/stack/sheet)) - if(!initial(sheet.point_value) || (initial(sheet.merge_type) && initial(sheet.merge_type) != sheet)) //ignore no-value sheets and x/fifty subtypes - continue - sheet_list += list(list("ore" = initial(sheet.name), "value" = initial(sheet.point_value))) - GLOB.labor_sheet_values = sort_list(sheet_list, GLOBAL_PROC_REF(cmp_sheet_list)) - /obj/machinery/mineral/labor_claim_console/Destroy() QDEL_NULL(security_radio) if(stacking_machine) @@ -46,11 +38,6 @@ GLOBAL_LIST(labor_sheet_values) ui = new(user, src, "LaborClaimConsole", name) ui.open() -/obj/machinery/mineral/labor_claim_console/ui_static_data(mob/user) - var/list/data = list() - data["ores"] = GLOB.labor_sheet_values - return data - /obj/machinery/mineral/labor_claim_console/ui_data(mob/user) var/list/data = list() var/can_go_home = FALSE @@ -155,8 +142,9 @@ GLOBAL_LIST(labor_sheet_values) labor_console = null return ..() -/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/inp) - points += inp.point_value * inp.amount +/obj/machinery/mineral/stacking_machine/laborstacker/process_sheet(obj/item/stack/sheet/input) + if (input.manufactured && input.gulag_valid) + points += SHEET_POINT_VALUE * input.amount return ..() /obj/machinery/mineral/stacking_machine/laborstacker/attackby(obj/item/weapon, mob/user, params) @@ -190,3 +178,5 @@ GLOBAL_LIST(labor_sheet_values) say("ID: [prisoner_id.registered_name].") say("Points Collected: [prisoner_id.points] / [prisoner_id.goal].") say("Collect points by bringing smelted minerals to the Labor Shuttle stacking machine. Reach your quota to earn your release.") + +#undef SHEET_POINT_VALUE diff --git a/code/modules/mining/machine_processing.dm b/code/modules/mining/machine_processing.dm index 11a941c4098..61318f63b92 100644 --- a/code/modules/mining/machine_processing.dm +++ b/code/modules/mining/machine_processing.dm @@ -143,6 +143,8 @@ var/datum/proximity_monitor/proximity_monitor ///Material container for materials var/datum/component/material_container/materials + /// What can be input into the machine? + var/accepted_type = /obj/item/stack /obj/machinery/mineral/processing_unit/Initialize(mapload) . = ..() @@ -153,7 +155,7 @@ SSmaterials.materials_by_category[MAT_CATEGORY_SILO], \ INFINITY, \ MATCONTAINER_EXAMINE, \ - allowed_items = /obj/item/stack \ + allowed_items = accepted_type \ ) if(!GLOB.autounlock_techwebs[/datum/techweb/autounlocking/smelter]) GLOB.autounlock_techwebs[/datum/techweb/autounlocking/smelter] = new /datum/techweb/autounlocking/smelter @@ -166,7 +168,7 @@ stored_research = null return ..() -/obj/machinery/mineral/processing_unit/proc/process_ore(obj/item/stack/ore/O) +/obj/machinery/mineral/processing_unit/proc/process_ore(obj/item/stack/O) if(QDELETED(O)) return var/material_amount = materials.get_item_material_amount(O) @@ -225,7 +227,7 @@ /obj/machinery/mineral/processing_unit/pickup_item(datum/source, atom/movable/target, direction) if(QDELETED(target)) return - if(istype(target, /obj/item/stack/ore)) + if(istype(target, accepted_type)) process_ore(target) /obj/machinery/mineral/processing_unit/process(seconds_per_tick) @@ -282,4 +284,8 @@ var/O = new P(src) unload_mineral(O) +/// Only accepts ore, for the work camp +/obj/machinery/mineral/processing_unit/gulag + accepted_type = /obj/item/stack/ore + #undef SMELT_AMOUNT diff --git a/code/modules/mining/machine_redemption.dm b/code/modules/mining/machine_redemption.dm index 0b9b7895c6b..14460cffb1b 100644 --- a/code/modules/mining/machine_redemption.dm +++ b/code/modules/mining/machine_redemption.dm @@ -203,18 +203,16 @@ default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS -/obj/machinery/mineral/ore_redemption/AltClick(mob/living/user) - . = ..() - if(!user.can_perform_action(src)) - return - if(panel_open) - input_dir = turn(input_dir, -90) - output_dir = turn(output_dir, -90) - to_chat(user, span_notice("You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].")) - unregister_input_turf() // someone just rotated the input and output directions, unregister the old turf - register_input_turf() // register the new one - update_appearance(UPDATE_OVERLAYS) - return TRUE +/obj/machinery/mineral/ore_redemption/click_alt(mob/living/user) + if(!panel_open) + return CLICK_ACTION_BLOCKING + input_dir = turn(input_dir, -90) + output_dir = turn(output_dir, -90) + to_chat(user, span_notice("You change [src]'s I/O settings, setting the input to [dir2text(input_dir)] and the output to [dir2text(output_dir)].")) + unregister_input_turf() // someone just rotated the input and output directions, unregister the old turf + register_input_turf() // register the new one + update_appearance(UPDATE_OVERLAYS) + return CLICK_ACTION_SUCCESS /obj/machinery/mineral/ore_redemption/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/modules/mining/machine_stacking.dm b/code/modules/mining/machine_stacking.dm index 286317a5d74..1f9a29a6e37 100644 --- a/code/modules/mining/machine_stacking.dm +++ b/code/modules/mining/machine_stacking.dm @@ -137,26 +137,26 @@ if (input_dir == output_dir) rotate(input) -/obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/inp) - if(QDELETED(inp)) +/obj/machinery/mineral/stacking_machine/proc/process_sheet(obj/item/stack/sheet/input) + if(QDELETED(input)) return // Dump the sheets to the silo if attached if(materials.silo && !materials.on_hold()) - var/matlist = inp.custom_materials & materials.mat_container.materials + var/matlist = input.custom_materials & materials.mat_container.materials if (length(matlist)) - materials.mat_container.insert_item(inp, context = src) + materials.insert_item(input) return // No silo attached process to internal storage - var/key = inp.merge_type + var/key = input.merge_type var/obj/item/stack/sheet/storage = stack_list[key] if(!storage) //It's the first of this sheet added - stack_list[key] = storage = new inp.type(src, 0) - storage.amount += inp.amount //Stack the sheets - qdel(inp) + stack_list[key] = storage = new input.type(src, 0) + storage.amount += input.amount //Stack the sheets + qdel(input) while(storage.amount >= stack_amt) //Get rid of excessive stackage - var/obj/item/stack/sheet/out = new inp.type(null, stack_amt) + var/obj/item/stack/sheet/out = new input.type(null, stack_amt) unload_mineral(out) storage.amount -= stack_amt diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index 3e262468298..98a771a06a9 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -121,6 +121,7 @@ return apply_atmos_requirements(mapload) apply_temperature_requirements(mapload) + apply_target_randomisation() /mob/living/basic/proc/on_ssair_init(datum/source) SIGNAL_HANDLER @@ -142,6 +143,11 @@ return AddElement(/datum/element/body_temp_sensitive, minimum_survivable_temperature, maximum_survivable_temperature, unsuitable_cold_damage, unsuitable_heat_damage, mapload) +/mob/living/basic/proc/apply_target_randomisation() + if (basic_mob_flags & PRECISE_ATTACK_ZONES) + return + AddElement(/datum/element/attack_zone_randomiser) + /mob/living/basic/Life(seconds_per_tick = SSMOBS_DT, times_fired) . = ..() if(staminaloss > 0) diff --git a/code/modules/mob/living/basic/bots/_bots.dm b/code/modules/mob/living/basic/bots/_bots.dm index c86160db025..ccd4b0d617f 100644 --- a/code/modules/mob/living/basic/bots/_bots.dm +++ b/code/modules/mob/living/basic/bots/_bots.dm @@ -41,6 +41,7 @@ GLOBAL_LIST_INIT(command_strings, list( light_power = 0.6 speed = 3 req_one_access = list(ACCESS_ROBOTICS) + interaction_flags_click = ALLOW_SILICON_REACH ///The Robot arm attached to this robot - has a 50% chance to drop on death. var/robot_arm = /obj/item/bodypart/arm/right/robot ///The inserted (if any) pAI in this bot. @@ -362,13 +363,9 @@ GLOBAL_LIST_INIT(command_strings, list( ui = new(user, src, "SimpleBot", name) ui.open() -/mob/living/basic/bot/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return +/mob/living/basic/bot/click_alt(mob/user) unlock_with_id(user) + return CLICK_ACTION_SUCCESS /mob/living/basic/bot/proc/unlock_with_id(mob/living/user) if(bot_access_flags & BOT_COVER_EMAGGED) diff --git a/code/modules/mob/living/basic/minebots/minebot.dm b/code/modules/mob/living/basic/minebots/minebot.dm index 26e549cbcf9..3361330915f 100644 --- a/code/modules/mob/living/basic/minebots/minebot.dm +++ b/code/modules/mob/living/basic/minebots/minebot.dm @@ -203,12 +203,12 @@ combat_overlay.color = selected_color update_appearance() -/mob/living/basic/mining_drone/AltClick(mob/living/user) - . = ..() +/mob/living/basic/mining_drone/click_alt(mob/living/user) if(user.combat_mode) - return + return CLICK_ACTION_BLOCKING set_combat_mode(!combat_mode) balloon_alert(user, "now [combat_mode ? "attacking wildlife" : "collecting loose ore"]") + return CLICK_ACTION_SUCCESS /mob/living/basic/mining_drone/RangedAttack(atom/target) if(!combat_mode) diff --git a/code/modules/mob/living/basic/ruin_defender/flesh.dm b/code/modules/mob/living/basic/ruin_defender/flesh.dm index a80d6847a8a..a6087f08589 100644 --- a/code/modules/mob/living/basic/ruin_defender/flesh.dm +++ b/code/modules/mob/living/basic/ruin_defender/flesh.dm @@ -38,6 +38,9 @@ if(!isnull(limb)) register_to_limb(limb) +/mob/living/basic/living_limb_flesh/apply_target_randomisation() + AddElement(/datum/element/attack_zone_randomiser, GLOB.limb_zones) + /mob/living/basic/living_limb_flesh/Destroy(force) . = ..() if(current_bodypart) @@ -71,6 +74,8 @@ if(!victim.CanReach(movable)) continue candidates += movable + if(!length(candidates)) + return var/atom/movable/candidate = pick(candidates) if(isnull(candidate)) return @@ -123,9 +128,9 @@ part_type = /obj/item/bodypart/leg/right/flesh target.visible_message(span_danger("[src] [target_part ? "tears off and attaches itself" : "attaches itself"] to where [target][target.p_s()] limb used to be!")) - var/obj/item/bodypart/new_bodypart = new part_type(TRUE) //dont_spawn_flesh, we cant use named arguments here - new_bodypart.replace_limb(target, TRUE) + var/obj/item/bodypart/new_bodypart = new part_type() forceMove(new_bodypart) + new_bodypart.replace_limb(target, TRUE) register_to_limb(new_bodypart) /mob/living/basic/living_limb_flesh/proc/owner_shocked(datum/source, shock_damage, shock_source, siemens_coeff, flags) diff --git a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm index 51379ce88a0..c0fb9b67e7f 100644 --- a/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm +++ b/code/modules/mob/living/basic/space_fauna/regal_rat/regal_rat.dm @@ -55,6 +55,7 @@ AddElementTrait(TRAIT_WADDLING, INNATE_TRAIT, /datum/element/waddling) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/door_pryer, pry_time = 5 SECONDS, interaction_key = REGALRAT_INTERACTION) + AddElement(/datum/element/poster_tearer, interaction_key = REGALRAT_INTERACTION) AddComponent(\ /datum/component/ghost_direct_control,\ poll_candidates = poll_ghosts,\ diff --git a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm index 266e7b5b414..21943d39d3d 100644 --- a/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm +++ b/code/modules/mob/living/basic/space_fauna/revenant/_revenant.dm @@ -204,7 +204,7 @@ ShiftClickOn(A) return if(LAZYACCESS(modifiers, ALT_CLICK)) - AltClickNoInteract(src, A) + base_click_alt(A) return if(LAZYACCESS(modifiers, RIGHT_CLICK)) ranged_secondary_attack(A, modifiers) diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm index a59e5021948..9df1697e400 100644 --- a/code/modules/mob/living/brain/posibrain.dm +++ b/code/modules/mob/living/brain/posibrain.dm @@ -75,17 +75,16 @@ GLOBAL_VAR(posibrain_notify_cooldown) update_appearance() addtimer(CALLBACK(src, PROC_REF(check_success)), ask_delay) -/obj/item/mmi/posibrain/AltClick(mob/living/user) - if(!istype(user) || !user.can_perform_action(src)) - return +/obj/item/mmi/posibrain/click_alt(mob/living/user) var/input_seed = tgui_input_text(user, "Enter a personality seed", "Enter seed", ask_role, MAX_NAME_LEN) if(isnull(input_seed)) - return - if(!istype(user) || !user.can_perform_action(src)) + return CLICK_ACTION_BLOCKING + if(!user.can_perform_action(src)) return to_chat(user, span_notice("You set the personality seed to \"[input_seed]\".")) ask_role = input_seed update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/mmi/posibrain/proc/check_success() searching = FALSE diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index a88002321d8..8a524d5e222 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -240,25 +240,34 @@ return TRUE return FALSE + /mob/living/carbon/resist_buckle() - if(HAS_TRAIT(src, TRAIT_RESTRAINED)) - changeNext_move(CLICK_CD_BREAKOUT) - last_special = world.time + CLICK_CD_BREAKOUT - var/buckle_cd = 60 SECONDS - if(handcuffed) - var/obj/item/restraints/O = src.get_item_by_slot(ITEM_SLOT_HANDCUFFED) - buckle_cd = O.breakouttime - visible_message(span_warning("[src] attempts to unbuckle [p_them()]self!"), \ - span_notice("You attempt to unbuckle yourself... (This will take around [round(buckle_cd/600,1)] minute\s, and you need to stay still.)")) - if(do_after(src, buckle_cd, target = src, timed_action_flags = IGNORE_HELD_ITEM)) - if(!buckled) - return - buckled.user_unbuckle_mob(src,src) - else - if(src && buckled) - to_chat(src, span_warning("You fail to unbuckle yourself!")) - else - buckled.user_unbuckle_mob(src,src) + if(!HAS_TRAIT(src, TRAIT_RESTRAINED)) + buckled.user_unbuckle_mob(src, src) + return + + changeNext_move(CLICK_CD_BREAKOUT) + last_special = world.time + CLICK_CD_BREAKOUT + var/buckle_cd = 1 MINUTES + + if(handcuffed) + var/obj/item/restraints/cuffs = src.get_item_by_slot(ITEM_SLOT_HANDCUFFED) + buckle_cd = cuffs.breakouttime + + visible_message(span_warning("[src] attempts to unbuckle [p_them()]self!"), + span_notice("You attempt to unbuckle yourself... \ + (This will take around [DisplayTimeText(buckle_cd)] and you must stay still.)")) + + if(!do_after(src, buckle_cd, target = src, timed_action_flags = IGNORE_HELD_ITEM, hidden = TRUE)) + if(buckled) + to_chat(src, span_warning("You fail to unbuckle yourself!")) + return + + if(QDELETED(src) || isnull(buckled)) + return + + buckled.user_unbuckle_mob(src, src) + /mob/living/carbon/resist_fire() return !!apply_status_effect(/datum/status_effect/stop_drop_roll) @@ -282,34 +291,40 @@ cuff_resist(I) -/mob/living/carbon/proc/cuff_resist(obj/item/I, breakouttime = 1 MINUTES, cuff_break = 0) - if((cuff_break != INSTANT_CUFFBREAK) && (SEND_SIGNAL(src, COMSIG_MOB_REMOVING_CUFFS, I) & COMSIG_MOB_BLOCK_CUFF_REMOVAL)) +/** + * Helper to break the cuffs from hands + * @param {obj/item} cuffs - The cuffs to break + * @param {number} breakouttime - The time it takes to break the cuffs. Use SECONDS/MINUTES defines + * @param {number} cuff_break - Speed multiplier, 0 is default, see _DEFINES\combat.dm + */ +/mob/living/carbon/proc/cuff_resist(obj/item/cuffs, breakouttime = 1 MINUTES, cuff_break = 0) + if((cuff_break != INSTANT_CUFFBREAK) && (SEND_SIGNAL(src, COMSIG_MOB_REMOVING_CUFFS, cuffs) & COMSIG_MOB_BLOCK_CUFF_REMOVAL)) return //The blocking object should sent a fluff-appropriate to_chat about cuff removal being blocked - if(I.item_flags & BEING_REMOVED) - to_chat(src, span_warning("You're already attempting to remove [I]!")) + if(cuffs.item_flags & BEING_REMOVED) + to_chat(src, span_warning("You're already attempting to remove [cuffs]!")) return - I.item_flags |= BEING_REMOVED - breakouttime = I.breakouttime + cuffs.item_flags |= BEING_REMOVED + breakouttime = cuffs.breakouttime if(!cuff_break) - visible_message(span_warning("[src] attempts to remove [I]!")) - to_chat(src, span_notice("You attempt to remove [I]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)")) - if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM)) - . = clear_cuffs(I, cuff_break) + visible_message(span_warning("[src] attempts to remove [cuffs]!")) + to_chat(src, span_notice("You attempt to remove [cuffs]... (This will take around [DisplayTimeText(breakouttime)] and you need to stand still.)")) + if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM, hidden = TRUE)) + . = clear_cuffs(cuffs, cuff_break) else - to_chat(src, span_warning("You fail to remove [I]!")) + to_chat(src, span_warning("You fail to remove [cuffs]!")) else if(cuff_break == FAST_CUFFBREAK) - breakouttime = 50 - visible_message(span_warning("[src] is trying to break [I]!")) - to_chat(src, span_notice("You attempt to break [I]... (This will take around 5 seconds and you need to stand still.)")) + breakouttime = 5 SECONDS + visible_message(span_warning("[src] is trying to break [cuffs]!")) + to_chat(src, span_notice("You attempt to break [cuffs]... (This will take around 5 seconds and you need to stand still.)")) if(do_after(src, breakouttime, target = src, timed_action_flags = IGNORE_HELD_ITEM)) - . = clear_cuffs(I, cuff_break) + . = clear_cuffs(cuffs, cuff_break) else - to_chat(src, span_warning("You fail to break [I]!")) + to_chat(src, span_warning("You fail to break [cuffs]!")) else if(cuff_break == INSTANT_CUFFBREAK) - . = clear_cuffs(I, cuff_break) - I.item_flags &= ~BEING_REMOVED + . = clear_cuffs(cuffs, cuff_break) + cuffs.item_flags &= ~BEING_REMOVED /mob/living/carbon/proc/uncuff() if (handcuffed) @@ -410,6 +425,9 @@ if((HAS_TRAIT(src, TRAIT_NOHUNGER) || HAS_TRAIT(src, TRAIT_TOXINLOVER)) && !force) return TRUE + if(!force && HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + lost_nutrition *= 0.5 + SEND_SIGNAL(src, COMSIG_CARBON_VOMITED, distance, force) // cache some stuff that we'll need later (at least multiple times) @@ -426,7 +444,10 @@ span_userdanger("You try to throw up, but there's nothing in your stomach!"), ) if(stun) - Stun(20 SECONDS) + var/stun_time = 20 SECONDS + if(HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + stun_time *= 0.5 + Stun(stun_time) if(knockdown) Knockdown(20 SECONDS) return TRUE @@ -449,11 +470,14 @@ add_mood_event("vomit", /datum/mood_event/vomit) if(stun) - Stun(8 SECONDS) + var/stun_time = 8 SECONDS + if(!blood && HAS_TRAIT(src, TRAIT_STRONG_STOMACH)) + stun_time *= 0.5 + Stun(stun_time) if(knockdown) Knockdown(8 SECONDS) - playsound(get_turf(src), 'sound/effects/splat.ogg', 50, TRUE) + playsound(src, 'sound/effects/splat.ogg', 50, TRUE) var/need_mob_update = FALSE var/turf/location = get_turf(src) diff --git a/code/modules/mob/living/carbon/death.dm b/code/modules/mob/living/carbon/death.dm index f74c2c1ca21..41a958518c5 100644 --- a/code/modules/mob/living/carbon/death.dm +++ b/code/modules/mob/living/carbon/death.dm @@ -1,5 +1,5 @@ /mob/living/carbon/death(gibbed) - if(stat == DEAD || HAS_TRAIT(src, TRAIT_NODEATH) && !gibbed) + if(stat == DEAD) return losebreath = 0 diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index cf843c43b00..f63e692a118 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -20,7 +20,7 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift) new /obj/effect/decal/remains/human(loc) /mob/living/carbon/human/death(gibbed) - if(stat == DEAD || HAS_TRAIT(src, TRAIT_NODEATH) && !gibbed) + if(stat == DEAD) return stop_sound_channel(CHANNEL_HEARTBEAT) var/obj/item/organ/internal/heart/human_heart = get_organ_slot(ORGAN_SLOT_HEART) diff --git a/code/modules/mob/living/carbon/human/human_stripping.dm b/code/modules/mob/living/carbon/human/human_stripping.dm index 41a00b59359..b2dce5234b4 100644 --- a/code/modules/mob/living/carbon/human/human_stripping.dm +++ b/code/modules/mob/living/carbon/human/human_stripping.dm @@ -222,7 +222,7 @@ GLOBAL_LIST_INIT(strippable_human_items, create_strippable_list(list( source.log_message("is being pickpocketed of [item] by [key_name(user)] ([pocket_side])", LOG_VICTIM, color="orange", log_globally=FALSE) item.add_fingerprint(src) - var/result = start_unequip_mob(item, source, user, POCKET_STRIP_DELAY) + var/result = start_unequip_mob(item, source, user, strip_delay = POCKET_STRIP_DELAY, hidden = TRUE) if (!(result || HAS_TRAIT(user, TRAIT_STICKY_FINGERS))) //SKYRAT EDIT ADDITION original if (!result) warn_owner(source) diff --git a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm index a1c3a245034..5a1c632acd7 100644 --- a/code/modules/mob/living/carbon/human/species_types/jellypeople.dm +++ b/code/modules/mob/living/carbon/human/species_types/jellypeople.dm @@ -731,10 +731,13 @@ to_chat(telepath, span_warning("You don't see anyone to send your thought to.")) return var/mob/living/recipient = tgui_input_list(telepath, "Choose a telepathic message recipient", "Telepathy", sort_names(recipient_options)) - if(isnull(recipient)) + if(isnull(recipient) || telepath.stat == DEAD || !is_species(telepath, /datum/species/jelly/stargazer)) return var/msg = tgui_input_text(telepath, title = "Telepathy") - if(isnull(msg)) + if(isnull(msg) || telepath.stat == DEAD || !is_species(telepath, /datum/species/jelly/stargazer)) + return + if(!(recipient in oview(telepath))) + to_chat(telepath, span_warning("You can't see [recipient] anymore!")) return if(recipient.can_block_magic(MAGIC_RESISTANCE_MIND, charge_cost = 0)) to_chat(telepath, span_warning("As you reach into [recipient]'s mind, you are stopped by a mental blockage. It seems you've been foiled.")) diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index 0819c285d9a..51fb86d73ad 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -108,7 +108,6 @@ INVOKE_ASYNC(src, TYPE_PROC_REF(/mob, emote), "deathgasp") set_stat(DEAD) - unset_machine() timeofdeath = world.time station_timestamp_timeofdeath = station_time_timestamp() var/turf/death_turf = get_turf(src) diff --git a/code/modules/mob/living/init_signals.dm b/code/modules/mob/living/init_signals.dm index ed496c0c84b..b8f1e60a612 100644 --- a/code/modules/mob/living/init_signals.dm +++ b/code/modules/mob/living/init_signals.dm @@ -157,7 +157,6 @@ /mob/living/proc/on_ui_blocked_trait_gain(datum/source) SIGNAL_HANDLER mobility_flags &= ~(MOBILITY_UI) - unset_machine() update_mob_action_buttons() /// Called when [TRAIT_UI_BLOCKED] is removed from the mob. diff --git a/code/modules/mob/living/life.dm b/code/modules/mob/living/life.dm index 35866514b79..dd7e8a17c9b 100644 --- a/code/modules/mob/living/life.dm +++ b/code/modules/mob/living/life.dm @@ -66,9 +66,6 @@ handle_wounds(seconds_per_tick, times_fired) - if(machine) - machine.check_eye(src) - if(stat != DEAD) return 1 diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 7dcdfe54496..e37981d038a 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1405,9 +1405,14 @@ var/datum/dna/mob_DNA = has_dna() if(!mob_DNA || !mob_DNA.check_mutation(/datum/mutation/human/telekinesis) || !tkMaxRangeCheck(src, target)) - to_chat(src, span_warning("You are too far away!")) + if(!(action_bitflags & SILENT_ADJACENCY)) + to_chat(src, span_warning("You are too far away!")) return FALSE + if((action_bitflags & NEED_VENTCRAWL) && !HAS_TRAIT(src, TRAIT_VENTCRAWLER_NUDE) && !HAS_TRAIT(src, TRAIT_VENTCRAWLER_ALWAYS)) + to_chat(src, span_warning("You wouldn't fit!")) + return FALSE + if((action_bitflags & NEED_DEXTERITY) && !ISADVANCEDTOOLUSER(src)) to_chat(src, span_warning("You don't have the dexterity to do this!")) return FALSE @@ -1851,7 +1856,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) if(registered_z && old_level_new_clients == 0) for(var/datum/ai_controller/controller as anything in SSai_controllers.ai_controllers_by_zlevel[registered_z]) controller.set_ai_status(AI_STATUS_OFF) - + //Check the amount of clients exists on the Z level we're moving towards, excluding ourselves. var/new_level_old_clients = SSmobs.clients_by_zlevel[new_z].len diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index 5f7571186be..292b0f1d7c1 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -1,17 +1,5 @@ #define CALL_BOT_COOLDOWN 900 -//Not sure why this is necessary... -/proc/AutoUpdateAI(obj/subject) - var/is_in_use = 0 - if (subject != null) - for(var/A in GLOB.ai_list) - var/mob/living/silicon/ai/M = A - if ((M.client && M.machine == subject)) - is_in_use = 1 - subject.attack_ai(M) - return is_in_use - - /mob/living/silicon/ai name = "AI" real_name = "AI" @@ -645,7 +633,6 @@ /mob/living/silicon/ai/proc/ai_network_change() set category = "AI Commands" set name = "Jump To Network" - unset_machine() ai_tracking_tool.reset_tracking() var/cameralist[0] diff --git a/code/modules/mob/living/silicon/ai/freelook/eye.dm b/code/modules/mob/living/silicon/ai/freelook/eye.dm index e8c1919b020..98a2e629776 100644 --- a/code/modules/mob/living/silicon/ai/freelook/eye.dm +++ b/code/modules/mob/living/silicon/ai/freelook/eye.dm @@ -202,7 +202,6 @@ current = null if(ai_tracking_tool) ai_tracking_tool.reset_tracking() - unset_machine() if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc)) to_chat(src, "ERROR: Eyeobj not found. Creating new eye...") diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm index 76f5c2eec93..b11f125d38c 100644 --- a/code/modules/mob/living/silicon/ai/life.dm +++ b/code/modules/mob/living/silicon/ai/life.dm @@ -12,9 +12,6 @@ if(isturf(loc) && (QDELETED(eyeobj) || !eyeobj.loc)) view_core() - if(machine) - machine.check_eye(src) - // Handle power damage (oxy) if(aiRestorePowerRoutine) // Lost power diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index 27bbec88292..ec204dbc82b 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -29,6 +29,7 @@ light_power = 0.6 del_on_death = TRUE req_one_access = list(ACCESS_ROBOTICS) + interaction_flags_click = ALLOW_SILICON_REACH ///Cooldown between salutations for commissioned bots COOLDOWN_DECLARE(next_salute_check) @@ -411,13 +412,9 @@ ui = new(user, src, "SimpleBot", name) ui.open() -/mob/living/simple_animal/bot/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return +/mob/living/simple_animal/bot/click_alt(mob/user) unlock_with_id(user) + return CLICK_ACTION_SUCCESS /mob/living/simple_animal/bot/proc/unlock_with_id(mob/user) if(bot_cover_flags & BOT_COVER_EMAGGED) diff --git a/code/modules/mob/living/simple_animal/bot/floorbot.dm b/code/modules/mob/living/simple_animal/bot/floorbot.dm index faa7d3a9edb..ae1c52d1652 100644 --- a/code/modules/mob/living/simple_animal/bot/floorbot.dm +++ b/code/modules/mob/living/simple_animal/bot/floorbot.dm @@ -140,7 +140,7 @@ return data // Actions received from TGUI -/mob/living/simple_animal/bot/floorbot/ui_act(action, params) +/mob/living/simple_animal/bot/floorbot/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(. || (bot_cover_flags & BOT_COVER_LOCKED && !HAS_SILICON_ACCESS(usr))) return @@ -161,7 +161,7 @@ tilestack.forceMove(drop_location()) if("line_mode") var/setdir = tgui_input_list(usr, "Select construction direction", "Direction", list("north", "east", "south", "west", "disable")) - if(isnull(setdir)) + if(isnull(setdir) || QDELETED(ui) || ui.status != UI_INTERACTIVE) return switch(setdir) if("north") diff --git a/code/modules/mob/logout.dm b/code/modules/mob/logout.dm index b8ceb33a37d..cf937e42bb7 100644 --- a/code/modules/mob/logout.dm +++ b/code/modules/mob/logout.dm @@ -2,7 +2,6 @@ SEND_SIGNAL(src, COMSIG_MOB_LOGOUT) log_message("[key_name(src)] is no longer owning mob [src]([src.type])", LOG_OWNERSHIP) SStgui.on_logout(src) - unset_machine() remove_from_player_list() ..() diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 6527205c2c7..4018207b6fa 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -30,7 +30,6 @@ else if(ckey) stack_trace("Mob without client but with associated ckey, [ckey], has been deleted.") - unset_machine() remove_from_mob_list() remove_from_dead_mob_list() remove_from_alive_mob_list() @@ -902,7 +901,6 @@ set name = "Cancel Camera View" set category = "OOC" reset_perspective(null) - unset_machine() //suppress the .click/dblclick macros so people can't use them to identify the location of items or aimbot /mob/verb/DisClick(argu = null as anything, sec = "" as text, number1 = 0 as num , number2 = 0 as num) @@ -1199,6 +1197,10 @@ * * FORBID_TELEKINESIS_REACH - If telekinesis is forbidden to perform action from a distance (ex. canisters are blacklisted from telekinesis manipulation) * * ALLOW_SILICON_REACH - If silicons are allowed to perform action from a distance (silicons can operate airlocks from far away) * * ALLOW_RESTING - If resting on the floor is allowed to perform action () + * * ALLOW_VENTCRAWL - Mobs with ventcrawl traits can alt-click this to vent + * + * silence_adjacency: Sometimes we want to use this proc to check interaction without allowing it to throw errors for base case adjacency + * Alt click uses this, as otherwise you can detect what is interactable from a distance via the error message **/ /mob/proc/can_perform_action(atom/movable/target, action_bitflags) return @@ -1616,9 +1618,6 @@ /mob/vv_edit_var(var_name, var_value) switch(var_name) - if(NAMEOF(src, machine)) - set_machine(var_value) - . = TRUE if(NAMEOF(src, focus)) set_focus(var_value) . = TRUE diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 44d0db90355..2a5be377ecd 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -79,9 +79,6 @@ var/computer_id = null var/list/logging = list() - /// The machine the mob is interacting with (this is very bad old code btw) - var/obj/machinery/machine = null - /// Tick time the mob can next move var/next_move = null diff --git a/code/modules/mod/mod_link.dm b/code/modules/mod/mod_link.dm index 634b1dd648f..c3d5040c815 100644 --- a/code/modules/mod/mod_link.dm +++ b/code/modules/mod/mod_link.dm @@ -191,6 +191,8 @@ /obj/item/clothing/neck/link_scryer/attack_self(mob/user, modifiers) var/new_label = reject_bad_text(tgui_input_text(user, "Change the visible name", "Set Name", label, MAX_NAME_LEN)) + if(!user.is_holding(src)) + return if(!new_label) balloon_alert(user, "invalid name!") return diff --git a/code/modules/modular_computers/computers/item/computer.dm b/code/modules/modular_computers/computers/item/computer.dm index c63441124ae..c7369724ee4 100644 --- a/code/modules/modular_computers/computers/item/computer.dm +++ b/code/modules/modular_computers/computers/item/computer.dm @@ -223,19 +223,18 @@ /obj/item/modular_computer/get_cell() return internal_cell -/obj/item/modular_computer/AltClick(mob/user) - . = ..() +/obj/item/modular_computer/click_alt(mob/user) if(issilicon(user)) - return FALSE - if(!user.can_perform_action(src)) - return FALSE + return NONE if(RemoveID(user)) - return TRUE + return CLICK_ACTION_SUCCESS if(istype(inserted_pai)) // Remove pAI remove_pai(user) - return TRUE + return CLICK_ACTION_SUCCESS + + return CLICK_ACTION_BLOCKING // Gets IDs/access levels from card slot. Would be useful when/if PDAs would become modular PCs. //guess what /obj/item/modular_computer/GetAccess() diff --git a/code/modules/modular_computers/computers/item/laptop.dm b/code/modules/modular_computers/computers/item/laptop.dm index 3bf46f16d1b..b55fb6d2ee6 100644 --- a/code/modules/modular_computers/computers/item/laptop.dm +++ b/code/modules/modular_computers/computers/item/laptop.dm @@ -91,14 +91,11 @@ toggle_open(user) -/obj/item/modular_computer/laptop/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(screen_on) // Close it. - try_toggle_open(user) - else - return ..() +/obj/item/modular_computer/laptop/click_alt(mob/user) + if(!screen_on) + return CLICK_ACTION_BLOCKING + try_toggle_open(user) // Close it. + return CLICK_ACTION_SUCCESS /obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null) if(screen_on) diff --git a/code/modules/modular_computers/computers/item/pda.dm b/code/modules/modular_computers/computers/item/pda.dm index 37ef56a0626..b6efa629d5e 100644 --- a/code/modules/modular_computers/computers/item/pda.dm +++ b/code/modules/modular_computers/computers/item/pda.dm @@ -161,12 +161,6 @@ inserted_item = attacking_item playsound(src, 'sound/machines/pda_button1.ogg', 50, TRUE) -/obj/item/modular_computer/pda/AltClick(mob/user) - . = ..() - if(.) - return - - remove_pen(user) /obj/item/modular_computer/pda/CtrlClick(mob/user) . = ..() diff --git a/code/modules/modular_computers/computers/machinery/modular_computer.dm b/code/modules/modular_computers/computers/machinery/modular_computer.dm index 0e17f012453..6f0050534cc 100644 --- a/code/modules/modular_computers/computers/machinery/modular_computer.dm +++ b/code/modules/modular_computers/computers/machinery/modular_computer.dm @@ -116,11 +116,11 @@ SIGNAL_HANDLER return update_icon(updates) -/obj/machinery/modular_computer/AltClick(mob/user) - . = ..() +/obj/machinery/modular_computer/click_alt(mob/user) if(CPU_INTERACTABLE(user) || !can_interact(user)) - return - cpu.AltClick(user) + return NONE + cpu.click_alt(user) + return CLICK_ACTION_SUCCESS //ATTACK HAND IGNORING PARENT RETURN VALUE // On-click handling. Turns on the computer if it's off and opens the GUI. diff --git a/code/modules/pai/door_jack.dm b/code/modules/pai/door_jack.dm index 182cdc10027..36220ecface 100644 --- a/code/modules/pai/door_jack.dm +++ b/code/modules/pai/door_jack.dm @@ -104,17 +104,17 @@ /mob/living/silicon/pai/proc/hack_door() if(!hacking_cable) return FALSE - if(!hacking_cable.machine) + if(!hacking_cable.hacking_machine) balloon_alert(src, "nothing connected") return FALSE playsound(src, 'sound/machines/airlock_alien_prying.ogg', 50, TRUE) balloon_alert(src, "overriding...") // Now begin hacking - if(!do_after(src, 15 SECONDS, hacking_cable.machine, timed_action_flags = NONE, progress = TRUE)) + if(!do_after(src, 15 SECONDS, hacking_cable.hacking_machine, timed_action_flags = NONE, progress = TRUE)) balloon_alert(src, "failed! retracting...") QDEL_NULL(hacking_cable) return FALSE - var/obj/machinery/door/door = hacking_cable.machine + var/obj/machinery/door/door = hacking_cable.hacking_machine balloon_alert(src, "success") door.open() QDEL_NULL(hacking_cable) diff --git a/code/modules/paperwork/carbonpaper.dm b/code/modules/paperwork/carbonpaper.dm index 1dfe4ea7821..9c8ec0b8640 100644 --- a/code/modules/paperwork/carbonpaper.dm +++ b/code/modules/paperwork/carbonpaper.dm @@ -20,11 +20,11 @@ return . += span_notice("Right-click to tear off the carbon-copy (you must use both hands).") -/obj/item/paper/carbon/AltClick(mob/living/user) +/obj/item/paper/carbon/click_alt(mob/living/user) if(!copied) to_chat(user, span_notice("Take off the carbon copy first.")) - return - return ..() + return CLICK_ACTION_BLOCKING + return CLICK_ACTION_SUCCESS /obj/item/paper/carbon/proc/removecopy(mob/living/user) if(copied) diff --git a/code/modules/paperwork/clipboard.dm b/code/modules/paperwork/clipboard.dm index 7285aa3172d..8b475ed6324 100644 --- a/code/modules/paperwork/clipboard.dm +++ b/code/modules/paperwork/clipboard.dm @@ -71,13 +71,16 @@ pen = null update_icon() -/obj/item/clipboard/AltClick(mob/user) - ..() - if(pen) - if(integrated_pen) - to_chat(user, span_warning("You can't seem to find a way to remove [src]'s [pen].")) - else - remove_pen(user) +/obj/item/clipboard/click_alt(mob/user) + if(isnull(pen)) + return CLICK_ACTION_BLOCKING + + if(integrated_pen) + to_chat(user, span_warning("You can't seem to find a way to remove [src]'s [pen].")) + return CLICK_ACTION_BLOCKING + + remove_pen(user) + return CLICK_ACTION_SUCCESS /obj/item/clipboard/update_overlays() . = ..() diff --git a/code/modules/paperwork/handlabeler.dm b/code/modules/paperwork/handlabeler.dm index e4ac04a22b6..d89d74d7f8e 100644 --- a/code/modules/paperwork/handlabeler.dm +++ b/code/modules/paperwork/handlabeler.dm @@ -107,12 +107,9 @@ to_chat(user, span_notice("You turn off [src].")) return TRUE -/obj/item/hand_labeler/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(. & ITEM_INTERACT_ANY_BLOCKER) - return . +/obj/item/hand_labeler/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!istype(tool, /obj/item/hand_labeler_refill)) - return . + return NONE balloon_alert(user, "refilled") qdel(tool) diff --git a/code/modules/paperwork/paper.dm b/code/modules/paperwork/paper.dm index 5aac6d93bc3..e1bddd6feff 100644 --- a/code/modules/paperwork/paper.dm +++ b/code/modules/paperwork/paper.dm @@ -30,6 +30,7 @@ grind_results = list(/datum/reagent/cellulose = 3) color = COLOR_WHITE item_flags = SKIP_FANTASY_ON_SPAWN + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS /// Lazylist of raw, unsanitised, unparsed text inputs that have been made to the paper. var/list/datum/paper_input/raw_text_inputs @@ -359,13 +360,13 @@ return TRUE return ..() -/obj/item/paper/AltClick(mob/living/user) - . = ..() - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/paper/click_alt(mob/living/user) if(HAS_TRAIT(user, TRAIT_PAPER_MASTER)) - return make_plane(user, /obj/item/paperplane/syndicate) - return make_plane(user, /obj/item/paperplane) + make_plane(user, /obj/item/paperplane/syndicate) + return CLICK_ACTION_SUCCESS + make_plane(user, /obj/item/paperplane) + return CLICK_ACTION_SUCCESS + /** @@ -374,9 +375,9 @@ * * Arguments: * * mob/living/user - who's folding - * * obj/item/paperplane/plane_type - what it will be folded into (path) + * * plane_type - what it will be folded into (path) */ -/obj/item/paper/proc/make_plane(mob/living/user, obj/item/paperplane/plane_type = /obj/item/paperplane) +/obj/item/paper/proc/make_plane(mob/living/user, plane_type = /obj/item/paperplane) balloon_alert(user, "folded into a plane") user.temporarilyRemoveItemFromInventory(src) var/obj/item/paperplane/new_plane = new plane_type(loc, src) diff --git a/code/modules/paperwork/paper_cutter.dm b/code/modules/paperwork/paper_cutter.dm index ae93a1daad8..8e4fedf2fda 100644 --- a/code/modules/paperwork/paper_cutter.dm +++ b/code/modules/paperwork/paper_cutter.dm @@ -141,16 +141,14 @@ return ..() -/obj/item/papercutter/AltClick(mob/user) - if(!user.Adjacent(src)) - return ..() - +/obj/item/papercutter/click_alt(mob/user) // can only remove one at a time; paper goes first, as its most likely what players will want to be taking out if(!isnull(stored_paper)) user.put_in_hands(stored_paper) else if(!isnull(stored_blade) && !blade_secured) user.put_in_hands(stored_blade) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/papercutter/attack_hand_secondary(mob/user, list/modifiers) if(!stored_blade) diff --git a/code/modules/paperwork/pen.dm b/code/modules/paperwork/pen.dm index 2a9e78e2489..ec71eda2e46 100644 --- a/code/modules/paperwork/pen.dm +++ b/code/modules/paperwork/pen.dm @@ -44,6 +44,7 @@ dart_insert_projectile_icon_state, \ CALLBACK(src, PROC_REF(get_dart_var_modifiers))\ ) + AddElement(/datum/element/tool_renaming) RegisterSignal(src, COMSIG_DART_INSERT_ADDED, PROC_REF(on_inserted_into_dart)) RegisterSignal(src, COMSIG_DART_INSERT_REMOVED, PROC_REF(on_removed_from_dart)) @@ -179,6 +180,7 @@ if(current_skin) desc = "It's an expensive [current_skin] fountain pen. The nib is quite sharp." + /obj/item/pen/fountain/captain/proc/reskin_dart_insert(datum/component/dart_insert/insert_comp) if(!istype(insert_comp)) //You really shouldn't be sending this signal from anything other than a dart_insert component return @@ -209,54 +211,6 @@ log_combat(user, M, "stabbed", src) return TRUE -/obj/item/pen/afterattack(obj/O, mob/living/user, proximity) - . = ..() - - if (!proximity) - return . - - . |= AFTERATTACK_PROCESSED_ITEM - - //Changing name/description of items. Only works if they have the UNIQUE_RENAME object flag set - if(isobj(O) && (O.obj_flags & UNIQUE_RENAME)) - var/penchoice = tgui_input_list(user, "What would you like to edit?", "Pen Setting", list("Rename", "Description", "Reset")) - if(QDELETED(O) || !user.can_perform_action(O)) - return - if(penchoice == "Rename") - var/input = tgui_input_text(user, "What do you want to name [O]?", "Object Name", "[O.name]", MAX_NAME_LEN) - var/oldname = O.name - if(QDELETED(O) || !user.can_perform_action(O)) - return - if(input == oldname || !input) - to_chat(user, span_notice("You changed [O] to... well... [O].")) - else - O.AddComponent(/datum/component/rename, input, O.desc) - to_chat(user, span_notice("You have successfully renamed \the [oldname] to [O].")) - ADD_TRAIT(O, TRAIT_WAS_RENAMED, PEN_LABEL_TRAIT) - O.update_appearance(UPDATE_ICON) - - if(penchoice == "Description") - var/input = tgui_input_text(user, "Describe [O]", "Description", "[O.desc]", 280) - var/olddesc = O.desc - if(QDELETED(O) || !user.can_perform_action(O)) - return - if(input == olddesc || !input) - to_chat(user, span_notice("You decide against changing [O]'s description.")) - else - O.AddComponent(/datum/component/rename, O.name, input) - to_chat(user, span_notice("You have successfully changed [O]'s description.")) - ADD_TRAIT(O, TRAIT_WAS_RENAMED, PEN_LABEL_TRAIT) - O.update_appearance(UPDATE_ICON) - - if(penchoice == "Reset") - if(QDELETED(O) || !user.can_perform_action(O)) - return - - qdel(O.GetComponent(/datum/component/rename)) - to_chat(user, span_notice("You have successfully reset [O]'s name and description.")) - REMOVE_TRAIT(O, TRAIT_WAS_RENAMED, PEN_LABEL_TRAIT) - O.update_appearance(UPDATE_ICON) - /obj/item/pen/get_writing_implement_details() return list( interaction_mode = MODE_WRITING, diff --git a/code/modules/photography/camera/camera.dm b/code/modules/photography/camera/camera.dm index a0a51da165f..35462a24d86 100644 --- a/code/modules/photography/camera/camera.dm +++ b/code/modules/photography/camera/camera.dm @@ -72,10 +72,9 @@ picture_size_y = min(clamp(desired_y, picture_size_y_min, picture_size_y_max), CAMERA_PICTURE_SIZE_HARD_LIMIT) return TRUE -/obj/item/camera/AltClick(mob/user) - if(!user.can_perform_action(src)) - return +/obj/item/camera/click_alt(mob/user) adjust_zoom(user) + return CLICK_ACTION_SUCCESS /obj/item/camera/attack(mob/living/carbon/human/M, mob/user) return @@ -251,11 +250,11 @@ to_chat(user, span_notice("[pictures_left] photos left.")) if(can_customise) - var/customise = tgui_alert(user, "Do you want to customize the photo?", "Customization", list("Yes", "No")) + var/customise = user.is_holding(new_photo) && tgui_alert(user, "Do you want to customize the photo?", "Customization", list("Yes", "No")) if(customise == "Yes") - var/name1 = tgui_input_text(user, "Set a name for this photo, or leave blank.", "Name", max_length = 32) - var/desc1 = tgui_input_text(user, "Set a description to add to photo, or leave blank.", "Description", max_length = 128) - var/caption = tgui_input_text(user, "Set a caption for this photo, or leave blank.", "Caption", max_length = 256) + var/name1 = user.is_holding(new_photo) && tgui_input_text(user, "Set a name for this photo, or leave blank.", "Name", max_length = 32) + var/desc1 = user.is_holding(new_photo) && tgui_input_text(user, "Set a description to add to photo, or leave blank.", "Description", max_length = 128) + var/caption = user.is_holding(new_photo) && tgui_input_text(user, "Set a caption for this photo, or leave blank.", "Caption", max_length = 256) if(name1) picture.picture_name = name1 if(desc1) diff --git a/code/modules/plumbing/ducts.dm b/code/modules/plumbing/ducts.dm index 33073ae910f..a7990f65ce5 100644 --- a/code/modules/plumbing/ducts.dm +++ b/code/modules/plumbing/ducts.dm @@ -343,9 +343,13 @@ All the important duct code: /obj/item/stack/ducts/attack_self(mob/user) var/new_layer = tgui_input_list(user, "Select a layer", "Layer", GLOB.plumbing_layers, duct_layer) + if(!user.is_holding(src)) + return if(new_layer) duct_layer = new_layer var/new_color = tgui_input_list(user, "Select a color", "Color", GLOB.pipe_paint_colors, duct_color) + if(!user.is_holding(src)) + return if(new_color) duct_color = new_color add_atom_colour(GLOB.pipe_paint_colors[new_color], FIXED_COLOUR_PRIORITY) diff --git a/code/modules/plumbing/plumbers/_plumb_machinery.dm b/code/modules/plumbing/plumbers/_plumb_machinery.dm index 132d882dbee..8baf59508b8 100644 --- a/code/modules/plumbing/plumbers/_plumb_machinery.dm +++ b/code/modules/plumbing/plumbers/_plumb_machinery.dm @@ -26,8 +26,6 @@ . = ..() . += span_notice("The maximum volume display reads: [reagents.maximum_volume] units.") -/obj/machinery/plumbing/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/machinery/plumbing/wrench_act(mob/living/user, obj/item/tool) . = ..() diff --git a/code/modules/plumbing/plumbers/filter.dm b/code/modules/plumbing/plumbers/filter.dm index 633f70830f0..c4df35164f0 100644 --- a/code/modules/plumbing/plumbers/filter.dm +++ b/code/modules/plumbing/plumbers/filter.dm @@ -30,7 +30,7 @@ data["right"] = english_right return data -/obj/machinery/plumbing/filter/ui_act(action, params) +/obj/machinery/plumbing/filter/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) . = ..() if(.) return @@ -42,6 +42,8 @@ var/selected_reagent = tgui_input_list(usr, "Select [which] reagent", "Reagent", GLOB.name2reagent) if(!selected_reagent) return TRUE + if(QDELETED(ui) || ui.status != UI_INTERACTIVE) + return FALSE var/datum/reagent/chem_id = GLOB.name2reagent[selected_reagent] if(!chem_id) @@ -69,5 +71,3 @@ if(english_right.Find(chem_name)) english_right -= chem_name right -= chem_id - - diff --git a/code/modules/plumbing/plumbers/iv_drip.dm b/code/modules/plumbing/plumbers/iv_drip.dm index 7ed187929df..f9a5ae47598 100644 --- a/code/modules/plumbing/plumbers/iv_drip.dm +++ b/code/modules/plumbing/plumbers/iv_drip.dm @@ -29,8 +29,6 @@ reagents.expose(get_turf(src), TOUCH) //splash on the floor reagents.clear_reagents() -/obj/machinery/iv_drip/plumbing/can_use_alt_click(mob/user) - return FALSE //Alt click is used for rotation /obj/machinery/iv_drip/plumbing/wrench_act(mob/living/user, obj/item/tool) if(default_unfasten_wrench(user, tool) == SUCCESSFUL_UNFASTEN) diff --git a/code/modules/plumbing/plumbers/reaction_chamber.dm b/code/modules/plumbing/plumbers/reaction_chamber.dm index 72caa603767..9bdac2f98b2 100644 --- a/code/modules/plumbing/plumbers/reaction_chamber.dm +++ b/code/modules/plumbing/plumbers/reaction_chamber.dm @@ -107,6 +107,8 @@ var/selected_reagent = tgui_input_list(ui.user, "Select reagent", "Reagent", GLOB.name2reagent) if(!selected_reagent) return FALSE + if(QDELETED(ui) || ui.status != UI_INTERACTIVE) + return FALSE var/datum/reagent/input_reagent = GLOB.name2reagent[selected_reagent] if(!input_reagent) diff --git a/code/modules/power/apc/apc_tool_act.dm b/code/modules/power/apc/apc_tool_act.dm index 00d3fc7a538..6a8835cd5b7 100644 --- a/code/modules/power/apc/apc_tool_act.dm +++ b/code/modules/power/apc/apc_tool_act.dm @@ -1,10 +1,7 @@ //attack with an item - open/close cover, insert cell, or (un)lock interface -/obj/machinery/power/apc/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - . = ..() - if(.) - return . - +/obj/machinery/power/apc/item_interaction(mob/living/user, obj/item/tool, list/modifiers) + . = NONE if(HAS_TRAIT(tool, TRAIT_APC_SHOCKING)) . = fork_outlet_act(user, tool) if(.) @@ -17,7 +14,7 @@ if(istype(tool, /obj/item/stock_parts/cell)) . = cell_act(user, tool) else if(istype(tool, /obj/item/stack/cable_coil)) - . = cable_act(user, tool, is_right_clicking) + . = cable_act(user, tool, LAZYACCESS(modifiers, RIGHT_CLICK)) else if(istype(tool, /obj/item/electronics/apc)) . = electronics_act(user, tool) else if(istype(tool, /obj/item/electroadaptive_pseudocircuit)) @@ -26,7 +23,7 @@ . = wallframe_act(user, tool) if(.) return . - + if(panel_open && !opened && is_wire_tool(tool)) wires.interact(user) return ITEM_INTERACT_SUCCESS @@ -73,44 +70,59 @@ update_appearance() return ITEM_INTERACT_SUCCESS +/// Checks if we can place a terminal on the APC +/obj/machinery/power/apc/proc/can_place_terminal(mob/living/user, obj/item/stack/cable_coil/installing_cable, silent = TRUE) + if(!opened) + return FALSE + var/turf/host_turf = get_turf(src) + if(host_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) + if(!silent && user) + balloon_alert(user, "remove the floor plating!") + return FALSE + if(!isnull(terminal)) + if(!silent && user) + balloon_alert(user, "already wired!") + return FALSE + if(!has_electronics) + if(!silent && user) + balloon_alert(user, "no board to wire!") + return FALSE + if(panel_open) + if(!silent && user) + balloon_alert(user, "wires prevent placing a terminal!") + return FALSE + if(installing_cable.get_amount() < 10) + if(!silent && user) + balloon_alert(user, "need ten lengths of cable!") + return FALSE + return TRUE + /// Called when we interact with the APC with a cable, attempts to wire the APC and create a terminal /obj/machinery/power/apc/proc/cable_act(mob/living/user, obj/item/stack/cable_coil/installing_cable, is_right_clicking) if(!opened) return NONE - - var/turf/host_turf = get_turf(src) - if(!host_turf) - CRASH("cable_act on APC when it's not on a turf") - if(host_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) - balloon_alert(user, "remove the floor plating!") - return ITEM_INTERACT_BLOCKING - if(terminal) - balloon_alert(user, "already wired!") - return ITEM_INTERACT_BLOCKING - if(!has_electronics) - balloon_alert(user, "no board to wire!") - return ITEM_INTERACT_BLOCKING - - if(installing_cable.get_amount() < 10) - balloon_alert(user, "need ten lengths of cable!") + if(!can_place_terminal(user, installing_cable, silent = FALSE)) return ITEM_INTERACT_BLOCKING var/terminal_cable_layer = cable_layer // Default to machine's cable layer if(is_right_clicking) var/choice = tgui_input_list(user, "Select Power Input Cable Layer", "Select Cable Layer", GLOB.cable_name_to_layer) - if(isnull(choice)) + if(isnull(choice) \ + || !user.is_holding(installing_cable) \ + || !user.Adjacent(src) \ + || user.incapacitated() \ + || !can_place_terminal(user, installing_cable, silent = TRUE) \ + ) return ITEM_INTERACT_BLOCKING terminal_cable_layer = GLOB.cable_name_to_layer[choice] - user.visible_message(span_notice("[user.name] adds cables to the APC frame.")) - balloon_alert(user, "adding cables to the frame...") - playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE) + user.visible_message(span_notice("[user.name] starts addding cables to the APC frame.")) + balloon_alert(user, "adding cables...") + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) if(!do_after(user, 2 SECONDS, target = src)) return ITEM_INTERACT_BLOCKING - if(installing_cable.get_amount() < 10 || !installing_cable) - return ITEM_INTERACT_BLOCKING - if(terminal || !opened || !has_electronics) + if(!can_place_terminal(user, installing_cable, silent = TRUE)) return ITEM_INTERACT_BLOCKING var/turf/our_turf = get_turf(src) var/obj/structure/cable/cable_node = our_turf.get_cable_node(terminal_cable_layer) @@ -118,7 +130,8 @@ do_sparks(5, TRUE, src) return ITEM_INTERACT_BLOCKING installing_cable.use(10) - balloon_alert(user, "cables added to the frame") + user.visible_message(span_notice("[user.name] adds cables to the APC frame.")) + balloon_alert(user, "cables added") make_terminal(terminal_cable_layer) terminal.connect_to_network() return ITEM_INTERACT_SUCCESS @@ -127,7 +140,7 @@ /obj/machinery/power/apc/proc/electronics_act(mob/living/user, obj/item/electronics/apc/installing_board) if(!opened) return NONE - + if(has_electronics) balloon_alert(user, "there is already a board!") return ITEM_INTERACT_BLOCKING diff --git a/code/modules/power/pipecleaners.dm b/code/modules/power/pipecleaners.dm index 2a41c70bdfe..7f1ef8fc2e3 100644 --- a/code/modules/power/pipecleaners.dm +++ b/code/modules/power/pipecleaners.dm @@ -163,10 +163,9 @@ By design, d1 is the smallest direction and d2 is the highest stored.color = colorC stored.update_appearance() -/obj/structure/pipe_cleaner/AltClick(mob/living/user) - if(!user.can_perform_action(src)) - return +/obj/structure/pipe_cleaner/click_alt(mob/living/user) cut_pipe_cleaner(user) + return CLICK_ACTION_SUCCESS /////////////////////////////////////////////// // The pipe cleaner coil object, used for laying pipe cleaner diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index a360fc12145..38f18d7d627 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -346,8 +346,6 @@ return return ..() -/obj/machinery/power/emitter/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/machinery/power/emitter/proc/integrate(obj/item/gun/energy/energy_gun, mob/user) if(!istype(energy_gun, /obj/item/gun/energy)) diff --git a/code/modules/power/smes.dm b/code/modules/power/smes.dm index c6fa9274cb3..2aa5247451d 100644 --- a/code/modules/power/smes.dm +++ b/code/modules/power/smes.dm @@ -112,55 +112,42 @@ //building and linking a terminal if(istype(item, /obj/item/stack/cable_coil)) - var/dir = get_dir(user,src) - if(dir & (dir-1))//we don't want diagonal click - return - - if(terminal) //is there already a terminal ? - to_chat(user, span_warning("This SMES already has a power terminal!")) - return - - if(!panel_open) //is the panel open ? - to_chat(user, span_warning("You must open the maintenance panel first!")) - return - - var/turf/turf = get_turf(user) - if (turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) //can we get to the underfloor? - to_chat(user, span_warning("You must first remove the floor plating!")) - return - - - var/obj/item/stack/cable_coil/cable = item - if(cable.get_amount() < 10) - to_chat(user, span_warning("You need more wires!")) + if(!can_place_terminal(user, item, silent = FALSE)) return var/terminal_cable_layer if(LAZYACCESS(params2list(params), RIGHT_CLICK)) var/choice = tgui_input_list(user, "Select Power Input Cable Layer", "Select Cable Layer", GLOB.cable_name_to_layer) - if(isnull(choice)) + if(isnull(choice) \ + || !user.is_holding(item) \ + || !user.Adjacent(src) \ + || user.incapacitated() \ + || !can_place_terminal(user, item, silent = TRUE) \ + ) return terminal_cable_layer = GLOB.cable_name_to_layer[choice] - to_chat(user, span_notice("You start building the power terminal...")) - playsound(src.loc, 'sound/items/deconstruct.ogg', 50, TRUE) + user.visible_message(span_notice("[user.name] starts adding cables to [src].")) + balloon_alert(user, "adding cables...") + playsound(src, 'sound/items/deconstruct.ogg', 50, TRUE) - if(do_after(user, 2 SECONDS, target = src)) - if(cable.get_amount() < 10 || !cable) - return - var/obj/structure/cable/connected_cable = turf.get_cable_node(terminal_cable_layer) //get the connecting node cable, if there's one - if (prob(50) && electrocute_mob(user, connected_cable, connected_cable, 1, TRUE)) //animate the electrocution if uncautious and unlucky - do_sparks(5, TRUE, src) - return - if(!terminal) - cable.use(10) - user.visible_message(span_notice("[user.name] builds a power terminal."),\ - span_notice("You build the power terminal.")) - - //build the terminal and link it to the network - make_terminal(turf, terminal_cable_layer) - terminal.connect_to_network() - connect_to_network() + if(!do_after(user, 2 SECONDS, target = src)) + return + if(!can_place_terminal(user, item, silent = TRUE)) + return + var/obj/item/stack/cable_coil/cable = item + var/turf/turf = get_turf(user) + var/obj/structure/cable/connected_cable = turf.get_cable_node(terminal_cable_layer) //get the connecting node cable, if there's one + if (prob(50) && electrocute_mob(user, connected_cable, connected_cable, 1, TRUE)) //animate the electrocution if uncautious and unlucky + do_sparks(5, TRUE, src) + return + cable.use(10) + user.visible_message(span_notice("[user.name] adds cables to [src]")) + balloon_alert(user, "cables added") + //build the terminal and link it to the network + make_terminal(turf, terminal_cable_layer) + terminal.connect_to_network() + connect_to_network() return //crowbarring it ! @@ -175,6 +162,31 @@ return ..() +/// Checks if we're in a valid state to place a terminal +/obj/machinery/power/smes/proc/can_place_terminal(mob/living/user, obj/item/stack/cable_coil/installing_cable, silent = TRUE) + var/set_dir = get_dir(user, src) + if(set_dir & (set_dir - 1))//we don't want diagonal click + return FALSE + + var/turf/terminal_turf = get_turf(user) + if(!panel_open) + if(!silent && user) + balloon_alert(user, "open the maintenance panel!") + return FALSE + if(terminal_turf.underfloor_accessibility < UNDERFLOOR_INTERACTABLE) + if(!silent && user) + balloon_alert(user, "remove the floor plating!") + return FALSE + if(terminal) + if(!silent && user) + balloon_alert(user, "already wired!") + return FALSE + if(installing_cable.get_amount() < 10) + if(!silent && user) + balloon_alert(user, "need ten lengths of cable!") + return FALSE + return TRUE + /obj/machinery/power/smes/wirecutter_act(mob/living/user, obj/item/item) //disassembling the terminal . = ..() diff --git a/code/modules/projectiles/guns/ballistic.dm b/code/modules/projectiles/guns/ballistic.dm index 456577d3981..b206e1977f4 100644 --- a/code/modules/projectiles/guns/ballistic.dm +++ b/code/modules/projectiles/guns/ballistic.dm @@ -496,18 +496,16 @@ update_weight_class(w_class - I.w_class) return ..() -/obj/item/gun/ballistic/AltClick(mob/user) - if (unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - return - if(loc == user) - if(suppressed && can_unsuppress) - var/obj/item/suppressor/S = suppressed - if(!user.is_holding(src)) - return ..() - balloon_alert(user, "[S.name] removed") - user.put_in_hands(S) - clear_suppressor() +/obj/item/gun/ballistic/click_alt(mob/user) + if(!suppressed || !can_unsuppress) + return CLICK_ACTION_BLOCKING + var/obj/item/suppressor/S = suppressed + if(!user.is_holding(src)) + return CLICK_ACTION_BLOCKING + balloon_alert(user, "[S.name] removed") + user.put_in_hands(S) + clear_suppressor() + return CLICK_ACTION_SUCCESS ///Prefire empty checks for the bolt drop /obj/item/gun/ballistic/proc/prefire_empty_checks() diff --git a/code/modules/projectiles/guns/ballistic/automatic.dm b/code/modules/projectiles/guns/ballistic/automatic.dm index 3d6940692d8..8c6e2ae7cbd 100644 --- a/code/modules/projectiles/guns/ballistic/automatic.dm +++ b/code/modules/projectiles/guns/ballistic/automatic.dm @@ -260,13 +260,12 @@ . += span_notice("It seems like you could use an empty hand to remove the magazine.") -/obj/item/gun/ballistic/automatic/l6_saw/AltClick(mob/user) - if(!user.can_perform_action(src)) - return +/obj/item/gun/ballistic/automatic/l6_saw/click_alt(mob/user) cover_open = !cover_open balloon_alert(user, "cover [cover_open ? "opened" : "closed"]") playsound(src, 'sound/weapons/gun/l6/l6_door.ogg', 60, TRUE) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/gun/ballistic/automatic/l6_saw/update_icon_state() . = ..() diff --git a/code/modules/projectiles/guns/ballistic/bows/_bow.dm b/code/modules/projectiles/guns/ballistic/bows/_bow.dm index c356d5b266c..86094d0fe17 100644 --- a/code/modules/projectiles/guns/ballistic/bows/_bow.dm +++ b/code/modules/projectiles/guns/ballistic/bows/_bow.dm @@ -29,13 +29,14 @@ . = ..() icon_state = chambered ? "[base_icon_state]_[drawn ? "drawn" : "nocked"]" : "[base_icon_state]" -/obj/item/gun/ballistic/bow/AltClick(mob/user) +/obj/item/gun/ballistic/bow/click_alt(mob/user) if(isnull(chambered)) - return ..() + return CLICK_ACTION_BLOCKING user.put_in_hands(chambered) chambered = magazine.get_round() update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/gun/ballistic/bow/proc/drop_arrow() chambered.forceMove(drop_location()) diff --git a/code/modules/projectiles/guns/ballistic/revolver.dm b/code/modules/projectiles/guns/ballistic/revolver.dm index 373607e53dd..e142616cbd4 100644 --- a/code/modules/projectiles/guns/ballistic/revolver.dm +++ b/code/modules/projectiles/guns/ballistic/revolver.dm @@ -37,9 +37,9 @@ ..() chamber_round() -/obj/item/gun/ballistic/revolver/AltClick(mob/user) - ..() +/obj/item/gun/ballistic/revolver/click_alt(mob/user) spin() + return CLICK_ACTION_SUCCESS /obj/item/gun/ballistic/revolver/fire_sounds() var/frequency_to_use = sin((90/magazine?.max_ammo) * get_ammo(TRUE, FALSE)) // fucking REVOLVERS diff --git a/code/modules/projectiles/guns/ballistic/shotgun.dm b/code/modules/projectiles/guns/ballistic/shotgun.dm index 37990971138..c584359b8ce 100644 --- a/code/modules/projectiles/guns/ballistic/shotgun.dm +++ b/code/modules/projectiles/guns/ballistic/shotgun.dm @@ -85,6 +85,7 @@ w_class = WEIGHT_CLASS_HUGE semi_auto = TRUE accepted_magazine_type = /obj/item/ammo_box/magazine/internal/shot/tube + interaction_flags_click = NEED_DEXTERITY|NEED_HANDS /// If defined, the secondary tube is this type, if you want different shell loads var/alt_mag_type /// If TRUE, we're drawing from the alternate_magazine @@ -131,10 +132,9 @@ else balloon_alert(user, "switched to tube A") -/obj/item/gun/ballistic/shotgun/automatic/dual_tube/AltClick(mob/living/user) - if(!user.can_perform_action(src, NEED_DEXTERITY|NEED_HANDS)) - return +/obj/item/gun/ballistic/shotgun/automatic/dual_tube/click_alt(mob/living/user) rack() + return CLICK_ACTION_SUCCESS // Bulldog shotgun // @@ -288,10 +288,6 @@ can_be_sawn_off = TRUE pb_knockback = 3 // it's a super shotgun! -/obj/item/gun/ballistic/shotgun/doublebarrel/AltClick(mob/user) - . = ..() - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) /obj/item/gun/ballistic/shotgun/doublebarrel/sawoff(mob/user) . = ..() diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 7850dd59f46..1d53eff0f9b 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -1204,7 +1204,6 @@ var/turf/startloc = get_turf(src) var/obj/projectile/bullet = new projectile_type(startloc) bullet.starting = startloc - var/list/ignore = list() for (var/atom/thing as anything in ignore_targets) bullet.impacted[WEAKREF(thing)] = TRUE bullet.firer = firer || src diff --git a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm index 80afcc534b0..6984c042f55 100644 --- a/code/modules/reagents/chemistry/machinery/chem_dispenser.dm +++ b/code/modules/reagents/chemistry/machinery/chem_dispenser.dm @@ -458,7 +458,7 @@ return ITEM_INTERACT_SUCCESS return ITEM_INTERACT_BLOCKING -/obj/machinery/chem_dispenser/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/chem_dispenser/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(is_reagent_container(tool) && !(tool.item_flags & ABSTRACT) && tool.is_open_container()) //SKYRAT EDIT ADDITION START - CHEMISTRY QOL var/obj/item/reagent_containers/container = tool @@ -467,12 +467,12 @@ transferAmounts = container.possible_transfer_amounts //SKYRAT EDIT ADDITION END if(!user.transferItemToLoc(tool, src)) - return ..() + return ITEM_INTERACT_BLOCKING replace_beaker(user, tool) ui_interact(user) return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/chem_dispenser/get_cell() return cell @@ -569,8 +569,6 @@ /obj/machinery/chem_dispenser/attack_ai_secondary(mob/user, list/modifiers) return attack_hand_secondary(user, modifiers) -/obj/machinery/chem_dispenser/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/machinery/chem_dispenser/drinks name = "soda dispenser" diff --git a/code/modules/reagents/chemistry/machinery/chem_heater.dm b/code/modules/reagents/chemistry/machinery/chem_heater.dm index 857cd5bd221..76b2dbf7f96 100644 --- a/code/modules/reagents/chemistry/machinery/chem_heater.dm +++ b/code/modules/reagents/chemistry/machinery/chem_heater.dm @@ -94,9 +94,9 @@ heater_coefficient *= micro_laser.tier -/obj/machinery/chem_heater/item_interaction(mob/living/user, obj/item/held_item, list/modifiers, is_right_clicking) +/obj/machinery/chem_heater/item_interaction(mob/living/user, obj/item/held_item, list/modifiers) if((held_item.item_flags & ABSTRACT) || (held_item.flags_1 & HOLOGRAM_1)) - return ..() + return NONE if(QDELETED(beaker)) if(istype(held_item, /obj/item/reagent_containers/dropper) || istype(held_item, /obj/item/reagent_containers/syringe)) @@ -110,7 +110,7 @@ balloon_alert(user, "beaker added") return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/chem_heater/wrench_act(mob/living/user, obj/item/tool) . = ITEM_INTERACT_BLOCKING diff --git a/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm b/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm index 298fe259814..ca75736db03 100644 --- a/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm +++ b/code/modules/reagents/chemistry/machinery/chem_mass_spec.dm @@ -147,9 +147,9 @@ for(var/datum/stock_part/micro_laser/laser in component_parts) cms_coefficient /= laser.tier -/obj/machinery/chem_mass_spec/item_interaction(mob/living/user, obj/item/item, list/modifiers, is_right_clicking) +/obj/machinery/chem_mass_spec/item_interaction(mob/living/user, obj/item/item, list/modifiers) if((item.item_flags & ABSTRACT) || (item.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return ..() + return NONE if(is_reagent_container(item) && item.is_open_container()) if(processing_reagents) @@ -160,13 +160,14 @@ if(!user.transferItemToLoc(beaker, src)) return ITEM_INTERACT_BLOCKING + var/is_right_clicking = LAZYACCESS(modifiers, RIGHT_CLICK) replace_beaker(user, !is_right_clicking, beaker) to_chat(user, span_notice("You add [beaker] to [is_right_clicking ? "output" : "input"] slot.")) update_appearance() ui_interact(user) return ITEM_INTERACT_SUCCESS - return ..() + return NONE /obj/machinery/chem_mass_spec/wrench_act(mob/living/user, obj/item/tool) . = ITEM_INTERACT_BLOCKING @@ -433,14 +434,12 @@ replace_beaker(ui.user, FALSE) return TRUE -/obj/machinery/chem_mass_spec/AltClick(mob/living/user) - . = ..() - if(!can_interact(user)) - return +/obj/machinery/chem_mass_spec/click_alt(mob/living/user) if(processing_reagents) balloon_alert(user, "still processing!") - return ..() + return CLICK_ACTION_BLOCKING replace_beaker(user, TRUE) + return CLICK_ACTION_SUCCESS /obj/machinery/chem_mass_spec/alt_click_secondary(mob/living/user) . = ..() diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm index fa3448c392f..9f210e12ee4 100644 --- a/code/modules/reagents/chemistry/machinery/chem_master.dm +++ b/code/modules/reagents/chemistry/machinery/chem_master.dm @@ -174,9 +174,9 @@ ) return containers -/obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/chem_master/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(user.combat_mode || (tool.item_flags & ABSTRACT) || (tool.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH | FORBID_TELEKINESIS_REACH)) - return ..() + return NONE if(is_reagent_container(tool) && tool.is_open_container()) replace_beaker(user, tool) @@ -186,7 +186,7 @@ else return ITEM_INTERACT_BLOCKING - return ..() + return NONE /obj/machinery/chem_master/wrench_act(mob/living/user, obj/item/tool) if(user.combat_mode) @@ -406,7 +406,7 @@ return FALSE //use energy - if(!use_energy(active_power_usage)) + if(!use_energy(active_power_usage, force = FALSE)) return FALSE //do the operation @@ -518,7 +518,9 @@ return //use power - if(!use_energy(active_power_usage)) + if(!use_energy(active_power_usage, force = FALSE)) + is_printing = FALSE + update_appearance(UPDATE_OVERLAYS) return //print the stuff diff --git a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm index a6113d2f0c6..25a7eecbd37 100644 --- a/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm +++ b/code/modules/reagents/chemistry/machinery/portable_chem_mixer.dm @@ -9,6 +9,7 @@ slot_flags = ITEM_SLOT_BELT custom_price = PAYCHECK_CREW * 10 custom_premium_price = PAYCHECK_CREW * 14 + interaction_flags_click = FORBID_TELEKINESIS_REACH ///Creating an empty slot for a beaker that can be added to dispense into var/obj/item/reagent_containers/beaker @@ -105,14 +106,14 @@ /obj/item/storage/portable_chem_mixer/ex_act(severity, target) return severity > EXPLODE_LIGHT ? ..() : FALSE -/obj/item/storage/portable_chem_mixer/item_interaction(mob/living/user, obj/item/weapon, list/modifiers, is_right_clicking) +/obj/item/storage/portable_chem_mixer/item_interaction(mob/living/user, obj/item/weapon, list/modifiers) if (!atom_storage.locked || \ (weapon.item_flags & ABSTRACT) || \ (weapon.flags_1 & HOLOGRAM_1) || \ !is_reagent_container(weapon) || \ !weapon.is_open_container() \ ) - return ..() + return NONE replace_beaker(user, weapon) update_appearance() @@ -250,15 +251,14 @@ var/atom/movable/screen/inventory/hand/H = over_object M.putItemFromInventoryInHandIfPossible(src, H.held_index) -/obj/item/storage/portable_chem_mixer/AltClick(mob/living/user) +/obj/item/storage/portable_chem_mixer/click_alt(mob/living/user) if(!atom_storage.locked) balloon_alert(user, "lock first to use alt eject!") - return ..() - if(!can_interact(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return + return CLICK_ACTION_BLOCKING replace_beaker(user) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/storage/portable_chem_mixer/CtrlClick(mob/living/user) if(atom_storage.locked == STORAGE_FULLY_LOCKED) diff --git a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm index f02d0d47deb..e206ffebbc9 100644 --- a/code/modules/reagents/chemistry/machinery/reagentgrinder.dm +++ b/code/modules/reagents/chemistry/machinery/reagentgrinder.dm @@ -211,9 +211,9 @@ return items_transfered -/obj/machinery/reagentgrinder/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/reagentgrinder/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(user.combat_mode || (tool.item_flags & ABSTRACT) || (tool.flags_1 & HOLOGRAM_1) || !can_interact(user) || !user.can_perform_action(src, ALLOW_SILICON_REACH)) - return ..() + return NONE //add the beaker if (is_reagent_container(tool) && tool.is_open_container()) @@ -262,7 +262,7 @@ to_chat(user, span_warning("You must drag & dump contents of [tool] into [src].")) return ITEM_INTERACT_BLOCKING - return ..() + return NONE /obj/machinery/reagentgrinder/wrench_act(mob/living/user, obj/item/tool) if(user.combat_mode) diff --git a/code/modules/reagents/reagent_containers/chem_pack.dm b/code/modules/reagents/reagent_containers/chem_pack.dm index 3345f1e99ef..98ffa2e596e 100644 --- a/code/modules/reagents/reagent_containers/chem_pack.dm +++ b/code/modules/reagents/reagent_containers/chem_pack.dm @@ -8,23 +8,29 @@ spillable = TRUE obj_flags = UNIQUE_RENAME resistance_flags = ACID_PROOF - var/sealed = FALSE fill_icon_thresholds = list(10, 20, 30, 40, 50, 60, 70, 80, 90, 100) has_variable_transfer_amount = FALSE + interaction_flags_click = NEED_DEXTERITY + /// Whether this has been sealed shut + var/sealed = FALSE -/obj/item/reagent_containers/chem_pack/AltClick(mob/living/user) - if(user.can_perform_action(src, NEED_DEXTERITY) && !sealed) - if(iscarbon(user) && (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))) - to_chat(user, span_warning("Uh... whoops! You accidentally spill the content of the bag onto yourself.")) - SplashReagents(user) - return +/obj/item/reagent_containers/chem_pack/click_alt(mob/living/user) + if(sealed) + balloon_alert(user, "sealed!") + return CLICK_ACTION_BLOCKING - reagents.flags = NONE - reagent_flags = DRAWABLE | INJECTABLE //To allow for sabotage or ghetto use. - reagents.flags = reagent_flags - spillable = FALSE - sealed = TRUE - to_chat(user, span_notice("You seal the bag.")) + if(iscarbon(user) && (HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))) + to_chat(user, span_warning("Uh... whoops! You accidentally spill the content of the bag onto yourself.")) + SplashReagents(user) + return CLICK_ACTION_BLOCKING + + reagents.flags = NONE + reagent_flags = DRAWABLE | INJECTABLE //To allow for sabotage or ghetto use. + reagents.flags = reagent_flags + spillable = FALSE + sealed = TRUE + balloon_alert(user, "sealed") + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/chem_pack/examine() . = ..() diff --git a/code/modules/reagents/reagent_containers/cups/_cup.dm b/code/modules/reagents/reagent_containers/cups/_cup.dm index a6e4dd42885..ea0eaf989dd 100644 --- a/code/modules/reagents/reagent_containers/cups/_cup.dm +++ b/code/modules/reagents/reagent_containers/cups/_cup.dm @@ -481,11 +481,13 @@ spillable = TRUE var/obj/item/grinded -/obj/item/reagent_containers/cup/mortar/AltClick(mob/user) - if(grinded) - grinded.forceMove(drop_location()) - grinded = null - to_chat(user, span_notice("You eject the item inside.")) +/obj/item/reagent_containers/cup/mortar/click_alt(mob/user) + if(!grinded) + return CLICK_ACTION_BLOCKING + grinded.forceMove(drop_location()) + grinded = null + balloon_alert(user, "ejected") + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/cup/mortar/attackby(obj/item/I, mob/living/carbon/human/user) ..() diff --git a/code/modules/reagents/reagent_containers/cups/bottle.dm b/code/modules/reagents/reagent_containers/cups/bottle.dm index 1e4466da8c3..75bc79c5a6a 100644 --- a/code/modules/reagents/reagent_containers/cups/bottle.dm +++ b/code/modules/reagents/reagent_containers/cups/bottle.dm @@ -513,7 +513,7 @@ return TRUE -/obj/item/reagent_containers/cup/bottle/syrup_bottle/AltClick(mob/user) +/obj/item/reagent_containers/cup/bottle/syrup_bottle/click_alt(mob/user) cap_on = !cap_on if(!cap_on) icon_state = "syrup_open" @@ -522,7 +522,7 @@ icon_state = "syrup" balloon_alert(user, "put pump cap on") update_icon_state() - return ..() + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/cup/bottle/syrup_bottle/proc/rename(mob/user, obj/item/writing_instrument) if(!user.can_write(writing_instrument)) diff --git a/code/modules/reagents/reagent_containers/cups/drinkingglass.dm b/code/modules/reagents/reagent_containers/cups/drinkingglass.dm index adcd2ff79fa..7441614682c 100644 --- a/code/modules/reagents/reagent_containers/cups/drinkingglass.dm +++ b/code/modules/reagents/reagent_containers/cups/drinkingglass.dm @@ -35,7 +35,7 @@ /obj/item/reagent_containers/cup/glass/drinkingglass/on_reagent_change(datum/reagents/holder, ...) . = ..() if(!length(reagents.reagent_list)) - REMOVE_TRAIT(src, TRAIT_WAS_RENAMED, PEN_LABEL_TRAIT) //so new drinks can rename the glass + REMOVE_TRAIT(src, TRAIT_WAS_RENAMED, RENAMING_TOOL_LABEL_TRAIT) //so new drinks can rename the glass // Having our icon state change removes fill thresholds /obj/item/reagent_containers/cup/glass/drinkingglass/on_cup_change(datum/glass_style/style) @@ -58,7 +58,7 @@ return REMOVE_TRAIT(src, TRAIT_WAS_RENAMED, SHAKER_LABEL_TRAIT) - REMOVE_TRAIT(src, TRAIT_WAS_RENAMED, PEN_LABEL_TRAIT) + REMOVE_TRAIT(src, TRAIT_WAS_RENAMED, RENAMING_TOOL_LABEL_TRAIT) name = initial(name) desc = initial(desc) update_appearance(UPDATE_NAME | UPDATE_DESC) diff --git a/code/modules/reagents/reagent_containers/cups/drinks.dm b/code/modules/reagents/reagent_containers/cups/drinks.dm index cba2f937da4..5a3ed446f60 100644 --- a/code/modules/reagents/reagent_containers/cups/drinks.dm +++ b/code/modules/reagents/reagent_containers/cups/drinks.dm @@ -122,10 +122,10 @@ . += span_notice("Alt-click to toggle cup lid.") return -/obj/item/reagent_containers/cup/glass/coffee/AltClick(mob/user) +/obj/item/reagent_containers/cup/glass/coffee/click_alt(mob/user) lid_open = !lid_open update_icon_state() - return ..() + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/cup/glass/coffee/update_icon_state() if(lid_open) @@ -248,11 +248,10 @@ else . += span_notice("The cap has been taken off. Alt-click to put a cap on.") -/obj/item/reagent_containers/cup/glass/waterbottle/AltClick(mob/user) - . = ..() +/obj/item/reagent_containers/cup/glass/waterbottle/click_alt(mob/user) if(cap_lost) to_chat(user, span_warning("The cap seems to be missing! Where did it go?")) - return + return CLICK_ACTION_BLOCKING var/fumbled = HAS_TRAIT(user, TRAIT_CLUMSY) && prob(5) if(cap_on || fumbled) @@ -270,6 +269,7 @@ spillable = FALSE to_chat(user, span_notice("You put the cap on [src].")) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/cup/glass/waterbottle/is_refillable() if(cap_on) @@ -436,6 +436,7 @@ amount_per_transfer_from_this = 10 volume = 100 isGlass = FALSE + interaction_flags_click = NEED_HANDS|FORBID_TELEKINESIS_REACH /// Whether or not poured drinks should use custom names and descriptions var/using_custom_drinks = FALSE /// Name custom drinks will have @@ -463,34 +464,30 @@ . += span_notice("Drinks poured from this shaker will have the following name: [custom_drink_name]") . += span_notice("Drinks poured from this shaker will have the following description: [custom_drink_desc]") -/obj/item/reagent_containers/cup/glass/shaker/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src, NEED_HANDS|FORBID_TELEKINESIS_REACH)) - return - +/obj/item/reagent_containers/cup/glass/shaker/click_alt(mob/user) if(using_custom_drinks) using_custom_drinks = FALSE disable_custom_drinks() balloon_alert(user, "custom drinks disabled") - return + return CLICK_ACTION_BLOCKING var/new_name = reject_bad_text(tgui_input_text(user, "Drink name", "Set drink name", custom_drink_name, 45, FALSE), 64) if(!new_name) balloon_alert(user, "invalid drink name!") using_custom_drinks = FALSE - return + return CLICK_ACTION_BLOCKING if(!user.can_perform_action(src, NEED_HANDS|FORBID_TELEKINESIS_REACH)) - return + return CLICK_ACTION_BLOCKING var/new_desc = reject_bad_text(tgui_input_text(user, "Drink description", "Set drink description", custom_drink_desc, 64, TRUE), 128) if(!new_desc) balloon_alert(user, "invalid drink description!") using_custom_drinks = FALSE - return + return CLICK_ACTION_BLOCKING if(!user.can_perform_action(src, NEED_HANDS|FORBID_TELEKINESIS_REACH)) - return + return CLICK_ACTION_BLOCKING using_custom_drinks = TRUE custom_drink_name = new_name @@ -498,6 +495,7 @@ enable_custom_drinks() balloon_alert(user, "now pouring custom drinks") + return CLICK_ACTION_SUCCESS /obj/item/reagent_containers/cup/glass/shaker/proc/enable_custom_drinks() RegisterSignal(src, COMSIG_REAGENTS_CUP_TRANSFER_TO, PROC_REF(handle_transfer)) diff --git a/code/modules/reagents/reagent_containers/medigel.dm b/code/modules/reagents/reagent_containers/medigel.dm index e6836c7a4c2..f6d7b116eff 100644 --- a/code/modules/reagents/reagent_containers/medigel.dm +++ b/code/modules/reagents/reagent_containers/medigel.dm @@ -32,6 +32,7 @@ "Purple" = "medigel_purple" ) + /obj/item/reagent_containers/medigel/mode_change_message(mob/user) var/squirt_mode = amount_per_transfer_from_this == initial(amount_per_transfer_from_this) to_chat(user, span_notice("You will now apply the medigel's contents in [squirt_mode ? "extended sprays":"short bursts"]. You'll now use [amount_per_transfer_from_this] units per use.")) diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm index ee512224184..98ba3a13ed2 100644 --- a/code/modules/reagents/reagent_containers/pill.dm +++ b/code/modules/reagents/reagent_containers/pill.dm @@ -314,6 +314,12 @@ list_reagents = list(/datum/reagent/gravitum = 5) rename_with_volume = TRUE +/obj/item/reagent_containers/pill/ondansetron + name = "ondansetron pill" + desc = "Alleviates nausea. May cause drowsiness." + icon_state = "pill11" + list_reagents = list(/datum/reagent/medicine/ondansetron = 10) + // Pill styles for chem master /obj/item/reagent_containers/pill/style diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index a554dca6a84..350f51492cf 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -437,9 +437,14 @@ "Yellow" = "sprayer_med_yellow", "Blue" = "sprayer_med_blue") -/obj/item/reagent_containers/spray/medical/AltClick(mob/user) - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) + +/obj/item/reagent_containers/spray/medical/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + + if(!current_skin) + context[SCREENTIP_CONTEXT_ALT_LMB] = "Reskin" + return CONTEXTUAL_SCREENTIP_SET + /obj/item/reagent_containers/spray/medical/reskin_obj(mob/M) ..() diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm index d9ce792f1a0..a25b3936a35 100644 --- a/code/modules/reagents/reagent_dispenser.dm +++ b/code/modules/reagents/reagent_dispenser.dm @@ -454,8 +454,6 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/structure/reagent_dispensers/wall/virusfood, 30 . = ..() AddComponent(/datum/component/simple_rotation) -/obj/structure/reagent_dispensers/plumbed/storage/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/structure/reagent_dispensers/plumbed/storage/update_overlays() . = ..() diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm index 4b8fef12924..903a59a9305 100644 --- a/code/modules/recycling/disposal/construction.dm +++ b/code/modules/recycling/disposal/construction.dm @@ -95,8 +95,6 @@ pipe_type = initial(temp.flip_type) update_appearance() -/obj/structure/disposalconstruct/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation // construction/deconstruction // wrench: (un)anchor diff --git a/code/modules/recycling/sortingmachinery.dm b/code/modules/recycling/sortingmachinery.dm index f11ad91e002..2497af7e54c 100644 --- a/code/modules/recycling/sortingmachinery.dm +++ b/code/modules/recycling/sortingmachinery.dm @@ -400,10 +400,10 @@ payments_acc = null to_chat(user, span_notice("You clear the registered account.")) -/obj/item/sales_tagger/AltClick(mob/user) - . = ..() +/obj/item/sales_tagger/click_alt(mob/user) var/potential_cut = input("How much would you like to pay out to the registered card?","Percentage Profit ([round(cut_min*100)]% - [round(cut_max*100)]%)") as num|null if(!potential_cut) cut_multiplier = initial(cut_multiplier) cut_multiplier = clamp(round(potential_cut/100, cut_min), cut_min, cut_max) to_chat(user, span_notice("[round(cut_multiplier*100)]% profit will be received if a package with a barcode is sold.")) + return CLICK_ACTION_SUCCESS diff --git a/code/modules/religion/burdened/psyker.dm b/code/modules/religion/burdened/psyker.dm index bd063dea439..1531d07f5ea 100644 --- a/code/modules/religion/burdened/psyker.dm +++ b/code/modules/religion/burdened/psyker.dm @@ -341,7 +341,7 @@ else times_dry_fired = 0 var/turf/target_turf = get_offset_target_turf(get_ranged_target_turf(owner, owner.dir, 7), dx = rand(-1, 1), dy = rand(-1, 1)) - held_gun.process_fire(target_turf, owner, TRUE, null, pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG)) + held_gun.process_fire(target_turf, owner, TRUE, null, pick(GLOB.all_body_zones)) held_gun.semicd = FALSE /datum/action/cooldown/spell/charged/psychic_booster diff --git a/code/modules/research/designs/autolathe/service_designs.dm b/code/modules/research/designs/autolathe/service_designs.dm index 6b8d7f474fc..e34fc5166c8 100644 --- a/code/modules/research/designs/autolathe/service_designs.dm +++ b/code/modules/research/designs/autolathe/service_designs.dm @@ -190,6 +190,19 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE +/datum/design/soup_pot + name = "Soup Pot" + id = "souppot" + build_type = AUTOLATHE | PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron =SHEET_MATERIAL_AMOUNT*5, /datum/material/bluespace =SMALL_MATERIAL_AMOUNT*4) + category = list(RND_CATEGORY_INITIAL, RND_CATEGORY_EQUIPMENT) + build_path = /obj/item/reagent_containers/cup/soup_pot + category = list( + RND_CATEGORY_INITIAL, + RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_KITCHEN, + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE + /datum/design/bowl name = "Bowl" id = "bowl" diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 03fffcec8ca..021c282958d 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -53,9 +53,9 @@ addtimer(CALLBACK(src, PROC_REF(finish_loading)), 1 SECONDS) return TRUE -/obj/machinery/rnd/destructive_analyzer/AltClick(mob/user) - . = ..() +/obj/machinery/rnd/destructive_analyzer/click_alt(mob/user) unload_item() + return CLICK_ACTION_SUCCESS /obj/machinery/rnd/destructive_analyzer/update_icon_state() icon_state = "[base_icon_state][loaded_item ? "_l" : null]" @@ -115,11 +115,11 @@ say("Destructive analysis failed!") return TRUE -//Let emags in on a right click -/obj/machinery/rnd/destructive_analyzer/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) - if(is_right_clicking && istype(tool, /obj/item/card/emag)) - return NONE - return ..() +/obj/machinery/rnd/destructive_analyzer/item_interaction_secondary(mob/living/user, obj/item/tool, list/modifiers) + // Cringe way to let emags insert on RMB because we still use attackby to insert + if(istype(tool, /obj/item/card/emag)) + return ITEM_INTERACT_SKIP_TO_ATTACK + return NONE //This allows people to put syndicate screwdrivers in the machine. Secondary act still passes. /obj/machinery/rnd/destructive_analyzer/screwdriver_act(mob/living/user, obj/item/tool) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index 8ed6ef04df0..c7a7b320345 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -452,12 +452,12 @@ drop_direction = direction balloon_alert(usr, "dropping [dir2text(drop_direction)]") -/obj/machinery/rnd/production/AltClick(mob/user) - . = ..() - if(!drop_direction || !can_interact(user)) - return +/obj/machinery/rnd/production/click_alt(mob/user) + if(drop_direction == 0) + return CLICK_ACTION_BLOCKING if(busy) balloon_alert(user, "busy printing!") - return + return CLICK_ACTION_BLOCKING balloon_alert(user, "drop direction reset") drop_direction = 0 + return CLICK_ACTION_SUCCESS diff --git a/code/modules/research/ordnance/doppler_array.dm b/code/modules/research/ordnance/doppler_array.dm index 7afe201f360..5e44c2dc703 100644 --- a/code/modules/research/ordnance/doppler_array.dm +++ b/code/modules/research/ordnance/doppler_array.dm @@ -250,8 +250,6 @@ SIGNAL_HANDLER set_light_on(!(machine_stat & NOPOWER)) -/obj/machinery/doppler_array/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/machinery/doppler_array/ui_interact(mob/user, datum/tgui/ui) ui = SStgui.try_update_ui(user, src, ui) diff --git a/code/modules/research/server.dm b/code/modules/research/server.dm index e446672bc33..1bd0d3560be 100644 --- a/code/modules/research/server.dm +++ b/code/modules/research/server.dm @@ -154,17 +154,16 @@ if(HDD_OVERLOADED) . += "The front panel is dangling open. The hdd inside is destroyed and the wires are all burned." -/obj/machinery/rnd/server/master/item_interaction(mob/living/user, obj/item/tool, list/modifiers, is_right_clicking) +/obj/machinery/rnd/server/master/item_interaction(mob/living/user, obj/item/tool, list/modifiers) if(!tool.tool_behaviour) - return ..() - // Only antags are given the training and knowledge to disassemble this thing. - if(is_special_character(user)) - return ..() - if(user.combat_mode) return NONE - - balloon_alert(user, "you can't find an obvious maintenance hatch!") - return ITEM_INTERACT_BLOCKING + // Only antags are given the training and knowledge to disassemble this thing. + if(!is_special_character(user)) + if(user.combat_mode) + return ITEM_INTERACT_SKIP_TO_ATTACK + balloon_alert(user, "you can't find an obvious maintenance hatch!") + return ITEM_INTERACT_BLOCKING + return NONE /obj/machinery/rnd/server/master/attackby(obj/item/attacking_item, mob/user, params) if(istype(attacking_item, /obj/item/computer_disk/hdd_theft)) diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 7b01536034c..7d64e1878b8 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -106,6 +106,7 @@ "slime_scanner", "solar_panel", "solar_tracker", + "souppot", "space_heater", "spoon", "status_display_frame", diff --git a/code/modules/research/xenobiology/xenobio_camera.dm b/code/modules/research/xenobiology/xenobio_camera.dm index 172959153aa..9724bd776d8 100644 --- a/code/modules/research/xenobiology/xenobio_camera.dm +++ b/code/modules/research/xenobiology/xenobio_camera.dm @@ -360,9 +360,9 @@ Due to keyboard shortcuts, the second one is not necessarily the remote eye's lo // Alternate clicks for slime, monkey and open turf if using a xenobio console -/mob/living/basic/slime/AltClick(mob/user) +/mob/living/basic/slime/click_alt(mob/user) SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_ALT, src) - ..() + return CLICK_ACTION_SUCCESS /mob/living/basic/slime/ShiftClick(mob/user) SEND_SIGNAL(user, COMSIG_XENO_SLIME_CLICK_SHIFT, src) diff --git a/code/modules/research/xenobiology/xenobiology.dm b/code/modules/research/xenobiology/xenobiology.dm index 3d4d9bc4b45..7f87a08b85b 100644 --- a/code/modules/research/xenobiology/xenobiology.dm +++ b/code/modules/research/xenobiology/xenobiology.dm @@ -705,10 +705,9 @@ context[SCREENTIP_CONTEXT_ALT_LMB] = "Set potion offer reason" return CONTEXTUAL_SCREENTIP_SET -/obj/item/slimepotion/slime/sentience/AltClick(mob/living/user) - if(!can_interact(user)) - return +/obj/item/slimepotion/slime/sentience/click_alt(mob/living/user) potion_reason = tgui_input_text(user, "Enter reason for offering potion", "Intelligence Potion", potion_reason, multiline = TRUE) + return CLICK_ACTION_SUCCESS /obj/item/slimepotion/slime/sentience/attack(mob/living/dumb_mob, mob/user) if(being_used || !isliving(dumb_mob)) diff --git a/code/modules/shuttle/emergency.dm b/code/modules/shuttle/emergency.dm index ff2da672213..28f344873ff 100644 --- a/code/modules/shuttle/emergency.dm +++ b/code/modules/shuttle/emergency.dm @@ -201,9 +201,11 @@ shuttle.setTimer(shuttle.timeLeft(1) + hijack_flight_time_increase) //give the guy more time to hijack if it's already in flight. return shuttle.hijack_status -/obj/machinery/computer/emergency_shuttle/AltClick(user) - if(isliving(user)) - attempt_hijack_stage(user) +/obj/machinery/computer/emergency_shuttle/click_alt(mob/living/user) + if(!isliving(user)) + return NONE + attempt_hijack_stage(user) + return CLICK_ACTION_SUCCESS /obj/machinery/computer/emergency_shuttle/proc/attempt_hijack_stage(mob/living/user) if(!user.CanReach(src)) @@ -826,10 +828,8 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/item/storage/pod, 32) return SECONDARY_ATTACK_CANCEL_ATTACK_CHAIN return ..() -/obj/item/storage/pod/AltClick(mob/user) - if(!can_interact(user)) - return - return ..() +/obj/item/storage/pod/click_alt(mob/user) + return CLICK_ACTION_SUCCESS /obj/item/storage/pod/can_interact(mob/user) if(!..()) diff --git a/code/modules/spells/spell_types/jaunt/shadow_walk.dm b/code/modules/spells/spell_types/jaunt/shadow_walk.dm index c5a47cd1740..c869fb19913 100644 --- a/code/modules/spells/spell_types/jaunt/shadow_walk.dm +++ b/code/modules/spells/spell_types/jaunt/shadow_walk.dm @@ -45,7 +45,7 @@ exit_jaunt(cast_on) return - playsound(get_turf(owner), 'sound/magic/ethereal_enter.ogg', 50, TRUE, -1) + playsound(get_turf(owner), 'sound/effects/nightmare_poof.ogg', 50, TRUE, -1, ignore_walls = FALSE) cast_on.visible_message(span_boldwarning("[cast_on] melts into the shadows!")) cast_on.SetAllImmobility(0) cast_on.setStaminaLoss(0, FALSE) @@ -107,7 +107,7 @@ reveal_turf.visible_message(span_boldwarning("[jaunter] is revealed by the light!")) else reveal_turf.visible_message(span_boldwarning("[jaunter] emerges from the darkness!")) - playsound(reveal_turf, 'sound/magic/ethereal_exit.ogg', 50, TRUE, -1) + playsound(reveal_turf, 'sound/effects/nightmare_reappear.ogg', 50, TRUE, -1, ignore_walls = FALSE) return ..() diff --git a/code/modules/surgery/bodyparts/_bodyparts.dm b/code/modules/surgery/bodyparts/_bodyparts.dm index b1d613f4091..abf3575f39e 100644 --- a/code/modules/surgery/bodyparts/_bodyparts.dm +++ b/code/modules/surgery/bodyparts/_bodyparts.dm @@ -771,14 +771,14 @@ if(owner == new_owner) return FALSE //`null` is a valid option, so we need to use a num var to make it clear no change was made. - SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, owner) - if(owner) . = owner //return value is old owner clear_ownership(owner) if(new_owner) apply_ownership(new_owner) + SEND_SIGNAL(src, COMSIG_BODYPART_CHANGED_OWNER, new_owner, owner) + refresh_bleed_rate() return . diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm index 6e83c413216..9d1453e2123 100644 --- a/code/modules/surgery/bodyparts/dismemberment.dm +++ b/code/modules/surgery/bodyparts/dismemberment.dm @@ -377,7 +377,7 @@ /mob/living/carbon/proc/regenerate_limbs(list/excluded_zones = list()) SEND_SIGNAL(src, COMSIG_CARBON_REGENERATE_LIMBS, excluded_zones) - var/list/zone_list = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + var/list/zone_list = GLOB.all_body_zones.Copy() var/list/dismembered_by_copy = body_zone_dismembered_by?.Copy() diff --git a/code/modules/surgery/bodyparts/helpers.dm b/code/modules/surgery/bodyparts/helpers.dm index 126bd3db33a..fb0647d0fb5 100644 --- a/code/modules/surgery/bodyparts/helpers.dm +++ b/code/modules/surgery/bodyparts/helpers.dm @@ -83,7 +83,7 @@ /mob/living/carbon/proc/get_missing_limbs() RETURN_TYPE(/list) - var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + var/list/full = GLOB.all_body_zones.Copy() for(var/zone in full) if(get_bodypart(zone)) full -= zone @@ -100,7 +100,7 @@ return list() /mob/living/carbon/get_disabled_limbs() - var/list/full = list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG) + var/list/full = GLOB.all_body_zones.Copy() var/list/disabled = list() for(var/zone in full) var/obj/item/bodypart/affecting = get_bodypart(zone) diff --git a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm index a5569d803b0..9cbb786d7e6 100644 --- a/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm +++ b/code/modules/surgery/bodyparts/species_parts/misc_bodyparts.dm @@ -584,38 +584,34 @@ limb_id = BODYPART_ID_MEAT should_draw_greyscale = FALSE -/obj/item/bodypart/arm/left/flesh/Initialize(mapload, dont_spawn_flesh = FALSE) +/obj/item/bodypart/arm/left/flesh/Initialize(mapload) . = ..() - if(!dont_spawn_flesh) - new /mob/living/basic/living_limb_flesh(src, src) ADD_TRAIT(src, TRAIT_IGNORED_BY_LIVING_FLESH, BODYPART_TRAIT) + AddElement(/datum/element/living_limb_initialiser) /obj/item/bodypart/arm/right/flesh limb_id = BODYPART_ID_MEAT should_draw_greyscale = FALSE -/obj/item/bodypart/arm/right/flesh/Initialize(mapload, dont_spawn_flesh = FALSE) +/obj/item/bodypart/arm/right/flesh/Initialize(mapload) . = ..() - if(!dont_spawn_flesh) - new /mob/living/basic/living_limb_flesh(src, src) ADD_TRAIT(src, TRAIT_IGNORED_BY_LIVING_FLESH, BODYPART_TRAIT) + AddElement(/datum/element/living_limb_initialiser) /obj/item/bodypart/leg/left/flesh limb_id = BODYPART_ID_MEAT should_draw_greyscale = FALSE -/obj/item/bodypart/leg/left/flesh/Initialize(mapload, dont_spawn_flesh = FALSE) +/obj/item/bodypart/leg/left/flesh/Initialize(mapload) . = ..() - if(!dont_spawn_flesh) - new /mob/living/basic/living_limb_flesh(src, src) ADD_TRAIT(src, TRAIT_IGNORED_BY_LIVING_FLESH, BODYPART_TRAIT) + AddElement(/datum/element/living_limb_initialiser) /obj/item/bodypart/leg/right/flesh limb_id = BODYPART_ID_MEAT should_draw_greyscale = FALSE -/obj/item/bodypart/leg/right/flesh/Initialize(mapload, dont_spawn_flesh = FALSE) +/obj/item/bodypart/leg/right/flesh/Initialize(mapload) . = ..() - if(!dont_spawn_flesh) - new /mob/living/basic/living_limb_flesh(src, src) ADD_TRAIT(src, TRAIT_IGNORED_BY_LIVING_FLESH, BODYPART_TRAIT) + AddElement(/datum/element/living_limb_initialiser) diff --git a/code/modules/tgui/tgui.dm b/code/modules/tgui/tgui.dm index 64bd496f2bb..754335494f9 100644 --- a/code/modules/tgui/tgui.dm +++ b/code/modules/tgui/tgui.dm @@ -118,6 +118,8 @@ /datum/asset/simple/namespaced/fontawesome)) flush_queue |= window.send_asset(get_asset_datum( /datum/asset/simple/namespaced/tgfont)) + flush_queue |= window.send_asset(get_asset_datum( + /datum/asset/json/icon_ref_map)) for(var/datum/asset/asset in src_object.ui_assets(user)) flush_queue |= window.send_asset(asset) if (flush_queue) diff --git a/code/modules/transport/tram/tram_remote.dm b/code/modules/transport/tram/tram_remote.dm index 4176117d8b2..3a45ec4e665 100644 --- a/code/modules/transport/tram/tram_remote.dm +++ b/code/modules/transport/tram/tram_remote.dm @@ -107,12 +107,9 @@ SEND_SIGNAL(src, COMSIG_TRANSPORT_REQUEST, specific_transport_id, destination, options) -/obj/item/assembly/control/transport/remote/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - +/obj/item/assembly/control/transport/remote/click_alt(mob/user) link_tram(user) + return CLICK_ACTION_SUCCESS /obj/item/assembly/control/transport/remote/proc/link_tram(mob/user) specific_transport_id = null diff --git a/code/modules/transport/tram/tram_signals.dm b/code/modules/transport/tram/tram_signals.dm index 88f18d2cb7d..eb648666030 100644 --- a/code/modules/transport/tram/tram_signals.dm +++ b/code/modules/transport/tram/tram_signals.dm @@ -169,20 +169,16 @@ obj_flags |= EMAGGED return TRUE -/obj/machinery/transport/crossing_signal/AltClick(mob/living/user) - . = ..() - if(!can_interact(user)) - return - +/obj/machinery/transport/crossing_signal/click_alt(mob/living/user) var/obj/item/tool = user.get_active_held_item() if(!panel_open || tool?.tool_behaviour != TOOL_WRENCH) - return FALSE + return CLICK_ACTION_BLOCKING tool.play_tool_sound(src, 50) setDir(turn(dir,-90)) - to_chat(user, span_notice("You rotate [src].")) + balloon_alert(user, "rotated") find_uplink() - return TRUE + return CLICK_ACTION_SUCCESS /obj/machinery/transport/crossing_signal/attackby_secondary(obj/item/weapon, mob/user, params) . = ..() diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index be41b619ed6..fb82c4fd748 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -117,6 +117,7 @@ #include "closets.dm" #include "clothing_under_armor_subtype_check.dm" #include "combat.dm" +#include "combat_welder.dm" #include "component_tests.dm" #include "confusion.dm" #include "connect_loc.dm" @@ -168,6 +169,7 @@ #include "ling_decap.dm" #include "liver.dm" #include "load_map_security.dm" +#include "lootpanel.dm" #include "lungs.dm" #include "machine_disassembly.dm" #include "mafia.dm" diff --git a/code/modules/unit_tests/combat_welder.dm b/code/modules/unit_tests/combat_welder.dm new file mode 100644 index 00000000000..2fa9052d6fb --- /dev/null +++ b/code/modules/unit_tests/combat_welder.dm @@ -0,0 +1,26 @@ +/datum/unit_test/welder_combat + +/datum/unit_test/welder_combat/Run() + var/mob/living/carbon/human/tider = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + var/mob/living/carbon/human/victim = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + var/obj/item/weldingtool/weapon = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + + tider.put_in_active_hand(weapon, forced = TRUE) + tider.set_combat_mode(TRUE) + weapon.attack_self(tider) + weapon.melee_attack_chain(tider, victim) + + TEST_ASSERT_NOTEQUAL(victim.getFireLoss(), 0, "Victim did not get burned by welder.") + TEST_ASSERT_EQUAL(weapon.get_fuel(), weapon.max_fuel - 1, "Welder did not consume fuel on attacking a mob") + + var/obj/structure/blob/blobby = allocate(__IMPLIED_TYPE__, run_loc_floor_bottom_left) + weapon.melee_attack_chain(tider, blobby) + + TEST_ASSERT_NOTEQUAL(blobby.get_integrity(), blobby.max_integrity, "Blob did not get burned by welder.") + TEST_ASSERT_EQUAL(weapon.get_fuel(), weapon.max_fuel - 2, "Welder did not consume fuel on attacking a blob") + + weapon.force = 999 + weapon.melee_attack_chain(tider, blobby) + + TEST_ASSERT(QDELETED(blobby), "Blob was not destroyed by welder.") + TEST_ASSERT_EQUAL(weapon.get_fuel(), weapon.max_fuel - 3, "Welder did not consume fuel on deleting a blob") diff --git a/code/modules/unit_tests/lootpanel.dm b/code/modules/unit_tests/lootpanel.dm new file mode 100644 index 00000000000..c0bec13288c --- /dev/null +++ b/code/modules/unit_tests/lootpanel.dm @@ -0,0 +1,34 @@ +/datum/unit_test/lootpanel + abstract_type = /datum/unit_test/lootpanel + +/datum/unit_test/lootpanel/contents/Run() + var/datum/client_interface/mock_client = new() + var/datum/lootpanel/panel = new(mock_client) + var/mob/living/carbon/human/labrat = allocate(/mob/living/carbon/human/consistent) + mock_client.mob = labrat + var/turf/one_over = locate(run_loc_floor_bottom_left.x + 1, run_loc_floor_bottom_left.y, run_loc_floor_bottom_left.z) + var/obj/item/storage/toolbox/box = allocate(/obj/item/storage/toolbox, one_over) + + panel.open(one_over) + TEST_ASSERT_EQUAL(length(panel.contents), 2, "Contents should populate on open") + TEST_ASSERT_EQUAL(length(panel.to_image), 2, "to_image should've populated (unit testing)") + TEST_ASSERT_EQUAL(panel.contents[1].item, one_over, "First item should be the source turf") + + var/datum/search_object/searchable = panel.contents[2] + TEST_ASSERT_EQUAL(searchable.item, box, "Second item should be the box") + + qdel(box) + TEST_ASSERT_EQUAL(length(panel.contents), 1, "Contents should update on searchobj deleted") + TEST_ASSERT_EQUAL(length(panel.to_image), 1, "to_image should update on searchobj deleted") + + var/obj/item/storage/toolbox/new_box = allocate(/obj/item/storage/toolbox, one_over) + TEST_ASSERT_EQUAL(length(panel.contents), 1, "Contents shouldn't update, we're dumb") + TEST_ASSERT_EQUAL(length(panel.to_image), 1, "to_image shouldn't update, we're dumb") + + panel.populate_contents() // this also calls reset_contents bc length(contents) + TEST_ASSERT_EQUAL(length(panel.contents), 2, "Contents should repopulate with the new toolbox") + + panel.populate_contents() + TEST_ASSERT_EQUAL(length(panel.contents), 2, "Panel shouldnt dupe searchables if reopened") + + mock_client.mob = null diff --git a/code/modules/vehicles/mecha/_mecha.dm b/code/modules/vehicles/mecha/_mecha.dm index 7cee1b6541c..995764e0bb7 100644 --- a/code/modules/vehicles/mecha/_mecha.dm +++ b/code/modules/vehicles/mecha/_mecha.dm @@ -272,7 +272,7 @@ /// If the former occupants get polymorphed, mutated, chestburstered, /// or otherwise replaced by another mob, that mob is no longer in .occupants /// and gets deleted with the mech. However, they do remain in .contents - var/list/potential_occupants = contents ^ occupants + var/list/potential_occupants = contents | occupants for(var/mob/buggy_ejectee in potential_occupants) mob_exit(buggy_ejectee, silent = TRUE) diff --git a/code/modules/vehicles/mecha/mech_fabricator.dm b/code/modules/vehicles/mecha/mech_fabricator.dm index 9fea0ebe82f..2dce26624ad 100644 --- a/code/modules/vehicles/mecha/mech_fabricator.dm +++ b/code/modules/vehicles/mecha/mech_fabricator.dm @@ -127,14 +127,12 @@ if(panel_open) . += span_notice("Alt-click to rotate the output direction.") -/obj/machinery/mecha_part_fabricator/AltClick(mob/user) - . = ..() - if(!user.can_perform_action(src)) - return - if(panel_open) - dir = turn(dir, -90) - balloon_alert(user, "rotated to [dir2text(dir)].") - return TRUE +/obj/machinery/mecha_part_fabricator/click_alt(mob/user) + if(!panel_open) + return CLICK_ACTION_BLOCKING + dir = turn(dir, -90) + balloon_alert(user, "rotated to [dir2text(dir)].") + return CLICK_ACTION_SUCCESS /** * Updates the `final_sets` and `buildable_parts` for the current mecha fabricator. diff --git a/code/modules/vehicles/mecha/mecha_actions.dm b/code/modules/vehicles/mecha/mecha_actions.dm index 2b410bd60c7..d544c829667 100644 --- a/code/modules/vehicles/mecha/mecha_actions.dm +++ b/code/modules/vehicles/mecha/mecha_actions.dm @@ -91,14 +91,15 @@ chassis.toggle_strafe() -/obj/vehicle/sealed/mecha/AltClick(mob/living/user) - if(!(user in occupants) || !user.can_perform_action(src)) - return +/obj/vehicle/sealed/mecha/click_alt(mob/living/user) + if(!(user in occupants)) + return CLICK_ACTION_BLOCKING if(!(user in return_controllers_with_flag(VEHICLE_CONTROL_DRIVE))) to_chat(user, span_warning("You're in the wrong seat to control movement.")) - return + return CLICK_ACTION_BLOCKING toggle_strafe() + return CLICK_ACTION_SUCCESS /obj/vehicle/sealed/mecha/proc/toggle_strafe() if(!(mecha_flags & CAN_STRAFE)) diff --git a/code/modules/vehicles/ridden.dm b/code/modules/vehicles/ridden.dm index 33f81184e9a..52c7924d72f 100644 --- a/code/modules/vehicles/ridden.dm +++ b/code/modules/vehicles/ridden.dm @@ -5,6 +5,7 @@ buckle_lying = 0 pass_flags_self = PASSTABLE COOLDOWN_DECLARE(message_cooldown) + interaction_flags_click = NEED_DEXTERITY /obj/vehicle/ridden/examine(mob/user) . = ..() @@ -39,16 +40,17 @@ inserted_key.forceMove(drop_location()) inserted_key = I -/obj/vehicle/ridden/AltClick(mob/user) - if(!inserted_key || !user.can_perform_action(src, NEED_DEXTERITY)) - return ..() +/obj/vehicle/ridden/click_alt(mob/user) + if(!inserted_key) + return CLICK_ACTION_BLOCKING if(!is_occupant(user)) to_chat(user, span_warning("You must be riding the [src] to remove [src]'s key!")) - return + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("You remove \the [inserted_key] from \the [src].")) inserted_key.forceMove(drop_location()) user.put_in_hands(inserted_key) inserted_key = null + return CLICK_ACTION_SUCCESS /obj/vehicle/ridden/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE) if(!in_range(user, src) || !in_range(M, src)) diff --git a/code/modules/vehicles/wheelchair.dm b/code/modules/vehicles/wheelchair.dm index e7b3d9b3a56..92fcb995f76 100644 --- a/code/modules/vehicles/wheelchair.dm +++ b/code/modules/vehicles/wheelchair.dm @@ -63,8 +63,6 @@ qdel(src) return ITEM_INTERACT_SUCCESS -/obj/vehicle/ridden/wheelchair/AltClick(mob/user) - return ..() // This hotkey is BLACKLISTED since it's used by /datum/component/simple_rotation /obj/vehicle/ridden/wheelchair/update_overlays() . = ..() diff --git a/code/modules/wiremod/shell/controller.dm b/code/modules/wiremod/shell/controller.dm index ad03867b89b..b46dad3673f 100644 --- a/code/modules/wiremod/shell/controller.dm +++ b/code/modules/wiremod/shell/controller.dm @@ -71,9 +71,9 @@ */ /obj/item/circuit_component/controller/proc/send_alternate_signal(atom/source, mob/user) SIGNAL_HANDLER - if(!user.Adjacent(source)) - return + handle_trigger(source, user, "alternate", alt) + return CLICK_ACTION_SUCCESS /** diff --git a/icons/obj/structures.dmi b/icons/obj/structures.dmi index 85367e1c47b570a2512ed4d6542520610275638a..1bb6689becf401fc9e63ea5f0bf95f8c0eb6228e 100644 GIT binary patch literal 83526 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
  • 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 literal 211058 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

    ux!mR?c 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 to toggle remote piping.") -/obj/item/pipe_dispenser/bluespace/AltClick(mob/user) - . = ..() - if(. == FALSE) - return // too far away +/obj/item/pipe_dispenser/bluespace/click_alt(mob/user) remote_piping_toggle = !remote_piping_toggle balloon_alert(user, "remote piping [remote_piping_toggle ? "on" : "off"]") playsound(get_turf(src), 'sound/machines/click.ogg', 50, TRUE) + return CLICK_ACTION_SUCCESS /obj/item/pipe_dispenser/bluespace/afterattack(atom/target, mob/user, prox) if(prox || !remote_piping_toggle) // If we are in proximity to the target or have our safety on, don't use charge and don't call this shitcode. diff --git a/modular_skyrat/modules/cargo_teleporter/code/cargo_teleporter.dm b/modular_skyrat/modules/cargo_teleporter/code/cargo_teleporter.dm index c5e0916f5e9..2efccbe8174 100644 --- a/modular_skyrat/modules/cargo_teleporter/code/cargo_teleporter.dm +++ b/modular_skyrat/modules/cargo_teleporter/code/cargo_teleporter.dm @@ -32,10 +32,11 @@ GLOBAL_LIST_EMPTY(cargo_marks) spawned_marker.parent_item = src marker_children += spawned_marker -/obj/item/cargo_teleporter/AltClick(mob/user) +/obj/item/cargo_teleporter/click_alt(mob/user) if(length(marker_children)) for(var/obj/effect/decal/cleanable/cargo_mark/destroy_children in marker_children) qdel(destroy_children) + return CLICK_ACTION_SUCCESS /obj/item/cargo_teleporter/afterattack(atom/target, mob/user, proximity_flag, click_parameters) if(!proximity_flag) diff --git a/modular_skyrat/modules/cellguns/code/cellguns.dm b/modular_skyrat/modules/cellguns/code/cellguns.dm index eaeaee29cc2..cce7b4435ef 100644 --- a/modular_skyrat/modules/cellguns/code/cellguns.dm +++ b/modular_skyrat/modules/cellguns/code/cellguns.dm @@ -72,10 +72,10 @@ charge_overlay.pixel_y = ammo_y_offset * (i - 1) . += new /mutable_appearance(charge_overlay) -/obj/item/gun/energy/cell_loaded/AltClick(mob/user, modifiers) +/obj/item/gun/energy/cell_loaded/click_alt(mob/user, modifiers) if(!installedcells.len) //Checks to see if there is a cell inside of the gun, before removal. to_chat(user, span_notice("The [src] has no cells inside")) - return ..() + return CLICK_ACTION_BLOCKING to_chat(user, span_notice("You remove a cell")) var/obj/item/last_cell = installedcells[installedcells.len] @@ -87,6 +87,7 @@ installedcells -= last_cell ammo_type.len-- select_fire(user) + return CLICK_ACTION_SUCCESS /// A cellgun used for debug, it is able to use any weaponcell. /obj/item/gun/energy/cell_loaded/alltypes diff --git a/modular_skyrat/modules/central_command_module/code/computers/station_goal_computer.dm b/modular_skyrat/modules/central_command_module/code/computers/station_goal_computer.dm index a1464e08d3e..9b488fd561f 100644 --- a/modular_skyrat/modules/central_command_module/code/computers/station_goal_computer.dm +++ b/modular_skyrat/modules/central_command_module/code/computers/station_goal_computer.dm @@ -47,8 +47,6 @@ if(machine_stat & (NOPOWER|BROKEN|MAINT)) return - usr.set_machine(src) - var/selected_goal = href_list["selected_goal"] if(href_list["close"]) @@ -62,4 +60,4 @@ SSstation.goals_by_type[iterating_goal] = goal_to_set goal_assigned = TRUE break - updateUsrDialog() + SStgui.update_uis(src) diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm index a7efee044b8..c600019f7de 100644 --- a/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm +++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/appliances.dm @@ -18,7 +18,6 @@ "portable_lil_pump", "portable_scrubbs", "survival_knife", // I just don't want to make a whole new node for this one sorry - "soup_pot", // This one too "water_synth", "hydro_synth", "frontier_sustenance_dispenser", diff --git a/modular_skyrat/modules/colony_fabricator/code/design_datums/equipment.dm b/modular_skyrat/modules/colony_fabricator/code/design_datums/equipment.dm index 067b0084046..d2c0f8887f2 100644 --- a/modular_skyrat/modules/colony_fabricator/code/design_datums/equipment.dm +++ b/modular_skyrat/modules/colony_fabricator/code/design_datums/equipment.dm @@ -12,16 +12,8 @@ ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE -/datum/design/soup_pot - name = "Soup Pot" - id = "soup_pot" - build_type = COLONY_FABRICATOR - materials = list( - /datum/material/iron = SHEET_MATERIAL_AMOUNT * 2.5, - ) - build_path = /obj/item/reagent_containers/cup/soup_pot - category = list( - RND_CATEGORY_INITIAL, - RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_KITCHEN, - ) - departmental_flags = DEPARTMENT_BITFLAG_SERVICE +// Lets colony fabricators make soup pots, removes bluespace crystal requirement. +/datum/design/soup_pot/New() + build_type |= COLONY_FABRICATOR + materials -= /datum/material/bluespace + return ..() diff --git a/modular_skyrat/modules/colony_fabricator/code/machines/arc_furnace.dm b/modular_skyrat/modules/colony_fabricator/code/machines/arc_furnace.dm index 66e4e209dab..6d692728674 100644 --- a/modular_skyrat/modules/colony_fabricator/code/machines/arc_furnace.dm +++ b/modular_skyrat/modules/colony_fabricator/code/machines/arc_furnace.dm @@ -107,7 +107,6 @@ if(isAI(user) && (machine_stat & NOPOWER)) return - usr.set_machine(src) // What does this even do?? switch(choice) if(RADIAL_CHOICE_EJECT) eject_contents() diff --git a/modular_skyrat/modules/conveyor_sorter/code/conveyor_sorter.dm b/modular_skyrat/modules/conveyor_sorter/code/conveyor_sorter.dm index 550de51f205..a5306c96bd2 100644 --- a/modular_skyrat/modules/conveyor_sorter/code/conveyor_sorter.dm +++ b/modular_skyrat/modules/conveyor_sorter/code/conveyor_sorter.dm @@ -53,10 +53,11 @@ current_sort += target.type to_chat(user, span_notice("[target] has been added to [src]'s sorting list.")) -/obj/item/conveyor_sorter/AltClick(mob/user) +/obj/item/conveyor_sorter/click_alt(mob/user) visible_message("[src] pings, resetting its sorting list!") playsound(src, 'sound/machines/ping.ogg', 30, TRUE) current_sort = list() + return CLICK_ACTION_SUCCESS /obj/effect/decal/conveyor_sorter name = "conveyor sorter" @@ -122,10 +123,11 @@ else return ..() -/obj/effect/decal/conveyor_sorter/AltClick(mob/user) +/obj/effect/decal/conveyor_sorter/click_alt(mob/user) visible_message("[src] pings, resetting its sorting list!") playsound(src, 'sound/machines/ping.ogg', 30, TRUE) sorting_list = list() + return CLICK_ACTION_SUCCESS /obj/effect/decal/conveyor_sorter/CtrlClick(mob/user) visible_message("[src] begins to ping violently!") diff --git a/modular_skyrat/modules/customization/modules/clothing/masks/paper.dm b/modular_skyrat/modules/customization/modules/clothing/masks/paper.dm index e141f569004..b9ff2f16cc8 100644 --- a/modular_skyrat/modules/customization/modules/clothing/masks/paper.dm +++ b/modular_skyrat/modules/customization/modules/clothing/masks/paper.dm @@ -57,7 +57,6 @@ /obj/item/clothing/mask/paper/Initialize(mapload) . = ..() - register_context() if(wear_hair_over) alternate_worn_layer = BACK_LAYER diff --git a/modular_skyrat/modules/customization/modules/clothing/neck/_neck.dm b/modular_skyrat/modules/customization/modules/clothing/neck/_neck.dm index 75c95556f6d..0135ef01d82 100644 --- a/modular_skyrat/modules/customization/modules/clothing/neck/_neck.dm +++ b/modular_skyrat/modules/customization/modules/clothing/neck/_neck.dm @@ -103,12 +103,12 @@ . = ..() AddComponent(/datum/component/toggle_icon, toggle_noun = "scarf") -/obj/item/clothing/neck/face_scarf/AltClick(mob/user) //Make sure that toggling actually hides the snout so that it doesn't clip - . = ..() +/obj/item/clothing/neck/face_scarf/click_alt(mob/user) //Make sure that toggling actually hides the snout so that it doesn't clip if(icon_state != "face_scarf_t") flags_inv = HIDEFACIALHAIR | HIDESNOUT else flags_inv = HIDEFACIALHAIR + return CLICK_ACTION_SUCCESS /obj/item/clothing/neck/maid_neck_cover name = "maid neck cover" diff --git a/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm b/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm index fa4052d1119..322e438374c 100644 --- a/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm +++ b/modular_skyrat/modules/customization/modules/clothing/storage/belts.dm @@ -7,6 +7,7 @@ worn_icon_state = "crusader_belt" inhand_icon_state = "utility" w_class = WEIGHT_CLASS_BULKY //Cant fit a sheath in your bag + interaction_flags_click = NEED_DEXTERITY /obj/item/storage/belt/crusader/Initialize(mapload) . = ..() @@ -64,9 +65,7 @@ atom_storage.show_contents(user) return -/obj/item/storage/belt/crusader/AltClick(mob/user) //This is basically the same as the normal sheath, but because there's always an item locked in the first slot it uses the second slot for swords - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/storage/belt/crusader/click_alt(mob/user) //This is basically the same as the normal sheath, but because there's always an item locked in the first slot it uses the second slot for swords if(contents.len == 2) var/obj/item/drawn_item = contents[2] add_fingerprint(user) @@ -74,12 +73,12 @@ if(!user.put_in_hands(drawn_item)) to_chat(user, span_notice("You fumble for [drawn_item] and it falls on the floor.")) update_appearance() - return + return CLICK_ACTION_SUCCESS user.visible_message(span_notice("[user] takes [drawn_item] out of [src]."), span_notice("You take [drawn_item] out of [src].")) update_appearance() else to_chat(user, span_warning("[src] is empty!")) - . = ..() + return CLICK_ACTION_SUCCESS /obj/item/storage/belt/crusader/update_icon(updates) if(contents.len == 2) //Checks for a sword/rod in the sheath slot, changes the sprite accordingly diff --git a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm index e8688a96b1a..b68119246c1 100644 --- a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm +++ b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_clothing.dm @@ -280,14 +280,14 @@ visor_flags_inv = HIDEFACE | HIDESNOUT w_class = WEIGHT_CLASS_SMALL tint = 0 + interaction_flags_click = NEED_DEXTERITY /obj/item/clothing/mask/gas/nightlight/attack_self(mob/user) adjustmask(user) -/obj/item/clothing/mask/gas/nightlight/AltClick(mob/user) - ..() - if(user.can_perform_action(src, NEED_DEXTERITY)) - adjustmask(user) +/obj/item/clothing/mask/gas/nightlight/click_alt(mob/user) + adjustmask(user) + return CLICK_ACTION_SUCCESS /obj/item/clothing/mask/gas/nightlight/examine(mob/user) . = ..() @@ -798,7 +798,6 @@ worn_icon = 'modular_skyrat/master_files/icons/donator/mob/clothing/head.dmi' icon_state = "emissionhelm" - // Donation reward for CandleJax /obj/item/clothing/head/helmet/space/plasmaman/candlejax2 name = "azulean's environment helmet" diff --git a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm index 6455bba4255..4cd4695d2fb 100644 --- a/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm +++ b/modular_skyrat/modules/customization/modules/clothing/~donator/donator_items.dm @@ -256,11 +256,11 @@ else icon_state = "catear_headphone[song?.playing ? "_on" : null]" -/obj/item/instrument/piano_synth/headphones/catear_headphone/AltClick(mob/user) - . = ..() +/obj/item/instrument/piano_synth/headphones/catear_headphone/click_alt(mob/user) catTailToggled = !catTailToggled user.update_worn_head() update_icon(UPDATE_OVERLAYS) + return CLICK_ACTION_SUCCESS /obj/item/instrument/piano_synth/headphones/catear_headphone/update_overlays() . = ..() diff --git a/modular_skyrat/modules/electric_welder/code/electric_welder.dm b/modular_skyrat/modules/electric_welder/code/electric_welder.dm index a3426e2cf78..c61e8333ef9 100644 --- a/modular_skyrat/modules/electric_welder/code/electric_welder.dm +++ b/modular_skyrat/modules/electric_welder/code/electric_welder.dm @@ -71,10 +71,6 @@ /obj/item/weldingtool/electric/use(used = 0) return isOn() -// This is what starts fires. Overriding it stops it starting fires -/obj/item/weldingtool/electric/handle_fuel_and_temps(used = 0, mob/living/user) - return - /obj/item/weldingtool/electric/examine() . = ..() . += "[src] is currently [powered ? "powered" : "unpowered"]." diff --git a/modular_skyrat/modules/exp_corps/code/clothing.dm b/modular_skyrat/modules/exp_corps/code/clothing.dm index 66e5422f012..d932d13b3ad 100644 --- a/modular_skyrat/modules/exp_corps/code/clothing.dm +++ b/modular_skyrat/modules/exp_corps/code/clothing.dm @@ -240,12 +240,9 @@ current_user.remove_client_colour(/datum/client_colour/glass_colour/lightgreen) current_user.update_sight() -/obj/item/clothing/head/helmet/expeditionary_corps/AltClick(mob/user) - . = ..() +/obj/item/clothing/head/helmet/expeditionary_corps/click_alt(mob/user) if(!current_user) return - if(!can_interact(user)) - return nightvision = !nightvision if(nightvision) @@ -255,6 +252,7 @@ to_chat(user, span_notice("You flip the NV goggles up.")) disable_nv() update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/clothing/head/helmet/expeditionary_corps/dropped(mob/user) . = ..() diff --git a/modular_skyrat/modules/goofsec/code/sec_clothing_overrides.dm b/modular_skyrat/modules/goofsec/code/sec_clothing_overrides.dm index 82f55eabd24..ba8ea2defbb 100644 --- a/modular_skyrat/modules/goofsec/code/sec_clothing_overrides.dm +++ b/modular_skyrat/modules/goofsec/code/sec_clothing_overrides.dm @@ -392,11 +392,11 @@ ), ) -/obj/item/clothing/neck/security_cape/AltClick(mob/user) - . = ..() +/obj/item/clothing/neck/security_cape/click_alt(mob/user) swapped = !swapped to_chat(user, span_notice("You swap which arm [src] will lay over.")) update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/clothing/neck/security_cape/update_appearance(updates) . = ..() diff --git a/modular_skyrat/modules/hev_suit/code/hev_suit.dm b/modular_skyrat/modules/hev_suit/code/hev_suit.dm index 1dbce983d32..eff170f0c7a 100644 --- a/modular_skyrat/modules/hev_suit/code/hev_suit.dm +++ b/modular_skyrat/modules/hev_suit/code/hev_suit.dm @@ -843,9 +843,11 @@ acid_static_cooldown = PCV_COOLDOWN_ACID suit_name = "PCV MARK II" -/obj/item/clothing/suit/space/hev_suit/pcv/AltClick(mob/living/user) - reskin_obj(user) +/obj/item/clothing/suit/space/hev_suit/pcv/add_context(atom/source, list/context, obj/item/held_item, mob/user) . = ..() + if(!current_skin) + context[SCREENTIP_CONTEXT_ALT_LMB] = "Reskin" + return CONTEXTUAL_SCREENTIP_SET #undef HEV_COLOR_GREEN #undef HEV_COLOR_RED diff --git a/modular_skyrat/modules/inflatables/code/inflatable.dm b/modular_skyrat/modules/inflatables/code/inflatable.dm index f58b4dde172..47cfef36777 100644 --- a/modular_skyrat/modules/inflatables/code/inflatable.dm +++ b/modular_skyrat/modules/inflatables/code/inflatable.dm @@ -53,11 +53,9 @@ return return ..() -/obj/structure/inflatable/AltClick(mob/user) - . = ..() - if(!user.can_interact_with(src)) - return +/obj/structure/inflatable/click_alt(mob/user) deflate(FALSE) + return CLICK_ACTION_SUCCESS /obj/structure/inflatable/play_attack_sound(damage_amount, damage_type, damage_flag) playsound(src, hit_sound, 75, TRUE) diff --git a/modular_skyrat/modules/knives/knives.dm b/modular_skyrat/modules/knives/knives.dm index 4a7439d3e76..d177a3a24d9 100644 --- a/modular_skyrat/modules/knives/knives.dm +++ b/modular_skyrat/modules/knives/knives.dm @@ -24,25 +24,26 @@ slot_flags = ITEM_SLOT_POCKETS w_class = WEIGHT_CLASS_BULKY resistance_flags = FLAMMABLE + interaction_flags_click = NEED_DEXTERITY /obj/item/storage/belt/bowie_sheath/Initialize(mapload) . = ..() atom_storage.max_slots = 1 atom_storage.max_total_storage = WEIGHT_CLASS_BULKY atom_storage.set_holdable(list( - /obj/item/knife/bowie + /obj/item/knife/bowie, )) -/obj/item/storage/belt/bowie_sheath/AltClick(mob/user) - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/item/storage/belt/bowie_sheath/click_alt(mob/user) if(length(contents)) var/obj/item/knife = contents[1] user.visible_message(span_notice("[user] takes [knife] out of [src]."), span_notice("You take [knife] out of [src].")) user.put_in_hands(knife) update_appearance() + return CLICK_ACTION_SUCCESS else to_chat(user, span_warning("[src] is empty!")) + return CLICK_ACTION_BLOCKING /obj/item/storage/belt/bowie_sheath/update_icon_state() icon_state = initial(icon_state) diff --git a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_pump.dm b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_pump.dm index 17d6c87f284..c97f06140c0 100644 --- a/modular_skyrat/modules/liquids/code/liquid_systems/liquid_pump.dm +++ b/modular_skyrat/modules/liquids/code/liquid_systems/liquid_pump.dm @@ -8,6 +8,7 @@ max_integrity = 500 anchored = FALSE resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF + interaction_flags_click = NEED_DEXTERITY /// How many reagents at maximum can it hold var/max_volume = 10000 /// Whether spewing reagents out, instead of siphoning them @@ -33,12 +34,11 @@ to_chat(user, span_notice("You turn [src] [turned_on ? "off" : "on"].")) toggle_working() -/obj/structure/liquid_pump/AltClick(mob/living/user) - if(!user.can_perform_action(src, NEED_DEXTERITY)) - return +/obj/structure/liquid_pump/click_alt(mob/living/user) to_chat(user, span_notice("You flick [src]'s spewing mode [spewing_mode ? "off" : "on"].")) spewing_mode = !spewing_mode update_icon() + return CLICK_ACTION_SUCCESS /obj/structure/liquid_pump/examine(mob/user) . = ..() diff --git a/modular_skyrat/modules/medical/code/anesthetic_machine.dm b/modular_skyrat/modules/medical/code/anesthetic_machine.dm index 69b1bab7b70..eb42dda946e 100644 --- a/modular_skyrat/modules/medical/code/anesthetic_machine.dm +++ b/modular_skyrat/modules/medical/code/anesthetic_machine.dm @@ -78,10 +78,9 @@ attached_tank = attacking_item update_icon() -/obj/machinery/anesthetic_machine/AltClick(mob/user) - . = ..() +/obj/machinery/anesthetic_machine/click_alt(mob/user) if(!attached_tank) - return + return CLICK_ACTION_BLOCKING attached_tank.forceMove(loc) to_chat(user, span_notice("You remove the [attached_tank].")) @@ -89,6 +88,7 @@ update_icon() if(mask_out) retract_mask() + return CLICK_ACTION_SUCCESS ///Retracts the attached_mask back into the machine /obj/machinery/anesthetic_machine/proc/retract_mask() diff --git a/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm b/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm index dfa8c97adea..6dce4269ef8 100644 --- a/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm +++ b/modular_skyrat/modules/microfusion/code/microfusion_energy_master.dm @@ -263,13 +263,12 @@ playsound(src, 'sound/items/crowbar.ogg', 70, TRUE) remove_emitter() -/obj/item/gun/microfusion/AltClick(mob/user) - . = ..() - if(can_interact(user)) - var/obj/item/microfusion_gun_attachment/to_remove = input(user, "Please select what part you'd like to remove.", "Remove attachment") as null|obj in sort_names(attachments) - if(!to_remove) - return - remove_attachment(to_remove, user) +/obj/item/gun/microfusion/click_alt(mob/user) + var/obj/item/microfusion_gun_attachment/to_remove = input(user, "Please select what part you'd like to remove.", "Remove attachment") as null|obj in sort_names(attachments) + if(!to_remove) + return CLICK_ACTION_BLOCKING + remove_attachment(to_remove, user) + return CLICK_ACTION_SUCCESS /obj/item/gun/microfusion/proc/remove_all_attachments() if(attachments.len) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/bdsm_mask.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/bdsm_mask.dm index 9d2fae9d311..ef18300ddab 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/bdsm_mask.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/bdsm_mask.dm @@ -72,20 +72,17 @@ "cyan" = image(icon = src.icon, icon_state = "mask_cyan_off")) // Using multitool on pole -/obj/item/clothing/mask/gas/bdsm_mask/AltClick(mob/user) +/obj/item/clothing/mask/gas/bdsm_mask/click_alt(mob/user) if(color_changed == FALSE) - if(.) - return var/choice = show_radial_menu(user, src, mask_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_mask_color = choice update_icon_state() update_icon() update_mob_action_buttonss() color_changed = TRUE - return - . = ..() + return CLICK_ACTION_SUCCESS // To check if we can change mask's model /obj/item/clothing/mask/gas/bdsm_mask/proc/check_menu(mob/living/user) @@ -321,6 +318,7 @@ volume = 50 possible_transfer_amounts = list(1, 2, 3, 4, 5, 10, 25, 50) list_reagents = list(/datum/reagent/drug/aphrodisiac/crocin = 50) + interaction_flags_click = NEED_DEXTERITY // Standard initialize code for filter /obj/item/reagent_containers/cup/lewd_filter/Initialize(mapload) @@ -337,11 +335,7 @@ addtimer(CALLBACK(reagents, TYPE_PROC_REF(/datum/reagents, trans_to), user, amount_per_transfer_from_this, TRUE, TRUE, FALSE, user, FALSE, INGEST), 0.5 SECONDS) // I just wanted to add 2th color variation. Because. -/obj/item/reagent_containers/cup/lewd_filter/AltClick(mob/user) - // Catch first AltClick and open reskin menu - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - return +/obj/item/reagent_containers/cup/lewd_filter/click_alt(mob/user) // After reskin all clicks go normal, but we can't change the flow rate if mask on and equipped var/obj/item/clothing/mask/gas/bdsm_mask/worn_mask = user.get_item_by_slot(ITEM_SLOT_MASK) if(worn_mask) @@ -350,8 +344,8 @@ if(worn_mask.mask_on == TRUE) if(istype(src, /obj/item/reagent_containers/cup/lewd_filter)) to_chat(user, span_warning("You can't change the flow rate of the valve while the mask is on!")) - return - . = ..() + return CLICK_ACTION_BLOCKING + return ..() // Filter click handling /obj/item/reagent_containers/cup/lewd_filter/attack_hand(mob/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/corset.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/corset.dm index 821e056bb94..f537d9166f8 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/corset.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/corset.dm @@ -17,10 +17,11 @@ /// Has it been laced tightly? var/laced_tight = FALSE -/obj/item/clothing/suit/corset/AltClick(mob/user) +/obj/item/clothing/suit/corset/click_alt(mob/user) laced_tight = !laced_tight to_chat(user, span_notice("You [laced_tight ? "tighten" : "loosen"] the corset, making it far [laced_tight ? "harder" : "easier"] to breathe.")) play_lewd_sound(user, laced_tight ? 'sound/items/handling/cloth_pickup.ogg' : 'sound/items/handling/cloth_drop.ogg', 40, TRUE) + . = CLICK_ACTION_SUCCESS if(laced_tight) slowdown = TIGHT_SLOWDOWN return diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/deprivation_helmet.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/deprivation_helmet.dm index a6188a546e4..7bcad0d4879 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/deprivation_helmet.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/deprivation_helmet.dm @@ -129,20 +129,18 @@ "tealn" = image(icon = src.icon, icon_state = "dephelmet_tealn")) // To change model -/obj/item/clothing/head/deprivation_helmet/AltClick(mob/user) +/obj/item/clothing/head/deprivation_helmet/click_alt(mob/user) if(color_changed == FALSE) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, helmet_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_helmet_color = choice update_icon() update_mob_action_buttonss() color_changed = TRUE + return CLICK_ACTION_SUCCESS else - return + return CLICK_ACTION_BLOCKING /obj/item/clothing/head/deprivation_helmet/proc/update_mob_action_buttonss() var/datum/action/item_action/action_button diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/hypnogoggles.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/hypnogoggles.dm index 554cfab890c..5150f7a539b 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/hypnogoggles.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/hypnogoggles.dm @@ -58,18 +58,16 @@ "teal" = image(icon = src.icon, icon_state = "hypnogoggles_teal")) //to change model -/obj/item/clothing/glasses/hypno/AltClick(mob/user) +/obj/item/clothing/glasses/hypno/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, hypnogoggles_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_hypnogoggles_color = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS //to check if we can change kinkphones's model /obj/item/clothing/glasses/hypno/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm index aa8ff23f1b6..025d267680e 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kink_collars.dm @@ -35,10 +35,7 @@ body_parts_covered = NECK slot_flags = ITEM_SLOT_NECK w_class = WEIGHT_CLASS_SMALL - /// What the name on the tag is - var/tagname = null - /// Item path of on-init creation in the collar's storage - var/treat_path = /obj/item/food/cookie + interaction_flags_click = NEED_DEXTERITY unique_reskin = list("Cyan" = "collar_cyan", "Yellow" = "collar_yellow", "Green" = "collar_green", @@ -50,6 +47,10 @@ "Black" = "collar_black", "Black-teal" = "collar_tealblack", "Spike" = "collar_spike") + /// What the name on the tag is + var/tagname = null + /// Item path of on-init creation in the collar's storage + var/treat_path = /obj/item/food/cookie //spawn thing in collar @@ -66,13 +67,6 @@ var/obj/item/key/kink_collar/collar_key = key collar_key.key_id = id -//reskin code - -/obj/item/clothing/neck/kink_collar/AltClick(mob/user) - . = ..() - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - //rename collar code /obj/item/clothing/neck/kink_collar/attack_self(mob/user) @@ -90,10 +84,7 @@ worn_icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/mob/lewd_clothing/lewd_neck.dmi' icon_state = "lock_collar_cyan" treat_path = /obj/item/key/kink_collar - /// If the collar is currently locked - var/locked = FALSE - /// If the collar has been broken or not - var/broken = FALSE + interaction_flags_click = NEED_DEXTERITY unique_reskin = list("Cyan" = "lock_collar_cyan", "Yellow" = "lock_collar_yellow", "Green" = "lock_collar_green", @@ -105,6 +96,10 @@ "Black" = "lock_collar_black", "Black-teal" = "lock_collar_tealblack", "Spike" = "lock_collar_spike") + /// If the collar is currently locked + var/locked = FALSE + /// If the collar has been broken or not + var/broken = FALSE /obj/item/clothing/neck/kink_collar/locked/Initialize(mapload) . = ..() @@ -112,13 +107,6 @@ //spawn thing in collar -//reskin code - -/obj/item/clothing/neck/kink_collar/locked/AltClick(mob/user) - . = ..() - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - //locking or unlocking collar code /obj/item/clothing/neck/kink_collar/locked/proc/IsLocked(to_lock, mob/user) @@ -176,6 +164,7 @@ icon = 'modular_skyrat/modules/modular_items/lewd_items/icons/obj/lewd_items/lewd_items.dmi' icon_state = "collar_key_metal" base_icon_state = "collar_key" + interaction_flags_click = NEED_DEXTERITY /// The name inscribed on the key var/keyname = null /// The ID of the key to pair with a collar. Will normally be the ref of the collar @@ -192,12 +181,6 @@ "Metal" = "collar_key_metal", "Black-teal" = "collar_key_tealblack") -//changing color of key in case if we using multiple collars -/obj/item/key/kink_collar/AltClick(mob/user) - . = ..() - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) - //changing name of key in case if we using multiple collars with same color /obj/item/key/kink_collar/attack_self(mob/user) keyname = stripped_input(user, "Would you like to change the name on the key?", "Renaming key", "Key", MAX_NAME_LEN) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_blindfold.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_blindfold.dm index 40a716f3175..6e1674fc87f 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_blindfold.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_blindfold.dm @@ -22,19 +22,16 @@ "teal" = image(icon = src.icon, icon_state = "kblindfold_teal")) //to change model -/obj/item/clothing/glasses/blindfold/kinky/AltClick(mob/user) +/obj/item/clothing/glasses/blindfold/kinky/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, kinkfold_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_kinkfold_color = choice update_icon() color_changed = TRUE - + return CLICK_ACTION_SUCCESS /// to check if we can change kinkphones's model /obj/item/clothing/glasses/blindfold/kinky/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_headphones.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_headphones.dm index eb4ed5bf142..8e73ea7d434 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_headphones.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_headphones.dm @@ -28,18 +28,16 @@ "teal" = image(icon = src.icon, icon_state = "kinkphones_teal_on")) //to change model -/obj/item/clothing/ears/kinky_headphones/AltClick(mob/user) +/obj/item/clothing/ears/kinky_headphones/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, kinkphones_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_kinkphones_color = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /// to check if we can change kinkphones's model /obj/item/clothing/ears/kinky_headphones/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_sleepbag.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_sleepbag.dm index 2f1588738cb..43e2ea0c904 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_sleepbag.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/kinky_sleepbag.dm @@ -48,23 +48,21 @@ "deflated" = image(icon = src.icon, icon_state = "sleepbag_teal_deflated_folded")) //to change model -/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/AltClick(mob/user) +/obj/item/clothing/suit/straight_jacket/kinky_sleepbag/click_alt(mob/user) var/mob/living/carbon/human/clicking_human = user if(istype(clicking_human.wear_suit, /obj/item/clothing/suit/straight_jacket/kinky_sleepbag)) to_chat(user, span_warning("Your hands are stuck, you can't do this!")) - return FALSE + return CLICK_ACTION_BLOCKING switch(color_changed) if(FALSE) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, bag_colors, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING bag_color = choice update_icon() update_icon_state() color_changed = TRUE + return CLICK_ACTION_SUCCESS if(TRUE) if(bag_state == "deflated") @@ -72,8 +70,10 @@ to_chat(user, span_notice("The sleeping bag now is [bag_fold? "folded." : "unfolded."]")) update_icon() update_icon_state() + return CLICK_ACTION_SUCCESS else to_chat(user, span_notice("You can't fold the bag while it's inflated!")) + return CLICK_ACTION_BLOCKING /obj/item/clothing/suit/straight_jacket/kinky_sleepbag/proc/check_menu(mob/living/user) if(!istype(user)) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_maid.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_maid.dm index 00caec39977..53b01b6699e 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_maid.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/lewd_maid.dm @@ -48,18 +48,16 @@ "yellow" = image (icon = src.icon, icon_state = "lewdapron_yellow")) //to change model -/obj/item/clothing/accessory/lewdapron/AltClick(mob/user) +/obj/item/clothing/accessory/lewdapron/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, apron_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /// to check if we can change kinkphones's model /obj/item/clothing/accessory/lewdapron/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shackles.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shackles.dm index e062af59494..5201bb02a35 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shackles.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shackles.dm @@ -33,19 +33,17 @@ "metal" = image (icon = src.icon, icon_state = "shackles_metal")) //to change model -/obj/item/clothing/suit/straight_jacket/shackles/AltClick(mob/user) - if(color_changed == FALSE) - . = ..() - if(.) - return - var/choice = show_radial_menu(user, src, shackles_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) - if(!choice) - return FALSE - current_color = choice - update_icon() - color_changed = TRUE - else - return +/obj/item/clothing/suit/straight_jacket/shackles/click_alt(mob/user) + if(color_changed) + return CLICK_ACTION_BLOCKING + + var/choice = show_radial_menu(user, src, shackles_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) + if(!choice) + return CLICK_ACTION_BLOCKING + current_color = choice + update_icon() + color_changed = TRUE + return CLICK_ACTION_SUCCESS //to check if we can change shackles' model /obj/item/clothing/suit/straight_jacket/shackles/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm index b248a0aca88..866526dea48 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/shibari_worn_uniform.dm @@ -60,13 +60,12 @@ . = ..() AddElement(/datum/element/update_icon_updates_onmob) -/obj/item/clothing/under/shibari/AltClick(mob/user) - . = ..() +/obj/item/clothing/under/shibari/click_alt(mob/user) if(!ishuman(loc)) - return + return CLICK_ACTION_BLOCKING var/mob/living/carbon/human/hooman = loc if(user == hooman) - return + return CLICK_ACTION_BLOCKING switch(tightness) if(SHIBARI_TIGHTNESS_LOW) tightness = SHIBARI_TIGHTNESS_MED @@ -74,6 +73,7 @@ tightness = SHIBARI_TIGHTNESS_HIGH if(SHIBARI_TIGHTNESS_HIGH) tightness = SHIBARI_TIGHTNESS_LOW + return CLICK_ACTION_SUCCESS /obj/item/clothing/under/shibari/process(seconds_per_tick) if(!ishuman(loc)) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm index ec2fc7c0d9b..a7e1182d324 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/strapon.dm @@ -23,19 +23,16 @@ "human" = image (icon = src.icon, icon_state = "strapon_human")) //to change model -/obj/item/clothing/strapon/AltClick(mob/user) - if(type_changed == FALSE) - . = ..() - if(.) - return - var/choice = show_radial_menu(user, src, strapon_types, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) - if(!choice) - return FALSE - strapon_type = choice - update_icon() - type_changed = TRUE - else - return +/obj/item/clothing/strapon/click_alt(mob/user) + if(type_changed) + return CLICK_ACTION_BLOCKING + var/choice = show_radial_menu(user, src, strapon_types, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) + if(!choice) + return CLICK_ACTION_BLOCKING + strapon_type = choice + update_icon() + type_changed = TRUE + return CLICK_ACTION_SUCCESS //Check if we can change strapon's model /obj/item/clothing/strapon/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/stripper_outfit.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/stripper_outfit.dm index 545748165c4..f2de5f7f900 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/stripper_outfit.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_clothing/stripper_outfit.dm @@ -11,6 +11,7 @@ can_adjust = FALSE icon_state = "stripper_cyan" inhand_icon_state = "b_suit" + interaction_flags_click = NEED_DEXTERITY unique_reskin = list("Cyan" = "stripper_cyan", "Yellow" = "stripper_yellow", "Green" = "stripper_green", @@ -21,8 +22,3 @@ "Purple" = "stripper_purple", "Black" = "stripper_black", "Black-teal" = "stripper_tealblack") - -/obj/item/clothing/under/stripper_outfit/AltClick(mob/user) - . = ..() - if(unique_reskin && !current_skin && user.can_perform_action(src, NEED_DEXTERITY)) - reskin_obj(user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm index 13423a26016..1db92a08430 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/attachable_vibrator.dm @@ -39,11 +39,8 @@ "pink" = image(icon = src.icon, icon_state = "[initial(base_icon_state)]_pink_low[(istype(src, /obj/item/clothing/sextoy/eggvib/signalvib)) ? "_on" : ""]"), "teal" = image(icon = src.icon, icon_state = "[initial(base_icon_state)]_teal_low[(istype(src, /obj/item/clothing/sextoy/eggvib/signalvib)) ? "_on" : ""]")) -/obj/item/clothing/sextoy/eggvib/AltClick(mob/user) +/obj/item/clothing/sextoy/eggvib/click_alt(mob/user) if(!color_changed) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, vib_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) return FALSE @@ -63,6 +60,7 @@ to_chat(user, span_notice("You turn off the vibrating egg. Fun time's over.")) update_icon() update_icon_state() + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/eggvib/Initialize(mapload) . = ..() @@ -190,18 +188,18 @@ //arousal stuff -/obj/item/clothing/sextoy/eggvib/signalvib/AltClick(mob/user) +/obj/item/clothing/sextoy/eggvib/signalvib/click_alt(mob/user) if(!color_changed) var/choice = show_radial_menu(user, src, vib_designs, custom_check = CALLBACK(src, /obj/item/clothing/sextoy/proc/check_menu, user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() color_changed = TRUE else if(!toy_on) - to_chat(usr, span_notice("You can't switch modes while the vibrating egg is turned off!")) - return + to_chat(user, span_notice("You can't switch modes while the vibrating egg is turned off!")) + return CLICK_ACTION_BLOCKING toggle_mode() soundloop1.stop() soundloop2.stop() @@ -218,6 +216,7 @@ soundloop3.start() update_icon() update_icon_state() + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/eggvib/signalvib/receive_signal(datum/signal/signal) if(!signal || signal.data["code"] != code) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm index 7297472b8da..5bcc5129e03 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/buttplug.dm @@ -40,31 +40,27 @@ "medium" = image(icon = src.icon, icon_state = "buttplug_pink_medium"), "big" = image(icon = src.icon, icon_state = "buttplug_pink_big")) -/obj/item/clothing/sextoy/buttplug/AltClick(mob/user) +/obj/item/clothing/sextoy/buttplug/click_alt(mob/user) if(!color_changed) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, buttplug_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() if(choice == "green") set_light(0.25) update_light() color_changed = TRUE + return CLICK_ACTION_SUCCESS else if(form_changed == FALSE) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, buttplug_forms, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_size = choice update_icon() form_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/buttplug/Initialize(mapload) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm index c7256be1610..9feeb963a09 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/dildo.dm @@ -41,18 +41,16 @@ "human" = image(icon = src.icon, icon_state = "[base_icon_state]_human"), "tentacle" = image(icon = src.icon, icon_state = "[base_icon_state]_tentacle")) -/obj/item/clothing/sextoy/dildo/AltClick(mob/user) +/obj/item/clothing/sextoy/dildo/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, dildo_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_type = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/dildo/Initialize(mapload) . = ..() @@ -209,23 +207,22 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors "medium" = image(icon = src.icon, icon_state = "[base_icon_state]_medium"), "big" = image(icon = src.icon, icon_state = "[base_icon_state]_big")) -/obj/item/clothing/sextoy/dildo/custom_dildo/AltClick(mob/living/user) +/obj/item/clothing/sextoy/dildo/custom_dildo/click_alt(mob/living/user) if(!size_changed) var/choice = show_radial_menu(user, src, dildo_sizes, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING poly_size = choice update_icon() size_changed = TRUE - else if(color_changed) - return + return CLICK_ACTION_BLOCKING if(!istype(user) || !user.can_perform_action(src, FORBID_TELEKINESIS_REACH)) - return + return CLICK_ACTION_BLOCKING customize(user) color_changed = TRUE - return TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/dildo/custom_dildo/Initialize(mapload) . = ..() @@ -292,8 +289,8 @@ GLOBAL_LIST_INIT(dildo_colors, list(//mostly neon colors /obj/item/clothing/sextoy/dildo/double_dildo/populate_dildo_designs() return -/obj/item/clothing/sextoy/dildo/double_dildo/AltClick(mob/user) - return +/obj/item/clothing/sextoy/dildo/double_dildo/click_alt(mob/user) + return NONE /obj/item/clothing/sextoy/dildo/double_dildo/lewd_equipped(mob/living/carbon/human/user, slot, initial) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm index 3099ce8c3d2..02b25ecefd4 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/fleshlight.dm @@ -39,18 +39,16 @@ icon_state = "[base_icon_state]_[current_color]" inhand_icon_state = "[base_icon_state]_[current_color]" -/obj/item/clothing/sextoy/fleshlight/AltClick(mob/user) +/obj/item/clothing/sextoy/fleshlight/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, fleshlight_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/fleshlight/attack(mob/living/carbon/human/target, mob/living/carbon/human/user) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm index 6e67c51fe34..21328dcea3a 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/kinky_shocker.dm @@ -69,8 +69,9 @@ to_chat(user, span_notice("You install a cell in [src].")) update_appearance() -/obj/item/kinky_shocker/AltClick(mob/user) +/obj/item/kinky_shocker/click_alt(mob/user) tryremovecell(user) + return CLICK_ACTION_SUCCESS /obj/item/kinky_shocker/proc/tryremovecell(mob/user) if(!(cell && can_remove_cell)) @@ -81,6 +82,7 @@ to_chat(user, span_notice("You remove the cell from [src].")) shocker_on = FALSE update_appearance() + return CLICK_ACTION_SUCCESS /obj/item/kinky_shocker/attack_self(mob/user) toggle_shocker(user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm index 83ac7d1ca0b..83beedd5605 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/leather_whip.dm @@ -88,32 +88,27 @@ update_overlays() //to change color -/obj/item/clothing/mask/leatherwhip/AltClick(mob/user) +/obj/item/clothing/mask/leatherwhip/click_alt(mob/user) if(!color_changed) - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, whip_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_whip_color = choice update_icon() update_icon_state() color_changed = TRUE - + return CLICK_ACTION_SUCCESS else if(form_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, whip_forms, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_whip_form = choice update_icon() update_icon_state() form_changed = TRUE + return CLICK_ACTION_SUCCESS /// A check to see if the radial menu can be used /obj/item/clothing/mask/leatherwhip/proc/check_menu(mob/living/user) diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm index dcab8ba06e4..facf60ea273 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/spanking_pad.dm @@ -43,19 +43,17 @@ icon_state = "[base_icon_state]_[current_color]" inhand_icon_state = "[base_icon_state]_[current_color]" -/obj/item/spanking_pad/AltClick(mob/user) +/obj/item/spanking_pad/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, spankpad_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon_state() update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/spanking_pad/attack(mob/living/carbon/human/target, mob/living/carbon/human/user) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm index fe0582abea5..3d24a3b649e 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/torture_candle.dm @@ -108,23 +108,24 @@ open_flame() update_brightness() -/obj/item/bdsm_candle/AltClick(mob/user) - . = ..() +/obj/item/bdsm_candle/click_alt(mob/user) if(!lit) if(color_changed) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, candle_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice light_color = candlelights[choice] update_icon() update_brightness() color_changed = TRUE + return CLICK_ACTION_SUCCESS else if(!put_out_candle()) - return + return CLICK_ACTION_BLOCKING user.visible_message(span_notice("[user] snuffs [src].")) + return CLICK_ACTION_SUCCESS /* * WAX DROPPING diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm index c8174fe64e5..35b8ce8855e 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibrator.dm @@ -42,19 +42,17 @@ "yellow" = image(icon = src.icon, icon_state = "vibrator_yellow_low"), "green" = image(icon = src.icon, icon_state = "vibrator_green_low")) -/obj/item/clothing/sextoy/vibrator/AltClick(mob/user) +/obj/item/clothing/sextoy/vibrator/click_alt(mob/user) if(color_changed) return - . = ..() - if(.) - return var/choice = show_radial_menu(user, src, vibrator_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon_state() update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/vibrator/Initialize(mapload) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm index dd531231d24..03b1ca6ade5 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_items/vibroring.dm @@ -36,18 +36,16 @@ "pink" = image(icon = src.icon, icon_state = "vibroring_pink_off"), "teal" = image(icon = src.icon, icon_state = "vibroring_teal_off")) -/obj/item/clothing/sextoy/vibroring/AltClick(mob/user) +/obj/item/clothing/sextoy/vibroring/click_alt(mob/user) if(color_changed) - return - . = ..() - if(.) - return + return CLICK_ACTION_BLOCKING var/choice = show_radial_menu(user, src, vibroring_designs, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() color_changed = TRUE + return CLICK_ACTION_SUCCESS /obj/item/clothing/sextoy/vibroring/Initialize(mapload) . = ..() diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm index fb6f72e832e..3833fdb8edb 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_machinery/milking_machine.dm @@ -310,7 +310,7 @@ return FALSE replace_beaker(user, used_container) - updateUsrDialog() + SStgui.update_uis(src) return TRUE // Beaker change handler @@ -586,7 +586,7 @@ data["current_vagina"] = current_vagina = null data["machine_color"] = machine_color - updateUsrDialog() + SStgui.update_uis(src) return data // User action handler in the interface diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/dancing_pole.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/dancing_pole.dm index e88b76206b9..6ba55958fe6 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/dancing_pole.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/dancing_pole.dm @@ -31,7 +31,8 @@ "green" = COLOR_GREEN, "white" = COLOR_WHITE, ) - + /// Is the pole in use currently? + var/pole_in_use /obj/structure/stripper_pole/examine(mob/user) . = ..() @@ -65,13 +66,14 @@ // Alt-click to turn the lights on or off. -/obj/structure/stripper_pole/AltClick(mob/user) +/obj/structure/stripper_pole/click_alt(mob/user) lights_enabled = !lights_enabled balloon_alert(user, "lights [lights_enabled ? "on" : "off"]") playsound(user, lights_enabled ? 'sound/weapons/magin.ogg' : 'sound/weapons/magout.ogg', 40, TRUE) update_icon_state() update_icon() update_brightness() + return CLICK_ACTION_SUCCESS /obj/structure/stripper_pole/Initialize(mapload) @@ -98,17 +100,17 @@ . = ..() if(.) return - if(obj_flags & IN_USE) + if(pole_in_use) balloon_alert(user, "already in use!") return - obj_flags |= IN_USE + pole_in_use = TRUE dancer = user user.setDir(SOUTH) user.Stun(10 SECONDS) user.forceMove(loc) user.visible_message(pick(span_purple("[user] dances on [src]!"), span_purple("[user] flexes their hip-moving skills on [src]!"))) dance_animate(user) - obj_flags &= ~IN_USE + pole_in_use = FALSE user.pixel_y = 0 user.pixel_z = pseudo_z_axis //incase we are off it when we jump on! dancer = null diff --git a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/pillow.dm b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/pillow.dm index 0ac4d57fafb..7ee77500f7a 100644 --- a/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/pillow.dm +++ b/modular_skyrat/modules/modular_items/lewd_items/code/lewd_structures/pillow.dm @@ -34,32 +34,28 @@ "square" = image (icon = src.icon, icon_state = "pillow_pink_square"), "round" = image(icon = src.icon, icon_state = "pillow_pink_round")) -/obj/item/fancy_pillow/AltClick(mob/user) - if(color_changed == FALSE) - . = ..() - if(.) - return +/obj/item/fancy_pillow/click_alt(mob/user) + if(!color_changed) var/choice = show_radial_menu(user, src, pillow_colors, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) if(!choice) - return FALSE + return CLICK_ACTION_BLOCKING current_color = choice update_icon() color_changed = TRUE update_icon_state() - if(color_changed == TRUE) - if(form_changed == FALSE) - . = ..() - if(.) - return - var/choice = show_radial_menu(user, src, pillow_forms, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) - if(!choice) - return FALSE - current_form = choice - update_icon() - form_changed = TRUE - update_icon_state() + return CLICK_ACTION_SUCCESS else - return + if(form_changed) + return CLICK_ACTION_BLOCKING + var/choice = show_radial_menu(user, src, pillow_forms, custom_check = CALLBACK(src, PROC_REF(check_menu), user), radius = 36, require_near = TRUE) + if(!choice) + return CLICK_ACTION_BLOCKING + current_form = choice + update_icon() + form_changed = TRUE + update_icon_state() + return CLICK_ACTION_SUCCESS + //to check if we can change pillow's model /obj/item/fancy_pillow/proc/check_menu(mob/living/user) @@ -185,7 +181,7 @@ //picking up the pillow -/obj/structure/bed/pillow_tiny/AltClick(mob/user) +/obj/structure/bed/pillow_tiny/click_alt(mob/user) to_chat(user, span_notice("You pick up [src].")) var/obj/item/fancy_pillow/taken_pillow = new() user.put_in_hands(taken_pillow) @@ -198,6 +194,7 @@ taken_pillow.update_icon_state() taken_pillow.update_icon() qdel(src) + return CLICK_ACTION_SUCCESS //when we lay on it @@ -308,7 +305,7 @@ icon_state = "[base_icon_state]_[current_color]" //Removing pillow from a pile -/obj/structure/chair/pillow_small/AltClick(mob/user) +/obj/structure/chair/pillow_small/click_alt(mob/user) to_chat(user, span_notice("You take [src] from the pile.")) var/obj/item/fancy_pillow/taken_pillow = new() var/obj/structure/bed/pillow_tiny/pillow_pile = new(get_turf(src)) @@ -330,6 +327,7 @@ pillow_pile.update_icon_state() pillow_pile.update_icon() qdel(src) + return CLICK_ACTION_SUCCESS //Upgrading pillow pile to a PILLOW PILE! /obj/structure/chair/pillow_small/attackby(obj/item/used_item, mob/living/user, params) @@ -437,7 +435,7 @@ icon_state = "[base_icon_state]_[current_color]" //Removing pillow from a pile -/obj/structure/bed/pillow_large/AltClick(mob/user) +/obj/structure/bed/pillow_large/click_alt(mob/user) to_chat(user, span_notice("You take [src] from the pile.")) var/obj/item/fancy_pillow/taken_pillow = new() var/obj/structure/chair/pillow_small/pillow_pile = new(get_turf(src)) @@ -464,3 +462,4 @@ pillow_pile.update_icon_state() pillow_pile.update_icon() qdel(src) + return CLICK_ACTION_SUCCESS diff --git a/modular_skyrat/modules/modular_weapons/code/gunsets.dm b/modular_skyrat/modules/modular_weapons/code/gunsets.dm index 7373e0fa729..09b523f2c86 100644 --- a/modular_skyrat/modules/modular_weapons/code/gunsets.dm +++ b/modular_skyrat/modules/modular_weapons/code/gunsets.dm @@ -30,10 +30,10 @@ else icon_state = initial(icon_state) -/obj/item/storage/toolbox/guncase/skyrat/AltClick(mob/user) - . = ..() +/obj/item/storage/toolbox/guncase/skyrat/click_alt(mob/user) opened = !opened update_icon() + return CLICK_ACTION_SUCCESS /obj/item/storage/toolbox/guncase/skyrat/attack_self(mob/user) . = ..() diff --git a/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm b/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm index d631246666c..01eda1e746d 100644 --- a/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm +++ b/modular_skyrat/modules/mounted_machine_gun/code/mounted_machine_gun.dm @@ -171,11 +171,9 @@ update_positioning() -/obj/machinery/mounted_machine_gun/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return +/obj/machinery/mounted_machine_gun/click_alt(mob/user) toggle_cover(user) + return CLICK_ACTION_SUCCESS /obj/machinery/mounted_machine_gun/attack_hand_secondary(mob/living/user, list/modifiers) . = ..() diff --git a/modular_skyrat/modules/mutants/code/mutant_cure.dm b/modular_skyrat/modules/mutants/code/mutant_cure.dm index 5bdff67d152..59fdca63325 100644 --- a/modular_skyrat/modules/mutants/code/mutant_cure.dm +++ b/modular_skyrat/modules/mutants/code/mutant_cure.dm @@ -219,8 +219,6 @@ if(machine_stat & (NOPOWER|BROKEN|MAINT)) return - usr.set_machine(src) - var/operation = href_list["function"] var/obj/item/process = locate(href_list["item"]) in src @@ -230,7 +228,7 @@ else if(operation == "eject") ejectItem() else if(operation == "refresh") - updateUsrDialog() + SStgui.update_uis(src) else if(status != STATUS_IDLE) to_chat(usr, span_warning("[src] is currently recombinating!")) @@ -246,7 +244,7 @@ recombinate_start() use_energy(3000 JOULES) - updateUsrDialog() + SStgui.update_uis(src) /obj/machinery/rnd/rna_recombinator/proc/ejectItem() if(loaded_item) diff --git a/modular_skyrat/modules/pollution/code/perfumes.dm b/modular_skyrat/modules/pollution/code/perfumes.dm index 2f07404ae76..80ccf1c253e 100644 --- a/modular_skyrat/modules/pollution/code/perfumes.dm +++ b/modular_skyrat/modules/pollution/code/perfumes.dm @@ -34,8 +34,9 @@ if(has_cap) . += span_notice("Alt-click [src] to [ cap ? "take the cap off" : "put the cap on"].") -/obj/item/perfume/AltClick(mob/user) +/obj/item/perfume/click_alt(mob/user) toggle_cap(user) + return CLICK_ACTION_SUCCESS /obj/item/perfume/attack_self(mob/user, modifiers) toggle_cap(user) diff --git a/modular_skyrat/modules/primitive_cooking_additions/code/big_mortar.dm b/modular_skyrat/modules/primitive_cooking_additions/code/big_mortar.dm index 4860aed6331..282369b1e85 100644 --- a/modular_skyrat/modules/primitive_cooking_additions/code/big_mortar.dm +++ b/modular_skyrat/modules/primitive_cooking_additions/code/big_mortar.dm @@ -33,13 +33,14 @@ drop_everything_contained() return ..() -/obj/structure/large_mortar/AltClick(mob/user) +/obj/structure/large_mortar/click_alt(mob/user) if(!length(contents)) balloon_alert(user, "nothing inside") - return + return CLICK_ACTION_BLOCKING drop_everything_contained() balloon_alert(user, "removed all items") + return CLICK_ACTION_SUCCESS /// Drops all contents at the mortar /obj/structure/large_mortar/proc/drop_everything_contained() diff --git a/modular_skyrat/modules/primitive_cooking_additions/code/cutting_board.dm b/modular_skyrat/modules/primitive_cooking_additions/code/cutting_board.dm index a71e4c7d35a..e677a617817 100644 --- a/modular_skyrat/modules/primitive_cooking_additions/code/cutting_board.dm +++ b/modular_skyrat/modules/primitive_cooking_additions/code/cutting_board.dm @@ -57,13 +57,14 @@ drop_everything_contained() return ..() -/obj/item/cutting_board/AltClick(mob/user) +/obj/item/cutting_board/click_alt(mob/user) if(!length(contents)) balloon_alert(user, "nothing on board") - return + return CLICK_ACTION_BLOCKING drop_everything_contained() balloon_alert(user, "cleared board") + return CLICK_ACTION_SUCCESS ///Drops all contents at the turf of the item /obj/item/cutting_board/proc/drop_everything_contained() diff --git a/modular_skyrat/modules/primitive_cooking_additions/code/millstone.dm b/modular_skyrat/modules/primitive_cooking_additions/code/millstone.dm index 57b5d23e385..76a21851856 100644 --- a/modular_skyrat/modules/primitive_cooking_additions/code/millstone.dm +++ b/modular_skyrat/modules/primitive_cooking_additions/code/millstone.dm @@ -51,13 +51,14 @@ transfer_fingerprints_to(stone) return ..() -/obj/structure/millstone/AltClick(mob/user) +/obj/structure/millstone/click_alt(mob/user) if(!length(contents)) - balloon_alert(user, "nothing inside") - return + balloon_alert(user, "nothing inside!") + return CLICK_ACTION_BLOCKING drop_everything_contained() balloon_alert(user, "removed all items") + return CLICK_ACTION_SUCCESS /obj/structure/millstone/CtrlShiftClick(mob/user) set_anchored(!anchored) diff --git a/modular_skyrat/modules/primitive_production/code/glassblowing.dm b/modular_skyrat/modules/primitive_production/code/glassblowing.dm index ed7b4a31ff8..59198aa1112 100644 --- a/modular_skyrat/modules/primitive_production/code/glassblowing.dm +++ b/modular_skyrat/modules/primitive_production/code/glassblowing.dm @@ -227,7 +227,6 @@ if(!Adjacent(usr)) return add_fingerprint(usr) - usr.set_machine(src) var/obj/item/glassblowing/molten_glass/glass = glass_ref?.resolve() var/actioning_speed = usr.mind.get_skill_modifier(/datum/skill/production, SKILL_SPEED_MODIFIER) * DEFAULT_TIMED diff --git a/modular_skyrat/modules/salon/code/hair_tie.dm b/modular_skyrat/modules/salon/code/hair_tie.dm index 2b490ec749b..73b1a28d93b 100644 --- a/modular_skyrat/modules/salon/code/hair_tie.dm +++ b/modular_skyrat/modules/salon/code/hair_tie.dm @@ -86,16 +86,16 @@ user.set_hairstyle(actual_hairstyle, update = TRUE) actual_hairstyle = null -/obj/item/clothing/head/hair_tie/AltClick(mob/living/user) - . = ..() +/obj/item/clothing/head/hair_tie/click_alt(mob/living/user) if(!(user.get_slot_by_item(src) == ITEM_SLOT_HANDS)) balloon_alert(user, "hold in-hand!") - return + return CLICK_ACTION_BLOCKING user.visible_message( span_danger("[user.name] puts [src] around [user.p_their()] fingers, beginning to flick it!"), span_notice("You try to flick [src]!"), ) flick_hair_tie(user) + return CLICK_ACTION_SUCCESS ///This proc flicks the hair tie out of the player's hand, tripping the target hit for 1 second /obj/item/clothing/head/hair_tie/proc/flick_hair_tie(mob/living/user) @@ -143,7 +143,7 @@ ) build_path = /obj/item/clothing/head/hair_tie/plastic_beads category = list( - RND_CATEGORY_INITIAL, + RND_CATEGORY_INITIAL, RND_CATEGORY_EQUIPMENT + RND_SUBCATEGORY_EQUIPMENT_SERVICE, ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE diff --git a/modular_skyrat/modules/self_actualization_device/code/self_actualization_device.dm b/modular_skyrat/modules/self_actualization_device/code/self_actualization_device.dm index e3b61e0f3e2..fb54222abdb 100644 --- a/modular_skyrat/modules/self_actualization_device/code/self_actualization_device.dm +++ b/modular_skyrat/modules/self_actualization_device/code/self_actualization_device.dm @@ -80,15 +80,15 @@ . = ..() . += span_notice("ALT-Click to turn ON when closed.") -/obj/machinery/self_actualization_device/AltClick(mob/user) - . = ..() +/obj/machinery/self_actualization_device/click_alt(mob/user) if(!powered() || !occupant || state_open) - return FALSE + return CLICK_ACTION_BLOCKING to_chat(user, "You power on [src].") addtimer(CALLBACK(src, PROC_REF(eject_new_you)), processing_time, TIMER_OVERRIDE|TIMER_UNIQUE) processing = TRUE update_appearance() + return CLICK_ACTION_SUCCESS /obj/machinery/self_actualization_device/container_resist_act(mob/living/user) if(state_open) diff --git a/modular_skyrat/modules/specialist_armor/code/sacrificial.dm b/modular_skyrat/modules/specialist_armor/code/sacrificial.dm index 45643140f83..71bbf99db4d 100644 --- a/modular_skyrat/modules/specialist_armor/code/sacrificial.dm +++ b/modular_skyrat/modules/specialist_armor/code/sacrificial.dm @@ -79,8 +79,9 @@ QDEL_NULL(face_shield) return ..() -/obj/item/clothing/head/helmet/sf_sacrificial/AltClick(mob/user) +/obj/item/clothing/head/helmet/sf_sacrificial/click_alt(mob/user) remove_face_shield(user) + return CLICK_ACTION_SUCCESS /// Attached the passed face shield to the helmet. /obj/item/clothing/head/helmet/sf_sacrificial/proc/add_face_shield(mob/living/carbon/human/user, obj/shield_in_question) diff --git a/modular_skyrat/modules/stasisrework/code/stasissleeper.dm b/modular_skyrat/modules/stasisrework/code/stasissleeper.dm index 3a909a7d87a..abb7437d6f6 100644 --- a/modular_skyrat/modules/stasisrework/code/stasissleeper.dm +++ b/modular_skyrat/modules/stasisrework/code/stasissleeper.dm @@ -13,6 +13,7 @@ var/last_stasis_sound = FALSE fair_market_price = 10 payment_department = ACCOUNT_MED + interaction_flags_click = ALLOW_SILICON_REACH /obj/machinery/stasissleeper/Destroy() . = ..() @@ -53,9 +54,7 @@ playsound(src, 'sound/machines/synth_no.ogg', 50, TRUE, frequency = sound_freq) last_stasis_sound = _running -/obj/machinery/stasissleeper/AltClick(mob/user) - if(!user.can_perform_action(src, ALLOW_SILICON_REACH)) - return +/obj/machinery/stasissleeper/click_alt(mob/user) if(!panel_open) user.visible_message(span_notice("\The [src] [state_open ? "hisses as it seals shut." : "hisses as it swings open."]."), \ span_notice("You [state_open ? "close" : "open"] \the [src]."), \ @@ -65,6 +64,8 @@ else open_machine() + return CLICK_ACTION_SUCCESS + /obj/machinery/stasissleeper/Exited(atom/movable/AM, atom/newloc) if(!state_open && AM == occupant) container_resist_act(AM) diff --git a/modular_skyrat/modules/stone/code/stone.dm b/modular_skyrat/modules/stone/code/stone.dm index 0ae23c39c45..ac36480ac79 100644 --- a/modular_skyrat/modules/stone/code/stone.dm +++ b/modular_skyrat/modules/stone/code/stone.dm @@ -11,7 +11,6 @@ resistance_flags = FIRE_PROOF merge_type = /obj/item/stack/sheet/mineral/stone grind_results = null - point_value = 0 material_type = /datum/material/stone matter_amount = 0 source = null diff --git a/modular_skyrat/modules/time_clock/code/console.dm b/modular_skyrat/modules/time_clock/code/console.dm index 2d4891366a1..8b399d94dec 100644 --- a/modular_skyrat/modules/time_clock/code/console.dm +++ b/modular_skyrat/modules/time_clock/code/console.dm @@ -77,14 +77,11 @@ MAPPING_DIRECTIONAL_HELPERS(/obj/machinery/time_clock, 28) playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE) return TRUE -/obj/machinery/time_clock/AltClick(mob/user) - . = ..() - if(!Adjacent(user)) - to_chat(user, span_warning("You are out of range of the [src]!")) - return FALSE - +/obj/machinery/time_clock/click_alt(mob/user) if(!eject_inserted_id(user)) - return FALSE + return CLICK_ACTION_BLOCKING + + return CLICK_ACTION_SUCCESS ///Ejects the ID stored inside of the parent machine, if there is one. /obj/machinery/time_clock/proc/eject_inserted_id(mob/recepient) diff --git a/modular_skyrat/modules/wargame_projectors/code/projectors.dm b/modular_skyrat/modules/wargame_projectors/code/projectors.dm index 49c3d46847a..fe34b3a68af 100644 --- a/modular_skyrat/modules/wargame_projectors/code/projectors.dm +++ b/modular_skyrat/modules/wargame_projectors/code/projectors.dm @@ -86,7 +86,7 @@ /obj/item/wargame_projector/attack_self(mob/user) select_hologram(user) -/obj/item/wargame_projector/AltClick(mob/user) +/obj/item/wargame_projector/click_alt(mob/user) var/selected_color = tgui_input_list(user, "Select a color", "Color Selection", color_options) if(isnull(selected_color)) balloon_alert(user, "no color change") @@ -95,6 +95,7 @@ holosign_color = color_to_set_to balloon_alert(user, "color changed") set_greyscale(holosign_color) + return CLICK_ACTION_SUCCESS /obj/item/wargame_projector/CtrlClick(mob/user) if(tgui_alert(usr,"Clear all currently active holograms?", "Hologram Removal", list("Yes", "No")) == "Yes") diff --git a/sound/attributions.txt b/sound/attributions.txt index 39f206e982a..be8df17c535 100644 --- a/sound/attributions.txt +++ b/sound/attributions.txt @@ -143,3 +143,7 @@ where refinery and wooping_teleport are modifications of that same electric dril paper_flip.ogg is made by gynation from Freesound.org, CC0 https://freesound.org/people/gynation/sounds/82378/ + +nightmare_poof.ogg and nightmare_reappear.ogg are comprised of breath_01.wav by Nightflame (CC0) and slide-click.wav by Qat (CC0) +https://freesound.org/people/Qat/sounds/108332/ +https://freesound.org/people/Nightflame/sounds/368615/ diff --git a/sound/effects/nightmare_poof.ogg b/sound/effects/nightmare_poof.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e8b44685ba43819bce614b2f7cf88495ca0203d1 GIT binary patch literal 15537 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 S_e{LSsmXWM&ijP#0sJo=Ii@H8 literal 0 HcmV?d00001 diff --git a/sound/effects/nightmare_reappear.ogg b/sound/effects/nightmare_reappear.ogg new file mode 100644 index 0000000000000000000000000000000000000000..3dec20064602a8b79bae109336eeea2a9c49d375 GIT binary patch literal 18238 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: (input: T, index: number, collection: T[]) => boolean) => - (collection: T[]): T[] => { - if (collection === null || collection === undefined) { - return collection; - } - if (Array.isArray(collection)) { - const result: T[] = []; - for (let i = 0; i < collection.length; i++) { - const item = collection[i]; - if (iterateeFn(item, i, collection)) { - result.push(item); - } +export const filter = ( + collection: T[], + iterateeFn: (input: T, index: number, collection: T[]) => boolean, +): T[] => { + if (collection === null || collection === undefined) { + return collection; + } + if (Array.isArray(collection)) { + const result: T[] = []; + for (let i = 0; i < collection.length; i++) { + const item = collection[i]; + if (iterateeFn(item, i, collection)) { + result.push(item); } - return result; } - throw new Error(`filter() can't iterate on type ${typeof collection}`); - }; + return result; + } + throw new Error(`filter() can't iterate on type ${typeof collection}`); +}; type MapFunction = { ( + collection: T[], iterateeFn: (value: T, index: number, collection: T[]) => U, - ): (collection: T[]) => U[]; + ): U[]; ( + collection: Record, iterateeFn: (value: T, index: K, collection: Record) => U, - ): (collection: Record) => U[]; + ): U[]; }; /** @@ -49,44 +52,30 @@ type MapFunction = { * If collection is 'null' or 'undefined', it will be returned "as is" * without emitting any errors (which can be useful in some cases). */ -export const map: MapFunction = - (iterateeFn) => - (collection: T[]): U[] => { - if (collection === null || collection === undefined) { - return collection; - } - - if (Array.isArray(collection)) { - return collection.map(iterateeFn); - } - - if (typeof collection === 'object') { - return Object.entries(collection).map(([key, value]) => { - return iterateeFn(value, key, collection); - }); - } - - throw new Error(`map() can't iterate on type ${typeof collection}`); - }; - -/** - * Given a collection, will run each element through an iteratee function. - * Will then filter out undefined values. - */ -export const filterMap = ( - collection: T[], - iterateeFn: (value: T) => U | undefined, -): U[] => { - const finalCollection: U[] = []; - - for (const value of collection) { - const output = iterateeFn(value); - if (output !== undefined) { - finalCollection.push(output); - } +export const map: MapFunction = (collection, iterateeFn) => { + if (collection === null || collection === undefined) { + return collection; } - return finalCollection; + if (Array.isArray(collection)) { + const result: unknown[] = []; + for (let i = 0; i < collection.length; i++) { + result.push(iterateeFn(collection[i], i, collection)); + } + return result; + } + + if (typeof collection === 'object') { + const result: unknown[] = []; + for (let i in collection) { + if (Object.prototype.hasOwnProperty.call(collection, i)) { + result.push(iterateeFn(collection[i], i, collection)); + } + } + return result; + } + + throw new Error(`map() can't iterate on type ${typeof collection}`); }; const COMPARATOR = (objA, objB) => { @@ -112,39 +101,38 @@ const COMPARATOR = (objA, objB) => { * * Iteratees are called with one argument (value). */ -export const sortBy = - (...iterateeFns: ((input: T) => unknown)[]) => - (array: T[]): T[] => { - if (!Array.isArray(array)) { - return array; - } - let length = array.length; - // Iterate over the array to collect criteria to sort it by - let mappedArray: { - criteria: unknown[]; - value: T; - }[] = []; - for (let i = 0; i < length; i++) { - const value = array[i]; - mappedArray.push({ - criteria: iterateeFns.map((fn) => fn(value)), - value, - }); - } - // Sort criteria using the base comparator - mappedArray.sort(COMPARATOR); +export const sortBy = ( + array: T[], + ...iterateeFns: ((input: T) => unknown)[] +): T[] => { + if (!Array.isArray(array)) { + return array; + } + let length = array.length; + // Iterate over the array to collect criteria to sort it by + let mappedArray: { + criteria: unknown[]; + value: T; + }[] = []; + for (let i = 0; i < length; i++) { + const value = array[i]; + mappedArray.push({ + criteria: iterateeFns.map((fn) => fn(value)), + value, + }); + } + // Sort criteria using the base comparator + mappedArray.sort(COMPARATOR); - // Unwrap values - const values: T[] = []; - while (length--) { - values[length] = mappedArray[length].value; - } - return values; - }; + // Unwrap values + const values: T[] = []; + while (length--) { + values[length] = mappedArray[length].value; + } + return values; +}; -export const sort = sortBy(); - -export const sortStrings = sortBy(); +export const sort = (array: T[]): T[] => sortBy(array); /** * Returns a range of numbers from start to end, exclusively. @@ -153,12 +141,34 @@ export const sortStrings = sortBy(); export const range = (start: number, end: number): number[] => new Array(end - start).fill(null).map((_, index) => index + start); +type ReduceFunction = { + ( + array: T[], + reducerFn: ( + accumulator: U, + currentValue: T, + currentIndex: number, + array: T[], + ) => U, + initialValue: U, + ): U; + ( + array: T[], + reducerFn: ( + accumulator: T, + currentValue: T, + currentIndex: number, + array: T[], + ) => T, + ): T; +}; + /** * A fast implementation of reduce. */ -export const reduce = (reducerFn, initialValue) => (array) => { +export const reduce: ReduceFunction = (array, reducerFn, initialValue?) => { const length = array.length; - let i; + let i: number; let result; if (initialValue === undefined) { i = 1; @@ -184,15 +194,16 @@ export const reduce = (reducerFn, initialValue) => (array) => { * is determined by the order they occur in the array. The iteratee is * invoked with one argument: value. */ -export const uniqBy = - (iterateeFn?: (value: T) => unknown) => - (array: T[]): T[] => { - const { length } = array; - const result: T[] = []; - const seen: unknown[] = iterateeFn ? [] : result; - let index = -1; - // prettier-ignore - outer: +export const uniqBy = ( + array: T[], + iterateeFn?: (value: T) => unknown, +): T[] => { + const { length } = array; + const result: T[] = []; + const seen: unknown[] = iterateeFn ? [] : result; + let index = -1; + // prettier-ignore + outer: while (++index < length) { let value: T | 0 = array[index]; const computed = iterateeFn ? iterateeFn(value) : value; @@ -214,10 +225,10 @@ export const uniqBy = result.push(value); } } - return result; - }; + return result; +}; -export const uniq = uniqBy(); +export const uniq = (array: T[]): T[] => uniqBy(array); type Zip = { [I in keyof T]: T[I] extends (infer U)[] ? U : never; @@ -247,17 +258,6 @@ export const zip = (...arrays: T): Zip => { return result; }; -/** - * This method is like "zip" except that it accepts iteratee to - * specify how grouped values should be combined. The iteratee is - * invoked with the elements of each group. - */ -export const zipWith = - (iterateeFn: (...values: T[]) => U) => - (...arrays: T[][]): U[] => { - return map((values: T[]) => iterateeFn(...values))(zip(...arrays)); - }; - const binarySearch = ( getKey: (value: T) => U, collection: readonly T[], @@ -293,13 +293,15 @@ const binarySearch = ( return compare > insertingKey ? middle : middle + 1; }; -export const binaryInsertWith = - (getKey: (value: T) => U) => - (collection: readonly T[], value: T) => { - const copy = [...collection]; - copy.splice(binarySearch(getKey, collection, value), 0, value); - return copy; - }; +export const binaryInsertWith = ( + collection: readonly T[], + value: T, + getKey: (value: T) => U, +): T[] => { + const copy = [...collection]; + copy.splice(binarySearch(getKey, collection, value), 0, value); + return copy; +}; /** * This method takes a collection of items and a number, returning a collection @@ -325,7 +327,8 @@ export const paginate = (collection: T[], maxPerPage: number): T[][] => { return pages; }; -const isObject = (obj: unknown) => typeof obj === 'object' && obj !== null; +const isObject = (obj: unknown): obj is object => + typeof obj === 'object' && obj !== null; // Does a deep merge of two objects. DO NOT FEED CIRCULAR OBJECTS!! export const deepMerge = (...objects: any[]): any => { diff --git a/tgui/packages/common/fp.js b/tgui/packages/common/fp.js index ba7df09d407..675e98d807e 100644 --- a/tgui/packages/common/fp.js +++ b/tgui/packages/common/fp.js @@ -23,27 +23,3 @@ export const flow = (...funcs) => (input, ...rest) => { } return output; }; - -/** - * Composes single-argument functions from right to left. - * - * All functions might accept a context in form of additional arguments. - * If the resulting function is called with more than 1 argument, rest of - * the arguments are passed to all functions unchanged. - * - * @param {...Function} funcs The functions to compose - * @returns {Function} A function obtained by composing the argument functions - * from right to left. For example, compose(f, g, h) is identical to doing - * (input, ...rest) => f(g(h(input, ...rest), ...rest), ...rest) - */ -export const compose = (...funcs) => { - if (funcs.length === 0) { - return (arg) => arg; - } - if (funcs.length === 1) { - return funcs[0]; - } - // prettier-ignore - return funcs.reduce((a, b) => (value, ...rest) => - a(b(value, ...rest), ...rest)); -}; diff --git a/tgui/packages/common/vector.js b/tgui/packages/common/vector.js deleted file mode 100644 index b1f85f7429d..00000000000 --- a/tgui/packages/common/vector.js +++ /dev/null @@ -1,48 +0,0 @@ -/** - * N-dimensional vector manipulation functions. - * - * Vectors are plain number arrays, i.e. [x, y, z]. - * - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { map, reduce, zipWith } from './collections'; - -const ADD = (a, b) => a + b; -const SUB = (a, b) => a - b; -const MUL = (a, b) => a * b; -const DIV = (a, b) => a / b; - -export const vecAdd = (...vecs) => { - return reduce((a, b) => zipWith(ADD)(a, b))(vecs); -}; - -export const vecSubtract = (...vecs) => { - return reduce((a, b) => zipWith(SUB)(a, b))(vecs); -}; - -export const vecMultiply = (...vecs) => { - return reduce((a, b) => zipWith(MUL)(a, b))(vecs); -}; - -export const vecDivide = (...vecs) => { - return reduce((a, b) => zipWith(DIV)(a, b))(vecs); -}; - -export const vecScale = (vec, n) => { - return map((x) => x * n)(vec); -}; - -export const vecInverse = (vec) => { - return map((x) => -x)(vec); -}; - -export const vecLength = (vec) => { - return Math.sqrt(reduce(ADD)(zipWith(MUL)(vec, vec))); -}; - -export const vecNormalize = (vec) => { - return vecDivide(vec, vecLength(vec)); -}; diff --git a/tgui/packages/common/vector.ts b/tgui/packages/common/vector.ts new file mode 100644 index 00000000000..c91715a8f99 --- /dev/null +++ b/tgui/packages/common/vector.ts @@ -0,0 +1,51 @@ +/** + * N-dimensional vector manipulation functions. + * + * Vectors are plain number arrays, i.e. [x, y, z]. + * + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { map, reduce, zip } from './collections'; + +const ADD = (a: number, b: number): number => a + b; +const SUB = (a: number, b: number): number => a - b; +const MUL = (a: number, b: number): number => a * b; +const DIV = (a: number, b: number): number => a / b; + +export type Vector = number[]; + +export const vecAdd = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, ADD)); +}; + +export const vecSubtract = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, SUB)); +}; + +export const vecMultiply = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, MUL)); +}; + +export const vecDivide = (...vecs: Vector[]): Vector => { + return map(zip(...vecs), (x) => reduce(x, DIV)); +}; + +export const vecScale = (vec: Vector, n: number): Vector => { + return map(vec, (x) => x * n); +}; + +export const vecInverse = (vec: Vector): Vector => { + return map(vec, (x) => -x); +}; + +export const vecLength = (vec: Vector): number => { + return Math.sqrt(reduce(vecMultiply(vec, vec), ADD)); +}; + +export const vecNormalize = (vec: Vector): Vector => { + const length = vecLength(vec); + return map(vec, (c) => c / length); +}; diff --git a/tgui/packages/tgui-panel/chat/selectors.ts b/tgui/packages/tgui-panel/chat/selectors.ts index 2908f661264..3c1e0b4f429 100644 --- a/tgui/packages/tgui-panel/chat/selectors.ts +++ b/tgui/packages/tgui-panel/chat/selectors.ts @@ -9,7 +9,7 @@ import { map } from 'common/collections'; export const selectChat = (state) => state.chat; export const selectChatPages = (state) => - map((id: string) => state.chat.pageById[id])(state.chat.pages); + map(state.chat.pages, (id: string) => state.chat.pageById[id]); export const selectCurrentChatPage = (state) => state.chat.pageById[state.chat.currentPageId]; diff --git a/tgui/packages/tgui/backend.ts b/tgui/packages/tgui/backend.ts index c85d5153210..9190d74dbe2 100644 --- a/tgui/packages/tgui/backend.ts +++ b/tgui/packages/tgui/backend.ts @@ -274,10 +274,6 @@ type BackendState = { shared: Record; suspending: boolean; suspended: boolean; - debug?: { - debugLayout: boolean; - kitchenSink: boolean; - }; }; /** diff --git a/tgui/packages/tgui/components/Autofocus.tsx b/tgui/packages/tgui/components/Autofocus.tsx index a0b3f6f7659..403dbe2e965 100644 --- a/tgui/packages/tgui/components/Autofocus.tsx +++ b/tgui/packages/tgui/components/Autofocus.tsx @@ -1,17 +1,23 @@ -import { createRef, PropsWithChildren, useEffect } from 'react'; +import { PropsWithChildren, useEffect, useRef } from 'react'; -export const Autofocus = (props: PropsWithChildren) => { - const ref = createRef(); +/** Used to force the window to steal focus on load. Children optional */ +export function Autofocus(props: PropsWithChildren) { + const { children } = props; + const ref = useRef(null); useEffect(() => { - setTimeout(() => { + const timer = setTimeout(() => { ref.current?.focus(); }, 1); + + return () => { + clearTimeout(timer); + }; }, []); return (

    ); -}; +} diff --git a/tgui/packages/tgui/components/Chart.tsx b/tgui/packages/tgui/components/Chart.tsx index 205dc6fbec1..bc33ff90606 100644 --- a/tgui/packages/tgui/components/Chart.tsx +++ b/tgui/packages/tgui/components/Chart.tsx @@ -4,7 +4,7 @@ * @license MIT */ -import { map, zipWith } from 'common/collections'; +import { map, zip } from 'common/collections'; import { Component, createRef, RefObject } from 'react'; import { Box, BoxProps } from './Box'; @@ -37,8 +37,8 @@ const normalizeData = ( return []; } - const min = zipWith(Math.min)(...data); - const max = zipWith(Math.max)(...data); + const min = map(zip(...data), (p) => Math.min(...p)); + const max = map(zip(...data), (p) => Math.max(...p)); if (rangeX !== undefined) { min[0] = rangeX[0]; @@ -50,11 +50,12 @@ const normalizeData = ( max[1] = rangeY[1]; } - const normalized = map((point: Point) => { - return zipWith((value: number, min: number, max: number, scale: number) => { - return ((value - min) / (max - min)) * scale; - })(point, min, max, scale); - })(data); + const normalized = map(data, (point) => + map( + zip(point, min, max, scale), + ([value, min, max, scale]) => ((value - min) / (max - min)) * scale, + ), + ); return normalized; }; diff --git a/tgui/packages/tgui/components/DmIcon.tsx b/tgui/packages/tgui/components/DmIcon.tsx new file mode 100644 index 00000000000..fb6816576ac --- /dev/null +++ b/tgui/packages/tgui/components/DmIcon.tsx @@ -0,0 +1,72 @@ +import { ReactNode, useEffect, useState } from 'react'; + +import { resolveAsset } from '../assets'; +import { fetchRetry } from '../http'; +import { BoxProps } from './Box'; +import { Image } from './Image'; + +enum Direction { + NORTH = 1, + SOUTH = 2, + EAST = 4, + WEST = 8, + NORTHEAST = NORTH | EAST, + NORTHWEST = NORTH | WEST, + SOUTHEAST = SOUTH | EAST, + SOUTHWEST = SOUTH | WEST, +} + +type Props = { + /** Required: The path of the icon */ + icon: string; + /** Required: The state of the icon */ + icon_state: string; +} & Partial<{ + /** Facing direction. See direction enum. Default is South */ + direction: Direction; + /** Fallback icon. */ + fallback: ReactNode; + /** Frame number. Default is 1 */ + frame: number; + /** Movement state. Default is false */ + movement: boolean; +}> & + BoxProps; + +let refMap: Record | undefined; + +export function DmIcon(props: Props) { + const { + className, + direction = Direction.SOUTH, + fallback, + frame = 1, + icon_state, + icon, + movement = false, + ...rest + } = props; + + const [iconRef, setIconRef] = useState(''); + + const query = `${iconRef}?state=${icon_state}&dir=${direction}&movement=${movement}&frame=${frame}`; + + useEffect(() => { + async function fetchRefMap() { + const response = await fetchRetry(resolveAsset('icon_ref_map.json')); + const data = await response.json(); + refMap = data; + setIconRef(data[icon]); + } + + if (!refMap) { + fetchRefMap(); + } else { + setIconRef(refMap[icon]); + } + }, []); + + if (!iconRef) return fallback; + + return ; +} diff --git a/tgui/packages/tgui/components/Grid.jsx b/tgui/packages/tgui/components/Grid.jsx deleted file mode 100644 index f5593c9e00a..00000000000 --- a/tgui/packages/tgui/components/Grid.jsx +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { Table } from './Table'; - -/** @deprecated */ -export const Grid = (props) => { - const { children, ...rest } = props; - return ( - - {children} -
    - ); -}; - -/** @deprecated */ -export const GridColumn = (props) => { - const { size = 1, style, ...rest } = props; - return ( - - ); -}; - -Grid.Column = GridColumn; diff --git a/tgui/packages/tgui/components/Grid.tsx b/tgui/packages/tgui/components/Grid.tsx new file mode 100644 index 00000000000..91c9256753b --- /dev/null +++ b/tgui/packages/tgui/components/Grid.tsx @@ -0,0 +1,44 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { PropsWithChildren } from 'react'; + +import { logger } from '../logging'; +import { BoxProps } from './Box'; +import { Table } from './Table'; + +/** @deprecated Do not use. Use stack instead. */ +export function Grid(props: PropsWithChildren) { + const { children, ...rest } = props; + logger.error('Grid component is deprecated. Use a Stack instead.'); + return ( + + {children} +
    + ); +} + +type Props = Partial<{ + /** Width of the column in percentage. */ + size: number; +}> & + BoxProps; + +/** @deprecated Do not use. Use stack instead. */ +export function GridColumn(props: Props) { + const { size = 1, style, ...rest } = props; + return ( + + ); +} + +Grid.Column = GridColumn; diff --git a/tgui/packages/tgui/components/Image.tsx b/tgui/packages/tgui/components/Image.tsx index 3e1519bfbbf..5d3a943feb0 100644 --- a/tgui/packages/tgui/components/Image.tsx +++ b/tgui/packages/tgui/components/Image.tsx @@ -1,12 +1,14 @@ -import { ReactNode } from 'react'; +import { useRef } from 'react'; import { BoxProps, computeBoxProps } from './Box'; -import { Tooltip } from './Tooltip'; type Props = Partial<{ - fixBlur: boolean; // true is default, this is an ie thing - objectFit: 'contain' | 'cover'; // fill is default - tooltip: ReactNode; + /** True is default, this fixes an ie thing */ + fixBlur: boolean; + /** False by default. Good if you're fetching images on UIs that do not auto update. This will attempt to fix the 'x' icon 5 times. */ + fixErrors: boolean; + /** Fill is default. */ + objectFit: 'contain' | 'cover'; }> & IconUnion & BoxProps; @@ -22,16 +24,18 @@ type IconUnion = src?: string; }; +const maxAttempts = 5; + /** Image component. Use this instead of Box as="img". */ -export const Image = (props: Props) => { +export function Image(props: Props) { const { - className, fixBlur = true, + fixErrors = false, objectFit = 'fill', src, - tooltip, ...rest } = props; + const attempts = useRef(0); const computedProps = computeBoxProps(rest); computedProps['style'] = { @@ -40,11 +44,20 @@ export const Image = (props: Props) => { objectFit, }; - let content = ; + return ( + { + if (fixErrors && attempts.current < maxAttempts) { + const imgElement = event.currentTarget; - if (tooltip) { - content = {content}; - } - - return content; -}; + setTimeout(() => { + imgElement.src = `${src}?attempt=${attempts.current}`; + attempts.current++; + }, 1000); + } + }} + src={src} + {...computedProps} + /> + ); +} diff --git a/tgui/packages/tgui/components/Table.tsx b/tgui/packages/tgui/components/Table.tsx index 87edfbb8fc9..f4dcf6f3f52 100644 --- a/tgui/packages/tgui/components/Table.tsx +++ b/tgui/packages/tgui/components/Table.tsx @@ -64,6 +64,8 @@ type CellProps = Partial<{ colSpan: number; /** Whether this is a header cell. */ header: boolean; + /** Rows for this cell to expand, assuming there is room. */ + rowSpan: number; }> & BoxProps; diff --git a/tgui/packages/tgui/components/index.ts b/tgui/packages/tgui/components/index.ts index 1a5f477d256..fdeb475ed3b 100644 --- a/tgui/packages/tgui/components/index.ts +++ b/tgui/packages/tgui/components/index.ts @@ -17,6 +17,7 @@ export { ColorBox } from './ColorBox'; export { Dialog } from './Dialog'; export { Dimmer } from './Dimmer'; export { Divider } from './Divider'; +export { DmIcon } from './DmIcon'; export { DraggableControl } from './DraggableControl'; export { Dropdown } from './Dropdown'; export { FitText } from './FitText'; diff --git a/tgui/packages/tgui/debug/KitchenSink.jsx b/tgui/packages/tgui/debug/KitchenSink.jsx index d215ab777d8..19044ca98d0 100644 --- a/tgui/packages/tgui/debug/KitchenSink.jsx +++ b/tgui/packages/tgui/debug/KitchenSink.jsx @@ -4,11 +4,12 @@ * @license MIT */ -import { useLocalState } from '../backend'; +import { useState } from 'react'; + import { Flex, Section, Tabs } from '../components'; import { Pane, Window } from '../layouts'; -const r = require.context('../stories', false, /\.stories\.js$/); +const r = require.context('../stories', false, /\.stories\.jsx$/); /** * @returns {{ @@ -22,8 +23,8 @@ const getStories = () => r.keys().map((path) => r(path)); export const KitchenSink = (props) => { const { panel } = props; - const [theme] = useLocalState('kitchenSinkTheme'); - const [pageIndex, setPageIndex] = useLocalState('pageIndex', 0); + const [theme] = useState(null); + const [pageIndex, setPageIndex] = useState(0); const stories = getStories(); const story = stories[pageIndex]; const Layout = panel ? Pane : Window; diff --git a/tgui/packages/tgui/drag.ts b/tgui/packages/tgui/drag.ts index 584666a97a6..0884b1b0bd7 100644 --- a/tgui/packages/tgui/drag.ts +++ b/tgui/packages/tgui/drag.ts @@ -209,7 +209,7 @@ export const dragStartHandler = (event) => { dragPointOffset = vecSubtract( [event.screenX, event.screenY], getWindowPosition(), - ); + ) as [number, number]; // Focus click target (event.target as HTMLElement)?.focus(); document.addEventListener('mousemove', dragMoveHandler); @@ -234,7 +234,10 @@ const dragMoveHandler = (event: MouseEvent) => { } event.preventDefault(); setWindowPosition( - vecSubtract([event.screenX, event.screenY], dragPointOffset), + vecSubtract([event.screenX, event.screenY], dragPointOffset) as [ + number, + number, + ], ); }; @@ -247,7 +250,7 @@ export const resizeStartHandler = dragPointOffset = vecSubtract( [event.screenX, event.screenY], getWindowPosition(), - ); + ) as [number, number]; initialSize = getWindowSize(); // Focus click target (event.target as HTMLElement)?.focus(); @@ -278,7 +281,10 @@ const resizeMoveHandler = (event: MouseEvent) => { ); const delta = vecSubtract(currentOffset, dragPointOffset); // Extra 1x1 area is added to ensure the browser can see the cursor - size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]); + size = vecAdd(initialSize, vecMultiply(resizeMatrix, delta), [1, 1]) as [ + number, + number, + ]; // Sane window size values size[0] = Math.max(size[0], 150 * pixelRatio); size[1] = Math.max(size[1], 50 * pixelRatio); diff --git a/tgui/packages/tgui/interfaces/AirlockElectronics.tsx b/tgui/packages/tgui/interfaces/AirlockElectronics.tsx index 7e0ebba0bb4..b90e8ac3b57 100644 --- a/tgui/packages/tgui/interfaces/AirlockElectronics.tsx +++ b/tgui/packages/tgui/interfaces/AirlockElectronics.tsx @@ -1,21 +1,31 @@ import { BooleanLike } from 'common/react'; import { useBackend } from '../backend'; -import { Button, Input, LabeledList, Section } from '../components'; +import { Button, Input, LabeledList, Section, Stack } from '../components'; import { Window } from '../layouts'; -import { AccessConfig } from './common/AccessConfig'; +import { AccessConfig, Region } from './common/AccessConfig'; type Data = { - oneAccess: BooleanLike; - unres_direction: number; - passedName: string; - passedCycleId: number; - regions: string[]; accesses: string[]; + oneAccess: BooleanLike; + passedCycleId: number; + passedName: string; + regions: Region[]; shell: BooleanLike; + unres_direction: number; }; -export const AirLockMainSection = (props) => { +export function AirlockElectronics(props) { + return ( + + + + + + ); +} + +export function AirLockMainSection(props) { const { act, data } = useBackend(); const { accesses = [], @@ -28,123 +38,125 @@ export const AirLockMainSection = (props) => { } = data; return ( -
    - - - { - act('set_shell', { on: !shell }); - }} - tooltip="Whether this airlock can have an integrated circuit placed inside of it or not." - /> - - -
    + + +
    + + + { + act('set_shell', { on: !shell }); + }} + tooltip="Whether this airlock can have an integrated circuit placed inside of it or not." + > + Shell + + + + + + + + + + + + + + act('passedName', { + passedName: value, + }) + } + /> + + + + act('passedCycleId', { + passedCycleId: value, + }) + } + /> + + +
    +
    + + + act('set', { + access: ref, + }) + } + grantAll={() => act('grant_all')} + denyAll={() => act('clear_all')} + grantDep={(ref) => + act('grant_region', { + region: ref, + }) + } + denyDep={(ref) => + act('deny_region', { + region: ref, + }) + } + /> + +
    ); -}; - -export const AirlockElectronics = (props) => { - return ( - - - - - - ); -}; +} diff --git a/tgui/packages/tgui/interfaces/ApcControl.jsx b/tgui/packages/tgui/interfaces/ApcControl.jsx index acf46f9f0b8..fb29c78032d 100644 --- a/tgui/packages/tgui/interfaces/ApcControl.jsx +++ b/tgui/packages/tgui/interfaces/ApcControl.jsx @@ -160,18 +160,21 @@ const ApcControlScene = (props) => { const [sortByField] = useLocalState('sortByField', 'name'); const apcs = flow([ - map((apc, i) => ({ - ...apc, - // Generate a unique id - id: apc.name + i, - })), - sortByField === 'name' && sortBy((apc) => apc.name), - sortByField === 'charge' && sortBy((apc) => -apc.charge), + (apcs) => + map(apcs, (apc, i) => ({ + ...apc, + // Generate a unique id + id: apc.name + i, + })), + sortByField === 'name' && ((apcs) => sortBy(apcs, (apc) => apc.name)), + sortByField === 'charge' && ((apcs) => sortBy(apcs, (apc) => -apc.charge)), sortByField === 'draw' && - sortBy( - (apc) => -powerRank(apc.load), - (apc) => -parseFloat(apc.load), - ), + ((apcs) => + sortBy( + apcs, + (apc) => -powerRank(apc.load), + (apc) => -parseFloat(apc.load), + )), ])(data.apcs); return ( @@ -255,14 +258,11 @@ const ApcControlScene = (props) => { const LogPanel = (props) => { const { data } = useBackend(); - const logs = flow([ - map((line, i) => ({ - ...line, - // Generate a unique id - id: line.entry + i, - })), - (logs) => logs.reverse(), - ])(data.logs); + const logs = map(data.logs, (line, i) => ({ + ...line, + // Generate a unique id + id: line.entry + i, + })).reverse(); return ( {logs.map((line) => ( diff --git a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx index 84d0ff08def..c39778c410f 100644 --- a/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx +++ b/tgui/packages/tgui/interfaces/AtmosControlPanel.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Box, Button, Flex, Section, Table } from '../components'; @@ -7,14 +6,14 @@ import { Window } from '../layouts'; export const AtmosControlPanel = (props) => { const { act, data } = useBackend(); - const groups = flow([ - map((group, i) => ({ + const groups = sortBy( + map(data.excited_groups, (group, i) => ({ ...group, // Generate a unique id id: group.area + i, })), - sortBy((group) => group.id), - ])(data.excited_groups); + (group) => group.id, + ); return (
    diff --git a/tgui/packages/tgui/interfaces/BluespaceSender.tsx b/tgui/packages/tgui/interfaces/BluespaceSender.tsx index 24e88f6ef5d..c8c9e47b0c7 100644 --- a/tgui/packages/tgui/interfaces/BluespaceSender.tsx +++ b/tgui/packages/tgui/interfaces/BluespaceSender.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { multiline } from 'common/string'; @@ -43,10 +42,10 @@ export const BluespaceSender = (props) => { const { act, data } = useBackend(); const { gas_transfer_rate, credits, bluespace_network_gases = [], on } = data; - const gases: Gas[] = flow([ - filter((gas) => gas.amount >= 0.01), - sortBy((gas) => -gas.amount), - ])(bluespace_network_gases); + const gases: Gas[] = sortBy( + filter(bluespace_network_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); const gasMax = Math.max(1, ...gases.map((gas) => gas.amount)); diff --git a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx index 3ba8289356b..6ce242e4e48 100644 --- a/tgui/packages/tgui/interfaces/BluespaceVendor.tsx +++ b/tgui/packages/tgui/interfaces/BluespaceVendor.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { multiline } from 'common/string'; @@ -50,10 +49,10 @@ export const BluespaceVendor = (props) => { tank_full, } = data; - const gases: Gas[] = flow([ - filter((gas) => gas.amount >= 0.01), - sortBy((gas) => -gas.amount), - ])(bluespace_network_gases); + const gases: Gas[] = sortBy( + filter(bluespace_network_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); const gasMax = Math.max(1, ...gases.map((gas) => gas.amount)); diff --git a/tgui/packages/tgui/interfaces/CameraConsole.tsx b/tgui/packages/tgui/interfaces/CameraConsole.tsx index adad4f8748e..3dafe1240df 100644 --- a/tgui/packages/tgui/interfaces/CameraConsole.tsx +++ b/tgui/packages/tgui/interfaces/CameraConsole.tsx @@ -1,5 +1,4 @@ -import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; +import { filter, sort } from 'common/collections'; import { BooleanLike, classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -68,15 +67,17 @@ const prevNextCamera = ( * Filters cameras, applies search terms and sorts the alphabetically. */ const selectCameras = (cameras: Camera[], searchText = ''): Camera[] => { - const testSearch = createSearch(searchText, (camera: Camera) => camera.name); + let queriedCameras = filter(cameras, (camera: Camera) => !!camera.name); + if (searchText) { + const testSearch = createSearch( + searchText, + (camera: Camera) => camera.name, + ); + queriedCameras = filter(queriedCameras, testSearch); + } + queriedCameras = sort(queriedCameras); - return flow([ - filter((camera: Camera) => !!camera.name), - // Optional search term - searchText && filter(testSearch), - // Slightly expensive, but way better than sorting in BYOND - sortBy((camera: Camera) => camera), - ])(cameras); + return queriedCameras; }; export const CameraConsole = (props) => { diff --git a/tgui/packages/tgui/interfaces/Cargo.jsx b/tgui/packages/tgui/interfaces/Cargo.jsx index f160970bc51..cbc0f5b112b 100644 --- a/tgui/packages/tgui/interfaces/Cargo.jsx +++ b/tgui/packages/tgui/interfaces/Cargo.jsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend, useSharedState } from '../backend'; import { @@ -169,16 +168,17 @@ const CargoStatus = (props) => { const searchForSupplies = (supplies, search) => { search = search.toLowerCase(); - return flow([ - (categories) => categories.flatMap((category) => category.packs), + const queriedSupplies = sortBy( filter( + supplies.flatMap((category) => category.packs), (pack) => pack.name?.toLowerCase().includes(search.toLowerCase()) || pack.desc?.toLowerCase().includes(search.toLowerCase()), ), - sortBy((pack) => pack.name), - (packs) => packs.slice(0, 25), - ])(supplies); + (pack) => pack.name, + ); + + return queriedSupplies.slice(0, 25); }; export const CargoCatalog = (props) => { diff --git a/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx b/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx index 0c81311a431..f37414a86f5 100644 --- a/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx +++ b/tgui/packages/tgui/interfaces/CircuitAccessChecker.tsx @@ -3,11 +3,11 @@ import { BooleanLike } from 'common/react'; import { useBackend } from '../backend'; import { Button, LabeledList } from '../components'; import { Window } from '../layouts'; -import { AccessConfig } from './common/AccessConfig'; +import { AccessConfig, Region } from './common/AccessConfig'; type Data = { oneAccess: BooleanLike; - regions: string[]; + regions: Region[]; accesses: string[]; }; @@ -28,8 +28,8 @@ export const CircuitAccessChecker = (props) => { act('set', { access: ref, diff --git a/tgui/packages/tgui/interfaces/ClockworkSlab.jsx b/tgui/packages/tgui/interfaces/ClockworkSlab.jsx index 6e9eef57d87..7acb39b8713 100644 --- a/tgui/packages/tgui/interfaces/ClockworkSlab.jsx +++ b/tgui/packages/tgui/interfaces/ClockworkSlab.jsx @@ -7,10 +7,10 @@ import { Button, Collapsible, Divider, - Grid, Icon, ProgressBar, Section, + Stack, Table, } from '../components'; import { TableRow } from '../components/Table'; @@ -311,12 +311,12 @@ const ClockworkOverviewStat = (props) => { const { title, iconName, amount, maxAmount, unit, overrideText } = props; return ( - - + + - - {title} - + + {title} + { > {overrideText ? overrideText : amount + ' ' + unit} - - + + ); }; diff --git a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx index b6dc5ac272f..0a1306a19d6 100644 --- a/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx +++ b/tgui/packages/tgui/interfaces/CommunicationsConsole.jsx @@ -29,10 +29,12 @@ const SWIPE_NEEDED = 'SWIPE_NEEDED'; const EMAG_SHUTTLE_NOTICE = 'This shuttle is deemed significantly dangerous to the crew, and is only supplied by the Syndicate.'; -const sortShuttles = sortBy( - (shuttle) => !shuttle.emagOnly, - (shuttle) => shuttle.initial_cost, -); +const sortShuttles = (shuttles) => + sortBy( + shuttles, + (shuttle) => !shuttle.emagOnly, + (shuttle) => shuttle.initial_cost, + ); const AlertButton = (props) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/CrewConsole.jsx b/tgui/packages/tgui/interfaces/CrewConsole.jsx index 8ed7efa5c11..ef3f8c31a38 100644 --- a/tgui/packages/tgui/interfaces/CrewConsole.jsx +++ b/tgui/packages/tgui/interfaces/CrewConsole.jsx @@ -86,7 +86,7 @@ export const CrewConsole = () => { const CrewTable = (props) => { const { act, data } = useBackend(); - const sensors = sortBy((s) => s.ijob)(data.sensors ?? []); + const sensors = sortBy(data.sensors ?? [], (s) => s.ijob); return ( diff --git a/tgui/packages/tgui/interfaces/DestinationTagger.tsx b/tgui/packages/tgui/interfaces/DestinationTagger.tsx index b1acfe192a7..5326f93e01b 100644 --- a/tgui/packages/tgui/interfaces/DestinationTagger.tsx +++ b/tgui/packages/tgui/interfaces/DestinationTagger.tsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Button, Section, Stack } from '../components'; @@ -25,13 +24,17 @@ type DestinationInfo = { * @returns The alphetically sorted list of destinations. */ const sortDestinations = (locations: string[]): DestinationInfo[] => { - return flow([ - map((name, index) => ({ - name: name.toUpperCase(), - sorting_id: index + 1, - })), - sortBy((dest) => dest.name), - ])(locations); + return sortBy( + map( + locations, + (name, index) => + ({ + name: name.toUpperCase(), + sorting_id: index + 1, + }) as DestinationInfo, + ), + (dest) => dest.name, + ); }; export const DestinationTagger = (props) => { diff --git a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx index 43c22b321a0..681b4f211d7 100644 --- a/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx +++ b/tgui/packages/tgui/interfaces/DnaConsole/DnaConsoleStorage.jsx @@ -211,7 +211,7 @@ const StorageButtons = (props) => { const StorageChromosomes = (props) => { const { data, act } = useBackend(); const chromos = data.chromoStorage ?? []; - const uniqueChromos = uniqBy((chromo) => chromo.Name)(chromos); + const uniqueChromos = uniqBy(chromos, (chromo) => chromo.Name); const chromoName = data.view.storageChromoName; const chromo = chromos.find((chromo) => chromo.Name === chromoName); diff --git a/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx b/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx index 89a1468d953..2629a029fa8 100644 --- a/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx +++ b/tgui/packages/tgui/interfaces/DnaConsole/MutationInfo.jsx @@ -1,5 +1,4 @@ import { filter, uniqBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../../backend'; import { @@ -122,10 +121,10 @@ export const MutationInfo = (props) => { isSameMutation(x, mutation), ); const savedToDisk = diskMutations.find((x) => isSameMutation(x, mutation)); - const combinedMutations = flow([ - uniqBy((mutation) => mutation.Name), - filter((x) => x.Name !== mutation.Name), - ])([...diskMutations, ...mutationStorage]); + const combinedMutations = filter( + uniqBy([...diskMutations, ...mutationStorage], (mutation) => mutation.Name), + (x) => x.Name !== mutation.Name, + ); return ( <> diff --git a/tgui/packages/tgui/interfaces/DnaVault.jsx b/tgui/packages/tgui/interfaces/DnaVault.tsx similarity index 75% rename from tgui/packages/tgui/interfaces/DnaVault.jsx rename to tgui/packages/tgui/interfaces/DnaVault.tsx index 5ac7711412d..2b6781d6cf7 100644 --- a/tgui/packages/tgui/interfaces/DnaVault.jsx +++ b/tgui/packages/tgui/interfaces/DnaVault.tsx @@ -1,28 +1,44 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; import { Box, Button, - Grid, LabeledList, ProgressBar, Section, + Stack, } from '../components'; import { Window } from '../layouts'; -export const DnaVault = (props) => { - const { act, data } = useBackend(); +type Data = { + animals_max: number; + animals: number; + choiceA: string; + choiceB: string; + completed: BooleanLike; + dna_max: number; + dna: number; + plants_max: number; + plants: number; + used: BooleanLike; +}; + +export function DnaVault(props) { + const { act, data } = useBackend(); const { - completed, - used, + animals_max, + animals, choiceA, choiceB, - dna, + completed, dna_max, - plants, + dna, plants_max, - animals, - animals_max, + plants, + used, } = data; + return ( @@ -50,37 +66,39 @@ export const DnaVault = (props) => { Applicable Gene Therapy Treatments - - + + + + + + )} ); -}; +} diff --git a/tgui/packages/tgui/interfaces/EightBallVote.tsx b/tgui/packages/tgui/interfaces/EightBallVote.tsx index 30ab7baae48..f85b8724ffe 100644 --- a/tgui/packages/tgui/interfaces/EightBallVote.tsx +++ b/tgui/packages/tgui/interfaces/EightBallVote.tsx @@ -2,50 +2,51 @@ import { BooleanLike } from 'common/react'; import { toTitleCase } from 'common/string'; import { useBackend } from '../backend'; -import { Box, Button, Grid, NoticeBox, Section } from '../components'; +import { Box, Button, NoticeBox, Section, Stack } from '../components'; import { Window } from '../layouts'; type Data = { - shaking: BooleanLike; - question: string; answers: Answer[]; + question: string; + shaking: BooleanLike; }; type Answer = { - answer: string; amount: number; + answer: string; selected: BooleanLike; }; -export const EightBallVote = (props) => { - const { act, data } = useBackend(); +export function EightBallVote(props) { + const { data } = useBackend(); const { shaking } = data; + return ( - {(!shaking && ( + {(shaking && ( No question is currently being asked. )) || } ); -}; +} -const EightBallVoteQuestion = (props) => { +function EightBallVoteQuestion(props) { const { act, data } = useBackend(); const { question, answers = [] } = data; + return (
    "{question}" - + {answers.map((answer) => ( - + {answer.amount} - + ))} - +
    ); -}; +} diff --git a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.tsx similarity index 73% rename from tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx rename to tgui/packages/tgui/interfaces/EmergencyShuttleConsole.tsx index b7b09bdca6a..04214e092d6 100644 --- a/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.jsx +++ b/tgui/packages/tgui/interfaces/EmergencyShuttleConsole.tsx @@ -1,17 +1,34 @@ +import { BooleanLike } from 'common/react'; + import { useBackend } from '../backend'; -import { Box, Button, Grid, Section } from '../components'; +import { Box, Button, Section, Stack } from '../components'; import { Window } from '../layouts'; -export const EmergencyShuttleConsole = (props) => { - const { act, data } = useBackend(); +type Data = { + authorizations_remaining: number; + authorizations: Authorization[]; + emagged: BooleanLike; + enabled: BooleanLike; + engines_started: BooleanLike; + timer_str: string; +}; + +type Authorization = { + job: string; + name: string; +}; + +export function EmergencyShuttleConsole(props) { + const { act, data } = useBackend(); const { - timer_str, - enabled, - emagged, - engines_started, - authorizations_remaining, authorizations = [], + authorizations_remaining, + emagged, + enabled, + engines_started, + timer_str, } = data; + return ( @@ -29,41 +46,42 @@ export const EmergencyShuttleConsole = (props) => {
    act('abort')} - /> + > + Repeal All + } > - - + + + + + +
    @@ -71,7 +89,11 @@ export const EmergencyShuttleConsole = (props) => { } > - {authorizations.length > 0 ? ( + {authorizations.length === 0 ? ( + + No Active Authorizations + + ) : ( authorizations.map((authorization) => ( { {authorization.name} ({authorization.job}) )) - ) : ( - - No Active Authorizations - )}
    @@ -93,4 +111,4 @@ export const EmergencyShuttleConsole = (props) => {
    ); -}; +} diff --git a/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx b/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx index 0c632afd283..d91bc564e44 100644 --- a/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx +++ b/tgui/packages/tgui/interfaces/ExperimentConfigure.jsx @@ -109,7 +109,7 @@ export const ExperimentConfigure = (props) => { const { always_active, has_start_callback } = data; let techwebs = data.techwebs ?? []; - const experiments = sortBy((exp) => exp.name)(data.experiments ?? []); + const experiments = sortBy(data.experiments ?? [], (exp) => exp.name); // Group servers together by web let webs = new Map(); diff --git a/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx b/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx index af5bddf4d5b..e4951bd0041 100644 --- a/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx +++ b/tgui/packages/tgui/interfaces/Fabrication/DesignBrowser.tsx @@ -229,8 +229,9 @@ export const DesignBrowser = ( - {sortBy((category: Category) => category.title)( + {sortBy( Object.values(root.subcategories), + (category: Category) => category.title, ).map((category) => ( (
    {searchText.length > 0 ? ( - {sortBy((design: T) => design.name)( + {sortBy( Object.values(root.descendants), + (design: T) => design.name, ) .filter((design) => design.name @@ -290,8 +292,9 @@ export const DesignBrowser = ( ) : selectedCategory === ALL_CATEGORY ? ( - {sortBy((design: T) => design.name)( + {sortBy( Object.values(root.descendants), + (design: T) => design.name, ).map((design) => buildRecipeElement( design, @@ -380,8 +383,9 @@ const DesignBrowserTab = ( Object.entries(category.subcategories).length > 0 && selectedCategory === category.title && (
    - {sortBy((category: Category) => category.title)( + {sortBy( Object.values(category.subcategories), + (category: Category) => category.title, ).map((subcategory) => ( ( const body = ( - {sortBy((design: T) => design.name)(category.children).map((design) => + {sortBy(category.children, (design: T) => design.name).map((design) => buildRecipeElement( design, availableMaterials || {}, diff --git a/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx b/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx index 2936d93df3f..83923d5bd48 100644 --- a/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx +++ b/tgui/packages/tgui/interfaces/Fabrication/MaterialAccessBar.tsx @@ -55,7 +55,7 @@ export const MaterialAccessBar = (props: MaterialAccessBarProps) => { return ( - {sortBy((m: Material) => MATERIAL_RARITY[m.name])(availableMaterials).map( + {sortBy(availableMaterials, (m: Material) => MATERIAL_RARITY[m.name]).map( (material) => ( { const { act } = useBackend(); const { data } = useBackend(); const faxes = data.faxes - ? sortBy((sortFax: FaxInfo) => sortFax.fax_name)( + ? sortBy( data.syndicate_network ? data.faxes.filter((filterFax: FaxInfo) => filterFax.visible) : data.faxes.filter( (filterFax: FaxInfo) => filterFax.visible && !filterFax.syndicate_network, ), + (sortFax: FaxInfo) => sortFax.fax_name, ) : []; return ( diff --git a/tgui/packages/tgui/interfaces/Filteriffic.jsx b/tgui/packages/tgui/interfaces/Filteriffic.jsx index b13cddc34f4..09bd1426452 100644 --- a/tgui/packages/tgui/interfaces/Filteriffic.jsx +++ b/tgui/packages/tgui/interfaces/Filteriffic.jsx @@ -155,10 +155,9 @@ const FilterFlagsEntry = (props) => { const filterInfo = data.filter_info; const flags = filterInfo[filterType]['flags']; - return map((bitField, flagName) => ( + return map(flags, (bitField, flagName) => ( act('modify_filter_value', { name: filterName, @@ -167,8 +166,11 @@ const FilterFlagsEntry = (props) => { }, }) } - /> - ))(flags); + key={flagName} + > + {flagName} + + )); }; const FilterDataEntry = (props) => { @@ -340,9 +342,9 @@ export const Filteriffic = (props) => { {!hasFilters ? ( No filters ) : ( - map((entry, key) => ( + map(filters, (entry, key) => ( - ))(filters) + )) )}
    diff --git a/tgui/packages/tgui/interfaces/FishCatalog.tsx b/tgui/packages/tgui/interfaces/FishCatalog.tsx index 01efc5e3531..7bb85054fb0 100644 --- a/tgui/packages/tgui/interfaces/FishCatalog.tsx +++ b/tgui/packages/tgui/interfaces/FishCatalog.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { capitalize } from 'common/string'; import { useState } from 'react'; @@ -38,9 +37,7 @@ type FishCatalogData = { export const FishCatalog = (props) => { const { act, data } = useBackend(); const { fish_info, sponsored_by } = data; - const fish_by_name = flow([sortBy((fish: FishInfo) => fish.name)])( - fish_info || [], - ); + const fish_by_name = sortBy(fish_info || [], (fish: FishInfo) => fish.name); const [currentFish, setCurrentFish] = useState(null); return ( diff --git a/tgui/packages/tgui/interfaces/Gps.jsx b/tgui/packages/tgui/interfaces/Gps.jsx index 662335fcdc3..c12ba6a9efd 100644 --- a/tgui/packages/tgui/interfaces/Gps.jsx +++ b/tgui/packages/tgui/interfaces/Gps.jsx @@ -7,30 +7,36 @@ import { useBackend } from '../backend'; import { Box, Button, Icon, LabeledList, Section, Table } from '../components'; import { Window } from '../layouts'; -const coordsToVec = (coords) => map(parseFloat)(coords.split(', ')); +const coordsToVec = (coords) => map(coords.split(', '), parseFloat); export const Gps = (props) => { const { act, data } = useBackend(); const { currentArea, currentCoords, globalmode, power, tag, updating } = data; const signals = flow([ - map((signal, index) => { - // Calculate distance to the target. BYOND distance is capped to 127, - // that's why we roll our own calculations here. - const dist = - signal.dist && - Math.round( - vecLength( - vecSubtract(coordsToVec(currentCoords), coordsToVec(signal.coords)), - ), - ); - return { ...signal, dist, index }; - }), - sortBy( - // Signals with distance metric go first - (signal) => signal.dist === undefined, - // Sort alphabetically - (signal) => signal.entrytag, - ), + (signals) => + map(signals, (signal, index) => { + // Calculate distance to the target. BYOND distance is capped to 127, + // that's why we roll our own calculations here. + const dist = + signal.dist && + Math.round( + vecLength( + vecSubtract( + coordsToVec(currentCoords), + coordsToVec(signal.coords), + ), + ), + ); + return { ...signal, dist, index }; + }), + (signals) => + sortBy( + signals, + // Signals with distance metric go first + (signal) => signal.dist === undefined, + // Sort alphabetically + (signal) => signal.entrytag, + ), ])(data.signals || []); return ( diff --git a/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx b/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx index 8482bd665ec..74457a365bb 100644 --- a/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx +++ b/tgui/packages/tgui/interfaces/Hypertorus/Gases.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { useBackend } from 'tgui/backend'; import { @@ -90,10 +89,10 @@ const GasList = (props: GasListProps) => { } = props; const { start_power, start_cooling } = data; - const gases: HypertorusGas[] = flow([ - filter((gas: HypertorusGas) => gas.amount >= 0.01), - sortBy((gas: HypertorusGas) => -gas.amount), - ])(raw_gases); + const gases: HypertorusGas[] = sortBy( + filter(raw_gases, (gas) => gas.amount >= 0.01), + (gas) => -gas.amount, + ); if (stickyGases) { ensure_gases(gases, stickyGases); diff --git a/tgui/packages/tgui/interfaces/Jukebox.tsx b/tgui/packages/tgui/interfaces/Jukebox.tsx index 073b97ddee1..f450b08ebc4 100644 --- a/tgui/packages/tgui/interfaces/Jukebox.tsx +++ b/tgui/packages/tgui/interfaces/Jukebox.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { BooleanLike } from '../../common/react'; import { useBackend } from '../backend'; @@ -32,7 +31,7 @@ export const Jukebox = () => { const { act, data } = useBackend(); const { active, looping, track_selected, volume, songs } = data; - const songs_sorted: Song[] = flow([sortBy((song: Song) => song.name)])(songs); + const songs_sorted: Song[] = sortBy(songs, (song: Song) => song.name); const song_selected: Song | undefined = songs.find( (song) => song.name === track_selected, ); diff --git a/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx b/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx index 27b3cc0e05f..4e0aebdcdd5 100644 --- a/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx +++ b/tgui/packages/tgui/interfaces/LaborClaimConsole.jsx @@ -35,25 +35,14 @@ export const LaborClaimConsole = (props) => {
    -
    -
    - - Material - - Value - - - {ores.map((ore) => ( - - {toTitleCase(ore.ore)} - - - {ore.value} - - - - ))} -
    +
    + The nearby stacking machine will unload crates and collect smelted + materials, points will be calculated based on volume of delivered + materials. +
    + Please note that only sheets printed with our manufacturer's seal of + quality, such as those produced from the work camp furnace, will be + accepted as proof of labour.
    diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.jsx b/tgui/packages/tgui/interfaces/LaunchpadConsole.jsx deleted file mode 100644 index c3145844fc2..00000000000 --- a/tgui/packages/tgui/interfaces/LaunchpadConsole.jsx +++ /dev/null @@ -1,276 +0,0 @@ -import { useBackend } from '../backend'; -import { - Box, - Button, - Divider, - Flex, - Grid, - Input, - NoticeBox, - NumberInput, - Section, -} from '../components'; -import { Window } from '../layouts'; - -const LaunchpadButtonPad = (props) => { - const { act } = useBackend(); - return ( - - -
    - ); -}; - -export const LaunchpadConsole = (props) => { - const { act, data } = useBackend(); - const { launchpads = [], selected_id } = data; - return ( - - - {(launchpads.length === 0 && ( - No Pads Connected - )) || ( -
    - - - {launchpads.map((launchpad) => ( -
    - )} -
    -
    - ); -}; diff --git a/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx b/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx new file mode 100644 index 00000000000..fdad9955894 --- /dev/null +++ b/tgui/packages/tgui/interfaces/LaunchpadConsole.tsx @@ -0,0 +1,259 @@ +import { useBackend } from '../backend'; +import { + Box, + Button, + Icon, + Input, + NoticeBox, + NumberInput, + Section, + Stack, + Tabs, +} from '../components'; +import { Window } from '../layouts'; + +type Data = { + launchpads: LaunchPad[]; + pad_active: number; + pad_name: string; + range: number; + selected_id: number; + selected_pad: string; + x: number; + y: number; +}; + +type LaunchPad = { + id: number; + name: string; +}; + +const buttonConfigs = [ + [ + { icon: 'arrow-left', iconRotation: 45, x: -1, y: 1 }, + { icon: 'arrow-left', x: -1 }, + { icon: 'arrow-down', iconRotation: 45, x: -1, y: -1 }, + ], + [ + { icon: 'arrow-up', y: 1 }, + { text: 'R', x: 0, y: 0 }, + { icon: 'arrow-down', y: -1 }, + ], + [ + { icon: 'arrow-up', iconRotation: 45, x: 1, y: 1 }, + { icon: 'arrow-right', x: 1 }, + { icon: 'arrow-right', iconRotation: 45, x: 1, y: -1 }, + ], +] as const; + +export function LaunchpadConsole(props) { + const { data } = useBackend(); + const { launchpads = [], selected_id } = data; + + return ( + + + {launchpads.length === 0 ? ( + No Pads Connected + ) : ( + + + + + + + {!selected_id ? ( + Please select a pad + ) : ( + + )} + + + )} + + + ); +} + +export function LaunchpadControl(props) { + return ( + + + + + +
    + + + + + + + + +
    +
    + + + +
    + ); +} + +function LaunchpadTabs(props) { + const { act, data } = useBackend(); + const { launchpads = [], selected_id } = data; + + return ( +
    + + {launchpads.map((pad) => ( + + act('select_pad', { + id: pad.id, + }) + } + > + {pad.name} + + ))} + +
    + ); +} + +function LaunchpadTitle(props) { + const { act, data } = useBackend(); + const { pad_name } = data; + + return ( +
    + + + + act('rename', { + name: value, + }) + } + /> + + + + + +
    + ); +} + +function LaunchpadButtonPad(props) { + const { act } = useBackend(); + + return ( +
    + + {buttonConfigs.map((buttonRow, i) => ( + + {buttonRow.map((buttonConfig, j) => ( + + ))} + + ))} + +
    + ); +} + +function TargetingControls(props) { + const { act, data } = useBackend(); + const { x, y, range } = data; + + const inputConfigs = [ + { value: x, axis: 'x', icon: 'arrows-alt-h' }, + { value: y, axis: 'y', icon: 'arrows-alt-v' }, + ]; + + return ( +
    + {inputConfigs.map((inputConfig, i) => ( + + + + {inputConfig.axis.toUpperCase()} + + + + + act('set_pos', { + [inputConfig.axis]: value, + }) + } + step={1} + stepPixelSize={10} + value={inputConfig.value} + width="90px" + /> + + + ))} +
    + ); +} + +function DeliveryButtons(props) { + const { act } = useBackend(); + + return ( +
    + + + + + + + + +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx index f805a46c7fb..fd88fdc48fb 100644 --- a/tgui/packages/tgui/interfaces/LibraryAdmin.tsx +++ b/tgui/packages/tgui/interfaces/LibraryAdmin.tsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { capitalize } from 'common/string'; import { useState } from 'react'; @@ -81,10 +80,14 @@ type Book = { category: string; title: string; id: number; +}; + +type AdminBook = Book & { + author_ckey: string; deleted: boolean; }; -type DisplayBook = Book & { +type DisplayAdminBook = AdminBook & { key: number; }; @@ -120,14 +123,18 @@ const SearchAndDisplay = (props) => { view_raw, show_deleted, } = data; - const books = flow([ - map((book, i) => ({ - ...book, - // Generate a unique id - key: i, - })), - sortBy((book) => book.key), - ])(pages); + const books = sortBy( + map( + pages, + (book, i) => + ({ + ...book, + // Generate a unique id + key: i, + }) as DisplayAdminBook, + ), + (book) => book.key, + ); return (
    diff --git a/tgui/packages/tgui/interfaces/LibraryConsole.jsx b/tgui/packages/tgui/interfaces/LibraryConsole.jsx index 83d034915b8..386ac3ca0c0 100644 --- a/tgui/packages/tgui/interfaces/LibraryConsole.jsx +++ b/tgui/packages/tgui/interfaces/LibraryConsole.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { useState } from 'react'; @@ -136,14 +135,14 @@ export const Inventory = (props) => { export const InventoryDetails = (props) => { const { act, data } = useBackend(); - const inventory = flow([ - map((book, i) => ({ + const inventory = sortBy( + map(data.inventory, (book, i) => ({ ...book, // Generate a unique id key: i, })), - sortBy((book) => book.key), - ])(data.inventory); + (book) => book.key, + ); return (
    @@ -261,14 +260,14 @@ export const CheckoutEntries = (props) => { const CheckoutModal = (props) => { const { act, data } = useBackend(); - const inventory = flow([ - map((book, i) => ({ + const inventory = sortBy( + map(data.inventory, (book, i) => ({ ...book, // Generate a unique id key: i, })), - sortBy((book) => book.key), - ])(data.inventory); + (book) => book.key, + ); const [checkoutBook, setCheckoutBook] = useLocalState('CheckoutBook', false); const [bookName, setBookName] = useState('Insert Book name...'); @@ -387,14 +386,14 @@ export const SearchAndDisplay = (props) => { params_changed, can_db_request, } = data; - const records = flow([ - map((record, i) => ({ + const records = sortBy( + map(data.pages, (record, i) => ({ ...record, // Generate a unique id key: i, })), - sortBy((record) => record.key), - ])(data.pages); + (record) => record.key, + ); return ( diff --git a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx index 6b8edb380be..cb27e42704d 100644 --- a/tgui/packages/tgui/interfaces/LibraryVisitor.jsx +++ b/tgui/packages/tgui/interfaces/LibraryVisitor.jsx @@ -1,5 +1,4 @@ import { map, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { @@ -71,14 +70,14 @@ const SearchAndDisplay = (props) => { author, params_changed, } = data; - const records = flow([ - map((record, i) => ({ + const records = sortBy( + map(data.pages, (record, i) => ({ ...record, // Generate a unique id key: i, })), - sortBy((record) => record.key), - ])(data.pages); + (record) => record.key, + ); return (
    diff --git a/tgui/packages/tgui/interfaces/LootPanel/GroupedContents.tsx b/tgui/packages/tgui/interfaces/LootPanel/GroupedContents.tsx new file mode 100644 index 00000000000..5bfecce0b8a --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/GroupedContents.tsx @@ -0,0 +1,47 @@ +import { createSearch } from 'common/string'; +import { useMemo } from 'react'; + +import { Flex } from '../../components'; +import { LootBox } from './LootBox'; +import { SearchGroup, SearchItem } from './types'; + +type Props = { + contents: SearchItem[]; + searchText: string; +}; + +export function GroupedContents(props: Props) { + const { contents, searchText } = props; + + // limitations: items with different stack counts, charges etc. + const contentsByPath = useMemo(() => { + const acc: Record = {}; + + for (let i = 0; i < contents.length; i++) { + const item = contents[i]; + if (item.path) { + if (!acc[item.path]) { + acc[item.path] = []; + } + acc[item.path].push(item); + } else { + acc[item.ref] = [item]; + } + } + return acc; + }, [contents]); + + const filteredContents: SearchGroup[] = Object.entries(contentsByPath) + .filter(createSearch(searchText, ([_, items]) => items[0].name)) + .map(([_, items]) => ({ amount: items.length, item: items[0] })); + + return ( + + {filteredContents.map((group) => ( + + + + ))} + + ); +} diff --git a/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx new file mode 100644 index 00000000000..889d50de884 --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/IconDisplay.tsx @@ -0,0 +1,24 @@ +import { DmIcon, Icon, Image } from '../../components'; +import { SearchItem } from './types'; + +type Props = { + item: SearchItem; +}; + +export function IconDisplay(props: Props) { + const { + item: { icon, icon_state }, + } = props; + + const fallback = ; + + if (!icon) { + return fallback; + } + + if (icon_state) { + return ; + } + + return ; +} diff --git a/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx b/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx new file mode 100644 index 00000000000..22b0c8532dd --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/LootBox.tsx @@ -0,0 +1,53 @@ +import { capitalizeAll } from 'common/string'; + +import { useBackend } from '../../backend'; +import { Tooltip } from '../../components'; +import { IconDisplay } from './IconDisplay'; +import { SearchGroup, SearchItem } from './types'; + +type Props = + | { + item: SearchItem; + } + | { + group: SearchGroup; + }; + +export function LootBox(props: Props) { + const { act } = useBackend(); + + let amount = 0; + let item: SearchItem; + if ('group' in props) { + amount = props.group.amount; + item = props.group.item; + } else { + item = props.item; + } + + return ( + +
    + act('grab', { + ctrl: event.ctrlKey, + ref: item.ref, + shift: event.shiftKey, + }) + } + onContextMenu={(event) => { + event.preventDefault(); + act('grab', { + middle: true, + ref: item.ref, + shift: true, + }); + }} + > + + {amount > 1 &&
    {amount}
    } +
    +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/LootPanel/RawContents.tsx b/tgui/packages/tgui/interfaces/LootPanel/RawContents.tsx new file mode 100644 index 00000000000..4241d36dc0a --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/RawContents.tsx @@ -0,0 +1,28 @@ +import { createSearch } from 'common/string'; + +import { Flex } from '../../components'; +import { LootBox } from './LootBox'; +import { SearchItem } from './types'; + +type Props = { + contents: SearchItem[]; + searchText: string; +}; + +export function RawContents(props: Props) { + const { contents, searchText } = props; + + const filteredContents = contents.filter( + createSearch(searchText, (item: SearchItem) => item.name), + ); + + return ( + + {filteredContents.map((item) => ( + + + + ))} + + ); +} diff --git a/tgui/packages/tgui/interfaces/LootPanel/index.tsx b/tgui/packages/tgui/interfaces/LootPanel/index.tsx new file mode 100644 index 00000000000..bd64113f302 --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/index.tsx @@ -0,0 +1,76 @@ +import { KEY } from 'common/keys'; +import { BooleanLike } from 'common/react'; +import { useState } from 'react'; + +import { useBackend } from '../../backend'; +import { Button, Input, Section, Stack } from '../../components'; +import { Window } from '../../layouts'; +import { GroupedContents } from './GroupedContents'; +import { RawContents } from './RawContents'; +import { SearchItem } from './types'; + +type Data = { + contents: SearchItem[]; + searching: BooleanLike; +}; + +export function LootPanel(props) { + const { act, data } = useBackend(); + const { contents = [], searching } = data; + + const [grouping, setGrouping] = useState(true); + const [searchText, setSearchText] = useState(''); + + const total = contents.length ? contents.length - 1 : 0; + + return ( + + { + if (event.key === KEY.Escape) { + Byond.sendMessage('close'); + } + }} + > +
    + + setSearchText(value)} + placeholder="Search" + /> + + +
    +
    +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/LootPanel/types.ts b/tgui/packages/tgui/interfaces/LootPanel/types.ts new file mode 100644 index 00000000000..f17b02b0c13 --- /dev/null +++ b/tgui/packages/tgui/interfaces/LootPanel/types.ts @@ -0,0 +1,13 @@ +export type SearchItem = { + name: string; + path: string; + ref: string; +} & Partial<{ + icon: string; + icon_state: string; +}>; + +export type SearchGroup = { + amount: number; + item: SearchItem; +}; diff --git a/tgui/packages/tgui/interfaces/MatMarket.tsx b/tgui/packages/tgui/interfaces/MatMarket.tsx index 11de67fb656..d793b8501f0 100644 --- a/tgui/packages/tgui/interfaces/MatMarket.tsx +++ b/tgui/packages/tgui/interfaces/MatMarket.tsx @@ -118,7 +118,7 @@ export const MatMarket = (props) => {
    - {sortBy((tempmat: Material) => tempmat.rarity)(materials).map( + {sortBy(materials, (tempmat: Material) => tempmat.rarity).map( (material, i) => (
    diff --git a/tgui/packages/tgui/interfaces/Mecha/data.ts b/tgui/packages/tgui/interfaces/Mecha/data.ts index 993c7a1d4be..0bccc7fa5c1 100644 --- a/tgui/packages/tgui/interfaces/Mecha/data.ts +++ b/tgui/packages/tgui/interfaces/Mecha/data.ts @@ -1,5 +1,7 @@ import { BooleanLike } from 'common/react'; +import { Region } from '../common/AccessConfig'; + export type AccessData = { name: string; number: number; @@ -25,7 +27,7 @@ export type MainData = { overclock_temp_percentage: number; one_access: BooleanLike; - regions: string[]; + regions: Region[]; accesses: string[]; servo_rating: number; diff --git a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx index 7aee1f276a8..03672e05008 100644 --- a/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx +++ b/tgui/packages/tgui/interfaces/MedicalRecords/RecordTabs.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useState } from 'react'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -28,10 +27,10 @@ export const MedicalRecordTabs = (props) => { const [search, setSearch] = useState(''); - const sorted: MedicalRecord[] = flow([ - filter((record: MedicalRecord) => isRecordMatch(record, search)), - sortBy((record: MedicalRecord) => record.name?.toLowerCase()), - ])(records); + const sorted: MedicalRecord[] = sortBy( + filter(records, (record) => isRecordMatch(record, search)), + (record) => record.name?.toLowerCase(), + ); return ( diff --git a/tgui/packages/tgui/interfaces/NtosArcade.jsx b/tgui/packages/tgui/interfaces/NtosArcade.jsx deleted file mode 100644 index b8c9aa05109..00000000000 --- a/tgui/packages/tgui/interfaces/NtosArcade.jsx +++ /dev/null @@ -1,135 +0,0 @@ -import { resolveAsset } from '../assets'; -import { useBackend } from '../backend'; -import { - AnimatedNumber, - Box, - Button, - Grid, - LabeledList, - ProgressBar, - Section, -} from '../components'; -import { NtosWindow } from '../layouts'; - -export const NtosArcade = (props) => { - const { act, data } = useBackend(); - return ( - - -
    - - - - - - - - {data.PlayerHitpoints}HP - - - - - {data.PlayerMP}MP - - - - -
    - {data.Status} -
    -
    - - - - HP - - -
    - -
    -
    -
    - -
    -
    -
    - ); -}; diff --git a/tgui/packages/tgui/interfaces/NtosArcade.tsx b/tgui/packages/tgui/interfaces/NtosArcade.tsx new file mode 100644 index 00000000000..ea63e62e037 --- /dev/null +++ b/tgui/packages/tgui/interfaces/NtosArcade.tsx @@ -0,0 +1,178 @@ +import { BooleanLike } from 'common/react'; + +import { resolveAsset } from '../assets'; +import { useBackend } from '../backend'; +import { + AnimatedNumber, + Box, + Button, + Divider, + LabeledList, + NoticeBox, + ProgressBar, + Section, + Stack, +} from '../components'; +import { NtosWindow } from '../layouts'; + +type Data = { + BossID: string; + GameActive: BooleanLike; + Hitpoints: number; + PauseState: BooleanLike; + PlayerHitpoints: number; + PlayerMP: number; + Status: string; + TicketCount: number; +}; + +export function NtosArcade(props) { + return ( + + +
    + + + + + + + + + +
    +
    +
    + ); +} + +function PlayerStats(props) { + const { data } = useBackend(); + const { PauseState, PlayerHitpoints, PlayerMP, Status } = data; + + return ( + <> + + + + {PlayerHitpoints}HP + + + + + {PlayerMP}MP + + + + + {Status} + + ); +} + +function BossBar(props) { + const { data } = useBackend(); + const { BossID, Hitpoints } = data; + + return ( + <> + + + HP + + +
    + +
    + + ); +} + +function BottomButtons(props) { + const { act, data } = useBackend(); + const { GameActive, PauseState, TicketCount } = data; + + return ( + <> + + + + + + + + + = 1 ? 'good' : 'normal'}> + Earned Tickets: {TicketCount} + + + ); +} diff --git a/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx index d8830fe79af..b0f17d449eb 100644 --- a/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx +++ b/tgui/packages/tgui/interfaces/NtosCrewManifest.jsx @@ -26,7 +26,7 @@ export const NtosCrewManifest = (props) => { /> } > - {map((entries, department) => ( + {map(manifest, (entries, department) => (
    {entries.map((entry) => ( @@ -45,7 +45,7 @@ export const NtosCrewManifest = (props) => { ))}
    - ))(manifest)} + ))}
    diff --git a/tgui/packages/tgui/interfaces/NtosEmojipedia.tsx b/tgui/packages/tgui/interfaces/NtosEmojipedia.tsx index 3852e90ac26..d9e6c7233ee 100644 --- a/tgui/packages/tgui/interfaces/NtosEmojipedia.tsx +++ b/tgui/packages/tgui/interfaces/NtosEmojipedia.tsx @@ -3,7 +3,7 @@ import { createSearch } from 'common/string'; import { useState } from 'react'; import { useBackend } from '../backend'; -import { Button, Image, Input, Section } from '../components'; +import { Button, Image, Input, Section, Tooltip } from '../components'; import { NtosWindow } from '../layouts'; type Data = { @@ -44,15 +44,15 @@ export const NtosEmojipedia = (props) => { } > {filteredEmojis.map((emoji) => ( - { - copyText(emoji.name); - }} - /> + + { + copyText(emoji.name); + }} + /> + ))} diff --git a/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx b/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx index 852ca62cc55..896ca90d602 100644 --- a/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx +++ b/tgui/packages/tgui/interfaces/NtosMessenger/index.tsx @@ -100,7 +100,8 @@ const ContactsScreen = (props: any) => { const [searchUser, setSearchUser] = useState(''); - const sortByUnreads = sortBy((chat) => chat.unread_messages); + const sortByUnreads = (array: NtChat[]) => + sortBy(array, (chat) => chat.unread_messages); const searchChatByName = createSearch( searchUser, diff --git a/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx b/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx index 0bfdafc7b62..489c443f0c1 100644 --- a/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx +++ b/tgui/packages/tgui/interfaces/NtosNetDownloader.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { scale, toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { createSearch } from 'common/string'; @@ -70,20 +69,22 @@ export const NtosNetDownloader = (props) => { searchItem, (program) => program.filedesc, ); - const items = flow([ + let items = searchItem.length > 0 ? // If we have a query, search everything for it. - filter(search) + filter(programs, search) : // Otherwise, show respective programs for the category. - filter((program: ProgramData) => program.category === selectedCategory), - // This sorts all programs in the lists by name and compatibility - sortBy( - (program: ProgramData) => !program.compatible, - (program: ProgramData) => program.filedesc, - ), + filter(programs, (program) => program.category === selectedCategory); + // This sorts all programs in the lists by name and compatibility + items = sortBy( + items, + (program: ProgramData) => !program.compatible, + (program: ProgramData) => program.filedesc, + ); + if (!emagged) { // This filters the list to only contain verified programs - !emagged && filter((program: ProgramData) => program.verifiedsource === 1), - ])(programs); + items = filter(items, (program) => program.verifiedsource === 1); + } const disk_free_space = downloading ? disk_size - Number(toFixed(disk_used + downloadcompletion)) : disk_size - disk_used; diff --git a/tgui/packages/tgui/interfaces/NuclearBomb.jsx b/tgui/packages/tgui/interfaces/NuclearBomb.tsx similarity index 64% rename from tgui/packages/tgui/interfaces/NuclearBomb.jsx rename to tgui/packages/tgui/interfaces/NuclearBomb.tsx index 81b2e622e9d..5cda6d97cbb 100644 --- a/tgui/packages/tgui/interfaces/NuclearBomb.jsx +++ b/tgui/packages/tgui/interfaces/NuclearBomb.tsx @@ -1,32 +1,40 @@ -import { classes } from 'common/react'; +import { BooleanLike, classes } from 'common/react'; import { useBackend } from '../backend'; -import { Box, Button, Flex, Grid, Icon } from '../components'; +import { Box, Button, Icon, Stack } from '../components'; import { Window } from '../layouts'; +type Data = { + disk_present: BooleanLike; + status1: string; + status2: string; + anchored: BooleanLike; +}; + +const KEYPAD = [ + ['1', '4', '7', 'C'], + ['2', '5', '8', '0'], + ['3', '6', '9', 'E'], +] as const; + // This ui is so many manual overrides and !important tags // and hand made width sets that changing pretty much anything // is going to require a lot of tweaking it get it looking correct again // I'm sorry, but it looks bangin -export const NukeKeypad = (props) => { +export function NukeKeypad(props) { const { act } = useBackend(); - const keypadKeys = [ - ['1', '4', '7', 'C'], - ['2', '5', '8', '0'], - ['3', '6', '9', 'E'], - ]; + return ( - - {keypadKeys.map((keyColumn) => ( - + + {KEYPAD.map((keyColumn) => ( + {keyColumn.map((key) => ( ))} - + ))} - + ); -}; +} + +export function NuclearBomb(props) { + const { act, data } = useBackend(); + const { status1, status2 } = data; -export const NuclearBomb = (props) => { - const { act, data } = useBackend(); - const { anchored, disk_present, status1, status2 } = data; return ( - - + + {status1} - - + + {status2} - - + + - - + + ); -}; +} diff --git a/tgui/packages/tgui/interfaces/Orbit/helpers.ts b/tgui/packages/tgui/interfaces/Orbit/helpers.ts index 8ad071c699b..c0668dd02d8 100644 --- a/tgui/packages/tgui/interfaces/Orbit/helpers.ts +++ b/tgui/packages/tgui/interfaces/Orbit/helpers.ts @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { HEALTH, THREAT } from './constants'; import type { AntagGroup, Antagonist, Observable } from './types'; @@ -18,7 +17,7 @@ export const getAntagCategories = (antagonists: Antagonist[]) => { categories[antag_group].push(player); }); - return sortBy(([key]) => key)(Object.entries(categories)); + return sortBy(Object.entries(categories), ([key]) => key); }; /** Returns a disguised name in case the person is wearing someone else's ID */ @@ -43,15 +42,19 @@ export const getMostRelevant = ( searchQuery: string, observables: Observable[][], ): Observable => { - return flow([ - // Filters out anything that doesn't match search - filter((observable) => - isJobOrNameMatch(observable, searchQuery), - ), + const queriedObservables = // Sorts descending by orbiters - sortBy((observable) => -(observable.orbiters || 0)), - // Makes a single Observables list for an easy search - ])(observables.flat())[0]; + sortBy( + // Filters out anything that doesn't match search + filter( + observables + // Makes a single Observables list for an easy search + .flat(), + (observable) => isJobOrNameMatch(observable, searchQuery), + ), + (observable) => -(observable.orbiters || 0), + ); + return queriedObservables[0]; }; /** Returns the display color for certain health percentages */ diff --git a/tgui/packages/tgui/interfaces/Orbit/index.tsx b/tgui/packages/tgui/interfaces/Orbit/index.tsx index 0d992d981a8..0103fe8750f 100644 --- a/tgui/packages/tgui/interfaces/Orbit/index.tsx +++ b/tgui/packages/tgui/interfaces/Orbit/index.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { capitalizeFirst, multiline } from 'common/string'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -204,16 +203,13 @@ const ObservableSection = (props: { const [searchQuery] = useLocalState('searchQuery', ''); - const filteredSection: Observable[] = flow([ - filter((observable) => - isJobOrNameMatch(observable, searchQuery), - ), - sortBy((observable) => + const filteredSection = sortBy( + filter(section, (observable) => isJobOrNameMatch(observable, searchQuery)), + (observable) => getDisplayName(observable.full_name, observable.name) .replace(/^"/, '') .toLowerCase(), - ), - ])(section); + ); if (!filteredSection.length) { return null; diff --git a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx index 2a848073769..96ec4b818b3 100644 --- a/tgui/packages/tgui/interfaces/PersonalCrafting.tsx +++ b/tgui/packages/tgui/interfaces/PersonalCrafting.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { BooleanLike, classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -186,44 +185,44 @@ export const PersonalCrafting = (props) => { const [activeType, setFoodType] = useState( Object.keys(craftability).length ? 'Can Make' : data.foodtypes[0], ); - const material_occurences = flow([ - sortBy((material) => -material.occurences), - ])(data.material_occurences); + const material_occurences = sortBy( + data.material_occurences, + (material) => -material.occurences, + ); const [activeMaterial, setMaterial] = useState( material_occurences[0].atom_id, ); const [tabMode, setTabMode] = useState(0); const searchName = createSearch(searchText, (item: Recipe) => item.name); - let recipes = flow([ - filter( - (recipe) => - // If craftable only is selected, then filter by craftability - (!display_craftable_only || Boolean(craftability[recipe.ref])) && - // Ignore categories and types when searching - (searchText.length > 0 || - // Is foodtype mode and the active type matches - (tabMode === TABS.foodtype && - mode === MODE.cooking && - ((activeType === 'Can Make' && Boolean(craftability[recipe.ref])) || - recipe.foodtypes?.includes(activeType))) || - // Is material mode and the active material or catalysts match - (tabMode === TABS.material && - Object.keys(recipe.reqs).includes(activeMaterial)) || - // Is category mode and the active categroy matches - (tabMode === TABS.category && - ((activeCategory === 'Can Make' && - Boolean(craftability[recipe.ref])) || - recipe.category === activeCategory))), - ), - sortBy((recipe) => [ - activeCategory === 'Can Make' - ? 99 - Object.keys(recipe.reqs).length - : Number(craftability[recipe.ref]), - recipe.name.toLowerCase(), - ]), - ])(data.recipes); + let recipes = filter( + data.recipes, + (recipe) => + // If craftable only is selected, then filter by craftability + (!display_craftable_only || Boolean(craftability[recipe.ref])) && + // Ignore categories and types when searching + (searchText.length > 0 || + // Is foodtype mode and the active type matches + (tabMode === TABS.foodtype && + mode === MODE.cooking && + ((activeType === 'Can Make' && Boolean(craftability[recipe.ref])) || + recipe.foodtypes?.includes(activeType))) || + // Is material mode and the active material or catalysts match + (tabMode === TABS.material && + Object.keys(recipe.reqs).includes(activeMaterial)) || + // Is category mode and the active categroy matches + (tabMode === TABS.category && + ((activeCategory === 'Can Make' && + Boolean(craftability[recipe.ref])) || + recipe.category === activeCategory))), + ); + recipes = sortBy(recipes, (recipe) => [ + activeCategory === 'Can Make' + ? 99 - Object.keys(recipe.reqs).length + : Number(craftability[recipe.ref]), + recipe.name.toLowerCase(), + ]); if (searchText.length > 0) { - recipes = recipes.filter(searchName); + recipes = filter(recipes, searchName); } const canMake = ['Can Make']; const categories = canMake diff --git a/tgui/packages/tgui/interfaces/Photocopier.jsx b/tgui/packages/tgui/interfaces/Photocopier.jsx index 29353918e19..e1b8b74bb0a 100644 --- a/tgui/packages/tgui/interfaces/Photocopier.jsx +++ b/tgui/packages/tgui/interfaces/Photocopier.jsx @@ -178,7 +178,7 @@ const Blanks = (props) => { const { act, data } = useBackend(); const { blanks, categories, category } = data; - const sortedBlanks = sortBy((blank) => blank.name)(blanks || []); + const sortedBlanks = sortBy(blanks || [], (blank) => blank.name); const selectedCategory = category ?? categories[0]; const visibleBlanks = sortedBlanks.filter( diff --git a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx b/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx index b045304c4ca..1a623bb2079 100644 --- a/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx +++ b/tgui/packages/tgui/interfaces/PlaneMasterDebug.tsx @@ -158,7 +158,7 @@ const sortConnectionRefs = function ( direction: ConnectionDirection, connectSources: AssocConnected, ) { - refs = sortBy((connection: ConnectionRef) => connection.sort_by)(refs); + refs = sortBy(refs, (connection: ConnectionRef) => connection.sort_by); refs.map((connection, index) => { let connectSource = connectSources[connection.ref]; if (direction === ConnectionDirection.Outgoing) { @@ -264,14 +264,15 @@ const positionPlanes = (connectSources: AssocConnected) => { // and get rid of the now unneeded parent refs const stack = depth_stack.map((layer) => flow([ - sortBy((plane: string) => plane_info[plane].plane), - sortBy((plane: string) => { - const read_from = plane_info[layer[plane]]; - if (!read_from) { - return 0; - } - return read_from.plane; - }), + (planes) => sortBy(planes, (plane: string) => plane_info[plane].plane), + (planes) => + sortBy(planes, (plane: string) => { + const read_from = plane_info[layer[plane]]; + if (!read_from) { + return 0; + } + return read_from.plane; + }), ])(Object.keys(layer)), ); @@ -932,7 +933,7 @@ const AddModal = (props) => { ); const plane_list = Object.keys(plane_info).map((plane) => plane_info[plane]); - const planes = sortBy((plane: Plane) => -plane.plane)(plane_list); + const planes = sortBy(plane_list, (plane: Plane) => -plane.plane); const plane_options = planes.map((plane) => plane.name); diff --git a/tgui/packages/tgui/interfaces/PortableChemMixer.tsx b/tgui/packages/tgui/interfaces/PortableChemMixer.tsx index fc0b4cbb95e..9ffbe06b943 100644 --- a/tgui/packages/tgui/interfaces/PortableChemMixer.tsx +++ b/tgui/packages/tgui/interfaces/PortableChemMixer.tsx @@ -26,8 +26,9 @@ export const PortableChemMixer = (props) => { const { act, data } = useBackend(); const { beaker } = data; const beakerTransferAmounts = beaker ? beaker.transferAmounts : []; - const chemicals = sortBy((chem: DispensableReagent) => chem.id)( + const chemicals = sortBy( data.chemicals, + (chem: DispensableReagent) => chem.id, ); return ( diff --git a/tgui/packages/tgui/interfaces/PowerMonitor.jsx b/tgui/packages/tgui/interfaces/PowerMonitor.jsx index 6e0e0dca173..039e700f68c 100644 --- a/tgui/packages/tgui/interfaces/PowerMonitor.jsx +++ b/tgui/packages/tgui/interfaces/PowerMonitor.jsx @@ -48,18 +48,22 @@ export const PowerMonitorContent = (props) => { const maxValue = Math.max(PEAK_DRAW, ...history.supply, ...history.demand); // Process area data const areas = flow([ - map((area, i) => ({ - ...area, - // Generate a unique id - id: area.name + i, - })), - sortByField === 'name' && sortBy((area) => area.name), - sortByField === 'charge' && sortBy((area) => -area.charge), + (areas) => + map(areas, (area, i) => ({ + ...area, + // Generate a unique id + id: area.name + i, + })), + sortByField === 'name' && ((areas) => sortBy(areas, (area) => area.name)), + sortByField === 'charge' && + ((areas) => sortBy(areas, (area) => -area.charge)), sortByField === 'draw' && - sortBy( - (area) => -powerRank(area.load), - (area) => -parseFloat(area.load), - ), + ((areas) => + sortBy( + areas, + (area) => -powerRank(area.load), + (area) => -parseFloat(area.load), + )), ])(data.areas); return ( <> diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx index 2ff85058da6..c00889536e0 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/AntagsPage.tsx @@ -25,9 +25,10 @@ const antagsByCategory = new Map(); // This will break at priorities higher than 10, but that almost definitely // will not happen. -const binaryInsertAntag = binaryInsertWith((antag: Antagonist) => { - return `${antag.priority}_${antag.name}`; -}); +const binaryInsertAntag = (collection: Antagonist[], value: Antagonist) => + binaryInsertWith(collection, value, (antag) => { + return `${antag.priority}_${antag.name}`; + }); for (const antagKey of requireAntag.keys()) { const antag = requireAntag<{ diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx index 05845ffe548..ad81b2c27fd 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/GamePreferencesPage.tsx @@ -13,11 +13,13 @@ type PreferenceChild = { children: ReactNode; }; -const binaryInsertPreference = binaryInsertWith( - (child) => child.name, -); +const binaryInsertPreference = ( + collection: PreferenceChild[], + value: PreferenceChild, +) => binaryInsertWith(collection, value, (child) => child.name); -const sortByName = sortBy<[string, PreferenceChild[]]>(([name]) => name); +const sortByName = (array: [string, PreferenceChild[]][]) => + sortBy(array, ([name]) => name); export const GamePreferencesPage = (props) => { const { act, data } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx index bf40f73049a..10c962a8302 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/JobsPage.tsx @@ -14,10 +14,11 @@ import { import { ServerPreferencesFetcher } from './ServerPreferencesFetcher'; const sortJobs = (entries: [string, Job][], head?: string) => - sortBy<[string, Job]>( + sortBy( + entries, ([key, _]) => (key === head ? -1 : 1), ([key, _]) => key, - )(entries); + ); const PRIORITY_BUTTON_SIZE = '18px'; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx index c3b123105ab..32d39c287df 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/KeybindingsPage.tsx @@ -67,15 +67,14 @@ const KEY_CODE_TO_BYOND: Record = { */ const DOM_KEY_LOCATION_NUMPAD = 3; -const sortKeybindings = sortBy(([_, keybinding]: [string, Keybinding]) => { - return keybinding.name; -}); +const sortKeybindings = (array: [string, Keybinding][]) => + sortBy(array, ([_, keybinding]) => { + return keybinding.name; + }); -const sortKeybindingsByCategory = sortBy( - ([category, _]: [string, Record]) => { - return category; - }, -); +const sortKeybindingsByCategory = ( + array: [string, Record][], +) => sortBy(array, ([category, _]) => category); const formatKeyboardEvent = (event: KeyboardEvent): string => { let text = ''; @@ -177,12 +176,6 @@ const KeybindingName = (props: { keybinding: Keybinding }) => { ); }; -KeybindingName.defaultHooks = { - onComponentShouldUpdate: (lastProps, nextProps) => { - return lastProps.keybinding !== nextProps.keybinding; - }, -}; - const ResetToDefaultButton = (props: { keybindingId: string }) => { const { act } = useBackend(); diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx index 30766576256..846c05199d2 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/MainPage.tsx @@ -1,11 +1,9 @@ -import { filterMap, sortBy } from 'common/collections'; +import { filter, map, sortBy } from 'common/collections'; import { exhaustiveCheck } from 'common/exhaustive'; import { classes } from 'common/react'; +import { createSearch } from 'common/string'; import { useState } from 'react'; -import { filter } from '../../../common/collections'; -import { flow } from '../../../common/fp'; -import { createSearch } from '../../../common/string'; import { sendAct, useBackend } from '../../backend'; import { Autofocus, @@ -229,8 +227,14 @@ const ChoicedSelection = (props: { }; const searchInCatalog = (searchText = '', catalog: Record) => { - const maybeSearch = createSearch(searchText, ([name, _icon]) => name); - return flow([searchText && filter(maybeSearch)])(Object.entries(catalog)); + let items = Object.entries(catalog); + if (searchText) { + items = filter( + items, + createSearch(searchText, ([name, _icon]) => name), + ); + } + return items; }; const GenderButton = (props: { @@ -395,10 +399,11 @@ const createSetRandomization = }); }; -const sortPreferences = sortBy<[string, unknown]>(([featureId, _]) => { - const feature = features[featureId]; - return feature?.name; -}); +const sortPreferences = (array: [string, unknown][]) => + sortBy(array, ([featureId, _]) => { + const feature = features[featureId]; + return feature?.name; + }); export const PreferenceList = (props: { act: typeof sendAct; @@ -478,22 +483,20 @@ export const getRandomization = ( const { data } = useBackend(); + if (!randomBodyEnabled) { + return {}; + } + return Object.fromEntries( - filterMap(Object.keys(preferences), (preferenceKey) => { - if (serverData.random.randomizable.indexOf(preferenceKey) === -1) { - return undefined; - } - - if (!randomBodyEnabled) { - return undefined; - } - - return [ - preferenceKey, - data.character_preferences.randomization[preferenceKey] || - RandomSetting.Disabled, - ]; - }), + map( + filter(Object.keys(preferences), (key) => + serverData.random.randomizable.includes(key), + ), + (key) => [ + key, + data.character_preferences.randomization[key] || RandomSetting.Disabled, + ], + ), ); }; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx index 6dd79e19bdf..b334e460cea 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/QuirksPage.tsx @@ -1,4 +1,4 @@ -import { filterMap } from 'common/collections'; +import { filter } from 'common/collections'; import { useState } from 'react'; import { useBackend } from '../../backend'; @@ -23,13 +23,9 @@ function getCorrespondingPreferences( relevant_preferences: Record, ) { return Object.fromEntries( - filterMap(Object.keys(relevant_preferences), (key) => { - if (!customization_options.includes(key)) { - return undefined; - } - - return [key, relevant_preferences[key]]; - }), + filter(Object.entries(relevant_preferences), ([key, value]) => + customization_options.includes(key), + ), ); } diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx index eadb6e94d42..039fb8027ad 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/names.tsx @@ -21,9 +21,11 @@ type NameWithKey = { name: Name; }; -const binaryInsertName = binaryInsertWith(({ key }) => key); +const binaryInsertName = (collection: NameWithKey[], value: NameWithKey) => + binaryInsertWith(collection, value, ({ key }) => key); -const sortNameWithKeyEntries = sortBy<[string, NameWithKey[]]>(([key]) => key); +const sortNameWithKeyEntries = (array: [string, NameWithKey[]][]) => + sortBy(array, ([key]) => key); export const MultiNameInput = (props: { handleClose: () => void; diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx index b911a196b3c..6da6aec820d 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/base.tsx @@ -1,4 +1,4 @@ -import { sortBy, sortStrings } from 'common/collections'; +import { sort, sortBy } from 'common/collections'; import { BooleanLike, classes } from 'common/react'; import { ComponentType, @@ -22,7 +22,8 @@ import { import { createSetPreference, PreferencesMenuData } from '../../data'; import { ServerPreferencesFetcher } from '../../ServerPreferencesFetcher'; -export const sortChoices = sortBy<[string, ReactNode]>(([name]) => name); +export const sortChoices = (array: [string, ReactNode][]) => + sortBy(array, ([name]) => name); export type Feature< TReceiving, @@ -210,7 +211,7 @@ export const FeatureDropdownInput = ( return ( ; }; -const sortHexValues = sortBy<[string, HexValue]>( - ([_, hexValue]) => -hexValue.lightness, -); +const sortHexValues = (array: [string, HexValue][]) => + sortBy(array, ([_, hexValue]) => -hexValue.lightness); export const skin_tone: Feature = { name: 'Skin tone', diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx index cfa323dce2c..ab973e3659e 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/ghost.tsx @@ -22,10 +22,13 @@ export const ghost_accs: FeatureChoiced = { component: FeatureDropdownInput, }; -const insertGhostForm = binaryInsertWith<{ +type GhostForm = { displayText: ReactNode; value: string; -}>(({ value }) => value); +}; + +const insertGhostForm = (collection: GhostForm[], value: GhostForm) => + binaryInsertWith(collection, value, ({ value }) => value); const GhostFormInput = ( props: FeatureValueProps, diff --git a/tgui/packages/tgui/interfaces/Radio.jsx b/tgui/packages/tgui/interfaces/Radio.jsx index d25409e033a..d852149571a 100644 --- a/tgui/packages/tgui/interfaces/Radio.jsx +++ b/tgui/packages/tgui/interfaces/Radio.jsx @@ -23,10 +23,10 @@ export const Radio = (props) => { const tunedChannel = RADIO_CHANNELS.find( (channel) => channel.freq === frequency, ); - const channels = map((value, key) => ({ + const channels = map(data.channels, (value, key) => ({ name: key, status: !!value, - }))(data.channels); + })); // Calculate window height let height = 106; if (subspace) { diff --git a/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx b/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx index 5195c451838..117df20b8c0 100644 --- a/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx +++ b/tgui/packages/tgui/interfaces/RequestsConsole/MessageWriteTab.tsx @@ -1,4 +1,4 @@ -import { sortStrings } from 'common/collections'; +import { sort } from 'common/collections'; import { useState } from 'react'; import { useBackend, useLocalState } from '../../backend'; @@ -22,9 +22,9 @@ export const MessageWriteTab = (props) => { information_consoles = [], } = data; - const sorted_assistance = sortStrings(assistance_consoles); - const sorted_supply = sortStrings(supply_consoles); - const sorted_information = sortStrings(information_consoles); + const sorted_assistance = sort(assistance_consoles); + const sorted_supply = sort(supply_consoles); + const sorted_information = sort(information_consoles); const resetMessage = () => { setMessageText(''); diff --git a/tgui/packages/tgui/interfaces/RestockTracker.jsx b/tgui/packages/tgui/interfaces/RestockTracker.jsx index 236f486069c..7df606adffc 100644 --- a/tgui/packages/tgui/interfaces/RestockTracker.jsx +++ b/tgui/packages/tgui/interfaces/RestockTracker.jsx @@ -17,8 +17,9 @@ export const Restock = (props) => { export const RestockTracker = (props) => { const { data } = useBackend(); - const vending_list = sortBy((vend) => vend.percentage)( + const vending_list = sortBy( data.vending_list ?? [], + (vend) => vend.percentage, ); return (
    diff --git a/tgui/packages/tgui/interfaces/Roulette.jsx b/tgui/packages/tgui/interfaces/Roulette.jsx deleted file mode 100644 index e07cf4e0ef4..00000000000 --- a/tgui/packages/tgui/interfaces/Roulette.jsx +++ /dev/null @@ -1,313 +0,0 @@ -import { classes } from 'common/react'; -import { useState } from 'react'; - -import { useBackend } from '../backend'; -import { Box, Button, Grid, NumberInput, Table } from '../components'; -import { Window } from '../layouts'; - -const getNumberColor = (number) => { - const inRedOddRange = - (number >= 1 && number <= 10) || (number >= 19 && number <= 28); - - if (number % 2 === 1) { - return inRedOddRange ? 'red' : 'black'; - } - return inRedOddRange ? 'black' : 'red'; -}; - -export const RouletteNumberCell = (props) => { - const { - buttonClass = null, - cellClass = null, - color, - colspan = '1', - rowspan = '1', - text, - value, - } = props; - const { act } = useBackend(); - - return ( - - - - ); -}; - -export const RouletteBoard = () => { - const firstRow = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36]; - const secondRow = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35]; - const thirdRow = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34]; - const fourthRow = { - 's1-12': '1st 12', - 's13-24': '2nd 12', - 's25-36': '3rd 12', - }; - const fifthRow = [ - { color: 'transparent', text: '1-18', value: 's1-18' }, - { color: 'transparent', text: 'Even', value: 'even' }, - { color: 'black', text: 'Black', value: 'black' }, - { color: 'red', text: 'Red', value: 'red' }, - { color: 'transparent', text: 'Odd', value: 'odd' }, - { color: 'transparent', text: '19-36', value: 's19-36' }, - ]; - - return ( - - - - - {firstRow.map((number) => ( - - ))} - - - - {secondRow.map((number) => ( - - ))} - - - - {thirdRow.map((number) => ( - - ))} - - - - - {Object.entries(fourthRow).map(([value, text]) => ( - - ))} - - - - {fifthRow.map((cell) => ( - - ))} - -
    -
    - ); -}; - -export const RouletteBetTable = (props) => { - const { act, data } = useBackend(); - - const [customBet, setCustomBet] = useState(500); - - let { BetType } = data; - - if (BetType.startsWith('s')) { - BetType = BetType.substring(1, BetType.length); - } - - return ( - - - - Last Spin: - - - Current Bet: - - - - - {data.LastSpin} - - - - {data.BetAmount} cr on {BetType} - - -
    - ); -}; - -export const Roulette = (props) => { - return ( - - - - - - - ); -}; diff --git a/tgui/packages/tgui/interfaces/Roulette/BetTable.tsx b/tgui/packages/tgui/interfaces/Roulette/BetTable.tsx new file mode 100644 index 00000000000..8abf081b288 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Roulette/BetTable.tsx @@ -0,0 +1,171 @@ +import { BooleanLike, classes } from 'common/react'; +import { useState } from 'react'; + +import { useBackend } from '../../backend'; +import { Box, Button, NumberInput, Stack, Table } from '../../components'; +import { getNumberColor } from './helpers'; + +type Data = { + IsAnchored: BooleanLike; + BetAmount: number; + BetType: string; + HouseBalance: number; + LastSpin: number; + Spinning: BooleanLike; + AccountBalance: number; + CanUnbolt: BooleanLike; +}; + +export function RouletteBetTable(props) { + const { act, data } = useBackend(); + const { LastSpin, HouseBalance, BetAmount, IsAnchored } = data; + + const [customBet, setCustomBet] = useState(500); + + let BetType = data.BetType; + + if (BetType.startsWith('s')) { + BetType = BetType.substring(1, BetType.length); + } + + return ( + + + + Last Spin: + + + Current Bet: + + + + + {LastSpin} + + + + {BetAmount} cr on {BetType} + + + + + + + + + + + + setCustomBet(value)} + /> + + + + + + + + + Swipe an ID card with a connected account to spin! + + + + + + + House Balance: + + {HouseBalance ? HouseBalance + ' cr' : 'None'} + + + + + +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/Roulette/Board.tsx b/tgui/packages/tgui/interfaces/Roulette/Board.tsx new file mode 100644 index 00000000000..0f0fbcdc681 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Roulette/Board.tsx @@ -0,0 +1,107 @@ +import { Box, Table } from '../../components'; +import { getNumberColor } from './helpers'; +import { RouletteNumberCell } from './NumberCell'; + +const firstRow = [3, 6, 9, 12, 15, 18, 21, 24, 27, 30, 33, 36] as const; +const secondRow = [2, 5, 8, 11, 14, 17, 20, 23, 26, 29, 32, 35] as const; +const thirdRow = [1, 4, 7, 10, 13, 16, 19, 22, 25, 28, 31, 34] as const; +const fourthRow = { + 's1-12': '1st 12', + 's13-24': '2nd 12', + 's25-36': '3rd 12', +} as const; +const fifthRow = [ + { color: 'transparent', text: '1-18', value: 's1-18' }, + { color: 'transparent', text: 'Even', value: 'even' }, + { color: 'black', text: 'Black', value: 'black' }, + { color: 'red', text: 'Red', value: 'red' }, + { color: 'transparent', text: 'Odd', value: 'odd' }, + { color: 'transparent', text: '19-36', value: 's19-36' }, +] as const; + +export function RouletteBoard(props) { + return ( + + + + + {firstRow.map((number) => ( + + ))} + + + + {secondRow.map((number) => ( + + ))} + + + + {thirdRow.map((number) => ( + + ))} + + + + + {Object.entries(fourthRow).map(([value, text]) => ( + + ))} + + + + {fifthRow.map((cell) => ( + + ))} + +
    +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/Roulette/NumberCell.tsx b/tgui/packages/tgui/interfaces/Roulette/NumberCell.tsx new file mode 100644 index 00000000000..1b8143905c3 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Roulette/NumberCell.tsx @@ -0,0 +1,48 @@ +import { classes } from 'common/react'; + +import { useBackend } from '../../backend'; +import { Button, Table } from '../../components'; + +type Props = { + color: string; + text: string; + value: string; +} & Partial<{ + buttonClass: string; + cellClass: string; + colspan: number; + rowspan: number; +}>; + +export function RouletteNumberCell(props: Props) { + const { + buttonClass, + cellClass, + color, + colspan = 1, + rowspan = 1, + text, + value, + } = props; + const { act } = useBackend(); + + return ( + + + + ); +} diff --git a/tgui/packages/tgui/interfaces/Roulette/helpers.tsx b/tgui/packages/tgui/interfaces/Roulette/helpers.tsx new file mode 100644 index 00000000000..b5c4f9b9b33 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Roulette/helpers.tsx @@ -0,0 +1,9 @@ +export function getNumberColor(number: number): 'red' | 'black' { + const inRedOddRange = + (number >= 1 && number <= 10) || (number >= 19 && number <= 28); + + if (number % 2 === 1) { + return inRedOddRange ? 'red' : 'black'; + } + return inRedOddRange ? 'black' : 'red'; +} diff --git a/tgui/packages/tgui/interfaces/Roulette/index.tsx b/tgui/packages/tgui/interfaces/Roulette/index.tsx new file mode 100644 index 00000000000..8c27a052a02 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Roulette/index.tsx @@ -0,0 +1,14 @@ +import { Window } from '../../layouts'; +import { RouletteBetTable } from './BetTable'; +import { RouletteBoard } from './Board'; + +export function Roulette(props) { + return ( + + + + + + + ); +} diff --git a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx index 8132027e3e6..8241148257a 100644 --- a/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx +++ b/tgui/packages/tgui/interfaces/SecurityRecords/RecordTabs.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useState } from 'react'; import { useBackend, useLocalState } from 'tgui/backend'; import { @@ -29,10 +28,10 @@ export const SecurityRecordTabs = (props) => { const [search, setSearch] = useState(''); - const sorted: SecurityRecord[] = flow([ - filter((record: SecurityRecord) => isRecordMatch(record, search)), - sortBy((record: SecurityRecord) => record.name), - ])(records); + const sorted = sortBy( + filter(records, (record) => isRecordMatch(record, search)), + (record) => record.name, + ); return ( diff --git a/tgui/packages/tgui/interfaces/SeedExtractor.tsx b/tgui/packages/tgui/interfaces/SeedExtractor.tsx index 3c05b3d0498..87955da243f 100644 --- a/tgui/packages/tgui/interfaces/SeedExtractor.tsx +++ b/tgui/packages/tgui/interfaces/SeedExtractor.tsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { classes } from 'common/react'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -67,9 +66,10 @@ export const SeedExtractor = (props) => { const search = createSearch(searchText, (item: SeedData) => item.name); const seeds_filtered = searchText.length > 0 ? data.seeds.filter(search) : data.seeds; - const seeds = flow([ - sortBy((item: SeedData) => item[sortField as keyof SeedData]), - ])(seeds_filtered || []); + const seeds = sortBy( + seeds_filtered || [], + (item: SeedData) => item[sortField as keyof SeedData], + ); sortField !== 'name' && seeds.reverse(); return ( diff --git a/tgui/packages/tgui/interfaces/SelectEquipment.jsx b/tgui/packages/tgui/interfaces/SelectEquipment.jsx index d43d28961e2..d8df6c71e7c 100644 --- a/tgui/packages/tgui/interfaces/SelectEquipment.jsx +++ b/tgui/packages/tgui/interfaces/SelectEquipment.jsx @@ -1,5 +1,4 @@ import { filter, map, sortBy, uniq } from 'common/collections'; -import { flow } from 'common/fp'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -30,10 +29,10 @@ export const SelectEquipment = (props) => { const isFavorited = (entry) => favorites?.includes(entry.path); - const outfits = map((entry) => ({ + const outfits = map([...data.outfits, ...data.custom_outfits], (entry) => ({ ...entry, favorite: isFavorited(entry), - }))([...data.outfits, ...data.custom_outfits]); + })); // even if no custom outfits were sent, we still want to make sure there's // at least a 'Custom' tab so the button to create a new one pops up @@ -49,15 +48,15 @@ export const SelectEquipment = (props) => { (entry) => entry.name + entry.path, ); - const visibleOutfits = flow([ - filter((entry) => entry.category === tab), - filter(searchFilter), - sortBy( - (entry) => !entry.favorite, - (entry) => !entry.priority, - (entry) => entry.name, + const visibleOutfits = sortBy( + filter( + filter(outfits, (entry) => entry.category === tab), + searchFilter, ), - ])(outfits); + (entry) => !entry.favorite, + (entry) => !entry.priority, + (entry) => entry.name, + ); const getOutfitEntry = (current_outfit) => outfits.find((outfit) => getOutfitKey(outfit) === current_outfit); diff --git a/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx index e64f47086cd..8cff913e8e9 100644 --- a/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx +++ b/tgui/packages/tgui/interfaces/ShuttleManipulator.jsx @@ -104,7 +104,7 @@ export const ShuttleManipulatorTemplates = (props) => { - {map((template, templateId) => ( + {map(templateObject, (template, templateId) => ( { > {template.port_id} - ))(templateObject)} + ))} diff --git a/tgui/packages/tgui/interfaces/StackCrafting.tsx b/tgui/packages/tgui/interfaces/StackCrafting.tsx index 255d8f501f1..7395306c11a 100644 --- a/tgui/packages/tgui/interfaces/StackCrafting.tsx +++ b/tgui/packages/tgui/interfaces/StackCrafting.tsx @@ -1,5 +1,3 @@ -import { filter, map, reduce, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { clamp } from 'common/math'; import { createSearch } from 'common/string'; import { useState } from 'react'; @@ -49,8 +47,7 @@ type RecipeBoxProps = { }; // RecipeList converted via Object.entries() for filterRecipeList -type RecipeListEntry = [string, RecipeList | Recipe]; -type RecipeListFilterableEntry = [string, RecipeList | Recipe | undefined]; +type RecipeListFilterableEntry = [string, RecipeList | Recipe]; /** * Type guard for recipe vs recipe list @@ -70,30 +67,29 @@ function isRecipeList(value: Recipe | RecipeList): value is RecipeList { const filterRecipeList = ( list: RecipeList, keyFilter: (key: string) => boolean, -) => { - const filteredList: RecipeList = flow([ - map((entry: RecipeListEntry): RecipeListFilterableEntry => { - const [key, recipe] = entry; +): RecipeList | undefined => { + const filteredList = Object.fromEntries( + Object.entries(list) + .flatMap((entry): RecipeListFilterableEntry[] => { + const [key, recipe] = entry; - if (isRecipeList(recipe)) { // If category name matches, return the whole thing. if (keyFilter(key)) { - return entry; + return [entry]; } - // otherwise, filter sub-entries. - return [key, filterRecipeList(recipe, keyFilter)]; - } + if (isRecipeList(recipe)) { + // otherwise, filter sub-entries. + const subEntries = filterRecipeList(recipe, keyFilter); + if (subEntries !== undefined) { + return [[key, subEntries]]; + } + } - return keyFilter(key) ? entry : [key, undefined]; - }), - filter((entry: RecipeListFilterableEntry) => entry[1] !== undefined), - sortBy((entry: RecipeListEntry) => entry[0].toLowerCase()), - reduce((obj: RecipeList, entry: RecipeListEntry) => { - obj[entry[0]] = entry[1]; - return obj; - }, {}), - ])(Object.entries(list)); + return []; + }) + .sort(([a], [b]) => (a < b ? -1 : a !== b ? 1 : 0)), + ); return Object.keys(filteredList).length ? filteredList : undefined; }; diff --git a/tgui/packages/tgui/interfaces/StationAlertConsole.jsx b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx index f80a5826b06..c4ff6812955 100644 --- a/tgui/packages/tgui/interfaces/StationAlertConsole.jsx +++ b/tgui/packages/tgui/interfaces/StationAlertConsole.jsx @@ -1,5 +1,4 @@ import { sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { useBackend } from '../backend'; import { Button, Section, Stack } from '../components'; @@ -30,8 +29,9 @@ export const StationAlertConsoleContent = (props) => { Camera: 5, }; - const sortedAlarms = flow([sortBy((alarm) => sortingKey[alarm.name])])( + const sortedAlarms = sortBy( data.alarms || [], + (alarm) => sortingKey[alarm.name], ); return ( diff --git a/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx b/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx index f3176415a4f..e6e7b02ce67 100644 --- a/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx +++ b/tgui/packages/tgui/interfaces/StationTraitsPanel.tsx @@ -1,4 +1,4 @@ -import { filterMap } from 'common/collections'; +import { filter, map } from 'common/collections'; import { exhaustiveCheck } from 'common/exhaustive'; import { BooleanLike } from 'common/react'; import { useState } from 'react'; @@ -110,15 +110,9 @@ const FutureStationTraitsPage = (props) => { icon="times" onClick={() => { act('setup_future_traits', { - station_traits: filterMap( - future_station_traits, - (otherTrait) => { - if (otherTrait.path === trait.path) { - return undefined; - } else { - return otherTrait.path; - } - }, + station_traits: filter( + map(future_station_traits, (t) => t.path), + (p) => p !== trait.path, ), }); }} diff --git a/tgui/packages/tgui/interfaces/Supermatter.tsx b/tgui/packages/tgui/interfaces/Supermatter.tsx index 9bb8661ca29..35d176db6cd 100644 --- a/tgui/packages/tgui/interfaces/Supermatter.tsx +++ b/tgui/packages/tgui/interfaces/Supermatter.tsx @@ -1,5 +1,4 @@ import { filter, sortBy } from 'common/collections'; -import { flow } from 'common/fp'; import { toFixed } from 'common/math'; import { BooleanLike } from 'common/react'; import { ReactNode, useState } from 'react'; @@ -123,10 +122,14 @@ export const SupermatterContent = (props: SupermatterProps) => { gas_metadata, } = props; const [allGasActive, setAllGasActive] = useState(false); - const gas_composition: [gas_path: string, amount: number][] = flow([ - !allGasActive && filter(([gas_path, amount]) => amount !== 0), - sortBy(([gas_path, amount]) => -amount), - ])(Object.entries(props.gas_composition)); + let gas_composition = Object.entries(props.gas_composition); + if (!allGasActive) { + gas_composition = filter( + gas_composition, + ([gas_path, amount]) => amount !== 0, + ); + } + gas_composition = sortBy(gas_composition, ([gas_path, amount]) => -amount); return ( diff --git a/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx b/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx index 2e9403794bf..c051315fe3b 100644 --- a/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx +++ b/tgui/packages/tgui/interfaces/SurgeryInitiator.tsx @@ -20,7 +20,8 @@ type SurgeryInitiatorData = { target_name: string; }; -const sortSurgeries = sortBy((surgery: Surgery) => surgery.name); +const sortSurgeries = (array: Surgery[]) => + sortBy(array, (surgery) => surgery.name); type SurgeryInitiatorInnerState = { selectedSurgeryIndex: number; diff --git a/tgui/packages/tgui/interfaces/SyndContractor.jsx b/tgui/packages/tgui/interfaces/SyndContractor.jsx index 472a1834df6..5daa40b1195 100644 --- a/tgui/packages/tgui/interfaces/SyndContractor.jsx +++ b/tgui/packages/tgui/interfaces/SyndContractor.jsx @@ -11,6 +11,7 @@ import { Modal, NoticeBox, Section, + Stack, Table, Tabs, } from '../components'; @@ -181,8 +182,8 @@ export const StatusPane = (props) => { } > - - + + { {String(data.earned_tc)} - - + + {String(data.contracts_completed)} ACTIVE - - + +
    ); }; diff --git a/tgui/packages/tgui/interfaces/SyndicateContractor.tsx b/tgui/packages/tgui/interfaces/SyndicateContractor.tsx index 81e01d682d7..66c07263981 100644 --- a/tgui/packages/tgui/interfaces/SyndicateContractor.tsx +++ b/tgui/packages/tgui/interfaces/SyndicateContractor.tsx @@ -5,32 +5,21 @@ import { Box, Button, Flex, - Grid, Icon, LabeledList, Modal, NoticeBox, Section, + Stack, } from '../components'; import { FakeTerminal } from '../components/FakeTerminal'; import { NtosWindow } from '../layouts'; -const CONTRACT_STATUS_INACTIVE = 1; -const CONTRACT_STATUS_ACTIVE = 2; -const CONTRACT_STATUS_BOUNTY_CONSOLE_ACTIVE = 3; -const CONTRACT_STATUS_EXTRACTING = 4; -const CONTRACT_STATUS_COMPLETE = 5; -const CONTRACT_STATUS_ABORTED = 6; - -export const SyndicateContractor = (props) => { - return ( - - - - - - ); -}; +enum CONTRACT { + Inactive = 1, + Active = 2, + Complete = 5, +} type Data = { contracts_completed: number; @@ -59,7 +48,47 @@ type ContractData = { target: string; }; -export const SyndicateContractorContent = (props) => { +const infoEntries = [ + 'SyndTract v2.0', + '', + "We've identified potentional high-value targets that are", + 'currently assigned to your mission area. They are believed', + 'to hold valuable information which could be of immediate', + 'importance to our organisation.', + '', + 'Listed below are all of the contracts available to you. You', + 'are to bring the specified target to the designated', + 'drop-off, and contact us via this uplink. We will send', + 'a specialised extraction unit to put the body into.', + '', + 'We want targets alive - but we will sometimes pay slight', + "amounts if they're not, you just won't receive the shown", + 'bonus. You can redeem your payment through this uplink in', + 'the form of raw telecrystals, which can be put into your', + 'regular Syndicate uplink to purchase whatever you may need.', + 'We provide you with these crystals the moment you send the', + 'target up to us, which can be collected at anytime through', + 'this system.', + '', + 'Targets extracted will be ransomed back to the station once', + 'their use to us is fulfilled, with us providing you a small', + 'percentage cut. You may want to be mindful of them', + 'identifying you when they come back. We provide you with', + 'a standard contractor loadout, which will help cover your', + 'identity.', +] as const; + +export function SyndicateContractor(props) { + return ( + + + + + + ); +} + +function SyndicateContractorContent(props) { const { data, act } = useBackend(); const { error, logged_in, first_load, info_screen } = data; @@ -84,37 +113,7 @@ export const SyndicateContractorContent = (props) => { 'Searching for available contracts...', 'CONTRACTS FOUND', 'WELCOME, AGENT', - ]; - - const infoEntries = [ - 'SyndTract v2.0', - '', - "We've identified potentional high-value targets that are", - 'currently assigned to your mission area. They are believed', - 'to hold valuable information which could be of immediate', - 'importance to our organisation.', - '', - 'Listed below are all of the contracts available to you. You', - 'are to bring the specified target to the designated', - 'drop-off, and contact us via this uplink. We will send', - 'a specialised extraction unit to put the body into.', - '', - 'We want targets alive - but we will sometimes pay slight', - "amounts if they're not, you just won't receive the shown", - 'bonus. You can redeem your payment through this uplink in', - 'the form of raw telecrystals, which can be put into your', - 'regular Syndicate uplink to purchase whatever you may need.', - 'We provide you with these crystals the moment you send the', - 'target up to us, which can be collected at anytime through', - 'this system.', - '', - 'Targets extracted will be ransomed back to the station once', - 'their use to us is fulfilled, with us providing you a small', - 'percentage cut. You may want to be mindful of them', - 'identifying you when they come back. We provide you with', - 'a standard contractor loadout, which will help cover your', - 'identity.', - ]; + ] as const; const errorPane = !!error && ( @@ -126,7 +125,7 @@ export const SyndicateContractorContent = (props) => { {error} - @@ -136,11 +135,9 @@ export const SyndicateContractorContent = (props) => { return (
    - {!!error && {error}}
    @@ -167,11 +164,12 @@ export const SyndicateContractorContent = (props) => {
    ); } @@ -183,38 +181,38 @@ export const SyndicateContractorContent = (props) => { ); -}; +} -export const StatusPane = (props) => { +function StatusPane(props) { const { act, data } = useBackend(); const { redeemable_tc, earned_tc, contracts_completed } = data; return (
    - Contractor Status - } + title="Contractor Status" > - - + + act('PRG_redeem_TC')} - /> + > + Claim + } > {String(redeemable_tc)} @@ -223,24 +221,28 @@ export const StatusPane = (props) => { {String(earned_tc)} - - + + {String(contracts_completed)} ACTIVE - - + +
    ); -}; +} -const ContractsTab = (props) => { +function ContractsTab(props) { const { act, data } = useBackend(); - const { contracts, ongoing_contract, extraction_enroute, dropoff_direction } = - data; + const { + contracts = [], + ongoing_contract, + extraction_enroute, + dropoff_direction, + } = data; return ( <> @@ -248,18 +250,19 @@ const ContractsTab = (props) => { title="Available Contracts" buttons={ } > {contracts.map((contract) => { - if (ongoing_contract && contract.status !== CONTRACT_STATUS_ACTIVE) { + if (ongoing_contract && contract.status !== CONTRACT.Active) { return; } - const active = contract.status > CONTRACT_STATUS_INACTIVE; - if (contract.status >= CONTRACT_STATUS_COMPLETE) { + const active = contract.status > CONTRACT.Inactive; + if (contract.status >= CONTRACT.Complete) { return; } return ( @@ -276,27 +279,28 @@ const ContractsTab = (props) => { {`${contract.payout} (+${contract.payout_bonus}) TC`}
    } > - - {contract.message} - + + {contract.message} + Dropoff Location: {contract.dropoff} - - + + ); })} @@ -310,4 +314,4 @@ const ContractsTab = (props) => { ); -}; +} diff --git a/tgui/packages/tgui/interfaces/Techweb.jsx b/tgui/packages/tgui/interfaces/Techweb.jsx index 96b52983ade..c041d646d7d 100644 --- a/tgui/packages/tgui/interfaces/Techweb.jsx +++ b/tgui/packages/tgui/interfaces/Techweb.jsx @@ -41,9 +41,9 @@ const selectRemappedStaticData = (data) => { ...node, id: remapId(id), costs, - prereq_ids: map(remapId)(node.prereq_ids || []), - design_ids: map(remapId)(node.design_ids || []), - unlock_ids: map(remapId)(node.unlock_ids || []), + prereq_ids: map(node.prereq_ids || [], remapId), + design_ids: map(node.design_ids || [], remapId), + unlock_ids: map(node.unlock_ids || [], remapId), required_experiments: node.required_experiments || [], discount_experiments: node.discount_experiments || [], }; @@ -251,10 +251,11 @@ const TechwebOverview = (props) => { ); }); } else { - displayedNodes = sortBy((x) => node_cache[x.id].name)( + displayedNodes = sortBy( tabIndex < 2 ? nodes.filter((x) => x.tier === tabIndex) : nodes.filter((x) => x.tier >= tabIndex), + (x) => node_cache[x.id].name, ); } diff --git a/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx index 3f1a3dcb543..f59b01b8b5d 100644 --- a/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx +++ b/tgui/packages/tgui/interfaces/TrackedPlaytime.jsx @@ -7,7 +7,7 @@ import { Window } from '../layouts'; const JOB_REPORT_MENU_FAIL_REASON_TRACKING_DISABLED = 1; const JOB_REPORT_MENU_FAIL_REASON_NO_RECORDS = 2; -const sortByPlaytime = sortBy(([_, playtime]) => -playtime); +const sortByPlaytime = (array) => sortBy(array, ([_, playtime]) => -playtime); const PlaytimeSection = (props) => { const { playtimes } = props; diff --git a/tgui/packages/tgui/interfaces/WarrantConsole.tsx b/tgui/packages/tgui/interfaces/WarrantConsole.tsx index 14415b3ee79..a87de8e017c 100644 --- a/tgui/packages/tgui/interfaces/WarrantConsole.tsx +++ b/tgui/packages/tgui/interfaces/WarrantConsole.tsx @@ -63,7 +63,7 @@ export const WarrantConsole = (props) => { const RecordList = (props) => { const { act, data } = useBackend(); const { records = [] } = data; - const sorted = sortBy((record: WarrantRecord) => record.crew_name)(records); + const sorted = sortBy(records, (record) => record.crew_name); const [selectedRecord, setSelectedRecord] = useLocalState< WarrantRecord | undefined diff --git a/tgui/packages/tgui/interfaces/common/AccessConfig.jsx b/tgui/packages/tgui/interfaces/common/AccessConfig.jsx deleted file mode 100644 index e91abecbde7..00000000000 --- a/tgui/packages/tgui/interfaces/common/AccessConfig.jsx +++ /dev/null @@ -1,136 +0,0 @@ -import { sortBy } from 'common/collections'; -import { useState } from 'react'; - -import { Button, Flex, Grid, Section, Tabs } from '../../components'; - -export const AccessConfig = (props) => { - const { - accesses = [], - selectedList = [], - accessMod, - grantAll, - denyAll, - grantDep, - denyDep, - } = props; - const [selectedAccessName, setSelectedAccessName] = useState( - accesses[0]?.name, - ); - const selectedAccess = accesses.find( - (access) => access.name === selectedAccessName, - ); - const selectedAccessEntries = sortBy((entry) => entry.desc)( - selectedAccess?.accesses || [], - ); - - const checkAccessIcon = (accesses) => { - let oneAccess = false; - let oneInaccess = false; - for (let element of accesses) { - if (selectedList.includes(element.ref)) { - oneAccess = true; - } else { - oneInaccess = true; - } - } - if (!oneAccess && oneInaccess) { - return 0; - } else if (oneAccess && oneInaccess) { - return 1; - } else { - return 2; - } - }; - - return ( -
    -
    - ); -}; - -const diffMap = { - 0: { - icon: 'times-circle', - color: 'bad', - }, - 1: { - icon: 'stop-circle', - color: null, - }, - 2: { - icon: 'check-circle', - color: 'good', - }, -}; diff --git a/tgui/packages/tgui/interfaces/common/AccessConfig.tsx b/tgui/packages/tgui/interfaces/common/AccessConfig.tsx new file mode 100644 index 00000000000..766a6724d77 --- /dev/null +++ b/tgui/packages/tgui/interfaces/common/AccessConfig.tsx @@ -0,0 +1,204 @@ +import { sortBy } from 'common/collections'; +import { useState } from 'react'; + +import { Button, Section, Stack, Tabs } from '../../components'; + +type BaseProps = { + accessMod: (ref: string) => void; + denyDep: (ref: string) => void; + grantDep: (ref: string) => void; +}; + +type ConfigProps = { + accesses: Region[]; + denyAll: () => void; + grantAll: () => void; + selectedList: string[]; +} & BaseProps; + +type AccessButtonProps = { + selectedAccess: Region; + selectedAccessEntries: Area[]; + selectedList: string[]; +} & BaseProps; + +export type Region = { + accesses: Area[]; + name: string; +}; + +export type Area = { + desc: string; + ref: string; +}; + +enum ACCESS { + Denied = 0, + Partial = 1, + Granted = 2, +} + +const DIFFMAP = [ + { + icon: 'times-circle', + color: 'bad', + }, + { + icon: 'stop-circle', + color: null, + }, + { + icon: 'check-circle', + color: 'good', + }, +] as const; + +export function AccessConfig(props: ConfigProps) { + const { + accesses = [], + selectedList = [], + accessMod, + grantAll, + denyAll, + grantDep, + denyDep, + } = props; + + const [selectedAccessName, setSelectedAccessName] = useState( + accesses[0]?.name, + ); + + const selectedAccess = + accesses.find((access) => access.name === selectedAccessName) || + accesses[0]; + + const selectedAccessEntries = sortBy( + selectedAccess?.accesses || [], + (entry: Area) => entry.desc, + ); + + function checkAccessIcon(accesses: Area[]) { + let oneAccess = false; + let oneInaccess = false; + for (let element of accesses) { + if (selectedList.includes(element.ref)) { + oneAccess = true; + } else { + oneInaccess = true; + } + } + if (!oneAccess && oneInaccess) { + return ACCESS.Denied; + } else if (oneAccess && oneInaccess) { + return ACCESS.Partial; + } else { + return ACCESS.Granted; + } + } + + return ( +
    + + + + } + > + + + + {accesses.map((access) => { + const entries = access.accesses || []; + const icon = DIFFMAP[checkAccessIcon(entries)].icon; + const color = DIFFMAP[checkAccessIcon(entries)].color; + return ( + setSelectedAccessName(access.name)} + > + {access.name} + + ); + })} + + + + + + + +
    + ); +} + +function AccessButtons(props: AccessButtonProps) { + const { + selectedAccessEntries, + selectedList, + accessMod, + grantDep, + denyDep, + selectedAccess, + } = props; + + return ( + + + + + + + + + + + + + +
    + {selectedAccessEntries.map((entry) => ( + accessMod(entry.ref)} + > + {entry.desc} + + ))} +
    +
    +
    + ); +} diff --git a/tgui/packages/tgui/interfaces/common/AccessList.jsx b/tgui/packages/tgui/interfaces/common/AccessList.jsx index a4096b6ce30..9ac1fe913b3 100644 --- a/tgui/packages/tgui/interfaces/common/AccessList.jsx +++ b/tgui/packages/tgui/interfaces/common/AccessList.jsx @@ -248,8 +248,9 @@ const RegionAccessList = (props) => { const selectedAccess = accesses.find( (access) => access.name === selectedAccessName, ); - const selectedAccessEntries = sortBy((entry) => entry.desc)( + const selectedAccessEntries = sortBy( selectedAccess?.accesses || [], + (entry) => entry.desc, ); const allWildcards = Object.keys(wildcardSlots); diff --git a/tgui/packages/tgui/layouts/Layout.tsx b/tgui/packages/tgui/layouts/Layout.tsx index 74aad987d21..173ed1cbb43 100644 --- a/tgui/packages/tgui/layouts/Layout.tsx +++ b/tgui/packages/tgui/layouts/Layout.tsx @@ -5,6 +5,7 @@ */ import { classes } from 'common/react'; +import { useEffect, useRef } from 'react'; import { BoxProps, @@ -40,6 +41,20 @@ type ContentProps = Partial<{ function LayoutContent(props: ContentProps) { const { className, scrollable, children, ...rest } = props; + const node = useRef(null); + + useEffect(() => { + const self = node.current; + + if (self && scrollable) { + addScrollableNode(self); + } + return () => { + if (self && scrollable) { + removeScrollableNode(self); + } + }; + }, []); return (
    {children} @@ -56,9 +72,4 @@ function LayoutContent(props: ContentProps) { ); } -LayoutContent.defaultHooks = { - onComponentDidMount: (node) => addScrollableNode(node), - onComponentWillUnmount: (node) => removeScrollableNode(node), -}; - Layout.Content = LayoutContent; diff --git a/tgui/packages/tgui/layouts/Pane.tsx b/tgui/packages/tgui/layouts/Pane.tsx index a2858fae7fc..f34205545fb 100644 --- a/tgui/packages/tgui/layouts/Pane.tsx +++ b/tgui/packages/tgui/layouts/Pane.tsx @@ -9,6 +9,7 @@ import { classes } from 'common/react'; import { useBackend } from '../backend'; import { Box } from '../components'; import { BoxProps } from '../components/Box'; +import { useDebug } from '../debug'; import { Layout } from './Layout'; type Props = Partial<{ @@ -18,11 +19,8 @@ type Props = Partial<{ export function Pane(props: Props) { const { theme, children, className, ...rest } = props; - const { suspended, debug } = useBackend(); - let debugLayout = false; - if (debug) { - debugLayout = debug.debugLayout; - } + const { suspended } = useBackend(); + const { debugLayout = false } = useDebug(); return ( diff --git a/tgui/packages/tgui/layouts/Window.tsx b/tgui/packages/tgui/layouts/Window.tsx index 2b6fbf87356..aa91370bfc3 100644 --- a/tgui/packages/tgui/layouts/Window.tsx +++ b/tgui/packages/tgui/layouts/Window.tsx @@ -13,6 +13,7 @@ import { globalStore } from '../backend'; import { Icon } from '../components'; import { BoxProps } from '../components/Box'; import { UI_DISABLED, UI_INTERACTIVE, UI_UPDATE } from '../constants'; +import { useDebug } from '../debug'; import { toggleKitchenSink } from '../debug/actions'; import { dragStartHandler, @@ -48,10 +49,8 @@ export const Window = (props: Props) => { height, } = props; - const { config, suspended, debug } = useBackend(); - if (suspended) { - return null; - } + const { config, suspended } = useBackend(); + const { debugLayout = false } = useDebug(); useEffect(() => { const updateGeometry = () => { @@ -80,11 +79,6 @@ export const Window = (props: Props) => { }; }, [width, height]); - let debugLayout = false; - if (debug) { - debugLayout = debug.debugLayout; - } - const dispatch = globalStore.dispatch; const fancy = config.window?.fancy; @@ -95,11 +89,11 @@ export const Window = (props: Props) => { ? config.status < UI_DISABLED : config.status < UI_INTERACTIVE); - return ( + return suspended ? null : ( { // Get the component for the current route export const getRoutedComponent = () => { - const { suspended, config, debug } = useBackend(); + const { suspended, config } = useBackend(); + const { kitchenSink = false } = useDebug(); + if (suspended) { return SuspendedWindow; } @@ -61,7 +64,7 @@ export const getRoutedComponent = () => { } if (process.env.NODE_ENV !== 'production') { // Show a kitchen sink - if (debug?.kitchenSink) { + if (kitchenSink) { return require('./debug').KitchenSink; } } diff --git a/tgui/packages/tgui/styles/components/SearchItem.scss b/tgui/packages/tgui/styles/components/SearchItem.scss new file mode 100644 index 00000000000..76946ec9c5a --- /dev/null +++ b/tgui/packages/tgui/styles/components/SearchItem.scss @@ -0,0 +1,22 @@ +@use '../colors.scss'; + +.SearchItem { + align-items: center; + background: black; + border: thin solid #212121; + display: flex; + height: 3rem; + justify-content: center; + position: relative; + width: 3rem; + margin-bottom: 0; +} + +.SearchItem--amount { + bottom: -1rem; + color: colors.$teal; + font-family: 'Roboto', sans-serif; + font-size: 1.5rem; + position: absolute; + right: -4px; +} diff --git a/tgui/packages/tgui/styles/main.scss b/tgui/packages/tgui/styles/main.scss index 25443ae606c..87715e2ae30 100644 --- a/tgui/packages/tgui/styles/main.scss +++ b/tgui/packages/tgui/styles/main.scss @@ -38,6 +38,7 @@ @include meta.load-css('./components/NumberInput.scss'); @include meta.load-css('./components/ProgressBar.scss'); @include meta.load-css('./components/RoundGauge.scss'); +@include meta.load-css('./components/SearchItem.scss'); @include meta.load-css('./components/Section.scss'); @include meta.load-css('./components/Slider.scss'); @include meta.load-css('./components/Stack.scss'); diff --git a/tools/test_merge_bot/main.js b/tools/test_merge_bot/main.js index 0ad51304ecf..ae7d4c7a98d 100644 --- a/tools/test_merge_bot/main.js +++ b/tools/test_merge_bot/main.js @@ -87,19 +87,41 @@ export async function processTestMerges({ github, context }) { } if (existingComment === undefined) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: newBody, - }); + try { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: newBody, + }); + } catch (error) { + if(error.status){ + console.error(`Failed to create comment for #{prNumber}`) + console.error(error) + continue; + } + else{ + throw error + } + } } else { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existingComment.databaseId, - body: newBody, - }); + try { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existingComment.databaseId, + body: newBody, + }); + } catch (error) { + if(error.status){ + console.error(`Failed to update comment for #{prNumber}`) + console.error(error) + continue; + } + else{ + throw error + } + } } } }