diff --git a/code/__defines/_planes+layers.dm b/code/__defines/_planes+layers.dm
index 4e9d7fd6ad..a2e15e6890 100644
--- a/code/__defines/_planes+layers.dm
+++ b/code/__defines/_planes+layers.dm
@@ -173,6 +173,5 @@ What is the naming convention for planes or layers?
plane = initial(plane)
layer = initial(layer)
-
// Check if a mob can "logically" see an atom plane
#define MOB_CAN_SEE_PLANE(M, P) (P <= PLANE_WORLD || (P in M.planes_visible))
diff --git a/code/_global_vars/lists/misc.dm b/code/_global_vars/lists/misc.dm
index e94aa8d652..742708face 100644
--- a/code/_global_vars/lists/misc.dm
+++ b/code/_global_vars/lists/misc.dm
@@ -7,4 +7,5 @@ GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the
// Reference list for disposal sort junctions. Filled up by sorting junction's New()
GLOBAL_LIST_EMPTY(tagger_locations)
-GLOBAL_LIST_INIT(char_directory_tags, list("Pred", "Prey", "Switch", "Non-Vore", "Unset"))
\ No newline at end of file
+GLOBAL_LIST_INIT(char_directory_tags, list("Pred", "Prey", "Switch", "Non-Vore", "Unset"))
+GLOBAL_LIST_INIT(char_directory_erptags, list("Top", "Bottom", "Switch", "No ERP", "Unset"))
\ No newline at end of file
diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm
index 7a49e8b387..f0787cec29 100644
--- a/code/_helpers/global_lists_vr.dm
+++ b/code/_helpers/global_lists_vr.dm
@@ -124,9 +124,13 @@ var/global/list/global_vore_egg_types = list(
"Slime glob",
"Chicken",
"Synthetic",
- "Cooking error",
+ "Bluespace Floppy",
+ "Bluespace Compressed File",
+ "Bluespace CD",
"Escape pod",
+ "Cooking error",
"Web cocoon",
+ "Honeycomb",
"Bug cocoon",
"Rock",
"Yellow",
@@ -155,9 +159,13 @@ var/global/list/tf_vore_egg_types = list(
"Slime glob" = /obj/item/weapon/storage/vore_egg/slimeglob,
"Chicken" = /obj/item/weapon/storage/vore_egg/chicken,
"Synthetic" = /obj/item/weapon/storage/vore_egg/synthetic,
- "Cooking error" = /obj/item/weapon/storage/vore_egg/badrecipe,
+ "Bluespace Floppy" = /obj/item/weapon/storage/vore_egg/floppy,
+ "Bluespace Compressed File" = /obj/item/weapon/storage/vore_egg/file,
+ "Bluespace CD" = /obj/item/weapon/storage/vore_egg/cd,
"Escape pod" = /obj/item/weapon/storage/vore_egg/escapepod,
+ "Cooking error" = /obj/item/weapon/storage/vore_egg/badrecipe,
"Web cocoon" = /obj/item/weapon/storage/vore_egg/cocoon,
+ "Honeycomb" = /obj/item/weapon/storage/vore_egg/honeycomb,
"Bug cocoon" = /obj/item/weapon/storage/vore_egg/bugcocoon,
"Rock" = /obj/item/weapon/storage/vore_egg/rock,
"Yellow" = /obj/item/weapon/storage/vore_egg/yellow,
diff --git a/code/_helpers/unsorted.dm b/code/_helpers/unsorted.dm
index c759e84c02..10fcc662f1 100644
--- a/code/_helpers/unsorted.dm
+++ b/code/_helpers/unsorted.dm
@@ -1616,6 +1616,46 @@ GLOBAL_REAL_VAR(list/stack_trace_storage)
/proc/href(href_src, list/href_params, href_text)
return "[href_text]"
+// This is a helper for anything that wants to render the map in TGUI
+/proc/get_tgui_plane_masters()
+ . = list()
+ // 'Utility' planes
+ . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
+ . += new /obj/screen/plane_master/lighting //Lighting system (but different!)
+ . += new /obj/screen/plane_master/ghosts //Ghosts!
+ . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
+
+ . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
+ . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
+ . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
+ . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
+ . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
+ . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
+ . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
+
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
+ . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
+
+ . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
+ . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
+
+ // Real tangible stuff planes
+ . += new /obj/screen/plane_master/main{plane = TURF_PLANE}
+ . += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
+ . += new /obj/screen/plane_master/main{plane = MOB_PLANE}
+ . += new /obj/screen/plane_master/cloaked //Cloaked atoms!
+
+ //VOREStation Add - Random other plane masters
+ . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_R} //Right-side status icon
+ . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100
+ . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status
+ . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags
+ . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality
+ //VOREStation Add End
/proc/CallAsync(datum/source, proctype, list/arguments)
set waitfor = FALSE
- return call(source, proctype)(arglist(arguments))
\ No newline at end of file
+ return call(source, proctype)(arglist(arguments))
diff --git a/code/controllers/subsystems/shuttles.dm b/code/controllers/subsystems/shuttles.dm
index 0bce940385..40039edceb 100644
--- a/code/controllers/subsystems/shuttles.dm
+++ b/code/controllers/subsystems/shuttles.dm
@@ -4,9 +4,6 @@
// Also handles initialization and processing of overmap sectors.
//
-// This global variable exists for legacy support so we don't have to rename every shuttle_controller to SSshuttles yet.
-var/global/datum/controller/subsystem/shuttles/shuttle_controller
-
SUBSYSTEM_DEF(shuttles)
name = "Shuttles"
wait = 2 SECONDS
@@ -35,9 +32,6 @@ SUBSYSTEM_DEF(shuttles)
var/tmp/list/current_run // Shuttles remaining to process this fire() tick
-/datum/controller/subsystem/shuttles/PreInit()
- global.shuttle_controller = src // TODO - Remove this! Change everything to point at SSshuttles intead
-
/datum/controller/subsystem/shuttles/Initialize(timeofday)
last_landmark_registration_time = world.time
// Find all declared shuttle datums and initailize them. (Okay, queue them for initialization a few lines further down)
diff --git a/code/controllers/subsystems/ticker.dm b/code/controllers/subsystems/ticker.dm
index f61ccd7ed2..f43a83ef91 100644
--- a/code/controllers/subsystems/ticker.dm
+++ b/code/controllers/subsystems/ticker.dm
@@ -49,10 +49,7 @@ var/global/datum/controller/subsystem/ticker/ticker
/datum/controller/subsystem/ticker/Initialize()
pregame_timeleft = config.pregame_time
send2mainirc("Server lobby is loaded and open at byond://[config.serverurl ? config.serverurl : (config.server ? config.server : "[world.address]:[world.port]")]")
-
- // Set up the global announcer
- GLOB.autospeaker = new (null, null, null, 1)
-
+ GLOB.autospeaker = new (null, null, null, 1) //Set up Global Announcer
return ..()
/datum/controller/subsystem/ticker/fire(resumed = FALSE)
diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm
index 165c5fbfef..28a7431ec1 100644
--- a/code/datums/supplypacks/engineering.dm
+++ b/code/datums/supplypacks/engineering.dm
@@ -77,6 +77,14 @@
containertype = /obj/structure/closet/crate/focalpoint
containername = "advanced hull shield generator crate"
+/datum/supply_pack/eng/pointdefense
+ name = "Point Defense Turret"
+ contains = list(/obj/machinery/porta_turret/pointdefense/orderable)
+ cost = 70
+ containertype = /obj/structure/closet/crate/large/secure/heph
+ containername = "point defense crate"
+ access = access_security
+
/datum/supply_pack/eng/electrical
name = "Electrical maintenance crate"
contains = list(
diff --git a/code/datums/supplypacks/recreation.dm b/code/datums/supplypacks/recreation.dm
index 9bb6b1a442..eae37c5869 100644
--- a/code/datums/supplypacks/recreation.dm
+++ b/code/datums/supplypacks/recreation.dm
@@ -51,7 +51,7 @@
/obj/item/weapon/reagent_containers/glass/paint/purple,
/obj/item/weapon/reagent_containers/glass/paint/black,
/obj/item/weapon/reagent_containers/glass/paint/white,
- /obj/item/weapon/contraband/poster,
+ /obj/item/weapon/contraband/poster/custom,
/obj/item/weapon/wrapping_paper = 3
)
cost = 10
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 0b0b8d014c..a6ceb219c8 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -13,7 +13,7 @@
/obj/effect/meteor/big=3,
/obj/effect/meteor/flaming=1,
/obj/effect/meteor/irradiated=3
- )
+ )
//for threatening meteor event
/var/list/meteors_threatening = list(
@@ -206,6 +206,21 @@
return
..()
+/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj)
+ if(Proj.excavation_amount)
+ get_hit()
+ if(!QDELETED(src))
+ wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors.
+ wall_power = max(1, wall_power)
+
+ if(wall_power < Proj.excavation_amount)
+ if(prob(min(90, 100 - Proj.damage)))
+ die(TRUE)
+ else
+ die(FALSE)
+ return
+ return
+
/obj/effect/meteor/proc/make_debris()
for(var/throws = dropamt, throws > 0, throws--)
var/obj/item/O = new meteordrop(get_turf(src))
diff --git a/code/game/machinery/pointdefense.dm b/code/game/machinery/pointdefense.dm
index 637ad9f53b..bb0517bd86 100644
--- a/code/game/machinery/pointdefense.dm
+++ b/code/game/machinery/pointdefense.dm
@@ -371,10 +371,13 @@ GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/power/pointdefense)
/obj/effect/projectile/tracer/pointdefense
+ icon = 'icons/obj/projectiles_vr.dmi'
icon_state = "beam_pointdef"
/obj/effect/projectile/muzzle/pointdefense
+ icon = 'icons/obj/projectiles_vr.dmi'
icon_state = "muzzle_pointdef"
/obj/effect/projectile/impact/pointdefense
+ icon = 'icons/obj/projectiles_vr.dmi'
icon_state = "impact_pointdef"
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 01a890bd79..2ab3cecae7 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -74,6 +74,7 @@
var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
var/check_all = FALSE //If active, will fire on anything, including synthetics.
+ var/fire_at_movement = FALSE // If active, will fire on the most recent thing to move in range, as soon as possible.
var/ailock = FALSE // AI cannot use this
var/check_down = FALSE //If active, will shoot to kill when lethals are also on
var/faction = null //if set, will not fire at people in the same faction for any reason.
@@ -95,6 +96,8 @@
var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down.
var/can_salvage = TRUE // If false, salvaging doesn't give you anything.
+ var/stay_up = FALSE // If true, the turret will remain open while it is powered.
+
/obj/machinery/porta_turret/crescent
req_one_access = list(access_cent_specops)
enabled = FALSE
@@ -276,6 +279,31 @@
)
return data
+/obj/machinery/porta_turret/pointdefense
+ name = "point-defense turret"
+ turret_type = "core"
+ desc = "A basic ship-mounted rock-breaker."
+ description_info = "This turret is capable of blasting incoming meteors into gravel, but it is very limited in range."
+
+ installation = /obj/item/weapon/gun/energy/mininglaser
+
+ check_arrest = FALSE
+ check_records = FALSE
+ check_weapons = FALSE
+ check_access = FALSE
+ check_anomalies = FALSE
+ check_synth = FALSE
+ check_all = FALSE
+ fire_at_movement = TRUE
+ stay_up = TRUE
+ ailock = FALSE
+ check_down = FALSE
+
+/obj/machinery/porta_turret/pointdefense/orderable
+ enabled = FALSE
+ anchored = FALSE
+ locked = FALSE
+
/obj/machinery/porta_turret/Initialize()
//Sets up a spark system
spark_system = new /datum/effect/effect/system/spark_spread
@@ -322,6 +350,8 @@
var/obj/item/projectile/P = initial(E.projectile_type)
//var/obj/item/ammo_casing/shottype = E.projectile_type
+ GLOB.moved_event.register_global(src, /obj/machinery/porta_turret/proc/point_defense)
+
projectile = P
lethal_projectile = projectile
shot_sound = initial(P.fire_sound)
@@ -377,11 +407,18 @@
lethal_shot_sound = 'sound/weapons/eluger.ogg'
shot_sound = 'sound/weapons/Taser.ogg'
-/obj/machinery/porta_turret/proc/HasController()
- var/area/A = get_area(src)
- return A && A.turret_controls.len > 0
+ if(/obj/item/weapon/gun/energy/mininglaser)
+ lethal_icon_color = "green"
+ lethal_projectile = /obj/item/projectile/beam/mininglaser
+ lethal_shot_sound = 'sound/weapons/eluger.ogg'
+ icon_color = "red"
+ projectile = /obj/item/projectile/beam/weaklaser
+ shot_sound = 'sound/weapons/Laser.ogg'
/obj/machinery/porta_turret/proc/isLocked(mob/user)
+ if(locked && !issilicon(user))
+ to_chat(user, "Controls locked.")
+ return 1
if(HasController())
return TRUE
if(isrobot(user) || isAI(user))
@@ -409,6 +446,10 @@
/obj/machinery/porta_turret/attack_hand(mob/user)
tgui_interact(user)
+/obj/machinery/porta_turret/proc/HasController()
+ var/area/A = get_area(src)
+ return A && A.turret_controls.len > 0
+
/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null)
if(HasController())
to_chat(user, "[src] can only be controlled using the assigned turret controller.")
@@ -671,6 +712,10 @@
popDown()
return
+ if(stay_up)
+ timeout = 10
+ popUp()
+
var/list/targets = list() //list of primary targets
var/list/secondarytargets = list() //targets that are least important
@@ -693,7 +738,7 @@
if(!tryToShootAt(targets))
if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets
timeout--
- if(timeout <= 0)
+ if(timeout <= 0 && !stay_up)
spawn()
popDown() // no valid targets, close the cover
@@ -701,6 +746,14 @@
use_power(20000)
health = min(health+1, maxhealth) // 1HP for 20kJ
+// We're expecting the first arg to be a target, for this we don't care about previous and next turfs.
+/obj/machinery/porta_turret/proc/point_defense(var/atom/movable/Targ)
+ if((stat & (NOPOWER|BROKEN)) || !fire_at_movement) // Are we even able or supposed to fire at non-moving targets?
+ return
+
+ if((isliving(Targ) && assess_living(Targ)) || ((istype(Targ, /obj/item) || istype(Targ, /obj/effect/meteor)) && Targ.invisibility < INVISIBILITY_LEVEL_ONE && Targ in view(7,src))) // Is the target a living thing, if so, is it a valid target? Or if it's not living, is it in sight?
+ target(Targ) // Yes? Blast it.
+
/obj/machinery/porta_turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets)
switch(assess_living(L))
if(TURRET_PRIORITY_TARGET)
@@ -727,10 +780,10 @@
if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really
return TURRET_NOT_TARGET //move onto next potential victim!
- if(get_dist(src, L) > 7) //if it's too far away, why bother?
+ if(get_dist(src, get_turf(L)) > 7) //if it's too far away, why bother?
return TURRET_NOT_TARGET
- if(!(L in check_trajectory(L, src))) //check if we have true line of sight
+ if(!(L in check_trajectory(get_turf(L), src))) //check if we have true line of sight
return TURRET_NOT_TARGET
if(emagged) // If emagged not even the dead get a rest
@@ -837,8 +890,9 @@
last_target = target
spawn()
popUp() //pop the turret up if it's not already up.
- set_dir(get_dir(src, target)) //even if you can't shoot, follow the target
- playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound
+ if(dir != get_dir(src, target))
+ set_dir(get_dir(src, target)) //even if you can't shoot, follow the target
+ playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound
spawn()
shootAt(target)
return 1
@@ -903,6 +957,8 @@
var/check_anomalies
var/check_all
var/check_down
+ var/fire_at_movement
+ var/stay_up
var/ailock
/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
@@ -919,6 +975,8 @@
check_anomalies = TC.check_anomalies
check_all = TC.check_all
check_down = TC.check_down
+ fire_at_movement = TC.fire_at_movement
+ stay_up = TC.stay_up
ailock = TC.ailock
power_change()
diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm
index ad55fe14a1..7f5a1d8e56 100644
--- a/code/game/machinery/turret_control.dm
+++ b/code/game/machinery/turret_control.dm
@@ -28,9 +28,12 @@
var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
var/check_all = FALSE //If active, will shoot at anything.
var/check_down = TRUE //If active, won't shoot laying targets.
+ var/stay_up = FALSE //If active, the turret will not pop-down unless it loses power or is disabled.
+ var/fire_at_movement = FALSE //If active, the turret will prioritize objects or creatures that move in its range.
var/ailock = FALSE //Silicons cannot use this
var/syndicate = FALSE
+
req_access = list(access_ai_upload)
/obj/machinery/turretid/stun
@@ -194,6 +197,8 @@
TC.check_anomalies = check_anomalies
TC.check_all = check_all
TC.check_down = check_down
+ TC.stay_up = stay_up
+ TC.fire_at_movement = fire_at_movement
TC.ailock = ailock
if(istype(control_area))
diff --git a/code/game/objects/effects/decals/contraband.dm b/code/game/objects/effects/decals/contraband.dm
index c3716b031c..5286670849 100644
--- a/code/game/objects/effects/decals/contraband.dm
+++ b/code/game/objects/effects/decals/contraband.dm
@@ -90,6 +90,39 @@
serial_number = given_serial
..(loc)
+//Selectable subtype
+/obj/item/weapon/contraband/poster/custom
+ name = "rolled-up poly-poster"
+ desc = "The poster comes with its own automatic adhesive mechanism, for easy pinning to any vertical surface. This one is made from some kind of e-paper, and could display almost anything!"
+ poster_type = /obj/structure/sign/poster/custom
+
+/obj/item/weapon/contraband/poster/custom/New(turf/loc, var/given_serial = 0)
+ if(given_serial == 0)
+ serial_number = 1 //Decidedly unrandom
+ else
+ serial_number = given_serial
+ ..(loc)
+
+/obj/item/weapon/contraband/poster/custom/verb/select_poster()
+ set name = "Set Poster type"
+ set category = "Object"
+ set desc = "Click to choose a poster to display."
+
+ var/mob/M = usr
+ var/list/options = list()
+ for(var/datum/poster/posteroption in poster_designs)
+ options[posteroption.listing_name] = posteroption
+
+ var/choice = input(M,"Choose a poster!","Customize Poster") in options
+ if(src && choice && !M.stat && in_range(M,src))
+ var serial = poster_designs.Find(options[choice])
+ serial_number = serial
+ name = "rolled-up poly-poster - No.[serial]"
+ to_chat(M, "The poster is now: [choice].")
+ return 1
+
+
+
//############################## THE ACTUAL DECALS ###########################
/obj/structure/sign/poster
@@ -187,6 +220,7 @@
// Description suffix
var/desc=""
var/icon_state=""
+ var/listing_name=""
// NT poster subtype.
/obj/structure/sign/poster/nanotrasen
@@ -201,3 +235,8 @@
set_poster(design)
..(newloc, placement_dir, serial, itemtype)
+
+//Non-Random Posters
+
+/obj/structure/sign/poster/custom
+ roll_type = /obj/item/weapon/contraband/poster/custom
\ No newline at end of file
diff --git a/code/game/objects/effects/decals/posters/bs12.dm b/code/game/objects/effects/decals/posters/bs12.dm
index 7aadcb2ead..5c1c32d98b 100644
--- a/code/game/objects/effects/decals/posters/bs12.dm
+++ b/code/game/objects/effects/decals/posters/bs12.dm
@@ -1,290 +1,348 @@
// baystation12 posters
/datum/poster/bay_1
icon_state="bsposter1"
- name = "Unlucky Space Explorer"
- desc = "This particular one depicts a skeletal form within a space suit."
+ name = "PSA: Unlucky Space Explorer"
+ desc = "This grim PSA depicts a skeletal form within a space suit. Thousands die every year as a result of failing to follow EVA safety guidelines."
+ listing_name = "PSA - EVA Accidents"
/datum/poster/bay_2
icon_state="bsposter2"
- name = "Positronic Logic Conflicts"
- desc = "This particular one depicts the cold, unmoving stare of a particular advanced AI."
+ name = "PSA: Disobedient Drones"
+ desc = "This PSA depicts the cold, unmoving stare of a particularly advanced AI. Contact information for the Emergent Intelligence Oversight is included."
+ listing_name = "PSA - Emergent Drones"
/datum/poster/bay_3
icon_state="bsposter3"
- name = "Paranoia"
- desc = "This particular one warns of the dangers of trusting your co-workers too much."
+ name = "PSA: Corporate Espionage"
+ desc = "This PSA warns of the dangers of trusting your co-workers with sensitive department information. They may be spying for a rival corporation, or worse!"
+ listing_name = "PSA - Corporate Espionage"
/datum/poster/bay_4
icon_state="bsposter4"
name = "Keep Calm"
- desc = "This particular one is of a famous New Earth design, although a bit modified. Someone has scribbled an O over the A on the poster."
+ desc = "An incredibly vague message, presented in a classic form. Someone has scribbled an O over the A on the poster."
+ listing_name = "PSA - Keep Calm"
/datum/poster/bay_5
icon_state="bsposter5"
- name = "Martian Warlord"
- desc = "This particular one depicts the cartoony mug of a certain Martial Warmonger."
+ name = "PSA: Saboteurs!"
+ desc = "A company PSA reminding employees to keep an eye out for suspicious activity, and report it immediately."
+ listing_name = "PSA - Saboteur"
/datum/poster/bay_6
icon_state="bsposter6"
- name = "Technological Singularity"
- desc = "This particular one is of the blood-curdling symbol of a long-since defeated enemy of humanity."
+ name = "Eradicator XV"
+ desc = "An ad for the fifteenth installment in the Eradicator movie franchise, about a violent robotic uprising hellbent on wiping out humanity."
+ listing_name = "Ad - Eradicator XV"
/datum/poster/bay_7
icon_state="bsposter7"
- name = "Wasteland"
- desc = "This particular one is of a couple of ragged gunmen, one male and one female, on top of a mound of rubble. The number \"12\" is visible on their blue jumpsuits."
+ name = "Wasteland: Battle for Earth"
+ desc = "This action movie ad shows a couple of ragged post-apocalyptic gunmen, one male and one female, on top of a mound of rubble."
+ listing_name = "Ad - Battle for Earth"
/datum/poster/bay_8
icon_state="bsposter8"
name = "Pinup Girl Cindy"
- desc = "This particular one is of a historical corporate PR girl, Cindy, in a particularly feminine pose."
+ desc = "This is a vintage 2450s pin-up of NanoTrasen's PR girl, Cindy, in a particularly feminine pose."
+ listing_name = "Pin-up - Cindi"
/datum/poster/bay_9
icon_state="bsposter9"
name = "Pinup Girl Amy"
- desc = "This particular one is of Amy, the nymphomaniac urban legend of deep space. How this photograph came to be is not known."
+ desc = "This pin-up is of Amy, one of NanoTrasen's most popular PR models."
+ listing_name = "Pin-up - Amy"
/datum/poster/bay_10
icon_state="bsposter10"
name = "Don't Panic"
- desc = "This particular one depicts some sort of star in a grimace. The \"Don't Panic\" is written in big, friendly letters."
+ desc = "This ad for Vey-Medical anxiety pills depicts some sort of star in a grimace. The \"Don't Panic\" is written in big, friendly letters."
+ listing_name = "Ad - Don't panic."
/datum/poster/bay_11
icon_state="bsposter11"
- name = "Underwater Laboratory"
- desc = "This particular one is of the fabled last crew of a previous Company project."
+ name = "Underwater Living"
+ desc = "This immigration poster encourages you to move to Earth;s premiere underwater city, Atlantis. One bedroom apartments start at half your salary."
+ listing_name = "Ad - Atlantis"
/datum/poster/bay_12
icon_state="bsposter12"
- name = "Rogue AI"
- desc = "This particular one depicts the shell of the infamous AI that catastropically comandeered one of humanity's earliest space stations. Back then, the Company was just known as TriOptimum."
+ name = "Beware Rogue AI"
+ desc = "This ad for the Emergent Intelligence Oversight Recruitment Division depicts a menacing AI and asks if you're a bad enough dude to take it on."
+ listing_name = "Recruitment - EIO"
/datum/poster/bay_13
icon_state="bsposter13"
- name = "User of the Arcane Arts"
- desc = "This particular one depicts a wizard, casting a spell. You can't really make out if it's an actual photograph or a computer-generated image."
+ name = "Become A User of the Arcane Arts"
+ desc = "This ad for popular Virtual Reality online game 'Wiz Online' depicts a wizard, casting a spell. You can't really make out if it's an actual photograph or a computer-generated image. Incredible graphics!"
+ listing_name = "Ad - Wiz Online"
/datum/poster/bay_14
icon_state="bsposter14"
name = "Levitating Skull"
- desc = "This particular one is the portrait of a flying enchanted skull. Its adventures along with its fabled companion are now fading through history..."
+ desc = "This tacky ad is a portrait of the flying enchanted skull, Bones. Its adventures along with its fabled companion are showing now on a broadcast service near you!"
+ listing_name = "Ad - Bones Adventures"
/datum/poster/bay_15
icon_state="bsposter15"
name = "Augmented Legend"
- desc = "This particular one is of an obviously augmented individual, gazing towards the sky. The cyber-city in the backround is rather punkish."
+ desc = "This ad for Ward-Takahashi cybernetic implants shows obviously augmented individual, gazing towards the sky. Is your body augmented?"
+ listing_name = "Ad - Ward-Takahashi Augments"
/datum/poster/bay_16
icon_state="bsposter16"
- name = "Dangerous Static"
- desc = "This particular one depicts nothing remarkable other than a rather mesmerising pattern of monitor static. There's a tag on the sides of the poster, but it's ripped off."
+ name = "Snow Crash"
+ desc = "This particular one depicts nothing remarkable other than a rather mesmerising pattern of monitor static. You think it's an ad for something, but you're not sure what."
+ listing_name = "Ad - Snow Crash"
/datum/poster/bay_17
icon_state="bsposter17"
name = "Pinup Girl Val"
desc = "Luscious Val McNeil, the vertically challenged Legal Extraordinaire, winner of Miss Space two years running and favoured pinup girl of Lawyers Weekly."
+ listing_name = "Pin-up - Val"
/datum/poster/bay_18
icon_state="bsposter18"
name = "Derpman, Enforcer of the State"
- desc = "Here to protect and serve... your donuts! A generously proportioned man, he teaches you the value of hiding your snacks."
+ desc = "An ad for Centauri Provisions donuts. Officer Derpman is here to protect and serve... your donuts! A generously proportioned man, he teaches you the value of hiding your snacks."
+ listing_name = "Ad - Officer Derpman Donuts"
/datum/poster/bay_19
icon_state="bsposter19"
name = "Respect an Unathi"
- desc = "This poster depicts a well dressed looking Unathi receiving a prestigious award. It appears to espouse greater co-operation and harmony between the two races."
+ desc = "This poster depicts a sharply dressed Unathi receiving a prestigious award. It appears to espouse greater co-operation and harmony between humanity and the Unathi."
+ listing_name = "PSA - Unathi"
/datum/poster/bay_20
icon_state="bsposter20"
- name = "Skrell Twilight"
- desc = "This poster depicts a mysteriously inscrutable, alien scene. Numerous Skrell can be seen conversing amidst great, crystalline towers rising above crashing waves"
+ name = "Skrell Tourism"
+ desc = "This tourism poster depicts a mysteriously inscrutable, alien scene. Numerous Skrell can be seen conversing amidst great, crystalline towers rising above crashing waves."
+ listing_name = "Ad - Skrellian Tourism"
/datum/poster/bay_21
icon_state="bsposter21"
name = "Join the Fuzz!"
desc = "It's a nice recruitment poster of a white haired Chinese woman that says; \"Big Guns, Hot Women, Good Times. Security. We get it done.\""
+ listing_name = "Recruitment - NanoTrasen Security - Pinup"
/datum/poster/bay_22
icon_state="bsposter22"
name = "Looking for a career with excitement?"
desc = "A recruitment poster starring a dark haired woman with glasses and a purple shirt that has \"Got Brains? Got Talent? Not afraid of electric flying monsters that want to suck the soul out of you? Then Xenobiology could use someone like you!\" written on the bottom."
+ listing_name = "Recruitment - Xenobiology"
/datum/poster/bay_23
icon_state="bsposter23"
name = "Safety first: because electricity doesn't wait!"
desc = "A safety poster starring a clueless looking redhead with frazzled hair. \"Every year, hundreds of NT employees expose themselves to electric shock. Play it safe. Avoid suspicious doors after electrical storms, and always wear protection when doing electric maintenance.\""
+ listing_name = "Safety - Electric Shock"
/datum/poster/bay_24
icon_state="bsposter24"
name = "Responsible medbay habits, No #259"
desc = "A poster with a nervous looking geneticist on it states; \"Friends Tell Friends They're Clones. It can cause severe and irreparable emotional trauma if a person is not properly informed of their recent demise. Always follow your contractual obligation and inform them of their recent rejuvenation.\""
+ listing_name = "Medical - Clone Disclosure"
/datum/poster/bay_25
icon_state="bsposter25"
name = "Irresponsible medbay habits, No #2"
desc = "This is a safety poster starring a perverted looking naked doctor. \"Sexual harassment is never okay. REPORT any acts of sexual deviance or harassment that disrupt a healthy working environment.\""
+ listing_name = "Medical - Sexual Harassment"
/datum/poster/bay_26
icon_state="bsposter26"
name = "The Men We Knew"
- desc = "This movie poster depicts a group of soldiers fighting a large mech, the movie seems to be a patriotic war movie."
+ desc = "This movie poster depicts a group of soldiers fighting a large mech, it seems to be some kind of movie set during the Hegemony War."
+ listing_name = "Ad - Men We Knew"
/datum/poster/bay_27
icon_state="bsposter27"
name = "Plastic Sheep Can't Scream"
- desc = "This is a movie poster for an upcoming horror movie, it features an AI in the front of it."
+ desc = "This is a movie poster for an upcoming horror movie, featuring a frightening looking robotic creature."
+ listing_name = "Ad - Plastic Sheep Can't Scream"
/datum/poster/bay_28
icon_state="bsposter28"
name = "The Stars Know Love"
- desc = "This is a movie poster. A bleeding woman is shown drawing a heart in her blood on the window of space ship, it seems to be a romantic Drama."
+ desc = "This is a movie poster. A bleeding woman is shown drawing a heart in her blood on the window of space ship, it seems to be a romantic drama."
+ listing_name = "Ad - The Stars Know Love"
/datum/poster/bay_29
icon_state="bsposter29"
name = "Winter Is Coming"
- desc = "On the poster is a frighteningly large wolf, he warns: \"Only YOU can keep the station from freezing during planetary occultation!\""
+ desc = "On the poster is a frighteningly large wolf, he warns: \"Only YOU can keep yourself from freezing this winter!\". Below is the logo of NanoThreads clothing."
+ listing_name = "Ad - Cold-weather Gear"
/datum/poster/bay_30
icon_state="bsposter30"
name = "Ambrosia Vulgaris"
- desc = "Just looking at this poster makes you feel a little bit dizzy."
+ desc = "It's an ad for getting absolutely shitfaced on psychadelics. Just looking at this poster makes you feel a little bit dizzy. Use responsibly."
+ listing_name = "Ad - Ambrosia Vulgaris"
/datum/poster/bay_31
icon_state="bsposter31"
- name = "Donut Corp"
- desc = "This is an advertisement for Donut Corp, the new innovation in donut technology!"
+ name = "Donut Panic"
+ desc = "This is an advertisement for Donut Panic, the bi-monthly periodical for donut lovers galaxy-wide."
+ listing_name = "Ad - Donuts Panic"
/datum/poster/bay_32
icon_state="bsposter32"
name = "Eat!"
- desc = "A poster depicting a hamburger. The poster orders you to consume the hamburger."
+ desc = "A NanoPastures poster depicting a synthmeat hamburger. The poster COMMANDS you to consume the hamburger."
+ listing_name = "Ad - Hamburger"
/datum/poster/bay_33
icon_state="bsposter33"
- name = "Tools, tools, tools"
- desc = "You can never have enough tools, thats for sure!"
+ name = "Tools, tools, tools!"
+ desc = "Xion Manufacturing reminds you: Never trust an off-brand tool. "
+ listing_name = "Ad - Xion Tools"
/datum/poster/bay_34
icon_state="bsposter34"
name = "Power Up!"
- desc = "High reward, higher risk!"
+ desc = "NanoTrasen are leaders in Phoron-based supermatter power! High reward, higher risk!"
+ listing_name = "Ad - Phoron Power"
/datum/poster/bay_35
icon_state="bsposter35"
- name = "Lamarr"
- desc = "This is a poster depicting the pet and mascot of the science department."
+ name = "Kendrick"
+ desc = "This is a poster depicting the pet and mascot of the science department, and national corporate ambassador for slime-based science solutions.."
+ listing_name = "Ad - Slime Science"
/datum/poster/bay_36
icon_state="bsposter36"
name = "Fancy Borg"
- desc = "A poster depicting a cyborg using the service module. 'Fancy Borg' is written on it."
+ desc = "A poster depicting a cyborg dressed to the nines. An ad for cybernetics enthusiast magazine, Borg Fancy."
+ listing_name = "Ad - Borg Fancy A"
/datum/poster/bay_37
icon_state="bsposter37"
name = "Fancier Borg"
- desc = "A poster depicting a cyborg using the service module. 'Fancy Borg' is written on it. This is even fancier than the first poster."
+ desc = "A poster depicting a cyborg dressed to the nines. An ad for cybernetics enthusiast magazine, Borg Fancy. This is even fancier than the other poster."
+ listing_name = "Ad - Borg Fancy B"
/datum/poster/bay_38
icon_state="bsposter38"
name = "Toaster Love"
- desc = "This is a poster of a toaster containing two slices of bread. The word LOVE is written in big pink letters underneath."
+ desc = "This is a poster of a toaster containing two slices of bread. The word LOVE is written in big pink letters underneath. An ad for a cybernetics 'enthusiast' magazine. "
+ listing_name = "Ad - Toaster Love"
/datum/poster/bay_39
icon_state="bsposter39"
name = "Responsible medbay habits, No #91"
desc = "A safety poster with a chemist holding a vial. \"Always wear safety gear while handling dangerous chemicals, even if it concerns only small amounts.\""
+ listing_name = "Safety - Chemicals"
/datum/poster/bay_40
icon_state="bsposter40"
name = "Agreeable work environment"
desc = "This poster depicts a young woman in a stylish dress. \"Try to aim for a pleasant atmosphere in the workspace. A friendly word can do more than forms in triplicate.\""
+ listing_name = "PSA - Agreeable Work Environment"
/datum/poster/bay_41
icon_state="bsposter41"
name = "Professional work environment"
desc = "A safety poster featuring a green haired woman in a shimmering blue dress. \"As an Internal Affairs Agent, your job is to create a fair and agreeable work environment for the crewmembers, as discreetly and professionally as possible.\""
+ listing_name = "Recruitment - Internal Affairs"
/datum/poster/bay_42
icon_state="bsposter42"
name = "Engineering pinup"
desc = "This is pin-up poster. A half-naked girl with white hair, toned muscles and stunning blue eyes looks back at you from the poster. Her welding helmet, tattoos and grey jumpsuit hanging around her waist gives a bit of a rugged feel."
+ listing_name = "Pin-up - Engineer"
/datum/poster/bay_43
icon_state="bsposter43"
name = "Responsible medbay habits, No #3"
desc = "A safety poster with a purple-haired surgeon. She looks a bit cross. \"Let the surgeons do their work. NEVER replace or remove a surgery tool from where the surgeon put it.\""
+ listing_name = "Safety - Surgical Tools"
/datum/poster/bay_44
icon_state="bsposter44"
name = "Time for a drink?"
- desc = "This poster depicts a friendly-looking Tajaran holding a tray of drinks."
+ desc = "This poster for a brand for \"Tajaran-style\" rum produced by Gilthari Exports depicts a friendly-looking Tajaran holding a tray of drinks."
+ listing_name = "Ad - Rarkajar Rum"
/datum/poster/bay_45
icon_state="bsposter45"
name = "Responsible engineering habits, No #1"
desc = "A safety poster featuring a blue haired engineer. \"When repairing a machine or construction, always aim for long-term solutions.\""
+ listing_name = "Safety - No Shortcut Fixes"
/datum/poster/bay_46
icon_state="bsposter46"
name = "Inspirational lawyer"
- desc = "An inspirational poster depicting a Skrellian lawyer. He seems to be shouting something, while pointing fiercely to the right."
+ desc = "An inspirational poster depicting a Skrellian lawyer. He seems to be shouting something, while pointing fiercely to the right. It's an ad for a NanoTrasen subsidiary law firm."
+ listing_name = "Ad - NanoTrasen Law"
/datum/poster/bay_47
icon_state="bsposter47"
name = "Security pinup"
desc = "This is a pin-up poster. A dark skinned white haired girl poses in the sunlight wearing a tank top with her stomach exposed. The text on the poster states \"M, Succubus of Security.\" and a lipstick mark stains the top right corner, as if kissed by the model herself."
+ listing_name = "Pin-up - Security"
/datum/poster/bay_48
icon_state="bsposter48"
- name = "Borg pinup?"
- desc = "This is a.. pin-up poster? It is a diagram on an old model of cyborg with a note scribbled in marker on the bottom, on the top there is a large XO written in red marker."
+ name = "Remote Drones 4 U"
+ desc = "This is an ad for a fairly basic model of drone produced by Grayson Manufactories Ltd. for use in hazardous environments."
+ listing_name = "Ad - Grayson Drones"
/datum/poster/bay_49
icon_state="bsposter49"
name = "Engineering recruitment"
desc = "This is a poster showing an engineer relaxing by a computer, the text states \"Living the life! Join Engineering today!\""
+ listing_name = "Recruitment - Engineering"
/datum/poster/bay_50
icon_state="bsposter50"
name = "Pinup Girl Cindy Kate"
- desc = "This particular one is of Cindy Kate, a seductive performer well known among less savoury circles."
+ desc = "This particular one is of Cindy Kate, a seductive virtual performer well known among less savoury exonet circles."
+ listing_name = "Pin-up - Cindi-Kate"
/datum/poster/bay_51
icon_state="bsposter51"
- name = "space appreciation poster"
- desc = "This is a poster produced by the Generic Space Company, as a part of a series of commemorative posters on the wonders of space. One of three."
+ name = "space tourism poster"
+ desc = "This is a poster produced by the Visit Space Society. This one is targeted at planet-dwellers, suggesting they generally visit space."
+ listing_name = "Tourism - Space"
/datum/poster/bay_52
icon_state="bsposter52"
name = "fire safety poster"
desc = "This is a poster reminding you of what you should do if you are on fire, or if you are at a dance party."
+ listing_name = "Safety - Stop Drop and Roll"
/datum/poster/bay_53
icon_state="bsposter53"
name = "fire extinguisher poster"
desc = "This is a poster reminding you of what you should use to put out a fire."
+ listing_name = "Safety - Fire Extinguishers"
/datum/poster/bay_54
icon_state="bsposter54"
name = "firefighter poster"
desc = "This is a poster of a particularly stern looking firefighter. The caption reads, \"Only you can prevent space fires.\""
+ listing_name = "Safety - Prevent Fires"
/datum/poster/bay_55
icon_state="bsposter55"
- name = "Earth appreciation poster"
- desc = "This is a poster produced by the Generic Space Company, as a part of a series of commemorative posters on the wonders of space. Two of three."
+ name = "Binma tourism poster"
+ desc = "This is a poster produced by the Visit Space Society. This one suggests you visit the exotic garden world of Binma, and try not to get eaten."
+ listing_name = "Tourism - Binma"
/datum/poster/bay_56
icon_state="bsposter56"
name = "Mars appreciation poster"
- desc = "This is a poster produced by the Generic Space Company, as a part of a series of commemorative posters on the wonders of space. Three of three."
+ desc = "This is a poster produced by the Visit Space Society. This one suggests you visit historic Mars. Come for the domes, stay for the monuments to hubris!"
/datum/poster/bay_57
icon_state="bsposter57"
name = "space carp warning poster"
desc = "This poster tells of the dangers of space carp infestations."
+ listing_name = "Safety - Carp"
/datum/poster/bay_58
icon_state="bsposter58"
name = "space carp information poster"
- desc = "This poster showcases an old spacer saying on the dangers of migrant space carp."
\ No newline at end of file
+ desc = "This poster showcases an old spacer saying on the dangers of migrant space carp."
+ listing_name = "Safety - Carp Information"
+
diff --git a/code/game/objects/effects/decals/posters/polarisposters.dm b/code/game/objects/effects/decals/posters/polarisposters.dm
index d40cdd0db0..45cfd8622d 100644
--- a/code/game/objects/effects/decals/posters/polarisposters.dm
+++ b/code/game/objects/effects/decals/posters/polarisposters.dm
@@ -2,41 +2,49 @@
icon_state="polposter1"
name = "Safety!"
desc = "A poster advising you to learn how to put on your internals at a moment's notice."
+ listing_name = "Safety - Internals"
/datum/poster/pol_2
icon_state="polposter2"
name = "Safety!"
desc = "A blue and white colored poster. This one advises you to wear your safety goggles when handling chemicals."
+ listing_name = "Safety - Goggles"
/datum/poster/pol_3
icon_state="polposter3"
name = "Safety!"
desc = "A safety poster instructing you to comply with the authorities, especially in an emergency."
+ listing_name = "Safety - Follow Instruction"
/datum/poster/pol_4
icon_state="polposter4"
name = "Clean Hands Save Lives"
desc = "A safety poster reminding you to wash your hands."
+ listing_name = "Safety - Hand Washing"
/datum/poster/pol_5
icon_state="polposter5"
name = "Help!"
desc = "This poster depicts a man helping another man get up."
+ listing_name = "Safety - Help Others"
/datum/poster/pol_6
icon_state="polposter6"
name = "Walk!"
desc = "This poster depicts a man walking, presumably to encourage you not to run in the halls."
+ listing_name = "Safety - No Running"
/datum/poster/pol_7
icon_state="polposter7"
name = "Place your signs!"
desc = "A safety poster reminding custodial staff to place wet floor signs where needed. This reminder's rarely heeded."
+ listing_name = "Safety - Wet Floor Signs"
/datum/poster/pol_8
icon_state="polposter8"
name = "Safety!"
desc = "An advertisement / safety poster for EVA training and certification. Training is available at your local Central Command."
+ listing_name = "Safety - EVA Training"
/datum/poster/pol_9
icon_state="poster10" //Recycling this icon
@@ -44,52 +52,138 @@
desc = "This poster appears to be reference material for maintenance personnel, instructing to always wear insulated gloves, that wirecutters and \
a multitool are the optimal tools to use, and where to find the maintenance panel on most airlocks. Unfortunately, the poster does not mention any \
wire codes."
+ listing_name = "Safety - Airlock Maintenance"
/datum/poster/pol_10
icon_state="polposter9"
name = "orchid"
desc = "This poster appears strangely familiar, depicting the flower of a tree native to the planet Earth."
+ listing_name = "General - Orchid"
-// A new subset of poster datum for Security posters.
+/datum/poster/pol_11
+ icon_state="polposter10"
+ name = "firefighter pinup"
+ desc = "A buff firefighter with his shirt off. Maybe he's trying to raise awareness about fire safety, but you're a little distracted."
+ listing_name = "Pin-up - Firefighter"
+
+/datum/poster/pol_12
+ icon_state="polposter11"
+ name = "CHUCK"
+ desc = "With the budget Cyber Solutions 'Chuck' model work drone, your days of menial labour are numbered! \"With Cyber Solutions, there's always someone beside you, helping you. You know their face. You know my face.\""
+ listing_name = "Ad - Chuck"
+
+/datum/poster/pol_13
+ icon_state="polposter12"
+ name = "New You"
+ desc = "An ad for Kaleidoscope Cosmetics. \"The common factor in all your failed relationships is you. Fix it with affordable gene therapy!\""
+ listing_name = "Ad - Kaleidoscope"
+
+/datum/poster/pol_14
+ icon_state="polposter13"
+ name = "Take a Taxi"
+ desc = "An ad for the Major Bill's Transportation drone-operated taxi service operating in most major cities throughout Solar space."
+ listing_name = "Ad - Taxi"
+
+/datum/poster/pol_15
+ icon_state="polposter14"
+ name = "Adopt A Teshpet"
+ desc = "An ad for definitively racist virtual pet game, Teshpets."
+ listing_name = "Ad - Teshpets"
+
+/datum/poster/pol_16
+ icon_state="polposter15"
+ name = "Train and Fight"
+ desc = "This poster of sweaty, near-naked wrestlers is the most masculine thing you've ever seen. You can barely tell where one man ends and the other begins. Always seek manliness!"
+ listing_name = "Pinup - Wrestling"
+
+/datum/poster/nt_3
+ icon_state = "ntposter03"
+ name = "Mechatronic Safety"
+ desc = "This poster displays three cutting-edge gygaxes standing in line in front of a man in plain clothes.\
+ The poster's captions explain the importance of knowing how to operate a mechatronic vehicle safely, especially near other personnel.\
+ The image seems important."
+ listing_name = "Safety - Mech Operation"
+
+/datum/poster/nt_4
+ icon_state = "ntposter04"
+ name = "Beware Aetotheans"
+ desc = "This poster displays a distinctly hostile-looking red Promethean in a black coat. The fine-print around the edges warns the reader about the dangers posed by Almachi Prometheans."
+ listing_name = "Safety - Aetothean Danger"
+
+/datum/poster/nt_5
+ icon_state = "ntposter05"
+ name = "Promethean"
+ desc = "This poster displays a friendly-looking green Promethean in a labcoat. The fine-print around the edges talks about the benefits Prometheans give in laboratories."
+ listing_name = "Safety - Prometheans"
+
+/datum/poster/nt_6
+ icon_state = "ntposter06"
+ name = "NanoTrasen"
+ desc = "This poster showcases an NT emblem. There is writing in the ring around the inner points, probably some sort of slogan no one bothers to memorize."
+ listing_name = "Ad - NanoTrasen"
+
+/datum/poster/nt_7
+ icon_state = "ntposter07"
+ name = "SolGov"
+ desc = "This poster showcases an SCG emblem. The outer ring reads,\
+ \"NIL MORTALIBUS ARDUI EST\".\
+ Solar Confederate Government."
+ listing_name = "Political - SolGov"
+
+/datum/poster/nt_8
+ icon_state = "ntposter08"
+ name = "wildlife hazard"
+ desc = "This SifGov poster warns against attempting to kill a fully grown giant spider or other hostile life-form alone."
+ listing_name = "Safety - Spiders"
+
+// A new subset of poster datum for ONLY 'Internal' NanoTrasen posters, given to Security.
/datum/poster/nanotrasen
icon_state = "polposter1"
name = "Safety!"
desc = "A poster advising you to learn how to put on your internals at a moment's notice."
+ listing_name = "Safety - Internals"
/datum/poster/nanotrasen/pol_2
icon_state="polposter2"
name = "Safety!"
desc = "A blue and white colored poster. This one advises you to wear your safety goggles when handling chemicals."
+ listing_name = "Safety - Goggles"
/datum/poster/nanotrasen/pol_3
icon_state="polposter3"
name = "Safety!"
desc = "A safety poster instructing you to comply with the authorities, especially in an emergency."
+ listing_name = "Safety - Follow Instruction"
/datum/poster/nanotrasen/pol_4
icon_state="polposter4"
name = "Clean Hands Save Lives"
desc = "A safety poster reminding you to wash your hands."
+ listing_name = "Safety - Hand Washing"
/datum/poster/nanotrasen/pol_5
icon_state="polposter5"
name = "Help!"
desc = "This poster depicts a man helping another man get up."
+ listing_name = "Safety - Help Others"
/datum/poster/nanotrasen/pol_6
icon_state="polposter6"
name = "Walk!"
desc = "This poster depicts a man walking, presumably to encourage you not to run in the halls."
+ listing_name = "Safety - No Running"
/datum/poster/nanotrasen/pol_7
icon_state="polposter7"
name = "Place your signs!"
desc = "A safety poster reminding custodial staff to place wet floor signs where needed. This reminder's rarely heeded."
+ listing_name = "Safety - Wet Floor Signs"
/datum/poster/nanotrasen/pol_8
icon_state="polposter8"
name = "Safety!"
desc = "An advertisement / safety poster for EVA training and certification. Training is available at your local Central Command."
+ listing_name = "Safety - EVA Training"
/datum/poster/nanotrasen/pol_9
icon_state="poster10"
@@ -97,81 +191,97 @@
desc = "This poster appears to be reference material for maintenance personnel, instructing to always wear insulated gloves, that wirecutters and \
a multitool are the optimal tools to use, and where to find the maintenance panel on most airlocks. Unfortunately, the poster does not mention any \
wire codes."
+ listing_name = "Safety - Airlock Maintenance"
/datum/poster/nanotrasen/pol_10
icon_state="polposter9"
name = "orchid"
desc = "This poster suggests a feeling of peace. It depicts the flower of a tree native to the planet Earth."
+ listing_name = "General - Orchid"
/datum/poster/nanotrasen/bay_9
icon_state="bsposter9"
name = "Pinup Girl Amy"
desc = "This particular one is of Amy, the nymphomaniac urban legend of deep space. How this photograph came to be is not known."
+ listing_name = "Pin-up - Amy"
/datum/poster/nanotrasen/bay_21
icon_state="bsposter21"
name = "Join the Fuzz!"
desc = "It's a nice recruitment poster of a white haired Chinese woman that says; \"Big Guns, Hot Women, Good Times. Security. We get it done.\""
+ listing_name = "Recruitment - NanoTrasen Security - Pinup"
/datum/poster/nanotrasen/bay_22
icon_state="bsposter22"
name = "Looking for a career with excitement?"
desc = "A recruitment poster starring a dark haired woman with glasses and a purple shirt that has \"Got Brains? Got Talent? Not afraid of electric flying monsters that want to suck the soul out of you? Then Xenobiology could use someone like you!\" written on the bottom."
+ listing_name = "Recruitment - Xenobiology"
/datum/poster/nanotrasen/bay_23
icon_state="bsposter23"
name = "Safety first: because electricity doesn't wait!"
desc = "A safety poster starring a clueless looking redhead with frazzled hair. \"Every year, hundreds of NT employees expose themselves to electric shock. Play it safe. Avoid suspicious doors after electrical storms, and always wear protection when doing electric maintenance.\""
+ listing_name = "Safety - Electric Shock"
/datum/poster/nanotrasen/bay_24
icon_state="bsposter24"
name = "Responsible medbay habits, No #259"
desc = "A poster with a nervous looking geneticist on it states; \"Friends Tell Friends They're Clones. It can cause severe and irreparable emotional trauma if a person is not properly informed of their recent demise. Always follow your contractual obligation and inform them of their recent rejuvenation.\""
+ listing_name = "Medical - Clone Disclosure"
/datum/poster/nanotrasen/bay_25
icon_state="bsposter25"
name = "Irresponsible medbay habits, No #2"
desc = "This is a safety poster starring a perverted looking naked doctor. \"Sexual harassment is never okay. REPORT any acts of sexual deviance or harassment that disrupt a healthy working environment.\""
+ listing_name = "Medical - Sexual Harassment"
/datum/poster/nanotrasen/bay_49
icon_state="bsposter49"
name = "Engineering recruitment"
desc = "This is a poster showing an engineer relaxing by a computer, the text states \"Living the life! Join Engineering today!\""
+ listing_name = "Recruitment - Engineering"
/datum/poster/nanotrasen/bay_52
icon_state="bsposter52"
name = "fire safety poster"
desc = "This is a poster reminding you of what you should do if you are on fire, or if you are at a dance party."
+ listing_name = "Safety - Stop Drop And Roll"
/datum/poster/nanotrasen/bay_53
icon_state="bsposter53"
name = "fire extinguisher poster"
desc = "This is a poster reminding you of what you should use to put out a fire."
+ listing_name = "Safety - Extinguishers"
/datum/poster/nanotrasen/bay_54
icon_state="bsposter54"
name = "firefighter poster"
desc = "This is a poster of a particularly stern looking firefighter. The caption reads, \"Only you can prevent space fires.\""
+ listing_name = "Safety - Prevent Fires"
/datum/poster/nanotrasen/bay_57
icon_state="bsposter57"
name = "space carp warning poster"
desc = "This poster tells of the dangers of space carp infestations."
+ listing_name = "Safety - Carp"
/datum/poster/nanotrasen/bay_58
icon_state="bsposter58"
name = "space carp information poster"
desc = "This poster showcases an old spacer saying on the dangers of migrant space carp."
+ listing_name = "Safety - Carp Information"
/datum/poster/nanotrasen/nt_1
icon_state = "ntposter01"
name = "Security recruitment"
desc = "This poster showcases an NT security guard in an excited pose, with a small blurb about the importance of Security."
+ listing_name = "Recruitment - NanoTrasen Security - Importance"
/datum/poster/nanotrasen/nt_2
icon_state = "ntposter02"
name = "Security recruitment"
desc = "This poster showcases an NT security guard in an excited pose, with a small blurb about Security Employee benefits."
+ listing_name = "Recruitment - NanoTrasen Security - Benefits"
/datum/poster/nanotrasen/nt_3
icon_state = "ntposter03"
@@ -179,6 +289,7 @@
desc = "This poster displays three cutting-edge gygaxes standing in line in front of a man in plain clothes.\
The poster's captions explain the importance of knowing how to operate a mechatronic vehicle safely, especially near other personnel.\
The image seems important."
+ listing_name = "Safety - Mech Operation"
//VOREStation Removal Start TFF 17/12/19 - lore not used in our station's own lore.
/*
@@ -186,6 +297,7 @@
icon_state = "ntposter04"
name = "Beware Aetotheans"
desc = "This poster displays a distinctly hostile-looking red Promethean in a black coat. The fine-print around the edges warns the reader about the dangers posed by Almachi Prometheans."
+ listing_name = "Safety - Aetothean Danger"
*/
//VOREstation Removal End
@@ -193,11 +305,13 @@
icon_state = "ntposter05"
name = "Promethean"
desc = "This poster displays a friendly-looking green Promethean in a labcoat. The fine-print around the edges talks about the benefits Prometheans give in laboratories."
+ listing_name = "Safety - Prometheans"
/datum/poster/nanotrasen/nt_6
icon_state = "ntposter06"
name = "NanoTrasen"
desc = "This poster showcases an NT emblem. There is writing in the ring around the inner points, probably some sort of slogan no one bothers to memorize."
+ listing_name = "Ad - NanoTrasen"
/datum/poster/nanotrasen/nt_7
icon_state = "ntposter07"
@@ -205,13 +319,16 @@
desc = "This poster showcases an SCG emblem. The outer ring reads,\
\"NIL MORTALIBUS ARDUI EST\".\
Solar Confederate Government."
+ listing_name = "Political - SolGov"
/datum/poster/nanotrasen/nt_8
icon_state = "ntposter08"
name = "wildlife hazard"
desc = "This poster warns against attempting to kill a fully grown giant spider or other hostile life-form alone."
+ listing_name = "Safety - Spiders"
/datum/poster/nanotrasen/nt_9
icon_state = "ntposter09"
name = "Regulations and You"
- desc = "This poster showcases an NT security guard reading from her PDA. The blurb advocates for the reader to keep corporate regulations in mind at all times, as an emergency can occur at any time."
+ desc = "This SifGov poster showcases an NT security guard reading from her PDA. The blurb advocates for the reader to keep corporate regulations in mind at all times, as an emergency can occur at any time."
+ listing_name = "Safety - Corporate Regulations"
diff --git a/code/game/objects/items/devices/communicator/UI_tgui.dm b/code/game/objects/items/devices/communicator/UI_tgui.dm
index 8dabf77f42..4745a02488 100644
--- a/code/game/objects/items/devices/communicator/UI_tgui.dm
+++ b/code/game/objects/items/devices/communicator/UI_tgui.dm
@@ -1,3 +1,103 @@
+// Etc UI-only vars
+/obj/item/device/communicator
+ // Stuff for moving cameras
+ var/turf/last_camera_turf
+ // Stuff needed to render the map
+ var/map_name
+ var/obj/screen/map_view/cam_screen
+ var/list/cam_plane_masters
+ var/obj/screen/background/cam_background
+ var/obj/screen/skybox/local_skybox
+
+// Proc: setup_tgui_camera()
+// Parameters: None
+// Description: This sets up all of the variables above to handle in-UI map windows.
+/obj/item/device/communicator/proc/setup_tgui_camera()
+ map_name = "communicator_[REF(src)]_map"
+
+ // Initialize map objects
+ cam_screen = new
+ cam_screen.name = "screen"
+ cam_screen.assigned_map = map_name
+ cam_screen.del_on_map_removal = FALSE
+ cam_screen.screen_loc = "[map_name]:1,1"
+
+ cam_plane_masters = get_tgui_plane_masters()
+
+ for(var/plane in cam_plane_masters)
+ var/obj/screen/instance = plane
+ instance.assigned_map = map_name
+ instance.del_on_map_removal = FALSE
+ instance.screen_loc = "[map_name]:CENTER"
+
+ local_skybox = new()
+ local_skybox.assigned_map = map_name
+ local_skybox.del_on_map_removal = FALSE
+ local_skybox.screen_loc = "[map_name]:CENTER,CENTER"
+ cam_plane_masters += local_skybox
+
+ cam_background = new
+ cam_background.assigned_map = map_name
+ cam_background.del_on_map_removal = FALSE
+
+// Proc: update_active_camera_screen()
+// Parameters: None
+// Description: This refreshes the camera location
+/obj/item/device/communicator/proc/update_active_camera_screen()
+ if(!video_source?.can_use())
+ show_static()
+ return
+
+ var/newturf = get_turf(video_source)
+ if(!is_on_same_plane_or_station(get_z(newturf), get_z(src)))
+ show_static()
+ return
+
+ var/obj/item/device/communicator/communicator = video_source.loc
+ if(istype(communicator))
+ if(communicator.selfie_mode)
+ var/mob/target = get(communicator, /mob)
+ if(istype(target))
+ cam_screen.vis_contents = list(target)
+ else
+ cam_screen.vis_contents = list(communicator)
+ cam_background.fill_rect(1, 1, 1, 1)
+ cam_background.icon_state = "clear"
+ local_skybox.cut_overlays()
+ return
+
+ // If we're not forcing an update for some reason and the cameras are in the same location,
+ // we don't need to update anything.
+ if(last_camera_turf == newturf)
+ return
+
+ // We get a new turf in case they've moved in the last half decisecond (it's BYOND, it might happen)
+ last_camera_turf = get_turf(video_source)
+
+ if(!is_on_same_plane_or_station(get_z(last_camera_turf), get_z(src)))
+ show_static()
+ return
+
+ var/list/visible_turfs = list()
+ var/list/visible_things = view(video_range, last_camera_turf)
+ for(var/turf/visible_turf in visible_things)
+ visible_turfs += visible_turf
+
+ cam_screen.vis_contents = visible_turfs
+ cam_background.icon_state = "clear"
+ cam_background.fill_rect(1, 1, (video_range * 2), (video_range * 2))
+
+ local_skybox.cut_overlays()
+ local_skybox.add_overlay(SSskybox.get_skybox(get_z(last_camera_turf)))
+ local_skybox.scale_to_view(video_range * 2)
+ local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - last_camera_turf.x, (world.maxy>>1) - last_camera_turf.y)
+
+/obj/item/device/communicator/proc/show_static()
+ cam_screen.vis_contents.Cut()
+ cam_background.icon_state = "scanline2"
+ cam_background.fill_rect(1, 1, (video_range * 2), (video_range * 2))
+ local_skybox.cut_overlays()
+
// Proc: tgui_state()
// Parameters: User
// Description: This tells TGUI to only allow us to be interacted with while in a mob inventory.
@@ -9,7 +109,15 @@
// Description: This proc handles opening the UI. It's basically just a standard stub.
/obj/item/device/communicator/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui, datum/tgui_state/custom_state)
ui = SStgui.try_update_ui(user, src, ui)
+ // Update the camera every SStgui tick in case it moves
+ update_active_camera_screen()
if(!ui)
+ // Register map objects
+ user.client.register_map_obj(cam_screen)
+ for(var/plane in cam_plane_masters)
+ user.client.register_map_obj(plane)
+ user.client.register_map_obj(cam_background)
+ // Setup UI
ui = new(user, src, "Communicator", name)
if(custom_state)
ui.set_state(custom_state)
@@ -188,6 +296,7 @@
data["target_feed"] = data["feeds"][newsfeed_channel]
else
data["target_feed"] = null
+ data["selfie_mode"] = selfie_mode
return data
@@ -200,6 +309,7 @@
if(data_core)
data_core.get_manifest_list()
data["manifest"] = PDA_Manifest
+ data["mapRef"] = map_name
return data
// Proc: tgui-act()
@@ -231,6 +341,9 @@
if("toggle_ringer")
ringer = !ringer
+ if("selfie_mode")
+ selfie_mode = !selfie_mode
+
if("add_hex")
var/hex = params["add_hex"]
add_to_EPv2(hex)
@@ -324,3 +437,4 @@
if("newsfeed")
newsfeed_channel = text2num(params["newsfeed"])
+#undef DEFAULT_MAP_SIZE
\ No newline at end of file
diff --git a/code/game/objects/items/devices/communicator/communicator.dm b/code/game/objects/items/devices/communicator/communicator.dm
index 358ef3b61b..46c89b0683 100644
--- a/code/game/objects/items/devices/communicator/communicator.dm
+++ b/code/game/objects/items/devices/communicator/communicator.dm
@@ -28,7 +28,7 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
origin_tech = list(TECH_ENGINEERING = 2, TECH_MAGNET = 2, TECH_BLUESPACE = 2, TECH_DATA = 2)
matter = list(DEFAULT_WALL_MATERIAL = 30,"glass" = 10)
- var/video_range = 4
+ var/video_range = 3
var/obj/machinery/camera/communicator/video_source // Their camera
var/obj/machinery/camera/communicator/camera // Our camera
@@ -72,6 +72,9 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
var/update_ticks = 0
var/newsfeed_channel = 0
+ // If you turn this on, it changes the way communicator video works. User configurable option.
+ var/selfie_mode = FALSE
+
// Proc: New()
// Parameters: None
// Description: Adds the new communicator to the global list of all communicators, sorts the list, obtains a reference to the Exonet node, then tries to
@@ -86,6 +89,8 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
camera.name = "[src] #[rand(100,999)]"
camera.c_tag = camera.name
+ setup_tgui_camera()
+
//This is a pretty terrible way of doing this.
addtimer(CALLBACK(src, .proc/register_to_holder), 5 SECONDS)
@@ -124,9 +129,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
/obj/item/device/communicator/examine(mob/user)
. = ..()
- if(Adjacent(user) && video_source)
- . += "It looks like it's on a video call: \[view\]"
-
for(var/mob/living/voice/voice in contents)
. += "On the screen, you can see a image feed of [voice]."
@@ -142,17 +144,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
else
. += "The device doesn't appear to be transmitting any data."
-// Proc: Topic()
-// Parameters: href, href_list - Data from a link
-// Description: Used by the above examine.
-/obj/item/device/communicator/Topic(href, href_list)
- if(..())
- return 1
-
- if(href_list["watchvideo"])
- if(video_source)
- watch_video(usr)
-
// Proc: emp_act()
// Parameters: None
// Description: Drops all calls when EMPed, so the holder can then get murdered by the antagonist.
@@ -234,8 +225,6 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
alert_called = 0
update_icon()
tgui_interact(user)
- if(video_source)
- watch_video(user)
// Proc: MouseDrop()
//Same thing PDAs do
@@ -316,6 +305,11 @@ var/global/list/obj/item/device/communicator/all_communicators = list()
QDEL_NULL(camera)
QDEL_NULL(exonet)
+ last_camera_turf = null
+ qdel(cam_screen)
+ QDEL_LIST(cam_plane_masters)
+ qdel(cam_background)
+
return ..()
// Proc: update_icon()
diff --git a/code/game/objects/items/devices/communicator/phone.dm b/code/game/objects/items/devices/communicator/phone.dm
index 7075dbe958..bb25dce953 100644
--- a/code/game/objects/items/devices/communicator/phone.dm
+++ b/code/game/objects/items/devices/communicator/phone.dm
@@ -332,12 +332,15 @@
if(video_source) //Already in a video
to_chat(user, "You are already connected to a video call!")
+ return
if(user.blinded) //User is blinded
to_chat(user, "You cannot see well enough to do that!")
+ return
if(!(src in comm.communicating) || !comm.camera) //You called someone with a broken communicator or one that's fake or yourself or something
to_chat(user, "[bicon(src)]ERROR: Video failed. Either bandwidth is too low, or the other communicator is malfunctioning.")
+ return
to_chat(user, "[bicon(src)] Attempting to start video over existing call.")
sleep(30)
@@ -345,42 +348,16 @@
video_source = comm.camera
comm.visible_message("[bicon(src)] New video connection from [comm].")
- watch_video(user)
+ update_active_camera_screen()
+ GLOB.moved_event.register(video_source, src, .proc/update_active_camera_screen)
update_icon()
-// Proc: watch_video()
-// Parameters: user - the mob doing the viewing of video
-// Description: Moves a mob's eye to the far end for the duration of viewing the far end
-/obj/item/device/communicator/proc/watch_video(mob/user)
- if(!Adjacent(user) || !video_source) return
- user.set_machine(video_source)
- user.reset_view(video_source)
- to_chat(user, "Now viewing video session. To leave camera view, close the communicator window OR: OOC -> Cancel Camera View")
- to_chat(user, "To return to an active video session, use the communicator in your hand.")
- spawn(0)
- while(user.machine == video_source && Adjacent(user))
- var/turf/T = get_turf(video_source)
- if(!T || !is_on_same_plane_or_station(T.z, user.z) || !video_source.can_use())
- to_chat(user, "The screen bursts into static, then goes black.")
- video_cleanup(user)
- return
- sleep(10)
-
- video_cleanup(user)
-
-// Proc: video_cleanup()
-// Parameters: user - the mob who doesn't want to see video anymore
-// Description: Cleans up mob's client when they stop watching a video
-/obj/item/device/communicator/proc/video_cleanup(mob/user)
- if(!user) return
-
- user.reset_view(null)
- user.unset_machine()
-
// Proc: end_video()
// Parameters: reason - the text reason to print for why it ended
// Description: Ends the video call by clearing video_source
/obj/item/device/communicator/proc/end_video(var/reason)
+ GLOB.moved_event.unregister(video_source, src, .proc/update_active_camera_screen)
+ show_static()
video_source = null
. = "[bicon(src)] [reason ? reason : "Video session ended"]."
diff --git a/code/game/objects/items/devices/radio/radio.dm b/code/game/objects/items/devices/radio/radio.dm
index d2511038dc..366853a7bf 100644
--- a/code/game/objects/items/devices/radio/radio.dm
+++ b/code/game/objects/items/devices/radio/radio.dm
@@ -297,7 +297,8 @@ var/global/list/default_medbay_channels = list(
playsound(src, "button", 10)
GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer)
-/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels) //BS12 EDIT
+
+/obj/item/device/radio/proc/autosay(var/message, var/from, var/channel, var/list/zlevels)
if(!GLOB.autospeaker)
return
var/datum/radio_frequency/connection = null
@@ -308,7 +309,7 @@ GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer)
else
connection = radio_connection
channel = null
- if (!istype(connection))
+ if(!istype(connection))
return
if(!LAZYLEN(zlevels))
@@ -507,7 +508,7 @@ GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer)
else if(adhoc_fallback) //Less huzzah, we have to fallback
to_chat(loc, "\The [src] pings as it falls back to local radio transmission.")
subspace_transmission = FALSE
-
+
else //Oh well
return FALSE
@@ -594,7 +595,7 @@ GLOBAL_DATUM(autospeaker, /mob/living/silicon/ai/announcer)
/obj/item/device/radio/examine(mob/user)
. = ..()
-
+
if((in_range(src, user) || loc == user))
if(b_stat)
. += "\The [src] can be attached and modified!"
diff --git a/code/game/objects/items/weapons/RPD_vr.dm b/code/game/objects/items/weapons/RPD_vr.dm
index d4650eb16a..d15236e7e7 100644
--- a/code/game/objects/items/weapons/RPD_vr.dm
+++ b/code/game/objects/items/weapons/RPD_vr.dm
@@ -24,6 +24,7 @@
throw_range = 5
w_class = ITEMSIZE_NORMAL
matter = list(MAT_STEEL = 50000, MAT_GLASS = 25000)
+ origin_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2)
var/datum/effect/effect/system/spark_spread/spark_system
var/p_dir = NORTH // Next pipe will be built with this dir
var/p_flipped = FALSE // If the next pipe should be built flipped
diff --git a/code/game/objects/items/weapons/gift_wrappaper.dm b/code/game/objects/items/weapons/gift_wrappaper.dm
index f828b97b23..cd48a4a78a 100644
--- a/code/game/objects/items/weapons/gift_wrappaper.dm
+++ b/code/game/objects/items/weapons/gift_wrappaper.dm
@@ -77,7 +77,7 @@
/obj/item/weapon/lipstick/random,
/obj/item/weapon/grenade/smokebomb,
/obj/item/weapon/corncob,
- /obj/item/weapon/contraband/poster,
+ /obj/item/weapon/contraband/poster/custom,
/obj/item/weapon/book/manual/barman_recipes,
/obj/item/weapon/book/manual/chef_recipes,
/obj/item/weapon/bikehorn,
diff --git a/code/game/objects/items/weapons/storage/egg_vr.dm b/code/game/objects/items/weapons/storage/egg_vr.dm
index 0ea3b673e1..f7849040db 100644
--- a/code/game/objects/items/weapons/storage/egg_vr.dm
+++ b/code/game/objects/items/weapons/storage/egg_vr.dm
@@ -113,24 +113,44 @@
/obj/item/weapon/storage/vore_egg/synthetic
name = "synthetic egg"
- desc = "Can robots lay eggs?"
+ desc = "Smells like Easter morning."
icon_state = "egg_synthetic"
-/obj/item/weapon/storage/vore_egg/badrecipe
- name = "Burned mess"
- desc = "Someone didn't cook this egg quite right..."
- icon_state = "egg_badrecipe"
-
/obj/item/weapon/storage/vore_egg/escapepod
name = "small escape pod"
desc = "Someone left in a hurry."
icon_state = "egg_escapepod"
+/obj/item/weapon/storage/vore_egg/floppy
+ name = "blue space floppy disc"
+ desc = "Probably shouldn't copy THIS floppy."
+ icon_state = "egg_floppy"
+
+/obj/item/weapon/storage/vore_egg/cd
+ name = "blue space cd"
+ desc = "What could even be on this?!"
+ icon_state = "egg_cd"
+
+/obj/item/weapon/storage/vore_egg/file
+ name = "blue space file"
+ desc = "Gotta wonder how much is compressed in there."
+ icon_state = "egg_file"
+
+/obj/item/weapon/storage/vore_egg/badrecipe
+ name = "Burned mess"
+ desc = "Someone didn't cook this egg quite right..."
+ icon_state = "egg_badrecipe"
+
/obj/item/weapon/storage/vore_egg/cocoon
name = "web cocoon"
desc = "It straight up smells like spiders in here."
icon_state = "egg_cocoon"
+/obj/item/weapon/storage/vore_egg/honeycomb
+ name = "honeycomb"
+ desc = "Smells delicious!"
+ icon_state = "egg_honeycomb"
+
/obj/item/weapon/storage/vore_egg/bugcocoon
name = "bug cocoon"
desc = "Metamorphosis!"
diff --git a/code/game/objects/random/misc.dm b/code/game/objects/random/misc.dm
index 13568c27e6..fdc84aa3c4 100644
--- a/code/game/objects/random/misc.dm
+++ b/code/game/objects/random/misc.dm
@@ -262,7 +262,7 @@
prob(8);/obj/item/weapon/haircomb,
prob(4);/obj/item/weapon/storage/pill_bottle/happy,
prob(4);/obj/item/weapon/storage/pill_bottle/zoom,
- prob(10);/obj/item/weapon/contraband/poster,
+ prob(10);/obj/item/weapon/contraband/poster/custom,
prob(4);/obj/item/weapon/material/butterfly,
prob(6);/obj/item/weapon/material/butterflyblade,
prob(6);/obj/item/weapon/material/butterflyhandle,
diff --git a/code/game/objects/structures/loot_piles.dm b/code/game/objects/structures/loot_piles.dm
index 57c9832fde..12f6f45282 100644
--- a/code/game/objects/structures/loot_piles.dm
+++ b/code/game/objects/structures/loot_piles.dm
@@ -254,8 +254,8 @@ Loot piles can be depleted, if loot_depleted is turned on. Note that players wh
/obj/item/stack/material/steel{amount = 5},
/obj/item/stack/material/cardboard{amount = 5},
/obj/item/weapon/contraband/poster,
+ /obj/item/weapon/contraband/poster/custom,
/obj/item/weapon/material/wirerod,
- /obj/item/weapon/contraband/poster,
/obj/item/weapon/newspaper,
/obj/item/weapon/paper/crumpled,
/obj/item/weapon/paper/crumpled/bloody
diff --git a/code/game/turfs/flooring/flooring_premade.dm b/code/game/turfs/flooring/flooring_premade.dm
index 5185a0e253..a85c143709 100644
--- a/code/game/turfs/flooring/flooring_premade.dm
+++ b/code/game/turfs/flooring/flooring_premade.dm
@@ -438,23 +438,20 @@
movement_cost = 0
#define FOOTSTEP_SPRITE_AMT 2
-/turf/snow/Entered(atom/A)
- if(isliving(A) && !istype(A, /mob/living/simple_mob))
- var/mdir = "[A.dir]"
- if(crossed_dirs[mdir])
- crossed_dirs[mdir] = min(crossed_dirs[mdir] + 1, FOOTSTEP_SPRITE_AMT)
- else
- crossed_dirs[mdir] = 1
- update_icon()
+// TODO: Move foortprints to a datum-component signal so they can actually be applied to other turf types, like sand, or mud
+/turf/simulated/floor/snow/Entered(atom/A)
+ if(isliving(A))
+ var/mob/living/L = A
+ if(L.hovering) // Flying things shouldn't make footprints.
+ return ..()
+ var/mdir = "[A.dir]"
+ crossed_dirs[mdir] = 1
+ update_icon()
+ . = ..()
- . = ..()
-
-/turf/snow/update_icon()
- cut_overlays()
- for(var/d in crossed_dirs)
- var/amt = crossed_dirs[d]
-
- for(var/i in 1 to amt)
- add_overlay(image(icon, "footprint[i]", text2num(d)))
+/turf/simulated/floor/snow/update_icon()
+ ..()
+ for(var/d in crossed_dirs)
+ add_overlay(image(icon = 'icons/turf/outdoors.dmi', icon_state = "snow_footprints", dir = text2num(d)))
diff --git a/code/game/verbs/character_directory.dm b/code/game/verbs/character_directory.dm
index 13d0120123..2c70b54ba7 100644
--- a/code/game/verbs/character_directory.dm
+++ b/code/game/verbs/character_directory.dm
@@ -32,6 +32,7 @@ GLOBAL_DATUM(character_directory, /datum/character_directory)
data["personalVisibility"] = user?.client?.prefs?.show_in_directory
data["personalTag"] = user?.client?.prefs?.directory_tag
+ data["personalErpTag"] = user?.client?.prefs?.directory_erptag
return data
@@ -50,6 +51,7 @@ GLOBAL_DATUM(character_directory, /datum/character_directory)
var/ooc_notes = null
var/flavor_text = null
var/tag = C.prefs.directory_tag
+ var/erptag = C.prefs.directory_erptag
var/character_ad = C.prefs.directory_ad
if(ishuman(C.mob))
@@ -85,6 +87,7 @@ GLOBAL_DATUM(character_directory, /datum/character_directory)
"name" = name,
"ooc_notes" = ooc_notes,
"tag" = tag,
+ "erptag" = erptag,
"character_ad" = character_ad,
"flavor_text" = flavor_text,
)))
@@ -109,11 +112,17 @@ GLOBAL_DATUM(character_directory, /datum/character_directory)
update_tgui_static_data(usr, ui)
return TRUE
if("setTag")
- var/list/new_tag = input(usr, "Pick a new tag for the character directory", "Character Tag", usr?.client?.prefs?.directory_tag) as null|anything in GLOB.char_directory_tags
+ var/list/new_tag = input(usr, "Pick a new Vore tag for the character directory", "Character Tag", usr?.client?.prefs?.directory_tag) as null|anything in GLOB.char_directory_tags
if(!new_tag)
return
usr?.client?.prefs?.directory_tag = new_tag
return TRUE
+ if("setErpTag")
+ var/list/new_erptag = input(usr, "Pick a new ERP tag for the character directory", "Character ERP Tag", usr?.client?.prefs?.directory_erptag) as null|anything in GLOB.char_directory_erptags
+ if(!new_erptag)
+ return
+ usr?.client?.prefs?.directory_erptag = new_erptag
+ return TRUE
if("setVisible")
usr?.client?.prefs?.show_in_directory = !usr?.client?.prefs?.show_in_directory
to_chat(usr, "You are now [usr.client.prefs.show_in_directory ? "shown" : "not shown"] in the directory.")
diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm
index 11a140a91b..49c14a3550 100644
--- a/code/modules/admin/admin.dm
+++ b/code/modules/admin/admin.dm
@@ -946,7 +946,7 @@ var/datum/announcement/minor/admin_min_announcer = new
log_admin("[key_name(usr)] toggled persistence to [config.persistence_disabled ? "Off" : "On"].")
world.update_status()
feedback_add_details("admin_verb","TPD") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
-
+
/datum/admins/proc/togglemaploadpersistence()
set category = "Server"
set desc="Whether mapload persistent data will be saved from now on."
diff --git a/code/modules/admin/admin_verb_lists.dm b/code/modules/admin/admin_verb_lists.dm
index 5fba4a679a..f9dd64ccf3 100644
--- a/code/modules/admin/admin_verb_lists.dm
+++ b/code/modules/admin/admin_verb_lists.dm
@@ -60,6 +60,7 @@ var/list/admin_verbs_admin = list(
/client/proc/check_ai_laws, //shows AI and borg laws,
/client/proc/rename_silicon, //properly renames silicons,
/client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. ,
+ /client/proc/shuttle_panel, // Allows controlling all shuttles remotely.
/client/proc/check_antagonists,
/client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others,
/client/proc/dsay, //talk in deadchat using our ckey/fakekey,
@@ -445,6 +446,7 @@ var/list/admin_verbs_event_manager = list(
/client/proc/check_ai_laws, //shows AI and borg laws,
/client/proc/rename_silicon, //properly renames silicons,
/client/proc/manage_silicon_laws, // Allows viewing and editing silicon laws. ,
+ /client/proc/shuttle_panel, // Allows controlling all shuttles remotely.
/client/proc/check_antagonists,
/client/proc/admin_memo, //admin memo system. show/delete/write. +SERVER needed to delete admin memos of others,
/client/proc/dsay, //talk in deadchat using our ckey/fakekey,
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index 6635b07061..d8deb0dce6 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -437,6 +437,17 @@
set_security_level(sec_level)
log_admin("[key_name(usr)] changed the security level to code [sec_level].")
+/client/proc/shuttle_panel()
+ set name = "Shuttle Control Panel"
+ set category = "Admin"
+
+ if(!check_rights(R_ADMIN | R_EVENT))
+ return
+
+ var/datum/tgui_module/admin_shuttle_controller/A = new(src)
+ A.tgui_interact(usr)
+ log_and_message_admins("has opened the shuttle panel.")
+ feedback_add_details("admin_verb","SHCP")
//---- bs12 verbs ----
@@ -525,4 +536,4 @@
T.spell_list += new S
feedback_add_details("admin_verb","GS") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
log_admin("[key_name(usr)] gave [key_name(T)] the spell [S].")
- message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1)
+ message_admins("[key_name_admin(usr)] gave [key_name(T)] the spell [S].", 1)
\ No newline at end of file
diff --git a/code/modules/admin/verbs/change_appearance.dm b/code/modules/admin/verbs/change_appearance.dm
index 2f7166a2c0..94c5170347 100644
--- a/code/modules/admin/verbs/change_appearance.dm
+++ b/code/modules/admin/verbs/change_appearance.dm
@@ -92,15 +92,7 @@
var/new_gender = alert(usr, "Please select gender.", "Character Generation", "Male", "Female", "Neuter")
if (new_gender)
- if(new_gender == "Male")
- M.gender = MALE
- M.dna.SetUIState(DNA_UI_GENDER, FALSE)
- else if (new_gender == "Female")
- M.gender = FEMALE
- M.dna.SetUIState(DNA_UI_GENDER, TRUE)
- else
- M.gender = NEUTER
- M.dna.SetUIState(DNA_UI_GENDER, FALSE)
+ M.set_gender(new_gender)
M.update_dna(M)
M.update_hair(FALSE)
diff --git a/code/modules/client/preference_setup/general/03_body.dm b/code/modules/client/preference_setup/general/03_body.dm
index 9956d0fade..7cbc67735e 100644
--- a/code/modules/client/preference_setup/general/03_body.dm
+++ b/code/modules/client/preference_setup/general/03_body.dm
@@ -150,6 +150,28 @@ var/global/list/valid_bloodtypes = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O
character.g_synth = pref.g_synth
character.b_synth = pref.b_synth
character.synth_markings = pref.synth_markings
+ character.ear_style = ear_styles_list[pref.ear_style]
+ character.r_ears = pref.r_ears
+ character.b_ears = pref.b_ears
+ character.g_ears = pref.g_ears
+ character.r_ears2 = pref.r_ears2
+ character.b_ears2 = pref.b_ears2
+ character.g_ears2 = pref.g_ears2
+ character.tail_style = tail_styles_list[pref.tail_style]
+ character.r_tail = pref.r_tail
+ character.b_tail = pref.b_tail
+ character.g_tail = pref.g_tail
+ character.r_tail2 = pref.r_tail2
+ character.b_tail2 = pref.b_tail2
+ character.g_tail2 = pref.g_tail2
+ character.wing_style = wing_styles_list[pref.wing_style]
+ character.r_wing = pref.r_wing
+ character.b_wing = pref.b_wing
+ character.g_wing = pref.g_wing
+ character.r_wing2 = pref.r_wing2
+ character.b_wing2 = pref.b_wing2
+ character.g_wing2 = pref.g_wing2
+ character.set_gender( pref.biological_gender)
if(pref.species == "Grey")//YWadd START
character.wingdings = pref.wingdings
diff --git a/code/modules/client/preference_setup/loadout/loadout_head.dm b/code/modules/client/preference_setup/loadout/loadout_head.dm
index 14f78b6e91..31bd79b6b9 100644
--- a/code/modules/client/preference_setup/loadout/loadout_head.dm
+++ b/code/modules/client/preference_setup/loadout/loadout_head.dm
@@ -88,6 +88,11 @@
display_name = "cap, grey"
path = /obj/item/clothing/head/soft/grey
+/datum/gear/head/cap/med
+ display_name = "cap, medical (Medical)"
+ path = /obj/item/clothing/head/soft/med
+ allowed_roles = list("Chief Medical Officer","Medical Doctor","Chemist","Psychiatrist","Paramedic","Search and Rescue")
+
/datum/gear/head/cap/orange
display_name = "cap, orange"
path = /obj/item/clothing/head/soft/orange
diff --git a/code/modules/client/preference_setup/vore/09_misc.dm b/code/modules/client/preference_setup/vore/09_misc.dm
index 7f868e7286..1396b4570f 100644
--- a/code/modules/client/preference_setup/vore/09_misc.dm
+++ b/code/modules/client/preference_setup/vore/09_misc.dm
@@ -7,12 +7,14 @@
/datum/category_item/player_setup_item/vore/misc/load_character(var/savefile/S)
S["show_in_directory"] >> pref.show_in_directory
S["directory_tag"] >> pref.directory_tag
+ S["directory_erptag"] >> pref.directory_erptag
S["directory_ad"] >> pref.directory_ad
S["sensorpref"] >> pref.sensorpref //TFF 5/8/19 - add sensor pref setting to load after saved
/datum/category_item/player_setup_item/vore/misc/save_character(var/savefile/S)
S["show_in_directory"] << pref.show_in_directory
S["directory_tag"] << pref.directory_tag
+ S["directory_erptag"] << pref.directory_erptag
S["directory_ad"] << pref.directory_ad
S["sensorpref"] << pref.sensorpref //TFF 5/8/19 - add sensor pref setting to be saveable
@@ -25,12 +27,14 @@
/datum/category_item/player_setup_item/vore/misc/sanitize_character()
pref.show_in_directory = sanitize_integer(pref.show_in_directory, 0, 1, initial(pref.show_in_directory))
pref.directory_tag = sanitize_inlist(pref.directory_tag, GLOB.char_directory_tags, initial(pref.directory_tag))
+ pref.directory_erptag = sanitize_inlist(pref.directory_erptag, GLOB.char_directory_erptags, initial(pref.directory_erptag))
pref.sensorpref = sanitize_integer(pref.sensorpref, 1, sensorpreflist.len, initial(pref.sensorpref)) //TFF - 5/8/19 - add santisation for sensor prefs
/datum/category_item/player_setup_item/vore/misc/content(var/mob/user)
. += " "
. += "Appear in Character Directory:[pref.show_in_directory ? "Yes" : "No"] "
- . += "Character Directory Tag:[pref.directory_tag] "
+ . += "Character Directory Vore Tag:[pref.directory_tag] "
+ . += "Character Directory ERP Tag:[pref.directory_erptag] "
. += "Character Directory Advertisement:Set Directory Ad "
. += "Suit Sensors Preference:[sensorpreflist[pref.sensorpref]] " //TFF 5/8/19 - Allow selection of sensor settings from off, binary, vitals, tracking, or random
@@ -39,11 +43,17 @@
pref.show_in_directory = pref.show_in_directory ? 0 : 1;
return TOPIC_REFRESH
else if(href_list["directory_tag"])
- var/new_tag = input(user, "Pick a new tag for the character directory", "Character Tag", pref.directory_tag) as null|anything in GLOB.char_directory_tags
+ var/new_tag = input(user, "Pick a new Vore tag for the character directory", "Character Vore Tag", pref.directory_tag) as null|anything in GLOB.char_directory_tags
if(!new_tag)
return
pref.directory_tag = new_tag
return TOPIC_REFRESH
+ else if(href_list["directory_erptag"])
+ var/new_erptag = input(user, "Pick a new ERP tag for the character directory", "Character ERP Tag", pref.directory_erptag) as null|anything in GLOB.char_directory_erptags
+ if(!new_erptag)
+ return
+ pref.directory_erptag = new_erptag
+ return TOPIC_REFRESH
else if(href_list["directory_ad"])
var/msg = sanitize(input(user,"Write your advertisement here!", "Flavor Text", html_decode(pref.directory_ad)) as message, extra = 0) //VOREStation Edit: separating out OOC notes
pref.directory_ad = msg
diff --git a/code/modules/client/preferences_vr.dm b/code/modules/client/preferences_vr.dm
index 137e7af82c..737ddddfe1 100644
--- a/code/modules/client/preferences_vr.dm
+++ b/code/modules/client/preferences_vr.dm
@@ -1,6 +1,7 @@
/datum/preferences
var/show_in_directory = 1 //Show in Character Directory
var/directory_tag = "Unset" //Sorting tag to use in character directory
+ var/directory_erptag = "No ERP" //ditto, but for non-vore scenes
var/directory_ad = "" //Advertisement stuff to show in character directory.
var/sensorpref = 5 //Set character's suit sensor level
var/job_talon_high = 0
diff --git a/code/modules/clothing/head/soft_caps.dm b/code/modules/clothing/head/soft_caps.dm
index 5bcec8a56e..c3bdad4e4e 100644
--- a/code/modules/clothing/head/soft_caps.dm
+++ b/code/modules/clothing/head/soft_caps.dm
@@ -99,3 +99,9 @@
desc = "It's a ballcap bearing the colors of Major Bill's Shipping."
icon_state = "mbillsoft"
catalogue_data = list(/datum/category_item/catalogue/information/organization/major_bills)
+
+/obj/item/clothing/head/soft/med
+ name = "medical cap"
+ desc = "It's a field cap in white, with a blue cross on the front."
+ icon_state = "medsoft"
+ item_state_slots = list(slot_r_hand_str = "mimesoft", slot_l_hand_str = "mimesoft")
diff --git a/code/modules/mob/living/carbon/human/gradient.dm b/code/modules/mob/living/carbon/human/gradient.dm
index 7a0e1d37f1..d7513f38f8 100644
--- a/code/modules/mob/living/carbon/human/gradient.dm
+++ b/code/modules/mob/living/carbon/human/gradient.dm
@@ -2,5 +2,13 @@ GLOBAL_LIST_INIT(hair_gradients, list(
"None" = "none",
"Fade (Up)" = "fadeup",
"Fade (Down)" = "fadedown",
- "Vertical Split" = "vsplit"
+ "Fade Low (Up)" = "fadeup_low",
+ "Bottom Flat" = "bottomflat",
+ "Fade Low (Down)" = "fadedown_low",
+ "Vertical Split" = "vsplit",
+ "Reflected" = "reflected",
+ "Reflected (Inverted)" = "reflected_inverse",
+ "Reflected High" = "reflected_high",
+ "Reflected High (Inverted)" = "reflected_inverse_high",
+ "Wavy" = "wavy"
))
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index 59d3576485..f2b4a1e588 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -196,3 +196,11 @@
var/list/all_bits = internal_organs|organs
for(var/obj/item/organ/O in all_bits)
O.set_dna(dna)
+
+/mob/living/carbon/human/proc/set_gender(var/g)
+ if(g != gender)
+ gender = g
+
+ if(dna.GetUIState(DNA_UI_GENDER) ^ gender == FEMALE) // XOR will catch both cases where they do not match
+ dna.SetUIState(DNA_UI_GENDER, gender == FEMALE)
+ sync_organ_dna(dna)
\ No newline at end of file
diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm
index 9d0acce5c3..d756271286 100644
--- a/code/modules/mob/living/carbon/human/inventory.dm
+++ b/code/modules/mob/living/carbon/human/inventory.dm
@@ -215,6 +215,7 @@ This saves us from having to call add_fingerprint() any time something is put in
if(slot_back)
src.back = W
W.equipped(src, slot)
+ worn_clothing += back
update_inv_back()
if(slot_wear_mask)
src.wear_mask = W
@@ -222,6 +223,7 @@ This saves us from having to call add_fingerprint() any time something is put in
update_hair() //rebuild hair
update_inv_ears()
W.equipped(src, slot)
+ worn_clothing += wear_mask
update_inv_wear_mask()
if(slot_handcuffed)
src.handcuffed = W
@@ -241,6 +243,7 @@ This saves us from having to call add_fingerprint() any time something is put in
if(slot_belt)
src.belt = W
W.equipped(src, slot)
+ worn_clothing += belt
update_inv_belt()
if(slot_wear_id)
src.wear_id = W
@@ -269,6 +272,7 @@ This saves us from having to call add_fingerprint() any time something is put in
if(slot_glasses)
src.glasses = W
W.equipped(src, slot)
+ worn_clothing += glasses
update_inv_glasses()
if(slot_gloves)
src.gloves = W
@@ -283,18 +287,22 @@ This saves us from having to call add_fingerprint() any time something is put in
if(istype(W,/obj/item/clothing/head/kitty))
W.update_icon(src)
W.equipped(src, slot)
+ worn_clothing += head
update_inv_head()
if(slot_shoes)
src.shoes = W
W.equipped(src, slot)
+ worn_clothing += shoes
update_inv_shoes()
if(slot_wear_suit)
src.wear_suit = W
W.equipped(src, slot)
+ worn_clothing += wear_suit
update_inv_wear_suit()
if(slot_w_uniform)
src.w_uniform = W
W.equipped(src, slot)
+ worn_clothing += w_uniform
update_inv_w_uniform()
if(slot_l_store)
src.l_store = W
@@ -397,4 +405,4 @@ This saves us from having to call add_fingerprint() any time something is put in
if(l_hand)
. += l_hand
if(r_hand)
- . += r_hand
\ No newline at end of file
+ . += r_hand
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index 8dddcfe53c..337a397df2 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -236,10 +236,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon()
//Create a new, blank icon for our mob to use.
var/icon/stand_icon = new(species.icon_template ? species.icon_template : 'icons/mob/human.dmi', icon_state = "blank")
- var/g = "male"
- if(gender == FEMALE)
- g = "female"
-
+ var/g = (gender == MALE ? "male" : "female")
var/icon_key = "[species.get_race_key(src)][g][s_tone][r_skin][g_skin][b_skin]"
if(lip_style)
icon_key += "[lip_style]"
diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm
index c09c2a6849..c27e4cad2e 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm
@@ -193,6 +193,7 @@ var/global/list/robot_modules = list(
"Insekt" = "insekt-Default",
"Usagi-II" = "tall2standard",
"Pyralis" = "Glitterfly-Standard",
+ "Decapod" = "decapod-Standard",
"Miss M" = "miss-standard" // YW change, Added Miss M
)
@@ -230,6 +231,7 @@ var/global/list/robot_modules = list(
"Insekt" = "insekt-Med",
"Usagi-II" = "tall2medical",
"Pyralis" = "Glitterfly-Surgeon",
+ "Decapod" = "decapod-Surgeon",
"Miss M" = "miss-medical" // YW change, Added Miss M
)
@@ -306,6 +308,7 @@ var/global/list/robot_modules = list(
"Insekt" = "insekt-Med",
"Usagi-II" = "tall2medical",
"Pyralis" = "Glitterfly-Crisis",
+ "Decapod" = "decapod-Crisis",
"Miss M" = "miss-medical" // YW change, Added Miss M
)
@@ -384,6 +387,7 @@ var/global/list/robot_modules = list(
"Handy" = "handy-engineer",
"Usagi-II" = "tall2engineer",
"Pyralis" = "Glitterfly-Engineering",
+ "Decapod" = "decapod-Engineering",
"Servitor" = "servitor", //YW Addition to add new Servitor Sprite
"Miss M" = "miss-engineer" // YW change, Added Miss M
)
@@ -500,6 +504,7 @@ var/global/list/robot_modules = list(
"Insekt" = "insekt-Sec",
"Usagi-II" = "tall2security",
"Pyralis" = "Glitterfly-Security",
+ "Decapod" = "decapod-Security",
"Miss M" = "miss-security" // YW change, Added Miss M
)
@@ -546,6 +551,7 @@ var/global/list/robot_modules = list(
"Drone" = "drone-janitor",
"Usagi-II" = "tall2janitor",
"Pyralis" = "Glitterfly-Janitor",
+ "Decapod" = "decapod-Janitor",
"Miss M" = "miss-janitor" // YW change, Added Miss M
)
@@ -609,6 +615,7 @@ var/global/list/robot_modules = list(
"Drone - Hydro" = "drone-hydro",
"Usagi-II" = "tall2service",
"Pyralis" = "Glitterfly-Service",
+ "Decapod" = "decapod-Service",
"Miss M" = "miss-service" // YW change, Added Miss M
)
@@ -663,7 +670,8 @@ var/global/list/robot_modules = list(
"Default" = "Service2",
"Drone" = "drone-blu",
"Usagi-II" = "tall2service",
- "Pyralis" = "Glitterfly-Clerical"
+ "Pyralis" = "Glitterfly-Clerical",
+ "Decapod" = "decapod-Clerical"
)
/obj/item/weapon/robot_module/robot/clerical/general/New()
@@ -702,6 +710,7 @@ var/global/list/robot_modules = list(
"Drone" = "drone-miner",
"Usagi-II" = "tall2miner",
"Pyralis" = "Glitterfly-Miner",
+ "Decapod" = "decapod-Miner",
"Miss M" = "miss-miner" // YW change, Added Miss M
)
@@ -732,7 +741,8 @@ var/global/list/robot_modules = list(
"Handy" = "handy-science",
"Insekt" = "insekt-Sci",
"Usagi-II" = "tall2peace",
- "Pyralis" = "Glitterfly-Research"
+ "Pyralis" = "Glitterfly-Research",
+ "Decapod" = "decapod-Research"
)
/obj/item/weapon/robot_module/robot/research/New()
@@ -797,7 +807,8 @@ var/global/list/robot_modules = list(
sprites = list(
"Haruka" = "marinaCB",
"Combat Android" = "droid-combat",
- "Insekt" = "insekt-Combat"
+ "Insekt" = "insekt-Combat",
+ "Decapod" = "decapod-Combat"
)
/obj/item/weapon/robot_module/robot/security/combat/New()
diff --git a/code/modules/mob/living/simple_mob/simple_mob_vr.dm b/code/modules/mob/living/simple_mob/simple_mob_vr.dm
index 853848b83d..03f26ce8d0 100644
--- a/code/modules/mob/living/simple_mob/simple_mob_vr.dm
+++ b/code/modules/mob/living/simple_mob/simple_mob_vr.dm
@@ -54,7 +54,7 @@
/mob/living/simple_mob/Destroy()
release_vore_contents()
prey_excludes.Cut()
- . = ..()
+ return ..()
//For all those ID-having mobs
/mob/living/simple_mob/GetIdCard()
diff --git a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm
index ba2d76b39f..dd22ad7fd0 100644
--- a/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/slime/xenobio/xenobio.dm
@@ -1,280 +1,280 @@
-// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear.
-
-/mob/living/simple_mob/slime/xenobio
- desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists."
- layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something.
- ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes.
- max_nutrition = 1000
- var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough.
- var/maxHealth_adult = 200
- var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough.
- var/mob/living/victim = null // the person the slime is currently feeding on
- var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly.
- var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes.
- var/list/slime_mutation = list(
- /mob/living/simple_mob/slime/xenobio/orange,
- /mob/living/simple_mob/slime/xenobio/metal,
- /mob/living/simple_mob/slime/xenobio/blue,
- /mob/living/simple_mob/slime/xenobio/purple
- )
- var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces
- var/number = 0 // This is used to make the slime semi-unique for indentification.
- var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce.
-
-/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor)
- ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime))
- number = rand(1, 1000)
- update_name()
-
- . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end.
-
- if(my_predecessor)
- inherit_information(my_predecessor)
-
-
-/mob/living/simple_mob/slime/xenobio/Destroy()
- if(victim)
- stop_consumption() // Unbuckle us from our victim.
- return ..()
-
-// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards.
-/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor)
- if(!predecessor)
- return
-
- var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
- var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder
- ASSERT(istype(AI))
- ASSERT(istype(previous_AI))
-
- // Now to transfer the information.
- // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done.
- AI.discipline = max(previous_AI.discipline - 1, 0)
- AI.obedience = max(previous_AI.obedience - 1, 0)
- AI.resentment = max(previous_AI.resentment - 1, 0)
- AI.rabid = previous_AI.rabid
-
-
-/mob/living/simple_mob/slime/xenobio/update_icon()
- icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]"
- icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead"
- icon_rest = icon_dead
- ..() // This will apply the correct icon_state and do the other overlay-related things.
-
-
-/mob/living/simple_mob/slime/xenobio/handle_special()
- if(stat != DEAD)
- handle_nutrition()
-
- if(victim)
- handle_consumption()
-
- handle_stuttering() // ??
-
- ..()
-
-/mob/living/simple_mob/slime/xenobio/examine(mob/user)
- . = ..()
- if(hat)
- . += "It is wearing \a [hat]."
-
- if(stat == DEAD)
- . += "It appears to be dead."
- else if(incapacitated(INCAPACITATION_DISABLED))
- . += "It appears to be incapacitated."
- else if(harmless)
- . += "It appears to have been pacified."
- else
- if(has_AI())
- var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
- if(AI.rabid)
- . += "It seems very, very angry and upset."
- else if(AI.obedience >= 5)
- . += "It looks rather obedient."
- else if(AI.discipline)
- . += "It has been subjugated by force, at least for now."
-
-/mob/living/simple_mob/slime/xenobio/proc/make_adult()
- if(is_adult)
- return
-
- is_adult = TRUE
- melee_damage_lower = round(melee_damage_lower * 2) // 20
- melee_damage_upper = round(melee_damage_upper * 2) // 30
- maxHealth = maxHealth_adult
- max_nutrition = 1200
- amount_grown = 0
- update_icon()
- update_name()
-
-/mob/living/simple_mob/slime/xenobio/proc/update_name()
- if(harmless) // Docile slimes are generally named, so we shouldn't mess with it.
- return
- name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])"
- real_name = name
-
-/mob/living/simple_mob/slime/xenobio/update_mood()
- var/old_mood = mood
- if(incapacitated(INCAPACITATION_DISABLED))
- mood = "sad"
- else if(harmless)
- mood = ":33"
- else if(has_AI())
- var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
- if(AI.rabid)
- mood = "angry"
- else if(AI.target)
- mood = "mischevous"
- else if(AI.discipline)
- mood = "pout"
- else
- mood = ":3"
- else
- mood = ":3"
-
- if(old_mood != mood)
- update_icon()
-
-/mob/living/simple_mob/slime/xenobio/proc/enrage()
- if(harmless)
- return
- if(has_AI())
- var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
- AI.enrage()
-
-/mob/living/simple_mob/slime/xenobio/proc/pacify()
- harmless = TRUE
- if(has_AI())
- var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
- AI.pacify()
-
- faction = "neutral"
-
- // If for whatever reason the mob AI (or player) decides to try to attack something anyways.
- melee_damage_upper = 0
- melee_damage_lower = 0
-
- update_mood()
-
-
-// These are verbs so that player slimes can evolve/split.
-/mob/living/simple_mob/slime/xenobio/verb/evolve()
- set category = "Slime"
- set desc = "This will let you evolve from baby to adult slime."
-
- if(stat)
- to_chat(src, span("warning", "I must be conscious to do this..."))
- return
-
- if(harmless)
- to_chat(src, span("warning", "I have been pacified. I cannot evolve..."))
- return
-
- if(!is_adult)
- if(amount_grown >= 10)
- make_adult()
- else
- to_chat(src, span("warning", "I am not ready to evolve yet..."))
- else
- to_chat(src, span("warning", "I have already evolved..."))
-
-
-/mob/living/simple_mob/slime/xenobio/verb/reproduce()
- set category = "Slime"
- set desc = "This will make you split into four new slimes."
-
- if(stat)
- to_chat(src, span("warning", "I must be conscious to do this..."))
- return
-
- if(harmless)
- to_chat(src, span("warning", "I have been pacified. I cannot reproduce..."))
- return
-
- if(is_adult)
- if(amount_grown >= 10)
- // Check if there's enough 'room' to split.
- var/list/nearby_things = orange(1, src)
- var/free_tiles = 0
- for(var/turf/T in nearby_things)
- var/free = TRUE
- if(T.density) // No walls.
- continue
- for(var/atom/movable/AM in T)
- if(AM.density)
- free = FALSE
- break
-
- if(free)
- free_tiles++
-
- if(free_tiles < 3) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies.
- to_chat(src, span("warning", "It is too cramped here to reproduce..."))
- return
-
- var/list/babies = list()
- for(var/i = 1 to 4)
- babies.Add(make_new_slime())
-
- var/mob/living/simple_mob/slime/new_slime = pick(babies)
- new_slime.universal_speak = universal_speak
- if(src.mind)
- src.mind.transfer_to(new_slime)
- else
- new_slime.key = src.key
- qdel(src)
- else
- to_chat(src, span("warning", "I am not ready to reproduce yet..."))
- else
- to_chat(src, span("warning", "I have not evolved enough to reproduce yet..."))
-
-// Used when reproducing or dying.
-/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type)
- var/t = src.type
- if(desired_type)
- t = desired_type
- if(prob(mutation_chance / 10))
- t = /mob/living/simple_mob/slime/xenobio/rainbow
- else if(prob(mutation_chance) && slime_mutation.len)
- t = slime_mutation[rand(1, slime_mutation.len)]
- var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src)
-
- // Handle 'inheriting' from parent slime.
- baby.mutation_chance = mutation_chance
- baby.power_charge = round(power_charge / 4)
-
- if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow))
- baby.unity = unity
- baby.faction = faction
- baby.friends = friends.Copy()
-
- step_away(baby, src)
- return baby
-
-/mob/living/simple_mob/slime/xenobio/get_description_interaction()
- var/list/results = list()
-
- if(!stat)
- results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad."
-
- results += ..()
-
- return results
-
-/mob/living/simple_mob/slime/xenobio/get_description_info()
- var/list/lines = list()
- var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \
- They can be extremely dangerous if not handled properly."
- lines.Add(intro_line)
- lines.Add(null) // To pad the line breaks.
-
- var/list/rewards = list()
- for(var/potential_color in slime_mutation)
- var/mob/living/simple_mob/slime/S = potential_color
- rewards.Add(initial(S.slime_color))
- var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough."
- lines.Add(reward_line)
- lines.Add(null)
-
- lines.Add(description_info)
- return lines.Join("\n")
+// These slimes have the mechanics xenobiologists care about, such as reproduction, mutating into new colors, and being able to submit through fear.
+
+/mob/living/simple_mob/slime/xenobio
+ desc = "The most basic of slimes. The grey slime has no remarkable qualities, however it remains one of the most useful colors for scientists."
+ layer = MOB_LAYER + 1 // Need them on top of other mobs or it looks weird when consuming something.
+ ai_holder_type = /datum/ai_holder/simple_mob/xenobio_slime // This should never be changed for xenobio slimes.
+ max_nutrition = 1000
+ var/is_adult = FALSE // Slimes turn into adults when fed enough. Adult slimes are somewhat stronger, and can reproduce if fed enough.
+ var/maxHealth_adult = 200
+ var/power_charge = 0 // Disarm attacks can shock someone if high/lucky enough.
+ var/mob/living/victim = null // the person the slime is currently feeding on
+ var/rainbow_core_candidate = TRUE // If false, rainbow cores cannot make this type randomly.
+ var/mutation_chance = 25 // Odds of spawning as a new color when reproducing. Can be modified by certain xenobio products. Carried across generations of slimes.
+ var/list/slime_mutation = list(
+ /mob/living/simple_mob/slime/xenobio/orange,
+ /mob/living/simple_mob/slime/xenobio/metal,
+ /mob/living/simple_mob/slime/xenobio/blue,
+ /mob/living/simple_mob/slime/xenobio/purple
+ )
+ var/amount_grown = 0 // controls how long the slime has been overfed, if 10, grows or reproduces
+ var/number = 0 // This is used to make the slime semi-unique for indentification.
+ var/harmless = FALSE // Set to true when pacified. Makes the slime harmless, not get hungry, and not be able to grow/reproduce.
+
+/mob/living/simple_mob/slime/xenobio/Initialize(mapload, var/mob/living/simple_mob/slime/xenobio/my_predecessor)
+ ASSERT(ispath(ai_holder_type, /datum/ai_holder/simple_mob/xenobio_slime))
+ number = rand(1, 1000)
+ update_name()
+
+ . = ..() // This will make the AI and do the other mob constructor things. It will also return the default hint at the end.
+
+ if(my_predecessor)
+ inherit_information(my_predecessor)
+
+
+/mob/living/simple_mob/slime/xenobio/Destroy()
+ if(victim)
+ stop_consumption() // Unbuckle us from our victim.
+ return ..()
+
+// Called when a slime makes another slime by splitting. The predecessor slime will be deleted shortly afterwards.
+/mob/living/simple_mob/slime/xenobio/proc/inherit_information(var/mob/living/simple_mob/slime/xenobio/predecessor)
+ if(!predecessor)
+ return
+
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ var/datum/ai_holder/simple_mob/xenobio_slime/previous_AI = predecessor.ai_holder
+ ASSERT(istype(AI))
+ ASSERT(istype(previous_AI))
+
+ // Now to transfer the information.
+ // Newly made slimes are bit more rebellious than their predecessors, but they also somewhat forget the atrocities the xenobiologist may have done.
+ AI.discipline = max(previous_AI.discipline - 1, 0)
+ AI.obedience = max(previous_AI.obedience - 1, 0)
+ AI.resentment = max(previous_AI.resentment - 1, 0)
+ AI.rabid = previous_AI.rabid
+
+
+/mob/living/simple_mob/slime/xenobio/update_icon()
+ icon_living = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"][victim ? " eating" : ""]"
+ icon_dead = "[icon_state_override ? "[icon_state_override] slime" : "slime"] [is_adult ? "adult" : "baby"] dead"
+ icon_rest = icon_dead
+ ..() // This will apply the correct icon_state and do the other overlay-related things.
+
+
+/mob/living/simple_mob/slime/xenobio/handle_special()
+ if(stat != DEAD)
+ handle_nutrition()
+
+ if(victim)
+ handle_consumption()
+
+ handle_stuttering() // ??
+
+ ..()
+
+/mob/living/simple_mob/slime/xenobio/examine(mob/user)
+ . = ..()
+ if(hat)
+ . += "It is wearing \a [hat]."
+
+ if(stat == DEAD)
+ . += "It appears to be dead."
+ else if(incapacitated(INCAPACITATION_DISABLED))
+ . += "It appears to be incapacitated."
+ else if(harmless)
+ . += "It appears to have been pacified."
+ else
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ if(AI.rabid)
+ . += "It seems very, very angry and upset."
+ else if(AI.obedience >= 5)
+ . += "It looks rather obedient."
+ else if(AI.discipline)
+ . += "It has been subjugated by force, at least for now."
+
+/mob/living/simple_mob/slime/xenobio/proc/make_adult()
+ if(is_adult)
+ return
+
+ is_adult = TRUE
+ melee_damage_lower = round(melee_damage_lower * 2) // 20
+ melee_damage_upper = round(melee_damage_upper * 2) // 30
+ maxHealth = maxHealth_adult
+ max_nutrition = 1200
+ amount_grown = 0
+ update_icon()
+ update_name()
+
+/mob/living/simple_mob/slime/xenobio/proc/update_name()
+ if(harmless) // Docile slimes are generally named, so we shouldn't mess with it.
+ return
+ name = "[slime_color] [is_adult ? "adult" : "baby"] [initial(name)] ([number])"
+ real_name = name
+
+/mob/living/simple_mob/slime/xenobio/update_mood()
+ var/old_mood = mood
+ if(incapacitated(INCAPACITATION_DISABLED))
+ mood = "sad"
+ else if(harmless)
+ mood = ":33"
+ else if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ if(AI.rabid)
+ mood = "angry"
+ else if(AI.target)
+ mood = "mischevous"
+ else if(AI.discipline)
+ mood = "pout"
+ else
+ mood = ":3"
+ else
+ mood = ":3"
+
+ if(old_mood != mood)
+ update_icon()
+
+/mob/living/simple_mob/slime/xenobio/proc/enrage()
+ if(harmless)
+ return
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ AI.enrage()
+
+/mob/living/simple_mob/slime/xenobio/proc/pacify()
+ harmless = TRUE
+ if(has_AI())
+ var/datum/ai_holder/simple_mob/xenobio_slime/AI = ai_holder
+ AI.pacify()
+
+ faction = "neutral"
+
+ // If for whatever reason the mob AI (or player) decides to try to attack something anyways.
+ melee_damage_upper = 0
+ melee_damage_lower = 0
+
+ update_mood()
+
+
+// These are verbs so that player slimes can evolve/split.
+/mob/living/simple_mob/slime/xenobio/verb/evolve()
+ set category = "Slime"
+ set desc = "This will let you evolve from baby to adult slime."
+
+ if(stat)
+ to_chat(src, span("warning", "I must be conscious to do this..."))
+ return
+
+ if(harmless)
+ to_chat(src, span("warning", "I have been pacified. I cannot evolve..."))
+ return
+
+ if(!is_adult)
+ if(amount_grown >= 10)
+ make_adult()
+ else
+ to_chat(src, span("warning", "I am not ready to evolve yet..."))
+ else
+ to_chat(src, span("warning", "I have already evolved..."))
+
+
+/mob/living/simple_mob/slime/xenobio/verb/reproduce()
+ set category = "Slime"
+ set desc = "This will make you split into four new slimes."
+
+ if(stat)
+ to_chat(src, span("warning", "I must be conscious to do this..."))
+ return
+
+ if(harmless)
+ to_chat(src, span("warning", "I have been pacified. I cannot reproduce..."))
+ return
+
+ if(is_adult)
+ if(amount_grown >= 10)
+ // Check if there's enough 'room' to split.
+ var/list/nearby_things = orange(1, src)
+ var/free_tiles = 0
+ for(var/turf/T in nearby_things)
+ var/free = TRUE
+ if(T.density) // No walls.
+ continue
+ for(var/atom/movable/AM in T)
+ if(AM.density || istype(AM, /mob/living/simple_mob/slime))
+ free = FALSE
+ break
+
+ if(free)
+ free_tiles++
+
+ if(free_tiles < 3) // Three free tiles are needed, as four slimes are made and the 4th tile is from the center tile that the current slime occupies.
+ to_chat(src, span("warning", "It is too cramped here to reproduce..."))
+ return
+
+ var/list/babies = list()
+ for(var/i = 1 to 4)
+ babies.Add(make_new_slime())
+
+ var/mob/living/simple_mob/slime/new_slime = pick(babies)
+ new_slime.universal_speak = universal_speak
+ if(src.mind)
+ src.mind.transfer_to(new_slime)
+ else
+ new_slime.key = src.key
+ qdel(src)
+ else
+ to_chat(src, span("warning", "I am not ready to reproduce yet..."))
+ else
+ to_chat(src, span("warning", "I have not evolved enough to reproduce yet..."))
+
+// Used when reproducing or dying.
+/mob/living/simple_mob/slime/xenobio/proc/make_new_slime(var/desired_type)
+ var/t = src.type
+ if(desired_type)
+ t = desired_type
+ if(prob(mutation_chance / 10))
+ t = /mob/living/simple_mob/slime/xenobio/rainbow
+ else if(prob(mutation_chance) && slime_mutation.len)
+ t = slime_mutation[rand(1, slime_mutation.len)]
+ var/mob/living/simple_mob/slime/xenobio/baby = new t(loc, src)
+
+ // Handle 'inheriting' from parent slime.
+ baby.mutation_chance = mutation_chance
+ baby.power_charge = round(power_charge / 4)
+
+ if(!istype(baby, /mob/living/simple_mob/slime/xenobio/rainbow))
+ baby.unity = unity
+ baby.faction = faction
+ baby.friends = friends.Copy()
+
+ step_away(baby, src)
+ return baby
+
+/mob/living/simple_mob/slime/xenobio/get_description_interaction()
+ var/list/results = list()
+
+ if(!stat)
+ results += "[desc_panel_image("slimebaton")]to stun the slime, if it's being bad."
+
+ results += ..()
+
+ return results
+
+/mob/living/simple_mob/slime/xenobio/get_description_info()
+ var/list/lines = list()
+ var/intro_line = "Slimes are generally the test subjects of Xenobiology, with different colors having different properties. \
+ They can be extremely dangerous if not handled properly."
+ lines.Add(intro_line)
+ lines.Add(null) // To pad the line breaks.
+
+ var/list/rewards = list()
+ for(var/potential_color in slime_mutation)
+ var/mob/living/simple_mob/slime/S = potential_color
+ rewards.Add(initial(S.slime_color))
+ var/reward_line = "This color of slime can mutate into [english_list(rewards)] colors, when it reproduces. It will do so when it has eatten enough."
+ lines.Add(reward_line)
+ lines.Add(null)
+
+ lines.Add(description_info)
+ return lines.Join("\n")
diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm
index 4cf5e49ad1..fa4595459a 100644
--- a/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm
+++ b/code/modules/mob/living/simple_mob/subtypes/vore/wolfgirl.dm
@@ -24,15 +24,6 @@
say_list_type = /datum/say_list/wolfgirl
ai_holder_type = /datum/ai_holder/simple_mob/retaliate/cooperative/wolfgirl
- var/loopstop = 0 //To prevent circular awoooos.
-/*
-/mob/living/simple_mob/retaliate/wolfgirl/hear_say()
- if(world.time - loopstop < 5 SECONDS)
- return
- else
- loopstop = world.time
- ..()
-*/
// Activate Noms!
/mob/living/simple_mob/vore/wolfgirl
vore_active = 1
@@ -59,6 +50,22 @@
if(findtext(message, "Awoo."))
delayed_say(pick("Awoo?"), speaker)
+ if(findtext(message, "Nice hat"))
+ delayed_say(pick("Thanks my grandma made it for me."), speaker)
+
+ if(findtext(message, "What's your phone number?"))
+ delayed_say(pick("Five six seven oh nine! Wait, who are you?"), speaker)
+
+ if(findtext(message, "Are you horny?"))
+ delayed_say(pick("No! I'm just hyperactive!"), speaker)
+
+ if(findtext(message, "Good girl"))
+ delayed_say(pick("Aww thanks... Wait, I'm not a dog!"), speaker)
+
+ if(findtext(message, "Fuyu"))
+ delayed_say(pick("You know my sister?!", "Is she causing problems again?"), speaker)
+
+
/datum/say_list/wolfgirl
speak = list("AwoooOOOOoooo!","Awoo~","I'll protect the forest! ... Where's the forest again?","All I need is my sword!","Awoo?","Anyone else smell that?")
emote_hear = list("awoooos!","hmms to herself","plays with her sword")
diff --git a/code/modules/mob/new_player/sprite_accessories.dm b/code/modules/mob/new_player/sprite_accessories.dm
index 74578c8e9a..b5b0c8eaa6 100644
--- a/code/modules/mob/new_player/sprite_accessories.dm
+++ b/code/modules/mob/new_player/sprite_accessories.dm
@@ -64,10 +64,45 @@
flags = HAIR_TIEABLE
/datum/sprite_accessory/hair/afro_large
- name = "Big Afro"
+ name = "Afro, Big"
icon_state = "hair_bigafro"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/afropuffdouble
+ name = "Afropuff, Double"
+ icon_state = "hair_afropuffdouble"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/afropuffleft
+ name = "Afropuff, Left"
+ icon_state = "hair_afropuffleft"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/afropuffright
+ name = "Afropuff, Right"
+ icon_state = "hair_afropuffright"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/angelique
+ name = "Angelique"
+ icon_state = "hair_angelique"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/antonio
+ name = "Antonio"
+ icon_state = "hair_antonio"
+ flags = HAIR_VERY_SHORT
+
+/datum/sprite_accessory/hair/aradia
+ name = "Aradia"
+ icon_state = "hair_aradia"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/messyhair
+ name = "All Up"
+ icon_state = "hair_messyhair"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/amazon
name = "Amazon"
icon_state = "hair_amazon"
@@ -77,6 +112,11 @@
name = "Antenna"
icon_state = "hair_antenna"
+/datum/sprite_accessory/hair/averagejoe
+ name = "Average Joe"
+ icon_state = "hair_averagejoe"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/bald
name = "Bald"
icon_state = "bald"
@@ -93,6 +133,11 @@
icon_state = "hair_e"
flags = HAIR_VERY_SHORT
+/datum/sprite_accessory/hair/band
+ name = "Band"
+ icon_state = "hair_band"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/beachwave
name = "Beach Waves"
icon_state = "hair_beachwave"
@@ -133,6 +178,10 @@
icon_state = "hair_beehive2"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/bieber
+ name = "Bieber"
+ icon_state = "hair_bieb"
+
/datum/sprite_accessory/hair/belenko
name = "Belenko"
icon_state = "hair_belenko"
@@ -143,6 +192,11 @@
icon_state = "hair_belenkotied"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/bigcurls
+ name = "Big Curls"
+ icon_state = "hair_bigcurls"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/bob
name = "Bob"
icon_state = "hair_bobcut"
@@ -154,6 +208,11 @@
icon_state = "hair_bobcutalt"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/midb
+ name = "Bob, Mid-length"
+ icon_state = "hair_midb"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/bobcurl
name = "Bobcurl"
icon_state = "hair_bobcurl"
@@ -182,6 +241,16 @@
icon_state = "hair_hbraid"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/longbraid
+ name = "Braid Long, Alt"
+ icon_state = "hair_longbraid"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/longbraidalt
+ name = "Braid Long, Alt 2"
+ icon_state = "hair_braidalt"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/mbraid
name = "Braid Medium"
icon_state = "hair_shortbraid"
@@ -282,6 +351,11 @@
name = "Combover"
icon_state = "hair_combover"
+/datum/sprite_accessory/hair/cornbun
+ name = "Cornbun"
+ icon_state = "hair_cornbun"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/country
name = "Country"
icon_state = "hair_country"
@@ -302,6 +376,11 @@
icon_state = "hair_c"
flags = HAIR_VERY_SHORT
+/datum/sprite_accessory/hair/darcy
+ name = "Darcy"
+ icon_state = "hair_darcy"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/dave
name = "Dave"
icon_state = "hair_dave"
@@ -310,6 +389,11 @@
name = "Devil Lock"
icon_state = "hair_devilock"
+/datum/sprite_accessory/hair/dirk
+ name = "Dirk"
+ icon_state = "hair_dirk"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/donutbun
name = "Donut Bun"
icon_state = "hair_donutbun"
@@ -319,11 +403,25 @@
icon_state = "hair_dreads"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/dreadslong
+ name = "Dreads Long"
+ icon_state = "hair_dreadslong"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/dreadslongalt
+ name = "Dreads Long, Alt"
+ icon_state = "hair_dreadlongalt"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/mahdrills
name = "Drillruru"
icon_state = "hair_drillruru"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/elize
+ name = "Elize"
+ icon_state = "hair_elize"
+
/datum/sprite_accessory/hair/emo
name = "Emo"
icon_state = "hair_emo"
@@ -344,11 +442,28 @@
icon_state = "hair_halfshaved"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/halfshavedlong
+ name = "Emo Half-Shaved Long"
+ icon_state = "hair_halfshavedL"
+
+/datum/sprite_accessory/hair/emoright
+ name = "Emo Mid-length"
+ icon_state = "hair_emoright"
+
/datum/sprite_accessory/hair/longemo
name = "Emo Long"
icon_state = "hair_emolong"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/equius
+ name = "Equius"
+ icon_state = "hair_equius"
+
+/datum/sprite_accessory/hair/fabio
+ name = "Fabio"
+ icon_state = "hair_fabio"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/highfade
name = "Fade High"
icon_state = "hair_highfade"
@@ -385,11 +500,20 @@
icon_state = "hair_feather"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/feferi
+ name = "Feferi"
+ icon_state = "hair_feferi"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/flair
name = "Flaired Hair"
icon_state = "hair_flair"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/flipped
+ name = "Flipped"
+ icon_state = "hair_flipped"
+
/datum/sprite_accessory/hair/sargeant
name = "Flat Top"
icon_state = "hair_sargeant"
@@ -414,6 +538,15 @@
icon_state = "hair_fringetail"
flags = HAIR_TIEABLE|HAIR_VERY_SHORT
+/datum/sprite_accessory/hair/froofy_long
+ name = "Froofy Long"
+ icon_state = "hair_froofy_long"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/gamzee
+ name = "Gamzee"
+ icon_state = "hair_gamzee"
+
/datum/sprite_accessory/hair/gelled
name = "Gelled Back"
icon_state = "hair_gelled"
@@ -429,6 +562,11 @@
icon_state = "hair_gentle2long"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/glammetal
+ name = "Glam Metal Long"
+ icon_state = "hair_glammetal"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/glossy
name = "Glossy"
icon_state = "hair_glossy"
@@ -471,11 +609,14 @@
icon_state = "hair_jade"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/jane
+ name = "Jane"
+ icon_state = "hair_jane"
+
/datum/sprite_accessory/hair/jensen
name = "Jensen"
icon_state = "hair_jensen"
-
/datum/sprite_accessory/hair/jessica
name = "Jessica"
icon_state = "hair_jessica"
@@ -485,11 +626,20 @@
name = "Joestar"
icon_state = "hair_joestar"
+/datum/sprite_accessory/hair/judge
+ name = "Judge"
+ icon_state = "hair_judge"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/kagami
name = "Kagami"
icon_state = "hair_kagami"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/kanaya
+ name = "Kanaya"
+ icon_state = "hair_kanaya"
+
/datum/sprite_accessory/hair/keanu
name = "Keanu Hair"
icon_state = "hair_keanu"
@@ -543,6 +693,15 @@
name = "Mary Sue"
icon_state = "hair_marysue"
+/datum/sprite_accessory/hair/mia
+ name = "Mia"
+ icon_state = "hair_mia"
+
+/datum/sprite_accessory/hair/mialong
+ name = "Mia Long"
+ icon_state = "hair_mialong"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/miles
name = "Miles Hair"
icon_state = "hair_miles"
@@ -574,6 +733,10 @@
name = "Mulder"
icon_state = "hair_mulder"
+/datum/sprite_accessory/hair/nepeta
+ name = "Nepeta"
+ icon_state = "hair_nepeta"
+
/datum/sprite_accessory/hair/newyou
name = "New You"
icon_state = "hair_newyou"
@@ -713,6 +876,16 @@
icon_state = "hair_newyou"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/rockandroll
+ name = "Rock and Roll"
+ icon_state = "hair_rockandroll"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/rockstarcurls
+ name = "Rockstar Curls"
+ icon_state = "hair_rockstarcurls"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/ronin
name = "Ronin"
icon_state = "hair_ronin"
@@ -723,6 +896,10 @@
icon_state = "hair_rosa"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/rose
+ name = "Rose"
+ icon_state = "hair_rose"
+
/datum/sprite_accessory/hair/rows
name = "Rows"
icon_state = "hair_rows1"
@@ -748,6 +925,10 @@
icon_state = "hair_rowbraid"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/roxy
+ name = "Roxy"
+ icon_state = "hair_roxy"
+
/datum/sprite_accessory/hair/sabitsuki
name = "Sabitsuki"
icon_state = "hair_sabitsuki"
@@ -766,6 +947,11 @@
name = "Short Bangs"
icon_state = "hair_shortbangs"
+/datum/sprite_accessory/hair/shortflip
+ name = "Short Flip"
+ icon_state = "hair_shortflip"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/short
name = "Short Hair" // try to capatilize the names please~
icon_state = "hair_a" // you do not need to define _s or _l sub-states, game automatically does this for you
@@ -796,6 +982,16 @@
icon_state = "hair_ponytailf"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/sideponytail5
+ name = "Side Ponytail 3"
+ icon_state = "hair_sidetail"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/sideponytail6
+ name = "Side Ponytail 4"
+ icon_state = "hair_sidetail2"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/sideponytail2
name = "Shoulder One"
icon_state = "hair_oneshoulder"
@@ -836,10 +1032,28 @@
icon_state = "hair_straightlong"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/suave
+ name = "Suave"
+ icon_state = "hair_suave"
+
+/datum/sprite_accessory/hair/suavetwo
+ name = "Suave 2"
+ icon_state = "hair_suave2"
+
/datum/sprite_accessory/hair/sweepshave
name = "Sweep Shave"
icon_state = "hair_sweepshave"
+/datum/sprite_accessory/hair/sweptfringe
+ name = "Swept Fringe"
+ icon_state = "hair_sweptfringe"
+ flags = HAIR_TIEABLE
+
+/datum/sprite_accessory/hair/terezi
+ name = "Terezi"
+ icon_state = "hair_terezi"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/thinning
name = "Thinning"
icon_state = "hair_thinning"
@@ -877,6 +1091,11 @@
icon_state = "hair_twintail"
flags = HAIR_TIEABLE
+/datum/sprite_accessory/hair/twinbob
+ name = "Twinbun Bob"
+ icon_state = "hair_bunbob"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/undercut1
name = "Undercut"
icon_state = "hair_undercut1"
@@ -895,6 +1114,11 @@
gender = MALE
flags = HAIR_VERY_SHORT
+/datum/sprite_accessory/hair/longundercut
+ name = "Undercut Long"
+ icon_state = "hair_undercutlong"
+ flags = HAIR_VERY_SHORT
+
/datum/sprite_accessory/hair/unkept
name = "Unkept"
icon_state = "hair_unkept"
@@ -909,6 +1133,11 @@
name = "Vegeta"
icon_state = "hair_toriyama2"
+/datum/sprite_accessory/hair/vriska
+ name = "Vriska"
+ icon_state = "hair_vriska"
+ flags = HAIR_TIEABLE
+
/datum/sprite_accessory/hair/vivi
name = "Vivi"
icon_state = "hair_vivi"
diff --git a/code/modules/overmap/ships/computers/engine_control.dm b/code/modules/overmap/ships/computers/engine_control.dm
index 5721c6f9c7..267381d9a4 100644
--- a/code/modules/overmap/ships/computers/engine_control.dm
+++ b/code/modules/overmap/ships/computers/engine_control.dm
@@ -86,7 +86,7 @@
E.set_thrust_limit(limit)
. = TRUE
- if("toggle")
+ if("toggle_engine")
var/datum/ship_engine/E = locate(params["engine"])
if(istype(E))
E.toggle()
diff --git a/code/modules/overmap/ships/computers/sensors.dm b/code/modules/overmap/ships/computers/sensors.dm
index bec21e16a3..30c9ed3632 100644
--- a/code/modules/overmap/ships/computers/sensors.dm
+++ b/code/modules/overmap/ships/computers/sensors.dm
@@ -105,7 +105,7 @@
if(nrange)
sensors.set_range(CLAMP(nrange, 1, world.view))
. = TRUE
- if("toggle")
+ if("toggle_sensor")
sensors.toggle()
. = TRUE
diff --git a/code/modules/persistence/filth.dm b/code/modules/persistence/filth.dm
index a7aa9e9cd4..2d8e91b5ce 100644
--- a/code/modules/persistence/filth.dm
+++ b/code/modules/persistence/filth.dm
@@ -5,8 +5,8 @@
icon_state = "mfloor1"
random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7")
color = "#464f33"
- persistent = TRUE
anchored = 1
+ persistent = TRUE
/obj/effect/decal/cleanable/filth/Initialize()
. = ..()
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 54428d570f..e0159e6333 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -64,6 +64,28 @@
item_state = "lrifle"
origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 3)
+/obj/item/weapon/gun/energy/mininglaser
+ name = "mining-laser rifle"
+ desc = "A Hephaestus Industries G22T rifle, now only produced for its impeccable ability to break stone with its pulsating blasts."
+ description_fluff = "The leading arms producer in the SCG, Hephaestus typically only uses its 'top level' branding for its military-grade equipment used by armed forces across human space."
+ icon = 'icons/obj/gun2.dmi'
+ icon_state = "mininglaser"
+ item_state = "laser"
+ wielded_item_state = "laser-wielded"
+ fire_delay = 8
+ slot_flags = SLOT_BELT|SLOT_BACK
+ w_class = ITEMSIZE_LARGE
+ force = 15
+ origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2)
+ matter = list(DEFAULT_WALL_MATERIAL = 2000)
+ projectile_type = /obj/item/projectile/beam/mininglaser
+ one_handed_penalty = 30
+
+ firemodes = list(
+ list(mode_name="mining", fire_delay=8, projectile_type=/obj/item/projectile/beam/mininglaser, charge_cost = 200),
+ list(mode_name="deter", fire_delay=5, projectile_type=/obj/item/projectile/beam/weaklaser, charge_cost = 80),
+ )
+
/obj/item/weapon/gun/energy/retro
name = "retro laser"
icon_state = "retro"
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 8c888fee3e..1612e1cab3 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -17,6 +17,8 @@
hitsound = 'sound/weapons/sear.ogg'
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ excavation_amount = 50
+
muzzle_type = /obj/effect/projectile/muzzle/laser
tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/impact/laser
@@ -46,6 +48,18 @@
damage = 40
armor_penetration = 10
+/obj/item/projectile/beam/mininglaser
+ name = "pulsating laser"
+ damage = 10
+ armor_penetration = 20
+ fire_sound = 'sound/weapons/eluger.ogg'
+
+ excavation_amount = 100
+
+ muzzle_type = /obj/effect/projectile/muzzle/emitter
+ tracer_type = /obj/effect/projectile/tracer/emitter
+ impact_type = /obj/effect/projectile/impact/emitter
+
/obj/item/projectile/beam/heavylaser
name = "heavy laser"
icon_state = "heavylaser"
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index c72d9b35ea..09919bc7cf 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -11,6 +11,7 @@
sharp = 1
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
+ excavation_amount = 20
var/mob_passthrough_check = 0
muzzle_type = /obj/effect/projectile/muzzle/bullet
diff --git a/code/modules/research/designs/engineering.dm b/code/modules/research/designs/engineering.dm
index 909fa83680..d74adc2361 100644
--- a/code/modules/research/designs/engineering.dm
+++ b/code/modules/research/designs/engineering.dm
@@ -31,15 +31,6 @@
build_path = /obj/item/weapon/tool/crowbar/power
sort_string = "NAAAC"
// YW addition begins
-/datum/design/item/tool/rpd
- name = "Rapid Piping Device (RPD)"
- desc = "A device used to rapidly pipe things."
- id = "RPD"
- req_tech = list(TECH_ENGINEERING = 3, TECH_MATERIAL = 2, TECH_BLUESPACE = 2)
- materials = list(DEFAULT_WALL_MATERIAL = 300, "silver" = 100, "diamond" = 500)
- build_path = /obj/item/weapon/pipe_dispenser
- sort_string = "NAAAB"
-
/datum/design/item/tool/rms
name = "Rapid Material Synthesizer (RMS)"
desc = "A tool that converts battery charge to materials."
@@ -48,8 +39,17 @@
materials = list(DEFAULT_WALL_MATERIAL = 500, "glass" = 500, "silver" = 300, "phoron" = 300, "diamond" = 500) //Probably change the price in the future just put something for now
build_path = /obj/item/weapon/rms
sort_string = "NAAAB"
-
// YW addition end
+
+/datum/design/item/tool/rpd
+ name = "Rapid Pipe Dispenser"
+ desc = "A counterpart to the rapid construction device that allows creating and placing atmospheric and disposal pipes."
+ id = "rapidpipedispenser"
+ req_tech = list(TECH_ENGINEERING = 4, TECH_MATERIAL = 2)
+ materials = list(DEFAULT_WALL_MATERIAL = 4000, MAT_GLASS = 2000)
+ build_path = /obj/item/weapon/pipe_dispenser
+ sort_string = "NAAAD"
+
// Other devices
/datum/design/item/engineering/AssembleDesignName()
diff --git a/code/modules/shuttles/shuttles_web.dm b/code/modules/shuttles/shuttles_web.dm
index acc7dc3461..ea11f21d48 100644
--- a/code/modules/shuttles/shuttles_web.dm
+++ b/code/modules/shuttles/shuttles_web.dm
@@ -131,14 +131,14 @@
return
autopilot = TRUE
autopilot_delay = initial(autopilot_delay)
- shuttle_controller.process_shuttles |= src
+ SSshuttles.process_shuttles |= src
if(process_state == IDLE_STATE)
process_state = DO_AUTOPILOT
else
if(!autopilot)
return
autopilot = FALSE
- shuttle_controller.process_shuttles -= src
+ SSshuttles.process_shuttles -= src
if (process_state == DO_AUTOPILOT)
process_state = initial(process_state)
@@ -196,7 +196,7 @@
log_shuttle("[my_area] shuttle computer couldn't find [lost] sensor!")
/obj/machinery/computer/shuttle_control/web/attackby(obj/I, mob/user)
- var/datum/shuttle/autodock/web_shuttle/shuttle = shuttle_controller.shuttles[shuttle_tag]
+ var/datum/shuttle/autodock/web_shuttle/shuttle = SSshuttles.shuttles[shuttle_tag]
if(shuttle && istype(I,/obj/item/clothing/head/pilot))
var/obj/item/clothing/head/pilot/H = I
H.shuttle_comp = src
@@ -418,7 +418,7 @@
// This is called whenever a shuttle is initialized. If its our shuttle, do our thing!
/obj/shuttle_connector/proc/setup_routes(var/new_shuttle)
- var/datum/shuttle/autodock/web_shuttle/ES = shuttle_controller.shuttles[shuttle_name]
+ var/datum/shuttle/autodock/web_shuttle/ES = SSshuttles.shuttles[shuttle_name]
if(ES != new_shuttle)
return // Its not our shuttle! Ignore!
if(destinations && istype(ES))
diff --git a/code/modules/tgui/modules/_base.dm b/code/modules/tgui/modules/_base.dm
index 7cece1c097..52891ab18d 100644
--- a/code/modules/tgui/modules/_base.dm
+++ b/code/modules/tgui/modules/_base.dm
@@ -78,52 +78,11 @@ Code is pretty much ripped verbatim from nano modules, but with un-needed stuff
return TRUE
// Just a nice little default interact in case the subtypes don't need any special behavior here
-/datum/tgui_module/tgui_interact(mob/user, datum/tgui/ui = null)
+/datum/tgui_module/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, tgui_id, name)
+ ui = new(user, src, tgui_id, name, parent_ui)
ui.open()
-// This is a helper for anything that wants to render the map.
-/datum/tgui_module/proc/get_plane_masters()
- . = list()
- // 'Utility' planes
- . += new /obj/screen/plane_master/fullbright //Lighting system (lighting_overlay objects)
- . += new /obj/screen/plane_master/lighting //Lighting system (but different!)
- . += new /obj/screen/plane_master/ghosts //Ghosts!
- . += new /obj/screen/plane_master{plane = PLANE_AI_EYE} //AI Eye!
-
- . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS} //Status is the synth/human icon left side of medhuds
- . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH} //Health bar
- . += new /obj/screen/plane_master{plane = PLANE_CH_LIFE} //Alive-or-not icon
- . += new /obj/screen/plane_master{plane = PLANE_CH_ID} //Job ID icon
- . += new /obj/screen/plane_master{plane = PLANE_CH_WANTED} //Wanted status
- . += new /obj/screen/plane_master{plane = PLANE_CH_IMPLOYAL} //Loyalty implants
- . += new /obj/screen/plane_master{plane = PLANE_CH_IMPTRACK} //Tracking implants
- . += new /obj/screen/plane_master{plane = PLANE_CH_IMPCHEM} //Chemical implants
- . += new /obj/screen/plane_master{plane = PLANE_CH_SPECIAL} //"Special" role stuff
- . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_OOC} //OOC status HUD
-
- . += new /obj/screen/plane_master{plane = PLANE_ADMIN1} //For admin use
- . += new /obj/screen/plane_master{plane = PLANE_ADMIN2} //For admin use
- . += new /obj/screen/plane_master{plane = PLANE_ADMIN3} //For admin use
-
- . += new /obj/screen/plane_master{plane = PLANE_MESONS} //Meson-specific things like open ceilings.
- . += new /obj/screen/plane_master{plane = PLANE_BUILDMODE} //Things that only show up while in build mode
-
- // Real tangible stuff planes
- . += new /obj/screen/plane_master/main{plane = TURF_PLANE}
- . += new /obj/screen/plane_master/main{plane = OBJ_PLANE}
- . += new /obj/screen/plane_master/main{plane = MOB_PLANE}
- . += new /obj/screen/plane_master/cloaked //Cloaked atoms!
-
- //VOREStation Add - Random other plane masters
- . += new /obj/screen/plane_master{plane = PLANE_CH_STATUS_R} //Right-side status icon
- . += new /obj/screen/plane_master{plane = PLANE_CH_HEALTH_VR} //Health bar but transparent at 100
- . += new /obj/screen/plane_master{plane = PLANE_CH_BACKUP} //Backup implant status
- . += new /obj/screen/plane_master{plane = PLANE_CH_VANTAG} //Vore Antags
- . += new /obj/screen/plane_master{plane = PLANE_AUGMENTED} //Augmented reality
- //VOREStation Add End
-
/datum/tgui_module/proc/relaymove(mob/user, direction)
return FALSE
diff --git a/code/modules/tgui/modules/admin_shuttle_controller.dm b/code/modules/tgui/modules/admin_shuttle_controller.dm
new file mode 100644
index 0000000000..20e5c47367
--- /dev/null
+++ b/code/modules/tgui/modules/admin_shuttle_controller.dm
@@ -0,0 +1,102 @@
+/* This is an admin tool to control all shuttles, including overmap & classic. */
+
+/datum/tgui_module/admin_shuttle_controller
+ name = "Admin Shuttle Controller"
+ tgui_id = "AdminShuttleController"
+
+/datum/tgui_module/admin_shuttle_controller/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ var/list/shuttles = list()
+ for(var/shuttle_name in SSshuttles.shuttles)
+ var/datum/shuttle/S = SSshuttles.shuttles[shuttle_name]
+ shuttles.Add(list(list(
+ "name" = shuttle_name,
+ "ref" = REF(S),
+ "current_location" = S.get_location_name(),
+ "status" = S.moving_status,
+ )))
+ data["shuttles"] = shuttles
+
+ var/list/overmap_ships = list()
+ for(var/ship in SSshuttles.ships)
+ var/obj/effect/overmap/visitable/ship/S = ship
+ overmap_ships.Add(list(list(
+ "name" = S.scanner_name || S.name,
+ "ref" = REF(S),
+ )))
+ data["overmap_ships"] = overmap_ships
+
+ return data
+
+/datum/tgui_module/admin_shuttle_controller/tgui_state(mob/user)
+ return GLOB.tgui_admin_state
+
+/datum/tgui_module/admin_shuttle_controller/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
+ if(..())
+ return
+
+ switch(action)
+ if("adminobserve")
+ var/datum/shuttle/S = locate(params["ref"])
+ if(istype(S))
+ var/client/C = usr.client
+ if(!isobserver(usr))
+ C.admin_ghost()
+ spawn(2)
+ C.jumptoturf(get_turf(S.current_location))
+ else if(istype(S, /obj/effect/overmap/visitable))
+ var/obj/effect/overmap/visitable/V = S
+ var/client/C = usr.client
+ if(!isobserver(usr))
+ C.admin_ghost()
+ spawn(2)
+ var/atom/target
+ if(LAZYLEN(V.generic_waypoints))
+ target = V.generic_waypoints[1]
+ else if(LAZYLEN(V.restricted_waypoints))
+ target = V.restricted_waypoints[1]
+ else
+ to_chat(C, "Unable to jump to [V].")
+ return FALSE
+ var/turf/T = get_turf(target)
+ if(!istype(T))
+ to_chat(C, "Unable to jump to [V].")
+ return FALSE
+ C.jumptoturf(T)
+ return TRUE
+ if("classicmove")
+ var/datum/shuttle/S = locate(params["ref"])
+ if(istype(S, /datum/shuttle/autodock/multi))
+ var/datum/shuttle/autodock/multi/shuttle = S
+ var/dest_key = input("Choose shuttle destination", "Shuttle Destination") as null|anything in shuttle.get_destinations()
+ if(dest_key)
+ shuttle.set_destination(dest_key, usr)
+ shuttle.launch(src)
+ else if(istype(S, /datum/shuttle/autodock/overmap))
+ var/datum/shuttle/autodock/overmap/shuttle = S
+ var/list/possible_d = shuttle.get_possible_destinations()
+ var/D
+ if(!LAZYLEN(possible_d))
+ to_chat(usr, "There are no possible destinations for [shuttle] ([shuttle.type])")
+ return FALSE
+ D = input("Choose shuttle destination", "Shuttle Destination") as null|anything in possible_d
+ if(D)
+ shuttle.set_destination(possible_d[D])
+ shuttle.launch()
+ else if(istype(S, /datum/shuttle/autodock))
+ var/datum/shuttle/autodock/shuttle = S
+ if(alert(usr, "Are you sure you want to launch [shuttle]?", "Launching Shuttle", "Yes", "No") == "Yes")
+ shuttle.launch(src)
+ else
+ to_chat(usr, "The shuttle control panel isn't quite sure how to move [S] ([S?.type]).")
+ return FALSE
+ to_chat(usr, "Launching shuttle [S].")
+ return TRUE
+ if("overmap_control")
+ var/obj/effect/overmap/visitable/ship/V = locate(params["ref"])
+ if(istype(V))
+ var/datum/tgui_module/ship/fullmonty/F = new(src, V)
+ F.tgui_interact(usr, null, ui)
+
+ return TRUE
diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm
index ab75e1d67c..180e63ec7d 100644
--- a/code/modules/tgui/modules/appearance_changer.dm
+++ b/code/modules/tgui/modules/appearance_changer.dm
@@ -20,10 +20,8 @@
var/list/cam_plane_masters
var/obj/screen/background/cam_background
var/obj/screen/skybox/local_skybox
- // Needed for moving camera support
- var/camera_diff_x = -1
- var/camera_diff_y = -1
- var/camera_diff_z = -1
+ // Stuff for moving cameras
+ var/turf/last_camera_turf
var/list/valid_earstyles = list()
var/list/valid_tailstyles = list()
@@ -45,7 +43,7 @@
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
- cam_plane_masters = get_plane_masters()
+ cam_plane_masters = get_tgui_plane_masters()
for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane
@@ -62,14 +60,18 @@
cam_background = new
cam_background.assigned_map = map_name
cam_background.del_on_map_removal = FALSE
- reload_cameraview()
+ update_active_camera_screen()
owner = H
+ if(owner)
+ GLOB.moved_event.register(owner, src, .proc/update_active_camera_screen)
check_whitelist = check_species_whitelist
whitelist = species_whitelist
blacklist = species_blacklist
/datum/tgui_module/appearance_changer/Destroy()
+ GLOB.moved_event.unregister(owner, src, .proc/update_active_camera_screen)
+ last_camera_turf = null
qdel(cam_screen)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
@@ -286,8 +288,8 @@
return
ui = SStgui.try_update_ui(user, src, ui)
+ update_active_camera_screen()
if(!ui)
- reload_cameraview()
// Register map objects
user.client.register_map_obj(cam_screen)
for(var/plane in cam_plane_masters)
@@ -332,7 +334,7 @@
/datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
var/list/data = ..()
- differential_check()
+ generate_data(check_whitelist, whitelist, blacklist)
var/mob/living/carbon/human/target = owner
if(customize_usr)
@@ -402,26 +404,15 @@
data["mapRef"] = map_name
return data
-/datum/tgui_module/appearance_changer/proc/differential_check()
- var/turf/T = get_turf(customize_usr ? tgui_host() : owner)
- if(T)
- var/new_x = T.x
- var/new_y = T.y
- var/new_z = T.z
- if((new_x != camera_diff_x) || (new_y != camera_diff_y) || (new_z != camera_diff_z))
- reload_cameraview()
-
-/datum/tgui_module/appearance_changer/proc/reload_cameraview()
- var/turf/camTurf = get_turf(customize_usr ? tgui_host() : owner)
- if(!camTurf)
+/datum/tgui_module/appearance_changer/proc/update_active_camera_screen()
+ var/turf/newturf = get_turf(customize_usr ? tgui_host() : owner)
+ if(newturf == last_camera_turf)
return
- camera_diff_x = camTurf.x
- camera_diff_y = camTurf.y
- camera_diff_z = camTurf.z
+ last_camera_turf = newturf
var/list/visible_turfs = list()
- for(var/turf/T in range(1, camTurf))
+ for(var/turf/T in range(1, newturf))
visible_turfs += T
cam_screen.vis_contents = visible_turfs
@@ -429,9 +420,9 @@
cam_background.fill_rect(1, 1, 3, 3)
local_skybox.cut_overlays()
- local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf)))
+ local_skybox.add_overlay(SSskybox.get_skybox(get_z(newturf)))
local_skybox.scale_to_view(3)
- local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y)
+ local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - newturf.x, (world.maxy>>1) - newturf.y)
/datum/tgui_module/appearance_changer/proc/update_dna()
var/mob/living/carbon/human/target = owner
diff --git a/code/modules/tgui/modules/appearance_changer_vr.dm b/code/modules/tgui/modules/appearance_changer_vr.dm
index 93ee4cfdf2..d5ace1b7e7 100644
--- a/code/modules/tgui/modules/appearance_changer_vr.dm
+++ b/code/modules/tgui/modules/appearance_changer_vr.dm
@@ -10,7 +10,7 @@
return STATUS_CLOSE
return ..()
-/datum/tgui_module/appearance_changer/vore/reload_cameraview()
+/datum/tgui_module/appearance_changer/vore/update_active_camera_screen()
cam_screen.vis_contents = list(owner)
cam_background.icon_state = "clear"
cam_background.fill_rect(1, 1, 1, 1)
@@ -42,4 +42,4 @@
to_chat(O, "You tingle as you make noticeable changes to your captive's body.")
if(APPEARANCECHANGER_CHANGED_EYES)
to_chat(M, "You feel lightheaded and drowsy...")
- to_chat(O, "You feel warm as you make subtle changes to your captive's body.")
\ No newline at end of file
+ to_chat(O, "You feel warm as you make subtle changes to your captive's body.")
diff --git a/code/modules/tgui/modules/camera.dm b/code/modules/tgui/modules/camera.dm
index dbb3c7ae97..86eb923c1c 100644
--- a/code/modules/tgui/modules/camera.dm
+++ b/code/modules/tgui/modules/camera.dm
@@ -18,10 +18,8 @@
var/obj/screen/background/cam_background
var/obj/screen/background/cam_foreground
var/obj/screen/skybox/local_skybox
- // Needed for moving camera support
- var/camera_diff_x = -1
- var/camera_diff_y = -1
- var/camera_diff_z = -1
+ // Stuff for moving cameras
+ var/turf/last_camera_turf
/datum/tgui_module/camera/New(host, list/network_computer)
. = ..()
@@ -37,7 +35,7 @@
cam_screen.del_on_map_removal = FALSE
cam_screen.screen_loc = "[map_name]:1,1"
- cam_plane_masters = get_plane_masters()
+ cam_plane_masters = get_tgui_plane_masters()
for(var/plane in cam_plane_masters)
var/obj/screen/instance = plane
@@ -70,6 +68,10 @@
cam_foreground.add_overlay(noise)
/datum/tgui_module/camera/Destroy()
+ if(active_camera)
+ GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen)
+ active_camera = null
+ last_camera_turf = null
qdel(cam_screen)
QDEL_LIST(cam_plane_masters)
qdel(cam_background)
@@ -106,7 +108,6 @@
var/list/data = list()
data["activeCamera"] = null
if(active_camera)
- differential_check()
data["activeCamera"] = list(
name = active_camera.c_tag,
status = active_camera.status,
@@ -139,9 +140,12 @@
var/c_tag = params["name"]
var/list/cameras = get_available_cameras(usr)
var/obj/machinery/camera/C = cameras["[ckey(c_tag)]"]
+ if(active_camera)
+ GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen)
active_camera = C
+ GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen)
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
- reload_cameraview()
+ update_active_camera_screen()
return TRUE
if(action == "pan")
@@ -163,36 +167,34 @@
target = C
if(target)
+ if(active_camera)
+ GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen)
active_camera = target
+ GLOB.moved_event.register(active_camera, src, .proc/update_active_camera_screen)
playsound(tgui_host(), get_sfx("terminal_type"), 25, FALSE)
- reload_cameraview()
+ update_active_camera_screen()
. = TRUE
-/datum/tgui_module/camera/proc/differential_check()
- var/turf/T = get_turf(active_camera)
- if(T)
- var/new_x = T.x
- var/new_y = T.y
- var/new_z = T.z
- if((new_x != camera_diff_x) || (new_y != camera_diff_y) || (new_z != camera_diff_z))
- reload_cameraview()
-
-/datum/tgui_module/camera/proc/reload_cameraview()
+/datum/tgui_module/camera/proc/update_active_camera_screen()
// Show static if can't use the camera
if(!active_camera?.can_use())
show_camera_static()
return TRUE
- var/turf/camTurf = get_turf(active_camera)
+ // If we're not forcing an update for some reason and the cameras are in the same location,
+ // we don't need to update anything.
+ // Most security cameras will end here as they're not moving.
+ var/turf/newturf = get_turf(active_camera)
+ if(newturf == last_camera_turf)
+ return
- camera_diff_x = camTurf.x
- camera_diff_y = camTurf.y
- camera_diff_z = camTurf.z
+ // Cameras that get here are moving, and are likely attached to some moving atom such as cyborgs.
+ last_camera_turf = get_turf(active_camera)
var/list/visible_turfs = list()
for(var/turf/T in (active_camera.isXRay() \
- ? range(active_camera.view_range, camTurf) \
- : view(active_camera.view_range, camTurf)))
+ ? range(active_camera.view_range, newturf) \
+ : view(active_camera.view_range, newturf)))
visible_turfs += T
var/list/bbox = get_bbox_of_atoms(visible_turfs)
@@ -206,9 +208,9 @@
cam_foreground.fill_rect(1, 1, size_x, size_y)
local_skybox.cut_overlays()
- local_skybox.add_overlay(SSskybox.get_skybox(get_z(camTurf)))
+ local_skybox.add_overlay(SSskybox.get_skybox(get_z(newturf)))
local_skybox.scale_to_view(size_x)
- local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - camTurf.x, (world.maxy>>1) - camTurf.y)
+ local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - newturf.x, (world.maxy>>1) - newturf.y)
// Returns the list of cameras accessible from this computer
// This proc operates in two distinct ways depending on the context in which the module is created.
@@ -271,6 +273,8 @@
user.client.clear_map(map_name)
// Turn off the console
if(length(concurrent_users) == 0 && is_living)
+ if(active_camera)
+ GLOB.moved_event.unregister(active_camera, src, .proc/update_active_camera_screen)
active_camera = null
playsound(tgui_host(), 'sound/machines/terminal_off.ogg', 25, FALSE)
diff --git a/code/modules/tgui/modules/overmap.dm b/code/modules/tgui/modules/overmap.dm
index 3b157fc0c2..2e566e1267 100644
--- a/code/modules/tgui/modules/overmap.dm
+++ b/code/modules/tgui/modules/overmap.dm
@@ -52,7 +52,7 @@
/datum/tgui_module/ship/proc/look(var/mob/user)
if(linked)
- user.set_machine(tgui_host())
+ user.set_machine(src)
user.reset_view(linked)
user.set_viewsize(world.view + extra_view)
GLOB.moved_event.register(user, src, /datum/tgui_module/ship/proc/unlook)
@@ -134,4 +134,327 @@
return TRUE
/datum/tgui_module/ship/nav/ntos
- ntos = TRUE
\ No newline at end of file
+ ntos = TRUE
+
+// Full monty control computer
+/datum/tgui_module/ship/fullmonty
+ name = "Full Monty Overmap Control"
+ tgui_id = "OvermapFull"
+ // HELM
+ var/autopilot = 0
+ var/autopilot_disabled = TRUE
+ var/list/known_sectors = list()
+ var/dx //desitnation
+ var/dy //coordinates
+ var/speedlimit = 1/(20 SECONDS) //top speed for autopilot, 5
+ var/accellimit = 0.001 //manual limiter for acceleration
+ // SENSORS
+ var/obj/machinery/shipsensors/sensors
+
+/datum/tgui_module/ship/fullmonty/tgui_state(mob/user)
+ return GLOB.tgui_admin_state
+
+/datum/tgui_module/ship/fullmonty/New(host, obj/effect/overmap/visitable/ship/new_linked)
+ . = ..()
+ if(!istype(new_linked))
+ CRASH("Warning, [new_linked] is not an overmap ship! Something went horribly wrong for [usr]!")
+ return
+ linked = new_linked
+ name = initial(name) + " ([linked.name])"
+ // HELM
+ var/area/overmap/map = locate() in world
+ for(var/obj/effect/overmap/visitable/sector/S in map)
+ if(S.known)
+ var/datum/computer_file/data/waypoint/R = new()
+ R.fields["name"] = S.name
+ R.fields["x"] = S.x
+ R.fields["y"] = S.y
+ known_sectors[S.name] = R
+ // SENSORS
+ for(var/obj/machinery/shipsensors/S in global.machines)
+ if(linked.check_ownership(S))
+ sensors = S
+ break
+
+/datum/tgui_module/ship/fullmonty/relaymove(var/mob/user, direction)
+ if(viewing_overmap(user) && linked)
+ direction = turn(direction,pick(90,-90))
+ linked.relaymove(user, direction, accellimit)
+ return 1
+ return ..()
+
+// Beware ye eyes. This holds all of the data from helm, engine, and sensor control all at once.
+/datum/tgui_module/ship/fullmonty/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state)
+ var/list/data = ..()
+
+ // HELM
+ var/turf/T = get_turf(linked)
+ var/obj/effect/overmap/visitable/sector/current_sector = locate() in T
+
+ data["sector"] = current_sector ? current_sector.name : "Deep Space"
+ data["sector_info"] = current_sector ? current_sector.desc : "Not Available"
+ data["landed"] = linked.get_landed_info()
+ data["s_x"] = linked.x
+ data["s_y"] = linked.y
+ data["dest"] = dy && dx
+ data["d_x"] = dx
+ data["d_y"] = dy
+ data["speedlimit"] = speedlimit ? speedlimit*1000 : "Halted"
+ data["accel"] = min(round(linked.get_acceleration()*1000, 0.01),accellimit*1000)
+ data["heading"] = linked.get_heading_degrees()
+ data["autopilot_disabled"] = autopilot_disabled
+ data["autopilot"] = autopilot
+ data["manual_control"] = viewing_overmap(user)
+ data["canburn"] = linked.can_burn()
+ data["accellimit"] = accellimit*1000
+
+ var/speed = round(linked.get_speed()*1000, 0.01)
+ var/speed_color = null
+ if(linked.get_speed() < SHIP_SPEED_SLOW)
+ speed_color = "good"
+ if(linked.get_speed() > SHIP_SPEED_FAST)
+ speed_color = "average"
+ data["speed"] = speed
+ data["speed_color"] = speed_color
+
+ if(linked.get_speed())
+ data["ETAnext"] = "[round(linked.ETA()/10)] seconds"
+ else
+ data["ETAnext"] = "N/A"
+
+ var/list/locations[0]
+ for (var/key in known_sectors)
+ var/datum/computer_file/data/waypoint/R = known_sectors[key]
+ var/list/rdata[0]
+ rdata["name"] = R.fields["name"]
+ rdata["x"] = R.fields["x"]
+ rdata["y"] = R.fields["y"]
+ rdata["reference"] = "\ref[R]"
+ locations.Add(list(rdata))
+
+ data["locations"] = locations
+
+ // ENGINES
+ data["global_state"] = linked.engines_state
+ data["global_limit"] = round(linked.thrust_limit*100)
+ var/total_thrust = 0
+
+ var/list/enginfo = list()
+ for(var/datum/ship_engine/E in linked.engines)
+ var/list/rdata = list()
+ rdata["eng_type"] = E.name
+ rdata["eng_on"] = E.is_on()
+ rdata["eng_thrust"] = E.get_thrust()
+ rdata["eng_thrust_limiter"] = round(E.get_thrust_limit()*100)
+ var/list/status = E.get_status()
+ if(!islist(status))
+ log_runtime(EXCEPTION("Warning, ship [E.name] (\ref[E]) for [linked.name] returned a non-list status!"))
+ status = list("Error")
+ rdata["eng_status"] = status
+ rdata["eng_reference"] = "\ref[E]"
+ total_thrust += E.get_thrust()
+ enginfo.Add(list(rdata))
+
+ data["engines_info"] = enginfo
+ data["total_thrust"] = total_thrust
+
+ // SENSORS
+ data["viewing"] = viewing_overmap(user)
+ data["on"] = 0
+ data["range"] = "N/A"
+ data["health"] = 0
+ data["max_health"] = 0
+ data["heat"] = 0
+ data["critical_heat"] = 0
+ data["status"] = "MISSING"
+ data["contacts"] = list()
+
+ if(sensors)
+ data["on"] = sensors.use_power
+ data["range"] = sensors.range
+ data["health"] = sensors.health
+ data["max_health"] = sensors.max_health
+ data["heat"] = sensors.heat
+ data["critical_heat"] = sensors.critical_heat
+ if(sensors.health == 0)
+ data["status"] = "DESTROYED"
+ else if(!sensors.powered())
+ data["status"] = "NO POWER"
+ else if(!sensors.in_vacuum())
+ data["status"] = "VACUUM SEAL BROKEN"
+ else
+ data["status"] = "OK"
+ var/list/contacts = list()
+ for(var/obj/effect/overmap/O in view(7,linked))
+ if(linked == O)
+ continue
+ if(!O.scannable)
+ continue
+ var/bearing = round(90 - ATAN2(O.x - linked.x, O.y - linked.y),5)
+ if(bearing < 0)
+ bearing += 360
+ contacts.Add(list(list("name"=O.name, "ref"="\ref[O]", "bearing"=bearing)))
+ data["contacts"] = contacts
+
+
+ return data
+
+// Beware ye eyes. This holds all of the ACTIONS from helm, engine, and sensor control all at once.
+/datum/tgui_module/ship/fullmonty/tgui_act(action, params)
+ if(..())
+ return TRUE
+
+ switch(action)
+ /* HELM */
+ if("add")
+ var/datum/computer_file/data/waypoint/R = new()
+ var/sec_name = input("Input navigation entry name", "New navigation entry", "Sector #[known_sectors.len]") as text
+ if(!sec_name)
+ sec_name = "Sector #[known_sectors.len]"
+ R.fields["name"] = sec_name
+ if(sec_name in known_sectors)
+ to_chat(usr, "Sector with that name already exists, please input a different name.")
+ return TRUE
+ switch(params["add"])
+ if("current")
+ R.fields["x"] = linked.x
+ R.fields["y"] = linked.y
+ if("new")
+ var/newx = input("Input new entry x coordinate", "Coordinate input", linked.x) as num
+ var/newy = input("Input new entry y coordinate", "Coordinate input", linked.y) as num
+ R.fields["x"] = CLAMP(newx, 1, world.maxx)
+ R.fields["y"] = CLAMP(newy, 1, world.maxy)
+ known_sectors[sec_name] = R
+ . = TRUE
+
+ if("remove")
+ var/datum/computer_file/data/waypoint/R = locate(params["remove"])
+ if(R)
+ known_sectors.Remove(R.fields["name"])
+ qdel(R)
+ . = TRUE
+
+ if("setcoord")
+ if(params["setx"])
+ var/newx = input("Input new destiniation x coordinate", "Coordinate input", dx) as num|null
+ if(newx)
+ dx = CLAMP(newx, 1, world.maxx)
+
+ if(params["sety"])
+ var/newy = input("Input new destiniation y coordinate", "Coordinate input", dy) as num|null
+ if(newy)
+ dy = CLAMP(newy, 1, world.maxy)
+ . = TRUE
+
+ if("setds")
+ dx = text2num(params["x"])
+ dy = text2num(params["y"])
+ . = TRUE
+
+ if("reset")
+ dx = 0
+ dy = 0
+ . = TRUE
+
+ if("speedlimit")
+ var/newlimit = input("Input new speed limit for autopilot (0 to brake)", "Autopilot speed limit", speedlimit*1000) as num|null
+ if(newlimit)
+ speedlimit = CLAMP(newlimit/1000, 0, 100)
+ . = TRUE
+
+ if("accellimit")
+ var/newlimit = input("Input new acceleration limit", "Acceleration limit", accellimit*1000) as num|null
+ if(newlimit)
+ accellimit = max(newlimit/1000, 0)
+ . = TRUE
+
+ if("move")
+ var/ndir = text2num(params["dir"])
+ ndir = turn(ndir,pick(90,-90))
+ linked.relaymove(usr, ndir, accellimit)
+ . = TRUE
+
+ if("brake")
+ linked.decelerate()
+ . = TRUE
+
+ if("apilot")
+ if(autopilot_disabled)
+ autopilot = FALSE
+ else
+ autopilot = !autopilot
+ . = TRUE
+
+ if("apilot_lock")
+ autopilot_disabled = !autopilot_disabled
+ autopilot = FALSE
+ . = TRUE
+
+ if("manual")
+ viewing_overmap(usr) ? unlook(usr) : look(usr)
+ . = TRUE
+ /* END HELM */
+ /* ENGINES */
+ if("global_toggle")
+ linked.engines_state = !linked.engines_state
+ for(var/datum/ship_engine/E in linked.engines)
+ if(linked.engines_state == !E.is_on())
+ E.toggle()
+ . = TRUE
+
+ if("set_global_limit")
+ var/newlim = input("Input new thrust limit (0..100%)", "Thrust limit", linked.thrust_limit*100) as num
+ linked.thrust_limit = clamp(newlim/100, 0, 1)
+ for(var/datum/ship_engine/E in linked.engines)
+ E.set_thrust_limit(linked.thrust_limit)
+ . = TRUE
+
+ if("global_limit")
+ linked.thrust_limit = clamp(linked.thrust_limit + text2num(params["global_limit"]), 0, 1)
+ for(var/datum/ship_engine/E in linked.engines)
+ E.set_thrust_limit(linked.thrust_limit)
+ . = TRUE
+
+ if("set_limit")
+ var/datum/ship_engine/E = locate(params["engine"])
+ var/newlim = input("Input new thrust limit (0..100)", "Thrust limit", E.get_thrust_limit()) as num
+ var/limit = clamp(newlim/100, 0, 1)
+ if(istype(E))
+ E.set_thrust_limit(limit)
+ . = TRUE
+
+ if("limit")
+ var/datum/ship_engine/E = locate(params["engine"])
+ var/limit = clamp(E.get_thrust_limit() + text2num(params["limit"]), 0, 1)
+ if(istype(E))
+ E.set_thrust_limit(limit)
+ . = TRUE
+
+ if("toggle_engine")
+ var/datum/ship_engine/E = locate(params["engine"])
+ if(istype(E))
+ E.toggle()
+ . = TRUE
+ /* END ENGINES */
+ /* SENSORS */
+ if("range")
+ var/nrange = input("Set new sensors range", "Sensor range", sensors.range) as num|null
+ if(nrange)
+ sensors.set_range(CLAMP(nrange, 1, world.view))
+ . = TRUE
+ if("toggle_sensor")
+ sensors.toggle()
+ . = TRUE
+ if("viewing")
+ if(usr && !isAI(usr))
+ viewing_overmap(usr) ? unlook(usr) : look(usr)
+ . = TRUE
+ /* END SENSORS */
+
+// We don't want these to do anything.
+/datum/tgui_module/ship/fullmonty/sync_linked()
+ return
+/datum/tgui_module/ship/fullmonty/attempt_hook_up_recursive()
+ return
+/datum/tgui_module/ship/fullmonty/attempt_hook_up()
+ return
\ No newline at end of file
diff --git a/code/modules/tgui/states/admin.dm b/code/modules/tgui/states/admin.dm
index 6d1c680927..7009f6f884 100644
--- a/code/modules/tgui/states/admin.dm
+++ b/code/modules/tgui/states/admin.dm
@@ -7,6 +7,6 @@
GLOBAL_DATUM_INIT(tgui_admin_state, /datum/tgui_state/admin_state, new)
/datum/tgui_state/admin_state/can_use_topic(src_object, mob/user)
- if(check_rights_for(user.client, R_ADMIN))
+ if(check_rights_for(user.client, R_ADMIN|R_EVENT))
return STATUS_INTERACTIVE
return STATUS_CLOSE
diff --git a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm
index adcb66c180..db65f1dfa5 100644
--- a/code/modules/vore/appearance/sprite_accessories_taur_vr.dm
+++ b/code/modules/vore/appearance/sprite_accessories_taur_vr.dm
@@ -592,6 +592,28 @@
name = "Shadekin Long Tail"
icon_state = "shadekin_long_s"
+/datum/sprite_accessory/tail/taur/pawcow // this grabs suit sprites from the normal cow, the outline is the same except for the tail
+ name = "Cow w/ paws (Taur)"
+ icon_state = "pawcow_s"
+ extra_overlay = "pawcow_markings"
+ suit_sprites = 'icons/mob/taursuits_cow_vr.dmi'
+ icon_sprite_tag = "pawcow"
+
+ msg_owner_disarm_run = "You quickly push %prey to the ground with your paw!"
+ msg_prey_disarm_run = "%owner pushes you down to the ground with their paw!"
+
+ msg_owner_disarm_walk = "You firmly push your paw down on %prey, painfully but harmlessly pinning them to the ground!"
+ msg_prey_disarm_walk = "%owner firmly pushes their paw down on you, quite painfully but harmlessly pinning you to the ground!"
+
+ msg_owner_harm_walk = "You methodically place your paw down upon %prey's body, slowly applying pressure, crushing them against the floor below!"
+ msg_prey_harm_walk = "%owner methodically places their paw upon your body, slowly applying pressure, crushing you against the floor below!"
+
+ msg_owner_grab_success = "You pin %prey to the ground before scooping them up with your paws!"
+ msg_prey_grab_success = "%owner pins you to the ground before scooping you up with their paws!"
+
+ msg_owner_grab_fail = "You step down onto %prey, squishing them and forcing them down to the ground!"
+ msg_prey_grab_fail = "%owner steps down and squishes you with their paw, forcing you down to the ground!"
+
// Special snowflake tails/taurhalves
//spoopylizz: Roiz Lizden
diff --git a/icons/_nanomaps/tether_nanomap_z1.png b/icons/_nanomaps/tether_nanomap_z1.png
index 9a0c9f85a8..ac33a420d8 100644
Binary files a/icons/_nanomaps/tether_nanomap_z1.png and b/icons/_nanomaps/tether_nanomap_z1.png differ
diff --git a/icons/mob/hair_gradients.dmi b/icons/mob/hair_gradients.dmi
index 0f40b098e9..1a3c330355 100644
Binary files a/icons/mob/hair_gradients.dmi and b/icons/mob/hair_gradients.dmi differ
diff --git a/icons/mob/taursuits_cow_vr.dmi b/icons/mob/taursuits_cow_vr.dmi
index ce592d5edb..5ca5ebda39 100644
Binary files a/icons/mob/taursuits_cow_vr.dmi and b/icons/mob/taursuits_cow_vr.dmi differ
diff --git a/icons/mob/vore/taurs_vr.dmi b/icons/mob/vore/taurs_vr.dmi
index a724ddd57d..a00d6bb489 100644
Binary files a/icons/mob/vore/taurs_vr.dmi and b/icons/mob/vore/taurs_vr.dmi differ
diff --git a/icons/obj/contraband.dmi b/icons/obj/contraband.dmi
index 019e008e41..344ea5b357 100644
Binary files a/icons/obj/contraband.dmi and b/icons/obj/contraband.dmi differ
diff --git a/icons/obj/egg_new_vr.dmi b/icons/obj/egg_new_vr.dmi
index b9804d69ba..20f5b01fd3 100644
Binary files a/icons/obj/egg_new_vr.dmi and b/icons/obj/egg_new_vr.dmi differ
diff --git a/icons/obj/egg_open_vr.dmi b/icons/obj/egg_open_vr.dmi
index e3faabb4dc..2e21e6039d 100644
Binary files a/icons/obj/egg_open_vr.dmi and b/icons/obj/egg_open_vr.dmi differ
diff --git a/icons/obj/gun2.dmi b/icons/obj/gun2.dmi
index a5c13c95c4..6b81e46b90 100644
Binary files a/icons/obj/gun2.dmi and b/icons/obj/gun2.dmi differ
diff --git a/icons/obj/projectiles.dmi b/icons/obj/projectiles.dmi
index cb79babe03..b2b6e0a0c7 100644
Binary files a/icons/obj/projectiles.dmi and b/icons/obj/projectiles.dmi differ
diff --git a/icons/obj/projectiles_impact.dmi b/icons/obj/projectiles_impact.dmi
index 19f6d1db96..9d63ee10fc 100644
Binary files a/icons/obj/projectiles_impact.dmi and b/icons/obj/projectiles_impact.dmi differ
diff --git a/icons/obj/projectiles_muzzle.dmi b/icons/obj/projectiles_muzzle.dmi
index 6c9d8cf694..86a8b747d8 100644
Binary files a/icons/obj/projectiles_muzzle.dmi and b/icons/obj/projectiles_muzzle.dmi differ
diff --git a/icons/obj/projectiles_tracer.dmi b/icons/obj/projectiles_tracer.dmi
index 935e3afd21..a633df6735 100644
Binary files a/icons/obj/projectiles_tracer.dmi and b/icons/obj/projectiles_tracer.dmi differ
diff --git a/icons/obj/projectiles_vr.dmi b/icons/obj/projectiles_vr.dmi
index ecc3445c92..7cdadb1a0e 100644
Binary files a/icons/obj/projectiles_vr.dmi and b/icons/obj/projectiles_vr.dmi differ
diff --git a/icons/obj/turrets.dmi b/icons/obj/turrets.dmi
index 32346c5a5e..76b1fab1f3 100644
Binary files a/icons/obj/turrets.dmi and b/icons/obj/turrets.dmi differ
diff --git a/maps/submaps/admin_use_vr/kk_mercship.dmm b/maps/submaps/admin_use_vr/kk_mercship.dmm
index 6625ae8cc7..47817bb21f 100644
--- a/maps/submaps/admin_use_vr/kk_mercship.dmm
+++ b/maps/submaps/admin_use_vr/kk_mercship.dmm
@@ -4054,10 +4054,6 @@
},
/turf/simulated/floor/tiled/techfloor,
/area/ship/manta/hallways_port)
-"tv" = (
-/obj/item/ammo_magazine/m762m/ap,
-/turf/simulated/wall/rshull,
-/area/ship/manta/armoury_st)
"tx" = (
/obj/effect/floor_decal/techfloor{
dir = 1
@@ -8439,7 +8435,22 @@
/turf/simulated/floor/wood,
/area/ship/manta/barracks)
"Ou" = (
-/obj/item/weapon/gun/projectile/automatic/bullpup,
+/obj/effect/floor_decal/techfloor{
+ dir = 8
+ },
+/obj/structure/table/rack,
+/obj/item/device/camera_film,
+/obj/item/device/camera_film,
+/obj/item/device/camera_film,
+/obj/item/device/camera_film,
+/obj/item/device/camera_film,
+/obj/item/device/camera_film,
+/obj/item/device/camera,
+/obj/item/device/camera,
+/obj/item/device/camera,
+/obj/item/device/camera,
+/obj/item/device/camera,
+/obj/item/device/camera,
/turf/simulated/floor/tiled/techfloor,
/area/ship/manta/armoury_st)
"Ox" = (
@@ -18542,7 +18553,7 @@ ZT
ZT
ZT
ZT
-tv
+ZT
ZT
ZT
ZT
diff --git a/maps/submaps/engine_submaps/engine.dm b/maps/submaps/engine_submaps/engine.dm
index 02cbd71ebb..74db547fb2 100644
--- a/maps/submaps/engine_submaps/engine.dm
+++ b/maps/submaps/engine_submaps/engine.dm
@@ -18,7 +18,7 @@
/obj/effect/landmark/engine_loader/proc/annihilate_bounds()
var/deleted_atoms = 0
- admin_notice("Annihilating objects in engine loading locatation.", R_DEBUG)
+ admin_notice("Annihilating objects in engine loading location.", R_DEBUG)
var/list/turfs_to_clean = get_turfs_to_clean()
if(turfs_to_clean.len)
for(var/x in 1 to 2) // Requires two passes to get everything.
@@ -26,4 +26,4 @@
for(var/atom/movable/AM in T)
++deleted_atoms
qdel(AM)
- admin_notice("Annihilated [deleted_atoms] objects.", R_DEBUG)
\ No newline at end of file
+ admin_notice("Annihilated [deleted_atoms] objects.", R_DEBUG)
diff --git a/tgui/packages/tgui/interfaces/AdminShuttleController.js b/tgui/packages/tgui/interfaces/AdminShuttleController.js
new file mode 100644
index 0000000000..24e498e8d3
--- /dev/null
+++ b/tgui/packages/tgui/interfaces/AdminShuttleController.js
@@ -0,0 +1,89 @@
+import { sortBy } from 'common/collections';
+import { Fragment } from 'inferno';
+import { useBackend } from "../backend";
+import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section, Table } from "../components";
+import { Window } from "../layouts";
+
+export const AdminShuttleController = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ return (
+
+
+
+
+
+ );
+};
+
+export const ShuttleList = (props, context) => {
+ const { act, data } = useBackend(context);
+
+ const {
+ shuttles,
+ overmap_ships,
+ } = data;
+
+ return (
+
+
+