diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 41cd2923af08..33ddd50f5df8 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -788,7 +788,7 @@ Each role inherits the lower role's responsibilities (IE: Headcoders also have c
`Headcoders` are the overarching "administrators" of the repository. People included in this role are:
* [farie82](https://github.com/farie82)
-* [Charliminator](https://github.com/hal9000PR)
+* [S34N](https://github.com/S34NW)
* [SteelSlayer](https://github.com/SteelSlayer)
---
@@ -797,21 +797,21 @@ Each role inherits the lower role's responsibilities (IE: Headcoders also have c
* [AffectedArc07](https://github.com/AffectedArc07)
+* [Charliminator](https://github.com/hal9000PR)
* [lewcc](https://github.com/lewcc)
-* [S34N](https://github.com/S34NW)
---
`Review Team` members are people who are denoted as having reviews which can affect mergeability status. People included in this role are:
-* [lewcc](https://github.com/lewcc)
-* [S34N](https://github.com/S34NW)
-* [Sirryan2002](https://github.com/Sirryan2002)
-* [Contrabang](https://github.com/Contrabang)
* [Burzah](https://github.com/Burzah)
+* [Charliminator](https://github.com/hal9000PR)
+* [Contrabang](https://github.com/Contrabang)
* [DGamerL](https://github.com/DGamerL)
-* [Warriorstar](https://github.com/warriorstar-orion)
* [Henri215](https://github.com/Henri215)
+* [lewcc](https://github.com/lewcc)
+* [Sirryan2002](https://github.com/Sirryan2002)
+* [Warriorstar](https://github.com/warriorstar-orion)
---
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a294c4b493c4..c02f49ef71f6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -37,6 +37,7 @@ jobs:
python tools/ci/unticked_files.py ${GITHUB_WORKSPACE}
python tools/ci/illegal_dme_files.py ${GITHUB_WORKSPACE}
python -m tools.ci.check_icon_conflicts
+ python -m tools.ci.check_icon_dupenames
python -m tools.maplint.source --github
DREAMCHECKER_EXIT_CODE=0
~/dreamchecker > ${GITHUB_WORKSPACE}/output-annotations.txt 2>&1 || DREAMCHECKER_EXIT_CODE=$?
diff --git a/code/__DEFINES/power_defines.dm b/code/__DEFINES/power_defines.dm
index 0a8694dbb1fb..15d4e1c1baa9 100644
--- a/code/__DEFINES/power_defines.dm
+++ b/code/__DEFINES/power_defines.dm
@@ -38,3 +38,5 @@
#define APC_IS_CHARGING 1
/// APC battery is at 100%
#define APC_FULLY_CHARGED 2
+
+#define KW *1000
diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index b19bf436541a..e3feed27c701 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -227,6 +227,7 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
//***** MIND TRAITS *****/
#define TRAIT_HOLY "is_holy" // The mob is holy in regards to religion
#define TRAIT_TABLE_LEAP "table_leap"
+#define TRAIT_SLEIGHT_OF_HAND "sleight_of_hand"
//***** ITEM AND MOB TRAITS *****//
/// Show what machine/door wires do when held.
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 159b0cb0baa1..036e141d9ed9 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -92,6 +92,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/datum/mind = list(
"TRAIT_HOLY" = TRAIT_HOLY,
+ "TRAIT_SLEIGHT_OF_HAND" = TRAIT_SLEIGHT_OF_HAND,
"TRAIT_TABLE_LEAP" = TRAIT_TABLE_LEAP
),
diff --git a/code/_onclick/click_override.dm b/code/_onclick/click_override.dm
index 9bdd7691a1a4..1309e3c9cdc2 100644
--- a/code/_onclick/click_override.dm
+++ b/code/_onclick/click_override.dm
@@ -71,7 +71,7 @@
var/atom/beam_from = user
var/atom/target_atom = A
- for(var/i in 0 to 3)
+ for(var/i in 0 to 2) //3 attempts. Shocks at the clicked source, tries to find a mob in 1 tile, then choses a random tile 1 away to try again. As such, can only hit a mob 2 tiles away from the click
beam_from.Beam(target_atom, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 6)
if(isliving(target_atom))
var/mob/living/L = target_atom
@@ -81,7 +81,12 @@
L.apply_damage(60, STAMINA)
L.Jitter(10 SECONDS)
var/atom/throw_target = get_edge_target_turf(user, get_dir(user, get_step_away(L, user)))
- L.throw_at(throw_target, powergrid / 100000, powergrid / 100000) //100 kW in grid throws 1 tile, 200 throws 2, etc.
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ if(H.gloves && H.gloves.siemens_coefficient == 0) //No throwing with insulated gloves (you still get stamina however)
+ break
+ L.throw_at(throw_target, powergrid / (150 KW), powergrid / (300 KW)) //150 kW in grid throws 1 tile, 300 throws 2, etc.
+
else
add_attack_logs(user, L, "electrocuted with[P.unlimited_power ? " unlimited" : null] power bio-chip")
if(P.unlimited_power)
@@ -90,13 +95,13 @@
electrocute_mob(L, C.powernet, P)
break
var/list/next_shocked = list()
- for(var/mob/M in range(3, target_atom)) //Try to jump to a mob first
+ for(var/mob/M in range(1, target_atom)) //Try to jump to a mob first
if(M == user || isobserver(M))
continue
next_shocked.Add(M)
break //Break this so it gets the closest, thank you
if(!length(next_shocked)) //No mob? Random bullshit go, try to get closer to a mob with luck
- for(var/atom/movable/AM in orange(3, target_atom))
+ for(var/atom/movable/AM in orange(1, target_atom))
if(AM == user || iseffect(AM) || isobserver(AM))
continue
next_shocked.Add(AM)
diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm
new file mode 100644
index 000000000000..60419bd5b25f
--- /dev/null
+++ b/code/datums/diseases/advance/symptoms/hair.dm
@@ -0,0 +1,49 @@
+/*
+//////////////////////////////////////
+Cranial Hypertrichosis
+
+ Very very Noticeable.
+ Decreases resistance slightly.
+ Decreases stage speed.
+ Reduced transmittability
+ Intense Level.
+
+BONUS
+ Makes the mob grow massive hair, regardless of gender.
+
+//////////////////////////////////////
+*/
+
+/datum/symptom/hair
+ name = "Cranial Hypertrichosis"
+ stealth = -3
+ resistance = -1
+ stage_speed = -3
+ transmittable = -1
+ level = 4
+ severity = 1
+
+/datum/symptom/hair/Activate(datum/disease/advance/A)
+ ..()
+ if(prob(SYMPTOM_ACTIVATION_PROB))
+ if(!ishuman(A.affected_mob))
+ return
+ var/mob/living/carbon/human/H = A.affected_mob
+ if(H.dna.species.bodyflags & BALD)
+ return
+ var/obj/item/organ/external/head/head_organ = H.get_organ("head")
+ if(!istype(head_organ))
+ return
+ switch(A.stage)
+ if(1, 2, 3)
+ to_chat(H, "Your scalp itches.")
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
+ else
+ to_chat(H, "Hair bursts forth from your scalp!")
+ var/datum/sprite_accessory/tmp_hair_style = GLOB.hair_styles_full_list["Very Long Hair"]
+
+ if(head_organ.dna.species.name in tmp_hair_style.species_allowed) //If 'Very Long Hair' is a style the person's species can have, give it to them.
+ head_organ.h_style = "Very Long Hair"
+ else //Otherwise, give them a random hair style.
+ head_organ.h_style = random_hair_style(H.gender, head_organ.dna.species.name)
+ H.update_hair()
diff --git a/code/datums/uplink_items/uplink_general.dm b/code/datums/uplink_items/uplink_general.dm
index ac1040135473..43713e9bdd03 100644
--- a/code/datums/uplink_items/uplink_general.dm
+++ b/code/datums/uplink_items/uplink_general.dm
@@ -697,6 +697,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 5
surplus = 10
+/datum/uplink_item/suits/smoke_grenade
+ name = "Smoke Grenade Module"
+ desc = "A module that dispenses primed smoke grenades to disperse crowds."
+ reference = "SGM"
+ item = /obj/item/mod/module/dispenser/smoke
+ cost = 10
+ surplus = 10
+
/datum/uplink_item/device_tools/binary
name = "Binary Translator Key"
desc = "A key, that when inserted into a radio headset, allows you to listen to and talk with artificial intelligences and cybernetic organisms in binary. To talk on the binary channel, type :+ before your radio message."
diff --git a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm
index 44be9000c319..d589d34ea973 100644
--- a/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm
+++ b/code/game/gamemodes/miniantags/pulsedemon/pulsedemon_abilities.dm
@@ -1,4 +1,3 @@
-#define KW *1000
#define PULSEDEMON_REMOTE_DRAIN_MULTIPLIER 5
#define PD_UPGRADE_HIJACK_SPEED "Speed"
@@ -484,4 +483,3 @@
return FALSE
return TRUE
-#undef KW
diff --git a/code/game/gamemodes/scoreboard.dm b/code/game/gamemodes/scoreboard.dm
index 71106e0ee581..3fd1e5e434ae 100644
--- a/code/game/gamemodes/scoreboard.dm
+++ b/code/game/gamemodes/scoreboard.dm
@@ -129,7 +129,7 @@ GLOBAL_VAR(scoreboard) // Variable to save the scoreboard string once it's been
/datum/scoreboard/proc/check_station_player(mob/M)
- if(!is_station_level(M.z) || M.stat < DEAD)
+ if(!is_station_level(M.z) || M.stat != DEAD)
return
if(isAI(M))
dead_ai = TRUE
diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index 7608f59fce04..efff255a6398 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -200,10 +200,8 @@
singlemutcheck(H, GLOB.soberblock, MUTCHK_FORCED)
H.dna.default_blocks.Add(GLOB.soberblock)
H.check_mutations = 1
- var/datum/martial_art/judo/under_siege/bouncer_delight = new
- bouncer_delight.teach(H)
ADD_TRAIT(H.mind, TRAIT_TABLE_LEAP, ROUNDSTART_TRAIT)
-
+ ADD_TRAIT(H.mind, TRAIT_SLEIGHT_OF_HAND, ROUNDSTART_TRAIT)
/datum/job/chef
title = "Chef"
diff --git a/code/game/objects/items/devices/autopsy.dm b/code/game/objects/items/devices/autopsy.dm
index 8877f2a74ea4..141a89738957 100644
--- a/code/game/objects/items/devices/autopsy.dm
+++ b/code/game/objects/items/devices/autopsy.dm
@@ -12,6 +12,7 @@
var/target_name = null
var/target_UID = null
var/timeofdeath = null
+ var/target_rank = null
/obj/item/autopsy_scanner/Destroy()
QDEL_LIST_ASSOC_VAL(wdata)
@@ -66,23 +67,28 @@
if(O.trace_chemicals[V] > 0 && !chemtraces.Find(V))
chemtraces += V
+/obj/item/autopsy_scanner/examine(mob/user)
+ . = ..()
+ if(Adjacent(user))
+ . += "You can use a pen on it to quickly write a coroner's report."
+
/obj/item/autopsy_scanner/attackby(obj/item/P, mob/user)
- if(is_pen(P))
- var/dead_name = input("Insert name of deceased individual")
- var/dead_rank = input("Insert rank of deceased individual")
- var/dead_tod = input("Insert time of death")
- var/dead_cause = input("Insert cause of death")
- var/dead_chems = input("Insert any chemical traces")
- var/dead_notes = input("Insert any relevant notes")
- var/obj/item/paper/R = new(user.loc)
- R.name = "Official Coroner's Report - [dead_name]"
- R.info = "[SSmapping.map_datum.fluff_name] - Coroner's Report
Name of Deceased: [dead_name]
Rank of Deceased: [dead_rank]
Time of Death: [dead_tod]
Cause of Death: [dead_cause]
Trace Chemicals: [dead_chems]
Additional Coroner's Notes: [dead_notes]
Coroner's Signature: "
- playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, 1)
- sleep(10)
- user.put_in_hands(R)
- else
+ if(!is_pen(P))
return ..()
+ var/dead_name = tgui_input_text(user, "Insert name of deceased individual", default = target_name, title = "Coroner's Report", max_length = 60)
+ var/rank = tgui_input_text(user, "Insert rank of deceased individual", default = target_rank, title = "Coroner's Report", max_length = 60)
+ var/tod = tgui_input_text(user, "Insert time of death", default = station_time_timestamp("hh:mm", timeofdeath), title = "Coroner's Report", max_length = 60)
+ var/cause = tgui_input_text(user, "Insert cause of death", title = "Coroner's Report", max_length = 60)
+ var/chems = tgui_input_text(user, "Insert any chemical traces", multiline = TRUE, title = "Coroner's Report")
+ var/notes = tgui_input_text(user, "Insert any relevant notes", multiline = TRUE, title = "Coroner's Report")
+ var/obj/item/paper/R = new(user.loc)
+ R.name = "Official Coroner's Report - [dead_name]"
+ R.info = "[station_name()] - Coroner's Report
Name of Deceased: [dead_name]
Rank of Deceased: [rank]
Time of Death: [tod]
Cause of Death: [cause]
Trace Chemicals: [chems]
Additional Coroner's Notes: [notes]
Coroner's Signature: "
+ playsound(loc, 'sound/goonstation/machines/printer_thermal.ogg', 50, TRUE)
+ sleep(1 SECONDS)
+ user.put_in_hands(R)
+
/obj/item/autopsy_scanner/attack_self(mob/user)
var/scan_data = ""
@@ -161,6 +167,7 @@
if(target_UID != M.UID())
target_UID = M.UID()
target_name = M.name
+ target_rank = M.get_assignment(if_no_id = "Unknown", if_no_job = null)
wdata.Cut()
chemtraces.Cut()
timeofdeath = null
diff --git a/code/game/objects/items/tools/welder.dm b/code/game/objects/items/tools/welder.dm
index 72f286458d94..5fbbf2187e99 100644
--- a/code/game/objects/items/tools/welder.dm
+++ b/code/game/objects/items/tools/welder.dm
@@ -146,6 +146,20 @@
return
remove_fuel(0.5)
+/obj/item/weldingtool/attack(mob/living/carbon/M, mob/living/carbon/user)
+ // For lighting other people's cigarettes.
+ var/obj/item/clothing/mask/cigarette/cig = M?.wear_mask
+ if(!istype(cig) || user.zone_selected != "mouth" || !tool_enabled)
+ return ..()
+
+ if(M == user)
+ cig.attackby(src, user)
+ return
+
+ cig.light("[user] holds out [src] out for [M], and casually lights [cig]. What a badass.")
+ playsound(src, 'sound/items/lighter/light.ogg', 25, TRUE)
+ M.update_inv_wear_mask()
+
/obj/item/weldingtool/use_tool(atom/target, user, delay, amount, volume, datum/callback/extra_checks)
target.add_overlay(GLOB.welding_sparks)
var/did_thing = ..()
diff --git a/code/game/objects/items/weapons/grenades/smokebomb.dm b/code/game/objects/items/weapons/grenades/smokebomb.dm
index 54074e29ed31..ad386ee55032 100644
--- a/code/game/objects/items/weapons/grenades/smokebomb.dm
+++ b/code/game/objects/items/weapons/grenades/smokebomb.dm
@@ -28,10 +28,5 @@
src.smoke.start()
sleep(10)
src.smoke.start()
-
- for(var/obj/structure/blob/B in view(8,src))
- var/damage = round(30/(get_dist(B,src)+1))
- B.take_damage(damage, BURN, MELEE, 0)
sleep(80)
qdel(src)
- return
diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm
index caf4ce0f190c..7079b62de981 100644
--- a/code/game/objects/items/weapons/storage/belt.dm
+++ b/code/game/objects/items/weapons/storage/belt.dm
@@ -588,6 +588,7 @@
..()
if(amount != length(contents))
update_icon()
+ orient2hud(user) // Update the displayed items and their counts
/obj/item/storage/belt/holster
name = "shoulder holster"
diff --git a/code/game/objects/items/weapons/stunbaton.dm b/code/game/objects/items/weapons/stunbaton.dm
index 03cfae8693ef..d94e72c3f573 100644
--- a/code/game/objects/items/weapons/stunbaton.dm
+++ b/code/game/objects/items/weapons/stunbaton.dm
@@ -187,6 +187,7 @@
return ..() // Whack them too if in harm intent
if(!turned_on)
+ user.do_attack_animation(L)
L.visible_message("[user] has prodded [L] with [src]. Luckily it was off.",
"[L == user ? "You prod yourself" : "[user] has prodded you"] with [src]. Luckily it was off.")
return
diff --git a/code/game/objects/mail.dm b/code/game/objects/mail.dm
index 8da165a7be11..6e23945bb58b 100644
--- a/code/game/objects/mail.dm
+++ b/code/game/objects/mail.dm
@@ -15,6 +15,8 @@
var/list/job_list = list()
/// The real name required to open the letter
var/recipient
+ /// The job of the recipient
+ var/recipient_job
var/has_been_scanned = FALSE
/obj/item/envelope/suicide_act(mob/user)
@@ -49,11 +51,16 @@
if(mail_attracted_people.assigned_role in job_list)
recipient = mail_attracted_people.current.real_name
name = "letter to [recipient]"
+ recipient_job = lowertext(mail_attracted_people.assigned_role)
return
if(!admin_spawned)
log_debug("Failed to find a new name to assign to [src]!")
qdel(src)
+/obj/item/envelope/examine(mob/user)
+ . = ..()
+ . += "This letter is addressed to [recipient], the [recipient_job]."
+
/obj/item/envelope/security
icon_state = "mail_sec"
possible_contents = list(/obj/item/food/snacks/donut/sprinkles,
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index d633e142fbd5..8421dbf71206 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -302,6 +302,9 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list(
spawn(1) // This setting exposes the profiler for people with R_VIEWRUNTIMES. They must still have it set in cfg/admin.txt
control_freak = 0
+ if(is_connecting_from_localhost())
+ verbs += /client/proc/export_current_character
+
/client/proc/remove_admin_verbs()
verbs.Remove(
@@ -1055,3 +1058,12 @@ GLOBAL_LIST_INIT(view_runtimes_verbs, list(
show_blurb(about_to_be_banned, 15, message, null, "center", "center", message_color, null, null, 1)
log_admin("[key_name(src)] sent an admin alert to [key_name(about_to_be_banned)] with custom message [message].")
message_admins("[key_name(src)] sent an admin alert to [key_name(about_to_be_banned)] with custom message [message].")
+
+
+/client/proc/export_current_character()
+ set name = "Export Character DMI/JSON"
+ set category = "Admin"
+
+ if(ishuman(mob))
+ var/mob/living/carbon/human/H = mob
+ H.export_dmi_json()
diff --git a/code/modules/admin/verbs/adminpm.dm b/code/modules/admin/verbs/adminpm.dm
index 66f5fab88d87..b856869ace8e 100644
--- a/code/modules/admin/verbs/adminpm.dm
+++ b/code/modules/admin/verbs/adminpm.dm
@@ -165,7 +165,7 @@
var/ping_link = check_rights(R_ADMIN, 0, mob) ? "(PING)" : ""
var/window_link = "(WINDOW)"
var/alert_link = check_rights(R_ADMIN, FALSE, mob) ? " (ALERT)" : ""
- to_chat(src, "[send_pm_type][type] to-[holder ? key_name(C, TRUE, type) : key_name_hidden(C, TRUE, type)]: [emoji_msg] [ping_link] [window_link][alert_link]")
+ to_chat(src, "[send_pm_type][type] to-[holder ? key_name(C, TRUE, type) : key_name_hidden(C, TRUE, type)]: [emoji_msg] [ping_link] [window_link][alert_link]", MESSAGE_TYPE_ADMINPM)
/*if(holder && !C.holder)
C.last_pm_recieved = world.time
diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm
index dcec4ba770a8..f6c1d2fb4108 100644
--- a/code/modules/antagonists/changeling/changeling_power.dm
+++ b/code/modules/antagonists/changeling/changeling_power.dm
@@ -90,6 +90,9 @@
if(HAS_TRAIT(user, TRAIT_FAKEDEATH) && !bypass_fake_death)
to_chat(user, "We are incapacitated.")
return FALSE
+ if(!cling.can_use_powers)
+ to_chat(owner, "Our cells are repairing themselves, we are unable to use our powers!")
+ return FALSE
return TRUE
// Transform the target to the chosen dna. Used in transform.dm and tiny_prick.dm (handy for changes since it's the same thing done twice)
diff --git a/code/modules/antagonists/changeling/datum_changeling.dm b/code/modules/antagonists/changeling/datum_changeling.dm
index f8c39e74fd9b..a507ad5cfa36 100644
--- a/code/modules/antagonists/changeling/datum_changeling.dm
+++ b/code/modules/antagonists/changeling/datum_changeling.dm
@@ -48,6 +48,10 @@
var/datum/action/changeling/sting/chosen_sting
/// If the changeling is in the process of regenerating from their fake death.
var/regenerating = FALSE
+ /// Did changeling use headslug?
+ var/headslugged = FALSE
+ /// Can you use abilities due to a recent revival?
+ var/can_use_powers = TRUE
blurb_text_color = COLOR_PURPLE
blurb_text_outline_width = 1
diff --git a/code/modules/antagonists/changeling/powers/absorb.dm b/code/modules/antagonists/changeling/powers/absorb.dm
index 75b3b4ea4a3f..6e8b98bc5ea3 100644
--- a/code/modules/antagonists/changeling/powers/absorb.dm
+++ b/code/modules/antagonists/changeling/powers/absorb.dm
@@ -86,6 +86,10 @@
target_cling.absorbed_dna.len = 1
target_cling.absorbed_count = 0
+ if(cling.headslugged)
+ cling.headslugged = FALSE
+ to_chat(user, "By absorbing [target], we are once again strong enough to turn into a headslug.")
+
cling.chem_charges = min(cling.chem_charges + 10, cling.chem_storage)
cling.is_absorbing = FALSE
diff --git a/code/modules/antagonists/changeling/powers/become_headslug.dm b/code/modules/antagonists/changeling/powers/become_headslug.dm
index 2ef080c80461..a5c7b712ffc4 100644
--- a/code/modules/antagonists/changeling/powers/become_headslug.dm
+++ b/code/modules/antagonists/changeling/powers/become_headslug.dm
@@ -1,19 +1,21 @@
/datum/action/changeling/headslug
name = "Last Resort"
- desc = "We sacrifice our current body in a moment of need, placing us in control of a vessel that can plant our likeness in a new host. Costs 20 chemicals."
- helptext = "We will be placed in control of a small, fragile creature. We may attack a corpse like this to plant an egg which will slowly mature into a new form for us."
+ desc = "We sacrifice our current body in a moment of need, placing us in control of a vessel that can plant our likeness in a new host. Costs 20 chemicals. We will need to absorb someone to use this ability again."
+ helptext = "We will be placed in control of a small, fragile creature. We may attack a corpse like this to plant an egg which will slowly mature into a new form for us. This ability is available only once per absorption."
button_icon_state = "last_resort"
chemical_cost = 20
- dna_cost = 2
req_human = TRUE
req_stat = DEAD
bypass_fake_death = TRUE
- power_type = CHANGELING_PURCHASABLE_POWER
- category = /datum/changeling_power_category/defence
+ power_type = CHANGELING_INNATE_POWER
/datum/action/changeling/headslug/try_to_sting(mob/user, mob/target)
- if(tgui_alert(user, "Are you sure you wish to do this? This action cannot be undone.", "Sting", list("Yes", "No")) == "No")
+ if(cling.headslugged)
+ to_chat(user, "We need to absorb a humanoid to headslug again.")
return
+ if(tgui_alert("Are you sure you wish to do this? This action cannot be undone.",,"Yes","No") != "Yes")
+ return
+ cling.headslugged = TRUE
..()
/datum/action/changeling/headslug/sting_action(mob/user)
diff --git a/code/modules/antagonists/changeling/powers/revive.dm b/code/modules/antagonists/changeling/powers/revive.dm
index 20e4c7933302..f5fc56bb4959 100644
--- a/code/modules/antagonists/changeling/powers/revive.dm
+++ b/code/modules/antagonists/changeling/powers/revive.dm
@@ -11,6 +11,10 @@
if(HAS_TRAIT(user, TRAIT_UNREVIVABLE))
to_chat(user, "Something is preventing us from regenerating, we will need to revive at another point.")
return FALSE
+
+ cling.can_use_powers = FALSE
+ addtimer(VARSET_CALLBACK(cling, can_use_powers, TRUE), 10 SECONDS)
+
REMOVE_TRAIT(user, TRAIT_FAKEDEATH, CHANGELING_TRAIT)
for(var/obj/item/grab/G in user.grabbed_by)
var/mob/living/carbon/M = G.assailant
diff --git a/code/modules/antagonists/vampire/vamp_datum.dm b/code/modules/antagonists/vampire/vamp_datum.dm
index 14fae8b6418d..3b5b91c81a0b 100644
--- a/code/modules/antagonists/vampire/vamp_datum.dm
+++ b/code/modules/antagonists/vampire/vamp_datum.dm
@@ -134,7 +134,6 @@
owner.current.set_nutrition(min(NUTRITION_LEVEL_WELL_FED, owner.current.nutrition + 5))
continue
-
if(H.stat != DEAD || H.has_status_effect(STATUS_EFFECT_RECENTLY_SUCCUMBED))
if(H.ckey || H.player_ghosted) //Requires ckey regardless if monkey or humanoid, or the body has been ghosted before it died
blood = min(20, H.blood_volume)
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index 3331ee4d8fd5..afa07900913f 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -929,6 +929,7 @@
desc = "A black coat with gold trim and an old US Chevron printed on the back. Edgy."
icon = 'icons/obj/custom_items.dmi'
icon_state = "shodancoat"
+ item_state = "shodancoat_item"
/obj/item/clothing/suit/storage/fluff/k3_webbing
name = "vox tactical webbing"
diff --git a/code/modules/martial_arts/judo.dm b/code/modules/martial_arts/judo.dm
index 7562d2d9d5cd..6fc113321b0d 100644
--- a/code/modules/martial_arts/judo.dm
+++ b/code/modules/martial_arts/judo.dm
@@ -70,33 +70,3 @@
/datum/martial_art/judo/explaination_footer(user)
to_chat(user, "Your unarmed strikes hit about twice as hard as your peers, on average.")
-
-/datum/martial_art/judo/under_siege
- name = "Professional Bodyguarding"
- var/static/list/areas_under_siege = typecacheof(list(/area/station/service/kitchen, /area/station/service/bar))
-
-/datum/martial_art/judo/under_siege/teach(mob/living/carbon/human/H, make_temporary)
- RegisterSignal(H, COMSIG_AREA_ENTERED, PROC_REF(bar_check))
- return ..()
-
-/datum/martial_art/judo/under_siege/remove(mob/living/carbon/human/H)
- UnregisterSignal(H, COMSIG_AREA_ENTERED)
- return ..()
-
-/datum/martial_art/judo/under_siege/proc/bar_check(mob/living/carbon/human/H, area/entered_area)
- SIGNAL_HANDLER
- if(!is_type_in_typecache(entered_area, areas_under_siege))
- var/list/held_items = list(H.get_active_hand(), H.get_inactive_hand())
- for(var/obj/item/slapper/parry/smacking_hand in held_items)
- qdel(smacking_hand)
- can_parry = FALSE
- weight = 0
- else
- can_parry = TRUE
- weight = 5
-
-/datum/martial_art/judo/under_siege/can_use(mob/living/carbon/human/H)
- var/area/A = get_area(H)
- if(!(is_type_in_typecache(A, areas_under_siege)))
- return FALSE
- return ..()
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index 1a1bbd268bd3..38d4653f196c 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -292,7 +292,7 @@
. = ..()
if(isliving(target) && target != src)
var/mob/living/L = target
- if(L.stat < DEAD)
+ if(L.stat != DEAD)
L.heal_overall_damage(heal_power, heal_power)
new /obj/effect/temp_visual/heal(get_turf(target), "#80F5FF")
diff --git a/code/modules/mob/living/carbon/alien/alien_base.dm b/code/modules/mob/living/carbon/alien/alien_base.dm
index 8d2a727019e1..97559889b956 100644
--- a/code/modules/mob/living/carbon/alien/alien_base.dm
+++ b/code/modules/mob/living/carbon/alien/alien_base.dm
@@ -260,25 +260,6 @@ and carry the owner just to make sure*/
. = ..()
ADD_TRAIT(src, TRAIT_IMMOBILIZED, LYING_DOWN_TRAIT) //Xenos can't crawl
-/mob/living/carbon/alien/consume_patch_or_pill(obj/item/reagent_containers/medicine, mob/user)
- var/apply_method = "swallow"
- var/how_many_reagents = medicine.reagents.total_volume
- var/reagent_application = REAGENT_INGEST
- if(ispatch(medicine))
- apply_method = "apply"
- how_many_reagents = clamp(medicine.reagents.total_volume, 0.1, 2)
- reagent_application = REAGENT_TOUCH
-
- visible_message("[user] attempts to force [src] to [apply_method] [medicine].")
- if(!do_after(user, 5 SECONDS, TRUE, src)) // You try feeding a xenomorph a pill
- return
-
- visible_message("[user] forces [src] to [apply_method] [medicine].")
- var/fraction = min(1 / medicine.reagents.total_volume, 1)
- medicine.reagents.reaction(src, reagent_application, fraction)
- medicine.reagents.trans_to(src, how_many_reagents)
- return TRUE
-
/mob/living/carbon/alien/update_stat(reason)
if(health <= HEALTH_THRESHOLD_CRIT && stat == CONSCIOUS)
KnockOut()
diff --git a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
index b6d3568ca887..fffd5c5e98ec 100644
--- a/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
+++ b/code/modules/mob/living/carbon/alien/special/alien_embryo.dm
@@ -98,7 +98,7 @@
spawn(6)
owner.cut_overlay(overlay)
- var/mob/living/carbon/alien/larva/new_xeno = new(owner.drop_location())
+ var/mob/living/carbon/alien/larva/new_xeno = new(get_turf(owner))
new_xeno.key = C.key
dust_if_respawnable(C)
if(SSticker && SSticker.mode)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index f3e4c40a82db..6172ff2d9441 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -1164,26 +1164,22 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
return TRUE
/mob/living/carbon/proc/eat(obj/item/food/to_eat, mob/user, bitesize_override)
- if(ispill(to_eat) || ispatch(to_eat)) // We first have to know if it's either a pill or a patch, only then can we check if it's a food item
- return consume_patch_or_pill(to_eat, user)
-
- if(!isfood(to_eat))
+ if(!istype(to_eat))
return FALSE
- var/obj/item/food/food = to_eat // It's not a patch or a pill so it must be food
var/fullness = nutrition + 10
- if(istype(food, /obj/item/food/snacks))
- for(var/datum/reagent/consumable/C in reagents.reagent_list) //we add the nutrition value of what we're currently digesting
+ if(istype(to_eat, /obj/item/food/snacks))
+ for(var/datum/reagent/consumable/C in reagents.reagent_list) // We add the nutrition value of what we're currently digesting
fullness += C.nutriment_factor * C.volume / (C.metabolization_rate * metabolism_efficiency)
if(user == src)
- if(!selfFeed(food, fullness))
+ if(!selfFeed(to_eat, fullness))
return FALSE
else
- if(!forceFed(food, user, fullness))
+ if(!forceFed(to_eat, user, fullness))
return FALSE
- consume(food, bitesize_override)
+ consume(to_eat, bitesize_override)
SSticker.score.score_food_eaten++
return TRUE
@@ -1208,25 +1204,22 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
return TRUE
/mob/living/carbon/proc/selfFeed(obj/item/food/to_eat, fullness)
- if(ispill(to_eat))
- to_chat(src, "You swallow [to_eat].")
- else if(ispatch(to_eat))
- to_chat(src, "You apply [to_eat].")
- else
- if(to_eat.junkiness && satiety < -150 && nutrition > NUTRITION_LEVEL_STARVING + 50)
- to_chat(src, "You don't feel like eating any more junk food at the moment.")
- return FALSE
- if(fullness <= 50)
- to_chat(src, "You hungrily chew out a piece of [to_eat] and gobble it!")
- else if(fullness > 50 && fullness < 150)
- to_chat(src, "You hungrily begin to eat [to_eat].")
- else if(fullness > 150 && fullness < 500)
- to_chat(src, "You take a bite of [to_eat].")
- else if(fullness > 500 && fullness < 600)
- to_chat(src, "You unwillingly chew a bit of [to_eat].")
- else if(fullness > (600 * (1 + overeatduration / 2000))) // The more you eat - the more you can eat
- to_chat(src, "You cannot force any more of [to_eat] to go down your throat.")
- return FALSE
+ if(to_eat.junkiness && satiety < -150 && nutrition > NUTRITION_LEVEL_STARVING + 50)
+ to_chat(src, "You don't feel like eating any more junk food at the moment.")
+ return FALSE
+
+ if(fullness <= 50)
+ to_chat(src, "You hungrily chew out a piece of [to_eat] and gobble it!")
+ else if(fullness > 50 && fullness < 150)
+ to_chat(src, "You hungrily begin to eat [to_eat].")
+ else if(fullness > 150 && fullness < 500)
+ to_chat(src, "You take a bite of [to_eat].")
+ else if(fullness > 500 && fullness < 600)
+ to_chat(src, "You unwillingly chew a bit of [to_eat].")
+ else if(fullness > (600 * (1 + overeatduration / 2000))) // The more you eat - the more you can eat
+ to_chat(src, "You cannot force any more of [to_eat] to go down your throat.")
+ return FALSE
+
return TRUE
/mob/living/carbon/proc/selfDrink(obj/item/reagent_containers/drinks/toDrink, mob/user)
@@ -1250,7 +1243,7 @@ GLOBAL_LIST_INIT(ventcrawl_machinery, list(/obj/machinery/atmospherics/unary/ven
/*TO DO - If/when stomach organs are introduced, override this at the human level sending the item to the stomach
so that different stomachs can handle things in different ways VB*/
/mob/living/carbon/proc/consume(obj/item/food/to_eat, bitesize_override)
- var/this_bite = bitesize_override ? bitesize_override : to_eat.bitesize
+ var/this_bite = bitesize_override || to_eat.bitesize
if(!to_eat.reagents)
return
if(satiety > -200)
@@ -1263,47 +1256,6 @@ so that different stomachs can handle things in different ways VB*/
to_eat.reagents.reaction(src, REAGENT_INGEST, fraction)
to_eat.reagents.trans_to(src, this_bite)
-/mob/living/carbon/proc/consume_patch_or_pill(obj/item/reagent_containers/medicine, mob/user) // medicine = patch or pill
- // The reason why this is bundled up is to avoid 2 procs that will be practically identical
- if(!medicine.reagents.total_volume)
- return TRUE // Doesn't have reagents, would be fine to use up
-
- if(!dna.species.dietflags) // You will not feed the IPC
- to_chat(user, "You cannot feed [src] [medicine]!")
- return FALSE
-
- var/apply_method = "swallow"
- var/reagent_application = REAGENT_INGEST
- var/requires_mouth = TRUE
- var/instant = FALSE
- var/how_many_reagents = medicine.reagents.total_volume
-
- if(ispatch(medicine))
- apply_method = "apply"
- reagent_application = REAGENT_TOUCH
- requires_mouth = FALSE
- how_many_reagents = clamp(medicine.reagents.total_volume, 0.1, 2) // Patches aren't that good at transporting reagents into the bloodstream
- var/obj/item/reagent_containers/patch/patch = medicine
- if(patch.instant_application)
- instant = TRUE
-
- if(user != src && !instant)
- if(requires_mouth && !get_organ("head"))
- to_chat(user, "You cannot feed [src] [medicine]!")
- return FALSE
- visible_message("[user] attempts to force [src] to [apply_method] [medicine].")
- if(!do_after(user, 3 SECONDS, TRUE, src, TRUE))
- return FALSE
- forceFedAttackLog(medicine, user)
- visible_message("[user] forces [src] to [apply_method] [medicine].")
- else
- to_chat(user, "You [apply_method] [medicine].")
-
- var/fraction = min(1 / medicine.reagents.total_volume, 1)
- medicine.reagents.reaction(src, reagent_application, fraction)
- medicine.reagents.trans_to(src, how_many_reagents)
- return TRUE
-
/mob/living/carbon/get_access()
. = ..()
diff --git a/code/modules/mob/living/carbon/human/human_mob.dm b/code/modules/mob/living/carbon/human/human_mob.dm
index eb8b65a91631..fb41d6e1bf9a 100644
--- a/code/modules/mob/living/carbon/human/human_mob.dm
+++ b/code/modules/mob/living/carbon/human/human_mob.dm
@@ -1254,6 +1254,46 @@
sec_hud_set_ID()
+
+/mob/living/carbon/human/proc/export_dmi_json()
+ var/filename = clean_input("filename", "filename", "my_character")
+
+ if(filename == "")
+ return
+
+ var/maxCount = 10
+ var/curCount = 0
+ var/dmipath = "data/exports/characters/[filename].dmi"
+ log_world("saving icon to [filename]")
+ var/icon/allDirs = null
+
+ var/list/directions = list(SOUTH, NORTH, EAST, WEST)
+ for(var/d in directions)
+ var/icon/new_icon
+ for(var/i = 0; i < maxCount; i++)
+ sleep(5)
+ curCount = i
+ new_icon = getFlatIcon(src, defdir=d)
+ if(new_icon != null)
+ break
+
+ if(new_icon == null)
+ log_world("ran [curCount] times and could not find icon")
+ else
+ log_world("ran [curCount] times and found icon")
+ if(allDirs == null)
+ allDirs = icon(new_icon, "", d)
+ else
+ allDirs.Insert(new_icon, "", d)
+
+ fcopy(allDirs, dmipath)
+ log_world("saved icon to [dmipath]")
+
+ var/jsonpath = "data/exports/characters/[filename].json"
+ var/json = json_encode(serialize())
+ text2file(json, jsonpath)
+ log_world("saved json to [jsonpath]")
+
/**
* Change a mob's species.
*
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 999f80125d5e..9d8e832a937e 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1104,7 +1104,7 @@
if((stat == DEAD) && (var_value < DEAD))//Bringing the dead back to life
GLOB.dead_mob_list -= src
GLOB.alive_mob_list += src
- if((stat < DEAD) && (var_value == DEAD))//Kill he
+ if((stat != DEAD) && (var_value == DEAD))//Kill he
GLOB.alive_mob_list -= src
GLOB.dead_mob_list += src
. = ..()
diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm
index 5baa6c1f92af..ca1b4c9aad76 100644
--- a/code/modules/mob/mob_grab.dm
+++ b/code/modules/mob/mob_grab.dm
@@ -303,7 +303,7 @@
return
if(world.time < (last_upgrade + UPGRADE_COOLDOWN))
return
- if(!(assailant.mobility_flags & MOBILITY_MOVE) || IS_HORIZONTAL(assailant))
+ if(HAS_TRAIT(assailant, TRAIT_HANDS_BLOCKED) || IS_HORIZONTAL(assailant))
qdel(src)
return
diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm
index 6927df61279a..6e5a95364955 100644
--- a/code/modules/mod/modules/modules_security.dm
+++ b/code/modules/mod/modules/modules_security.dm
@@ -289,3 +289,18 @@
C.reagents.add_reagent("ice", reagent_volume)
qdel(src)
+/// Smoke Grenade Module, its tacticool.
+/obj/item/mod/module/dispenser/smoke
+ name = "MOD smoke grenade dispenser module"
+ desc = "A module that dispenses primed smoke grenades to disperse crowds."
+ icon_state = "smoke_grenade"
+ cooldown_time = 10 SECONDS
+ complexity = 1
+ overlay_state_inactive = "module_smoke_grenade"
+ dispense_type = /obj/item/grenade/smokebomb
+
+/obj/item/mod/module/dispenser/smoke/on_use()
+ var/obj/item/grenade/smokebomb/grenade = ..()
+ if(!grenade)
+ return
+ grenade.attack_self(mod.wearer)
diff --git a/code/modules/paperwork/photography.dm b/code/modules/paperwork/photography.dm
index 010bd5d0dbf4..1885adb49ac3 100644
--- a/code/modules/paperwork/photography.dm
+++ b/code/modules/paperwork/photography.dm
@@ -47,30 +47,36 @@
txt = copytext(txt, 1, 128)
if(loc == user && user.stat == 0)
scribble = txt
- else if(istype(P, /obj/item/lighter))
+ else if(P.get_heat())
burnphoto(P, user)
..()
-/obj/item/photo/proc/burnphoto(obj/item/lighter/P, mob/user)
- var/class = ""
+/obj/item/photo/proc/burnphoto(obj/item/P, mob/user)
+ if(user.restrained())
+ return
- if(P.lit && !user.restrained())
- if(istype(P, /obj/item/lighter/zippo))
- class = ""
+ var/class = "warning"
+ if(istype(P, /obj/item/lighter/zippo))
+ class = "rose"
- user.visible_message("[class][user] holds \the [P] up to \the [src], it looks like [user.p_theyre()] trying to burn it!", \
- "[class]You hold [P] up to [src], burning it slowly.")
+ user.visible_message("[user] holds [P] up to [src], it looks like [user.p_theyre()] trying to burn it!", \
+ "You hold [P] up to [src], burning it slowly.")
- if(do_after(user, 50, target = src))
- if(user.get_active_hand() == P && P.lit)
- user.visible_message("[class][user] burns right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \
- "[class]You burn right through \the [src], turning it to ash. It flutters through the air before settling on the floor in a heap.")
- if(user.is_in_inactive_hand(src))
- user.unEquip(src)
- new /obj/effect/decal/cleanable/ash(get_turf(src))
- qdel(src)
- else
- to_chat(user, "You must hold \the [P] steady to burn \the [src].")
+ if(!do_after(user, 5 SECONDS, target = src))
+ return
+
+ if(user.get_active_hand() != P || !P.get_heat())
+ to_chat(user, "You must hold [P] steady to burn [src].")
+ return
+
+ user.visible_message("[class][user] burns right through [src], turning it to ash. It flutters through the air before settling on the floor in a heap.", \
+ "[class]You burn right through [src], turning it to ash. It flutters through the air before settling on the floor in a heap.")
+
+ if(user.is_in_inactive_hand(src))
+ user.unEquip(src)
+
+ new /obj/effect/decal/cleanable/ash(get_turf(src))
+ qdel(src)
/obj/item/photo/examine(mob/user)
. = ..()
diff --git a/code/modules/projectiles/ammunition/ammo_boxes.dm b/code/modules/projectiles/ammunition/ammo_boxes.dm
index a2be11cee534..9feb5d0bc375 100644
--- a/code/modules/projectiles/ammunition/ammo_boxes.dm
+++ b/code/modules/projectiles/ammunition/ammo_boxes.dm
@@ -1,6 +1,6 @@
/obj/item/ammo_box/a357
name = "speed loader (.357)"
- desc = "Designed to quickly reload revolvers."
+ desc = "A small device designed to quickly reload revolvers. Seven round capacity."
materials = list()
ammo_type = /obj/item/ammo_casing/a357
max_ammo = 7
@@ -9,7 +9,7 @@
/obj/item/ammo_box/b357
name = "ammo box (.357)"
- desc = "Contains up to seven .357 bullets, intended to either be inserted into a speed loader or into the gun manually."
+ desc = "An ammunition box filled with .357 magnum rounds, commonly used in high-caliber revolvers."
w_class = WEIGHT_CLASS_NORMAL
ammo_type = /obj/item/ammo_casing/a357
max_ammo = 7
@@ -18,6 +18,7 @@
/obj/item/ammo_box/c9mm
name = "ammo box (9mm)"
+ desc = "An ammunition box filled with 9mm pistol cartridges, commonly used in handguns and submachine guns."
icon_state = "9mmbox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/c9mm
@@ -25,6 +26,7 @@
/obj/item/ammo_box/c10mm
name = "ammo box (10mm)"
+ desc = "An ammunition box filled with 10mm pistol cartridges, commonly used in Syndicate handguns."
icon_state = "10mmbox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/c10mm
@@ -32,6 +34,7 @@
/obj/item/ammo_box/c45
name = "ammo box (.45)"
+ desc = "An ammunition box filled with .45 caliber pistol cartridges, commonly used in high-power pistols and submachine guns."
icon_state = "45box"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/c45
@@ -39,12 +42,14 @@
/obj/item/ammo_box/rubber45
name = "ammo box (.45 rubber)"
+ desc = "An ammunition box filled with .45 caliber rubber bullets for less-lethal actions."
icon_state = "45box-r"
ammo_type = /obj/item/ammo_casing/rubber45
max_ammo = 16
/obj/item/ammo_box/a40mm
name = "ammo box (40mm grenades)"
+ desc = "An ammunition box containing four 40mm grenades, for use with a launcher. Dropping them is ill-advised."
icon_state = "40mm"
ammo_type = /obj/item/ammo_casing/a40mm
max_ammo = 4
@@ -52,7 +57,7 @@
/obj/item/ammo_box/a762
name = "stripper clip (7.62mm)"
- desc = "A stripper clip."
+ desc = "A stripper clip for 7.62mm cartridges, used in Mosin-Nagant rifles. Five round capacity."
icon_state = "762"
ammo_type = /obj/item/ammo_casing/a762
max_ammo = 5
@@ -60,6 +65,7 @@
/obj/item/ammo_box/n762
name = "ammo box (7.62x38mmR)"
+ desc = "An ammunition box full of 7.62x38mmR pistol cartridges, for use in antique revolvers."
icon_state = "riflebox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/n762
@@ -67,6 +73,7 @@
/obj/item/ammo_box/wt550
name = "ammo box (4.6x30mm)"
+ desc = "An ammunition box containing 4.6x30mm PDW cartridges, for use in submachine guns and low-caliber rifles."
icon_state = "riflebox"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/c46x30mm
@@ -76,21 +83,25 @@
/obj/item/ammo_box/wt550/wtap
name = "ammo box (Armor Piercing 4.6x30mm)"
+ desc = "An ammunition box containing 4.6x30mm PDW cartridges. These are AP rounds, sacrificing damage for armor penetration."
icon_state = "wtbox_AP"
ammo_type = /obj/item/ammo_casing/c46x30mm/ap
/obj/item/ammo_box/wt550/wtic
name = "ammo box (Incendiary 4.6x30mm)"
+ desc = "An ammunition box containing 4.6x30mm PDW ammunition, tipped with an incendiary chemical payload."
icon_state = "wtbox_inc"
ammo_type = /obj/item/ammo_casing/c46x30mm/inc
/obj/item/ammo_box/wt550/wttx
name = "ammo box (Toxin Tipped 4.6x30mm)"
+ desc = "An ammunition box containing 4.6x30mm cartridges, tipped with lethal toxins. Possibly a war crime."
icon_state = "wtbox_tox"
ammo_type = /obj/item/ammo_casing/c46x30mm/tox
/obj/item/ammo_box/laser
name = "ammo box (laser)"
+ desc = "An ammunition box containing caseless laser cartridges, for use in IK-series laser rifles."
icon_state = "laserbox"
origin_tech = "combat=3"
ammo_type = /obj/item/ammo_casing/caseless/laser
@@ -99,6 +110,7 @@
/obj/item/ammo_box/shotgun
name = "shotgun speedloader (Slug)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Slugs."
icon_state = "slugloader"
origin_tech = "combat=2"
ammo_type = /obj/item/ammo_casing/shotgun
@@ -108,39 +120,46 @@
/obj/item/ammo_box/shotgun/buck
name = "shotgun speedloader (Buckshot)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Buckshot."
icon_state = "buckloader"
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
/obj/item/ammo_box/shotgun/confetti
name = "shotgun speedloader (Confetti)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Confetti shot."
icon_state = "partyloader"
ammo_type = /obj/item/ammo_casing/shotgun/confetti
multi_sprite_step = 1
/obj/item/ammo_box/shotgun/dragonsbreath
name = "shotgun speedloader (Dragonsbreath)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Dragonsbreath rounds."
icon_state = "dragonsbreathloader"
ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
/obj/item/ammo_box/shotgun/stun
name = "shotgun speedloader (Stun shells)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Stun slugs."
icon_state = "stunloader"
ammo_type = /obj/item/ammo_casing/shotgun/stunslug
/obj/item/ammo_box/shotgun/beanbag
name = "shotgun speedloader (Beanbag shells)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Beanbag slugs."
icon_state = "beanbagloader"
ammo_type = /obj/item/ammo_casing/shotgun/beanbag
materials = list(MAT_METAL=1750)
/obj/item/ammo_box/shotgun/rubbershot
name = "shotgun speedloader (Rubbershot shells)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Rubbershot shells."
icon_state = "rubbershotloader"
ammo_type = /obj/item/ammo_casing/shotgun/rubbershot
materials = list(MAT_METAL=1750)
/obj/item/ammo_box/shotgun/tranquilizer
name = "shotgun speedloader (Tranquilizer darts)"
+ desc = "A specialized speedloader for swiftly reloading shotguns. This one is meant for Tranquilizer dart shells."
icon_state = "tranqloader"
ammo_type = /obj/item/ammo_casing/shotgun/tranquilizer
materials = list(MAT_METAL=1750)
@@ -149,6 +168,7 @@
//FOAM DARTS
/obj/item/ammo_box/foambox
name = "ammo box (Foam Darts)"
+ desc = "An ammunition box, filled with foam darts for use in toy weapons."
icon = 'icons/obj/guns/toy.dmi'
icon_state = "foambox"
ammo_type = /obj/item/ammo_casing/caseless/foam_dart
@@ -157,11 +177,13 @@
/obj/item/ammo_box/foambox/riot
icon_state = "foambox_riot"
+ desc = "An ammunition box, filled with riot darts for use in toy weapons. Not safe for children."
ammo_type = /obj/item/ammo_casing/caseless/foam_dart/riot
materials = list(MAT_METAL = 50000)
/obj/item/ammo_box/foambox/sniper
name = "ammo box (Foam Sniper Darts)"
+ desc = "An ammunition box full of sniper darts for toy weapons."
icon = 'icons/obj/guns/toy.dmi'
icon_state = "foambox_sniper"
ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper
@@ -170,12 +192,14 @@
/obj/item/ammo_box/foambox/sniper/riot
icon_state = "foambox_sniper_riot"
+ desc = "An ammunition box full of powerful sniper riot darts."
ammo_type = /obj/item/ammo_casing/caseless/foam_dart/sniper/riot
materials = list(MAT_METAL = 90000)
/obj/item/ammo_box/caps
name = "speed loader (caps)"
+ desc = "A revolver speedloader for a cap gun. Cannot chamber live ammunition."
icon_state = "357"
ammo_type = /obj/item/ammo_casing/cap
max_ammo = 7
diff --git a/code/modules/projectiles/ammunition/ammo_casings.dm b/code/modules/projectiles/ammunition/ammo_casings.dm
index 50d5cbb6700d..7d541fcfe80b 100644
--- a/code/modules/projectiles/ammunition/ammo_casings.dm
+++ b/code/modules/projectiles/ammunition/ammo_casings.dm
@@ -1,18 +1,21 @@
/obj/item/ammo_casing/a357
- desc = "A .357 bullet casing."
+ name = ".357 magnum round"
+ desc = "A .357 magnum cartridge, often used in revolvers. Very deadly."
caliber = "357"
projectile_type = /obj/item/projectile/bullet
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
/obj/item/ammo_casing/rubber9mm
- desc = "A 9mm rubber bullet casing."
+ name = "9mm rubber round"
+ desc = "A 9mm pistol cartridge. This one has a rubber tip for less-lethal takedowns."
caliber = "9mm"
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/weakbullet4
/obj/item/ammo_casing/a762
- desc = "A 7.62mm bullet casing."
+ name = "7.62mm round"
+ desc = "A 7.62mm rifle cartridge, often used in Soviet rifles."
icon_state = "762-casing"
caliber = "a762"
projectile_type = /obj/item/projectile/bullet
@@ -20,10 +23,13 @@
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
/obj/item/ammo_casing/a762/enchanted
+ name = "enchanted 7.62 round"
+ desc = "A 7.62mm rifle cartridge. It sparkles with magic."
projectile_type = /obj/item/projectile/bullet/weakbullet3
/obj/item/ammo_casing/a50
- desc = "A .50AE bullet casing."
+ name = ".50 AE round"
+ desc = "A .50AE pistol cartridge, used in large caliber handguns."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
@@ -37,62 +43,86 @@
projectile_type = /obj/item/projectile/bullet/mime/fake
/obj/item/ammo_casing/c10mm
- desc = "A 10mm bullet casing."
+ name = "10mm round"
+ desc = "A 10mm pistol cartridge, commonly used in Syndicate sidearms."
caliber = "10mm"
projectile_type = /obj/item/projectile/bullet/midbullet3
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/c10mm/ap
+ name = "10mm armor piercing round"
+ desc = "A 10mm pistol cartridge, with a Teflon coating to increase armor penetration, at the cost of damage."
projectile_type = /obj/item/projectile/bullet/midbullet3/ap
/obj/item/ammo_casing/c10mm/fire
+ name = "10mm incendiary round"
+ desc = "A 10mm pistol cartridge, containing a payload of flammable chemicals."
projectile_type = /obj/item/projectile/bullet/midbullet3/fire
muzzle_flash_color = LIGHT_COLOR_FIRE
/obj/item/ammo_casing/c10mm/hp
+ name = "10mm hollow point round"
+ desc = "A 10mm pistol cartridge, with an expanding tip that greatly increases stopping power, at the cost of armor penetration."
projectile_type = /obj/item/projectile/bullet/midbullet3/hp
/obj/item/ammo_casing/overgrown
+ name = "overgrown round"
+ desc = "A pistol cartridge... probably. Why does it resemble a pea?"
projectile_type = /obj/item/projectile/bullet/midbullet3/overgrown
icon_state = "peashooter_bullet"
/obj/item/ammo_casing/c9mm
- desc = "A 9mm bullet casing."
+ name = "9mm round"
+ desc = "A 9mm pistol cartridge, commonly used in handguns and submachine guns."
caliber = "9mm"
projectile_type = /obj/item/projectile/bullet/weakbullet3
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/c9mm/ap
+ name = "9mm armor piercing round"
+ desc = "A 9mm pistol cartridge, with a Teflon coating to increase armor penetration, at the cost of stopping power."
projectile_type = /obj/item/projectile/bullet/armourpiercing
/obj/item/ammo_casing/c9mm/tox
+ name = "9mm toxic round"
+ desc = "A 9mm pistol cartridge, tipped with lethal toxins. Likely a war crime."
projectile_type = /obj/item/projectile/bullet/toxinbullet
/obj/item/ammo_casing/c9mm/inc
+ name = "9mm incendiary round"
+ desc = "A 9mm pistol cartridge, tipped with an incendiary chemical payload."
projectile_type = /obj/item/projectile/bullet/incendiary/firebullet
muzzle_flash_color = LIGHT_COLOR_FIRE
/obj/item/ammo_casing/c46x30mm
- desc = "A 4.6x30mm bullet casing."
+ name = "4.6x30mm round"
+ desc = "A 4.6x30mm PDW cartridge, commonly used in submachine guns and small-caliber rifles."
caliber = "4.6x30mm"
projectile_type = /obj/item/projectile/bullet/weakbullet3
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_WEAK
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/c46x30mm/ap
+ name = "4.6x30mm armor piercing round"
+ desc = "A 4.6x30mm PDW cartridge, with a tungsten core to increase armor penetration, at the cost of stopping power."
projectile_type = /obj/item/projectile/bullet/armourpiercing/wt550
/obj/item/ammo_casing/c46x30mm/tox
+ name = "4.6x30mm toxic round"
+ desc = "A 4.6x30mm PDW cartridge, tipped with lethal toxins. Probably a war crime."
projectile_type = /obj/item/projectile/bullet/toxinbullet
/obj/item/ammo_casing/c46x30mm/inc
+ name = "4.6x30 incendiary round"
+ desc = "a 4.6x30mm PDW cartridge, tipped with an incendiary chemical payload."
projectile_type = /obj/item/projectile/bullet/incendiary/firebullet
muzzle_flash_color = LIGHT_COLOR_FIRE
/obj/item/ammo_casing/rubber45
- desc = "A .45 rubber bullet casing."
+ name = ".45 rubber round"
+ desc = "A .45 caliber pistol cartridge. Features a rubber bullet for less-lethal takedowns."
caliber = ".45"
icon_state = "r-casing"
projectile_type = /obj/item/projectile/bullet/midbullet_r
@@ -100,7 +130,8 @@
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/c45
- desc = "A .45 bullet casing."
+ name = ".45 round"
+ desc = "A .45 caliber pistol cartridge, commonly used in pistols and submachine guns."
caliber = ".45"
projectile_type = /obj/item/projectile/bullet/midbullet
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
@@ -110,7 +141,8 @@
projectile_type = /obj/item/projectile/bullet/midbullet3
/obj/item/ammo_casing/n762
- desc = "A 7.62x38mmR bullet casing."
+ name = "7.62x38mmR round"
+ desc = "A 7.62x38mmR pistol cartridge, commonly used by antique revolvers."
caliber = "n762"
projectile_type = /obj/item/projectile/bullet
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
@@ -128,7 +160,7 @@
/obj/item/ammo_casing/shotgun
name = "shotgun slug"
- desc = "A 12 gauge lead slug."
+ desc = "A 12 gauge lead slug. Fires a single solid projectile."
icon_state = "blshell"
caliber = "shotgun"
casing_drop_sound = 'sound/weapons/gun_interactions/shotgun_fall.ogg'
@@ -140,7 +172,7 @@
/obj/item/ammo_casing/shotgun/buckshot
name = "buckshot shell"
- desc = "A 12 gauge buckshot shell."
+ desc = "A 12 gauge buckshot shell. Fires a spread of lethal shot."
icon_state = "gshell"
projectile_type = /obj/item/projectile/bullet/pellet
pellets = 6
@@ -148,7 +180,7 @@
/obj/item/ammo_casing/shotgun/rubbershot
name = "rubber shot"
- desc = "A shotgun casing filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
+ desc = "A 12 gauge shell filled with densely-packed rubber balls, used to incapacitate crowds from a distance."
icon_state = "cshell"
projectile_type = /obj/item/projectile/bullet/pellet/rubber
pellets = 6
@@ -158,7 +190,7 @@
/obj/item/ammo_casing/shotgun/beanbag
name = "beanbag slug"
- desc = "A weak beanbag slug for riot control."
+ desc = "A 12 gauge shell loaded with a beanbag slug for less-lethal takedowns."
icon_state = "bshell"
projectile_type = /obj/item/projectile/bullet/weakbullet
materials = list(MAT_METAL=250)
@@ -167,7 +199,7 @@
/obj/item/ammo_casing/shotgun/stunslug
name = "taser slug"
- desc = "A stunning taser slug."
+ desc = "A 12 gauge shell loaded with a taser cartridge, somehow."
icon_state = "stunshell"
projectile_type = /obj/item/projectile/bullet/stunshot
materials = list(MAT_METAL=250)
@@ -178,35 +210,33 @@
/obj/item/ammo_casing/shotgun/meteorslug
name = "meteorslug shell"
- desc = "A shotgun shell rigged with CMC technology, which launches a massive slug when fired."
+ desc = "A 12 gauge shell rigged with CMC technology, which launches a massive slug when fired."
icon_state = "mshell"
projectile_type = /obj/item/projectile/bullet/meteorshot
/obj/item/ammo_casing/shotgun/pulseslug
name = "proto pulse slug"
- desc = "A delicate device which can be loaded into a shotgun. The primer acts as a button which triggers the gain medium and fires a powerful \
- energy blast. While the heat and power drain limit it to one use, it can still allow an operator to engage targets that ballistic ammunition \
- would have difficulty with."
+ desc = "A 12 gauge shell loaded with a powerful energy emitter, firing a devastating pulse blast."
icon_state = "pshell"
projectile_type = /obj/item/projectile/beam/pulse/shot
muzzle_flash_color = LIGHT_COLOR_DARKBLUE
/obj/item/ammo_casing/shotgun/incendiary
name = "incendiary slug"
- desc = "An incendiary-coated shotgun slug."
+ desc = "A 12 gauge slug shell containing an incendiary chemical payload."
icon_state = "ishell"
projectile_type = /obj/item/projectile/bullet/incendiary/shell
muzzle_flash_color = LIGHT_COLOR_FIRE
/obj/item/ammo_casing/shotgun/frag12
name = "\improper FRAG-12 slug"
- desc = "A high explosive breaching round for a 12 gauge shotgun."
+ desc = "A 12 gauge shell, loaded with a high-explosive slug."
icon_state = "heshell"
projectile_type = /obj/item/projectile/bullet/frag12
/obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
name = "dragonsbreath shell"
- desc = "A shotgun shell which fires a spread of incendiary pellets."
+ desc = "A 12 gauge shell which fires a spread of incendiary pellets."
icon_state = "ishell2"
projectile_type = /obj/item/projectile/bullet/incendiary/shell/dragonsbreath
pellets = 4
@@ -215,9 +245,7 @@
/obj/item/ammo_casing/shotgun/ion
name = "ion shell"
- desc = "An advanced shotgun shell which uses a subspace ansible crystal to produce an effect similar to a standard ion rifle. \
- The unique properties of the crystal splot the pulse into a spread of individually weaker bolts."
- icon_state = "ionshell"
+ desc = "An advanced 12 gauge shell that fires a spread of ion bolts."
projectile_type = /obj/item/projectile/ion/weak
pellets = 4
variance = 35
@@ -227,7 +255,7 @@
/obj/item/ammo_casing/shotgun/lasershot
name = "lasershot"
- desc = "An advanced shotgun shell that uses a multitude of lenses to split a high-powered laser into eight small pellets."
+ desc = "An advanced 12 gauge shell that uses a multitude of lenses to split a high-powered laser into eight small beams."
icon_state = "lshell"
projectile_type = /obj/item/projectile/beam/scatter
pellets = 8
@@ -238,13 +266,13 @@
/obj/item/ammo_casing/shotgun/techshell
name = "unloaded technological shell"
- desc = "A high-tech shotgun shell which can be loaded with materials to produce unique effects."
+ desc = "An empty 12 gauge shell, ready to be loaded with all manner of projectiles."
icon_state = "cshell"
projectile_type = null
/obj/item/ammo_casing/shotgun/dart
name = "shotgun dart"
- desc = "A dart for use in shotguns. Can be injected with up to 30 units of any chemical."
+ desc = "A 12 gauge shell loaded with a hypodermic needle. Can be injected with up to 30 units of any chemical."
icon_state = "cshell"
container_type = OPENCONTAINER
projectile_type = /obj/item/projectile/bullet/dart
@@ -271,7 +299,7 @@
/obj/item/ammo_casing/shotgun/tranquilizer
name = "tranquilizer darts"
- desc = "A tranquilizer round used to subdue individuals utilizing stimulants."
+ desc = "A 12 gauge dart shell loaded with powerful tranquilizers."
icon_state = "nshell"
projectile_type = /obj/item/projectile/bullet/dart/syringe/tranquilizer
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
@@ -280,19 +308,21 @@
/obj/item/ammo_casing/shotgun/confetti
name = "confettishot"
- desc = "It's party time!"
+ desc = "A 12 gauge shell loaded with... confetti?"
icon_state = "partyshell"
projectile_type = /obj/item/projectile/bullet/confetti
/obj/item/ammo_casing/a556
- desc = "A 5.56mm bullet casing."
+ name = "5.56mm round"
+ desc = "A 5.56mm rifle round, produced in incredible quantities by the Trans-Solar Federation."
caliber = "a556"
projectile_type = /obj/item/projectile/bullet/heavybullet
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/a545
- desc = "A 5.45x39mm bullet casing."
+ name = "5.45x39mm round"
+ desc = "A 5.45x39mm rifle round, used by the elite marines of the USSP."
caliber = "a545"
projectile_type = /obj/item/projectile/bullet/midbullet3
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
@@ -300,15 +330,15 @@
/obj/item/ammo_casing/shotgun/fakebeanbag
name = "beanbag shell"
- desc = "A weak beanbag shell."
+ desc = "A 12 gauge beanbag slug. Smells strongly of alcohol."
icon_state = "bshell"
projectile_type = /obj/item/projectile/bullet/weakbullet/booze
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_NORMAL
muzzle_flash_range = MUZZLE_FLASH_RANGE_NORMAL
/obj/item/ammo_casing/rocket
- name = "rocket shell"
- desc = "A high explosive designed to be fired from a launcher."
+ name = "rocket propelled grenade"
+ desc = "A high explosive rocket meant to be fired from a launcher."
icon_state = "rocketshell"
projectile_type = /obj/item/projectile/missile
caliber = "rocket"
@@ -325,15 +355,16 @@
return FALSE
/obj/item/ammo_casing/caseless/a75
- desc = "A .75 bullet casing."
+ name = ".75 gyrojet round"
+ desc = "A .75 caliber gyrojet cartridge, for use in experimental weaponry. There's a Sunburst Heavy Industries logo on the side."
caliber = "75"
projectile_type = /obj/item/projectile/bullet/gyro
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
/obj/item/ammo_casing/a40mm
- name = "40mm HE shell"
- desc = "A cased high explosive grenade that can only be activated once fired out of a grenade launcher."
+ name = "40mm HE grenade"
+ desc = "A 40mm high-explosive grenade round, meant to be fired from a grenade launcher."
caliber = "40mm"
icon_state = "40mmHE"
projectile_type = /obj/item/projectile/bullet/a40mm
@@ -442,7 +473,7 @@
/obj/item/ammo_casing/shotgun/assassination
name = "assassination shell"
- desc = "A specialist shrapnel shell that has been laced with a silencing toxin."
+ desc = "A 12 gauge buckshot shell containing a powerful silencing toxin."
projectile_type = /obj/item/projectile/bullet/pellet/assassination
muzzle_flash_effect = null
icon_state = "gshell"
@@ -450,6 +481,7 @@
variance = 25
/obj/item/ammo_casing/cap
+ name = "cap"
desc = "A cap for children toys."
caliber = "cap"
projectile_type = /obj/item/projectile/bullet/cap
@@ -458,6 +490,7 @@
harmful = FALSE
/obj/item/ammo_casing/caseless/laser
+ name = "caseless laser round"
desc = "An experimental laser casing, designed to vaporize when fired."
caliber = "laser"
projectile_type = /obj/item/projectile/beam/laser/ik //Subtype that breaks on firing if emp'd
diff --git a/code/modules/projectiles/ammunition/energy_lens.dm b/code/modules/projectiles/ammunition/energy_lens.dm
index 5935376b9bb2..322d210681af 100644
--- a/code/modules/projectiles/ammunition/energy_lens.dm
+++ b/code/modules/projectiles/ammunition/energy_lens.dm
@@ -166,6 +166,15 @@
harmful = FALSE
delay = 0.6 SECONDS
+/obj/item/ammo_casing/energy/disabler/smg
+ projectile_type = /obj/item/projectile/beam/disabler/weak
+ e_cost = 25
+ fire_sound = 'sound/weapons/taser3.ogg'
+ click_cooldown_override = 2
+ variance = 15
+ randomspread = 1
+ delay = 2
+
/obj/item/ammo_casing/energy/disabler/cyborg //seperate balancing for cyborg, again
e_cost = 250
diff --git a/code/modules/projectiles/guns/energy/stun.dm b/code/modules/projectiles/guns/energy/stun.dm
index 53c1120c998b..1b299dd77fc0 100644
--- a/code/modules/projectiles/guns/energy/stun.dm
+++ b/code/modules/projectiles/guns/energy/stun.dm
@@ -67,6 +67,18 @@
return ..()
+/obj/item/gun/energy/disabler/smg
+ name = "disabler smg"
+ desc = "An automatic disabler variant, as opposed to the conventional model. Boasts a higher ammunition capacity at the cost of slightly reduced beam effectiveness."
+ icon_state = "disabler_smg"
+ weapon_weight = WEAPON_HEAVY
+ w_class = WEIGHT_CLASS_BULKY
+ ammo_type = list(/obj/item/ammo_casing/energy/disabler/smg)
+ burst_size = 2
+ fire_delay = 2.5
+ shaded_charge = TRUE
+ can_holster = FALSE
+
/obj/item/gun/energy/disabler/cyborg
name = "cyborg disabler"
desc = "An integrated disabler that draws from a cyborg's power cell. This weapon contains a limiter to prevent the cyborg's power cell from overheating."
diff --git a/code/modules/projectiles/guns/projectile/revolver.dm b/code/modules/projectiles/guns/projectile/revolver.dm
index 37c26c4569fe..381d1d641d9e 100644
--- a/code/modules/projectiles/guns/projectile/revolver.dm
+++ b/code/modules/projectiles/guns/projectile/revolver.dm
@@ -374,6 +374,7 @@
/obj/item/gun/projectile/revolver/doublebarrel/attack_self(mob/living/user)
var/num_unloaded = 0
+
while(get_ammo() > 0)
var/obj/item/ammo_casing/CB
CB = magazine.get_round(0)
@@ -383,11 +384,39 @@
CB.update_icon()
playsound(get_turf(CB), 'sound/weapons/gun_interactions/shotgun_fall.ogg', 70, 1)
num_unloaded++
+
+ if(sleight_of_handling(user))
+ return
+
if(num_unloaded)
- to_chat(user, "You break open \the [src] and unload [num_unloaded] shell\s.")
+ to_chat(user, "You break open [src] and unload [num_unloaded] shell\s.")
else
to_chat(user, "[src] is empty.")
+/obj/item/gun/projectile/revolver/doublebarrel/proc/sleight_of_handling(mob/living/carbon/human/user)
+ if(!istype(get_area(user), /area/station/service/bar))
+ return FALSE
+ if(!istype(user) || !HAS_MIND_TRAIT(user, TRAIT_SLEIGHT_OF_HAND))
+ return FALSE
+ if(!istype(user.belt, /obj/item/storage/belt/bandolier))
+ return FALSE
+ var/obj/item/storage/belt/bandolier/our_bandolier = user.belt
+
+ var/loaded_shells = 0
+ for(var/obj/item/ammo_casing/shotgun/shell in our_bandolier)
+ if(loaded_shells == magazine.max_ammo)
+ break
+
+ our_bandolier.remove_from_storage(shell)
+ magazine.give_round(shell)
+ chamber_round()
+
+ loaded_shells++
+
+ if(loaded_shells)
+ to_chat(user, "You quickly load [loaded_shells] shell\s from your bandolier into [src].")
+ return TRUE
+
// IMPROVISED SHOTGUN //
/obj/item/gun/projectile/revolver/doublebarrel/improvised
diff --git a/code/modules/projectiles/guns/projectile/saw.dm b/code/modules/projectiles/guns/projectile/saw.dm
index 5d69bbdedec8..9c50158c7b01 100644
--- a/code/modules/projectiles/guns/projectile/saw.dm
+++ b/code/modules/projectiles/guns/projectile/saw.dm
@@ -129,7 +129,8 @@
//casings//
/obj/item/ammo_casing/mm556x45
- desc = "A 556x45mm bullet casing."
+ name = "5.56x45mm round"
+ desc = "A 5.56x45mm rifle cartridge, commonly used in light machine guns."
icon_state = "762-casing"
caliber = "mm55645"
projectile_type = /obj/item/projectile/bullet/saw
@@ -137,19 +138,23 @@
muzzle_flash_range = MUZZLE_FLASH_RANGE_STRONG
/obj/item/ammo_casing/mm556x45/bleeding
- desc = "A 556x45mm bullet casing with specialized inner-casing, that when it makes contact with a target, release tiny shrapnel to induce internal bleeding."
+ name = "5.56x45mm 'Shredder' round"
+ desc = "A 5.56x45mm 'Shredder' cartridge, with a heavily serrated tip intended to cause massive bleeding."
icon_state = "762-casing"
projectile_type = /obj/item/projectile/bullet/saw/bleeding
/obj/item/ammo_casing/mm556x45/hollow
- desc = "A 556x45mm bullet casing designed to cause more damage to unarmored targets."
+ name = "5.56x45mm hollow point round"
+ desc = "A 5.56x45mm rifle cartridge designed to cause more damage to unarmored targets."
projectile_type = /obj/item/projectile/bullet/saw/hollow
/obj/item/ammo_casing/mm556x45/ap
- desc = "A 556x45mm bullet casing designed with a hardened-tipped core to help penetrate armored targets."
+ name = "5.56x45mm armor piercing round"
+ desc = "A 5.56x45mm rifle cartridge with a hardened tungsten core to increase armor penetration."
projectile_type = /obj/item/projectile/bullet/saw/ap
/obj/item/ammo_casing/mm556x45/incen
- desc = "A 556x45mm bullet casing designed with a chemical-filled capsule on the tip that when bursted, reacts with the atmosphere to produce a fireball, engulfing the target in flames. "
+ name = "5.56x45mm incendiary round"
+ desc = "A 5.56x45mm rifle cartridge with an incendiary chemical payload."
projectile_type = /obj/item/projectile/bullet/saw/incen
muzzle_flash_color = LIGHT_COLOR_FIRE
diff --git a/code/modules/projectiles/guns/projectile/sniper.dm b/code/modules/projectiles/guns/projectile/sniper.dm
index 8d1af168a7c5..680b5e6e7e5f 100644
--- a/code/modules/projectiles/guns/projectile/sniper.dm
+++ b/code/modules/projectiles/guns/projectile/sniper.dm
@@ -56,7 +56,8 @@
icon_state = "[initial(icon_state)]"
/obj/item/ammo_casing/point50
- desc = "A .50 bullet casing."
+ name = ".50 BMG round"
+ desc = "A .50 BMG rifle cartridge, commonly used in anti-materiel rifles and heavy machine guns."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper
muzzle_flash_strength = MUZZLE_FLASH_STRENGTH_STRONG
@@ -79,7 +80,8 @@
ammo_type = /obj/item/ammo_casing/antimatter
/obj/item/ammo_casing/antimatter
- desc = "A .50 antimatter bullet casing, designed to cause massive damage to whatever is hit."
+ name = ".50 BMG anti-matter round"
+ desc = "A .50 BMG high-explosive cartridge. Does not actually contain antimatter."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper/antimatter
icon_state = ".50"
@@ -105,7 +107,8 @@
max_ammo = 3
/obj/item/ammo_casing/soporific
- desc = "A .50 bullet casing, specialised in sending the target to sleep, instead of hell."
+ name = ".50 BMG soporific round"
+ desc = "A .50 BMG hypodermic cartridge, loaded with sedatives for instant incapacitation."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper/soporific
icon_state = ".50"
@@ -132,7 +135,8 @@
max_ammo = 5
/obj/item/ammo_casing/haemorrhage
- desc = "A .50 bullet casing, specialised in causing massive bloodloss"
+ name = ".50 BMG shredder round"
+ desc = "A .50 BMG 'Shredder' cartridge, with a heavily serrated bullet intended to cause massive blood loss."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper/haemorrhage
icon_state = ".50"
@@ -159,7 +163,8 @@
max_ammo = 5
/obj/item/ammo_casing/penetrator
- desc = "A .50 caliber penetrator round casing."
+ name = ".50 BMG sabot round"
+ desc = "A .50 BMG Sabot Penetrator cartridge, capable of punching through just about anything."
caliber = ".50"
projectile_type = /obj/item/projectile/bullet/sniper/penetrator
icon_state = ".50"
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 3d6211b949d1..d934d34dbe94 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -99,6 +99,12 @@
impact_effect_type = /obj/effect/temp_visual/impact_effect/blue_laser
light_color = LIGHT_COLOR_CYAN
+/obj/item/projectile/beam/disabler/weak
+ name = "weakened disabler beam"
+ damage = 15
+ armour_penetration_flat = -10
+ light_color = LIGHT_COLOR_BLUE
+
/obj/item/projectile/beam/pulse
name = "pulse"
icon_state = "u_laser"
diff --git a/code/modules/projectiles/projectile/magic_projectiles.dm b/code/modules/projectiles/projectile/magic_projectiles.dm
index c870715dad83..ff7c643a3e75 100644
--- a/code/modules/projectiles/projectile/magic_projectiles.dm
+++ b/code/modules/projectiles/projectile/magic_projectiles.dm
@@ -325,8 +325,11 @@
S.icon = change.icon
if(H.mind)
H.mind.transfer_to(S)
- to_chat(S, "You are an animated statue. You cannot move when monitored, but are nearly invincible and deadly when unobserved!")
- to_chat(S, "Do not harm [firer.name], your creator.")
+ var/list/messages = list()
+ messages.Add("You have been transformed into an animated statue.")
+ messages.Add("You cannot move when monitored, but are nearly invincible and deadly when unobserved! Hunt down those who shackle you.")
+ messages.Add("Do not harm [firer.name], your creator.")
+ to_chat(S, chat_box_red(messages.Join("
")))
H = change
H.loc = S
qdel(src)
diff --git a/code/modules/reagents/chemistry/machinery/chem_master.dm b/code/modules/reagents/chemistry/machinery/chem_master.dm
index 6e34d4404cef..567522866765 100644
--- a/code/modules/reagents/chemistry/machinery/chem_master.dm
+++ b/code/modules/reagents/chemistry/machinery/chem_master.dm
@@ -173,7 +173,7 @@
/obj/machinery/chem_master/wrench_act(mob/user, obj/item/I)
if(panel_open)
return
- default_unfasten_wrench(user, I, 4 SECONDS)
+ return default_unfasten_wrench(user, I, 4 SECONDS)
/obj/machinery/chem_master/ui_act(action, params, datum/tgui/ui, datum/ui_state/state)
if(..())
diff --git a/code/modules/reagents/reagent_containers/patch.dm b/code/modules/reagents/reagent_containers/patch.dm
index a0f33f2ed620..779b8b389883 100644
--- a/code/modules/reagents/reagent_containers/patch.dm
+++ b/code/modules/reagents/reagent_containers/patch.dm
@@ -12,22 +12,36 @@
var/instant_application = FALSE
var/needs_to_apply_reagents = TRUE
-/obj/item/reagent_containers/patch/attack(mob/living/carbon/M, mob/user, def_zone)
- return apply(M, user)
+/obj/item/reagent_containers/patch/attack(mob/living/carbon/C, mob/user)
+ return apply(C, user)
/obj/item/reagent_containers/patch/attack_self(mob/user)
return apply(user, user)
-/obj/item/reagent_containers/patch/proc/apply(mob/living/carbon/M, mob/user)
- if(!istype(M))
+/obj/item/reagent_containers/patch/proc/apply(mob/living/carbon/C, mob/user)
+ if(!istype(C))
return FALSE
- if(M.eat(src, user))
- if(user.get_active_hand() == src)
- user.drop_item() // Only drop if they're holding the patch directly
- forceMove(M)
- LAZYADD(M.processing_patches, src)
- return TRUE
- return FALSE
+
+ if(ismachineperson(C))
+ to_chat(user, "[user == C ? "You" : C] can't use [src]!")
+ return FALSE
+
+ if(user == C)
+ to_chat(user, "You apply [src].")
+ else
+ if(!instant_application)
+ C.visible_message("[user] attempts to force [C] to apply [src].")
+ if(!do_after(user, 3 SECONDS, TRUE, C, TRUE))
+ return FALSE
+
+ C.forceFedAttackLog(src, user)
+ C.visible_message("[user] forces [C] to apply [src].")
+
+ if(user.get_active_hand() == src)
+ user.drop_item() // Only drop if they're holding the patch directly
+ forceMove(C)
+ LAZYADD(C.processing_patches, src)
+ return TRUE
/obj/item/reagent_containers/patch/styptic
name = "brute patch"
diff --git a/code/modules/reagents/reagent_containers/pill.dm b/code/modules/reagents/reagent_containers/pill.dm
index 2c843596059c..20bed7001567 100644
--- a/code/modules/reagents/reagent_containers/pill.dm
+++ b/code/modules/reagents/reagent_containers/pill.dm
@@ -18,16 +18,37 @@
if(!icon_state)
icon_state = "pill[rand(1, 20)]"
-/obj/item/reagent_containers/pill/proc/apply(mob/living/carbon/M, mob/user, def_zone)
- if(!istype(M))
+/obj/item/reagent_containers/pill/proc/apply(mob/living/carbon/C, mob/user)
+ if(!istype(C))
return FALSE
- if(M.eat(src, user))
+
+ if(!reagents.total_volume)
qdel(src)
return TRUE
- return FALSE
-/obj/item/reagent_containers/pill/attack(mob/living/carbon/M, mob/user, def_zone)
- return apply(M, user)
+ if(ishuman(C))
+ var/mob/living/carbon/human/H = C
+ if(!H.check_has_mouth())
+ to_chat(user, "[user == H ? "You" : H] can't ingest [src]!")
+ return FALSE
+
+ if(user == C)
+ to_chat(user, "You swallow [src].")
+ else
+ C.visible_message("[user] attempts to force [C] to swallow [src].")
+ if(!do_after(user, 3 SECONDS, TRUE, C, TRUE))
+ return FALSE
+
+ C.forceFedAttackLog(src, user)
+ C.visible_message("[user] forces [C] to swallow [src].")
+
+ reagents.reaction(C, REAGENT_INGEST)
+ reagents.trans_to(C, reagents.total_volume)
+ qdel(src)
+ return TRUE
+
+/obj/item/reagent_containers/pill/attack(mob/living/carbon/C, mob/user)
+ return apply(C, user)
/obj/item/reagent_containers/pill/attack_self(mob/user)
return apply(user, user)
diff --git a/code/modules/research/designs/modsuit_designs.dm b/code/modules/research/designs/modsuit_designs.dm
index 5eb37656612c..957774f5902a 100644
--- a/code/modules/research/designs/modsuit_designs.dm
+++ b/code/modules/research/designs/modsuit_designs.dm
@@ -338,6 +338,13 @@
materials = list(MAT_METAL = 10000, MAT_GLASS = 4000, MAT_SILVER = 2000)
build_path = /obj/item/mod/module/plasma_stabilizer
+/datum/design/module/smoke_grenade
+ name = "Smoke Grenade Module"
+ id = "mod_smokegrenade"
+ req_tech = list("materials" = 5, "engineering" = 6, "syndicate" = 2)
+ materials = list(MAT_METAL = 12500, MAT_SILVER = 12050, MAT_GOLD = 2000, MAT_PLASMA = 5000)
+ build_path = /obj/item/mod/module/dispenser/smoke
+
/datum/design/module/plate_compression
name = "Plate Compression Module"
id = "mod_compression"
diff --git a/code/modules/supply/supply_packs/pack_security.dm b/code/modules/supply/supply_packs/pack_security.dm
index f6e26bb1a441..ed278f83aaf4 100644
--- a/code/modules/supply/supply_packs/pack_security.dm
+++ b/code/modules/supply/supply_packs/pack_security.dm
@@ -280,6 +280,14 @@
cost = 400
containername = "tranquilizer shell crate"
+/datum/supply_packs/security/armory/disablersmg
+ name = "WT-450 Disabler SMG Crate"
+ contains = list(/obj/item/gun/energy/disabler/smg,
+ /obj/item/gun/energy/disabler/smg)
+ cost = 550
+ containertype = /obj/structure/closet/crate/secure/plasma
+ containername = "disabler smg crate"
+
/////// Implants & etc
/datum/supply_packs/security/armory/mindshield
diff --git a/icons/mob/inhands/guns_lefthand.dmi b/icons/mob/inhands/guns_lefthand.dmi
index 3e211172c990..7cb890d0548f 100644
Binary files a/icons/mob/inhands/guns_lefthand.dmi and b/icons/mob/inhands/guns_lefthand.dmi differ
diff --git a/icons/mob/inhands/guns_righthand.dmi b/icons/mob/inhands/guns_righthand.dmi
index deac7d152f0a..bbbda0e17cdd 100644
Binary files a/icons/mob/inhands/guns_righthand.dmi and b/icons/mob/inhands/guns_righthand.dmi differ
diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi
index f890b05a742e..5692365bace0 100644
Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ
diff --git a/icons/mob/screen_bot.dmi b/icons/mob/screen_bot.dmi
index 7522623f3273..9079dfba15eb 100644
Binary files a/icons/mob/screen_bot.dmi and b/icons/mob/screen_bot.dmi differ
diff --git a/icons/mob/screen_robot.dmi b/icons/mob/screen_robot.dmi
index d14ac2967aca..2fb11de46abe 100644
Binary files a/icons/mob/screen_robot.dmi and b/icons/mob/screen_robot.dmi differ
diff --git a/icons/obj/clothing/modsuit/mod_modules.dmi b/icons/obj/clothing/modsuit/mod_modules.dmi
index 598c2b1bf269..ba019b126cc2 100644
Binary files a/icons/obj/clothing/modsuit/mod_modules.dmi and b/icons/obj/clothing/modsuit/mod_modules.dmi differ
diff --git a/icons/obj/custom_items.dmi b/icons/obj/custom_items.dmi
index 882d60d35a1a..8f2ec75eec3c 100644
Binary files a/icons/obj/custom_items.dmi and b/icons/obj/custom_items.dmi differ
diff --git a/icons/obj/flora/snowflora.dmi b/icons/obj/flora/snowflora.dmi
index 4a010012fe22..b86a88cc3b5b 100644
Binary files a/icons/obj/flora/snowflora.dmi and b/icons/obj/flora/snowflora.dmi differ
diff --git a/icons/obj/guns/energy.dmi b/icons/obj/guns/energy.dmi
index 8494991416e9..60f136f507d0 100644
Binary files a/icons/obj/guns/energy.dmi and b/icons/obj/guns/energy.dmi differ
diff --git a/icons/obj/wizard.dmi b/icons/obj/wizard.dmi
index 636e9f6ca2ec..c9722eeed29e 100644
Binary files a/icons/obj/wizard.dmi and b/icons/obj/wizard.dmi differ
diff --git a/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi b/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi
index 6d788556420e..82a95e338117 100644
Binary files a/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi and b/modular_ss220/aesthetics/skin/icons/screen_clockwork.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 300e339ded67..72709be4c539 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -449,6 +449,7 @@
#include "code\datums\diseases\advance\symptoms\fever.dm"
#include "code\datums\diseases\advance\symptoms\fire.dm"
#include "code\datums\diseases\advance\symptoms\flesh_eating.dm"
+#include "code\datums\diseases\advance\symptoms\hair.dm"
#include "code\datums\diseases\advance\symptoms\hallucigen.dm"
#include "code\datums\diseases\advance\symptoms\headache.dm"
#include "code\datums\diseases\advance\symptoms\heal.dm"
diff --git a/sound/weapons/taser3.ogg b/sound/weapons/taser3.ogg
new file mode 100644
index 000000000000..bfe904c902a2
Binary files /dev/null and b/sound/weapons/taser3.ogg differ
diff --git a/tools/ci/check_grep2.py b/tools/ci/check_grep2.py
index 7354b59fa011..f8152ffbf4e0 100644
--- a/tools/ci/check_grep2.py
+++ b/tools/ci/check_grep2.py
@@ -14,7 +14,7 @@
def print_error(message: str, filename: str, line_number: int):
if os.getenv("GITHUB_ACTIONS") == "true": # We're on github, output in a special format.
- print(f"::error file={filename},line={line_number},title=Check Grep::{message}")
+ print(f"::error file={filename},line={line_number},title=Check Grep::{filename}:{line_number}: {RED}{message}{NC}")
else:
print(f"{filename}:{line_number}: {RED}{message}{NC}")
diff --git a/tools/ci/check_icon_dupenames.py b/tools/ci/check_icon_dupenames.py
new file mode 100644
index 000000000000..da4f988810ce
--- /dev/null
+++ b/tools/ci/check_icon_dupenames.py
@@ -0,0 +1,40 @@
+from collections import defaultdict
+import glob
+import sys
+import time
+
+from ..dmi import Dmi
+
+
+if __name__ == "__main__":
+ print("check_icon_dupenames started")
+
+ count = 0
+ exit_code = 0
+ start = time.time()
+
+ findings = defaultdict(list)
+
+ for dmi_path in glob.glob("**/*.dmi", recursive=True):
+ dmi = Dmi.from_file(dmi_path)
+ states = set()
+ for state in dmi.states:
+ # Movement states have the same name as their non-movement counterparts
+ if (state.name, state.movement) in states:
+ findings[dmi_path].append(state.name)
+ states.add((state.name, state.movement))
+ count += 1
+
+ if findings:
+ exit_code = 1
+
+ for filename in sorted(findings.keys()):
+ states = findings[filename]
+ for state in sorted(states):
+ print(f"{filename}: duplicate state name `{state}`")
+
+
+ end = time.time()
+ print(f"\ncheck_icon_dupenames checked {count} files in {end - start:.2f}s\n")
+
+ sys.exit(exit_code)