diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index de7eb672e8..b466475222 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -44,3 +44,4 @@ var/MAX_EXPLOSION_RANGE = 14
#define VENT_GAS_SMOKE "Smoke"
#define VENT_GAS_CN20 "CN20 Nerve Gas"
#define VENT_GAS_CN20_XENO "CN20-X Nerve Gas"
+#define VENT_GAS_LSD "ALD-91 LSD Gas"
diff --git a/code/__pragmas.dm b/code/__pragmas.dm
index 39c14e1bbc..84fcc0dfc3 100644
--- a/code/__pragmas.dm
+++ b/code/__pragmas.dm
@@ -12,7 +12,6 @@
#pragma SoftReservedKeyword error
#pragma DuplicateVariable error
#pragma DuplicateProcDefinition error
-#pragma TooManyArguments error
#pragma PointlessParentCall error
#pragma PointlessBuiltinCall error
#pragma SuspiciousMatrixCall error
diff --git a/code/_macros.dm b/code/_macros.dm
index e8a97cbada..6c1f37b4bc 100644
--- a/code/_macros.dm
+++ b/code/_macros.dm
@@ -90,6 +90,9 @@
#define GENERATE_DEBUG_ID "[rand(0, 9)][rand(0, 9)][rand(0, 9)][rand(0, 9)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)][pick(alphabet_lowercase)]"
#define RECT new /datum/shape/rectangle
+#define SQUARE new /datum/shape/rectangle/square
+#define ELLIPSE new /datum/shape/ellipse
+#define CIRCLE new /datum/shape/ellipse/circle
#define QTREE new /datum/quadtree
#define SEARCH_QTREE(qtree, shape_range, flags) qtree.query_range(shape_range, null, flags)
diff --git a/code/controllers/subsystem/sound.dm b/code/controllers/subsystem/sound.dm
index 4fdfd79353..010850dda2 100644
--- a/code/controllers/subsystem/sound.dm
+++ b/code/controllers/subsystem/sound.dm
@@ -19,8 +19,7 @@ SUBSYSTEM_DEF(sound)
if(!run_hearers) // Initialize for handling next template
run_hearers = run_queue[run_template] // get base hearers
if(run_template.range) // ranging
- var/datum/shape/rectangle/zone = RECT(run_template.x, run_template.y, run_template.range * 2, run_template.range * 2)
- run_hearers |= SSquadtree.players_in_range(zone, run_template.z)
+ run_hearers |= SSquadtree.players_in_range(SQUARE(run_template.x, run_template.y, run_template.range * 2), run_template.z)
if(MC_TICK_CHECK)
return
while(length(run_hearers)) // Output sound to hearers
diff --git a/code/controllers/subsystem/techtree.dm b/code/controllers/subsystem/techtree.dm
index 04ac2591bc..5f22373228 100644
--- a/code/controllers/subsystem/techtree.dm
+++ b/code/controllers/subsystem/techtree.dm
@@ -34,17 +34,6 @@ SUBSYSTEM_DEF(techtree)
var/datum/space_level/zpos = SSmapping.add_new_zlevel(tree.name, list(ZTRAIT_TECHTREE))
tree.zlevel = zpos
- var/zlevel = zpos.z_value
- var/turf/z_min = locate(1, 1, zlevel)
- var/turf/z_max = locate(world.maxx, world.maxy, zlevel)
-
-
-
- for(var/t in block(z_min, z_max))
- var/turf/Tu = t
- Tu.ChangeTurf(/turf/closed/void, list(/turf/closed/void))
- new /area/techtree(Tu)
-
for(var/tier in tree.tree_tiers)
tree.unlocked_techs += tier
tree.all_techs += tier
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index e06e1ac458..cfe66421c9 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -21,7 +21,11 @@ SUBSYSTEM_DEF(ticker)
var/list/login_music = null //Music played in pregame lobby
var/delay_end = FALSE //If set true, the round will not restart on it's own
+#if defined(UNIT_TESTS) //must be FALSE for unit tests else they hang indefinitely
var/delay_start = FALSE
+#else
+ var/delay_start = TRUE
+#endif
var/admin_delay_notice = "" //A message to display to anyone who tries to restart the world after a delay
var/time_left //Pre-game timer
@@ -109,14 +113,6 @@ SUBSYSTEM_DEF(ticker)
mode.declare_completion(force_ending)
REDIS_PUBLISH("byond.round", "type" = "round-complete")
flash_clients()
- addtimer(CALLBACK(
- SSvote,
- /datum/controller/subsystem/vote/proc/initiate_vote,
- "gamemode",
- "SERVER",
- CALLBACK(src, PROC_REF(handle_map_reboot)),
- TRUE
- ), 3 SECONDS)
Master.SetRunLevel(RUNLEVEL_POSTGAME)
/// Attempt to start game asynchronously if applicable
@@ -161,16 +157,6 @@ SUBSYSTEM_DEF(ticker)
return FALSE
return TRUE
-/datum/controller/subsystem/ticker/proc/handle_map_reboot()
- addtimer(CALLBACK(
- SSvote,
- /datum/controller/subsystem/vote/proc/initiate_vote,
- "groundmap",
- "SERVER",
- CALLBACK(src, PROC_REF(Reboot)),
- TRUE
- ), 3 SECONDS)
-
/datum/controller/subsystem/ticker/proc/setup()
to_chat(world, SPAN_BOLDNOTICE("Enjoy the game!"))
var/init_start = world.timeofday
@@ -349,40 +335,6 @@ SUBSYSTEM_DEF(ticker)
fdel("data/mode.txt")
WRITE_FILE(file("data/mode.txt"), the_mode)
-
-/datum/controller/subsystem/ticker/proc/Reboot(reason, delay)
- set waitfor = FALSE
-
- if(usr && !check_rights(R_SERVER))
- return
-
- if(graceful)
- to_chat_forced(world, "
[SPAN_BOLDNOTICE("Shutting down...")]
")
- world.Reboot(FALSE)
- return
-
- if(!delay)
- delay = CONFIG_GET(number/round_end_countdown) * 10
-
- var/skip_delay = check_rights()
- if(delay_end && !skip_delay)
- to_chat(world, SPAN_BOLDNOTICE("An admin has delayed the round end."))
- return
-
- to_chat(world, SPAN_BOLDNOTICE("Rebooting World in [DisplayTimeText(delay)]. [reason]"))
-
- var/start_wait = world.time
- sleep(delay - (world.time - start_wait))
-
- if(delay_end && !skip_delay)
- to_chat(world, SPAN_BOLDNOTICE("Reboot was cancelled by an admin."))
- return
-
- log_game("Rebooting World. [reason]")
- to_chat_forced(world, "[SPAN_BOLDNOTICE("Rebooting...")]
")
-
- world.Reboot(TRUE)
-
/datum/controller/subsystem/ticker/proc/create_characters()
if(!RoleAuthority)
return
diff --git a/code/datums/ammo/bullet/shotgun.dm b/code/datums/ammo/bullet/shotgun.dm
index c5f81a67c0..95db0c6c39 100644
--- a/code/datums/ammo/bullet/shotgun.dm
+++ b/code/datums/ammo/bullet/shotgun.dm
@@ -155,6 +155,7 @@
/datum/ammo/bullet/shotgun/buckshot/special
name = "buckshot shell, USCM special type"
+ handful_state = "special_buck"
bonus_projectiles_type = /datum/ammo/bullet/shotgun/spread/special
accurate_range = 8
diff --git a/code/datums/global_variables.dm b/code/datums/global_variables.dm
index 953f42f172..53e9c0391e 100644
--- a/code/datums/global_variables.dm
+++ b/code/datums/global_variables.dm
@@ -118,15 +118,11 @@
/client/proc/debug_global_variable(name, value, level)
var/html = ""
- var/change = 0
//to make the value bold if changed
if(!(admin_holder.rights & R_DEBUG))
return html
html += "EC "
- if(value != initial(global.vars[name]))
- html += ""
- change = 1
if (isnull(value))
html += "[name] = null"
@@ -175,8 +171,6 @@
else
html += "[name] = [value]"
- if(change)
- html += ""
html += ""
@@ -353,7 +347,6 @@
if(admin_holder && admin_holder.marked_datum)
possible_classes += "marked datum"
possible_classes += "edit referenced object"
- possible_classes += "restore to default"
class = tgui_input_list(usr, "What kind of variable?","Variable Type", possible_classes)
if(!class)
@@ -365,9 +358,6 @@
mod_list(global.vars[variable])
return
- if("restore to default")
- global.vars[variable] = initial(global.vars[variable])
-
if("edit referenced object")
return .(global.vars[variable])
diff --git a/code/datums/quadtree.dm b/code/datums/quadtree.dm
index bf5cd8000b..ec3e577a24 100644
--- a/code/datums/quadtree.dm
+++ b/code/datums/quadtree.dm
@@ -49,43 +49,124 @@
..()
return QDEL_HINT_IWILLGC
-/datum/shape //Leaving rectangles as a subtype if anyone decides to add circles later
+/// A simple geometric shape for testing collisions and intersections. This one is a single point.
+/datum/shape
+ /// Horizontal position of the shape's center point.
var/center_x = 0
+ /// Vertical position of the shape's center point.
var/center_y = 0
+ /// Distance from the shape's leftmost to rightmost extent.
+ var/bounds_x = 0
+ /// Distance from the shape's topmost to bottommost extent.
+ var/bounds_y = 0
-/datum/shape/proc/intersects()
- return
-/datum/shape/proc/contains()
- return
+/datum/shape/New(center_x, center_y)
+ set_shape(center_x, center_y)
+/// Assign shape variables.
+/datum/shape/proc/set_shape(center_x, center_y)
+ src.center_x = center_x
+ src.center_y = center_y
+
+/// Returns TRUE if the coordinates x, y are in or on the shape, otherwise FALSE.
+/datum/shape/proc/contains_xy(x, y)
+ return center_x == x && center_y == y
+
+/// Returns TRUE if the coord datum is in or on the shape, otherwise FALSE.
+/datum/shape/proc/contains_coords(datum/coords/coords)
+ return contains_xy(coords.x_pos, coords.y_pos)
+
+/// Returns TRUE if the atom is in or on the shape, otherwise FALSE.
+/datum/shape/proc/contains_atom(atom/atom)
+ return contains_xy(atom.x, atom.y)
+
+/// Returns TRUE if this shape's bounding box intersects the provided shape's bounding box, otherwise FALSE. Generally faster than a full intersection test.
+/datum/shape/proc/intersects_aabb(datum/shape/aabb)
+ return (abs(src.center_x - aabb.center_x) <= (src.bounds_x + aabb.bounds_x) * 0.5) && (abs(src.center_y - aabb.center_y) <= (src.bounds_y + aabb.bounds_y) * 0.5)
+
+/// Returns TRUE if this shape intersects the provided rectangle shape, otherwise FALSE.
+/datum/shape/proc/intersects_rect(datum/shape/rectangle/rect)
+ return rect.contains_xy(src.center_x, src.center_y)
+
+/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned rectangle.
/datum/shape/rectangle
+ /// Distance from the shape's leftmost to rightmost extent.
+ var/width = 0
+ /// Distance from the shape's topmost to bottommost extent.
+ var/height = 0
+
+/datum/shape/rectangle/New(center_x, center_y, width, height)
+ set_shape(center_x, center_y, width, height)
+
+/datum/shape/rectangle/set_shape(center_x, center_y, width, height)
+ ..()
+ src.bounds_x = width
+ src.bounds_y = height
+ src.width = width
+ src.height = height
+
+/datum/shape/rectangle/contains_xy(x, y)
+ return (abs(center_x - x) <= width * 0.5) && (abs(center_y - y) <= height * 0.5)
+
+/datum/shape/rectangle/intersects_rect(datum/shape/rectangle/rect)
+ return intersects_aabb(rect)
+
+/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned square.
+/datum/shape/rectangle/square
+ /// Distance between the shape's opposing extents.
+ var/length = 0
+
+/datum/shape/rectangle/square/New(center_x, center_y, length)
+ set_shape(center_x, center_y, length)
+
+/datum/shape/rectangle/square/set_shape(center_x, center_y, length)
+ ..(center_x, center_y, length, length)
+ src.length = length
+
+/// A simple geometric shape for testing collisions and intersections. This one is an axis-aligned ellipse.
+/datum/shape/ellipse
+ /// Distance from the shape's leftmost to rightmost extent.
var/width = 0
+ /// Distance from the shape's topmost to bottommost extent.
var/height = 0
+ VAR_PROTECTED/_axis_x_sq = 0
+ VAR_PROTECTED/_axis_y_sq = 0
+
+/datum/shape/ellipse/New(center_x, center_y, width, height)
+ set_shape(center_x, center_y, width, height)
-/datum/shape/rectangle/New(x, y, w, h)
+/datum/shape/ellipse/set_shape(center_x, center_y, width, height)
..()
- center_x = x
- center_y = y
- width = w
- height = h
-
-/datum/shape/rectangle/intersects(datum/shape/rectangle/range)
- return !(range.center_x + range.width/2 < center_x - width / 2|| \
- range.center_x - range.width/2 > center_x + width / 2|| \
- range.center_y + range.height/2 < center_y - height / 2|| \
- range.center_y - range.height/2 > center_y + height / 2)
-
-/datum/shape/rectangle/contains(datum/coords/coords)
- return (coords.x_pos >= center_x - width / 2 \
- && coords.x_pos <= center_x + width / 2 \
- && coords.y_pos >= center_y - height /2 \
- && coords.y_pos <= center_y + height / 2)
-
-/datum/shape/rectangle/proc/contains_atom(atom/A)
- return (A.x >= center_x - width / 2 \
- && A.x <= center_x + width / 2 \
- && A.y >= center_y - height /2 \
- && A.y <= center_y + height / 2)
+ src.bounds_x = width
+ src.bounds_y = height
+ src.width = width
+ src.height = height
+ src._axis_x_sq = (width * 0.5)**2
+ src._axis_y_sq = (height * 0.5)**2
+
+/datum/shape/ellipse/contains_xy(x, y)
+ return ((center_x - x)**2 / _axis_x_sq + (center_y - y)**2 / _axis_y_sq <= 1)
+
+/datum/shape/ellipse/intersects_rect(datum/shape/rectangle/rect)
+ if(..())
+ return TRUE
+
+ var/nearest_x = clamp(src.center_x, rect.center_x - rect.width * 0.5, rect.center_x + rect.width * 0.5)
+ var/nearest_y = clamp(src.center_y, rect.center_y - rect.height * 0.5, rect.center_y + rect.height * 0.5)
+
+ return src.contains_xy(nearest_x, nearest_y)
+
+/// A simple geometric shape for testing collisions and intersections. This one is a circle.
+/datum/shape/ellipse/circle
+ /// Distance from the shape's center to edge.
+ var/radius = 0
+
+/datum/shape/ellipse/circle/New(center_x, center_y, radius)
+ set_shape(center_x, center_y, radius)
+
+/datum/shape/ellipse/circle/set_shape(center_x, center_y, radius)
+ ..(center_x, center_y, radius * 2, radius * 2)
+ src.radius = radius
/datum/quadtree/proc/subdivide()
//Warning: this might give you eye cancer
@@ -96,7 +177,7 @@
is_divided = TRUE
/datum/quadtree/proc/insert_player(datum/coords/qtplayer/p_coords)
- if(!boundary.contains(p_coords))
+ if(!boundary.contains_coords(p_coords))
return FALSE
if(!player_coords)
@@ -118,11 +199,11 @@
player_coords.Add(p_coords)
return TRUE
-/datum/quadtree/proc/query_range(datum/shape/rectangle/range, list/found_players, flags = 0)
+/datum/quadtree/proc/query_range(datum/shape/range, list/found_players, flags = 0)
if(!found_players)
found_players = list()
. = found_players
- if(!range?.intersects(boundary))
+ if(!range?.intersects_rect(boundary))
return
if(is_divided)
nw_branch.query_range(range, found_players, flags)
@@ -136,7 +217,7 @@
continue
if((flags & QTREE_EXCLUDE_OBSERVER) && P.is_observer)
continue
- if(range.contains(P))
+ if(range.contains_coords(P))
if(flags & QTREE_SCAN_MOBS)
found_players.Add(P.player)
continue
diff --git a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
index 42aa2e13b8..1e9a8715a2 100644
--- a/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
+++ b/code/game/machinery/vending/vendor_types/squad_prep/squad_prep.dm
@@ -451,6 +451,7 @@
list("Roller Bed", round(scale * 2), /obj/item/roller, VENDOR_ITEM_REGULAR),
list("Machete Scabbard (Full)", round(scale * 5), /obj/item/storage/large_holster/machete/full, VENDOR_ITEM_REGULAR),
list("Binoculars", round(scale * 1), /obj/item/device/binoculars, VENDOR_ITEM_REGULAR),
+ list("AN/PSQ-55 Sentry Console", round(scale * 1), /obj/item/device/sentry_computer, VENDOR_ITEM_REGULAR),
list("Spare PDT/L Battle Buddy Kit", round(scale * 3), /obj/item/storage/box/pdt_kit, VENDOR_ITEM_REGULAR),
list("Rail Flashlight", round(scale * 5), /obj/item/attachable/flashlight, VENDOR_ITEM_REGULAR),
)
diff --git a/code/game/objects/effects/effect_system/smoke.dm b/code/game/objects/effects/effect_system/smoke.dm
index b80f53b14d..4cd3c4a37d 100644
--- a/code/game/objects/effects/effect_system/smoke.dm
+++ b/code/game/objects/effects/effect_system/smoke.dm
@@ -258,6 +258,7 @@
var/xeno_affecting = FALSE
opacity = FALSE
alpha = 75
+ time_to_live = 20
/obj/effect/particle_effect/smoke/cn20/xeno
name = "CN20-X nerve gas"
@@ -276,10 +277,14 @@
/obj/effect/particle_effect/smoke/cn20/affect(mob/living/carbon/creature)
var/mob/living/carbon/xenomorph/xeno_creature
var/mob/living/carbon/human/human_creature
+ var/datum/internal_organ/lungs/lungs
+ var/datum/internal_organ/eyes/eyes
if(isxeno(creature))
xeno_creature = creature
else if(ishuman(creature))
human_creature = creature
+ lungs = human_creature.internal_organs_by_name["lungs"]
+ eyes = human_creature.internal_organs_by_name["eyes"]
if(!istype(creature) || issynth(creature) || creature.stat == DEAD)
return FALSE
if(!xeno_affecting && xeno_creature)
@@ -297,14 +302,18 @@
if(xeno_creature)
if(xeno_creature.interference < 4)
to_chat(xeno_creature, SPAN_XENOHIGHDANGER("Your awareness dims to a small area!"))
+ creature.apply_damage(20, BRUTE)
xeno_creature.interference = 10
xeno_creature.blinded = TRUE
else
- creature.apply_damage(12, OXY)
+ creature.apply_damage(12, TOX)
+ creature.apply_damage(2, BRAIN)
+ lungs.take_damage(2)
creature.SetEarDeafness(max(creature.ear_deaf, round(effect_amt*1.5))) //Paralysis of hearing system, aka deafness
- if(!xeno_creature && !creature.eye_blind) //Eye exposure damage
+ if(!xeno_creature) //Eye exposure damage
to_chat(creature, SPAN_DANGER("Your eyes sting. You can't see!"))
creature.SetEyeBlind(round(effect_amt/3))
+ eyes.take_damage(2)
if(!xeno_creature && creature.coughedtime != 1 && !creature.stat) //Coughing/gasping
creature.coughedtime = 1
if(prob(50))
@@ -330,6 +339,40 @@
human_creature.recalculate_move_delay = TRUE
return TRUE
+/////////////////////////////////////////////
+// ALD-91 LSD Gas
+/////////////////////////////////////////////
+
+/obj/effect/particle_effect/smoke/LSD
+ name = "ALD-91 LSD Gas"
+ smokeranking = SMOKE_RANK_HIGH
+ color = "#6e006e"
+ opacity = FALSE
+ alpha = 75
+ time_to_live = 20
+ var/stun_chance = 60
+
+/obj/effect/particle_effect/smoke/LSD/Move()
+ . = ..()
+ for(var/mob/living/carbon/human/human in get_turf(src))
+ affect(human)
+
+/obj/effect/particle_effect/smoke/LSD/affect(mob/living/carbon/human/creature)
+ if(!istype(creature) || issynth(creature) || creature.stat == DEAD || isyautja(creature))
+ return FALSE
+
+ if(creature.wear_mask && (creature.wear_mask.flags_inventory & BLOCKGASEFFECT))
+ return FALSE
+ if(creature.head.flags_inventory & BLOCKGASEFFECT)
+ return FALSE
+
+ creature.hallucination += 15
+ creature.druggy += 1
+
+ if(prob(stun_chance))
+ creature.apply_effect(1, WEAKEN)
+
+
//////////////////////////////////////
// FLASHBANG SMOKE
////////////////////////////////////
@@ -633,6 +676,9 @@
/datum/effect_system/smoke_spread/cn20/xeno
smoke_type = /obj/effect/particle_effect/smoke/cn20/xeno
+/datum/effect_system/smoke_spread/LSD
+ smoke_type = /obj/effect/particle_effect/smoke/LSD
+
// XENO SMOKES
/datum/effect_system/smoke_spread/xeno_acid
diff --git a/code/game/objects/items/devices/motion_detector.dm b/code/game/objects/items/devices/motion_detector.dm
index 99b11d1ff6..60cf62b4e2 100644
--- a/code/game/objects/items/devices/motion_detector.dm
+++ b/code/game/objects/items/devices/motion_detector.dm
@@ -37,7 +37,7 @@
var/iff_signal = FACTION_MARINE
actions_types = list(/datum/action/item_action)
var/scanning = FALSE // controls if MD is in process of scan
- var/datum/shape/rectangle/range_bounds
+ var/datum/shape/rectangle/square/range_bounds
var/long_range_locked = FALSE //only long-range MD
var/ping_overlay
@@ -53,7 +53,7 @@
/obj/item/device/motiondetector/Initialize()
. = ..()
- range_bounds = new //Just creating a rectangle datum
+ range_bounds = new //Just creating a square datum
update_icon()
/obj/item/device/motiondetector/Destroy()
@@ -215,12 +215,7 @@
if(!istype(cur_turf))
return
- if(!range_bounds)
- range_bounds = new/datum/shape/rectangle
- range_bounds.center_x = cur_turf.x
- range_bounds.center_y = cur_turf.y
- range_bounds.width = detector_range * 2
- range_bounds.height = detector_range * 2
+ range_bounds.set_shape(cur_turf.x, cur_turf.y, detector_range * 2)
var/list/ping_candidates = SSquadtree.players_in_range(range_bounds, cur_turf.z, QTREE_EXCLUDE_OBSERVER | QTREE_SCAN_MOBS)
diff --git a/code/game/objects/items/devices/radio/encryptionkey.dm b/code/game/objects/items/devices/radio/encryptionkey.dm
index 1e10978350..a945aa76b8 100644
--- a/code/game/objects/items/devices/radio/encryptionkey.dm
+++ b/code/game/objects/items/devices/radio/encryptionkey.dm
@@ -98,10 +98,10 @@
/obj/item/device/encryptionkey/mcom
name = "\improper Marine Command Radio Encryption Key"
icon_state = "cap_key"
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = TRUE, SQUAD_MARINE_2 = TRUE, SQUAD_MARINE_3 = TRUE, SQUAD_MARINE_4 = TRUE, SQUAD_MARINE_5 = TRUE, SQUAD_MARINE_CRYO = TRUE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_SENTRY = TRUE)
/obj/item/device/encryptionkey/mcom/alt
- channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE)
+ channels = list(RADIO_CHANNEL_COMMAND = TRUE, SQUAD_MARINE_1 = FALSE, SQUAD_MARINE_2 = FALSE, SQUAD_MARINE_3 = FALSE, SQUAD_MARINE_4 = FALSE, SQUAD_MARINE_5 = FALSE, SQUAD_MARINE_CRYO = FALSE, RADIO_CHANNEL_ENGI = TRUE, RADIO_CHANNEL_MEDSCI = TRUE, RADIO_CHANNEL_REQ = TRUE, RADIO_CHANNEL_JTAC = TRUE, RADIO_CHANNEL_INTEL = TRUE, RADIO_CHANNEL_SENTRY = TRUE)
// MARINE ENGINEERING
diff --git a/code/game/objects/items/explosives/grenades/marines.dm b/code/game/objects/items/explosives/grenades/marines.dm
index 46d2d4eba9..36ba614041 100644
--- a/code/game/objects/items/explosives/grenades/marines.dm
+++ b/code/game/objects/items/explosives/grenades/marines.dm
@@ -484,7 +484,7 @@
/// The typepath of the nerve gas
var/nerve_gas_type = /datum/effect_system/smoke_spread/cn20
/// The radius the gas will reach
- var/nerve_gas_radius = 2
+ var/nerve_gas_radius = 4
/obj/item/explosive/grenade/nerve_gas/Initialize(mapload, ...)
. = ..()
@@ -505,6 +505,38 @@
name = "\improper CN20-X canister grenade"
nerve_gas_type = /datum/effect_system/smoke_spread/cn20/xeno
+/*
+//================================================
+ LSD Gas Grenades
+//================================================
+*/
+/obj/item/explosive/grenade/LSD
+ name = "\improper ALD-91 canister grenade"
+ desc = "A canister grenade of nonlethal LSD gas. It is set to detonate in 4 seconds."
+ icon_state = "flashbang2"//temp icon
+ det_time = 40
+ item_state = "grenade_phos_clf"//temp icon
+ underslug_launchable = FALSE
+ harmful = TRUE
+ antigrief_protection = FALSE
+ var/datum/effect_system/smoke_spread/LSD/LSD_gas
+ var/LSD_gas_radius = 4
+
+/obj/item/explosive/grenade/LSD/Initialize()
+ . = ..() //if it ain't broke don't fix it
+ LSD_gas = new /datum/effect_system/smoke_spread/LSD
+ LSD_gas.attach(src)
+
+/obj/item/explosive/grenade/LSD/Destroy()
+ QDEL_NULL(LSD_gas)
+ return ..()
+
+/obj/item/explosive/grenade/LSD/prime()
+ playsound(src.loc, 'sound/effects/smoke.ogg', 25, 1, 4)
+ LSD_gas.set_up(LSD_gas_radius, 0, get_turf(src), null, 6)
+ LSD_gas.start()
+ qdel(src)
+
/*
//================================================
Airburst Smoke Grenades
diff --git a/code/game/objects/items/reagent_containers/food/cans.dm b/code/game/objects/items/reagent_containers/food/cans.dm
index aab2ee066e..d6d1b9b423 100644
--- a/code/game/objects/items/reagent_containers/food/cans.dm
+++ b/code/game/objects/items/reagent_containers/food/cans.dm
@@ -270,7 +270,7 @@
/obj/item/reagent_container/food/drinks/cans/boda
name = "\improper Boda"
desc = "State regulated soda beverage. Enjoy comrades."
- desc_lore = "Designed back in 2159, the advertising campaign for BODA started out as an attempt by the UPP to win the hearts and minds of colonists and settlers across the galaxy. Soon after, the ubiquitous cyan vendors and large supplies of the drink began to crop up in UA warehouses with seemingly no clear origin. Despite some concerns, after initial testing determined that the stored products were safe for consumption and surprisingly popular when blind-tested with focus groups, the strange surplus of BODA was authorized for usage within the UA-associated colonies. Subsequently, it enjoyed a relative popularity before falling into obscurity in the coming decades as supplies dwindled."
+ desc_lore = "Designed back in 2159, the advertising campaign for BODA started out as an attempt by the UPP to win the hearts and minds of colonists and settlers across the galaxy. Soon after, the ubiquitous cyan vendors and large supplies of the drink began to crop up in UA warehouses with seemingly no clear origin. Despite some concerns, after initial testing determined that the stored products were safe for consumption and surprisingly popular when blind-tested with focus groups, the strange surplus of BODA was authorized for usage within the UA-associated colonies. Subsequently, it enjoyed a relative popularity before falling into obscurity in the coming decades as supplies dwindled."
icon_state = "boda"
center_of_mass = "x=16;y=10"
@@ -347,10 +347,6 @@
icon_state = "souto_diet_classic"
item_state = "souto_diet_classic"
-/obj/item/reagent_container/food/drinks/cans/souto/diet/Initialize()
- . = ..()
- reagents.add_reagent("water", 25)
-
/obj/item/reagent_container/food/drinks/cans/souto/classic
name = "\improper Souto Classic"
desc = "The can boldly proclaims it to be tangerine flavored. You can't help but think that's a lie. Canned in Havana."
@@ -359,7 +355,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/classic/Initialize()
. = ..()
- reagents.add_reagent("souto_classic", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_classic", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/classic
name = "\improper Diet Souto"
@@ -369,7 +367,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/classic/Initialize()
. = ..()
- reagents.add_reagent("souto_classic", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_classic", 20)
/obj/item/reagent_container/food/drinks/cans/souto/cherry
name = "\improper Cherry Souto"
@@ -379,7 +379,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/cherry/Initialize()
. = ..()
- reagents.add_reagent("souto_cherry", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_cherry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/cherry
name = "\improper Diet Cherry Souto"
@@ -389,7 +391,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/cherry/Initialize()
. = ..()
- reagents.add_reagent("souto_cherry", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_cherry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/lime
name = "\improper Lime Souto"
@@ -399,7 +403,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/lime/Initialize()
. = ..()
- reagents.add_reagent("souto_lime", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_lime", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/lime
name = "\improper Diet Lime Souto"
@@ -409,7 +415,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/lime/Initialize()
. = ..()
- reagents.add_reagent("souto_lime", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_lime", 20)
/obj/item/reagent_container/food/drinks/cans/souto/grape
name = "\improper Grape Souto"
@@ -419,7 +427,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/grape/Initialize()
. = ..()
- reagents.add_reagent("souto_grape", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_grape", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/grape
name = "\improper Diet Grape Souto"
@@ -429,7 +439,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/grape/Initialize()
. = ..()
- reagents.add_reagent("souto_grape", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_grape", 20)
/obj/item/reagent_container/food/drinks/cans/souto/blue
name = "\improper Blue Raspberry Souto"
@@ -440,7 +452,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/blue/Initialize()
. = ..()
- reagents.add_reagent("souto_blueraspberry", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_blueraspberry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/blue
name = "\improper Diet Blue Raspberry Souto"
@@ -450,7 +464,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/blue/Initialize()
. = ..()
- reagents.add_reagent("souto_blueraspberry", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_blueraspberry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/peach
name = "\improper Peach Souto"
@@ -460,7 +476,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/peach/Initialize()
. = ..()
- reagents.add_reagent("souto_peach", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_peach", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/peach
name = "\improper Diet Peach Souto"
@@ -470,7 +488,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/peach/Initialize()
. = ..()
- reagents.add_reagent("souto_peach", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_peach", 20)
/obj/item/reagent_container/food/drinks/cans/souto/cranberry
name = "\improper Cranberry Souto"
@@ -480,7 +500,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/cranberry/Initialize()
. = ..()
- reagents.add_reagent("souto_cranberry", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_cranberry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/cranberry
name = "\improper Diet Cranberry Souto"
@@ -490,8 +512,9 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/cranberry/Initialize()
. = ..()
- reagents.add_reagent("souto_cranberry", 25)
- reagents.add_reagent("water", 25)
+ reagents.add_reagent("sucralose", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_cranberry", 20)
/obj/item/reagent_container/food/drinks/cans/souto/vanilla
name = "\improper Vanilla Souto"
@@ -511,29 +534,32 @@
/obj/item/reagent_container/food/drinks/cans/souto/diet/vanilla/Initialize()
. = ..()
+ reagents.add_reagent("sodawater", 25)
reagents.add_reagent("souto_vanilla", 25)
- reagents.add_reagent("water", 25)
/obj/item/reagent_container/food/drinks/cans/souto/pineapple
name = "\improper Pineapple Souto"
- desc = "This tastes like battery acid with a full cup of sugar mixed in. Canned in Havana."
+ desc = "This tastes like battery acid with a full cup of corn syrup mixed in. Canned in Havana."
icon_state = "souto_pineapple"
item_state = "souto_pineapple"
/obj/item/reagent_container/food/drinks/cans/souto/pineapple/Initialize()
. = ..()
- reagents.add_reagent("souto_pineapple", 50)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_pineapple", 20)
/obj/item/reagent_container/food/drinks/cans/souto/diet/pineapple
name = "\improper Diet Pineapple Souto"
- desc = "This tastes like battery acid with a half cup of sugar mixed in. Canned in Havana."
+ desc = "This tastes like battery acid with a full cup of sugar substitute mixed in. Canned in Havana."
icon_state = "souto_diet_pineapple"
item_state = "souto_diet_pineapple"
/obj/item/reagent_container/food/drinks/cans/souto/diet/pineapple/Initialize()
. = ..()
- reagents.add_reagent("souto_pineapple", 25)
- reagents.add_reagent("water", 25)
+ reagents.add_reagent("cornsyrup", 15)
+ reagents.add_reagent("sodawater", 15)
+ reagents.add_reagent("souto_pineapple", 20)
//ASPEN
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index b29c69e8af..6a4b479929 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -3,6 +3,7 @@
desc = "A large metal mesh strewn between two poles. Intended as a cheap way to separate areas, while allowing one to see through it."
icon = 'icons/obj/structures/props/fence.dmi'
icon_state = "fence0"
+ throwpass = TRUE
density = TRUE
anchored = TRUE
layer = WINDOW_LAYER
diff --git a/code/game/objects/structures/pipes/vents/vents.dm b/code/game/objects/structures/pipes/vents/vents.dm
index 298fbc57f4..926f14cd2f 100644
--- a/code/game/objects/structures/pipes/vents/vents.dm
+++ b/code/game/objects/structures/pipes/vents/vents.dm
@@ -139,7 +139,7 @@
if(welded)
to_chat(usr, SPAN_WARNING("You cannot release gas from a welded vent."))
return FALSE
- var/list/options = list(VENT_GAS_SMOKE, VENT_GAS_CN20, VENT_GAS_CN20_XENO)
+ var/list/options = list(VENT_GAS_SMOKE, VENT_GAS_CN20, VENT_GAS_CN20_XENO, VENT_GAS_LSD)
var/gas_choice = tgui_input_list(user, "What gas do you wish to use?", "Gas Choice", options, 20 SECONDS)
if(!gas_choice)
return FALSE
@@ -166,6 +166,8 @@
spreader = new /datum/effect_system/smoke_spread/cn20
if(VENT_GAS_CN20_XENO)
spreader = new /datum/effect_system/smoke_spread/cn20/xeno
+ if(VENT_GAS_LSD)
+ spreader = new /datum/effect_system/smoke_spread/LSD
if(!spreader)
return FALSE
gas_holder = spreader
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 50cb19a7e9..6e47391fc1 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -330,6 +330,7 @@ var/list/roundstart_mod_verbs = list(
if(CLIENT_HAS_RIGHTS(src, R_BUILDMODE))
add_verb(src, /client/proc/togglebuildmodeself)
add_verb(src, /client/proc/toggle_game_master)
+ add_verb(src, /client/proc/open_resin_panel)
add_verb(src, /client/proc/open_sound_panel)
add_verb(src, /client/proc/toggle_join_xeno)
add_verb(src, /client/proc/game_master_rename_platoon)
@@ -365,6 +366,7 @@ var/list/roundstart_mod_verbs = list(
admin_verbs_default,
/client/proc/togglebuildmodeself,
/client/proc/toggle_game_master,
+ /client/proc/open_resin_panel,
/client/proc/open_sound_panel,
/client/proc/toggle_join_xeno,
/client/proc/game_master_rename_platoon,
diff --git a/code/modules/admin/banjob.dm b/code/modules/admin/banjob.dm
index 18f06e79a6..230590fdf9 100644
--- a/code/modules/admin/banjob.dm
+++ b/code/modules/admin/banjob.dm
@@ -146,6 +146,12 @@ WARNING!*/
else
jobs += "Emergency Response Team | "
+ //Freed Mobs
+ if(jobban_isbanned(M, "Freed Mob", P) || isbanned_dept)
+ jobs += "Freed Mob | "
+ else
+ jobs += "Freed Mob | "
+
//Survivor
if(jobban_isbanned(M, "Survivor", P) || isbanned_dept)
jobs += "Survivor | "
diff --git a/code/modules/admin/game_master/game_master.dm b/code/modules/admin/game_master/game_master.dm
index 947c49aeb5..2745287727 100644
--- a/code/modules/admin/game_master/game_master.dm
+++ b/code/modules/admin/game_master/game_master.dm
@@ -158,7 +158,8 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
data["game_master_objectives"] = length(GLOB.game_master_objectives) ? GLOB.game_master_objectives : ""
// Communication stuff
- data["communication_clarity"] = GLOB.radio_communication_clarity
+ data["radio_clarity"] = GLOB.radio_communication_clarity
+ data["radio_clarity_example"] = stars("The quick brown fox jumped over the lazy dog.", GLOB.radio_communication_clarity)
return data
@@ -294,7 +295,7 @@ GLOBAL_VAR_INIT(radio_communication_clarity, 100)
if("use_game_master_phone")
game_master_phone.attack_hand(ui.user)
- if("set_communication_clarity")
+ if("set_radio_clarity")
var/new_clarity = text2num(params["clarity"])
if(!isnum(new_clarity))
return
diff --git a/code/modules/admin/game_master/resin_panel.dm b/code/modules/admin/game_master/resin_panel.dm
new file mode 100644
index 0000000000..bab4097173
--- /dev/null
+++ b/code/modules/admin/game_master/resin_panel.dm
@@ -0,0 +1,177 @@
+#define RESIN_PANEL_STRUCTURES \
+list( \
+ /datum/resin_construction/resin_turf/wall, \
+ /datum/resin_construction/resin_turf/wall/thick, \
+ /datum/resin_construction/resin_turf/wall/reflective, \
+ /datum/resin_construction/resin_turf/membrane, \
+ /datum/resin_construction/resin_turf/membrane/thick, \
+ /datum/resin_construction/resin_obj/door, \
+ /datum/resin_construction/resin_obj/door/thick, \
+ /datum/resin_construction/resin_obj/resin_node, \
+ /datum/resin_construction/resin_obj/sticky_resin, \
+ /datum/resin_construction/resin_obj/fast_resin, \
+ /datum/resin_construction/resin_obj/resin_spike, \
+ /datum/resin_construction/resin_obj/acid_pillar, \
+ /turf/closed/wall/mineral/bone_resin \
+)
+
+/client/proc/open_resin_panel()
+ set name = "Resin Panel"
+ set category = "Game Master"
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ new /datum/resin_panel(usr)
+
+/datum/resin_panel
+ var/static/list/structure_list
+ var/static/list/removal_allowlist
+ var/selected_structure
+ var/selected_hive = XENO_HIVE_NORMAL
+ var/client/holder
+ var/build_click_intercept = FALSE
+
+/datum/resin_panel/New(user)
+ if(isnull(structure_list)) //first run, init statics
+ structure_list = get_structures()
+
+ removal_allowlist = list()
+ for(var/structure as anything in RESIN_PANEL_STRUCTURES)
+ if(structure in GLOB.resin_constructions_list)
+ var/datum/resin_construction/construct = structure
+ removal_allowlist += construct.build_path
+ else
+ removal_allowlist += structure
+
+ if(isclient(user))
+ holder = user
+ else
+ var/mob/mob = user
+ holder = mob.client
+
+ holder.click_intercept = src
+ tgui_interact(holder.mob)
+
+/datum/resin_panel/proc/get_structures()
+ var/list/structures = list()
+ for(var/structure as anything in RESIN_PANEL_STRUCTURES)
+ var/list/entry = list()
+
+ if(structure in GLOB.resin_constructions_list)
+ var/datum/resin_construction/construct = structure
+ entry["name"] = construct.name
+ entry["image"] = replacetext(construct.construction_name, " ", "-")
+ entry["id"] = "[construct]"
+ else if(structure in typesof(/turf))
+ var/turf/turf = structure
+ entry["name"] = turf.name
+ switch(turf)
+ if(/turf/closed/wall/mineral/bone_resin)
+ entry["image"] = "reflective-resin-wall" //looks just like it, saves on making new spritesheet for one image
+ else
+ entry["image"] = turf.icon_state
+ entry["id"] = "[turf]"
+
+ structures += list(entry)
+
+ return structures
+
+/datum/resin_panel/ui_assets(mob/user)
+ return list(
+ get_asset_datum(/datum/asset/spritesheet/choose_resin),
+ )
+
+/datum/resin_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["structure_list"] = structure_list
+ data["hives_list"] = ALL_XENO_HIVES
+
+ return data
+
+/datum/resin_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ data["selected_structure"] = selected_structure
+ data["selected_hive"] = selected_hive
+ data["build_click_intercept"] = build_click_intercept
+
+ return data
+
+/datum/resin_panel/ui_close(mob/user)
+ holder = null
+ build_click_intercept = FALSE
+ qdel(src)
+
+/datum/resin_panel/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/resin_panel/ui_status(mob/user, datum/ui_state/state)
+ return UI_INTERACTIVE
+
+/datum/resin_panel/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "ResinPanel", "Resin Panel")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/datum/resin_panel/proc/InterceptClickOn(mob/user, params, atom/object)
+ if(!build_click_intercept)
+ return
+
+ var/list/modifiers = params2list(params)
+
+ if(LAZYACCESS(modifiers, MIDDLE_CLICK)) //remove
+ if(!(object.type in removal_allowlist))
+ return
+
+ if(isturf(object))
+ var/turf/turf = object
+ turf.ScrapeAway()
+ else
+ qdel(object)
+ else //add
+ if(!selected_structure)
+ return
+
+ var/turf/current_turf = get_turf(object)
+
+ var/atom/new_structure
+ if(selected_structure in GLOB.resin_constructions_list)
+ var/datum/resin_construction/construct = GLOB.resin_constructions_list[selected_structure]
+ new_structure = construct.build(current_turf, selected_hive)
+ else if(selected_structure in typesof(/turf))
+ var/turf/turf = selected_structure
+ new_structure = current_turf.PlaceOnTop(turf)
+ new_structure?.add_hiddenprint(user) //so admins know who placed it
+
+ return TRUE
+
+/datum/resin_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ if(!check_rights(R_ADMIN))
+ return
+
+ switch(action)
+ if("set_selected_structure")
+ var/selected_type = text2path(params["type"])
+ if(!(selected_type in RESIN_PANEL_STRUCTURES))
+ return
+ selected_structure = selected_type
+ return TRUE
+ if("set_selected_hive")
+ var/hive = params["selected_hive"]
+ if(!(hive in ALL_XENO_HIVES))
+ return
+ selected_hive = hive
+ return TRUE
+ if("toggle_build_click_intercept")
+ build_click_intercept = !build_click_intercept
+ return TRUE
+
+#undef RESIN_PANEL_STRUCTURES
diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm
index 868d33a332..9b8b319731 100644
--- a/code/modules/client/preferences_gear.dm
+++ b/code/modules/client/preferences_gear.dm
@@ -1073,3 +1073,8 @@ var/global/list/gear_datums_by_name = list()
display_name = "Cut-throat razor"
path = /obj/item/weapon/straight_razor
cost = 3
+
+/datum/gear/misc/flak
+ display_name = "M67 flak jacket"
+ path = /obj/item/clothing/accessory/flak
+ cost = 3
diff --git a/code/modules/clothing/shoes/marine_shoes.dm b/code/modules/clothing/shoes/marine_shoes.dm
index 1a11545ea4..365a8a551a 100644
--- a/code/modules/clothing/shoes/marine_shoes.dm
+++ b/code/modules/clothing/shoes/marine_shoes.dm
@@ -231,7 +231,8 @@
/obj/item/clothing/shoes/royal_marine
name = "\improper L10 pattern combat boots"
desc = "Standard issue combat boots for combat scenarios or combat situations. Used by the three world empires royal marines commando units."
- icon_state = "rmc_boots"
+ icon_state = "marine"
+ item_state = "marine"
armor_melee = CLOTHING_ARMOR_MEDIUMHIGH
armor_bullet = CLOTHING_ARMOR_MEDIUMHIGH
armor_laser = CLOTHING_ARMOR_LOW
diff --git a/code/modules/clothing/under/ties.dm b/code/modules/clothing/under/ties.dm
index 23080104a4..e1c029bae3 100644
--- a/code/modules/clothing/under/ties.dm
+++ b/code/modules/clothing/under/ties.dm
@@ -937,3 +937,27 @@
icon = 'icons/obj/items/clothing/ties.dmi'
icon_state = "owlf_vest"
item_state = "owlf_vest"
+
+/obj/item/clothing/accessory/flak
+ name = "M67 flak vest"
+ desc = "An older model of flak jacket worn by combat support personnel such as dropship crew, or occasionally by smartgunners. Much comfier than its M70 successor, and can be worn under most combat armor, however, the ballistic protection leaves much to be desired."
+ icon_state = "flak"
+ item_state = "flak"
+ w_class = SIZE_MEDIUM
+ var/tucked_in = FALSE
+
+/obj/item/clothing/accessory/flak/get_examine_text(mob/user)
+ . = ..()
+ . += SPAN_NOTICE("You can wear it differently by using it in hand.")
+
+/obj/item/clothing/accessory/flak/attack_self(mob/user)
+ ..()
+
+ tucked_in = !tucked_in
+ if(tucked_in)
+ icon_state = "flakslim"
+ user.visible_message(SPAN_NOTICE("[user] tucks in [src]'s sleeves."), SPAN_NOTICE("You tuck in [src]'s sleeves."))
+ else
+ icon_state = initial(icon_state)
+ user.visible_message(SPAN_NOTICE("[user] decides to keep [src] nice and puffy."), SPAN_NOTICE("You decide to keep [src] nice and puffy."))
+ item_state = icon_state
diff --git a/code/modules/cm_aliens/structures/special/egg_morpher.dm b/code/modules/cm_aliens/structures/special/egg_morpher.dm
index 1fd154eb35..e8040f81ba 100644
--- a/code/modules/cm_aliens/structures/special/egg_morpher.dm
+++ b/code/modules/cm_aliens/structures/special/egg_morpher.dm
@@ -14,14 +14,14 @@
var/huggers_to_grow_max = 12
var/huggers_reserved = 0
var/mob/captured_mob
- var/datum/shape/rectangle/range_bounds
+ var/datum/shape/range_bounds
appearance_flags = KEEP_TOGETHER
layer = FACEHUGGER_LAYER
/obj/effect/alien/resin/special/eggmorph/Initialize(mapload, hive_ref)
. = ..()
- range_bounds = RECT(x, y, EGGMORPG_RANGE, EGGMORPG_RANGE)
+ range_bounds = SQUARE(x, y, EGGMORPG_RANGE)
/obj/effect/alien/resin/special/eggmorph/Destroy()
if (stored_huggers && linked_hive)
@@ -155,7 +155,7 @@
/obj/effect/alien/resin/special/eggmorph/proc/check_facehugger_target()
if(!range_bounds)
- range_bounds = RECT(x, y, EGGMORPG_RANGE, EGGMORPG_RANGE)
+ range_bounds = SQUARE(x, y, EGGMORPG_RANGE)
var/list/targets = SSquadtree.players_in_range(range_bounds, z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
if(isnull(targets) || !length(targets))
diff --git a/code/modules/cm_aliens/weeds.dm b/code/modules/cm_aliens/weeds.dm
index 736d4f9474..9d15211f21 100644
--- a/code/modules/cm_aliens/weeds.dm
+++ b/code/modules/cm_aliens/weeds.dm
@@ -511,9 +511,9 @@
overlay_node = TRUE
overlays += staticnode
-/obj/effect/alien/weeds/node/Initialize(mapload, obj/effect/alien/weeds/node/node, mob/living/carbon/xenomorph/xeno, datum/hive_status/hive)
- if (istype(hive))
- linked_hive = hive
+/obj/effect/alien/weeds/node/Initialize(mapload, hive, mob/living/carbon/xenomorph/xeno)
+ if (hive)
+ linked_hive = GLOB.hive_datum[hive]
else if (istype(xeno) && xeno.hive)
linked_hive = xeno.hive
else
diff --git a/code/modules/cm_tech/techtree.dm b/code/modules/cm_tech/techtree.dm
index 6c39d8ab9c..a027789185 100644
--- a/code/modules/cm_tech/techtree.dm
+++ b/code/modules/cm_tech/techtree.dm
@@ -58,16 +58,24 @@
if(longest_tier < tier_length)
longest_tier = tier_length
- // Clear out the area
- for(var/t in block(locate(1, 1, zlevel.z_value), locate(longest_tier * 2 + 1, length(all_techs) * 3 + 1, zlevel.z_value)))
+ // Clear out and create the area
+ // (The `+ 2` on both of these is 1 for a buffer tile, and 1 for the outer `/turf/closed/void`.)
+ var/area_max_x = longest_tier * 2 + 2
+ var/area_max_y = length(all_techs) * 3 + 2
+ for(var/t in block(locate(1, 1, zlevel.z_value), locate(area_max_x, area_max_y, zlevel.z_value)))
var/turf/pos = t
for(var/A in pos)
qdel(A)
- pos.ChangeTurf(/turf/open/blank)
- pos.color = "#000000"
-
+ if(pos.x == area_max_x || pos.y == area_max_y)
+ // The turfs around the edge are closed.
+ pos.ChangeTurf(/turf/closed/void)
+ else
+ pos.ChangeTurf(/turf/open/blank)
+ pos.color = "#000000"
+ new /area/techtree(pos)
+ // Create the tech nodes
var/y_offset = 1
for(var/tier in all_techs)
var/tier_length = length(all_techs[tier])
diff --git a/code/modules/defenses/bell_tower.dm b/code/modules/defenses/bell_tower.dm
index 52207298c4..a9d6c08bc7 100644
--- a/code/modules/defenses/bell_tower.dm
+++ b/code/modules/defenses/bell_tower.dm
@@ -257,7 +257,7 @@
STOP_PROCESSING(SSobj, src)
return
- var/list/targets = SSquadtree.players_in_range(RECT(M.x, M.y, area_range, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
+ var/list/targets = SSquadtree.players_in_range(SQUARE(M.x, M.y, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
if(!targets)
return
diff --git a/code/modules/defenses/planted_flag.dm b/code/modules/defenses/planted_flag.dm
index fac725047f..f0f6b05565 100644
--- a/code/modules/defenses/planted_flag.dm
+++ b/code/modules/defenses/planted_flag.dm
@@ -7,7 +7,7 @@
desc = "A planted flag with the iconic USCM flag plastered all over it, you feel a burst of energy by its mere sight."
handheld_type = /obj/item/defenses/handheld/planted_flag
disassemble_time = 10
- var/datum/shape/rectangle/range_bounds
+ var/datum/shape/range_bounds
var/area_range = PLANTED_FLAG_RANGE
var/buff_intensity = PLANTED_FLAG_BUFF
health = 200
@@ -33,7 +33,7 @@
apply_area_effect()
start_processing()
- range_bounds = RECT(x, y, PLANTED_FLAG_RANGE, PLANTED_FLAG_RANGE)
+ range_bounds = SQUARE(x, y, PLANTED_FLAG_RANGE)
update_icon()
/obj/structure/machinery/defenses/planted_flag/Destroy()
@@ -70,9 +70,9 @@
/obj/structure/machinery/defenses/planted_flag/proc/apply_area_effect()
if(!range_bounds)
- range_bounds = RECT(x, y, area_range, area_range)
+ range_bounds = SQUARE(x, y, area_range)
- var/list/targets = SSquadtree.players_in_range(RECT(x, y, area_range, area_range), z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
+ var/list/targets = SSquadtree.players_in_range(SQUARE(x, y, area_range), z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
if(!targets)
return
@@ -146,7 +146,7 @@
if(!M.x && !M.y && !M.z)
return
- var/list/targets = SSquadtree.players_in_range(RECT(M.x, M.y, area_range, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
+ var/list/targets = SSquadtree.players_in_range(SQUARE(M.x, M.y, area_range), M.z, QTREE_SCAN_MOBS | QTREE_EXCLUDE_OBSERVER)
targets |= M
for(var/mob/living/carbon/human/H in targets)
diff --git a/code/modules/defenses/sentry_computer.dm b/code/modules/defenses/sentry_computer.dm
index 906473056e..bb6f55d72b 100644
--- a/code/modules/defenses/sentry_computer.dm
+++ b/code/modules/defenses/sentry_computer.dm
@@ -274,7 +274,7 @@
if (do_after(user, 1 SECONDS, INTERRUPT_NO_NEEDHAND, BUSY_ICON_GENERIC))
unpair_sentry(sentry)
to_chat(user, SPAN_NOTICE("[sentry] has been decrypted."))
- var/message = "[sentry] removed from from [src]"
+ var/message = "[sentry] removed from [src]"
INVOKE_ASYNC(src, PROC_REF(send_message), message)
/**
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 44a3592329..409c88388f 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -1034,6 +1034,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, SPAN_WARNING("The game hasn't started yet!"))
return
+ if(jobban_isbanned(src, "Freed Mob"))
+ to_chat(src, SPAN_WARNING("You are banned from being able to join as a freed mob."))
+ return
+
var/list/mobs_by_role = list() // the list the mobs are assigned to first, for sorting purposes
for(var/mob/freed_mob as anything in GLOB.freed_mob_list)
var/role_name = freed_mob.get_role_name()
@@ -1059,6 +1063,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(!istype(freed_mob) || !(freed_mob in GLOB.freed_mob_list))
return
+ if(jobban_isbanned(src, "Freed Mob"))
+ to_chat(src, SPAN_WARNING("You are banned from being able to join as a freed mob."))
+ return
+
if(QDELETED(freed_mob) || freed_mob.client)
GLOB.freed_mob_list -= freed_mob
to_chat(src, SPAN_WARNING("Something went wrong."))
diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
index 958e0f7a2f..aa8bf7eae6 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_powers.dm
@@ -67,7 +67,7 @@
xeno.visible_message(SPAN_XENONOTICE("\The [xeno] regurgitates a pulsating node and plants it on the ground!"), \
SPAN_XENONOTICE("You regurgitate a pulsating node and plant it on the ground!"), null, 5)
- var/obj/effect/alien/weeds/node/new_node = new node_type(xeno.loc, src, xeno)
+ var/obj/effect/alien/weeds/node/new_node = new node_type(xeno.loc, xeno.hivenumber, xeno)
if(to_convert)
for(var/cur_weed in to_convert)
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 08412dab48..b60039840d 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -249,7 +249,7 @@ var/global/list/limb_types_by_name = list(
message = replace_X.Replace(message, "CKTH")
return message
-#define PIXELS_PER_STRENGTH_VAL 24
+#define PIXELS_PER_STRENGTH_VAL 28
/proc/shake_camera(mob/M, steps = 1, strength = 1, time_per_step = 1)
if(!M?.client || (M.shakecamera > world.time))
@@ -260,10 +260,10 @@ var/global/list/limb_types_by_name = list(
var/old_X = M.client.pixel_x
var/old_y = M.client.pixel_y
- animate(M.client, pixel_x = old_X + rand(-(strength), strength), pixel_y = old_y + rand(-(strength), strength), easing = JUMP_EASING, time = time_per_step, flags = ANIMATION_PARALLEL)
+ animate(M.client, pixel_x = old_X + rand(-(strength), strength), pixel_y = old_y + rand(-(strength), strength), easing = CUBIC_EASING | EASE_IN, time = time_per_step, flags = ANIMATION_PARALLEL)
var/i = 1
while(i < steps)
- animate(pixel_x = old_X + rand(-(strength), strength), pixel_y = old_y + rand(-(strength), strength), easing = JUMP_EASING, time = time_per_step)
+ animate(pixel_x = old_X + rand(-(strength), strength), pixel_y = old_y + rand(-(strength), strength), easing = CUBIC_EASING | EASE_IN, time = time_per_step)
i++
animate(pixel_x = old_X, pixel_y = old_y,time = Clamp(Floor(strength/PIXELS_PER_STRENGTH_VAL),2,4))//ease it back
diff --git a/code/modules/projectiles/ammo_boxes/magazine_boxes.dm b/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
index 7bdf49f0da..320693db36 100644
--- a/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
+++ b/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
@@ -72,6 +72,14 @@
/obj/item/ammo_box/magazine/mk1/empty
empty = TRUE
+/obj/item/ammo_box/magazine/mk1/heap
+ name = "magazine box (HEAP M41A MK1 X 10)"
+ overlay_ammo_type = "_heap"
+ overlay_content = "_heap"
+ magazine_type = /obj/item/ammo_magazine/rifle/m41aMK1/heap
+
+/obj/item/ammo_box/magazine/mk1/heap/empty
+ empty = TRUE
//-----------------------M39 Rifle Mag Boxes-----------------------
/obj/item/ammo_box/magazine/m39
diff --git a/code/modules/projectiles/magazines/shotguns.dm b/code/modules/projectiles/magazines/shotguns.dm
index 9e2574eabb..3c26685a55 100644
--- a/code/modules/projectiles/magazines/shotguns.dm
+++ b/code/modules/projectiles/magazines/shotguns.dm
@@ -82,6 +82,7 @@ var/list/shotgun_boxes_12g = list(
/obj/item/ammo_magazine/shotgun/buckshot/special
name = "box of buckshot shells, USCM special type"
desc = "A box filled with buckshot spread shotgun shells, USCM special type. 12 Gauge."
+ icon_state = "special"
default_ammo = /datum/ammo/bullet/shotgun/buckshot/special
//-------------------------------------------------------
@@ -205,6 +206,7 @@ var/list/shotgun_handfuls_12g = list(
/obj/item/ammo_magazine/handful/shotgun/buckshot/special
name = "handful of shotgun buckshot shells, USCM special type (12g)"
+ icon_state = "special_buck_5"
default_ammo = /datum/ammo/bullet/shotgun/buckshot/special
/obj/item/ammo_magazine/handful/shotgun/buckshot/incendiary
diff --git a/code/modules/reagents/chemistry_reagents/other.dm b/code/modules/reagents/chemistry_reagents/other.dm
index 45e66c182e..bbeeca2e3c 100644
--- a/code/modules/reagents/chemistry_reagents/other.dm
+++ b/code/modules/reagents/chemistry_reagents/other.dm
@@ -363,6 +363,21 @@
properties = list(PROPERTY_NUTRITIOUS = 1)
flags = REAGENT_TYPE_MEDICAL
+/datum/reagent/cornsyrup
+ name = "High-Fructose Corn Syrup"
+ id = "cornsyrup"
+ description = "Ah, some good old, all-american, high-fructose corn syrup. Sickeningly sweet. There's worse things you can put in your body, but very few of them are made to be put there."
+ color = "#593512" // rgb: 89, 53, 18
+ chemclass = CHEM_CLASS_NONE
+ properties = list(PROPERTY_NUTRITIOUS = 1)
+
+/datum/reagent/sucralose
+ name = "Sucralose"
+ id = "sucralose"
+ description = "600 times the flavor of sugar, with none of the calories!"
+ color = "#FFFFFF" // rgb: 255, 255, 255
+ chemclass = CHEM_CLASS_NONE
+
/datum/reagent/glycerol
name = "Glycerol"
id = "glycerol"
diff --git a/code/modules/vehicles/hardpoints/primary/arc_frontal.dm b/code/modules/vehicles/hardpoints/primary/arc_frontal.dm
index 68dd21aafa..a9f720412d 100644
--- a/code/modules/vehicles/hardpoints/primary/arc_frontal.dm
+++ b/code/modules/vehicles/hardpoints/primary/arc_frontal.dm
@@ -13,8 +13,6 @@
health = 2000
firing_arc = 120
- origins = list(0, -2)
-
allowed_seat = VEHICLE_DRIVER
ammo = new /obj/item/ammo_magazine/hardpoint/m56_cupola/frontal_cannon
diff --git a/code/modules/vehicles/multitile/multitile_movement.dm b/code/modules/vehicles/multitile/multitile_movement.dm
index b5f3081447..b95a7bd057 100644
--- a/code/modules/vehicles/multitile/multitile_movement.dm
+++ b/code/modules/vehicles/multitile/multitile_movement.dm
@@ -159,7 +159,7 @@
// Crashed with something that stopped us
if(!can_move)
- move_momentum = Floor(move_momentum/2)
+ move_momentum = trunc(move_momentum/2)
update_next_move()
interior_crash_effect()
@@ -251,10 +251,10 @@
return
// Not enough momentum for anything serious
- if(abs(move_momentum) <= 1)
+ if(abs(move_momentum) < 1)
return
- var/fling_distance = Ceiling(move_momentum/move_max_momentum) * 2
+ var/fling_distance = Ceiling(abs(move_momentum)/move_max_momentum) * 2
var/turf/target = interior.get_middle_turf()
for (var/x in 0 to fling_distance-1)
@@ -272,7 +272,7 @@
if(isliving(A))
var/mob/living/M = A
- shake_camera(M, 2, Ceiling(move_momentum/move_max_momentum) * 1)
+ shake_camera(M, 2, Ceiling(abs(move_momentum)/move_max_momentum) * 1)
if(!M.buckled)
M.apply_effect(1, STUN)
M.apply_effect(2, WEAKEN)
diff --git a/colonialmarines.dme b/colonialmarines.dme
index d75338ecfe..58cee33fcf 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -1388,6 +1388,7 @@
#include "code\modules\admin\ToRban.dm"
#include "code\modules\admin\game_master\game_master.dm"
#include "code\modules\admin\game_master\game_master_submenu.dm"
+#include "code\modules\admin\game_master\resin_panel.dm"
#include "code\modules\admin\game_master\sound_panel.dm"
#include "code\modules\admin\game_master\extra_buttons\rappel_menu.dm"
#include "code\modules\admin\game_master\extra_buttons\rename_platoon.dm"
diff --git a/dependencies.sh b/dependencies.sh
index 01d0ca5c97..1934553cb0 100644
--- a/dependencies.sh
+++ b/dependencies.sh
@@ -18,4 +18,4 @@ export NODE_VERSION_PRECISE=14.16.1
export SPACEMAN_DMM_VERSION=suite-1.8
# Python version for mapmerge and other tools
-export PYTHON_VERSION=3.7.9
+export PYTHON_VERSION=3.9.0
diff --git a/html/changelogs/archive/2024-07.yml b/html/changelogs/archive/2024-07.yml
new file mode 100644
index 0000000000..f7d36bac1a
--- /dev/null
+++ b/html/changelogs/archive/2024-07.yml
@@ -0,0 +1,19 @@
+2024-07-19:
+ Meatstuff882:
+ - rscadd: Added an icon for special buckshot.
+2024-07-22:
+ Doubleumc:
+ - admin: Radio Clarity slider now shows an example message
+ - qol: ARC turret shots start from the center of the ARC
+2024-07-24:
+ Doubleumc:
+ - bugfix: sounds & motion detectors should be more reliable
+ - rscdel: Removed automatic end of round vote and reboot
+2024-07-25:
+ Doubleumc:
+ - code_imp: Made the Tech Tree subsystem initialise faster.
+ Zonespace27:
+ - admin: Added freed mob bans to the jobban panel.
+2024-07-31:
+ Doubleumc:
+ - admin: round start delayed by default
diff --git a/html/changelogs/archive/2024-08.yml b/html/changelogs/archive/2024-08.yml
new file mode 100644
index 0000000000..f2770928ed
--- /dev/null
+++ b/html/changelogs/archive/2024-08.yml
@@ -0,0 +1,3 @@
+2024-08-01:
+ Doubleumc:
+ - ui: reduced size of Discord button
diff --git a/icons/mob/humans/onmob/back.dmi b/icons/mob/humans/onmob/back.dmi
index 670afcf34e..39abfe5ce6 100644
Binary files a/icons/mob/humans/onmob/back.dmi and b/icons/mob/humans/onmob/back.dmi differ
diff --git a/icons/mob/humans/onmob/belt.dmi b/icons/mob/humans/onmob/belt.dmi
index 3750cb985c..9079bda5e7 100644
Binary files a/icons/mob/humans/onmob/belt.dmi and b/icons/mob/humans/onmob/belt.dmi differ
diff --git a/icons/mob/humans/onmob/feet.dmi b/icons/mob/humans/onmob/feet.dmi
index 84121e6b09..909a629f72 100644
Binary files a/icons/mob/humans/onmob/feet.dmi and b/icons/mob/humans/onmob/feet.dmi differ
diff --git a/icons/mob/humans/onmob/head_1.dmi b/icons/mob/humans/onmob/head_1.dmi
index 139dae66e8..fcd8917c9c 100644
Binary files a/icons/mob/humans/onmob/head_1.dmi and b/icons/mob/humans/onmob/head_1.dmi differ
diff --git a/icons/mob/humans/onmob/suit_1.dmi b/icons/mob/humans/onmob/suit_1.dmi
index 3096925329..279ce1ca7b 100644
Binary files a/icons/mob/humans/onmob/suit_1.dmi and b/icons/mob/humans/onmob/suit_1.dmi differ
diff --git a/icons/mob/humans/onmob/ties.dmi b/icons/mob/humans/onmob/ties.dmi
index f277dc02a7..4573d24239 100644
Binary files a/icons/mob/humans/onmob/ties.dmi and b/icons/mob/humans/onmob/ties.dmi differ
diff --git a/icons/obj/items/clothing/belts.dmi b/icons/obj/items/clothing/belts.dmi
index 0a194a7bbc..18e011f048 100644
Binary files a/icons/obj/items/clothing/belts.dmi and b/icons/obj/items/clothing/belts.dmi differ
diff --git a/icons/obj/items/clothing/cm_hats.dmi b/icons/obj/items/clothing/cm_hats.dmi
index ada7540e96..1411de23fb 100644
Binary files a/icons/obj/items/clothing/cm_hats.dmi and b/icons/obj/items/clothing/cm_hats.dmi differ
diff --git a/icons/obj/items/clothing/cm_suits.dmi b/icons/obj/items/clothing/cm_suits.dmi
index bc012f71ca..016a1cd6c0 100644
Binary files a/icons/obj/items/clothing/cm_suits.dmi and b/icons/obj/items/clothing/cm_suits.dmi differ
diff --git a/icons/obj/items/clothing/ties.dmi b/icons/obj/items/clothing/ties.dmi
index 17118d8760..6a4677c5f6 100644
Binary files a/icons/obj/items/clothing/ties.dmi and b/icons/obj/items/clothing/ties.dmi differ
diff --git a/icons/obj/items/clothing/ties_overlay.dmi b/icons/obj/items/clothing/ties_overlay.dmi
index 599c89484b..ab2276b00d 100644
Binary files a/icons/obj/items/clothing/ties_overlay.dmi and b/icons/obj/items/clothing/ties_overlay.dmi differ
diff --git a/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi b/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi
index d883a24e71..0d50208cc8 100644
Binary files a/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi and b/icons/obj/items/weapons/guns/ammo_by_faction/uscm.dmi differ
diff --git a/icons/obj/items/weapons/guns/handful.dmi b/icons/obj/items/weapons/guns/handful.dmi
index bbea110531..1d46db248d 100644
Binary files a/icons/obj/items/weapons/guns/handful.dmi and b/icons/obj/items/weapons/guns/handful.dmi differ
diff --git a/interface/skin.dmf b/interface/skin.dmf
index 79ec957f08..ea5f18a91a 100644
--- a/interface/skin.dmf
+++ b/interface/skin.dmf
@@ -287,16 +287,12 @@ window "infowindow"
elem "discord"
type = BUTTON
pos = 420,5
- size = 210x45
+ size = 100x20
anchor1 = 66,0
- anchor2 = 97,0
+ anchor2 = 81,0
background-color = #7289da
- font-size = 20px
- font-family = "Arial"
- text-align = center
- font-weight = bold
saved-params = "is-checked"
- text = "discord.gg/PVE-CMSS13"
+ text = "Discord"
command = "discord"
window "outputwindow"
diff --git a/maps/corsat.json b/maps/corsat.json
index e819264405..24e8afccbe 100644
--- a/maps/corsat.json
+++ b/maps/corsat.json
@@ -42,7 +42,7 @@
"xeno_hive_charlie": 0,
"xeno_hive_delta": 0
},
- "camouflage": "snow",
+ "camouflage": "jungle",
"gamemodes": [
"Distress Signal",
"Hunter Games",
diff --git a/maps/derelict_almayer.json b/maps/derelict_almayer.json
index 0d0fc08da4..46108fc4ea 100644
--- a/maps/derelict_almayer.json
+++ b/maps/derelict_almayer.json
@@ -7,7 +7,7 @@
"announce_text": "An automated distress signal has been received from the \"USS Almayer\". A response team from the ###SHIPNAME### will be dispatched shortly to investigate.",
"traits": [{ "Ground": true }],
"nightmare_path": "maps/Nightmare/maps/derelict_almayer/",
- "camouflage": "classic",
+ "camouflage": "jungle",
"gamemodes": [
"Distress Signal",
"Hunter Games",
diff --git a/maps/fiorina_sciannex.json b/maps/fiorina_sciannex.json
index dfc7365541..f278861370 100644
--- a/maps/fiorina_sciannex.json
+++ b/maps/fiorina_sciannex.json
@@ -33,7 +33,7 @@
"xeno_hive_bravo": 0,
"xeno_hive_charlie": 0
},
- "camouflage": "classic",
+ "camouflage": "jungle",
"gamemodes": [
"Distress Signal",
"Hunter Games",
diff --git a/maps/lv522_chances_claim.json b/maps/lv522_chances_claim.json
index ac7dfa5f1d..4e050d2aa9 100644
--- a/maps/lv522_chances_claim.json
+++ b/maps/lv522_chances_claim.json
@@ -37,7 +37,7 @@
"xeno_hive_charlie": 0,
"xeno_hive_delta": 0
},
- "camouflage": "classic",
+ "camouflage": "jungle",
"gamemodes": [
"Distress Signal",
"Hunter Games",
diff --git a/maps/override_ground.json b/maps/override_ground.json
index 94d3c70757..8cfc8e1ca0 100644
--- a/maps/override_ground.json
+++ b/maps/override_ground.json
@@ -1,15 +1,9 @@
{
"override_map": true,
- "map_name": "Unknown",
- "survivor_types": [
- "/datum/equipment_preset/survivor/civilian",
- "/datum/equipment_preset/survivor/goon"
- ],
+ "map_name": "Area of Operation",
"map_item_type": "/obj/item/map/big_red_map",
- "announce_text": "We've lost contact with a Weyland-Yutani's research facility. The ###SHIPNAME### has been dispatched to assist.",
- "monkey_types": [
- "neaera"
- ],
+ "announce_text": "Now in orbit above Area of Operation.",
+ "camouflage": "jungle",
"traits": [{ "Ground": true }],
"gamemodes": [
"Distress Signal",
diff --git a/maps/prison_station_fop.json b/maps/prison_station_fop.json
index b29f7255c2..907129fedb 100644
--- a/maps/prison_station_fop.json
+++ b/maps/prison_station_fop.json
@@ -32,7 +32,7 @@
"xeno_hive_bravo": 0,
"xeno_hive_charlie": 0
},
- "camouflage": "classic",
+ "camouflage": "jungle",
"gamemodes": [
"Distress Signal",
"Hunter Games",
diff --git a/tgui/packages/tgui/interfaces/GameMaster.js b/tgui/packages/tgui/interfaces/GameMaster.js
index 32949fea54..fa0312c01a 100644
--- a/tgui/packages/tgui/interfaces/GameMaster.js
+++ b/tgui/packages/tgui/interfaces/GameMaster.js
@@ -1,5 +1,5 @@
import { useBackend } from '../backend';
-import { Dropdown, Button, Section, Slider, Collapsible, Stack, Divider } from '../components';
+import { Dropdown, Box, Button, Section, Slider, Collapsible, Stack, Divider } from '../components';
import { Window } from '../layouts';
export const GameMaster = (props, context) => {
@@ -216,18 +216,22 @@ export const GameMasterCommunicationPanel = (props, context) => {
}}
/>
- Communication Clarity
+ Radio Clarity
{
- act('set_communication_clarity', { clarity });
+ act('set_radio_clarity', { clarity });
}}
/>
+
+ {`"${data.radio_clarity_example}"`}
+
);
diff --git a/tgui/packages/tgui/interfaces/ResinPanel.tsx b/tgui/packages/tgui/interfaces/ResinPanel.tsx
new file mode 100644
index 0000000000..e4587fe826
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/ResinPanel.tsx
@@ -0,0 +1,94 @@
+import { useBackend } from 'tgui/backend';
+import { Box, Button, Dropdown, Section, Stack, Tabs } from 'tgui/components';
+import { Window } from 'tgui/layouts';
+import { BooleanLike, classes } from 'common/react';
+
+interface ResinPanelData {
+ structure_list: StructureEntry[];
+ hives_list: string[];
+ selected_structure: string;
+ selected_hive: string;
+ build_click_intercept: BooleanLike;
+}
+
+interface StructureEntry {
+ name: string;
+ image: string;
+ id: string;
+}
+
+export const ResinPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ structure_list,
+ hives_list,
+ selected_structure,
+ selected_hive,
+ build_click_intercept,
+ } = data;
+
+ let compact = false;
+
+ return (
+
+
+
+
+
+
+ {
+ act('set_selected_hive', { selected_hive });
+ }}
+ />
+
+
+ }
+ scrollable
+ fill>
+
+ {structure_list.map((item, index) => (
+
+ act('set_selected_structure', { type: item.id })
+ }>
+
+
+
+
+
+ {item.name}
+
+
+
+ ))}
+
+
+
+
+ );
+};