diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 7e57a394988..5e52d25538f 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1919,6 +1919,16 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) return pois +/* + * Returns all player mobs in an assoc list with ckey as key and the mob as value +*/ +/proc/get_assoc_mob_list_by_ckey() + var/list/mob/mobs = list() + for(var/i in GLOB.player_list) + var/mob/M = i + mobs[M.ckey] = M + return mobs + /proc/flash_color(mob_or_client, flash_color="#960000", flash_time=20) var/client/C if(istype(mob_or_client, /mob)) diff --git a/code/_onclick/item_attack.dm b/code/_onclick/item_attack.dm index f88244c0cd8..4fd5b956df5 100644 --- a/code/_onclick/item_attack.dm +++ b/code/_onclick/item_attack.dm @@ -81,7 +81,7 @@ user.do_attack_animation(M) . = M.attacked_by(src, user, def_zone) - add_attack_logs(user, M, "Attacked with [name] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL) + add_attack_logs(user, M, "Attacked with [name] ([uppertext(user.a_intent)]) ([uppertext(damtype)])", (M.ckey && force > 0 && damtype != STAMINA) ? null : ATKLOG_ALMOSTALL) add_fingerprint(user) diff --git a/code/datums/log_record.dm b/code/datums/log_record.dm index 9b7d52b5390..7d127eedc7d 100644 --- a/code/datums/log_record.dm +++ b/code/datums/log_record.dm @@ -8,10 +8,10 @@ /datum/log_record/New(_log_type, _who, _what, _target, _where, _raw_time) log_type = _log_type - - who = get_subject_text(_who) + + who = get_subject_text(_who, _log_type) what = _what - target = get_subject_text(_target) + target = get_subject_text(_target, _log_type) if(!_where) _where = get_turf(_who) where = _where @@ -19,16 +19,31 @@ _raw_time = world.time raw_time = _raw_time -/datum/log_record/proc/get_subject_text(subject) +/datum/log_record/proc/get_subject_text(subject, log_type) if(ismob(subject) || isclient(subject) || istype(subject, /datum/mind)) - return key_name_admin(subject) - if(isatom(subject)) + . = key_name_admin(subject) + if(should_log_health(log_type) && isliving(subject)) + . += get_health_string(subject) + else if(isatom(subject)) var/atom/A = subject - return A.name - if(istype(subject, /datum)) + . = A.name + else if(istype(subject, /datum)) var/datum/D = subject return D.type - return subject + else + . = subject + +/datum/log_record/proc/get_health_string(var/mob/living/L) + var/OX = L.getOxyLoss() > 50 ? "[L.getOxyLoss()]" : L.getOxyLoss() + var/TX = L.getToxLoss() > 50 ? "[L.getToxLoss()]" : L.getToxLoss() + var/BU = L.getFireLoss() > 50 ? "[L.getFireLoss()]" : L.getFireLoss() + var/BR = L.getBruteLoss() > 50 ? "[L.getBruteLoss()]" : L.getBruteLoss() + return " ([L.health]: [OX] - [TX] - [BU] - [BR])" + +/datum/log_record/proc/should_log_health(log_type) + if(log_type == ATTACK_LOG || log_type == DEFENSE_LOG) + return TRUE + return FALSE /proc/compare_log_record(datum/log_record/A, datum/log_record/B) var/time_diff = A.raw_time - B.raw_time diff --git a/code/datums/log_viewer.dm b/code/datums/log_viewer.dm index 67eb3d21d86..72872e93a3c 100644 --- a/code/datums/log_viewer.dm +++ b/code/datums/log_viewer.dm @@ -4,13 +4,13 @@ var/time_from = 0 var/time_to = 4 HOURS // 4 Hours should be enough. INFINITY would screw the UI up var/list/selected_mobs = list() // The mobs in question - var/list/selected_log_types = list() // The log types being searched for + var/list/selected_log_types = ALL_LOGS // The log types being searched for var/list/log_records = list() // Found and sorted records /datum/log_viewer/proc/clear_all() selected_mobs.Cut() - selected_log_types.Cut() + selected_log_types = ALL_LOGS time_from = initial(time_from) time_to = initial(time_to) log_records.Cut() @@ -91,6 +91,14 @@ return start return 0 +/datum/log_viewer/proc/add_mobs(list/mob/mobs) + if(!mobs?.len) + return + for(var/i in mobs) + var/mob/M = i + if(istype(M)) + selected_mobs |= M + /datum/log_viewer/proc/add_mob(mob/user, mob/M) if(!M || !user) return @@ -103,8 +111,8 @@ var/trStyleTop = "border-top:2px solid; border-bottom:2px solid; padding-top: 5px; padding-bottom: 5px;" var/trStyle = "border-top:1px solid; border-bottom:1px solid; padding-top: 5px; padding-bottom: 5px;" var/dat - dat += "" - dat += "
" + dat += "" + dat += "
" dat += "Time Search Range: [gameTimestamp(wtime = time_from)]" dat += " To: [gameTimestamp(wtime = time_to)]" dat += "
" @@ -115,8 +123,9 @@ if(QDELETED(M)) selected_mobs -= i continue - dat += "[M.name]" + dat += "[get_display_name(M)]" dat += "Add Mob" + dat += "Add Mob (by ckey)" dat += "Clear All Mobs" dat += "
" @@ -142,9 +151,9 @@ // Search results var/tdStyleTime = "width:80px; text-align:center;" var/tdStyleType = "width:80px; text-align:center;" - var/tdStyleWho = "width:300px; text-align:center;" + var/tdStyleWho = "width:400px; text-align:center;" var/tdStyleWhere = "width:150px; text-align:center;" - dat += "
" + dat += "
" dat += "" dat += "" for(var/i in log_records) @@ -158,7 +167,7 @@ dat += "
WhenTypeWhoWhatTargetWhere
" dat += "
" - var/datum/browser/popup = new(user, "Log viewer", "Log viewer", 1400, 600) + var/datum/browser/popup = new(user, "Log Viewer", "Log Viewer", 1500, 600) popup.set_content(dat) popup.open() @@ -203,6 +212,11 @@ var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a mob: ", mobs) A.on_close(CALLBACK(src, .proc/add_mob, usr)) return + if(href_list["add_mob_ckey"]) + var/list/mobs = get_assoc_mob_list_by_ckey() + var/datum/async_input/A = input_autocomplete_async(usr, "Please, select a ckey: ", mobs) + A.on_close(CALLBACK(src, .proc/add_mob, usr)) + return if(href_list["remove_mob"]) var/mob/M = locate(href_list["remove_mob"]) if(M) @@ -233,3 +247,11 @@ if(MISC_LOG) return "gray" return "slategray" + +/datum/log_viewer/proc/get_display_name(mob/M) + var/name = M.name + if(M.name != M.real_name) + name = "[name] ([M.real_name])" + if(isobserver(M)) + name = "[name] (DEAD)" + return name diff --git a/code/game/gamemodes/blob/blob_report.dm b/code/game/gamemodes/blob/blob_report.dm index 9c7fb99fe81..8f6ba965222 100644 --- a/code/game/gamemodes/blob/blob_report.dm +++ b/code/game/gamemodes/blob/blob_report.dm @@ -18,7 +18,7 @@ intercepttext += "
Note in the event of a quarantine breach or uncontrolled spread of the biohazard, the directive 7-10 may be upgraded to a directive 7-12.
" intercepttext += "Message ends." if(2) - var/nukecode = rand(10000, 99999) + var/nukecode = "[rand(10000, 99999)]" for(var/obj/machinery/nuclearbomb/bomb in GLOB.machines) if(bomb && bomb.r_code) if(is_station_level(bomb.z)) diff --git a/code/game/gamemodes/nuclear/nuclear.dm b/code/game/gamemodes/nuclear/nuclear.dm index 5fed8ae00f4..8316364781b 100644 --- a/code/game/gamemodes/nuclear/nuclear.dm +++ b/code/game/gamemodes/nuclear/nuclear.dm @@ -53,7 +53,6 @@ proc/issyndicate(mob/living/M as mob) for(var/datum/mind/synd_mind in syndicates) synd_mind.assigned_role = SPECIAL_ROLE_NUKEOPS //So they aren't chosen for other jobs. synd_mind.special_role = SPECIAL_ROLE_NUKEOPS - synd_mind.offstation_role = TRUE return 1 @@ -113,7 +112,7 @@ proc/issyndicate(mob/living/M as mob) if(spawnpos > synd_spawn.len) spawnpos = 2 synd_mind.current.loc = synd_spawn[spawnpos] - + synd_mind.offstation_role = TRUE forge_syndicate_objectives(synd_mind) create_syndicate(synd_mind) greet_syndicate(synd_mind) diff --git a/code/game/machinery/cryopod.dm b/code/game/machinery/cryopod.dm index 36196ea4eca..2e0c4c54375 100644 --- a/code/game/machinery/cryopod.dm +++ b/code/game/machinery/cryopod.dm @@ -302,6 +302,7 @@ // Eject dead people if(occupant.stat == DEAD) go_out() + return // Allow a gap between entering the pod and actually despawning. if(world.time - time_entered < time_till_despawn) diff --git a/code/game/mecha/equipment/tools/mining_tools.dm b/code/game/mecha/equipment/tools/mining_tools.dm index 5916d0f96c7..2248c37f518 100644 --- a/code/game/mecha/equipment/tools/mining_tools.dm +++ b/code/game/mecha/equipment/tools/mining_tools.dm @@ -94,7 +94,7 @@ /obj/item/mecha_parts/mecha_equipment/drill/proc/drill_mob(mob/living/target, mob/user) target.visible_message("[chassis] is drilling [target] with [src]!", "[chassis] is drilling you with [src]!") - add_attack_logs(user, target, "DRILLED with [src] (INTENT: [uppertext(user.a_intent)]) (DAMTYPE: [uppertext(damtype)])") + add_attack_logs(user, target, "DRILLED with [src] ([uppertext(user.a_intent)]) ([uppertext(damtype)])") if(target.stat == DEAD && target.getBruteLoss() >= 200) add_attack_logs(user, target, "gibbed") if(LAZYLEN(target.butcher_results)) diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 63eeb21e165..5962a1eccbb 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -60,7 +60,7 @@ target.visible_message("[chassis] squeezes [target].", \ "[chassis] squeezes [target].",\ "You hear something crack.") - add_attack_logs(chassis.occupant, M, "Squeezed with [src] (INTENT: [uppertext(chassis.occupant.a_intent)]) (DAMTYE: [uppertext(damtype)])") + add_attack_logs(chassis.occupant, M, "Squeezed with [src] ([uppertext(chassis.occupant.a_intent)]) ([uppertext(damtype)])") start_cooldown() else step_away(M,chassis) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 1dc09d13e63..540e1074844 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -870,7 +870,7 @@ return 0 use_power(melee_energy_drain) if(M.damtype == BRUTE || M.damtype == BURN) - add_attack_logs(M.occupant, src, "Mecha-attacked with [M] (INTENT: [uppertext(M.occupant.a_intent)]) (DAMTYPE: [uppertext(M.damtype)])") + add_attack_logs(M.occupant, src, "Mecha-attacked with [M] ([uppertext(M.occupant.a_intent)]) ([uppertext(M.damtype)])") . = ..() /obj/mecha/emag_act(mob/user) diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index 76361776b0a..baaf0cab16b 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -506,7 +506,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /image, image("icon" = 'icons/goonstation/effect "You stab yourself in the eyes with [src]!" \ ) - add_attack_logs(user, M, "Eye-stabbed with [src] (INTENT: [uppertext(user.a_intent)])") + add_attack_logs(user, M, "Eye-stabbed with [src] ([uppertext(user.a_intent)])") if(istype(H)) var/obj/item/organ/internal/eyes/eyes = H.get_int_organ(/obj/item/organ/internal/eyes) diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm index 7cf09199cfd..b497bc518b0 100644 --- a/code/game/objects/items/devices/radio/radio.dm +++ b/code/game/objects/items/devices/radio/radio.dm @@ -411,11 +411,16 @@ GLOBAL_LIST_INIT(default_medbay_channels, list( jobname = "Unknown" voicemask = TRUE + // Copy the message pieces so we can safely edit comms line without affecting the actual line + var/list/message_pieces_copy = list() + for(var/datum/multilingual_say_piece/S in message_pieces) + message_pieces_copy += new /datum/multilingual_say_piece(S.speaking, S.message) + // Make us a message datum! var/datum/tcomms_message/tcm = new tcm.sender_name = displayname tcm.sender_job = jobname - tcm.message_pieces = message_pieces + tcm.message_pieces = message_pieces_copy tcm.source_level = position.z tcm.freq = connection.frequency tcm.vmask = voicemask diff --git a/code/game/objects/items/robot/robot_items.dm b/code/game/objects/items/robot/robot_items.dm index eae59151a38..f1487a09986 100644 --- a/code/game/objects/items/robot/robot_items.dm +++ b/code/game/objects/items/robot/robot_items.dm @@ -29,7 +29,7 @@ "[user] has prodded you with [src]!") playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1) - add_attack_logs(user, M, "Stunned with [src] (INTENT: [uppertext(user.a_intent)])") + add_attack_logs(user, M, "Stunned with [src] ([uppertext(user.a_intent)])") /obj/item/borg/overdrive name = "Overdrive" diff --git a/code/game/objects/items/weapons/storage/firstaid.dm b/code/game/objects/items/weapons/storage/firstaid.dm index 4ff769a7d3f..1e22015bc97 100644 --- a/code/game/objects/items/weapons/storage/firstaid.dm +++ b/code/game/objects/items/weapons/storage/firstaid.dm @@ -205,7 +205,7 @@ if(empty) return new /obj/item/reagent_containers/hypospray/combat(src) - new /obj/item/reagent_containers/applicator/dual(src) // Because you ain't got no time to look at what damage dey taking yo + new /obj/item/reagent_containers/applicator/dual/syndi(src) // Because you ain't got no time to look at what damage dey taking yo new /obj/item/defibrillator/compact/combat/loaded(src) new /obj/item/clothing/glasses/hud/health/night(src) diff --git a/code/game/objects/structures/girders.dm b/code/game/objects/structures/girders.dm index 5fea799ecb0..6d27b21e686 100644 --- a/code/game/objects/structures/girders.dm +++ b/code/game/objects/structures/girders.dm @@ -76,14 +76,14 @@ if(istype(W,/obj/item/stack/rods)) var/obj/item/stack/rods/S = W if(state == GIRDER_DISPLACED) - if(S.get_amount() < 2) - to_chat(user, "You need at least two rods to create a false wall!") + if(S.get_amount() < 5) + to_chat(user, "You need at least five rods to create a false wall!") return to_chat(user, "You start building a reinforced false wall...") if(do_after(user, 20, target = src)) - if(!loc || !S || S.get_amount() < 2) + if(!loc || !S || S.get_amount() < 5) return - S.use(2) + S.use(5) to_chat(user, "You create a false wall. Push on it to open or close the passage.") var/obj/structure/falsewall/iron/FW = new (loc) transfer_fingerprints_to(FW) diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 9aab12234d2..293504919d4 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -103,7 +103,8 @@ GLOBAL_PROTECT(banlist_savefile) // Obvious reasons GLOB.banlist_savefile.cd = "/base" if( GLOB.banlist_savefile.dir.Find("[ckey][computerid]") ) - to_chat(usr, "Ban already exists.") + if(usr) + to_chat(usr, "Ban already exists.") return 0 else GLOB.banlist_savefile.dir.Add("[ckey][computerid]") diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 1eed8083a97..958dedae362 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -78,6 +78,7 @@ GLOBAL_VAR_INIT(nologevent, 0) body += " \[Heal\] " body += "

\[ " + body += "LOGS - " body += "VV - " body += "[ADMIN_TP(M,"TP")] - " if(M.client) diff --git a/code/modules/admin/sql_notes.dm b/code/modules/admin/sql_notes.dm index a438a43eaec..04e28737479 100644 --- a/code/modules/admin/sql_notes.dm +++ b/code/modules/admin/sql_notes.dm @@ -2,7 +2,8 @@ if(checkrights && !check_rights(R_ADMIN|R_MOD)) return if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!target_ckey) @@ -19,7 +20,8 @@ log_game("SQL ERROR obtaining ckey from player table. Error : \[[err]\]\n") return if(!query_find_ckey.NextRow()) - to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.") + if(usr) + to_chat(usr, "[target_ckey] has not been seen before, you can only add notes to known players.") return var/exp_data = query_find_ckey.item[2] @@ -52,8 +54,8 @@ log_game("SQL ERROR adding new note to table. Error : \[[err]\]\n") return if(logged) - log_admin("[key_name(usr)] has added a note to [target_ckey]: [notetext]") - message_admins("[key_name_admin(usr)] has added a note to [target_ckey]:
[notetext]") + log_admin("[usr ? key_name(usr) : adminckey] has added a note to [target_ckey]: [notetext]") + message_admins("[usr ? key_name_admin(usr) : adminckey] has added a note to [target_ckey]:
[notetext]") show_note(target_ckey) /proc/remove_note(note_id) @@ -63,7 +65,8 @@ var/notetext var/adminckey if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!note_id) return @@ -82,15 +85,16 @@ var/err = query_del_note.ErrorMsg() log_game("SQL ERROR removing note from table. Error : \[[err]\]\n") return - log_admin("[key_name(usr)] has removed a note made by [adminckey] from [ckey]: [notetext]") - message_admins("[key_name_admin(usr)] has removed a note made by [adminckey] from [ckey]:
[notetext]") + log_admin("[usr ? key_name(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]: [notetext]") + message_admins("[usr ? key_name_admin(usr) : "Bot"] has removed a note made by [adminckey] from [ckey]:
[notetext]") show_note(ckey) /proc/edit_note(note_id) if(!check_rights(R_ADMIN|R_MOD)) return if(!GLOB.dbcon.IsConnected()) - to_chat(usr, "Failed to establish database connection.") + if(usr) + to_chat(usr, "Failed to establish database connection.") return if(!note_id) return @@ -117,8 +121,8 @@ var/err = query_update_note.ErrorMsg() log_game("SQL ERROR editing note. Error : \[[err]\]\n") return - log_admin("[key_name(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") - message_admins("[key_name_admin(usr)] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") + log_admin("[usr ? key_name(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") + message_admins("[usr ? key_name_admin(usr) : "Bot"] has edited [target_ckey]'s note made by [adminckey] from \"[old_note]\" to \"[new_note]\"") show_note(target_ckey) /proc/show_note(target_ckey, index, linkless = 0) diff --git a/code/modules/admin/topic.dm b/code/modules/admin/topic.dm index 19bbc40ba25..1d2c75aff0a 100644 --- a/code/modules/admin/topic.dm +++ b/code/modules/admin/topic.dm @@ -911,6 +911,11 @@ //M.client = null qdel(M.client) + else if(href_list["open_logging_view"]) + var/mob/M = locateUID(href_list["open_logging_view"]) + if(ismob(M)) + usr.client.open_logging_view(list(M), TRUE) + //Player Notes else if(href_list["addnote"]) var/target_ckey = href_list["addnote"] diff --git a/code/modules/admin/verbs/logging_view.dm b/code/modules/admin/verbs/logging_view.dm index 903926f4923..cafaab8f1d7 100644 --- a/code/modules/admin/verbs/logging_view.dm +++ b/code/modules/admin/verbs/logging_view.dm @@ -2,10 +2,20 @@ GLOBAL_LIST_INIT(open_logging_views, list()) /client/proc/cmd_admin_open_logging_view() set category = "Admin" - set name = "Open Logging View" + set name = "Logging View" set desc = "Opens the detailed logging viewer" + open_logging_view() - if(!GLOB.open_logging_views[usr.client.ckey]) - GLOB.open_logging_views[usr.client.ckey] = new /datum/log_viewer() - var/datum/log_viewer/LV = GLOB.open_logging_views[usr.client.ckey] - LV.show_ui(usr) +/client/proc/open_logging_view(list/mob/mobs_to_add = null, clear_view = FALSE) + var/datum/log_viewer/cur_view = GLOB.open_logging_views[usr.client.ckey] + if(!cur_view) + cur_view = new /datum/log_viewer() + GLOB.open_logging_views[usr.client.ckey] = cur_view + else if(clear_view) + cur_view.clear_all() + + if(mobs_to_add?.len) + cur_view.add_mobs(mobs_to_add) + + cur_view.show_ui(usr) + diff --git a/code/modules/client/preference/loadout/loadout_accessories.dm b/code/modules/client/preference/loadout/loadout_accessories.dm index 8805c6dd9a9..b9318fd9e8c 100644 --- a/code/modules/client/preference/loadout/loadout_accessories.dm +++ b/code/modules/client/preference/loadout/loadout_accessories.dm @@ -154,9 +154,6 @@ display_name = "corset, blue" path = /obj/item/clothing/accessory/corset/blue -/datum/gear/accessory/armband/job - subtype_path = /datum/gear/accessory/armband/job - subtype_cost_overlap = FALSE /datum/gear/accessory/armband_red display_name = "armband" @@ -166,41 +163,41 @@ display_name = "armband, blue-yellow" path = /obj/item/clothing/accessory/armband/yb -/datum/gear/accessory/armband/job/sec +/datum/gear/accessory/armband_job + subtype_path = /datum/gear/accessory/armband_job + subtype_cost_overlap = FALSE + +/datum/gear/accessory/armband_job/sec display_name = " armband, security" path = /obj/item/clothing/accessory/armband/sec allowed_roles = list("Head of Security", "Warden", "Detective", "Security Officer", "Brig Physician", "Security Pod Pilot") -/datum/gear/accessory/armband/job/cargo +/datum/gear/accessory/armband_job/cargo display_name = "cargo armband" path = /obj/item/clothing/accessory/armband/cargo allowed_roles = list("Quartermaster","Cargo Technician", "Shaft Miner") -/datum/gear/accessory/armband/job/medical +/datum/gear/accessory/armband_job/medical display_name = "armband, medical" path = /obj/item/clothing/accessory/armband/med allowed_roles = list("Chief Medical Officer", "Medical Doctor", "Coroner", "Paramedic", "Brig Physician") -/datum/gear/accessory/armband/job/emt +/datum/gear/accessory/armband_job/emt display_name = "armband, EMT" path = /obj/item/clothing/accessory/armband/medgreen allowed_roles = list("Paramedic", "Brig Physician") -/datum/gear/accessory/armband/job/engineering +/datum/gear/accessory/armband_job/engineering display_name = "armband, engineering" path = /obj/item/clothing/accessory/armband/engine allowed_roles = list("Chief Engineer","Station Engineer", "Life Support Specialist") -/datum/gear/accessory/armband/job/hydro +/datum/gear/accessory/armband_job/hydro display_name = "armband, hydroponics" path = /obj/item/clothing/accessory/armband/hydro allowed_roles = list("Botanist") -/datum/gear/accessory/armband/job/sci +/datum/gear/accessory/armband_job/sci display_name = "armband, science" path = /obj/item/clothing/accessory/armband/science allowed_roles = list("Research Director","Scientist", "Roboticist") - - - - diff --git a/code/modules/client/preference/loadout/loadout_hat.dm b/code/modules/client/preference/loadout/loadout_hat.dm index 758e70f654f..4993c8502ea 100644 --- a/code/modules/client/preference/loadout/loadout_hat.dm +++ b/code/modules/client/preference/loadout/loadout_hat.dm @@ -108,50 +108,47 @@ display_name = "cowboy hat, pink" path = /obj/item/clothing/head/cowboyhat/pink -//Berets Refactor -//Defining all Job Berets as no-overlap -//Somehow this doesn't break the DB. -/datum/gear/hat/beret/job - subtype_path = /datum/gear/hat/beret/job - subtype_cost_overlap = FALSE - -/datum/gear/hat/beret/purple +/datum/gear/hat/beret_purple display_name = "beret, purple" path = /obj/item/clothing/head/beret/purple_normal -/datum/gear/hat/beret/black +/datum/gear/hat/beret_black display_name = "beret, black" path = /obj/item/clothing/head/beret/black -/datum/gear/hat/beret/blue +/datum/gear/hat/beret_blue display_name = "beret, blue" path = /obj/item/clothing/head/beret/blue -/datum/gear/hat/beret/red +/datum/gear/hat/beret_red display_name = "beret, red" path = /obj/item/clothing/head/beret -/datum/gear/hat/beret/job/sec +/datum/gear/hat/beret_job + subtype_path = /datum/gear/hat/beret_job + subtype_cost_overlap = FALSE + +/datum/gear/hat/beret_job/sec display_name = "security beret" path = /obj/item/clothing/head/beret/sec allowed_roles = list("Head of Security", "Warden", "Security Officer", "Security Pod Pilot") -/datum/gear/hat/beret/job/sci +/datum/gear/hat/beret_job/sci display_name = "science beret" path = /obj/item/clothing/head/beret/sci allowed_roles = list("Research Director", "Scientist") -/datum/gear/hat/beret/job/med +/datum/gear/hat/beret_job/med display_name = "medical beret" path = /obj/item/clothing/head/beret/med allowed_roles = list("Chief Medical Officer", "Medical Doctor" , "Virologist", "Brig Physician" , "Coroner") -/datum/gear/hat/beret/job/eng +/datum/gear/hat/beret_job/eng display_name = "engineering beret" path = /obj/item/clothing/head/beret/eng allowed_roles = list("Chief Engineer", "Station Engineer") -/datum/gear/hat/beret/job/atmos +/datum/gear/hat/beret_job/atmos display_name = "atmospherics beret" path = /obj/item/clothing/head/beret/atmos allowed_roles = list("Chief Engineer", "Life Support Specialist") diff --git a/code/modules/events/mass_hallucination.dm b/code/modules/events/mass_hallucination.dm index b8bde0a1a67..b8f45023a27 100644 --- a/code/modules/events/mass_hallucination.dm +++ b/code/modules/events/mass_hallucination.dm @@ -6,6 +6,9 @@ var/mob/living/carbon/human/H = thing if(H.stat == DEAD) continue + var/turf/T = get_turf(H) + if(!is_station_level(T.z)) + continue var/armor = H.getarmor(type = "rad") if((RADIMMUNE in H.dna.species.species_traits) || armor >= 75) // Leave radiation-immune species/rad armored players completely unaffected continue diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm index 905b629a0b9..21b99acb910 100644 --- a/code/modules/flufftext/Hallucination.dm +++ b/code/modules/flufftext/Hallucination.dm @@ -149,10 +149,12 @@ Gunshots/explosions/opening doors/less rare audio (done) /obj/effect/hallucination/fake_flood/process() if(!target) qdel(src) + return if(next_expand <= world.time) radius++ if(radius > FAKE_FLOOD_MAX_RADIUS) qdel(src) + return Expand() if((get_turf(target) in flood_turfs) && !target.internal) target.hallucinate("fake_alert", "too_much_tox") diff --git a/code/modules/hydroponics/grown.dm b/code/modules/hydroponics/grown.dm index 5ad8a5f32f4..bad8813ea01 100644 --- a/code/modules/hydroponics/grown.dm +++ b/code/modules/hydroponics/grown.dm @@ -117,6 +117,7 @@ /obj/item/reagent_containers/food/snacks/grown/throw_impact(atom/hit_atom) if(!..()) //was it caught by a mob? if(seed) + log_action(thrownby, hit_atom, "Thrown [src] at") for(var/datum/plant_gene/trait/T in seed.genes) T.on_throw_impact(src, hit_atom) if(seed.get_gene(/datum/plant_gene/trait/squash)) @@ -147,11 +148,11 @@ qdel(src) -/obj/item/reagent_containers/food/snacks/grown/On_Consume() - if(iscarbon(usr)) +/obj/item/reagent_containers/food/snacks/grown/On_Consume(mob/M, mob/user) + if(iscarbon(M)) if(seed) for(var/datum/plant_gene/trait/T in seed.genes) - T.on_consume(src, usr) + T.on_consume(src, M) ..() /obj/item/reagent_containers/food/snacks/grown/after_slip(mob/living/carbon/human/H) @@ -190,3 +191,16 @@ else return ..() +/obj/item/reagent_containers/food/snacks/grown/proc/log_action(mob/user, atom/target, what_done) + var/reagent_str = reagents.log_list() + var/genes_str = "No genes" + if(seed && length(seed.genes)) + var/list/plant_gene_names = list() + for(var/thing in seed.genes) + var/datum/plant_gene/G = thing + if(G.dangerous) + plant_gene_names += G.name + genes_str = english_list(plant_gene_names) + + add_attack_logs(user, target, "[what_done] ([reagent_str] | [genes_str])") + diff --git a/code/modules/hydroponics/plant_genes.dm b/code/modules/hydroponics/plant_genes.dm index a4c6fe7280d..c2f4914351c 100644 --- a/code/modules/hydroponics/plant_genes.dm +++ b/code/modules/hydroponics/plant_genes.dm @@ -1,5 +1,7 @@ /datum/plant_gene var/name + /// Used to determine if the trait should be logged when the holder is used + var/dangerous = FALSE /datum/plant_gene/proc/get_name() // Used for manipulator display and gene disk name. return name @@ -194,6 +196,7 @@ name = "Liquid Contents" examine_line = "It has a lot of liquid contents inside." origin_tech = list("biotech" = 5) + dangerous = TRUE /datum/plant_gene/trait/slip // Makes plant slippery, unless it has a grown-type trash. Then the trash gets slippery. @@ -201,6 +204,7 @@ name = "Slippery Skin" rate = 0.1 examine_line = "It has a very slippery skin." + dangerous = TRUE /datum/plant_gene/trait/slip/on_new(obj/item/reagent_containers/food/snacks/grown/G, newloc) . = ..() @@ -224,6 +228,7 @@ name = "Electrical Activity" rate = 0.2 origin_tech = list("powerstorage" = 5) + dangerous = TRUE /datum/plant_gene/trait/cell_charge/on_slip(obj/item/reagent_containers/food/snacks/grown/G, mob/living/carbon/C) var/power = G.seed.potency*rate @@ -298,6 +303,7 @@ name = "Bluespace Activity" rate = 0.1 origin_tech = list("bluespace" = 5) + dangerous = TRUE /datum/plant_gene/trait/teleport/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target) if(isliving(target)) @@ -385,6 +391,7 @@ /datum/plant_gene/trait/stinging name = "Hypodermic Prickles" + dangerous = TRUE /datum/plant_gene/trait/stinging/on_throw_impact(obj/item/reagent_containers/food/snacks/grown/G, atom/target) if(isliving(target) && G.reagents && G.reagents.total_volume) @@ -398,6 +405,7 @@ /datum/plant_gene/trait/smoke name = "gaseous decomposition" + dangerous = TRUE /datum/plant_gene/trait/smoke/on_squash(obj/item/reagent_containers/food/snacks/grown/G, atom/target) var/datum/effect_system/smoke_spread/chem/S = new diff --git a/code/modules/mining/lavaland/loot/ashdragon_loot.dm b/code/modules/mining/lavaland/loot/ashdragon_loot.dm index a0ea3eb7639..21bc247b0df 100644 --- a/code/modules/mining/lavaland/loot/ashdragon_loot.dm +++ b/code/modules/mining/lavaland/loot/ashdragon_loot.dm @@ -120,18 +120,13 @@ return var/mob/living/carbon/human/H = user - var/random = rand(1,3) + var/random = rand(1,2) switch(random) if(1) to_chat(user, "Your flesh begins to melt! Miraculously, you seem fine otherwise.") H.set_species(/datum/species/skeleton) if(2) - to_chat(user, "Power courses through you! You can now shift your form at will.") - if(user.mind) - var/obj/effect/proc_holder/spell/targeted/shapeshift/dragon/D = new - user.mind.AddSpell(D) - if(3) to_chat(user, "You feel like you could walk straight through lava now.") H.weather_immunities |= "lava" diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index 10c2ac8f119..b69c13987b9 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -308,14 +308,14 @@ m_type = 1 if("bow", "bows") - if(!buckled) + if(!restrained()) var/M = handle_emote_param(param) message = "[src] bows[M ? " to [M]" : ""]." m_type = 1 if("salute", "salutes") - if(!buckled) + if(!restrained()) var/M = handle_emote_param(param) message = "[src] salutes[M ? " to [M]" : ""]." diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index b38f1819524..1f0f1cfa0f4 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -113,7 +113,7 @@ var/armor = run_armor_check(zone, "melee", "Your armor has protected your [parse_zone(zone)].", "Your armor has softened hit to your [parse_zone(zone)].", I.armour_penetration) apply_damage(I.throwforce, dtype, zone, armor, is_sharp(I), I) if(I.thrownby) - add_attack_logs(I.thrownby, src, "Hit with thrown [I]") + add_attack_logs(I.thrownby, src, "Hit with thrown [I]", !I.throwforce ? ATKLOG_ALMOSTALL : null) // Only message if the person gets damages else return 1 else diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index 3276416fe7f..3419279ffe5 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -95,6 +95,10 @@ for(var/mob/A in searched_for) . += A +// All bee sprites are made up of overlays. They do not have any special sprite overlays for items placed on them, such as collars, so this proc is unneeded. +/mob/living/simple_animal/hostile/poison/bees/regenerate_icons() + return + /mob/living/simple_animal/hostile/poison/bees/proc/generate_bee_visuals() overlays.Cut() diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm index 80b10ab3a8b..bead9c935ef 100644 --- a/code/modules/power/singularity/narsie.dm +++ b/code/modules/power/singularity/narsie.dm @@ -127,6 +127,8 @@ /obj/singularity/narsie/proc/acquire(var/mob/food) if(food == target) return + if(!target) + return to_chat(target, "[uppertext(SSticker.cultdat.entity_name)] HAS LOST INTEREST IN YOU") target = food if(ishuman(target)) diff --git a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm index f2740676314..abdf0305934 100644 --- a/code/modules/reagents/chemistry/recipes/pyrotechnics.dm +++ b/code/modules/reagents/chemistry/recipes/pyrotechnics.dm @@ -56,7 +56,6 @@ /datum/chemical_reaction/nitroglycerin name = "Nitroglycerin" id = "nitroglycerin" - result = "nitroglycerin" required_reagents = list("glycerol" = 1, "facid" = 1, "sacid" = 1) result_amount = 2 diff --git a/code/modules/reagents/reagent_containers/applicator.dm b/code/modules/reagents/reagent_containers/applicator.dm index 73c3c3759b1..962f12b4a08 100644 --- a/code/modules/reagents/reagent_containers/applicator.dm +++ b/code/modules/reagents/reagent_containers/applicator.dm @@ -5,6 +5,7 @@ icon_state = "mender" item_state = "mender" volume = 200 + possible_transfer_amounts = null resistance_flags = ACID_PROOF container_type = REFILLABLE | AMOUNT_VISIBLE temperature_min = 270 @@ -93,6 +94,20 @@ playsound(get_turf(src), pick('sound/goonstation/items/mender.ogg', 'sound/goonstation/items/mender2.ogg'), 50, 1) +/obj/item/reagent_containers/applicator/verb/empty() + set name = "Empty Applicator" + set category = "Object" + set src in usr + + if(usr.incapacitated()) + return + if(alert(usr, "Are you sure you want to empty [src]?", "Empty Applicator:", "Yes", "No") != "Yes") + return + if(!usr.incapacitated() && isturf(usr.loc) && loc == usr) + to_chat(usr, "You empty [src] onto the floor.") + reagents.reaction(usr.loc) + reagents.clear_reagents() + /obj/item/reagent_containers/applicator/brute name = "brute auto-mender" list_reagents = list("styptic_powder" = 200) @@ -104,3 +119,6 @@ /obj/item/reagent_containers/applicator/dual name = "dual auto-mender" list_reagents = list("synthflesh" = 200) + +/obj/item/reagent_containers/applicator/dual/syndi // It magically goes through hardsuits. Don't ask how. + ignore_flags = TRUE diff --git a/code/modules/reagents/reagent_containers/glass_containers.dm b/code/modules/reagents/reagent_containers/glass_containers.dm index 1e42899111b..175ae757df4 100644 --- a/code/modules/reagents/reagent_containers/glass_containers.dm +++ b/code/modules/reagents/reagent_containers/glass_containers.dm @@ -47,6 +47,9 @@ reagents.reaction(M, REAGENT_TOUCH) reagents.clear_reagents() else + if(!iscarbon(M)) // Non-carbons can't process reagents + to_chat(user, "You cannot find a way to feed [M].") + return if(M != user) M.visible_message("[user] attempts to feed something to [M].", \ "[user] attempts to feed something to you.") diff --git a/goon/browserassets/js/browserOutput.js b/goon/browserassets/js/browserOutput.js index 40a69245188..3cac81ca8e2 100644 --- a/goon/browserassets/js/browserOutput.js +++ b/goon/browserassets/js/browserOutput.js @@ -166,30 +166,79 @@ function emojiparse(el) { } } -// Colorizes the highlight spans -function setHighlightColor(match) { - match.style.background = opts.highlightColor +// Recolorizes the highlight spans +function setHighlightColor() { + var highlightspans = document.getElementsByClassName("highlight") + for(var i in highlightspans){ + highlightspans[i].setAttribute("style","background-color:"+opts.highlightColor) + } +} + +function escapeRegexCharacters(input){ //escapes any characters that could be interpreted as regex patterns, potentially causing patterns to break if not escaped + return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } //Highlights words based on user settings function highlightTerms(el) { - if(regexHasError) return; //just stop right there ig the regex is gonna except + + if (regexHasError) return; //just stop right there ig the regex is gonna except + + function highlightRecursor(element, term){ //recursor function to do the highlighting proper + var regex = new RegExp(term, "gi"); + + function replace(str) { + return str.replace(regex, '$&'); + } + + var s = ''; + var work = element.innerHTML; + var ind = 0; + + while(ind < work.length) { + + var next_term = work.substring(ind).search(regex); + if(next_term != -1) next_term += ind; + var next_tag = work.indexOf('<', ind); + if(next_tag == -1) { + s+=replace(work.substring(ind)); + break; + } + else if(next_term==-1) { + s += work.substring(ind); + break; + } + else if(next_tag < next_term) { + var temp = work.indexOf('>', next_tag); + s += work.substring(ind,temp+1); + ind = temp+1; + } + else { + s += replace(work.substring(ind, next_tag)); + ind = next_tag; + } + } + + element.innerHTML = s; + } + for (var i = 0; i < opts.highlightTerms.length; i++) { //Each highlight term if(opts.highlightTerms[i]) { if(!opts.highlightRegexEnable){ - if(el.innerText.toString().toLowerCase().includes(opts.highlightTerms[i].toLowerCase())) //match normally - el.innerHTML = ''+el.innerHTML+'' //encloseincludes - continue; + var innerTerms = opts.highlightTerms[i].split(" ") + for(var a in innerTerms){ + highlightRecursor(el, escapeRegexCharacters(innerTerms[a])) + } } - var rexp; - try{ - rexp = new RegExp(opts.highlightTerms[i],"gmi") - } catch(e){ - el.innerHTML+='
Your highlight regex - '+opts.highlightTerms[i]+' - is malformed. Thrown exception: '+e+'' - regexHasError = true; - return; + else { + try{ + new RegExp(opts.highlightTerms[i], "gmi"); // check to make sure the pattern wont cause issues + } catch(e){ + el.innerHTML += '
Your highlight regex pattern -- ' + opts.highlightTerms[i] + ' -- is malformed.
Your highlights have been disabled until they are next edited
Thrown exception: '+e+'
'; + regexHasError = true; + return; + } + highlightRecursor(el, opts.highlightTerms[i]); } - el.innerHTML = el.innerHTML.replace(rexp,"$0") //i cant figure out a proper, non snowflakey way to let people select the group that gets highlighted } } } @@ -1043,15 +1092,21 @@ $(function() { count++; } - var color = $('#highlightColor').val(); opts.highlightRegexEnable = document.querySelector("#highlightRegexEnable").checked - color = color.trim(); - if (color == '' || color.charAt(0) != '#') { - opts.highlightColor = '#FFFF00'; - } else { - opts.highlightColor = color; + + var color = $('#highlightColor').val(); + if(color != opts.highlightColor) { // did the color even change? + color = color.trim(); + if (color == '' || color.charAt(0) != '#') { + opts.highlightColor = '#FFFF00'; + } else { + opts.highlightColor = color; + } + setHighlightColor(); } + regexHasError = false; //they changed the regex so it might be valid now + internalOutput('Highlights have been updated.',"internal") // simplest way to test if pattern works, why reinvent the wheel? var $popup = $('#highlightPopup').closest('.popup'); $popup.remove(); diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 8cc9b0c818e..84cab5eb253 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -1241,9 +1241,9 @@ bluebird@^3.5.5: integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== + version "4.11.9" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" + integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== body-parser@1.19.0: version "1.19.0" @@ -2198,9 +2198,9 @@ electron-to-chromium@^1.3.390: integrity sha512-DbCBdwtARI0l3e3m6ZIxVaTNahb6dSsmGjuag/twiVcWuM4MSpL5IfsJsJSyqLqxosE/m0CXlZaBmxegQW/dAg== elliptic@^6.0.0: - version "6.5.2" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.2.tgz#05c5678d7173c049d8ca433552224a495d0e3762" - integrity sha512-f4x70okzZbIQl/NSRLkI/+tteV/9WqL98zx+SQ69KbXxmVrmjwsNUPn/gYJJ0sHvEak24cZgHIPegRePAtA/xw== + version "6.5.3" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" + integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== dependencies: bn.js "^4.4.0" brorand "^1.0.1"