diff --git a/code/__DEFINES/dcs/signals/atom/mob/living/signals_human.dm b/code/__DEFINES/dcs/signals/atom/mob/living/signals_human.dm
index 0a9e00b59e04..90e6db45dbaa 100644
--- a/code/__DEFINES/dcs/signals/atom/mob/living/signals_human.dm
+++ b/code/__DEFINES/dcs/signals/atom/mob/living/signals_human.dm
@@ -63,3 +63,6 @@
#define COMSIG_HUMAN_SURGERY_APPLY_MODIFIERS "human_surgery_apply_modifiers"
/// From /mob/living/carbon/human/proc/get_flags_cold_protection()
#define COMSIG_HUMAN_COLD_PROTECTION_APPLY_MODIFIERS "human_cold_protection_apply_modifiers"
+
+/// From /obj/item/proc/dig_out_shrapnel() : ()
+#define COMSIG_HUMAN_SHRAPNEL_REMOVED "human_shrapnel_removed"
diff --git a/code/__DEFINES/dcs/signals/atom/signals_obj.dm b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
index c43105fc516f..0f761ef64494 100644
--- a/code/__DEFINES/dcs/signals/atom/signals_obj.dm
+++ b/code/__DEFINES/dcs/signals/atom/signals_obj.dm
@@ -35,3 +35,6 @@
/// from /proc/vendor_successful_vend() : (obj/structure/machinery/cm_vending/vendor, list/itemspec, mob/living/carbon/human/user)
#define COMSIG_VENDOR_SUCCESSFUL_VEND "vendor_successful_vend"
+
+/// from /obj/limb/proc/remove_all_bleeding() : (external, internal)
+#define COMSIG_LIMB_STOP_BLEEDING "limb_stop_bleeding"
diff --git a/code/datums/tutorial/_tutorial.dm b/code/datums/tutorial/_tutorial.dm
index e93634d0f19d..887971a33854 100644
--- a/code/datums/tutorial/_tutorial.dm
+++ b/code/datums/tutorial/_tutorial.dm
@@ -81,6 +81,7 @@ GLOBAL_LIST_EMPTY(ongoing_tutorials)
REMOVE_TRAIT(tutorial_mob, TRAIT_IN_TUTORIAL, TRAIT_SOURCE_TUTORIAL)
if(tutorial_mob.client?.prefs && completed)
tutorial_mob.client.prefs.completed_tutorials |= tutorial_id
+ tutorial_mob.client.prefs.save_preferences()
var/mob/new_player/new_player = new
if(!tutorial_mob.mind)
tutorial_mob.mind_initialize()
diff --git a/code/datums/tutorial/marine/basic_marine.dm b/code/datums/tutorial/marine/basic_marine.dm
index b5699ece68c9..d98c94111a92 100644
--- a/code/datums/tutorial/marine/basic_marine.dm
+++ b/code/datums/tutorial/marine/basic_marine.dm
@@ -145,7 +145,8 @@
UnregisterSignal(tutorial_mob, COMSIG_MOB_GUN_EMPTY)
message_to_player("Your gun's out of ammo. Go grab some more from the Weaponry Vendor and kill the Xenomorph.")
TUTORIAL_ATOM_FROM_TRACKING(/obj/structure/machinery/cm_vending/sorted/cargo_guns/squad_prep/tutorial, gun_vendor)
- gun_vendor.load_ammo() // 99 magazines, just to make sure that the xeno dies
+ gun_vendor.req_access = list()
+ gun_vendor.load_ammo() // 99 magazines, to make sure that the xeno dies
/datum/tutorial/marine/basic/proc/on_xeno_death(datum/source)
SIGNAL_HANDLER
diff --git a/code/datums/tutorial/marine/medical_basic.dm b/code/datums/tutorial/marine/medical_basic.dm
index 6ee18c341912..2ec7437ff832 100644
--- a/code/datums/tutorial/marine/medical_basic.dm
+++ b/code/datums/tutorial/marine/medical_basic.dm
@@ -51,11 +51,11 @@
message_to_player("All medicines take time to work after injection. Next is Burn damage. It is obtained from things like acid or being set on fire.")
update_objective("")
var/mob/living/living_mob = tutorial_mob
- living_mob.adjustBurnLoss(10)
+ living_mob.adjustFireLoss(10)
addtimer(CALLBACK(src, PROC_REF(burn_tutorial)), 4 SECONDS)
/datum/tutorial/marine/medical_basic/proc/burn_tutorial()
- message_to_player("Kelotane is used to fix burn overtime. Inject yourself with the kelotane EZ autoinjector.")
+ message_to_player("Kelotane is used to fix burn over time. Inject yourself with the kelotane EZ autoinjector.")
update_objective("Inject yourself with the kelotane injector.")
var/obj/item/reagent_container/hypospray/autoinjector/kelotane/skillless/one_use/burn_injector = new(loc_from_corner(0, 4))
add_to_tracking_atoms(burn_injector)
@@ -75,9 +75,85 @@
message_to_player("Good. Now, when you normally take damage, you will also feel pain. Pain slows you down and can knock you out if left unchecked.")
update_objective("")
var/mob/living/living_mob = tutorial_mob
- living_mob.pain.apply_pain(PAIN_BONE_BREAK)
- addtimer(CALLBACK(src, PROC_REF(pain_tutorial)), 4 SECONDS) // add tram injecting
+ living_mob.pain.apply_pain(PAIN_CHESTBURST_STRONG)
+ addtimer(CALLBACK(src, PROC_REF(pain_tutorial)), 4 SECONDS)
+
+/datum/tutorial/marine/medical_basic/proc/pain_tutorial()
+ message_to_player("Tramadol is used to reduce your pain. Inject yourself with the tramadol EZ autoinjector.")
+ update_objective("Inject yourself with the tramadol injector.")
+ var/obj/item/reagent_container/hypospray/autoinjector/tramadol/skillless/one_use/pain_injector = new(loc_from_corner(0, 4))
+ add_to_tracking_atoms(pain_injector)
+ add_highlight(pain_injector)
+ RegisterSignal(tutorial_mob, COMSIG_LIVING_HYPOSPRAY_INJECTED, PROC_REF(on_pain_inject))
+
+/datum/tutorial/marine/medical_basic/proc/on_pain_inject(datum/source, obj/item/reagent_container/hypospray/injector)
+ SIGNAL_HANDLER
+
+ if(!istype(injector, /obj/item/reagent_container/hypospray/autoinjector/tramadol/skillless/one_use))
+ return
+
+ UnregisterSignal(tutorial_mob, COMSIG_LIVING_HYPOSPRAY_INJECTED)
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/item/reagent_container/hypospray/autoinjector/tramadol/skillless/one_use, pain_injector)
+ remove_highlight(pain_injector)
+ message_to_player("Good. Keep in mind that you can overdose on chemicals, so don't inject yourself with the same chemical too much too often. In the field, injectors have 3 uses.")
+ update_objective("Don't overdose! Generally, 3 injections of a chemical will overdose you.")
+ var/mob/living/living_mob = tutorial_mob
+ living_mob.pain.apply_pain(-PAIN_CHESTBURST_STRONG) // just to make sure
+ addtimer(CALLBACK(src, PROC_REF(bleed_tutorial)), 4 SECONDS)
+
+/datum/tutorial/marine/medical_basic/proc/bleed_tutorial()
+ message_to_player("You can sometimes start bleeding from things like bullets or slashes. Losing blood will accumulate oxygen damage, eventually causing death.")
+ update_objective("")
+ var/mob/living/carbon/human/human_mob = tutorial_mob
+ var/obj/limb/chest/mob_chest = locate(/obj/limb/chest) in human_mob.limbs
+ mob_chest.add_bleeding(damage_amount = 15)
+ addtimer(CALLBACK(src, PROC_REF(bleed_tutorial_2)), 4 SECONDS)
+
+/datum/tutorial/marine/medical_basic/proc/bleed_tutorial_2()
+ message_to_player("Bleeding wounds can clot themselves over time, or you can fix it quickly with gauze. Pick up the gauze and click on yourself while targeting your chest.")
+ update_objective("Gauze your chest, or let it clot on its own.")
+ var/obj/item/stack/medical/bruise_pack/two/bandage = new(loc_from_corner(0, 4))
+ add_to_tracking_atoms(bandage)
+ add_highlight(bandage)
+ var/mob/living/carbon/human/human_mob = tutorial_mob
+ var/obj/limb/chest/mob_chest = locate(/obj/limb/chest) in human_mob.limbs
+ RegisterSignal(mob_chest, COMSIG_LIMB_STOP_BLEEDING, PROC_REF(on_chest_bleed_stop))
+
+/datum/tutorial/marine/medical_basic/proc/on_chest_bleed_stop(datum/source, external, internal)
+ SIGNAL_HANDLER
+
+ var/mob/living/carbon/human/human_mob = tutorial_mob
+ var/obj/limb/chest/mob_chest = locate(/obj/limb/chest) in human_mob.limbs
+ UnregisterSignal(mob_chest, COMSIG_LIMB_STOP_BLEEDING)
+
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/item/stack/medical/bruise_pack/two, bandage)
+ remove_from_tracking_atoms(bandage)
+ remove_highlight(bandage)
+ qdel(bandage)
+
+ message_to_player("Good. Sometimes, a bullet or bone shard can result in you getting shrapnel, dealing damage over time. Pick up the knife and use it in-hand to remove the shrapnel.")
+ update_objective("Remove your shrapnel by using the knife in-hand.")
+ var/mob/living/living_mob = tutorial_mob
+ living_mob.pain.feels_pain = FALSE
+
+ var/obj/item/attachable/bayonet/knife = new(loc_from_corner(0, 4))
+ add_to_tracking_atoms(knife)
+ add_highlight(knife)
+
+ var/obj/item/shard/shrapnel/tutorial/shrapnel = new
+ shrapnel.on_embed(tutorial_mob, mob_chest)
+
+ RegisterSignal(tutorial_mob, COMSIG_HUMAN_SHRAPNEL_REMOVED, PROC_REF(on_shrapnel_removed))
+
+/datum/tutorial/marine/medical_basic/proc/on_shrapnel_removed()
+ SIGNAL_HANDLER
+ UnregisterSignal(tutorial_mob, COMSIG_HUMAN_SHRAPNEL_REMOVED)
+ TUTORIAL_ATOM_FROM_TRACKING(/obj/item/attachable/bayonet, knife)
+ remove_highlight(knife)
+ message_to_player("Good. This is the end of the basic marine medical tutorial. The tutorial will end shortly.")
+ update_objective("Tutorial completed.")
+ tutorial_end_in(5 SECONDS)
// END OF SCRIPTING
// START OF SCRIPT HELPERS
@@ -86,7 +162,7 @@
/datum/tutorial/marine/medical_basic/init_mob()
. = ..()
- arm_equipment(tutorial_mob, /datum/equipment_preset/tutorial)
+ arm_equipment(tutorial_mob, /datum/equipment_preset/tutorial/fed)
TUTORIAL_ATOM_FROM_TRACKING(/obj/structure/machinery/cryopod/tutorial, tutorial_pod)
tutorial_pod.go_in_cryopod(tutorial_mob, TRUE, FALSE)
diff --git a/code/datums/tutorial/ss13/_ss13.dm b/code/datums/tutorial/ss13/_ss13.dm
index 24165316378f..dd66b9be03a5 100644
--- a/code/datums/tutorial/ss13/_ss13.dm
+++ b/code/datums/tutorial/ss13/_ss13.dm
@@ -35,5 +35,5 @@
tutorial_mob = new_character
RegisterSignal(tutorial_mob, list(COMSIG_PARENT_QDELETING, COMSIG_MOB_DEATH, COMSIG_LIVING_GHOSTED), PROC_REF(end_tutorial))
RegisterSignal(tutorial_mob.client, COMSIG_PARENT_QDELETING, PROC_REF(end_tutorial))
- arm_equipment(tutorial_mob, /datum/equipment_preset/tutorial)
+ arm_equipment(tutorial_mob, /datum/equipment_preset/tutorial/fed)
return ..()
diff --git a/code/datums/tutorial/tutorial_example.dm b/code/datums/tutorial/tutorial_example.dm
index 067c705f623c..9042346f8d39 100644
--- a/code/datums/tutorial/tutorial_example.dm
+++ b/code/datums/tutorial/tutorial_example.dm
@@ -8,6 +8,7 @@
/datum/tutorial/marine/example/start_tutorial(mob/starting_mob)
// Here, we're calling parent and checking its return value. If it has a falsey one (as done by !.), then something went wrong and we should abort
+ // There isn't really a reason that you _shouldn't_ have this
. = ..()
if(!.)
return
diff --git a/code/game/objects/items/reagent_containers/autoinjectors.dm b/code/game/objects/items/reagent_containers/autoinjectors.dm
index 4a5d4866354e..14dd156571b7 100644
--- a/code/game/objects/items/reagent_containers/autoinjectors.dm
+++ b/code/game/objects/items/reagent_containers/autoinjectors.dm
@@ -143,6 +143,7 @@
desc = "An EZ autoinjector loaded with 3 uses of Tramadol, a weak but effective painkiller for normal wounds. Doesn't require any training to use."
volume = 15
amount_per_transfer_from_this = 15
+ uses_left = 1
/obj/item/reagent_container/hypospray/autoinjector/oxycodone
name = "oxycodone autoinjector (EXTREME PAINKILLER)"
@@ -173,6 +174,7 @@
desc = "An EZ autoinjector loaded with 1 use of Kelotane, a common burn medicine. Doesn't require any training to use."
volume = 15
amount_per_transfer_from_this = 15
+ uses_left = 1
/obj/item/reagent_container/hypospray/autoinjector/bicaridine
name = "bicaridine autoinjector"
@@ -194,6 +196,7 @@
desc = "An EZ autoinjector loaded with 1 use of Bicaridine, a common brute and circulatory damage medicine. Doesn't require any training to use."
volume = 15
amount_per_transfer_from_this = 15
+ uses_left = 1
/obj/item/reagent_container/hypospray/autoinjector/inaprovaline
name = "inaprovaline autoinjector"
diff --git a/code/game/objects/items/shards.dm b/code/game/objects/items/shards.dm
index 84c3d5b83427..37dfdcca337a 100644
--- a/code/game/objects/items/shards.dm
+++ b/code/game/objects/items/shards.dm
@@ -228,7 +228,7 @@
if(H.species.flags & NO_SHRAPNEL)
return
var/obj/limb/organ = embedded_organ
- if(istype(organ))
+ if(istype(organ) && damage_on_move)
organ.take_damage(damage_on_move * count, 0, 0, no_limb_loss = TRUE)
embedded_mob.pain.apply_pain(damage_on_move * count)
@@ -261,3 +261,15 @@
name = "alien bone fragments"
icon_state = "alienbonechips"
desc = "Sharp, jagged fragments of alien bone. Looks like the previous owner exploded violently..."
+
+/obj/item/shard/shrapnel/tutorial
+ damage_on_move = 0
+
+/obj/item/shard/shrapnel/tutorial/on_embed(mob/embedded_mob, obj/limb/target_organ)
+ if(!ishuman(embedded_mob))
+ return
+ var/mob/living/carbon/human/H = embedded_mob
+ if(H.species.flags & NO_SHRAPNEL)
+ return
+ if(istype(target_organ))
+ target_organ.embed(src, TRUE)
diff --git a/code/game/objects/items/stacks/medical.dm b/code/game/objects/items/stacks/medical.dm
index f96903cfb687..5434aa006137 100644
--- a/code/game/objects/items/stacks/medical.dm
+++ b/code/game/objects/items/stacks/medical.dm
@@ -95,6 +95,9 @@
to_chat(user, SPAN_WARNING("There are no wounds on [possessive] [affecting.display_name]."))
return TRUE
+/obj/item/stack/medical/bruise_pack/two
+ amount = 2
+
/obj/item/stack/medical/ointment
name = "ointment"
desc = "Used to treat burns, infected wounds, and relieve itching in unusual places."
diff --git a/code/game/objects/items/weapons/blades.dm b/code/game/objects/items/weapons/blades.dm
index 4b4b31539064..0abe77616f1c 100644
--- a/code/game/objects/items/weapons/blades.dm
+++ b/code/game/objects/items/weapons/blades.dm
@@ -223,6 +223,8 @@
else
INVOKE_ASYNC(embedded_human, TYPE_PROC_REF(/mob, emote), "me", 1, pick("winces.", "grimaces.", "flinches."))
+ SEND_SIGNAL(embedded_human, COMSIG_HUMAN_SHRAPNEL_REMOVED)
+
else
to_chat(user, SPAN_NOTICE("You couldn't find any shrapnel."))
diff --git a/code/modules/gear_presets/other.dm b/code/modules/gear_presets/other.dm
index 2192a26d6d8c..b7f70517dfd2 100644
--- a/code/modules/gear_presets/other.dm
+++ b/code/modules/gear_presets/other.dm
@@ -961,6 +961,12 @@
faction_group = FACTION_LIST_MARINE
languages = list(LANGUAGE_ENGLISH)
idtype = /obj/item/card/id
+ /// If the player should start out underfed
+ var/underfed = TRUE
/datum/equipment_preset/tutorial/load_status(mob/living/carbon/human/new_human)
- new_human.nutrition = NUTRITION_LOW
+ if(underfed)
+ new_human.nutrition = NUTRITION_LOW
+
+/datum/equipment_preset/tutorial/fed
+ underfed = FALSE
diff --git a/code/modules/maptext_alerts/screen_alerts.dm b/code/modules/maptext_alerts/screen_alerts.dm
index 3a25f9c07381..9c88dac5c52e 100644
--- a/code/modules/maptext_alerts/screen_alerts.dm
+++ b/code/modules/maptext_alerts/screen_alerts.dm
@@ -65,10 +65,10 @@
style_close = ""
/atom/movable/screen/text/screen_text/command_order/tutorial
- letters_per_update = 5 // overall, pretty fast while not immediately popping in
+ letters_per_update = 4 // overall, pretty fast while not immediately popping in
play_delay = 0.1
- fade_out_delay = 1.5 SECONDS
- fade_out_time = 0.2 SECONDS
+ fade_out_delay = 2 SECONDS
+ fade_out_time = 0.4 SECONDS
///proc for actually playing this screen_text on a mob.
/atom/movable/screen/text/screen_text/proc/play_to_client()
diff --git a/code/modules/organs/limbs.dm b/code/modules/organs/limbs.dm
index 58d0a4780681..a68aa0d0abc4 100644
--- a/code/modules/organs/limbs.dm
+++ b/code/modules/organs/limbs.dm
@@ -546,6 +546,7 @@ This function completely restores a damaged organ to perfect condition.
/obj/limb/proc/remove_all_bleeding(external = FALSE, internal = FALSE)
+ SEND_SIGNAL(src, COMSIG_LIMB_STOP_BLEEDING, external, internal)
if(external)
for(var/datum/effects/bleeding/external/B in bleeding_effects_list)
qdel(B)