From d603829661c7b38ea0a952b316de44951ef5aae4 Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 01:40:47 +0000 Subject: [PATCH 01/71] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-7645.yml | 4 -- html/changelogs/AutoChangeLog-pr-8505.yml | 6 --- html/changelogs/AutoChangeLog-pr-8556.yml | 4 -- html/changelogs/AutoChangeLog-pr-8568.yml | 5 --- html/changelogs/AutoChangeLog-pr-8581.yml | 4 -- html/changelogs/AutoChangeLog-pr-8582.yml | 4 -- html/changelogs/AutoChangeLog-pr-8586.yml | 4 -- html/changelogs/AutoChangeLog-pr-8587.yml | 4 -- html/changelogs/AutoChangeLog-pr-8597.yml | 4 -- html/changelogs/AutoChangeLog-pr-8599.yml | 5 --- html/changelogs/AutoChangeLog-pr-8601.yml | 5 --- html/changelogs/AutoChangeLog-pr-8602.yml | 4 -- html/changelogs/AutoChangeLog-pr-8605.yml | 4 -- html/changelogs/AutoChangeLog-pr-8611.yml | 4 -- html/changelogs/AutoChangeLog-pr-8615.yml | 4 -- html/changelogs/AutoChangeLog-pr-8616.yml | 4 -- html/changelogs/AutoChangeLog-pr-8617.yml | 4 -- html/changelogs/AutoChangeLog-pr-8621.yml | 5 --- html/changelogs/AutoChangeLog-pr-8629.yml | 4 -- html/changelogs/AutoChangeLog-pr-8633.yml | 4 -- html/changelogs/AutoChangeLog-pr-8639.yml | 4 -- html/changelogs/AutoChangeLog-pr-8647.yml | 4 -- html/changelogs/archive/2025-03.yml | 49 +++++++++++++++++++++++ 23 files changed, 49 insertions(+), 94 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-7645.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8505.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8556.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8568.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8581.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8582.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8586.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8587.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8597.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8599.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8601.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8602.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8605.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8611.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8615.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8616.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8617.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8621.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8629.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8633.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8639.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8647.yml diff --git a/html/changelogs/AutoChangeLog-pr-7645.yml b/html/changelogs/AutoChangeLog-pr-7645.yml deleted file mode 100644 index 107f8d5ddcc2..000000000000 --- a/html/changelogs/AutoChangeLog-pr-7645.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "hry-gh" -delete-after: True -changes: - - rscadd: "added a poll button to the lobby screen. engage democratically." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8505.yml b/html/changelogs/AutoChangeLog-pr-8505.yml deleted file mode 100644 index 9480a357f385..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8505.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "TrollerNoob" -delete-after: True -changes: - - rscadd: "New shipments of Chestrigs and Technician Welder Chestrigs have arrived from Chinook station. Urban and Snow operations are now covered." - - imageadd: "Added Urban/Snow variants for Chestrig and Welder Chestrig" - - code_imp: "Removed some unneeded code from Chestrig/Welder Chestrig for automatic map variants." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8556.yml b/html/changelogs/AutoChangeLog-pr-8556.yml deleted file mode 100644 index 06ff0d7f870c..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8556.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "lanse12" -delete-after: True -changes: - - spellcheck: "fixed some turret related typos" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8568.yml b/html/changelogs/AutoChangeLog-pr-8568.yml deleted file mode 100644 index 7b37dc8c1aca..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8568.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Syndro101" -delete-after: True -changes: - - code_imp: "changed lots of landmarks to use their new icons" - - imageadd: "new landmark icons" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8581.yml b/html/changelogs/AutoChangeLog-pr-8581.yml deleted file mode 100644 index 2a2015d193d4..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8581.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - code_imp: "Removed unused variables in dancer behavior delegate" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8582.yml b/html/changelogs/AutoChangeLog-pr-8582.yml deleted file mode 100644 index f612b3f8ec84..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8582.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "private-tristan" -delete-after: True -changes: - - admin: "Create-Xenos range defaults to 0, instead of 1, hooray!" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8586.yml b/html/changelogs/AutoChangeLog-pr-8586.yml deleted file mode 100644 index 2060a0388dfb..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8586.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "MistChristmas" -delete-after: True -changes: - - bugfix: "Fixes setting the amount of belts in the SecTech twice." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8587.yml b/html/changelogs/AutoChangeLog-pr-8587.yml deleted file mode 100644 index 4f717cbe9d75..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8587.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fixed chat theme always defaulting to dark on a setting change" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8597.yml b/html/changelogs/AutoChangeLog-pr-8597.yml deleted file mode 100644 index 04988d67f88d..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8597.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JackieEstegado" -delete-after: True -changes: - - bugfix: "Antiparasitic chems can cure stage 2+ infected again." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8599.yml b/html/changelogs/AutoChangeLog-pr-8599.yml deleted file mode 100644 index 1166bf452101..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8599.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - bugfix: "Fixes hunting ground spawner code checking for if theres people above the min_mob instead of below" - - rscadd: "Hunting ground prey and youngblood now reset their timers if nobody elligible were found" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8601.yml b/html/changelogs/AutoChangeLog-pr-8601.yml deleted file mode 100644 index 7ad6cd660bd6..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8601.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - bugfix: "Fixed UPP supply packs overriding all normal variant supply packs" - - code_imp: "Added a check to ensure all usage of GLOBAL_SUBTYPE_PATH_INDEXED has uniquely indexed entries" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8602.yml b/html/changelogs/AutoChangeLog-pr-8602.yml deleted file mode 100644 index 7bb209020e16..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8602.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - bugfix: "Girders are now able to be deconstructed/dismantled etc post repair" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8605.yml b/html/changelogs/AutoChangeLog-pr-8605.yml deleted file mode 100644 index eea28b71eb22..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8605.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - bugfix: "Strain resetting no longer gives you an evolution button as facehugger" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8611.yml b/html/changelogs/AutoChangeLog-pr-8611.yml deleted file mode 100644 index a4b2d161dd41..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8611.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "NHC" -delete-after: True -changes: - - imageadd: "changed jacket icon name from pilot_flightsuit_dj to pilot_flightsuit_alt_dj" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8615.yml b/html/changelogs/AutoChangeLog-pr-8615.yml deleted file mode 100644 index a0a5d52c44d9..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8615.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - bugfix: "Yautja no longer get buffed by orders" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8616.yml b/html/changelogs/AutoChangeLog-pr-8616.yml deleted file mode 100644 index 628d5eb81aea..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8616.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - bugfix: "Yautja no longer see the security level in the stat panel" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8617.yml b/html/changelogs/AutoChangeLog-pr-8617.yml deleted file mode 100644 index b4a3c8811077..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8617.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "MeH0y" -delete-after: True -changes: - - bugfix: "burrower blip is correctly colored" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8621.yml b/html/changelogs/AutoChangeLog-pr-8621.yml deleted file mode 100644 index 5809ac800a45..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8621.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Danilcus-W" -delete-after: True -changes: - - qol: "Trappers can now see how much \"Insight\" they currently have." - - bugfix: "Trapper abilities empowered overlay is now shown." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8629.yml b/html/changelogs/AutoChangeLog-pr-8629.yml deleted file mode 100644 index 06ebd8492055..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8629.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "remindme" -delete-after: True -changes: - - bugfix: "Some MREs that should've had veggies in them had no veggies (ripoff!)" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8633.yml b/html/changelogs/AutoChangeLog-pr-8633.yml deleted file mode 100644 index f90bce4a3856..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8633.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Drathek" -delete-after: True -changes: - - balance: "Grace timer for being AFK is reduced from 20 mins down to 3 mins after round start" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8639.yml b/html/changelogs/AutoChangeLog-pr-8639.yml deleted file mode 100644 index 4584f57a04da..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8639.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - admin: "Added logging for shuttle manipulation." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8647.yml b/html/changelogs/AutoChangeLog-pr-8647.yml deleted file mode 100644 index ce0ed49ac6c0..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8647.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "larentoun" -delete-after: True -changes: - - qol: "Added a visual timer for cooldowns of Xeno Abilities" \ No newline at end of file diff --git a/html/changelogs/archive/2025-03.yml b/html/changelogs/archive/2025-03.yml index 922986b03b07..758115d8abda 100644 --- a/html/changelogs/archive/2025-03.yml +++ b/html/changelogs/archive/2025-03.yml @@ -24,3 +24,52 @@ - imageadd: Added a blank OOC icon for missing sprites. - imageadd: Added a StaffManager OOC icon. - imageadd: Re-Added missing RatKing OOC icon. +2025-03-05: + Danilcus-W: + - qol: Trappers can now see how much "Insight" they currently have. + - bugfix: Trapper abilities empowered overlay is now shown. + Drathek: + - bugfix: Fixed UPP supply packs overriding all normal variant supply packs + - code_imp: Added a check to ensure all usage of GLOBAL_SUBTYPE_PATH_INDEXED has + uniquely indexed entries + - bugfix: Fixed chat theme always defaulting to dark on a setting change + - code_imp: Removed unused variables in dancer behavior delegate + - balance: Grace timer for being AFK is reduced from 20 mins down to 3 mins after + round start + JackieEstegado: + - bugfix: Antiparasitic chems can cure stage 2+ infected again. + MeH0y: + - bugfix: burrower blip is correctly colored + MistChristmas: + - bugfix: Fixes setting the amount of belts in the SecTech twice. + NHC: + - imageadd: changed jacket icon name from pilot_flightsuit_dj to pilot_flightsuit_alt_dj + Red-byte3D: + - bugfix: Yautja no longer see the security level in the stat panel + - bugfix: Yautja no longer get buffed by orders + - bugfix: Girders are now able to be deconstructed/dismantled etc post repair + - admin: Added logging for shuttle manipulation. + - bugfix: Fixes hunting ground spawner code checking for if theres people above + the min_mob instead of below + - rscadd: Hunting ground prey and youngblood now reset their timers if nobody elligible + were found + - bugfix: Strain resetting no longer gives you an evolution button as facehugger + Syndro101: + - code_imp: changed lots of landmarks to use their new icons + - imageadd: new landmark icons + TrollerNoob: + - rscadd: New shipments of Chestrigs and Technician Welder Chestrigs have arrived + from Chinook station. Urban and Snow operations are now covered. + - imageadd: Added Urban/Snow variants for Chestrig and Welder Chestrig + - code_imp: Removed some unneeded code from Chestrig/Welder Chestrig for automatic + map variants. + hry-gh: + - rscadd: added a poll button to the lobby screen. engage democratically. + lanse12: + - spellcheck: fixed some turret related typos + larentoun: + - qol: Added a visual timer for cooldowns of Xeno Abilities + private-tristan: + - admin: Create-Xenos range defaults to 0, instead of 1, hooray! + remindme: + - bugfix: Some MREs that should've had veggies in them had no veggies (ripoff!) From 6045a55fb08c75886a1e736502be4aa4921a0d4b Mon Sep 17 00:00:00 2001 From: Killfish <145384199+TheKillfish@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:51:26 +0100 Subject: [PATCH 02/71] Light Replacer Improvements (#8509) # About the pull request Revival of #7381. Adds Light Replacer to ComTech and normal tool storage vendors, adds recipe to Autolathe. Recycles broken lightbulbs, both when replacing them in holders or inserting them manually, turning 3 broken bulbs into 1 new light. # Explain why it's good for the game Light Replacers are handy but understated tools for quickly getting light back to places with active APCs but is barely avaliable outside of ones placed on maps and isn't as smooth to use as it could be. # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: add: Added the Light Replacer to tool storage and to the autolathe. The Light Replacer can now also recycle broken bulbs, either directly when replacing or when inserted by hand. /:cl: --------- Co-authored-by: harry --- code/game/machinery/autolathe_datums.dm | 5 ++ .../vending/vendor_types/engineering.dm | 4 +- .../objects/items/devices/lightreplacer.dm | 69 +++++++++++-------- code/modules/power/lighting.dm | 2 +- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/code/game/machinery/autolathe_datums.dm b/code/game/machinery/autolathe_datums.dm index fcff34a86f8e..a9eee084b34c 100644 --- a/code/game/machinery/autolathe_datums.dm +++ b/code/game/machinery/autolathe_datums.dm @@ -67,6 +67,11 @@ path = /obj/item/tool/wrench category = AUTOLATHE_CATEGORY_TOOLS +/datum/autolathe/recipe/lightreplacer + name = "light replacer" + path = /obj/item/device/lightreplacer/empty + category = AUTOLATHE_CATEGORY_TOOLS + /datum/autolathe/recipe/mop name = "mop" path = /obj/item/tool/mop diff --git a/code/game/machinery/vending/vendor_types/engineering.dm b/code/game/machinery/vending/vendor_types/engineering.dm index 35e78e5cc962..1fa773f9cf65 100644 --- a/code/game/machinery/vending/vendor_types/engineering.dm +++ b/code/game/machinery/vending/vendor_types/engineering.dm @@ -40,7 +40,8 @@ list("ME3 Hand Welder", floor(scale * 2), /obj/item/tool/weldingtool/simple, VENDOR_ITEM_REGULAR), list("Screwdriver", floor(scale * 4), /obj/item/tool/screwdriver, VENDOR_ITEM_REGULAR), list("Wirecutters", floor(scale * 4), /obj/item/tool/wirecutters, VENDOR_ITEM_REGULAR), - list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR) + list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR), + list("Light Replacer", floor(scale * 4), /obj/item/device/lightreplacer, VENDOR_ITEM_REGULAR) ) /obj/structure/machinery/cm_vending/sorted/tech/comtech_tools @@ -65,6 +66,7 @@ list("Wrench", floor(scale * 4), /obj/item/tool/wrench, VENDOR_ITEM_REGULAR), list("Multitool", floor(scale * 4), /obj/item/device/multitool, VENDOR_ITEM_REGULAR), list("ME3 Hand Welder", floor(scale * 2), /obj/item/tool/weldingtool/simple, VENDOR_ITEM_REGULAR), + list("Light Replacer", floor(scale * 4), /obj/item/device/lightreplacer, VENDOR_ITEM_REGULAR), list("UTILITY", -1, null, null), list("Sentry Gun Network Laptop", 4, /obj/item/device/sentry_computer, VENDOR_ITEM_REGULAR), diff --git a/code/game/objects/items/devices/lightreplacer.dm b/code/game/objects/items/devices/lightreplacer.dm index 42af8347a5ee..dcf2ad5c17e4 100644 --- a/code/game/objects/items/devices/lightreplacer.dm +++ b/code/game/objects/items/devices/lightreplacer.dm @@ -41,7 +41,7 @@ /obj/item/device/lightreplacer name = "light replacer" - desc = "A device to automatically replace lights. Refill with working lightbulbs." + desc = "A device to automatically replace lights. Can be refill with working lightbulbs and sheets of glass, and can recycle broken lightbulbs." icon = 'icons/obj/janitor.dmi' icon_state = "lightreplacer0" @@ -54,51 +54,59 @@ flags_atom = FPRINT|CONDUCT flags_equip_slot = SLOT_WAIST + matter = list("metal" = 20,"glass" = 50) var/max_uses = 50 - var/uses = 0 + var/uses = 50 var/failmsg = "" var/charge = 1 + var/recycle = 0 + var/max_recycle = 3 + +/obj/item/device/lightreplacer/empty + uses = 0 /obj/item/device/lightreplacer/Initialize() . = ..() - uses = max_uses failmsg = "The [name]'s refill light blinks red." /obj/item/device/lightreplacer/get_examine_text(mob/user) . = ..() - . += "It has [uses] lights remaining." + . += "It has [uses] lights remaining, and [recycle] broken lights stored." /obj/item/device/lightreplacer/attackby(obj/item/W, mob/user) if(istype(W, /obj/item/stack/sheet/glass)) - var/obj/item/stack/sheet/glass/G = W + var/obj/item/stack/sheet/glass/glass = W if(uses >= max_uses) - to_chat(user, SPAN_WARNING("[src.name] is full.")) + to_chat(user, SPAN_WARNING("[src] is full.")) return - else if(G.use(1)) + else if(glass.use(1)) AddUses(5) - to_chat(user, SPAN_NOTICE("You insert a piece of glass into the [src.name]. You have [uses] lights remaining.")) + to_chat(user, SPAN_NOTICE("You insert a piece of glass into the [src]. You have [uses] lights remaining.")) return else to_chat(user, SPAN_WARNING("You need one sheet of glass to replace lights.")) if(istype(W, /obj/item/light_bulb)) - var/obj/item/light_bulb/L = W - if(L.status == 0) // LIGHT OKAY + var/obj/item/light_bulb/bulb = W + if(bulb.status == 0) // LIGHT OKAY if(uses < max_uses) AddUses(1) - to_chat(user, "You insert the [L.name] into the [src.name]. You have [uses] lights remaining.") + to_chat(user, SPAN_NOTICE("You insert the [bulb] into [src]. You have [uses] lights remaining.")) user.drop_held_item() - qdel(L) + qdel(bulb) return else - to_chat(user, "You need a working light.") + Recycle() + to_chat(user, SPAN_NOTICE("You insert the [bulb] into [src] for recycling.")) + user.drop_held_item() + qdel(bulb) return /obj/item/device/lightreplacer/attack_self(mob/user) ..() - to_chat(usr, "It has [uses] lights remaining.") + to_chat(usr, "It has [uses] lights remaining, and has [recycle] broken lights stored.") /obj/item/device/lightreplacer/update_icon() icon_state = "lightreplacer0" @@ -106,12 +114,12 @@ /obj/item/device/lightreplacer/proc/Use(mob/user) - playsound(src.loc, 'sound/machines/click.ogg', 25, 1) AddUses(-1) return 1 // Negative numbers will subtract /obj/item/device/lightreplacer/proc/AddUses(amount = 1) + playsound(src, 'sound/machines/click.ogg', 25, 1) uses = min(max(uses + amount, 0), max_uses) /obj/item/device/lightreplacer/proc/Charge(mob/user) @@ -120,6 +128,16 @@ AddUses(1) charge = 1 +/obj/item/device/lightreplacer/proc/Recycle(mob/living/U) + if(recycle == max_recycle) + recycle = 0 + AddUses(1) + playsound(src, 'sound/machines/ding.ogg', 5, 1) + return + else + playsound(src, 'sound/machines/click.ogg', 25, 1) + recycle += 1 + /obj/item/device/lightreplacer/proc/ReplaceLight(obj/structure/machinery/light/target, mob/living/U) if(target.status != LIGHT_OK) @@ -130,33 +148,28 @@ if(target.status != LIGHT_EMPTY) - var/obj/item/light_bulb/L1 = new target.light_type(target.loc) - L1.status = target.status - L1.rigged = target.rigged - L1.brightness = target.brightness - L1.switchcount = target.switchcount target.switchcount = 0 - L1.update() - target.status = LIGHT_EMPTY target.update() - var/obj/item/light_bulb/L2 = new target.light_type() + Recycle() + + var/obj/item/light_bulb/bulb = new target.light_type() - target.status = L2.status - target.switchcount = L2.switchcount + target.status = bulb.status + target.switchcount = bulb.switchcount target.rigged = FALSE - target.brightness = L2.brightness + target.brightness = bulb.brightness target.on = target.has_power() target.update() - qdel(L2) + qdel(bulb) if(target.on && target.rigged) target.explode() return else - to_chat(U, failmsg) + to_chat(U, SPAN_DANGER(failmsg)) return else to_chat(U, "There is a working [target.fitting] already inserted.") diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 8e3435aebf28..a610310f6fc1 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -783,7 +783,7 @@ /obj/item/light_bulb/proc/shatter() if(status == LIGHT_OK || status == LIGHT_BURNED) - src.visible_message(SPAN_DANGER("[name] shatters."),SPAN_DANGER("You hear a small glass object shatter.")) + visible_message(SPAN_DANGER("[src] shatters."), SPAN_DANGER("You hear a small glass object shatter.")) status = LIGHT_BROKEN force = 5 sharp = IS_SHARP_ITEM_SIMPLE From a8401b6a1cefcca4fe9998a449628288a0d96461 Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 10:00:16 +0000 Subject: [PATCH 03/71] Automatic changelog for PR #8509 [ci skip] --- html/changelogs/AutoChangeLog-pr-8509.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8509.yml diff --git a/html/changelogs/AutoChangeLog-pr-8509.yml b/html/changelogs/AutoChangeLog-pr-8509.yml new file mode 100644 index 000000000000..6946f52878e0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8509.yml @@ -0,0 +1,4 @@ +author: "TheKillfish" +delete-after: True +changes: + - rscadd: "Added the Light Replacer to tool storage and to the autolathe. The Light Replacer can now also recycle broken bulbs, either directly when replacing or when inserted by hand." \ No newline at end of file From 7e9e09ce1c7afd8c68570e18e33f36ce6229b6eb Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 01:42:07 +0000 Subject: [PATCH 04/71] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-8509.yml | 4 ---- html/changelogs/archive/2025-03.yml | 5 +++++ 2 files changed, 5 insertions(+), 4 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-8509.yml diff --git a/html/changelogs/AutoChangeLog-pr-8509.yml b/html/changelogs/AutoChangeLog-pr-8509.yml deleted file mode 100644 index 6946f52878e0..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8509.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "TheKillfish" -delete-after: True -changes: - - rscadd: "Added the Light Replacer to tool storage and to the autolathe. The Light Replacer can now also recycle broken bulbs, either directly when replacing or when inserted by hand." \ No newline at end of file diff --git a/html/changelogs/archive/2025-03.yml b/html/changelogs/archive/2025-03.yml index 758115d8abda..000ccdbb5d57 100644 --- a/html/changelogs/archive/2025-03.yml +++ b/html/changelogs/archive/2025-03.yml @@ -73,3 +73,8 @@ - admin: Create-Xenos range defaults to 0, instead of 1, hooray! remindme: - bugfix: Some MREs that should've had veggies in them had no veggies (ripoff!) +2025-03-06: + TheKillfish: + - rscadd: Added the Light Replacer to tool storage and to the autolathe. The Light + Replacer can now also recycle broken bulbs, either directly when replacing or + when inserted by hand. From 79c35e71641f7f686aa3c997030d0c295ff3c04f Mon Sep 17 00:00:00 2001 From: Mister-moon1 <109922915+Mister-moon1@users.noreply.github.com> Date: Thu, 6 Mar 2025 06:31:49 +0000 Subject: [PATCH 05/71] Reverts shrapnel damage changes from #7581 (#8613) # About the pull request Title # Explain why it's good for the game Changes were untested, over-tuned and a net negative for the game, numerous interaction changes such as all shrapnel from a NON OT claymore can deal upwards of 400 damage under the circumstances of all shrapnel hitting, possibly killing a majority of t2s without a chance to dispute said damage, this among every single other thing that uses shrapnel, breach charges, grenades, etc, make it unfun and quite frankly just stupid to fight. # Testing Photographs and Procedure https://streamable.com/kplvey https://streamable.com/xkt19k # Changelog :cl: balance: reverted all direct shrapnel buffs from #7581 /:cl: --- code/datums/ammo/shrapnel.dm | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/code/datums/ammo/shrapnel.dm b/code/datums/ammo/shrapnel.dm index 02a57202ff53..f98c9723f1b1 100644 --- a/code/datums/ammo/shrapnel.dm +++ b/code/datums/ammo/shrapnel.dm @@ -6,26 +6,24 @@ /datum/ammo/bullet/shrapnel name = "shrapnel" icon_state = "buckshot" + accurate_range_min = 5 flags_ammo_behavior = AMMO_BALLISTIC|AMMO_STOPPED_BY_COVER - accuracy = HIT_ACCURACY_TIER_4 - accurate_range = 7 - max_range = 10 - damage = 30 - penetration = ARMOR_PENETRATION_TIER_2 + accuracy = HIT_ACCURACY_TIER_3 + accurate_range = 32 + max_range = 8 + damage = 25 + damage_var_low = -PROJECTILE_VARIANCE_TIER_6 + damage_var_high = PROJECTILE_VARIANCE_TIER_6 + penetration = ARMOR_PENETRATION_TIER_4 shell_speed = AMMO_SPEED_TIER_2 + shrapnel_chance = 5 /datum/ammo/bullet/shrapnel/on_hit_obj(obj/O, obj/projectile/P) if(istype(O, /obj/structure/barricade)) var/obj/structure/barricade/B = O - B.health -= rand(5, 10) + B.health -= rand(2, 5) B.update_health(1) -/datum/ammo/bullet/shrapnel/on_hit_mob(mob/living/carbon/xeno, obj/projectile/projectile, mob/user) - if(!shrapnel_chance) // no shrapnell , no special effects - return - if(isxeno(xeno)) - xeno.apply_effect(4, SLOW) // multiple hits dont stack they just renew the duration - xeno.apply_armoured_damage(damage * 0.6, ARMOR_BULLET, BRUTE, , penetration) // xenos have a lot of HP /datum/ammo/bullet/shrapnel/breaching/set_bullet_traits() . = ..() @@ -49,7 +47,7 @@ name = ".22 hornet round" icon_state = "hornet_round" flags_ammo_behavior = AMMO_BALLISTIC - damage = 10 + damage = 8 shrapnel_chance = 0 shell_speed = AMMO_SPEED_TIER_3//she fast af boi penetration = ARMOR_PENETRATION_TIER_5 @@ -70,7 +68,7 @@ icon_state = "beanbag" // looks suprisingly a lot like flaming shrapnel chunks flags_ammo_behavior = AMMO_STOPPED_BY_COVER shell_speed = AMMO_SPEED_TIER_1 - damage = 30 + damage = 20 penetration = ARMOR_PENETRATION_TIER_4 /datum/ammo/bullet/shrapnel/incendiary/set_bullet_traits() @@ -95,6 +93,7 @@ /datum/ammo/bullet/shrapnel/metal name = "metal shrapnel" icon_state = "shrapnelshot_bit" + flags_ammo_behavior = AMMO_STOPPED_BY_COVER|AMMO_BALLISTIC shell_speed = AMMO_SPEED_TIER_1 damage = 30 shrapnel_chance = 15 From fceff52011d049682808331c167cdb2174a3f6b8 Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 06:43:34 +0000 Subject: [PATCH 06/71] Automatic changelog for PR #8613 [ci skip] --- html/changelogs/AutoChangeLog-pr-8613.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8613.yml diff --git a/html/changelogs/AutoChangeLog-pr-8613.yml b/html/changelogs/AutoChangeLog-pr-8613.yml new file mode 100644 index 000000000000..67b00a3cdcac --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8613.yml @@ -0,0 +1,4 @@ +author: "Mister-moon1" +delete-after: True +changes: + - balance: "reverted all direct shrapnel buffs from #7581" \ No newline at end of file From 4c11ce1585f90d62bdd3ebff9b734e6dc9083b28 Mon Sep 17 00:00:00 2001 From: Red <47158596+Red-byte3D@users.noreply.github.com> Date: Thu, 6 Mar 2025 23:24:59 +0300 Subject: [PATCH 07/71] Moves crusher powers into its own files and removes one letter vars (#8496) # About the pull request title # Explain why it's good for the game continues the last prs i was waiting for prs to get merged before making this # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: code: Moves all of crusher abilities to its own files, strains to theirs and removes some one letter vars /:cl: --------- Co-authored-by: InsaneRed --- .../abilities/crusher/crusher_powers.dm | 304 ------------------ .../living/carbon/xenomorph/castes/Crusher.dm | 235 ++++++++++++++ .../strains/castes/crusher/charger.dm | 68 ++++ colonialmarines.dme | 1 - 4 files changed, 303 insertions(+), 305 deletions(-) delete mode 100644 code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm deleted file mode 100644 index 3bef23de8dd0..000000000000 --- a/code/modules/mob/living/carbon/xenomorph/abilities/crusher/crusher_powers.dm +++ /dev/null @@ -1,304 +0,0 @@ - -/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects_always() - var/mob/living/carbon/xenomorph/X = owner - if (!istype(X)) - return - - for (var/mob/living/carbon/H in orange(1, get_turf(X))) - if(X.can_not_harm(H)) - continue - - new /datum/effects/xeno_slow(H, X, null, null, 3.5 SECONDS) - to_chat(H, SPAN_XENODANGER("You are slowed as the impact of [X] shakes the ground!")) - -/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects(mob/living/L) - if (!isxeno_human(L)) - return - - var/mob/living/carbon/H = L - if (H.stat == DEAD) - return - - var/mob/living/carbon/xenomorph/X = owner - if (!istype(X)) - return - - X.emote("roar") - L.apply_effect(2, WEAKEN) - X.visible_message(SPAN_XENODANGER("[X] overruns [H], brutally trampling them underfoot!"), SPAN_XENODANGER("We brutalize [H] as we crush them underfoot!")) - - H.apply_armoured_damage(get_xeno_damage_slash(H, direct_hit_damage), ARMOR_MELEE, BRUTE) - X.throw_carbon(H, X.dir, 3) - - H.last_damage_data = create_cause_data(X.caste_type, X) - return - -/datum/action/xeno_action/activable/pounce/crusher_charge/pre_windup_effects() - RegisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE, PROC_REF(check_directional_armor)) - - var/mob/living/carbon/xenomorph/xeno_owner = owner - if(!istype(xeno_owner)) - return - - var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate - if(!istype(crusher_delegate)) - return - - crusher_delegate.is_charging = TRUE - xeno_owner.update_icons() - -/datum/action/xeno_action/activable/pounce/crusher_charge/post_windup_effects(interrupted) - ..() - UnregisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE) - var/mob/living/carbon/xenomorph/xeno_owner = owner - if(!istype(xeno_owner)) - return - - var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate - if(!istype(crusher_delegate)) - return - - addtimer(CALLBACK(src, PROC_REF(undo_charging_icon)), 0.5 SECONDS) // let the icon be here for a bit, it looks cool - -/datum/action/xeno_action/activable/pounce/crusher_charge/proc/undo_charging_icon() - var/mob/living/carbon/xenomorph/xeno_owner = owner - if(!istype(xeno_owner)) - return - - var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate - if(!istype(crusher_delegate)) - return - - crusher_delegate.is_charging = FALSE - xeno_owner.update_icons() - -/datum/action/xeno_action/activable/pounce/crusher_charge/proc/check_directional_armor(mob/living/carbon/xenomorph/X, list/damagedata) - SIGNAL_HANDLER - var/projectile_direction = damagedata["direction"] - if(X.dir & REVERSE_DIR(projectile_direction)) - // During the charge windup, crusher gets an extra 15 directional armor in the direction its charging - damagedata["armor"] += frontal_armor - - -// This ties the pounce/throwing backend into the old collision backend -/mob/living/carbon/xenomorph/crusher/pounced_obj(obj/O) - var/datum/action/xeno_action/activable/pounce/crusher_charge/CCA = get_action(src, /datum/action/xeno_action/activable/pounce/crusher_charge) - if (istype(CCA) && !CCA.action_cooldown_check() && !(O.type in CCA.not_reducing_objects)) - CCA.reduce_cooldown(50) - - gain_plasma(10) - - if (!handle_collision(O)) // Check old backend - obj_launch_collision(O) - -/mob/living/carbon/xenomorph/crusher/pounced_turf(turf/T) - T.ex_act(EXPLOSION_THRESHOLD_VLOW, , create_cause_data(caste_type, src)) - ..(T) - -/datum/action/xeno_action/onclick/crusher_stomp/use_ability(atom/Atom) - var/mob/living/carbon/xenomorph/xeno = owner - - if (!action_cooldown_check()) - return - - if (!xeno.check_state()) - return - - if(!check_and_use_plasma_owner()) - return - - playsound(xeno, 'sound/effects/bang.ogg', 25) - xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!")) - xeno.create_stomp() - - - for (var/mob/living/carbon/targets in orange(distance, xeno)) - - if (targets.stat == DEAD || xeno.can_not_harm(targets)) - continue - - if(targets in get_turf(xeno)) - targets.apply_armoured_damage(get_xeno_damage_slash(targets, damage), ARMOR_MELEE, BRUTE) - - if(targets.mob_size < MOB_SIZE_BIG) - targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN) - - new /datum/effects/xeno_slow(targets, xeno, ttl = get_xeno_stun_duration(targets, effect_duration)) - targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN) - to_chat(targets, SPAN_XENOHIGHDANGER("You are slowed as [xeno] knocks you off balance!")) - - apply_cooldown() - return ..() - -/datum/action/xeno_action/onclick/crusher_stomp/charger/use_ability() - var/mob/living/carbon/xenomorph/Xeno = owner - var/mob/living/carbon/Targeted - if (!istype(Xeno)) - return - - if (!action_cooldown_check()) - return - - if (!Xeno.check_state()) - return - - if (!check_and_use_plasma_owner()) - return - - playsound(get_turf(Xeno), 'sound/effects/bang.ogg', 25, 0) - Xeno.visible_message(SPAN_XENODANGER("[Xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!")) - Xeno.create_stomp() - - for (var/mob/living/carbon/Human in get_turf(Xeno)) // MOBS ONTOP - if (Human.stat == DEAD || Xeno.can_not_harm(Human)) - continue - - new effect_type_base(Human, Xeno, , , get_xeno_stun_duration(Human, effect_duration)) - to_chat(Human, SPAN_XENOHIGHDANGER("You are BRUTALLY crushed and stomped on by [Xeno]!!!")) - shake_camera(Human, 10, 2) - if(Human.mob_size < MOB_SIZE_BIG) - Human.apply_effect(get_xeno_stun_duration(Human, 0.2), WEAKEN) - - Human.apply_armoured_damage(get_xeno_damage_slash(Human, damage), ARMOR_MELEE, BRUTE,"chest", 3) - Human.apply_armoured_damage(15, BRUTE) // random - Human.last_damage_data = create_cause_data(Xeno.caste_type, Xeno) - Human.emote("pain") - Targeted = Human - for (var/mob/living/carbon/Human in orange(distance, get_turf(Xeno))) // MOBS AROUND - if (Human.stat == DEAD || Xeno.can_not_harm(Human)) - continue - if(Human.client) - shake_camera(Human, 10, 2) - if(Targeted) - to_chat(Human, SPAN_XENOHIGHDANGER("You watch as [Targeted] gets crushed by [Xeno]!")) - to_chat(Human, SPAN_XENOHIGHDANGER("You are shaken as [Xeno] quakes the earth!")) - - apply_cooldown() - return ..() - -/datum/action/xeno_action/onclick/crusher_shield/use_ability(atom/Target) - var/mob/living/carbon/xenomorph/xeno = owner - - if (!istype(xeno)) - return - - if (!action_cooldown_check()) - return - - if (!xeno.check_state()) - return - - if (!check_and_use_plasma_owner()) - return - - xeno.visible_message(SPAN_XENOWARNING("[xeno] hunkers down and bolsters its defenses!"), SPAN_XENOHIGHDANGER("We hunker down and bolster our defenses!")) - button.icon_state = "template_active" - - xeno.create_crusher_shield() - - xeno.add_xeno_shield(shield_amount, XENO_SHIELD_SOURCE_CRUSHER, /datum/xeno_shield/crusher) - xeno.overlay_shields() - - xeno.explosivearmor_modifier += 1000 - xeno.recalculate_armor() - - addtimer(CALLBACK(src, PROC_REF(remove_explosion_immunity)), explosion_immunity_dur) - addtimer(CALLBACK(src, PROC_REF(remove_shield)), shield_dur) - - apply_cooldown() - return ..() - -/datum/action/xeno_action/onclick/crusher_shield/proc/remove_explosion_immunity() - var/mob/living/carbon/xenomorph/xeno = owner - if (!istype(xeno)) - return - - xeno.explosivearmor_modifier -= 1000 - xeno.recalculate_armor() - to_chat(xeno, SPAN_XENODANGER("Our immunity to explosion damage ends!")) - -/datum/action/xeno_action/onclick/crusher_shield/proc/remove_shield() - var/mob/living/carbon/xenomorph/xeno = owner - if (!istype(xeno)) - return - - var/datum/xeno_shield/found - for (var/datum/xeno_shield/shield in xeno.xeno_shields) - if (shield.shield_source == XENO_SHIELD_SOURCE_CRUSHER) - found = shield - break - - if (istype(found)) - found.on_removal() - qdel(found) - to_chat(xeno, SPAN_XENOHIGHDANGER("We feel our enhanced shield end!")) - button.icon_state = "template" - - xeno.overlay_shields() - -/datum/action/xeno_action/onclick/charger_charge/use_ability(atom/Target) - var/mob/living/carbon/xenomorph/Xeno = owner - - activated = !activated - var/will_charge = "[activated ? "now" : "no longer"]" - to_chat(Xeno, SPAN_XENONOTICE("We will [will_charge] charge when moving.")) - if(activated) - RegisterSignal(Xeno, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement)) - RegisterSignal(Xeno, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_position_change)) - RegisterSignal(Xeno, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change)) - RegisterSignal(Xeno, COMSIG_XENO_RECALCULATE_SPEED, PROC_REF(update_speed)) - RegisterSignal(Xeno, COMSIG_XENO_STOP_MOMENTUM, PROC_REF(stop_momentum)) - RegisterSignal(Xeno, COMSIG_MOVABLE_ENTERED_RIVER, PROC_REF(handle_river)) - RegisterSignal(Xeno, COMSIG_LIVING_PRE_COLLIDE, PROC_REF(handle_collision)) - RegisterSignal(Xeno, COMSIG_XENO_START_CHARGING, PROC_REF(start_charging)) - button.icon_state = "template_active" - else - stop_momentum() - UnregisterSignal(Xeno, list( - COMSIG_MOVABLE_MOVED, - COMSIG_LIVING_SET_BODY_POSITION, - COMSIG_ATOM_DIR_CHANGE, - COMSIG_XENO_RECALCULATE_SPEED, - COMSIG_MOVABLE_ENTERED_RIVER, - COMSIG_LIVING_PRE_COLLIDE, - COMSIG_XENO_STOP_MOMENTUM, - COMSIG_XENO_START_CHARGING, - )) - button.icon_state = "template" - return ..() - -/datum/action/xeno_action/activable/tumble/use_ability(atom/Target) - if(!action_cooldown_check()) - return - var/mob/living/carbon/xenomorph/Xeno = owner - if (!Xeno.check_state()) - return - if(Xeno.plasma_stored <= plasma_cost) - return - var/target_dist = get_dist(Xeno, Target) - var/dir_between = get_dir(Xeno, Target) - var/target_dir - for(var/perpen_dir in get_perpen_dir(Xeno.dir)) - if(dir_between & perpen_dir) - target_dir = perpen_dir - break - - if(!target_dir) - return - - Xeno.visible_message(SPAN_XENOWARNING("[Xeno] tumbles over to the side!"), SPAN_XENOHIGHDANGER("We tumble over to the side!")) - Xeno.spin(5,1) // note: This spins the sprite and DOES NOT affect directional armor - var/start_charging = HAS_TRAIT(Xeno, TRAIT_CHARGING) - SEND_SIGNAL(Xeno, COMSIG_XENO_STOP_MOMENTUM) - Xeno.flags_atom |= DIRLOCK - playsound(Xeno,"alien_tail_swipe", 50, 1) - - Xeno.use_plasma(plasma_cost) - - var/target = get_step(get_step(Xeno, target_dir), target_dir) - var/list/collision_callbacks = list(/mob/living/carbon/human = CALLBACK(src, PROC_REF(handle_mob_collision))) - var/list/end_throw_callbacks = list(CALLBACK(src, PROC_REF(on_end_throw), start_charging)) - Xeno.throw_atom(target, target_dist, SPEED_FAST, launch_type = LOW_LAUNCH, pass_flags = PASS_CRUSHER_CHARGE, end_throw_callbacks = end_throw_callbacks, collision_callbacks = collision_callbacks) - - apply_cooldown() - return ..() diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm index 97f554fbb8b2..70a723849875 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Crusher.dm @@ -288,3 +288,238 @@ if(bound_xeno.throwing || is_charging) //Let it build up a bit so we're not changing icons every single turf bound_xeno.icon_state = "[bound_xeno.get_strain_icon()] Crusher Charging" return TRUE + +/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects_always() + var/mob/living/carbon/xenomorph/xeno = owner + if (!istype(xeno)) + return + + for (var/mob/living/carbon/target in orange(1, get_turf(xeno))) + if(xeno.can_not_harm(target)) + continue + + new /datum/effects/xeno_slow(target, xeno, null, null, 3.5 SECONDS) + to_chat(target, SPAN_XENODANGER("You are slowed as the impact of [xeno] shakes the ground!")) + +/datum/action/xeno_action/activable/pounce/crusher_charge/additional_effects(mob/living/target) + var/mob/living/carbon/xenomorph/xeno = owner + var/mob/living/carbon/target_to_check = target + if (!isxeno_human(target)) + return + + if (target_to_check.stat == DEAD) + return + + xeno.emote("roar") + target.apply_effect(2, WEAKEN) + xeno.visible_message(SPAN_XENODANGER("[xeno] overruns [target_to_check], brutally trampling them underfoot!"), SPAN_XENODANGER("We brutalize [target_to_check] as we crush them underfoot!")) + + target_to_check.apply_armoured_damage(get_xeno_damage_slash(target_to_check, direct_hit_damage), ARMOR_MELEE, BRUTE) + xeno.throw_carbon(target_to_check, xeno.dir, 3) + + target_to_check.last_damage_data = create_cause_data(xeno.caste_type, xeno) + return + +/datum/action/xeno_action/activable/pounce/crusher_charge/pre_windup_effects() + RegisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE, PROC_REF(check_directional_armor)) + + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(!istype(xeno_owner)) + return + + var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate + if(!istype(crusher_delegate)) + return + + crusher_delegate.is_charging = TRUE + xeno_owner.update_icons() + +/datum/action/xeno_action/activable/pounce/crusher_charge/post_windup_effects(interrupted) + ..() + UnregisterSignal(owner, COMSIG_XENO_PRE_CALCULATE_ARMOURED_DAMAGE_PROJECTILE) + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(!istype(xeno_owner)) + return + + var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate + if(!istype(crusher_delegate)) + return + + addtimer(CALLBACK(src, PROC_REF(undo_charging_icon)), 0.5 SECONDS) // let the icon be here for a bit, it looks cool + +/datum/action/xeno_action/activable/pounce/crusher_charge/proc/undo_charging_icon() + var/mob/living/carbon/xenomorph/xeno_owner = owner + if(!istype(xeno_owner)) + return + + var/datum/behavior_delegate/crusher_base/crusher_delegate = xeno_owner.behavior_delegate + if(!istype(crusher_delegate)) + return + + crusher_delegate.is_charging = FALSE + xeno_owner.update_icons() + +/datum/action/xeno_action/activable/pounce/crusher_charge/proc/check_directional_armor(mob/living/carbon/xenomorph/X, list/damagedata) + SIGNAL_HANDLER + var/projectile_direction = damagedata["direction"] + if(X.dir & REVERSE_DIR(projectile_direction)) + // During the charge windup, crusher gets an extra 15 directional armor in the direction its charging + damagedata["armor"] += frontal_armor + + +// This ties the pounce/throwing backend into the old collision backend +/mob/living/carbon/xenomorph/crusher/pounced_obj(obj/pounced_object) + var/datum/action/xeno_action/activable/pounce/crusher_charge/crusher_charge_action = get_action(src, /datum/action/xeno_action/activable/pounce/crusher_charge) + if (istype(crusher_charge_action) && !crusher_charge_action.action_cooldown_check() && !(pounced_object.type in crusher_charge_action.not_reducing_objects)) + crusher_charge_action.reduce_cooldown(50) + + gain_plasma(10) + + if (!handle_collision(pounced_object)) // Check old backend + obj_launch_collision(pounced_object) + +/mob/living/carbon/xenomorph/crusher/pounced_turf(turf/pounced_turf) + pounced_turf.ex_act(EXPLOSION_THRESHOLD_VLOW, , create_cause_data(caste_type, src)) + ..(pounced_turf) + +/datum/action/xeno_action/onclick/crusher_stomp/use_ability(atom/Atom) + var/mob/living/carbon/xenomorph/xeno = owner + + if (!action_cooldown_check()) + return + + if (!xeno.check_state()) + return + + if(!check_and_use_plasma_owner()) + return + + playsound(xeno, 'sound/effects/bang.ogg', 25) + xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!")) + xeno.create_stomp() + + + for (var/mob/living/carbon/targets in orange(distance, xeno)) + + if (targets.stat == DEAD || xeno.can_not_harm(targets)) + continue + + if(targets in get_turf(xeno)) + targets.apply_armoured_damage(get_xeno_damage_slash(targets, damage), ARMOR_MELEE, BRUTE) + + if(targets.mob_size < MOB_SIZE_BIG) + targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN) + + new /datum/effects/xeno_slow(targets, xeno, ttl = get_xeno_stun_duration(targets, effect_duration)) + targets.apply_effect(get_xeno_stun_duration(targets, 0.2), WEAKEN) + to_chat(targets, SPAN_XENOHIGHDANGER("You are slowed as [xeno] knocks you off balance!")) + + apply_cooldown() + return ..() + +/datum/action/xeno_action/onclick/crusher_stomp/charger/use_ability() + var/mob/living/carbon/xenomorph/xeno = owner + var/mob/living/carbon/stomped_carbon + + if (!istype(xeno)) + return + + if (!action_cooldown_check()) + return + + if (!xeno.check_state()) + return + + if (!check_and_use_plasma_owner()) + return + + playsound(get_turf(xeno), 'sound/effects/bang.ogg', 25, 0) + xeno.visible_message(SPAN_XENODANGER("[xeno] smashes into the ground!"), SPAN_XENODANGER("We smash into the ground!")) + xeno.create_stomp() + + for (var/mob/living/carbon/target_to_stomp in get_turf(xeno)) // MOBS ONTOP + if (target_to_stomp.stat == DEAD || xeno.can_not_harm(target_to_stomp)) + continue + + new effect_type_base(target_to_stomp, xeno, null, null, get_xeno_stun_duration(target_to_stomp, effect_duration)) + to_chat(target_to_stomp, SPAN_XENOHIGHDANGER("You are BRUTALLY crushed and stomped on by [xeno]!!!")) + shake_camera(target_to_stomp, 10, 2) + if(target_to_stomp.mob_size < MOB_SIZE_BIG) + target_to_stomp.apply_effect(get_xeno_stun_duration(target_to_stomp, 0.2), WEAKEN) + + target_to_stomp.apply_armoured_damage(get_xeno_damage_slash(target_to_stomp, damage), ARMOR_MELEE, BRUTE,"chest", 3) + target_to_stomp.apply_armoured_damage(15, BRUTE) // random + target_to_stomp.last_damage_data = create_cause_data(xeno.caste_type, xeno) + target_to_stomp.emote("pain") + target_to_stomp = stomped_carbon + for (var/mob/living/carbon/targets_to_get in orange(distance, get_turf(xeno))) // MOBS AROUND + if (targets_to_get.stat == DEAD || xeno.can_not_harm(targets_to_get)) + continue + if(targets_to_get.client) + shake_camera(targets_to_get, 10, 2) + if(stomped_carbon) + to_chat(targets_to_get, SPAN_XENOHIGHDANGER("You watch as [stomped_carbon] gets crushed by [xeno]!")) + to_chat(targets_to_get, SPAN_XENOHIGHDANGER("You are shaken as [xeno] quakes the earth!")) + + apply_cooldown() + return ..() + +/datum/action/xeno_action/onclick/crusher_shield/use_ability(atom/Target) + var/mob/living/carbon/xenomorph/xeno = owner + + if (!istype(xeno)) + return + + if (!action_cooldown_check()) + return + + if (!xeno.check_state()) + return + + if (!check_and_use_plasma_owner()) + return + + xeno.visible_message(SPAN_XENOWARNING("[xeno] hunkers down and bolsters its defenses!"), SPAN_XENOHIGHDANGER("We hunker down and bolster our defenses!")) + button.icon_state = "template_active" + + xeno.create_crusher_shield() + + xeno.add_xeno_shield(shield_amount, XENO_SHIELD_SOURCE_CRUSHER, /datum/xeno_shield/crusher) + xeno.overlay_shields() + + xeno.explosivearmor_modifier += 1000 + xeno.recalculate_armor() + + addtimer(CALLBACK(src, PROC_REF(remove_explosion_immunity)), explosion_immunity_dur) + addtimer(CALLBACK(src, PROC_REF(remove_shield)), shield_dur) + + apply_cooldown() + return ..() + +/datum/action/xeno_action/onclick/crusher_shield/proc/remove_explosion_immunity() + var/mob/living/carbon/xenomorph/xeno = owner + if (!istype(xeno)) + return + + xeno.explosivearmor_modifier -= 1000 + xeno.recalculate_armor() + to_chat(xeno, SPAN_XENODANGER("Our immunity to explosion damage ends!")) + +/datum/action/xeno_action/onclick/crusher_shield/proc/remove_shield() + var/mob/living/carbon/xenomorph/xeno = owner + if (!istype(xeno)) + return + + var/datum/xeno_shield/found + for (var/datum/xeno_shield/shield in xeno.xeno_shields) + if (shield.shield_source == XENO_SHIELD_SOURCE_CRUSHER) + found = shield + break + + if (istype(found)) + found.on_removal() + qdel(found) + to_chat(xeno, SPAN_XENOHIGHDANGER("We feel our enhanced shield end!")) + button.icon_state = "template" + + xeno.overlay_shields() diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm index 7eebd3c6ec90..02c659ad06c3 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/crusher/charger.dm @@ -652,6 +652,74 @@ charger_ability.stop_momentum() +/datum/action/xeno_action/onclick/charger_charge/use_ability(atom/Target) + var/mob/living/carbon/xenomorph/Xeno = owner + + activated = !activated + var/will_charge = "[activated ? "now" : "no longer"]" + to_chat(Xeno, SPAN_XENONOTICE("We will [will_charge] charge when moving.")) + if(activated) + RegisterSignal(Xeno, COMSIG_MOVABLE_MOVED, PROC_REF(handle_movement)) + RegisterSignal(Xeno, COMSIG_LIVING_SET_BODY_POSITION, PROC_REF(handle_position_change)) + RegisterSignal(Xeno, COMSIG_ATOM_DIR_CHANGE, PROC_REF(handle_dir_change)) + RegisterSignal(Xeno, COMSIG_XENO_RECALCULATE_SPEED, PROC_REF(update_speed)) + RegisterSignal(Xeno, COMSIG_XENO_STOP_MOMENTUM, PROC_REF(stop_momentum)) + RegisterSignal(Xeno, COMSIG_MOVABLE_ENTERED_RIVER, PROC_REF(handle_river)) + RegisterSignal(Xeno, COMSIG_LIVING_PRE_COLLIDE, PROC_REF(handle_collision)) + RegisterSignal(Xeno, COMSIG_XENO_START_CHARGING, PROC_REF(start_charging)) + button.icon_state = "template_active" + else + stop_momentum() + UnregisterSignal(Xeno, list( + COMSIG_MOVABLE_MOVED, + COMSIG_LIVING_SET_BODY_POSITION, + COMSIG_ATOM_DIR_CHANGE, + COMSIG_XENO_RECALCULATE_SPEED, + COMSIG_MOVABLE_ENTERED_RIVER, + COMSIG_LIVING_PRE_COLLIDE, + COMSIG_XENO_STOP_MOMENTUM, + COMSIG_XENO_START_CHARGING, + )) + button.icon_state = "template" + return ..() + +/datum/action/xeno_action/activable/tumble/use_ability(atom/Target) + if(!action_cooldown_check()) + return + var/mob/living/carbon/xenomorph/Xeno = owner + if (!Xeno.check_state()) + return + if(Xeno.plasma_stored <= plasma_cost) + return + var/target_dist = get_dist(Xeno, Target) + var/dir_between = get_dir(Xeno, Target) + var/target_dir + for(var/perpen_dir in get_perpen_dir(Xeno.dir)) + if(dir_between & perpen_dir) + target_dir = perpen_dir + break + + if(!target_dir) + return + + Xeno.visible_message(SPAN_XENOWARNING("[Xeno] tumbles over to the side!"), SPAN_XENOHIGHDANGER("We tumble over to the side!")) + Xeno.spin(5,1) // note: This spins the sprite and DOES NOT affect directional armor + var/start_charging = HAS_TRAIT(Xeno, TRAIT_CHARGING) + SEND_SIGNAL(Xeno, COMSIG_XENO_STOP_MOMENTUM) + Xeno.flags_atom |= DIRLOCK + playsound(Xeno,"alien_tail_swipe", 50, 1) + + Xeno.use_plasma(plasma_cost) + + var/target = get_step(get_step(Xeno, target_dir), target_dir) + var/list/collision_callbacks = list(/mob/living/carbon/human = CALLBACK(src, PROC_REF(handle_mob_collision))) + var/list/end_throw_callbacks = list(CALLBACK(src, PROC_REF(on_end_throw), start_charging)) + Xeno.throw_atom(target, target_dist, SPEED_FAST, launch_type = LOW_LAUNCH, pass_flags = PASS_CRUSHER_CHARGE, end_throw_callbacks = end_throw_callbacks, collision_callbacks = collision_callbacks) + + apply_cooldown() + return ..() + + #undef CHARGER_DESTROY #undef CHARGER_DAMAGE_CADE diff --git a/colonialmarines.dme b/colonialmarines.dme index 9305cf0fe0fe..e30e6ab17aa6 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -2114,7 +2114,6 @@ #include "code\modules\mob\living\carbon\xenomorph\abilities\carrier\carrier_macros.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\crusher\crusher_abilities.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\crusher\crusher_macros.dm" -#include "code\modules\mob\living\carbon\xenomorph\abilities\crusher\crusher_powers.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\defender\defender_abilities.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\defender\defender_macros.dm" #include "code\modules\mob\living\carbon\xenomorph\abilities\facehugger\facehugger_abilities.dm" From c20d21622048d99106e10ed40c4bc2cb1a2ebd56 Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Thu, 6 Mar 2025 20:40:32 +0000 Subject: [PATCH 08/71] Automatic changelog for PR #8496 [ci skip] --- html/changelogs/AutoChangeLog-pr-8496.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-8496.yml diff --git a/html/changelogs/AutoChangeLog-pr-8496.yml b/html/changelogs/AutoChangeLog-pr-8496.yml new file mode 100644 index 000000000000..fcffe1cfe1ba --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-8496.yml @@ -0,0 +1,4 @@ +author: "Red-byte3D" +delete-after: True +changes: + - code_imp: "Moves all of crusher abilities to its own files, strains to theirs and removes some one letter vars" \ No newline at end of file From 1a1c3dec3c4995936a25bea017d5ab41f2bf2e6b Mon Sep 17 00:00:00 2001 From: "cmss13-ci[bot]" <180991813+cmss13-ci[bot]@users.noreply.github.com> Date: Fri, 7 Mar 2025 01:41:28 +0000 Subject: [PATCH 09/71] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-8496.yml | 4 ---- html/changelogs/AutoChangeLog-pr-8613.yml | 4 ---- html/changelogs/archive/2025-03.yml | 6 ++++++ 3 files changed, 6 insertions(+), 8 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-8496.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-8613.yml diff --git a/html/changelogs/AutoChangeLog-pr-8496.yml b/html/changelogs/AutoChangeLog-pr-8496.yml deleted file mode 100644 index fcffe1cfe1ba..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8496.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Red-byte3D" -delete-after: True -changes: - - code_imp: "Moves all of crusher abilities to its own files, strains to theirs and removes some one letter vars" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-8613.yml b/html/changelogs/AutoChangeLog-pr-8613.yml deleted file mode 100644 index 67b00a3cdcac..000000000000 --- a/html/changelogs/AutoChangeLog-pr-8613.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Mister-moon1" -delete-after: True -changes: - - balance: "reverted all direct shrapnel buffs from #7581" \ No newline at end of file diff --git a/html/changelogs/archive/2025-03.yml b/html/changelogs/archive/2025-03.yml index 000ccdbb5d57..30d7a79aef59 100644 --- a/html/changelogs/archive/2025-03.yml +++ b/html/changelogs/archive/2025-03.yml @@ -78,3 +78,9 @@ - rscadd: Added the Light Replacer to tool storage and to the autolathe. The Light Replacer can now also recycle broken bulbs, either directly when replacing or when inserted by hand. +2025-03-07: + Mister-moon1: + - balance: 'reverted all direct shrapnel buffs from #7581' + Red-byte3D: + - code_imp: Moves all of crusher abilities to its own files, strains to theirs and + removes some one letter vars From e8fdb8415ed4570f04ad55fcb764503576a1f087 Mon Sep 17 00:00:00 2001 From: Drathek <76988376+Drulikar@users.noreply.github.com> Date: Fri, 7 Mar 2025 17:35:28 -0600 Subject: [PATCH 10/71] [s] Adds a wrapper for icon_states for debugging (#8681) # About the pull request This PR simply adds a wrapper for icon_states so it can be called via adv proccall. # Explain why it's good for the game Debugging! # Changelog No player facing changes. --- code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm index 64e6bb85bed6..a99d27faf0d8 100644 --- a/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm +++ b/code/modules/admin/verbs/SDQL2/SDQL_2_wrappers.dm @@ -266,6 +266,13 @@ /proc/_turn(dir, angle) return turn(dir, angle) +/proc/_icon_states(icon/thing, mode) + if(istype(thing)) + return icon_states(thing, mode) + if(isatom(thing)) + var/atom/atom_thing = thing + return icon_states(atom_thing.icon, mode) + /// Auxtools REALLY doesn't know how to handle filters as values; /// when passed as arguments to auxtools-called procs, they aren't simply treated as nulls - /// they don't even count towards the length of args. From a0a76e24caa421599d9ce45759b8aa226b2ce835 Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Sat, 8 Mar 2025 02:15:50 +0100 Subject: [PATCH 11/71] some Tgui fixes (#8652) # About the pull request There's a rare case where the chat might not init, this also adds a second check if telemetry was received and if not, re-requests it once. Fixing some tgui issues. recommending to TM. Brings the chat closer in sync with https://github.com/tgstation/tgstation again, some of the other changes that exist on TG are likely better for you to check on your own, especially the option to link / unlink font sizes, etc. # Explain why it's good for the game # Testing Photographs and Procedure
Screenshots & Videos Put screenshots and videos here with an empty line between the screenshots and the `
` tags.
# Changelog :cl: fix: numberInput works properly again with mouse movement fix: the chat will no longer init before settings are loaded qol: chat settings can be in / exported qol: chat tabs can be reordered /:cl: --- code/modules/tgui_panel/tgui_panel.dm | 1 + tgui/.eslintrc.yml | 2 +- tgui/package.json | 1 + tgui/packages/common/keys.ts | 16 + tgui/packages/common/perf.ts | 1 - tgui/packages/common/string.ts | 1 - tgui/packages/common/type-utils.ts | 1 - tgui/packages/common/uuid.ts | 1 - ...tPageSettings.jsx => ChatPageSettings.tsx} | 76 +++- tgui/packages/tgui-panel/chat/actions.js | 2 + tgui/packages/tgui-panel/chat/middleware.js | 30 +- tgui/packages/tgui-panel/chat/model.js | 2 + .../chat/{reducer.js => reducer.ts} | 69 ++++ tgui/packages/tgui-panel/chat/types.ts | 9 + .../tgui-panel/ping/PingIndicator.tsx | 27 +- .../settings/SettingTabs/SettingsGeneral.tsx | 192 ++++++++++ .../SettingTabs/SettingsStatPanel.tsx | 84 +++++ .../settings/SettingTabs/TextHighlight.tsx | 177 +++++++++ .../tgui-panel/settings/SettingsPanel.jsx | 356 ------------------ .../tgui-panel/settings/SettingsPanel.tsx | 50 +++ tgui/packages/tgui-panel/settings/actions.ts | 7 + .../tgui-panel/settings/middleware.js | 73 ---- .../tgui-panel/settings/middleware.ts | 151 ++++++++ tgui/packages/tgui-panel/settings/reducer.js | 184 --------- tgui/packages/tgui-panel/settings/reducer.ts | 230 +++++++++++ .../tgui-panel/settings/settingsImExport.ts | 55 +++ .../tgui-panel/styles/components/Ping.scss | 4 + tgui/packages/tgui-panel/telemetry.js | 8 + tgui/packages/tgui/components/Autofocus.tsx | 1 - tgui/packages/tgui/components/BlockQuote.tsx | 1 - tgui/packages/tgui/components/Button.tsx | 1 - tgui/packages/tgui/components/Collapsible.tsx | 1 - tgui/packages/tgui/components/ColorBox.tsx | 1 - tgui/packages/tgui/components/Dimmer.tsx | 1 - tgui/packages/tgui/components/Divider.tsx | 1 - tgui/packages/tgui/components/DmIcon.tsx | 1 - tgui/packages/tgui/components/Dropdown.tsx | 1 - tgui/packages/tgui/components/Image.tsx | 1 - tgui/packages/tgui/components/Input.tsx | 1 - tgui/packages/tgui/components/Knob.tsx | 1 - .../tgui/components/LabeledControls.tsx | 1 - tgui/packages/tgui/components/Modal.tsx | 1 - tgui/packages/tgui/components/NoticeBox.tsx | 1 - tgui/packages/tgui/components/NumberInput.tsx | 188 ++++----- tgui/packages/tgui/components/Popper.tsx | 1 - tgui/packages/tgui/components/RoundGauge.tsx | 1 - tgui/packages/tgui/components/Slider.tsx | 1 - tgui/packages/tgui/components/Stack.tsx | 1 - tgui/packages/tgui/components/Table.tsx | 1 - tgui/packages/tgui/interfaces/AlertModal.tsx | 1 - tgui/packages/tgui/interfaces/CardMod.jsx | 1 + .../interfaces/LootPanel/GroupedContents.tsx | 1 - .../tgui/interfaces/LootPanel/IconDisplay.tsx | 1 - .../tgui/interfaces/LootPanel/LootBox.tsx | 1 - .../tgui/interfaces/LootPanel/RawContents.tsx | 1 - .../tgui/interfaces/LootPanel/index.tsx | 1 - .../tgui/interfaces/OverwatchConsole.jsx | 6 + tgui/packages/tgui/interfaces/PredPicker.tsx | 1 + tgui/packages/tgui/layouts/Layout.tsx | 1 - tgui/packages/tgui/layouts/Pane.tsx | 1 - tgui/yarn.lock | 8 + 61 files changed, 1244 insertions(+), 798 deletions(-) rename tgui/packages/tgui-panel/chat/{ChatPageSettings.jsx => ChatPageSettings.tsx} (64%) rename tgui/packages/tgui-panel/chat/{reducer.js => reducer.ts} (72%) create mode 100644 tgui/packages/tgui-panel/chat/types.ts create mode 100644 tgui/packages/tgui-panel/settings/SettingTabs/SettingsGeneral.tsx create mode 100644 tgui/packages/tgui-panel/settings/SettingTabs/SettingsStatPanel.tsx create mode 100644 tgui/packages/tgui-panel/settings/SettingTabs/TextHighlight.tsx delete mode 100644 tgui/packages/tgui-panel/settings/SettingsPanel.jsx create mode 100644 tgui/packages/tgui-panel/settings/SettingsPanel.tsx delete mode 100644 tgui/packages/tgui-panel/settings/middleware.js create mode 100644 tgui/packages/tgui-panel/settings/middleware.ts delete mode 100644 tgui/packages/tgui-panel/settings/reducer.js create mode 100644 tgui/packages/tgui-panel/settings/reducer.ts create mode 100644 tgui/packages/tgui-panel/settings/settingsImExport.ts diff --git a/code/modules/tgui_panel/tgui_panel.dm b/code/modules/tgui_panel/tgui_panel.dm index 69a352d76fb3..3124149b6a07 100644 --- a/code/modules/tgui_panel/tgui_panel.dm +++ b/code/modules/tgui_panel/tgui_panel.dm @@ -53,6 +53,7 @@ // Other setup request_telemetry() addtimer(CALLBACK(src, PROC_REF(on_initialize_timed_out)), 5 SECONDS) + window.send_message("testTelemetryCommand") /** * private diff --git a/tgui/.eslintrc.yml b/tgui/.eslintrc.yml index 4ff25c1f7fe4..8a6e605e48ea 100644 --- a/tgui/.eslintrc.yml +++ b/tgui/.eslintrc.yml @@ -338,7 +338,7 @@ rules: ## Require or disallow named function expressions # func-names: error ## Enforce the consistent use of either function declarations or expressions - func-style: [error, expression] + # func-style: [error, expression] ## Enforce line breaks between arguments of a function call # function-call-argument-newline: error ## Enforce consistent line breaks inside function parentheses diff --git a/tgui/package.json b/tgui/package.json index 624b43ad7cc5..21139aebc861 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -27,6 +27,7 @@ "@types/jest": "^29.5.12", "@types/node": "^20.12.3", "@types/webpack-env": "^1.18.4", + "@types/wicg-file-system-access": "^2023.10.5", "@typescript-eslint/parser": "^7.5.0", "@typescript-eslint/utils": "^7.5.0", "css-loader": "^6.10.0", diff --git a/tgui/packages/common/keys.ts b/tgui/packages/common/keys.ts index 34ac9e1614dd..812bbedc6832 100644 --- a/tgui/packages/common/keys.ts +++ b/tgui/packages/common/keys.ts @@ -25,6 +25,7 @@ export enum KEY { Down = 'ArrowDown', End = 'End', Enter = 'Enter', + Esc = 'Esc', Escape = 'Escape', Home = 'Home', Insert = 'Insert', @@ -37,3 +38,18 @@ export enum KEY { Tab = 'Tab', Up = 'ArrowUp', } + +/** + * ### isEscape + * + * Checks if the user has hit the 'ESC' key on their keyboard. + * There's a weirdness in BYOND where this could be either the string + * 'Escape' or 'Esc' depending on the browser. This function handles + * both cases. + * + * @param key - the key to check, typically from event.key + * @returns true if key is Escape or Esc, false otherwise + */ +export function isEscape(key: string): boolean { + return key === KEY.Esc || key === KEY.Escape; +} diff --git a/tgui/packages/common/perf.ts b/tgui/packages/common/perf.ts index 3e8ac2ab050c..9266d88a896a 100644 --- a/tgui/packages/common/perf.ts +++ b/tgui/packages/common/perf.ts @@ -1,4 +1,3 @@ -/* eslint-disable func-style */ /** * Ghetto performance measurement tools. * diff --git a/tgui/packages/common/string.ts b/tgui/packages/common/string.ts index 4c93d155e662..d6f328750c42 100644 --- a/tgui/packages/common/string.ts +++ b/tgui/packages/common/string.ts @@ -1,4 +1,3 @@ -/* eslint-disable func-style */ /** * @file * @copyright 2020 Aleksej Komarov diff --git a/tgui/packages/common/type-utils.ts b/tgui/packages/common/type-utils.ts index e6d3fa5cbd4a..a73c0c1d5956 100644 --- a/tgui/packages/common/type-utils.ts +++ b/tgui/packages/common/type-utils.ts @@ -1,4 +1,3 @@ -/* eslint-disable func-style */ /** * Helps visualize highly complex ui data on the fly. * @example diff --git a/tgui/packages/common/uuid.ts b/tgui/packages/common/uuid.ts index 02dab15dd43b..250809ab6a7d 100644 --- a/tgui/packages/common/uuid.ts +++ b/tgui/packages/common/uuid.ts @@ -1,4 +1,3 @@ -/* eslint-disable func-style */ /** * @file * @copyright 2020 Aleksej Komarov diff --git a/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx b/tgui/packages/tgui-panel/chat/ChatPageSettings.tsx similarity index 64% rename from tgui/packages/tgui-panel/chat/ChatPageSettings.jsx rename to tgui/packages/tgui-panel/chat/ChatPageSettings.tsx index 51c08c05a364..fb36ec150154 100644 --- a/tgui/packages/tgui-panel/chat/ChatPageSettings.jsx +++ b/tgui/packages/tgui-panel/chat/ChatPageSettings.tsx @@ -14,7 +14,13 @@ import { Stack, } from 'tgui/components'; -import { removeChatPage, toggleAcceptedType, updateChatPage } from './actions'; +import { + moveChatPageLeft, + moveChatPageRight, + removeChatPage, + toggleAcceptedType, + updateChatPage, +} from './actions'; import { MESSAGE_TYPES } from './constants'; import { selectCurrentChatPage } from './selectors'; @@ -24,7 +30,23 @@ export const ChatPageSettings = (props) => { return (
- + {!page.isMain && ( + + - + {!page.isMain && ( + + + + )} -
+
{MESSAGE_TYPES.filter( (typeDef) => !typeDef.important && !typeDef.admin, ).map((typeDef) => ( diff --git a/tgui/packages/tgui-panel/chat/actions.js b/tgui/packages/tgui-panel/chat/actions.js index 52582a4e299c..0b6a73f659a7 100644 --- a/tgui/packages/tgui-panel/chat/actions.js +++ b/tgui/packages/tgui-panel/chat/actions.js @@ -21,3 +21,5 @@ export const toggleAcceptedType = createAction('chat/toggleAcceptedType'); export const removeChatPage = createAction('chat/removePage'); export const changeScrollTracking = createAction('chat/changeScrollTracking'); export const saveChatToDisk = createAction('chat/saveToDisk'); +export const moveChatPageLeft = createAction('chat/movePageLeft'); +export const moveChatPageRight = createAction('chat/movePageRight'); diff --git a/tgui/packages/tgui-panel/chat/middleware.js b/tgui/packages/tgui-panel/chat/middleware.js index 512b78532b1a..0bb731fa9227 100644 --- a/tgui/packages/tgui-panel/chat/middleware.js +++ b/tgui/packages/tgui-panel/chat/middleware.js @@ -9,6 +9,7 @@ import DOMPurify from 'dompurify'; import { addHighlightSetting, + importSettings, loadSettings, removeHighlightSetting, updateHighlightSetting, @@ -21,6 +22,8 @@ import { changeScrollTracking, clearChat, loadChat, + moveChatPageLeft, + moveChatPageRight, rebuildChat, removeChatPage, saveChatToDisk, @@ -98,12 +101,14 @@ export const chatMiddleware = (store) => { chatRenderer.events.on('scrollTrackingChanged', (scrollTracking) => { store.dispatch(changeScrollTracking(scrollTracking)); }); - setInterval(() => { - saveChatToStorage(store); - }, MESSAGE_SAVE_INTERVAL); return (next) => (action) => { const { type, payload } = action; - if (!initialized) { + const settings = selectSettings(store.getState()); + // Load the chat once settings are loaded + if (!initialized && settings.initialized) { + setInterval(() => { + saveChatToStorage(store); + }, MESSAGE_SAVE_INTERVAL); initialized = true; loadChatFromStorage(store); } @@ -111,13 +116,15 @@ export const chatMiddleware = (store) => { let payload_obj; try { payload_obj = JSON.parse(payload); - } catch { + } catch (err) { return; } + const sequence = payload_obj.sequence; if (sequences.includes(sequence)) { return; } + const sequence_count = sequences.length; seq_check: if (sequence_count > 0) { if (sequences_requested.includes(sequence)) { @@ -156,7 +163,9 @@ export const chatMiddleware = (store) => { type === changeChatPage.type || type === addChatPage.type || type === removeChatPage.type || - type === toggleAcceptedType.type + type === toggleAcceptedType.type || + type === moveChatPageLeft.type || + type === moveChatPageRight.type ) { next(action); const page = selectCurrentChatPage(store.getState()); @@ -173,13 +182,14 @@ export const chatMiddleware = (store) => { type === loadSettings.type || type === addHighlightSetting.type || type === removeHighlightSetting.type || - type === updateHighlightSetting.type + type === updateHighlightSetting.type || + type === importSettings.type ) { next(action); - const settings = selectSettings(store.getState()); + const nextSettings = selectSettings(store.getState()); chatRenderer.setHighlight( - settings.highlightSettings, - settings.highlightSettingById, + nextSettings.highlightSettings, + nextSettings.highlightSettingById, ); return; diff --git a/tgui/packages/tgui-panel/chat/model.js b/tgui/packages/tgui-panel/chat/model.js index 6e10a3d02eef..ca2c585d2ec8 100644 --- a/tgui/packages/tgui-panel/chat/model.js +++ b/tgui/packages/tgui-panel/chat/model.js @@ -19,6 +19,7 @@ export const createPage = (obj) => { } return { + isMain: false, id: createUuid(), name: 'New Tab', acceptedTypes: acceptedTypes, @@ -35,6 +36,7 @@ export const createMainPage = () => { acceptedTypes[typeDef.type] = true; } return createPage({ + isMain: true, name: 'Main', acceptedTypes, }); diff --git a/tgui/packages/tgui-panel/chat/reducer.js b/tgui/packages/tgui-panel/chat/reducer.ts similarity index 72% rename from tgui/packages/tgui-panel/chat/reducer.js rename to tgui/packages/tgui-panel/chat/reducer.ts index 7a7681ada741..dda8f860dd2b 100644 --- a/tgui/packages/tgui-panel/chat/reducer.js +++ b/tgui/packages/tgui-panel/chat/reducer.ts @@ -4,17 +4,21 @@ * @license MIT */ +import { importSettings } from '../settings/actions'; import { addChatPage, changeChatPage, changeScrollTracking, loadChat, + moveChatPageLeft, + moveChatPageRight, removeChatPage, toggleAcceptedType, updateChatPage, updateMessageCount, } from './actions'; import { canPageAcceptType, createMainPage } from './model'; +import { Page } from './types'; const mainPage = createMainPage(); @@ -125,6 +129,24 @@ export const chatReducer = (state = initialState, action) => { }, }; } + if (type === importSettings.type) { + const pagesById: Record[] = payload.newPages; + if (!pagesById) { + return state; + } + const newPageIds: string[] = Object.keys(pagesById); + if (!newPageIds) { + return state; + } + + const nextState = { + ...state, + currentPageId: newPageIds[0], + pages: [...newPageIds], + pageById: { ...pagesById }, + }; + return nextState; + } if (type === changeChatPage.type) { const { pageId } = payload; const page = { @@ -188,5 +210,52 @@ export const chatReducer = (state = initialState, action) => { } return nextState; } + if (type === moveChatPageLeft.type) { + const { pageId } = payload; + const nextState = { + ...state, + pages: [...state.pages], + pageById: { + ...state.pageById, + }, + }; + const tmpPage = nextState.pageById[pageId]; + const fromIndex = nextState.pages.indexOf(tmpPage.id); + const toIndex = fromIndex - 1; + // don't ever move leftmost page + if (fromIndex > 0) { + // don't ever move anything to the leftmost page + if (toIndex > 0) { + const tmp = nextState.pages[fromIndex]; + nextState.pages[fromIndex] = nextState.pages[toIndex]; + nextState.pages[toIndex] = tmp; + } + } + return nextState; + } + + if (type === moveChatPageRight.type) { + const { pageId } = payload; + const nextState = { + ...state, + pages: [...state.pages], + pageById: { + ...state.pageById, + }, + }; + const tmpPage = nextState.pageById[pageId]; + const fromIndex = nextState.pages.indexOf(tmpPage.id); + const toIndex = fromIndex + 1; + // don't ever move leftmost page + if (fromIndex > 0) { + // don't ever move anything out of the array + if (toIndex < nextState.pages.length) { + const tmp = nextState.pages[fromIndex]; + nextState.pages[fromIndex] = nextState.pages[toIndex]; + nextState.pages[toIndex] = tmp; + } + } + return nextState; + } return state; }; diff --git a/tgui/packages/tgui-panel/chat/types.ts b/tgui/packages/tgui-panel/chat/types.ts new file mode 100644 index 000000000000..91ab9fd3a7f0 --- /dev/null +++ b/tgui/packages/tgui-panel/chat/types.ts @@ -0,0 +1,9 @@ +export type Page = { + isMain: boolean; + id: string; + name: string; + acceptedTypes: Record; + unreadCount: number; + hideUnreadCount: boolean; + createdAt: number; +}; diff --git a/tgui/packages/tgui-panel/ping/PingIndicator.tsx b/tgui/packages/tgui-panel/ping/PingIndicator.tsx index cd5fb3a0a2a9..9c0e0f9a2430 100644 --- a/tgui/packages/tgui-panel/ping/PingIndicator.tsx +++ b/tgui/packages/tgui-panel/ping/PingIndicator.tsx @@ -7,7 +7,7 @@ import { Color } from 'common/color'; import { toFixed } from 'common/math'; import { useBackend, useSelector } from 'tgui/backend'; -import { Box, Button } from 'tgui/components'; +import { Box, Tooltip } from 'tgui/components'; import { selectPing } from './selectors'; @@ -21,19 +21,16 @@ export const PingIndicator = (props) => { ]).toString(); const roundtrip = ping.roundtrip ? toFixed(ping.roundtrip) : '--'; return ( - + +
act('ping_relays')} + > + + {roundtrip} +
+
); }; diff --git a/tgui/packages/tgui-panel/settings/SettingTabs/SettingsGeneral.tsx b/tgui/packages/tgui-panel/settings/SettingTabs/SettingsGeneral.tsx new file mode 100644 index 000000000000..209691ec29c4 --- /dev/null +++ b/tgui/packages/tgui-panel/settings/SettingTabs/SettingsGeneral.tsx @@ -0,0 +1,192 @@ +import { toFixed } from 'common/math'; +import { capitalize } from 'common/string'; +import { useState } from 'react'; +import { useDispatch, useSelector } from 'tgui/backend'; +import { + Button, + Collapsible, + Divider, + Input, + LabeledList, + Section, + Slider, + Stack, +} from 'tgui/components'; + +import { clearChat, saveChatToDisk } from '../../chat/actions'; +import { THEMES } from '../../themes'; +import { exportSettings, updateSettings } from '../actions'; +import { FONTS } from '../constants'; +import { selectSettings } from '../selectors'; +import { importChatSettings } from '../settingsImExport'; + +export function SettingsGeneral(props) { + const { theme, fontFamily, fontSize, lineHeight } = + useSelector(selectSettings); + const dispatch = useDispatch(); + const [freeFont, setFreeFont] = useState(false); + + return ( +
+ + + {THEMES.map((THEME) => ( + + ))} + + + + {!freeFont ? ( + { + setFreeFont(!freeFont); + }} + > + Custom font + + } + > + {FONTS.map((FONT) => ( + + ))} + + ) : ( + + + dispatch( + updateSettings({ + fontFamily: value, + }), + ) + } + /> + + + )} + + + + + + toFixed(value)} + onChange={(e, value) => + dispatch(updateSettings({ fontSize: value })) + } + /> + + + + + toFixed(value, 2)} + onDrag={(e, value) => + dispatch( + updateSettings({ + lineHeight: value, + }), + ) + } + /> + + + + + + + + + importChatSettings(files)} + > + Import settings + + + + + + + dispatch(clearChat())} + > + Clear chat + + + +
+ ); +} diff --git a/tgui/packages/tgui-panel/settings/SettingTabs/SettingsStatPanel.tsx b/tgui/packages/tgui-panel/settings/SettingTabs/SettingsStatPanel.tsx new file mode 100644 index 000000000000..cf089dad59e5 --- /dev/null +++ b/tgui/packages/tgui-panel/settings/SettingTabs/SettingsStatPanel.tsx @@ -0,0 +1,84 @@ +import { toFixed } from 'common/math'; +import { capitalize } from 'common/string'; +import { useDispatch, useSelector } from 'tgui/backend'; +import { + Button, + LabeledList, + NoticeBox, + Section, + Slider, + Stack, +} from 'tgui/components'; + +import { updateSettings } from '../actions'; +import { selectSettings } from '../selectors'; + +const TabsViews = ['default', 'classic', 'scrollable']; +const LinkedToChat = () => ( + Unlink Stat Panel from chat! +); + +export function SettingsStatPanel(props) { + const { statLinked, statFontSize, statTabsStyle } = + useSelector(selectSettings); + const dispatch = useDispatch(); + + return ( +
+ + + + + {TabsViews.map((view) => ( + + ))} + + + + {statLinked ? ( + + ) : ( + toFixed(value)} + onChange={(e, value) => + dispatch(updateSettings({ statFontSize: value })) + } + /> + )} + + + + + + + + + +
+ ); +} diff --git a/tgui/packages/tgui-panel/settings/SettingTabs/TextHighlight.tsx b/tgui/packages/tgui-panel/settings/SettingTabs/TextHighlight.tsx new file mode 100644 index 000000000000..b1e9cf6daa80 --- /dev/null +++ b/tgui/packages/tgui-panel/settings/SettingTabs/TextHighlight.tsx @@ -0,0 +1,177 @@ +import { useDispatch, useSelector } from 'tgui/backend'; +import { + Box, + Button, + ColorBox, + Divider, + Input, + Section, + Stack, + TextArea, +} from 'tgui/components'; + +import { rebuildChat } from '../../chat/actions'; +import { + addHighlightSetting, + removeHighlightSetting, + updateHighlightSetting, +} from '../actions'; +import { + selectHighlightSettingById, + selectHighlightSettings, +} from '../selectors'; + +export function TextHighlightSettings(props) { + const highlightSettings = useSelector(selectHighlightSettings); + const dispatch = useDispatch(); + + return ( +
+ + {highlightSettings.map((id, i) => ( + + ))} + + + + + + + + + + + Can freeze the chat for a while. + + +
+ ); +} + +function TextHighlightSetting(props) { + const { id, ...rest } = props; + const highlightSettingById = useSelector(selectHighlightSettingById); + const dispatch = useDispatch(); + const { + highlightColor, + highlightText, + highlightWholeMessage, + matchWord, + matchCase, + } = highlightSettingById[id]; + + return ( + + + + + + + + dispatch( + updateHighlightSetting({ + id: id, + highlightWholeMessage: !highlightWholeMessage, + }), + ) + } + > + Whole Message + + + + + dispatch( + updateHighlightSetting({ + id: id, + matchWord: !matchWord, + }), + ) + } + > + Exact + + + + + dispatch( + updateHighlightSetting({ + id: id, + matchCase: !matchCase, + }), + ) + } + > + Case + + + + + + dispatch( + updateHighlightSetting({ + id: id, + highlightColor: value, + }), + ) + } + /> + + +