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/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index c0ccd5164b..598cfcdfec 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -11,6 +11,9 @@
//#define AREA_LAYER 1
+#define DISPLACEMENT_PLATE_RENDER_LAYER 1
+#define DISPLACEMENT_PLATE_RENDER_TARGET "*DISPLACEMENT_PLATE_RENDER_TARGET"
+
#define UNDER_TURF_LAYER 1.99
#define TURF_LAYER 2
diff --git a/code/__DEFINES/sounds.dm b/code/__DEFINES/sounds.dm
index 541d95d281..6c5d2879f3 100644
--- a/code/__DEFINES/sounds.dm
+++ b/code/__DEFINES/sounds.dm
@@ -1,6 +1,6 @@
#define FALLOFF_SOUNDS 1
-#define FREE_CHAN_END 1016
+#define FREE_CHAN_END 1014
#define INITIAL_SOUNDSCAPE_COOLDOWN 20
#define EAR_DEAF_MUTE 1
@@ -21,6 +21,7 @@
#define ITEM_EQUIP_VOLUME 50
//Reserved channels
+#define SOUND_CHANNEL_TEST 1015
#define SOUND_CHANNEL_NOTIFY 1016
#define SOUND_CHANNEL_VOX 1017
#define SOUND_CHANNEL_MUSIC 1018
diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 24e39ff16c..73b494e989 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -920,8 +920,9 @@ world
// From /datum/preferences/proc/copy_appearance_to
body.age = original.age
body.gender = original.gender
- body.ethnicity = original.ethnicity
+ body.skin_color = original.skin_color
body.body_type = original.body_type
+ body.body_size = original.body_size
body.r_eyes = original.r_eyes
body.g_eyes = original.g_eyes
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index 9aa1bdc3ea..38d84c2922 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -1,11 +1,14 @@
#define isdeaf(A) (ismob(A) && ((A?:sdisabilities & DISABILITY_DEAF) || A?:ear_deaf))
#define xeno_hivenumber(A) (isxeno(A) ? A?:hivenumber : FALSE)
-/proc/random_ethnicity()
- return pick(GLOB.ethnicities_list)
+/proc/random_skin_color()
+ return pick(GLOB.skin_color_list)
/proc/random_body_type()
- return pick(GLOB.body_types_list)
+ return pick(GLOB.body_type_list)
+
+/proc/random_body_size()
+ return pick(GLOB.body_size_list)
/proc/random_hair_style(gender, species = "Human")
var/h_style = "Crewcut"
diff --git a/code/__HELPERS/sanitize_values.dm b/code/__HELPERS/sanitize_values.dm
index 85e102a3c1..decec60d36 100644
--- a/code/__HELPERS/sanitize_values.dm
+++ b/code/__HELPERS/sanitize_values.dm
@@ -45,18 +45,24 @@
else return default
return default
-/proc/sanitize_ethnicity(ethnicity, default = "Western")
- if (ethnicity in GLOB.ethnicities_list)
- return ethnicity
+/proc/sanitize_skin_color(skin_color, default = "Pale 2")
+ if(skin_color in GLOB.skin_color_list)
+ return skin_color
return default
-/proc/sanitize_body_type(body_type, default = "Mesomorphic (Average)")
- if (body_type in GLOB.body_types_list)
+/proc/sanitize_body_type(body_type, default = "Lean")
+ if(body_type in GLOB.body_type_list)
return body_type
return default
+/proc/sanitize_body_size(body_size, default = "Average")
+ if(body_size in GLOB.body_size_list)
+ return body_size
+
+ return default
+
/proc/sanitize_hexcolor(color, default="#000000")
if(!istext(color)) return default
var/len = length(color)
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/_globalvars/global_lists.dm b/code/_globalvars/global_lists.dm
index 3ba92a7c4d..c2cfb8263f 100644
--- a/code/_globalvars/global_lists.dm
+++ b/code/_globalvars/global_lists.dm
@@ -198,10 +198,11 @@ GLOBAL_LIST_INIT(custom_event_info_list, setup_custom_event_info())
GLOBAL_LIST_INIT(poster_designs, subtypesof(/datum/poster))
//Preferences stuff
- // Ethnicities
-GLOBAL_REFERENCE_LIST_INDEXED(ethnicities_list, /datum/ethnicity, name) // Stores /datum/ethnicity indexed by name
- // Body Types
-GLOBAL_REFERENCE_LIST_INDEXED(body_types_list, /datum/body_type, name) // Stores /datum/body_type indexed by name
+ // Skin colors
+GLOBAL_REFERENCE_LIST_INDEXED(skin_color_list, /datum/skin_color, name) // Stores /datum/skin_color indexed by name
+ // Body
+GLOBAL_REFERENCE_LIST_INDEXED(body_type_list, /datum/body_type, name) // Stores /datum/body_type indexed by name
+GLOBAL_REFERENCE_LIST_INDEXED(body_size_list, /datum/body_size, name) // Stores /datum/body_size indexed by name
//Hairstyles
GLOBAL_REFERENCE_LIST_INDEXED(hair_styles_list, /datum/sprite_accessory/hair, name) //stores /datum/sprite_accessory/hair indexed by name
GLOBAL_REFERENCE_LIST_INDEXED(facial_hair_styles_list, /datum/sprite_accessory/facial_hair, name) //stores /datum/sprite_accessory/facial_hair indexed by name
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/_onclick/hud/rendering/plane_master.dm b/code/_onclick/hud/rendering/plane_master.dm
index 91c0e24fae..c337ee198e 100644
--- a/code/_onclick/hud/rendering/plane_master.dm
+++ b/code/_onclick/hud/rendering/plane_master.dm
@@ -175,3 +175,10 @@
plane = ESCAPE_MENU_PLANE
appearance_flags = PLANE_MASTER|NO_CLIENT_COLOR
render_relay_plane = RENDER_PLANE_MASTER
+
+/atom/movable/screen/plane_master/displacement
+ name = "displacement plane"
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ plane = DISPLACEMENT_PLATE_RENDER_LAYER
+ render_target = DISPLACEMENT_PLATE_RENDER_TARGET
+ render_relay_plane = null
diff --git a/code/_onclick/hud/rendering/render_plate.dm b/code/_onclick/hud/rendering/render_plate.dm
index 18236c6ee7..cb579eb4ff 100644
--- a/code/_onclick/hud/rendering/render_plate.dm
+++ b/code/_onclick/hud/rendering/render_plate.dm
@@ -39,6 +39,10 @@
plane = RENDER_PLANE_GAME
render_relay_plane = RENDER_PLANE_MASTER
+/atom/movable/screen/plane_master/rendering_plate/game_world/Initialize(mapload, datum/hud/hud_owner)
+ . = ..()
+ add_filter("displacer", 1, displacement_map_filter(render_source = DISPLACEMENT_PLATE_RENDER_TARGET, size = 10))
+
///render plate for OOC stuff like ghosts, hud-screen effects, etc
/atom/movable/screen/plane_master/rendering_plate/non_game
name = "non-game rendering plate"
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/ticker.dm b/code/controllers/subsystem/ticker.dm
index e06e1ac458..ef796bc439 100644
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -109,14 +109,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 +153,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 +331,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/autocells/explosion.dm b/code/datums/autocells/explosion.dm
index 0b54a53a57..2efc7a8a3e 100644
--- a/code/datums/autocells/explosion.dm
+++ b/code/datums/autocells/explosion.dm
@@ -282,6 +282,9 @@ as having entered the turf.
if(QDELETED(E))
return
+ if(power >= 150) //shockwave for anything over 150 power
+ new /obj/effect/shockwave(epicenter, power/60)
+
E.power = power
E.power_falloff = falloff
E.falloff_shape = falloff_shape
diff --git a/code/datums/datacore.dm b/code/datums/datacore.dm
index 7c50c34338..26ef0e5d0a 100644
--- a/code/datums/datacore.dm
+++ b/code/datums/datacore.dm
@@ -355,31 +355,38 @@ GLOBAL_DATUM_INIT(data_core, /datum/datacore, new)
var/icon/icobase = H.species.icobase
var/icon/temp
- var/datum/ethnicity/ET = GLOB.ethnicities_list[H.ethnicity]
- var/datum/body_type/B = GLOB.body_types_list[H.body_type]
+ var/datum/skin_color/set_skin_color = GLOB.skin_color_list[H.skin_color]
+ var/datum/body_type/set_body_type = GLOB.body_type_list[H.body_type]
+ var/datum/body_size/set_body_size = GLOB.body_size_list[H.body_size]
- var/e_icon
- var/b_icon
+ var/skin_color_icon
+ var/body_type_icon
+ var/body_size_icon
- if (!ET)
- e_icon = "western"
+ if(!set_skin_color)
+ skin_color_icon = "pale2"
else
- e_icon = ET.icon_name
+ skin_color_icon = set_skin_color.icon_name
- if (!B)
- b_icon = "mesomorphic"
+ if(!set_body_type)
+ body_type_icon = "lean"
else
- b_icon = B.icon_name
+ body_type_icon = set_body_type.icon_name
- preview_icon = new /icon(icobase, get_limb_icon_name(H.species, b_icon, H.gender, "torso", e_icon))
- temp = new /icon(icobase, get_limb_icon_name(H.species, b_icon, H.gender, "groin", e_icon))
+ if(!set_body_size)
+ body_size_icon = "avg"
+ else
+ body_size_icon = set_body_size.icon_name
+
+ preview_icon = new /icon(icobase, get_limb_icon_name(H.species, body_size_icon, body_type_icon, H.gender, "torso", skin_color_icon))
+ temp = new /icon(icobase, get_limb_icon_name(H.species, body_size_icon, body_type_icon, H.gender, "groin", skin_color_icon))
preview_icon.Blend(temp, ICON_OVERLAY)
- temp = new /icon(icobase, get_limb_icon_name(H.species, b_icon, H.gender, "head", e_icon))
+ temp = new /icon(icobase, get_limb_icon_name(H.species, body_size_icon, body_type_icon, H.gender, "head", skin_color_icon))
preview_icon.Blend(temp, ICON_OVERLAY)
for(var/obj/limb/E in H.limbs)
if(E.status & LIMB_DESTROYED) continue
- temp = new /icon(icobase, get_limb_icon_name(H.species, b_icon, H.gender, E.name, e_icon))
+ temp = new /icon(icobase, get_limb_icon_name(H.species, body_size_icon, body_type_icon, H.gender, E.name, skin_color_icon))
if(E.status & LIMB_ROBOT)
temp.MapColors(rgb(77,77,77), rgb(150,150,150), rgb(28,28,28), rgb(0,0,0))
preview_icon.Blend(temp, ICON_OVERLAY)
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/keybinding/mob.dm b/code/datums/keybinding/mob.dm
index b7b83249b3..b2bf989a7a 100644
--- a/code/datums/keybinding/mob.dm
+++ b/code/datums/keybinding/mob.dm
@@ -2,16 +2,6 @@
category = CATEGORY_HUMAN
weight = WEIGHT_MOB
-/datum/keybinding/mob/down(client/user)
- . = ..()
- if(isobserver(user.mob))
- return TRUE
-
-/datum/keybinding/mob/up(client/user)
- . = ..()
- if(isobserver(user.mob))
- return TRUE
-
/datum/keybinding/mob/stop_pulling
hotkey_keys = list("H", "Delete")
classic_keys = list("Delete")
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/datums/skills/uscm.dm b/code/datums/skills/uscm.dm
index 7e6658d9c1..c2d05bd786 100644
--- a/code/datums/skills/uscm.dm
+++ b/code/datums/skills/uscm.dm
@@ -8,6 +8,10 @@ United States Colonial Marines
name = "Private"
//same as default
+/datum/skills/pfc/recon
+ name = "Recon Private"
+ skills = list(SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED)
+
/datum/skills/combat_medic
name = "Combat Medic"
skills = list(
@@ -16,6 +20,15 @@ United States Colonial Marines
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
+/datum/skills/combat_medic/recon
+ name = "Recon Medic"
+ skills = list(
+ SKILL_MEDICAL = SKILL_MEDICAL_MEDIC,
+ SKILL_SURGERY = SKILL_SURGERY_NOVICE,
+ SKILL_JTAC = SKILL_JTAC_BEGINNER,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ )
+
/datum/skills/combat_engineer
name = "Combat Engineer"
skills = list(
@@ -32,6 +45,16 @@ United States Colonial Marines
SKILL_JTAC = SKILL_JTAC_BEGINNER,
)
+/datum/skills/smartgunner/recon
+ name = "Recon Smartgunner"
+ skills = list(
+ SKILL_SPEC_WEAPONS = SKILL_SPEC_SMARTGUN,
+ SKILL_JTAC = SKILL_JTAC_BEGINNER,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ )
+
+
+
/datum/skills/specialist
name = "Squad Weapons Specialist"
skills = list(
@@ -51,6 +74,15 @@ United States Colonial Marines
SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
)
+/datum/skills/tl/recon
+ name = "Recon Fireteam Leader"
+ skills = list(
+ SKILL_JTAC = SKILL_JTAC_TRAINED,
+ SKILL_LEADERSHIP = SKILL_LEAD_TRAINED,
+ SKILL_ENDURANCE = SKILL_ENDURANCE_TRAINED,
+ )
+
+
/datum/skills/SL
name = "Squad Leader"
skills = list(
diff --git a/code/datums/soundOutput.dm b/code/datums/soundOutput.dm
index 1f4512b28d..85548d6c90 100644
--- a/code/datums/soundOutput.dm
+++ b/code/datums/soundOutput.dm
@@ -13,7 +13,7 @@
. = ..()
/datum/soundOutput/proc/process_sound(datum/sound_template/T)
- var/sound/S = sound(T.file, T.wait, T.repeat)
+ var/sound/S = sound(T.file, T.repeat, T.wait)
S.volume = owner.volume_preferences[T.volume_cat] * T.volume
if(T.channel == 0)
S.channel = get_free_channel()
@@ -21,6 +21,8 @@
S.channel = T.channel
S.frequency = T.frequency
S.falloff = T.falloff
+ S.offset = T.offset
+ S.pitch = T.pitch
S.status = T.status
S.echo = T.echo
if(T.x && T.y && T.z)
diff --git a/code/defines/procs/records.dm b/code/defines/procs/records.dm
index a1e2ade2b7..b4612f10f4 100644
--- a/code/defines/procs/records.dm
+++ b/code/defines/procs/records.dm
@@ -6,7 +6,7 @@
G.fields["real_rank"] = "Unassigned"
G.fields["sex"] = "Male"
G.fields["age"] = "Unknown"
- G.fields["ethnicity"] = "Unknown"
+ G.fields["skin_color"] = "Unknown"
G.fields["p_stat"] = "Active"
G.fields["m_stat"] = "Stable"
G.fields["species"] = "Human"
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/effects/temporary_visuals.dm b/code/game/objects/effects/temporary_visuals.dm
index 4dc07b76f3..1f3800fa01 100644
--- a/code/game/objects/effects/temporary_visuals.dm
+++ b/code/game/objects/effects/temporary_visuals.dm
@@ -96,3 +96,25 @@
splatter_type = "csplatter"
color = BLOOD_COLOR_SYNTHETIC
+//------------------------------------------
+//Shockwaves
+//------------------------------------------
+
+/obj/effect/shockwave
+ icon = 'icons/effects/light_overlays/shockwave.dmi'
+ icon_state = "shockwave"
+ plane = DISPLACEMENT_PLATE_RENDER_LAYER
+ pixel_x = -496
+ pixel_y = -496
+
+/obj/effect/shockwave/Initialize(mapload, radius, speed, easing_type = LINEAR_EASING, y_offset, x_offset)
+ . = ..()
+ if(!speed)
+ speed = 1
+ if(y_offset)
+ pixel_y += y_offset
+ if(x_offset)
+ pixel_x += x_offset
+ QDEL_IN(src, 0.5 * radius * speed)
+ transform = matrix().Scale(32 / 1024, 32 / 1024)
+ animate(src, time = 0.5 * radius * speed, transform=matrix().Scale((32 / 1024) * radius * 1.5, (32 / 1024) * radius * 1.5), easing = easing_type)
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/pamphlets.dm b/code/game/objects/items/pamphlets.dm
index 682215be67..c1544d6d73 100644
--- a/code/game/objects/items/pamphlets.dm
+++ b/code/game/objects/items/pamphlets.dm
@@ -200,7 +200,7 @@
to_chat(user, SPAN_WARNING("You know this already!"))
return FALSE
- if(user.job != JOB_SQUAD_MARINE)
+ if(!(user.job in JOB_SQUAD_ROLES_LIST))
to_chat(user, SPAN_WARNING("Only squad riflemen can use this."))
return FALSE
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/crates_lockers/largecrate_supplies.dm b/code/game/objects/structures/crates_lockers/largecrate_supplies.dm
index b9486e4f09..9a45b4256d 100644
--- a/code/game/objects/structures/crates_lockers/largecrate_supplies.dm
+++ b/code/game/objects/structures/crates_lockers/largecrate_supplies.dm
@@ -161,6 +161,9 @@
desc = "An ammunition case containing 20 M41A MK1 magazines."
supplies = list(/obj/item/ammo_magazine/rifle/m41aMK1 = 20)
+/obj/structure/largecrate/supply/ammo/m41amk1/forecon
+ supplies = list(/obj/item/ammo_magazine/rifle/m41aMK1/recon = 20)
+
/obj/structure/largecrate/supply/ammo/m41a_box
name = "\improper M41A ammunition box case (x4)"
desc = "An ammunition case containing four M41A 600 round boxes of ammunition."
diff --git a/code/game/objects/structures/gun_rack.dm b/code/game/objects/structures/gun_rack.dm
index 0a3e668444..099d8d4c20 100644
--- a/code/game/objects/structures/gun_rack.dm
+++ b/code/game/objects/structures/gun_rack.dm
@@ -84,3 +84,10 @@
max_stored = 2
initial_stored = 0
allowed_type = /obj/item/ammo_magazine/hardpoint/boyars_dualcannon
+
+/obj/structure/gun_rack/m41/recon
+ icon_state = "m41arecon"
+ populate_type = /obj/item/weapon/gun/rifle/m41aMK1/forecon
+
+/obj/structure/gun_rack/m41/recon/unloaded
+ populate_type = /obj/item/weapon/gun/rifle/m41aMK1/forecon/unloaded
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/game/objects/structures/vulture_spotter.dm b/code/game/objects/structures/vulture_spotter.dm
index ab23a80867..4111bdfcdd 100644
--- a/code/game/objects/structures/vulture_spotter.dm
+++ b/code/game/objects/structures/vulture_spotter.dm
@@ -183,7 +183,10 @@
unscope()
scope_attached = FALSE
desc = initial(desc) + " Though, it doesn't seem to have one attached yet."
- new /obj/item/device/vulture_spotter_scope(get_turf(src), bound_rifle)
+ if(skillless)
+ new /obj/item/device/vulture_spotter_scope/skillless(get_turf(src), bound_rifle)
+ else
+ new /obj/item/device/vulture_spotter_scope(get_turf(src), bound_rifle)
/// Handler for user folding up the tripod, picking it up
/obj/structure/vulture_spotter_tripod/proc/fold_up(mob/user)
diff --git a/code/game/sound.dm b/code/game/sound.dm
index 02be8dbace..f2574975a7 100644
--- a/code/game/sound.dm
+++ b/code/game/sound.dm
@@ -1,22 +1,44 @@
/datum/sound_template //Basically a sound datum, but only serves as a way to carry info to soundOutput
- var/file //The sound itself
- var/file_muffled // Muffled variant for those that are deaf
- var/wait = 0
- var/repeat = 0
+ //copied sound datum vars
+ ///This is the file that will be played when the sound is sent to a player.
+ var/file
+ ///Set to TRUE to repeat the sound indefinitely once it begins playing, 2 to repeat it forwards and backwards.
+ var/repeat = FALSE
+ ///Set to TRUE to wait for other sounds in this channel to finish before playing this one.
+ var/wait = FALSE
+ ///For sound effects, set to 1 through 1024 to choose a specific sound channel. For values of 0 or less, any available channel will be chosen.
var/channel = 0
+ ///Set to a percentage from 0 to 100 of the sound's full volume.
var/volume = 100
- var/status = 0 //Sound status flags
+ ///Any value from -100 to 100 will play this sound at a multiple of its normal frequency. A value of 0 or 1 will play the sound at its normal frequency.
var/frequency = 1
+ ///Can be used to set a starting time, in seconds, for a sound.
+ var/offset = 0
+ ///Can be used to shift the pitch of a sound up or down. This works similarly to frequency except that it doesn't impact playback speed.
+ var/pitch = 1
+ ///Alter the way the sound is heard by affecting several different on/off values which combine as bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
+ var/status = NONE
+ ///Within the falloff distance a 3D sound stays at the constant loudest volume possible. Outside of this distance it attenuates at a rate determined by the falloff.
var/falloff = 1
+ ///Changes the environmental reverb for all 3D sounds until another environment is specified. The default value (-1) specifies no change in environment. A numeric value from 0 to 25 specifies a set of reverb presets for the environment.
+ var/environment = -1
+ ///If set to an 18-element list, this value customizes reverbration settings for this sound only.
+ var/list/echo
+
+ //custom vars
+ ///The category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes) and VOLUME_ADM (Admin sounds and some other stuff)
var/volume_cat = VOLUME_SFX
+ ///Maximum theoretical range (in tiles) of the sound, by default is equal to the volume.
var/range = 0
- var/list/echo
- var/x //Map coordinates, not sound coordinates
+ //Map coordinates, not sound coordinates, generated by the procs
+ var/x
var/y
var/z
- var/y_s_offset // Vertical sound offset
- var/x_s_offset // Horizontal sound offset
+ ///Horizontal sound position offset.
+ var/x_s_offset
+ ///Vertical sound position offset.
+ var/y_s_offset
/proc/get_free_channel()
var/static/cur_chan = 1
@@ -24,58 +46,73 @@
if(cur_chan > FREE_CHAN_END)
cur_chan = 1
-//Proc used to play a sound effect. Avoid using this proc for non-IC sounds, as there are others
-//source: self-explanatory.
-//soundin: the .ogg to use.
-//vol: the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
-//freq: the frequency of the sound. Setting it to 1 will assign it a random frequency
-//sound_range: the maximum theoretical range (in tiles) of the sound, by default is equal to the volume.
-//vol_cat: the category of this sound, used in client volume. There are 3 volume categories: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes) and VOLUME_ADM (Admin sounds and some other stuff)
-//channel: use this only when you want to force the sound to play on a specific channel
-//status: the regular 4 sound flags
-//falloff: max range till sound volume starts dropping as distance increases
-
-/proc/playsound(atom/source, soundin, vol = 100, vary = FALSE, sound_range, vol_cat = VOLUME_SFX, channel = 0, status , falloff = 1, echo, y_s_offset,x_s_offset)
+/**
+ * Play a spatialized sound effect to everyone within hearing distance.
+ *
+ * Arguments:
+ * * source - origin atom for the sound
+ * * soundin - sound datum ( sound() ), sound file ('mysound.ogg'), or string to get a SFX ("male_warcry")
+ * * vol - the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
+ * * vary - the frequency of the sound. Setting it to 1 will assign it a random frequency
+ * * sound_range - maximum theoretical range (in tiles) of the sound, by default is equal to the volume.
+ * * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
+ * * channel - use this only when you want to force the sound to play on a specific channel
+ * * status - combined bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
+ * * falloff - max range till sound volume starts dropping as distance increases
+ * * echo - customizes reverbration settings for this sound
+ * * y_s_offset - vertical sound position offset
+ * * x_s_offset - horizontal sound position offset
+ *
+ * Returns selected channel on success, FALSE on failure
+ */
+/proc/playsound(atom/source, sound/soundin, vol = 100, vary = FALSE, sound_range, vol_cat = VOLUME_SFX, channel = 0, status, falloff = 1, list/echo, y_s_offset, x_s_offset)
if(isarea(source))
error("[source] is an area and is trying to make the sound: [soundin]")
return FALSE
- var/datum/sound_template/S = new()
-
- var/sound/SD = soundin
- if(istype(SD))
- S.file = SD.file
- S.wait = SD.wait
- S.repeat = SD.repeat
- else
- S.file = get_sfx(soundin)
- S.channel = channel ? channel : get_free_channel()
- S.status = status
- S.falloff = falloff
- S.volume = vol
- S.volume_cat = vol_cat
- S.echo = echo
- S.y_s_offset = y_s_offset
- S.x_s_offset = x_s_offset
- if(vary != FALSE)
- if(vary > 1)
- S.frequency = vary
- else
- S.frequency = GET_RANDOM_FREQ // Same frequency for everybody
-
- if(!sound_range)
- sound_range = round(0.25*vol) //if no specific range, the max range is equal to a quarter of the volume.
- S.range = sound_range
var/turf/turf_source = get_turf(source)
- if(!turf_source || !turf_source.z)
+ if(!turf_source?.z)
return FALSE
- S.x = turf_source.x
- S.y = turf_source.y
- S.z = turf_source.z
+
+ var/datum/sound_template/template = new()
+
+ if(istype(soundin))
+ template.file = soundin.file
+ template.repeat = soundin.repeat
+ template.wait = soundin.wait
+ //template.channel = soundin.channel
+ //template.volume = soundin.volume
+ template.frequency = soundin.frequency
+ template.offset = soundin.offset
+ template.pitch = soundin.pitch
+ //template.status = soundin.status
+ //template.falloff = soundin.falloff
+ //template.environment = soundin.environment
+ //template.echo = soundin.echo
+ else
+ template.file = get_sfx(soundin)
+
+ template.channel = channel || get_free_channel()
+ template.volume = vol
+ if(vary > 1)
+ template.frequency = vary
+ else if(vary)
+ template.frequency = GET_RANDOM_FREQ // Same frequency for everybody
+ template.status = status
+ template.falloff = falloff
+ template.echo = echo
+
+ template.volume_cat = vol_cat
+ template.range = sound_range || floor(0.25 * vol) //if no specific range, the max range is equal to a quarter of the volume.
+ template.x = turf_source.x
+ template.y = turf_source.y
+ template.z = turf_source.z
+ template.x_s_offset = x_s_offset
+ template.y_s_offset = y_s_offset
if(!SSinterior)
- SSsound.queue(S)
- return S.channel
+ SSsound.queue(template)
+ return template.channel
var/list/datum/interior/extra_interiors = list()
// If we're in an interior, range the chunk, then adjust to do so from outside instead
@@ -85,66 +122,127 @@
extra_interiors |= VI
if(VI.exterior)
var/turf/new_turf_source = get_turf(VI.exterior)
- S.x = new_turf_source.x
- S.y = new_turf_source.y
- S.z = new_turf_source.z
- else sound_range = 0
+ template.x = new_turf_source.x
+ template.y = new_turf_source.y
+ template.z = new_turf_source.z
// Range for 'nearby interiors' aswell
for(var/datum/interior/VI in SSinterior.interiors)
if(VI?.ready && VI.exterior?.z == turf_source.z && get_dist(VI.exterior, turf_source) <= sound_range)
extra_interiors |= VI
- SSsound.queue(S, null, extra_interiors)
- return S.channel
+ SSsound.queue(template, null, extra_interiors)
+ return template.channel
-//This is the replacement for playsound_local. Use this for sending sounds directly to a client
-/proc/playsound_client(client/C, soundin, atom/origin, vol = 100, random_freq, vol_cat = VOLUME_SFX, channel = 0, status, list/echo, y_s_offset, x_s_offset)
+/**
+ * Play a sound effect directly to a client.
+ *
+ * Arguments:
+ * * C - client to hear the sound
+ * * soundin - sound datum ( sound() ), sound file ('mysound.ogg'), or string to get a SFX ("male_warcry")
+ * * origin - origin atom for the sound
+ * * vol - the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
+ * * random_freq - assign the sound a random frequency
+ * * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
+ * * channel - use this only when you want to force the sound to play on a specific channel
+ * * status - combined bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
+ * * echo - customizes reverbration settings for this sound
+ * * y_s_offset - vertical sound position offset
+ * * x_s_offset - horizontal sound position offset
+ *
+ * Returns FALSE on failure
+ */
+/proc/playsound_client(client/C, sound/soundin, atom/origin, vol = 100, random_freq, vol_cat = VOLUME_SFX, channel = 0, status, list/echo, y_s_offset, x_s_offset)
if(!istype(C) || !C.soundOutput) return FALSE
- var/datum/sound_template/S = new()
- if(origin)
- var/turf/T = get_turf(origin)
- if(T)
- S.x = T.x
- S.y = T.y
- S.z = T.z
- var/sound/SD = soundin
- if(istype(SD))
- S.file = SD.file
- S.wait = SD.wait
- S.repeat = SD.repeat
+
+ var/datum/sound_template/template = new()
+
+ if(istype(soundin))
+ template.file = soundin.file
+ template.repeat = soundin.repeat
+ template.wait = soundin.wait
+ //template.channel = soundin.channel
+ //template.volume = soundin.volume
+ template.frequency = soundin.frequency
+ template.offset = soundin.offset
+ template.pitch = soundin.pitch
+ //template.status = soundin.status
+ //template.falloff = soundin.falloff
+ //template.environment = soundin.environment
+ //template.echo = soundin.echo
else
- S.file = get_sfx(soundin)
+ template.file = get_sfx(soundin)
+ template.channel = channel
+ template.volume = vol
if(random_freq)
- S.frequency = GET_RANDOM_FREQ
- S.volume = vol
- S.volume_cat = vol_cat
- S.channel = channel
- S.status = status
- S.echo = echo
- S.y_s_offset = y_s_offset
- S.x_s_offset = x_s_offset
- SSsound.queue(S, list(C))
-
-/// Plays sound to all mobs that are map-level contents of an area
-/proc/playsound_area(area/A, soundin, vol = 100, channel = 0, status, vol_cat = VOLUME_SFX, list/echo, y_s_offset, x_s_offset)
+ template.frequency = GET_RANDOM_FREQ
+ template.status = status
+ template.echo = echo
+
+ template.volume_cat = vol_cat
+ var/turf/turf_origin = get_turf(origin)
+ if(turf_origin)
+ template.x = turf_origin.x
+ template.y = turf_origin.y
+ template.z = turf_origin.z
+ template.x_s_offset = x_s_offset
+ template.y_s_offset = y_s_offset
+
+ SSsound.queue(template, list(C))
+
+/**
+ * Play a sound effect to all mobs that are map-level contents of an area.
+ *
+ * Arguments:
+ * * A - affected area to hear the sound
+ * * soundin - sound datum ( sound() ), sound file ('mysound.ogg'), or string to get a SFX ("male_warcry")
+ * * vol - the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
+ * * channel - use this only when you want to force the sound to play on a specific channel
+ * * status - combined bit flags: SOUND_MUTE, SOUND_PAUSED, SOUND_STREAM, SOUND_UPDATE
+ * * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
+ * * echo - customizes reverbration settings for this sound
+ *
+ * Returns FALSE on failure
+ */
+/proc/playsound_area(area/A, sound/soundin, vol = 100, channel = 0, status, vol_cat = VOLUME_SFX, list/echo, y_s_offset, x_s_offset)
if(!isarea(A))
return FALSE
- var/datum/sound_template/S = new()
- S.file = soundin
- S.volume = vol
- S.channel = channel
- S.status = status
- S.volume_cat = vol_cat
+
+ var/datum/sound_template/template = new()
+
+ if(istype(soundin))
+ template.file = soundin.file
+ template.repeat = soundin.repeat
+ template.wait = soundin.wait
+ //template.channel = soundin.channel
+ //template.volume = soundin.volume
+ template.frequency = soundin.frequency
+ template.offset = soundin.offset
+ template.pitch = soundin.pitch
+ //template.status = soundin.status
+ //template.falloff = soundin.falloff
+ //template.environment = soundin.environment
+ //template.echo = soundin.echo
+ else
+ template.file = get_sfx(soundin)
+
+ template.channel = channel
+ template.volume = vol
+ template.status = status
+
+ template.volume_cat = vol_cat
+ template.x_s_offset = x_s_offset
+ template.y_s_offset = y_s_offset
var/list/hearers = list()
- for(var/mob/living/M in A.contents)
- if(!M || !M.client || !M.client.soundOutput)
+ for(var/mob/living/living_mob in A.contents)
+ if(!living_mob || !living_mob.client || !living_mob.client.soundOutput)
continue
- hearers += M.client
- SSsound.queue(S, hearers)
+ hearers += living_mob.client
+
+ SSsound.queue(template, hearers)
/client/proc/playtitlemusic()
if(!SSticker?.login_music)
@@ -153,21 +251,53 @@
playsound_client(src, SSticker.login_music, null, 70, 0, VOLUME_LOBBY, SOUND_CHANNEL_LOBBY, SOUND_STREAM)
-/// Play sound for all on-map clients on a given Z-level. Good for ambient sounds.
-/proc/playsound_z(z, soundin, volume = 100, vol_cat = VOLUME_SFX, echo, y_s_offset, x_s_offset)
- var/datum/sound_template/S = new()
- S.file = soundin
- S.volume = volume
- S.channel = SOUND_CHANNEL_Z
- S.volume_cat = vol_cat
- S.echo = echo
- S.y_s_offset = y_s_offset
- S.x_s_offset = x_s_offset
+/**
+ * Play a sound effect for all on-map clients on a given Z-level.
+ *
+ * Arguments:
+ * * z - list of affected [/datum/space_level] to hear the sound
+ * * soundin - sound datum ( sound() ), sound file ('mysound.ogg'), or string to get a SFX ("male_warcry")
+ * * volume - the initial volume of the sound, 0 is no sound at all, 75 is loud queen screech.
+ * * vol_cat - the category of this sound for client volume purposes: VOLUME_SFX (Sound effects), VOLUME_AMB (Ambience and Soundscapes), VOLUME_ADM (Admin sounds)
+ * * echo - customizes reverbration settings for this sound
+ * * y_s_offset - vertical sound position offset
+ * * x_s_offset - horizontal sound position offset
+ *
+ * Returns selected channel on success, FALSE on failure
+ */
+/proc/playsound_z(list/z, sound/soundin, volume = 100, vol_cat = VOLUME_SFX, echo, y_s_offset, x_s_offset)
+ var/datum/sound_template/template = new()
+
+ if(istype(soundin))
+ template.file = soundin.file
+ template.repeat = soundin.repeat
+ template.wait = soundin.wait
+ //template.channel = soundin.channel
+ //template.volume = soundin.volume
+ template.frequency = soundin.frequency
+ template.offset = soundin.offset
+ template.pitch = soundin.pitch
+ //template.status = soundin.status
+ //template.falloff = soundin.falloff
+ //template.environment = soundin.environment
+ //template.echo = soundin.echo
+ else
+ template.file = get_sfx(soundin)
+
+ template.channel = SOUND_CHANNEL_Z
+ template.volume = volume
+ template.echo = echo
+
+ template.volume_cat = vol_cat
+ template.x_s_offset = x_s_offset
+ template.y_s_offset = y_s_offset
+
var/list/hearers = list()
- for(var/mob/M in GLOB.player_list)
- if((M.z in z) && M.client.soundOutput)
- hearers += M.client
- SSsound.queue(S, hearers)
+ for(var/mob/mob in GLOB.player_list)
+ if((mob.z in z) && mob.client.soundOutput)
+ hearers += mob.client
+
+ SSsound.queue(template, hearers)
// The pick() proc has a built-in chance that can be added to any option by adding ,X; to the end of an option, where X is the % chance it will play.
/proc/get_sfx(S)
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 34cc3ee3c8..6e47391fc1 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -330,6 +330,8 @@ 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)
add_verb(src, /client/proc/toggle_vehicle_blockers)
@@ -364,6 +366,8 @@ 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,
/client/proc/toggle_vehicle_blockers,
diff --git a/code/modules/admin/game_master/extra_buttons/rename_platoon.dm b/code/modules/admin/game_master/extra_buttons/rename_platoon.dm
index 9d805ab9d1..575319ad73 100644
--- a/code/modules/admin/game_master/extra_buttons/rename_platoon.dm
+++ b/code/modules/admin/game_master/extra_buttons/rename_platoon.dm
@@ -28,8 +28,8 @@ GLOBAL_VAR_INIT(main_platoon_initial_name, GLOB.main_platoon_name)
if(!new_name || !istext(new_name))
return
- if(length(new_name) > 10)
- to_chat(src, SPAN_NOTICE("The platoon name should be 10 characters or less."))
+ if(length(new_name) > 16)
+ to_chat(src, SPAN_NOTICE("The platoon name should be 16 characters or less."))
return
var/old_name = GLOB.main_platoon_name
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/admin/game_master/sound_panel.dm b/code/modules/admin/game_master/sound_panel.dm
new file mode 100644
index 0000000000..8aff8b0f23
--- /dev/null
+++ b/code/modules/admin/game_master/sound_panel.dm
@@ -0,0 +1,272 @@
+/client/proc/open_sound_panel()
+ set name = "Sound Panel"
+ set category = "Admin.Panels"
+
+ if(!check_rights(R_SOUNDS))
+ return
+
+ new /datum/sound_panel(usr)
+
+/datum/sound_panel
+ var/static/list/sound_list
+ var/static/list/category_list
+ var/static/list/category_lookup
+ var/static/list/zlevel_list
+ var/static/list/zlevel_lookup
+ var/static/list/group_list
+ var/client/holder
+ var/sound_path = ""
+ var/sound_category
+ var/sound_volume = 50
+ var/sound_pitch = 1
+ var/sound_duration = 1
+ var/mob/target_player
+ var/turf/target_loc
+ var/loc_click_intercept = FALSE
+ var/loc_click_play = FALSE
+ var/target_zlevel
+ var/target_group
+
+/datum/sound_panel/New(user)
+ if(isnull(sound_list)) //first run, init statics
+ sound_list = get_sounds()
+
+ category_list = list("Sound FX", "Ambience", "Admin")
+ category_lookup = list("Sound FX" = VOLUME_SFX, "Ambience" = VOLUME_AMB, "Admin" = VOLUME_ADM)
+
+ zlevel_list = list()
+ zlevel_lookup = list()
+ for(var/datum/space_level/level as anything in SSmapping.z_list)
+ zlevel_list += level.name
+ zlevel_lookup[level.name] = level.z_value
+
+ group_list = list("Global", "Humans", "Xenos", "Ghosts")
+
+ sound_category = category_list[1]
+ target_zlevel = zlevel_list[1]
+ target_group = group_list[1]
+
+ if(isclient(user))
+ holder = user
+ else
+ var/mob/mob = user
+ holder = mob.client
+
+ holder.click_intercept = src
+ tgui_interact(holder.mob)
+
+/datum/sound_panel/proc/get_sounds()
+ var/static/list/extensions = list("mid", "midi", "mod", "it", "s3m", "xm", "oxm", "wav", "ogg", "mp3", "raw", "wma", "aiff")
+ var/static/regex/ext_rgx = new("\\.(?:[jointext(extensions, "|")])$", "i")
+
+ var/list/dirs = list("sound/")
+ var/list/file_paths = list()
+ for(var/i = 1, i <= length(dirs), i++)
+ var/path = dirs[i]
+
+ var/list/filenames = flist(path)
+ for(var/filename as anything in filenames)
+ if(findtext(filename, "/", -1)) //found directory, add to search
+ dirs += "[path][filename]"
+ continue
+ if(!findtext(filename, ext_rgx)) //extension check
+ continue
+ file_paths += "[path][filename]"
+
+ return file_paths
+
+/datum/sound_panel/ui_static_data(mob/user)
+ var/list/data = list()
+
+ data["sound_list"] = sound_list
+ data["category_list"] = category_list
+ data["zlevel_list"] = zlevel_list
+ data["group_list"] = group_list
+
+ return data
+
+/datum/sound_panel/ui_data(mob/user)
+ var/list/data = list()
+
+ data["sound_path"] = sound_path
+ data["sound_category"] = sound_category
+ data["sound_volume"] = sound_volume
+ data["sound_pitch"] = sound_pitch
+ data["sound_duration"] = sound_duration
+ data["target_player_desc"] = target_player?.name
+ data["target_loc_desc"] = target_loc ? "[target_loc.name]: [target_loc.x],[target_loc.y],[target_loc.z]" : null
+ data["loc_click_intercept"] = loc_click_intercept
+ data["loc_click_play"] = loc_click_play
+ data["target_zlevel"] = target_zlevel
+ data["target_group"] = target_group
+
+ return data
+
+/datum/sound_panel/ui_close(mob/user)
+ holder = null
+ target_loc = null
+ target_player = null
+ loc_click_intercept = FALSE
+ qdel(src)
+
+/datum/sound_panel/ui_state(mob/user)
+ return GLOB.admin_state
+
+/datum/sound_panel/ui_status(mob/user, datum/ui_state/state)
+ return UI_INTERACTIVE
+
+/datum/sound_panel/tgui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "SoundPanel", "Sound Panel")
+ ui.set_autoupdate(FALSE)
+ ui.open()
+
+/datum/sound_panel/proc/InterceptClickOn(mob/user, params, atom/object)
+ if(loc_click_intercept)
+ var/turf/chosen_loc = get_turf(object)
+ if(QDELETED(chosen_loc))
+ return
+
+ target_loc = chosen_loc
+ SStgui.update_uis(src)
+
+ if(loc_click_play)
+ if(!sound_path)
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ playsound(target_loc, sound_datum, sound_volume, vol_cat = category_lookup[sound_category])
+
+ return TRUE
+
+/datum/sound_panel/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state)
+ . = ..()
+ if(.)
+ return
+
+ if(!check_rights(R_SOUNDS))
+ return
+
+ switch(action)
+ if("set_sound_path")
+ var/sound = params["sound_path"]
+ if(!(sound in sound_list))
+ return
+ sound_path = sound
+ return TRUE
+ if("set_sound_category")
+ var/category = params["sound_category"]
+ if(isnull(category_lookup[category]))
+ return
+ sound_category = category
+ return TRUE
+ if("set_sound_volume")
+ sound_volume = clamp(params["sound_volume"], 0, 100)
+ return TRUE
+ if("set_sound_pitch")
+ sound_pitch = clamp(params["sound_pitch"], 0.5, 2)
+ return TRUE
+ if("set_sound_duration")
+ sound_duration = clamp(params["sound_duration"], 0.5, 2)
+ return TRUE
+ if("set_target_zlevel")
+ var/target_z = params["target_zlevel"]
+ if(isnull(zlevel_lookup[target_z]))
+ return
+ target_zlevel = target_z
+ return TRUE
+ if("set_target_group")
+ var/group = params["target_group"]
+ if(!(group in group_list))
+ return
+ target_group = group
+ return TRUE
+ if("play_preview")
+ if(!sound_path)
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ playsound_client(holder, sound_datum, vol = sound_volume, vol_cat = category_lookup[sound_category], channel = SOUND_CHANNEL_TEST)
+ return TRUE
+ if("stop_preview")
+ var/sound/sound_datum = sound()
+ sound_datum.channel = SOUND_CHANNEL_TEST
+ sound_datum.status = SOUND_MUTE|SOUND_UPDATE
+ sound_to(holder, sound_datum)
+ return TRUE
+ if("select_client")
+ var/mob/chosen_player = tgui_input_list(holder.mob, "Who should hear the sound?", "Player Select", GLOB.player_list)
+ if(QDELETED(chosen_player))
+ return
+
+ target_player = chosen_player
+ return TRUE
+ if("play_client")
+ if(!sound_path)
+ return
+ if(QDELETED(target_player))
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ playsound_client(target_player.client, sound_datum, vol = sound_volume, vol_cat = category_lookup[sound_category])
+ return TRUE
+ if("toggle_loc_click_intercept")
+ loc_click_intercept = !loc_click_intercept
+ return TRUE
+ if("toggle_loc_click_play")
+ loc_click_play = !loc_click_play
+ return TRUE
+ if("play_local")
+ if(!sound_path)
+ return
+ if(QDELETED(target_loc))
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ playsound(target_loc, sound_datum, sound_volume, vol_cat = category_lookup[sound_category])
+ return TRUE
+ if("play_zlevel")
+ if(!sound_path)
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ playsound_z(list(zlevel_lookup[target_zlevel]), sound_datum, sound_volume, vol_cat = category_lookup[sound_category])
+ return TRUE
+ if("play_group")
+ if(!sound_path)
+ return
+
+ var/sound/sound_datum = sound(sound_path)
+ sound_datum.frequency = 1 / sound_duration
+ sound_datum.pitch = sound_pitch * sound_duration
+
+ var/list/targets = list()
+ switch(target_group)
+ if("Global")
+ targets = GLOB.mob_list
+ if("Humans")
+ targets = GLOB.human_mob_list + GLOB.dead_mob_list
+ if("Xenos")
+ targets = GLOB.xeno_mob_list + GLOB.dead_mob_list
+ if("Ghosts")
+ targets = GLOB.observer_list + GLOB.dead_mob_list
+
+ for(var/mob/target as anything in targets)
+ playsound_client(target.client, sound_datum, vol = sound_volume, vol_cat = category_lookup[sound_category])
+ return TRUE
diff --git a/code/modules/almayer/machinery.dm b/code/modules/almayer/machinery.dm
index 9491475ace..400e1f0f6f 100644
--- a/code/modules/almayer/machinery.dm
+++ b/code/modules/almayer/machinery.dm
@@ -353,3 +353,14 @@
icon_state = "ob_console"
dir = WEST
flags_atom = ON_BORDER|CONDUCT|FPRINT
+
+/obj/structure/machinery/prop/almayer/CICmap/arc
+ name = "map terminal"
+ desc = "A terminal that displays a map of the current operation location."
+ icon = 'icons/obj/vehicles/interiors/arc.dmi'
+ icon_state = "arcmap"
+ density = FALSE
+
+/obj/structure/machinery/computer/overwatch/almayer/arc
+ icon = 'icons/obj/vehicles/interiors/arc.dmi'
+ icon_state = "arcconsole"
diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm
index 262ae11219..f6aa0a9018 100644
--- a/code/modules/client/preferences.dm
+++ b/code/modules/client/preferences.dm
@@ -152,8 +152,10 @@ var/const/MAX_SAVE_SLOTS = 10
var/g_eyes = 0 //Eye color
var/b_eyes = 0 //Eye color
var/species = "Human" //Species datum to use.
- var/ethnicity = "Western" // Ethnicity
- var/body_type = "Mesomorphic (Average)" // Body Type
+ var/ethnicity = "Western" //Legacy, kept to update save files
+ var/skin_color = "Pale 2" // Skin color
+ var/body_size = "Average" // Body Size
+ var/body_type = "Lean" // Body Type
var/language = "None" //Secondary language
var/list/gear //Custom/fluff item loadout.
var/preferred_squad = "None"
@@ -333,8 +335,9 @@ var/const/MAX_SAVE_SLOTS = 10
dat += "®"
dat += "Age: [age]
"
dat += "Gender: [gender == MALE ? "Male" : "Female"]
"
- dat += "Ethnicity: [ethnicity]
"
- dat += "Body Type: [body_type]
"
+ dat += "Skin Color: [skin_color]
"
+ dat += "Body Size: [body_size]
"
+ dat += "Body Muscularity: [body_type]
"
dat += "Traits: Character Traits"
dat += "
"
@@ -1183,10 +1186,12 @@ var/const/MAX_SAVE_SLOTS = 10
real_name = character_origin.generate_human_name(gender)
if ("age")
age = rand(AGE_MIN, AGE_MAX)
- if ("ethnicity")
- ethnicity = random_ethnicity()
+ if ("skin_color")
+ skin_color = random_skin_color()
if ("body_type")
body_type = random_body_type()
+ if ("body_size")
+ body_size = random_body_size()
if ("hair")
r_hair = rand(0,255)
g_hair = rand(0,255)
@@ -1545,18 +1550,24 @@ var/const/MAX_SAVE_SLOTS = 10
if(new_h_gradient_style)
grad_style = new_h_gradient_style
- if ("ethnicity")
- var/new_ethnicity = tgui_input_list(user, "Choose your character's ethnicity:", "Character Preferences", GLOB.ethnicities_list)
+ if ("skin_color")
+ var/new_skin_color = tgui_input_list(user, "Choose your character's skin color:", "Character Preferences", GLOB.skin_color_list)
- if (new_ethnicity)
- ethnicity = new_ethnicity
+ if (new_skin_color)
+ skin_color = new_skin_color
if ("body_type")
- var/new_body_type = tgui_input_list(user, "Choose your character's body type:", "Character Preferences", GLOB.body_types_list)
+ var/new_body_type = tgui_input_list(user, "Choose your character's body type:", "Character Preferences", GLOB.body_type_list)
if (new_body_type)
body_type = new_body_type
+ if ("body_size")
+ var/new_body_size = tgui_input_list(user, "Choose your character's body size:", "Character Preferences", GLOB.body_size_list)
+
+ if (new_body_size)
+ body_size = new_body_size
+
if("facial")
var/new_facial = input(user, "Choose your character's facial-hair color:", "Character Preference", rgb(r_facial, g_facial, b_facial)) as color|null
if(new_facial)
@@ -2047,8 +2058,9 @@ var/const/MAX_SAVE_SLOTS = 10
character.age = age
character.gender = gender
- character.ethnicity = ethnicity
+ character.skin_color = skin_color
character.body_type = body_type
+ character.body_size = body_size
character.r_eyes = r_eyes
character.g_eyes = g_eyes
@@ -2120,15 +2132,16 @@ var/const/MAX_SAVE_SLOTS = 10
message_admins("[character] ([character.ckey]) has spawned with their gender as plural or neuter. Please notify coders.")
character.gender = MALE
-// Transfers the character's physical characteristics (age, gender, ethnicity, etc) to the mob
+// Transfers the character's physical characteristics (age, gender, skin color, etc) to the mob
/datum/preferences/proc/copy_appearance_to(mob/living/carbon/human/character, safety = 0)
if(!istype(character))
return
character.age = age
character.gender = gender
- character.ethnicity = ethnicity
+ character.skin_color = skin_color
character.body_type = body_type
+ character.body_size = body_size
character.r_eyes = r_eyes
character.g_eyes = g_eyes
diff --git a/code/modules/client/preferences_gear.dm b/code/modules/client/preferences_gear.dm
index d0ea7b626a..9b8b319731 100644
--- a/code/modules/client/preferences_gear.dm
+++ b/code/modules/client/preferences_gear.dm
@@ -573,10 +573,67 @@ var/global/list/gear_datums_by_name = list()
display_name = "Die, 20 sides"
path = /obj/item/toy/dice/d20
-/datum/gear/toy/walkman
+/datum/gear/cassettes
+ category = "Cassettes"
+
+/datum/gear/cassettes/walkman
display_name = "Walkman"
path = /obj/item/device/walkman
+/datum/gear/cassettes/pop1
+ display_name = "Blue Cassette"
+ path = /obj/item/device/cassette_tape/pop1
+ cost = 1
+
+/datum/gear/cassettes/pop2
+ display_name = "Rainbow Cassette"
+ path = /obj/item/device/cassette_tape/pop2
+ cost = 1
+
+/datum/gear/cassettes/pop3
+ display_name = "Orange Cassette"
+ path = /obj/item/device/cassette_tape/pop3
+ cost = 1
+
+/datum/gear/cassettes/pop4
+ display_name = "Blue Cassette"
+ path = /obj/item/device/cassette_tape/pop4
+ cost = 1
+
+/datum/gear/cassettes/heavymetal
+ display_name = "Red-Black Cassette"
+ path = /obj/item/device/cassette_tape/heavymetal
+ cost = 1
+
+/datum/gear/cassettes/hairmetal
+ display_name = "Red Striped Cassette"
+ path = /obj/item/device/cassette_tape/hairmetal
+ cost = 1
+
+/datum/gear/cassettes/indie
+ display_name = "Rising Sun Cassette"
+ path = /obj/item/device/cassette_tape/indie
+ cost = 1
+
+/datum/gear/cassettes/hiphop
+ display_name = "Blue Stripe Cassette"
+ path = /obj/item/device/cassette_tape/hiphop
+ cost = 1
+
+/datum/gear/cassettes/nam
+ display_name = "Green Cassette"
+ path = /obj/item/device/cassette_tape/nam
+ cost = 1
+
+/datum/gear/cassettes/ocean
+ display_name = "Ocean Cassette"
+ path = /obj/item/device/cassette_tape/ocean
+ cost = 1
+
+/datum/gear/cassettes/pouch
+ display_name = "Cassette Pouch"
+ path = /obj/item/storage/pouch/cassette
+
/datum/gear/toy/crayon
display_name = "Crayon"
path = /obj/item/toy/crayon/rainbow
@@ -1016,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/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm
index 44af545e2e..1403b64bc4 100644
--- a/code/modules/client/preferences_savefile.dm
+++ b/code/modules/client/preferences_savefile.dm
@@ -1,5 +1,5 @@
#define SAVEFILE_VERSION_MIN 8
-#define SAVEFILE_VERSION_MAX 24
+#define SAVEFILE_VERSION_MAX 25
//handles converting savefiles to new formats
//MAKE SURE YOU KEEP THIS UP TO DATE!
@@ -110,6 +110,53 @@
S.cd = "/"
+ if(savefile_version < 25)
+ var/ethnicity
+ var/skin_color = "pale2"
+ S["ethnicity"] >> ethnicity
+ switch(ethnicity)
+ if("anglo")
+ skin_color = "pale2"
+ if("western")
+ skin_color = "tan2"
+ if("germanic")
+ skin_color = "pale2"
+ if("scandinavian")
+ skin_color = "pale3"
+ if("baltic")
+ skin_color = "pale3"
+ if("sinoorient")
+ skin_color = "pale1"
+ if("southorient")
+ skin_color = "tan1"
+ if("indian")
+ skin_color = "tan3"
+ if("sino")
+ skin_color = "tan1"
+ if("mesoamerican")
+ skin_color = "tan3"
+ if("northamerican")
+ skin_color = "tan3"
+ if("southamerican")
+ skin_color = "tan2"
+ if("circumpolar")
+ skin_color = "tan1"
+ if("northafrican")
+ skin_color = "tan3"
+ if("centralafrican")
+ skin_color = "dark1"
+ if("costalafrican")
+ skin_color = "dark3"
+ if("persian")
+ skin_color = "tan3"
+ if("levant")
+ skin_color = "tan3"
+ if("australasian")
+ skin_color = "dark2"
+ if("polynesian")
+ skin_color = "tan3"
+ S["skin_color"] << skin_color
+
savefile_version = SAVEFILE_VERSION_MAX
return 1
@@ -472,7 +519,9 @@
S["gender"] >> gender
S["age"] >> age
S["ethnicity"] >> ethnicity
+ S["skin_color"] >> skin_color
S["body_type"] >> body_type
+ S["body_size"] >> body_size
S["language"] >> language
S["spawnpoint"] >> spawnpoint
@@ -549,8 +598,9 @@
be_random_body = sanitize_integer(be_random_body, 0, 1, initial(be_random_body))
gender = sanitize_gender(gender)
age = sanitize_integer(age, AGE_MIN, AGE_MAX, initial(age))
- ethnicity = sanitize_ethnicity(ethnicity)
+ skin_color = sanitize_skin_color(skin_color)
body_type = sanitize_body_type(body_type)
+ body_size = sanitize_body_size(body_size)
r_hair = sanitize_integer(r_hair, 0, 255, initial(r_hair))
g_hair = sanitize_integer(g_hair, 0, 255, initial(g_hair))
b_hair = sanitize_integer(b_hair, 0, 255, initial(b_hair))
@@ -628,7 +678,9 @@
S["gender"] << gender
S["age"] << age
S["ethnicity"] << ethnicity
+ S["skin_color"] << skin_color
S["body_type"] << body_type
+ S["body_size"] << body_size
S["language"] << language
S["hair_red"] << r_hair
S["hair_green"] << g_hair
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/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/gear_presets/fun.dm b/code/modules/gear_presets/fun.dm
index 2e17dfe9ef..687fca47f7 100644
--- a/code/modules/gear_presets/fun.dm
+++ b/code/modules/gear_presets/fun.dm
@@ -299,7 +299,7 @@
new_human.change_real_name(new_human, "Ivan")
new_human.f_style = "Shaved"
new_human.h_style = "Shaved Head"
- new_human.ethnicity = "Scandinavian"
+ new_human.skin_color = "pale3"
new_human.r_hair = 165
new_human.g_hair = 42
new_human.b_hair = 42
@@ -362,7 +362,7 @@
new_human.b_facial = 51
new_human.h_style = "Mullet"
new_human.f_style = "Full English"
- new_human.ethnicity = "Anglo"
+ new_human.skin_color = "pale2"
new_human.r_eyes = 102 //Brown eyes.
new_human.g_eyes = 51
new_human.b_eyes = 0
diff --git a/code/modules/gear_presets/uscm.dm b/code/modules/gear_presets/uscm.dm
index bc014d60e1..5cc00b0f42 100644
--- a/code/modules/gear_presets/uscm.dm
+++ b/code/modules/gear_presets/uscm.dm
@@ -123,6 +123,7 @@
/datum/equipment_preset/uscm/pfc/forecon
name = "FORECON Squad Rifleman"
paygrade = "ME3"
+ skills = /datum/skills/pfc/recon
/datum/equipment_preset/uscm/pfc/forecon/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/standard
@@ -191,6 +192,7 @@
/datum/equipment_preset/uscm/sg/forecon
name = "FORECON Squad Smartgunner"
paygrade = "ME5"
+ skills = /datum/skills/smartgunner/recon
/datum/equipment_preset/uscm/sg/forecon/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/standard
@@ -465,6 +467,7 @@
name = "FORECON Squad Corpsman"
assignment = "Squad Corpsman"
paygrade = "ME5"
+ skills = /datum/skills/combat_medic/recon
/datum/equipment_preset/uscm/medic/forecon/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/medic/standard
@@ -524,6 +527,8 @@
name = "FORECON Assistant Squad Leader"
assignment = "Assistant Squad Leader"
paygrade = "ME6"
+ role_comm_title = "aSL"
+ skills = /datum/skills/tl/recon
/datum/equipment_preset/uscm/tl/forecon/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/standard
@@ -616,9 +621,10 @@
paygrade = "UE6"
/datum/equipment_preset/uscm/leader/forecon
- name = "FORECON Squad Sergeant"
- assignment = "Squad Sergeant"
+ name = "FORECON Squad Leader"
+ assignment = "Squad Leader"
paygrade = "ME8"
+ role_comm_title = "SL"
/datum/equipment_preset/uscm/leader/forecon/load_gear(mob/living/carbon/human/new_human)
var/back_item = /obj/item/storage/backpack/marine/satchel/standard
diff --git a/code/modules/gear_presets/yautja.dm b/code/modules/gear_presets/yautja.dm
index 27eac7215f..8ffd8664a9 100644
--- a/code/modules/gear_presets/yautja.dm
+++ b/code/modules/gear_presets/yautja.dm
@@ -12,13 +12,13 @@
/datum/equipment_preset/yautja/load_race(mob/living/carbon/human/new_human, client/mob_client)
new_human.set_species(SPECIES_YAUTJA)
- new_human.ethnicity = "tan"
+ new_human.skin_color = "tan"
new_human.body_type = "pred" //can be removed in future for body types
if(!mob_client)
mob_client = new_human.client
if(mob_client?.prefs)
new_human.h_style = mob_client.prefs.predator_h_style
- new_human.ethnicity = mob_client.prefs.predator_skin_color
+ new_human.skin_color = mob_client.prefs.predator_skin_color
/datum/equipment_preset/yautja/load_id(mob/living/carbon/human/new_human)
new_human.job = rank
diff --git a/code/modules/mob/living/carbon/human/exercise.dm b/code/modules/mob/living/carbon/human/exercise.dm
index 3a2976da91..7e40c63d88 100644
--- a/code/modules/mob/living/carbon/human/exercise.dm
+++ b/code/modules/mob/living/carbon/human/exercise.dm
@@ -101,7 +101,7 @@ Verbs related to getting fucking jacked, bro
/mob/living/carbon/human/proc/calculate_stamina_loss_per_pushup(on_knees = FALSE)
//humans have 100 stamina
//default loss per pushup = 5 stamina
- var/stamina_loss = 5
+ var/stamina_loss = 2
if(!skills || issynth(src))
return 0
switch(skills.get_skill_level(SKILL_ENDURANCE))
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index cd6abd2262..a3424a1815 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -25,8 +25,9 @@
var/g_eyes = 0
var/b_eyes = 0
- var/ethnicity = "Western" // Ethnicity
- var/body_type = "Mesomorphic (Average)" // Body Type
+ var/skin_color = "Pale 2" // Skin color
+ var/body_size = "Average" // Body Size
+ var/body_type = "Lean" // Body Buffness
//Skin color
var/r_skin = 0
diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm
index a98de60d3e..9a3b796764 100644
--- a/code/modules/mob/living/carbon/human/human_helpers.dm
+++ b/code/modules/mob/living/carbon/human/human_helpers.dm
@@ -9,72 +9,65 @@
g = "f"
return g
-/proc/get_limb_icon_name(datum/species/S, body_type, gender, limb_name, ethnicity)
- if(S.uses_ethnicity)
+/proc/get_limb_icon_name(datum/species/S, body_size, body_type, gender, limb_name, skin_color)
+ if(S.uses_skin_color)
+ if(S.special_body_types)
+ switch(limb_name)
+ if("torso")
+ return "[skin_color]_torso_[body_size]_[body_type]"
+ if("chest")
+ return "[skin_color]_torso_[body_size]_[body_type]"
+ if("head")
+ return "[skin_color]_[limb_name]"
+ if("groin")
+ return "[skin_color]_[limb_name]_[body_size]"
+
+ if(!S.special_body_types)
+ switch(limb_name)
+ if("torso")
+ return "[skin_color]_torso_[body_type]_[get_gender_name(gender)]"
+ if("chest")
+ return "[skin_color]_torso_[body_type]_[get_gender_name(gender)]"
+ if("head")
+ return "[skin_color]_[limb_name]_[get_gender_name(gender)]"
+ if("groin")
+ return "[skin_color]_[limb_name]_[body_type]_[get_gender_name(gender)]"
switch(limb_name)
- if ("torso")
- return "[ethnicity]_torso_[body_type]_[get_gender_name(gender)]"
-
- if ("chest")
- return "[ethnicity]_torso_[body_type]_[get_gender_name(gender)]"
-
- if ("head")
- return "[ethnicity]_[limb_name]_[get_gender_name(gender)]"
-
- if ("groin")
- return "[ethnicity]_[limb_name]_[get_gender_name(gender)]"
-
if("synthetic head")
return "head_[get_gender_name(gender)]"
- if ("r_arm")
- return "[ethnicity]_right_arm"
-
- if ("right arm")
- return "[ethnicity]_right_arm"
-
- if ("l_arm")
- return "[ethnicity]_left_arm"
-
- if ("left arm")
- return "[ethnicity]_left_arm"
-
- if ("r_leg")
- return "[ethnicity]_right_leg"
-
- if ("right leg")
- return "[ethnicity]_right_leg"
-
- if ("l_leg")
- return "[ethnicity]_left_leg"
-
- if ("left leg")
- return "[ethnicity]_left_leg"
-
- if ("r_hand")
- return "[ethnicity]_right_hand"
-
- if ("right hand")
- return "[ethnicity]_right_hand"
-
- if ("l_hand")
- return "[ethnicity]_left_hand"
-
- if ("left hand")
- return "[ethnicity]_left_hand"
-
- if ("r_foot")
- return "[ethnicity]_right_foot"
-
- if ("right foot")
- return "[ethnicity]_right_foot"
-
- if ("l_foot")
- return "[ethnicity]_left_foot"
-
- if ("left foot")
- return "[ethnicity]_left_foot"
-
+ if("r_arm")
+ return "[skin_color]_right_arm"
+ if("right arm")
+ return "[skin_color]_right_arm"
+ if("l_arm")
+ return "[skin_color]_left_arm"
+ if("left arm")
+ return "[skin_color]_left_arm"
+ if("r_leg")
+ return "[skin_color]_right_leg"
+ if("right leg")
+ return "[skin_color]_right_leg"
+ if("l_leg")
+ return "[skin_color]_left_leg"
+ if("left leg")
+ return "[skin_color]_left_leg"
+ if("r_hand")
+ return "[skin_color]_right_hand"
+ if("right hand")
+ return "[skin_color]_right_hand"
+ if("l_hand")
+ return "[skin_color]_left_hand"
+ if("left hand")
+ return "[skin_color]_left_hand"
+ if("r_foot")
+ return "[skin_color]_right_foot"
+ if("right foot")
+ return "[skin_color]_right_foot"
+ if("l_foot")
+ return "[skin_color]_left_foot"
+ if("left foot")
+ return "[skin_color]_left_foot"
else
message_admins("DEBUG: Something called get_limb_icon_name() incorrectly, they use the name [limb_name]")
return null
@@ -147,28 +140,37 @@
return null
/mob/living/carbon/human/proc/set_limb_icons()
- var/datum/ethnicity/E = GLOB.ethnicities_list[ethnicity]
- var/datum/body_type/B = GLOB.body_types_list[body_type]
+ var/datum/skin_color/set_skin_color = GLOB.skin_color_list[skin_color]
+ var/datum/body_size/set_body_size = GLOB.body_size_list[body_size]
+ var/datum/body_type/set_body_type = GLOB.body_type_list[body_type]
- var/e_icon
- var/b_icon
+ var/skin_color_icon
+ var/body_size_icon
+ var/body_type_icon
- if (!E)
- e_icon = "western"
+ if(!set_skin_color)
+ skin_color_icon = "pale2"
else
- e_icon = E.icon_name
+ skin_color_icon = set_skin_color.icon_name
- if (!B)
- b_icon = "mesomorphic"
+ if(!set_body_size)
+ body_size_icon = "avg"
else
- b_icon = B.icon_name
+ body_size_icon = set_body_size.icon_name
+
+
+ if(!set_body_type)
+ body_type_icon = "lean"
+ else
+ body_type_icon = set_body_type.icon_name
if(isspeciesyautja(src))
- e_icon = src.ethnicity
- b_icon = src.body_type
+ skin_color_icon = skin_color
+ body_size_icon = body_size
+ body_type_icon = body_type
- for(var/obj/limb/L in limbs)
- L.icon_name = get_limb_icon_name(species, b_icon, gender, L.display_name, e_icon)
+ for(var/obj/limb/L as anything in limbs)
+ L.icon_name = get_limb_icon_name(species, body_size_icon, body_type_icon, gender, L.display_name, skin_color_icon)
/mob/living/carbon/human/can_inject(mob/user, error_msg, target_zone)
if(species?.flags & IS_SYNTHETIC)
diff --git a/code/modules/mob/living/carbon/human/species/human.dm b/code/modules/mob/living/carbon/human/species/human.dm
index add78365a3..a50b6a9285 100644
--- a/code/modules/mob/living/carbon/human/species/human.dm
+++ b/code/modules/mob/living/carbon/human/species/human.dm
@@ -88,7 +88,8 @@
unarmed_type = /datum/unarmed_attack/punch
flags = HAS_SKIN_TONE|HAS_LIPS|HAS_UNDERWEAR|HAS_HARDCRIT
mob_flags = KNOWS_TECHNOLOGY
- uses_ethnicity = TRUE
+ uses_skin_color = TRUE
+ special_body_types = TRUE
/datum/species/human/handle_on_fire(humanoidmob)
. = ..()
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index d2177cf49a..da14b0f88f 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -14,7 +14,8 @@
var/icobase_source // if we want to use sourcing system
var/deform_source
var/eyes = "eyes_s" // Icon for eyes.
- var/uses_ethnicity = FALSE //Set to TRUE to load proper ethnicities and what have you
+ var/uses_skin_color = FALSE //Set to TRUE to load proper skin_colors and what have you
+ var/special_body_types = FALSE
var/primitive // Lesser form, if any (ie. monkey for humans)
var/tail // Name of tail image in species effects icon file.
diff --git a/code/modules/mob/living/carbon/human/species/synthetic.dm b/code/modules/mob/living/carbon/human/species/synthetic.dm
index d521ed2a63..b418c0acb0 100644
--- a/code/modules/mob/living/carbon/human/species/synthetic.dm
+++ b/code/modules/mob/living/carbon/human/species/synthetic.dm
@@ -2,7 +2,8 @@
group = SPECIES_SYNTHETIC
name = SYNTH_GEN_THREE
name_plural = "synthetics"
- uses_ethnicity = TRUE //Uses ethnic presets
+ uses_skin_color = TRUE //Uses skin color presets
+ special_body_types = TRUE
unarmed_type = /datum/unarmed_attack/punch/synthetic
pain_type = /datum/pain/synthetic
@@ -58,7 +59,8 @@
/datum/species/synthetic/gen_one
name = SYNTH_GEN_ONE
- uses_ethnicity = FALSE
+ uses_skin_color = FALSE
+ special_body_types = FALSE
mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES)
hair_color = "#000000"
@@ -67,12 +69,14 @@
/datum/species/synthetic/gen_two
name = SYNTH_GEN_TWO
- uses_ethnicity = FALSE //2nd gen uses generic human look
+ uses_skin_color = FALSE //2nd gen uses generic human look
+ special_body_types = FALSE
/datum/species/synthetic/colonial
name = SYNTH_COLONY
name_plural = "Colonial Synthetics"
- uses_ethnicity = TRUE
+ uses_skin_color = TRUE
+ special_body_types = TRUE
brute_mod = 0.8
burn_mod = 0.8
mob_inherent_traits = list(TRAIT_SUPER_STRONG)
@@ -84,11 +88,12 @@
/datum/species/synthetic/colonial/colonial_gen_two
name = SYNTH_COLONY_GEN_TWO
- uses_ethnicity = FALSE //2nd gen uses generic human look
+ uses_skin_color = FALSE //2nd gen uses generic human look
/datum/species/synthetic/colonial/colonial_gen_one
name = SYNTH_COLONY_GEN_ONE
- uses_ethnicity = FALSE
+ uses_skin_color = FALSE
+ special_body_types = FALSE
mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES)
//sets colonial_gen_one synth's hair to black
hair_color = "#000000"
@@ -100,7 +105,8 @@
/datum/species/synthetic/colonial/combat
name = SYNTH_COMBAT
name_plural = "Combat Synthetics"
- uses_ethnicity = FALSE
+ uses_skin_color = FALSE
+ special_body_types = FALSE
mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES)
brute_mod = 0.5
@@ -121,7 +127,7 @@
/datum/species/synthetic/infiltrator
name = SYNTH_INFILTRATOR
name_plural = "Infiltrator Synthetics"
- uses_ethnicity = TRUE
+ uses_skin_color = TRUE
mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INFILTRATOR_SYNTH)
bloodsplatter_type = /obj/effect/temp_visual/dir_setting/bloodsplatter/human
diff --git a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
index 292c302f93..b0dd35c74b 100644
--- a/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/working_joe/_species.dm
@@ -2,7 +2,7 @@
name = SYNTH_WORKING_JOE
name_plural = "Working Joes"
death_message = "violently gargles fluid and seizes up, the glow in their eyes dimming..."
- uses_ethnicity = FALSE
+ uses_skin_color = FALSE
burn_mod = 0.65 // made for hazardous environments, withstanding temperatures up to 1210 degrees
mob_inherent_traits = list(TRAIT_SUPER_STRONG, TRAIT_INTENT_EYES, TRAIT_EMOTE_CD_EXEMPT, TRAIT_CANNOT_EAT, TRAIT_UNSTRIPPABLE)
diff --git a/code/modules/mob/living/carbon/human/species/yautja/_species.dm b/code/modules/mob/living/carbon/human/species/yautja/_species.dm
index f8ecb3e059..ec82a58dff 100644
--- a/code/modules/mob/living/carbon/human/species/yautja/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/yautja/_species.dm
@@ -6,7 +6,7 @@
burn_mod = 0.65
reagent_tag = IS_YAUTJA
mob_flags = KNOWS_TECHNOLOGY
- uses_ethnicity = TRUE
+ uses_skin_color = TRUE
flags = IS_WHITELISTED|HAS_SKIN_COLOR|NO_CLONE_LOSS|NO_POISON|NO_NEURO|SPECIAL_BONEBREAK|NO_SHRAPNEL|HAS_HARDCRIT
mob_inherent_traits = list(
TRAIT_YAUTJA_TECH,
diff --git a/code/modules/mob/living/carbon/xenomorph/Abilities.dm b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
index 0a6e0ff2ca..2b254fdba9 100644
--- a/code/modules/mob/living/carbon/xenomorph/Abilities.dm
+++ b/code/modules/mob/living/carbon/xenomorph/Abilities.dm
@@ -141,7 +141,7 @@
playsound(xeno.loc, pick(xeno.screech_sound_effect_list), 75, 0, status = 0)
xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] emits an ear-splitting guttural roar!"))
- xeno.create_shriekwave() //Adds the visual effect. Wom wom wom
+ xeno.create_shriekwave(14) //Adds the visual effect. Wom wom wom, 14 shriekwaves
for(var/mob/mob in view())
if(mob && mob.client)
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/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
index 3c475743c0..73d5ace965 100644
--- a/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
+++ b/code/modules/mob/living/carbon/xenomorph/abilities/predalien/predalien_powers.dm
@@ -15,7 +15,7 @@
playsound(xeno.loc, pick(predalien_roar), 75, 0, status = 0)
xeno.visible_message(SPAN_XENOHIGHDANGER("[xeno] emits a guttural roar!"))
- xeno.create_shriekwave(color = "#FF0000")
+ xeno.create_shriekwave(7)
for(var/mob/living/carbon/carbon in view(7, xeno))
if(ishuman(carbon))
diff --git a/code/modules/mob/living/carbon/xenomorph/update_icons.dm b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
index 880f23f2d8..0fbe71192a 100644
--- a/code/modules/mob/living/carbon/xenomorph/update_icons.dm
+++ b/code/modules/mob/living/carbon/xenomorph/update_icons.dm
@@ -190,31 +190,46 @@
overlays_standing[X_LEGCUFF_LAYER] = image("icon" = 'icons/mob/xenos/effects.dmi', "icon_state" = "legcuff", "layer" =-X_LEGCUFF_LAYER)
apply_overlay(X_LEGCUFF_LAYER)
-/mob/living/carbon/xenomorph/proc/create_shriekwave(color = null)
- var/image/screech_image
-
- var/offset_x = 0
- var/offset_y = 0
- if(mob_size <= MOB_SIZE_XENO)
- offset_x = -7
- offset_y = -10
-
- if (color)
- screech_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state" = "shriek_waves_greyscale") // For Praetorian screech
- screech_image.color = color
- else
- screech_image = image("icon"='icons/mob/xenos/overlay_effects64x64.dmi', "icon_state" = "shriek_waves") //Ehh, suit layer's not being used.
-
- screech_image.pixel_x = offset_x
- screech_image.pixel_y = offset_y
-
- screech_image.appearance_flags |= RESET_COLOR
-
- remove_suit_layer()
-
- overlays_standing[X_SUIT_LAYER] = screech_image
- apply_overlay(X_SUIT_LAYER)
- addtimer(CALLBACK(src, PROC_REF(remove_overlay), X_SUIT_LAYER), 30)
+/mob/living/carbon/xenomorph/proc/create_shriekwave(shriekwaves_left)
+ var/offset_y = 8
+ if(mob_size == MOB_SIZE_XENO)
+ offset_y = 24
+ if(mob_size == MOB_SIZE_IMMOBILE)
+ offset_y = 28
+
+ //the shockwave center is updated eachtime shockwave is called and offset relative to the mob_size.
+ //due to the speed of the shockwaves, it isn't required to be tied to the exact mob movements
+ var/epicenter = loc //center of the shockwave, set at the center of the tile that the mob is currently standing on
+ var/easing = QUAD_EASING | EASE_OUT
+ var/stage1_radius = rand(11, 12)
+ var/stage2_radius = rand(9, 11)
+ var/stage3_radius = rand(8, 10)
+ var/stage4_radius = 7.5
+
+ //shockwaves are iterated, counting down once per shriekwave, with the total amount being determined on the respective xeno ability tile
+ if(shriekwaves_left > 12)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage1_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 2)
+ return
+ if(shriekwaves_left > 8)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage2_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left > 4)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage3_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left > 1)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, stage4_radius, 0.5, easing, offset_y)
+ addtimer(CALLBACK(src, PROC_REF(create_shriekwave), shriekwaves_left), 3)
+ return
+ if(shriekwaves_left == 1)
+ shriekwaves_left--
+ new /obj/effect/shockwave(epicenter, 10.5, 0.6, easing, offset_y)
/mob/living/carbon/xenomorph/proc/create_stomp()
remove_suit_layer()
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/mob/new_player/body.dm b/code/modules/mob/new_player/body.dm
new file mode 100644
index 0000000000..9f2b3e8182
--- /dev/null
+++ b/code/modules/mob/new_player/body.dm
@@ -0,0 +1,31 @@
+/datum/body_type
+ var/name
+ var/icon_name
+
+/datum/body_type/twig
+ name = "No Muscles"
+ icon_name = "twig"
+
+/datum/body_type/lean
+ name = "Lean"
+ icon_name = "lean"
+
+/datum/body_type/ripped
+ name = "Ripped"
+ icon_name = "buff"
+
+/datum/body_size
+ var/name
+ var/icon_name
+
+/datum/body_size/thin
+ name = "Thin"
+ icon_name = "sml"
+
+/datum/body_size/average
+ name = "Average"
+ icon_name = "avg"
+
+/datum/body_size/large
+ name = "Large"
+ icon_name = "lrg"
diff --git a/code/modules/mob/new_player/body_type.dm b/code/modules/mob/new_player/body_type.dm
deleted file mode 100644
index ad35dc2fee..0000000000
--- a/code/modules/mob/new_player/body_type.dm
+++ /dev/null
@@ -1,15 +0,0 @@
-/datum/body_type
- var/name
- var/icon_name
-
-/datum/body_type/ectomorphic
- name = "Ectomorphic (Underweight)"
- icon_name = "ecto"
-
-/datum/body_type/mesomorphic
- name = "Mesomorphic (Average)"
- icon_name = "meso"
-
-/datum/body_type/endomorphic
- name = "Endomorphic (Overweight)"
- icon_name = "endo"
diff --git a/code/modules/mob/new_player/ethnicity.dm b/code/modules/mob/new_player/ethnicity.dm
deleted file mode 100644
index 433a370f56..0000000000
--- a/code/modules/mob/new_player/ethnicity.dm
+++ /dev/null
@@ -1,114 +0,0 @@
-/datum/ethnicity
- var/name
- var/icon_name
- var/ethnic_category
-
-/datum/ethnicity/anglo
- name = "Anglo"
- icon_name = "anglo"
- ethnic_category = "European"
-
-/datum/ethnicity/western
- name = "Western"
- icon_name = "western"
- ethnic_category = "European"
-
-/datum/ethnicity/germanic
- name = "Germanic"
- icon_name = "germanic"
- ethnic_category = "European"
-
-/datum/ethnicity/scandinavian
- name = "Scandinavian"
- icon_name = "scandinavian"
- ethnic_category = "European"
-
-/datum/ethnicity/baltic
- name = "Baltic"
- icon_name = "baltic"
- ethnic_category = "European"
-
-/datum/ethnicity/sinoorient
- name = "Sino-Orient"
- icon_name = "sinoorient"
- ethnic_category = "Oriental"
-
-/datum/ethnicity/eastorient
- name = "East-Orient"
- icon_name = "eastorient"
- ethnic_category = "Oriental"
-
-/datum/ethnicity/southorient
- name = "South-Orient"
- icon_name = "southasian"
- ethnic_category = "Oriental"
-
-/datum/ethnicity/indian
- name = "Indian"
- icon_name = "indian"
- ethnic_category = "Oriental"
-
-/datum/ethnicity/sino
- name = "Eurasian"
- icon_name = "eurasian"
- ethnic_category = "Oriental"
-
-/datum/ethnicity/mesoamerican
- name = "Mesoamerican"
- icon_name = "mesoamerican"
- ethnic_category = "American"
-
-/datum/ethnicity/northamerican
- name = "North American"
- icon_name = "northamerican"
- ethnic_category = "American"
-
-/datum/ethnicity/southamerican
- name = "South American"
- icon_name = "southamerican"
- ethnic_category = "American"
-
-/datum/ethnicity/circumpolar
- name = "Circumpolar"
- icon_name = "circumpolar"
- ethnic_category = "American"
-
-/datum/ethnicity/northafrican
- name = "North African"
- icon_name = "northafrican"
- ethnic_category = "African"
-
-/datum/ethnicity/centralafrican
- name = "Central African"
- icon_name = "centralafrican"
- ethnic_category = "African"
-
-/datum/ethnicity/costalafrican
- name = "Coastal African"
- icon_name = "costalafrican"
- ethnic_category = "African"
-
-/datum/ethnicity/persian
- name = "Persian"
- icon_name = "persian"
- ethnic_category = "Middle Eastern"
-
-/datum/ethnicity/arabian
- name = "Arabian"
- icon_name = "arabian"
- ethnic_category = "Middle Eastern"
-
-/datum/ethnicity/levant
- name = "Levant"
- icon_name = "levant"
- ethnic_category = "Middle Eastern"
-
-/datum/ethnicity/australasian
- name = "Australasian"
- icon_name = "australasian"
- ethnic_category = "Oceania"
-
-/datum/ethnicity/polynesian
- name = "Polynesian"
- icon_name = "polynesian"
- ethnic_category = "Oceania"
diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm
index 5c2f73922c..b76f980480 100644
--- a/code/modules/mob/new_player/preferences_setup.dm
+++ b/code/modules/mob/new_player/preferences_setup.dm
@@ -6,8 +6,9 @@
else
gender = FEMALE
- ethnicity = random_ethnicity()
+ skin_color = random_skin_color()
body_type = random_body_type()
+ body_size = random_body_size()
h_style = random_hair_style(gender, species)
f_style = random_facial_hair_style(gender, species)
diff --git a/code/modules/mob/new_player/skin_color.dm b/code/modules/mob/new_player/skin_color.dm
new file mode 100644
index 0000000000..f3158613c3
--- /dev/null
+++ b/code/modules/mob/new_player/skin_color.dm
@@ -0,0 +1,47 @@
+/datum/skin_color
+ var/name
+ var/icon_name
+
+/datum/skin_color/cmplayer
+ name = "Extra Pale"
+ icon_name = "cmp1"
+
+/datum/skin_color/pale1
+ name = "Pale 1"
+ icon_name = "pale1"
+
+/datum/skin_color/pale2
+ name = "Pale 2"
+ icon_name = "pale2"
+
+/datum/skin_color/pale3
+ name = "Pale 3"
+ icon_name = "pale3"
+
+/datum/skin_color/tan1
+ name = "Tan 1"
+ icon_name = "tan1"
+
+/datum/skin_color/tan2
+ name = "Tan 2"
+ icon_name = "tan2"
+
+/datum/skin_color/tan3
+ name = "Tan 3"
+ icon_name = "tan3"
+
+/datum/skin_color/dark1
+ name = "Dark 1"
+ icon_name = "dark1"
+
+/datum/skin_color/dark2
+ name = "Dark 2"
+ icon_name = "dark2"
+
+/datum/skin_color/dark3
+ name = "Dark 3"
+ icon_name = "dark3"
+
+/datum/skin_color/melanated
+ name = "Melanated"
+ icon_name = "mel1"
diff --git a/code/modules/mob/new_player/sprite_accessories/undershirt.dm b/code/modules/mob/new_player/sprite_accessories/undershirt.dm
index 39f0e3ddd1..5919b75636 100644
--- a/code/modules/mob/new_player/sprite_accessories/undershirt.dm
+++ b/code/modules/mob/new_player/sprite_accessories/undershirt.dm
@@ -112,12 +112,6 @@ GLOBAL_LIST_INIT_TYPED(undershirt_f, /datum/sprite_accessory/undershirt, setup_u
gender = FEMALE
camo_conforming = TRUE
-/datum/sprite_accessory/undershirt/halter_top
- name = "Haltertop"
- icon_state = "halter"
- gender = FEMALE
- camo_conforming = TRUE
-
/datum/sprite_accessory/undershirt/strapless_bra
name = "Strapless Bra"
icon_state = "strapless"
diff --git a/code/modules/mob/new_player/sprite_accessories/underwear.dm b/code/modules/mob/new_player/sprite_accessories/underwear.dm
index 200f3f2f67..869179619e 100644
--- a/code/modules/mob/new_player/sprite_accessories/underwear.dm
+++ b/code/modules/mob/new_player/sprite_accessories/underwear.dm
@@ -58,29 +58,33 @@ GLOBAL_LIST_INIT_TYPED(underwear_f, /datum/sprite_accessory/underwear, setup_und
if("s")
name += " (Snow)"
-// Plural
+// Both
/datum/sprite_accessory/underwear/boxers
name = "Boxers"
icon_state = "boxers"
- gender = PLURAL
+ gender = NEUTER
camo_conforming = TRUE
-// Male
/datum/sprite_accessory/underwear/briefs
name = "Briefs"
icon_state = "briefs"
- gender = MALE
+ gender = NEUTER
camo_conforming = TRUE
-// Female
-/datum/sprite_accessory/underwear/panties
- name = "Panties"
- icon_state = "panties"
- gender = FEMALE
+/datum/sprite_accessory/underwear/lowriders
+ name = "Lowriders"
+ icon_state = "lowriders"
+ gender = NEUTER
camo_conforming = TRUE
-/datum/sprite_accessory/underwear/thong
- name = "Thong"
- icon_state = "thong"
- gender = FEMALE
+/datum/sprite_accessory/underwear/satin
+ name = "Satin"
+ icon_state = "satin"
+ gender = NEUTER
+ camo_conforming = TRUE
+
+/datum/sprite_accessory/underwear/tanga
+ name = "Tanga"
+ icon_state = "tanga"
+ gender = NEUTER
camo_conforming = TRUE
diff --git a/code/modules/organs/limb_objects.dm b/code/modules/organs/limb_objects.dm
index 734f303c7f..4bc3ae5cfa 100644
--- a/code/modules/organs/limb_objects.dm
+++ b/code/modules/organs/limb_objects.dm
@@ -22,27 +22,34 @@
icon = base
- var/datum/ethnicity/E = GLOB.ethnicities_list[H.ethnicity]
- var/datum/body_type/B = GLOB.body_types_list[H.body_type]
+ var/datum/skin_color/set_skin_color = GLOB.skin_color_list[H.skin_color]
+ var/datum/body_type/set_body_type = GLOB.body_type_list[H.body_type]
+ var/datum/body_size/set_body_size = GLOB.body_size_list[H.body_size]
- var/e_icon
- var/b_icon
+ var/skin_color_icon
+ var/body_type_icon
+ var/body_size_icon
- if (!E)
- e_icon = "western"
+ if(!set_skin_color)
+ skin_color_icon = "pale2"
else
- e_icon = E.icon_name
+ skin_color_icon = set_skin_color.icon_name
- if (!B)
- b_icon = "mesomorphic"
+ if(!set_body_type)
+ body_type_icon = "lean"
else
- b_icon = B.icon_name
+ body_type_icon = set_body_type.icon_name
+
+ if(!set_body_size)
+ body_size_icon = "avg"
+ else
+ body_size_icon = set_body_size.icon_name
if(isspeciesyautja(H))
- e_icon = H.ethnicity
- b_icon = H.body_type
+ skin_color_icon = H.skin_color
+ body_type_icon = H.body_type
- icon_state = "[get_limb_icon_name(H.species, b_icon, H.gender, name, e_icon)]"
+ icon_state = "[get_limb_icon_name(H.species, body_size_icon, body_type_icon, H.gender, name, skin_color_icon)]"
setDir(SOUTH)
apply_transform(turn(transform, rand(70,130)))
diff --git a/code/modules/organs/limbs.dm b/code/modules/organs/limbs.dm
index de7adcf2ae..99a104ea55 100644
--- a/code/modules/organs/limbs.dm
+++ b/code/modules/organs/limbs.dm
@@ -71,11 +71,14 @@
var/status = LIMB_ORGANIC
var/processing = FALSE
- /// ethnicity of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
- var/ethnicity = "western"
+ /// skin color of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
+ var/skin_color = "Pale 2"
- /// body type of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
- var/body_type = "mesomorphic"
+ /// body size of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
+ var/body_size = "Average"
+
+ /// body muscularity of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
+ var/body_type = "Lean"
/// species of the owner, used for limb appearance, set in [/obj/limb/proc/update_limb()]
var/datum/species/species
@@ -686,22 +689,29 @@ This function completely restores a damaged organ to perfect condition.
/obj/limb/proc/update_limb()
SHOULD_CALL_PARENT(TRUE)
- var/datum/ethnicity/owner_ethnicity = GLOB.ethnicities_list[owner?.ethnicity]
+ var/datum/skin_color/owner_skin_color = GLOB.skin_color_list[owner?.skin_color]
- if(owner_ethnicity)
- ethnicity = owner_ethnicity.icon_name
+ if(owner_skin_color)
+ skin_color = owner_skin_color.icon_name
else
- ethnicity = "western"
+ skin_color = "pale2"
- var/datum/body_type/owner_body_type = GLOB.body_types_list[owner?.body_type]
+ var/datum/body_type/owner_body_type = GLOB.body_type_list[owner?.body_type]
if(owner_body_type)
body_type = owner_body_type.icon_name
else
- body_type = "mesomorphic"
+ body_type = "lean"
+
+ var/datum/body_type/owner_body_size = GLOB.body_size_list[owner?.body_size]
+
+ if(owner_body_size)
+ body_size = owner_body_size.icon_name
+ else
+ body_size = "avg"
if(isspeciesyautja(owner))
- ethnicity = owner.ethnicity
+ skin_color = owner.skin_color
body_type = owner.body_type
species = owner?.species ? owner.species : GLOB.all_species[SPECIES_HUMAN]
@@ -731,7 +741,7 @@ This function completely restores a damaged organ to perfect condition.
return
limb.icon = species.icobase
- limb.icon_state = "[get_limb_icon_name(species, body_type, limb_gender, icon_name, ethnicity)]"
+ limb.icon_state = "[get_limb_icon_name(species, body_size, body_type, limb_gender, icon_name, skin_color)]"
. += limb
@@ -741,7 +751,7 @@ This function completely restores a damaged organ to perfect condition.
/obj/limb/proc/get_limb_icon_key()
SHOULD_CALL_PARENT(TRUE)
- return "[species.name]-[body_type]-[limb_gender]-[icon_name]-[ethnicity]-[status]"
+ return "[species.name]-[body_size]-[body_type]-[limb_gender]-[icon_name]-[skin_color]-[status]"
// new damage icon system
// returns just the brute/burn damage code
diff --git a/code/modules/projectiles/ammo_boxes/ammo_boxes.dm b/code/modules/projectiles/ammo_boxes/ammo_boxes.dm
index df8a7d7bdd..91c11b242a 100644
--- a/code/modules/projectiles/ammo_boxes/ammo_boxes.dm
+++ b/code/modules/projectiles/ammo_boxes/ammo_boxes.dm
@@ -79,6 +79,7 @@
var/overlay_gun_type = "_m41" //used for text overlay
var/overlay_content = "_reg"
var/magazine_type = /obj/item/ammo_magazine/rifle
+ var/list/allowed_magazines = list()
var/num_of_magazines = 10
var/handfuls = FALSE
var/icon_state_deployed = null
diff --git a/code/modules/projectiles/ammo_boxes/box_structures.dm b/code/modules/projectiles/ammo_boxes/box_structures.dm
index 77020baab5..a7cbb43f68 100644
--- a/code/modules/projectiles/ammo_boxes/box_structures.dm
+++ b/code/modules/projectiles/ammo_boxes/box_structures.dm
@@ -131,7 +131,7 @@
to_chat(user, SPAN_DANGER("It's on fire and might explode!"))
return
if(!item_box.handfuls)
- if(istypestrict(W,item_box.magazine_type))
+ if(istypestrict(W,item_box.magazine_type) || is_type_in_list(W, item_box.allowed_magazines))
if(istype(W, /obj/item/storage/box/m94))
var/obj/item/storage/box/m94/flare_pack = W
if(flare_pack.contents.len < flare_pack.max_storage_space)
diff --git a/code/modules/projectiles/ammo_boxes/magazine_boxes.dm b/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
index 09d98809a7..320693db36 100644
--- a/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
+++ b/code/modules/projectiles/ammo_boxes/magazine_boxes.dm
@@ -60,17 +60,26 @@
/obj/item/ammo_box/magazine/heap/empty
empty = TRUE
-
+
/obj/item/ammo_box/magazine/mk1
name = "magazine box (M41A MK1 X 10)"
flags_equip_slot = SLOT_BACK
overlay_ammo_type = "_reg"
overlay_content = "_reg"
magazine_type = /obj/item/ammo_magazine/rifle/m41aMK1
+ allowed_magazines = list(/obj/item/ammo_magazine/rifle/m41aMK1/recon)
/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/gun_attachables.dm b/code/modules/projectiles/gun_attachables.dm
index 4f0daf2a2b..0b14f1db09 100644
--- a/code/modules/projectiles/gun_attachables.dm
+++ b/code/modules/projectiles/gun_attachables.dm
@@ -2822,6 +2822,10 @@ Defined in conflicts.dm of the #defines folder.
max_range = 10
attachment_firing_delay = 30
+/obj/item/attachable/attached_gun/grenade/mk1/recon
+ icon_state = "green_grenade-mk1"
+ attach_icon = "green_grenade-mk1_a"
+
/obj/item/attachable/attached_gun/grenade/m203 //M16 GL, only DD have it.
name = "\improper M203 Grenade Launcher"
desc = "An antique underbarrel grenade launcher. Adopted in 1969 for the M16, it was made obsolete centuries ago; how its ended up here is a mystery to you. Holds only one propriatary 40mm grenade, does not have modern IFF systems, it won't pass through your friends."
diff --git a/code/modules/projectiles/guns/boltaction.dm b/code/modules/projectiles/guns/boltaction.dm
index c18e45e38d..c97d0f82f9 100644
--- a/code/modules/projectiles/guns/boltaction.dm
+++ b/code/modules/projectiles/guns/boltaction.dm
@@ -171,11 +171,17 @@
/obj/item/weapon/gun/boltaction/vulture/update_icon()
..()
+ var/new_icon_state = src::icon_state
+ if(!current_mag)
+ new_icon_state += "_e"
+
+ icon_state = new_icon_state
+
if(!bolted)
overlays += "vulture_bolt_open"
-/obj/item/weapon/gun/boltaction/vulture/set_gun_config_values() //check that these work
+/obj/item/weapon/gun/boltaction/vulture/set_gun_config_values()
..()
set_fire_delay(FIRE_DELAY_TIER_VULTURE)
accuracy_mult = BASE_ACCURACY_MULT + HIT_ACCURACY_MULT_TIER_7
@@ -192,13 +198,11 @@
attachable_offset = list("muzzle_x" = 33, "muzzle_y" = 19, "rail_x" = 11, "rail_y" = 24, "under_x" = 25, "under_y" = 14, "stock_x" = 11, "stock_y" = 15)
/obj/item/weapon/gun/boltaction/vulture/able_to_fire(mob/user)
- . = ..()
- if(!.)
- return
-
if(!bypass_trait && !HAS_TRAIT(user, TRAIT_VULTURE_USER))
to_chat(user, SPAN_WARNING("You don't know how to use this!"))
- return
+ return FALSE
+
+ return ..()
/obj/item/weapon/gun/boltaction/vulture/Fire(atom/target, mob/living/user, params, reflex, dual_wield)
var/obj/item/attachable/vulture_scope/scope = attachments["rail"]
diff --git a/code/modules/projectiles/guns/rifles.dm b/code/modules/projectiles/guns/rifles.dm
index ba2e925ed5..48918ef2ee 100644
--- a/code/modules/projectiles/guns/rifles.dm
+++ b/code/modules/projectiles/guns/rifles.dm
@@ -425,6 +425,17 @@
/obj/item/weapon/gun/rifle/m41aMK1/anchorpoint/gl
desc = "A classic M41 MK1 Pulse Rifle painted in a fresh coat of the classic Humbrol 170 camoflauge. This one appears to be used by the Colonial Marine contingent aboard Anchorpoint Station, and is equipped with an underbarrel grenade launcher. Uses 10x24mm caseless ammunition."
starting_attachment_types = list(/obj/item/attachable/stock/rifle/collapsible, /obj/item/attachable/attached_gun/grenade/mk1)
+
+/obj/item/weapon/gun/rifle/m41aMK1/forecon
+ desc = "Pulse action 10x24mm caseless assault rifle of the USCMC, personal friend of any Marine. This one is painted in a fresh coat of the newer Humbrol 76 camouflage and is used by Force Reconnaissance units."
+ icon_state = "reconm41amk1"
+ item_state = "reconm41amk1"
+ current_mag = /obj/item/ammo_magazine/rifle/m41aMK1/recon
+ starting_attachment_types = list(/obj/item/attachable/attached_gun/grenade/mk1/recon, /obj/item/attachable/stock/rifle/collapsible)
+
+/obj/item/weapon/gun/rifle/m41aMK1/forecon/unloaded
+ current_mag = null
+
//----------------------------------------------
//Special gun for the CO to replace the smartgun
diff --git a/code/modules/projectiles/magazines/rifles.dm b/code/modules/projectiles/magazines/rifles.dm
index 91f7bbca33..6721920583 100644
--- a/code/modules/projectiles/magazines/rifles.dm
+++ b/code/modules/projectiles/magazines/rifles.dm
@@ -86,6 +86,7 @@
default_ammo = /datum/ammo/bullet/rifle
ammo_band_icon = "+m41a_mk1_band"
ammo_band_icon_empty = "+m41a_mk1_band_e"
+ bonus_overlay = "m41a_mk1_overlay"
/obj/item/ammo_magazine/rifle/m41aMK1/rubber
name = "\improper M41A Less Lethal magazine"
@@ -128,6 +129,11 @@
desc = "A long rectangular box of rounds that is only compatible with the older M41A MK1. Holds up to 99 rounds. This one contains wall-penetrating bullets."
default_ammo = /datum/ammo/bullet/rifle/ap/penetrating
ammo_band_color = AMMO_BAND_COLOR_PENETRATING
+
+/obj/item/ammo_magazine/rifle/m41aMK1/recon
+ icon_state = "m41a_mk1_recon"
+ current_rounds = 95
+ bonus_overlay = "m41a_mk1_recon_overlay"
//-------------------------------------------------------
//M4RA, l42 reskin, same stats as before but different, lore friendly, shell.
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 9851acb625..58cee33fcf 100644
--- a/colonialmarines.dme
+++ b/colonialmarines.dme
@@ -1388,6 +1388,8 @@
#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"
#include "code\modules\admin\game_master\extra_buttons\toggle_join_xeno.dm"
@@ -2128,12 +2130,12 @@
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
-#include "code\modules\mob\new_player\body_type.dm"
-#include "code\modules\mob\new_player\ethnicity.dm"
+#include "code\modules\mob\new_player\body.dm"
#include "code\modules\mob\new_player\login.dm"
#include "code\modules\mob\new_player\logout.dm"
#include "code\modules\mob\new_player\new_player.dm"
#include "code\modules\mob\new_player\preferences_setup.dm"
+#include "code\modules\mob\new_player\skin_color.dm"
#include "code\modules\mob\new_player\sprite_accessories\facial_hair.dm"
#include "code\modules\mob\new_player\sprite_accessories\hair.dm"
#include "code\modules\mob\new_player\sprite_accessories\hair_gradients.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..a8046763ac
--- /dev/null
+++ b/html/changelogs/archive/2024-07.yml
@@ -0,0 +1,11 @@
+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
diff --git a/icons/mob/humans/onmob/back.dmi b/icons/mob/humans/onmob/back.dmi
index 44ac56e1af..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 68194665bc..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/items_lefthand_1.dmi b/icons/mob/humans/onmob/items_lefthand_1.dmi
index 04faa8dc5b..92ae62c7bc 100644
Binary files a/icons/mob/humans/onmob/items_lefthand_1.dmi and b/icons/mob/humans/onmob/items_lefthand_1.dmi differ
diff --git a/icons/mob/humans/onmob/items_righthand_1.dmi b/icons/mob/humans/onmob/items_righthand_1.dmi
index e59858d94b..78e7ab199a 100644
Binary files a/icons/mob/humans/onmob/items_righthand_1.dmi and b/icons/mob/humans/onmob/items_righthand_1.dmi differ
diff --git a/icons/mob/humans/onmob/suit_1.dmi b/icons/mob/humans/onmob/suit_1.dmi
index 5ee10a5942..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/suit_slot.dmi b/icons/mob/humans/onmob/suit_slot.dmi
index 7311c0a733..555b5a0e7a 100644
Binary files a/icons/mob/humans/onmob/suit_slot.dmi and b/icons/mob/humans/onmob/suit_slot.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/mob/humans/onmob/uniform_0.dmi b/icons/mob/humans/onmob/uniform_0.dmi
index 5942c9dda6..39e3223891 100644
Binary files a/icons/mob/humans/onmob/uniform_0.dmi and b/icons/mob/humans/onmob/uniform_0.dmi differ
diff --git a/icons/mob/humans/species/r_human.dmi b/icons/mob/humans/species/r_human.dmi
index 3e8f63d9f3..4ab300efe5 100644
Binary files a/icons/mob/humans/species/r_human.dmi and b/icons/mob/humans/species/r_human.dmi differ
diff --git a/icons/mob/humans/species/r_predator.dmi b/icons/mob/humans/species/r_predator.dmi
index e8fe1c1170..192f7d698b 100644
Binary files a/icons/mob/humans/species/r_predator.dmi and b/icons/mob/humans/species/r_predator.dmi differ
diff --git a/icons/mob/humans/undershirt.dmi b/icons/mob/humans/undershirt.dmi
index 225d413ae0..468778851c 100644
Binary files a/icons/mob/humans/undershirt.dmi and b/icons/mob/humans/undershirt.dmi differ
diff --git a/icons/mob/humans/underwear.dmi b/icons/mob/humans/underwear.dmi
index b639d0a615..3276ca12ae 100644
Binary files a/icons/mob/humans/underwear.dmi and b/icons/mob/humans/underwear.dmi differ
diff --git a/icons/obj/items/clothing/belts.dmi b/icons/obj/items/clothing/belts.dmi
index b608d278ff..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 f6a6b57f6b..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 8db72b11cb..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/clothing/uniforms.dmi b/icons/obj/items/clothing/uniforms.dmi
index 5b9d981ed8..4442a8049d 100644
Binary files a/icons/obj/items/clothing/uniforms.dmi and b/icons/obj/items/clothing/uniforms.dmi differ
diff --git a/icons/obj/items/storage.dmi b/icons/obj/items/storage.dmi
index 42f91cbab0..52ff2c5baa 100644
Binary files a/icons/obj/items/storage.dmi and b/icons/obj/items/storage.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 0c0fcc473a..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/attachments/under.dmi b/icons/obj/items/weapons/guns/attachments/under.dmi
index 5a9be75423..a3691b8cc5 100644
Binary files a/icons/obj/items/weapons/guns/attachments/under.dmi and b/icons/obj/items/weapons/guns/attachments/under.dmi differ
diff --git a/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi b/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi
index caa62ef556..17e7e6f221 100644
Binary files a/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi and b/icons/obj/items/weapons/guns/guns_by_faction/colony.dmi differ
diff --git a/icons/obj/items/weapons/guns/guns_by_faction/uscm.dmi b/icons/obj/items/weapons/guns/guns_by_faction/uscm.dmi
index c896053c8e..c5946b587a 100644
Binary files a/icons/obj/items/weapons/guns/guns_by_faction/uscm.dmi and b/icons/obj/items/weapons/guns/guns_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/icons/obj/items/weapons/guns/lineart.dmi b/icons/obj/items/weapons/guns/lineart.dmi
index 5d52fd6582..35241cf3a3 100644
Binary files a/icons/obj/items/weapons/guns/lineart.dmi and b/icons/obj/items/weapons/guns/lineart.dmi differ
diff --git a/icons/obj/structures/gun_racks.dmi b/icons/obj/structures/gun_racks.dmi
index fb4e3c4b9c..5b2819a2bc 100644
Binary files a/icons/obj/structures/gun_racks.dmi and b/icons/obj/structures/gun_racks.dmi differ
diff --git a/icons/obj/vehicles/interiors/arc.dmi b/icons/obj/vehicles/interiors/arc.dmi
index 9f921a02f8..f6406fab83 100644
Binary files a/icons/obj/vehicles/interiors/arc.dmi and b/icons/obj/vehicles/interiors/arc.dmi differ
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/interiors/arc.dmm b/maps/interiors/arc.dmm
index 50c5f96b62..f9371b327e 100644
--- a/maps/interiors/arc.dmm
+++ b/maps/interiors/arc.dmm
@@ -22,7 +22,14 @@
/obj/effect/landmark/interior/spawn/vehicle_driver_seat/armor{
dir = 4
},
-/turf/open/floor,
+/obj/structure/gun_rack/apc/frontal{
+ desc = "A small compartment that stores ammunition for the ARC's 'Bleihagel RE-RE850 Frontal Rotary Cannon'.";
+ pixel_y = 6;
+ pixel_x = 2;
+ layer = 2.9;
+ density = 0
+ },
+/turf/open/floor/almayer,
/area/vehicle/apc/arc)
"G" = (
/obj/structure/bed/chair/vehicle{
@@ -41,17 +48,30 @@
layer = 3.1;
pixel_y = 5
},
-/turf/open/floor,
+/obj/structure/machinery/prop/almayer/CICmap/arc{
+ pixel_x = 16
+ },
+/obj/structure/machinery/computer/overwatch/almayer/arc{
+ pixel_y = 5;
+ pixel_x = 20
+ },
+/turf/open/floor/almayer,
/area/vehicle/apc/arc)
"I" = (
/obj/structure/bed/chair/vehicle{
pixel_x = -8
},
/obj/effect/landmark/interior/spawn/interior_viewport{
- pixel_x = 8;
- pixel_y = 4;
+ pixel_x = -16;
+ pixel_y = 11;
layer = 2.9
},
+/obj/effect/landmark/interior/spawn/weapons_loader{
+ icon = 'icons/obj/vehicles/interiors/arc.dmi';
+ icon_state = "arcloader";
+ pixel_y = 13;
+ pixel_x = 7
+ },
/turf/open/floor,
/area/vehicle/apc/arc)
"N" = (
@@ -72,8 +92,7 @@
pixel_x = 8
},
/obj/structure/machinery/cm_vending/sorted/medical/wall_med/vehicle{
- pixel_y = 14;
- pixel_x = -8
+ pixel_y = 16
},
/turf/open/floor,
/area/vehicle/apc/arc)
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/map_files/rover/rover.dmm b/maps/map_files/rover/rover.dmm
index ced3bde380..439969153d 100644
--- a/maps/map_files/rover/rover.dmm
+++ b/maps/map_files/rover/rover.dmm
@@ -175,7 +175,11 @@
pixel_x = 8;
pixel_y = 34
},
-/obj/structure/gun_rack/m41/unloaded,
+/obj/structure/gun_rack/m41/recon/unloaded,
+/obj/item/attachable/attached_gun/grenade/mk1{
+ pixel_y = 15;
+ pixel_x = 8
+ },
/turf/open/floor/almayer{
icon_state = "plate"
},
@@ -241,6 +245,7 @@
"ct" = (
/obj/structure/surface/table/almayer,
/obj/item/facepaint/sniper,
+/obj/item/attachable/attached_gun/grenade/mk1,
/turf/open/floor/almayer,
/area/golden_arrow/dorms)
"cJ" = (
@@ -1290,6 +1295,10 @@
pixel_x = -6;
pixel_y = 8
},
+/obj/item/attachable/attached_gun/grenade/mk1{
+ pixel_y = -1;
+ pixel_x = 6
+ },
/turf/open/floor/almayer{
icon_state = "plate"
},
@@ -1481,10 +1490,10 @@
/area/golden_arrow/cryo_cells)
"mJ" = (
/obj/structure/largecrate,
-/obj/structure/largecrate{
- pixel_y = 16
- },
/obj/structure/machinery/light,
+/obj/structure/largecrate/supply/ammo/m41amk1{
+ pixel_y = 15
+ },
/turf/open/floor/almayer{
icon_state = "test_floor5"
},
@@ -2816,14 +2825,30 @@
/area/golden_arrow/dorms)
"As" = (
/obj/structure/closet/crate/green,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
-/obj/item/rappel_harness,
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
+/obj/item/rappel_harness/extract{
+ shuttle_id = "dropship_cyclone"
+ },
/turf/open/floor/almayer{
icon_state = "cargo"
},
@@ -2883,6 +2908,8 @@
/area/golden_arrow/cryo_cells)
"AD" = (
/obj/structure/closet/crate/trashcart,
+/obj/item/attachable/attached_gun/grenade/mk1,
+/obj/item/attachable/attached_gun/grenade/mk1,
/turf/open/floor/plating,
/area/golden_arrow/hangar)
"AF" = (
@@ -3041,8 +3068,8 @@
/area/golden_arrow/supply)
"CO" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/largecrate/supply/ammo/m41amk1,
-/obj/structure/largecrate/supply/ammo/m41amk1{
+/obj/structure/largecrate/supply/ammo/m41amk1/forecon,
+/obj/structure/largecrate/supply/ammo/m41amk1/forecon{
pixel_x = 3;
pixel_y = 10
},
@@ -3213,7 +3240,7 @@
/obj/structure/machinery/light{
dir = 1
},
-/obj/structure/gun_rack/m41/unloaded,
+/obj/structure/gun_rack/m41/recon/unloaded,
/turf/open/floor/almayer{
icon_state = "plate"
},
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/maps/shuttles/dropship_cyclone.dmm b/maps/shuttles/dropship_cyclone.dmm
index e717f90657..4bf150c179 100644
--- a/maps/shuttles/dropship_cyclone.dmm
+++ b/maps/shuttles/dropship_cyclone.dmm
@@ -6,7 +6,9 @@
},
/area/shuttle/cyclone)
"aP" = (
-/obj/structure/shuttle/part/dropship2/left_outer_wing_connector,
+/obj/structure/shuttle/part/dropship2/left_outer_wing_connector{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"bw" = (
@@ -15,7 +17,9 @@
},
/area/shuttle/cyclone)
"cj" = (
-/obj/structure/shuttle/part/dropship2/nose_front_left,
+/obj/structure/shuttle/part/dropship2/nose_front_left{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"cm" = (
@@ -33,7 +37,9 @@
},
/area/shuttle/cyclone)
"cJ" = (
-/obj/structure/shuttle/part/dropship2/transparent/outer_left_weapons,
+/obj/structure/shuttle/part/dropship2/transparent/outer_left_weapons{
+ name = "\improper Cyclone"
+ },
/obj/effect/attach_point/weapon/midway/right_fore{
ship_tag = "dropship_cyclone"
},
@@ -62,11 +68,15 @@
},
/area/shuttle/cyclone)
"eT" = (
-/obj/structure/shuttle/part/dropship2/transparent/left_outer_bottom_wing,
+/obj/structure/shuttle/part/dropship2/transparent/left_outer_bottom_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"fo" = (
-/obj/structure/shuttle/part/dropship2/transparent/engine_left_cap,
+/obj/structure/shuttle/part/dropship2/transparent/engine_left_cap{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"fr" = (
@@ -100,22 +110,29 @@
/area/shuttle/cyclone)
"hd" = (
/obj/effect/attach_point/fuel/dropship2{
- pixel_x = -32
+ pixel_x = -32;
+ ship_tag = "dropship_cyclone"
},
/turf/closed/shuttle/cyclone/transparent{
icon_state = "33"
},
/area/shuttle/cyclone)
"hu" = (
-/obj/structure/shuttle/part/dropship2/transparent/right_outer_bottom_wing,
+/obj/structure/shuttle/part/dropship2/transparent/right_outer_bottom_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"hS" = (
-/obj/structure/shuttle/part/dropship2/transparent/engine_right_cap,
+/obj/structure/shuttle/part/dropship2/transparent/engine_right_cap{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"hV" = (
-/obj/structure/shuttle/part/dropship2/transparent/engine_right_exhaust,
+/obj/structure/shuttle/part/dropship2/transparent/engine_right_exhaust{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"ia" = (
@@ -127,12 +144,15 @@
},
/area/shuttle/cyclone)
"iO" = (
-/obj/structure/shuttle/part/dropship2/right_inner_wing_connector,
+/obj/structure/shuttle/part/dropship2/right_inner_wing_connector{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"jr" = (
/obj/effect/attach_point/crew_weapon/midway{
- ship_tag = "dropship_cyclone"
+ ship_tag = "dropship_cyclone";
+ dir = 8
},
/turf/open/shuttle/dropship{
icon_state = "rasputin15"
@@ -166,7 +186,9 @@
},
/area/shuttle/cyclone)
"mO" = (
-/obj/structure/shuttle/part/dropship2/transparent/upper_left_wing,
+/obj/structure/shuttle/part/dropship2/transparent/upper_left_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"nj" = (
@@ -210,7 +232,9 @@
},
/area/shuttle/cyclone)
"pz" = (
-/obj/structure/shuttle/part/dropship2/transparent/middle_left_wing,
+/obj/structure/shuttle/part/dropship2/transparent/middle_left_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"qj" = (
@@ -237,8 +261,19 @@
icon_state = "48"
},
/area/shuttle/cyclone)
+"rk" = (
+/obj/effect/attach_point/crew_weapon/midway{
+ ship_tag = "dropship_cyclone";
+ dir = 4
+ },
+/turf/open/shuttle/dropship{
+ icon_state = "rasputin15"
+ },
+/area/shuttle/cyclone)
"ro" = (
-/obj/structure/shuttle/part/dropship2/nose_front_right,
+/obj/structure/shuttle/part/dropship2/nose_front_right{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"rB" = (
@@ -285,7 +320,9 @@
},
/area/shuttle/cyclone)
"vT" = (
-/obj/structure/shuttle/part/dropship2/transparent/right_inner_bottom_wing,
+/obj/structure/shuttle/part/dropship2/transparent/right_inner_bottom_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"wk" = (
@@ -297,11 +334,15 @@
},
/area/shuttle/cyclone)
"xY" = (
-/obj/structure/shuttle/part/dropship2/lower_left_wall,
+/obj/structure/shuttle/part/dropship2/lower_left_wall{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"yA" = (
-/obj/structure/shuttle/part/dropship2/transparent/nose_center,
+/obj/structure/shuttle/part/dropship2/transparent/nose_center{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"yP" = (
@@ -329,7 +370,9 @@
},
/area/shuttle/cyclone)
"zL" = (
-/obj/structure/shuttle/part/dropship2/transparent/nose_top_right,
+/obj/structure/shuttle/part/dropship2/transparent/nose_top_right{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Ak" = (
@@ -342,13 +385,15 @@
},
/area/shuttle/cyclone)
"At" = (
-/obj/structure/shuttle/part/dropship2/lower_right_wall,
+/obj/structure/shuttle/part/dropship2/lower_right_wall{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Ax" = (
/obj/structure/blocker/invisible_wall,
/obj/structure/machinery/computer/cameras/dropship/two{
- name = "\improper 'Cyclon' camera controls";
+ name = "\improper 'Cyclone' camera controls";
network = list("Cyclone","Laser Targets")
},
/turf/open/shuttle/dropship{
@@ -374,19 +419,27 @@
},
/area/shuttle/cyclone)
"Cb" = (
-/obj/structure/shuttle/part/dropship2/transparent/upper_right_wing,
+/obj/structure/shuttle/part/dropship2/transparent/upper_right_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Cf" = (
-/obj/structure/shuttle/part/dropship2/left_inner_wing_connector,
+/obj/structure/shuttle/part/dropship2/left_inner_wing_connector{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Da" = (
-/obj/structure/shuttle/part/dropship2/transparent/engine_left_exhaust,
+/obj/structure/shuttle/part/dropship2/transparent/engine_left_exhaust{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Df" = (
-/obj/structure/shuttle/part/dropship2/right_outer_wing_connector,
+/obj/structure/shuttle/part/dropship2/right_outer_wing_connector{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Dl" = (
@@ -414,7 +467,9 @@
},
/area/shuttle/cyclone)
"Fz" = (
-/obj/effect/attach_point/fuel/dropship2,
+/obj/effect/attach_point/fuel/dropship2{
+ ship_tag = "dropship_cyclone"
+ },
/turf/closed/shuttle/cyclone/transparent{
icon_state = "28"
},
@@ -460,14 +515,18 @@
/area/shuttle/cyclone)
"IP" = (
/obj/effect/attach_point/weapon/dropship2/left_wing,
-/obj/structure/shuttle/part/dropship2/transparent/lower_left_wing,
+/obj/structure/shuttle/part/dropship2/transparent/lower_left_wing{
+ name = "\improper Cyclone"
+ },
/obj/effect/attach_point/weapon/midway/left_wing{
ship_tag = "dropship_cyclone"
},
/turf/template_noop,
/area/shuttle/cyclone)
"IZ" = (
-/obj/structure/shuttle/part/dropship2/transparent/middle_right_wing,
+/obj/structure/shuttle/part/dropship2/transparent/middle_right_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"JZ" = (
@@ -511,7 +570,7 @@
/area/shuttle/cyclone)
"MU" = (
/obj/structure/machinery/computer/dropship_weapons/dropship2{
- name = "\improper 'Cyclon' weapons controls"
+ name = "\improper 'Cyclone' weapons controls"
},
/obj/structure/phone_base/rotary{
name = "Cyclone Telephone";
@@ -548,14 +607,18 @@
/area/shuttle/cyclone)
"QN" = (
/obj/effect/attach_point/weapon/dropship2/right_wing,
-/obj/structure/shuttle/part/dropship2/transparent/lower_right_wing,
+/obj/structure/shuttle/part/dropship2/transparent/lower_right_wing{
+ name = "\improper Cyclone"
+ },
/obj/effect/attach_point/weapon/midway/right_wing{
ship_tag = "dropship_cyclone"
},
/turf/template_noop,
/area/shuttle/cyclone)
"RR" = (
-/obj/structure/shuttle/part/dropship2/transparent/nose_top_left,
+/obj/structure/shuttle/part/dropship2/transparent/nose_top_left{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"Sk" = (
@@ -616,12 +679,15 @@
"Zd" = (
/obj/structure/shuttle/part/dropship2/transparent/outer_right_weapons,
/obj/effect/attach_point/weapon/midway/left_fore{
- ship_tag = "dropship_cyclone"
+ ship_tag = "dropship_cyclone";
+ name = "\improper Cyclone"
},
/turf/template_noop,
/area/shuttle/cyclone)
"ZG" = (
-/obj/structure/shuttle/part/dropship2/transparent/left_outer_inner_wing,
+/obj/structure/shuttle/part/dropship2/transparent/left_outer_inner_wing{
+ name = "\improper Cyclone"
+ },
/turf/template_noop,
/area/shuttle/cyclone)
"ZU" = (
@@ -759,7 +825,7 @@ Kv
ml
oX
FD
-jr
+rk
JZ
qX
GE
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}
+
+
+
+ ))}
+
+
+
+
+ );
+};
diff --git a/tgui/packages/tgui/interfaces/SoundPanel.tsx b/tgui/packages/tgui/interfaces/SoundPanel.tsx
new file mode 100644
index 0000000000..645bf037b7
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/SoundPanel.tsx
@@ -0,0 +1,438 @@
+import { useBackend, useLocalState } from 'tgui/backend';
+import { Box, Button, Dropdown, Input, Section, Slider, Stack, Tabs } from 'tgui/components';
+import { Window } from 'tgui/layouts';
+import { Component, Fragment } from 'inferno';
+import { debounce } from 'common/timer';
+
+interface SoundPanelData {
+ sound_list: string[];
+ category_list: string[];
+ zlevel_list: string[];
+ group_list: string[];
+ sound_path: string;
+ sound_category: number;
+ sound_volume: number;
+ sound_pitch: number;
+ sound_duration: number;
+ target_player_desc: string;
+ target_loc_desc: string;
+ loc_click_intercept: string;
+ loc_click_play: string;
+ target_zlevel: string;
+ target_group: string;
+}
+
+export const SoundPanel = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { sound_list, sound_path } = data;
+
+ const PAGES = [
+ {
+ title: 'Client',
+ component: ,
+ },
+ {
+ title: 'Local',
+ component: ,
+ },
+ {
+ title: 'ZLevel',
+ component: ,
+ },
+ {
+ title: 'Server',
+ component: ,
+ },
+ ];
+ const [tabIndex, setTabIndex] = useLocalState(
+ context,
+ 'tabIndex',
+ PAGES.findIndex((page) => page.title === 'Local')
+ );
+
+ return (
+
+
+
+
+
+
+ act('set_sound_path', { sound_path: value })
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+ {PAGES.map((page, i) => {
+ return (
+ setTabIndex(i)}>
+ {page.title}
+
+ );
+ })}
+
+ {PAGES[tabIndex].component}
+
+
+
+
+
+
+
+ );
+};
+
+interface ListSearchBoxProps {
+ items: string[];
+ selection: string;
+ onSelection: (value: string) => void;
+}
+
+interface ListSearchBoxState {
+ query: string;
+ // selection: string;
+}
+
+interface ListSearchBoxEntry {
+ id: number;
+ fileName: string;
+ dirName: string;
+ fullPath: string;
+}
+
+class ListSearchBox extends Component {
+ state: ListSearchBoxState = {
+ query: '',
+ };
+ handleQueryChange(value: string) {
+ if (value === this.state.query) {
+ return;
+ }
+ this.setState({
+ query: value,
+ });
+ this.handleSelectionChange('');
+ }
+ handleSelectionChange(value: string) {
+ this.props.onSelection(value);
+ }
+
+ onSearch = debounce((value: string) => {
+ this.handleQueryChange(value);
+ }, 250);
+
+ parsedItems = this.props.items
+ .map((item, index) => {
+ const dirIndex = item.lastIndexOf('/');
+ const dirName = item.slice(6, dirIndex);
+ const extIndex = item.lastIndexOf('.');
+ const fileName = item.slice(dirIndex + 1, extIndex);
+
+ return {
+ id: index,
+ fileName: fileName,
+ dirName: dirName,
+ fullPath: item,
+ };
+ })
+ .sort((a, b) => {
+ if (a.fileName > b.fileName) return 1;
+ if (a.fileName < b.fileName) return -1;
+ if (a.dirName > b.dirName) return 1;
+ if (a.dirName < b.dirName) return -1;
+ return a.id - b.id;
+ });
+
+ render() {
+ return (
+
+
+
+ {this.parsedItems
+ .filter((item, index) => {
+ return item.fileName
+ .toLowerCase()
+ .includes(this.state.query.toLowerCase());
+ })
+ .map((item) => {
+ return (
+
+ );
+ })}
+
+
+
+ this.onSearch(value)}
+ placeholder="Search..."
+ value={this.state.query}
+ />
+
+
+ );
+ }
+}
+
+const SoundOptions = (props, context) => {
+ const { act, data } = useBackend(context);
+ const {
+ category_list,
+ sound_path,
+ sound_category,
+ sound_volume,
+ sound_pitch,
+ sound_duration,
+ } = data;
+
+ return (
+
+
+
+
+
+
+ Category
+
+
+ act('set_sound_category', { sound_category: value })
+ }
+ options={category_list}
+ selected={sound_category}
+ />
+
+
+
+ Volume
+
+
+ act('set_sound_volume', { sound_volume: value })
+ }
+ />
+
+
+
+
+
+
+
+ Pitch
+
+
+ act('set_sound_pitch', { sound_pitch: value })
+ }
+ />
+
+
+
+ Duration
+
+
+ act('set_sound_duration', { sound_duration: value })
+ }
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const ClientPage = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { target_player_desc, sound_path } = data;
+
+ return (
+
+
+
+
+ {target_player_desc || 'N/A'}
+
+
+
+
+ );
+};
+
+const LocalPage = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { target_loc_desc, sound_path, loc_click_intercept, loc_click_play } =
+ data;
+
+ return (
+
+
+
+
+
+
+ act('toggle_loc_click_play')}
+ />
+
+
+
+
+ {target_loc_desc || 'N/A'}
+
+
+
+
+ );
+};
+
+const ZLevelPage = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { zlevel_list, sound_path, target_zlevel } = data;
+
+ return (
+
+
+
+ act('set_target_zlevel', { target_zlevel: value })
+ }
+ width="180px"
+ noscroll
+ over
+ />
+
+
+
+
+ );
+};
+
+const ServerPage = (props, context) => {
+ const { act, data } = useBackend(context);
+ const { group_list, sound_path, target_group } = data;
+
+ return (
+
+
+
+ act('set_target_group', { target_group: value })
+ }
+ width="80px"
+ noscroll
+ over
+ />
+
+
+
+
+ );
+};