diff --git a/code/__DEFINES/uplink.dm b/code/__DEFINES/uplink.dm
index cdfdf4680f7929..929b558dfec475 100644
--- a/code/__DEFINES/uplink.dm
+++ b/code/__DEFINES/uplink.dm
@@ -36,7 +36,7 @@
/// Typepath used for uplink items which don't actually produce an item (essentially just a placeholder)
/// Future todo: Make this not necessary / make uplink items support item-less items natively
-#define ABSTRACT_UPLINK_ITEM /obj/effect/gibspawner/generic
+#define ABSTRACT_UPLINK_ITEM /obj/item/loot_table_maker
/// Lower threshold for which an uplink items's TC cost is considered "low" for spy bounties picking rewards
#define SPY_LOWER_COST_THRESHOLD 5
diff --git a/code/_onclick/hud/radial.dm b/code/_onclick/hud/radial.dm
index dc425083ad8960..3bd370120b3a42 100644
--- a/code/_onclick/hud/radial.dm
+++ b/code/_onclick/hud/radial.dm
@@ -317,14 +317,14 @@ GLOBAL_LIST_EMPTY(radial_menus)
current_page = WRAP(current_page + 1,1,pages+1)
update_screen_objects()
-/datum/radial_menu/proc/show_to(mob/M)
+/datum/radial_menu/proc/show_to(mob/M, offset_x = 0, offset_y = 0)
if(current_user)
hide()
if(!M.client || !anchor)
return
current_user = M.client
//Blank
- menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing", layer = RADIAL_BACKGROUND_LAYER)
+ menu_holder = image(icon='icons/effects/effects.dmi',loc=anchor,icon_state="nothing", layer = RADIAL_BACKGROUND_LAYER, pixel_x = offset_x, pixel_y = offset_y)
SET_PLANE_EXPLICIT(menu_holder, ABOVE_HUD_PLANE, M)
menu_holder.appearance_flags |= KEEP_APART|RESET_ALPHA|RESET_COLOR|RESET_TRANSFORM
menu_holder.vis_contents += elements + close_button
@@ -356,7 +356,7 @@ GLOBAL_LIST_EMPTY(radial_menus)
Choices should be a list where list keys are movables or text used for element names and return value
and list values are movables/icons/images used for element icons
*/
-/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, radial_slice_icon = "radial_slice", autopick_single_option = TRUE, entry_animation = TRUE, click_on_hover = FALSE)
+/proc/show_radial_menu(mob/user, atom/anchor, list/choices, uniqueid, radius, datum/callback/custom_check, require_near = FALSE, tooltips = FALSE, no_repeat_close = FALSE, radial_slice_icon = "radial_slice", autopick_single_option = TRUE, entry_animation = TRUE, click_on_hover = FALSE, user_space = FALSE)
if(!user || !anchor || !length(choices))
return
@@ -379,11 +379,18 @@ GLOBAL_LIST_EMPTY(radial_menus)
menu.radius = radius
if(istype(custom_check))
menu.custom_check_callback = custom_check
- menu.anchor = anchor
+ menu.anchor = user_space ? user : anchor
menu.radial_slice_icon = radial_slice_icon
menu.check_screen_border(user) //Do what's needed to make it look good near borders or on hud
menu.set_choices(choices, tooltips, click_on_hover)
- menu.show_to(user)
+ var/offset_x = 0
+ var/offset_y = 0
+ if (user_space)
+ var/turf/user_turf = get_turf(user)
+ var/turf/anchor_turf = get_turf(anchor)
+ offset_x = (anchor_turf.x - user_turf.x) * world.icon_size + anchor.pixel_x - user.pixel_x
+ offset_y = (anchor_turf.y - user_turf.y) * world.icon_size + anchor.pixel_y - user.pixel_y
+ menu.show_to(user, offset_x, offset_y)
menu.wait(user, anchor, require_near)
var/answer = menu.selected_choice
qdel(menu)
diff --git a/code/datums/components/callouts.dm b/code/datums/components/callouts.dm
index 24e7f081fbe78a..98d489cc915a97 100644
--- a/code/datums/components/callouts.dm
+++ b/code/datums/components/callouts.dm
@@ -111,7 +111,7 @@
for(var/datum/callout_option/callout_option as anything in callout_options)
callout_items[callout_option] = image(icon = 'icons/hud/radial.dmi', icon_state = callout_option::icon_state)
- var/datum/callout_option/selection = show_radial_menu(user, get_turf(clicked_atom), callout_items, entry_animation = FALSE, click_on_hover = TRUE)
+ var/datum/callout_option/selection = show_radial_menu(user, get_turf(clicked_atom), callout_items, entry_animation = FALSE, click_on_hover = TRUE, user_space = TRUE)
if (!selection)
return
diff --git a/code/datums/components/uplink.dm b/code/datums/components/uplink.dm
index a5ff46964bf6fa..d831ba2f0d964a 100644
--- a/code/datums/components/uplink.dm
+++ b/code/datums/components/uplink.dm
@@ -227,9 +227,12 @@
for(var/datum/uplink_item/item as anything in uplink_handler.extra_purchasable)
if(item.stock_key in stock_list)
extra_purchasable_stock[REF(item)] = stock_list[item.stock_key]
+ var/atom/actual_item = item.item
extra_purchasable += list(list(
"id" = item.type,
"name" = item.name,
+ "icon" = actual_item.icon,
+ "icon_state" = actual_item.icon_state,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category ? initial(item.category.name) : null,
@@ -288,6 +291,11 @@
return
item = SStraitor.uplink_items_by_type[item_path]
uplink_handler.purchase_item(ui.user, item, parent)
+ if("buy_raw_tc")
+ if (uplink_handler.telecrystals <= 0)
+ return
+ var/desired_amount = tgui_input_number(ui.user, "How many raw telecrystals to buy?", "Buy Raw TC", default = uplink_handler.telecrystals, max_value = uplink_handler.telecrystals)
+ uplink_handler.purchase_raw_tc(ui.user, desired_amount, parent)
if("lock")
if(!lockable)
return TRUE
diff --git a/code/game/objects/effects/particles/fire.dm b/code/game/objects/effects/particles/fire.dm
index 9d90d0d29c29a4..9904685807364b 100644
--- a/code/game/objects/effects/particles/fire.dm
+++ b/code/game/objects/effects/particles/fire.dm
@@ -33,3 +33,21 @@
drift = generator(GEN_VECTOR, list(-0.1,0), list(0.1,0.025), UNIFORM_RAND)
spin = generator(GEN_NUM, list(-15,15), NORMAL_RAND)
scale = generator(GEN_VECTOR, list(0.5,0.5), list(2,2), NORMAL_RAND)
+
+/particles/embers/spark
+ count = 3
+ spawning = 2
+ gradient = list("#FBAF4D", "#FCE6B6", "#FFFFFF")
+ lifespan = 1.5 SECONDS
+ fade = 1 SECONDS
+ fadein = 0.1 SECONDS
+ grow = -0.1
+ velocity = generator(GEN_CIRCLE, 3, 3, SQUARE_RAND)
+ position = generator(GEN_SPHERE, 0, 0, LINEAR_RAND)
+ scale = generator(GEN_VECTOR, list(0.5, 0.5), list(1,1), NORMAL_RAND)
+ drift = list(0)
+
+/particles/embers/spark/severe
+ count = 10
+ spawning = 5
+ gradient = list("#FCE6B6", "#FFFFFF")
diff --git a/code/game/objects/effects/particles/smoke.dm b/code/game/objects/effects/particles/smoke.dm
index 27249c65a683e2..776c90534a957b 100644
--- a/code/game/objects/effects/particles/smoke.dm
+++ b/code/game/objects/effects/particles/smoke.dm
@@ -84,3 +84,16 @@
grow = 0.05
spin = 2
color = "#fcffff77"
+
+/particles/smoke/cyborg
+ count = 5
+ spawning = 1
+ lifespan = 1 SECONDS
+ fade = 1.8 SECONDS
+ position = list(0, 0, 0)
+ scale = list(0.5, 0.5)
+ grow = 0.1
+
+/particles/smoke/cyborg/heavy_damage
+ lifespan = 0.8 SECONDS
+ fade = 0.8 SECONDS
diff --git a/code/game/objects/items/tools/weldingtool.dm b/code/game/objects/items/tools/weldingtool.dm
index b2b0109c04c882..fb40a70cbeea0e 100644
--- a/code/game/objects/items/tools/weldingtool.dm
+++ b/code/game/objects/items/tools/weldingtool.dm
@@ -145,22 +145,30 @@
if(user.combat_mode)
return NONE
+ return try_heal_loop(interacting_with, user)
+
+/obj/item/weldingtool/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE)
var/mob/living/carbon/human/attacked_humanoid = interacting_with
var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected))
if(isnull(affecting) || !IS_ROBOTIC_LIMB(affecting))
return NONE
- var/use_delay = 0
+ if (!affecting.get_damage())
+ return
+ user.visible_message(span_notice("[user] starts to fix some of the dents on [attacked_humanoid == user ? user.p_their() : "[attacked_humanoid]'s"] [affecting.name]."),
+ span_notice("You start fixing some of the dents on [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
+ var/use_delay = repeating ? 1 SECONDS : 0
if(user == attacked_humanoid)
- user.visible_message(span_notice("[user] starts to fix some of the dents on [attacked_humanoid]'s [affecting.name]."),
- span_notice("You start fixing some of the dents on [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
use_delay = 5 SECONDS
if(!use_tool(attacked_humanoid, user, use_delay, volume=50, amount=1))
return ITEM_INTERACT_BLOCKING
- attacked_humanoid.item_heal(user, brute_heal = 15, burn_heal = 0, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC)
+ if (!attacked_humanoid.item_heal(user, brute_heal = 15, burn_heal = 0, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC))
+ return ITEM_INTERACT_BLOCKING
+
+ INVOKE_ASYNC(src, PROC_REF(try_heal_loop), interacting_with, user, TRUE)
return ITEM_INTERACT_SUCCESS
/obj/item/weldingtool/afterattack(atom/target, mob/user, click_parameters)
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 895660fafa44c1..8dc8d82ff5f7de 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -356,6 +356,7 @@
if((shove_flags & SHOVE_KNOCKDOWN_BLOCKED) || !(shove_flags & SHOVE_BLOCKED))
return
target.Knockdown(SHOVE_KNOCKDOWN_TABLE)
+ target.apply_status_effect(/datum/status_effect/next_shove_stuns)
target.visible_message(span_danger("[shover.name] shoves [target.name] onto \the [src]!"),
span_userdanger("You're shoved onto \the [src] by [shover.name]!"), span_hear("You hear aggressive shuffling followed by a loud thud!"), COMBAT_MESSAGE_RANGE, shover)
to_chat(shover, span_danger("You shove [target.name] onto \the [src]!"))
diff --git a/code/modules/admin/verbs/lua/lua_editor.dm b/code/modules/admin/verbs/lua/lua_editor.dm
index c0b37fd87c1ec9..a28ae8b40f19bf 100644
--- a/code/modules/admin/verbs/lua/lua_editor.dm
+++ b/code/modules/admin/verbs/lua/lua_editor.dm
@@ -75,6 +75,7 @@
if(last_error)
data["lastError"] = last_error
last_error = null
+ data["supressRuntimes"] = current_state.supress_runtimes
data["states"] = list()
for(var/datum/lua_state/state as anything in SSlua.states)
data["states"] += state.display_name
@@ -228,7 +229,7 @@
if(result["status"] == "error")
last_error = result["message"]
arguments.Cut()
- return TRUE
+ return
if("resumeTask")
var/task_index = params["index"]
SSlua.queue_resume(current_state, task_index, arguments)
@@ -261,6 +262,9 @@
if("toggleShowGlobalTable")
show_global_table = !show_global_table
return TRUE
+ if("toggleSupressRuntimes")
+ current_state.supress_runtimes = !current_state.supress_runtimes
+ return TRUE
if("nextPage")
page = min(page+1, CEILING(current_state.log.len/50, 1)-1)
return TRUE
diff --git a/code/modules/admin/verbs/lua/lua_state.dm b/code/modules/admin/verbs/lua/lua_state.dm
index b3ede122384448..37a60f1fdb5e14 100644
--- a/code/modules/admin/verbs/lua/lua_state.dm
+++ b/code/modules/admin/verbs/lua/lua_state.dm
@@ -24,6 +24,9 @@ GLOBAL_PROTECT(lua_state_stack)
/// Whether the timer.lua script has been included into this lua context state.
var/timer_enabled = FALSE
+ /// Whether to supress logging BYOND runtimes for this state.
+ var/supress_runtimes = FALSE
+
/// Callbacks that need to be ran on next tick
var/list/functions_to_execute = list()
diff --git a/code/modules/antagonists/traitor/uplink_handler.dm b/code/modules/antagonists/traitor/uplink_handler.dm
index f78ddb0247892a..2d27f3c4a0eff4 100644
--- a/code/modules/antagonists/traitor/uplink_handler.dm
+++ b/code/modules/antagonists/traitor/uplink_handler.dm
@@ -126,6 +126,21 @@
on_update()
return TRUE
+/datum/uplink_handler/proc/purchase_raw_tc(mob/user, amount, atom/movable/source)
+ if(shop_locked)
+ return FALSE
+ if(telecrystals < amount)
+ return FALSE
+
+ telecrystals -= amount
+ var/tcs = new /obj/item/stack/telecrystal(get_turf(user), amount)
+ user.put_in_hands(tcs)
+
+ log_uplink("[key_name(user)] purchased [amount] raw telecrystals from [source]'s uplink")
+ on_update()
+ return TRUE
+
+
/// Generates objectives for this uplink handler
/datum/uplink_handler/proc/generate_objectives()
var/potential_objectives_left = maximum_potential_objectives - (length(potential_objectives) + length(active_objectives))
diff --git a/code/modules/asset_cache/assets/uplink.dm b/code/modules/asset_cache/assets/uplink.dm
index e85ee1b35b5c13..35a907a234dfa3 100644
--- a/code/modules/asset_cache/assets/uplink.dm
+++ b/code/modules/asset_cache/assets/uplink.dm
@@ -18,10 +18,13 @@
for(var/datum/uplink_item/item_path as anything in subtypesof(/datum/uplink_item))
var/datum/uplink_item/item = new item_path()
+ var/atom/actual_item = item.item
if(item.item) {
items += list(list(
"id" = item_path,
"name" = item.name,
+ "icon" = actual_item.icon,
+ "icon_state" = actual_item.icon_state,
"cost" = item.cost,
"desc" = item.desc,
"category" = item.category ? initial(item.category.name) : null,
diff --git a/code/modules/bitrunning/components/avatar_connection.dm b/code/modules/bitrunning/components/avatar_connection.dm
index 51263c339319eb..abf3a7809fcda2 100644
--- a/code/modules/bitrunning/components/avatar_connection.dm
+++ b/code/modules/bitrunning/components/avatar_connection.dm
@@ -20,7 +20,7 @@
help_text,
)
- if(!isliving(parent) || !isliving(old_body) || !server.is_operational || !pod.is_operational)
+ if(!isliving(parent) || !isliving(old_body) || !old_mind || !server.is_operational || !pod.is_operational)
return COMPONENT_INCOMPATIBLE
var/mob/living/avatar = parent
@@ -66,6 +66,9 @@
if(alias && avatar.real_name != alias)
avatar.fully_replace_character_name(avatar.real_name, alias)
+ for(var/skill_type in old_mind.known_skills)
+ avatar.mind.set_experience(skill_type, old_mind.get_skill_exp(skill_type), silent = TRUE)
+
avatar.playsound_local(avatar, 'sound/magic/blink.ogg', 25, TRUE)
avatar.set_static_vision(2 SECONDS)
avatar.set_temp_blindness(1 SECONDS) // I'm in
@@ -281,6 +284,10 @@
if(isnull(old_mind) || isnull(old_body))
return
+ for(var/skill_type in avatar.mind.known_skills)
+ old_mind.set_experience(skill_type, avatar.mind.get_skill_exp(skill_type), silent = TRUE)
+ avatar.mind.set_experience(skill_type, 0, silent = TRUE)
+
ghost.mind = old_mind
if(old_body.stat != DEAD)
old_mind.transfer_to(old_body, force_key_move = TRUE)
diff --git a/code/modules/cargo/supplypod.dm b/code/modules/cargo/supplypod.dm
index 86ee482a9109d9..69b9342007c41d 100644
--- a/code/modules/cargo/supplypod.dm
+++ b/code/modules/cargo/supplypod.dm
@@ -421,18 +421,22 @@
insert(turf_underneath, holder)
/obj/structure/closet/supplypod/insert(atom/to_insert, atom/movable/holder)
- if(insertion_allowed(to_insert))
- if(isturf(to_insert))
- var/turf/turf_to_insert = to_insert
- turfs_in_cargo += turf_to_insert.type
- turf_to_insert.ScrapeAway()
- else
- var/atom/movable/movable_to_insert = to_insert
- movable_to_insert.forceMove(holder)
- return TRUE
- else
+ if(!insertion_allowed(to_insert))
return FALSE
+ if(isturf(to_insert))
+ var/turf/turf_to_insert = to_insert
+ turfs_in_cargo += turf_to_insert.type
+ turf_to_insert.ScrapeAway()
+ return TRUE
+
+ var/atom/movable/movable_to_insert = to_insert
+ if (ismob(movable_to_insert))
+ var/mob/mob_to_insert = movable_to_insert
+ if (!isnull(mob_to_insert.buckled))
+ mob_to_insert.buckled.unbuckle_mob(mob_to_insert, force = TRUE)
+ movable_to_insert.forceMove(holder)
+
/obj/structure/closet/supplypod/insertion_allowed(atom/to_insert)
if(to_insert.invisibility == INVISIBILITY_ABSTRACT)
return FALSE
diff --git a/code/modules/fishing/sources/_fish_source.dm b/code/modules/fishing/sources/_fish_source.dm
index feaa93424f9a88..4a0419f98f55f4 100644
--- a/code/modules/fishing/sources/_fish_source.dm
+++ b/code/modules/fishing/sources/_fish_source.dm
@@ -57,6 +57,11 @@ GLOBAL_LIST_INIT(specific_fish_icons, zebra_typecacheof(list(
var/explosive_malus = FALSE
/// If explosive_malus is true, this will be used to keep track of the turfs where an explosion happened for when we'll spawn the loot.
var/list/exploded_turfs
+ /// Mindless mobs that can fish will never pull up items on this list
+ var/static/list/profound_fisher_blacklist = typecacheof(list(
+ /mob/living/basic/mining/lobstrosity,
+ /obj/structure/closet/crate/necropolis/tendril,
+ ))
/datum/fish_source/New()
if(!PERFORM_ALL_TESTS(focus_only/fish_sources_tables))
@@ -276,6 +281,9 @@ GLOBAL_LIST(fishing_property_cache)
var/list/fish_list_properties = collect_fish_properties()
+
+ if(HAS_TRAIT(fisherman, TRAIT_PROFOUND_FISHER) && !fisherman.client)
+ final_table -= profound_fisher_blacklist
for(var/result in final_table)
final_table[result] *= rod.hook?.get_hook_bonus_multiplicative(result)
final_table[result] += rod.hook?.get_hook_bonus_additive(result)//Decide on order here so it can be multiplicative
diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm
index 3a8cec66efd6fb..5c4384302b8d69 100644
--- a/code/modules/mob/living/silicon/robot/death.dm
+++ b/code/modules/mob/living/silicon/robot/death.dm
@@ -21,18 +21,30 @@
else
logevent("FATAL -- SYSTEM HALT")
modularInterface.shutdown_computer()
+ eye_flash_timer = addtimer(CALLBACK(src, PROC_REF(flash_headlamp)), 2 SECONDS, TIMER_STOPPABLE | TIMER_LOOP)
. = ..()
locked = FALSE //unlock cover
if(!QDELETED(builtInCamera) && builtInCamera.camera_enabled)
builtInCamera.toggle_cam(src,0)
- toggle_headlamp(TRUE) //So borg lights are disabled when killed.
+ toggle_headlamp(TRUE) //So borg lights are disabled when killed.
drop_all_held_items() // particularly to ensure sight modes are cleared
-
update_icons()
-
unbuckle_all_mobs(TRUE)
-
SSblackbox.ReportDeath(src)
+
+/mob/living/silicon/robot/proc/flash_headlamp()
+ if(eye_lights)
+ eye_lights = null
+ regenerate_icons()
+ return
+
+ eye_lights = new()
+ eye_lights.icon_state = "[model.special_light_key ? "[model.special_light_key]":"[model.cyborg_base_icon]"]_e_r"
+ eye_lights.color = COLOR_WHITE
+ SET_PLANE_EXPLICIT(eye_lights, ABOVE_GAME_PLANE, src)
+ eye_lights.icon = icon
+ regenerate_icons()
+ add_overlay(eye_lights)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 3fcf652cc6abb0..4871370d16c3d0 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -583,6 +583,7 @@
/mob/living/silicon/robot/updatehealth()
..()
+ update_damage_particles()
if(!model.breakable_modules)
return
@@ -680,6 +681,9 @@
builtInCamera.toggle_cam(src, 0)
if(full_heal_flags & HEAL_ADMIN)
locked = TRUE
+ if(eye_flash_timer)
+ deltimer(eye_flash_timer)
+ eye_flash_timer = null
src.set_stat(CONSCIOUS)
notify_ai(AI_NOTIFICATION_NEW_BORG)
toggle_headlamp(FALSE, TRUE) //This will reenable borg headlamps if doomsday is currently going on still.
diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm
index 549762fd7a943d..16da8d89783af7 100644
--- a/code/modules/mob/living/silicon/robot/robot_defense.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defense.dm
@@ -186,6 +186,52 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
return ..()
+#define LOW_DAMAGE_UPPER_BOUND 1/3
+#define MODERATE_DAMAGE_UPPER_BOUND 2/3
+
+/mob/living/silicon/robot/proc/update_damage_particles()
+ var/brute_percent = bruteloss / maxHealth
+ var/burn_percent = fireloss / maxHealth
+
+ if (brute_percent > MODERATE_DAMAGE_UPPER_BOUND)
+ if(!smoke_particles)
+ smoke_particles = new(src, /particles/smoke/cyborg/heavy_damage, PARTICLE_ATTACH_MOB)
+ else if(!istype(smoke_particles.particles, /particles/smoke/cyborg/heavy_damage)) //TODO: needs to be darker
+ QDEL_NULL(smoke_particles)
+ smoke_particles = new(src, /particles/smoke/cyborg/heavy_damage, PARTICLE_ATTACH_MOB)
+
+ else if (brute_percent > LOW_DAMAGE_UPPER_BOUND)
+ if(!smoke_particles)
+ smoke_particles = new(src, /particles/smoke/cyborg, PARTICLE_ATTACH_MOB)
+ else if(!istype(smoke_particles.particles, /particles/smoke/cyborg))
+ QDEL_NULL(smoke_particles)
+ smoke_particles = new(src, /particles/smoke/cyborg, PARTICLE_ATTACH_MOB)
+
+ else
+ if(smoke_particles)
+ QDEL_NULL(smoke_particles)
+
+ if (burn_percent > MODERATE_DAMAGE_UPPER_BOUND)
+ if(!spark_particles)
+ spark_particles = new(src, /particles/embers/spark/severe, PARTICLE_ATTACH_MOB)
+ else if(!istype(spark_particles.particles, /particles/embers/spark/severe)) //TODO: needs to be more dramatic
+ QDEL_NULL(spark_particles)
+ spark_particles = new(src, /particles/embers/spark/severe, PARTICLE_ATTACH_MOB)
+
+ else if (burn_percent > LOW_DAMAGE_UPPER_BOUND)
+ if(!spark_particles)
+ spark_particles = new(src, /particles/embers/spark, PARTICLE_ATTACH_MOB)
+ else if(!istype(spark_particles.particles, /particles/embers/spark))
+ QDEL_NULL(spark_particles)
+ spark_particles = new(src, /particles/embers/spark, PARTICLE_ATTACH_MOB)
+
+ else
+ if(spark_particles)
+ QDEL_NULL(spark_particles)
+
+#undef LOW_DAMAGE_UPPER_BOUND
+#undef MODERATE_DAMAGE_UPPER_BOUND
+
/mob/living/silicon/robot/attack_alien(mob/living/carbon/alien/adult/user, list/modifiers)
if (LAZYACCESS(modifiers, RIGHT_CLICK))
if(body_position == STANDING_UP)
@@ -401,7 +447,7 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real
if(stat != DEAD)
adjustBruteLoss(30)
else
- investigate_log("has been gibbed a blob.", INVESTIGATE_DEATHS)
+ investigate_log("has been gibbed by a blob.", INVESTIGATE_DEATHS)
gib(DROP_ALL_REMAINS)
return TRUE
diff --git a/code/modules/mob/living/silicon/robot/robot_defines.dm b/code/modules/mob/living/silicon/robot/robot_defines.dm
index 33026d70921fbe..e6af597bc60b70 100644
--- a/code/modules/mob/living/silicon/robot/robot_defines.dm
+++ b/code/modules/mob/living/silicon/robot/robot_defines.dm
@@ -57,6 +57,8 @@
// Overlay for borg eye lights
var/mutable_appearance/eye_lights
+ ///Holds a reference to the timer taking care of blinking lights on dead cyborgs
+ var/eye_flash_timer = null
// Overlay for borg hat
var/mutable_appearance/hat_overlay
@@ -115,6 +117,10 @@
var/low_power_mode = FALSE
///So they can initialize sparks whenever/N
var/datum/effect_system/spark_spread/spark_system
+ ///Smoke particle holder for brute damage
+ var/obj/effect/abstract/particle_holder/smoke_particles = null
+ ///Spark particle holder for burn damage
+ var/obj/effect/abstract/particle_holder/spark_particles = null
///Jetpack-like effect.
var/ionpulse = FALSE
diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm
index 78ff7e9952bd10..cf91aaf482c224 100644
--- a/code/modules/mod/mod_control.dm
+++ b/code/modules/mod/mod_control.dm
@@ -537,7 +537,7 @@
radial_anchor = get_turf(user.loc) //they're phased out via some module, anchor the radial on the turf so it may still display
if (!isnull(anchor_override))
radial_anchor = anchor_override
- var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = isnull(anchor_override), tooltips = TRUE)
+ var/pick = show_radial_menu(user, radial_anchor, items, custom_check = FALSE, require_near = isnull(anchor_override), tooltips = TRUE, user_space = !isnull(anchor_override))
if(!pick)
return
var/module_reference = display_names[pick]
diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm
index a8e20cde8c7a0b..65b4f017229598 100644
--- a/code/modules/power/cable.dm
+++ b/code/modules/power/cable.dm
@@ -565,22 +565,42 @@ GLOBAL_LIST_INIT(wire_node_generating_types, typecacheof(list(/obj/structure/gri
// General procedures
///////////////////////////////////
//you can use wires to heal robotics
-/obj/item/stack/cable_coil/attack(mob/living/carbon/human/H, mob/user)
- if(!istype(H))
- return ..()
- var/obj/item/bodypart/affecting = H.get_bodypart(check_zone(user.zone_selected))
- if(affecting && IS_ROBOTIC_LIMB(affecting))
- if(user == H)
- user.visible_message(span_notice("[user] starts to fix some of the wires in [H]'s [affecting.name]."), span_notice("You start fixing some of the wires in [H == user ? "your" : "[H]'s"] [affecting.name]."))
- if(!do_after(user, 5 SECONDS, H))
- return
- if(H.item_heal(user, 0, 15, "dents", "burnt wires", BODYTYPE_ROBOTIC))
- use(1)
+/obj/item/stack/cable_coil/interact_with_atom(atom/interacting_with, mob/living/user, list/modifiers)
+ if(!ishuman(interacting_with))
+ return NONE
+
+ if(user.combat_mode)
+ return NONE
+
+ return try_heal_loop(interacting_with, user)
+
+/obj/item/stack/cable_coil/proc/try_heal_loop(atom/interacting_with, mob/living/user, repeating = FALSE)
+ var/mob/living/carbon/human/attacked_humanoid = interacting_with
+ var/obj/item/bodypart/affecting = attacked_humanoid.get_bodypart(check_zone(user.zone_selected))
+ if(isnull(affecting) || !IS_ROBOTIC_LIMB(affecting))
+ return NONE
+
+ if (!affecting.get_damage())
return
- else
- return ..()
+ user.visible_message(span_notice("[user] starts to fix some of the wires in [attacked_humanoid == user ? user.p_their() : "[attacked_humanoid]'s"] [affecting.name]."),
+ span_notice("You start fixing some of the wires in [attacked_humanoid == user ? "your" : "[attacked_humanoid]'s"] [affecting.name]."))
+
+ var/use_delay = repeating ? 1 SECONDS : 0
+ if(user == attacked_humanoid)
+ use_delay = 5 SECONDS
+
+ if(!do_after(user, use_delay, attacked_humanoid))
+ return ITEM_INTERACT_BLOCKING
+
+ if (!attacked_humanoid.item_heal(user, brute_heal = 0, burn_heal = 15, heal_message_brute = "dents", heal_message_burn = "burnt wires", required_bodytype = BODYTYPE_ROBOTIC))
+ return ITEM_INTERACT_BLOCKING
+
+ if (use(1) && amount > 0)
+ INVOKE_ASYNC(src, PROC_REF(try_heal_loop), interacting_with, user, TRUE)
+
+ return ITEM_INTERACT_SUCCESS
///////////////////////////////////////////////
// Cable laying procedures
diff --git a/code/modules/uplink/uplink_items/bundle.dm b/code/modules/uplink/uplink_items/bundle.dm
index dae6166d49cebd..b6cdc2fd3d6578 100644
--- a/code/modules/uplink/uplink_items/bundle.dm
+++ b/code/modules/uplink/uplink_items/bundle.dm
@@ -40,18 +40,7 @@
// Don't add telecrystals to the purchase_log since
// it's just used to buy more items (including itself!)
purchase_log_vis = FALSE
-
-/datum/uplink_item/bundles_tc/telecrystal/five
- name = "5 Raw Telecrystals"
- desc = "Five telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
- item = /obj/item/stack/telecrystal/five
- cost = 5
-
-/datum/uplink_item/bundles_tc/telecrystal/twenty
- name = "20 Raw Telecrystals"
- desc = "Twenty telecrystals in their rawest and purest form; can be utilized on active uplinks to increase their telecrystal count."
- item = /obj/item/stack/telecrystal/twenty
- cost = 20
+ purchasable_from = NONE
/datum/uplink_item/bundles_tc/bundle_a
name = "Syndi-kit Tactical"
diff --git a/html/changelogs/AutoChangeLog-pr-85443.yml b/html/changelogs/AutoChangeLog-pr-85443.yml
deleted file mode 100644
index e3459099c07d13..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85443.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-author: "SmArtKar"
-delete-after: True
-changes:
- - bugfix: "Bamboo staves can now be wielded"
- - bugfix: "Bostaff no longer disappears forever when wielded"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-85445.yml b/html/changelogs/AutoChangeLog-pr-85445.yml
deleted file mode 100644
index 918cfdb676eb61..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85445.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "FlufflesTheDog"
-delete-after: True
-changes:
- - bugfix: "Existing gas flow meters have been recalled and replaced due to a faulty screen connection, and once again convey pressure and temperature"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-85448.yml b/html/changelogs/AutoChangeLog-pr-85448.yml
deleted file mode 100644
index 270d19314e31ae..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85448.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "carlarctg"
-delete-after: True
-changes:
- - rscadd: "Adds a bronze dimensional theme"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-85454.yml b/html/changelogs/AutoChangeLog-pr-85454.yml
deleted file mode 100644
index f6b0ce36ed3774..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85454.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Majkl-J"
-delete-after: True
-changes:
- - bugfix: "Flashdarks now broduce darkness upon toggling"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-85457.yml b/html/changelogs/AutoChangeLog-pr-85457.yml
deleted file mode 100644
index 9fdd6e881a5323..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85457.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "SmArtKar"
-delete-after: True
-changes:
- - bugfix: "Rice hat no longer disappears upon being toggled and can be raised back up. Toggling sprites is now done by alt-clicking"
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-85462.yml b/html/changelogs/AutoChangeLog-pr-85462.yml
deleted file mode 100644
index 2f30fe9f45dba6..00000000000000
--- a/html/changelogs/AutoChangeLog-pr-85462.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-author: "Jacquerel"
-delete-after: True
-changes:
- - admin: "Adds some missing traits to the mob trait list in VV"
\ No newline at end of file
diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml
index 7b10ef0da9589b..27ac50a47b3a12 100644
--- a/html/changelogs/archive/2024-08.yml
+++ b/html/changelogs/archive/2024-08.yml
@@ -99,3 +99,46 @@
- rscadd: 'durathread robes can now store botany gear
:cl:'
+2024-08-02:
+ FlufflesTheDog:
+ - bugfix: Existing gas flow meters have been recalled and replaced due to a faulty
+ screen connection, and once again convey pressure and temperature
+ Ghommie:
+ - qol: Skills are passed down to bitrunning avatars and then back to the original
+ body.
+ - bugfix: Stop clientless lobstrosities from fishing other lobstrosities, which
+ in turn can fish other lobstrosities and so on.
+ - balance: Stop clientless lobstrosities from fishing the lavaland fishing spot
+ chest.
+ Jacquerel:
+ - admin: Adds some missing traits to the mob trait list in VV
+ MTandi:
+ - qol: new uplink UI
+ - qol: made it possible to buy a custom amount of TC, instead of bundles with fixed
+ amounts
+ Majkl-J:
+ - bugfix: Flashdarks now broduce darkness upon toggling
+ SmArtKar:
+ - bugfix: Bamboo staves can now be wielded
+ - bugfix: Bostaff no longer disappears forever when wielded
+ - bugfix: Rice hat no longer disappears upon being toggled and can be raised back
+ up. Toggling sprites is now done by alt-clicking
+ - qol: Callouts and MODsuit quick module pickers now track user
+ - qol: Cable coil and welding tool healing now loops similarly to sutures/meshes
+ - bugfix: Fixed cable coil/welding tool heal message not displaying when healing
+ someone else
+ - bugfix: Putting people you're fireman carrying into contractor pods no longer
+ sends both of you to zelda's shadow realm
+ Tattle:
+ - qol: dead cyborgs now blink yellow lights
+ - qol: damaged cyborgs have smoke particles when they've taken brute damage, and
+ sparks for burn
+ Y0SH1M4S73R:
+ - admin: The layout of the lua editor has been tweaked slightly.
+ - admin: In the lua editor, you can now toggle whether to log runtimes the viewed
+ state is involved in.
+ carlarctg:
+ - rscadd: Adds a bronze dimensional theme
+ necromanceranne:
+ - bugfix: Shoving someone onto a table now causes them to become vulnerable to being
+ stunned.
diff --git a/icons/mob/silicon/robots.dmi b/icons/mob/silicon/robots.dmi
index 5c79a0f95e39bd..c2780ce26abea7 100644
Binary files a/icons/mob/silicon/robots.dmi and b/icons/mob/silicon/robots.dmi differ
diff --git a/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx b/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
index e64673ed5a5968..078f3e8026fa89 100644
--- a/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
+++ b/tgui/packages/tgui/interfaces/AntagInfoMalf.tsx
@@ -183,6 +183,8 @@ export const AntagInfoMalf = (props) => {
items.push({
id: item.name,
name: item.name,
+ icon: item.icon,
+ icon_state: item.icon_state,
category: category.name,
cost: `${item.cost} PT`,
desc: item.desc,
diff --git a/tgui/packages/tgui/interfaces/LuaEditor/Log.tsx b/tgui/packages/tgui/interfaces/LuaEditor/Log.tsx
index b4f2b82bb3e9f6..be0730a350df11 100644
--- a/tgui/packages/tgui/interfaces/LuaEditor/Log.tsx
+++ b/tgui/packages/tgui/interfaces/LuaEditor/Log.tsx
@@ -120,8 +120,8 @@ export const Log = (props: LogProps) => {
}`
: ''}
.
-
- {return_values.length ? (
+ {return_values.length ? (
+
{
})
}
/>
- ) : (
-
- )}
-
+
+ ) : (
+
+ )}
>
);
messageColor = 'green';
@@ -187,7 +187,7 @@ export const Log = (props: LogProps) => {
if (chunk) {
output = (
<>
- {output}
+ {output}
+ }
+ >
+
+ {item.desc}
+
+
+ )}
+
+
))}
diff --git a/tgui/packages/tgui/interfaces/Uplink/PrimaryObjectiveMenu.tsx b/tgui/packages/tgui/interfaces/Uplink/PrimaryObjectiveMenu.tsx
index ee6e81160d3c38..f0364e88d9fdb4 100644
--- a/tgui/packages/tgui/interfaces/Uplink/PrimaryObjectiveMenu.tsx
+++ b/tgui/packages/tgui/interfaces/Uplink/PrimaryObjectiveMenu.tsx
@@ -12,19 +12,17 @@ export const PrimaryObjectiveMenu = (props: PrimaryObjectiveMenuProps) => {
const { act } = useBackend();
const { primary_objectives, final_objective, can_renegotiate } = props;
return (
-
-
-
- {
- 'Agent, your Primary Objectives are as follows. Complete these at all costs.'
- }
-
-
- {
- 'Completing Secondary Objectives may allow you to aquire additional equipment.'
- }
-
-
+
+
+ WELCOME, AGENT.
+
+
+ Your Primary Objectives are as follows. Complete these at all costs.
+
+
+ Completing Secondary Objectives may allow you to aquire additional
+ equipment.
+
{final_objective && (
{
)}
-
-
- {primary_objectives.map((prim_obj, index) => (
-
-
-
- ))}
-
-
+
+ {primary_objectives.map((prim_obj, index) => (
+
+
+
+ ))}
+
{!!can_renegotiate && (
{
/>
)}
+
+ SyndOS Version 3.17
+ Connection Secure
+
);
};
diff --git a/tgui/packages/tgui/interfaces/Uplink/calculateDangerLevel.tsx b/tgui/packages/tgui/interfaces/Uplink/calculateDangerLevel.tsx
index e8951e367a9c25..d67dd37391e3e3 100644
--- a/tgui/packages/tgui/interfaces/Uplink/calculateDangerLevel.tsx
+++ b/tgui/packages/tgui/interfaces/Uplink/calculateDangerLevel.tsx
@@ -90,7 +90,7 @@ export const dangerLevelsTooltip = (
color="white"
className={value.gradient}
style={{
- borderRadius: '5px',
+ borderRadius: '2px',
display: 'inline-block',
}}
px={0.8}
@@ -137,11 +137,12 @@ export const calculateDangerLevel = (
color="white"
className={dangerLevel.gradient}
style={{
- borderRadius: '5px',
+ borderRadius: '2px',
display: 'inline-block',
}}
px={0.8}
py={0.6}
+ textAlign="center"
>
{dangerLevel.title} ({displayedProgression})
diff --git a/tgui/packages/tgui/interfaces/Uplink/index.tsx b/tgui/packages/tgui/interfaces/Uplink/index.tsx
index 4949fc94ee7f5b..3ed7d34a214ec9 100644
--- a/tgui/packages/tgui/interfaces/Uplink/index.tsx
+++ b/tgui/packages/tgui/interfaces/Uplink/index.tsx
@@ -7,6 +7,7 @@ import {
Box,
Button,
Dimmer,
+ NoticeBox,
Section,
Stack,
Tabs,
@@ -27,6 +28,8 @@ import { PrimaryObjectiveMenu } from './PrimaryObjectiveMenu';
type UplinkItem = {
id: string;
name: string;
+ icon: string;
+ icon_state: string;
cost: number;
desc: string;
category: string;
@@ -90,6 +93,8 @@ type ServerData = {
type ItemExtraData = Item & {
extraData: {
ref?: string;
+ icon: string;
+ icon_state: string;
};
};
@@ -198,7 +203,6 @@ export class Uplink extends Component<{}, UplinkState> {
shop_locked,
} = data;
const { allItems, allCategories, currentTab } = this.state as UplinkState;
-
const itemsToAdd = [...allItems];
const items: ItemExtraData[] = [];
itemsToAdd.push(...extra_purchasable);
@@ -224,19 +228,21 @@ export class Uplink extends Component<{}, UplinkState> {
items.push({
id: item.id,
name: item.name,
+ icon: item.icon,
+ icon_state: item.icon_state,
category: item.category,
desc: (
-
- {item.desc}
+ <>
+ {item.desc}
{(item.lock_other_purchases && (
-
+
Taking this item will lock you from further purchasing from the
marketplace. Additionally, if you have already purchased an
item, you will not be able to purchase this.
-
+
)) ||
null}
-
+ >
),
cost: (
@@ -259,6 +265,8 @@ export class Uplink extends Component<{}, UplinkState> {
(item.lock_other_purchases && purchased_items > 0),
extraData: {
ref: item.ref,
+ icon: item.icon,
+ icon_state: item.icon_state,
},
});
}
@@ -274,25 +282,14 @@ export class Uplink extends Component<{}, UplinkState> {
// Round it and convert it into a percentage
progressionPercentage = Math.round(progressionPercentage * 1000) / 10;
return (
-
-
-
-
-
-
-
-
- SyndOS Version 3.17
-
- Connection Secure
-
-
-
- WELCOME, AGENT.
-
-
-
-
+
+
+
+ {!!has_progression && (
+
+
+
+
{
}
>
{/* If we have no progression,
- just give them a generic title */}
+ just give them a generic title */}
{has_progression
? calculateDangerLevel(progression_points, false)
: calculateDangerLevel(dangerDefault, false)}
-
-
- {telecrystals} TC
-
-
-
-
-
-
-
-
-
-
- {!!has_objectives && (
- <>
- this.setState({ currentTab: 0 })}
- >
- Primary Objectives
-
- this.setState({ currentTab: 1 })}
- >
- Secondary Objectives
-
- >
- )}
- this.setState({ currentTab: 2 })}
- >
- Market
-
-
-
- {!!lockable && (
-
- act('lock')}
- />
- )}
-
-
-
+
+
+
+ {!!has_objectives && (
+ <>
+ this.setState({ currentTab: 0 })}
+ >
+ Primary Objectives
+
+ this.setState({ currentTab: 1 })}
+ >
+ Secondary Objectives
+
+ >
+ )}
+ this.setState({ currentTab: 2 })}
+ >
+ Market
+
+
+
+
+ {!!lockable && (
+
+ act('lock')}
+ >
+ Lock
+
+
+ )}
+
+
+
+ )}
{(currentTab === 0 && has_objectives && (
{
handleRequestObjectives={() => act('regenerate_objectives')}
/>
)) || (
-
+ <>
{
@@ -471,7 +483,7 @@ export class Uplink extends Component<{}, UplinkState> {
)) ||
null}
-
+ >
)}