diff --git a/_maps/map_files/Deltastation/DeltaStation2.dmm b/_maps/map_files/Deltastation/DeltaStation2.dmm
index efae305c74..b2c40d5672 100644
--- a/_maps/map_files/Deltastation/DeltaStation2.dmm
+++ b/_maps/map_files/Deltastation/DeltaStation2.dmm
@@ -141054,9 +141054,9 @@ cHW
cHW
cHW
cOy
-cQx
-cRP
-cTA
+epl
+epm
+epn
cOy
cHW
cHS
@@ -155987,7 +155987,7 @@ esD
dAw
dBO
dDd
-esF
+esB
dFd
dGE
dHP
@@ -156238,13 +156238,13 @@ drs
dsS
drs
dvx
-esA
+esz
dyb
dzf
dAx
dBP
dDe
-esG
+esB
dFe
dGF
dHP
@@ -157015,7 +157015,7 @@ dzi
dAA
dBS
esE
-esH
+esB
dEY
dGA
dHP
diff --git a/code/__DEFINES/layers.dm b/code/__DEFINES/layers.dm
index d33eaa35da..ee9e97cbd9 100644
--- a/code/__DEFINES/layers.dm
+++ b/code/__DEFINES/layers.dm
@@ -20,6 +20,7 @@
#define LATTICE_LAYER 2.2
#define DISPOSAL_PIPE_LAYER 2.3
#define GAS_PIPE_HIDDEN_LAYER 2.35
+#define TURF_DECAL_LAYER 2.39
#define WIRE_LAYER 2.4
#define WIRE_TERMINAL_LAYER 2.45
#define GAS_SCRUBBER_LAYER 2.46
diff --git a/code/_onclick/hud/_defines.dm b/code/_onclick/hud/_defines.dm
index ce7014d765..28c7183d4a 100644
--- a/code/_onclick/hud/_defines.dm
+++ b/code/_onclick/hud/_defines.dm
@@ -1,162 +1,156 @@
-/*
- These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var.
-
- The short version:
-
- Everything is encoded as strings because apparently that's how Byond rolls.
-
- "1,1" is the bottom left square of the user's screen. This aligns perfectly with the turf grid.
- "1:2,3:4" is the square (1,3) with pixel offsets (+2, +4); slightly right and slightly above the turf grid.
- Pixel offsets are used so you don't perfectly hide the turf under them, that would be crappy.
-
- In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective
- screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your
- UI to scale with screen size.
-
- The size of the user's screen is defined by client.view (indirectly by world.view), in our case "15x15".
- Therefore, the top right corner (except during admin shenanigans) is at "15,15"
-*/
-
-//Lower left, persistent menu
-#define ui_inventory "WEST:6,SOUTH:5"
-
-//Middle left indicators
-#define ui_lingchemdisplay "WEST,CENTER-1:15"
-#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
-#define ui_crafting "12:-10,1:5"
-#define ui_building "12:-10,1:21"
-#define ui_language_menu "11:6,2:-11"
-
-#define ui_devilsouldisplay "WEST:6,CENTER-1:15"
-
-//Lower center, persistent menu
-#define ui_sstore1 "CENTER-5:10,SOUTH:5"
-#define ui_id "CENTER-4:12,SOUTH:5"
-#define ui_belt "CENTER-3:14,SOUTH:5"
-#define ui_back "CENTER-2:14,SOUTH:5"
-
-/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5)
- var/x_off = -(!(i % 2))
- var/y_off = round((i-1) / 2)
- return"CENTER+[x_off]:16,SOUTH+[y_off]:5"
-
-/proc/ui_equip_position(mob/M)
- var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5)
- return "CENTER:-16,SOUTH+[y_off+1]:5"
-
-/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5)
- var/x_off = which == 1 ? -1 : 0
- var/y_off = round((M.held_items.len-1) / 2)
- return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5"
-
-#define ui_storage1 "CENTER+1:18,SOUTH:5"
-#define ui_storage2 "CENTER+2:20,SOUTH:5"
-
-#define ui_borg_sensor "CENTER-3:16, SOUTH:5" //borgs
-#define ui_borg_lamp "CENTER-4:16, SOUTH:5" //borgs
-#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //borgs
-#define ui_inv1 "CENTER-2:16,SOUTH:5" //borgs
-#define ui_inv2 "CENTER-1 :16,SOUTH:5" //borgs
-#define ui_inv3 "CENTER :16,SOUTH:5" //borgs
-#define ui_borg_module "CENTER+1:16,SOUTH:5" //borgs
-#define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs
-#define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs
-#define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs
-#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs
-
-#define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey
-#define ui_monkey_mask "CENTER-4:14,SOUTH:5" //monkey
-#define ui_monkey_neck "CENTER-3:15,SOUTH:5" //monkey
-#define ui_monkey_back "CENTER-2:16,SOUTH:5" //monkey
-
-#define ui_alien_storage_l "CENTER-2:14,SOUTH:5"//alien
-#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"//alien
-#define ui_alien_language_menu "EAST-3:26,SOUTH:5" //alien
-
-#define ui_drone_drop "CENTER+1:18,SOUTH:5" //maintenance drones
-#define ui_drone_pull "CENTER+2:2,SOUTH:5" //maintenance drones
-#define ui_drone_storage "CENTER-2:14,SOUTH:5" //maintenance drones
-#define ui_drone_head "CENTER-3:14,SOUTH:5" //maintenance drones
-
-//Lower right, persistent menu
-#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
-#define ui_pull_resist "EAST-2:26,SOUTH+1:7"
-#define ui_movi "EAST-2:26,SOUTH:5"
-#define ui_acti "EAST-3:24,SOUTH:5"
-#define ui_zonesel "EAST-1:28,SOUTH:5"
-#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12)
-
-#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
-#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
-#define ui_borg_intents "EAST-2:26,SOUTH:5"
-
-
-//Upper-middle right (alerts)
-#define ui_alert1 "EAST-1:28,CENTER+5:27"
-#define ui_alert2 "EAST-1:28,CENTER+4:25"
-#define ui_alert3 "EAST-1:28,CENTER+3:23"
-#define ui_alert4 "EAST-1:28,CENTER+2:21"
-#define ui_alert5 "EAST-1:28,CENTER+1:19"
-
-
-//Middle right (status indicators)
-#define ui_healthdoll "EAST-1:28,CENTER-2:13"
-#define ui_health "EAST-1:28,CENTER-1:15"
-#define ui_internal "EAST-1:28,CENTER:17"
-
-//borgs
-#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator.
-
-//aliens
-#define ui_alien_health "EAST,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator.
-#define ui_alienplasmadisplay "EAST,CENTER-2:15"
-#define ui_alien_queen_finder "EAST,CENTER-3:15"
-
-//constructs
-#define ui_construct_pull "EAST,CENTER-2:15"
-#define ui_construct_health "EAST,CENTER:15" //same as borgs and humans
-
-// AI
-
-#define ui_ai_core "SOUTH:6,WEST"
-#define ui_ai_camera_list "SOUTH:6,WEST+1"
-#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
-#define ui_ai_camera_light "SOUTH:6,WEST+3"
-#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
-#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
-#define ui_ai_alerts "SOUTH:6,WEST+6"
-#define ui_ai_announcement "SOUTH:6,WEST+7"
-#define ui_ai_shuttle "SOUTH:6,WEST+8"
-#define ui_ai_state_laws "SOUTH:6,WEST+9"
-#define ui_ai_pda_send "SOUTH:6,WEST+10"
-#define ui_ai_pda_log "SOUTH:6,WEST+11"
-#define ui_ai_take_picture "SOUTH:6,WEST+12"
-#define ui_ai_view_images "SOUTH:6,WEST+13"
-#define ui_ai_sensor "SOUTH:6,WEST+14"
-
-//Pop-up inventory
-#define ui_shoes "WEST+1:8,SOUTH:5"
-
-#define ui_iclothing "WEST:6,SOUTH+1:7"
-#define ui_oclothing "WEST+1:8,SOUTH+1:7"
-#define ui_gloves "WEST+2:10,SOUTH+1:7"
-
-#define ui_glasses "WEST:6,SOUTH+3:11"
-#define ui_mask "WEST+1:8,SOUTH+2:9"
-#define ui_ears "WEST+2:10,SOUTH+2:9"
-#define ui_neck "WEST:6,SOUTH+2:9"
-#define ui_head "WEST+1:8,SOUTH+3:11"
-
-//Ghosts
-
-#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24"
-#define ui_ghost_orbit "SOUTH:6,CENTER-1:24"
-#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24"
-#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
-#define ui_ghost_pai "SOUTH: 6, CENTER+2:24"
-
-//Hand of God, god
-
-#define ui_deityhealth "EAST-1:28,CENTER-2:13"
-#define ui_deitypower "EAST-1:28,CENTER-1:15"
-#define ui_deityfollowers "EAST-1:28,CENTER:17"
+/*
+ These defines specificy screen locations. For more information, see the byond documentation on the screen_loc var.
+
+ The short version:
+
+ Everything is encoded as strings because apparently that's how Byond rolls.
+
+ "1,1" is the bottom left square of the user's screen. This aligns perfectly with the turf grid.
+ "1:2,3:4" is the square (1,3) with pixel offsets (+2, +4); slightly right and slightly above the turf grid.
+ Pixel offsets are used so you don't perfectly hide the turf under them, that would be crappy.
+
+ In addition, the keywords NORTH, SOUTH, EAST, WEST and CENTER can be used to represent their respective
+ screen borders. NORTH-1, for example, is the row just below the upper edge. Useful if you want your
+ UI to scale with screen size.
+
+ The size of the user's screen is defined by client.view (indirectly by world.view), in our case "15x15".
+ Therefore, the top right corner (except during admin shenanigans) is at "15,15"
+*/
+
+//Lower left, persistent menu
+#define ui_inventory "WEST:6,SOUTH:5"
+
+//Middle left indicators
+#define ui_lingchemdisplay "WEST,CENTER-1:15"
+#define ui_lingstingdisplay "WEST:6,CENTER-3:11"
+#define ui_crafting "12:-10,1:5"
+#define ui_building "12:-10,1:21"
+#define ui_language_menu "11:6,2:-11"
+
+#define ui_devilsouldisplay "WEST:6,CENTER-1:15"
+
+//Lower center, persistent menu
+#define ui_sstore1 "CENTER-5:10,SOUTH:5"
+#define ui_id "CENTER-4:12,SOUTH:5"
+#define ui_belt "CENTER-3:14,SOUTH:5"
+#define ui_back "CENTER-2:14,SOUTH:5"
+
+/proc/ui_hand_position(i) //values based on old hand ui positions (CENTER:-/+16,SOUTH:5)
+ var/x_off = -(!(i % 2))
+ var/y_off = round((i-1) / 2)
+ return"CENTER+[x_off]:16,SOUTH+[y_off]:5"
+
+/proc/ui_equip_position(mob/M)
+ var/y_off = round((M.held_items.len-1) / 2) //values based on old equip ui position (CENTER: +/-16,SOUTH+1:5)
+ return "CENTER:-16,SOUTH+[y_off+1]:5"
+
+/proc/ui_swaphand_position(mob/M, which = 1) //values based on old swaphand ui positions (CENTER: +/-16,SOUTH+1:5)
+ var/x_off = which == 1 ? -1 : 0
+ var/y_off = round((M.held_items.len-1) / 2)
+ return "CENTER+[x_off]:16,SOUTH+[y_off+1]:5"
+
+#define ui_storage1 "CENTER+1:18,SOUTH:5"
+#define ui_storage2 "CENTER+2:20,SOUTH:5"
+
+#define ui_borg_sensor "CENTER-3:16, SOUTH:5" //borgs
+#define ui_borg_lamp "CENTER-4:16, SOUTH:5" //borgs
+#define ui_borg_thrusters "CENTER-5:16, SOUTH:5" //borgs
+#define ui_inv1 "CENTER-2:16,SOUTH:5" //borgs
+#define ui_inv2 "CENTER-1 :16,SOUTH:5" //borgs
+#define ui_inv3 "CENTER :16,SOUTH:5" //borgs
+#define ui_borg_module "CENTER+1:16,SOUTH:5" //borgs
+#define ui_borg_store "CENTER+2:16,SOUTH:5" //borgs
+#define ui_borg_camera "CENTER+3:21,SOUTH:5" //borgs
+#define ui_borg_album "CENTER+4:21,SOUTH:5" //borgs
+#define ui_borg_language_menu "CENTER+4:21,SOUTH+1:5" //borgs
+
+#define ui_monkey_head "CENTER-5:13,SOUTH:5" //monkey
+#define ui_monkey_mask "CENTER-4:14,SOUTH:5" //monkey
+#define ui_monkey_neck "CENTER-3:15,SOUTH:5" //monkey
+#define ui_monkey_back "CENTER-2:16,SOUTH:5" //monkey
+
+#define ui_alien_storage_l "CENTER-2:14,SOUTH:5"//alien
+#define ui_alien_storage_r "CENTER+1:18,SOUTH:5"//alien
+#define ui_alien_language_menu "EAST-3:26,SOUTH:5" //alien
+
+#define ui_drone_drop "CENTER+1:18,SOUTH:5" //maintenance drones
+#define ui_drone_pull "CENTER+2:2,SOUTH:5" //maintenance drones
+#define ui_drone_storage "CENTER-2:14,SOUTH:5" //maintenance drones
+#define ui_drone_head "CENTER-3:14,SOUTH:5" //maintenance drones
+
+//Lower right, persistent menu
+#define ui_drop_throw "EAST-1:28,SOUTH+1:7"
+#define ui_pull_resist "EAST-2:26,SOUTH+1:7"
+#define ui_movi "EAST-2:26,SOUTH:5"
+#define ui_acti "EAST-3:24,SOUTH:5"
+#define ui_zonesel "EAST-1:28,SOUTH:5"
+#define ui_acti_alt "EAST-1:28,SOUTH:5" //alternative intent switcher for when the interface is hidden (F12)
+
+#define ui_borg_pull "EAST-2:26,SOUTH+1:7"
+#define ui_borg_radio "EAST-1:28,SOUTH+1:7"
+#define ui_borg_intents "EAST-2:26,SOUTH:5"
+
+
+//Upper-middle right (alerts)
+#define ui_alert1 "EAST-1:28,CENTER+5:27"
+#define ui_alert2 "EAST-1:28,CENTER+4:25"
+#define ui_alert3 "EAST-1:28,CENTER+3:23"
+#define ui_alert4 "EAST-1:28,CENTER+2:21"
+#define ui_alert5 "EAST-1:28,CENTER+1:19"
+
+
+//Middle right (status indicators)
+#define ui_healthdoll "EAST-1:28,CENTER-2:13"
+#define ui_health "EAST-1:28,CENTER-1:15"
+#define ui_internal "EAST-1:28,CENTER:17"
+
+//borgs
+#define ui_borg_health "EAST-1:28,CENTER-1:15" //borgs have the health display where humans have the pressure damage indicator.
+
+//aliens
+#define ui_alien_health "EAST,CENTER-1:15" //aliens have the health display where humans have the pressure damage indicator.
+#define ui_alienplasmadisplay "EAST,CENTER-2:15"
+#define ui_alien_queen_finder "EAST,CENTER-3:15"
+
+//constructs
+#define ui_construct_pull "EAST,CENTER-2:15"
+#define ui_construct_health "EAST,CENTER:15" //same as borgs and humans
+
+// AI
+
+#define ui_ai_core "SOUTH:6,WEST"
+#define ui_ai_camera_list "SOUTH:6,WEST+1"
+#define ui_ai_track_with_camera "SOUTH:6,WEST+2"
+#define ui_ai_camera_light "SOUTH:6,WEST+3"
+#define ui_ai_crew_monitor "SOUTH:6,WEST+4"
+#define ui_ai_crew_manifest "SOUTH:6,WEST+5"
+#define ui_ai_alerts "SOUTH:6,WEST+6"
+#define ui_ai_announcement "SOUTH:6,WEST+7"
+#define ui_ai_shuttle "SOUTH:6,WEST+8"
+#define ui_ai_state_laws "SOUTH:6,WEST+9"
+#define ui_ai_pda_send "SOUTH:6,WEST+10"
+#define ui_ai_pda_log "SOUTH:6,WEST+11"
+#define ui_ai_take_picture "SOUTH:6,WEST+12"
+#define ui_ai_view_images "SOUTH:6,WEST+13"
+#define ui_ai_sensor "SOUTH:6,WEST+14"
+
+//Pop-up inventory
+#define ui_shoes "WEST+1:8,SOUTH:5"
+
+#define ui_iclothing "WEST:6,SOUTH+1:7"
+#define ui_oclothing "WEST+1:8,SOUTH+1:7"
+#define ui_gloves "WEST+2:10,SOUTH+1:7"
+
+#define ui_glasses "WEST:6,SOUTH+3:11"
+#define ui_mask "WEST+1:8,SOUTH+2:9"
+#define ui_ears "WEST+2:10,SOUTH+2:9"
+#define ui_neck "WEST:6,SOUTH+2:9"
+#define ui_head "WEST+1:8,SOUTH+3:11"
+
+//Ghosts
+
+#define ui_ghost_jumptomob "SOUTH:6,CENTER-2:24"
+#define ui_ghost_orbit "SOUTH:6,CENTER-1:24"
+#define ui_ghost_reenter_corpse "SOUTH:6,CENTER:24"
+#define ui_ghost_teleport "SOUTH:6,CENTER+1:24"
+#define ui_ghost_pai "SOUTH: 6, CENTER+2:24"
diff --git a/code/_onclick/hud/hud.dm b/code/_onclick/hud/hud.dm
index 8c1c5a983c..a212d46f99 100644
--- a/code/_onclick/hud/hud.dm
+++ b/code/_onclick/hud/hud.dm
@@ -23,9 +23,6 @@
var/obj/screen/devil/soul_counter/devilsouldisplay
- var/obj/screen/deity_power_display
- var/obj/screen/deity_follower_display
-
var/obj/screen/nightvisionicon
var/obj/screen/action_intent
var/obj/screen/zone_select
@@ -114,8 +111,6 @@
blobpwrdisplay = null
alien_plasma_display = null
alien_queen_finder = null
- deity_power_display = null
- deity_follower_display = null
nightvisionicon = null
if(plane_masters.len)
diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 0906a099a0..397e04a4f5 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -485,11 +485,6 @@
icon = 'icons/mob/screen_cyborg.dmi'
screen_loc = ui_borg_health
-/obj/screen/healths/deity
- name = "Nexus Health"
- icon_state = "deity_nexus"
- screen_loc = ui_deityhealth
-
/obj/screen/healths/blob
name = "blob health"
icon_state = "block"
diff --git a/code/citadel/_cit_helpers.dm b/code/citadel/_cit_helpers.dm
index f20e59b0b6..9ac58122fe 100644
--- a/code/citadel/_cit_helpers.dm
+++ b/code/citadel/_cit_helpers.dm
@@ -93,6 +93,9 @@ GLOBAL_LIST_EMPTY(mentors)
GLOBAL_VAR_INIT(looc_allowed, 1)
GLOBAL_VAR_INIT(dlooc_allowed, 1)
+//Crew objective and miscreants stuff
+GLOBAL_VAR_INIT(miscreants_allowed, FALSE)
+
/client/proc/reload_mentors()
set name = "Reload Mentors"
set category = "Admin"
diff --git a/code/citadel/cit_crewobjectives.dm b/code/citadel/cit_crewobjectives.dm
new file mode 100644
index 0000000000..2d9149f5b1
--- /dev/null
+++ b/code/citadel/cit_crewobjectives.dm
@@ -0,0 +1,44 @@
+/datum/controller/subsystem/ticker/proc/generate_crew_objectives()
+ for(var/datum/mind/crewMind in SSticker.minds)
+ if(prob(2) && !issilicon(crewMind.current) && !jobban_isbanned(crewMind, "Syndicate") && GLOB.miscreants_allowed)
+ generate_miscreant_objectives(crewMind)
+ else
+ if(CONFIG_GET(flag/allow_crew_objectives))
+ generate_individual_objectives(crewMind)
+ return
+
+/datum/controller/subsystem/ticker/proc/generate_individual_objectives(var/datum/mind/crewMind)
+ if(!(CONFIG_GET(flag/allow_crew_objectives)))
+ return
+ if(!crewMind)
+ return
+ if(!crewMind.current || !crewMind.objectives || crewMind.special_role)
+ return
+ if(!crewMind.assigned_role)
+ return
+ var/list/validobjs = get_valid_crew_objs(ckey(crewMind.assigned_role))
+ if(!validobjs || !validobjs.len)
+ return
+ var/selectedObj = pick(validobjs)
+ var/datum/objective/crew/newObjective = new selectedObj
+ if(!newObjective)
+ return
+ newObjective.owner = crewMind
+ crewMind.objectives += newObjective
+ to_chat(crewMind, "Your objective: [newObjective.explanation_text]")
+
+/datum/controller/subsystem/ticker/proc/get_valid_crew_objs(var/job = "")//taken from old hippie with adjustments
+ var/list/objpaths = typesof(/datum/objective/crew)
+ var/list/objlist = list()
+ for(var/hoorayhackyshit in objpaths)
+ var/datum/objective/crew/obj = hoorayhackyshit //dm is not a sane language in any way, shape, or form.
+ var/list/availableto = splittext(initial(obj.jobs),",")
+ if(job in availableto)
+ objlist += obj
+ return objlist
+
+/datum/objective/crew/
+ var/jobs = ""
+ explanation_text = "Yell on the development discussion channel on Citadels discord if this ever shows up. Something just broke here, dude"
+
+/datum/objective/crew/proc/setup()
diff --git a/code/citadel/cit_guns.dm b/code/citadel/cit_guns.dm
index 73a98db4e3..7127371830 100644
--- a/code/citadel/cit_guns.dm
+++ b/code/citadel/cit_guns.dm
@@ -182,7 +182,7 @@
multiple_sprites = 2
/obj/item/gun/ballistic/automatic/x9/toy
- name = "donksoft X9"
+ name = "\improper Foam Force X9"
desc = "An old but reliable assault rifle made for combat against unknown enemies. Appears to be hastily converted. Ages 8 and up."
icon = 'icons/obj/guns/cit_guns.dmi'
icon_state = "toy9"
@@ -192,6 +192,14 @@
casing_ejector = 0
spread = 45 //MAXIMUM XCOM MEMES (actually that'd be 90 spread)
+/datum/design/foam_x9
+ name = "Foam Force X9 Rifle"
+ id = "foam_x9"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 20000, MAT_GLASS = 10000)
+ build_path = /obj/item/gun/ballistic/automatic/x9/toy
+ category = list("hacked", "Misc")
+
////////XCOM2 Magpistol/////////
@@ -470,6 +478,34 @@
materials = list(MAT_METAL = 6000, MAT_SILVER = 500, MAT_TITANIUM = 500)
build_path = /obj/item/ammo_box/magazine/mmag
+///foamagrifle///
+
+/obj/item/ammo_box/magazine/toy/foamag
+ name = "foam force magrifle magazine"
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "foamagmag"
+ max_ammo = 15
+ multiple_sprites = 2
+ ammo_type = /obj/item/ammo_casing/caseless/foam_dart/mag
+
+/obj/item/gun/ballistic/automatic/magrifle/toy
+ name = "foamag rifle"
+ desc = "A foam launching magnetic rifle. Ages 8 and up."
+ icon_state = "foamagrifle"
+ needs_permit = 0
+ mag_type = /obj/item/ammo_box/magazine/toy/foamag
+ casing_ejector = FALSE
+ origin_tech = "combat=2;engineering=2;magnets=2"
+
+/datum/design/foam_magrifle
+ name = "Foam Force MagRifle"
+ id = "foam_magrifle"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 15000, MAT_GLASS = 7500)
+ build_path = /obj/item/gun/ballistic/automatic/magrifle/toy
+ category = list("hacked", "Misc")
+
+
//////Hyper-Burst Rifle//////
///projectiles///
@@ -556,6 +592,53 @@
..()
icon_state = "hyperburst[magazine ? "-[get_ammo()]" : ""][chambered ? "" : "-e"]"
+///toy memes///
+
+/obj/item/projectile/beam/lasertag/mag //the projectile, compatible with regular laser tag armor
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "magjectile-toy"
+ name = "lasertag magbolt"
+ forcedodge = TRUE //for penetration memes
+ range = 5 //so it isn't super annoying
+ light_range = 2
+ light_color = LIGHT_COLOR_YELLOW
+ eyeblur = 0
+
+/obj/item/ammo_casing/energy/laser/magtag
+ projectile_type = /obj/item/projectile/beam/lasertag/mag
+ select_name = "magtag"
+ pellets = 3
+ variance = 30
+ e_cost = 1000
+ fire_sound = 'sound/weapons/magburst.ogg'
+
+/obj/item/gun/energy/laser/practice/hyperburst
+ name = "toy hyper-burst launcher"
+ desc = "A toy laser with a unique beam shaping lens that projects harmless bolts capable of going through objects. Compatible with existing laser tag systems."
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/magtag)
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "toyburst"
+ clumsy_check = FALSE
+ needs_permit = FALSE
+ fire_delay = 40
+ weapon_weight = WEAPON_HEAVY
+ selfcharge = TRUE
+ charge_delay = 2
+ recoil = 2
+ cell_type = /obj/item/stock_parts/cell/toymagburst
+
+/obj/item/stock_parts/cell/toymagburst
+ name = "toy mag burst rifle power supply"
+ maxcharge = 4000
+
+/datum/design/foam_hyperburst
+ name = "MagTag Hyper Rifle"
+ id = "foam_hyperburst"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 35000, MAT_GLASS = 15000)
+ build_path = /obj/item/gun/energy/laser/practice/hyperburst
+ category = list("hacked", "Misc")
+
/* made redundant by reskinnable stetchkins
//////Stealth Pistol//////
@@ -583,6 +666,40 @@
*/
+///foam stealth pistol///
+
+/obj/item/gun/ballistic/automatic/toy/pistol/stealth
+ name = "foam force stealth pistol"
+ desc = "A small, easily concealable toy bullpup handgun. Ages 8 and up."
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "foamsp"
+ w_class = WEIGHT_CLASS_SMALL
+ mag_type = /obj/item/ammo_box/magazine/toy/pistol
+ can_suppress = FALSE
+ fire_sound = 'sound/weapons/gunshot_silenced.ogg'
+ suppressed = TRUE
+ burst_size = 1
+ fire_delay = 0
+ actions_types = list()
+
+/obj/item/gun/ballistic/automatic/toy/pistol/stealth/update_icon()
+ ..()
+ if(magazine)
+ cut_overlays()
+ add_overlay("foamsp-magazine")
+ else
+ cut_overlays()
+ icon_state = "[initial(icon_state)][chambered ? "" : "-e"]"
+
+/datum/design/foam_sp
+ name = "Foam Force Stealth Pistol"
+ id = "foam_sp"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 15000, MAT_GLASS = 1000)
+ build_path = /obj/item/gun/ballistic/automatic/toy/pistol/stealth
+ category = list("hacked", "Misc")
+
+
//////10mm soporific bullets//////
obj/item/projectile/bullet/c10mm/soporific
@@ -768,4 +885,33 @@ obj/item/projectile/bullet/c10mm/soporific
cut_overlays()
add_overlay("[unique_reskin[current_skin]]-magazine")
else
- cut_overlays()
\ No newline at end of file
+ cut_overlays()
+
+/////////RAYGUN MEMES/////////
+
+/obj/item/projectile/beam/lasertag/ray //the projectile, compatible with regular laser tag armor
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "ray"
+ name = "ray bolt"
+ eyeblur = 0
+
+/obj/item/ammo_casing/energy/laser/raytag
+ projectile_type = /obj/item/projectile/beam/lasertag/ray
+ select_name = "raytag"
+ fire_sound = 'sound/weapons/raygun.ogg'
+
+/obj/item/gun/energy/laser/practice/raygun
+ name = "toy ray gun"
+ icon = 'icons/obj/guns/cit_guns.dmi'
+ icon_state = "raygun"
+ desc = "A toy laser with a classic, retro feel and look. Compatible with existing laser tag systems."
+ ammo_type = list(/obj/item/ammo_casing/energy/laser/raytag)
+ selfcharge = TRUE
+
+/datum/design/toyray
+ name = "RayTag Gun"
+ id = "toyray"
+ build_type = AUTOLATHE
+ materials = list(MAT_METAL = 7500, MAT_GLASS = 1000)
+ build_path = /obj/item/gun/energy/laser/practice/raygun
+ category = list("hacked", "Misc")
diff --git a/code/citadel/cit_miscreants.dm b/code/citadel/cit_miscreants.dm
new file mode 100644
index 0000000000..f828127b55
--- /dev/null
+++ b/code/citadel/cit_miscreants.dm
@@ -0,0 +1,61 @@
+/datum/controller/subsystem/ticker/proc/generate_miscreant_objectives(var/datum/mind/crewMind)
+ if(!GLOB.miscreants_allowed)
+ return
+ if(!crewMind)
+ return
+ if(!crewMind.current || !crewMind.objectives || crewMind.special_role)
+ return
+ if(!crewMind.assigned_role)
+ return
+ if(jobban_isbanned(crewMind, "Syndicate"))
+ return
+ var/list/objectiveTypes = typesof(/datum/objective/miscreant) - /datum/objective/miscreant
+ if(!objectiveTypes.len)
+ return
+ var/selectedType = pick(objectiveTypes)
+ var/datum/objective/miscreant/newObjective = new selectedType
+ if(!newObjective)
+ return
+ newObjective.owner = crewMind
+ crewMind.objectives += newObjective
+ crewMind.special_role = "miscreant"
+ to_chat(crewMind, "You are a Miscreant.")
+ to_chat(crewMind, "Pursuing your objective is entirely optional, as the completion of your objective is unable to be tracked. Performing traitorous acts not directly related to your objective may result in permanent termination of your employment.")
+ to_chat(crewMind, "Your objective: [newObjective.explanation_text]")
+
+/datum/objective/miscreant
+ explanation_text = "Something broke. Horribly. Dear god, im so sorry. Yell about this in the development discussion channel of citadels discord."
+
+/* Goon's Miscreant Objectives */
+
+/datum/objective/miscreant/blockade
+ explanation_text = "Try to completely block off access to an area, under the pretense that it's too dangerous."
+
+/datum/objective/miscreant/incompetent
+ explanation_text = "Be as useless and incompetent as possible without getting killed."
+
+/datum/objective/miscreant/litterbug
+ explanation_text = "Make a huge mess wherever you go."
+
+/datum/objective/miscreant/stalk
+ explanation_text = "Single out a crew member and stalk them everywhere."
+
+/datum/objective/miscreant/creepy
+ explanation_text = "Sneak around looking as suspicious as possible without actually doing anything illegal."
+
+/datum/objective/miscreant/construction
+ explanation_text = "Perform obnoxious construction and renovation projects. Insist that you're just doing your job."
+
+/datum/objective/miscreant/access
+ explanation_text = "Make as much of the station as possible accessible to the public."
+
+/datum/objective/miscreant/whiny
+ explanation_text = "Complain incessantly about every minor issue you find."
+
+/* Citadel's Miscreant Objectives */
+
+/datum/objective/miscreant/immersions
+ explanation_text = "Act as out of character as you possibly can."
+
+/datum/objective/miscreant/racism
+ explanation_text = "Attempt to establish superiority of your race."
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_cargo.dm b/code/citadel/crew_objectives/cit_crewobjectives_cargo.dm
new file mode 100644
index 0000000000..d164123c4d
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_cargo.dm
@@ -0,0 +1,81 @@
+/* CARGO OBJECTIVES */
+
+/datum/objective/crew/petsplosion
+ explanation_text = "Ensure there are at least (If you see this, yell on citadels discord in the development discussion channel) pets on the station by the end of the shift. Interpret this as you wish."
+ jobs = "quartermaster,cargotechnician"
+
+/datum/objective/crew/petsplosion/New()
+ . = ..()
+ target_amount = rand(10,30)
+ update_explanation_text()
+
+/datum/objective/crew/petsplosion/update_explanation_text()
+ . = ..()
+ explanation_text = "Ensure there are at least [target_amount] pets on the station by the end of the shift. Interpret this as you wish."
+
+/datum/objective/crew/petsplosion/check_completion()
+ var/petcount = target_amount
+ for(var/mob/living/simple_animal/pet/P in GLOB.mob_list)
+ if(!(P.stat == DEAD))
+ if(P.z == ZLEVEL_STATION_PRIMARY || SSshuttle.emergency.shuttle_areas[get_area(P)])
+ petcount--
+ for(var/mob/living/carbon/human/H in GLOB.mob_list)
+ if(!(H.stat == DEAD))
+ if(H.z == ZLEVEL_STATION_PRIMARY || SSshuttle.emergency.shuttle_areas[get_area(H)])
+ if(istype(H.wear_neck, /obj/item/clothing/neck/petcollar))
+ petcount--
+ if(petcount <= 0)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/points //ported from old hippie
+ explanation_text = "Make sure the station has at least (Something broke, report this to the development discussion channel of citadels discord) supply points at the end of the shift."
+ jobs = "quartermaster,cargotechnician"
+
+/datum/objective/crew/points/New()
+ . = ..()
+ target_amount = rand(25000,100000)
+ update_explanation_text()
+
+/datum/objective/crew/points/update_explanation_text()
+ . = ..()
+ explanation_text = "Make sure the station has at least [target_amount] supply points at the end of the shift."
+
+/datum/objective/crew/points/check_completion()
+ if(SSshuttle.points >= target_amount)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/bubblegum
+ explanation_text = "Ensure Bubblegum is dead at the end of the shift."
+ jobs = "shaftminer"
+
+/datum/objective/crew/bubblegum/check_completion()
+ for(var/mob/living/simple_animal/hostile/megafauna/bubblegum/B in GLOB.mob_list)
+ if(!(B.stat == DEAD))
+ return FALSE
+ return TRUE
+
+/datum/objective/crew/fatstacks //ported from old hippie
+ explanation_text = "Have at least (something broke, report this to the development discussion channel of citadels discord) mining points on your ID at the end of the shift."
+ jobs = "shaftminer"
+
+/datum/objective/crew/fatstacks/New()
+ . = ..()
+ target_amount = rand(15000,50000)
+ update_explanation_text()
+
+/datum/objective/crew/fatstacks/update_explanation_text()
+ . = ..()
+ explanation_text = "Have at least [target_amount] mining points on your ID at the end of the shift."
+
+/datum/objective/crew/fatstacks/check_completion()
+ if(owner && owner.current)
+ var/mob/living/carbon/human/H = owner.current
+ var/obj/item/card/id/theID = H.get_idcard()
+ if(istype(theID))
+ if(theID.mining_points >= target_amount)
+ return TRUE
+ return FALSE
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_civilian.dm b/code/citadel/crew_objectives/cit_crewobjectives_civilian.dm
new file mode 100644
index 0000000000..81427c7951
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_civilian.dm
@@ -0,0 +1,267 @@
+/* CIVILIAN OBJECTIVES */
+
+/datum/objective/crew/druglordbot //ported from old Hippie with adjustments
+ var/targetchem = "none"
+ var/datum/reagent/chempath
+ explanation_text = "Have at least (somethin broke here) harvested plants containing (report this on the development discussion channel of citadel's discord) when the shift ends."
+ jobs = "botanist"
+
+/datum/objective/crew/druglordbot/New()
+ . = ..()
+ target_amount = rand(3,20)
+ var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/menthol, /datum/reagent/medicine, /datum/reagent/medicine/adminordrazine, /datum/reagent/medicine/adminordrazine/nanites, /datum/reagent/medicine/mine_salve, /datum/reagent/medicine/syndicate_nanites, /datum/reagent/medicine/strange_reagent, /datum/reagent/medicine/miningnanites, /datum/reagent/medicine/changelingAdrenaline, /datum/reagent/medicine/changelingAdrenaline2)
+ var/drugs = typesof(/datum/reagent/drug) - blacklist
+ var/meds = typesof(/datum/reagent/medicine) - blacklist
+ var/chemlist = drugs + meds
+ chempath = pick(chemlist)
+ targetchem = initial(chempath.id)
+ update_explanation_text()
+
+/datum/objective/crew/druglordbot/update_explanation_text()
+ . = ..()
+ explanation_text = "Have at least [target_amount] harvested plants containing [initial(chempath.name)] when the shift ends."
+
+/datum/objective/crew/druglordbot/check_completion()
+ var/pillcount = target_amount
+ if(owner.current)
+ if(owner.current.contents)
+ for(var/obj/item/reagent_containers/food/snacks/grown/P in owner.current.get_contents())
+ if(P.reagents.has_reagent(targetchem))
+ pillcount--
+ if(pillcount <= 0)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/foodhoard
+ var/datum/crafting_recipe/food/targetfood
+ var/obj/item/reagent_containers/food/foodpath
+ explanation_text = "Personally deliver at least (yo something broke) (report this to the developer discussion channel in citadels discord)s to Centcom."
+ jobs = "cook"
+
+/datum/objective/crew/foodhoard/New()
+ . = ..()
+ target_amount = rand(2,10)
+ var/blacklist = list(/datum/crafting_recipe/food, /datum/crafting_recipe/food/cak)
+ var/possiblefoods = typesof(/datum/crafting_recipe/food) - blacklist
+ targetfood = pick(possiblefoods)
+ foodpath = initial(targetfood.result)
+ update_explanation_text()
+
+/datum/objective/crew/foodhoard/update_explanation_text()
+ . = ..()
+ explanation_text = "Personally deliver at least [target_amount] [initial(foodpath.name)]s to Centcom."
+
+/datum/objective/crew/foodhoard/check_completion()
+ if(owner.current && owner.current.check_contents_for(foodpath) && SSshuttle.emergency.shuttle_areas[get_area(owner.current)])
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/responsibility
+ explanation_text = "Make sure nobody dies with alcohol poisoning."
+ jobs = "bartender"
+
+/datum/objective/crew/responsibility/check_completion()
+ for(var/mob/living/carbon/human/H in GLOB.mob_list)
+ if(H.stat == DEAD && H.drunkenness >= 80)
+ if(H.z == ZLEVEL_STATION_PRIMARY || SSshuttle.emergency.shuttle_areas[get_area(H)])
+ return FALSE
+ return TRUE
+
+/datum/objective/crew/clean //ported from old Hippie
+ var/list/areas = list()
+ var/hardmode = FALSE
+ explanation_text = "Ensure sure that (Yo, something broke. Yell about this in citadels devlopmeent discussion channel.) remain spotless at the end of the shift."
+ jobs = "janitor"
+
+/datum/objective/crew/clean/New()
+ . = ..()
+ if(prob(1))
+ hardmode = TRUE
+ var/list/blacklistnormal = list(typesof(/area/space) - typesof(/area/lavaland) - typesof(/area/mine) - typesof(/area/ai_monitored/turret_protected) - typesof(/area/tcommsat))
+ var/list/blacklisthard = list(typesof(/area/lavaland) - typesof(/area/mine))
+ var/list/possibleareas = list()
+ if(hardmode)
+ possibleareas = GLOB.teleportlocs - /area - blacklisthard
+ else
+ possibleareas = GLOB.teleportlocs - /area - blacklistnormal
+ for(var/i in 1 to rand(1,6))
+ areas |= pick_n_take(possibleareas)
+ update_explanation_text()
+
+/datum/objective/crew/clean/update_explanation_text()
+ . = ..()
+ explanation_text = "Ensure that the"
+ for(var/i in 1 to areas.len)
+ var/area/A = areas[i]
+ explanation_text += " [A]"
+ if(i != areas.len && areas.len >= 3)
+ explanation_text += ","
+ if(i == areas.len - 1)
+ explanation_text += "and"
+ explanation_text += " [(areas.len ==1) ? "is completely" : "are [(areas.len == 2) ? "completely" : "all"]"] clean at the end of the shift."
+ if(hardmode)
+ explanation_text += " Chop-chop."
+
+/datum/objective/crew/clean/check_completion()
+ for(var/area/A in areas)
+ for(var/obj/effect/decal/cleanable/C in area_contents(A))
+ return FALSE
+ return TRUE
+
+/datum/objective/crew/slipster //ported from old Hippie with adjustments
+ explanation_text = "Slip at least (Yell on citadel's development discussion channel if you see this) different people with your PDA, and have it on you at the end of the shift."
+ jobs = "clown"
+
+/datum/objective/crew/slipster/New()
+ . = ..()
+ target_amount = rand(5, 20)
+ update_explanation_text()
+
+/datum/objective/crew/slipster/update_explanation_text()
+ . = ..()
+ explanation_text = "Slip at least [target_amount] different people with your PDA, and have it on you at the end of the shift."
+
+/datum/objective/crew/slipster/check_completion()
+ var/list/uniqueslips = list()
+ if(owner && owner.current)
+ for(var/obj/item/device/pda/clown/PDA in owner.current.get_contents())
+ for(var/mob/living/carbon/human/H in PDA.slipvictims)
+ uniqueslips |= H
+ if(uniqueslips.len >= target_amount)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/vow //ported from old Hippie
+ explanation_text = "Never break your vow of silence."
+ jobs = "mime"
+
+/datum/objective/crew/vow/check_completion()
+ if(owner && owner.current)
+ var/list/say_log = owner.current.logging[INDIVIDUAL_SAY_LOG]
+ if(say_log.len > 0)
+ return FALSE
+ return TRUE
+
+/datum/objective/crew/nullrod
+ explanation_text = "Don't lose your holy rod."
+ jobs = "chaplain"
+
+/datum/objective/crew/nullrod/check_completion()
+ if(owner && owner.current)
+ if(owner.current.check_contents_for(typesof(/obj/item/nullrod)))
+ return TRUE
+ if(owner.current.getorgan(/obj/item/organ/genital/penis))
+ return TRUE
+ return FALSE
+
+/datum/objective/crew/reporter //ported from old hippie
+ var/charcount = 100
+ explanation_text = "Publish at least (Yo something broke) articles containing at least (Report this to Citadels development channel) characters."
+ jobs = "curator"
+
+/datum/objective/crew/reporter/New()
+ . = ..()
+ target_amount = rand(2,10)
+ charcount = rand(20,250)
+ update_explanation_text()
+
+/datum/objective/crew/reporter/update_explanation_text()
+ . = ..()
+ explanation_text = "Publish at least [target_amount] articles containing at least [charcount] characters."
+
+/datum/objective/crew/reporter/check_completion()
+ if(owner && owner.current)
+ var/ownername = "[ckey(owner.current.real_name)][ckey(owner.assigned_role)]"
+ for(var/datum/newscaster/feed_channel/chan in GLOB.news_network.network_channels)
+ for(var/datum/newscaster/feed_message/msg in chan.messages)
+ if(ckey(msg.returnAuthor()) == ckey(ownername))
+ if(length(msg.returnBody()) >= charcount)
+ target_amount--
+ if(target_amount <= 0)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/departmentclothes
+ var/obj/item/clothing/under/rank/targetuniform
+ explanation_text = "Be wearing a (Yo, this objective broke. report this to citadels discord via the development channel) at the end of the shift."
+ jobs = "assistant"
+
+/datum/objective/crew/departmentclothes/New()
+ . = ..()
+ var/list/blacklist = list(/obj/item/clothing/under/rank, /obj/item/clothing/under/rank/miner, /obj/item/clothing/under/rank/medical/blue, /obj/item/clothing/under/rank/medical/green, /obj/item/clothing/under/rank/medical/purple, /obj/item/clothing/under/rank/security/grey, /obj/item/clothing/under/rank/warden/grey, /obj/item/clothing/under/rank/head_of_security/grey, /obj/item/clothing/under/rank/mailman, /obj/item/clothing/under/rank/psyche, /obj/item/clothing/under/rank/clown/sexy, /obj/item/clothing/under/rank/centcom_officer, /obj/item/clothing/under/rank/centcom_commander, /obj/item/clothing/under/rank/security/navyblue/russian, /obj/item/clothing/under/rank/security/blueshirt)
+ var/list/validclothes = typesof(/obj/item/clothing/under/rank) - blacklist
+ targetuniform = pick(validclothes)
+ update_explanation_text()
+
+/datum/objective/crew/departmentclothes/update_explanation_text()
+ . = ..()
+ explanation_text = "Be wearing a [initial(targetuniform.name)] at the end of the shift."
+
+/datum/objective/crew/departmentclothes/check_completion()
+ if(owner && owner.current)
+ var/mob/living/carbon/human/H = owner.current
+ if(istype(H.w_uniform, targetuniform))
+ return TRUE
+ return FALSE
+
+/datum/objective/crew/pwrgame //ported from Goon with adjustments
+ var/obj/item/clothing/targettidegarb
+ explanation_text = "Get your grubby hands on a (Dear god something broke. Report this to Citadel's development dicussion channel)."
+ jobs = "assistant"
+
+/datum/objective/crew/pwrgame/New()
+ . = ..()
+ var/list/muhvalids = list(/obj/item/clothing/mask/gas, /obj/item/clothing/head/welding, /obj/item/clothing/head/ushanka, /obj/item/clothing/gloves/color/yellow, /obj/item/clothing/mask/gas/owl_mask, /obj/item/clothing/suit/space)
+ targettidegarb = pick(muhvalids)
+ update_explanation_text()
+
+/datum/objective/crew/pwrgame/update_explanation_text()
+ . = ..()
+ explanation_text = "Get your grubby hands on a [initial(targettidegarb.name)]."
+/* DM is not a sane language in any way, shape, or form. If anyone wants to try to get this bit functioning proper, I hold no responsibility for broken keyboards.
+ if(owner && owner.current)
+ var/mob/living/carbon/human/H = owner.current
+ if(H && H.dna && H.dna.species && H.dna.species.id)
+ explanation_text = "Get your "
+ if(H.dna.species.id == "avian")
+ explanation_text += "scratchy claws "
+ else if(H.dna.species.id == "mammal")
+ explanation_text += "dirty paws "
+ else if(H.dna.species.id == "aquatic")
+ explanation_text += "fishy hands "
+ else if(H.dna.species.id == "xeno")
+ explanation_text += "weird claws "
+ else if(H.dna.species.id == "guilmon")
+ explanation_text += "digital claws "
+ else if(H.dna.species.id == "lizard")
+ explanation_text += "slimy claws "
+ else if(H.dna.species.id == "datashark")
+ explanation_text += "glitchy hands "
+ else if(H.dna.species.id == "insect")
+ explanation_text += "gross grabbers "
+ else
+ explanation_text += "grubby hands "
+ explanation_text += "on a space suit." replace this if you're making this monstrosity work */
+
+/datum/objective/crew/pwrgame/check_completion()
+ if(owner.current && owner.current.check_contents_for(typesof(targettidegarb)))
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/promotion //ported from Goon
+ explanation_text = "Have a non-assistant ID registered to you at the end of the shift."
+ jobs = "assistant"
+
+/datum/objective/crew/promotion/check_completion()
+ if(owner && owner.current)
+ var/mob/living/carbon/human/H = owner.current
+ var/obj/item/card/id/theID = H.get_idcard()
+ if(istype(theID))
+ if(!(H.get_assignment() == "Assistant") && !(H.get_assignment() == "No id") && !(H.get_assignment() == "No job"))
+ return TRUE
+ return FALSE
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_command.dm b/code/citadel/crew_objectives/cit_crewobjectives_command.dm
new file mode 100644
index 0000000000..b4f0d07c08
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_command.dm
@@ -0,0 +1,33 @@
+/* COMMAND OBJECTIVES */
+
+/datum/objective/crew/caphat //Ported from Goon
+ explanation_text = "Don't lose your hat."
+ jobs = "captain"
+
+/datum/objective/crew/caphat/check_completion()
+ if(owner.current && owner.current.check_contents_for(/obj/item/clothing/head/caphat))
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/datfukkendisk //Ported from old Hippie
+ explanation_text = "Defend the nuclear authentication disk at all costs, and be the one to personally deliver it to Centcom."
+ jobs = "captain" //give this to other heads at your own risk.
+
+/datum/objective/crew/datfukkendisk/check_completion()
+ if(owner.current && owner.current.check_contents_for(/obj/item/disk/nuclear) && SSshuttle.emergency.shuttle_areas[get_area(owner.current)])
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/ian //Ported from old Hippie
+ explanation_text = "Defend Ian at all costs, and ensure he gets delivered to Centcom at the end of the shift."
+ jobs = "headofpersonnel"
+
+/datum/objective/crew/ian/check_completion()
+ if(owner.current)
+ for(var/mob/living/simple_animal/pet/dog/corgi/Ian/goodboy in GLOB.mob_list)
+ if(goodboy.stat != DEAD && SSshuttle.emergency.shuttle_areas[get_area(goodboy)])
+ return TRUE
+ return FALSE
+ return FALSE
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_engineering.dm b/code/citadel/crew_objectives/cit_crewobjectives_engineering.dm
new file mode 100644
index 0000000000..7cf523fd2c
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_engineering.dm
@@ -0,0 +1,34 @@
+/* ENGINEERING OBJECTIVES */
+
+/datum/objective/crew/integrity //ported from old Hippie
+ explanation_text = "Ensure the station's integrity rating is at least (Yo something broke, yell on the development discussion channel of citadels discord about this)% when the shift ends."
+ jobs = "chiefengineer,stationengineer"
+
+/datum/objective/crew/integrity/New()
+ . = ..()
+ target_amount = rand(60,95)
+ update_explanation_text()
+
+/datum/objective/crew/integrity/update_explanation_text()
+ . = ..()
+ explanation_text = "Ensure the station's integrity rating is at least [target_amount]% when the shift ends."
+
+/datum/objective/crew/integrity/check_completion()
+ var/datum/station_state/end_state = new /datum/station_state()
+ end_state.count()
+ var/station_integrity = min(PERCENT(GLOB.start_state.score(end_state)), 100)
+ if(!SSticker.mode.station_was_nuked && station_integrity >= target_amount)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/poly
+ explanation_text = "Make sure Poly keeps his headset, and stays alive until the end of the shift."
+ jobs = "chiefengineer"
+
+/datum/objective/crew/poly/check_completion()
+ for(var/mob/living/simple_animal/parrot/Poly/dumbbird in GLOB.mob_list)
+ if(!(dumbbird.stat == DEAD) && dumbbird.ears)
+ if(istype(dumbbird.ears, /obj/item/device/radio/headset))
+ return TRUE
+ return FALSE
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_medical.dm b/code/citadel/crew_objectives/cit_crewobjectives_medical.dm
new file mode 100644
index 0000000000..43d470f2f3
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_medical.dm
@@ -0,0 +1,86 @@
+/* MEDICAL OBJECTIVES */
+
+/datum/objective/crew/morgue //Ported from old Hippie
+ explanation_text = "Ensure there are no corpses on the station outside of the morgue when the shift ends."
+ jobs = "chiefmedicalofficer,geneticist,medicaldoctor"
+
+/datum/objective/crew/morgue/check_completion()
+ for(var/mob/living/carbon/human/H in GLOB.mob_list)
+ if(H.stat == DEAD && H.z == ZLEVEL_STATION_PRIMARY)
+ if(get_area(H) != /area/medical/morgue)
+ return FALSE
+ return TRUE
+
+/datum/objective/crew/chems //Ported from old Hippie
+ var/targetchem = "none"
+ var/datum/reagent/chempath
+ explanation_text = "Have (yell about this in the development discussion channel of citadel's discord, something broke) in your bloodstream when the shift ends."
+ jobs = "chiefmedicalofficer,chemist"
+
+/datum/objective/crew/chems/New()
+ . = ..()
+ var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/nicotine, /datum/reagent/drug/menthol, /datum/reagent/medicine, /datum/reagent/medicine/adminordrazine, /datum/reagent/medicine/adminordrazine/nanites, /datum/reagent/medicine/mine_salve, /datum/reagent/medicine/omnizine, /datum/reagent/medicine/syndicate_nanites, /datum/reagent/medicine/earthsblood, /datum/reagent/medicine/strange_reagent, /datum/reagent/medicine/miningnanites, /datum/reagent/medicine/changelingAdrenaline, /datum/reagent/medicine/changelingAdrenaline2)
+ var/drugs = typesof(/datum/reagent/drug) - blacklist
+ var/meds = typesof(/datum/reagent/medicine) - blacklist
+ var/chemlist = drugs + meds
+ chempath = pick(chemlist)
+ targetchem = initial(chempath.id)
+ update_explanation_text()
+
+/datum/objective/crew/chems/update_explanation_text()
+ . = ..()
+ explanation_text = "Have [initial(chempath.name)] in your bloodstream when the shift ends."
+
+/datum/objective/crew/chems/check_completion()
+ if(owner.current)
+ if(!owner.current.stat == DEAD && owner.current.reagents)
+ if(owner.current.reagents.has_reagent(targetchem))
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/druglordchem //ported from old Hippie with adjustments
+ var/targetchem = "none"
+ var/datum/reagent/chempath
+ var/chemamount = 0
+ explanation_text = "Have at least (somethin broke here) pills containing at least (like really broke) units of(report this on the development discussion channel of citadel's discord) when the shift ends."
+ jobs = "chemist"
+
+/datum/objective/crew/druglordchem/New()
+ . = ..()
+ target_amount = rand(5,50)
+ chemamount = rand(1,20)
+ var/blacklist = list(/datum/reagent/drug, /datum/reagent/drug/nicotine, /datum/reagent/drug/menthol)
+ var/drugs = typesof(/datum/reagent/drug) - blacklist
+ var/chemlist = drugs
+ chempath = pick(chemlist)
+ targetchem = initial(chempath.id)
+ update_explanation_text()
+
+/datum/objective/crew/druglordchem/update_explanation_text()
+ . = ..()
+ explanation_text = "Have at least [target_amount] pills containing at least [chemamount] units of [initial(chempath.name)] when the shift ends."
+
+/datum/objective/crew/druglordchem/check_completion()
+ var/pillcount = target_amount
+ if(owner.current)
+ if(owner.current.contents)
+ for(var/obj/item/reagent_containers/pill/P in owner.current.get_contents())
+ if(P.reagents.has_reagent(targetchem, chemamount))
+ pillcount--
+ if(pillcount <= 0)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/noinfections
+ explanation_text = "Ensure no living crew members are infected with harmful viruses at the end of the shift"
+ jobs = "virologist"
+
+/datum/objective/crew/noinfections/check_completion()
+ for(var/mob/living/carbon/human/H in GLOB.mob_list)
+ if(!H.stat == DEAD)
+ if(H.z == ZLEVEL_STATION_PRIMARY || SSshuttle.emergency.shuttle_areas[get_area(H)])
+ if(H.check_virus() == 2)
+ return FALSE
+ return TRUE
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_science.dm b/code/citadel/crew_objectives/cit_crewobjectives_science.dm
new file mode 100644
index 0000000000..f04f97b174
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_science.dm
@@ -0,0 +1,45 @@
+/* SCIENCE OBJECTIVES */
+
+/datum/objective/crew/cyborgs //Ported from old Hippie
+ explanation_text = "Ensure there are at least (Yo something broke here, yell on citadel's development discussion channel about this) functioning cyborgs when the shift ends."
+ jobs = "researchdirector,roboticist"
+
+/datum/objective/crew/cyborgs/New()
+ . = ..()
+ target_amount = rand(3,10)
+ update_explanation_text()
+
+/datum/objective/crew/cyborgs/update_explanation_text()
+ . = ..()
+ explanation_text = "Ensure there are at least [target_amount] functioning cyborgs when the shift ends."
+
+/datum/objective/crew/cyborgs/check_completion()
+ var/borgcount = target_amount
+ for(var/mob/living/silicon/robot/R in GLOB.living_mob_list)
+ if(!(R.stat == DEAD))
+ borgcount--
+ if(borgcount <= 0)
+ return TRUE
+ else
+ return FALSE
+
+/datum/objective/crew/research //inspired by old hippie's research level objective. should hopefully be compatible with techwebs when that gets finished. hopefully. should be easy to update in the event that it is incompatible with techwebs.
+ var/datum/design/targetdesign
+ explanation_text = "Make sure the research required to produce a (something broke, yell on citadel's development discussion channel about this) is available on the R&D server by the end of the shift."
+ jobs = "researchdirector,scientist"
+
+/datum/objective/crew/research/New()
+ . = ..()
+ targetdesign = pick(subtypesof(/datum/design))
+ update_explanation_text()
+
+/datum/objective/crew/research/update_explanation_text()
+ . = ..()
+ explanation_text = "Make sure the research required to produce a [initial(targetdesign.name)] is available on the R&D server by the end of the shift."
+
+/datum/objective/crew/research/check_completion()
+ for(var/obj/machinery/r_n_d/server/S in GLOB.machines)
+ if(S && S.files && S.files.known_designs)
+ if(targetdesign in S.files.known_designs)
+ return TRUE
+ return FALSE
\ No newline at end of file
diff --git a/code/citadel/crew_objectives/cit_crewobjectives_security.dm b/code/citadel/crew_objectives/cit_crewobjectives_security.dm
new file mode 100644
index 0000000000..4e1ca87582
--- /dev/null
+++ b/code/citadel/crew_objectives/cit_crewobjectives_security.dm
@@ -0,0 +1,33 @@
+/* SECURITY OBJECTIVES */
+
+/datum/objective/crew/enjoyyourstay
+ explanation_text = "Welcome to Space Station 13. Enjoy your stay."
+ jobs = "headofsecurity,securityofficer,warden,detective"
+ var/list/edglines = list("Welcome to Space Station 13. Enjoy your stay.", "You signed up for this.", "Abandon hope.", "The tide's gonna stop eventually.", "Hey, someone's gotta do it.", "No, you can't resign.", "Security is a mission, not an intermission.")
+
+/datum/objective/crew/enjoyyourstay/New()
+ . = ..()
+ update_explanation_text()
+
+/datum/objective/crew/enjoyyourstay/update_explanation_text()
+ . = ..()
+ explanation_text = pick(edglines)
+
+/datum/objective/crew/enjoyyourstay/check_completion()
+ explanation_text = "Enforce Space Law to the best of your ability."
+ if(owner && owner.current)
+ if(owner.current.stat != DEAD)
+ return TRUE
+ return FALSE
+
+/datum/objective/crew/justicecrew
+ explanation_text = "Ensure there are no innocent crew members in the brig when the shift ends."
+ jobs = "lawyer"
+
+/datum/objective/crew/justicecrew/check_completion()
+ if(owner && owner.current)
+ for(var/datum/mind/M in SSticker.minds)
+ if(M.current && isliving(M.current))
+ if(!M.special_role && !(M.assigned_role == "Security Officer") && !(M.assigned_role == "Detective") && !(M.assigned_role == "Head of Security") && !(M.assigned_role == "Lawyer") && !(M.assigned_role == "Warden") && get_area(M.current) != typesof(/area/security))
+ return FALSE
+ return TRUE
diff --git a/code/controllers/configuration/entries/game_options.dm b/code/controllers/configuration/entries/game_options.dm
index 106804a576..65c7dbaf4c 100644
--- a/code/controllers/configuration/entries/game_options.dm
+++ b/code/controllers/configuration/entries/game_options.dm
@@ -256,6 +256,10 @@ CONFIG_DEF(number/bombcap)
value = 14
min_val = 4
+CONFIG_DEF(flag/allow_crew_objectives)
+CONFIG_DEF(flag/allow_miscreants)
+CONFIG_DEF(flag/allow_extended_miscreants)
+
/datum/config_entry/number/bombcap/ValidateAndSet(str_val)
. = ..()
if(.)
diff --git a/code/controllers/subsystem/lighting.dm b/code/controllers/subsystem/lighting.dm
index 989c5b43c4..8e19b265cf 100644
--- a/code/controllers/subsystem/lighting.dm
+++ b/code/controllers/subsystem/lighting.dm
@@ -88,3 +88,11 @@ SUBSYSTEM_DEF(lighting)
/datum/controller/subsystem/lighting/Recover()
initialized = SSlighting.initialized
..()
+
+
+/datum/controller/subsystem/lighting/proc/initialize_lighting_objects(list/turfs)
+ for(var/turf/T in turfs)
+ if(!IS_DYNAMIC_LIGHTING(T))
+ continue
+ new/atom/movable/lighting_object(T)
+ CHECK_TICK
\ No newline at end of file
diff --git a/code/controllers/subsystem/ticker.dm b/code/controllers/subsystem/ticker.dm
index 060fe93219..fc53bfa25a 100755
--- a/code/controllers/subsystem/ticker.dm
+++ b/code/controllers/subsystem/ticker.dm
@@ -258,6 +258,13 @@ SUBSYSTEM_DEF(ticker)
//Deleting Startpoints but we need the ai point to AI-ize people later
if(S.name != "AI")
qdel(S)
+
+ //assign crew objectives and generate miscreants
+ if(CONFIG_GET(flag/allow_extended_miscreants) && GLOB.master_mode == "extended")
+ GLOB.miscreants_allowed = TRUE
+ if(CONFIG_GET(flag/allow_miscreants) && GLOB.master_mode != "extended")
+ GLOB.miscreants_allowed = TRUE
+ generate_crew_objectives()
var/list/adm = get_admin_counts()
var/list/allmins = adm["present"]
@@ -333,6 +340,8 @@ SUBSYSTEM_DEF(ticker)
var/num_survivors = 0
var/num_escapees = 0
var/num_shuttle_escapees = 0
+ var/list/successfulCrew = list()
+ var/list/miscreants = list()
to_chat(world, "
The round has ended.")
if(LAZYLEN(GLOB.round_end_notifiees))
@@ -459,6 +468,37 @@ SUBSYSTEM_DEF(ticker)
CHECK_TICK
+ for(var/datum/mind/crewMind in minds)
+ if(!crewMind.current || !crewMind.objectives.len)
+ continue
+ for(var/datum/objective/miscreant/MO in crewMind.objectives)
+ miscreants += "[crewMind.current.real_name] (Played by: [crewMind.key]). Objective: [MO.explanation_text]"
+ for(var/datum/objective/crew/CO in crewMind.objectives)
+ if(CO.check_completion())
+ to_chat(crewMind.current, "
Your objective: [CO.explanation_text] Success!")
+ successfulCrew += "[crewMind.current.real_name] (Played by: [crewMind.key]). Objective: [CO.explanation_text]"
+ else
+ to_chat(crewMind.current, "
Your objective: [CO.explanation_text] Failed.")
+
+ if (successfulCrew.len)
+ var/completedObjectives = "The following crew members completed their Crew Objectives:
"
+ for(var/i in successfulCrew)
+ completedObjectives += "[i]
"
+ to_chat(world, "[completedObjectives]
")
+ else
+ if(CONFIG_GET(flag/allow_crew_objectives))
+ to_chat(world, "Nobody completed their Crew Objectives!
")
+
+ CHECK_TICK
+
+ if (miscreants.len)
+ var/miscreantObjectives = "The following crew members were miscreants:
"
+ for(var/i in miscreants)
+ miscreantObjectives += "[i]
"
+ to_chat(world, "[miscreantObjectives]
")
+
+ CHECK_TICK
+
mode.declare_station_goal_completion()
CHECK_TICK
diff --git a/code/game/gamemodes/changeling/changeling.dm b/code/game/gamemodes/changeling/changeling.dm
index ae09e8ee12..eafc5cd717 100644
--- a/code/game/gamemodes/changeling/changeling.dm
+++ b/code/game/gamemodes/changeling/changeling.dm
@@ -245,13 +245,21 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
if(changeling.objectives.len)
var/count = 1
for(var/datum/objective/objective in changeling.objectives)
- if(objective.check_completion())
- text += "
Objective #[count]: [objective.explanation_text] Success!"
- SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
+ if(istype(objective, /datum/objective/crew))
+ if(objective.check_completion())
+ text += "
Objective #[count]: [objective.explanation_text] Success! (Optional)"
+ SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
+ else
+ text += "
Objective #[count]: [objective.explanation_text] Fail. (Optional)"
+ SSblackbox.add_details("changeling_objective","[objective.type]|FAIL")
else
- text += "
Objective #[count]: [objective.explanation_text] Fail."
- SSblackbox.add_details("changeling_objective","[objective.type]|FAIL")
- changelingwin = 0
+ if(objective.check_completion())
+ text += "
Objective #[count]: [objective.explanation_text] Success!"
+ SSblackbox.add_details("changeling_objective","[objective.type]|SUCCESS")
+ else
+ text += "
Objective #[count]: [objective.explanation_text] Fail."
+ SSblackbox.add_details("changeling_objective","[objective.type]|FAIL")
+ changelingwin = 0
count++
if(changelingwin)
@@ -520,4 +528,3 @@ GLOBAL_LIST_INIT(slot2type, list("head" = /obj/item/clothing/head/changeling, "w
var/datum/atom_hud/antag/hud = GLOB.huds[ANTAG_HUD_CHANGELING]
hud.leave_hud(changling_mind.current)
set_antag_hud(changling_mind.current, null)
-
diff --git a/code/game/gamemodes/traitor/traitor.dm b/code/game/gamemodes/traitor/traitor.dm
index 64d4f84eec..9881c49b68 100644
--- a/code/game/gamemodes/traitor/traitor.dm
+++ b/code/game/gamemodes/traitor/traitor.dm
@@ -111,13 +111,21 @@
if(traitor.objectives.len)//If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in traitor.objectives)
- if(objective.check_completion())
- objectives += "
Objective #[count]: [objective.explanation_text] Success!"
- SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
+ if(istype(objective, /datum/objective/crew))
+ if(objective.check_completion())
+ objectives += "
Objective #[count]: [objective.explanation_text] Success! (Optional)"
+ SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
+ else
+ objectives += "
Objective #[count]: [objective.explanation_text] Fail. (Optional)"
+ SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
else
- objectives += "
Objective #[count]: [objective.explanation_text] Fail."
- SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
- traitorwin = 0
+ if(objective.check_completion())
+ objectives += "
Objective #[count]: [objective.explanation_text] Success!"
+ SSblackbox.add_details("traitor_objective","[objective.type]|SUCCESS")
+ else
+ objectives += "
Objective #[count]: [objective.explanation_text] Fail."
+ SSblackbox.add_details("traitor_objective","[objective.type]|FAIL")
+ traitorwin = 0
count++
if(uplink_true)
diff --git a/code/game/gamemodes/wizard/wizard.dm b/code/game/gamemodes/wizard/wizard.dm
index d25c7613bc..dee9ab4a3d 100644
--- a/code/game/gamemodes/wizard/wizard.dm
+++ b/code/game/gamemodes/wizard/wizard.dm
@@ -212,13 +212,21 @@
var/count = 1
var/wizardwin = 1
for(var/datum/objective/objective in wizard.objectives)
- if(objective.check_completion())
- text += "
Objective #[count]: [objective.explanation_text] Success!"
- SSblackbox.add_details("wizard_objective","[objective.type]|SUCCESS")
+ if(istype(objective, /datum/objective/crew))
+ if(objective.check_completion())
+ text += "
Objective #[count]: [objective.explanation_text] Success! (Optional)"
+ SSblackbox.add_details("wizard_objective","[objective.type]|SUCCESS")
+ else
+ text += "
Objective #[count]: [objective.explanation_text] Fail. (Optional)"
+ SSblackbox.add_details("wizard_objective","[objective.type]|FAIL")
else
- text += "
Objective #[count]: [objective.explanation_text] Fail."
- SSblackbox.add_details("wizard_objective","[objective.type]|FAIL")
- wizardwin = 0
+ if(objective.check_completion())
+ text += "
Objective #[count]: [objective.explanation_text] Success!"
+ SSblackbox.add_details("wizard_objective","[objective.type]|SUCCESS")
+ else
+ text += "
Objective #[count]: [objective.explanation_text] Fail."
+ SSblackbox.add_details("wizard_objective","[objective.type]|FAIL")
+ wizardwin = 0
count++
if(wizard.current && wizard.current.stat!=2 && wizardwin)
diff --git a/code/game/objects/effects/decals/decal.dm b/code/game/objects/effects/decals/decal.dm
index 14a8e25eba..06333277cd 100644
--- a/code/game/objects/effects/decals/decal.dm
+++ b/code/game/objects/effects/decals/decal.dm
@@ -1,82 +1,83 @@
-/obj/effect/decal
- name = "decal"
+/obj/effect/decal
+ name = "decal"
anchored = TRUE
- resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
-
-/obj/effect/decal/ex_act(severity, target)
- qdel(src)
-
-/obj/effect/decal/fire_act(exposed_temperature, exposed_volume)
- if(!(resistance_flags & FIRE_PROOF)) //non fire proof decal or being burned by lava
- qdel(src)
-
-/obj/effect/decal/HandleTurfChange(turf/T)
- ..()
- if(T == loc && (isspaceturf(T) || isclosedturf(T) || islava(T) || istype(T, /turf/open/water) || istype(T, /turf/open/chasm)))
- qdel(src)
-
-/obj/effect/turf_decal
- var/group = TURF_DECAL_PAINT
- icon = 'icons/turf/decals.dmi'
- icon_state = "warningline"
+ resistance_flags = FIRE_PROOF | UNACIDABLE | ACID_PROOF
+
+/obj/effect/decal/ex_act(severity, target)
+ qdel(src)
+
+/obj/effect/decal/fire_act(exposed_temperature, exposed_volume)
+ if(!(resistance_flags & FIRE_PROOF)) //non fire proof decal or being burned by lava
+ qdel(src)
+
+/obj/effect/decal/HandleTurfChange(turf/T)
+ ..()
+ if(T == loc && (isspaceturf(T) || isclosedturf(T) || islava(T) || istype(T, /turf/open/water) || istype(T, /turf/open/chasm)))
+ qdel(src)
+
+/obj/effect/turf_decal
+ var/group = TURF_DECAL_PAINT
+ icon = 'icons/turf/decals.dmi'
+ icon_state = "warningline"
+ layer = TURF_DECAL_LAYER
anchored = TRUE
-
-//in case we need some special decals
-/obj/effect/turf_decal/proc/get_decal()
- return image(icon='icons/turf/decals.dmi',icon_state=icon_state,dir=dir,layer=TURF_LAYER)
-
-/obj/effect/turf_decal/Initialize()
- ..()
- var/turf/T = loc
- if(!istype(T)) //you know this will happen somehow
- CRASH("Turf decal initialized in an object/nullspace")
- T.add_decal(get_decal(),group)
- return INITIALIZE_HINT_QDEL
-
-/obj/effect/turf_decal/stripes/line
- icon_state = "warningline"
-
-/obj/effect/turf_decal/stripes/end
- icon_state = "warn_end"
-
-/obj/effect/turf_decal/stripes/corner
- icon_state = "warninglinecorner"
-
-/obj/effect/turf_decal/stripes/asteroid/line
- icon_state = "ast_warn"
-
-/obj/effect/turf_decal/stripes/asteroid/end
- icon_state = "ast_warn_end"
-
-/obj/effect/turf_decal/stripes/asteroid/corner
- icon_state = "ast_warn_corner"
-
-/obj/effect/turf_decal/delivery
- icon_state = "delivery"
-
-/obj/effect/turf_decal/bot
- icon_state = "bot"
-
-/obj/effect/turf_decal/loading_area
- icon_state = "loading_area"
-
-/obj/effect/turf_decal/sand
- icon_state = "sandyfloor"
-
+
+//in case we need some special decals
+/obj/effect/turf_decal/proc/get_decal()
+ return image(icon='icons/turf/decals.dmi',icon_state=icon_state,dir=dir,layer=TURF_LAYER)
+
+/obj/effect/turf_decal/Initialize()
+ ..()
+ var/turf/T = loc
+ if(!istype(T)) //you know this will happen somehow
+ CRASH("Turf decal initialized in an object/nullspace")
+ T.add_decal(get_decal(),group)
+ return INITIALIZE_HINT_QDEL
+
+/obj/effect/turf_decal/stripes/line
+ icon_state = "warningline"
+
+/obj/effect/turf_decal/stripes/end
+ icon_state = "warn_end"
+
+/obj/effect/turf_decal/stripes/corner
+ icon_state = "warninglinecorner"
+
+/obj/effect/turf_decal/stripes/asteroid/line
+ icon_state = "ast_warn"
+
+/obj/effect/turf_decal/stripes/asteroid/end
+ icon_state = "ast_warn_end"
+
+/obj/effect/turf_decal/stripes/asteroid/corner
+ icon_state = "ast_warn_corner"
+
+/obj/effect/turf_decal/delivery
+ icon_state = "delivery"
+
+/obj/effect/turf_decal/bot
+ icon_state = "bot"
+
+/obj/effect/turf_decal/loading_area
+ icon_state = "loading_area"
+
+/obj/effect/turf_decal/sand
+ icon_state = "sandyfloor"
+
/obj/effect/turf_decal/sand/warning
icon_state = "sandy_warn"
/obj/effect/turf_decal/sand/warning/corner
icon_state = "sandy_warn_corner"
-/obj/effect/turf_decal/sand/plating
- icon_state = "sandyplating"
-
+/obj/effect/turf_decal/sand/plating
+ icon_state = "sandyplating"
+
/obj/effect/turf_decal/sand/plating/warning
icon_state = "sandy_plating_warn"
/obj/effect/turf_decal/sand/plating/warning/corner
icon_state = "sandy_plating_warn_corner"
-/obj/effect/turf_decal/plaque
- icon_state = "plaque"
+/obj/effect/turf_decal/plaque
+ icon_state = "plaque"
diff --git a/code/game/objects/items/devices/PDA/PDA_types.dm b/code/game/objects/items/devices/PDA/PDA_types.dm
index 08e417b9d5..4f2db14fa6 100644
--- a/code/game/objects/items/devices/PDA/PDA_types.dm
+++ b/code/game/objects/items/devices/PDA/PDA_types.dm
@@ -6,6 +6,7 @@
icon_state = "pda-clown"
desc = "A portable microcomputer by Thinktronic Systems, LTD. The surface is coated with polytetrafluoroethylene and banana drippings."
ttone = "honk"
+ var/slipvictims = list()
/obj/item/device/pda/clown/Initialize()
. = ..()
@@ -18,6 +19,7 @@
return
var/mob/living/carbon/human/M = S.slip_victim
if (istype(M) && (M.real_name != src.owner))
+ slipvictims |= M
var/obj/item/cartridge/virus/clown/cart = cartridge
if(istype(cart) && cart.charges < 5)
cart.charges++
diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm
index 0d30d7f7b3..f19c384f98 100644
--- a/code/game/objects/items/stacks/sheets/sheet_types.dm
+++ b/code/game/objects/items/stacks/sheets/sheet_types.dm
@@ -274,7 +274,7 @@ GLOBAL_LIST_INIT(runed_metal_recipes, list ( \
desc = "Sheets of cold metal with shifting inscriptions writ upon them."
singular_name = "runed metal sheet"
icon_state = "sheet-runed"
- icon = 'icons/obj/items_and_weapons.dmi'
+ icon = 'icons/obj/stack_objects.dmi'
sheettype = "runed"
merge_type = /obj/item/stack/sheet/runed_metal
novariants = TRUE
@@ -326,7 +326,7 @@ GLOBAL_LIST_INIT(brass_recipes, list ( \
desc = "Sheets made out of brass."
singular_name = "brass sheet"
icon_state = "sheet-brass"
- icon = 'icons/obj/items_and_weapons.dmi'
+ icon = 'icons/obj/stack_objects.dmi'
resistance_flags = FIRE_PROOF | ACID_PROOF
throwforce = 10
max_amount = 50
diff --git a/code/modules/awaymissions/mission_code/stationCollision.dm b/code/modules/awaymissions/mission_code/stationCollision.dm
index e3af2a99d7..6e6ee2dc4a 100644
--- a/code/modules/awaymissions/mission_code/stationCollision.dm
+++ b/code/modules/awaymissions/mission_code/stationCollision.dm
@@ -136,40 +136,18 @@ GLOBAL_VAR_INIT(sc_safecode5, "[rand(0,9)]")
/*
* Modified Nar-Sie
*/
-/obj/singularity/narsie/sc_Narsie
+/obj/singularity/narsie/mini
desc = "Your body becomes weak and your feel your mind slipping away as you try to comprehend what you know can't be possible."
move_self = 0 //Contianed narsie does not move!
grav_pull = 0 //Contained narsie does not pull stuff in!
- var/uneatable = list(/turf/open/space, /obj/effect/overlay, /mob/living/simple_animal/hostile/construct)
//Override this to prevent no adminlog runtimes and admin warnings about a singularity without containment
-/obj/singularity/narsie/sc_Narsie/admin_investigate_setup()
+/obj/singularity/narsie/mini/admin_investigate_setup()
return
-/obj/singularity/narsie/sc_Narsie/process()
+/obj/singularity/narsie/mini/process()
eat()
if(prob(25))
mezzer()
-/obj/singularity/narsie/sc_Narsie/consume(atom/A)
- if(is_type_in_list(A, uneatable))
- return 0
- if(isliving(A))
- var/mob/living/L = A
- L.gib()
- else if(istype(A, /obj/))
- var/obj/O = A
- O.ex_act(EXPLODE_DEVASTATE)
- if(O) qdel(O)
- else if(isturf(A))
- var/turf/T = A
- if(T.intact)
- for(var/obj/O in T.contents)
- if(O.level != 1)
- continue
- if(O.invisibility == INVISIBILITY_MAXIMUM)
- src.consume(O)
- T.ChangeTurf(/turf/open/space)
+/obj/singularity/narsie/mini/ex_act()
return
-
-/obj/singularity/narsie/sc_Narsie/ex_act()
- return
\ No newline at end of file
diff --git a/code/modules/mapping/map_template.dm b/code/modules/mapping/map_template.dm
index c8ef0ebe2a..9ee242eaba 100644
--- a/code/modules/mapping/map_template.dm
+++ b/code/modules/mapping/map_template.dm
@@ -53,6 +53,7 @@
smooth_zlevel(world.maxz)
repopulate_sorted_areas()
+ SSlighting.initialize_lighting_objects(block(locate(bounds[MAP_MINX], bounds[MAP_MINY], bounds[MAP_MINZ]),locate(bounds[MAP_MAXX], bounds[MAP_MAXY], bounds[MAP_MAXZ])))
//initialize things that are normally initialized after map load
initTemplateBounds(bounds)
log_game("Z-level [name] loaded at at [x],[y],[world.maxz]")
diff --git a/code/modules/mob/dead/new_player/new_player.dm b/code/modules/mob/dead/new_player/new_player.dm
index 8a978bc44b..9aab9e54f1 100644
--- a/code/modules/mob/dead/new_player/new_player.dm
+++ b/code/modules/mob/dead/new_player/new_player.dm
@@ -375,6 +375,11 @@
if(GLOB.highlander)
to_chat(humanc, "THERE CAN BE ONLY ONE!!!")
humanc.make_scottish()
+ if(prob(2) && !issilicon(humanc) && !jobban_isbanned(humanc.mind, "Syndicate") && GLOB.miscreants_allowed)
+ SSticker.generate_miscreant_objectives(humanc.mind)
+ else
+ if(CONFIG_GET(flag/allow_crew_objectives))
+ SSticker.generate_individual_objectives(humanc.mind)
GLOB.joined_player_list += character.ckey
GLOB.latejoiners += character
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index 0ca0bed9fc..c120f4f637 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -281,7 +281,7 @@
if(getBrainLoss() >= 60)
msg += "[t_He] [t_has] a stupid expression on [t_his] face.\n"
if(InCritical())
- msg += "[t_He] is barely concious.\n"
+ msg += "[t_He] is barely conscious.\n"
if(getorgan(/obj/item/organ/brain))
if(istype(src, /mob/living/carbon/human/interactive))
var/mob/living/carbon/human/interactive/auto = src
diff --git a/config/game_options.txt b/config/game_options.txt
index 77e33fa873..3fc1787726 100644
--- a/config/game_options.txt
+++ b/config/game_options.txt
@@ -496,3 +496,14 @@ ARRIVALS_SHUTTLE_DOCK_WINDOW 55
MICE_ROUNDSTART 10
+
+## CREW OBJECTIVES ##
+## Comment to disable objectives for innocent crew members.
+ALLOW_CREW_OBJECTIVES
+
+## MISCREANTS ##
+## Comment to allow miscreants to spawn. Miscreants are a mini-antag with objectives that are not tracked.
+ALLOW_MISCREANTS
+
+## Uncomment to let miscreants spawn during Extended. I hold no responsibility for fun that may occur while this is enabled.
+#ALLOW_EXTENDED_MISCREANTS
\ No newline at end of file
diff --git a/html/changelogs/AutoChangeLog-pr-2603.yml b/html/changelogs/AutoChangeLog-pr-2603.yml
new file mode 100644
index 0000000000..447c9cd78a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-2603.yml
@@ -0,0 +1,5 @@
+author: "deathride58"
+delete-after: True
+changes:
+ - rscadd: "Ported Crew Objectives from Goon, featuring objectives from both Goon and old Hippie."
+ - rscadd: "Ported Miscreants from Goon. They will appear based on the server's configuration."
diff --git a/html/changelogs/AutoChangeLog-pr-3178.yml b/html/changelogs/AutoChangeLog-pr-3178.yml
new file mode 100644
index 0000000000..7642360f68
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3178.yml
@@ -0,0 +1,4 @@
+author: "Xhuis"
+delete-after: True
+changes:
+ - bugfix: "Runed metal and brass are no longer invisible."
diff --git a/html/changelogs/AutoChangeLog-pr-3179.yml b/html/changelogs/AutoChangeLog-pr-3179.yml
new file mode 100644
index 0000000000..6c9321c4a6
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3179.yml
@@ -0,0 +1,10 @@
+author: "Toriate"
+delete-after: True
+changes:
+ - rscadd: "Added foam Magrifles"
+ - rscadd: "Added foam Stealth Pistols"
+ - rscadd: "Added toy Hyper Burst Rifles"
+ - rscadd: "Added toy ray gun"
+ - tweak: "foam X9s now available in autolathe"
+ - soundadd: "added raygun noises"
+ - imageadd: "added all the sprites for all those toys"
diff --git a/html/changelogs/AutoChangeLog-pr-3185.yml b/html/changelogs/AutoChangeLog-pr-3185.yml
new file mode 100644
index 0000000000..fa69ba3fe3
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-3185.yml
@@ -0,0 +1,4 @@
+author: "CitadelStationBot"
+delete-after: True
+changes:
+ - imageadd: "Changed the drop, throw, pull and resist icons."
diff --git a/icons/mob/screen_clockwork.dmi b/icons/mob/screen_clockwork.dmi
index 8605aa170a..ad314fa3b1 100644
Binary files a/icons/mob/screen_clockwork.dmi and b/icons/mob/screen_clockwork.dmi differ
diff --git a/icons/mob/screen_midnight.dmi b/icons/mob/screen_midnight.dmi
index b1c8843966..b8002561f2 100644
Binary files a/icons/mob/screen_midnight.dmi and b/icons/mob/screen_midnight.dmi differ
diff --git a/icons/mob/screen_operative.dmi b/icons/mob/screen_operative.dmi
index 65259c8f58..eea5c662e0 100644
Binary files a/icons/mob/screen_operative.dmi and b/icons/mob/screen_operative.dmi differ
diff --git a/icons/mob/screen_plasmafire.dmi b/icons/mob/screen_plasmafire.dmi
index c76ff741e9..81fae8e0d0 100644
Binary files a/icons/mob/screen_plasmafire.dmi and b/icons/mob/screen_plasmafire.dmi differ
diff --git a/icons/mob/screen_slimecore.dmi b/icons/mob/screen_slimecore.dmi
index 63d67e6fba..f28d73a285 100644
Binary files a/icons/mob/screen_slimecore.dmi and b/icons/mob/screen_slimecore.dmi differ
diff --git a/icons/obj/guns/cit_guns.dmi b/icons/obj/guns/cit_guns.dmi
index c6b2ffe49f..99fe102ffe 100644
Binary files a/icons/obj/guns/cit_guns.dmi and b/icons/obj/guns/cit_guns.dmi differ
diff --git a/sound/weapons/raygun.ogg b/sound/weapons/raygun.ogg
new file mode 100644
index 0000000000..17d2f367a5
Binary files /dev/null and b/sound/weapons/raygun.ogg differ
diff --git a/tgstation.dme b/tgstation.dme
index b996531842..32de5b6c33 100755
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -130,7 +130,6 @@
#include "code\_onclick\click.dm"
#include "code\_onclick\cyborg.dm"
#include "code\_onclick\drag_drop.dm"
-#include "code\_onclick\god.dm"
#include "code\_onclick\item_attack.dm"
#include "code\_onclick\observer.dm"
#include "code\_onclick\other_mobs.dm"
@@ -165,16 +164,25 @@
#include "code\citadel\_cit_helpers.dm"
#include "code\citadel\cit_areas.dm"
#include "code\citadel\cit_arousal.dm"
+#include "code\citadel\cit_crewobjectives.dm"
#include "code\citadel\cit_emotes.dm"
#include "code\citadel\cit_genemods.dm"
#include "code\citadel\cit_guns.dm"
#include "code\citadel\cit_kegs.dm"
+#include "code\citadel\cit_miscreants.dm"
#include "code\citadel\cit_reagents.dm"
#include "code\citadel\cit_spawners.dm"
#include "code\citadel\cit_uniforms.dm"
#include "code\citadel\cit_vendors.dm"
#include "code\citadel\dogborgstuff.dm"
#include "code\citadel\plasmacases.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_cargo.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_civilian.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_command.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_engineering.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_medical.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_science.dm"
+#include "code\citadel\crew_objectives\cit_crewobjectives_security.dm"
#include "code\citadel\custom_loadout\custom_items.dm"
#include "code\citadel\custom_loadout\load_to_mob.dm"
#include "code\citadel\custom_loadout\read_from_file.dm"