Merge remote-tracking branch 'tgstation/master' into upstream-2025-11-29

# Conflicts:
#	_maps/RandomRuins/SpaceRuins/derelict_sulaco.dmm
#	_maps/RandomRuins/SpaceRuins/garbagetruck2.dmm
#	_maps/map_files/CatwalkStation/CatwalkStation_2023.dmm
#	_maps/map_files/tramstation/tramstation.dmm
#	code/_onclick/hud/new_player.dm
#	code/datums/components/squashable.dm
#	code/datums/diseases/advance/symptoms/heal.dm
#	code/datums/diseases/chronic_illness.dm
#	code/datums/status_effects/buffs.dm
#	code/datums/status_effects/debuffs/drunk.dm
#	code/datums/status_effects/debuffs/stamcrit.dm
#	code/game/machinery/computer/crew.dm
#	code/game/objects/items/devices/scanners/health_analyzer.dm
#	code/game/objects/items/wall_mounted.dm
#	code/game/turfs/closed/indestructible.dm
#	code/modules/admin/view_variables/filterrific.dm
#	code/modules/antagonists/heretic/influences.dm
#	code/modules/cargo/orderconsole.dm
#	code/modules/client/preferences.dm
#	code/modules/events/space_vines/vine_mutations.dm
#	code/modules/mob/dead/new_player/new_player.dm
#	code/modules/mob/living/carbon/human/death.dm
#	code/modules/mob/living/carbon/human/species_types/jellypeople.dm
#	code/modules/mob/living/damage_procs.dm
#	code/modules/mob/living/living.dm
#	code/modules/mob_spawn/ghost_roles/mining_roles.dm
#	code/modules/mob_spawn/mob_spawn.dm
#	code/modules/projectiles/ammunition/energy/laser.dm
#	code/modules/projectiles/guns/ballistic/launchers.dm
#	code/modules/projectiles/guns/energy/laser.dm
#	code/modules/reagents/chemistry/machinery/chem_dispenser.dm
#	code/modules/reagents/chemistry/reagents/cat2_medicine_reagents.dm
#	code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm
#	code/modules/reagents/chemistry/reagents/medicine_reagents.dm
#	code/modules/surgery/healing.dm
#	code/modules/unit_tests/designs.dm
#	icons/mob/inhands/items_lefthand.dmi
#	icons/mob/inhands/items_righthand.dmi
#	tgui/packages/tgui/interfaces/ChemDispenser.tsx
This commit is contained in:
nevimer
2025-11-29 22:49:21 -05:00
1185 changed files with 39068 additions and 32028 deletions
+30 -45
View File
@@ -115,15 +115,13 @@
mode = RCD_TURF
user.visible_message(span_suicide("[user] sets the RCD to 'Wall' and points it down [user.p_their()] throat! It looks like [user.p_theyre()] trying to commit suicide!"))
if(checkResource(16, user)) // It takes 16 resources to construct a wall
if(useResource(16, user)) // It takes 16 resources to construct a wall
var/success = T.rcd_act(user, src, list("[RCD_DESIGN_MODE]" = RCD_TURF, "[RCD_DESIGN_PATH]" = /turf/open/floor/plating/rcd))
T = get_turf(user)
// If the RCD placed a floor instead of a wall, having a wall without plating under it is cursed
// There isn't an easy programmatical way to check if rcd_act will place a floor or a wall, so just repeat using it for free
if(success && isopenturf(T))
T.rcd_act(user, src, list("[RCD_DESIGN_MODE]" = RCD_TURF, "[RCD_DESIGN_PATH]" = /turf/open/floor/plating/rcd))
useResource(16, user)
activate()
playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
user.gib(DROP_ALL_REMAINS)
return MANUAL_SUICIDE
@@ -173,9 +171,18 @@
* if we are trying to create plating on turf which is not a proper floor then don't check for objects on top of the turf just allow that turf to be converted into plating. e.g. create plating beneath a player or underneath a machine frame/any dense object
* if we are trying to finish a wall girder then let it finish then make sure no one/nothing is stuck in the girder
*/
else if(rcd_mode == RCD_TURF && rcd_structure == /turf/open/floor/plating/rcd && (!istype(target_turf, /turf/open/floor) || istype(target, /obj/structure/girder)))
else if(rcd_mode == RCD_TURF && rcd_structure == /turf/open/floor/plating/rcd)
//if a player builds a wallgirder on top of himself manually with iron sheets he can't finish the wall if he is still on the girder. Exclude the girder itself when checking for other dense objects on the turf
if(istype(target, /obj/structure/girder) && target_turf.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = list(target)))
var/list/ignore_types = list()
if(isturf(target))
if(isfloorturf(target))
ignore_types += /obj/structure/girder
else
ignore_types = null
else if(istype(target, /obj/structure/girder))
ignore_types += /obj/structure/girder
if(ignore_types && target_turf.is_blocked_turf(exclude_mobs = FALSE, source_atom = null, ignore_atoms = ignore_types, type_list = TRUE))
playsound(get_turf(user), SFX_TOOL_SWITCH, 20, TRUE)
balloon_alert(user, "something is on the girder!")
return FALSE
@@ -288,7 +295,7 @@
var/obj/effect/constructing_effect/rcd_effect = new(get_turf(target), delay, rcd_results["[RCD_DESIGN_MODE]"], construction_upgrades)
//resource & structure placement sanity checks before & after delay along with beam effects
if(!checkResource(rcd_results["cost"], user) || !can_place(target, rcd_results, user))
if(!useResource(rcd_results["cost"], user, TRUE) || !can_place(target, rcd_results, user))
qdel(rcd_effect)
return FALSE
var/beam
@@ -302,17 +309,14 @@
return FALSE
if (QDELETED(rcd_effect))
return FALSE
if(!checkResource(rcd_results["cost"], user) || !can_place(target, rcd_results, user))
if(!useResource(rcd_results["cost"], user, TRUE) || !can_place(target, rcd_results, user))
qdel(rcd_effect)
return FALSE
if(!useResource(rcd_results["cost"], user))
qdel(rcd_effect)
return FALSE
activate()
if(!target.rcd_act(user, src, rcd_results))
qdel(rcd_effect)
return FALSE
useResource(rcd_results["cost"], user)
playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
rcd_effect.end_animation()
return TRUE
@@ -486,31 +490,20 @@
max_matter = borgy.cell.maxcharge
return borgy.cell.charge
/obj/item/construction/rcd/borg/useResource(amount, mob/user)
if(!iscyborg(user))
return 0
/obj/item/construction/rcd/borg/useResource(amount, mob/user, dry_run)
var/mob/living/silicon/robot/borgy = user
if(!iscyborg(borgy))
return FALSE
if(!borgy.cell)
if(user)
balloon_alert(user, "no cell found!")
return 0
. = borgy.cell.use(amount * energyfactor) //borgs get 1.3x the use of their RCDs
if(!. && user)
balloon_alert(user, "no cell found!")
return FALSE
if(borgy.cell.charge < (amount * energyfactor))
balloon_alert(user, "insufficient charge!")
return .
/obj/item/construction/rcd/borg/checkResource(amount, mob/user)
if(!iscyborg(user))
return 0
var/mob/living/silicon/robot/borgy = user
if(!borgy.cell)
if(user)
balloon_alert(user, "no cell found!")
return 0
. = borgy.cell.charge >= (amount * energyfactor)
if(!. && user)
balloon_alert(user, "insufficient charge!")
return .
return FALSE
if(!dry_run)
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
return borgy.cell.use(amount * energyfactor)
return TRUE
/obj/item/construction/rcd/borg/syndicate
name = "syndicate RCD"
@@ -603,26 +596,18 @@
var/obj/vehicle/sealed/mecha/gundam = owner
return round(gundam.get_charge() / MASS_TO_ENERGY)
/obj/item/construction/rcd/exosuit/useResource(amount, mob/user)
/obj/item/construction/rcd/exosuit/useResource(amount, mob/user, dry_run)
if(silo_link)
return ..()
if(!ismecha(owner))
return 0
var/obj/vehicle/sealed/mecha/gundam = owner
if(!gundam.use_energy(amount * MASS_TO_ENERGY))
gundam.balloon_alert(user, "insufficient charge!")
return FALSE
return TRUE
/obj/item/construction/rcd/exosuit/checkResource(amount, mob/user)
if(silo_link)
return ..()
if(!ismecha(owner))
return 0
var/obj/vehicle/sealed/mecha/gundam = owner
if(!gundam.has_charge(amount * MASS_TO_ENERGY))
gundam.balloon_alert(user, "insufficient charge!")
return FALSE
if(!dry_run)
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
return gundam.use_energy(amount * MASS_TO_ENERGY)
return TRUE
#undef MASS_TO_ENERGY
+21 -29
View File
@@ -166,9 +166,6 @@
balloon_alert(user, "storage full!")
return FALSE
/obj/item/construction/proc/activate()
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
/obj/item/construction/attack_self(mob/user)
playsound(loc, 'sound/effects/pop.ogg', 50, FALSE)
if(prob(20))
@@ -181,27 +178,39 @@
if(ratio > 0)
. += "[icon_state]_charge[ratio]"
/obj/item/construction/proc/useResource(amount, mob/user)
/**
* Uses resource to do some action. Returns amount of resource used or TRUE/FALSE if only an dry run is required
*
* Arguments
* * amount - the amount of resource to use
* * mob/user - the player using the resource
* * dry_run - if TRUE will only check if the amount of resource is available but will not use any
*/
/obj/item/construction/proc/useResource(amount, mob/user, dry_run = FALSE)
if(!silo_mats || !silo_link)
if(matter < amount)
if(has_ammobar)
flick("[icon_state]_empty", src)
if(user)
balloon_alert(user, "not enough matter!")
return FALSE
matter -= amount
update_appearance()
return TRUE
if(!dry_run)
matter -= amount
update_appearance()
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
else
if(!silo_mats.mat_container)
if(!silo_mats.can_use_resource(user_data = ID_DATA(user)))
if(user)
balloon_alert(user, "no silo detected!")
balloon_alert(user, "permission denied!")
return FALSE
if(!silo_mats.mat_container.has_enough_of_material(/datum/material/iron, amount * SILO_USE_AMOUNT))
if(user)
balloon_alert(user, "not enough silo material!")
return FALSE
silo_mats.use_materials(list(/datum/material/iron = SILO_USE_AMOUNT), multiplier = amount, action = "RESTOCKED", name = "x restocked an RCD", user_data = ID_DATA(user))
return TRUE
if(!dry_run)
amount = silo_mats.use_materials(list(/datum/material/iron = SILO_USE_AMOUNT), multiplier = amount, action = "RESTOCKED", name = "x restocked an RCD", user_data = ID_DATA(user))
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
return dry_run ? TRUE : amount
/obj/item/construction/ui_static_data(mob/user)
. = list()
@@ -254,23 +263,6 @@
/obj/item/construction/proc/handle_ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
return null
/obj/item/construction/proc/checkResource(amount, mob/user)
if(!silo_mats || !silo_mats.mat_container || !silo_link)
if(silo_link)
balloon_alert(user, "silo link invalid!")
return FALSE
else
. = matter >= amount
else
if(!silo_mats.can_use_resource(user_data = ID_DATA(user)))
return FALSE
. = silo_mats.mat_container.has_enough_of_material(/datum/material/iron, amount * SILO_USE_AMOUNT)
if(!. && user)
balloon_alert(user, "low ammo!")
if(has_ammobar)
flick("[icon_state]_empty", src) //somewhat hacky thing to make RCDs with ammo counters actually have a blinking yellow light
return .
/obj/item/construction/proc/range_check(atom/target, mob/user)
if(target.z != user.z)
return
+12 -31
View File
@@ -1,11 +1,11 @@
// RAPID LIGHTING DEVICE
// modes of operation
//modes of operation
#define GLOW_MODE 1
#define LIGHT_MODE 2
#define REMOVE_MODE 3
// operation costs
//operation costs
#define LIGHT_TUBE_COST 10
#define FLOOR_LIGHT_COST 15
#define GLOW_STICK_COST 5
@@ -32,7 +32,6 @@
/// mode of operation see above defines
var/mode = LIGHT_MODE
///reference to thr original icons
var/static/list/original_options = list(
"Color Pick" = icon(icon = 'icons/hud/radial.dmi', icon_state = "omni"),
@@ -42,6 +41,7 @@
)
///will contain the original icons modified with the color choice
var/list/display_options = list()
///Current color choice
var/color_choice = "#ffffff"
/obj/item/construction/rld/Initialize(mapload)
@@ -78,7 +78,7 @@
if(option == "Color Pick" || option == "Deconstruct" || option == "Silo Link")
continue
var/icon/icon = icon(original_options[option])
icon.SetIntensity(new_rgb[1]/255, new_rgb[2]/255, new_rgb[3]/255) //apply new scale
icon.SetIntensity(new_rgb[1] / 255, new_rgb[2] / 255, new_rgb[3] / 255) //apply new scale
display_options[option] = icon
color_choice = new_choice
@@ -101,8 +101,8 @@
/**
* Try to place/remove a light or throw a glowstick
* Arguments
*
* Arguments
* * atom/interacting_with - the target atom to light or throw glowsticks at
* * mob/user - the player doing this action
*/
@@ -119,26 +119,24 @@
return NONE
//resource sanity checks before & after delay
if(!checkResource(DECONSTRUCT_COST, user))
if(!useResource(DECONSTRUCT_COST, user, TRUE))
return ITEM_INTERACT_BLOCKING
var/beam = user.Beam(interacting_with, icon_state="light_beam", time = 15)
playsound(src, 'sound/machines/click.ogg', 50, TRUE)
if(!do_after(user, REMOVE_DELAY, target = interacting_with))
qdel(beam)
return ITEM_INTERACT_BLOCKING
if(!checkResource(DECONSTRUCT_COST, user))
if(!useResource(DECONSTRUCT_COST, user, TRUE))
return ITEM_INTERACT_BLOCKING
if(!useResource(DECONSTRUCT_COST, user))
return ITEM_INTERACT_BLOCKING
activate()
qdel(interacting_with)
useResource(DECONSTRUCT_COST, user)
return ITEM_INTERACT_SUCCESS
if(LIGHT_MODE)
//resource sanity checks before & after delay
var/cost = iswallturf(interacting_with) ? LIGHT_TUBE_COST : FLOOR_LIGHT_COST
if(!checkResource(cost, user))
if(!useResource(cost, user, TRUE))
return ITEM_INTERACT_BLOCKING
var/beam = user.Beam(interacting_with, icon_state="light_beam", time = BUILD_DELAY)
playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
@@ -146,7 +144,7 @@
if(!do_after(user, BUILD_DELAY, target = interacting_with))
qdel(beam)
return ITEM_INTERACT_BLOCKING
if(!checkResource(cost, user))
if(!useResource(cost, user, TRUE))
return ITEM_INTERACT_BLOCKING
if(iswallturf(interacting_with))
@@ -173,31 +171,26 @@
if(!winner)
balloon_alert(user, "no valid target!")
return ITEM_INTERACT_BLOCKING
if(!useResource(cost, user))
return ITEM_INTERACT_BLOCKING
activate()
var/obj/machinery/light/L = new /obj/machinery/light(get_turf(winner))
L.setDir(get_dir(winner, interacting_with))
L.color = color_choice
L.set_light_color(color_choice)
useResource(cost, user)
return ITEM_INTERACT_SUCCESS
if(isfloorturf(interacting_with))
var/turf/target = get_turf(interacting_with)
if(locate(/obj/machinery/light/floor) in target)
return ITEM_INTERACT_BLOCKING
if(!useResource(cost, user))
return ITEM_INTERACT_BLOCKING
activate()
var/obj/machinery/light/floor/FL = new /obj/machinery/light/floor(target)
FL.color = color_choice
FL.set_light_color(color_choice)
useResource(cost, user)
return ITEM_INTERACT_SUCCESS
if(GLOW_MODE)
if(!useResource(GLOW_STICK_COST, user))
return ITEM_INTERACT_BLOCKING
activate()
// Picks the closest fitting color for the fluid by hue
var/closest_diff = null
var/closest_fluid = null
@@ -259,18 +252,6 @@
balloon_alert(user, "insufficient charge!")
return .
/obj/item/construction/rld/cyborg/checkResource(amount, mob/user)
if(!iscyborg(user))
return 0
var/mob/living/silicon/robot/borgy = user
if(!borgy.cell)
if(user)
balloon_alert(user, "no cell found!")
return 0
. = borgy.cell.charge >= (amount * energyfactor)
if(!. && user)
balloon_alert(user, "insufficient charge!")
return .
#undef LIGHT_TUBE_COST
#undef FLOOR_LIGHT_COST
+5 -5
View File
@@ -204,26 +204,24 @@
//resource & placement sanity check before & after delay
var/is_allowed = TRUE
if(!checkResource(cost, user) || !(is_allowed = canPlace(destination)))
if(!useResource(cost, user, TRUE) || !(is_allowed = canPlace(destination)))
if(!is_allowed)
balloon_alert(user, "tile is blocked!")
return FALSE
if(!build_delay(user, cost, target = destination))
return FALSE
if(!checkResource(cost, user) || !(is_allowed = canPlace(destination)))
if(!useResource(cost, user, TRUE) || !(is_allowed = canPlace(destination)))
if(!is_allowed)
balloon_alert(user, "tile is blocked!")
return FALSE
if(!useResource(cost, user))
return FALSE
activate()
playsound(loc, 'sound/machines/click.ogg', 50, TRUE)
if(ispath(blueprint, /obj/machinery/duct))
var/is_omni = current_color == DUCT_COLOR_OMNI
new blueprint(destination, FALSE, GLOB.pipe_paint_colors[current_color], GLOB.plumbing_layers[current_layer], null, is_omni)
else
new blueprint(destination, FALSE, GLOB.plumbing_layers[current_layer])
useResource(cost, user)
return TRUE
/obj/item/construction/plumbing/proc/canPlace(turf/destination)
@@ -319,6 +317,8 @@
name = "service plumbing constructor"
desc = "A type of plumbing constructor designed to rapidly deploy the machines needed to make a brewery."
icon_state = "plumberer_service"
///Extra price because it appears in bartender's vendor
custom_premium_price = PAYCHECK_CREW * 6
///Design types for plumbing service constructor
var/static/list/service_design_types = list(
//Category 1 synthesizers
+16 -29
View File
@@ -285,7 +285,7 @@
return ITEM_INTERACT_BLOCKING
//resource sanity check before & after delay along with special effects
if(!checkResource(selected_design.cost, user))
if(!useResource(selected_design.cost, user, TRUE))
return ITEM_INTERACT_BLOCKING
var/delay = CONSTRUCTION_TIME(selected_design.cost)
var/obj/effect/constructing_effect/rcd_effect = new(floor, delay, RCD_TURF)
@@ -299,15 +299,11 @@
qdel(beam)
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
if(!checkResource(selected_design.cost, user))
if(!useResource(selected_design.cost, user, TRUE))
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
//do the tilling
if(!useResource(selected_design.cost, user))
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
activate()
//step 1 create tile
var/obj/item/stack/tile/final_tile = selected_design.new_tile(user.drop_location(), selected_direction)
if(QDELETED(final_tile)) //if you were standing on a stack of tiles this newly spawned tile could get merged with it cause its spawned on your location
@@ -320,6 +316,7 @@
for(var/datum/overlay_info/info in design_overlays)
info.add_decal(new_turf)
rcd_effect.end_animation()
useResource(selected_design.cost, user)
return ITEM_INTERACT_SUCCESS
@@ -357,7 +354,7 @@
return ITEM_INTERACT_BLOCKING
//resource sanity check before & after delay along with beam effects
if(!checkResource(cost * 0.7, user)) //no ballon alert for checkResource as it already spans an alert to chat
if(!useResource(cost * 0.7, user, TRUE)) //no ballon alert for checkResource as it already spans an alert to chat
return ITEM_INTERACT_BLOCKING
var/delay = DECONSTRUCTION_TIME(cost)
var/obj/effect/constructing_effect/rcd_effect = new(floor, delay, RCD_DECONSTRUCT)
@@ -371,15 +368,11 @@
qdel(beam)
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
if(!checkResource(cost * 0.7, user))
if(!useResource(cost * 0.7, user, TRUE))
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
//begin deconstruction
if(!useResource(cost * 0.7, user))
qdel(rcd_effect)
return ITEM_INTERACT_BLOCKING
activate()
//find & collect all decals
var/list/all_decals = list()
for(var/obj/effect/decal in floor.contents)
@@ -393,6 +386,7 @@
else //for every other turf we scrape away exposing base turf underneath
floor.ScrapeAway(flags = CHANGETURF_INHERIT_AIR)
rcd_effect.end_animation()
useResource(cost * 0.7, user)
return ITEM_INTERACT_SUCCESS
@@ -415,27 +409,20 @@
max_matter = borgy.cell.maxcharge
return borgy.cell.charge
/obj/item/construction/rtd/borg/useResource(amount, mob/user)
if(!iscyborg(user))
return 0
/obj/item/construction/rtd/borg/useResource(amount, mob/user, dry_run)
var/mob/living/silicon/robot/borgy = user
if(!iscyborg(borgy))
return FALSE
if(!borgy.cell)
balloon_alert(user, "no cell found!")
return 0
. = borgy.cell.use(amount * RTD_BORG_ENERGY_FACTOR)
if(!.)
balloon_alert(user, "insufficient charge!")
/obj/item/construction/rtd/borg/checkResource(amount, mob/user)
if(!iscyborg(user))
return 0
var/mob/living/silicon/robot/borgy = user
if(!borgy.cell)
balloon_alert(user, "no cell found!")
return 0
. = borgy.cell.charge >= (amount * RTD_BORG_ENERGY_FACTOR)
if(!.)
return FALSE
if(borgy.cell.charge < (amount * RTD_BORG_ENERGY_FACTOR))
balloon_alert(user, "insufficient charge!")
return FALSE
if(!dry_run)
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
return borgy.cell.use(amount * RTD_BORG_ENERGY_FACTOR)
return TRUE
#undef RTD_BORG_ENERGY_FACTOR