From fb83617ff94d6294b0d48c8c6c57488237508d11 Mon Sep 17 00:00:00 2001 From: Wallem <66052067+Wallemations@users.noreply.github.com> Date: Thu, 8 Feb 2024 07:32:57 -0500 Subject: [PATCH 001/508] Adds a suicide_act() to the smite spell (#81252) ## About The Pull Request What it says on the tin ## Why It's Good For The Game https://youtu.be/cvfLHTyDv9o?t=4 ## Changelog :cl: Wallem add: Adds a new suicide_act() to the smite spell /:cl: --- code/modules/spells/spell_types/touch/smite.dm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/modules/spells/spell_types/touch/smite.dm b/code/modules/spells/spell_types/touch/smite.dm index f02ea8247dd22..7bef97c8a6582 100644 --- a/code/modules/spells/spell_types/touch/smite.dm +++ b/code/modules/spells/spell_types/touch/smite.dm @@ -57,3 +57,13 @@ icon = 'icons/obj/weapons/hand.dmi' icon_state = "disintegrate" inhand_icon_state = "disintegrate" + +/obj/item/melee/touch_attack/smite/suicide_act(mob/living/user) + + user.visible_message(span_suicide("[user] spreads [user.p_their()] arms apart, lightning arcing between them! It looks like [user.p_theyre()] going out with a bang!")) + user.say("SHIA KAZING!!", forced = "smite suicide") + do_sparks(4, FALSE, get_turf(user)) + explosion(user, heavy_impact_range = 2, explosion_cause = src) //Cheap explosion imitation because putting detonate() here causes runtimes + user.gib(DROP_BODYPARTS) + qdel(src) + return MANUAL_SUICIDE From 59796f050d8607736db501e5b51bbcafb7e53c3e Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:33:21 +1300 Subject: [PATCH 002/508] Automatic changelog for PR #81252 [ci skip] --- html/changelogs/AutoChangeLog-pr-81252.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81252.yml diff --git a/html/changelogs/AutoChangeLog-pr-81252.yml b/html/changelogs/AutoChangeLog-pr-81252.yml new file mode 100644 index 0000000000000..fe5b5391f3bea --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81252.yml @@ -0,0 +1,4 @@ +author: "Wallem" +delete-after: True +changes: + - rscadd: "Adds a new suicide_act() to the smite spell" \ No newline at end of file From e4b23f2b4b6edab0b483d0512e02cb7cd94fe600 Mon Sep 17 00:00:00 2001 From: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Date: Thu, 8 Feb 2024 18:04:03 +0530 Subject: [PATCH 003/508] General maintenance for Lathes (#81244) ## About The Pull Request 1. **Qol Stuff** - Screentips & examines for screwdriver, crowbar acts, multiool & wirecutter Also for Alt click - Techfabs can now also use the Mouse drag functionality to set drop target for items - Lathe printing animation now plays on loop instead of just flicking once till printing is finished for more visual feedback 2. **Code Improvements** - Merged `start_making()` with `do_make_item()`. That proc was like only 3 lines long and used only in 1 place so let's just move that code to `ui_act()` - Merged `user_print_item_id()` with `ui_act()`. Again was used only in 1 place so let's just move that code in to save some proc overhead - Sets `processing_flags` for autolathe to `NONE` cause we don't use `process()` - Autodocs vars such as `hacked` , `shocked` etc & procs - `maxmult` is now computed client side saving backend bandwidth, `construction_time` is removed from lathes which did not use it - Removed all usages of lathe taxes and their related vars, removed engineering lathe no tax from ice moon, replaced with normal engineering lathe 3. **Fixes** - Lathe sheet insertion animations are now linked & work again for all material types inserted via remote silo/local storage, silver/titanium/plastic all play the same animation(that is `protolathe_shiny` overlay). Other materials have their own respective overlays - Fixes #81243. Calling `update_static_data_for_all_viewers()` is too expensive for the UI. We should instead use `SStgui.update_uis(src)` which will report the `busy` status to the UI more immediatly - Fixes #81236. Some problems with the params passed to the timer callback. It should now print the correct number of requested items - Fixes #81192. `design.materials` would runtime for custom material items as they were list of texts not materials. We have to pass our manually parsed list of materials for an specific item to ensure they are set & used correctly. Same fixes apply for techfabs as well ## Changelog :cl: qol: adds screentips & examines for screwdriver & crowbar acts & alt click. qol: techfabs can now use the mouse drop functionality to set drop target. qol: lathe printing animation plays on loop while printing rather than flicking once for more visual feedback fix: lathe sheet insertion animations are now linked & work again for all material types inserted via remote silo/local storage fix: printing custom materials items from autolathe works again. fix: printing multiple items from lathes will actually print that correct quantity of items requested. fix: printing items the 2nd time around from lathes won't cause the UI to reload each time. code: autodoc for some vars & procs, merges procs. refactor: Optimized code for autolathe & techfabs in general. Report bugs on github /:cl: --------- Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- .../IceRuins/icemoon_surface_engioutpost.dmm | 2 +- .../components/material/remote_materials.dm | 18 +- code/game/machinery/autolathe.dm | 328 ++++++++------ .../machines/machine_circuitboards.dm | 3 - .../oldstation/oldstation_rnd.dm | 13 +- code/modules/research/destructive_analyzer.dm | 21 +- .../modules/research/machinery/_production.dm | 425 +++++++++++------- .../research/machinery/circuit_imprinter.dm | 15 +- .../machinery/departmental_protolathe.dm | 4 - code/modules/research/machinery/protolathe.dm | 1 - code/modules/research/machinery/techfab.dm | 1 - code/modules/research/rdmachines.dm | 97 ++-- .../modules/wiremod/core/component_printer.dm | 3 +- icons/obj/machines/research.dmi | Bin 26907 -> 28134 bytes tgui/packages/tgui/interfaces/Autolathe.tsx | 41 +- .../tgui/interfaces/ExosuitFabricator.tsx | 5 + .../tgui/interfaces/Fabrication/Types.ts | 10 - tgui/packages/tgui/interfaces/Fabricator.tsx | 12 +- 18 files changed, 606 insertions(+), 393 deletions(-) diff --git a/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm b/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm index 5c9336c5c5ca4..6f86429720ac0 100644 --- a/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm +++ b/_maps/RandomRuins/IceRuins/icemoon_surface_engioutpost.dmm @@ -88,7 +88,7 @@ /turf/open/floor/iron, /area/ruin/planetengi) "aA" = ( -/obj/machinery/rnd/production/protolathe/department/engineering/no_tax, +/obj/machinery/rnd/production/protolathe/department/engineering, /obj/effect/turf_decal/trimline/yellow/filled/warning{ dir = 9 }, diff --git a/code/datums/components/material/remote_materials.dm b/code/datums/components/material/remote_materials.dm index 97c6b4e62826c..e418d4276be10 100644 --- a/code/datums/components/material/remote_materials.dm +++ b/code/datums/components/material/remote_materials.dm @@ -118,6 +118,12 @@ handles linking back and forth. else silo.holds -= src +/** + * Sets the storage size for local materials when not linked with silo + * Arguments + * + * * size - the new size for local storage. measured in SHEET_MATERIAL_SIZE units + */ /datum/component/remote_materials/proc/set_local_size(size) local_size = size if (!silo && mat_container) @@ -209,7 +215,7 @@ handles linking back and forth. return check_z_level() ? silo.holds[src] : FALSE /** - * Internal proc to check if this connection can use any materials from the silo + * Check if this connection can use any materials from the silo * Returns true only if * - The parent is of type movable atom * - A mat container is actually present @@ -217,9 +223,7 @@ handles linking back and forth. * Arguments * * check_hold - should we check if the silo is on hold */ -/datum/component/remote_materials/proc/_can_use_resource(check_hold = TRUE) - PRIVATE_PROC(TRUE) - +/datum/component/remote_materials/proc/can_use_resource(check_hold = TRUE) var/atom/movable/movable_parent = parent if (!istype(movable_parent)) return FALSE @@ -243,7 +247,7 @@ handles linking back and forth. * name- For logging only. the design you are trying to build e.g. matter bin, etc. */ /datum/component/remote_materials/proc/use_materials(list/mats, coefficient = 1, multiplier = 1, action = "build", name = "design") - if(!_can_use_resource()) + if(!can_use_resource()) return 0 var/amount_consumed = mat_container.use_materials(mats, coefficient, multiplier) @@ -265,7 +269,7 @@ handles linking back and forth. * [drop_target][atom]- optional where to drop the sheets. null means it is dropped at this components parent location */ /datum/component/remote_materials/proc/eject_sheets(datum/material/material_ref, eject_amount, atom/drop_target = null) - if(!_can_use_resource()) + if(!can_use_resource()) return 0 var/atom/movable/movable_parent = parent @@ -282,7 +286,7 @@ handles linking back and forth. * * multiplier - the multiplier applied on the materials consumed */ /datum/component/remote_materials/proc/insert_item(obj/item/weapon, multiplier = 1) - if(!_can_use_resource(FALSE)) + if(!can_use_resource(FALSE)) return MATERIAL_INSERT_ITEM_FAILURE return mat_container.insert_item(weapon, multiplier, parent) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index 03473ed5d394e..f6600942cdeee 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -7,24 +7,24 @@ active_power_usage = BASE_MACHINE_ACTIVE_CONSUMPTION * 0.5 circuit = /obj/item/circuitboard/machine/autolathe layer = BELOW_OBJ_LAYER + processing_flags = NONE + ///Is the autolathe hacked via wiring var/hacked = FALSE + ///Is the autolathe disabled via wiring var/disabled = FALSE + ///Did we recently shock a mob who medled with the wiring var/shocked = FALSE + ///Are we currently printing something var/busy = FALSE - - /// Coefficient applied to consumed materials. Lower values result in lower material consumption. + ///Coefficient applied to consumed materials. Lower values result in lower material consumption. var/creation_efficiency = 1.6 - - var/datum/design/being_built + ///Designs related to the autolathe var/datum/techweb/autounlocking/stored_research - ///Designs imported from technology disks that we can print. var/list/imported_designs = list() - ///The container to hold materials var/datum/component/material_container/materials - ///direction we output onto (if 0, on top of us) var/drop_direction = 0 @@ -43,17 +43,69 @@ GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe] = new /datum/techweb/autounlocking/autolathe stored_research = GLOB.autounlock_techwebs[/datum/techweb/autounlocking/autolathe] + register_context() + /obj/machinery/autolathe/Destroy() materials = null QDEL_NULL(wires) return ..() +/obj/machinery/autolathe/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + return + + . += span_notice("Material usage cost at [creation_efficiency * 100]%") + if(drop_direction) + . += span_notice("Currently configured to drop printed objects [dir2text(drop_direction)].") + . += span_notice("[EXAMINE_HINT("Alt-click")] to reset.") + else + . += span_notice("[EXAMINE_HINT("Drag")] towards a direction (while next to it) to change drop direction.") + + . += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].") + if(panel_open) + . += span_notice("The machine can be [EXAMINE_HINT("pried")] apart.") + +/obj/machinery/autolathe/add_context(atom/source, list/context, obj/item/held_item, mob/user) + if(drop_direction) + context[SCREENTIP_CONTEXT_ALT_LMB] = "Reset Drop" + return CONTEXTUAL_SCREENTIP_SET + + if(isnull(held_item)) + return NONE + + if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_RMB] = "[panel_open ? "Close" : "Open"] Panel" + return CONTEXTUAL_SCREENTIP_SET + + if(panel_open && held_item.tool_behaviour == TOOL_CROWBAR) + context[SCREENTIP_CONTEXT_LMB] = "Deconstruct" + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_deconstruction_crowbar(tool)) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/autolathe/screwdriver_act_secondary(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING + if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", tool)) + return ITEM_INTERACT_SUCCESS + +/obj/machinery/autolathe/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context) + SIGNAL_HANDLER + + flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src) + + directly_use_power(round((amount_inserted / SHEET_MATERIAL_AMOUNT) * active_power_usage * 0.0025)) + /obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui) if(!is_operational) return if(shocked && !(machine_stat & NOPOWER)) shock(user, 50) + return ui = SStgui.try_update_ui(user, src, ui) @@ -61,66 +113,40 @@ ui = new(user, src, "Autolathe") ui.open() -/obj/machinery/autolathe/ui_static_data(mob/user) - var/list/data = materials.ui_static_data() - - var/max_available = materials.total_amount() - for(var/datum/material/container_mat as anything in materials.materials) - var/available = materials.materials[container_mat] - if(available) - max_available = max(max_available, available) - - data["designs"] = handle_designs(stored_research.researched_designs, max_available) - if(imported_designs.len) - data["designs"] += handle_designs(imported_designs, max_available) - if(hacked) - data["designs"] += handle_designs(stored_research.hacked_designs, max_available) - - return data - -/obj/machinery/autolathe/ui_data(mob/user) - var/list/data = list() - - data["materials"] = list() - data["materialtotal"] = materials.total_amount() - data["materialsmax"] = materials.max_amount - data["active"] = busy - data["materials"] = materials.ui_data() - return data +/** + * Converts all the designs supported by this autolathe into UI data + * Arguments + * + * * list/designs - the list of techweb designs we are trying to send to the UI + */ +/obj/machinery/autolathe/proc/handle_designs(list/designs) + PRIVATE_PROC(TRUE) -/obj/machinery/autolathe/proc/handle_designs(list/designs, max_available) var/list/output = list() var/datum/asset/spritesheet/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet/research_designs) var/size32x32 = "[spritesheet.name]32x32" - var/max_multiplier = INFINITY for(var/design_id in designs) var/datum/design/design = SSresearch.techweb_design_by_id(design_id) if(design.make_reagent) continue //compute cost & maximum number of printable items - max_multiplier = INFINITY var/coeff = (ispath(design.build_path, /obj/item/stack) ? 1 : creation_efficiency) var/list/cost = list() + var/customMaterials = FALSE for(var/i in design.materials) var/datum/material/mat = i var/design_cost = OPTIMAL_COST(design.materials[i] * coeff) if(istype(mat)) cost[mat.name] = design_cost + customMaterials = FALSE else cost[i] = design_cost - - var/mat_available - if(istype(mat)) //regular mat - mat_available = materials.get_material_amount(mat) - else //category mat means we can make it from any mat, use largest available mat - mat_available = max_available - - max_multiplier = min(max_multiplier, 50, round(mat_available / design_cost)) + customMaterials = TRUE //create & send ui data var/icon_size = spritesheet.icon_size_id(design.id) @@ -131,65 +157,87 @@ "id" = design.id, "categories" = design.category, "icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]", - "constructionTime" = -1, - "maxmult" = max_multiplier + "customMaterials" = customMaterials ) output += list(design_data) return output +/obj/machinery/autolathe/ui_static_data(mob/user) + var/list/data = materials.ui_static_data() + + data["designs"] = handle_designs(stored_research.researched_designs) + if(imported_designs.len) + data["designs"] += handle_designs(imported_designs) + if(hacked) + data["designs"] += handle_designs(stored_research.hacked_designs) + + return data + + /obj/machinery/autolathe/ui_assets(mob/user) return list( get_asset_datum(/datum/asset/spritesheet/sheetmaterials), get_asset_datum(/datum/asset/spritesheet/research_designs), ) +/obj/machinery/autolathe/ui_data(mob/user) + var/list/data = list() + + data["materials"] = list() + data["materialtotal"] = materials.total_amount() + data["materialsmax"] = materials.max_amount + data["active"] = busy + data["materials"] = materials.ui_data() + + return data + /obj/machinery/autolathe/ui_act(action, list/params, datum/tgui/ui) . = ..() if(.) return + //sanity checks to start printing if(action != "make") stack_trace("unknown autolathe ui_act: [action]") return - if(disabled) say("Unable to print, voltage mismatch in internal wiring.") return - if(busy) - balloon_alert(ui.user, "busy!") - return - - var/turf/target_location = get_step(src, drop_direction) - if(isclosedturf(target_location)) - say("Output path is obstructed by a large object.") + say("currently printing.") return + //validate design var/design_id = params["id"] - + if(!design_id) + return var/valid_design = stored_research.researched_designs[design_id] valid_design ||= stored_research.hacked_designs[design_id] valid_design ||= imported_designs[design_id] if(!valid_design) return - var/datum/design/design = SSresearch.techweb_design_by_id(design_id) if(isnull(design)) stack_trace("got passed an invalid design id: [design_id] and somehow made it past all checks") return - if(!(design.build_type & AUTOLATHE)) + say("This fabricator does not have the necessary keys to decrypt this design.") return - var/build_count = text2num(params["multiplier"]) - if(!build_count) + //validate print quantity + var/build_count = params["multiplier"] + if(isnull(build_count)) + return + build_count = text2num(build_count) + if(isnull(build_count)) return build_count = clamp(build_count, 1, 50) + //check for materials required. For custom material items decode their required materials var/list/materials_needed = list() - for(var/datum/material/material as anything in design.materials) + for(var/material in design.materials) var/amount_needed = design.materials[material] if(istext(material)) // category var/list/choices = list() @@ -215,64 +263,70 @@ return materials_needed[material] = amount_needed + //checks for available materials var/material_cost_coefficient = ispath(design.build_path, /obj/item/stack) ? 1 : creation_efficiency if(!materials.has_materials(materials_needed, material_cost_coefficient, build_count)) say("Not enough materials to begin production.") return - //use power - var/total_charge = 0 + //compute power & time to print 1 item + var/charge_per_item = 0 for(var/material in design.materials) - total_charge += round(design.materials[material] * material_cost_coefficient * build_count) - var/charge_per_item = total_charge / build_count - - var/total_time = (design.construction_time * design.lathe_time_factor * build_count) ** 0.8 - var/time_per_item = total_time / build_count - start_making(design, build_count, time_per_item, material_cost_coefficient, charge_per_item) - return TRUE - -/// Begins the act of making the given design the given number of items -/// Does not check or use materials/power/etc -/obj/machinery/autolathe/proc/start_making(datum/design/design, build_count, build_time_per_item, material_cost_coefficient, charge_per_item) - PROTECTED_PROC(TRUE) + charge_per_item += design.materials[material] + charge_per_item = min(active_power_usage, round(charge_per_item * material_cost_coefficient)) + var/build_time_per_item = (design.construction_time * design.lathe_time_factor) ** 0.8 + //do the printing sequentially busy = TRUE icon_state = "autolathe_n" - update_static_data_for_all_viewers() + SStgui.update_uis(src) + var/turf/target_location + if(drop_direction) + target_location = get_step(src, drop_direction) + if(isclosedturf(target_location)) + target_location = get_turf(src) + else + target_location = get_turf(src) + addtimer(CALLBACK(src, PROC_REF(do_make_item), design, build_count, build_time_per_item, material_cost_coefficient, charge_per_item, materials_needed, target_location), build_time_per_item) - addtimer(CALLBACK(src, PROC_REF(do_make_item), design, material_cost_coefficient, build_time_per_item, charge_per_item, build_count), build_time_per_item) + return TRUE -/// Callback for start_making, actually makes the item -/// Called using timers started by start_making -/obj/machinery/autolathe/proc/do_make_item(datum/design/design, material_cost_coefficient, time_per_item, charge_per_item, items_remaining) +/** + * Callback for start_making, actually makes the item + * Arguments + * + * * datum/design/design - the design we are trying to print + * * items_remaining - the number of designs left out to print + * * build_time_per_item - the time taken to print 1 item + * * material_cost_coefficient - the cost efficiency to print 1 design + * * charge_per_item - the amount of power to print 1 item + * * list/materials_needed - the list of materials to print 1 item + * * turf/target - the location to drop the printed item on +*/ +/obj/machinery/autolathe/proc/do_make_item(datum/design/design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, list/materials_needed, turf/target) PROTECTED_PROC(TRUE) - if(!items_remaining) // how + if(items_remaining <= 0) // how finalize_build() return - if(!directly_use_power(charge_per_item)) + if(!is_operational || !directly_use_power(charge_per_item)) say("Unable to continue production, power failure.") finalize_build() return - var/list/design_materials = design.materials var/is_stack = ispath(design.build_path, /obj/item/stack) - if(!materials.has_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1)) + if(!materials.has_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1)) say("Unable to continue production, missing materials.") return - materials.use_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1) - - var/turf/target = get_step(src, drop_direction) - if(isclosedturf(target)) - target = get_turf(src) + materials.use_materials(materials_needed, material_cost_coefficient, is_stack ? items_remaining : 1) var/atom/movable/created if(is_stack) created = new design.build_path(target, items_remaining) else created = new design.build_path(target) - split_materials_uniformly(design_materials, material_cost_coefficient, created) + split_materials_uniformly(materials_needed, material_cost_coefficient, created) created.pixel_x = created.base_pixel_x + rand(-6, 6) created.pixel_y = created.base_pixel_y + rand(-6, 6) @@ -283,26 +337,44 @@ else items_remaining -= 1 - if(!items_remaining) + if(items_remaining <= 0) finalize_build() return - addtimer(CALLBACK(src, PROC_REF(do_make_item), design, material_cost_coefficient, time_per_item, items_remaining), time_per_item) + addtimer(CALLBACK(src, PROC_REF(do_make_item), design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, materials_needed, target), build_time_per_item) -/// Resets the icon state and busy flag -/// Called at the end of do_make_item's timer loop +/** + * Resets the icon state and busy flag + * Called at the end of do_make_item's timer loop +*/ /obj/machinery/autolathe/proc/finalize_build() PROTECTED_PROC(TRUE) + icon_state = initial(icon_state) busy = FALSE - update_static_data_for_all_viewers() + SStgui.update_uis(src) -/obj/machinery/autolathe/crowbar_act(mob/living/user, obj/item/tool) - if(default_deconstruction_crowbar(tool)) - return ITEM_INTERACT_SUCCESS +/obj/machinery/autolathe/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) + . = ..() + if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr)) + return + if(busy) + balloon_alert(usr, "printing started!") + return + var/direction = get_dir(src, over_location) + if(!direction) + return + drop_direction = direction + balloon_alert(usr, "dropping [dir2text(drop_direction)]") -/obj/machinery/autolathe/screwdriver_act_secondary(mob/living/user, obj/item/tool) - if(default_deconstruction_screwdriver(user, "autolathe_t", "autolathe", tool)) - return ITEM_INTERACT_SUCCESS +/obj/machinery/autolathe/AltClick(mob/user) + . = ..() + if(!drop_direction || !can_interact(user)) + return + if(busy) + balloon_alert(user, "busy printing!") + return + balloon_alert(user, "drop direction reset") + drop_direction = 0 /obj/machinery/autolathe/attackby(obj/item/attacking_item, mob/living/user, params) if(user.combat_mode) //so we can hit the machine @@ -346,25 +418,6 @@ return ..() -/obj/machinery/autolathe/proc/AfterMaterialInsert(container, obj/item/item_inserted, last_inserted_id, mats_consumed, amount_inserted, atom/context) - SIGNAL_HANDLER - - flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src) - - use_power(min(active_power_usage * 0.25, amount_inserted / SHEET_MATERIAL_AMOUNT)) - - update_static_data_for_all_viewers() - -/obj/machinery/autolathe/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) - . = ..() - if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr)) - return - var/direction = get_dir(src, over_location) - if(!direction) - return - drop_direction = direction - balloon_alert(usr, "dropping [dir2text(drop_direction)]") - /obj/machinery/autolathe/RefreshParts() . = ..() var/mat_capacity = 0 @@ -377,24 +430,12 @@ efficiency -= new_servo.tier * 0.2 creation_efficiency = max(1,efficiency) // creation_efficiency goes 1.6 -> 1.4 -> 1.2 -> 1 per level of servo efficiency -/obj/machinery/autolathe/examine(mob/user) - . += ..() - if(in_range(user, src) || isobserver(user)) - . += span_notice("The status display reads: Storing up to [materials.max_amount] material units.
Material consumption at [creation_efficiency*100]%.") - if(drop_direction) - . += span_notice("Currently configured to drop printed objects [dir2text(drop_direction)].") - . += span_notice("Alt-click to reset.") - else - . += span_notice("Drag towards a direction (while next to it) to change drop direction.") - -/obj/machinery/autolathe/AltClick(mob/user) - . = ..() - if(!can_interact(user)) - return - if(drop_direction) - balloon_alert(user, "drop direction reset") - drop_direction = 0 - +/** + * Cut a wire in the autolathe + * Arguments + * + * * wire - the wire we are trying to cut + */ /obj/machinery/autolathe/proc/reset(wire) switch(wire) if(WIRE_HACK) @@ -407,6 +448,13 @@ if(!wires.is_cut(wire)) disabled = FALSE +/** + * Shock a mob who is trying to interact with the autolathe + * Arguments + * + * * mob/user - the mob we are trying to shock + * * prb - the probability of getting shocked + */ /obj/machinery/autolathe/proc/shock(mob/user, prb) if(machine_stat & (BROKEN|NOPOWER)) // unpowered, no shock return FALSE @@ -417,6 +465,12 @@ s.start() return electrocute_mob(user, get_area(src), src, 0.7, TRUE) +/** + * Is the autolathe hacked. Allowing us to acess hidden designs + * Arguments + * + * state - TRUE/FALSE for is the autolathe hacked + */ /obj/machinery/autolathe/proc/adjust_hacked(state) hacked = state update_static_data_for_all_viewers() diff --git a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm index 7ebfcab4e0077..8e5f2d1dfc5ef 100644 --- a/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm +++ b/code/game/objects/items/circuitboards/machines/machine_circuitboards.dm @@ -309,9 +309,6 @@ greyscale_colors = CIRCUIT_COLOR_ENGINEERING build_path = /obj/machinery/rnd/production/protolathe/department/engineering -/obj/item/circuitboard/machine/protolathe/department/engineering/no_tax - build_path = /obj/machinery/rnd/production/protolathe/department/engineering/no_tax - /obj/item/circuitboard/machine/rtg name = "RTG" greyscale_colors = CIRCUIT_COLOR_ENGINEERING diff --git a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_rnd.dm b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_rnd.dm index 58d14e0d041a4..5f0935bb8bde9 100644 --- a/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_rnd.dm +++ b/code/modules/mapfluff/ruins/spaceruin_code/oldstation/oldstation_rnd.dm @@ -4,15 +4,24 @@ req_access = list(ACCESS_AWAY_SCIENCE) /obj/machinery/rnd/server/oldstation/Initialize(mapload) - register_context() var/datum/techweb/oldstation_web = locate(/datum/techweb/oldstation) in SSresearch.techwebs stored_research = oldstation_web return ..() /obj/machinery/rnd/server/oldstation/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(held_item && istype(held_item, /obj/item/research_notes)) context[SCREENTIP_CONTEXT_LMB] = "Generate research points" - return CONTEXTUAL_SCREENTIP_SET + return CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/rnd/server/oldstation/examine(mob/user) + . = ..() + + if(!in_range(user, src) && !isobserver(user)) + return + + . += span_notice("Insert [EXAMINE_HINT("Research Notes")] to generate points.") /obj/machinery/rnd/server/oldstation/attackby(obj/item/attacking_item, mob/user, params) if(istype(attacking_item, /obj/item/research_notes) && stored_research) diff --git a/code/modules/research/destructive_analyzer.dm b/code/modules/research/destructive_analyzer.dm index 72fd12ced7046..e9e34bfd806f0 100644 --- a/code/modules/research/destructive_analyzer.dm +++ b/code/modules/research/destructive_analyzer.dm @@ -14,16 +14,29 @@ base_icon_state = "d_analyzer" circuit = /obj/item/circuitboard/machine/destructive_analyzer -/obj/machinery/rnd/destructive_analyzer/Initialize(mapload) +/obj/machinery/rnd/destructive_analyzer/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) . = ..() - register_context() -/obj/machinery/rnd/destructive_analyzer/add_context(atom/source, list/context, obj/item/held_item, mob/living/user) + var/screentip_set = FALSE if(loaded_item) context[SCREENTIP_CONTEXT_ALT_LMB] = "Remove Item" + screentip_set = TRUE else if(!isnull(held_item)) context[SCREENTIP_CONTEXT_LMB] = "Insert Item" - return CONTEXTUAL_SCREENTIP_SET + screentip_set = TRUE + + if(screentip_set) + . = CONTEXTUAL_SCREENTIP_SET + +/obj/machinery/rnd/destructive_analyzer/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + return + + if(loaded_item) + . += span_notice("[EXAMINE_HINT("Left-Click")] to remove loaded item inside.") + else + . += span_notice("An item can be loaded inside via [EXAMINE_HINT("Left-Click")].") /obj/machinery/rnd/destructive_analyzer/attackby(obj/item/weapon, mob/living/user, params) if(user.combat_mode) diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index dd282040ab04c..d71e6244e886f 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -1,38 +1,37 @@ /obj/machinery/rnd/production name = "technology fabricator" desc = "Makes researched and prototype items with materials and energy." - layer = BELOW_OBJ_LAYER /// The efficiency coefficient. Material costs and print times are multiplied by this number; - /// better parts result in a higher efficiency (and lower value). var/efficiency_coeff = 1 - /// The material storage used by this fabricator. var/datum/component/remote_materials/materials - /// Which departments are allowed to process this design var/allowed_department_flags = ALL - - /// What's flick()'d on print. + /// Icon state when production has started var/production_animation - /// The types of designs this fabricator can print. var/allowed_buildtypes = NONE - /// All designs in the techweb that can be fabricated by this machine, since the last update. var/list/datum/design/cached_designs - /// What color is this machine's stripe? Leave null to not have a stripe. var/stripe_color = null - - /// Does this charge the user's ID on fabrication? - var/charges_tax = TRUE + ///direction we output onto (if 0, on top of us) + var/drop_direction = 0 /obj/machinery/rnd/production/Initialize(mapload) . = ..() cached_designs = list() - materials = AddComponent(/datum/component/remote_materials, mapload) + materials = AddComponent( + /datum/component/remote_materials, \ + mapload, \ + mat_container_signals = list( \ + COMSIG_MATCONTAINER_ITEM_CONSUMED = TYPE_PROC_REF(/obj/machinery/rnd, local_material_insert) + ) \ + ) + + RegisterSignal(src, COMSIG_SILO_ITEM_CONSUMED, TYPE_PROC_REF(/obj/machinery/rnd/production, silo_material_insert)) AddComponent( /datum/component/payment, \ @@ -42,9 +41,49 @@ TRUE, \ ) - RefreshParts() update_icon(UPDATE_OVERLAYS) +/obj/machinery/rnd/production/Destroy() + materials = null + cached_designs = null + return ..() + + +// Stuff for the stripe on the department machines +/obj/machinery/rnd/production/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver) + . = ..() + + update_icon(UPDATE_OVERLAYS) + +/obj/machinery/rnd/production/update_overlays() + . = ..() + + if(!stripe_color) + return + + var/mutable_appearance/stripe = mutable_appearance('icons/obj/machines/research.dmi', "protolathe_stripe[panel_open ? "_t" : ""]") + stripe.color = stripe_color + . += stripe + + +/obj/machinery/rnd/production/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + return + + . += span_notice("Material usage cost at [efficiency_coeff * 100]%") + if(drop_direction) + . += span_notice("Currently configured to drop printed objects [dir2text(drop_direction)].") + . += span_notice("[EXAMINE_HINT("Alt-click")] to reset.") + else + . += span_notice("[EXAMINE_HINT("Drag")] towards a direction (while next to it) to change drop direction.") + +/obj/machinery/rnd/production/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = ..() + if(drop_direction) + context[SCREENTIP_CONTEXT_ALT_LMB] = "Reset Drop" + return CONTEXTUAL_SCREENTIP_SET + /obj/machinery/rnd/production/connect_techweb(datum/techweb/new_techweb) if(stored_research) UnregisterSignal(stored_research, list(COMSIG_TECHWEB_ADD_DESIGN, COMSIG_TECHWEB_REMOVE_DESIGN)) @@ -55,24 +94,14 @@ RegisterSignals( stored_research, list(COMSIG_TECHWEB_ADD_DESIGN, COMSIG_TECHWEB_REMOVE_DESIGN), - PROC_REF(on_techweb_update) + TYPE_PROC_REF(/obj/machinery/rnd/production, on_techweb_update) ) update_designs() -/obj/machinery/rnd/production/Destroy() - materials = null - cached_designs = null - return ..() - -/obj/machinery/rnd/production/proc/on_techweb_update() - SIGNAL_HANDLER - - // We're probably going to get more than one update (design) at a time, so batch - // them together. - addtimer(CALLBACK(src, PROC_REF(update_designs)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) - /// Updates the list of designs this fabricator can print. /obj/machinery/rnd/production/proc/update_designs() + PROTECTED_PROC(TRUE) + var/previous_design_count = cached_designs.len cached_designs.Cut() @@ -91,12 +120,98 @@ update_static_data_for_all_viewers() +/obj/machinery/rnd/production/proc/on_techweb_update() + SIGNAL_HANDLER + + // We're probably going to get more than one update (design) at a time, so batch + // them together. + addtimer(CALLBACK(src, PROC_REF(update_designs)), 2 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE) + +///When materials are instered via silo link +/obj/machinery/rnd/proc/silo_material_insert(obj/machinery/rnd/machine, container, obj/item/item_inserted, last_inserted_id, list/mats_consumed, amount_inserted) + SIGNAL_HANDLER + + process_item(item_inserted, mats_consumed, amount_inserted) + +/** + * Consumes power for the item inserted either into silo or local storage. + * Arguments + * + * * obj/item/item_inserted - the item to process + * * list/mats_consumed - list of mats consumed + * * amount_inserted - amount of material actually processed + */ +/obj/machinery/rnd/proc/process_item(obj/item/item_inserted, list/mats_consumed, amount_inserted) + PRIVATE_PROC(TRUE) + + if(directly_use_power(round((amount_inserted / SHEET_MATERIAL_AMOUNT) * active_power_usage * 0.00025))) + var/mat_name = "iron" + + var/highest_mat = 0 + for(var/datum/material/mat as anything in mats_consumed) + var/present_mat = mats_consumed[mat] + if(present_mat > highest_mat) + mat_name = initial(mat.name) + if(mat_name == "silver" || mat_name == "titanium" || mat_name == "plastic") //these materials have similar appearances so use an common overlay for them + mat_name = "shiny" + highest_mat = present_mat + + flick_animation(mat_name) +/** + * Plays an visual animation when materials are inserted + * Arguments + * + * * mat_name - the name of the material we are trying to animate on the machine + */ +/obj/machinery/rnd/proc/flick_animation(mat_name) + PROTECTED_PROC(TRUE) + SHOULD_CALL_PARENT(FALSE) + + flick_overlay_view(mutable_appearance('icons/obj/machines/research.dmi', "protolathe_[mat_name]"), 1 SECONDS) + +///When materials are instered into local storage +/obj/machinery/rnd/proc/local_material_insert(container, obj/item/item_inserted, last_inserted_id, list/mats_consumed, amount_inserted, atom/context) + SIGNAL_HANDLER + + process_item(item_inserted, mats_consumed, amount_inserted) + /obj/machinery/rnd/production/RefreshParts() . = ..() - calculate_efficiency() + if(materials) + var/total_storage = 0 + for(var/datum/stock_part/matter_bin/bin in component_parts) + total_storage += bin.tier * 37.5 * SHEET_MATERIAL_AMOUNT + materials.set_local_size(total_storage) + + efficiency_coeff = compute_efficiency() + update_static_data_for_all_viewers() +///Computes this machines cost efficiency based on the available parts +/obj/machinery/rnd/production/proc/compute_efficiency() + PROTECTED_PROC(TRUE) + + var/efficiency = 1.2 + for(var/datum/stock_part/servo/servo in component_parts) + efficiency -= servo.tier * 0.1 + + return efficiency + +/** + * The cost efficiency for an particular design + * Arguments + * + * * path - the design path to check for + */ +/obj/machinery/rnd/production/proc/build_efficiency(path) + PRIVATE_PROC(TRUE) + + if(ispath(path, /obj/item/stack/sheet) || ispath(path, /obj/item/stack/ore/bluespace_crystal)) + return 1 + else + return efficiency_coeff + /obj/machinery/rnd/production/ui_assets(mob/user) return list( get_asset_datum(/datum/asset/spritesheet/sheetmaterials), @@ -117,16 +232,13 @@ var/datum/asset/spritesheet/research_designs/spritesheet = get_asset_datum(/datum/asset/spritesheet/research_designs) var/size32x32 = "[spritesheet.name]32x32" - var/max_multiplier = INFINITY var/coefficient for(var/datum/design/design in cached_designs) var/cost = list() - max_multiplier = INFINITY coefficient = build_efficiency(design.build_path) for(var/datum/material/mat in design.materials) cost[mat.name] = OPTIMAL_COST(design.materials[mat] * coefficient) - max_multiplier = min(max_multiplier, 50, round(materials.mat_container.get_material_amount(mat) / cost[mat.name])) var/icon_size = spritesheet.icon_size_id(design.id) designs[design.id] = list( @@ -135,9 +247,7 @@ "cost" = cost, "id" = design.id, "categories" = design.category, - "icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]", - "constructionTime" = 0, - "maxmult" = max_multiplier + "icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]" ) data["designs"] = designs @@ -158,141 +268,126 @@ /obj/machinery/rnd/production/ui_act(action, list/params, datum/tgui/ui) . = ..() - if(.) return - . = TRUE - switch (action) if("remove_mat") var/datum/material/material = locate(params["ref"]) - var/amount = text2num(params["amount"]) - // SAFETY: eject_sheets checks for valid mats - materials.eject_sheets(material, amount) - - if("build") - user_try_print_id(ui.user, params["ref"], params["amount"]) - -/// Updates the fabricator's efficiency coefficient based on the installed parts. -/obj/machinery/rnd/production/proc/calculate_efficiency() - efficiency_coeff = 1 - - if(materials) - var/total_storage = 0 - - for(var/datum/stock_part/matter_bin/bin in component_parts) - total_storage += bin.tier * (37.5*SHEET_MATERIAL_AMOUNT) - - materials.set_local_size(total_storage) - - var/total_rating = 1.2 - - for(var/datum/stock_part/servo/servo in component_parts) - total_rating -= servo.tier * 0.1 - - efficiency_coeff = max(total_rating, 0) - -/obj/machinery/rnd/production/proc/build_efficiency(path) - if(ispath(path, /obj/item/stack/sheet) || ispath(path, /obj/item/stack/ore/bluespace_crystal)) - return 1 - else - return efficiency_coeff - -/obj/machinery/rnd/production/proc/user_try_print_id(mob/user, design_id, print_quantity) - if(!design_id) - return FALSE - - if(istext(print_quantity)) - print_quantity = text2num(print_quantity) - - if(isnull(print_quantity)) - print_quantity = 1 - - var/datum/design/design = stored_research.researched_designs[design_id] ? SSresearch.techweb_design_by_id(design_id) : null - - if(!istype(design)) - return FALSE - - if(busy) - say("Warning: fabricator is busy!") - return FALSE - - if(!(isnull(allowed_department_flags) || (design.departmental_flags & allowed_department_flags))) - say("This fabricator does not have the necessary keys to decrypt this design.") - return FALSE - - if(design.build_type && !(design.build_type & allowed_buildtypes)) - say("This fabricator does not have the necessary manipulation systems for this design.") - return FALSE + if(!istype(material)) + return - if(!materials.mat_container) - say("No connection to material storage, please contact the quartermaster.") - return FALSE + var/amount = params["amount"] + if(isnull(amount)) + return - if(materials.on_hold()) - say("Mineral access is on hold, please contact the quartermaster.") - return FALSE + amount = text2num(amount) + if(isnull(amount)) + return - print_quantity = clamp(print_quantity, 1, 50) - var/coefficient = build_efficiency(design.build_path) - - // check if sufficient materials are available. - if(!materials.mat_container.has_materials(design.materials, coefficient, print_quantity)) - say("Not enough materials to complete prototype[print_quantity > 1 ? "s" : ""].") - return FALSE - - //use power - var/total_charge = 0 - for(var/material in design.materials) - total_charge += round(design.materials[material] * coefficient * print_quantity / 35) - var/charge_per_item = total_charge / print_quantity - - if(production_animation) - flick(production_animation, src) - - var/total_time = (design.construction_time * design.lathe_time_factor * print_quantity) ** 0.8 - var/time_per_item = total_time / print_quantity - start_making(design, print_quantity, time_per_item, coefficient, charge_per_item) - - return TRUE - -/// Begins the act of making the given design the given number of items -/// Does not check or use materials/power/etc -/obj/machinery/rnd/production/proc/start_making(datum/design/design, build_count, build_time_per_item, build_efficiency, charge_per_item) - PROTECTED_PROC(TRUE) - - busy = TRUE - update_static_data_for_all_viewers() - addtimer(CALLBACK(src, PROC_REF(do_make_item), design, build_efficiency, build_time_per_item, charge_per_item, build_count), build_time_per_item) + materials.eject_sheets(material, amount) + return TRUE -/// Callback for start_making, actually makes the item -/// Called using timers started by start_making -/obj/machinery/rnd/production/proc/do_make_item(datum/design/design, build_efficiency, time_per_item, charge_per_item, items_remaining) + if("build") + if(busy) + say("Warning: fabricator is busy!") + return + + //validate design + var/design_id = params["ref"] + if(!design_id) + return + var/datum/design/design = stored_research.researched_designs[design_id] ? SSresearch.techweb_design_by_id(design_id) : null + if(!istype(design)) + return FALSE + if(!(isnull(allowed_department_flags) || (design.departmental_flags & allowed_department_flags))) + say("This fabricator does not have the necessary keys to decrypt this design.") + return FALSE + if(design.build_type && !(design.build_type & allowed_buildtypes)) + say("This fabricator does not have the necessary manipulation systems for this design.") + return FALSE + + //validate print quantity + var/print_quantity = params["amount"] + if(isnull(print_quantity)) + return + print_quantity = text2num(print_quantity) + if(isnull(print_quantity)) + return + print_quantity = clamp(print_quantity, 1, 50) + + //efficiency for this design, stacks use exact materials + var/coefficient = build_efficiency(design.build_path) + + //check for materials + if(!materials.can_use_resource()) + return + if(!materials.mat_container.has_materials(design.materials, coefficient, print_quantity)) + say("Not enough materials to complete prototype[print_quantity > 1 ? "s" : ""].") + return FALSE + + //compute power & time to print 1 item + var/charge_per_item = 0 + for(var/material in design.materials) + charge_per_item += design.materials[material] + charge_per_item = min(active_power_usage, round(charge_per_item * coefficient)) + var/build_time_per_item = (design.construction_time * design.lathe_time_factor) ** 0.8 + + //start production + busy = TRUE + SStgui.update_uis(src) + if(production_animation) + icon_state = production_animation + var/turf/target_location + if(drop_direction) + target_location = get_step(src, drop_direction) + if(isclosedturf(target_location)) + target_location = get_turf(src) + else + target_location = get_turf(src) + addtimer(CALLBACK(src, PROC_REF(do_make_item), design, print_quantity, build_time_per_item, coefficient, charge_per_item, target_location), build_time_per_item) + + return TRUE + +/** + * Callback for start_making, actually makes the item + * Arguments + * + * * datum/design/design - the design we are trying to print + * * items_remaining - the number of designs left out to print + * * build_time_per_item - the time taken to print 1 item + * * material_cost_coefficient - the cost efficiency to print 1 design + * * charge_per_item - the amount of power to print 1 item + * * turf/target - the location to drop the printed item on +*/ +/obj/machinery/rnd/production/proc/do_make_item(datum/design/design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, turf/target) PROTECTED_PROC(TRUE) if(!items_remaining) // how finalize_build() return - if(!directly_use_power(charge_per_item)) + if(!is_operational || !directly_use_power(charge_per_item)) say("Unable to continue production, power failure.") finalize_build() return + if(!materials.can_use_resource()) + finalize_build() + return var/is_stack = ispath(design.build_path, /obj/item/stack) var/list/design_materials = design.materials - if(!materials.mat_container.has_materials(design_materials, build_efficiency, is_stack ? items_remaining : 1)) + if(!materials.mat_container.has_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1)) say("Unable to continue production, missing materials.") return - materials.use_materials(design_materials, build_efficiency, is_stack ? items_remaining : 1, "built", "[design.name]") + materials.use_materials(design_materials, material_cost_coefficient, is_stack ? items_remaining : 1, "built", "[design.name]") var/atom/movable/created if(is_stack) - created = new design.build_path(get_turf(src), items_remaining) + created = new design.build_path(target, items_remaining) else - created = new design.build_path(get_turf(src)) - split_materials_uniformly(design_materials, build_efficiency, created) + created = new design.build_path(target) + split_materials_uniformly(design_materials, material_cost_coefficient, created) created.pixel_x = created.base_pixel_x + rand(-6, 6) created.pixel_y = created.base_pixel_y + rand(-6, 6) @@ -305,40 +400,36 @@ if(!items_remaining) finalize_build() return - addtimer(CALLBACK(src, PROC_REF(do_make_item), design, build_efficiency, time_per_item, items_remaining), time_per_item) + addtimer(CALLBACK(src, PROC_REF(do_make_item), design, items_remaining, build_time_per_item, material_cost_coefficient, charge_per_item, target), build_time_per_item) /// Resets the busy flag /// Called at the end of do_make_item's timer loop /obj/machinery/rnd/production/proc/finalize_build() PROTECTED_PROC(TRUE) - busy = FALSE - update_static_data_for_all_viewers() -// Stuff for the stripe on the department machines -/obj/machinery/rnd/production/default_deconstruction_screwdriver(mob/user, icon_state_open, icon_state_closed, obj/item/screwdriver) - . = ..() - - update_icon(UPDATE_OVERLAYS) + busy = FALSE + SStgui.update_uis(src) + icon_state = initial(icon_state) -/obj/machinery/rnd/production/update_overlays() +/obj/machinery/rnd/production/MouseDrop(atom/over, src_location, over_location, src_control, over_control, params) . = ..() - - if(!stripe_color) + if((!issilicon(usr) && !isAdminGhostAI(usr)) && !Adjacent(usr)) return + if(busy) + balloon_alert(usr, "busy printing!") + return + var/direction = get_dir(src, over_location) + if(!direction) + return + drop_direction = direction + balloon_alert(usr, "dropping [dir2text(drop_direction)]") - var/mutable_appearance/stripe = mutable_appearance('icons/obj/machines/research.dmi', "protolate_stripe") - - if(!panel_open) - stripe.icon_state = "protolathe_stripe" - else - stripe.icon_state = "protolathe_stripe_t" - - stripe.color = stripe_color - - . += stripe - -/obj/machinery/rnd/production/examine(mob/user) +/obj/machinery/rnd/production/AltClick(mob/user) . = ..() - - if(in_range(user, src) || isobserver(user)) - . += span_notice("The status display reads: Storing up to [materials.local_size] material units.
Material consumption at [efficiency_coeff * 100]%.
Build time reduced by [100 - efficiency_coeff * 100]%.") + if(!drop_direction || !can_interact(user)) + return + if(busy) + balloon_alert(user, "busy printing!") + return + balloon_alert(user, "drop direction reset") + drop_direction = 0 diff --git a/code/modules/research/machinery/circuit_imprinter.dm b/code/modules/research/machinery/circuit_imprinter.dm index 8127bc45d0ab0..2dcbde23663ad 100644 --- a/code/modules/research/machinery/circuit_imprinter.dm +++ b/code/modules/research/machinery/circuit_imprinter.dm @@ -2,23 +2,22 @@ name = "circuit imprinter" desc = "Manufactures circuit boards for the construction of machines." icon_state = "circuit_imprinter" - circuit = /obj/item/circuitboard/machine/circuit_imprinter production_animation = "circuit_imprinter_ani" + circuit = /obj/item/circuitboard/machine/circuit_imprinter allowed_buildtypes = IMPRINTER -/obj/machinery/rnd/production/circuit_imprinter/calculate_efficiency() - . = ..() - +/obj/machinery/rnd/production/circuit_imprinter/compute_efficiency() var/rating = 0 - for(var/datum/stock_part/servo/servo in component_parts) - rating += servo.tier // There is only one. + rating += servo.tier + + return 0.5 ** max(rating - 1, 0) // One sheet, half sheet, quarter sheet, eighth sheet. - efficiency_coeff = 0.5 ** max(rating - 1, 0) // One sheet, half sheet, quarter sheet, eighth sheet. +/obj/machinery/rnd/production/circuit_imprinter/flick_animation(mat_name) + return //we presently have no animation /obj/machinery/rnd/production/circuit_imprinter/offstation name = "ancient circuit imprinter" desc = "Manufactures circuit boards for the construction of machines. Its ancient construction may limit its ability to print all known technology." allowed_buildtypes = AWAY_IMPRINTER circuit = /obj/item/circuitboard/machine/circuit_imprinter/offstation - charges_tax = FALSE diff --git a/code/modules/research/machinery/departmental_protolathe.dm b/code/modules/research/machinery/departmental_protolathe.dm index dc0b882ef5176..81e83f8451e59 100644 --- a/code/modules/research/machinery/departmental_protolathe.dm +++ b/code/modules/research/machinery/departmental_protolathe.dm @@ -11,10 +11,6 @@ stripe_color = "#EFB341" payment_department = ACCOUNT_ENG -/obj/machinery/rnd/production/protolathe/department/engineering/no_tax - circuit = /obj/item/circuitboard/machine/protolathe/department/engineering/no_tax - charges_tax = FALSE - /obj/machinery/rnd/production/protolathe/department/service name = "department protolathe (Service)" allowed_department_flags = DEPARTMENT_BITFLAG_SERVICE diff --git a/code/modules/research/machinery/protolathe.dm b/code/modules/research/machinery/protolathe.dm index b48f64ca9d74a..fb912bba67ca0 100644 --- a/code/modules/research/machinery/protolathe.dm +++ b/code/modules/research/machinery/protolathe.dm @@ -23,4 +23,3 @@ desc = "Converts raw materials into useful objects. Its ancient construction may limit its ability to print all known technology." circuit = /obj/item/circuitboard/machine/protolathe/offstation allowed_buildtypes = AWAY_LATHE - charges_tax = FALSE diff --git a/code/modules/research/machinery/techfab.dm b/code/modules/research/machinery/techfab.dm index 38df3fc038df1..7f1428882e7d3 100644 --- a/code/modules/research/machinery/techfab.dm +++ b/code/modules/research/machinery/techfab.dm @@ -3,6 +3,5 @@ desc = "Produces researched prototypes with raw materials and energy." icon_state = "protolathe" circuit = /obj/item/circuitboard/machine/techfab - console_link = FALSE production_animation = "protolathe_n" allowed_buildtypes = PROTOLATHE | IMPRINTER diff --git a/code/modules/research/rdmachines.dm b/code/modules/research/rdmachines.dm index 4ab9d73dca7b5..a8fa3e36d4735 100644 --- a/code/modules/research/rdmachines.dm +++ b/code/modules/research/rdmachines.dm @@ -7,21 +7,22 @@ icon = 'icons/obj/machines/research.dmi' density = TRUE use_power = IDLE_POWER_USE + + ///Are we currently printing a machine var/busy = FALSE + ///Is this machne hacked via wires var/hacked = FALSE - var/console_link = TRUE //allow console link. + ///Is this machine disabled via wires var/disabled = FALSE - /// Ref to global science techweb. + ///Ref to global science techweb. var/datum/techweb/stored_research ///The item loaded inside the machine, used by experimentors and destructive analyzers only. var/obj/item/loaded_item -/obj/machinery/rnd/proc/reset_busy() - busy = FALSE - /obj/machinery/rnd/Initialize(mapload) . = ..() set_wires(new /datum/wires/rnd(src)) + register_context() /obj/machinery/rnd/LateInitialize() . = ..() @@ -37,6 +38,46 @@ QDEL_NULL(wires) return ..() +/obj/machinery/rnd/examine(mob/user) + . = ..() + if(!in_range(user, src) && !isobserver(user)) + return + + . += span_notice("A [EXAMINE_HINT("multitool")] with techweb designs can be uploaded here.") + . += span_notice("Its maintainence panel can be [EXAMINE_HINT("screwed")] [panel_open ? "closed" : "open"].") + if(panel_open) + . += span_notice("Use a [EXAMINE_HINT("multitool")] or [EXAMINE_HINT("wirecutters")] to interact with wires.") + . += span_notice("The machine can be [EXAMINE_HINT("pried")] apart.") + +/obj/machinery/rnd/add_context(atom/source, list/context, obj/item/held_item, mob/user) + . = NONE + if(isnull(held_item)) + return + + if(held_item.tool_behaviour == TOOL_SCREWDRIVER) + context[SCREENTIP_CONTEXT_LMB] = "[panel_open ? "Close" : "Open"] Panel" + context[SCREENTIP_CONTEXT_RMB] = "[panel_open ? "Close" : "Open"] Panel" + return CONTEXTUAL_SCREENTIP_SET + + if(panel_open) + var/msg + if(held_item.tool_behaviour == TOOL_CROWBAR) + msg = "Deconstruct" + else if(is_wire_tool(held_item)) + msg = "Open Wires" + + if(msg) + context[SCREENTIP_CONTEXT_LMB] = msg + context[SCREENTIP_CONTEXT_RMB] = msg + return CONTEXTUAL_SCREENTIP_SET + else + if(held_item.tool_behaviour == TOOL_MULTITOOL) + var/obj/item/multitool/tool = held_item + if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb)) + context[SCREENTIP_CONTEXT_LMB] = "Upload Techweb" + context[SCREENTIP_CONTEXT_RMB] = "Upload Techweb" + return CONTEXTUAL_SCREENTIP_SET + ///Called when attempting to connect the machine to a techweb, forgetting the old. /obj/machinery/rnd/proc/connect_techweb(datum/techweb/new_techweb) if(stored_research) @@ -49,52 +90,42 @@ /obj/machinery/rnd/proc/on_connected_techweb() SHOULD_CALL_PARENT(FALSE) -/obj/machinery/rnd/proc/shock(mob/user, prb) - if(machine_stat & (BROKEN|NOPOWER)) // unpowered, no shock - return FALSE - if(!prob(prb)) - return FALSE - do_sparks(5, TRUE, src) - if (electrocute_mob(user, get_area(src), src, 0.7, TRUE)) - return TRUE - else - return FALSE +///Reset the state of this machine +/obj/machinery/rnd/proc/reset_busy() + busy = FALSE /obj/machinery/rnd/crowbar_act(mob/living/user, obj/item/tool) return default_deconstruction_crowbar(tool) /obj/machinery/rnd/crowbar_act_secondary(mob/living/user, obj/item/tool) - return default_deconstruction_crowbar(tool) + return crowbar_act(user, tool) /obj/machinery/rnd/screwdriver_act(mob/living/user, obj/item/tool) return default_deconstruction_screwdriver(user, "[initial(icon_state)]_t", initial(icon_state), tool) /obj/machinery/rnd/screwdriver_act_secondary(mob/living/user, obj/item/tool) - return default_deconstruction_screwdriver(user, "[initial(icon_state)]_t", initial(icon_state), tool) + return screwdriver_act(user, tool) /obj/machinery/rnd/multitool_act(mob/living/user, obj/item/multitool/tool) + . = ITEM_INTERACT_BLOCKING if(panel_open) wires.interact(user) - return TRUE + return ITEM_INTERACT_SUCCESS if(!QDELETED(tool.buffer) && istype(tool.buffer, /datum/techweb)) connect_techweb(tool.buffer) - return TRUE - return FALSE + return ITEM_INTERACT_SUCCESS /obj/machinery/rnd/multitool_act_secondary(mob/living/user, obj/item/tool) - if(panel_open) - wires.interact(user) - return TRUE + return multitool_act(user, tool) /obj/machinery/rnd/wirecutter_act(mob/living/user, obj/item/tool) + . = ITEM_INTERACT_BLOCKING if(panel_open) wires.interact(user) - return TRUE + return ITEM_INTERACT_SUCCESS /obj/machinery/rnd/wirecutter_act_secondary(mob/living/user, obj/item/tool) - if(panel_open) - wires.interact(user) - return TRUE + return wirecutter_act(user, tool) //whether the machine can have an item inserted in its current state. /obj/machinery/rnd/proc/is_insertion_ready(mob/user) @@ -123,15 +154,3 @@ if(loaded_item) loaded_item.forceMove(drop_location()) ..() - -/obj/machinery/rnd/proc/AfterMaterialInsert(item_inserted, id_inserted, amount_inserted) - var/stack_name - if(istype(item_inserted, /obj/item/stack/ore/bluespace_crystal)) - stack_name = "bluespace" - use_power(SHEET_MATERIAL_AMOUNT / 10) - else - var/obj/item/stack/S = item_inserted - stack_name = S.name - use_power(min(active_power_usage, (amount_inserted / 100))) - add_overlay("protolathe_[stack_name]") - addtimer(CALLBACK(src, TYPE_PROC_REF(/atom, cut_overlay), "protolathe_[stack_name]"), 10) diff --git a/code/modules/wiremod/core/component_printer.dm b/code/modules/wiremod/core/component_printer.dm index 3fb736540ec18..7c691e3a4c47b 100644 --- a/code/modules/wiremod/core/component_printer.dm +++ b/code/modules/wiremod/core/component_printer.dm @@ -184,8 +184,7 @@ "cost" = cost, "id" = researched_design_id, "categories" = design.category, - "icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]", - "constructionTime" = -1 + "icon" = "[icon_size == size32x32 ? "" : "[icon_size] "][design.id]" ) data["designs"] = designs diff --git a/icons/obj/machines/research.dmi b/icons/obj/machines/research.dmi index 02d848eb4e9dab872564abb8b9cac79bb5662a98..544054279e97af245675d35f021ae8328d8bda33 100644 GIT binary patch literal 28134 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 literal 26907 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
    YW1M)!$&WC0>ZVCwTYLrS}!RH`T$WZho8v)qIDk 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

    #3O4 zCRuGV_^CHwQL;MV^wJ+(vH>Fx;@DOZlHc;Vm0*8sdJ47xpL<@RQNoApH@ZE!jeD=F z^7@bHLs1wG6IUd3Me~V2v2y1jdCiicGY&;at0C9+5!%GwB9Xt4<(s61&YR}sw}YAs z51kY`Ki<~%NZyk{yjcolZocM;$pp|T`Zh_1)1KVi+ymdWKO3%{{kS{&?c1Gj7+GN* zb+?LJ%S$n`Ein<6)yLXZ`{=SKK1C<{16+~dJ!AR(b*Jt=PLhMSrH%~>w6u(Q5AvQr zB7cq!_r&eN&_7a)>UR%x4SVgYN_!H2d)Ejph?z!xMy{M(qk5UV=XOjzxF5L1%-GL; z9pS-gM@^7O6vBs>*ao><38)j=vaf~ln87jUH|eCdZ$Q^-i)a=vAGe%=0=LmjnQVc? zYtAH6do?LGNHm`GO3srhfT0EDAG$w_}^;So{?Ex1S!9x;N6Ex^{Ek$paWSd=4#@P17FWI&-hKc zK=-0?chVD8H9t2z@yUYl$`NdQGVeIG@$w4KA$$BWchMX_4^pjRr5)Na{N$s=b>QFe zan{;2`}E46rRZ};fkQ(>cQmhbQbFda(I#e|OqEJS1-!q0{CK+v7c>ERCje~0&wR4Z zrMwZd%?EHWAt51!Px80mHOuSkO@M&IsMAOO5+@gz`oo8;kA9iGo}RY&keDbaE`FxF zyL;=H2nzpPFn;u?0ejGBC1us0p=oDl7dYk8Iy=iJBrM!NKK{D8TFGeV8Um4c|FA{a z(B6nm=tf6s^`rCa_zE27yrSxP61FlB8`W#H^WK+ujBCEv8)0e5`1P?bmjrVEw@&Q5 zpv6uLIVqdWLZ^A9V)J)Q;>_W~he7rywPLp827eR6kW}*8aKdoLsV>j>=N-m0nZ&m4 zZX<-2!S0*SSy@>oMb>n1INVD1b5Q`^-P6aX(E(*05O9e|ByLUqVbBYyw*qRSp{Yq% zTwENVk`g;KWEc}0TkleCaYigo>AjE25U?8QU z@J<#P2e{1dgO$1-h z6xZf%v}YYmKIg&l;Uewn07Wz2&voV5c&9GvZ=D{}FZ%iKnVNEmh={}|CB2@Xx8B>^ z<6}N)1^NoF;YmnH$jQxpD2~p|q+x;)q<&e{M3G2{WgPrlc6OtaEM_3K+?ZdKTo4o#1XJRwxVR?pl`)=3t<9vv-`@gglasX69iEAuSN{H$ zE1$^sAZh_Ie zUAE`CI`cmP>BHfQPO@CZB_;h66VU`hz+r&Orw2l%Wn}__g6b$jdOANqhXnK$j}{o++qFu_=B3X&gRY^41ZWe9`Do8F>i9 zh@4n@PUXHb0J`_L&hCbQs3-#vTU*^vqujpXVY;;5h^VO3jEsy{V73F(q~hh8yT&=)O*R{_U!(=#i@}Q0gsZjU zm;ODwgB|%m7nv-a)blMT=cJdHSK+u5CnslJ_h-2|9DHsU4<_EJf>DN&@tAtf)|AEI zVTb1S_rlWGuLl>PNf#+khvP&lD=Pt0CU`OZT?5xX-Ys}9fQ-9J|)5^G&s2HJ4mvwGjIYh^9B{A7Cp zjLp9-tONaqCohi>-PnQoe&$b1-z_d_2Ie33m#!UqNaG(#7EWA3@qd_J)#^atBnF>@ zhj>m*xT3ke^16AXPc(XBr%;Zg=H?gM0v2T~s|XmB*3%A?P|*vCdP)YT9gUfe*q5#q zXtMLCH+PtwzuM{;*C2OpJ%~J?HJjlyCOQRomKSNqt4AzrroM zo>2I-AXL&~;99RBcY$(Luz$yPxhZcW?^)v6(WR@F+SiJNI{asM!kJXZJn&v9Da!!F z*UIGb;?#x&QDb0C1_GT0=X$G-18#XYRs7DslWq+EHbO#V=7zkgNn>!1945OhM3@{NG(7SKW1_U>Q(xb9T5Jq~zps!s) zro9V3krx&Wwe{&Do2`RfeXssv`pn)<-3v=ji{nKf@I=SSw_66k{jUW@a4I)K z`Q^^dB5O`Xi(9U}Xu9$f@+0C#Z%#2wTTTE23kQFSx{ajT7%LL|)q6b+-=*6{zzv&z zucp2o`eR3P>X}ZcpTaqj{(%1J={ErS+r27dQNRIKGLYg#SrAy|$vR5-_sWNj=`;54 zQtV6unQK=!TjEeI8j&@UCryQW_u1t5rgl3v?#oQ2*V=b;_C=XA z;0KCGb|A!JvypFLu_{uz`;)JK>~P$}(n~cPNohP8Kc37T_=LT<`UnxJxEtZ??s?zv zpA4bg;nP)9XRD@ohH=Lada@?s$U+3p(#E+u8$m&^7K5LT<(k26SBYKq5>y*gx6a7I z(vdEKptp1PD`d4%seBseBVv2=^DXvd7}sle5`7(YD3sTph9nol?8sD*mDX@qE65PW zYS#pm7$)uSki*jU$`&UT0#FWYBVTUOLw9s_Ig%x99TOy%I-L6)@C&kbMFe1h35tIU zfUe#dO#hZgedRfu?*f75F!2}`2k$*v6+hJ1Vj#Fy2l9HAc8zmhqAyQts^1Xq_nxEu zBk@HqwY3Ai`8$u$bs((@vGdVd-S_bslIIIrb-4R?jL$#K-{_sc;=qHj!%Sja&MNiK zAvCThMvR>yRpI>Pv7*N!W6u`_8ps!0GvWW#GXF^y;iDY2!0v)m{?3Bg?=u;@f)jj- zu<=oJa%o4R+$`I)#W$D9?++~XNqiOxyvNKj6C8SoMG=_$dYnl}d>G<3=`O7jedyPM z{9*3#GVO*k=Yfx#vwVsIlHF63QQ27#Z4j2F_A&yP!`dlMjJn9m+L}7>g+f<_i~MRV zs;g0keojRu5HwLXHO=7Y;!;Z-vum)vK7`Br-3yi0(JytahWxvWGRMKRT@F&}B(iC<$x(|I`@ zH9}1mbgT}tw$tR=4C=?_JOy^-Kk7I>vF7avUzCg|b~W_eICrt758l(~*iy8Z+f#?TS##Y zkK5a;n{&Im`vPoyFyt=PY%_Q2n+tYonoYnTH|%)~x&QZ(!TY zEWR!;p#AEVGKiOj1LV3#W|EZv_UBy<4Qsbc%mM-e=iVsueHa47NgChI><2Qlu)K8> zQ;m#_ydw{eVc=PlF?4sPVC(u4abgZgv$5`WaL;ZoDwqCmHm{O68ww`jtYF)y3-e9Z zJjuesGL>+FA$~?Fqmh;O&e+siUB8i>M(Jx%hrI&a7)Gb4Zs%_83Krnmi~5NQ1Et^576kKy*3E%MgwiA#p5XMb#s&9gFFID&pIRCk4mMQp zGyuC;o);@94i!RoMSubMMDFPme{Yi6O1hs1nPEMQU*JidNnre<8Uqtp^O9NwLP6WD zK}`TB!@y^fZ#o*fv^!I3yo){yZA~wqfDUhmhoOQZBbz8M077FQJuY*d`Q?5;qdknC zh2;u8JKx<<<8o$}{($jJnIt1B?uQ zeg@{G0Dru%Yw_(}*66bW8Gjo+{%u1Anf~q_j$Ci`u3hlaBj~QCj>a!FtKk0uM}A%F diff --git a/tgui/packages/tgui/interfaces/Autolathe.tsx b/tgui/packages/tgui/interfaces/Autolathe.tsx index 45d7051e437ae..8d5d5bafa16a6 100644 --- a/tgui/packages/tgui/interfaces/Autolathe.tsx +++ b/tgui/packages/tgui/interfaces/Autolathe.tsx @@ -19,12 +19,16 @@ import { MaterialCostSequence } from './Fabrication/MaterialCostSequence'; import { Design, MaterialMap } from './Fabrication/Types'; import { Material } from './Fabrication/Types'; +type AutolatheDesign = Design & { + customMaterials: BooleanLike; +}; + type AutolatheData = { materials: Material[]; materialtotal: number; materialsmax: number; SHEET_MATERIAL_AMOUNT: number; - designs: Design[]; + designs: AutolatheDesign[]; active: BooleanLike; }; @@ -174,7 +178,7 @@ const PrintButton = (props: PrintButtonProps) => { }; type AutolatheRecipeProps = { - design: Design; + design: AutolatheDesign; availableMaterials: MaterialMap; SHEET_MATERIAL_AMOUNT: number; }; @@ -183,7 +187,38 @@ const AutolatheRecipe = (props: AutolatheRecipeProps) => { const { act } = useBackend(); const { design, availableMaterials, SHEET_MATERIAL_AMOUNT } = props; - const maxmult = design.maxmult; + let maxmult = 0; + if (design.customMaterials) { + const smallest_mat = + Object.entries(availableMaterials).reduce( + (accumulator: number, [material, amount]) => { + return Math.min(accumulator, amount); + }, + Infinity, + ) || 0; + + if (smallest_mat > 0) { + maxmult = Object.entries(design.cost).reduce( + (accumulator: number, [material, required]) => { + return Math.min(accumulator, smallest_mat / required); + }, + Infinity, + ); + } else { + maxmult = 0; + } + } else { + maxmult = Object.entries(design.cost).reduce( + (accumulator: number, [material, required]) => { + return Math.min( + accumulator, + (availableMaterials[material] || 0) / required, + ); + }, + Infinity, + ); + } + maxmult = Math.min(Math.floor(maxmult), 50); const canPrint = maxmult > 0; return ( diff --git a/tgui/packages/tgui/interfaces/ExosuitFabricator.tsx b/tgui/packages/tgui/interfaces/ExosuitFabricator.tsx index 07c0daabe3203..e78aae9591bd0 100644 --- a/tgui/packages/tgui/interfaces/ExosuitFabricator.tsx +++ b/tgui/packages/tgui/interfaces/ExosuitFabricator.tsx @@ -9,8 +9,13 @@ import { MaterialAccessBar } from './Fabrication/MaterialAccessBar'; import { MaterialCostSequence } from './Fabrication/MaterialCostSequence'; import { Design, FabricatorData, MaterialMap } from './Fabrication/Types'; +type ExosuitDesign = Design & { + constructionTime: number; +}; + type ExosuitFabricatorData = FabricatorData & { processing: BooleanLike; + designs: Record; }; export const ExosuitFabricator = (props) => { diff --git a/tgui/packages/tgui/interfaces/Fabrication/Types.ts b/tgui/packages/tgui/interfaces/Fabrication/Types.ts index e05b977dd7e6c..f214252ef70aa 100644 --- a/tgui/packages/tgui/interfaces/Fabrication/Types.ts +++ b/tgui/packages/tgui/interfaces/Fabrication/Types.ts @@ -68,16 +68,6 @@ export type Design = { * 32x32.** */ icon: string; - - /** - * The amount of time, in seconds, that this design takes to print. - */ - constructionTime: number; - - /** - * The maximum number of items than can be printed - */ - maxmult: number; }; /** diff --git a/tgui/packages/tgui/interfaces/Fabricator.tsx b/tgui/packages/tgui/interfaces/Fabricator.tsx index 1b02e9f8639b2..0511907618023 100644 --- a/tgui/packages/tgui/interfaces/Fabricator.tsx +++ b/tgui/packages/tgui/interfaces/Fabricator.tsx @@ -116,10 +116,14 @@ type CustomPrintProps = { const CustomPrint = (props: CustomPrintProps) => { const { act } = useBackend(); const { design, available } = props; - const canPrint = !Object.entries(design.cost).some( - ([material, amount]) => - !available[material] || amount > (available[material] ?? 0), + let maxMult = Object.entries(design.cost).reduce( + (accumulator: number, [material, required]) => { + return Math.min(accumulator, (available[material] || 0) / required); + }, + Infinity, ); + maxMult = Math.min(Math.floor(maxMult), 50); + const canPrint = maxMult > 0; return (

    { }) } > - [Max: {design.maxmult}] + [Max: {maxMult}]
    ); From ab69286f9c3e974c74d7834482072e923e0de90c Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:34:21 +1300 Subject: [PATCH 004/508] Automatic changelog for PR #81244 [ci skip] --- html/changelogs/AutoChangeLog-pr-81244.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81244.yml diff --git a/html/changelogs/AutoChangeLog-pr-81244.yml b/html/changelogs/AutoChangeLog-pr-81244.yml new file mode 100644 index 0000000000000..164e0b7edb81d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81244.yml @@ -0,0 +1,12 @@ +author: "SyncIt21" +delete-after: True +changes: + - qol: "adds screentips & examines for screwdriver & crowbar acts & alt click." + - qol: "techfabs can now use the mouse drop functionality to set drop target." + - qol: "lathe printing animation plays on loop while printing rather than flicking once for more visual feedback" + - bugfix: "lathe sheet insertion animations are now linked & work again for all material types inserted via remote silo/local storage" + - bugfix: "printing custom materials items from autolathe works again." + - bugfix: "printing multiple items from lathes will actually print that correct quantity of items requested." + - bugfix: "printing items the 2nd time around from lathes won't cause the UI to reload each time." + - code_imp: "autodoc for some vars & procs, merges procs." + - refactor: "Optimized code for autolathe & techfabs in general. Report bugs on github" \ No newline at end of file From a40986353d1337cd2f2c7b136a6e7ce2e92e8fe6 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 8 Feb 2024 06:37:02 -0600 Subject: [PATCH 005/508] Fixes surgeries runtiming constantly when having the surgery initator open, fixes some surgeries missing sounds (#81307) ## About The Pull Request Fixes #79318 - See the issue for more information. I fixed the runtimes as expected, and then removed `SURGERY_REQUIRE_LIMB` from some surgeries which don't actually require a limb, such as implant removal, dissection, and living revival. I could've easily missed some, and as a result some surgeries are lost to the void and unselectable, but from what I could tell in testing it seems... fine. - Adds `SHOULD_CALL_PARENT` to surgery `can_start`. Cleans up some surgery `can_start` overrides. - Adds missing sounds to puncture repair surgery. ## Changelog :cl: Melbert fix: Fixed Puncture Repair surgery not having surgical sounds fix: Fixed Surgery Initiator potentially showing invalid surgeries /:cl: --- code/datums/components/surgery_initiator.dm | 49 +++++++++---------- .../abductor/equipment/abduction_surgery.dm | 8 +-- code/modules/surgery/autopsy.dm | 3 +- code/modules/surgery/bone_mending.dm | 21 -------- code/modules/surgery/brain_surgery.dm | 5 +- code/modules/surgery/burn_dressing.dm | 14 +++--- code/modules/surgery/core_removal.dm | 4 +- code/modules/surgery/coronary_bypass.dm | 7 ++- code/modules/surgery/ear_surgery.dm | 5 +- .../surgery/experimental_dissection.dm | 11 +++-- code/modules/surgery/eye_surgery.dm | 3 +- code/modules/surgery/gastrectomy.dm | 8 ++- code/modules/surgery/healing.dm | 8 ++- code/modules/surgery/hepatectomy.dm | 7 ++- code/modules/surgery/implant_removal.dm | 2 + code/modules/surgery/lipoplasty.dm | 12 +++-- code/modules/surgery/lobectomy.dm | 7 ++- .../modules/surgery/prosthetic_replacement.dm | 2 + code/modules/surgery/repair_puncture.dm | 15 +++--- code/modules/surgery/revival.dm | 3 +- code/modules/surgery/surgery.dm | 15 ++++-- 21 files changed, 99 insertions(+), 110 deletions(-) diff --git a/code/datums/components/surgery_initiator.dm b/code/datums/components/surgery_initiator.dm index 1cc60d78b5557..f4bd7f0d9f500 100644 --- a/code/datums/components/surgery_initiator.dm +++ b/code/datums/components/surgery_initiator.dm @@ -81,32 +81,31 @@ /datum/component/surgery_initiator/proc/get_available_surgeries(mob/user, mob/living/target) var/list/available_surgeries = list() - var/mob/living/carbon/carbon_target - var/obj/item/bodypart/affecting - if (iscarbon(target)) - carbon_target = target - affecting = carbon_target.get_bodypart(check_zone(user.zone_selected)) + var/obj/item/bodypart/affecting = target.get_bodypart(check_zone(user.zone_selected)) for(var/datum/surgery/surgery as anything in GLOB.surgeries_list) if(!surgery.possible_locs.Find(user.zone_selected)) continue - if(affecting) - if(!(surgery.surgery_flags & SURGERY_REQUIRE_LIMB)) + if(!is_type_in_list(target, surgery.target_mobtypes)) + continue + + if(isnull(affecting)) + if(surgery.surgery_flags & SURGERY_REQUIRE_LIMB) continue + else if(surgery.requires_bodypart_type && !(affecting.bodytype & surgery.requires_bodypart_type)) continue + if(surgery.targetable_wound && !affecting.get_wound_type(surgery.targetable_wound)) + continue if((surgery.surgery_flags & SURGERY_REQUIRES_REAL_LIMB) && (affecting.bodypart_flags & BODYPART_PSEUDOPART)) continue - else if(carbon_target && (surgery.surgery_flags & SURGERY_REQUIRE_LIMB)) //mob with no limb in surgery zone when we need a limb - continue + if(IS_IN_INVALID_SURGICAL_POSITION(target, surgery)) continue if(!surgery.can_start(user, target)) continue - for(var/path in surgery.target_mobtypes) - if(istype(target, path)) - available_surgeries += surgery - break + + available_surgeries += surgery return available_surgeries @@ -284,24 +283,20 @@ target.balloon_alert(user, "can't start the surgery!") return - var/obj/item/bodypart/affecting_limb - var/selected_zone = user.zone_selected + var/obj/item/bodypart/affecting_limb = target.get_bodypart(check_zone(selected_zone)) - if (iscarbon(target)) - var/mob/living/carbon/carbon_target = target - affecting_limb = carbon_target.get_bodypart(check_zone(selected_zone)) - - if ((surgery.surgery_flags & SURGERY_REQUIRE_LIMB) == isnull(affecting_limb)) - if (surgery.surgery_flags & SURGERY_REQUIRE_LIMB) - target.balloon_alert(user, "patient has no [parse_zone(selected_zone)]!") - else - target.balloon_alert(user, "patient has \a [parse_zone(selected_zone)]!") + if ((surgery.surgery_flags & SURGERY_REQUIRE_LIMB) && isnull(affecting_limb)) + target.balloon_alert(user, "patient has no [parse_zone(selected_zone)]!") return - if (!isnull(affecting_limb) && surgery.requires_bodypart_type && !(affecting_limb.bodytype & surgery.requires_bodypart_type)) - target.balloon_alert(user, "not the right type of limb!") - return + if (!isnull(affecting_limb)) + if(surgery.requires_bodypart_type && !(affecting_limb.bodytype & surgery.requires_bodypart_type)) + target.balloon_alert(user, "not the right type of limb!") + return + if(surgery.targetable_wound && !affecting_limb.get_wound_type(surgery.targetable_wound)) + target.balloon_alert(user, "no wound to operate on!") + return if (IS_IN_INVALID_SURGICAL_POSITION(target, surgery)) target.balloon_alert(user, "patient is not lying down!") diff --git a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm index a23eb3c025230..a02d89e6b1d47 100644 --- a/code/modules/antagonists/abductor/equipment/abduction_surgery.dm +++ b/code/modules/antagonists/abductor/equipment/abduction_surgery.dm @@ -14,10 +14,12 @@ /datum/surgery/organ_extraction/can_start(mob/user, mob/living/carbon/target) if(!ishuman(user)) return FALSE - var/mob/living/carbon/human/H = user - if(H.dna.species.id == SPECIES_ABDUCTOR) + if(!..()) + return FALSE + if(isabductor(user)) return TRUE - for(var/obj/item/implant/abductor/A in H.implants) + var/mob/living/non_abductor = user + if(locate(/obj/item/implant/abductor) in non_abductor.implants) return TRUE return FALSE diff --git a/code/modules/surgery/autopsy.dm b/code/modules/surgery/autopsy.dm index 2d106cfd4415e..6ff32f8b465c9 100644 --- a/code/modules/surgery/autopsy.dm +++ b/code/modules/surgery/autopsy.dm @@ -10,7 +10,8 @@ ) /datum/surgery/autopsy/can_start(mob/user, mob/living/patient) - . = ..() + if(!..()) + return FALSE if(patient.stat != DEAD) return FALSE if(HAS_TRAIT_FROM(patient, TRAIT_DISSECTED, AUTOPSY_TRAIT)) diff --git a/code/modules/surgery/bone_mending.dm b/code/modules/surgery/bone_mending.dm index cac9051ceb44f..87fc3db0af2c4 100644 --- a/code/modules/surgery/bone_mending.dm +++ b/code/modules/surgery/bone_mending.dm @@ -20,13 +20,6 @@ /datum/surgery_step/close, ) -/datum/surgery/repair_bone_hairline/can_start(mob/living/user, mob/living/carbon/target) - . = ..() - if(.) - var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) - return(targeted_bodypart.get_wound_type(targetable_wound)) - - ///// Repair Compound Fracture (Critical) /datum/surgery/repair_bone_compound name = "Repair Compound Fracture" @@ -49,12 +42,6 @@ /datum/surgery_step/close, ) -/datum/surgery/repair_bone_compound/can_start(mob/living/user, mob/living/carbon/target) - . = ..() - if(.) - var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) - return(targeted_bodypart.get_wound_type(targetable_wound)) - //SURGERY STEPS ///// Repair Hairline Fracture (Severe) @@ -217,14 +204,6 @@ /datum/surgery_step/repair_skull ) -/datum/surgery/cranial_reconstruction/can_start(mob/living/user, mob/living/carbon/target) - . = ..() - if(!.) - return FALSE - - var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) - return !isnull(targeted_bodypart.get_wound_type(targetable_wound)) - /datum/surgery_step/clamp_bleeders/discard_skull_debris name = "discard skull debris (hemostat)" implements = list( diff --git a/code/modules/surgery/brain_surgery.dm b/code/modules/surgery/brain_surgery.dm index 294b87afb866f..f9de1e01514d9 100644 --- a/code/modules/surgery/brain_surgery.dm +++ b/code/modules/surgery/brain_surgery.dm @@ -24,10 +24,7 @@ failure_sound = 'sound/surgery/organ2.ogg' /datum/surgery/brain_surgery/can_start(mob/user, mob/living/carbon/target) - var/obj/item/organ/internal/brain/target_brain = target.get_organ_slot(ORGAN_SLOT_BRAIN) - if(!target_brain) - return FALSE - return TRUE + return target.get_organ_slot(ORGAN_SLOT_BRAIN) && ..() /datum/surgery_step/fix_brain/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) display_results( diff --git a/code/modules/surgery/burn_dressing.dm b/code/modules/surgery/burn_dressing.dm index c22a8f4be9734..61be9056f6c18 100644 --- a/code/modules/surgery/burn_dressing.dm +++ b/code/modules/surgery/burn_dressing.dm @@ -20,12 +20,14 @@ ) /datum/surgery/debride/can_start(mob/living/user, mob/living/carbon/target) - if(!istype(target)) - return FALSE - if(..()) - var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) - var/datum/wound/burn/flesh/burn_wound = targeted_bodypart.get_wound_type(targetable_wound) - return(burn_wound && burn_wound.infestation > 0) + . = ..() + if(!.) + return . + + var/datum/wound/burn/flesh/burn_wound = target.get_bodypart(user.zone_selected).get_wound_type(targetable_wound) + // Should be guaranteed to have the wound by this point + ASSERT(burn_wound, "[type] on [target] has no burn wound when it should have been guaranteed to have one by can_start") + return burn_wound.infestation > 0 //SURGERY STEPS diff --git a/code/modules/surgery/core_removal.dm b/code/modules/surgery/core_removal.dm index 47f1e983f1986..aa4028997e762 100644 --- a/code/modules/surgery/core_removal.dm +++ b/code/modules/surgery/core_removal.dm @@ -16,9 +16,7 @@ ) /datum/surgery/core_removal/can_start(mob/user, mob/living/target) - if(target.stat == DEAD) - return TRUE - return FALSE + return target.stat == DEAD && ..() //extract brain /datum/surgery_step/extract_core diff --git a/code/modules/surgery/coronary_bypass.dm b/code/modules/surgery/coronary_bypass.dm index 7536598fe7ddd..af08987fd9398 100644 --- a/code/modules/surgery/coronary_bypass.dm +++ b/code/modules/surgery/coronary_bypass.dm @@ -14,10 +14,9 @@ /datum/surgery/coronary_bypass/can_start(mob/user, mob/living/carbon/target) var/obj/item/organ/internal/heart/target_heart = target.get_organ_slot(ORGAN_SLOT_HEART) - if(target_heart) - if(target_heart.damage > 60 && !target_heart.operated) - return TRUE - return FALSE + if(isnull(target_heart) || target_heart.damage < 60 || target_heart.operated) + return FALSE + return ..() //an incision but with greater bleed, and a 90% base success chance diff --git a/code/modules/surgery/ear_surgery.dm b/code/modules/surgery/ear_surgery.dm index 416857bafb256..4333b00913ba3 100644 --- a/code/modules/surgery/ear_surgery.dm +++ b/code/modules/surgery/ear_surgery.dm @@ -23,10 +23,7 @@ time = 64 /datum/surgery/ear_surgery/can_start(mob/user, mob/living/carbon/target) - var/obj/item/organ/internal/ears/target_ears = target.get_organ_slot(ORGAN_SLOT_EARS) - if(!target_ears) - return FALSE - return TRUE + return target.get_organ_slot(ORGAN_SLOT_EARS) && ..() /datum/surgery_step/fix_ears/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) display_results( diff --git a/code/modules/surgery/experimental_dissection.dm b/code/modules/surgery/experimental_dissection.dm index 6ae5e447e0bf2..69c246d9e8de5 100644 --- a/code/modules/surgery/experimental_dissection.dm +++ b/code/modules/surgery/experimental_dissection.dm @@ -10,16 +10,19 @@ /datum/surgery_step/experimental_dissection, /datum/surgery_step/close, ) - surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_MORBID_CURIOSITY + surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_MORBID_CURIOSITY possible_locs = list(BODY_ZONE_CHEST) target_mobtypes = list(/mob/living) /datum/surgery/advanced/experimental_dissection/can_start(mob/user, mob/living/target) . = ..() + if(!.) + return . if(HAS_TRAIT_FROM(target, TRAIT_DISSECTED, EXPERIMENTAL_SURGERY_TRAIT)) return FALSE if(target.stat != DEAD) return FALSE + return . /datum/surgery_step/experimental_dissection name = "dissection" @@ -44,8 +47,7 @@ var/obj/item/research_notes/hand_dossier = user.get_inactive_held_item() hand_dossier.merge(the_dossier) - var/obj/item/bodypart/target_chest = target.get_bodypart(BODY_ZONE_CHEST) - target.apply_damage(80, BRUTE, target_chest) + target.apply_damage(80, BRUTE, BODY_ZONE_CHEST) ADD_TRAIT(target, TRAIT_DISSECTED, EXPERIMENTAL_SURGERY_TRAIT) return ..() @@ -61,8 +63,7 @@ var/obj/item/research_notes/hand_dossier = user.get_inactive_held_item() hand_dossier.merge(the_dossier) - var/obj/item/bodypart/L = target.get_bodypart(BODY_ZONE_CHEST) - target.apply_damage(80, BRUTE, L) + target.apply_damage(80, BRUTE, BODY_ZONE_CHEST) return TRUE ///Calculates how many research points dissecting 'target' is worth. diff --git a/code/modules/surgery/eye_surgery.dm b/code/modules/surgery/eye_surgery.dm index 748dab1f4ec85..fb759baca8771 100644 --- a/code/modules/surgery/eye_surgery.dm +++ b/code/modules/surgery/eye_surgery.dm @@ -21,8 +21,7 @@ time = 64 /datum/surgery/eye_surgery/can_start(mob/user, mob/living/carbon/target) - var/obj/item/organ/internal/eyes/target_eyes = target.get_organ_slot(ORGAN_SLOT_EYES) - return !isnull(target_eyes) + return target.get_organ_slot(ORGAN_SLOT_EYES) && ..() /datum/surgery_step/fix_eyes/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) display_results( diff --git a/code/modules/surgery/gastrectomy.dm b/code/modules/surgery/gastrectomy.dm index 417e6716eb57b..a86805e3e5825 100644 --- a/code/modules/surgery/gastrectomy.dm +++ b/code/modules/surgery/gastrectomy.dm @@ -16,10 +16,9 @@ /datum/surgery/gastrectomy/can_start(mob/user, mob/living/carbon/target) var/obj/item/organ/internal/stomach/target_stomach = target.get_organ_slot(ORGAN_SLOT_STOMACH) - if(target_stomach) - if(target_stomach.damage > 50 && !target_stomach.operated) - return TRUE - return FALSE + if(isnull(target_stomach) || target_stomach.damage < 50 || target_stomach.operated) + return FALSE + return ..() ////Gastrectomy, because we truly needed a way to repair stomachs. //95% chance of success to be consistent with most organ-repairing surgeries. @@ -72,4 +71,3 @@ span_warning("[user] cuts the wrong part of [target]'s stomach!"), ) display_pain(target, "Your stomach throbs with pain; it's not getting any better!") - diff --git a/code/modules/surgery/healing.dm b/code/modules/surgery/healing.dm index efef97cca74f3..e2e65003a3231 100644 --- a/code/modules/surgery/healing.dm +++ b/code/modules/surgery/healing.dm @@ -2,7 +2,7 @@ target_mobtypes = list(/mob/living) requires_bodypart_type = NONE replaced_by = /datum/surgery - surgery_flags = SURGERY_IGNORE_CLOTHES | SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB + surgery_flags = SURGERY_IGNORE_CLOTHES | SURGERY_REQUIRE_RESTING possible_locs = list(BODY_ZONE_CHEST) steps = list( /datum/surgery_step/incise, @@ -18,8 +18,11 @@ /datum/surgery/healing/can_start(mob/user, mob/living/patient) . = ..() + if(!.) + return . if(!(patient.mob_biotypes & (MOB_ORGANIC|MOB_HUMANOID))) return FALSE + return . /datum/surgery/healing/New(surgery_target, surgery_location, surgery_bodypart) ..() @@ -27,7 +30,8 @@ steps = list( /datum/surgery_step/incise/nobleed, healing_step_type, //hehe cheeky - /datum/surgery_step/close) + /datum/surgery_step/close, + ) /datum/surgery_step/heal name = "repair body (hemostat)" diff --git a/code/modules/surgery/hepatectomy.dm b/code/modules/surgery/hepatectomy.dm index 1d609836a8e7d..934e6589e9df5 100644 --- a/code/modules/surgery/hepatectomy.dm +++ b/code/modules/surgery/hepatectomy.dm @@ -15,10 +15,9 @@ /datum/surgery/hepatectomy/can_start(mob/user, mob/living/carbon/target) var/obj/item/organ/internal/liver/target_liver = target.get_organ_slot(ORGAN_SLOT_LIVER) - if(target_liver) - if(target_liver.damage > 50 && !target_liver.operated) - return TRUE - return FALSE + if(isnull(target_liver) || target_liver.damage < 50 || target_liver.operated) + return FALSE + return ..() ////hepatectomy, removes damaged parts of the liver so that the liver may regenerate properly //95% chance of success, not 100 because organs are delicate diff --git a/code/modules/surgery/implant_removal.dm b/code/modules/surgery/implant_removal.dm index 7f43d56ffb1b0..a2b9eb33cbf06 100644 --- a/code/modules/surgery/implant_removal.dm +++ b/code/modules/surgery/implant_removal.dm @@ -2,6 +2,7 @@ name = "Implant Removal" target_mobtypes = list(/mob/living) possible_locs = list(BODY_ZONE_CHEST) + surgery_flags = SURGERY_REQUIRE_RESTING steps = list( /datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, @@ -83,6 +84,7 @@ name = "Implant Removal" requires_bodypart_type = BODYTYPE_ROBOTIC target_mobtypes = list(/mob/living/carbon/human) // Simpler mobs don't have bodypart types + surgery_flags = parent_type::surgery_flags | SURGERY_REQUIRE_LIMB steps = list( /datum/surgery_step/mechanic_open, /datum/surgery_step/open_hatch, diff --git a/code/modules/surgery/lipoplasty.dm b/code/modules/surgery/lipoplasty.dm index f635d0da02032..b28d6841512f9 100644 --- a/code/modules/surgery/lipoplasty.dm +++ b/code/modules/surgery/lipoplasty.dm @@ -10,9 +10,9 @@ ) /datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target) - if(HAS_TRAIT(target, TRAIT_FAT) && target.nutrition >= NUTRITION_LEVEL_WELL_FED) - return TRUE - return FALSE + if(!HAS_TRAIT(target, TRAIT_FAT) || target.nutrition >= NUTRITION_LEVEL_WELL_FED) + return FALSE + return ..() //cut fat @@ -24,6 +24,10 @@ /obj/item/hatchet = 35, /obj/item/knife/butcher = 25) time = 64 + preop_sound = list( + /obj/item/circular_saw = 'sound/surgery/saw.ogg', + /obj/item = 'sound/surgery/scalpel1.ogg', + ) /datum/surgery_step/cut_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) user.visible_message(span_notice("[user] begins to cut away [target]'s excess fat."), span_notice("You begin to cut away [target]'s excess fat...")) @@ -55,6 +59,8 @@ TOOL_SCREWDRIVER = 45, TOOL_WIRECUTTER = 35) time = 32 + preop_sound = 'sound/surgery/retractor1.ogg' + success_sound = 'sound/surgery/retractor2.ogg' /datum/surgery_step/remove_fat/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) display_results( diff --git a/code/modules/surgery/lobectomy.dm b/code/modules/surgery/lobectomy.dm index 56e256534f7fb..83f9279818b8d 100644 --- a/code/modules/surgery/lobectomy.dm +++ b/code/modules/surgery/lobectomy.dm @@ -13,10 +13,9 @@ /datum/surgery/lobectomy/can_start(mob/user, mob/living/carbon/target) var/obj/item/organ/internal/lungs/target_lungs = target.get_organ_slot(ORGAN_SLOT_LUNGS) - if(target_lungs) - if(target_lungs.damage > 60 && !target_lungs.operated) - return TRUE - return FALSE + if(isnull(target_lungs) || target_lungs.damage < 60 || target_lungs.operated) + return FALSE + return ..() //lobectomy, removes the most damaged lung lobe with a 95% base success chance diff --git a/code/modules/surgery/prosthetic_replacement.dm b/code/modules/surgery/prosthetic_replacement.dm index b2c8c7485375e..4b285d161bb06 100644 --- a/code/modules/surgery/prosthetic_replacement.dm +++ b/code/modules/surgery/prosthetic_replacement.dm @@ -17,6 +17,8 @@ ) /datum/surgery/prosthetic_replacement/can_start(mob/user, mob/living/carbon/target) + if(!..()) + return FALSE if(!iscarbon(target)) return FALSE var/mob/living/carbon/carbon_target = target diff --git a/code/modules/surgery/repair_puncture.dm b/code/modules/surgery/repair_puncture.dm index bb33c314689bc..9b9071cff89c5 100644 --- a/code/modules/surgery/repair_puncture.dm +++ b/code/modules/surgery/repair_puncture.dm @@ -27,13 +27,13 @@ ) /datum/surgery/repair_puncture/can_start(mob/living/user, mob/living/carbon/target) - if(!istype(target)) - return FALSE . = ..() - if(.) - var/obj/item/bodypart/targeted_bodypart = target.get_bodypart(user.zone_selected) - var/datum/wound/burn/flesh/pierce_wound = targeted_bodypart.get_wound_type(targetable_wound) - return(pierce_wound && pierce_wound.blood_flow > 0) + if(!.) + return . + + var/datum/wound/pierce/bleed/pierce_wound = target.get_bodypart(user.zone_selected).get_wound_type(targetable_wound) + ASSERT(pierce_wound, "[type] on [target] has no pierce wound when it should have been guaranteed to have one by can_start") + return pierce_wound.blood_flow > 0 //SURGERY STEPS @@ -45,6 +45,7 @@ TOOL_SCALPEL = 85, TOOL_WIRECUTTER = 40) time = 3 SECONDS + preop_sound = 'sound/surgery/hemostat1.ogg' /datum/surgery_step/repair_innards/preop(mob/user, mob/living/carbon/target, target_zone, obj/item/tool, datum/surgery/surgery) var/datum/wound/pierce/bleed/pierce_wound = surgery.operated_wound @@ -104,6 +105,8 @@ TOOL_WELDER = 70, /obj/item = 30) time = 4 SECONDS + preop_sound = 'sound/surgery/cautery1.ogg' + success_sound = 'sound/surgery/cautery2.ogg' /datum/surgery_step/seal_veins/tool_check(mob/user, obj/item/tool) if(implement_type == TOOL_WELDER || implement_type == /obj/item) diff --git a/code/modules/surgery/revival.dm b/code/modules/surgery/revival.dm index 0d920b80c8135..fc55598643103 100644 --- a/code/modules/surgery/revival.dm +++ b/code/modules/surgery/revival.dm @@ -5,7 +5,7 @@ requires_bodypart_type = NONE possible_locs = list(BODY_ZONE_CHEST) target_mobtypes = list(/mob/living) - surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_REQUIRE_LIMB | SURGERY_MORBID_CURIOSITY + surgery_flags = SURGERY_REQUIRE_RESTING | SURGERY_MORBID_CURIOSITY steps = list( /datum/surgery_step/incise, /datum/surgery_step/retract_skin, @@ -123,6 +123,7 @@ /datum/surgery/revival/carbon possible_locs = list(BODY_ZONE_HEAD) target_mobtypes = list(/mob/living/carbon) + surgery_flags = parent_type::surgery_flags | SURGERY_REQUIRE_LIMB /datum/surgery/revival/carbon/is_valid_target(mob/living/carbon/patient) var/obj/item/organ/internal/brain/target_brain = patient.get_organ_slot(ORGAN_SLOT_BRAIN) diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm index 7320e6ed94753..a555548e43268 100644 --- a/code/modules/surgery/surgery.dm +++ b/code/modules/surgery/surgery.dm @@ -20,17 +20,19 @@ var/list/possible_locs = list() ///Mobs that are valid to have surgery performed on them. var/list/target_mobtypes = list(/mob/living/carbon/human) + ///The person the surgery is being performed on. Funnily enough, it isn't always a carbon. - var/mob/living/carbon/target + VAR_FINAL/mob/living/carbon/target ///The specific bodypart being operated on. - var/obj/item/bodypart/operated_bodypart + VAR_FINAL/obj/item/bodypart/operated_bodypart ///The wound datum that is being operated on. - var/datum/wound/operated_wound - ///Types of wounds this surgery can target. - var/datum/wound/targetable_wound + VAR_FINAL/datum/wound/operated_wound + ///Types of wounds this surgery can target. + var/targetable_wound ///The types of bodyparts that this surgery can have performed on it. Used for augmented surgeries. var/requires_bodypart_type = BODYTYPE_ORGANIC + ///The speed modifier given to the surgery through external means. var/speed_modifier = 0 ///Whether the surgery requires research to do. You need to add a design if using this! @@ -69,6 +71,8 @@ /datum/surgery/proc/can_start(mob/user, mob/living/patient) //FALSE to not show in list + SHOULD_CALL_PARENT(TRUE) + . = TRUE if(replaced_by == /datum/surgery) return FALSE @@ -102,6 +106,7 @@ return FALSE if(type in opcomputer.advanced_surgeries) return TRUE + return . /datum/surgery/proc/next_step(mob/living/user, modifiers) if(location != user.zone_selected) From e8b5b52d54a60b651d72e610cfb35a237aef6efe Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 01:41:37 +1300 Subject: [PATCH 006/508] Automatic changelog for PR #81307 [ci skip] --- html/changelogs/AutoChangeLog-pr-81307.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81307.yml diff --git a/html/changelogs/AutoChangeLog-pr-81307.yml b/html/changelogs/AutoChangeLog-pr-81307.yml new file mode 100644 index 0000000000000..8666849c68879 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81307.yml @@ -0,0 +1,5 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "Fixed Puncture Repair surgery not having surgical sounds" + - bugfix: "Fixed Surgery Initiator potentially showing invalid surgeries" \ No newline at end of file From af6406d5ed061ac62ae0bde1a1662365f94c950c Mon Sep 17 00:00:00 2001 From: Kyle Spier-Swenson Date: Thu, 8 Feb 2024 05:00:18 -0800 Subject: [PATCH 007/508] Disable rust version checking in tgs precompile.sh hook (#81319) Updates tgs/precompile.sh hook to match what is deployed on campbell. rust-lang/cargo#12654 has set a policy of setting this to be the latest version-2, which kills any kind of signal this could have ever had. cargo's subcrates like `home` are used in almost any complex rust package, so this basically sets the tone for all packages and all crates published after October 8th. A min compiler version should be based on an actual need to use a specific compiler version because of specific features that version has or bugs that version doesn't have. This is signal. Setting to some evergreen value as a matter of course is not signal, its noise. I will not subject myself nor our downstreams to such nonsense. --- tools/tgs_scripts/PreCompile.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/tgs_scripts/PreCompile.sh b/tools/tgs_scripts/PreCompile.sh index 9f2f94646edc3..ae8cba7a45b96 100755 --- a/tools/tgs_scripts/PreCompile.sh +++ b/tools/tgs_scripts/PreCompile.sh @@ -28,7 +28,7 @@ fi echo "Deploying rust-g..." git checkout "$RUST_G_VERSION" -env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --release --target=i686-unknown-linux-gnu +env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --ignore-rust-version --release --target=i686-unknown-linux-gnu mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so" cd .. From 01fe1457601387a0428d196d0987eb15288988d6 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:00:34 -0500 Subject: [PATCH 008/508] Barcode scanners can now be printed. (#81324) ## About The Pull Request Currently the only way to get a barcode scanner is by spawning as a Curator, this is lame and prevents people to job change into a librarian, so now it can be printed like basically all other service job's tools. Part of computer tech ![image](https://github.com/tgstation/tgstation/assets/53777086/26254e14-b957-41e4-9349-bd4bf848c18c) ## Why It's Good For The Game You no longer have to spawn as a Curator to be able to work in the Library, and Curators can now replace their otherwise completely irreplaceable equipment. ## Changelog :cl: qol: The barcode scanner is now part of computer tech and can be printed at the service techfab. /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/modules/library/barcode_scanner.dm | 1 + .../research/designs/autolathe/service_designs.dm | 11 +++++++++++ code/modules/research/techweb/all_nodes.dm | 3 ++- 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/code/modules/library/barcode_scanner.dm b/code/modules/library/barcode_scanner.dm index f96b60358c2ec..5cacf03e01356 100644 --- a/code/modules/library/barcode_scanner.dm +++ b/code/modules/library/barcode_scanner.dm @@ -6,6 +6,7 @@ throw_speed = 3 throw_range = 5 w_class = WEIGHT_CLASS_TINY + custom_materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2) ///Weakref to the library computer we are connected to. var/datum/weakref/computer_ref ///The current scanning mode (BARCODE_SCANNER_CHECKIN|BARCODE_SCANNER_INVENTORY) diff --git a/code/modules/research/designs/autolathe/service_designs.dm b/code/modules/research/designs/autolathe/service_designs.dm index cccef8e740dfa..6b8d7f474fca4 100644 --- a/code/modules/research/designs/autolathe/service_designs.dm +++ b/code/modules/research/designs/autolathe/service_designs.dm @@ -580,3 +580,14 @@ RND_CATEGORY_CONSTRUCTION + RND_SUBCATEGORY_CONSTRUCTION_MOUNTS, ) departmental_flags = DEPARTMENT_BITFLAG_SERVICE + +/datum/design/barcode_scanner + name = "Barcode Scanner" + id = "barcode_scanner" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SHEET_MATERIAL_AMOUNT * 2) + build_path = /obj/item/barcodescanner + category = list( + RND_CATEGORY_TOOLS + RND_SUBCATEGORY_TOOLS_SERVICE, + ) + departmental_flags = DEPARTMENT_BITFLAG_SERVICE diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 12b72f283a0d5..6cd0688eae6c9 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1180,10 +1180,11 @@ description = "Computers and how they work." prereq_ids = list("datatheory") design_ids = list( + "bankmachine", + "barcode_scanner", "cargo", "cargorequest", "comconsole", - "bankmachine", "crewconsole", "idcard", "libraryconsole", From c78af5680ac00bc1f7055030f822d8e877ca0a3d Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 02:00:48 +1300 Subject: [PATCH 009/508] Automatic changelog for PR #81324 [ci skip] --- html/changelogs/AutoChangeLog-pr-81324.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81324.yml diff --git a/html/changelogs/AutoChangeLog-pr-81324.yml b/html/changelogs/AutoChangeLog-pr-81324.yml new file mode 100644 index 0000000000000..e1cb652ed50f7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81324.yml @@ -0,0 +1,4 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - qol: "The barcode scanner is now part of computer tech and can be printed at the service techfab." \ No newline at end of file From 1389351ef9e49ce9cdb378bdc22b1093fc5519e4 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Thu, 8 Feb 2024 08:19:31 -0500 Subject: [PATCH 010/508] You can now move and talk through statues and mannequins (#81188) ## About The Pull Request I recently played a game where I rotated my skeleton model while rotating my own character at the same time and it being in sync gave me the realization on how cool it would be if the Coroner was able to simply control the skeleton body. I find skeleton displays very funny and I want to see more funny things happen with them, so I thought this would be a good place to start, with the benefits that it also works on mannequins and statues too so they aren't left out. Basically, while it is unanchored, if you have a statue/mannequin grabbed, it will change its direction as you do, and speak the same words you do. Your own messages can only be heard if the person is directly next to you, revealing that it was you talking through it all along. I was originally gonna add this to the simple rotation component but moved off when I decided to add talking through it, I left in the code improvements I made to the component though since it is one of the oldest components and hasn't been touched in a while. Video demonstration (before I added the person also talking, just ignore that missing) https://github.com/tgstation/tgstation/assets/53777086/27242fc3-9649-418d-95cb-b31619319e97 While fixing the Toilet bong's rotation stuff I noticed a lot of it wasn't up to proper code standards so I went over it and fixed issues I had with it. It now doesn't give text saying you found something nasty to species that still likes mice (like flypeople), and fixed its update appearance to match the codebase standard set by the introduction of ``update_appearance`` many years ago. ## Why It's Good For The Game It's a funny small idea I had and got inspired to add, it's a niche mechanic that I think fits the aesthetic I am going for with Coroner and also give a funny interaction with the human-like inanimate objects. ## Changelog :cl: fix: Species that can eat mice don't get disgusted from seeing one in the toilet bong. add: Grabbing an unwrenched statue/mannequin/skeleton model will now move its direction as you move yours, and you can talk through it. /:cl: --- code/datums/components/marionette.dm | 77 +++++++++++++ code/datums/components/rotation.dm | 59 +++++----- code/game/objects/items/rcd/RPD.dm | 2 +- code/game/objects/structures/mannequin.dm | 2 +- code/game/objects/structures/toiletbong.dm | 107 +++++++++--------- .../transit_tube_construction.dm | 4 +- code/game/objects/structures/window.dm | 4 +- code/modules/art/statues.dm | 1 + code/modules/assembly/infrared.dm | 4 +- code/modules/basketball/hoop.dm | 2 +- .../recycling/disposal/construction.dm | 6 +- .../surgery/organs/internal/tongue/_tongue.dm | 1 + tgstation.dme | 1 + 13 files changed, 170 insertions(+), 100 deletions(-) create mode 100644 code/datums/components/marionette.dm diff --git a/code/datums/components/marionette.dm b/code/datums/components/marionette.dm new file mode 100644 index 0000000000000..a2f58031768e0 --- /dev/null +++ b/code/datums/components/marionette.dm @@ -0,0 +1,77 @@ +/** + * Marionette component + * + * Upon being grabbed, we will align the direction of the parent with the direction of the grabber when they rotate. + * While grabbed, will also speak out whatever the original person says + */ +/datum/component/marionette + ///Reference to the mob that is grabbing us, which we hook signals to for marionette stuff. + var/mob/grabber + +/datum/component/marionette/Destroy() + if(grabber) + UnregisterSignal(grabber, list(COMSIG_MOVABLE_KEYBIND_FACE_DIR, COMSIG_MOB_SAY, COMSIG_QDELETING)) + grabber = null + return ..() + +/datum/component/marionette/RegisterWithParent() + RegisterSignal(parent, COMSIG_LIVING_TRYING_TO_PULL, PROC_REF(on_pull)) + RegisterSignal(parent, COMSIG_ATOM_NO_LONGER_PULLED, PROC_REF(on_stop_pull)) + +/datum/component/marionette/UnregisterFromParent() + UnregisterSignal(parent, list( + COMSIG_LIVING_TRYING_TO_PULL, + COMSIG_ATOM_NO_LONGER_PULLED, + )) + return ..() + +///Called when something starts pulling us, we now listen in to that thing for rotation. +/datum/component/marionette/proc/on_pull(atom/movable/source, atom/movable/puller, force) + SIGNAL_HANDLER + + if(!puller) + return + grabber = puller + RegisterSignal(grabber, COMSIG_MOVABLE_KEYBIND_FACE_DIR, PROC_REF(on_puller_turn)) + RegisterSignal(grabber, COMSIG_MOB_SAY, PROC_REF(on_puller_speech)) + RegisterSignal(grabber, COMSIG_QDELETING, PROC_REF(on_puller_qdel)) + +///Stopped pulling, we clear out signals and references. +/datum/component/marionette/proc/on_stop_pull(datum/source, atom/movable/was_pulling) + SIGNAL_HANDLER + if(grabber) + UnregisterSignal(grabber, list(COMSIG_MOVABLE_KEYBIND_FACE_DIR, COMSIG_MOB_SAY, COMSIG_QDELETING)) + grabber = null + +///Callled when the person grabbin us turns, we rotate to match their direction. +/datum/component/marionette/proc/on_puller_turn(mob/living/source, direction) + SIGNAL_HANDLER + var/atom/movable/parent_movable = parent + parent_movable.setDir(direction) + +///Called when the person grabbing us speaks, we lower their volume to 1 tile and speak what they said through us. +/datum/component/marionette/proc/on_puller_speech(datum/source, list/speech_args) + SIGNAL_HANDLER + + if(HAS_TRAIT(grabber, TRAIT_SIGN_LANG)) + return + + var/message = speech_args[SPEECH_MESSAGE] + var/list/spans = speech_args[SPEECH_SPANS] + var/language = speech_args[SPEECH_LANGUAGE] + var/saymode = speech_args[SPEECH_SAYMODE] + var/atom/movable/movable_parent = parent + movable_parent.say( + message = message, + spans = spans.Copy(), + language = language, + forced = "[source]'s marionette", + saymode = saymode, + ) + speech_args[SPEECH_RANGE] = WHISPER_RANGE + +///Called when our puller is somehow deleted, we simply clear the reference to them. +/datum/component/marionette/proc/on_puller_qdel() + SIGNAL_HANDLER + + grabber = null diff --git a/code/datums/components/rotation.dm b/code/datums/components/rotation.dm index 4bcfa8b01c9b9..7c55579c9992d 100644 --- a/code/datums/components/rotation.dm +++ b/code/datums/components/rotation.dm @@ -1,6 +1,6 @@ /datum/component/simple_rotation /// Additional stuff to do after rotation - var/datum/callback/AfterRotation + var/datum/callback/post_rotation /// Rotation flags for special behavior var/rotation_flags = NONE @@ -9,9 +9,9 @@ * * args: * * rotation_flags (optional) Bitflags that determine behavior for rotation (defined at the top of this file) - * * AfterRotation (optional) Callback proc that is used after the object is rotated (sound effects, balloon alerts, etc.) -**/ -/datum/component/simple_rotation/Initialize(rotation_flags = NONE, AfterRotation) + * * post_rotation (optional) Callback proc that is used after the object is rotated (sound effects, balloon alerts, etc.) + **/ +/datum/component/simple_rotation/Initialize(rotation_flags = NONE, post_rotation) if(!ismovable(parent)) return COMPONENT_INCOMPATIBLE @@ -19,19 +19,13 @@ source.flags_1 |= HAS_CONTEXTUAL_SCREENTIPS_1 src.rotation_flags = rotation_flags - src.AfterRotation = AfterRotation || CALLBACK(src, PROC_REF(DefaultAfterRotation)) + src.post_rotation = post_rotation || CALLBACK(src, PROC_REF(default_post_rotation)) -/datum/component/simple_rotation/proc/AddSignals() - RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(RotateLeft)) - RegisterSignal(parent, COMSIG_CLICK_ALT_SECONDARY, PROC_REF(RotateRight)) +/datum/component/simple_rotation/RegisterWithParent() + RegisterSignal(parent, COMSIG_CLICK_ALT, PROC_REF(rotate_left)) + RegisterSignal(parent, COMSIG_CLICK_ALT_SECONDARY, PROC_REF(rotate_right)) RegisterSignal(parent, COMSIG_ATOM_EXAMINE, PROC_REF(ExamineMessage)) RegisterSignal(parent, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, PROC_REF(on_requesting_context_from_item)) - -/datum/component/simple_rotation/proc/RemoveSignals() - UnregisterSignal(parent, list(COMSIG_CLICK_ALT, COMSIG_CLICK_ALT_SECONDARY, COMSIG_ATOM_EXAMINE, COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM)) - -/datum/component/simple_rotation/RegisterWithParent() - AddSignals() return ..() /datum/component/simple_rotation/PostTransfer() @@ -41,15 +35,16 @@ return COMPONENT_NOTRANSFER /datum/component/simple_rotation/UnregisterFromParent() - RemoveSignals() + UnregisterSignal(parent, list( + COMSIG_CLICK_ALT, + COMSIG_CLICK_ALT_SECONDARY, + COMSIG_ATOM_EXAMINE, + COMSIG_ATOM_REQUESTING_CONTEXT_FROM_ITEM, + )) return ..() /datum/component/simple_rotation/Destroy() - AfterRotation = null - //Signals + verbs removed via UnRegister - return ..() - -/datum/component/simple_rotation/ClearFromParent() + post_rotation = null return ..() /datum/component/simple_rotation/proc/ExamineMessage(datum/source, mob/user, list/examine_list) @@ -57,15 +52,15 @@ if(rotation_flags & ROTATION_REQUIRE_WRENCH) examine_list += span_notice("This requires a wrench to be rotated.") -/datum/component/simple_rotation/proc/RotateRight(datum/source, mob/user) +/datum/component/simple_rotation/proc/rotate_right(datum/source, mob/user) SIGNAL_HANDLER - Rotate(user, ROTATION_CLOCKWISE) + rotate(user, ROTATION_CLOCKWISE) -/datum/component/simple_rotation/proc/RotateLeft(datum/source, mob/user) +/datum/component/simple_rotation/proc/rotate_left(datum/source, mob/user) SIGNAL_HANDLER - Rotate(user, ROTATION_COUNTERCLOCKWISE) + rotate(user, ROTATION_COUNTERCLOCKWISE) -/datum/component/simple_rotation/proc/Rotate(mob/user, degrees) +/datum/component/simple_rotation/proc/rotate(mob/user, degrees) if(QDELETED(user)) CRASH("[src] is being rotated [user ? "with a qdeleting" : "without a"] user") if(!istype(user)) @@ -73,7 +68,7 @@ if(!isnum(degrees)) CRASH("[src] is being rotated without providing the amount of degrees needed") - if(!CanBeRotated(user, degrees) || !CanUserRotate(user, degrees)) + if(!can_be_rotated(user, degrees) || !can_user_rotate(user, degrees)) return var/obj/rotated_obj = parent @@ -81,16 +76,16 @@ if(rotation_flags & ROTATION_REQUIRE_WRENCH) playsound(rotated_obj, 'sound/items/ratchet.ogg', 50, TRUE) - AfterRotation.Invoke(user, degrees) + post_rotation.Invoke(user, degrees) -/datum/component/simple_rotation/proc/CanUserRotate(mob/user, degrees) +/datum/component/simple_rotation/proc/can_user_rotate(mob/user, degrees) if(isliving(user) && user.can_perform_action(parent, NEED_DEXTERITY)) return TRUE if((rotation_flags & ROTATION_GHOSTS_ALLOWED) && isobserver(user) && CONFIG_GET(flag/ghost_interaction)) return TRUE return FALSE -/datum/component/simple_rotation/proc/CanBeRotated(mob/user, degrees, silent=FALSE) +/datum/component/simple_rotation/proc/can_be_rotated(mob/user, degrees, silent=FALSE) var/obj/rotated_obj = parent if(!rotated_obj.Adjacent(user)) silent = TRUE @@ -120,7 +115,7 @@ return FALSE return TRUE -/datum/component/simple_rotation/proc/DefaultAfterRotation(mob/user, degrees) +/datum/component/simple_rotation/proc/default_post_rotation(mob/user, degrees) return // maybe we don't need the item context proc but instead the hand one? since we don't need to check held_item @@ -129,10 +124,10 @@ var/rotation_screentip = FALSE - if(CanBeRotated(user, ROTATION_CLOCKWISE, silent=TRUE)) + if(can_be_rotated(user, ROTATION_CLOCKWISE, silent=TRUE)) context[SCREENTIP_CONTEXT_ALT_LMB] = "Rotate left" rotation_screentip = TRUE - if(CanBeRotated(user, ROTATION_COUNTERCLOCKWISE, silent=TRUE)) + if(can_be_rotated(user, ROTATION_COUNTERCLOCKWISE, silent=TRUE)) context[SCREENTIP_CONTEXT_ALT_RMB] = "Rotate right" rotation_screentip = TRUE diff --git a/code/game/objects/items/rcd/RPD.dm b/code/game/objects/items/rcd/RPD.dm index 9af1041779e1e..e47779776059b 100644 --- a/code/game/objects/items/rcd/RPD.dm +++ b/code/game/objects/items/rcd/RPD.dm @@ -587,7 +587,7 @@ GLOBAL_LIST_INIT(transit_tube_recipes, list( if(queued_pipe_flipped) tube.setDir(turn(queued_pipe_dir, 45 + ROTATION_FLIP)) - tube.AfterRotation(user, ROTATION_FLIP) + tube.post_rotation(user, ROTATION_FLIP) tube.add_fingerprint(usr) if(mode & WRENCH_MODE) diff --git a/code/game/objects/structures/mannequin.dm b/code/game/objects/structures/mannequin.dm index 74127638d83dd..bdb5344d7fe10 100644 --- a/code/game/objects/structures/mannequin.dm +++ b/code/game/objects/structures/mannequin.dm @@ -64,6 +64,7 @@ icon_state = "mannequin_[material]_[body_type == FEMALE ? "female" : "male"]" AddElement(/datum/element/strippable, GLOB.strippable_mannequin_items) AddComponent(/datum/component/simple_rotation, ROTATION_IGNORE_ANCHORED) + AddComponent(/datum/component/marionette) update_appearance() /obj/structure/mannequin/Destroy() @@ -190,7 +191,6 @@ name = "skeleton model" desc = "Not to knock over." material = MANNEQUIN_SKELETON - anchored = TRUE obj_flags = UNIQUE_RENAME starting_items = list( /obj/item/clothing/glasses/eyepatch, diff --git a/code/game/objects/structures/toiletbong.dm b/code/game/objects/structures/toiletbong.dm index e5c5193e46f95..0ea21e9ff8480 100644 --- a/code/game/objects/structures/toiletbong.dm +++ b/code/game/objects/structures/toiletbong.dm @@ -3,29 +3,28 @@ desc = "A repurposed toilet with re-arranged piping and an attached flamethrower. Why would anyone build this?" icon = 'icons/obj/watercloset.dmi' icon_state = "toiletbong" + base_icon_state = "toiletbong" density = FALSE anchored = TRUE - var/emagged = FALSE var/smokeradius = 1 var/mutable_appearance/weed_overlay /obj/structure/toiletbong/Initialize(mapload) . = ..() create_storage() - AddComponent(/datum/component/simple_rotation, AfterRotation = CALLBACK(src, PROC_REF(AfterRotation))) + AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation))) create_storage(max_total_storage = 100, max_slots = 12, canhold = /obj/item/food) atom_storage.attack_hand_interact = FALSE atom_storage.rustle_sound = FALSE atom_storage.animated = FALSE - weed_overlay = mutable_appearance('icons/obj/watercloset.dmi', "toiletbong_overlay") + weed_overlay = mutable_appearance('icons/obj/watercloset.dmi', "[base_icon_state]_overlay") START_PROCESSING(SSobj, src) -/obj/structure/toiletbong/update_icon() +/obj/structure/toiletbong/update_overlays() . = ..() - cut_overlays() if (LAZYLEN(contents)) - add_overlay(weed_overlay) + . += weed_overlay /obj/structure/toiletbong/attack_hand(mob/living/carbon/user) . = ..() @@ -36,47 +35,44 @@ user.balloon_alert(user, "it's empty!") return user.visible_message(span_boldnotice("[user] takes a huge drag on the [src].")) - if (do_after(user, 2 SECONDS, target = src)) - var/turf/toiletbong_location = loc - toiletbong_location.hotspot_expose(1000, 5) - for (var/obj/item/item in contents) - if (item.resistance_flags & INDESTRUCTIBLE) - user.balloon_alert(user, "[item.name] is blocking the pipes!") - continue - playsound(src, 'sound/items/modsuit/flamethrower.ogg', 50) - var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/puff = new - puff.set_up(smokeradius, holder = src, location = user, carry = item.reagents, efficiency = 20) - puff.start() - if (prob(5) && !emagged) - if(islizard(user)) - user.balloon_alert(user, "a hidden treat!") - user.visible_message(span_danger("[user] fishes a mouse out of the pipes.")) - else - to_chat(user, span_userdanger("There was something disgusting in the pipes!")) - user.visible_message(span_danger("[user] spits out a mouse.")) - user.adjust_disgust(50) - user.vomit(VOMIT_CATEGORY_DEFAULT) - var/mob/living/spawned_mob = new /mob/living/basic/mouse(get_turf(user)) - spawned_mob.faction |= "[REF(user)]" - if(prob(50)) - for(var/j in 1 to rand(1, 3)) - step(spawned_mob, pick(NORTH,SOUTH,EAST,WEST)) - qdel(item) - if(!emagged) - break - update_icon() + if (!do_after(user, 2 SECONDS, target = src)) + return + var/turf/toiletbong_location = loc + toiletbong_location.hotspot_expose(1000, 5) + for (var/obj/item/item in contents) + if (item.resistance_flags & INDESTRUCTIBLE) + user.balloon_alert(user, "[item.name] is blocking the pipes!") + continue + playsound(src, 'sound/items/modsuit/flamethrower.ogg', 50) + var/datum/effect_system/fluid_spread/smoke/chem/smoke_machine/puff = new + puff.set_up(smokeradius, holder = src, location = user, carry = item.reagents, efficiency = 20) + puff.start() + if (prob(5) && !(obj_flags & EMAGGED)) + if(user.get_liked_foodtypes() & GORE) + user.balloon_alert(user, "a hidden treat!") + user.visible_message(span_danger("[user] fishes a mouse out of the pipes.")) + else + to_chat(user, span_userdanger("There was something disgusting in the pipes!")) + user.visible_message(span_danger("[user] spits out a mouse.")) + user.adjust_disgust(50) + user.vomit(VOMIT_CATEGORY_DEFAULT) + var/mob/living/spawned_mob = new /mob/living/basic/mouse(get_turf(user)) + spawned_mob.faction |= "[REF(user)]" + if(prob(50)) + for(var/j in 1 to rand(1, 3)) + step(spawned_mob, pick(NORTH,SOUTH,EAST,WEST)) + qdel(item) + if(!(obj_flags & EMAGGED)) + break + update_appearance(UPDATE_ICON) /obj/structure/toiletbong/wrench_act(mob/living/user, obj/item/tool) - tool.play_tool_sound(src) - if(anchored) - to_chat(user, span_notice("You begin unsecuring the [src].")) - anchored = FALSE - else - to_chat(user, span_notice("You secure the [src] to the floor.")) - anchored = TRUE - return TRUE + ..() + default_unfasten_wrench(user, tool) + return ITEM_INTERACT_SUCCESS -/obj/structure/toiletbong/proc/AfterRotation(mob/user, degrees) +///Called in the simple rotation's post_rotation callback, playing a sound cue to players. +/obj/structure/toiletbong/proc/post_rotation(mob/user, degrees) playsound(src, 'sound/items/deconstruct.ogg', 50) /obj/structure/toiletbong/crowbar_act(mob/living/user, obj/item/tool) @@ -94,18 +90,17 @@ return TRUE /obj/structure/toiletbong/emag_act(mob/user, obj/item/card/emag/emag_card) + if(obj_flags & EMAGGED) + return FALSE + obj_flags |= EMAGGED + smokeradius = 2 playsound(src, 'sound/effects/fish_splash.ogg', 50) - user.balloon_alert(user, "whoops!") - if(!emagged) - emagged = TRUE - smokeradius = 2 - balloon_alert(user, "toilet broke") - if (emag_card) - to_chat(user, span_boldwarning("The [emag_card] falls into the toilet. You fish it back out. Looks like you broke the toilet.")) - return TRUE - return FALSE + balloon_alert(user, "toilet broke") + if (emag_card) + to_chat(user, span_boldwarning("The [emag_card] falls into the toilet. You fish it back out. Looks like you broke the toilet.")) + return TRUE -/obj/structure/toiletbong/attackby(obj/item/I, mob/user, params) - if(istype(I, /obj/item/card/emag)) +/obj/structure/toiletbong/attackby(obj/item/attacking_item, mob/user, params) + if(istype(attacking_item, /obj/item/card/emag)) return - . = ..() + return ..() diff --git a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm index cc9de4cdb95cf..44952801ec7d3 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_construction.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_construction.dm @@ -15,7 +15,7 @@ /obj/structure/c_transit_tube/Initialize(mapload) . = ..() - AddComponent(/datum/component/simple_rotation, AfterRotation = CALLBACK(src, PROC_REF(AfterRotation))) + AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation))) /obj/structure/c_transit_tube/proc/can_wrench_in_loc(mob/user) var/turf/source_turf = get_turf(loc) @@ -27,7 +27,7 @@ return FALSE return TRUE -/obj/structure/c_transit_tube/proc/AfterRotation(mob/user, degrees) +/obj/structure/c_transit_tube/proc/post_rotation(mob/user, degrees) if(flipped_build_type && degrees == ROTATION_FLIP) setDir(turn(dir, degrees)) //Turn back we don't actually flip flipped = !flipped diff --git a/code/game/objects/structures/window.dm b/code/game/objects/structures/window.dm index f7510de55862e..bb42ad4c3b000 100644 --- a/code/game/objects/structures/window.dm +++ b/code/game/objects/structures/window.dm @@ -67,7 +67,7 @@ flags_1 |= ALLOW_DARK_PAINTS_1 RegisterSignal(src, COMSIG_OBJ_PAINTED, PROC_REF(on_painted)) AddElement(/datum/element/atmos_sensitive, mapload) - AddComponent(/datum/component/simple_rotation, ROTATION_NEEDS_ROOM, AfterRotation = CALLBACK(src, PROC_REF(AfterRotation))) + AddComponent(/datum/component/simple_rotation, ROTATION_NEEDS_ROOM, post_rotation = CALLBACK(src, PROC_REF(post_rotation))) var/static/list/loc_connections = list( COMSIG_ATOM_EXIT = PROC_REF(on_exit), @@ -358,7 +358,7 @@ dropped_debris += new /obj/item/stack/rods(location, (fulltile ? 2 : 1)) return dropped_debris -/obj/structure/window/proc/AfterRotation(mob/user, degrees) +/obj/structure/window/proc/post_rotation(mob/user, degrees) air_update_turf(TRUE, FALSE) /obj/structure/window/proc/on_painted(obj/structure/window/source, mob/user, obj/item/toy/crayon/spraycan/spraycan, is_dark_color) diff --git a/code/modules/art/statues.dm b/code/modules/art/statues.dm index 2158104b6eefe..a3c43a13b7e7b 100644 --- a/code/modules/art/statues.dm +++ b/code/modules/art/statues.dm @@ -26,6 +26,7 @@ AddElement(art_type, impressiveness) AddElement(/datum/element/beauty, impressiveness * 75) AddComponent(/datum/component/simple_rotation) + AddComponent(/datum/component/marionette) /obj/structure/statue/wrench_act(mob/living/user, obj/item/tool) . = ..() diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index a7a641bb8842c..0353744b298e0 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -18,9 +18,9 @@ . = ..() beams = list() START_PROCESSING(SSobj, src) - AddComponent(/datum/component/simple_rotation, AfterRotation = CALLBACK(src, PROC_REF(AfterRotation))) + AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation))) -/obj/item/assembly/infra/proc/AfterRotation(mob/user, degrees) +/obj/item/assembly/infra/proc/post_rotation(mob/user, degrees) refreshBeam() /obj/item/assembly/infra/AltClick(mob/user) diff --git a/code/modules/basketball/hoop.dm b/code/modules/basketball/hoop.dm index f356fc5231588..b26fa462b0999 100644 --- a/code/modules/basketball/hoop.dm +++ b/code/modules/basketball/hoop.dm @@ -24,7 +24,7 @@ /obj/structure/hoop/Initialize(mapload) . = ..() - AddComponent(/datum/component/simple_rotation, ROTATION_REQUIRE_WRENCH|ROTATION_IGNORE_ANCHORED, AfterRotation = CALLBACK(src, PROC_REF(reset_appearance))) + AddComponent(/datum/component/simple_rotation, ROTATION_REQUIRE_WRENCH|ROTATION_IGNORE_ANCHORED, post_rotation = CALLBACK(src, PROC_REF(reset_appearance))) update_appearance() register_context() diff --git a/code/modules/recycling/disposal/construction.dm b/code/modules/recycling/disposal/construction.dm index b1556ca679f88..4b8fef129244e 100644 --- a/code/modules/recycling/disposal/construction.dm +++ b/code/modules/recycling/disposal/construction.dm @@ -33,12 +33,12 @@ pipename = initial(pipe_type.name) - AddComponent(/datum/component/simple_rotation, AfterRotation = CALLBACK(src, PROC_REF(AfterRotation))) + AddComponent(/datum/component/simple_rotation, post_rotation = CALLBACK(src, PROC_REF(post_rotation))) AddElement(/datum/element/undertile, TRAIT_T_RAY_VISIBLE) if(flip) var/datum/component/simple_rotation/rotcomp = GetComponent(/datum/component/simple_rotation) - rotcomp.Rotate(usr, ROTATION_FLIP) // this only gets used by pipes created by RPDs or pipe dispensers + rotcomp.rotate(usr, ROTATION_FLIP) // this only gets used by pipes created by RPDs or pipe dispensers update_appearance(UPDATE_ICON) @@ -86,7 +86,7 @@ dpdir |= REVERSE_DIR(dir) return dpdir -/obj/structure/disposalconstruct/proc/AfterRotation(mob/user, degrees) +/obj/structure/disposalconstruct/proc/post_rotation(mob/user, degrees) if(degrees == ROTATION_FLIP) var/obj/structure/disposalpipe/temp = pipe_type if(initial(temp.flip_type)) diff --git a/code/modules/surgery/organs/internal/tongue/_tongue.dm b/code/modules/surgery/organs/internal/tongue/_tongue.dm index a39bdfc727ef7..5098d738d305b 100644 --- a/code/modules/surgery/organs/internal/tongue/_tongue.dm +++ b/code/modules/surgery/organs/internal/tongue/_tongue.dm @@ -184,6 +184,7 @@ liked_foodtypes = GORE | MEAT | SEAFOOD | NUTS | BUGS disliked_foodtypes = GRAIN | DAIRY | CLOTH | GROSS voice_filter = @{"[0:a] asplit [out0][out2]; [out0] asetrate=%SAMPLE_RATE%*0.9,aresample=%SAMPLE_RATE%,atempo=1/0.9,aformat=channel_layouts=mono,volume=0.2 [p0]; [out2] asetrate=%SAMPLE_RATE%*1.1,aresample=%SAMPLE_RATE%,atempo=1/1.1,aformat=channel_layouts=mono,volume=0.2[p2]; [p0][0][p2] amix=inputs=3"} + /obj/item/organ/internal/tongue/lizard/modify_speech(datum/source, list/speech_args) var/static/regex/lizard_hiss = new("s+", "g") var/static/regex/lizard_hiSS = new("S+", "g") diff --git a/tgstation.dme b/tgstation.dme index 92c4b4ddb166c..d14bab6c226f4 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1113,6 +1113,7 @@ #include "code\datums\components\manual_blinking.dm" #include "code\datums\components\manual_breathing.dm" #include "code\datums\components\manual_heart.dm" +#include "code\datums\components\marionette.dm" #include "code\datums\components\mind_linker.dm" #include "code\datums\components\mirv.dm" #include "code\datums\components\mob_chain.dm" From 6d1105a8489d618788e4648ead1423fd42e8edca Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 02:19:48 +1300 Subject: [PATCH 011/508] Automatic changelog for PR #81188 [ci skip] --- html/changelogs/AutoChangeLog-pr-81188.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81188.yml diff --git a/html/changelogs/AutoChangeLog-pr-81188.yml b/html/changelogs/AutoChangeLog-pr-81188.yml new file mode 100644 index 0000000000000..341cdf288adf3 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81188.yml @@ -0,0 +1,5 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - bugfix: "Species that can eat mice don't get disgusted from seeing one in the toilet bong." + - rscadd: "Grabbing an unwrenched statue/mannequin/skeleton model will now move its direction as you move yours, and you can talk through it." \ No newline at end of file From 0a496f180c627b9de26d3982d775cbf323fbc459 Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Thu, 8 Feb 2024 05:28:18 -0800 Subject: [PATCH 012/508] Refactors fancy type generation (#81259) ## About The Pull Request [Refactors fancy type generation](https://github.com/tgstation/tgstation/commit/3f218ac7b714a87477d3bd96425df709c6e7fa27) Ok so we have this proc that generates concatenated names for types so admins have a nice list to sort through. The trouble is this is done by, for each type, iterating all possible replacements, and seeing which ones apply (with expensive string operations) A clean run of this applied to all datums takes about 3.5 seconds on my pc. This sucks. Ok so can we do better. Well, yes, pretty easily. Rather then, for each potential type, iterating all the options, let's build a zebra typecache (a lookup list of type -> string to use), and use that. Then we can use a list of replacement -> the bit to tear out to figure out what to remove. This works quite well. It does mean that we're doing it based off the type tree and not type paths, so if we didn't have a replacement for like, mob, it'd look weird, but we don't have cases like that so it's fine. Or well we sorta did, didn't have anything for atom movables or areas, but I fixed that so sall good. Anyway, we only need to do this work once. It takes about 0.3 seconds on my machine, so we can cache it. Just this on its own would technically slow init, since we have a some code that's running this proc off static, but we can just not, that's fine (technically saves init time too since we don't have to burn 0.1 seconds on it anymore). This brings the cost of generating this list for all datums from 3 seconds to 0.16, assuming we have the static pre generated. We could in theory pre-generate just like, all the strings? But I don't think the cached cost is high enough for that to be a real problem. IDK open to other thoughts Oh also I had to reorder the strings in that list, cause zebra_typecacheof has reverse priority. s life [Updates stat tracking macro to work at world start](https://github.com/tgstation/tgstation/commit/1fbfb701a16e6df7170ee642bef66b16652281d3) It for some reason doesn't actually get anything this early, but now at least the logging would in theory function ## Why It's Good For The Game Better response times for admins, faster code, more better --- code/__DEFINES/stat_tracking.dm | 3 +- code/__HELPERS/type_processing.dm | 142 +++++++++++++++++------------- code/modules/admin/admin_verbs.dm | 3 +- 3 files changed, 82 insertions(+), 66 deletions(-) diff --git a/code/__DEFINES/stat_tracking.dm b/code/__DEFINES/stat_tracking.dm index d143e5df4c70e..9bd697440406c 100644 --- a/code/__DEFINES/stat_tracking.dm +++ b/code/__DEFINES/stat_tracking.dm @@ -54,7 +54,8 @@ #define EXPORT_STATS_TO_FILE_LATER(filename, costs, counts, proc) \ do { \ var/static/last_export = 0; \ - if (world.time - last_export > 1.1 SECONDS) { \ + /* Need to always run if we haven't yet, since this code can be placed ANYWHERE */ \ + if (world.time - last_export > 1.1 SECONDS || (last_export == 0)) { \ last_export = world.time; \ /* spawn() is used here because this is often used to track init times, where timers act oddly. */ \ /* I was making timers and even after init times were complete, the timers didn't run :shrug: */ \ diff --git a/code/__HELPERS/type_processing.dm b/code/__HELPERS/type_processing.dm index 117067a057828..dfd74f7e3c6bc 100644 --- a/code/__HELPERS/type_processing.dm +++ b/code/__HELPERS/type_processing.dm @@ -1,14 +1,60 @@ /proc/make_types_fancy(list/types) if (ispath(types)) types = list(types) - . = list() - for(var/type in types) - var/typename = "[type]" - // Longest paths comes first - var/static/list/TYPES_SHORTCUTS = list( - /obj/effect/decal/cleanable = "CLEANABLE", + var/static/list/types_to_replacement + var/static/list/replacement_to_text + if(!types_to_replacement) + // Longer paths come after shorter ones, try and keep the structure + var/list/work_from = list( + /datum = "DATUM", + /area = "AREA", + /atom/movable = "MOVABLE", + /obj = "OBJ", + /turf = "TURF", + /turf/closed = "CLOSED", + /turf/open = "OPEN", + + /mob = "MOB", + /mob/living = "LIVING", + /mob/living/carbon = "CARBON", + /mob/living/carbon/human = "HUMANOID", + /mob/living/simple_animal = "SIMPLE", + /mob/living/basic = "BASIC", + /mob/living/silicon = "SILICON", + /mob/living/silicon/robot = "CYBORG", + + /obj/item = "ITEM", + /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", + /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", + /obj/item/organ = "ORGAN", + /obj/item/mod/control = "MODSUIT", + /obj/item/mod/module = "MODSUIT_MOD", + /obj/item/gun = "GUN", + /obj/item/gun/magic = "GUN_MAGIC", + /obj/item/gun/energy = "GUN_ENERGY", + /obj/item/gun/energy/laser = "GUN_LASER", + /obj/item/gun/ballistic = "GUN_BALLISTIC", + /obj/item/gun/ballistic/automatic = "GUN_AUTOMATIC", + /obj/item/gun/ballistic/revolver = "GUN_REVOLVER", + /obj/item/gun/ballistic/rifle = "GUN_RIFLE", + /obj/item/gun/ballistic/shotgun = "GUN_SHOTGUN", + /obj/item/stack/sheet = "SHEET", + /obj/item/stack/sheet/mineral = "MINERAL_SHEET", + /obj/item/stack/ore = "ORE", + /obj/item/ai_module = "AI_LAW_MODULE", + /obj/item/circuitboard = "CIRCUITBOARD", + /obj/item/circuitboard/machine = "MACHINE_BOARD", + /obj/item/circuitboard/computer = "COMPUTER_BOARD", + /obj/item/reagent_containers = "REAGENT_CONTAINERS", + /obj/item/reagent_containers/pill = "PILL", + /obj/item/reagent_containers/pill/patch = "MEDPATCH", + /obj/item/reagent_containers/hypospray/medipen = "MEDIPEN", + /obj/item/reagent_containers/cup/glass = "DRINK", + /obj/item/food = "FOOD", /obj/item/bodypart = "BODYPART", + /obj/effect/decal/cleanable = "CLEANABLE", /obj/item/radio/headset = "HEADSET", + /obj/item/clothing = "CLOTHING", /obj/item/clothing/accessory = "ACCESSORY", /obj/item/clothing/mask/gas = "GASMASK", /obj/item/clothing/mask = "MASK", @@ -21,75 +67,45 @@ /obj/item/clothing/head/helmet = "HELMET", /obj/item/clothing/head = "HEAD", /obj/item/clothing/neck = "NECK", - /obj/item/clothing = "CLOTHING", /obj/item/storage/backpack = "BACKPACK", /obj/item/storage/belt = "BELT", - /obj/item/book/manual = "MANUAL", /obj/item/storage/pill_bottle = "PILL_BOTTLE", - /obj/item/reagent_containers/pill/patch = "MEDPATCH", - /obj/item/reagent_containers/pill = "PILL", - /obj/item/reagent_containers/hypospray/medipen = "MEDIPEN", - /obj/item/reagent_containers/cup/glass = "DRINK", - /obj/item/food = "FOOD", - /obj/item/reagent_containers = "REAGENT_CONTAINERS", - /obj/machinery/atmospherics = "ATMOS_MECH", - /obj/machinery/portable_atmospherics = "PORT_ATMOS", - /obj/item/mecha_parts/mecha_equipment/weapon = "MECHA_WEAPON", - /obj/item/mecha_parts/mecha_equipment = "MECHA_EQUIP", - /obj/item/organ = "ORGAN", - /obj/item/mod/control = "MODSUIT", - /obj/item/mod/module = "MODSUIT_MOD", - /obj/item/gun/ballistic/automatic = "GUN_AUTOMATIC", - /obj/item/gun/ballistic/revolver = "GUN_REVOLVER", - /obj/item/gun/ballistic/rifle = "GUN_RIFLE", - /obj/item/gun/ballistic/shotgun = "GUN_SHOTGUN", - /obj/item/gun/ballistic = "GUN_BALLISTIC", - /obj/item/gun/energy/laser = "GUN_LASER", - /obj/item/gun/energy = "GUN_ENERGY", - /obj/item/gun/magic = "GUN_MAGIC", - /obj/item/gun = "GUN", - /obj/item/stack/sheet/mineral = "MINERAL_SHEET", - /obj/item/stack/sheet = "SHEET", - /obj/item/stack/ore = "ORE", - /obj/item/ai_module = "AI_LAW_MODULE", - /obj/item/circuitboard/machine = "MACHINE_BOARD", - /obj/item/circuitboard/computer = "COMPUTER_BOARD", - /obj/item/circuitboard = "CIRCUITBOARD", - /obj/item = "ITEM", - /obj/structure/closet/crate/secure = "LOCKED_CRATE", + /obj/item/book/manual = "MANUAL", + + /obj/structure = "STRUCTURE", + /obj/structure/closet = "CLOSET", /obj/structure/closet/crate = "CRATE", + /obj/structure/closet/crate/secure = "LOCKED_CRATE", /obj/structure/closet/secure_closet = "LOCKED_CLOSET", - /obj/structure/closet = "CLOSET", - /obj/structure = "STRUCTURE", - /obj/machinery/door/airlock = "AIRLOCK", + + /obj/machinery = "MACHINERY", + /obj/machinery/atmospherics = "ATMOS_MECH", + /obj/machinery/portable_atmospherics = "PORT_ATMOS", /obj/machinery/door = "DOOR", + /obj/machinery/door/airlock = "AIRLOCK", /obj/machinery/rnd/production = "RND_FABRICATOR", - /obj/machinery/computer/camera_advanced/shuttle_docker = "DOCKING_COMPUTER", /obj/machinery/computer = "COMPUTER", - /obj/machinery/vending/wardrobe = "JOBDROBE", + /obj/machinery/computer/camera_advanced/shuttle_docker = "DOCKING_COMPUTER", /obj/machinery/vending = "VENDING", - /obj/machinery = "MACHINERY", + /obj/machinery/vending/wardrobe = "JOBDROBE", /obj/effect = "EFFECT", /obj/projectile = "PROJECTILE", - /obj = "O", - /datum = "D", - /turf/open = "OPEN", - /turf/closed = "CLOSED", - /turf = "T", - /mob/living/carbon/human = "HUMANOID", - /mob/living/carbon = "CARBON", - /mob/living/simple_animal = "SIMPLE", - /mob/living/basic = "BASIC", - /mob/living/silicon/robot = "CYBORG", - /mob/living/silicon = "SILICON", - /mob/living = "LIVING", - /mob = "M", ) - for (var/tn in TYPES_SHORTCUTS) - if(copytext(typename, 1, length("[tn]/") + 1) == "[tn]/" /*findtextEx(typename,"[tn]/",1,2)*/ ) - typename = TYPES_SHORTCUTS[tn] + copytext(typename, length("[tn]/")) - break - .[typename] = type + // ignore_root_path so we can draw the root normally + types_to_replacement = zebra_typecacheof(work_from, ignore_root_path = TRUE) + replacement_to_text = list() + for(var/key in work_from) + replacement_to_text[work_from[key]] = "[key]" + + . = list() + for(var/type in types) + var/replace_with = types_to_replacement[type] + if(!replace_with) + .["[type]"] = type + continue + var/cut_out = replacement_to_text[replace_with] + // + 1 to account for / + .[replace_with + copytext("[type]", length(cut_out) + 1)] = type /proc/get_fancy_list_of_atom_types() var/static/list/pre_generated_list diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7bff29bc32c8d..8a278c1cc9131 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -1173,8 +1173,7 @@ GLOBAL_PROTECT(admin_verbs_poll) var/desired_mob = text2path(attempted_target_path) if(!ispath(desired_mob)) - var/static/list/mob_paths = make_types_fancy(subtypesof(/mob/living)) - desired_mob = pick_closest_path(attempted_target_path, mob_paths) + desired_mob = pick_closest_path(attempted_target_path, make_types_fancy(subtypesof(/mob/living))) if(isnull(desired_mob) || !ispath(desired_mob) || QDELETED(head)) return //The user pressed "Cancel" From 9dc8c12cb0d44b37be3715565e4c77c911d71fff Mon Sep 17 00:00:00 2001 From: Swift Date: Thu, 8 Feb 2024 12:01:52 -0600 Subject: [PATCH 013/508] Adds head-only target hotkey (#81222) ## About The Pull Request Adds a new optional hotkey that targets only the head and does not cycle to eyes or mouth. Currently the way the numpad hotkeys work on /tg/ is every key targets one body part... except 8. Numpad 8 Currently cycles through head-eyes-mouth, despite _**numpad 7 already handling mouth and numpad 9 already handling eyes**_. There's no head-only key. --- code/__DEFINES/keybinding.dm | 1 + code/datums/keybinding/mob.dm | 9 +++++++++ code/modules/mob/mob_movement.dm | 19 +++++++++++++++---- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/code/__DEFINES/keybinding.dm b/code/__DEFINES/keybinding.dm index 285b7e6ea588f..2a2a092c6d05b 100644 --- a/code/__DEFINES/keybinding.dm +++ b/code/__DEFINES/keybinding.dm @@ -66,6 +66,7 @@ #define COMSIG_KB_MOB_ACTIVATEINHAND_DOWN "keybinding_mob_activateinhand_down" #define COMSIG_KB_MOB_DROPITEM_DOWN "keybinding_mob_dropitem_down" #define COMSIG_KB_MOB_TARGETCYCLEHEAD_DOWN "keybinding_mob_targetcyclehead_down" +#define COMSIG_KB_MOB_TARGETHEAD_DOWN "keybinding_mob_targethead_down" #define COMSIG_KB_MOB_TARGETEYES_DOWN "keybinding_mob_targeteyes_down" #define COMSIG_KB_MOB_TARGETMOUTH_DOWN "keybinding_mob_targetmouth_down" #define COMSIG_KB_MOB_TARGETRIGHTARM_DOWN "keybinding_mob_targetrightarm_down" diff --git a/code/datums/keybinding/mob.dm b/code/datums/keybinding/mob.dm index def4ec36fb2c1..4963e87266cbe 100644 --- a/code/datums/keybinding/mob.dm +++ b/code/datums/keybinding/mob.dm @@ -80,6 +80,8 @@ switch(keybind_signal) if(COMSIG_KB_MOB_TARGETCYCLEHEAD_DOWN) user.body_toggle_head() + if(COMSIG_KB_MOB_TARGETHEAD_DOWN) + user.body_head() if(COMSIG_KB_MOB_TARGETEYES_DOWN) user.body_eyes() if(COMSIG_KB_MOB_TARGETMOUTH_DOWN) @@ -108,6 +110,13 @@ description = "Pressing this key targets the head, and continued presses will cycle to the eyes and mouth. This will impact where you hit people, and can be used for surgery." keybind_signal = COMSIG_KB_MOB_TARGETCYCLEHEAD_DOWN +/datum/keybinding/mob/target/head + hotkey_keys = list("Unbound") + name = "target_head" + full_name = "Target: Head" + description = "Pressing this key targets the head. This will impact where you hit people, and can be used for surgery." + keybind_signal = COMSIG_KB_MOB_TARGETHEAD_DOWN + /datum/keybinding/mob/target/eyes hotkey_keys = list("Numpad7") name = "target_eyes" diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index b96f3aec6ca51..9b62bd0774331 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -352,7 +352,7 @@ //bodypart selection verbs - Cyberboss //8: repeated presses toggles through head - eyes - mouth -//9: eyes 8: head 7: mouth +//7: mouth 8: head 9: eyes //4: r-arm 5: chest 6: l-arm //1: r-leg 2: groin 3: l-leg @@ -361,12 +361,12 @@ return mob && mob.hud_used && mob.hud_used.zone_select && istype(mob.hud_used.zone_select, /atom/movable/screen/zone_sel) /** - * Hidden verb to set the target zone of a mob to the head + * Hidden verbs to set desired body target zone * - * (bound to 8) - repeated presses toggles through head - eyes - mouth + * Uses numpad keys 1-9 */ -///Hidden verb to target the head, bound to 8 +///Hidden verb to cycle through head zone with repeated presses, head - eyes - mouth. Bound to 8 /client/verb/body_toggle_head() set name = "body-toggle-head" set hidden = TRUE @@ -386,6 +386,17 @@ var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select selector.set_selected_zone(next_in_line, mob) +///Hidden verb to target the head, unbound by default. +/client/verb/body_head() + set name = "body-head" + set hidden = TRUE + + if(!check_has_body_select()) + return + + var/atom/movable/screen/zone_sel/selector = mob.hud_used.zone_select + selector.set_selected_zone(BODY_ZONE_HEAD, mob) + ///Hidden verb to target the eyes, bound to 7 /client/verb/body_eyes() set name = "body-eyes" From 6b8f156ed7fba65ab15cfce86115ede883ae44b0 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 07:02:16 +1300 Subject: [PATCH 014/508] Automatic changelog for PR #81222 [ci skip] --- html/changelogs/AutoChangeLog-pr-81222.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81222.yml diff --git a/html/changelogs/AutoChangeLog-pr-81222.yml b/html/changelogs/AutoChangeLog-pr-81222.yml new file mode 100644 index 0000000000000..9ec413002c040 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81222.yml @@ -0,0 +1,4 @@ +author: "Swiftfeather" +delete-after: True +changes: + - qol: "Added a new head-only target hotkey, unbound by default." \ No newline at end of file From 9fa20d9bcede3f1e05470115368856864f73549d Mon Sep 17 00:00:00 2001 From: lessthanthree <83487515+lessthnthree@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:11:54 +0000 Subject: [PATCH 015/508] SM cascade warnings only show to players in game (#81343) ## About The Pull Request Fixes https://github.com/tgstation/tgstation/issues/81337 ## Changelog :cl: LT3 fix: SM cascade delam messages no longer display to clients not in game /:cl: --- .../supermatter/supermatter_delamination/cascade_delam.dm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm b/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm index 5d8cfecaa40d2..a9c7a87045da3 100644 --- a/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm +++ b/code/modules/power/supermatter/supermatter_delamination/cascade_delam.dm @@ -28,8 +28,7 @@ "Something feels very off.", "A drowning sense of dread washes over you.", ) - for(var/mob/victim as anything in GLOB.player_list) - to_chat(victim, span_danger(pick(messages))) + dispatch_announcement_to_players(span_danger(pick(messages)), should_play_sound = FALSE) return TRUE From 456a6f354ef1acb1fea5a0dd3c96606260a7ad2f Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:12:12 +1300 Subject: [PATCH 016/508] Automatic changelog for PR #81343 [ci skip] --- html/changelogs/AutoChangeLog-pr-81343.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81343.yml diff --git a/html/changelogs/AutoChangeLog-pr-81343.yml b/html/changelogs/AutoChangeLog-pr-81343.yml new file mode 100644 index 0000000000000..1dbe541d7b8a8 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81343.yml @@ -0,0 +1,4 @@ +author: "LT3" +delete-after: True +changes: + - bugfix: "SM cascade delam messages no longer display to clients not in game" \ No newline at end of file From 0305747692a35e87af7d140d73b3f7a346d01e8e Mon Sep 17 00:00:00 2001 From: Zergspower Date: Thu, 8 Feb 2024 14:13:15 -0600 Subject: [PATCH 017/508] Fixes icebox ore generation (#81336) ## About The Pull Request Simple as the title, the last PR https://github.com/tgstation/tgstation/pull/81103 i think missed the ores that go around the vents as seen below ![image](https://github.com/tgstation/tgstation/assets/22140677/a6ba27b5-ce9e-4989-828a-67d81eca624b) It's calling wall turfs that have no ore generation inbedded in them, this corrects that ![image](https://github.com/tgstation/tgstation/assets/22140677/52307a76-b992-4d23-be68-f81b019dbac1) ## Why It's Good For The Game ## Changelog :cl: fix: icebox ore generation underground is normal again /:cl: --- code/datums/mapgen/Cavegens/IcemoonCaves.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/datums/mapgen/Cavegens/IcemoonCaves.dm b/code/datums/mapgen/Cavegens/IcemoonCaves.dm index 9d7fb39af710c..91348dd5c1783 100644 --- a/code/datums/mapgen/Cavegens/IcemoonCaves.dm +++ b/code/datums/mapgen/Cavegens/IcemoonCaves.dm @@ -1,7 +1,7 @@ /datum/map_generator/cave_generator/icemoon weighted_open_turf_types = list(/turf/open/misc/asteroid/snow/icemoon = 19, /turf/open/misc/ice/icemoon = 1) weighted_closed_turf_types = list( - /turf/closed/mineral/snowmountain/icemoon = 100, + /turf/closed/mineral/random/snow = 100, /turf/closed/mineral/gibtonite/ice/icemoon = 4, ) @@ -70,7 +70,7 @@ /datum/map_generator/cave_generator/icemoon/surface/noruins //use this for when you don't want ruins to spawn in a certain area /datum/map_generator/cave_generator/icemoon/deep - weighted_closed_turf_types = list(/turf/closed/mineral/snowmountain/icemoon = 1) + weighted_closed_turf_types = list(/turf/closed/mineral/random/snow = 1) weighted_mob_spawn_list = list( SPAWN_MEGAFAUNA = 1, /mob/living/basic/mining/ice_demon = 100, From fc1020631f7d743509845a8e69bd877ecdc49cca Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:13:35 +1300 Subject: [PATCH 018/508] Automatic changelog for PR #81336 [ci skip] --- html/changelogs/AutoChangeLog-pr-81336.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81336.yml diff --git a/html/changelogs/AutoChangeLog-pr-81336.yml b/html/changelogs/AutoChangeLog-pr-81336.yml new file mode 100644 index 0000000000000..db11fd25d0c26 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81336.yml @@ -0,0 +1,4 @@ +author: "Zergspower" +delete-after: True +changes: + - bugfix: "icebox ore generation underground is normal again" \ No newline at end of file From 49fc6207f4fc9b4a06c4746fdd17a7428daae9ba Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:15:34 -0600 Subject: [PATCH 019/508] Fix Krav Maga not shoving (#81340) ## About The Pull Request I thought it was normal for Krav to not shove but apparently it was reverted ## Changelog :cl: Melbert fix: Krav Maga users can shove again /:cl: --- code/datums/martial/krav_maga.dm | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/code/datums/martial/krav_maga.dm b/code/datums/martial/krav_maga.dm index c34afd6c1cae8..cda53bbe6475e 100644 --- a/code/datums/martial/krav_maga.dm +++ b/code/datums/martial/krav_maga.dm @@ -186,7 +186,6 @@ return MARTIAL_ATTACK_FAIL if(check_streak(attacker, defender)) return MARTIAL_ATTACK_SUCCESS - attacker.do_attack_animation(defender, ATTACK_EFFECT_DISARM) var/obj/item/stuff_in_hand = defender.get_active_held_item() if(prob(60) && stuff_in_hand && defender.temporarilyRemoveItemFromInventory(stuff_in_hand)) attacker.put_in_hands(stuff_in_hand) @@ -200,19 +199,7 @@ to_chat(attacker, span_danger("You disarm [defender]!")) playsound(defender, 'sound/weapons/thudswoosh.ogg', 50, TRUE, -1) log_combat(attacker, defender, "disarmed (Krav Maga)", addition = "(disarmed of [stuff_in_hand])") - return MARTIAL_ATTACK_SUCCESS - - defender.visible_message( - span_danger("[attacker] fails to disarm [defender]!"), \ - span_userdanger("You're nearly disarmed by [attacker]!"), - span_hear("You hear a swoosh!"), - COMBAT_MESSAGE_RANGE, - attacker, - ) - to_chat(attacker, span_warning("You fail to disarm [defender]!")) - playsound(defender, 'sound/weapons/punchmiss.ogg', 25, TRUE, -1) - log_combat(attacker, defender, "failed to disarm (Krav Maga)") - return MARTIAL_ATTACK_FAIL + return MARTIAL_ATTACK_INVALID // normal shove //Krav Maga Gloves From c0162f49ac4145b45046713958fc1bb3fd681711 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 09:20:53 +1300 Subject: [PATCH 020/508] Automatic changelog for PR #81340 [ci skip] --- html/changelogs/AutoChangeLog-pr-81340.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81340.yml diff --git a/html/changelogs/AutoChangeLog-pr-81340.yml b/html/changelogs/AutoChangeLog-pr-81340.yml new file mode 100644 index 0000000000000..8e693fd826d47 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81340.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "Krav Maga users can shove again" \ No newline at end of file From 1f2bf04e75dc2ebde12d1208453f21eeb7d60622 Mon Sep 17 00:00:00 2001 From: ViktorKoL <44502667+ViktorKoL@users.noreply.github.com> Date: Thu, 8 Feb 2024 22:37:14 +0100 Subject: [PATCH 021/508] fix return_inv() returning a null sometimes, causing get_all_gear() to runtime (#81344) ## About The Pull Request Fixes the return_inv() proc of datum/storage returning a null in addition to inventory items. This caused get_all_gear() proc of mob/living to runtime at times, because it would try to recursively access a null's inventory... ## Why It's Good For The Game Bugs bad. ## Changelog :cl: fix: fix heretic's rust mark failing to damage any items if the victim has any container on them with another item inside, and maybe other bugs of similar nature /:cl: --- code/datums/storage/storage.dm | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/code/datums/storage/storage.dm b/code/datums/storage/storage.dm index 915bdea22d4d3..8213bf347abfd 100644 --- a/code/datums/storage/storage.dm +++ b/code/datums/storage/storage.dm @@ -605,12 +605,11 @@ GLOBAL_LIST_EMPTY(cached_storage_typecaches) */ /datum/storage/proc/return_inv(recursive = TRUE) var/list/ret = list() - ret |= real_location.contents for(var/atom/found_thing as anything in real_location) ret |= found_thing - if(recursive) - ret |= found_thing.atom_storage?.return_inv(ret, recursive = TRUE) + if(recursive && found_thing.atom_storage) + ret |= found_thing.atom_storage.return_inv(recursive = TRUE) return ret From c6a1204431582d92765ce17e629b56920fb7886d Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 10:37:31 +1300 Subject: [PATCH 022/508] Automatic changelog for PR #81344 [ci skip] --- html/changelogs/AutoChangeLog-pr-81344.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81344.yml diff --git a/html/changelogs/AutoChangeLog-pr-81344.yml b/html/changelogs/AutoChangeLog-pr-81344.yml new file mode 100644 index 0000000000000..0af3c19a0e49b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81344.yml @@ -0,0 +1,4 @@ +author: "ViktorKoL" +delete-after: True +changes: + - bugfix: "fix heretic's rust mark failing to damage any items if the victim has any container on them with another item inside, and maybe other bugs of similar nature" \ No newline at end of file From bc8dd08d5d3fcd5096ce67dae874cb6a54b143cb Mon Sep 17 00:00:00 2001 From: Changelogs Date: Fri, 9 Feb 2024 00:19:23 +0000 Subject: [PATCH 023/508] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-81170.yml | 5 --- html/changelogs/AutoChangeLog-pr-81188.yml | 5 --- html/changelogs/AutoChangeLog-pr-81222.yml | 4 -- html/changelogs/AutoChangeLog-pr-81244.yml | 12 ------ html/changelogs/AutoChangeLog-pr-81252.yml | 4 -- html/changelogs/AutoChangeLog-pr-81281.yml | 5 --- html/changelogs/AutoChangeLog-pr-81307.yml | 5 --- html/changelogs/AutoChangeLog-pr-81324.yml | 4 -- html/changelogs/AutoChangeLog-pr-81336.yml | 4 -- html/changelogs/AutoChangeLog-pr-81340.yml | 4 -- html/changelogs/AutoChangeLog-pr-81343.yml | 4 -- html/changelogs/AutoChangeLog-pr-81344.yml | 4 -- html/changelogs/archive/2024-02.yml | 45 ++++++++++++++++++++++ 13 files changed, 45 insertions(+), 60 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-81170.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81188.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81222.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81244.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81252.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81281.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81307.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81324.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81336.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81340.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81343.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81344.yml diff --git a/html/changelogs/AutoChangeLog-pr-81170.yml b/html/changelogs/AutoChangeLog-pr-81170.yml deleted file mode 100644 index 8cefce36647c7..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81170.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Ghommie" -delete-after: True -changes: - - bugfix: "Fixed text effects for runechat messages (the stuff enclosed in +, | and _ characters)." - - spellcheck: "Improved the tip for say/text effects." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81188.yml b/html/changelogs/AutoChangeLog-pr-81188.yml deleted file mode 100644 index 341cdf288adf3..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81188.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - bugfix: "Species that can eat mice don't get disgusted from seeing one in the toilet bong." - - rscadd: "Grabbing an unwrenched statue/mannequin/skeleton model will now move its direction as you move yours, and you can talk through it." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81222.yml b/html/changelogs/AutoChangeLog-pr-81222.yml deleted file mode 100644 index 9ec413002c040..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81222.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Swiftfeather" -delete-after: True -changes: - - qol: "Added a new head-only target hotkey, unbound by default." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81244.yml b/html/changelogs/AutoChangeLog-pr-81244.yml deleted file mode 100644 index 164e0b7edb81d..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81244.yml +++ /dev/null @@ -1,12 +0,0 @@ -author: "SyncIt21" -delete-after: True -changes: - - qol: "adds screentips & examines for screwdriver & crowbar acts & alt click." - - qol: "techfabs can now use the mouse drop functionality to set drop target." - - qol: "lathe printing animation plays on loop while printing rather than flicking once for more visual feedback" - - bugfix: "lathe sheet insertion animations are now linked & work again for all material types inserted via remote silo/local storage" - - bugfix: "printing custom materials items from autolathe works again." - - bugfix: "printing multiple items from lathes will actually print that correct quantity of items requested." - - bugfix: "printing items the 2nd time around from lathes won't cause the UI to reload each time." - - code_imp: "autodoc for some vars & procs, merges procs." - - refactor: "Optimized code for autolathe & techfabs in general. Report bugs on github" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81252.yml b/html/changelogs/AutoChangeLog-pr-81252.yml deleted file mode 100644 index fe5b5391f3bea..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81252.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Wallem" -delete-after: True -changes: - - rscadd: "Adds a new suicide_act() to the smite spell" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81281.yml b/html/changelogs/AutoChangeLog-pr-81281.yml deleted file mode 100644 index 1dd471b0e3fba..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81281.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "EEASAS" -delete-after: True -changes: - - bugfix: "Fixed some things in Ice Box's gas station ruin" - - rscadd: "Adds some things in Ice Box's gas station ruin" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81307.yml b/html/changelogs/AutoChangeLog-pr-81307.yml deleted file mode 100644 index 8666849c68879..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81307.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "Fixed Puncture Repair surgery not having surgical sounds" - - bugfix: "Fixed Surgery Initiator potentially showing invalid surgeries" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81324.yml b/html/changelogs/AutoChangeLog-pr-81324.yml deleted file mode 100644 index e1cb652ed50f7..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81324.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - qol: "The barcode scanner is now part of computer tech and can be printed at the service techfab." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81336.yml b/html/changelogs/AutoChangeLog-pr-81336.yml deleted file mode 100644 index db11fd25d0c26..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81336.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Zergspower" -delete-after: True -changes: - - bugfix: "icebox ore generation underground is normal again" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81340.yml b/html/changelogs/AutoChangeLog-pr-81340.yml deleted file mode 100644 index 8e693fd826d47..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81340.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "Krav Maga users can shove again" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81343.yml b/html/changelogs/AutoChangeLog-pr-81343.yml deleted file mode 100644 index 1dbe541d7b8a8..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81343.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "LT3" -delete-after: True -changes: - - bugfix: "SM cascade delam messages no longer display to clients not in game" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81344.yml b/html/changelogs/AutoChangeLog-pr-81344.yml deleted file mode 100644 index 0af3c19a0e49b..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81344.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "ViktorKoL" -delete-after: True -changes: - - bugfix: "fix heretic's rust mark failing to damage any items if the victim has any container on them with another item inside, and maybe other bugs of similar nature" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index 8001507bdbd48..aec4c211b5605 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -191,3 +191,48 @@ condition to rotate them optimumtact, whomst didn't write this CL entry.: - bugfix: Fixed the colorful lights of the ethereal disco ball. +2024-02-09: + EEASAS: + - bugfix: Fixed some things in Ice Box's gas station ruin + - rscadd: Adds some things in Ice Box's gas station ruin + Ghommie: + - bugfix: Fixed text effects for runechat messages (the stuff enclosed in +, | and + _ characters). + - spellcheck: Improved the tip for say/text effects. + JohnFulpWillard: + - qol: The barcode scanner is now part of computer tech and can be printed at the + service techfab. + - bugfix: Species that can eat mice don't get disgusted from seeing one in the toilet + bong. + - rscadd: Grabbing an unwrenched statue/mannequin/skeleton model will now move its + direction as you move yours, and you can talk through it. + LT3: + - bugfix: SM cascade delam messages no longer display to clients not in game + Melbert: + - bugfix: Fixed Puncture Repair surgery not having surgical sounds + - bugfix: Fixed Surgery Initiator potentially showing invalid surgeries + - bugfix: Krav Maga users can shove again + Swiftfeather: + - qol: Added a new head-only target hotkey, unbound by default. + SyncIt21: + - qol: adds screentips & examines for screwdriver & crowbar acts & alt click. + - qol: techfabs can now use the mouse drop functionality to set drop target. + - qol: lathe printing animation plays on loop while printing rather than flicking + once for more visual feedback + - bugfix: lathe sheet insertion animations are now linked & work again for all material + types inserted via remote silo/local storage + - bugfix: printing custom materials items from autolathe works again. + - bugfix: printing multiple items from lathes will actually print that correct quantity + of items requested. + - bugfix: printing items the 2nd time around from lathes won't cause the UI to reload + each time. + - code_imp: autodoc for some vars & procs, merges procs. + - refactor: Optimized code for autolathe & techfabs in general. Report bugs on github + ViktorKoL: + - bugfix: fix heretic's rust mark failing to damage any items if the victim has + any container on them with another item inside, and maybe other bugs of similar + nature + Wallem: + - rscadd: Adds a new suicide_act() to the smite spell + Zergspower: + - bugfix: icebox ore generation underground is normal again From d91278a589206ed784ba84f47d5e90ddb930ff33 Mon Sep 17 00:00:00 2001 From: 1393F <59183821+1393F@users.noreply.github.com> Date: Thu, 8 Feb 2024 20:35:40 -0500 Subject: [PATCH 024/508] fixes the crystallizer providing itself as the dangerous recipe made + fixes the max temperature on the HFR interface (#81345) ## About The Pull Request the crystallizer gave itself as the dangerous recipe created instead of the created thing the HFR temp thingie was typed incorrectly ## Why It's Good For The Game the correct information is shown ingame ![image](https://github.com/tgstation/tgstation/assets/59183821/a3b12370-c3f5-4563-ac67-b79aeeccbe6f) yummy yummy temperature ## Changelog :cl: fix: The HFR now provides the max temperature for recipes fix: the crystallizer now provides the dangerous object created instead of itself in admin logging /:cl: --- .../machinery/components/gas_recipe_machines/crystallizer.dm | 4 ++-- tgui/packages/tgui/interfaces/Hypertorus/Recipes.tsx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm index a005caf86b98b..1db5f8ad1c68e 100644 --- a/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm +++ b/code/modules/atmospherics/machinery/components/gas_recipe_machines/crystallizer.dm @@ -228,8 +228,8 @@ var/obj/creation = new path(get_step(src, SOUTH)) creation.name = "[quality_control] [creation.name]" if(selected_recipe.dangerous) - investigate_log("has been created in the crystallizer.", INVESTIGATE_ENGINE) - message_admins("[src] has been created in the crystallizer [ADMIN_JMP(src)].") + investigate_log("[creation.name] has been created in the crystallizer.", INVESTIGATE_ENGINE) + message_admins("[creation.name] has been created in the crystallizer [ADMIN_JMP(src)].") quality_loss = 0 diff --git a/tgui/packages/tgui/interfaces/Hypertorus/Recipes.tsx b/tgui/packages/tgui/interfaces/Hypertorus/Recipes.tsx index eb573184fa73e..cc8c4e6eff448 100644 --- a/tgui/packages/tgui/interfaces/Hypertorus/Recipes.tsx +++ b/tgui/packages/tgui/interfaces/Hypertorus/Recipes.tsx @@ -88,7 +88,7 @@ const recipe_effect_structure: Recipe[] = [ override_base: 0.85, scale: 1.15, tooltip: (v, d) => - 'Maximum: ' + (d.baseMaximumTemperature * v).toExponential() + ' K', + 'Maximum: ' + (d.baseMaxTemperature * v).toExponential() + ' K', }, ]; From bb77f65c495341dcf8fe5125c8ece75f7cad3223 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:35:51 -0600 Subject: [PATCH 025/508] Fix finger gun chat messages (#81347) ## About The Pull Request It should not say "[the guy that you're aiming at] fires their gun" ## Changelog :cl: Melbert fix: Fixed Finger Guns giving a misleading chat message /:cl: --- code/modules/spells/spell_types/pointed/finger_guns.dm | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/code/modules/spells/spell_types/pointed/finger_guns.dm b/code/modules/spells/spell_types/pointed/finger_guns.dm index 5ed666d9e64ed..24f51a0e90862 100644 --- a/code/modules/spells/spell_types/pointed/finger_guns.dm +++ b/code/modules/spells/spell_types/pointed/finger_guns.dm @@ -46,4 +46,7 @@ /datum/action/cooldown/spell/pointed/projectile/finger_guns/before_cast(atom/cast_on) . = ..() - invocation = span_notice("[cast_on] fires [cast_on.p_their()] finger gun!") + if(isnull(owner)) + invocation = initial(invocation) + else + invocation = span_notice("[owner] fires [owner.p_their()] finger gun!") From bdbd2edd489902d70b21e5df866cebad41dca3ce Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:36:01 +1300 Subject: [PATCH 026/508] Automatic changelog for PR #81345 [ci skip] --- html/changelogs/AutoChangeLog-pr-81345.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81345.yml diff --git a/html/changelogs/AutoChangeLog-pr-81345.yml b/html/changelogs/AutoChangeLog-pr-81345.yml new file mode 100644 index 0000000000000..0bbc14b258605 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81345.yml @@ -0,0 +1,5 @@ +author: "1393F" +delete-after: True +changes: + - bugfix: "The HFR now provides the max temperature for recipes" + - bugfix: "the crystallizer now provides the dangerous object created instead of itself in admin logging" \ No newline at end of file From 369b3242e177c5b9fe90774cfd759d55dce48103 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:36:10 +1300 Subject: [PATCH 027/508] Automatic changelog for PR #81347 [ci skip] --- html/changelogs/AutoChangeLog-pr-81347.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81347.yml diff --git a/html/changelogs/AutoChangeLog-pr-81347.yml b/html/changelogs/AutoChangeLog-pr-81347.yml new file mode 100644 index 0000000000000..ed3b938860a1d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81347.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "Fixed Finger Guns giving a misleading chat message" \ No newline at end of file From fb4aa75924a22d61d484e826957b228a0b6b4492 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:36:40 -0600 Subject: [PATCH 028/508] Fix `get_visual_offset` results being reversed (#81348) ## About The Pull Request Height -> width, width -> height ## Changelog :cl: Melbert fix: You know that one bug that makes the cryo cells on Deltastation unusuable? Well it's not fixed but at least those cryo cells are usuable again, maybe at the cost of another station's cryo cells. Who knows! /:cl: --- code/__HELPERS/turfs.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm index 069bf55576f66..3c343eea2b58f 100644 --- a/code/__HELPERS/turfs.dm +++ b/code/__HELPERS/turfs.dm @@ -233,8 +233,8 @@ Turf and target are separate in case you want to teleport some distance from a t //Irregular objects var/list/icon_dimensions = get_icon_dimensions(checked_atom.icon) - var/checked_atom_icon_height = icon_dimensions["width"] - var/checked_atom_icon_width = icon_dimensions["height"] + var/checked_atom_icon_height = icon_dimensions["height"] + var/checked_atom_icon_width = icon_dimensions["width"] if(checked_atom_icon_height != world.icon_size || checked_atom_icon_width != world.icon_size) pixel_x_offset += ((checked_atom_icon_width / world.icon_size) - 1) * (world.icon_size * 0.5) pixel_y_offset += ((checked_atom_icon_height / world.icon_size) - 1) * (world.icon_size * 0.5) From 320e63a219121684bf9ae6e9455b4c8e620801cb Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:36:53 -0600 Subject: [PATCH 029/508] `pixel_w` and `pixel_z` are included in `get_visual_offset` (#81349) ## About The Pull Request Closes #81095 Includes `pixel_w` and `pixel_z` in calculating an icon's `get_visual_offset`. Why? In particular, `get_visual_offset` is supposed to return how _visually_ offset an atom is from its loc. `pixel_w` and `pixel_z`, while they do not physically represent the same things as `pixel_x` and `pixel_y` (in that they're actually vertical (as seen with client dir)), still contribute to where an atom visually sits to the user. You may call this another nail in the client dir coffin if you wish. This proc is intended for visual use anyways, so anything using it for physical boundaries is wrong anyways. ## Changelog :cl: Melbert fix: Fixed some situations in which you couldn't interact with heretic runes /:cl: --- code/__HELPERS/turfs.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/__HELPERS/turfs.dm b/code/__HELPERS/turfs.dm index 3c343eea2b58f..32a570ae8fc11 100644 --- a/code/__HELPERS/turfs.dm +++ b/code/__HELPERS/turfs.dm @@ -228,8 +228,8 @@ Turf and target are separate in case you want to teleport some distance from a t //Find checked_atom's matrix so we can use it's X/Y pixel shifts var/matrix/atom_matrix = matrix(checked_atom.transform) - var/pixel_x_offset = checked_atom.pixel_x + atom_matrix.get_x_shift() - var/pixel_y_offset = checked_atom.pixel_y + atom_matrix.get_y_shift() + var/pixel_x_offset = checked_atom.pixel_x + checked_atom.pixel_w + atom_matrix.get_x_shift() + var/pixel_y_offset = checked_atom.pixel_y + checked_atom.pixel_z + atom_matrix.get_y_shift() //Irregular objects var/list/icon_dimensions = get_icon_dimensions(checked_atom.icon) From ed8a9ec3141c4b6c3d471f9152265787c45d850f Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:44:17 +1300 Subject: [PATCH 030/508] Automatic changelog for PR #81348 [ci skip] --- html/changelogs/AutoChangeLog-pr-81348.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81348.yml diff --git a/html/changelogs/AutoChangeLog-pr-81348.yml b/html/changelogs/AutoChangeLog-pr-81348.yml new file mode 100644 index 0000000000000..5e7867c9d1b6b --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81348.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "You know that one bug that makes the cryo cells on Deltastation unusuable? Well it's not fixed but at least those cryo cells are usuable again, maybe at the cost of another station's cryo cells. Who knows!" \ No newline at end of file From 0d1958f79dc255f086f3d587e50361920021dd9c Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:44:51 +1300 Subject: [PATCH 031/508] Automatic changelog for PR #81349 [ci skip] --- html/changelogs/AutoChangeLog-pr-81349.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81349.yml diff --git a/html/changelogs/AutoChangeLog-pr-81349.yml b/html/changelogs/AutoChangeLog-pr-81349.yml new file mode 100644 index 0000000000000..23efa8d682eb7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81349.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - bugfix: "Fixed some situations in which you couldn't interact with heretic runes" \ No newline at end of file From c6f137fdb4dbb6e1f21ecbaca239facc64dddafc Mon Sep 17 00:00:00 2001 From: Holoo <38228316+Holoo-1@users.noreply.github.com> Date: Fri, 9 Feb 2024 06:52:12 +0200 Subject: [PATCH 032/508] Minor update to admin secrets panel (free antags for everyone) (#81292) ## About The Pull Request Remakes a button in admin secrets panel from everyone is traitor to everyone is admin chosen antag. ## Why It's Good For The Game Slightly better buttons for admin to push ## Changelog :cl: admin: remade everyone is traitor into everyone is antag in secrets panel /:cl: --- code/modules/admin/verbs/secrets.dm | 118 +++++++++++++----- code/modules/antagonists/malf_ai/malf_ai.dm | 2 - .../antagonists/traitor/datum_traitor.dm | 1 - .../tgui/interfaces/AntagInfoChangeling.tsx | 6 +- tgui/packages/tgui/interfaces/Secrets.jsx | 4 +- 5 files changed, 88 insertions(+), 43 deletions(-) diff --git a/code/modules/admin/verbs/secrets.dm b/code/modules/admin/verbs/secrets.dm index f9879428ddfe2..20a05685bc9ed 100644 --- a/code/modules/admin/verbs/secrets.dm +++ b/code/modules/admin/verbs/secrets.dm @@ -1,4 +1,4 @@ -GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) +GLOBAL_DATUM(everyone_an_antag, /datum/everyone_is_an_antag_controller) /client/proc/secrets() //Creates a verb for admins to open up the ui set name = "Secrets" @@ -457,24 +457,35 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) for(var/i in GLOB.human_list) var/mob/living/carbon/human/H = i INVOKE_ASYNC(H, TYPE_PROC_REF(/mob/living/carbon, monkeyize)) - if("traitor_all") + if("antag_all") if(!is_funmin) return if(!SSticker.HasRoundStarted()) tgui_alert(usr,"The game hasn't started yet!") return - if(GLOB.everyone_a_traitor) - tgui_alert(usr, "The everyone is a traitor secret has already been triggered") + if(GLOB.everyone_an_antag) + var/are_we_antagstacking = tgui_alert(usr, "The everyone is antag secret has already been triggered. Do you want to stack antags?", "DANGER ZONE. Are you sure about this?", list("Confirm", "Abort")) + if(are_we_antagstacking != "Confirm") + return + + var/chosen_antag = tgui_input_list(usr, "Choose antag", "Chose antag", list(ROLE_TRAITOR, ROLE_CHANGELING, ROLE_HERETIC, ROLE_CULTIST, ROLE_NINJA, ROLE_WIZARD, ROLE_NIGHTMARE)) + if(!chosen_antag) return - var/objective = tgui_input_text(holder, "Enter an objective", "Objective") + var/objective = tgui_input_text(usr, "Enter an objective", "Objective") if(!objective) return - GLOB.everyone_a_traitor = new /datum/everyone_is_a_traitor_controller(objective) - SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("Traitor All", "[objective]")) + var/confirmation = tgui_alert(usr, "Make everyone in to [chosen_antag] with objective: [objective]", "Are you sure about this?", list("Confirm", "Abort")) + if(confirmation != "Confirm") + return + var/keep_generic_objecives = tgui_alert(usr, "Generate normal objectives?", "Give default objectives?", list("Yes", "No")) + keep_generic_objecives = (keep_generic_objecives != "Yes") ? FALSE : TRUE + + GLOB.everyone_an_antag = new /datum/everyone_is_an_antag_controller(chosen_antag, objective, keep_generic_objecives) + SSblackbox.record_feedback("nested tally", "admin_secrets_fun_used", 1, list("[chosen_antag] All", "[objective]")) for(var/mob/living/player in GLOB.player_list) - GLOB.everyone_a_traitor.make_traitor(null, player) - message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is a traitor secret. Objective is [objective]")) - log_admin("[key_name(holder)] used everyone is a traitor secret. Objective is [objective]") + GLOB.everyone_an_antag.make_antag(null, player) + message_admins(span_adminnotice("[key_name_admin(holder)] used everyone is antag secret. Antag is [chosen_antag]. Objective is [objective]. Generate default objectives: [keep_generic_objecives]")) + log_admin("[key_name(holder)] used everyone is antag secret: [chosen_antag] . Objective is [objective]. Generate default objectives: [keep_generic_objecives]. ") if("massbraindamage") if(!is_funmin) return @@ -641,38 +652,79 @@ GLOBAL_DATUM(everyone_a_traitor, /datum/everyone_is_a_traitor_controller) T.flick_overlay_static(portal_appearance[GET_TURF_PLANE_OFFSET(T) + 1], 15) playsound(T, 'sound/magic/lightningbolt.ogg', rand(80, 100), TRUE) -///Makes sure latejoining crewmembers also become traitors. -/datum/everyone_is_a_traitor_controller +/datum/everyone_is_an_antag_controller + var/chosen_antag = "" var/objective = "" + var/keep_generic_objecives -/datum/everyone_is_a_traitor_controller/New(objective) +/datum/everyone_is_an_antag_controller/New(chosen_antag, objective, keep_generic_objecives) + . = ..() + src.chosen_antag = chosen_antag src.objective = objective - RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_traitor)) + src.keep_generic_objecives = keep_generic_objecives + RegisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED, PROC_REF(make_antag_delay)) -/datum/everyone_is_a_traitor_controller/Destroy() +/datum/everyone_is_an_antag_controller/Destroy() UnregisterSignal(SSdcs, COMSIG_GLOB_CREWMEMBER_JOINED) return ..() -/datum/everyone_is_a_traitor_controller/proc/make_traitor(datum/source, mob/living/player) +/datum/everyone_is_an_antag_controller/proc/assign_admin_objective_and_antag(mob/living/player, datum/antagonist/antag_datum) + var/datum/objective/new_objective = new(objective) + new_objective.team = player + new_objective.team_explanation_text = objective + antag_datum.objectives += new_objective + player.mind.add_antag_datum(antag_datum) + +/datum/everyone_is_an_antag_controller/proc/make_antag_delay(datum/source, mob/living/player) SIGNAL_HANDLER + INVOKE_ASYNC(src, PROC_REF(make_antag), source, player) + + +/datum/everyone_is_an_antag_controller/proc/make_antag(datum/source, mob/living/player) if(player.stat == DEAD || !player.mind) return - if(is_special_character(player)) - return + sleep(1) if(ishuman(player)) - var/datum/antagonist/traitor/traitor_datum = new(give_objectives = FALSE) - var/datum/objective/new_objective = new - new_objective.owner = player - new_objective.explanation_text = objective - traitor_datum.objectives += new_objective - player.mind.add_antag_datum(traitor_datum) - var/datum/uplink_handler/uplink = traitor_datum.uplink_handler - uplink.has_progression = FALSE - uplink.has_objectives = FALSE + switch(chosen_antag) + if(ROLE_TRAITOR) + var/datum/antagonist/traitor/antag_datum = new(give_objectives = keep_generic_objecives) + assign_admin_objective_and_antag(player, antag_datum) + var/datum/uplink_handler/uplink = antag_datum.uplink_handler + uplink.has_progression = FALSE + uplink.has_objectives = FALSE + if(ROLE_CHANGELING) + var/datum/antagonist/changeling/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_HERETIC) + var/datum/antagonist/heretic/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_CULTIST) + var/datum/antagonist/cult/antag_datum = new + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_NINJA) + var/datum/antagonist/ninja/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + for(var/obj/item/item_to_drop in player) + if(!istype(item_to_drop, /obj/item/implant)) //avoid removing implanted uplinks + player.dropItemToGround(item_to_drop, FALSE) + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_WIZARD) + var/datum/antagonist/wizard/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + antag_datum.move_to_lair = FALSE + for(var/obj/item/item_to_drop in player) //avoid deleting player's items + if(!istype(item_to_drop, /obj/item/implant)) + player.dropItemToGround(item_to_drop, FALSE) + assign_admin_objective_and_antag(player, antag_datum) + if(ROLE_NIGHTMARE) + var/datum/antagonist/nightmare/antag_datum = new + assign_admin_objective_and_antag(player, antag_datum) + player.set_species(/datum/species/shadow/nightmare) + else if(isAI(player)) - var/datum/antagonist/malf_ai/malfunction_datum = new(give_objectives = FALSE) - var/datum/objective/new_objective = new - new_objective.owner = player - new_objective.explanation_text = objective - malfunction_datum.objectives += new_objective - player.mind.add_antag_datum(malfunction_datum) + var/datum/antagonist/malf_ai/antag_datum = new + antag_datum.give_objectives = keep_generic_objecives + assign_admin_objective_and_antag(player, antag_datum) + diff --git a/code/modules/antagonists/malf_ai/malf_ai.dm b/code/modules/antagonists/malf_ai/malf_ai.dm index 33a8dc8f80a09..f9f422d6e6fc3 100644 --- a/code/modules/antagonists/malf_ai/malf_ai.dm +++ b/code/modules/antagonists/malf_ai/malf_ai.dm @@ -60,8 +60,6 @@ /// Generates a complete set of malf AI objectives up to the traitor objective limit. /datum/antagonist/malf_ai/proc/forge_ai_objectives() - objectives.Cut() - if(prob(PROB_SPECIAL)) forge_special_objective() diff --git a/code/modules/antagonists/traitor/datum_traitor.dm b/code/modules/antagonists/traitor/datum_traitor.dm index 13d3e6ebd8e1f..221f1a35ea71d 100644 --- a/code/modules/antagonists/traitor/datum_traitor.dm +++ b/code/modules/antagonists/traitor/datum_traitor.dm @@ -209,7 +209,6 @@ /// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack. /datum/antagonist/traitor/proc/forge_traitor_objectives() - objectives.Cut() var/objective_count = 0 if((GLOB.joined_player_list.len >= HIJACK_MIN_PLAYERS) && prob(HIJACK_PROB)) diff --git a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx index d7c6c6a4bc7a3..5b1a46c0070e5 100644 --- a/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx +++ b/tgui/packages/tgui/interfaces/AntagInfoChangeling.tsx @@ -133,11 +133,7 @@ const IntroductionSection = (props) => { const { act, data } = useBackend(); const { true_name, hive_name, objectives, can_change_objective } = data; return ( -
    4} - > +
    You are {true_name} from the diff --git a/tgui/packages/tgui/interfaces/Secrets.jsx b/tgui/packages/tgui/interfaces/Secrets.jsx index 95d0189a1cca4..ebd0b998d2c28 100644 --- a/tgui/packages/tgui/interfaces/Secrets.jsx +++ b/tgui/packages/tgui/interfaces/Secrets.jsx @@ -482,8 +482,8 @@ const FunForYouTab = (props) => { color="red" icon="user-secret" fluid - content="Everyone is the traitor" - onClick={() => act('traitor_all')} + content="Everyone is the antag" + onClick={() => act('antag_all')} /> From fb2bcbef3364f48d583cb46aff6a2d4ae027e22f Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 17:52:30 +1300 Subject: [PATCH 033/508] Automatic changelog for PR #81292 [ci skip] --- html/changelogs/AutoChangeLog-pr-81292.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81292.yml diff --git a/html/changelogs/AutoChangeLog-pr-81292.yml b/html/changelogs/AutoChangeLog-pr-81292.yml new file mode 100644 index 0000000000000..88999998073f5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81292.yml @@ -0,0 +1,4 @@ +author: "Holoo-1" +delete-after: True +changes: + - admin: "remade everyone is traitor into everyone is antag in secrets panel" \ No newline at end of file From 55ba5842c269131fedb34a5fcdfb115b991b3087 Mon Sep 17 00:00:00 2001 From: lessthanthree <83487515+lessthnthree@users.noreply.github.com> Date: Fri, 9 Feb 2024 05:40:43 +0000 Subject: [PATCH 034/508] Health analyzer displays blood alcohol content (#81306) ## About The Pull Request Health analyzer includes a blood alcohol content if the scanned target is inebriated. ## Why It's Good For The Game It's useful to know, sometimes their blood is filtered, stomach pumped, but this effect will still be in action. ## Changelog :cl: LT3 qol: Health analyzer will now display blood alcohol content /:cl: --------- Co-authored-by: san7890 --- code/game/machinery/medical_kiosk.dm | 2 ++ .../items/devices/scanners/health_analyzer.dm | 14 +++++++++++--- code/modules/mob/living/blood.dm | 11 +++++++++++ tgui/packages/tgui/interfaces/MedicalKiosk.jsx | 14 ++++++++++++++ 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/code/game/machinery/medical_kiosk.dm b/code/game/machinery/medical_kiosk.dm index d88c9c8209996..eb75dfbfa3aac 100644 --- a/code/game/machinery/medical_kiosk.dm +++ b/code/game/machinery/medical_kiosk.dm @@ -239,6 +239,7 @@ var/blood_percent = round((patient.blood_volume / BLOOD_VOLUME_NORMAL)*100) var/blood_type = patient.dna.blood_type var/blood_warning = " " + var/blood_alcohol = patient.get_blood_alcohol_content() for(var/thing in patient.diseases) //Disease Information var/datum/disease/D = thing @@ -354,6 +355,7 @@ data["bleed_status"] = bleed_status data["blood_levels"] = blood_percent - (chaos_modifier * (rand(1,35))) data["blood_status"] = blood_status + data["blood_alcohol"] = blood_alcohol data["chemical_list"] = chemical_list data["overdose_list"] = overdose_list data["addict_list"] = addict_list diff --git a/code/game/objects/items/devices/scanners/health_analyzer.dm b/code/game/objects/items/devices/scanners/health_analyzer.dm index 0db62c5436994..a27faee2094bc 100644 --- a/code/game/objects/items/devices/scanners/health_analyzer.dm +++ b/code/game/objects/items/devices/scanners/health_analyzer.dm @@ -372,11 +372,19 @@ var/datum/reagent/R = GLOB.chemical_reagents_list[blood_id] blood_type = R ? R.name : blood_id if(carbontarget.blood_volume <= BLOOD_VOLUME_SAFE && carbontarget.blood_volume > BLOOD_VOLUME_OKAY) - render_list += "Blood level: LOW [blood_percent] %, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" + render_list += "Blood level: LOW [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" else if(carbontarget.blood_volume <= BLOOD_VOLUME_OKAY) - render_list += "Blood level: CRITICAL [blood_percent] %, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" + render_list += "Blood level: CRITICAL [blood_percent]%, [carbontarget.blood_volume] cl, [span_info("type: [blood_type]")]\n" else - render_list += "Blood level: [blood_percent] %, [carbontarget.blood_volume] cl, type: [blood_type]\n" + render_list += "Blood level: [blood_percent]%, [carbontarget.blood_volume] cl, type: [blood_type]\n" + + // Blood Alcohol Content + var/blood_alcohol_content = target.get_blood_alcohol_content() + if(blood_alcohol_content > 0) + if(blood_alcohol_content >= 0.24) + render_list += "Blood alcohol content: CRITICAL [blood_alcohol_content]%\n" + else + render_list += "Blood alcohol content: [blood_alcohol_content]%\n" // Cybernetics if(iscarbon(target)) diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm index 0818532cc4df7..4caa7dd1966e2 100644 --- a/code/modules/mob/living/blood.dm +++ b/code/modules/mob/living/blood.dm @@ -1,4 +1,6 @@ #define BLOOD_DRIP_RATE_MOD 90 //Greater number means creating blood drips more often while bleeding +// Conversion between internal drunk power and common blood alcohol content +#define DRUNK_POWER_TO_BLOOD_ALCOHOL 0.003 /**************************************************** BLOOD SYSTEM @@ -376,4 +378,13 @@ if(!B) B = new(T) +/mob/living/proc/get_blood_alcohol_content() + var/blood_alcohol_content = 0 + var/datum/status_effect/inebriated/inebriation = has_status_effect(/datum/status_effect/inebriated) + if(!isnull(inebriation)) + blood_alcohol_content = round(inebriation.drunk_value * DRUNK_POWER_TO_BLOOD_ALCOHOL, 0.01) + + return blood_alcohol_content + #undef BLOOD_DRIP_RATE_MOD +#undef DRUNK_POWER_TO_BLOOD_ALCOHOL diff --git a/tgui/packages/tgui/interfaces/MedicalKiosk.jsx b/tgui/packages/tgui/interfaces/MedicalKiosk.jsx index 5d8b081e0ef48..98d71c0433148 100644 --- a/tgui/packages/tgui/interfaces/MedicalKiosk.jsx +++ b/tgui/packages/tgui/interfaces/MedicalKiosk.jsx @@ -248,6 +248,7 @@ const MedicalKioskScanResults4 = (props) => { overdose_list = [], addict_list = [], hallucinating_status, + blood_alcohol, } = data; return (
    @@ -281,6 +282,19 @@ const MedicalKioskScanResults4 = (props) => { {hallucinating_status} + + + + +
    ); From 2b02f7f18ca196ec4f35877d7011526b9b91a3ac Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:41:04 +1300 Subject: [PATCH 035/508] Automatic changelog for PR #81306 [ci skip] --- html/changelogs/AutoChangeLog-pr-81306.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81306.yml diff --git a/html/changelogs/AutoChangeLog-pr-81306.yml b/html/changelogs/AutoChangeLog-pr-81306.yml new file mode 100644 index 0000000000000..98c9d89afa146 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81306.yml @@ -0,0 +1,4 @@ +author: "LT3" +delete-after: True +changes: + - qol: "Health analyzer will now display blood alcohol content" \ No newline at end of file From f6f87205948abc7b6998bd9da15ee26c5d19c087 Mon Sep 17 00:00:00 2001 From: larentoun <31931237+larentoun@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:41:50 +0900 Subject: [PATCH 036/508] [Fix] Visually closing fakewalls when in fact they shouldn't be closed (#81353) ## About The Pull Request Moves update_appearance() proc further after checking if it can be closed. It was possible to stand inside an open falsewall, try to close it only for it to "close" visually and don't change any properties. ## Why It's Good For The Game Less bugs is good. No more fake-closed falsewalls. ## Changelog :cl: fix: Now falsewalls visually don't close when they shouldn't. /:cl: --- code/game/objects/structures/false_walls.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/game/objects/structures/false_walls.dm b/code/game/objects/structures/false_walls.dm index 9f4f4fdd73f4f..eaf830b7db78e 100644 --- a/code/game/objects/structures/false_walls.dm +++ b/code/game/objects/structures/false_walls.dm @@ -43,12 +43,12 @@ return opening = TRUE - update_appearance() if(!density) var/srcturf = get_turf(src) for(var/mob/living/obstacle in srcturf) //Stop people from using this as a shield opening = FALSE return + update_appearance() addtimer(CALLBACK(src, TYPE_PROC_REF(/obj/structure/falsewall, toggle_open)), 5) /obj/structure/falsewall/proc/toggle_open() From 50ff7c7c7fb91cb7c6b8763b45f2f7b999454e2c Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:42:07 +1300 Subject: [PATCH 037/508] Automatic changelog for PR #81353 [ci skip] --- html/changelogs/AutoChangeLog-pr-81353.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81353.yml diff --git a/html/changelogs/AutoChangeLog-pr-81353.yml b/html/changelogs/AutoChangeLog-pr-81353.yml new file mode 100644 index 0000000000000..a33ebc0cbae1e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81353.yml @@ -0,0 +1,4 @@ +author: "larentoun" +delete-after: True +changes: + - bugfix: "Now falsewalls visually don't close when they shouldn't." \ No newline at end of file From 87c270f3ca0d58512ff44fc45c7272dc4159954a Mon Sep 17 00:00:00 2001 From: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com> Date: Thu, 8 Feb 2024 21:42:29 -0800 Subject: [PATCH 038/508] Removes Halloween Screen Tint (#81355) ## What Reverts tgstation/tgstation#79062 ## Why It was a good idea (I swear) but as an everpresent effect it is far too oppressive and opinionated. It causes issues for people with less then perfect vision, fucked monitor setups (many people it seems) or those who play in the day (can you tell when I do most of my development?) I plan on reusing the concept of bracketing to implement conditional nightvision that makes bright things blow out your screen and such, but that's not happening for a while. I still think it was pretty but it's not worth it ## Changelog :cl: del: Removes halloween screen tint, we're taking him to retire by the seaside (he was alone and unloved) /:cl: --- code/_onclick/hud/rendering/render_plate.dm | 4 ---- 1 file changed, 4 deletions(-) diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm index f3d838f8594bf..e42c1f94f68a2 100644 --- a/code/_onclick/hud/rendering/render_plate.dm +++ b/code/_onclick/hud/rendering/render_plate.dm @@ -80,10 +80,6 @@ /atom/movable/screen/plane_master/rendering_plate/game_plate/Initialize(mapload, datum/hud/hud_owner) . = ..() add_filter("displacer", 1, displacement_map_filter(render_source = OFFSET_RENDER_TARGET(GRAVITY_PULSE_RENDER_TARGET, offset), size = 10)) - if(check_holidays(HALLOWEEN)) - // Makes things a tad greyscale (leaning purple) and drops low colors for vibes - // We're basically using alpha as better constant here btw - add_filter("spook_color", 2, color_matrix_filter(list(0.75,0.13,0.13,0, 0.13,0.7,0.13,0, 0.13,0.13,0.75,0, -0.06,-0.09,-0.08,1, 0,0,0,0))) /atom/movable/screen/plane_master/rendering_plate/game_plate/show_to(mob/mymob) . = ..() From 2d0667d8340bf1bb4208d8944c46dc79b8ac2ed8 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:48:59 +1300 Subject: [PATCH 039/508] Automatic changelog for PR #81355 [ci skip] --- html/changelogs/AutoChangeLog-pr-81355.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81355.yml diff --git a/html/changelogs/AutoChangeLog-pr-81355.yml b/html/changelogs/AutoChangeLog-pr-81355.yml new file mode 100644 index 0000000000000..970c4c6432176 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81355.yml @@ -0,0 +1,4 @@ +author: "LemonInTheDark" +delete-after: True +changes: + - rscdel: "Removes halloween screen tint, we're taking him to retire by the seaside (he was alone and unloved)" \ No newline at end of file From 44ce6d923d13ef5d19ad3bfc8e58fdf5eab7921d Mon Sep 17 00:00:00 2001 From: A miscellaneous Fern <80640114+FernandoJ8@users.noreply.github.com> Date: Fri, 9 Feb 2024 14:56:57 +0100 Subject: [PATCH 040/508] Adds an IS_CHANGELING helper (#81228) ## About The Pull Request What it says on the tin. I also replaced checks with it everywhere I found appropriate, hopefully without accidentally breaking anything. ## Why It's Good For The Game We have a helper for most other antags, and it makes code a lot cleaner and easier to read. I'm surprised we didn't have this one already. ## Changelog :cl: code: added an IS_CHANGELING() helper and used it where applicable /:cl: --- code/__DEFINES/antagonists.dm | 3 +++ code/datums/saymode.dm | 2 +- code/game/gamemodes/objective.dm | 2 +- .../abductor/equipment/gear/abductor_items.dm | 2 +- .../changeling/changeling_power.dm | 6 +++--- .../antagonists/changeling/powers/absorb.dm | 8 ++++---- .../changeling/powers/fakedeath.dm | 2 +- .../changeling/powers/lesserform.dm | 2 +- .../changeling/powers/mimic_voice.dm | 4 ++-- .../changeling/powers/mutations.dm | 6 +++--- .../changeling/powers/pheromone_receptors.dm | 6 +++--- .../antagonists/changeling/powers/shriek.dm | 2 +- .../changeling/powers/tiny_prick.dm | 18 ++++++++--------- .../changeling/powers/transform.dm | 20 +++++++++---------- .../changeling/powers/void_adaption.dm | 4 ++-- .../living/basic/guardian/guardian_creator.dm | 2 +- code/modules/mob/living/brain/brain_item.dm | 2 +- code/modules/mob/living/carbon/carbon.dm | 2 +- .../reagents/drinks/alcohol_reagents.dm | 2 +- .../chemistry/reagents/other_reagents.dm | 2 +- code/modules/unit_tests/changeling.dm | 2 +- 21 files changed, 51 insertions(+), 48 deletions(-) diff --git a/code/__DEFINES/antagonists.dm b/code/__DEFINES/antagonists.dm index e903d0539cfc8..10b2f8dc63515 100644 --- a/code/__DEFINES/antagonists.dm +++ b/code/__DEFINES/antagonists.dm @@ -213,6 +213,9 @@ GLOBAL_LIST_INIT(ai_employers, list( /// Checks if the given mob is a blood cultist #define IS_CULTIST(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/cult)) +/// Checks if the given mob is a changeling +#define IS_CHANGELING(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/changeling)) + /// Checks if the given mob is a nuclear operative #define IS_NUKE_OP(mob) (mob?.mind?.has_antag_datum(/datum/antagonist/nukeop)) diff --git a/code/datums/saymode.dm b/code/datums/saymode.dm index 363f484a41583..3598bd764cac7 100644 --- a/code/datums/saymode.dm +++ b/code/datums/saymode.dm @@ -20,7 +20,7 @@ if(user.mind.has_antag_datum(/datum/antagonist/fallen_changeling)) to_chat(user, span_changeling("We're cut off from the hivemind! We've lost everything! EVERYTHING!!")) return FALSE - var/datum/antagonist/changeling/ling_sender = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/ling_sender = IS_CHANGELING(user) if(!ling_sender) return FALSE if(HAS_TRAIT(user, TRAIT_CHANGELING_HIVEMIND_MUTE)) diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm index 495c4374954c0..a386dec8db2f5 100644 --- a/code/game/gamemodes/objective.dm +++ b/code/game/gamemodes/objective.dm @@ -781,7 +781,7 @@ GLOBAL_LIST_EMPTY(possible_items) n_p ++ else if (SSticker.IsRoundInProgress()) for(var/mob/living/carbon/human/P in GLOB.player_list) - if(!(P.mind.has_antag_datum(/datum/antagonist/changeling)) && !(P.mind in owners)) + if(!(IS_CHANGELING(P)) && !(P.mind in owners)) n_p ++ target_amount = min(target_amount, n_p) diff --git a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm index 66004140e5929..14d8d73788655 100644 --- a/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm +++ b/code/modules/antagonists/abductor/equipment/gear/abductor_items.dm @@ -446,7 +446,7 @@ Congratulations! You are now trained for invasive xenobiology research!"} if(ishuman(victim)) var/mob/living/carbon/human/human_victim = victim species = span_notice("[human_victim.dna.species.name]") - if(human_victim.mind && human_victim.mind.has_antag_datum(/datum/antagonist/changeling)) + if(IS_CHANGELING(human_victim)) species = span_warning("Changeling lifeform") var/obj/item/organ/internal/heart/gland/temp = locate() in human_victim.organs if(temp) diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm index efbf906809be0..23b4f9548c424 100644 --- a/code/modules/antagonists/changeling/changeling_power.dm +++ b/code/modules/antagonists/changeling/changeling_power.dm @@ -44,7 +44,7 @@ the same goes for Remove(). if you override Remove(), call parent or else your p /datum/action/changeling/Trigger(trigger_flags) var/mob/user = owner - if(!user || !user.mind || !user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(!user || !IS_CHANGELING(user)) return try_to_sting(user) @@ -61,7 +61,7 @@ the same goes for Remove(). if you override Remove(), call parent or else your p /datum/action/changeling/proc/try_to_sting(mob/living/user, mob/living/target) if(!can_sting(user, target)) return FALSE - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(sting_action(user, target)) sting_feedback(user, target) changeling.adjust_chemicals(-chemical_cost) @@ -81,7 +81,7 @@ the same goes for Remove(). if you override Remove(), call parent or else your p /datum/action/changeling/proc/can_sting(mob/living/user, mob/living/target) if(!can_be_used_by(user)) return FALSE - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(changeling.chem_charges < chemical_cost) user.balloon_alert(user, "needs [chemical_cost] chemicals!") return FALSE diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm index 1fdb50a7babf7..5bd0f390cb8bb 100644 --- a/code/modules/antagonists/changeling/powers/absorb.dm +++ b/code/modules/antagonists/changeling/powers/absorb.dm @@ -24,13 +24,13 @@ return var/mob/living/carbon/target = owner.pulling - var/datum/antagonist/changeling/changeling = owner.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(owner) return changeling.can_absorb_dna(target) /datum/action/changeling/absorb_dna/sting_action(mob/owner) SHOULD_CALL_PARENT(FALSE) // the only reason to call parent is for proper blackbox logging, and we do that ourselves in a snowflake way - var/datum/antagonist/changeling/changeling = owner.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(owner) var/mob/living/carbon/human/target = owner.pulling is_absorbing = TRUE @@ -68,7 +68,7 @@ /datum/action/changeling/absorb_dna/proc/absorb_memories(mob/living/carbon/human/target) var/datum/mind/suckedbrain = target.mind - var/datum/antagonist/changeling/changeling = owner.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(owner) for(var/memory_type in suckedbrain.memories) var/datum/memory/stolen_memory = suckedbrain.memories[memory_type] @@ -109,7 +109,7 @@ to_chat(owner, span_boldnotice("We have no more knowledge of [target]'s speech patterns.")) - var/datum/antagonist/changeling/target_ling = target.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/target_ling = IS_CHANGELING(target) if(target_ling)//If the target was a changeling, suck out their extra juice and objective points! to_chat(owner, span_boldnotice("[target] was one of us. We have absorbed their power.")) diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm index cd748e8315953..d39fa9eb66d76 100644 --- a/code/modules/antagonists/changeling/powers/fakedeath.dm +++ b/code/modules/antagonists/changeling/powers/fakedeath.dm @@ -116,7 +116,7 @@ if(QDELETED(src) || QDELETED(user)) return - var/datum/antagonist/changeling/ling = user.mind?.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/ling = IS_CHANGELING(user) if(QDELETED(ling) || !(src in ling.innate_powers + ling.purchased_powers)) // checking both innate and purchased for full coverage return if(!HAS_TRAIT_FROM(user, TRAIT_DEATHCOMA, CHANGELING_TRAIT)) diff --git a/code/modules/antagonists/changeling/powers/lesserform.dm b/code/modules/antagonists/changeling/powers/lesserform.dm index 87bd7c7c8b669..55238ce762daa 100644 --- a/code/modules/antagonists/changeling/powers/lesserform.dm +++ b/code/modules/antagonists/changeling/powers/lesserform.dm @@ -30,7 +30,7 @@ if(user.movement_type & VENTCRAWLING) user.balloon_alert(user, "can't transform in pipes!") return FALSE - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) var/datum/changeling_profile/chosen_form = select_form(changeling, user) if(!chosen_form) return FALSE diff --git a/code/modules/antagonists/changeling/powers/mimic_voice.dm b/code/modules/antagonists/changeling/powers/mimic_voice.dm index 3a9314a953af5..9caf0abb450f5 100644 --- a/code/modules/antagonists/changeling/powers/mimic_voice.dm +++ b/code/modules/antagonists/changeling/powers/mimic_voice.dm @@ -9,7 +9,7 @@ // Fake Voice /datum/action/changeling/mimicvoice/sting_action(mob/user) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(changeling.mimicing) changeling.mimicing = "" changeling.chem_recharge_slowdown -= 0.25 @@ -27,7 +27,7 @@ return TRUE /datum/action/changeling/mimicvoice/Remove(mob/user) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(changeling?.mimicing) changeling.chem_recharge_slowdown = max(0, changeling.chem_recharge_slowdown - 0.25) changeling.mimicing = "" diff --git a/code/modules/antagonists/changeling/powers/mutations.dm b/code/modules/antagonists/changeling/powers/mutations.dm index 9c5b6a10ec9d5..7ad2b14448c2e 100644 --- a/code/modules/antagonists/changeling/powers/mutations.dm +++ b/code/modules/antagonists/changeling/powers/mutations.dm @@ -121,7 +121,7 @@ //checks if we already have an organic suit and casts it off. /datum/action/changeling/suit/proc/check_suit(mob/user) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(!ishuman(user) || !changeling) return 1 var/mob/living/carbon/human/H = user @@ -154,7 +154,7 @@ user.equip_to_slot_if_possible(new suit_type(user), ITEM_SLOT_OCLOTHING, 1, 1, 1) user.equip_to_slot_if_possible(new helmet_type(user), ITEM_SLOT_HEAD, 1, 1, 1) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) changeling.chem_recharge_slowdown += recharge_slowdown return TRUE @@ -477,7 +477,7 @@ weapon_name_simple = "shield" /datum/action/changeling/weapon/shield/sting_action(mob/user) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) //So we can read the absorbed_count. + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) //So we can read the absorbed_count. if(!changeling) return diff --git a/code/modules/antagonists/changeling/powers/pheromone_receptors.dm b/code/modules/antagonists/changeling/powers/pheromone_receptors.dm index 8ed9c27599b33..18fda4bf4ff02 100644 --- a/code/modules/antagonists/changeling/powers/pheromone_receptors.dm +++ b/code/modules/antagonists/changeling/powers/pheromone_receptors.dm @@ -15,14 +15,14 @@ /datum/action/changeling/pheromone_receptors/Remove(mob/living/carbon/user) if(receptors_active) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) changeling.chem_recharge_slowdown -= 0.25 user.remove_status_effect(/datum/status_effect/agent_pinpointer/changeling) ..() /datum/action/changeling/pheromone_receptors/sting_action(mob/living/carbon/user) ..() - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(!receptors_active) to_chat(user, span_warning("We search for the scent of any nearby changelings.")) changeling.chem_recharge_slowdown += 0.25 @@ -49,7 +49,7 @@ for(var/mob/living/carbon/C in GLOB.alive_mob_list) if(C != owner && C.mind) - var/datum/antagonist/changeling/antag_datum = C.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/antag_datum = IS_CHANGELING(C) if(istype(antag_datum)) var/their_loc = get_turf(C) var/distance = get_dist_euclidian(my_loc, their_loc) diff --git a/code/modules/antagonists/changeling/powers/shriek.dm b/code/modules/antagonists/changeling/powers/shriek.dm index badd18f19f0a1..aa204d89a166e 100644 --- a/code/modules/antagonists/changeling/powers/shriek.dm +++ b/code/modules/antagonists/changeling/powers/shriek.dm @@ -16,7 +16,7 @@ for(var/mob/living/M in get_hearers_in_view(4, user)) if(iscarbon(M)) var/mob/living/carbon/C = M - if(!C.mind || !C.mind.has_antag_datum(/datum/antagonist/changeling)) + if(!IS_CHANGELING(C)) var/obj/item/organ/internal/ears/ears = C.get_organ_slot(ORGAN_SLOT_EARS) if(ears) ears.adjustEarDamage(0, 30) diff --git a/code/modules/antagonists/changeling/powers/tiny_prick.dm b/code/modules/antagonists/changeling/powers/tiny_prick.dm index f58d27c7501a8..d6767f5c1b1cb 100644 --- a/code/modules/antagonists/changeling/powers/tiny_prick.dm +++ b/code/modules/antagonists/changeling/powers/tiny_prick.dm @@ -6,7 +6,7 @@ var/mob/user = owner if(!user || !user.mind) return - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(!changeling) return if(!changeling.chosen_sting) @@ -17,7 +17,7 @@ /datum/action/changeling/sting/proc/set_sting(mob/user) to_chat(user, span_notice("We prepare our sting. Alt+click or click the middle mouse button on a target to sting them.")) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) changeling.chosen_sting = src changeling.lingstingdisplay.icon_state = button_icon_state @@ -25,7 +25,7 @@ /datum/action/changeling/sting/proc/unset_sting(mob/user) to_chat(user, span_warning("We retract our sting, we can't sting anyone for now.")) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) changeling.chosen_sting = null changeling.lingstingdisplay.icon_state = null @@ -40,7 +40,7 @@ /datum/action/changeling/sting/can_sting(mob/user, mob/target) if(!..()) return - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(!changeling.chosen_sting) to_chat(user, "We haven't prepared our sting yet!") if(!iscarbon(target)) @@ -49,7 +49,7 @@ return if(!length(get_path_to(user, target, max_distance = changeling.sting_range, simulated_only = FALSE))) return // no path within the sting's range is found. what a weird place to use the pathfinding system - if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling)) + if(IS_CHANGELING(target)) sting_feedback(user, target) changeling.chem_charges -= chemical_cost return 1 @@ -58,7 +58,7 @@ if(!target) return to_chat(user, span_notice("We stealthily sting [target.name].")) - if(target.mind && target.mind.has_antag_datum(/datum/antagonist/changeling)) + if(IS_CHANGELING(target)) to_chat(target, span_warning("You feel a tiny prick.")) return 1 @@ -93,7 +93,7 @@ /datum/action/changeling/sting/transformation/set_sting(mob/user) selected_dna = null - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) var/datum/changeling_profile/new_selected_dna = changeling.select_dna() if(QDELETED(src) || QDELETED(changeling) || QDELETED(user)) return @@ -197,13 +197,13 @@ /datum/action/changeling/sting/extract_dna/can_sting(mob/user, mob/target) if(..()) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) return changeling.can_absorb_dna(target) /datum/action/changeling/sting/extract_dna/sting_action(mob/user, mob/living/carbon/human/target) ..() log_combat(user, target, "stung", "extraction sting") - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) if(!changeling.has_profile_with_dna(target.dna)) changeling.add_new_profile(target) return TRUE diff --git a/code/modules/antagonists/changeling/powers/transform.dm b/code/modules/antagonists/changeling/powers/transform.dm index 804dd2bffaca0..e4a2b36ca684f 100644 --- a/code/modules/antagonists/changeling/powers/transform.dm +++ b/code/modules/antagonists/changeling/powers/transform.dm @@ -13,7 +13,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/glasses/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -25,7 +25,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/under/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -38,7 +38,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/suit/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -51,7 +51,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/head/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -63,7 +63,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/shoes/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -75,7 +75,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/gloves/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -87,7 +87,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/clothing/mask/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -100,7 +100,7 @@ //ATTACK HAND IGNORING PARENT RETURN VALUE /obj/item/changeling/attack_hand(mob/user, list/modifiers) - if(loc == user && user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling)) + if(loc == user && IS_CHANGELING(user)) to_chat(user, span_notice("You reabsorb [src] into your body.")) qdel(src) return @@ -134,7 +134,7 @@ //Change our DNA to that of somebody we've absorbed. /datum/action/changeling/transform/sting_action(mob/living/carbon/human/user) - var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(user) var/datum/changeling_profile/chosen_prof = changeling.select_dna() if(!chosen_prof) @@ -185,7 +185,7 @@ /datum/antagonist/changeling/proc/check_menu(mob/living/carbon/user) if(!istype(user)) return FALSE - var/datum/antagonist/changeling/changeling_datum = user.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling_datum = IS_CHANGELING(user) if(!changeling_datum) return FALSE return TRUE diff --git a/code/modules/antagonists/changeling/powers/void_adaption.dm b/code/modules/antagonists/changeling/powers/void_adaption.dm index 76c0eeffc972d..934ae2f5770b3 100644 --- a/code/modules/antagonists/changeling/powers/void_adaption.dm +++ b/code/modules/antagonists/changeling/powers/void_adaption.dm @@ -55,14 +55,14 @@ if (!should_be_active) on_removed_adaption(void_adapted, "Our cells relax in safer air.") return - var/datum/antagonist/changeling/changeling_data = void_adapted.mind?.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling_data = IS_CHANGELING(void_adapted) to_chat(void_adapted, span_changeling("Our cells harden themselves against the [pick(active_reasons)].")) changeling_data?.chem_recharge_slowdown -= recharge_slowdown currently_active = TRUE /// Called when we stop being adapted /datum/action/changeling/void_adaption/proc/on_removed_adaption(mob/living/former, message) - var/datum/antagonist/changeling/changeling_data = former.mind?.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling_data = IS_CHANGELING(former) to_chat(former, span_changeling(message)) changeling_data?.chem_recharge_slowdown += recharge_slowdown currently_active = FALSE diff --git a/code/modules/mob/living/basic/guardian/guardian_creator.dm b/code/modules/mob/living/basic/guardian/guardian_creator.dm index d01383427df85..3f1f092752217 100644 --- a/code/modules/mob/living/basic/guardian/guardian_creator.dm +++ b/code/modules/mob/living/basic/guardian/guardian_creator.dm @@ -66,7 +66,7 @@ GLOBAL_LIST_INIT(guardian_radial_images, setup_guardian_radial()) if(length(guardians) && !allow_multiple) balloon_alert(user, "already have one!") return - if(user.mind && user.mind.has_antag_datum(/datum/antagonist/changeling) && !allow_changeling) + if(IS_CHANGELING(user) && !allow_changeling) to_chat(user, ling_failure) return if(used) diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm index a613e2ad83932..117b5f03835d2 100644 --- a/code/modules/mob/living/brain/brain_item.dm +++ b/code/modules/mob/living/brain/brain_item.dm @@ -61,7 +61,7 @@ name = initial(name) // Special check for if you're trapped in a body you can't control because it's owned by a ling. - if(brain_owner?.mind?.has_antag_datum(/datum/antagonist/changeling) && !(movement_flags & NO_ID_TRANSFER)) + if(IS_CHANGELING(brain_owner) && !(movement_flags & NO_ID_TRANSFER)) if(brainmob && !(brain_owner.stat == DEAD || (HAS_TRAIT(brain_owner, TRAIT_DEATHCOMA)))) to_chat(brainmob, span_danger("You can't feel your body! You're still just a brain!")) forceMove(brain_owner) diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 98cb200ac0e18..657b22ee31b7e 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -922,7 +922,7 @@ return ..() /mob/living/carbon/can_be_revived() - if(!get_organ_by_type(/obj/item/organ/internal/brain) && (!mind || !mind.has_antag_datum(/datum/antagonist/changeling)) || HAS_TRAIT(src, TRAIT_HUSK)) + if(!get_organ_by_type(/obj/item/organ/internal/brain) && (!IS_CHANGELING(src)) || HAS_TRAIT(src, TRAIT_HUSK)) return FALSE return ..() diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 93954ab944fc7..1381ed81f12da 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -1195,7 +1195,7 @@ /datum/reagent/consumable/ethanol/changelingsting/on_mob_life(mob/living/carbon/target, seconds_per_tick, times_fired) . = ..() - var/datum/antagonist/changeling/changeling = target.mind?.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(target) changeling?.adjust_chemicals(metabolization_rate * REM * seconds_per_tick) /datum/reagent/consumable/ethanol/irishcarbomb diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 148894386f893..238f0f5252296 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2535,7 +2535,7 @@ /datum/reagent/bz_metabolites/on_mob_life(mob/living/carbon/target, seconds_per_tick, times_fired) . = ..() if(target.mind) - var/datum/antagonist/changeling/changeling = target.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/changeling = IS_CHANGELING(target) if(changeling) changeling.adjust_chemicals(-2 * REM * seconds_per_tick) diff --git a/code/modules/unit_tests/changeling.dm b/code/modules/unit_tests/changeling.dm index c01105ce9c44c..1393eee838988 100644 --- a/code/modules/unit_tests/changeling.dm +++ b/code/modules/unit_tests/changeling.dm @@ -8,7 +8,7 @@ /datum/unit_test/transformation_sting/Run() var/mob/living/carbon/human/ling = setup_ling() var/mob/living/carbon/human/victim = setup_victim() - var/datum/antagonist/changeling/ling_datum = ling.mind.has_antag_datum(/datum/antagonist/changeling) + var/datum/antagonist/changeling/ling_datum = IS_CHANGELING(ling) // Get the ability we're testing ling_datum.purchase_power(/datum/action/changeling/sting/transformation) From f5fa61b57f1facd39b8e1e60e1be2818407000b4 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 02:57:15 +1300 Subject: [PATCH 041/508] Automatic changelog for PR #81228 [ci skip] --- html/changelogs/AutoChangeLog-pr-81228.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81228.yml diff --git a/html/changelogs/AutoChangeLog-pr-81228.yml b/html/changelogs/AutoChangeLog-pr-81228.yml new file mode 100644 index 0000000000000..20b504aa51a86 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81228.yml @@ -0,0 +1,4 @@ +author: "FernandoJ8" +delete-after: True +changes: + - code_imp: "added an IS_CHANGELING() helper and used it where applicable" \ No newline at end of file From 05271b6fa31f95783db3848583997a536b83a239 Mon Sep 17 00:00:00 2001 From: Ben10Omintrix <138636438+Ben10Omintrix@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:54:55 +0200 Subject: [PATCH 042/508] settlers can rename their pets (#81190) ## About The Pull Request settlers can now rename their tamed pets ## Why It's Good For The Game adds another fun and harmless interaction between settlers and their pets ## Changelog :cl: add: settlers can rename their pets /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- code/datums/components/tameable.dm | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/code/datums/components/tameable.dm b/code/datums/components/tameable.dm index 3da6d61614997..67325b489d367 100644 --- a/code/datums/components/tameable.dm +++ b/code/datums/components/tameable.dm @@ -70,18 +70,28 @@ return living_parent.faction.Find(REF(potential_friend)) ///Ran once taming succeeds -/datum/component/tameable/proc/on_tame(datum/source, mob/living/tamer, atom/food, inform_tamer = FALSE) +/datum/component/tameable/proc/on_tame(atom/source, mob/living/tamer, atom/food, inform_tamer = FALSE) SIGNAL_HANDLER after_tame?.Invoke(tamer, food)//Run custom behavior if needed if(isliving(parent) && isliving(tamer)) - var/mob/living/tamed = parent - INVOKE_ASYNC(tamed, TYPE_PROC_REF(/mob/living, befriend), tamer) + INVOKE_ASYNC(source, TYPE_PROC_REF(/mob/living, befriend), tamer) if(inform_tamer) - var/atom/atom_parent = source - atom_parent.balloon_alert(tamer, "tamed") + source.balloon_alert(tamer, "tamed") + if(HAS_TRAIT(tamer, TRAIT_SETTLER)) + INVOKE_ASYNC(src, PROC_REF(rename_pet), source, tamer) if(unique) qdel(src) else current_tame_chance = tame_chance + +/datum/component/tameable/proc/rename_pet(mob/living/animal, mob/living/tamer) + var/chosen_name = sanitize_name(tgui_input_text(tamer, "Choose your pet's name!", "Name pet", animal.name, MAX_NAME_LEN), allow_numbers = TRUE) + if(QDELETED(animal) || chosen_name == animal.name) + return + if(!chosen_name) + to_chat(tamer, span_warning("Please enter a valid name.")) + rename_pet(animal, tamer) + return + animal.fully_replace_character_name(animal.name, chosen_name) From 25f302d6ac5fc9e3e5a8f30f9f3c21d37f6b46a3 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 03:55:14 +1300 Subject: [PATCH 043/508] Automatic changelog for PR #81190 [ci skip] --- html/changelogs/AutoChangeLog-pr-81190.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81190.yml diff --git a/html/changelogs/AutoChangeLog-pr-81190.yml b/html/changelogs/AutoChangeLog-pr-81190.yml new file mode 100644 index 0000000000000..b4adc2b12a12e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81190.yml @@ -0,0 +1,4 @@ +author: "Ben10Omintrix" +delete-after: True +changes: + - rscadd: "settlers can rename their pets" \ No newline at end of file From cba381c8034476aa0066ab97dc461ebc105b2965 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Fri, 9 Feb 2024 11:22:19 -0500 Subject: [PATCH 044/508] Reviving stasis now puts you in stasis (#81339) ## About The Pull Request In the title, Changeling's "Revival Stasis" ability with the description "We fall into a stasis", now puts you in stasis ## Why It's Good For The Game I just found out that this doesn't actually put you in stasis and it's kinda fucked up, it's in the name it's in the description, why is it not the actual case? It would be nice if Changelings can use this ability to actually fake death from critical condition without risking a death gasp when they die for real just because you got oxygen damage while in your "stasis", thus going against the defibrillator explosion ability. ## Changelog :cl: add: Changeling's reviving stasis ability now puts you in stasis. /:cl: --- code/modules/antagonists/changeling/powers/fakedeath.dm | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm index d39fa9eb66d76..921dde3549263 100644 --- a/code/modules/antagonists/changeling/powers/fakedeath.dm +++ b/code/modules/antagonists/changeling/powers/fakedeath.dm @@ -40,6 +40,7 @@ return changeling.fakedeath(CHANGELING_TRAIT) + ADD_TRAIT(changeling, TRAIT_STASIS, CHANGELING_TRAIT) addtimer(CALLBACK(src, PROC_REF(ready_to_regenerate), changeling), fakedeath_duration * duration_modifier, TIMER_UNIQUE) // Basically, these let the ling exit stasis without giving away their ling-y-ness if revived through other means RegisterSignal(changeling, SIGNAL_REMOVETRAIT(TRAIT_DEATHCOMA), PROC_REF(fakedeath_reset)) @@ -54,6 +55,7 @@ revive_ready = FALSE build_all_button_icons(UPDATE_BUTTON_NAME|UPDATE_BUTTON_ICON) + REMOVE_TRAIT(changeling, TRAIT_STASIS, CHANGELING_TRAIT) UnregisterSignal(changeling, SIGNAL_REMOVETRAIT(TRAIT_DEATHCOMA)) UnregisterSignal(changeling, COMSIG_MOB_STATCHANGE) From a1145922a339100f91486a761b13a16d36cfc62d Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 05:22:46 +1300 Subject: [PATCH 045/508] Automatic changelog for PR #81339 [ci skip] --- html/changelogs/AutoChangeLog-pr-81339.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81339.yml diff --git a/html/changelogs/AutoChangeLog-pr-81339.yml b/html/changelogs/AutoChangeLog-pr-81339.yml new file mode 100644 index 0000000000000..70e0496102887 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81339.yml @@ -0,0 +1,4 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - rscadd: "Changeling's reviving stasis ability now puts you in stasis." \ No newline at end of file From f73ed9c34c4acc764a1187c2d537566ed725e69b Mon Sep 17 00:00:00 2001 From: IndieanaJones <47086570+IndieanaJones@users.noreply.github.com> Date: Fri, 9 Feb 2024 13:18:44 -0500 Subject: [PATCH 046/508] [NoGBP] Fixes Equipment Slowdown (#81352) ## About The Pull Request Fixes #81350 #81321 wasn't done as tactfully as it should have, resulting in some oddities like bolas in your pockets giving you slowdown. This PR cleans up the code and makes it work as intended. Thanks to Melbert for literally just giving me the code for this. I did test it and added proc comments so I can guarantee everything works. ## Why It's Good For The Game You shouldn't be slowed down from a bola in your pocket, and this logic is much nicer than what we had before. ## Changelog :cl: fix: Bolas in your pockets no longer slow you down. /:cl: --- .../mob/living/carbon/human/inventory.dm | 7 ++----- code/modules/mob/living/carbon/inventory.dm | 7 ++----- code/modules/mob/mob.dm | 17 ++++++++++------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index 8a61096767d62..29e5bd25e14dc 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -228,11 +228,8 @@ return not_handled //For future deeper overrides -/mob/living/carbon/human/equipped_speed_mods() - . = ..() - for(var/sloties in get_all_worn_items() - list(l_store, r_store, s_store, back, wear_mask, wear_neck, head, handcuffed, legcuffed)) - var/obj/item/thing = sloties - . += thing?.slowdown +/mob/living/carbon/human/get_equipped_speed_mod_items() + return ..() - list(l_store, r_store, s_store) /mob/living/carbon/human/doUnEquip(obj/item/I, force, newloc, no_move, invdrop = TRUE, silent = FALSE) . = ..() //See mob.dm for an explanation on this and some rage about people copypasting instead of calling ..() like they should. diff --git a/code/modules/mob/living/carbon/inventory.dm b/code/modules/mob/living/carbon/inventory.dm index 8e154b0977f6f..2c6649dced8c9 100644 --- a/code/modules/mob/living/carbon/inventory.dm +++ b/code/modules/mob/living/carbon/inventory.dm @@ -128,11 +128,8 @@ return not_handled -/mob/living/carbon/equipped_speed_mods() - . = ..() - for(var/sloties in get_all_worn_items()) - var/obj/item/thing = sloties - . += thing?.slowdown +/mob/living/carbon/get_equipped_speed_mod_items() + return ..() + get_all_worn_items() /// This proc is called after an item has been successfully handled and equipped to a slot. /mob/living/carbon/proc/has_equipped(obj/item/item, slot, initial = FALSE) diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 9dcbc28420921..0301b59e8a0a7 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -1509,8 +1509,11 @@ /mob/proc/set_nutrition(change) //Seriously fuck you oldcoders. nutrition = max(0, change) +///Apply a proper movespeed modifier based on items we have equipped /mob/proc/update_equipment_speed_mods() - var/speedies = equipped_speed_mods() + var/speedies = 0 + for(var/obj/item/thing in get_equipped_speed_mod_items()) + speedies += thing.slowdown if(speedies > 0 && HAS_TRAIT(src, TRAIT_SETTLER)) //if our movespeed mod is in the negatives, we don't modify it since that's a benefit speedies *= 0.2 if(!speedies) @@ -1518,12 +1521,12 @@ else add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/equipment_speedmod, multiplicative_slowdown = speedies) -/// Gets the combined speed modification of all worn items -/// Except base mob type doesnt really wear items -/mob/proc/equipped_speed_mods() - for(var/obj/item/I in held_items) - if(I.item_flags & SLOWS_WHILE_IN_HAND) - . += I.slowdown +///Get all items in our possession that should affect our movespeed +/mob/proc/get_equipped_speed_mod_items() + . = list() + for(var/obj/item/thing in held_items) + if(thing.item_flags & SLOWS_WHILE_IN_HAND) + . += thing /mob/proc/set_stat(new_stat) if(new_stat == stat) From d419b124551fcd4ffc27721941f291d695c98349 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 07:19:03 +1300 Subject: [PATCH 047/508] Automatic changelog for PR #81352 [ci skip] --- html/changelogs/AutoChangeLog-pr-81352.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81352.yml diff --git a/html/changelogs/AutoChangeLog-pr-81352.yml b/html/changelogs/AutoChangeLog-pr-81352.yml new file mode 100644 index 0000000000000..57c2cef8c56a0 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81352.yml @@ -0,0 +1,4 @@ +author: "IndieanaJones" +delete-after: True +changes: + - bugfix: "Bolas in your pockets no longer slow you down." \ No newline at end of file From f01ded9dd41c954874a689c6b2920f924153a363 Mon Sep 17 00:00:00 2001 From: _0Steven <42909981+00-Steven@users.noreply.github.com> Date: Fri, 9 Feb 2024 23:22:12 +0100 Subject: [PATCH 048/508] Fixes screwdriver cocktail not being a screwdriver. (#81030) ## About The Pull Request Alternative title: The screwdriver cocktail is now the world's worst screwdriver (for real this time). As mentioned in my writeup in the related issue (#81017), as far as I know, it has never actually worked. Tl;dr: It requires `on_transfer` to be called with the method as ingestion, but in no way does transferring into a drinking glass ever use that method, and most ways don't even specify a method and thus `on_transfer` doesn't even get called in the first place. Then I went back to the original pr and tried it, and it didn't work. Then for resolving it, it feels unwieldy to do all this trickery on the reagent to change a value on the drinking glass when the drinking glass already has the perfect procs for this: the `on_cup_change` and `on_cup_reset` callbacks. Instead of using `on_transfer` and registering a hell of a lot of signals to re-check the master reagent every time the contents get changed, we just check whether the style we changed into is that of a screwdriver cocktail. This actually works. Oh, and I also added use sounds because it didn't have them, which I believe only actually get used when using it as a tool and thus only when it's a screwdriver. ## Why It's Good For The Game Fixes #81017. ## Changelog :cl: fix: As they should've for a while, screwdrivers (cocktail) actually work as screwdrivers (tool). sound: The screwdriver cocktail also actually plays the screwdriver sound when used. /:cl: --------- Co-authored-by: Ghom <42542238+Ghommie@users.noreply.github.com> --- .../reagents/drinks/alcohol_reagents.dm | 52 +++++++++++++------ 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm index 1381ed81f12da..2cc88da41ade8 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/alcohol_reagents.dm @@ -589,37 +589,55 @@ taste_description = "oranges" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED -/datum/reagent/consumable/ethanol/screwdrivercocktail/on_transfer(atom/atom, methods = TOUCH, trans_volume) - if(!(methods & INGEST)) - return ..() - - if(src == atom.reagents.get_master_reagent() && istype(atom, /obj/item/reagent_containers/cup/glass/drinkingglass)) - var/obj/item/reagent_containers/cup/glass/drinkingglass/drink = atom - drink.tool_behaviour = TOOL_SCREWDRIVER +/datum/reagent/consumable/ethanol/screwdrivercocktail/on_new(data) + . = ..() + // We want to turn only base drinking glasses with screwdriver(cocktail) into screwdrivers(tool), + // but we can't check style so we have to check type, and we don't want it match subtypes like istype does + if(holder?.my_atom && holder.my_atom.type == /obj/item/reagent_containers/cup/glass/drinkingglass/) var/list/reagent_change_signals = list( COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_REM_REAGENT, - COMSIG_REAGENTS_DEL_REAGENT, - COMSIG_REAGENTS_CLEAR_REAGENTS, - COMSIG_REAGENTS_REACTED, ) - RegisterSignals(drink.reagents, reagent_change_signals, PROC_REF(on_reagent_change)) - - return ..() + RegisterSignals(holder, reagent_change_signals, PROC_REF(on_reagent_change)) + RegisterSignal(holder, COMSIG_REAGENTS_CLEAR_REAGENTS, PROC_REF(on_reagents_clear)) + RegisterSignal(holder, COMSIG_REAGENTS_DEL_REAGENT, PROC_REF(on_reagent_delete)) + if(src == holder.get_master_reagent()) + var/obj/item/reagent_containers/cup/glass/drinkingglass/drink = holder.my_atom + drink.tool_behaviour = TOOL_SCREWDRIVER + drink.usesound = list('sound/items/screwdriver.ogg', 'sound/items/screwdriver2.ogg') /datum/reagent/consumable/ethanol/screwdrivercocktail/proc/on_reagent_change(datum/reagents/reagents) SIGNAL_HANDLER - if(src != reagents.get_master_reagent()) - var/obj/item/reagent_containers/cup/glass/drinkingglass/drink = reagents.my_atom + var/obj/item/reagent_containers/cup/glass/drinkingglass/drink = reagents.my_atom + if(reagents.get_master_reagent() == src) + drink.tool_behaviour = TOOL_SCREWDRIVER + drink.usesound = list('sound/items/screwdriver.ogg', 'sound/items/screwdriver2.ogg') + else + drink.tool_behaviour = initial(drink.tool_behaviour) + drink.usesound = initial(drink.usesound) + +/datum/reagent/consumable/ethanol/screwdrivercocktail/proc/on_reagents_clear(datum/reagents/reagents) + SIGNAL_HANDLER + unregister_screwdriver(reagents) + +/datum/reagent/consumable/ethanol/screwdrivercocktail/proc/on_reagent_delete(datum/reagents/reagents, datum/reagent/deleted_reagent) + SIGNAL_HANDLER + if(deleted_reagent != src) + return + unregister_screwdriver(reagents) + +/datum/reagent/consumable/ethanol/screwdrivercocktail/proc/unregister_screwdriver(datum/reagents/reagents) + var/obj/item/reagent_containers/cup/glass/drinkingglass/drink = reagents.my_atom + if(drink.tool_behaviour == TOOL_SCREWDRIVER) drink.tool_behaviour = initial(drink.tool_behaviour) - UnregisterSignal(reagents, list( + drink.usesound = initial(drink.usesound) + UnregisterSignal(reagents, list( COMSIG_REAGENTS_ADD_REAGENT, COMSIG_REAGENTS_NEW_REAGENT, COMSIG_REAGENTS_REM_REAGENT, COMSIG_REAGENTS_DEL_REAGENT, COMSIG_REAGENTS_CLEAR_REAGENTS, - COMSIG_REAGENTS_REACTED, )) /datum/reagent/consumable/ethanol/screwdrivercocktail/on_mob_life(mob/living/carbon/drinker, seconds_per_tick, times_fired) From 7159e051386d9b80bbabd9663105d6762d30611e Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:22:30 +1300 Subject: [PATCH 049/508] Automatic changelog for PR #81030 [ci skip] --- html/changelogs/AutoChangeLog-pr-81030.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81030.yml diff --git a/html/changelogs/AutoChangeLog-pr-81030.yml b/html/changelogs/AutoChangeLog-pr-81030.yml new file mode 100644 index 0000000000000..bbbbfc7575265 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81030.yml @@ -0,0 +1,5 @@ +author: "00-Steven" +delete-after: True +changes: + - bugfix: "As they should've for a while, screwdrivers (cocktail) actually work as screwdrivers (tool)." + - sound: "The screwdriver cocktail also actually plays the screwdriver sound when used." \ No newline at end of file From 7054f85a8d755b0b9c151143a45f7cf4070511bb Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sat, 10 Feb 2024 00:19:18 +0000 Subject: [PATCH 050/508] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-81030.yml | 5 ---- html/changelogs/AutoChangeLog-pr-81190.yml | 4 --- html/changelogs/AutoChangeLog-pr-81228.yml | 4 --- html/changelogs/AutoChangeLog-pr-81292.yml | 4 --- html/changelogs/AutoChangeLog-pr-81306.yml | 4 --- html/changelogs/AutoChangeLog-pr-81339.yml | 4 --- html/changelogs/AutoChangeLog-pr-81345.yml | 5 ---- html/changelogs/AutoChangeLog-pr-81347.yml | 4 --- html/changelogs/AutoChangeLog-pr-81348.yml | 4 --- html/changelogs/AutoChangeLog-pr-81349.yml | 4 --- html/changelogs/AutoChangeLog-pr-81352.yml | 4 --- html/changelogs/AutoChangeLog-pr-81353.yml | 4 --- html/changelogs/AutoChangeLog-pr-81355.yml | 4 --- html/changelogs/archive/2024-02.yml | 33 ++++++++++++++++++++++ 14 files changed, 33 insertions(+), 54 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-81030.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81190.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81228.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81292.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81306.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81339.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81345.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81347.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81348.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81349.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81352.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81353.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81355.yml diff --git a/html/changelogs/AutoChangeLog-pr-81030.yml b/html/changelogs/AutoChangeLog-pr-81030.yml deleted file mode 100644 index bbbbfc7575265..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81030.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "00-Steven" -delete-after: True -changes: - - bugfix: "As they should've for a while, screwdrivers (cocktail) actually work as screwdrivers (tool)." - - sound: "The screwdriver cocktail also actually plays the screwdriver sound when used." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81190.yml b/html/changelogs/AutoChangeLog-pr-81190.yml deleted file mode 100644 index b4adc2b12a12e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81190.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Ben10Omintrix" -delete-after: True -changes: - - rscadd: "settlers can rename their pets" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81228.yml b/html/changelogs/AutoChangeLog-pr-81228.yml deleted file mode 100644 index 20b504aa51a86..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81228.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "FernandoJ8" -delete-after: True -changes: - - code_imp: "added an IS_CHANGELING() helper and used it where applicable" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81292.yml b/html/changelogs/AutoChangeLog-pr-81292.yml deleted file mode 100644 index 88999998073f5..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81292.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Holoo-1" -delete-after: True -changes: - - admin: "remade everyone is traitor into everyone is antag in secrets panel" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81306.yml b/html/changelogs/AutoChangeLog-pr-81306.yml deleted file mode 100644 index 98c9d89afa146..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81306.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "LT3" -delete-after: True -changes: - - qol: "Health analyzer will now display blood alcohol content" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81339.yml b/html/changelogs/AutoChangeLog-pr-81339.yml deleted file mode 100644 index 70e0496102887..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81339.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - rscadd: "Changeling's reviving stasis ability now puts you in stasis." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81345.yml b/html/changelogs/AutoChangeLog-pr-81345.yml deleted file mode 100644 index 0bbc14b258605..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81345.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "1393F" -delete-after: True -changes: - - bugfix: "The HFR now provides the max temperature for recipes" - - bugfix: "the crystallizer now provides the dangerous object created instead of itself in admin logging" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81347.yml b/html/changelogs/AutoChangeLog-pr-81347.yml deleted file mode 100644 index ed3b938860a1d..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81347.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "Fixed Finger Guns giving a misleading chat message" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81348.yml b/html/changelogs/AutoChangeLog-pr-81348.yml deleted file mode 100644 index 5e7867c9d1b6b..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81348.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "You know that one bug that makes the cryo cells on Deltastation unusuable? Well it's not fixed but at least those cryo cells are usuable again, maybe at the cost of another station's cryo cells. Who knows!" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81349.yml b/html/changelogs/AutoChangeLog-pr-81349.yml deleted file mode 100644 index 23efa8d682eb7..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81349.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - bugfix: "Fixed some situations in which you couldn't interact with heretic runes" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81352.yml b/html/changelogs/AutoChangeLog-pr-81352.yml deleted file mode 100644 index 57c2cef8c56a0..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81352.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "IndieanaJones" -delete-after: True -changes: - - bugfix: "Bolas in your pockets no longer slow you down." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81353.yml b/html/changelogs/AutoChangeLog-pr-81353.yml deleted file mode 100644 index a33ebc0cbae1e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81353.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "larentoun" -delete-after: True -changes: - - bugfix: "Now falsewalls visually don't close when they shouldn't." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81355.yml b/html/changelogs/AutoChangeLog-pr-81355.yml deleted file mode 100644 index 970c4c6432176..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81355.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "LemonInTheDark" -delete-after: True -changes: - - rscdel: "Removes halloween screen tint, we're taking him to retire by the seaside (he was alone and unloved)" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index aec4c211b5605..e9299211d16b6 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -236,3 +236,36 @@ - rscadd: Adds a new suicide_act() to the smite spell Zergspower: - bugfix: icebox ore generation underground is normal again +2024-02-10: + 00-Steven: + - bugfix: As they should've for a while, screwdrivers (cocktail) actually work as + screwdrivers (tool). + - sound: The screwdriver cocktail also actually plays the screwdriver sound when + used. + 1393F: + - bugfix: The HFR now provides the max temperature for recipes + - bugfix: the crystallizer now provides the dangerous object created instead of + itself in admin logging + Ben10Omintrix: + - rscadd: settlers can rename their pets + FernandoJ8: + - code_imp: added an IS_CHANGELING() helper and used it where applicable + Holoo-1: + - admin: remade everyone is traitor into everyone is antag in secrets panel + IndieanaJones: + - bugfix: Bolas in your pockets no longer slow you down. + JohnFulpWillard: + - rscadd: Changeling's reviving stasis ability now puts you in stasis. + LT3: + - qol: Health analyzer will now display blood alcohol content + LemonInTheDark: + - rscdel: Removes halloween screen tint, we're taking him to retire by the seaside + (he was alone and unloved) + Melbert: + - bugfix: Fixed some situations in which you couldn't interact with heretic runes + - bugfix: Fixed Finger Guns giving a misleading chat message + - bugfix: You know that one bug that makes the cryo cells on Deltastation unusuable? + Well it's not fixed but at least those cryo cells are usuable again, maybe at + the cost of another station's cryo cells. Who knows! + larentoun: + - bugfix: Now falsewalls visually don't close when they shouldn't. From d9243d1016e3e370e4564495472a2f8d0ec59dc6 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:26:00 -0600 Subject: [PATCH 051/508] Refactors fire overlays once again to make it not get stuck so often (#81367) ## About The Pull Request Maybe finally fixes #77701 A big reason why this kept happening is because fire uses standing overlays. But fire isn't managed by mobs anymore. Meaning in some situations, fire can cease to exist but the overlay can still be on the mob. So it gets stuck. So like, why use standing overlays anymore? We can just hook `update_overlays` signal. Isn't that neat. ## Changelog :cl: Melbert refactor: Fire effects get added to mobs in a different way now. Maybe it will get stuck less. Report any oddities. /:cl: --- code/__DEFINES/mobs.dm | 9 ++--- code/__DEFINES/status_effects.dm | 5 +++ code/__DEFINES/traits/declarations.dm | 4 +- code/_globalvars/traits/_traits.dm | 2 +- code/_globalvars/traits/admin_tooling.dm | 2 +- .../datums/elements/permanent_fire_overlay.dm | 26 ++++++++++++ .../status_effects/debuffs/fire_stacks.dm | 40 +++++++++---------- code/game/turfs/open/lava.dm | 7 ++-- code/modules/mob/living/basic/basic.dm | 22 +++++----- .../mob/living/carbon/carbon_update_icons.dm | 31 +++++--------- .../carbon/human/species_types/plasmamen.dm | 2 +- .../modules/mob/living/carbon/init_signals.dm | 12 ------ code/modules/mob/living/living.dm | 24 +++++------ .../modules/mob/living/silicon/robot/robot.dm | 26 +++++------- tgstation.dme | 1 + 15 files changed, 103 insertions(+), 110 deletions(-) create mode 100644 code/datums/elements/permanent_fire_overlay.dm diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index d85e62615d48b..2e1eac6672d2a 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -486,9 +486,6 @@ #define ROBOTIC_BRUTE_EXAMINE_TEXT "denting" #define ROBOTIC_BURN_EXAMINE_TEXT "charring" -// If a mob has a higher threshold than this, the icon shown will be increased to the big fire icon. -#define MOB_BIG_FIRE_STACK_THRESHOLD 3 - #define GRAB_PIXEL_SHIFT_PASSIVE 6 #define GRAB_PIXEL_SHIFT_AGGRESSIVE 12 #define GRAB_PIXEL_SHIFT_NECK 16 @@ -756,8 +753,8 @@ GLOBAL_LIST_INIT(human_heights_to_offsets, list( #define WOUND_LAYER 3 /// Blood cult ascended halo layer, because there's currently no better solution for adding/removing #define HALO_LAYER 2 -/// Fire layer when you're on fire -#define FIRE_LAYER 1 +/// The highest most layer for mob overlays. Unused +#define HIGHEST_LAYER 1 #define UPPER_BODY "upper body" #define LOWER_BODY "lower body" @@ -798,7 +795,7 @@ GLOBAL_LIST_INIT(layers_to_offset, list( // BODY_BEHIND_LAYER (external organs like wings) // BODY_FRONT_LAYER (external organs like wings) // DAMAGE_LAYER (full body) - // FIRE_LAYER (full body) + // HIGHEST_LAYER (full body) // UNIFORM_LAYER (full body) // WOUND_LAYER (full body) )) diff --git a/code/__DEFINES/status_effects.dm b/code/__DEFINES/status_effects.dm index 768f1faa514f1..4e901c4ba2ce2 100644 --- a/code/__DEFINES/status_effects.dm +++ b/code/__DEFINES/status_effects.dm @@ -31,6 +31,11 @@ /// If the incapacitated status effect will ignore a mob being agressively grabbed #define IGNORE_GRAB (1<<2) +/// Maxamounts of fire stacks a mob can get +#define MAX_FIRE_STACKS 20 +/// If a mob has a higher threshold than this, the icon shown will be increased to the big fire icon. +#define MOB_BIG_FIRE_STACK_THRESHOLD 3 + // Grouped effect sources, see also code/__DEFINES/traits.dm #define STASIS_MACHINE_EFFECT "stasis_machine" diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm index 867f567be00fa..4e0a0f5b8f984 100644 --- a/code/__DEFINES/traits/declarations.dm +++ b/code/__DEFINES/traits/declarations.dm @@ -353,8 +353,8 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai #define TRAIT_TUMOR_SUPPRESSED "brain_tumor_suppressed" /// Prevents hallucinations from the hallucination brain trauma (RDS) #define TRAIT_RDS_SUPPRESSED "rds_suppressed" -/// mobs that have this trait cannot be extinguished -#define TRAIT_PERMANENTLY_ONFIRE "permanently_onfire" +/// Mobs that have this trait cannot be extinguished +#define TRAIT_NO_EXTINGUISH "no_extinguish" /// Indicates if the mob is currently speaking with sign language #define TRAIT_SIGN_LANG "sign_language" /// This mob is able to use sign language over the radio. diff --git a/code/_globalvars/traits/_traits.dm b/code/_globalvars/traits/_traits.dm index aba502d16ee71..d58940485ac88 100644 --- a/code/_globalvars/traits/_traits.dm +++ b/code/_globalvars/traits/_traits.dm @@ -318,6 +318,7 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_NO_DEBRAIN_OVERLAY" = TRAIT_NO_DEBRAIN_OVERLAY, "TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY, "TRAIT_NO_DNA_SCRAMBLE" = TRAIT_NO_DNA_SCRAMBLE, + "TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH, "TRAIT_NO_FLOATING_ANIM" = TRAIT_NO_FLOATING_ANIM, "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE, "TRAIT_NO_GUN_AKIMBO" = TRAIT_NO_GUN_AKIMBO, @@ -353,7 +354,6 @@ GLOBAL_LIST_INIT(traits_by_type, list( "TRAIT_PASSTABLE" = TRAIT_PASSTABLE, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, "TRAIT_PERMANENTLY_MORTAL" = TRAIT_PERMANENTLY_MORTAL, - "TRAIT_PERMANENTLY_ONFIRE" = TRAIT_PERMANENTLY_ONFIRE, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_PLANT_SAFE" = TRAIT_PLANT_SAFE, diff --git a/code/_globalvars/traits/admin_tooling.dm b/code/_globalvars/traits/admin_tooling.dm index c2148f2b7b698..01269087d3660 100644 --- a/code/_globalvars/traits/admin_tooling.dm +++ b/code/_globalvars/traits/admin_tooling.dm @@ -129,6 +129,7 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_NO_AUGMENTS" = TRAIT_NO_AUGMENTS, "TRAIT_NO_BLOOD_OVERLAY" = TRAIT_NO_BLOOD_OVERLAY, "TRAIT_NO_DNA_COPY" = TRAIT_NO_DNA_COPY, + "TRAIT_NO_EXTINGUISH" = TRAIT_NO_EXTINGUISH, "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE, "TRAIT_NO_PLASMA_TRANSFORM" = TRAIT_NO_PLASMA_TRANSFORM, "TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL, @@ -162,7 +163,6 @@ GLOBAL_LIST_INIT(admin_visible_traits, list( "TRAIT_PARALYSIS_R_LEG" = TRAIT_PARALYSIS_R_LEG, "TRAIT_PASSTABLE" = TRAIT_PASSTABLE, "TRAIT_PERFECT_ATTACKER" = TRAIT_PERFECT_ATTACKER, - "TRAIT_PERMANENTLY_ONFIRE" = TRAIT_PERMANENTLY_ONFIRE, "TRAIT_PHOTOGRAPHER" = TRAIT_PHOTOGRAPHER, "TRAIT_PIERCEIMMUNE" = TRAIT_PIERCEIMMUNE, "TRAIT_PLANT_SAFE" = TRAIT_PLANT_SAFE, diff --git a/code/datums/elements/permanent_fire_overlay.dm b/code/datums/elements/permanent_fire_overlay.dm new file mode 100644 index 0000000000000..e4a61852aed74 --- /dev/null +++ b/code/datums/elements/permanent_fire_overlay.dm @@ -0,0 +1,26 @@ +/// When applied to a mob, they will always have a fire overlay regardless of if they are *actually* on fire. +/datum/element/perma_fire_overlay + +/datum/element/perma_fire_overlay/Attach(atom/target) + . = ..() + if(!isliving(target)) + return ELEMENT_INCOMPATIBLE + + RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_fire_overlay)) + target.update_appearance(UPDATE_OVERLAYS) + ADD_TRAIT(target, TRAIT_NO_EXTINGUISH, ELEMENT_TRAIT(type)) + +/datum/element/perma_fire_overlay/Detach(atom/target) + . = ..() + UnregisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS) + REMOVE_TRAIT(target, TRAIT_NO_EXTINGUISH, ELEMENT_TRAIT(type)) + target.update_appearance(UPDATE_OVERLAYS) + +/datum/element/perma_fire_overlay/proc/add_fire_overlay(mob/living/source, list/overlays) + SIGNAL_HANDLER + + var/mutable_appearance/created_overlay = source.get_fire_overlay(stacks = MAX_FIRE_STACKS, on_fire = TRUE) + if(isnull(created_overlay)) + return + + overlays |= created_overlay diff --git a/code/datums/status_effects/debuffs/fire_stacks.dm b/code/datums/status_effects/debuffs/fire_stacks.dm index 4a6e7b6b730f6..2f32ff5b3bedf 100644 --- a/code/datums/status_effects/debuffs/fire_stacks.dm +++ b/code/datums/status_effects/debuffs/fire_stacks.dm @@ -7,7 +7,7 @@ /// Current amount of stacks we have var/stacks /// Maximum of stacks that we could possibly get - var/stack_limit = 20 + var/stack_limit = MAX_FIRE_STACKS /// What status effect types do we remove uppon being applied. These are just deleted without any deduction from our or their stacks when forced. var/list/enemy_types /// What status effect types do we merge into if they exist. Ignored when forced. @@ -116,12 +116,8 @@ owner.clear_alert(ALERT_FIRE) else if(!was_on_fire && owner.on_fire) owner.throw_alert(ALERT_FIRE, /atom/movable/screen/alert/fire) - -/** - * Used to update owner's effect overlay - */ - -/datum/status_effect/fire_handler/proc/update_overlay() + owner.update_appearance(UPDATE_OVERLAYS) + update_particles() /datum/status_effect/fire_handler/fire_stacks id = "fire_stacks" //fire_stacks and wet_stacks should have different IDs or else has_status_effect won't work @@ -132,8 +128,6 @@ /// If we're on fire var/on_fire = FALSE - /// Stores current fire overlay icon state, for optimisation purposes - var/last_icon_state /// Reference to the mob light emitter itself var/obj/effect/dummy/lighting_obj/moblight /// Type of mob light emitter we use when on fire @@ -160,8 +154,6 @@ return TRUE deal_damage(seconds_between_ticks) - update_overlay() - update_particles() /datum/status_effect/fire_handler/fire_stacks/update_particles() if(on_fire) @@ -239,8 +231,6 @@ moblight = new moblight_type(owner) cache_stacks() - update_overlay() - update_particles() SEND_SIGNAL(owner, COMSIG_LIVING_IGNITED, owner) return TRUE @@ -254,8 +244,6 @@ owner.clear_mood_event("on_fire") SEND_SIGNAL(owner, COMSIG_LIVING_EXTINGUISHED, owner) cache_stacks() - update_overlay() - update_particles() for(var/obj/item/equipped in owner.get_equipped_items()) equipped.extinguish() @@ -263,16 +251,26 @@ if(on_fire) extinguish() set_stacks(0) - update_overlay() - update_particles() + UnregisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS) + owner.update_appearance(UPDATE_OVERLAYS) return ..() -/datum/status_effect/fire_handler/fire_stacks/update_overlay() - last_icon_state = owner.update_fire_overlay(stacks, on_fire, last_icon_state) - /datum/status_effect/fire_handler/fire_stacks/on_apply() . = ..() - update_overlay() + RegisterSignal(owner, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_fire_overlay)) + owner.update_appearance(UPDATE_OVERLAYS) + +/datum/status_effect/fire_handler/fire_stacks/proc/add_fire_overlay(mob/living/source, list/overlays) + SIGNAL_HANDLER + + if(stacks <= 0 || !on_fire) + return + + var/mutable_appearance/created_overlay = owner.get_fire_overlay(stacks, on_fire) + if(isnull(created_overlay)) + return + + overlays |= created_overlay /obj/effect/dummy/lighting_obj/moblight/fire name = "fire" diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index 1a174723d85b8..30bed8fc87c1c 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -52,7 +52,7 @@ /turf/open/lava/Destroy() for(var/mob/living/leaving_mob in contents) - REMOVE_TRAIT(leaving_mob, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) + leaving_mob.RemoveElement(/datum/element/perma_fire_overlay) return ..() /turf/open/lava/update_overlays() @@ -144,7 +144,7 @@ /turf/open/lava/Exited(atom/movable/gone, direction) . = ..() if(isliving(gone) && !islava(gone.loc)) - REMOVE_TRAIT(gone, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) + gone.RemoveElement(/datum/element/perma_fire_overlay) /turf/open/lava/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(burn_stuff(AM)) @@ -311,10 +311,9 @@ return var/mob/living/burn_living = burn_target - ADD_TRAIT(burn_living, TRAIT_PERMANENTLY_ONFIRE, TURF_TRAIT) + burn_living.AddElement(/datum/element/perma_fire_overlay) burn_living.ignite_mob() burn_living.adjust_fire_stacks(lava_firestacks * seconds_per_tick) - burn_living.update_fire() burn_living.adjustFireLoss(lava_damage * seconds_per_tick) /turf/open/lava/can_cross_safely(atom/movable/crossing) diff --git a/code/modules/mob/living/basic/basic.dm b/code/modules/mob/living/basic/basic.dm index ba07c944652e4..d69665d6bcdec 100644 --- a/code/modules/mob/living/basic/basic.dm +++ b/code/modules/mob/living/basic/basic.dm @@ -270,17 +270,17 @@ /mob/living/basic/on_fire_stack(seconds_per_tick, datum/status_effect/fire_handler/fire_stacks/fire_handler) adjust_bodytemperature((maximum_survivable_temperature + (fire_handler.stacks * 12)) * 0.5 * seconds_per_tick) -/mob/living/basic/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/mutable_appearance/fire_overlay = mutable_appearance('icons/mob/effects/onfire.dmi', "generic_fire") - if(on_fire && isnull(last_icon_state)) - add_overlay(fire_overlay) - return fire_overlay - else if(!on_fire && !isnull(last_icon_state)) - cut_overlay(fire_overlay) - return null - else if(on_fire && !isnull(last_icon_state)) - return last_icon_state - return null +/mob/living/basic/get_fire_overlay(stacks, on_fire) + var/fire_icon = "generic_fire" + if(!GLOB.fire_appearances[fire_icon]) + GLOB.fire_appearances[fire_icon] = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) + + return GLOB.fire_appearances[fire_icon] /mob/living/basic/put_in_hands(obj/item/I, del_on_fail = FALSE, merge_stacks = TRUE, ignore_animation = TRUE) . = ..() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 7aa9afba95ea1..8dfa0be5e2b0c 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -278,8 +278,8 @@ update_held_items() update_worn_handcuffs() update_worn_legcuffs() - update_fire() update_body() + update_appearance(UPDATE_OVERLAYS) /mob/living/carbon/update_held_items() . = ..() @@ -315,27 +315,18 @@ hands += I.build_worn_icon(default_layer = HANDS_LAYER, default_icon_file = icon_file, isinhands = TRUE) return hands -/mob/living/carbon/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/fire_icon = "[dna?.species.fire_overlay || "human"]_[stacks > MOB_BIG_FIRE_STACK_THRESHOLD ? "big_fire" : "small_fire"][suffix]" +/mob/living/carbon/get_fire_overlay(stacks, on_fire) + var/fire_icon = "[dna?.species.fire_overlay || "human"]_[stacks > MOB_BIG_FIRE_STACK_THRESHOLD ? "big_fire" : "small_fire"]" if(!GLOB.fire_appearances[fire_icon]) - GLOB.fire_appearances[fire_icon] = mutable_appearance('icons/mob/effects/onfire.dmi', fire_icon, -FIRE_LAYER, appearance_flags = RESET_COLOR) - - if((stacks > 0 && on_fire) || HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) - if(fire_icon == last_icon_state) - return last_icon_state - - remove_overlay(FIRE_LAYER) - overlays_standing[FIRE_LAYER] = GLOB.fire_appearances[fire_icon] - apply_overlay(FIRE_LAYER) - return fire_icon - - if(!last_icon_state) - return last_icon_state - - remove_overlay(FIRE_LAYER) - apply_overlay(FIRE_LAYER) - return null + GLOB.fire_appearances[fire_icon] = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) + + return GLOB.fire_appearances[fire_icon] /mob/living/carbon/update_damage_overlays() remove_overlay(DAMAGE_LAYER) diff --git a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm index 8ce8be6f6d6b5..c9fa732b2880d 100644 --- a/code/modules/mob/living/carbon/human/species_types/plasmamen.dm +++ b/code/modules/mob/living/carbon/human/species_types/plasmamen.dm @@ -115,7 +115,7 @@ else internal_fire = FALSE - H.update_fire() + H.update_appearance(UPDATE_OVERLAYS) /datum/species/plasmaman/handle_fire(mob/living/carbon/human/H, seconds_per_tick, no_protection = FALSE) if(internal_fire) diff --git a/code/modules/mob/living/carbon/init_signals.dm b/code/modules/mob/living/carbon/init_signals.dm index 190fe9d845342..e64410ab63573 100644 --- a/code/modules/mob/living/carbon/init_signals.dm +++ b/code/modules/mob/living/carbon/init_signals.dm @@ -13,12 +13,6 @@ RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_TOXIMMUNE), PROC_REF(on_toximmune_trait_gain)) RegisterSignal(src, SIGNAL_ADDTRAIT(TRAIT_GENELESS), PROC_REF(on_geneless_trait_gain)) - - RegisterSignals(src, list( - SIGNAL_ADDTRAIT(TRAIT_PERMANENTLY_ONFIRE), - SIGNAL_REMOVETRAIT(TRAIT_PERMANENTLY_ONFIRE), - ), PROC_REF(update_permanently_on_fire)) - /** * On gain of TRAIT_AGENDER * @@ -90,12 +84,6 @@ reagents.end_metabolization(keep_liverless = TRUE) -///On gain of TRAIT_PERMANENTLY_ONFIRE, update the visuals if not on fire -/mob/living/carbon/proc/update_permanently_on_fire(datum/source) - SIGNAL_HANDLER - if(!on_fire) - update_fire() - /** * On gain of TRAIT_VIRUSIMMUNE * diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index f518e29a2b426..e096434cbb966 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -1580,11 +1580,6 @@ GLOBAL_LIST_EMPTY(fire_appearances) return fire_status.ignite(silent) -/mob/living/proc/update_fire() - var/datum/status_effect/fire_handler/fire_stacks/fire_stacks = has_status_effect(/datum/status_effect/fire_handler/fire_stacks) - if(fire_stacks) - fire_stacks.update_overlay() - /** * Extinguish all fire on the mob * @@ -1592,7 +1587,7 @@ GLOBAL_LIST_EMPTY(fire_appearances) * Signals the extinguishing. */ /mob/living/proc/extinguish_mob() - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //The everlasting flames will not be extinguished + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //The everlasting flames will not be extinguished return var/datum/status_effect/fire_handler/fire_stacks/fire_status = has_status_effect(/datum/status_effect/fire_handler/fire_stacks) if(!fire_status || !fire_status.on_fire) @@ -1611,13 +1606,13 @@ GLOBAL_LIST_EMPTY(fire_appearances) /mob/living/proc/adjust_fire_stacks(stacks, fire_type = /datum/status_effect/fire_handler/fire_stacks) if(stacks < 0) - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //You can't reduce fire stacks of the everlasting flames + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //You can't reduce fire stacks of the everlasting flames return stacks = max(-fire_stacks, stacks) apply_status_effect(fire_type, stacks) /mob/living/proc/adjust_wet_stacks(stacks, wet_type = /datum/status_effect/fire_handler/wet_stacks) - if(HAS_TRAIT(src, TRAIT_PERMANENTLY_ONFIRE)) //The everlasting flames will not be extinguished + if(HAS_TRAIT(src, TRAIT_NO_EXTINGUISH)) //The everlasting flames will not be extinguished return if(stacks < 0) stacks = max(fire_stacks, stacks) @@ -1695,19 +1690,18 @@ GLOBAL_LIST_EMPTY(fire_appearances) ignite_mob() /** - * Sets fire overlay of the mob. + * Gets the fire overlay to use for this mob * - * Vars: + * Args: * * stacks: Current amount of fire_stacks * * on_fire: If we're lit on fire - * * last_icon_state: Holds last fire overlay icon state, used for optimization - * * suffix: Suffix for the fire icon state for special fire types * - * This should return last_icon_state for the fire status efect + * Return a mutable appearance, the overlay that will be applied. */ -/mob/living/proc/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - return last_icon_state +/mob/living/proc/get_fire_overlay(stacks, on_fire) + RETURN_TYPE(/mutable_appearance) + return null /** * Handles effects happening when mob is on normal fire diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index ed9cbe09352c3..f73336fe963d7 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -352,7 +352,7 @@ var/mutable_appearance/head_overlay = hat.build_worn_icon(default_layer = 20, default_icon_file = 'icons/mob/clothing/head/default.dmi') head_overlay.pixel_z += hat_offset add_overlay(head_overlay) - update_fire() + update_appearance(UPDATE_OVERLAYS) /mob/living/silicon/robot/on_changed_z_level(turf/old_turf, turf/new_turf, same_z_layer, notify_contents) if(same_z_layer) @@ -1017,25 +1017,19 @@ /mob/living/silicon/robot/proc/untip_roleplay() to_chat(src, span_notice("Your frustration has empowered you! You can now right yourself faster!")) -/mob/living/silicon/robot/update_fire_overlay(stacks, on_fire, last_icon_state, suffix = "") - var/fire_icon = "generic_fire[suffix]" +/mob/living/silicon/robot/get_fire_overlay(stacks, on_fire) + var/fire_icon = "generic_fire" if(!GLOB.fire_appearances[fire_icon]) - var/mutable_appearance/new_fire_overlay = mutable_appearance('icons/mob/effects/onfire.dmi', fire_icon, -FIRE_LAYER) - new_fire_overlay.appearance_flags = RESET_COLOR + var/mutable_appearance/new_fire_overlay = mutable_appearance( + 'icons/mob/effects/onfire.dmi', + fire_icon, + -HIGHEST_LAYER, + appearance_flags = RESET_COLOR, + ) GLOB.fire_appearances[fire_icon] = new_fire_overlay - if(stacks && on_fire) - if(last_icon_state == fire_icon) - return last_icon_state - add_overlay(GLOB.fire_appearances[fire_icon]) - return fire_icon - - if(!last_icon_state) - return last_icon_state - - cut_overlay(GLOB.fire_appearances[fire_icon]) - return null + return GLOB.fire_appearances[fire_icon] /// Draw power from the robot /mob/living/silicon/robot/proc/draw_power(power_to_draw) diff --git a/tgstation.dme b/tgstation.dme index d14bab6c226f4..0c076bc191d09 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -1444,6 +1444,7 @@ #include "code\datums\elements\openspace_item_click_handler.dm" #include "code\datums\elements\ore_collecting.dm" #include "code\datums\elements\organ_set_bonus.dm" +#include "code\datums\elements\permanent_fire_overlay.dm" #include "code\datums\elements\pet_bonus.dm" #include "code\datums\elements\plant_backfire.dm" #include "code\datums\elements\point_of_interest.dm" From addb9e0a3cb415f1a68bbe3602bb7c143004b19a Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Fri, 9 Feb 2024 18:26:16 -0600 Subject: [PATCH 052/508] Nerfs probability that a rat decides to bite a cable (#81364) ## About The Pull Request Rats are 5x less likely to decide to bite a cable ## Why It's Good For The Game Way back when I converted rats to basic mobs, *something* went wrong and rats bite cables wayyyy too often now - it's not uncommon to see a rat has de-cabled an entire section of maint due to some good luck. Funny but not how it functioned originally. I always intended to tone it back down and just never got around to it. ## Changelog :cl: Melbert balance: Rats are now 5x less likely to decide to eat a cable when idling. (1%, down from 5%) /:cl: --- code/datums/ai/hunting_behavior/hunting_mouse.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/datums/ai/hunting_behavior/hunting_mouse.dm b/code/datums/ai/hunting_behavior/hunting_mouse.dm index d4160f826dd6b..d0e7161fd2de6 100644 --- a/code/datums/ai/hunting_behavior/hunting_mouse.dm +++ b/code/datums/ai/hunting_behavior/hunting_mouse.dm @@ -11,7 +11,7 @@ finding_behavior = /datum/ai_behavior/find_hunt_target/mouse_cable hunt_targets = list(/obj/structure/cable) hunt_range = 0 // Only look below us - hunt_chance = 5 + hunt_chance = 1 // When looking for a cable, we can only bite things we can reach. /datum/ai_behavior/find_hunt_target/mouse_cable From 657f0d2ff0e9456f56a17ea5b01278f0dfbb71c2 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 13:26:18 +1300 Subject: [PATCH 053/508] Automatic changelog for PR #81367 [ci skip] --- html/changelogs/AutoChangeLog-pr-81367.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81367.yml diff --git a/html/changelogs/AutoChangeLog-pr-81367.yml b/html/changelogs/AutoChangeLog-pr-81367.yml new file mode 100644 index 0000000000000..336fefb2e7497 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81367.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - refactor: "Fire effects get added to mobs in a different way now. Maybe it will get stuck less. Report any oddities." \ No newline at end of file From 708cec1e5166ba4348573691f9b283b9ec36403d Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 13:26:39 +1300 Subject: [PATCH 054/508] Automatic changelog for PR #81364 [ci skip] --- html/changelogs/AutoChangeLog-pr-81364.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81364.yml diff --git a/html/changelogs/AutoChangeLog-pr-81364.yml b/html/changelogs/AutoChangeLog-pr-81364.yml new file mode 100644 index 0000000000000..aa8ad537b2ed6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81364.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - balance: "Rats are now 5x less likely to decide to eat a cable when idling. (1%, down from 5%)" \ No newline at end of file From 18a5b5011f7cb1ab9a8eeac2fc1832338db5fd4c Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:15:41 -0500 Subject: [PATCH 055/508] Cucumber Lemonade now has a price (#81368) ## About The Pull Request One of the bar restaurant bots asks for cucumber lemonade but it has no price attached to it, so it takes this drink (that requires help from botany) and gives nothing in exchange, this fixes that. ## Why It's Good For The Game bug fix ## Changelog :cl: fix: Bar bots asking for Cucumber Lemonade now gives you money for completing it. /:cl: --- .../modules/reagents/chemistry/reagents/drinks/drink_reagents.dm | 1 + 1 file changed, 1 insertion(+) diff --git a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm index e76e0e8fbb5c5..5365cea34841f 100644 --- a/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/drinks/drink_reagents.dm @@ -1170,6 +1170,7 @@ quality = DRINK_GOOD taste_description = "citrus soda with cucumber" chemical_flags = REAGENT_CAN_BE_SYNTHESIZED + glass_price = DRINK_PRICE_HIGH /datum/reagent/consumable/cucumberlemonade/on_mob_life(mob/living/carbon/doll, seconds_per_tick, times_fired) . = ..() From 4bb50476b86d593e743a2fd77412134cc7200dc0 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:16:00 +1300 Subject: [PATCH 056/508] Automatic changelog for PR #81368 [ci skip] --- html/changelogs/AutoChangeLog-pr-81368.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81368.yml diff --git a/html/changelogs/AutoChangeLog-pr-81368.yml b/html/changelogs/AutoChangeLog-pr-81368.yml new file mode 100644 index 0000000000000..e4e5edda82dc5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81368.yml @@ -0,0 +1,4 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - bugfix: "Bar bots asking for Cucumber Lemonade now gives you money for completing it." \ No newline at end of file From 3e325829ab96f966e4cd2b41a8c1597719a7a057 Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Sat, 10 Feb 2024 00:17:29 -0500 Subject: [PATCH 057/508] Space dragon no longer turns the entire roundend report bold (#81370) ## About The Pull Request Fixes the entire roundend report turning bold if there was a space dragon with carp. ## Why It's Good For The Game yet another roundend report issue fixed. ## Changelog :cl: fix: Space Dragon's carp allies no longer turn the entire roundend report into bold. /:cl: --- code/modules/antagonists/space_dragon/space_dragon.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm index 4b385b70e596c..25543fbc8fe86 100644 --- a/code/modules/antagonists/space_dragon/space_dragon.dm +++ b/code/modules/antagonists/space_dragon/space_dragon.dm @@ -263,7 +263,7 @@ players_to_carp_taken[carpy.key] += 1 var/list = "" for(var/carp_user in players_to_carp_taken) - list += "
  1. [carp_user], who played [players_to_carp_taken[carp_user]] space carps.
  2. " + list += "
  3. [carp_user], who played [players_to_carp_taken[carp_user]] space carps.
  4. " parts += list parts += "" From a0092ac0e33dd2c42df6957b6f4be448a70fc0f4 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:19:47 +1300 Subject: [PATCH 058/508] Automatic changelog for PR #81370 [ci skip] --- html/changelogs/AutoChangeLog-pr-81370.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81370.yml diff --git a/html/changelogs/AutoChangeLog-pr-81370.yml b/html/changelogs/AutoChangeLog-pr-81370.yml new file mode 100644 index 0000000000000..62147dfb7e88c --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81370.yml @@ -0,0 +1,4 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - bugfix: "Space Dragon's carp allies no longer turn the entire roundend report into bold." \ No newline at end of file From 07cb0d9a0caced35a5898ef6dd06d4c76efecab4 Mon Sep 17 00:00:00 2001 From: necromanceranne <40847847+necromanceranne@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:07:18 +1100 Subject: [PATCH 059/508] Fixes the typepath of the shove blocker module. (#81374) ## About The Pull Request The bulwark module had the wrong typepath for most of the descriptive elements and its complexity. So the shove block was a free module. And technically not incompatible with itself. Oh my. Not super relevant for actual play, as there is no access to this module anywhere currently, but who knows. ## Why It's Good For The Game Typepaths. ## Changelog :cl: fix: The shove blocker module parent type now has the correct typepath. /:cl: --- code/modules/mod/modules/modules_security.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index fddec2ef13d6c..0b8c0299f04b9 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -579,7 +579,7 @@ #undef STORMTROOPER_MODE #undef SHARPSHOOTER_MODE -/obj/item/mod/module/anti_stagger +/obj/item/mod/module/shove_blocker name = "MOD bulwark module" desc = "Layers upon layers of shock dampening plates, just to stop you from getting shoved into a wall by an angry mob." icon_state = "bulwark" From 3d59db14267ee1acab584106dbf842df2614ce53 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:07:37 +1300 Subject: [PATCH 060/508] Automatic changelog for PR #81374 [ci skip] --- html/changelogs/AutoChangeLog-pr-81374.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81374.yml diff --git a/html/changelogs/AutoChangeLog-pr-81374.yml b/html/changelogs/AutoChangeLog-pr-81374.yml new file mode 100644 index 0000000000000..718ea78e81c20 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81374.yml @@ -0,0 +1,4 @@ +author: "necromanceranne" +delete-after: True +changes: + - bugfix: "The shove blocker module parent type now has the correct typepath." \ No newline at end of file From 2f93bbdd4c10069f80596cc2dc7295323b8f4bbf Mon Sep 17 00:00:00 2001 From: SyncIt21 <110812394+SyncIt21@users.noreply.github.com> Date: Sat, 10 Feb 2024 21:45:09 +0530 Subject: [PATCH 061/508] Fixes high power consumption for lathes (#81375) ## About The Pull Request This employs a formula that creates a relationship between total stacks of material used & the machines active power consumption - When inserting/ejecting a full stack of materials, lathes use 1% of the machine active power usage. To put it in player terms if your apc has a normal high capacity power cell it will use 2% of power when inserting a full stack(50 sheets) of material or when ejecting a full stack of materials - Fixes #81366. When printing multiple items that would require a full stack of materials (50 sheets or roughly 5000 matter units) It now uses 5% of the machine active power usage. To see the comparision we will see the same examples used in the issue **Old Behaviour** - Printing 10 large beakers for tier 1 lathe would consume 48% of apc cell - Printing 1 circular saw for tier 1 lathe would consume 32% of apc cell **New Behaviour** - Printing 10 large beakers for tier 1 lathe now consumes just 5% of apc cell - Printing 1 circular saw for tier 1 lathe now consumes just 1% of apc cell - Higher tier parts will consume more power to compensate for the lower material costs because the machines active power usage increases with higher tier parts, assuming your apc has a normal high capacity power cell - Printing 10 large beakers for tier 4 lathe now consumes 12% of apc cell - Printing 1 circular saw for tier 4 lathe now consumes 5% of apc cell This formula is experimental and i just made it up so let's see how this plays out ## Changelog :cl: fix: lathes now use moderate power for printing operations /:cl: --- code/game/machinery/autolathe.dm | 5 +++-- code/modules/research/machinery/_production.dm | 13 ++++++++++--- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/code/game/machinery/autolathe.dm b/code/game/machinery/autolathe.dm index f6600942cdeee..ba9667b3e5809 100644 --- a/code/game/machinery/autolathe.dm +++ b/code/game/machinery/autolathe.dm @@ -97,7 +97,8 @@ flick("autolathe_[item_inserted.has_material_type(/datum/material/glass) ? "r" : "o"]", src) - directly_use_power(round((amount_inserted / SHEET_MATERIAL_AMOUNT) * active_power_usage * 0.0025)) + //we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benifit from it + directly_use_power(ROUND_UP((amount_inserted / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * 0.01 * initial(active_power_usage))) /obj/machinery/autolathe/ui_interact(mob/user, datum/tgui/ui) if(!is_operational) @@ -273,7 +274,7 @@ var/charge_per_item = 0 for(var/material in design.materials) charge_per_item += design.materials[material] - charge_per_item = min(active_power_usage, round(charge_per_item * material_cost_coefficient)) + charge_per_item = ROUND_UP((charge_per_item / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * material_cost_coefficient * 0.05 * active_power_usage) var/build_time_per_item = (design.construction_time * design.lathe_time_factor) ** 0.8 //do the printing sequentially diff --git a/code/modules/research/machinery/_production.dm b/code/modules/research/machinery/_production.dm index d71e6244e886f..f95145b4b9406 100644 --- a/code/modules/research/machinery/_production.dm +++ b/code/modules/research/machinery/_production.dm @@ -23,6 +23,7 @@ . = ..() cached_designs = list() + materials = AddComponent( /datum/component/remote_materials, \ mapload, \ @@ -65,7 +66,6 @@ stripe.color = stripe_color . += stripe - /obj/machinery/rnd/production/examine(mob/user) . = ..() if(!in_range(user, src) && !isobserver(user)) @@ -144,7 +144,8 @@ /obj/machinery/rnd/proc/process_item(obj/item/item_inserted, list/mats_consumed, amount_inserted) PRIVATE_PROC(TRUE) - if(directly_use_power(round((amount_inserted / SHEET_MATERIAL_AMOUNT) * active_power_usage * 0.00025))) + //we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benifit from it + if(directly_use_power(ROUND_UP((amount_inserted / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * 0.01 * initial(active_power_usage)))) var/mat_name = "iron" var/highest_mat = 0 @@ -206,6 +207,7 @@ */ /obj/machinery/rnd/production/proc/build_efficiency(path) PRIVATE_PROC(TRUE) + SHOULD_BE_PURE(TRUE) if(ispath(path, /obj/item/stack/sheet) || ispath(path, /obj/item/stack/ore/bluespace_crystal)) return 1 @@ -285,6 +287,11 @@ if(isnull(amount)) return + //we use initial(active_power_usage) because higher tier parts will have higher active usage but we have no benifit from it + if(!directly_use_power(ROUND_UP((amount / MAX_STACK_SIZE) * 0.01 * initial(active_power_usage)))) + say("No power to dispense sheets") + return + materials.eject_sheets(material, amount) return TRUE @@ -330,7 +337,7 @@ var/charge_per_item = 0 for(var/material in design.materials) charge_per_item += design.materials[material] - charge_per_item = min(active_power_usage, round(charge_per_item * coefficient)) + charge_per_item = ROUND_UP((charge_per_item / (MAX_STACK_SIZE * SHEET_MATERIAL_AMOUNT)) * coefficient * 0.05 * active_power_usage) var/build_time_per_item = (design.construction_time * design.lathe_time_factor) ** 0.8 //start production From f37ddd575f47e7e85155216c199e1128df577c62 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:15:28 +1300 Subject: [PATCH 062/508] Automatic changelog for PR #81375 [ci skip] --- html/changelogs/AutoChangeLog-pr-81375.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81375.yml diff --git a/html/changelogs/AutoChangeLog-pr-81375.yml b/html/changelogs/AutoChangeLog-pr-81375.yml new file mode 100644 index 0000000000000..a45fe58737d5d --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81375.yml @@ -0,0 +1,4 @@ +author: "SyncIt21" +delete-after: True +changes: + - bugfix: "lathes now use moderate power for printing operations" \ No newline at end of file From 33d59877392a9a832a19c6336db3a4a6aaed5292 Mon Sep 17 00:00:00 2001 From: Rhials <28870487+Rhials@users.noreply.github.com> Date: Sat, 10 Feb 2024 11:29:26 -0500 Subject: [PATCH 063/508] [NO GBP] Fixes raw ectoplasmic anomaly refining (#81377) ## About The Pull Request This adds a proper cap for raw ectoplasmic cores, so they can actually be refined now. Cool! ## Why It's Good For The Game Broken thing need fix oops argh. Closes #81369. ## Changelog :cl: Rhials fix: You can now refine ectoplasmic raw cores at the implosion machine thing. /:cl: --- code/__DEFINES/research/anomalies.dm | 1 + code/controllers/subsystem/research.dm | 1 + 2 files changed, 2 insertions(+) diff --git a/code/__DEFINES/research/anomalies.dm b/code/__DEFINES/research/anomalies.dm index 12a114439c7d9..707b7bd7a02e1 100644 --- a/code/__DEFINES/research/anomalies.dm +++ b/code/__DEFINES/research/anomalies.dm @@ -7,6 +7,7 @@ #define MAX_CORES_HALLUCINATION 8 #define MAX_CORES_BIOSCRAMBLER 8 #define MAX_CORES_DIMENSIONAL 8 +#define MAX_CORES_ECTOPLASMIC 8 ///Defines for the different types of explosion a flux anomaly can have #define FLUX_NO_EXPLOSION 0 diff --git a/code/controllers/subsystem/research.dm b/code/controllers/subsystem/research.dm index d0ebe00089380..612c599c0f62c 100644 --- a/code/controllers/subsystem/research.dm +++ b/code/controllers/subsystem/research.dm @@ -62,6 +62,7 @@ SUBSYSTEM_DEF(research) /obj/item/assembly/signaler/anomaly/hallucination = MAX_CORES_HALLUCINATION, /obj/item/assembly/signaler/anomaly/bioscrambler = MAX_CORES_BIOSCRAMBLER, /obj/item/assembly/signaler/anomaly/dimensional = MAX_CORES_DIMENSIONAL, + /obj/item/assembly/signaler/anomaly/ectoplasm = MAX_CORES_ECTOPLASMIC, ) /// Lookup list for ordnance briefers. From d850977c7cb1ceeabfc7568beb211fb2185e33cd Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 05:29:46 +1300 Subject: [PATCH 064/508] Automatic changelog for PR #81377 [ci skip] --- html/changelogs/AutoChangeLog-pr-81377.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81377.yml diff --git a/html/changelogs/AutoChangeLog-pr-81377.yml b/html/changelogs/AutoChangeLog-pr-81377.yml new file mode 100644 index 0000000000000..12be442e6a62f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81377.yml @@ -0,0 +1,4 @@ +author: "Rhials" +delete-after: True +changes: + - bugfix: "You can now refine ectoplasmic raw cores at the implosion machine thing." \ No newline at end of file From fdcd4d39799552a406765d209cf0dc99f97ae5dc Mon Sep 17 00:00:00 2001 From: John Willard <53777086+JohnFulpWillard@users.noreply.github.com> Date: Sat, 10 Feb 2024 12:57:19 -0500 Subject: [PATCH 065/508] Adds a signal to buying items from the uplink (& fixes TC misinfo) (#81372) ## About The Pull Request Adds a signal when someone buys an item from the uplink and removes single-letter vars from the ``spawn_item`` proc, and adds/standardizes add/removing of telecrystals from uplinks (and admin setting how much TC they have) to ensure the UI always has the right amount of telecrystals displayed in it. ## Why It's Good For The Game There are reasons why someone would want to hook up to a traitor's uplink and listen to items they purchase to do any special effect on-purchase, so this adds support to do anything in the future with it. Also tells players how much TC they actually have without forcing them to close/reopen the UI every time they insert some TC in it by hand. ## Changelog :cl: fix: Uplinks now update their UI when you add telecrystals in them, so you don't need to close and reopen it. /:cl: --- code/__DEFINES/dcs/signals/uplink.dm | 2 + code/datums/components/uplink.dm | 11 +---- code/datums/elements/uplink_reimburse.dm | 7 ++-- code/datums/mind/_mind.dm | 11 +++-- code/game/objects/items/stacks/telecrystal.dm | 2 +- .../nukeop/equipment/nuclear_challenge.dm | 2 +- code/modules/antagonists/nukeop/nukeop.dm | 4 +- .../antagonists/traitor/traitor_objective.dm | 4 +- .../antagonists/traitor/uplink_handler.dm | 9 ++++ .../living/basic/drone/extra_drone_types.dm | 2 +- .../computers/item/disks/virus_disk.dm | 2 +- code/modules/uplink/uplink_items.dm | 42 ++++++++++++------- tgstation.dme | 1 + 13 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 code/__DEFINES/dcs/signals/uplink.dm diff --git a/code/__DEFINES/dcs/signals/uplink.dm b/code/__DEFINES/dcs/signals/uplink.dm new file mode 100644 index 0000000000000..1daa4f312705c --- /dev/null +++ b/code/__DEFINES/dcs/signals/uplink.dm @@ -0,0 +1,2 @@ +///Signal sent to a mob when they purchase an item from their uplink: (datum/uplink_handler/uplink_handler_source, atom/spawned_item, mob/user) +#define COMSIG_ON_UPLINK_PURCHASE "comsig_on_uplink_purchase" diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm index e418988c05806..e4e6e611ebca1 100644 --- a/code/datums/components/uplink.dm +++ b/code/datums/components/uplink.dm @@ -113,15 +113,6 @@ new /obj/effect/decal/cleanable/ash(get_turf(uplink_item)) qdel(uplink_item) -/// Adds telecrystals to the uplink. It is bad practice to use this outside of the component itself. -/datum/component/uplink/proc/add_telecrystals(telecrystals_added) - set_telecrystals(uplink_handler.telecrystals + telecrystals_added) - -/// Sets the telecrystals of the uplink. It is bad practice to use this outside of the component itself. -/datum/component/uplink/proc/set_telecrystals(new_telecrystal_amount) - uplink_handler.telecrystals = new_telecrystal_amount - uplink_handler.on_update() - /datum/component/uplink/InheritComponent(datum/component/uplink/uplink) lockable |= uplink.lockable active |= uplink.active @@ -135,7 +126,7 @@ if(!silent) to_chat(user, span_notice("You slot [telecrystals] into [parent] and charge its internal uplink.")) var/amt = telecrystals.amount - uplink_handler.telecrystals += amt + uplink_handler.add_telecrystals(amt) telecrystals.use(amt) log_uplink("[key_name(user)] loaded [amt] telecrystals into [parent]'s uplink") diff --git a/code/datums/elements/uplink_reimburse.dm b/code/datums/elements/uplink_reimburse.dm index 3ff182ec2314d..73a2032fee1df 100644 --- a/code/datums/elements/uplink_reimburse.dm +++ b/code/datums/elements/uplink_reimburse.dm @@ -22,7 +22,7 @@ RegisterSignal(target, COMSIG_ATOM_EXAMINE, PROC_REF(on_examine)) RegisterSignal(target, COMSIG_ITEM_ATTEMPT_TC_REIMBURSE, PROC_REF(reimburse)) RegisterSignal(target,COMSIG_TRAITOR_ITEM_USED(target.type), PROC_REF(used)) - + /datum/element/uplink_reimburse/Detach(datum/target) UnregisterSignal(target, list(COMSIG_ATOM_EXAMINE, COMSIG_TRAITOR_ITEM_USED(target.type), COMSIG_ITEM_ATTEMPT_TC_REIMBURSE)) @@ -47,10 +47,11 @@ to_chat(user, span_notice("You tap [uplink_comp.uplink_handler] with [refund_item], and a moment after [refund_item] disappears in a puff of red smoke!")) do_sparks(2, source = uplink_comp.uplink_handler) - uplink_comp.add_telecrystals(refundable_tc) + uplink_comp.uplink_handler.add_telecrystals(refundable_tc) qdel(refund_item) + /// If the item is used, it needs to no longer be refundable /datum/element/uplink_reimburse/proc/used(datum/target) SIGNAL_HANDLER - + Detach(target) diff --git a/code/datums/mind/_mind.dm b/code/datums/mind/_mind.dm index 84942a9684c3c..6bfdd6070c746 100644 --- a/code/datums/mind/_mind.dm +++ b/code/datums/mind/_mind.dm @@ -457,9 +457,14 @@ if(check_rights(R_FUN)) var/datum/component/uplink/U = find_syndicate_uplink() if(U) - var/crystals = input("Amount of telecrystals for [key]","Syndicate uplink", U.uplink_handler.telecrystals) as null | num - if(!isnull(crystals)) - U.uplink_handler.telecrystals = crystals + var/crystals = tgui_input_number( + user = usr, + message = "Amount of telecrystals for [key]", + title = "Syndicate uplink", + default = U.uplink_handler.telecrystals, + ) + if(crystals && isnum(crystals)) + U.uplink_handler.set_telecrystals(crystals) message_admins("[key_name_admin(usr)] changed [current]'s telecrystal count to [crystals].") log_admin("[key_name(usr)] changed [current]'s telecrystal count to [crystals].") if("progression") diff --git a/code/game/objects/items/stacks/telecrystal.dm b/code/game/objects/items/stacks/telecrystal.dm index 51d21d6ef6cc3..a6bbe3bfe19d8 100644 --- a/code/game/objects/items/stacks/telecrystal.dm +++ b/code/game/objects/items/stacks/telecrystal.dm @@ -21,7 +21,7 @@ var/datum/component/uplink/hidden_uplink = uplink.GetComponent(/datum/component/uplink) if(hidden_uplink) - hidden_uplink.add_telecrystals(amount) + hidden_uplink.uplink_handler.add_telecrystals(amount) use(amount) to_chat(user, span_notice("You press [src] onto yourself and charge your hidden uplink.")) return ITEM_INTERACT_SUCCESS diff --git a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm index 734025e9a370e..a7611c2444821 100644 --- a/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm +++ b/code/modules/antagonists/nukeop/equipment/nuclear_challenge.dm @@ -118,7 +118,7 @@ GLOBAL_LIST_EMPTY(jam_on_wardec) var/tc_per_nukie = round(tc_to_distribute / (length(orphans)+length(uplinks))) for (var/datum/component/uplink/uplink in uplinks) - uplink.add_telecrystals(tc_per_nukie) + uplink.uplink_handler.add_telecrystals(tc_per_nukie) tc_to_distribute -= tc_per_nukie for (var/mob/living/L in orphans) diff --git a/code/modules/antagonists/nukeop/nukeop.dm b/code/modules/antagonists/nukeop/nukeop.dm index cf7d90bccf6c3..6278d5ddaea91 100644 --- a/code/modules/antagonists/nukeop/nukeop.dm +++ b/code/modules/antagonists/nukeop/nukeop.dm @@ -63,7 +63,7 @@ var/extra_tc = CEILING(GLOB.joined_player_list.len/5, 5) var/datum/component/uplink/uplink = owner.find_syndicate_uplink() if (uplink) - uplink.add_telecrystals(extra_tc) + uplink.uplink_handler.add_telecrystals(extra_tc) var/datum/component/uplink/uplink = owner.find_syndicate_uplink() if(uplink) @@ -617,7 +617,7 @@ nukie.mind.add_antag_datum(antag_datum, src) var/datum/component/uplink/uplink = nukie.mind.find_syndicate_uplink() - uplink?.set_telecrystals(tc_to_spawn) + uplink?.uplink_handler.set_telecrystals(tc_to_spawn) // add some pizzazz do_sparks(4, FALSE, spawn_loc) diff --git a/code/modules/antagonists/traitor/traitor_objective.dm b/code/modules/antagonists/traitor/traitor_objective.dm index d60820c3fceeb..3e13340157334 100644 --- a/code/modules/antagonists/traitor/traitor_objective.dm +++ b/code/modules/antagonists/traitor/traitor_objective.dm @@ -191,7 +191,7 @@ handle_cleanup() log_traitor("[key_name(handler.owner)] [objective_state == OBJECTIVE_STATE_INACTIVE? "missed" : "failed"] [to_debug_string()]") if(penalty_cost) - handler.telecrystals -= penalty_cost + handler.add_telecrystals(-penalty_cost) objective_state = OBJECTIVE_STATE_FAILED else objective_state = OBJECTIVE_STATE_INVALID @@ -227,7 +227,7 @@ /// Called when rewards should be given to the user. /datum/traitor_objective/proc/completion_payout() handler.progression_points += progression_reward - handler.telecrystals += telecrystal_reward + handler.add_telecrystals(telecrystal_reward) /// Used for sending data to the uplink UI /datum/traitor_objective/proc/uplink_ui_data(mob/user) diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm index aa26b360a7561..f78ddb0247892 100644 --- a/code/modules/antagonists/traitor/uplink_handler.dm +++ b/code/modules/antagonists/traitor/uplink_handler.dm @@ -254,3 +254,12 @@ return to_act_on.ui_perform_action(user, action) + +///Helper to add telecrystals to the uplink handler, calling set_telecrystals. +/datum/uplink_handler/proc/add_telecrystals(amount) + set_telecrystals(telecrystals + amount) + +///Sets how many telecrystals the uplink handler has, then updates the UI for any players watching. +/datum/uplink_handler/proc/set_telecrystals(amount) + telecrystals = amount + on_update() diff --git a/code/modules/mob/living/basic/drone/extra_drone_types.dm b/code/modules/mob/living/basic/drone/extra_drone_types.dm index 927d28f0ca249..08c9278b75331 100644 --- a/code/modules/mob/living/basic/drone/extra_drone_types.dm +++ b/code/modules/mob/living/basic/drone/extra_drone_types.dm @@ -33,7 +33,7 @@ /mob/living/basic/drone/syndrone/Initialize(mapload) . = ..() var/datum/component/uplink/hidden_uplink = internal_storage.GetComponent(/datum/component/uplink) - hidden_uplink.set_telecrystals(telecrystal_count) + hidden_uplink.uplink_handler.set_telecrystals(telecrystal_count) /obj/effect/mob_spawn/ghost_role/drone/syndrone name = "syndrone shell" diff --git a/code/modules/modular_computers/computers/item/disks/virus_disk.dm b/code/modules/modular_computers/computers/item/disks/virus_disk.dm index e3eac7736f504..3f646b22d8a06 100644 --- a/code/modules/modular_computers/computers/item/disks/virus_disk.dm +++ b/code/modules/modular_computers/computers/item/disks/virus_disk.dm @@ -154,7 +154,7 @@ hidden_uplink.uplink_handler.generate_objectives() SStraitor.register_uplink_handler(hidden_uplink.uplink_handler) else - hidden_uplink.add_telecrystals(telecrystals) + hidden_uplink.uplink_handler.add_telecrystals(telecrystals) telecrystals = 0 hidden_uplink.locked = FALSE hidden_uplink.active = TRUE diff --git a/code/modules/uplink/uplink_items.dm b/code/modules/uplink/uplink_items.dm index 32783504fe871..65935f077e33d 100644 --- a/code/modules/uplink/uplink_items.dm +++ b/code/modules/uplink/uplink_items.dm @@ -1,4 +1,3 @@ - // TODO: Work into reworked uplinks. /// Selects a set number of unique items from the uplink, and deducts a percentage discount from them /proc/create_uplink_sales(num, datum/uplink_category/category, limited_stock, list/sale_items) @@ -8,7 +7,20 @@ var/datum/uplink_item/taken_item = pick_n_take(sale_items_copy) var/datum/uplink_item/uplink_item = new taken_item.type() var/discount = uplink_item.get_discount() - var/list/disclaimer = list("Void where prohibited.", "Not recommended for children.", "Contains small parts.", "Check local laws for legality in region.", "Do not taunt.", "Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.", "Keep away from fire or flames.", "Product is provided \"as is\" without any implied or expressed warranties.", "As seen on TV.", "For recreational use only.", "Use only as directed.", "16% sales tax will be charged for orders originating within Space Nebraska.") + var/static/list/disclaimer = list( + "Void where prohibited.", + "Not recommended for children.", + "Contains small parts.", + "Check local laws for legality in region.", + "Do not taunt.", + "Not responsible for direct, indirect, incidental or consequential damages resulting from any defect, error or failure to perform.", + "Keep away from fire or flames.", + "Product is provided \"as is\" without any implied or expressed warranties.", + "As seen on TV.", + "For recreational use only.", + "Use only as directed.", + "16% sales tax will be charged for orders originating within Space Nebraska.", + ) uplink_item.limited_stock = limited_stock if(uplink_item.cost >= 20) //Tough love for nuke ops discount *= 0.5 @@ -111,10 +123,10 @@ /// Spawns an item and logs its purchase /datum/uplink_item/proc/purchase(mob/user, datum/uplink_handler/uplink_handler, atom/movable/source) - var/atom/A = spawn_item(item, user, uplink_handler, source) + var/atom/spawned_item = spawn_item(item, user, uplink_handler, source) log_uplink("[key_name(user)] purchased [src] for [cost] telecrystals from [source]'s uplink") if(purchase_log_vis && uplink_handler.purchase_log) - uplink_handler.purchase_log.LogPurchase(A, src, cost) + uplink_handler.purchase_log.LogPurchase(spawned_item, src, cost) if(lock_other_purchases) uplink_handler.shop_locked = TRUE @@ -122,20 +134,20 @@ /datum/uplink_item/proc/spawn_item(spawn_path, mob/user, datum/uplink_handler/uplink_handler, atom/movable/source) if(!spawn_path) return - var/atom/A + var/atom/spawned_item if(ispath(spawn_path)) - A = new spawn_path(get_turf(user)) + spawned_item = new spawn_path(get_turf(user)) else - A = spawn_path + spawned_item = spawn_path if(refundable) - A.AddElement(/datum/element/uplink_reimburse, (refund_amount ? refund_amount : cost)) - if(ishuman(user) && isitem(A)) - var/mob/living/carbon/human/H = user - if(H.put_in_hands(A)) - to_chat(H, span_boldnotice("[A] materializes into your hands!")) - return A - to_chat(user, span_boldnotice("[A] materializes onto the floor!")) - return A + spawned_item.AddElement(/datum/element/uplink_reimburse, (refund_amount ? refund_amount : cost)) + var/mob/living/carbon/human/human_user = user + if(istype(human_user) && isitem(spawned_item) && human_user.put_in_hands(spawned_item)) + to_chat(human_user, span_boldnotice("[spawned_item] materializes into your hands!")) + else + to_chat(user, span_boldnotice("[spawned_item] materializes onto the floor!")) + SEND_SIGNAL(uplink_handler, COMSIG_ON_UPLINK_PURCHASE, spawned_item, user) + return spawned_item ///For special overrides if an item can be bought or not. /datum/uplink_item/proc/can_be_bought(datum/uplink_handler/source) diff --git a/tgstation.dme b/tgstation.dme index 0c076bc191d09..29c504f216880 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -357,6 +357,7 @@ #include "code\__DEFINES\dcs\signals\signals_wash.dm" #include "code\__DEFINES\dcs\signals\signals_wizard.dm" #include "code\__DEFINES\dcs\signals\signals_xeno_control.dm" +#include "code\__DEFINES\dcs\signals\uplink.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_attack.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_explosion.dm" #include "code\__DEFINES\dcs\signals\signals_atom\signals_atom_lighting.dm" From 4f4b33f9ce3750639e2dd80165c4016777bc013d Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 06:57:41 +1300 Subject: [PATCH 066/508] Automatic changelog for PR #81372 [ci skip] --- html/changelogs/AutoChangeLog-pr-81372.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81372.yml diff --git a/html/changelogs/AutoChangeLog-pr-81372.yml b/html/changelogs/AutoChangeLog-pr-81372.yml new file mode 100644 index 0000000000000..efbf7ba1c6739 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81372.yml @@ -0,0 +1,4 @@ +author: "JohnFulpWillard" +delete-after: True +changes: + - bugfix: "Uplinks now update their UI when you add telecrystals in them, so you don't need to close and reopen it." \ No newline at end of file From dc13f71b2f689e89fd0f47e991a5d94595b38cf6 Mon Sep 17 00:00:00 2001 From: Rhials <28870487+Rhials@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:35:03 -0500 Subject: [PATCH 067/508] Moves teleblocker/beacon implants to the techweb, new research node. Exile implants can now be printed (#81230) ## About The Pull Request This adjusts some of the techweb stuff related to security implants. I meant to do this in the original PR but got LAZY because I wanted to push it out the door, and then the feature freeze happened. Teleport Blocker and Beacon implants have been moved from cargo to the departmental lathe, printable at (where else?) security. **They can no longer be purchased from cargo.** They are behind a new research node, which requires Subdermal Implants and Miniature bluespace research. This node costs 2500 points. Exile implants can now be printed from the security lathe. Security Implants now have their own lathe category. This also slightly adjusts the descriptions for the implant case designs to reflect their contents. ## Why It's Good For The Game First and foremost -- I really had meant to do this in the original PR. Throwing these implants into cargo was intended to gate access to them until later in the round. In hindsight, cargo doesn't really accomplish that in the way I'd hoped. It's still available roundstart, and no price will change that. Having these be handled by science is a much more sound idea. (Also security already has enough to be ordering from cargo, and not nearly enough reasons to be yelling at science!) Exile implants should be easier to access, especially for how little impact they actually have. The simple convenience may be the difference between a peaceful resolution or being beaten to death in the back of the brig. Adjustments to the lathe categories, descriptions are for slightly better UX. --- .../__DEFINES/research/research_categories.dm | 1 + code/modules/cargo/packs/security.dm | 14 ------ .../research/designs/medical_designs.dm | 48 ++++++++++++++++--- code/modules/research/techweb/all_nodes.dm | 12 +++++ 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/code/__DEFINES/research/research_categories.dm b/code/__DEFINES/research/research_categories.dm index 65de3edca532e..3f6428eb73178 100644 --- a/code/__DEFINES/research/research_categories.dm +++ b/code/__DEFINES/research/research_categories.dm @@ -187,6 +187,7 @@ #define RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_TOOLS "/Cybernetic Implanting Tools" #define RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_UTILITY "/Cybernetic Utility Implants" #define RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_MISC "/Cybernetic Miscellaneous Implants" +#define RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY "/Cybernetic Security Implants" #define RND_SUBCATEGORY_CYBERNETICS_ADVANCED_LIMBS "/Cybernetic Advanced Limbs" // Limb Categories diff --git a/code/modules/cargo/packs/security.dm b/code/modules/cargo/packs/security.dm index eacd24fdbd016..0b0073258298b 100644 --- a/code/modules/cargo/packs/security.dm +++ b/code/modules/cargo/packs/security.dm @@ -342,17 +342,3 @@ access_view = ACCESS_SECURITY contains = list(/obj/item/clothing/glasses/sunglasses = 1) crate_name = "sunglasses crate" - -/datum/supply_pack/security/armory/beacon_imp - name = "Beacon Implants Crate" - desc = "Contains five Beacon implants." - cost = CARGO_CRATE_VALUE * 5.5 - contains = list(/obj/item/storage/box/beaconimp) - crate_name = "beacon implant crate" - -/datum/supply_pack/security/armory/teleport_blocker_imp - name = "Bluespace Grounding Implants Crate" - desc = "Contains five Bluespace Grounding implants." - cost = CARGO_CRATE_VALUE * 7 - contains = list(/obj/item/storage/box/teleport_blocker) - crate_name = "bluespace grounding implant crate" diff --git a/code/modules/research/designs/medical_designs.dm b/code/modules/research/designs/medical_designs.dm index 0b09edf44ac94..46e43b0ac6661 100644 --- a/code/modules/research/designs/medical_designs.dm +++ b/code/modules/research/designs/medical_designs.dm @@ -679,28 +679,64 @@ /datum/design/implant_chem name = "Chemical Implant Case" - desc = "A glass case containing an implant." + desc = "A glass case containing a chemical implant." id = "implant_chem" build_type = PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/glass = SMALL_MATERIAL_AMOUNT*7) + materials = list(/datum/material/glass = SMALL_MATERIAL_AMOUNT * 7) build_path = /obj/item/implantcase/chem category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_MISC + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY ) departmental_flags = DEPARTMENT_BITFLAG_SECURITY | DEPARTMENT_BITFLAG_MEDICAL /datum/design/implant_tracking name = "Tracking Implant Case" - desc = "A glass case containing an implant." + desc = "A glass case containing a tracking implant." id = "implant_tracking" build_type = PROTOLATHE | AWAY_LATHE - materials = list(/datum/material/iron =SMALL_MATERIAL_AMOUNT*5, /datum/material/glass =SMALL_MATERIAL_AMOUNT*5) + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5) build_path = /obj/item/implantcase/tracking category = list( - RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_MISC + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY ) departmental_flags = DEPARTMENT_BITFLAG_SECURITY | DEPARTMENT_BITFLAG_MEDICAL +/datum/design/implant_beacon + name = "Beacon Implant Case" + desc = "A glass case containing a beacon implant." + id = "implant_beacon" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SMALL_MATERIAL_AMOUNT * 3) + build_path = /obj/item/implantcase/beacon + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY + ) + departmental_flags = DEPARTMENT_BITFLAG_SECURITY + +/datum/design/implant_bluespace + name = "Bluespace Grounding Implant Case" + desc = "A glass case containing a teleport blocker implant." + id = "implant_bluespace" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5, /datum/material/bluespace = SMALL_MATERIAL_AMOUNT * 3) + build_path = /obj/item/implantcase/teleport_blocker + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY + ) + departmental_flags = DEPARTMENT_BITFLAG_SECURITY + +/datum/design/implant_exile + name = "Exile Implant Case" + desc = "A glass case containing an exile implant." + id = "implant_exile" + build_type = PROTOLATHE | AWAY_LATHE + materials = list(/datum/material/iron = SMALL_MATERIAL_AMOUNT * 5, /datum/material/glass = SMALL_MATERIAL_AMOUNT * 5, /datum/material/titanium = SMALL_MATERIAL_AMOUNT * 3) + build_path = /obj/item/implantcase/exile + category = list( + RND_CATEGORY_CYBERNETICS + RND_SUBCATEGORY_CYBERNETICS_IMPLANTS_SECURITY + ) + departmental_flags = DEPARTMENT_BITFLAG_SECURITY + //Cybernetic organs /datum/design/cybernetic_liver diff --git a/code/modules/research/techweb/all_nodes.dm b/code/modules/research/techweb/all_nodes.dm index 6cd0688eae6c9..32d834a19baef 100644 --- a/code/modules/research/techweb/all_nodes.dm +++ b/code/modules/research/techweb/all_nodes.dm @@ -1340,12 +1340,24 @@ "c38_trac", "implant_chem", "implant_tracking", + "implant_exile", "implantcase", "implanter", "locator", ) research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) +/datum/techweb_node/advanced_implants + id = "adv_subdermal_implants" + display_name = "Advanced Subdermal Implants" + description = "Subdermal implants that leverage bluespace research to control their bluespace signature." + prereq_ids = list("subdermal_implants", "micro_bluespace") + design_ids = list( + "implant_beacon", + "implant_bluespace", + ) + research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 2500) + /datum/techweb_node/cyber_organs id = "cyber_organs" display_name = "Cybernetic Organs" From e4f0c2a974c56ae34843cb8753f69d25d67dae89 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 09:35:22 +1300 Subject: [PATCH 068/508] Automatic changelog for PR #81230 [ci skip] --- html/changelogs/AutoChangeLog-pr-81230.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81230.yml diff --git a/html/changelogs/AutoChangeLog-pr-81230.yml b/html/changelogs/AutoChangeLog-pr-81230.yml new file mode 100644 index 0000000000000..4802131f8e35e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81230.yml @@ -0,0 +1,6 @@ +author: "Rhials" +delete-after: True +changes: + - balance: "Beacon and Teleport Blocker implants have been moved from cargo to the security lathe." + - balance: "A new research node has been created, requiring Subdermal Implants and Miniature Bluespace, to unlock Beacon/Teleport Blocker implants." + - balance: "Exile implants can now be printed from the security lathe after researching basic cybernetic implants." \ No newline at end of file From afb29836a7e2727044819cb3b0a85150e7e55609 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 10 Feb 2024 15:39:01 -0600 Subject: [PATCH 069/508] Fix Flaky Failure From Fire (#81399) ## About The Pull Request Closes #81396 , Closes #81391 , Closes #81403, Closes #81402 I don't know why but I thought this proc was only called once, when the mob entered the turf. That was silly. And going back at it, I'm not entirely sure why I tied `TRAIT_NO_EXTINGUISH` to the element anyways, rather than the lava like it originally was. While going back over this, I cleaned up the proc a bit. ## Changelog Not necessary --- .../datums/elements/permanent_fire_overlay.dm | 2 -- code/game/turfs/open/lava.dm | 26 ++++++++++++------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/code/datums/elements/permanent_fire_overlay.dm b/code/datums/elements/permanent_fire_overlay.dm index e4a61852aed74..514d0f121a457 100644 --- a/code/datums/elements/permanent_fire_overlay.dm +++ b/code/datums/elements/permanent_fire_overlay.dm @@ -8,12 +8,10 @@ RegisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS, PROC_REF(add_fire_overlay)) target.update_appearance(UPDATE_OVERLAYS) - ADD_TRAIT(target, TRAIT_NO_EXTINGUISH, ELEMENT_TRAIT(type)) /datum/element/perma_fire_overlay/Detach(atom/target) . = ..() UnregisterSignal(target, COMSIG_ATOM_UPDATE_OVERLAYS) - REMOVE_TRAIT(target, TRAIT_NO_EXTINGUISH, ELEMENT_TRAIT(type)) target.update_appearance(UPDATE_OVERLAYS) /datum/element/perma_fire_overlay/proc/add_fire_overlay(mob/living/source, list/overlays) diff --git a/code/game/turfs/open/lava.dm b/code/game/turfs/open/lava.dm index 30bed8fc87c1c..8f9e7b44aa600 100644 --- a/code/game/turfs/open/lava.dm +++ b/code/game/turfs/open/lava.dm @@ -53,6 +53,7 @@ /turf/open/lava/Destroy() for(var/mob/living/leaving_mob in contents) leaving_mob.RemoveElement(/datum/element/perma_fire_overlay) + REMOVE_TRAIT(leaving_mob, TRAIT_NO_EXTINGUISH, TURF_TRAIT) return ..() /turf/open/lava/update_overlays() @@ -145,6 +146,7 @@ . = ..() if(isliving(gone) && !islava(gone.loc)) gone.RemoveElement(/datum/element/perma_fire_overlay) + REMOVE_TRAIT(gone, TRAIT_NO_EXTINGUISH, TURF_TRAIT) /turf/open/lava/hitby(atom/movable/AM, skipcatch, hitpush, blocked, datum/thrownthing/throwingdatum) if(burn_stuff(AM)) @@ -292,11 +294,10 @@ if(QDELETED(burn_target)) return FALSE - . = TRUE if(isobj(burn_target)) var/obj/burn_obj = burn_target if(burn_obj.resistance_flags & ON_FIRE) // already on fire; skip it. - return + return TRUE if(!(burn_obj.resistance_flags & FLAMMABLE)) burn_obj.resistance_flags |= FLAMMABLE //Even fireproof things burn up in lava if(burn_obj.resistance_flags & FIRE_PROOF) @@ -305,16 +306,21 @@ burn_obj.set_armor_rating(FIRE, 50) burn_obj.fire_act(temperature_damage, 1000 * seconds_per_tick) if(istype(burn_obj, /obj/structure/closet)) - var/obj/structure/closet/burn_closet = burn_obj - for(var/burn_content in burn_closet.contents) + for(var/burn_content in burn_target) burn_stuff(burn_content) - return + return TRUE - var/mob/living/burn_living = burn_target - burn_living.AddElement(/datum/element/perma_fire_overlay) - burn_living.ignite_mob() - burn_living.adjust_fire_stacks(lava_firestacks * seconds_per_tick) - burn_living.adjustFireLoss(lava_damage * seconds_per_tick) + if(isliving(burn_target)) + var/mob/living/burn_living = burn_target + if(!HAS_TRAIT_FROM(burn_living, TRAIT_NO_EXTINGUISH, TURF_TRAIT)) + burn_living.AddElement(/datum/element/perma_fire_overlay) + ADD_TRAIT(burn_living, TRAIT_NO_EXTINGUISH, TURF_TRAIT) + burn_living.adjust_fire_stacks(lava_firestacks * seconds_per_tick) + burn_living.ignite_mob() + burn_living.adjustFireLoss(lava_damage * seconds_per_tick) + return TRUE + + return FALSE /turf/open/lava/can_cross_safely(atom/movable/crossing) return HAS_TRAIT(src, TRAIT_LAVA_STOPPED) || HAS_TRAIT(crossing, immunity_trait ) || HAS_TRAIT(crossing, TRAIT_MOVE_FLYING) From 938df27f597dbda04061ecd7eefb675afcf7923f Mon Sep 17 00:00:00 2001 From: Lucy Date: Sat, 10 Feb 2024 16:50:24 -0500 Subject: [PATCH 070/508] Fix last words being double-encoded when done from the alert popup (#81386) ## About The Pull Request Fixes succumb last words, when typed in the tgui input popup, being double-encoded/sanitized, resulting in things like this: Upstream port of https://github.com/Monkestation/Monkestation2.0/pull/1182 ## Why It's Good For The Game This bug is annoying and makes text uglier and less readable. Also, bugs are bad. Do I even need to fill this part out for a blatant bugfix? --- code/_onclick/hud/alert.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index e73cebf14c0cb..1f5f7588162ab 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -464,7 +464,7 @@ or shoot a gun to move around via Newton's 3rd Law of Motion." var/mob/living/living_owner = owner var/last_whisper if(!HAS_TRAIT(living_owner, TRAIT_SUCCUMB_OVERRIDE)) - last_whisper = tgui_input_text(usr, "Do you have any last words?", "Goodnight, Sweet Prince") + last_whisper = tgui_input_text(usr, "Do you have any last words?", "Goodnight, Sweet Prince", encode = FALSE) // saycode already handles sanitization if(isnull(last_whisper)) if(!HAS_TRAIT(living_owner, TRAIT_SUCCUMB_OVERRIDE)) return From 674c7f299177c2af3e3ea393214a5dd9eee6ee5e Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 10:50:45 +1300 Subject: [PATCH 071/508] Automatic changelog for PR #81386 [ci skip] --- html/changelogs/AutoChangeLog-pr-81386.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81386.yml diff --git a/html/changelogs/AutoChangeLog-pr-81386.yml b/html/changelogs/AutoChangeLog-pr-81386.yml new file mode 100644 index 0000000000000..0d72a81efb6f6 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81386.yml @@ -0,0 +1,4 @@ +author: "Absolucy" +delete-after: True +changes: + - bugfix: "Fix succumb last words being double-encoded (i.e `i'm` becoming `i'lm`)" \ No newline at end of file From a9c38ffad94f233a835c18352c82176373452178 Mon Sep 17 00:00:00 2001 From: Rhials <28870487+Rhials@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:08:25 -0500 Subject: [PATCH 072/508] Replaces beach bar virtual domain varedited bar closets with prefabs (#81384) ## About The Pull Request This changes the varedited/manually populated bar closets in the lavaland virtual domain with the proper prefabs used on lavaland. ## Why It's Good For The Game The doors don't look wonky now. ![image](https://github.com/tgstation/tgstation/assets/28870487/109ed9d5-762f-48ee-953e-6c669b51bbeb) ## Changelog :cl: Rhials fix: The beach bar virtual domain's bar closets no longer have default locker doors. /:cl: --- .../LavaRuins/lavaland_biodome_beach.dmm | 2 +- _maps/virtual_domains/beach_bar.dmm | 42 +------------------ 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm index 3b220a5022aed..c97bd25796016 100644 --- a/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm +++ b/_maps/RandomRuins/LavaRuins/lavaland_biodome_beach.dmm @@ -524,8 +524,8 @@ /turf/closed/wall/mineral/sandstone, /area/ruin/powered/beach) "qK" = ( -/obj/structure/closet/secure_closet/bar/lavaland_bartender_clothes, /obj/machinery/light/small/directional/east, +/obj/structure/closet/secure_closet/bar/lavaland_bartender_clothes, /turf/open/floor/wood, /area/ruin/powered/beach) "qT" = ( diff --git a/_maps/virtual_domains/beach_bar.dmm b/_maps/virtual_domains/beach_bar.dmm index edea28b064f50..7da71e943d172 100644 --- a/_maps/virtual_domains/beach_bar.dmm +++ b/_maps/virtual_domains/beach_bar.dmm @@ -84,34 +84,7 @@ /turf/open/water/beach, /area/virtual_domain/fullbright) "db" = ( -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/vending_refill/cigarette, -/obj/item/vending_refill/boozeomat, -/obj/structure/closet/secure_closet{ - icon_state = "cabinet"; - name = "booze storage"; - req_access = list("bar") - }, -/obj/item/storage/backpack/duffelbag, -/obj/item/etherealballdeployer, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/bottle/beer/light, -/obj/item/reagent_containers/cup/glass/colocup, -/obj/item/reagent_containers/cup/glass/colocup, -/obj/item/reagent_containers/cup/glass/colocup, -/obj/item/reagent_containers/cup/glass/colocup, -/obj/item/reagent_containers/cup/glass/colocup, +/obj/structure/closet/secure_closet/bar/lavaland_bartender_booze, /turf/open/floor/wood, /area/virtual_domain/fullbright) "di" = ( @@ -395,19 +368,8 @@ /turf/open/misc/asteroid/basalt/lava_land_surface, /area/virtual_domain/fullbright) "ug" = ( -/obj/structure/closet/secure_closet{ - icon_state = "cabinet"; - name = "bartender's closet"; - req_access = list("bar") - }, -/obj/item/clothing/shoes/sandal{ - desc = "A very fashionable pair of flip-flops."; - name = "flip-flops" - }, -/obj/item/clothing/neck/beads, -/obj/item/clothing/glasses/sunglasses/reagent, -/obj/item/clothing/suit/costume/hawaiian, /obj/machinery/light/small/directional/east, +/obj/structure/closet/secure_closet/bar/lavaland_bartender_clothes, /turf/open/floor/wood, /area/virtual_domain/fullbright) "uk" = ( From afe6a6683defa34ae7217bb4b17f35a459417cf3 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:08:43 +1300 Subject: [PATCH 073/508] Automatic changelog for PR #81384 [ci skip] --- html/changelogs/AutoChangeLog-pr-81384.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81384.yml diff --git a/html/changelogs/AutoChangeLog-pr-81384.yml b/html/changelogs/AutoChangeLog-pr-81384.yml new file mode 100644 index 0000000000000..12fed023bf086 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81384.yml @@ -0,0 +1,4 @@ +author: "Rhials" +delete-after: True +changes: + - bugfix: "The beach bar virtual domain's bar closets no longer have default locker doors." \ No newline at end of file From 3cc3ad393596275ca200feafca6cbbf524518445 Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:13:19 -0600 Subject: [PATCH 074/508] Mining Bluespace Survival Capsule's "Stand back!" alert now actually has meaning (#81193) ## About The Pull Request - Standing in range of a Bluespace Survival Capsule being deployed will now toss you away from the deployed location of the pod, to roughly 2 tiles away. ## Why It's Good For The Game I always found it weird (and a little misleading) that triggering a survival capsule alerts people to "Stand back!", but it's actually harmless to stand on top of it and even smart in some contexts since you phase into it like nothing happened. So I added the missing flavor. This also has some potential shenanigans involving antagging, as you can time it perfectly so that you toss it just in time to throw people away from it. Note: The Luxury Elite pod is so large, that even after being thrown away, you may still end up inside the pod by the time it deploys, but I think that's funny. So. ## Changelog :cl: Melbert add: Shaft Miner's Bluespace Survival Capsules will now throw people away from it when it deploys. Be sure to heed the warning to "Stand back". /:cl: --- code/modules/mining/equipment/survival_pod.dm | 102 +++++++++++++----- 1 file changed, 75 insertions(+), 27 deletions(-) diff --git a/code/modules/mining/equipment/survival_pod.dm b/code/modules/mining/equipment/survival_pod.dm index c221e78d69023..f4463434066c7 100644 --- a/code/modules/mining/equipment/survival_pod.dm +++ b/code/modules/mining/equipment/survival_pod.dm @@ -37,35 +37,83 @@ . += "This capsule has the [template.name] stored." . += template.description -/obj/item/survivalcapsule/attack_self() +/obj/item/survivalcapsule/interact(mob/user) + . = ..() + if(.) + return . + //Can't grab when capsule is New() because templates aren't loaded then get_template() - if(!used) - loc.visible_message(span_warning("\The [src] begins to shake. Stand back!")) - used = TRUE - sleep(5 SECONDS) - var/turf/deploy_location = get_turf(src) - var/status = template.check_deploy(deploy_location) - switch(status) - if(SHELTER_DEPLOY_BAD_AREA) - src.loc.visible_message(span_warning("\The [src] will not function in this area.")) - if(SHELTER_DEPLOY_BAD_TURFS, SHELTER_DEPLOY_ANCHORED_OBJECTS, SHELTER_DEPLOY_OUTSIDE_MAP) - var/width = template.width - var/height = template.height - src.loc.visible_message(span_warning("\The [src] doesn't have room to deploy! You need to clear a [width]x[height] area!")) - if(status != SHELTER_DEPLOY_ALLOWED) - used = FALSE - return - - template.load(deploy_location, centered = TRUE) - var/turf/T = deploy_location - if(!is_mining_level(T.z)) //only report capsules away from the mining/lavaland level - message_admins("[ADMIN_LOOKUPFLW(usr)] activated a bluespace capsule away from the mining level! [ADMIN_VERBOSEJMP(T)]") - log_admin("[key_name(usr)] activated a bluespace capsule away from the mining level at [AREACOORD(T)]") - - playsound(src, 'sound/effects/phasein.ogg', 100, TRUE) - new /obj/effect/particle_effect/fluid/smoke(get_turf(src)) - qdel(src) + if(used) + return FALSE + + loc.visible_message(span_warning("[src] begins to shake. Stand back!")) + used = TRUE + addtimer(CALLBACK(src, PROC_REF(expand), user), 5 SECONDS) + return TRUE + +/// Expands the capsule into a full shelter, placing the template at the item's location (NOT triggerer's location) +/obj/item/survivalcapsule/proc/expand(mob/triggerer) + if(QDELETED(src)) + return + + var/turf/deploy_location = get_turf(src) + var/status = template.check_deploy(deploy_location) + switch(status) + if(SHELTER_DEPLOY_BAD_AREA) + loc.visible_message(span_warning("[src] will not function in this area.")) + if(SHELTER_DEPLOY_BAD_TURFS, SHELTER_DEPLOY_ANCHORED_OBJECTS, SHELTER_DEPLOY_OUTSIDE_MAP) + loc.visible_message(span_warning("[src] doesn't have room to deploy! You need to clear a [template.width]x[template.height] area!")) + + if(status != SHELTER_DEPLOY_ALLOWED) + used = FALSE + return + + yote_nearby(deploy_location) + template.load(deploy_location, centered = TRUE) + trigger_admin_alert(triggerer, deploy_location) + playsound(src, 'sound/effects/phasein.ogg', 100, TRUE) + new /obj/effect/particle_effect/fluid/smoke(get_turf(src)) + qdel(src) + +/// Throws any mobs near the deployed location away from the item / shelter +/// Does some math to make closer mobs get thrown further +/obj/item/survivalcapsule/proc/yote_nearby(turf/deploy_location) + var/width = template.width + var/height = template.height + var/base_x_throw_distance = ceil(width / 2) + var/base_y_throw_distance = ceil(height / 2) + for(var/mob/living/did_not_stand_back in range(loc, "[width]x[height]")) + var/dir_to_center = get_dir(deploy_location, did_not_stand_back) || pick(GLOB.alldirs) + // Aiming to throw the target just enough to get them out of the range of the shelter + // IE: Stronger if they're closer, weaker if they're further away + var/throw_dist = 0 + var/x_component = abs(did_not_stand_back.x - deploy_location.x) + var/y_component = abs(did_not_stand_back.y - deploy_location.y) + if(ISDIAGONALDIR(dir_to_center)) + throw_dist = ceil(sqrt(base_x_throw_distance ** 2 + base_y_throw_distance ** 2) - (sqrt(x_component ** 2 + y_component ** 2))) + else if(dir_to_center & (NORTH|SOUTH)) + throw_dist = base_y_throw_distance - y_component + 1 + else if(dir_to_center & (EAST|WEST)) + throw_dist = base_x_throw_distance - x_component + 1 + + did_not_stand_back.Paralyze(3 SECONDS) + did_not_stand_back.Knockdown(6 SECONDS) + did_not_stand_back.throw_at( + target = get_edge_target_turf(did_not_stand_back, dir_to_center), + range = throw_dist, + speed = 3, + force = MOVE_FORCE_VERY_STRONG, + ) + +/// Logs if the capsule was triggered, by default only if it happened on non-lavaland +/obj/item/survivalcapsule/proc/trigger_admin_alert(mob/triggerer, turf/trigger_loc) + //only report capsules away from the mining/lavaland level + if(is_mining_level(trigger_loc.z)) + return + + message_admins("[ADMIN_LOOKUPFLW(triggerer)] activated a bluespace capsule away from the mining level! [ADMIN_VERBOSEJMP(trigger_loc)]") + log_admin("[key_name(triggerer)] activated a bluespace capsule away from the mining level at [AREACOORD(trigger_loc)]") //Non-default pods From 2ef75d9944954ca6b0fe28abeb2c7f2d89bc18c8 Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:13:38 +1300 Subject: [PATCH 075/508] Automatic changelog for PR #81193 [ci skip] --- html/changelogs/AutoChangeLog-pr-81193.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81193.yml diff --git a/html/changelogs/AutoChangeLog-pr-81193.yml b/html/changelogs/AutoChangeLog-pr-81193.yml new file mode 100644 index 0000000000000..8023c1ab06f47 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81193.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - rscadd: "Shaft Miner's Bluespace Survival Capsules will now throw people away from it when it deploys. Be sure to heed the warning to \"Stand back\"." \ No newline at end of file From 815c7913d7b5ef376b0cc916865745156afeaeb5 Mon Sep 17 00:00:00 2001 From: Nick <42454181+Momo8289@users.noreply.github.com> Date: Sat, 10 Feb 2024 18:34:48 -0500 Subject: [PATCH 076/508] Adds fire ants as a chem (#81214) ## About The Pull Request Adds fire ants as a chem. They work pretty much exactly the same as normal ants, but they deal roughly 2x damage. Also mildly cleans up whatever ant code I touched ## Why It's Good For The Game Doesn't really make sense that when you scoop up an anthill of fire ants, you get a beaker full of normal ants. It's honestly an injustice. Also, new flavours for the bartender and chef to work with are always nice. ## Changelog :cl: add: Fire ants can now be scooped up and used as a chem like normal space ants /:cl: --------- Co-authored-by: Aki Ito <11748095+ExcessiveUseOfCobblestone@users.noreply.github.com> --- code/datums/status_effects/debuffs/debuffs.dm | 15 ++++++- .../objects/effects/decals/cleanable/misc.dm | 1 + .../chemistry/reagents/other_reagents.dm | 44 ++++++++++++++----- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/code/datums/status_effects/debuffs/debuffs.dm b/code/datums/status_effects/debuffs/debuffs.dm index 8cdc2840e2c48..74eb198cfcec9 100644 --- a/code/datums/status_effects/debuffs/debuffs.dm +++ b/code/datums/status_effects/debuffs/debuffs.dm @@ -792,6 +792,8 @@ processing_speed = STATUS_EFFECT_NORMAL_PROCESS /// Will act as the main timer as well as changing how much damage the ants do. var/ants_remaining = 0 + /// Amount of damage done per ant on the victim + var/damage_per_ant = 0.0016 /// Common phrases people covered in ants scream var/static/list/ant_debuff_speech = list( "GET THEM OFF ME!!", @@ -838,7 +840,7 @@ /datum/status_effect/ants/tick(seconds_between_ticks) var/mob/living/carbon/human/victim = owner var/need_mob_update - need_mob_update = victim.adjustBruteLoss(max(0.1, round((ants_remaining * 0.0016) * seconds_between_ticks,0.1)), updating_health = FALSE) //Scales with # of ants (lowers with time). Roughly 10 brute over 50 seconds. + need_mob_update = victim.adjustBruteLoss(max(0.1, round((ants_remaining * damage_per_ant) * seconds_between_ticks,0.1)), updating_health = FALSE) //Scales with # of ants (lowers with time). Roughly 10 brute over 50 seconds. if(victim.stat <= SOFT_CRIT) //Makes sure people don't scratch at themselves while they're in a critical condition if(prob(15)) switch(rand(1,2)) @@ -868,7 +870,7 @@ if(need_mob_update) victim.updatehealth() if(ants_remaining <= 0 || victim.stat >= HARD_CRIT) - victim.remove_status_effect(/datum/status_effect/ants) //If this person has no more ants on them or are dead, they are no longer affected. + victim.remove_status_effect(type) //If this person has no more ants on them or are dead, they are no longer affected. /atom/movable/screen/alert/status_effect/ants name = "Ants!" @@ -886,6 +888,15 @@ to_chat(living, span_notice("You manage to get some of the ants off!")) ant_covered.ants_remaining -= 10 // 5 Times more ants removed per second than just waiting in place +/datum/status_effect/ants/fire + id = "fire_ants" + alert_type = /atom/movable/screen/alert/status_effect/ants/fire + damage_per_ant = 0.0064 + +/atom/movable/screen/alert/status_effect/ants/fire + name = "Fire Ants!" + desc = span_warning("JESUS FUCKING CHRIST IT BURNS! CLICK TO GET THOSE THINGS OFF!") + /datum/status_effect/stagger id = "stagger" status_type = STATUS_EFFECT_REFRESH diff --git a/code/game/objects/effects/decals/cleanable/misc.dm b/code/game/objects/effects/decals/cleanable/misc.dm index c93b105767a10..1a1dc80fe2594 100644 --- a/code/game/objects/effects/decals/cleanable/misc.dm +++ b/code/game/objects/effects/decals/cleanable/misc.dm @@ -403,6 +403,7 @@ /obj/effect/decal/cleanable/ants/fire name = "space fire ants" desc = "A small colony no longer. We are the fire nation." + decal_reagent = /datum/reagent/ants/fire icon_state = "fire_ants" mergeable_decal = FALSE diff --git a/code/modules/reagents/chemistry/reagents/other_reagents.dm b/code/modules/reagents/chemistry/reagents/other_reagents.dm index 238f0f5252296..48c30271158f9 100644 --- a/code/modules/reagents/chemistry/reagents/other_reagents.dm +++ b/code/modules/reagents/chemistry/reagents/other_reagents.dm @@ -2858,10 +2858,16 @@ taste_description = "tiny legs scuttling down the back of your throat" metabolization_rate = 5 * REAGENTS_METABOLISM //1u per second ph = 4.6 // Ants contain Formic Acid - /// How much damage the ants are going to be doing (rises with each tick the ants are in someone's body) - var/ant_damage = 0 + /// Number of ticks the ants have been in the person's body + var/ant_ticks = 0 + /// Amount of damage done per tick the ants have been in the person's system + var/ant_damage = 0.025 /// Tells the debuff how many ants we are being covered with. var/amount_left = 0 + /// Decal to spawn when spilled + var/ants_decal = /obj/effect/decal/cleanable/ants + /// Status effect applied by splashing ants + var/status_effect = /datum/status_effect/ants /// List of possible common statements to scream when eating ants var/static/list/ant_screams = list( "THEY'RE UNDER MY SKIN!!", @@ -2878,15 +2884,15 @@ /datum/reagent/ants/on_mob_life(mob/living/carbon/victim, seconds_per_tick) . = ..() - victim.adjustBruteLoss(max(0.1, round((ant_damage * 0.025),0.1))) //Scales with time. Roughly 32 brute with 100u. - ant_damage++ - if(ant_damage < 5) // Makes ant food a little more appetizing, since you won't be screaming as much. + victim.adjustBruteLoss(max(0.1, round((ant_ticks * ant_damage),0.1))) //Scales with time. Roughly 32 brute with 100u. + ant_ticks++ + if(ant_ticks < 5) // Makes ant food a little more appetizing, since you won't be screaming as much. return if(SPT_PROB(5, seconds_per_tick)) if(SPT_PROB(5, seconds_per_tick)) //Super rare statement - victim.say("AUGH NO NOT THE ANTS! NOT THE ANTS! AAAAUUGH THEY'RE IN MY EYES! MY EYES! AUUGH!!", forced = /datum/reagent/ants) + victim.say("AUGH NO NOT THE ANTS! NOT THE ANTS! AAAAUUGH THEY'RE IN MY EYES! MY EYES! AUUGH!!", forced = type) else - victim.say(pick(ant_screams), forced = /datum/reagent/ants) + victim.say(pick(ant_screams), forced = type) if(SPT_PROB(15, seconds_per_tick)) victim.emote("scream") if(SPT_PROB(2, seconds_per_tick)) // Stuns, but purges ants. @@ -2894,8 +2900,8 @@ /datum/reagent/ants/on_mob_end_metabolize(mob/living/living_anthill) . = ..() - ant_damage = 0 - to_chat(living_anthill, "You feel like the last of the ants are out of your system.") + ant_ticks = 0 + to_chat(living_anthill, span_notice("You feel like the last of the [name] are out of your system.")) /datum/reagent/ants/expose_mob(mob/living/exposed_mob, methods=TOUCH, reac_volume) . = ..() @@ -2903,7 +2909,7 @@ return if(methods & (PATCH|TOUCH|VAPOR)) amount_left = round(reac_volume,0.1) - exposed_mob.apply_status_effect(/datum/status_effect/ants, amount_left) + exposed_mob.apply_status_effect(status_effect, amount_left) /datum/reagent/ants/expose_obj(obj/exposed_obj, reac_volume) . = ..() @@ -2922,14 +2928,28 @@ if((reac_volume <= 10)) // Makes sure people don't duplicate ants. return - var/obj/effect/decal/cleanable/ants/pests = exposed_turf.spawn_unique_cleanable(/obj/effect/decal/cleanable/ants) + var/obj/effect/decal/cleanable/ants/pests = exposed_turf.spawn_unique_cleanable(ants_decal) if(!pests) return var/spilled_ants = (round(reac_volume,1) - 5) // To account for ant decals giving 3-5 ants on initialize. - pests.reagents.add_reagent(/datum/reagent/ants, spilled_ants) + pests.reagents.add_reagent(type, spilled_ants) pests.update_ant_damage() +/datum/reagent/ants/fire + name = "Fire ants" + description = "A rare mutation of space ants, born from the heat of a plasma fire. Their bites land a 3.7 on the Schmidt Pain Scale." + color = "#b51f1f" + taste_description = "tiny flaming legs scuttling down the back of your throat" + ant_damage = 0.05 // Roughly 64 brute with 100u + ants_decal = /obj/effect/decal/cleanable/ants/fire + status_effect = /datum/status_effect/ants/fire + +/datum/glass_style/drinking_glass/fire_ants + required_drink_type = /datum/reagent/ants/fire + name = "glass of fire ants" + desc = "This is a terrible idea." + //This is intended to a be a scarce reagent to gate certain drugs and toxins with. Do not put in a synthesizer. Renewable sources of this reagent should be inefficient. /datum/reagent/lead name = "Lead" From 8db6f1728b3576128a89dd3b662ac68175a1693a Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:35:05 +1300 Subject: [PATCH 077/508] Automatic changelog for PR #81214 [ci skip] --- html/changelogs/AutoChangeLog-pr-81214.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81214.yml diff --git a/html/changelogs/AutoChangeLog-pr-81214.yml b/html/changelogs/AutoChangeLog-pr-81214.yml new file mode 100644 index 0000000000000..3f851c20c4de7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81214.yml @@ -0,0 +1,4 @@ +author: "Momo8289" +delete-after: True +changes: + - rscadd: "Fire ants can now be scooped up and used as a chem like normal space ants" \ No newline at end of file From 06a4d479459410d8b44d1fad49e9f915e59ad05a Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:36:53 -0600 Subject: [PATCH 078/508] Freedom implant and Biodegrade works on knotted shoes (#81376) ## About The Pull Request - Freedom Implant will un-knot knotted shoes. - Biodegrade will melt knotted shoes. ## Why It's Good For The Game Just a niche interaction idea I had. Knotted shoes are, obviously, obstructing your movement so these two tools that aim to un-obstruct your movement should do something about it, right? Also it would be funny to see a Ling melt their own shoes. Biodegrade prioritizes handcuffs over anything else so it shouldn't be of great concern. ## Changelog :cl: Melbert add: Freedom Implants and Biodegrade can you free you of the shackles of knotted shoes. /:cl: --- .../objects/items/implants/implant_freedom.dm | 16 +++++++++++++++- .../antagonists/changeling/powers/biodegrade.dm | 12 ++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/code/game/objects/items/implants/implant_freedom.dm b/code/game/objects/items/implants/implant_freedom.dm index af5d955dfbc9a..827cc8216a58b 100644 --- a/code/game/objects/items/implants/implant_freedom.dm +++ b/code/game/objects/items/implants/implant_freedom.dm @@ -17,17 +17,31 @@ /obj/item/implant/freedom/activate() . = ..() var/mob/living/carbon/carbon_imp_in = imp_in - if(!carbon_imp_in.handcuffed && !carbon_imp_in.legcuffed) + if(!can_trigger(carbon_imp_in)) balloon_alert(carbon_imp_in, "no restraints!") return uses-- carbon_imp_in.uncuff() + var/obj/item/clothing/shoes/shoes = carbon_imp_in.shoes + if(istype(shoes) && shoes.tied == SHOES_KNOTTED) + shoes.adjust_laces(SHOES_TIED, carbon_imp_in) + if(!uses) addtimer(CALLBACK(carbon_imp_in, TYPE_PROC_REF(/atom, balloon_alert), carbon_imp_in, "implant degraded!"), 1 SECONDS) qdel(src) +/obj/item/implant/freedom/proc/can_trigger(mob/living/carbon/implanted_in) + if(implanted_in.handcuffed || implanted_in.legcuffed) + return TRUE + + var/obj/item/clothing/shoes/shoes = implanted_in.shoes + if(istype(shoes) && shoes.tied == SHOES_KNOTTED) + return TRUE + + return FALSE + /obj/item/implant/freedom/get_data() return "Implant Specifications:
    \ Name: Freedom Beacon
    \ diff --git a/code/modules/antagonists/changeling/powers/biodegrade.dm b/code/modules/antagonists/changeling/powers/biodegrade.dm index eba507ad5e079..ef3070356d5d2 100644 --- a/code/modules/antagonists/changeling/powers/biodegrade.dm +++ b/code/modules/antagonists/changeling/powers/biodegrade.dm @@ -65,6 +65,18 @@ ..() return TRUE + var/obj/item/clothing/shoes/shoes = user.shoes + if(istype(shoes) && shoes.tied == SHOES_KNOTTED && !(shoes.resistance_flags & (INDESTRUCTIBLE|UNACIDABLE|ACID_PROOF))) + new /obj/effect/decal/cleanable/greenglow(shoes.drop_location()) + user.visible_message( + span_warning("[user] vomits a glob of acid on [user.p_their()] tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"), + span_warning("We vomit acidic ooze onto our tied up [shoes.name], melting [shoes.p_them()] into a pool of goo!"), + ) + log_combat(user, shoes, "melted own shoes", addition = "(biodegrade)") + qdel(shoes) + ..() + return TRUE + user.balloon_alert(user, "already free!") return FALSE From b22d0e62fb9942349ed906cb23714029709f8e2c Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:37:13 +1300 Subject: [PATCH 079/508] Automatic changelog for PR #81376 [ci skip] --- html/changelogs/AutoChangeLog-pr-81376.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81376.yml diff --git a/html/changelogs/AutoChangeLog-pr-81376.yml b/html/changelogs/AutoChangeLog-pr-81376.yml new file mode 100644 index 0000000000000..6769cb512ba8e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81376.yml @@ -0,0 +1,4 @@ +author: "Melbert" +delete-after: True +changes: + - rscadd: "Freedom Implants and Biodegrade can you free you of the shackles of knotted shoes." \ No newline at end of file From c7d5e6d72b63f054f989ad3d499b44d780cb281e Mon Sep 17 00:00:00 2001 From: jimmyl <70376633+mc-oofert@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:43:50 +0100 Subject: [PATCH 080/508] two new tgui commands (for coders) (#81381) ## About The Pull Request tgui:prettier-fix runs prettier fixes tgui:eslint-fix runs eslint fixes ## Why It's Good For The Game ![image](https://github.com/tgstation/tgstation/assets/70376633/dadf7047-5fc0-498b-93a5-53d03a706b73) --- tgui/package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tgui/package.json b/tgui/package.json index 1370aacc48a05..bfbc5a34aba21 100644 --- a/tgui/package.json +++ b/tgui/package.json @@ -17,7 +17,9 @@ "tgui:test": "jest --watch", "tgui:test-simple": "CI=true jest --color", "tgui:test-ci": "CI=true jest --color --collect-coverage", - "tgui:tsc": "tsc" + "tgui:tsc": "tsc", + "tgui:prettier-fix": "prettier --write .", + "tgui:eslint-fix": "eslint --fix packages --ext .js,.cjs,.ts,.tsx" }, "dependencies": { "@swc/core": "^1.3.100", From 9825f0e392498e7691b85d667ee9a2a8046a427c Mon Sep 17 00:00:00 2001 From: Higgin Date: Sat, 10 Feb 2024 15:47:26 -0800 Subject: [PATCH 081/508] [FIX] Makes Deathtrap Recycler Movable (#81282) ## About The Pull Request Fixes #81205 ~~Adds a new flag for letting you move otherwise un-deconstructable objects and making deconstructable objects immovable with a wrench.~~ ~~Adds that flag onto the ORM.~~ makes ~~ORM un-unwrenchable,~~ deathtrap recycler unwrenchable. ## Why It's Good For The Game seems like an oversight on the recycler that unnecessarily limited gimmicks, ~~and one of the long-running peeves with something like the ORM has been how damn easy it is to walk up to it, unwrench it, and walk into cargo through the windoor mapped for mineral storage access (which basically everyone has.) This makes it so if you'd otherwise do that, now you at least need to either deconstruct or destroy the ORM first (or just take another less obvious way in.) Always seemed like an oversight but can atomize it if wanted.~~ atomized out, will ask around before putting back up ## Changelog :cl: fix: deathtrap recycler can now be moved. refactor: moved check for NO_DECONSTRUCTION flag to be inside can_be_unfasten_wrench, allowing us to set specific machines to be movable but not deconstructable. /:cl: --- code/game/machinery/recycler.dm | 6 ++++++ code/game/objects/objs.dm | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm index 0f6002c97253d..2ee22338d0e0c 100644 --- a/code/game/machinery/recycler.dm +++ b/code/game/machinery/recycler.dm @@ -83,6 +83,12 @@ default_unfasten_wrench(user, tool) return ITEM_INTERACT_SUCCESS +/obj/machinery/recycler/can_be_unfasten_wrench(mob/user, silent) + if(!(isfloorturf(loc) || isindestructiblefloor(loc)) && !anchored) + to_chat(user, span_warning("[src] needs to be on the floor to be secured!")) + return FAILED_UNFASTEN + return SUCCESSFUL_UNFASTEN + /obj/machinery/recycler/attackby(obj/item/I, mob/user, params) if(default_deconstruction_screwdriver(user, "grinder-oOpen", "grinder-o0", I)) return diff --git a/code/game/objects/objs.dm b/code/game/objects/objs.dm index 91f1ca925abf8..cb57c5049bb8c 100644 --- a/code/game/objects/objs.dm +++ b/code/game/objects/objs.dm @@ -373,6 +373,8 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) /// If we can unwrench this object; returns SUCCESSFUL_UNFASTEN and FAILED_UNFASTEN, which are both TRUE, or CANT_UNFASTEN, which isn't. /obj/proc/can_be_unfasten_wrench(mob/user, silent) + if(obj_flags & NO_DECONSTRUCTION) + return CANT_UNFASTEN if(!(isfloorturf(loc) || isindestructiblefloor(loc)) && !anchored) to_chat(user, span_warning("[src] needs to be on the floor to be secured!")) return FAILED_UNFASTEN @@ -380,7 +382,7 @@ GLOBAL_LIST_EMPTY(objects_by_id_tag) /// Try to unwrench an object in a WONDERFUL DYNAMIC WAY /obj/proc/default_unfasten_wrench(mob/user, obj/item/wrench, time = 20) - if((obj_flags & NO_DECONSTRUCTION) || wrench.tool_behaviour != TOOL_WRENCH) + if(wrench.tool_behaviour != TOOL_WRENCH) return CANT_UNFASTEN var/turf/ground = get_turf(src) From f3ba27bdc940aee4e051563785517933fba66ebc Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 12:53:24 +1300 Subject: [PATCH 082/508] Automatic changelog for PR #81282 [ci skip] --- html/changelogs/AutoChangeLog-pr-81282.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81282.yml diff --git a/html/changelogs/AutoChangeLog-pr-81282.yml b/html/changelogs/AutoChangeLog-pr-81282.yml new file mode 100644 index 0000000000000..b1a4165f532c1 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81282.yml @@ -0,0 +1,5 @@ +author: "Higgin" +delete-after: True +changes: + - bugfix: "deathtrap recycler can now be moved." + - refactor: "moved check for NO_DECONSTRUCTION flag to be inside can_be_unfasten_wrench, allowing us to set specific machines to be movable but not deconstructable." \ No newline at end of file From 94f5221af4487fb982d2ef3d042ec2162bb77443 Mon Sep 17 00:00:00 2001 From: Rhials <28870487+Rhials@users.noreply.github.com> Date: Sat, 10 Feb 2024 19:03:14 -0500 Subject: [PATCH 083/508] Fixes the energy cake slice's on-eat effect triggering before it is eaten (#81382) ## About The Pull Request This makes the energy cake deal its damage/effects AFTER being fed, instead of before. It also adds combat logging to force-fed energy cakes, just in case. Also, this adds a balloon alert to the pacifist check. Cool! ## Why It's Good For The Game Fixes a wacky combat bug, and adds some minor adjustments made along the way. Closes #81351. ## Changelog :cl: Rhials fix: The Energy Cake slice now does its on-eat effect AFTER being eaten, instead of before. /:cl: --- code/game/objects/items/food/cake.dm | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/code/game/objects/items/food/cake.dm b/code/game/objects/items/food/cake.dm index 07d5818b4662c..0b443554bb3b6 100644 --- a/code/game/objects/items/food/cake.dm +++ b/code/game/objects/items/food/cake.dm @@ -288,16 +288,23 @@ tastes = list("cake" = 3, "a Vlad's Salad" = 1) crafting_complexity = FOOD_COMPLEXITY_4 -/obj/item/food/cakeslice/birthday/energy/proc/energy_bite(mob/living/user) - to_chat(user, "As you eat the cake slice, you accidentally hurt yourself on the embedded energy dagger!") - user.apply_damage(18, BRUTE, BODY_ZONE_HEAD) - playsound(user, 'sound/weapons/blade1.ogg', 5, TRUE) +/obj/item/food/cakeslice/birthday/energy/Initialize(mapload) + . = ..() + RegisterSignal(src, COMSIG_FOOD_EATEN, PROC_REF(bite_taken)) /obj/item/food/cakeslice/birthday/energy/attack(mob/living/target_mob, mob/living/user) - . = ..() if(HAS_TRAIT(user, TRAIT_PACIFISM) && target_mob != user) //Prevents pacifists from attacking others directly - return - energy_bite(target_mob, user) + balloon_alert(user, "that's dangerous!") + return FALSE + return ..() + +/obj/item/food/cakeslice/birthday/energy/proc/bite_taken(datum/source, mob/living/eater, mob/living/feeder) + SIGNAL_HANDLER + to_chat(eater, "As you eat the cake slice, you accidentally hurt yourself on the embedded energy dagger!") + if(eater != feeder) + log_combat(feeder, eater, "fed an energy cake to", src) + eater.apply_damage(18, BRUTE, BODY_ZONE_HEAD) + playsound(eater, 'sound/weapons/blade1.ogg', 5, TRUE) /obj/item/food/cake/apple name = "apple cake" From fa1a81699bf4c025cc22fec353dc19f0907392ec Mon Sep 17 00:00:00 2001 From: orange man <61334995+comfyorange@users.noreply.github.com> Date: Sun, 11 Feb 2024 13:03:31 +1300 Subject: [PATCH 084/508] Automatic changelog for PR #81382 [ci skip] --- html/changelogs/AutoChangeLog-pr-81382.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-81382.yml diff --git a/html/changelogs/AutoChangeLog-pr-81382.yml b/html/changelogs/AutoChangeLog-pr-81382.yml new file mode 100644 index 0000000000000..40055d8158f7f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-81382.yml @@ -0,0 +1,4 @@ +author: "Rhials" +delete-after: True +changes: + - bugfix: "The Energy Cake slice now does its on-eat effect AFTER being eaten, instead of before." \ No newline at end of file From f424ff490624bb92b876b866fb93c0fd5226dd32 Mon Sep 17 00:00:00 2001 From: Changelogs Date: Sun, 11 Feb 2024 00:22:02 +0000 Subject: [PATCH 085/508] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-81193.yml | 4 --- html/changelogs/AutoChangeLog-pr-81214.yml | 4 --- html/changelogs/AutoChangeLog-pr-81230.yml | 6 ---- html/changelogs/AutoChangeLog-pr-81282.yml | 5 --- html/changelogs/AutoChangeLog-pr-81364.yml | 4 --- html/changelogs/AutoChangeLog-pr-81367.yml | 4 --- html/changelogs/AutoChangeLog-pr-81368.yml | 4 --- html/changelogs/AutoChangeLog-pr-81370.yml | 4 --- html/changelogs/AutoChangeLog-pr-81372.yml | 4 --- html/changelogs/AutoChangeLog-pr-81374.yml | 4 --- html/changelogs/AutoChangeLog-pr-81375.yml | 4 --- html/changelogs/AutoChangeLog-pr-81376.yml | 4 --- html/changelogs/AutoChangeLog-pr-81377.yml | 4 --- html/changelogs/AutoChangeLog-pr-81382.yml | 4 --- html/changelogs/AutoChangeLog-pr-81384.yml | 4 --- html/changelogs/AutoChangeLog-pr-81386.yml | 4 --- html/changelogs/archive/2024-02.yml | 41 ++++++++++++++++++++++ 17 files changed, 41 insertions(+), 67 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-81193.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81214.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81230.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81282.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81364.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81367.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81368.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81370.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81372.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81374.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81375.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81376.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81377.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81382.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81384.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-81386.yml diff --git a/html/changelogs/AutoChangeLog-pr-81193.yml b/html/changelogs/AutoChangeLog-pr-81193.yml deleted file mode 100644 index 8023c1ab06f47..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81193.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - rscadd: "Shaft Miner's Bluespace Survival Capsules will now throw people away from it when it deploys. Be sure to heed the warning to \"Stand back\"." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81214.yml b/html/changelogs/AutoChangeLog-pr-81214.yml deleted file mode 100644 index 3f851c20c4de7..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81214.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Momo8289" -delete-after: True -changes: - - rscadd: "Fire ants can now be scooped up and used as a chem like normal space ants" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81230.yml b/html/changelogs/AutoChangeLog-pr-81230.yml deleted file mode 100644 index 4802131f8e35e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81230.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - balance: "Beacon and Teleport Blocker implants have been moved from cargo to the security lathe." - - balance: "A new research node has been created, requiring Subdermal Implants and Miniature Bluespace, to unlock Beacon/Teleport Blocker implants." - - balance: "Exile implants can now be printed from the security lathe after researching basic cybernetic implants." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81282.yml b/html/changelogs/AutoChangeLog-pr-81282.yml deleted file mode 100644 index b1a4165f532c1..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81282.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Higgin" -delete-after: True -changes: - - bugfix: "deathtrap recycler can now be moved." - - refactor: "moved check for NO_DECONSTRUCTION flag to be inside can_be_unfasten_wrench, allowing us to set specific machines to be movable but not deconstructable." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81364.yml b/html/changelogs/AutoChangeLog-pr-81364.yml deleted file mode 100644 index aa8ad537b2ed6..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81364.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - balance: "Rats are now 5x less likely to decide to eat a cable when idling. (1%, down from 5%)" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81367.yml b/html/changelogs/AutoChangeLog-pr-81367.yml deleted file mode 100644 index 336fefb2e7497..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81367.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - refactor: "Fire effects get added to mobs in a different way now. Maybe it will get stuck less. Report any oddities." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81368.yml b/html/changelogs/AutoChangeLog-pr-81368.yml deleted file mode 100644 index e4e5edda82dc5..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81368.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - bugfix: "Bar bots asking for Cucumber Lemonade now gives you money for completing it." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81370.yml b/html/changelogs/AutoChangeLog-pr-81370.yml deleted file mode 100644 index 62147dfb7e88c..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81370.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - bugfix: "Space Dragon's carp allies no longer turn the entire roundend report into bold." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81372.yml b/html/changelogs/AutoChangeLog-pr-81372.yml deleted file mode 100644 index efbf7ba1c6739..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81372.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "JohnFulpWillard" -delete-after: True -changes: - - bugfix: "Uplinks now update their UI when you add telecrystals in them, so you don't need to close and reopen it." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81374.yml b/html/changelogs/AutoChangeLog-pr-81374.yml deleted file mode 100644 index 718ea78e81c20..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81374.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "necromanceranne" -delete-after: True -changes: - - bugfix: "The shove blocker module parent type now has the correct typepath." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81375.yml b/html/changelogs/AutoChangeLog-pr-81375.yml deleted file mode 100644 index a45fe58737d5d..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81375.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SyncIt21" -delete-after: True -changes: - - bugfix: "lathes now use moderate power for printing operations" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81376.yml b/html/changelogs/AutoChangeLog-pr-81376.yml deleted file mode 100644 index 6769cb512ba8e..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81376.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Melbert" -delete-after: True -changes: - - rscadd: "Freedom Implants and Biodegrade can you free you of the shackles of knotted shoes." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81377.yml b/html/changelogs/AutoChangeLog-pr-81377.yml deleted file mode 100644 index 12be442e6a62f..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81377.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - bugfix: "You can now refine ectoplasmic raw cores at the implosion machine thing." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81382.yml b/html/changelogs/AutoChangeLog-pr-81382.yml deleted file mode 100644 index 40055d8158f7f..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81382.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - bugfix: "The Energy Cake slice now does its on-eat effect AFTER being eaten, instead of before." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81384.yml b/html/changelogs/AutoChangeLog-pr-81384.yml deleted file mode 100644 index 12fed023bf086..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81384.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Rhials" -delete-after: True -changes: - - bugfix: "The beach bar virtual domain's bar closets no longer have default locker doors." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-81386.yml b/html/changelogs/AutoChangeLog-pr-81386.yml deleted file mode 100644 index 0d72a81efb6f6..0000000000000 --- a/html/changelogs/AutoChangeLog-pr-81386.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Absolucy" -delete-after: True -changes: - - bugfix: "Fix succumb last words being double-encoded (i.e `i'm` becoming `i'lm`)" \ No newline at end of file diff --git a/html/changelogs/archive/2024-02.yml b/html/changelogs/archive/2024-02.yml index e9299211d16b6..079c5c6b6440c 100644 --- a/html/changelogs/archive/2024-02.yml +++ b/html/changelogs/archive/2024-02.yml @@ -269,3 +269,44 @@ the cost of another station's cryo cells. Who knows! larentoun: - bugfix: Now falsewalls visually don't close when they shouldn't. +2024-02-11: + Absolucy: + - bugfix: Fix succumb last words being double-encoded (i.e `i'm` becoming `i'lm`) + Higgin: + - bugfix: deathtrap recycler can now be moved. + - refactor: moved check for NO_DECONSTRUCTION flag to be inside can_be_unfasten_wrench, + allowing us to set specific machines to be movable but not deconstructable. + JohnFulpWillard: + - bugfix: Space Dragon's carp allies no longer turn the entire roundend report into + bold. + - bugfix: Uplinks now update their UI when you add telecrystals in them, so you + don't need to close and reopen it. + - bugfix: Bar bots asking for Cucumber Lemonade now gives you money for completing + it. + Melbert: + - balance: Rats are now 5x less likely to decide to eat a cable when idling. (1%, + down from 5%) + - refactor: Fire effects get added to mobs in a different way now. Maybe it will + get stuck less. Report any oddities. + - rscadd: Freedom Implants and Biodegrade can you free you of the shackles of knotted + shoes. + - rscadd: Shaft Miner's Bluespace Survival Capsules will now throw people away from + it when it deploys. Be sure to heed the warning to "Stand back". + Momo8289: + - rscadd: Fire ants can now be scooped up and used as a chem like normal space ants + Rhials: + - bugfix: You can now refine ectoplasmic raw cores at the implosion machine thing. + - balance: Beacon and Teleport Blocker implants have been moved from cargo to the + security lathe. + - balance: A new research node has been created, requiring Subdermal Implants and + Miniature Bluespace, to unlock Beacon/Teleport Blocker implants. + - balance: Exile implants can now be printed from the security lathe after researching + basic cybernetic implants. + - bugfix: The Energy Cake slice now does its on-eat effect AFTER being eaten, instead + of before. + - bugfix: The beach bar virtual domain's bar closets no longer have default locker + doors. + SyncIt21: + - bugfix: lathes now use moderate power for printing operations + necromanceranne: + - bugfix: The shove blocker module parent type now has the correct typepath. From 0207210990a6651d14498f81b4e47bb6cb60b8bf Mon Sep 17 00:00:00 2001 From: lessthanthree <83487515+lessthnthree@users.noreply.github.com> Date: Sun, 11 Feb 2024 00:39:54 +0000 Subject: [PATCH 086/508] MuleBOT UI/ID Fixes (#81380) ## About The Pull Request - Fixes https://github.com/tgstation/tgstation/issues/81363 - Fixes unable to set MuleBOT home from control panel - Fixes missing MuleBOT ID from Botkeeper PDA app - Adds input validation for changing MuleBOT ID from control panel - Removes hardcoding of MuleBOT IDs and home destinations - MuleBOT will now automatically set its home to the navigation beacon on its initialization turf ## Changelog :cl: LT3 fix: MULEbot will correctly display its loaded cargo on BotKeeper fix: MULEbot home beacon can be set from control panel code: MULEbot home location is automatically set on init /:cl: --- _maps/map_files/Birdshot/birdshot.dmm | 5 +-- .../map_files/Deltastation/DeltaStation2.dmm | 10 ++---- .../map_files/IceBoxStation/IceBoxStation.dmm | 10 ++---- _maps/map_files/tramstation/tramstation.dmm | 20 +++-------- .../mob/living/simple_animal/bot/mulebot.dm | 35 +++++++++++-------- .../file_system/programs/robocontrol.dm | 4 +-- tgui/packages/tgui/interfaces/Mule.jsx | 28 +++++++-------- .../tgui/interfaces/NtosRoboControl.jsx | 3 +- .../Scripts/81380_mulebot_vars.txt | 3 ++ 9 files changed, 50 insertions(+), 68 deletions(-) create mode 100644 tools/UpdatePaths/Scripts/81380_mulebot_vars.txt diff --git a/_maps/map_files/Birdshot/birdshot.dmm b/_maps/map_files/Birdshot/birdshot.dmm index 09563ab18686e..72516d9c396fa 100644 --- a/_maps/map_files/Birdshot/birdshot.dmm +++ b/_maps/map_files/Birdshot/birdshot.dmm @@ -35810,10 +35810,7 @@ location = "QM #1" }, /obj/effect/turf_decal/delivery, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #1"; - suffix = "#1" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/storage) "mKD" = ( diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm index 1ed7706202cbd..bd942b1bd8787 100644 --- a/_maps/map_files/Deltastation/DeltaStation2.dmm +++ b/_maps/map_files/Deltastation/DeltaStation2.dmm @@ -30045,10 +30045,7 @@ location = "QM #1" }, /obj/effect/turf_decal/delivery, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #3"; - suffix = "#3" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/storage) "htQ" = ( @@ -81530,10 +81527,7 @@ id = "warehouse_shutters"; name = "warehouse shutters control" }, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #1"; - suffix = "#1" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/storage) "upB" = ( diff --git a/_maps/map_files/IceBoxStation/IceBoxStation.dmm b/_maps/map_files/IceBoxStation/IceBoxStation.dmm index 5f09aa250d132..efeb6bc25c74e 100644 --- a/_maps/map_files/IceBoxStation/IceBoxStation.dmm +++ b/_maps/map_files/IceBoxStation/IceBoxStation.dmm @@ -58769,10 +58769,7 @@ }, /obj/effect/turf_decal/bot, /obj/machinery/light/small/directional/east, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #2"; - suffix = "#2" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/storage) "rUz" = ( @@ -75177,10 +75174,7 @@ location = "QM #1" }, /obj/effect/turf_decal/bot, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #1"; - suffix = "#1" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/storage) "xaH" = ( diff --git a/_maps/map_files/tramstation/tramstation.dmm b/_maps/map_files/tramstation/tramstation.dmm index 9f005e43e00b2..bd915e60abb13 100644 --- a/_maps/map_files/tramstation/tramstation.dmm +++ b/_maps/map_files/tramstation/tramstation.dmm @@ -36569,10 +36569,7 @@ location = "QM #2" }, /obj/effect/turf_decal/tile/brown/fourcorners, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #2"; - suffix = "#2" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/warehouse) "lQH" = ( @@ -41744,10 +41741,7 @@ location = "QM #6" }, /obj/effect/turf_decal/tile/brown/fourcorners, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #6"; - suffix = "#6" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/warehouse) "nIQ" = ( @@ -47921,10 +47915,7 @@ location = "QM #3" }, /obj/effect/turf_decal/tile/brown/fourcorners, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #3"; - suffix = "#3" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/warehouse) "pXC" = ( @@ -52540,10 +52531,7 @@ location = "QM #1" }, /obj/effect/turf_decal/tile/brown/fourcorners, -/mob/living/simple_animal/bot/mulebot{ - home_destination = "QM #1"; - suffix = "#1" - }, +/mob/living/simple_animal/bot/mulebot, /turf/open/floor/iron, /area/station/cargo/warehouse) "rCL" = ( diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm index 7e3b0ab0c2ee9..7f13a1954ab84 100644 --- a/code/modules/mob/living/simple_animal/bot/mulebot.dm +++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm @@ -79,13 +79,15 @@ cell = new /obj/item/stock_parts/cell/upgraded(src, 2000) - var/static/mulebot_count = 0 - mulebot_count += 1 - set_id(suffix || id || "#[mulebot_count]") - suffix = null AddElement(/datum/element/ridable, /datum/component/riding/creature/mulebot) diag_hud_set_mulebotcell() + set_id(suffix || assign_random_name()) + suffix = null + if(name == "\improper MULEbot") + name = "\improper MULEbot [id]" + set_home(loc) + /mob/living/simple_animal/bot/mulebot/Exited(atom/movable/gone, direction) . = ..() if(gone == load) @@ -138,6 +140,18 @@ /mob/living/simple_animal/bot/mulebot/proc/set_id(new_id) id = new_id +/mob/living/simple_animal/bot/mulebot/proc/set_home(turf/home_loc) + if(!istype(home_loc)) + CRASH("MULEbot [id] was requested to set a home location to [home_loc ? "an invalid home loc ([home_loc.type])" : "null"]") + + var/obj/machinery/navbeacon/home_beacon = locate() in home_loc + if(!isnull(home_beacon)) + home_destination = home_beacon.location + log_transport("[id]: MULEbot successfuly set home location to ID [home_destination] at [home_beacon.x], [home_beacon.y], [home_beacon.z]") + return + + log_transport("[id]: MULEbot failed to set home at [home_loc.x], [home_loc.y], [home_loc.z]") + /mob/living/simple_animal/bot/mulebot/bot_reset() ..() reached_target = FALSE @@ -322,19 +336,12 @@ if(new_dest) set_destination(new_dest) if("setid") - var/new_id - if(pda) - new_id = tgui_input_text(user, "Enter ID", "ID Assignment", id, MAX_NAME_LEN) - else - new_id = params["value"] + var/new_id = tgui_input_text(user, "Enter ID", "ID Assignment", id, MAX_NAME_LEN) if(new_id) set_id(new_id) + name = "\improper MULEbot [new_id]" if("sethome") - var/new_home - if(pda) - new_home = tgui_input_list(user, "Enter Home", "Mulebot Settings", GLOB.deliverybeacontags, home_destination) - else - new_home = params["value"] + var/new_home = tgui_input_list(user, "Enter Home", "Mulebot Settings", GLOB.deliverybeacontags, home_destination) if(new_home) home_destination = new_home if("unload") diff --git a/code/modules/modular_computers/file_system/programs/robocontrol.dm b/code/modules/modular_computers/file_system/programs/robocontrol.dm index 00bd00f3e67b5..68f7ba3ddef05 100644 --- a/code/modules/modular_computers/file_system/programs/robocontrol.dm +++ b/code/modules/modular_computers/file_system/programs/robocontrol.dm @@ -49,6 +49,7 @@ var/mob/living/simple_animal/bot/mulebot/simple_mulebot = simple_bot mulelist += list(list( "name" = simple_mulebot.name, + "id" = simple_mulebot.id, "dest" = simple_mulebot.destination, "power" = simple_mulebot.cell ? simple_mulebot.cell.percent() : 0, "home" = simple_mulebot.home_destination, @@ -56,9 +57,8 @@ "autoPickup" = simple_mulebot.auto_pickup, "reportDelivery" = simple_mulebot.report_delivery, "mule_ref" = REF(simple_mulebot), + "load" = simple_mulebot.get_load_name(), )) - if(simple_mulebot.load) - data["load"] = simple_mulebot.load.name newbot["mule_check"] = TRUE botlist += list(newbot) diff --git a/tgui/packages/tgui/interfaces/Mule.jsx b/tgui/packages/tgui/interfaces/Mule.jsx index b5313664c0e90..1b289be2743fb 100644 --- a/tgui/packages/tgui/interfaces/Mule.jsx +++ b/tgui/packages/tgui/interfaces/Mule.jsx @@ -3,7 +3,6 @@ import { Button, Dropdown, Flex, - Input, LabeledList, ProgressBar, Section, @@ -105,33 +104,32 @@ export const Mule = (props) => { > - act('setid', { value })} - /> + + />
    diff --git a/tgui/packages/tgui-panel/chat/ChatTabs.jsx b/tgui/packages/tgui-panel/chat/ChatTabs.jsx index 2031f6a255b00..3e3880a2aec2f 100644 --- a/tgui/packages/tgui-panel/chat/ChatTabs.jsx +++ b/tgui/packages/tgui-panel/chat/ChatTabs.jsx @@ -39,6 +39,7 @@ export const ChatTabs = (props) => { key={page.id} selected={page === currentPage} rightSlot={ + !page.hideUnreadCount && page.unreadCount > 0 && ( ) diff --git a/tgui/packages/tgui-panel/chat/actions.js b/tgui/packages/tgui-panel/chat/actions.js index 3814fbe611f79..52582a4e299c9 100644 --- a/tgui/packages/tgui-panel/chat/actions.js +++ b/tgui/packages/tgui-panel/chat/actions.js @@ -10,6 +10,7 @@ import { createPage } from './model'; export const loadChat = createAction('chat/load'); export const rebuildChat = createAction('chat/rebuild'); +export const clearChat = createAction('chat/clear'); export const updateMessageCount = createAction('chat/updateMessageCount'); export const addChatPage = createAction('chat/addPage', () => ({ payload: createPage(), diff --git a/tgui/packages/tgui-panel/chat/middleware.js b/tgui/packages/tgui-panel/chat/middleware.js index 0eeb0d6957d74..e973441c7775b 100644 --- a/tgui/packages/tgui-panel/chat/middleware.js +++ b/tgui/packages/tgui-panel/chat/middleware.js @@ -19,6 +19,7 @@ import { addChatPage, changeChatPage, changeScrollTracking, + clearChat, loadChat, rebuildChat, removeChatPage, @@ -190,6 +191,10 @@ export const chatMiddleware = (store) => { chatRenderer.saveToDisk(); return; } + if (type === clearChat.type) { + chatRenderer.clearChat(); + return; + } return next(action); }; }; diff --git a/tgui/packages/tgui-panel/chat/model.js b/tgui/packages/tgui-panel/chat/model.js index fdb5521b505b9..6e10a3d02eef0 100644 --- a/tgui/packages/tgui-panel/chat/model.js +++ b/tgui/packages/tgui-panel/chat/model.js @@ -23,6 +23,7 @@ export const createPage = (obj) => { name: 'New Tab', acceptedTypes: acceptedTypes, unreadCount: 0, + hideUnreadCount: false, createdAt: Date.now(), ...obj, }; diff --git a/tgui/packages/tgui-panel/chat/renderer.jsx b/tgui/packages/tgui-panel/chat/renderer.jsx index 899839e56cc11..0d26061f60b0a 100644 --- a/tgui/packages/tgui-panel/chat/renderer.jsx +++ b/tgui/packages/tgui-panel/chat/renderer.jsx @@ -205,7 +205,7 @@ class ChatRenderer { const highlightWholeMessage = setting.highlightWholeMessage; const matchWord = setting.matchWord; const matchCase = setting.matchCase; - const allowedRegex = /^[a-z0-9_\-$/^[\s\]\\]+$/gi; + const allowedRegex = /^[a-zа-яё0-9_\-$/^[\s\]\\]+$/gi; const regexEscapeCharacters = /[!#$%^&*)(+=.<>{}[\]:;'"|~`_\-\\/]/g; const lines = String(text) .split(',') @@ -562,6 +562,29 @@ class ChatRenderer { }); } + /** + * @clearChat + * @copyright 2023 + * @author Cheffie + * @link https://github.com/CheffieGithub + * @license MIT + */ + clearChat() { + const messages = this.visibleMessages; + this.visibleMessages = []; + for (let i = 0; i < messages.length; i++) { + const message = messages[i]; + this.rootNode.removeChild(message.node); + // Mark this message as pruned + message.node = 'pruned'; + } + // Remove pruned messages from the message array + this.messages = this.messages.filter( + (message) => message.node !== 'pruned', + ); + logger.log(`Cleared chat`); + } + saveToDisk() { // Allow only on IE11 if (Byond.IS_LTE_IE10) { diff --git a/tgui/packages/tgui-panel/chat/replaceInTextNode.js b/tgui/packages/tgui-panel/chat/replaceInTextNode.js index 4c91b604dacf7..7148bfe7aa698 100644 --- a/tgui/packages/tgui-panel/chat/replaceInTextNode.js +++ b/tgui/packages/tgui-panel/chat/replaceInTextNode.js @@ -93,7 +93,7 @@ export const replaceInTextNode = (regex, words, createNode) => (node) => { for (let word of words) { // Capture if the word is at the beginning, end, middle, // or by itself in a message - wordRegexStr += `^${word}\\W|\\W${word}\\W|\\W${word}$|^${word}$`; + wordRegexStr += `^${word}\\s\\W|\\s\\W${word}\\s\\W|\\s\\W${word}$|^${word}\\s\\W$`; // Make sure the last character for the expression is NOT '|' if (++i !== words.length) { wordRegexStr += '|'; diff --git a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx index a7c90af8cc922..230f655181ec3 100644 --- a/tgui/packages/tgui-panel/settings/SettingsPanel.jsx +++ b/tgui/packages/tgui-panel/settings/SettingsPanel.jsx @@ -13,7 +13,6 @@ import { ColorBox, Divider, Dropdown, - Flex, Input, LabeledList, NumberInput, @@ -24,7 +23,7 @@ import { } from 'tgui/components'; import { ChatPageSettings } from '../chat'; -import { rebuildChat, saveChatToDisk } from '../chat/actions'; +import { clearChat, rebuildChat, saveChatToDisk } from '../chat/actions'; import { THEMES } from '../themes'; import { addHighlightSetting, @@ -130,7 +129,6 @@ export const SettingsGeneral = (props) => { content="Custom font" icon={freeFont ? 'lock-open' : 'lock'} color={freeFont ? 'good' : 'bad'} - ml={1} onClick={() => { setFreeFont(!freeFont); }} @@ -140,7 +138,7 @@ export const SettingsGeneral = (props) => { { { - + + +
    ); }; @@ -188,30 +201,28 @@ const TextHighlightSettings = (props) => { const highlightSettings = useSelector(selectHighlightSettings); const dispatch = useDispatch(); return ( -
    -
    - - {highlightSettings.map((id, i) => ( - + + {highlightSettings.map((id, i) => ( + + ))} + {highlightSettings.length < MAX_HIGHLIGHT_SETTINGS && ( + +
    + + )} +