From 4f1588b5a8d5f4bee7829820b9613f6d749cd04b Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Wed, 10 Dec 2025 16:38:40 +0100 Subject: [PATCH] Medigun Port (#18079) * Medigun Port * order * error on that * Medgunportupdate (#16) * Automatic changelog for PR #18444 [ci skip] * no encode here (#18445) * digi & sleeveless bodysuits (#18424) * Automatic changelog for PR #18424 [ci skip] * Incredibly minor capsule fixes (#18436) * Swaps fighter landing gear raise/lower command wordings (#18443) * Swaps fighter landing gear raise/lower command wordings * what the fuck mech code??? * Automatic changelog for PR #18443 [ci skip] * Adds some digi sprites for some clothes (#18447) * Automatic changelog for PR #18447 [ci skip] * clothing _vr dmi cleanup: hats and headwear [IDB IGNORE] (#18348) * _vr dmi cleanup: backpacks, bags, and belts * _vr dmi cleanup: backpacks, bags, and belts * dmi cleanup: ears/headsets * clothing _vr dmi cleanup: hats and headgear * Update rig_pieces.dm --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18348 [ci skip] * Automatic changelog compile [ci skip] * Upport pursuit teshari rig helm sprite (#18448) * Automatic changelog for PR #18448 [ci skip] * Archives some old icon, fixes gorka icons (#18449) * Automatic changelog for PR #18449 [ci skip] * Fixes beds and sofa in hotsprings redgate, makes Tether research outpost dorms dorm-worthy (#18419) * Fixes beds and sofa in hotsprings redgate, makes Tether research outpost dorms dorm-worthy * actually fuck it give em a proper area and privacy switches too * also this is a double bed now too * Fixup maps in TGM format 03b8ced84291: maps/tether/tether-07-solars.dmm Automatically commited by: tools\mapmerge2\fixup.py --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18419 [ci skip] * Disposal Connector Component (#18431) * looks like we're just recoding disposals entirely lol * wip * fixes * doc * code cleanup * define use * use doafter * disposal flooring breaks now properly drop floor sheets * Moved disposal mail tag to packet init instead of before it * modernization * cleans up overrides that shouldn't exist * more cleanup * Update code/modules/recycling/disposal_holder.dm Co-authored-by: Cameron Lennox * Update code/modules/recycling/disposal_holder.dm Co-authored-by: Cameron Lennox * Update code/modules/recycling/disposal_machines.dm Co-authored-by: Cameron Lennox --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18431 [ci skip] * gorka archiving (#18450) * Automatic changelog for PR #18450 [ci skip] * Adds cafe capsule to research tiers because I accidentally left them out (#18451) * Fix these maps (#18452) * using accessories no longer drops items on spawn (#18453) * fixes accessory dropping items on spawn * up that * Automatic changelog for PR #18452 [ci skip] * Automatic changelog for PR #18453 [ci skip] * list access fix (#18438) * Automatic changelog for PR #18438 [ci skip] * Whitelist update (#18417) Co-authored-by: Tost * JSON Logging Refactor (#18252) * First pass * fixes * more fixes * num2hex length changes * pass 2 * fixed warning * looc log fix * . * update tgui * . * . * . * . * perttier * cleanup * . * . * fix token * no * . * . * . * , * modsay eventsay * . --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> * Automatic changelog for PR #18252 [ci skip] * Fixes looc log category (#18459) * Automatic changelog for PR #18459 [ci skip] * fix bad persistent client handling (#18460) * Automatic changelog for PR #18460 [ci skip] * fix attacak logging (#18461) * Automatic changelog for PR #18461 [ci skip] * Power Outage Buffs (#18457) * Power Outage Buffs Added emergency door releases to autoresleever rooms. Changed autoresleevers to still work without power. Added short range radios (off by default) to emergency supply boxes. Changed APCs to allow the cover to be opened by anyone beneath 15% charge. * Update stellar_delight3.dmm Gives APCs on the SD comms rooms more charge. Seems tether already has these. * Update stellar_delight3.dmm * Automatic changelog for PR #18457 [ci skip] * Automatic changelog compile [ci skip] * more target (#18486) * Automatic changelog for PR #18486 [ci skip] * Automatic changelog compile [ci skip] * Mop do_after multi use (#18485) * Mop do_after Makes mop do after allow multiple uses * Update mop.dm * fixes * Gets rid of these unused vars * cleanup --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> * Automatic changelog for PR #18485 [ci skip] * Added missing complexities and added bugfixes (#18427) * Automatic changelog for PR #18427 [ci skip] * Fixes holo collars not rendering when worn on suit slot (#18437) * Automatic changelog for PR #18437 [ci skip] * clean up flags (#18439) * clean up flags * wtf? * pass it * . * ... * . * . * fix retrace * . * . * properly build * eh static * . * . --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18439 [ci skip] * Rotatable Atom Element (#18440) * initial work * a much better way of doing this * these are needed * minimize change * nope * missing elements * whitespace * species cases * reduce pasta in ghosts * more small fixes * fixes for windows and inventory * unneeded * protected * circulator uses examine --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18440 [ci skip] * Moves destroy_on_drop to TG style and adds signal (#18454) * to tg style * unify drop proc * protection and logging on qdeleted item pickup * prevent stripping items from the inventory menu too --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18454 [ci skip] * ship names update (#18456) * Automatic changelog for PR #18456 [ci skip] * swapadoodle (#18482) * Automatic changelog for PR #18482 [ci skip] * adds xenotail vwag option (#18477) Co-authored-by: Aroliacue * Automatic changelog for PR #18477 [ci skip] * Adds a miniature description to the paperwork dispenser (#18478) * Automatic changelog for PR #18478 [ci skip] * Instrument Update (#18483) * Instrument Update * View fix * fix lint warnings --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> * Automatic changelog for PR #18483 [ci skip] * Adds card searching/retrieval to card decks (#18488) * Adds card searching/retrieval to card decks * woopsie I thought I fixed that already * I am so sorry for my trash code lmaooo * oops forgot that- * SOBBING VIOLENTLY I MISSED THAT TOO * Also maybe good to have an improper check in the visible messages * Automatic changelog for PR #18488 [ci skip] * sleeper forcemove (#18491) * fix bad ssprocesing (#18487) * Automatic changelog for PR #18491 [ci skip] * Automatic changelog for PR #18487 [ci skip] * Reshades Mercenary Modular Armor Pieces (#18489) * Automatic changelog compile [ci skip] * Reshades Merc Modular Armor --------- Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com> Co-authored-by: Aroliacue * Automatic changelog for PR #18489 [ci skip] * Bird up. Added 3 cawing emotes for the crows lover (#18469) * Adds crow caws. 2 single versions, and one where you caw multiple times. * Added copyright information for the 2 sounds * Also adds GWAH * Automatic changelog for PR #18469 [ci skip] * jetpacks in zero G should hold you up (#18473) * Automatic changelog for PR #18473 [ci skip] * Consts (#18455) * Gets rid of this const * Unused const * Update forensics.dm * Drinks defines * uNUSED * Update Keywords.dm * Update Keywords.dm * Add * yar * Fingerprint * Automatic changelog for PR #18455 [ci skip] * Gives internal organs passive regeneration (#18470) * Organ heal * Changes * Use the config * Automatic changelog for PR #18470 [ci skip] * Caw addon (#18497) * Add an implantable health scanner (#18465) * Add an health scanner implant. Corrects slight naming mistakes of nearby implants. * Makes it craftable + advanced --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18465 [ci skip] * Automatic changelog compile [ci skip] * drinky tweaks (#18466) * Automatic changelog for PR #18466 [ci skip] * barricade breakthrough (#18474) * barricade breakthrough * fire deals more * Automatic changelog for PR #18474 [ci skip] * Adds the loincloth to the loadout options (#18490) * Automatic changelog for PR #18490 [ci skip] * Vomit fixes (#18468) * Vomit fixes * Guti abused span_userdanger again * Automatic changelog for PR #18468 [ci skip] * Fixes a medical chem not taking species chem strength into account (#18496) Fixes a medical chem not taking species chem strength into account * Automatic changelog for PR #18496 [ci skip] * op21 cosmetics (#18475) * Automatic changelog for PR #18475 [ci skip] * Automatic changelog compile [ci skip] * Some Additional Hanner Beast Forms (#18503) Added the following forms to beast form for hanner and lleill: Hyena, Lion, Reindeer, Armadillo and Slug. I have considered more fanciful things like unicorns and gryphons, but for now we'll just keep it simple with normal animals. * Automatic changelog for PR #18503 [ci skip] * Service borg buffs + QoL (#18502) * Service borg buffs + QoL * oops forgot to lowercase that one * wait this would double-spam balloon alerts theoretically so- * Fix resin doors not being breakable (#18495) * Fix resin doors not being breakable * Update simple_doors.dm * Automatic changelog for PR #18502 [ci skip] * Automatic changelog for PR #18495 [ci skip] * Automatic changelog compile [ci skip] * Port /datum/status_effect and convert wetness and fire stacks to it (#18180) * Port /datum/status_effect system * Port fire stacks to status_effects * Fixes and adjustments to wetness * One last little thing * Fixes these compile errors A few things on the backend got updated...Adjusts them here. --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: Cameron Lennox * Automatic changelog for PR #18180 [ci skip] * Extra bartender slot (#18505) Added an extra bartender slot on request. Reasoning is that it's one of the most social roles and keeps other people moving socially too. However, it's also a very common one for the players to vanish to do scenes, which we don't want to discourage. Instead, letting another person in to fill the role in the meantime is perfect. This already works fine on the tether where there are 3 bars (4 if you include the maint one). SD will still need to share a bit though, or get creative, not really a biggie. * Automatic changelog for PR #18505 [ci skip] * runtime viewer fixes (#18512) * Automatic changelog for PR #18512 [ci skip] * Implements Autowiki (#18480) * Implements Autowiki * Disable autowiki for vending machines for now * Automatic changelog for PR #18480 [ci skip] * Negative trait point changes. (#18509) Changed some of the negative traits point values, primarily as they give so many points relative to the small cost of positive traits: Low Blood Volume from 3 to 2 and the extreme version from 5 to 3. These are serious, but they're reasonably in-line with other serious weakness traits. Reduced biocompatibility 2->1, 4->2, 8->4. These are really not that much of an issue, if you are in medbay getting treated, it just means they need to stick you with a couple extra syringes and rarely actually causes a problem other than taking a couple more minutes. 4 points is still a lot. Ambulant Blood 1 to 0, this has no effect at all here where changelings are never a risk. * Automatic changelog for PR #18509 [ci skip] * Adds new sprites for some guns, consolidates _vr file [IDB IGNORE] (#18306) * Adds new sprites for some guns, consolidates _vr file CHANGED: Captains laser new sprite: https://i.imgur.com/LbelowO.png Energy gun new sprite: https://i.imgur.com/0KSAIEW.png Nagant new sprite: https://i.imgur.com/6BtQdO6.png Makarov new sprite: https://i.imgur.com/ojBjcwb.png SecGun new sprite: https://i.imgur.com/snhIhB7.png StunRevolver new sprite: https://i.imgur.com/2Iy29CH.png (The vintage stun revolver still exists, so the old sprite is still accessible) * Shrink netgun * Automatic changelog for PR #18306 [ci skip] * Automatic changelog compile [ci skip] * Moves a few icons out of the archives (#18516) * Moves a few icons out of the archives Used downstream * yea * Update gun.dmi * Update gun.dmi * Fluff icon fixes [IDB IGNORE] (#18420) * Move unused icons * Fixes a LOT of icons * Fixes this cloak * Fixes these as well * These too * More fixes * Last fixes * Automatic changelog for PR #18420 [ci skip] * Whitelist update (#18506) Approved here: https://forum.vore-station.net/viewtopic.php?p=14347#p14347 * adds missing icon (#18521) * Automatic changelog for PR #18521 [ci skip] * icon and prefab (#18530) * Automatic changelog for PR #18530 [ci skip] * Landmine fixes (#18532) * mine updates * correct var * disposal atom flushed signal (#18527) * Automatic changelog for PR #18532 [ci skip] * use em (#18522) * Automatic changelog for PR #18527 [ci skip] * Automatic changelog for PR #18522 [ci skip] * corrected bag (#18524) * Automatic changelog for PR #18524 [ci skip] * sloog (#18526) * Automatic changelog for PR #18526 [ci skip] * forcemove (#18528) * allow precision item placement on casino table (#18525) * allow precision on casino table * need these * Automatic changelog for PR #18528 [ci skip] * Automatic changelog for PR #18525 [ci skip] * Addiction Withdrawal Shown On Body Scanners (#18531) * show withdrawals on body scanner * better text * Automatic changelog for PR #18531 [ci skip] * scanner (#18518) * Automatic changelog for PR #18518 [ci skip] * borer can't do that (#18520) * Automatic changelog for PR #18520 [ci skip] * [Fluff Item] Modifies Holly's custom capsule shelter (#18493) * Alphabetically sorts hanner/lleil beast form list (#18515) * Alphabetically sorts hanner beast form list * Actually let's label some of these too! * oops lol * fuck * oops missed this one in the hanner list * and this one FUCK * Automatic changelog for PR #18515 [ci skip] * tracking implant uses mob (#18517) * Automatic changelog for PR #18517 [ci skip] * Belt holders (#18535) * Automatic changelog for PR #18535 [ci skip] * Maglev track tweaks (#18529) * tweaks * better * Automatic changelog for PR #18529 [ci skip] * Cyborg gambling buffs (#18508) * Cyborg gambling buffs * I'M ALWAYS FORGETTING THIS ABOUT BALLON ALERTS * Improves quality of roll sound * fuck wrong audio quality setting lmao * Automatic changelog for PR #18508 [ci skip] * Adds Coroner as an alternative title for medical doctors (#18492) * Automatic changelog for PR #18492 [ci skip] * Dragon Plushie Upport (#18523) * Upport dragon plushies * Whoops, forgot the icons * Add to loadout blacklist * Automatic changelog for PR #18523 [ci skip] * general command selector (#18501) * Automatic changelog for PR #18501 [ci skip] * Automatic changelog compile [ci skip] * moves law outputs to a block (#18513) * moves law outputs to a block * eh * . * urg * , * wtf * . * Update _sheets.dm * Automatic changelog for PR #18513 [ci skip] * ling and defines (#18537) * Adds logging to bear traps (#18533) * THE LOGS * . * Update * Automatic changelog for PR #18537 [ci skip] * Automatic changelog for PR #18533 [ci skip] * Fix this (#18544) * Automatic changelog for PR #18544 [ci skip] * Even STRICTER reaction unittest (#18542) * none shall hide * agony * what? * baddef * test all * fix * all of them * actually fail * blocker * what * test * these are hard lockups anyway * better test * better * buh * nope * another * logging * logging * I'm a raving madman * anotherfix * undef * lets try this * how about this * this it? * alright this * oops * limit log * force it * condense * Condensing more * another * wat * oop * fix * test agent * test 2 * what about this * testing * tied this in correctly * try now * oops again * try this * inhibitors * virus mix * move to a real useful signal * test * Revert "test" This reverts commit 40d05e9351905c3f7034ac0e687b2dbe6697ce6b. * order * unnneeded * minimize diff * Automatic changelog for PR #18542 [ci skip] * Automatic changelog compile [ci skip] * grab fix (#18547) * Automatic changelog for PR #18547 [ci skip] * Gives the borg service fabricator all radial menus (#18538) * Makes borg serivce fabricator use all radial menus * last minute fixes, my specialty! * Reduces radial menu radius to 40 * Automatic changelog for PR #18538 [ci skip] * Shibari! (#18514) * Shibari! Added a new scene item, shibari bindings! These by default only cover the torso, but by clicking on them in your hands, they can also be set to bind arms and legs, acting in a similar way to handcuffs. They are worn in the suit slot. They can be found in the costume vendors, maint and the restraints crate from cargo. They have sprites for humanoid mobs and digitigrade legs, but not tesh yet. * Fix mistake Fixes error in previous commit * Update code/game/objects/items/weapons/handcuffs.dm Co-authored-by: ShadowLarkens * Update code/modules/mob/living/carbon/human/human.dm Co-authored-by: ShadowLarkens * Make these defines to prevent copy paste errors --------- Co-authored-by: ShadowLarkens Co-authored-by: Cameron Lennox * Automatic changelog for PR #18514 [ci skip] * Customizable dragon plushie! (#18534) * Add customizable plushie * Add wing underside * Add description * Add to loadout blacklist * Change ' to " * Fix typo, add name to GUI * Fix indentation * Fix indentation (again) * clean up the DM code * initial cleanup * won't work yet * . * initial styling * that really shouldn't be tick updated * handle import * proper stack sizes * stacking * typos * rm comment * Update OverlaySelector.tsx * Update PlushEditor.scss * Minor tweaks * Wawa * please make sure biome is setup * rm unused props * cache user --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> * Automatic changelog for PR #18534 [ci skip] * dependency up (#18549) * dependency up * tgs * obj screen to atom movable screen (#18434) * obj screen to atom movable screen * . * Automatic changelog for PR #18434 [ci skip] * fixes one of my first ever coding sins here (#18412) * fixes one of my first ever coding sins here make it explicit naming fix that ancient bug too confirm sane . . move it up better keep it there only color objects . properly reset on bad values . urg . * , --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18412 [ci skip] * Automatic changelog compile [ci skip] * Tethered Item Component (#18499) * tethered component * can't do that * visible_message should always show * forbid storing * catching some small issues * visible message is fighting a bit too much --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18499 [ci skip] * various map related features (#18510) * various map related features * add icons * tick da file * adds no docking codes var * cetus plaques * switch to weakref * annihilate single letter refs and such * fixes after testing * reverse an oopsy --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18510 [ci skip] * Makes poker chips half scale (#18550) * Automatic changelog for PR #18550 [ci skip] * Automatic changelog compile [ci skip] * Modernizing doors (#18511) * the agony begins * fixes * signaling * removed remaining attempt_vr * corrected behaviors * keep the history * oop * wording * filter for intent here * oop * no harm in these either * Update code/_onclick/item_attack.dm Co-authored-by: Cameron Lennox * Get rid of old procs --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18511 [ci skip] * Automatic changelog compile [ci skip] * Turf slip component [NEEDS TESTING] (#18057) * turf slip component * remove magic numbers * don't dupe timers * Water fix, lube buff * fix --------- Co-authored-by: Cameron Lennox * Automatic changelog for PR #18057 [ci skip] * Upports Churros (#18553) * churros * Remove sin * Oops! * Automatic changelog for PR #18553 [ci skip] * adds sexes (#18551) * Automatic changelog for PR #18551 [ci skip] * Automatic changelog compile [ci skip] * remove dead proc (#18562) * Automatic changelog for PR #18562 [ci skip] * a few more changes related to map (#18557) * we got a thing * use flags * makes it betterer * Automatic changelog for PR #18557 [ci skip] * Luckier 7 event map content + Card deck init improvement/bugfixing (#18561) * Starts da map * more areas!! * adds big sized casino card deck * moar rooms * VIOLENTLY VOMITS UP A DIFFERENT DESIGN * let's give the main floor more STUFF also these should be normal rwalls * moar place.... * Good for the Tost-Pass now I think * TAAAABLLLLLLLLLLE!!! * Adds dorm areas * DELETES the repo's map because tost has her finished copy of the map saved externally on her PC now!! * Adds credits for audio sources in sound file metadata * ... oops * sound credits * greatly cleans up card generation code and fixes some jank * Automatic changelog for PR #18561 [ci skip] * Fixes Lucky 7 map needing power (oops) (#18566) * Fixes Luck 7 map needing power (oops) * WAIT IT WAS MISSING LIGHTNG AND GRAVITY TOO?! * fix vorepanel button for AI (#18555) * fix vorepanel button for AI * . * this can be null * fix balloon alert for wide icons * Resprites casino chips (#18558) * Automatic changelog for PR #18555 [ci skip] * Automatic changelog for PR #18558 [ci skip] * Lore Pamphlet for new players (#18564) Added a new lore pamphlet for new players, which covers the most essential lore and gives an immediate understanding of how the world works. This automatically spawns in their hand until they've been here for 10 days old. * Automatic changelog for PR #18564 [ci skip] * Automatic changelog compile [ci skip] * Strips byondUI procs not needed for TGUI interaction (#18563) * all of these are moved to tgui * still uses topic * uses topic * uses topic * not needed * Automatic changelog for PR #18563 [ci skip] * Gatorsnout (#18536) * oh man idfk this SHOULD work * small stylistic change * trailing newline (what, okay) * Update .gitignore Please don't just edit that file. Tools is supposed to be on the repo even --------- Co-authored-by: Bandit Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> * Automatic changelog for PR #18536 [ci skip] * Improved Smes Map Upgrades (#18467) * fixing oldcode * fix * proc fix * this is a prefab anyway * qdel fix * Automatic changelog for PR #18467 [ci skip] * Oil and blood with no dna can be streaked again (#18559) * bloodyshoes * safety * that needs a db, so check if it actually was set (#18569) * Automatic changelog for PR #18559 [ci skip] * Automatic changelog for PR #18569 [ci skip] * fix organ deletion (#18572) * Automatic changelog for PR #18572 [ci skip] * Fixes invisibility potions (#18565) * Fixes invisibility potions Fixed invisibility potions being able to spawn in fruit. Cleaned up some missing reagent defines for glamour potions. * Fix thingies * Oopsies * Automatic changelog for PR #18565 [ci skip] * ports and fixes TG's keycombo input (#18573) * ports and fixes TG's keycombo input * . * . * cleaner * . * Automatic changelog for PR #18573 [ci skip] * Fluff item flute (#18571) Approved: https://forum.vore-station.net/viewtopic.php?p=14361#p14361 * Automatic changelog compile [ci skip] * Load alien/job whitelists from database, if enabled (#17952) * Load alien/job whitelists from database, if enabled * SQL Template for Table * Fixes * Commands * . * . * fix * cannot be a list * . * . * embed * . * . * query test * query test * query test * . * fix * Automatic changelog for PR #17952 [ci skip] * Door after_attack behavior fix (#18576) * these need handling * fix * respect attackby behavior * Automatic changelog for PR #18576 [ci skip] * Adds a few new sprites (#18578) * start this for latr * adds new markings * removes stray palette * changes a few shades i dislike * fix the dang dme * i haaaaaaaaaaate dreammaker * why do you do this * Unarchives Halo Shotgun (#18579) * Automatic changelog for PR #18578 [ci skip] * Remove it from the archive as well for clarity's sake (#18580) * Medigun stuffs * oops --------- Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com> Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> Co-authored-by: Killian <49700375+KillianKirilenko@users.noreply.github.com> Co-authored-by: Jenny Co-authored-by: Cameron Lennox Co-authored-by: Will <7099514+Willburd@users.noreply.github.com> Co-authored-by: TheToaster98 <51209769+TheToaster98@users.noreply.github.com> Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com> Co-authored-by: SatinIsle <98125273+SatinIsle@users.noreply.github.com> Co-authored-by: Aura Dusklight <46622484+NovaDusklight@users.noreply.github.com> Co-authored-by: Aroliacue <96730930+Aroliacue@users.noreply.github.com> Co-authored-by: Aroliacue Co-authored-by: nesquik <24830358+lbnesquik@users.noreply.github.com> Co-authored-by: ShadowLarkens Co-authored-by: MeepleMuncher <76881946+MeepleMuncher@users.noreply.github.com> Co-authored-by: Olive <49600480+zeskorion@users.noreply.github.com> Co-authored-by: shybandit5213 Co-authored-by: Bandit * Delete html/changelogs/AutoChangeLog-pr-17952.yml * Delete html/changelogs/AutoChangeLog-pr-18576.yml * Delete html/changelogs/AutoChangeLog-pr-18578.yml * medfix (#19) * Adjust (#20) * Update medigun_backpack.dm --------- Co-authored-by: Guti <32563288+TheCaramelion@users.noreply.github.com> Co-authored-by: vorestation-ci[bot] <199609141+vorestation-ci[bot]@users.noreply.github.com> Co-authored-by: Killian <49700375+KillianKirilenko@users.noreply.github.com> Co-authored-by: Jenny Co-authored-by: Cameron Lennox Co-authored-by: Will <7099514+Willburd@users.noreply.github.com> Co-authored-by: TheToaster98 <51209769+TheToaster98@users.noreply.github.com> Co-authored-by: Selis <12716288+ItsSelis@users.noreply.github.com> Co-authored-by: SatinIsle <98125273+SatinIsle@users.noreply.github.com> Co-authored-by: Aura Dusklight <46622484+NovaDusklight@users.noreply.github.com> Co-authored-by: Aroliacue <96730930+Aroliacue@users.noreply.github.com> Co-authored-by: Aroliacue Co-authored-by: nesquik <24830358+lbnesquik@users.noreply.github.com> Co-authored-by: ShadowLarkens Co-authored-by: MeepleMuncher <76881946+MeepleMuncher@users.noreply.github.com> Co-authored-by: Olive <49600480+zeskorion@users.noreply.github.com> Co-authored-by: shybandit5213 Co-authored-by: Bandit --- ATTRIBUTIONS.md | 17 + code/__defines/clothing.dm | 3 +- code/__defines/medigun.dm | 3 + code/__defines/research/techweb_nodes.dm | 1 + code/game/machinery/recharger.dm | 3 +- .../items/weapons/medigun/bork_medigun.dm | 115 ++++ .../items/weapons/medigun/linked_medigun.dm | 280 +++++++++ .../items/weapons/medigun/medigun_backpack.dm | 569 ++++++++++++++++++ .../weapons/medigun/medigun_backpack_ui.dm | 167 +++++ .../weapons/medigun/medigun_modifiers.dm | 8 + .../items/weapons/medigun/medigun_verbs.dm | 28 + .../objects/items/weapons/melee/energy.dm | 1 + .../research/tg/designs/weapon_designs.dm | 11 + .../experisci/experiment/instances/people.dm | 13 + .../tg/techwebs/nodes/medbay_nodes.dm | 12 + icons/mob/items/lefthand_medigun.dmi | Bin 0 -> 1564 bytes icons/mob/items/righthand_medigun.dmi | Bin 0 -> 1479 bytes icons/obj/borkmedigun.dmi | Bin 0 -> 9706 bytes .../Medigun/MedigunHelpers/ChargeStatus.tsx | 39 ++ .../Medigun/MedigunTabs/MedigunContent.tsx | 270 +++++++++ .../Medigun/MedigunTabs/MedigunParts.tsx | 149 +++++ .../tgui/interfaces/Medigun/constants.ts | 11 + .../tgui/interfaces/Medigun/index.tsx | 45 ++ .../packages/tgui/interfaces/Medigun/types.ts | 46 ++ .../tgui/interfaces/TraitTutorial.tsx | 1 - vorestation.dme | 6 + 26 files changed, 1795 insertions(+), 3 deletions(-) create mode 100644 code/__defines/medigun.dm create mode 100644 code/game/objects/items/weapons/medigun/bork_medigun.dm create mode 100644 code/game/objects/items/weapons/medigun/linked_medigun.dm create mode 100644 code/game/objects/items/weapons/medigun/medigun_backpack.dm create mode 100644 code/game/objects/items/weapons/medigun/medigun_backpack_ui.dm create mode 100644 code/game/objects/items/weapons/medigun/medigun_modifiers.dm create mode 100644 code/game/objects/items/weapons/medigun/medigun_verbs.dm create mode 100644 icons/mob/items/lefthand_medigun.dmi create mode 100644 icons/mob/items/righthand_medigun.dmi create mode 100644 icons/obj/borkmedigun.dmi create mode 100644 tgui/packages/tgui/interfaces/Medigun/MedigunHelpers/ChargeStatus.tsx create mode 100644 tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunContent.tsx create mode 100644 tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunParts.tsx create mode 100644 tgui/packages/tgui/interfaces/Medigun/constants.ts create mode 100644 tgui/packages/tgui/interfaces/Medigun/index.tsx create mode 100644 tgui/packages/tgui/interfaces/Medigun/types.ts diff --git a/ATTRIBUTIONS.md b/ATTRIBUTIONS.md index ceaf2d56747..c8e739501a1 100644 --- a/ATTRIBUTIONS.md +++ b/ATTRIBUTIONS.md @@ -110,3 +110,20 @@ **Creator / Copyright:** Toriate
**License Holders:** Matica
**License:** [CC BY-NC-SA 3.0](https://creativecommons.org/licenses/by-nc-sa/3.0/)
+
+**File:** `icons/obj/borkmedigun.dmi`
+**Creator:** Commissioned by Cross_Exonar from Toriate
+**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010
+**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)
+
+**File:** `icons/mob/items/lefthand_medigun.dmi`
+**Icon-States:**medblaster-wielded, medblaster, medblaster_cmo
+**Creator:** Commissioned by Cross_Exonar from Toriate
+**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010
+**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)
+
+**File:** `icons/mob/items/righthand_medigun.dmi`
+**Icon-States:**medblaster-wielded, medblaster, medblaster_cmo
+**Creator:** Commissioned by Cross_Exonar from Toriate
+**Link:** https://github.com/TS-Rogue-Star/Rogue-Star/pull/1010
+**License:** [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)
diff --git a/code/__defines/clothing.dm b/code/__defines/clothing.dm index 0eda9396561..549cf6243f1 100644 --- a/code/__defines/clothing.dm +++ b/code/__defines/clothing.dm @@ -168,7 +168,8 @@ NOTICE: Do not leave trailing commas!!!! #define POCKET_BAYSUIT \ /obj/item/storage/backpack, \ /obj/item/bluespaceradio, \ - /obj/item/defib_kit + /obj/item/defib_kit, \ + /obj/item/medigun_backpack /// Wrapper for adding clothing based traits #define ADD_CLOTHING_TRAIT(mob, trait) ADD_TRAIT(mob, trait, "[CLOTHING_TRAIT]_[REF(src)]") diff --git a/code/__defines/medigun.dm b/code/__defines/medigun.dm new file mode 100644 index 00000000000..87167eeac22 --- /dev/null +++ b/code/__defines/medigun.dm @@ -0,0 +1,3 @@ +#define MEDIGUN_IDLE 0 +#define MEDIGUN_CANCELLED 1 +#define MEDIGUN_BUSY 2 diff --git a/code/__defines/research/techweb_nodes.dm b/code/__defines/research/techweb_nodes.dm index 333fa50229c..d2b9135a1c8 100644 --- a/code/__defines/research/techweb_nodes.dm +++ b/code/__defines/research/techweb_nodes.dm @@ -85,6 +85,7 @@ #define TECHWEB_NODE_MEDBAY_EQUIP_ADV "medbay_equip_adv" #define TECHWEB_NODE_MEDBAY_EQUIP_HIGH_TECH "medbay_equip_high_tech" #define TECHWEB_NODE_MEDIGUN "medbay_medigun" +#define TECHWEB_NODE_MEDIGUN_CONSTANT "medbay_medigun_constant" #define TECHWEB_NODE_MINING "mining" #define TECHWEB_NODE_MINING_ADV "mining_adv" #define TECHWEB_NODE_MOD_ANOMALY "mod_anomaly" diff --git a/code/game/machinery/recharger.dm b/code/game/machinery/recharger.dm index 8ee26b5dad2..b4beba089e7 100644 --- a/code/game/machinery/recharger.dm +++ b/code/game/machinery/recharger.dm @@ -16,7 +16,8 @@ GLOBAL_LIST_INIT(allowed_recharger_devices, list( /obj/item/paicard, /obj/item/personal_shield_generator, /obj/item/gun/projectile/cell_loaded, - /obj/item/ammo_magazine/cell_mag + /obj/item/ammo_magazine/cell_mag, + /obj/item/medigun_backpack )) GLOBAL_LIST_INIT(allowed_wallcharger_devices, list( diff --git a/code/game/objects/items/weapons/medigun/bork_medigun.dm b/code/game/objects/items/weapons/medigun/bork_medigun.dm new file mode 100644 index 00000000000..220b9aeffff --- /dev/null +++ b/code/game/objects/items/weapons/medigun/bork_medigun.dm @@ -0,0 +1,115 @@ +/obj/item/bork_medigun + name = "prototype bluespace medigun" + desc = "The Bork BSM-92 or 'Blue Space Medigun' utilizes advanced bluespace technology to transfer beneficial reagents directly to torn tissue. This way, even larger wounds can be mended efficiently in short periods of time" + icon = 'icons/obj/borkmedigun.dmi' + icon_state = "medblaster" + var/wielded_item_state = "medblaster-wielded" + var/base_icon_state = "medblaster" + item_icons = list( + slot_l_hand_str = 'icons/mob/items/lefthand_medigun.dmi', + slot_r_hand_str = 'icons/mob/items/righthand_medigun.dmi', + ) + w_class = ITEMSIZE_HUGE + force = 0 + var/beam_range = 3 // How many tiles away it can scan. Changing this also changes the box size. + var/busy = MEDIGUN_IDLE // Set to true when scanning, to stop multiple scans. + var/action_cancelled = FALSE + var/wielded = FALSE + var/mob/current_target + var/mgcmo + canremove = FALSE + +/obj/item/bork_medigun/update_twohanding() + var/mob/living/M = loc + if(istype(M) && M.item_is_in_hands(src) && !M.hands_are_full()) + wielded = TRUE + name = "[initial(name)] (wielded)" + else + wielded = FALSE + name = initial(name) + ..() + +/obj/item/bork_medigun/update_held_icon() + if(wielded_item_state) + var/mob/living/M = loc + if(istype(M)) + if(M.can_wield_item(src) && src.is_held_twohanded(M)) + LAZYSET(item_state_slots, slot_l_hand_str, wielded_item_state) + LAZYSET(item_state_slots, slot_r_hand_str, wielded_item_state) + else + LAZYSET(item_state_slots, slot_l_hand_str, initial(item_state)) + LAZYSET(item_state_slots, slot_r_hand_str, initial(item_state)) + ..() + +// Draws a box showing the limits of movement while scanning something. +// Only the client supplied will see the box. +/obj/item/bork_medigun/proc/draw_box(atom/A, box_size, client/C) + var/list/our_box = list() + // Things moved with pixel_[x|y] will move the box, so this is to correct that. + var/pixel_x_correction = -A.pixel_x + var/pixel_y_correction = -A.pixel_y + + var/our_icon_size = world.icon_size + // First, place the bottom-left corner. + our_box += draw_line(A, SOUTHWEST, (-box_size * our_icon_size) + pixel_x_correction, (-box_size * our_icon_size) + pixel_y_correction, C) + + var/rendered_size = (box_size * 2) - 1 + // Make a line on the bottom, going right. + for(var/i = 1 to rendered_size) + var/x_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_x_correction + var/y_displacement = (-box_size * our_icon_size) + pixel_y_correction + our_box += draw_line(A, SOUTH, x_displacement, y_displacement, C) + + // Bottom-right corner. + our_box += draw_line(A, SOUTHEAST, (box_size * our_icon_size) + pixel_x_correction, (-box_size * our_icon_size) + pixel_y_correction, C) + + // Second line, for the right side going up. + for(var/i = 1 to rendered_size) + var/x_displacement = (box_size * our_icon_size) + pixel_x_correction + var/y_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_y_correction + our_box += draw_line(A, EAST, x_displacement, y_displacement, C) + + // Top-right corner. + our_box += draw_line(A, NORTHEAST, (box_size * our_icon_size) + pixel_x_correction, (box_size * our_icon_size) + pixel_y_correction, C) + + // Third line, for the top, going right. + for(var/i = 1 to rendered_size) + var/x_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_x_correction + var/y_displacement = (box_size * our_icon_size) + pixel_y_correction + our_box += draw_line(A, NORTH, x_displacement, y_displacement, C) + + // Top-left corner. + our_box += draw_line(A, NORTHWEST, (-box_size * our_icon_size) + pixel_x_correction, (box_size * our_icon_size) + pixel_y_correction, C) + + // Fourth and last line, for the left side going up. + for(var/i = 1 to rendered_size) + var/x_displacement = (-box_size * our_icon_size) + pixel_x_correction + var/y_displacement = (-box_size * our_icon_size) + (our_icon_size * i) + pixel_y_correction + our_box += draw_line(A, WEST, x_displacement, y_displacement, C) + return our_box + +// Draws an individual segment of the box. +/obj/item/bork_medigun/proc/draw_line(atom/A, line_dir, line_pixel_x, line_pixel_y, client/C) + var/image/line = image(icon = 'icons/effects/effects.dmi', loc = A, icon_state = "stripes", dir = line_dir) + line.pixel_x = line_pixel_x + line.pixel_y = line_pixel_y + line.plane = PLANE_FULLSCREEN // It's technically a HUD element but it doesn't need to show above item slots. + line.appearance_flags = RESET_TRANSFORM|RESET_COLOR|RESET_ALPHA|NO_CLIENT_COLOR|TILE_BOUND + line.alpha = 125 + C.images += line + return line + +// Removes the box that was generated before from the client. +/obj/item/bork_medigun/proc/delete_box(list/box_segments, client/C) + for(var/i in box_segments) + C.images -= i + qdel(i) + +/obj/item/bork_medigun/proc/color_box(list/box_segments, new_color, new_time) + for(var/i in box_segments) + animate(i, color = new_color, time = new_time) + +/obj/item/bork_medigun/attack_hand(mob/user) + if(user.get_inactive_hand() == src)// && loc != get_turf) + return + return ..() diff --git a/code/game/objects/items/weapons/medigun/linked_medigun.dm b/code/game/objects/items/weapons/medigun/linked_medigun.dm new file mode 100644 index 00000000000..40c5fc84091 --- /dev/null +++ b/code/game/objects/items/weapons/medigun/linked_medigun.dm @@ -0,0 +1,280 @@ +/obj/item/bork_medigun/linked + var/obj/item/medigun_backpack/medigun_base_unit + +/obj/item/bork_medigun/linked/Destroy() + if(medigun_base_unit) + var/obj/item/bork_medigun/medigun = medigun_base_unit.get_medigun() + //ensure the base unit's icon updates + if(medigun == src) + medigun = null + medigun_base_unit.replace_icon() + if(ismob(loc)) + var/mob/user = loc + user.update_inv_back() + medigun_base_unit = null + return ..() + +/obj/item/bork_medigun/linked/forceMove(atom/destination) //Forcemove override, ugh + if(destination == medigun_base_unit || destination == medigun_base_unit.loc || isturf(destination)) + . = doMove(destination, 0, 0) + if(isturf(destination)) + for(var/atom/A as anything in destination) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. + if(istype(A,/obj/structure/closet )) + break + +/obj/item/bork_medigun/linked/proc/check_charge(var/charge_amt) + return (medigun_base_unit.bcell && medigun_base_unit.bcell.check_charge(charge_amt)) + +/obj/item/bork_medigun/linked/proc/checked_use(var/charge_amt) + return (medigun_base_unit.bcell && medigun_base_unit.bcell.checked_use(charge_amt)) + +/obj/item/bork_medigun/linked/attack_self(mob/living/user) + if(medigun_base_unit.is_twohanded()) + update_twohanding() + if(busy) + busy = MEDIGUN_CANCELLED + +/obj/item/bork_medigun/linked/proc/should_stop(var/mob/living/target, var/mob/living/user, var/active_hand) + if(!target || !user || (!active_hand && medigun_base_unit.is_twohanded()) || !istype(target) || !istype(user) || busy < MEDIGUN_BUSY) + return TRUE + + if((user.get_active_hand() != active_hand || wielded == 0) && medigun_base_unit.is_twohanded()) + to_chat(user, span_warning("Please keep your hands free!")) + return TRUE + + if(user.is_incorporeal()) + return TRUE + + if(user.incapacitated(INCAPACITATION_DEFAULT | INCAPACITATION_KNOCKDOWN | INCAPACITATION_DISABLED | INCAPACITATION_KNOCKOUT | INCAPACITATION_STUNNED | INCAPACITATION_RESTRAINED)) + return TRUE + + if(user.stat) + return TRUE + + if(target.isSynthetic()) + to_chat(user, span_warning("Target is not organic.")) + return TRUE + + //if(get_dist(user, target) > beam_range) + if(!(target in range(beam_range, user)) || (!(target in view(10, user)) && !(medigun_base_unit.smodule.get_rating() >= 5))) + to_chat(user, span_warning("You are too far away from \the [target] to heal them, Or they are not in view. Get closer.")) + return TRUE + + if(!isliving(target)) + //to_chat(user, span_warning("\the [target] is not a valid target.")) + return TRUE + + if(!ishuman(target)) + return TRUE + + /*if(!H.getBruteLoss() && !H.getFireLoss() && !H.getToxLoss())// && !H.getOxyLoss()) // No point Wasting fuel/power if target healed + playsound(src, 'sound/machines/ping.ogg', 50) + to_chat(user, span_warning("\the [target] is fully healed.")) + return TRUE + */ + return FALSE + +/obj/item/bork_medigun/linked/afterattack(atom/target, mob/user, proximity_flag) + // Things that invalidate the scan immediately. + if(isturf(target)) + for(var/atom/A as anything in target) // If we can't scan the turf, see if we can scan anything on it, to help with aiming. + if(isliving(A)) + target = A + break + if(!istype(medigun_base_unit, /obj/item/medigun_backpack/cmo)) + update_twohanding() + if(busy && !(target == current_target) && isliving(target)) + to_chat(user, span_warning("\The [src] is already targeting something.")) + return + + if(!ishuman(target)) + return + + if(!medigun_base_unit.smanipulator) + to_chat(user, span_warning("\The [src] Blinks a red error light, Manipulator missing.")) + return + if(!medigun_base_unit.scapacitor) + to_chat(user, span_warning("\The [src] Blinks a blue error light, capacitor missing.")) + return + if(!medigun_base_unit.slaser) + to_chat(user, span_warning("\The [src] Blinks an orange error light, laser missing.")) + return + if(!medigun_base_unit.smodule) + to_chat(user, span_warning("\The [src] Blinks a pink error light, scanning module missing.")) + return + if(!check_charge(5)) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + return + if(get_dist(target, user) > beam_range) + to_chat(user, span_warning("You are too far away from \the [target] to affect it. Get closer.")) + return + + if(target == current_target && busy) + busy = MEDIGUN_CANCELLED + return + if(target == user) + to_chat(user, span_warning("Cant heal yourself.")) + return + if(!(target in range(beam_range, user)) || (!(target in view(10, user)) && !medigun_base_unit.smodule)) + to_chat(user, span_warning("You are too far away from \the [target] to heal them, Or they are not in view. Get closer.")) + return + + current_target = target + busy = MEDIGUN_BUSY + update_icon() + var/myicon = "medbeam_basic" + var/mycolor = "#037ffc" + var/datum/beam/scan_beam = user.Beam(target, icon = 'icons/obj/borkmedigun.dmi', icon_state = myicon, time = 6000) + var/filter = filter(type = "outline", size = 1, color = mycolor) + var/list/box_segments = list() + playsound(src, 'sound/weapons/wave.ogg', 50) + var/mob/living/carbon/human/H = target + to_chat(user, span_notice("Locking on to [H]")) + to_chat(H, span_warning("[user] is targetting you with their medigun")) + if(user.client) + box_segments = draw_box(target, beam_range, user.client) + color_box(box_segments, mycolor, 5) + process_medigun(H, user, filter) + + action_cancelled = FALSE + busy = MEDIGUN_IDLE + current_target = null + + // Now clean up the effects. + update_icon() + QDEL_NULL(scan_beam) + target.filters -= filter + if(user.client) // If for some reason they logged out mid-scan the box will be gone anyways. + delete_box(box_segments, user.client) + +/obj/item/bork_medigun/linked/proc/process_medigun(mob/living/carbon/human/H, mob/user, filter, ishealing = FALSE) + if(should_stop(H, user, user.get_active_hand())) + return + + if(do_after(user, 1 SECOND, user, timed_action_flags = IGNORE_USER_LOC_CHANGE, hidden = TRUE)) + var/washealing = ishealing // Did we heal last cycle + ishealing = FALSE // The default is 'we didn't heal this cycle' + if(!checked_use(5)) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + return + if(H.stat == DEAD) + process_medigun(H, user, filter) + return + var/lastier = medigun_base_unit.slaser.get_rating() + if(lastier >= 2) + if(checked_use(5)) + H.add_modifier(/datum/modifier/medbeameffect, 2 SECONDS) + if(H.getHalLoss() && checked_use(5)) + H.adjustHalLoss(-20) + if(H.weakened && checked_use(5)) + H.AdjustWeakened(-1) + if(lastier >= 3) + if(H.paralysis && (checked_use(15))) + H.AdjustParalysis(-1) + + var/healmod = lastier + /*if(H.getBruteLoss()) + healmod = min(lastier,medigun_base_unit.brutecharge,H.getBruteLoss()) + if(medigun_base_unit.brutecharge >= healmod) + if(!checked_use(healmod)) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + break + if(healmod < 0) + healmod = 0 + else + H.adjustBruteLoss(-healmod) + medigun_base_unit.brutecharge -= healmod + ishealing = 1 + if(H.getFireLoss()) + healmod = min(lastier,medigun_base_unit.burncharge,H.getFireLoss()) + if(medigun_base_unit.burncharge >= healmod) + if(!checked_use(healmod)) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + break + if(healmod < 0) + healmod = 0 + else + H.adjustFireLoss(-healmod) + medigun_base_unit.burncharge -= healmod + ishealing = 1*/ + if(H.getToxLoss()) + healmod = min(lastier,medigun_base_unit.toxcharge,H.getToxLoss()) + if(medigun_base_unit.toxcharge >= healmod) + if(!checked_use(healmod)) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + return + if(healmod < 0) + healmod = 0 + else + H.adjustToxLoss(-healmod) + medigun_base_unit.toxcharge -= healmod + ishealing = TRUE + if(H.getOxyLoss()) + healmod = min(10*lastier,H.getOxyLoss()) + if(!checked_use(min(10,healmod))) + to_chat(user, span_warning("\The [src] doesn't have enough charge left to do that.")) + return + H.adjustOxyLoss(-healmod) + ishealing = TRUE + + ishealing = process_wounds(H, lastier, lastier, ishealing) + //if(medigun_base_unit.brutecharge <= 0 || medigun_base_unit.burncharge <= 0 || medigun_base_unit.toxcharge <= 0) + medigun_base_unit.update_icon() + //if(medigun_base_unit.slaser.get_rating() >= 5) + + //Blood regeneration if there is some space + if(lastier >= 5) + if(H.vessel.get_reagent_amount("blood") < H.species.blood_volume) + var/datum/reagent/blood/B = locate() in H.vessel.reagent_list //Grab some blood + B.volume += min(5, (H.species.blood_volume - H.vessel.get_reagent_amount("blood")))// regenerate blood + + if(ishealing != washealing) // Either we stopped or started healing this cycle + if(ishealing) + H.filters += filter + else + H.filters -= filter + + process_medigun(H, user, filter, ishealing) + +/obj/item/bork_medigun/linked/proc/process_wounds(mob/living/carbon/human/H, heal_ticks, remaining_strength, ishealing) + while(heal_ticks > 0) + if(remaining_strength <= 0) + return ishealing + if((!H.getFireLoss() || medigun_base_unit.burncharge <= 0) && (!H.getBruteLoss() || medigun_base_unit.burncharge <= 0)) + return ishealing + + for(var/name in BP_ALL) + var/obj/item/organ/external/O = H.organs_by_name[name] + for(var/datum/wound/W in O.wounds) + if (W.internal) + continue + //if (W.bandaged && W.disinfected) + // continue + if (W.damage_type == BRUISE || W.damage_type == CUT || W.damage_type == PIERCE) + if(medigun_base_unit.brutecharge >= 1) + if(W.damage <= 1) + O.wounds -= W + medigun_base_unit.brutecharge -= 1 + ishealing = TRUE + else if(medigun_base_unit.brutecharge >= 1) + W.damage -= 1 + medigun_base_unit.brutecharge -= 1 + remaining_strength -= 1 + ishealing = TRUE + if (W.damage_type == BURN) + if(medigun_base_unit.burncharge >= 1) + if(W.damage <= 1) + O.wounds -= W + medigun_base_unit.burncharge -= 1 + ishealing = TRUE + else if(medigun_base_unit.burncharge >= 1) + W.damage -= 1 + medigun_base_unit.burncharge -= 1 + remaining_strength -= 1 + ishealing = TRUE + if(remaining_strength <= 0) + return ishealing + if(remaining_strength <= 0) + return ishealing + heal_ticks-- + return ishealing diff --git a/code/game/objects/items/weapons/medigun/medigun_backpack.dm b/code/game/objects/items/weapons/medigun/medigun_backpack.dm new file mode 100644 index 00000000000..c12b1a3db28 --- /dev/null +++ b/code/game/objects/items/weapons/medigun/medigun_backpack.dm @@ -0,0 +1,569 @@ +//backpack item +/obj/item/medigun_backpack + name = "protoype bluespace medigun backpack" + desc = "Contains a bluespace medigun, this portable unit digitizes and stores chems and battery power used by the attached gun." + icon = 'icons/obj/borkmedigun.dmi' + icon_override = 'icons/obj/borkmedigun.dmi' + icon_state = "mg-backpack" + item_state = "mg-backpack-onmob" + slot_flags = SLOT_BACK + force = 5 + throwforce = 6 + preserve_item = TRUE + w_class = ITEMSIZE_HUGE + unacidable = TRUE + origin_tech = list(TECH_BIO = 4, TECH_POWER = 2, TECH_BLUESPACE = 4) + + var/obj/item/bork_medigun/linked/medigun_path = /obj/item/bork_medigun/linked + var/obj/item/cell/bcell = /obj/item/cell + var/obj/item/cell/ccell = null + var/obj/item/stock_parts/matter_bin/sbin = /obj/item/stock_parts/matter_bin + var/obj/item/stock_parts/scanning_module/smodule = /obj/item/stock_parts/scanning_module + var/obj/item/stock_parts/manipulator/smanipulator = /obj/item/stock_parts/manipulator + var/obj/item/stock_parts/capacitor/scapacitor = /obj/item/stock_parts/capacitor + var/obj/item/stock_parts/micro_laser/slaser = /obj/item/stock_parts/micro_laser + var/charging = FALSE + var/brutecharge = 0 + var/toxcharge = 0 + var/burncharge = 0 + var/brutevol = 0 + var/toxvol = 0 + var/burnvol = 0 + var/chemcap = 60 + var/tankmax = 30 + var/containsgun = TRUE + var/maintenance = FALSE + var/smaniptier = 1 + var/sbintier = 1 + var/gridstatus = 0 + var/chargecap = 1000 + +//backpack item +/obj/item/medigun_backpack/cmo + name = "prototype bluespace medigun backpack - CMO" + desc = "Contains a compact version of the bluespace medigun able to be used one handed, this portable unit digitizes and stores chems and battery power used by the attached gun." + icon_state = "mg-backpack_cmo" + item_state = "mg-backpack_cmo-onmob" + scapacitor = /obj/item/stock_parts/capacitor/adv + smanipulator = /obj/item/stock_parts/manipulator/nano + smodule = /obj/item/stock_parts/scanning_module/adv + slaser = /obj/item/stock_parts/micro_laser/high + bcell = /obj/item/cell/apc + tankmax = 60 + brutecharge = 60 + toxcharge = 60 + burncharge = 60 + chemcap = 120 + brutevol = 120 + toxvol = 120 + burnvol = 120 + chargecap = 5000 + +/obj/item/medigun_backpack/proc/is_twohanded() + return TRUE + +/obj/item/medigun_backpack/cmo/is_twohanded() + return FALSE + +/obj/item/medigun_backpack/proc/apc_charge() + gridstatus = 0 + var/area/A = get_area(src) + if(!istype(A) || !A.powered(EQUIP)) + return FALSE + gridstatus = 1 + if(bcell && (bcell.charge < bcell.maxcharge)) + var/cur_charge = bcell.charge + var/delta = min(50, bcell.maxcharge-cur_charge) + bcell.give(delta) + A.use_power_oneoff(delta*100, EQUIP) + gridstatus = 2 + return TRUE + +/obj/item/medigun_backpack/proc/adjust_brutevol(modifier) + if(modifier > brutevol) + modifier = brutevol + if(modifier > (tankmax - brutecharge)) + modifier = tankmax - brutecharge + brutevol -= modifier + brutecharge += modifier + +/obj/item/medigun_backpack/proc/adjust_burnvol(modifier) + if(modifier > burnvol) + modifier = burnvol + if(modifier > (tankmax - burncharge)) + modifier = tankmax - burncharge + burnvol -= modifier + burncharge += modifier + +/obj/item/medigun_backpack/proc/adjust_toxvol(modifier) + if(modifier > toxvol) + modifier = toxvol + if(modifier > (tankmax - toxcharge)) + modifier = tankmax - toxcharge + toxvol -= modifier + toxcharge += modifier + +/obj/item/medigun_backpack/process() + if(!bcell) + return + + var/obj/item/bork_medigun/medigun = get_medigun() + + if(bcell.charge >= 10) + var/icon_needs_update = FALSE + if(brutecharge < tankmax && brutevol > 0 && (bcell.checked_use(smaniptier * 2))) + adjust_brutevol(smaniptier * 2) + icon_needs_update = TRUE + if(burncharge < tankmax && burnvol > 0 && (bcell.checked_use(smaniptier * 2))) + adjust_burnvol(smaniptier * 2) + icon_needs_update = TRUE + if(toxcharge < tankmax && toxvol > 0 && (bcell.checked_use(smaniptier * 2))) + adjust_toxvol(smaniptier * 2) + icon_needs_update = TRUE + //Alien tier + if(sbintier >= 5 && medigun.busy == MEDIGUN_IDLE && (bcell.charge >= 10)) + if(brutevol < chemcap && (bcell.checked_use(10))) + icon_needs_update = TRUE + brutevol ++ + if(burnvol < chemcap && (bcell.checked_use(10))) + icon_needs_update = TRUE + burnvol ++ + if(toxvol < chemcap && (bcell.checked_use(10))) + icon_needs_update = TRUE + toxvol ++ + + if(icon_needs_update) + update_icon() + + if(scapacitor.get_rating() >= 5) + if(apc_charge()) + charging = FALSE + return + charging = TRUE + + if(!charging || !ccell) + return + + var/scaptier = scapacitor.get_rating() + var/missing = min(scaptier*25, bcell.amount_missing()) + + if(missing > 0) + if(ccell && ccell.checked_use(missing)) + bcell.give(missing) + update_icon() + return + + if(ismob(loc)) + to_chat(loc, span_notice("The [ccell] runs out of power..")) + charging = FALSE + +/obj/item/medigun_backpack/get_cell() + return bcell + +/obj/item/medigun_backpack/update_icon() + . = ..() + cut_overlays() + if((bcell.percent() <= 5 )) + add_overlay(image('icons/obj/borkmedigun.dmi', "no_battery")) + else if((bcell.percent() <= 25 && bcell.percent() > 5)) + add_overlay(image('icons/obj/borkmedigun.dmi', "low_battery")) + + if(brutevol <= 0 && brutecharge > 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "red")) + else if(brutecharge <= 0 && brutevol <= 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "redstrike-blink")) + + if(toxvol <= 0 && toxcharge > 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "green")) + else if(toxcharge <= 0 && toxvol <= 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "greenstrike-blink")) + + if(burnvol <= 0 && burncharge > 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "orange")) + else if(burncharge <= 0 && burnvol <= 0) + add_overlay(image('icons/obj/borkmedigun.dmi', "orangestrike-blink")) + +/obj/item/medigun_backpack/proc/replace_icon(inhand) + var/obj/item/bork_medigun/medigun = get_medigun() + if(inhand) + icon_state = "mg-backpack-deployed" + item_state = "mg-backpack-deployed-onmob" + if(is_twohanded()) + medigun.icon_state = "medblaster" + medigun.base_icon_state = "medblaster" + medigun.wielded_item_state = "medblaster-wielded" + medigun.update_icon() + else + medigun.icon_state = "medblaster_cmo" + medigun.base_icon_state = "medblaster_cmo" + medigun.wielded_item_state = "" + medigun.update_icon() + else if(is_twohanded()) + icon_state = "mg-backpack" + item_state = "mg-backpack-onmob" + medigun.icon_state = "medblaster" + medigun.base_icon_state = "medblaster" + else + icon_state = "mg-backpack_cmo" + item_state = "mg-backpack_cmo-onmob" + medigun.icon_state = "medblaster_cmo" + medigun.base_icon_state = "medblaster_cmo" + + update_icon() + +/obj/item/medigun_backpack/Initialize(mapload) + AddComponent(/datum/component/tethered_item, medigun_path) + . = ..() + + var/obj/item/bork_medigun/linked/medigun = get_medigun() + medigun.medigun_base_unit = src + + if(!is_twohanded()) + medigun.beam_range = 4 + medigun.icon_state = "medblaster_cmo" + medigun.base_icon_state = "medblaster_cmo" + medigun.wielded_item_state = "" + medigun.update_icon() + if(ispath(bcell)) + bcell = new bcell(src) + bcell.charge = 0 + if(ispath(sbin)) + sbin = new sbin(src) + if(ispath(smodule)) + smodule = new smodule(src) + START_PROCESSING(SSobj, src) + if(ispath(smanipulator)) + smanipulator = new smanipulator(src) + if(ispath(scapacitor)) + scapacitor = new scapacitor(src) + if(ispath(slaser)) + slaser = new slaser(src) + update_icon() + + +/obj/item/medigun_backpack/Destroy() + STOP_PROCESSING(SSobj, src) + QDEL_NULL(bcell) + QDEL_NULL(smodule) + QDEL_NULL(smanipulator) + QDEL_NULL(scapacitor) + QDEL_NULL(slaser) + . = ..() + +/obj/item/medigun_backpack/proc/get_medigun() + var/datum/component/tethered_item/TI = GetComponent(/datum/component/tethered_item) + return TI.get_handheld() + +/obj/item/medigun_backpack/emp_act(severity) + . = ..() + if(bcell) + bcell.emp_act(severity) + +/obj/item/medigun_backpack/attack_hand(mob/living/user) + // See important note in tethered_item.dm + if(SEND_SIGNAL(src,COMSIG_ITEM_ATTACK_SELF,user) & COMPONENT_CANCEL_ATTACK_CHAIN) + return TRUE + . = ..() + +/obj/item/medigun_backpack/MouseDrop() + if(ismob(src.loc)) + if(!CanMouseDrop(src)) + return + var/mob/M = src.loc + if(!M.unEquip(src)) + return + src.add_fingerprint(usr) + M.put_in_any_hand_if_possible(src) + /*icon_state = "mg-backpack" + item_state = "mg-backpack-onmob" + update_icon() //success + usr.update_inv_back()*/ + + +/obj/item/medigun_backpack/attackby(obj/item/W, mob/user, params) + if(refill_reagent(W, user)) + return + + var/obj/item/bork_medigun/medigun = get_medigun() + + if(W.is_crowbar() && maintenance) + if(smodule ) + smodule.forceMove(get_turf(loc)) + smodule = null + + if(smanipulator) + STOP_PROCESSING(SSobj, src) + smanipulator.forceMove(get_turf(loc)) + smanipulator = null + smaniptier = 0 + + if(slaser) + slaser.forceMove(get_turf(loc)) + slaser = null + + if(scapacitor) + STOP_PROCESSING(SSobj, src) + scapacitor.forceMove(get_turf(loc)) + scapacitor = null + + if(sbin) + STOP_PROCESSING(SSobj, src) + sbin.forceMove(get_turf(loc)) + sbin = null + sbintier = 0 + + to_chat(user, span_notice("You remove the Components from \the [src].")) + update_icon() + return TRUE + + if(W.is_screwdriver()) + if(!maintenance) + maintenance = TRUE + to_chat(user, span_notice("You open the maintenance hatch on \the [src].")) + return + + maintenance = FALSE + to_chat(user, span_notice("You close the maintenance hatch on \the [src].")) + return + + if(istype(W, /obj/item/cell)) + if(!user.unEquip(W)) + return + W.forceMove(src) + if(ccell) + to_chat(user, span_notice("You swap the [W] for \the [ccell].")) + ccell = W + to_chat(user, span_notice("You install the [W] into \the [src].")) + charging = TRUE + return + + if(maintenance) + if(istype(W, /obj/item/stock_parts/scanning_module)) + if(smodule) + to_chat(user, span_notice("\The [src] already has a scanning module.")) + else + if(!user.unEquip(W)) + return + W.forceMove(src) + smodule = W + to_chat(user, span_notice("You install the [W] into \the [src].")) + medigun.beam_range = 3+smodule.get_rating() + update_icon() + return + + if(istype(W, /obj/item/stock_parts/manipulator)) + if(smanipulator) + to_chat(user, span_notice("\The [src] already has a manipulator.")) + return + if(!user.unEquip(W)) + return + W.forceMove(src) + smanipulator = W + smaniptier = smanipulator.get_rating() + if(sbin && scapacitor)START_PROCESSING(SSobj, src) + to_chat(user, span_notice("You install the [W] into \the [src].")) + update_icon() + return + + if(istype(W, /obj/item/stock_parts/micro_laser)) + if(slaser) + to_chat(user, span_notice("\The [src] already has a micro laser.")) + return + if(!user.unEquip(W)) + return + W.forceMove(src) + slaser = W + to_chat(user, span_notice("You install the [W] into \the [src].")) + update_icon() + return + + if(istype(W, /obj/item/stock_parts/capacitor)) + if(scapacitor) + to_chat(user, span_notice("\The [src] already has a capacitor.")) + return + if(!user.unEquip(W)) + return + W.forceMove(src) + scapacitor = W + var/scaptier = scapacitor.get_rating() + if(scaptier == 1) + chargecap = 1000 + bcell.maxcharge = 1000 + if(bcell.charge > chargecap) + bcell.charge = chargecap + else if(scaptier == 2) + chargecap = 2000 + bcell.maxcharge = 2000 + if(bcell.charge > chargecap) + bcell.charge = chargecap + else if(scaptier == 3) + chargecap = 3000 + bcell.maxcharge = 3000 + if(bcell.charge > chargecap) + bcell.charge = chargecap + else if(scaptier == 4) + chargecap = 4000 + bcell.maxcharge = 4000 + if(bcell.charge > chargecap) + bcell.charge = chargecap + else if(scaptier == 5) + chargecap = 5000 + bcell.maxcharge = 5000 + if(bcell.charge > chargecap) + bcell.charge = chargecap + + if(sbin && smanipulator)START_PROCESSING(SSobj, src) + to_chat(user, span_notice("You install the [W] into \the [src].")) + update_icon() + return + + if(istype(W, /obj/item/stock_parts/matter_bin)) + if(sbin) + to_chat(user, span_notice("\The [src] already has a matter bin.")) + return + if(!user.unEquip(W)) + return + W.forceMove(src) + sbin = W + sbintier = sbin.get_rating() + if(sbintier >= 5) + chemcap = 300 + tankmax = 150 + else + chemcap = 60*(sbintier) + tankmax = 30*sbintier + if(brutecharge > chemcap) + brutecharge = chemcap + if(burncharge > chemcap) + burncharge = chemcap + if(toxcharge > chemcap) + toxcharge = chemcap + if(brutecharge > tankmax) + brutecharge = tankmax + if(burncharge > tankmax) + burncharge = tankmax + if(toxcharge > tankmax) + toxcharge = tankmax + if(scapacitor && smanipulator)START_PROCESSING(SSobj, src) + to_chat(user, span_notice("You install the [W] into \the [src].")) + update_icon() + return + + return ..() + + +/obj/item/medigun_backpack/proc/refill_reagent(var/obj/item/container, mob/user) + . = FALSE + if(!maintenance && (istype(container, /obj/item/reagent_containers/glass/beaker) || istype(container, /obj/item/reagent_containers/glass/bottle))) + + if(!(container.flags & OPENCONTAINER)) + to_chat(user, span_warning("You need to open the [container] first!")) + return + + var/reagentwhitelist = list(REAGENT_ID_BICARIDINE, REAGENT_ID_ANTITOXIN, REAGENT_ID_KELOTANE, REAGENT_ID_DERMALINE)//, "tricordrazine") + + for(var/G in container.reagents.reagent_list) + var/datum/reagent/R = G + var/modifier = 1 + var/totransfer = 0 + var/name = "" + + if(R.id in reagentwhitelist) + switch(R.id) + if(REAGENT_ID_BICARIDINE) + name = "bruteheal" + modifier = 4 + totransfer = chemcap - brutevol + if(REAGENT_ID_ANTITOXIN) + name = "toxheal" + modifier = 4 + totransfer = chemcap - toxvol + if(REAGENT_ID_KELOTANE) + name = "burnheal" + modifier = 4 + totransfer = chemcap - burnvol + if(REAGENT_ID_DERMALINE) + name = "burnheal" + modifier = 8 + totransfer = chemcap - burnvol + /*if("tricordrazine") + name = "tricordrazine" + modifier = 1 + if((brutevol != chemcap) && (burnvol != chemcap) && (toxvol != chemcap)) + totransfer = 1 //tempcheck to get past the totransfer check + else + totransfer = 0*/ + if(totransfer <= 0) + to_chat(user, span_notice("The [src] cannot accept anymore [name]!")) + totransfer = min(totransfer, container.reagents.get_reagent_amount(R.id) * modifier) + + switch(R.id) + if(REAGENT_ID_BICARIDINE) + brutevol += totransfer + if(REAGENT_ID_ANTITOXIN) + toxvol += totransfer + if(REAGENT_ID_KELOTANE) + burnvol += totransfer + if(REAGENT_ID_DERMALINE) + burnvol += totransfer + /*if("tricordrazine") //Tricord too problematic + var/maxamount = container.reagents.get_reagent_amount(R.id) + var/amountused + var/oldbrute = brutevol + var/oldburn = burnvol + var/oldtox = toxvol + + while(maxamount > 0) + if(brutevol >= chemcap && burnvol >= chemcap && toxvol >= chemcap) + break + maxamount -- + amountused++ + totransfer ++ + if(brutevol < chemcap) + brutevol ++ + if(burnvol < chemcap) + burnvol ++ + if(toxvol < chemcap) + toxvol ++ + var/readout = "You add [amountused] units of [R.name] to the [src]. \n The [src] Stores " + var/readoutadditions = FALSE + if(oldbrute != brutevol) + readout += "[round(brutevol - oldbrute)] U of bruteheal vol" + readoutadditions = TRUE + if(oldburn != burnvol) + if(readoutadditions) + readout += ", " + readout += "[round(burnvol - oldburn)] U of burnheal vol" + readoutadditions = TRUE + if(oldtox != toxvol) + if(readoutadditions) + readout += ", " + readout += "[round(toxvol - oldtox)] U of toxheal vol" + if(oldbrute != brutevol || oldburn != burnvol || oldtox != toxvol)to_chat(user, span_notice("[readout]."))*/ + if(totransfer > 0) + if(R.id != "tricordrazine") + to_chat(user, span_notice("You add [totransfer / modifier] units of [R.name] to the [src]. \n The [src] stores [round(totransfer)] U of [name].")) + container.reagents.remove_reagent(R.id, totransfer / modifier) + playsound(src, 'sound/weapons/empty.ogg', 50, 1) + update_icon() + . = TRUE + return + +//checks that the base unit is in the correct slot to be used +/obj/item/medigun_backpack/proc/slot_check() + var/mob/M = loc + if(!istype(M)) + return FALSE //not equipped + + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_back) == src) + return TRUE + if((slot_flags & SLOT_BACK) && M.get_equipped_item(slot_s_store) == src) + return TRUE + return FALSE + +/obj/item/medigun_backpack/dropped(mob/user) + ..() + replace_icon() + +/obj/item/medigun_backpack/proc/checked_use(var/charge_amt) + return (bcell && bcell.checked_use(charge_amt)) diff --git a/code/game/objects/items/weapons/medigun/medigun_backpack_ui.dm b/code/game/objects/items/weapons/medigun/medigun_backpack_ui.dm new file mode 100644 index 00000000000..b53ff6e229c --- /dev/null +++ b/code/game/objects/items/weapons/medigun/medigun_backpack_ui.dm @@ -0,0 +1,167 @@ +/obj/item/medigun_backpack/tgui_interact(mob/user, datum/tgui/ui) + ui = SStgui.try_update_ui(user, src, ui) + if(!ui) + ui = new(user, src, "Medigun", name) + ui.open() + +/obj/item/medigun_backpack/tgui_data(mob/user) + var/obj/item/bork_medigun/medigun = get_medigun() + var/mob/living/carbon/human/H = medigun.current_target + var/patientname + var/patienthealth = 0 + var/patientbruteloss = 0 + var/patientfireloss = 0 + var/patienttoxloss = 0 + var/patientoxyloss = 0 + var/patientstatus = 0 + var/list/bloodData = list() + var/inner_bleeding = FALSE + var/organ_damage = FALSE + + //var/minhealth = 0 + if(scapacitor?.get_rating() < 5) + gridstatus = 3 + if(H) + for(var/obj/item/organ/org in H.internal_organs) + if(org.robotic >= ORGAN_ROBOT) + continue + if(org.status & ORGAN_BLEEDING) + inner_bleeding = TRUE + if(org.damage >= 1 && !istype(org, /obj/item/organ/internal/brain)) + organ_damage = TRUE + patientname = H + patienthealth = max(0, (H.health + abs(-H.getMaxHealth())) / (H.getMaxHealth() + abs(-H.getMaxHealth()))) + patientbruteloss = H.getBruteLoss() + patientfireloss = H.getFireLoss() + patienttoxloss = H.getToxLoss() + patientoxyloss = H.getOxyLoss() + patientstatus = H.stat + if(H.vessel) + bloodData["volume"] = round(H.vessel.get_reagent_amount("blood")) + bloodData["max_volume"] = H.species.blood_volume + var/list/data = list( + "maintenance" = maintenance, + "generator" = charging, + "gridstatus" = gridstatus, + "tankmax" = tankmax, + "power_cell_status" = bcell ? bcell.percent() : null, + "cell_status" = ccell ? (ccell.percent()/100) : null, + "bruteheal_charge" = scapacitor ? brutecharge : null, + "burnheal_charge" = scapacitor ? burncharge : null, + "toxheal_charge" = scapacitor ? toxcharge : null, + "bruteheal_vol" = sbin ? brutevol : null, + "burnheal_vol" = sbin ? burnvol : null, + "toxheal_vol" = sbin ? toxvol : null, + "patient_name" = smodule ? patientname : null, + "patient_health" = smodule ? patienthealth : null, + "patient_brute" = smodule ? patientbruteloss : null, + "patient_burn" = smodule ? patientfireloss : null, + "patient_tox" = smodule ? patienttoxloss : null, + "patient_oxy" = smodule ? patientoxyloss : null, + "blood_status" = smodule ? bloodData : null, + "patient_status" = smodule ? patientstatus : null, + "organ_damage" = smodule ? organ_damage : null, + "inner_bleeding" = smodule ? inner_bleeding : null, + "examine_data" = get_examine_data() + ) + return data + +/obj/item/medigun_backpack/proc/get_examine_data() + var/obj/item/bork_medigun/medigun = get_medigun() + + return list( + "smodule" = smodule ? list("name" = smodule.name, "range" = medigun.beam_range, "rating" = smodule.get_rating()) : null, + "smanipulator" = smanipulator ? list("name" = smanipulator.name, "rating" = smaniptier) : null, + "slaser" = slaser ? list("name" = slaser.name, "rating" = slaser.get_rating()) : null, + "scapacitor" = scapacitor ? list("name" = scapacitor.name, "chargecap" = chargecap, "rating" = scapacitor.get_rating()) : null, + "sbin" = sbin ? list("name" = sbin.name, "chemcap" = chemcap, "tankmax" = tankmax, "rating" = sbin.get_rating()) : null + ) + +/obj/item/medigun_backpack/tgui_act(action, params, datum/tgui/ui) + if(..()) + return TRUE + + . = TRUE + + var/obj/item/bork_medigun/medigun = get_medigun() + + switch(action) + if("celleject") + cell_eject() + return TRUE + + if("cancel_healing") + if(medigun?.busy) + medigun.busy = MEDIGUN_CANCELLED + return TRUE + + if("toggle_maintenance") + maintenance = !maintenance + return TRUE + + if("rem_smodule") + if(!smodule || !maintenance) + return FALSE + smodule.forceMove(get_turf(loc)) + to_chat(ui.user, span_notice("You remove the [smodule] from \the [src].")) + smodule = null + update_icon() + return TRUE + + if("rem_mani") + if(!smanipulator || !maintenance) + return FALSE + STOP_PROCESSING(SSobj, src) + smanipulator.forceMove(get_turf(loc)) + to_chat(ui.user, span_notice("You remove the [smanipulator] from \the [src].")) + smanipulator = null + smaniptier = 0 + update_icon() + return TRUE + + if("rem_laser") + if(!slaser || !maintenance) + return FALSE + slaser.forceMove(get_turf(loc)) + to_chat(ui.user, span_notice("You remove the [slaser] from \the [src].")) + slaser = null + update_icon() + return TRUE + + if("rem_cap") + if(!scapacitor || !maintenance) + return FALSE + STOP_PROCESSING(SSobj, src) + scapacitor.forceMove(get_turf(loc)) + to_chat(ui.user, span_notice("You remove the [scapacitor] from \the [src].")) + scapacitor = null + update_icon() + return TRUE + + if("rem_bin") + if(!sbin || !maintenance) + return FALSE + STOP_PROCESSING(SSobj, src) + sbin.forceMove(get_turf(loc)) + to_chat(ui.user, span_notice("You remove the [sbin] from \the [src].")) + sbin = null + sbintier = 0 + update_icon() + return TRUE + +/obj/item/medigun_backpack/ShiftClick(mob/user) + . = ..() + var/obj/item/bork_medigun/medigun = get_medigun() + if(!medigun) + return + tgui_interact(user) + +/obj/item/medigun_backpack/proc/cell_eject() + if(!ccell) + return FALSE + charging = FALSE + ccell.forceMove(get_turf(loc)) + to_chat(usr, span_notice("You remove the [ccell] from \the [src].")) + ccell = null + update_icon() + return TRUE diff --git a/code/game/objects/items/weapons/medigun/medigun_modifiers.dm b/code/game/objects/items/weapons/medigun/medigun_modifiers.dm new file mode 100644 index 00000000000..2cea3e761bf --- /dev/null +++ b/code/game/objects/items/weapons/medigun/medigun_modifiers.dm @@ -0,0 +1,8 @@ +/datum/modifier/medbeameffect + name = "medgunffect" + desc = "You're being stabilized" + mob_overlay_state = "medigun_effect" + stacks = MODIFIER_STACK_EXTEND + //pain_immunity = TRUE + bleeding_rate_percent = 0.1 //only a little + incoming_oxy_damage_percent = 0 diff --git a/code/game/objects/items/weapons/medigun/medigun_verbs.dm b/code/game/objects/items/weapons/medigun/medigun_verbs.dm new file mode 100644 index 00000000000..37997f4be69 --- /dev/null +++ b/code/game/objects/items/weapons/medigun/medigun_verbs.dm @@ -0,0 +1,28 @@ +/obj/item/medigun_backpack/verb/toggle_medigun() + set name = "Toggle medigun" + set category = "Object" + + var/mob/living/carbon/human/user = usr + if(maintenance) + to_chat(user, span_warning("Please close the maintenance hatch with a screwdriver first, or to remove components, use a crowbar.")) + return + + if(!medigun) + to_chat(user, span_warning("The medigun is missing!")) + return + + if(medigun.loc != src) + reattach_medigun(user) //Remove from their hands and back onto the medigun unit + return + + if(!slot_check()) + to_chat(user, span_warning("You need to equip [src] before taking out [medigun].")) + else + if(!user.put_in_hands(medigun)) //Detach the medigun into the user's hands + to_chat(user, span_warning("You need a free hand to hold the medigun!")) + else + containsgun = 0 + replace_icon(TRUE) + if(is_twohanded()) + medigun.update_twohanding() + user.update_inv_back() diff --git a/code/game/objects/items/weapons/melee/energy.dm b/code/game/objects/items/weapons/melee/energy.dm index 134171c4e3b..6bd03c4f35f 100644 --- a/code/game/objects/items/weapons/melee/energy.dm +++ b/code/game/objects/items/weapons/melee/energy.dm @@ -406,6 +406,7 @@ active_armourpen = 25 projectile_parry_chance = 40 colorable = TRUE + item_flags = DROPDEL | NOSTRIP hitcost = 75 diff --git a/code/modules/research/tg/designs/weapon_designs.dm b/code/modules/research/tg/designs/weapon_designs.dm index 599d9ec6962..2a634b86e58 100644 --- a/code/modules/research/tg/designs/weapon_designs.dm +++ b/code/modules/research/tg/designs/weapon_designs.dm @@ -870,6 +870,17 @@ ) departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE +/datum/design_techweb/medigun_constant + name = "Prototype Bluespace Medigun Backpack" + id = "medigun_constant" + build_type = PROTOLATHE + materials = list(MAT_STEEL = 8000, MAT_PLASTIC = 8000, MAT_GLASS = 5000, MAT_SILVER = 1000, MAT_GOLD = 1000, MAT_URANIUM = 1000) + build_path = /obj/item/medigun_backpack + category = list( + RND_CATEGORY_WEAPONS + RND_SUBCATEGORY_WEAPONS_RANGED + ) + departmental_flags = DEPARTMENT_BITFLAG_MEDICAL | DEPARTMENT_BITFLAG_SCIENCE + /datum/design_techweb/upgradeAOE name = "Mining Explosion Upgrade" desc = "An area of effect upgrade for the Proto-Kinetic Accelerator." diff --git a/code/modules/research/tg/experisci/experiment/instances/people.dm b/code/modules/research/tg/experisci/experiment/instances/people.dm index a0a45dc7412..93bdd5b2cc9 100644 --- a/code/modules/research/tg/experisci/experiment/instances/people.dm +++ b/code/modules/research/tg/experisci/experiment/instances/people.dm @@ -9,3 +9,16 @@ if(check.size_multiplier < 1.25 && check.size_multiplier > 0.75) return FALSE return TRUE + +/datum/experiment/scanning/people/hurt_medigun + required_count = 3 + required_traits_desc = "Our newly and improvised Medi-Gun needs field testing! Surely, there has to be someone who's gotten a few bruises or scratches here or there." + +/datum/experiment/scanning/people/hurt_medigun/is_valid_scan_target(mob/living/carbon/human/check, datum/component/experiment_handler/experiment_handler) + . = ..() + if(!.) + return + var/HP_percent = check.health/check.getMaxHealth() + if(HP_percent < 1) + return TRUE + return FALSE diff --git a/code/modules/research/tg/techwebs/nodes/medbay_nodes.dm b/code/modules/research/tg/techwebs/nodes/medbay_nodes.dm index 5ca0eccdf0e..39c3ded19d6 100644 --- a/code/modules/research/tg/techwebs/nodes/medbay_nodes.dm +++ b/code/modules/research/tg/techwebs/nodes/medbay_nodes.dm @@ -173,6 +173,18 @@ research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS) announce_channels = list(CHANNEL_MEDICAL) +/datum/techweb_node/medigun_constant + id = TECHWEB_NODE_MEDIGUN_CONSTANT + display_name = "Medigun Backpack" + description = "A revised version of the ML3M series. This one features a cell-powered constant beam, and ability to charge it with chemicals." + prereq_ids = list(TECHWEB_NODE_MEDIGUN) + design_ids = list( + "medigun_constant" + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = TECHWEB_TIER_5_POINTS) + announce_channels = list(CHANNEL_MEDICAL) + discount_experiments = list(/datum/experiment/scanning/people/hurt_medigun = TECHWEB_TIER_3_POINTS) + /datum/techweb_node/nif id = TECHWEB_NODE_NIF display_name = "Nanite-Implant Frameworks" diff --git a/icons/mob/items/lefthand_medigun.dmi b/icons/mob/items/lefthand_medigun.dmi new file mode 100644 index 0000000000000000000000000000000000000000..3c7f99c98133c78e68ed9a17b3cd4321cbfd7442 GIT binary patch literal 1564 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 literal 0 HcmV?d00001 diff --git a/icons/obj/borkmedigun.dmi b/icons/obj/borkmedigun.dmi new file mode 100644 index 0000000000000000000000000000000000000000..e4ab2827a23ab9734188a7b5864534b6a0c2c30f GIT binary patch literal 9706 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 AX8-^I literal 0 HcmV?d00001 diff --git a/tgui/packages/tgui/interfaces/Medigun/MedigunHelpers/ChargeStatus.tsx b/tgui/packages/tgui/interfaces/Medigun/MedigunHelpers/ChargeStatus.tsx new file mode 100644 index 00000000000..84915a1d203 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/MedigunHelpers/ChargeStatus.tsx @@ -0,0 +1,39 @@ +import { Box, LabeledList, ProgressBar, Stack } from 'tgui-core/components'; + +export const ChargeStatus = (props: { + name: string; + color: string; + charge: number | null; + max: number | null; + volume: number | null; +}) => { + const { name, color, charge, max, volume } = props; + + return ( + + + + {charge !== null ? ( + + {charge.toFixed()} / {max} + + ) : ( + Missing Capacitor + )} + + + Reserve: + + + {volume !== null ? ( + + {volume.toFixed()} + + ) : ( + Missing Bin + )} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunContent.tsx b/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunContent.tsx new file mode 100644 index 00000000000..63faad850aa --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunContent.tsx @@ -0,0 +1,270 @@ +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + LabeledList, + ProgressBar, + Section, + Stack, +} from 'tgui-core/components'; +import { + gridStatusToColor, + gridStatusToText, + powerToColor, + powerToText, + statToColor, + statToString, +} from '../constants'; +import { ChargeStatus } from '../MedigunHelpers/ChargeStatus'; +import type { Data, SModule } from '../types'; + +export const MedigunContent = (props: { smodule: SModule }) => { + const { act, data } = useBackend(); + + const { + maintenance, + tankmax, + generator, + gridstatus, + power_cell_status, + cell_status, + bruteheal_charge, + burnheal_charge, + toxheal_charge, + bruteheal_vol, + burnheal_vol, + toxheal_vol, + patient_name, + patient_health, + patient_brute, + patient_burn, + patient_tox, + patient_oxy, + blood_status, + patient_status, + organ_damage, + inner_bleeding, + } = data; + + const { smodule } = props; + + const moduleLevel = smodule?.rating || 0; + + return ( + + +
+ + + {maintenance ? ( + Maintenance Hatch Open + ) : power_cell_status !== null ? ( + + ) : ( + Missing Cell + )} + + {gridstatus !== 3 && ( + + {gridStatusToText[gridstatus] || 'Unavailable'} + + )} + act('celleject')} + /> + } + > + [ {powerToText[generator] || 'Unavailable'} ] + + {cell_status !== null ? ( + + + + ) : ( + Missing Cell + )} + +
+
+ +
+ + + + + +
+
+ +
+ + {patient_health !== null && + patient_brute !== null && + patient_burn !== null && + patient_tox !== null && + patient_oxy !== null ? ( + <> + + + + {patient_name ? ( + + {patient_name} + + + + + ) : ( + 'No Target' + )} + + {!!data.patient_name && ( + <> + + + + {moduleLevel >= 2 && ( + <> + + {!!organ_damage && ( + + Organ Damage! + + )} + {!!patient_status && ( + + + {statToString[patient_status]} + + + )} + + )} + + {`${(patient_health * 100).toFixed()}%`} + + + + + {moduleLevel >= 2 && ( + + {blood_status ? ( + + + + {!!inner_bleeding && ( + + Inner Bleeding! + + )} + + {`${( + (blood_status.volume / + blood_status.max_volume) * + 100 + ).toFixed()}%`} + + + + ) : ( + No Blood Detected + )} + + )} + + )} + + + + {!!data.patient_name && moduleLevel >= 2 && ( + + + + + {patient_brute} + + + {patient_burn} + + + + + + + {patient_tox} + + + {patient_oxy} + + + + + )} + + + ) : ( + Missing Scanning Module + )} + +
+
+
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunParts.tsx b/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunParts.tsx new file mode 100644 index 00000000000..b9d28144f12 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/MedigunTabs/MedigunParts.tsx @@ -0,0 +1,149 @@ +import { useBackend } from 'tgui/backend'; +import { Box, Button, LabeledList, Section, Stack } from 'tgui-core/components'; +import type { BooleanLike } from 'tgui-core/react'; +import type { ExamineData } from '../types'; + +export const MedigunParts = (props: { + examineData: ExamineData; + maintenance: BooleanLike; +}) => { + const { act } = useBackend(); + const { examineData, maintenance } = props; + const { smodule, smanipulator, slaser, scapacitor, sbin } = examineData; + + return ( +
act('toggle_maintenance')} + selected={maintenance} + tooltip="Toggle maintenance mode" + icon="wrench" + /> + } + > + + + + + {smodule ? ( + maintenance ? ( + act('rem_smodule')}> + Remove Module + + ) : ( + + {'It has a ' + + smodule.name + + ' installed, device will function within ' + + smodule.range + + ' tiles'} + {smodule.rating >= 5 ? ' and' : undefined} + {smodule.rating >= 5 ? ' through walls' : undefined} + {'.'} + + ) + ) : ( + Missing + )} + + + {smanipulator ? ( + maintenance ? ( + act('rem_mani')}> + Remove Manipulator + + ) : ( + + {'It has a ' + + smanipulator.name + + ' installed, chem digitizing is '} + {smanipulator.rating >= 5 + ? '125% Efficient' + : `${(smanipulator.rating / 4) * 100}% Efficient`} + {'.'} + + ) + ) : ( + Missing + )} + + + {slaser ? ( + maintenance ? ( + act('rem_laser')}> + Remove Laser + + ) : ( + + {'It has a ' + + slaser.name + + ' installed, and can heal ' + + slaser.rating + + ' damage per cycle'} + {slaser.rating >= 5 ? ' and will' : undefined} + {slaser.rating >= 5 ? ' regenerate blood' : undefined} + {slaser.rating >= 5 ? ' while beam is focused' : ''} + {'.'} + + ) + ) : ( + Missing + )} + + + {scapacitor ? ( + maintenance ? ( + act('rem_cap')}> + Remove Capacitor + + ) : ( + + {'It has a ' + + scapacitor.name + + ' installed, battery Capacity is ' + + scapacitor.chargecap + + ' Units'} + {scapacitor.rating >= 5 + ? ' the cell will recharge from the local power grid' + : ''} + {''} + {'.'} + + ) + ) : ( + Missing + )} + + + {sbin ? ( + maintenance ? ( + act('rem_bin')}> + Remove Bin + + ) : ( + + {'It has a ' + + sbin.name + + ' installed, can hold ' + + sbin.tankmax + + ' charge and ' + + sbin.chemcap + + ' reserve chems'} + {sbin.rating >= 5 + ? 'and will slowly generate chems in exchange for power.' + : '.'} + + ) + ) : ( + Missing + )} + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Medigun/constants.ts b/tgui/packages/tgui/interfaces/Medigun/constants.ts new file mode 100644 index 00000000000..b486ece4be4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/constants.ts @@ -0,0 +1,11 @@ +export const powerToColor = ['bad', 'good']; + +export const powerToText = ['Cell Offline', 'Cell Charging']; + +export const gridStatusToColor = ['bad', 'average', 'good']; + +export const gridStatusToText = ['Off Grid', 'Grid Available', 'Using Grid']; + +export const statToString = ['Alive', 'Unconscious', 'Dead']; + +export const statToColor = ['orange', 'red']; diff --git a/tgui/packages/tgui/interfaces/Medigun/index.tsx b/tgui/packages/tgui/interfaces/Medigun/index.tsx new file mode 100644 index 00000000000..cfe749ebac8 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/index.tsx @@ -0,0 +1,45 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Stack, Tabs } from 'tgui-core/components'; +import { MedigunContent } from './MedigunTabs/MedigunContent'; +import { MedigunParts } from './MedigunTabs/MedigunParts'; +import type { Data } from './types'; + +export const Medigun = (props) => { + const { data } = useBackend(); + const { maintenance, examine_data } = data; + const [selectedTab, setSelectedTab] = useState(0); + + const tab: React.JSX.Element[] = []; + tab[0] = ; + tab[1] = ( + + ); + + return ( + + + + + + setSelectedTab(0)} + > + Medigun Content + + setSelectedTab(1)} + > + Medigun Parts + + + + {tab[selectedTab]} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Medigun/types.ts b/tgui/packages/tgui/interfaces/Medigun/types.ts new file mode 100644 index 00000000000..95a6046d82f --- /dev/null +++ b/tgui/packages/tgui/interfaces/Medigun/types.ts @@ -0,0 +1,46 @@ +import type { BooleanLike } from 'tgui-core/react'; + +export type Data = { + maintenance: BooleanLike; + tankmax: number; + generator: number; + gridstatus: number; + power_cell_status: number | null; + cell_status: number | null; + bruteheal_charge: number | null; + burnheal_charge: number | null; + toxheal_charge: number | null; + bruteheal_vol: number | null; + burnheal_vol: number | null; + toxheal_vol: number | null; + patient_name: string | null; + patient_health: number | null; + patient_brute: number | null; + patient_burn: number | null; + patient_tox: number | null; + patient_oxy: number | null; + blood_status: { volume: number; max_volume: number } | null; + patient_status: number | null; + organ_damage: BooleanLike; + inner_bleeding: BooleanLike; + examine_data: ExamineData; +}; + +export type SModule = { name: string; range: number; rating: number } | null; + +export type ExamineData = { + smodule: SModule; + smanipulator: { name: string; rating: number } | null; + slaser: { name: string; rating: number } | null; + scapacitor: { + name: string; + chargecap: number; + rating: number; + } | null; + sbin: { + name: string; + chemcap: number; + tankmax: number; + rating: number; + } | null; +}; diff --git a/tgui/packages/tgui/interfaces/TraitTutorial.tsx b/tgui/packages/tgui/interfaces/TraitTutorial.tsx index 8569b4a17dc..10787fb01df 100644 --- a/tgui/packages/tgui/interfaces/TraitTutorial.tsx +++ b/tgui/packages/tgui/interfaces/TraitTutorial.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react/no-danger */ import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; import { Box, Section, Stack, Tabs } from 'tgui-core/components'; diff --git a/vorestation.dme b/vorestation.dme index f37b7dee1df..def6b739e9d 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -126,6 +126,7 @@ #include "code\__defines\matrices.dm" #include "code\__defines\MC.dm" #include "code\__defines\mecha.dm" +#include "code\__defines\medigun.dm" #include "code\__defines\misc.dm" #include "code\__defines\mobs.dm" #include "code\__defines\movement.dm" @@ -1868,6 +1869,11 @@ #include "code\game\objects\items\weapons\material\twohanded.dm" #include "code\game\objects\items\weapons\material\twohanded_vr.dm" #include "code\game\objects\items\weapons\material\whetstone.dm" +#include "code\game\objects\items\weapons\medigun\bork_medigun.dm" +#include "code\game\objects\items\weapons\medigun\linked_medigun.dm" +#include "code\game\objects\items\weapons\medigun\medigun_backpack.dm" +#include "code\game\objects\items\weapons\medigun\medigun_backpack_ui.dm" +#include "code\game\objects\items\weapons\medigun\medigun_modifiers.dm" #include "code\game\objects\items\weapons\melee\deflect.dm" #include "code\game\objects\items\weapons\melee\energy.dm" #include "code\game\objects\items\weapons\melee\energy_vr.dm"