Skip to content

Commit

Permalink
Tutorial System: Retutorializing (#5030)
Browse files Browse the repository at this point in the history
# About the pull request
Reopening of #4442

Adds a tutorial system to the game, accessible from the lobby screen.
The tutorial system is entirely isolated from the main game, allowing
players to get a curated experience to be taught the mechanics of SS13
or specific roles within CM. The tutorial system is fully capable of
supporting a theoretically infinite amount of players at once, each
getting their own instance.

See below video for an example of the in-dev "Marine - Basic" tutorial.

https://www.youtube.com/watch?v=aWEtd6EAZWk 

# Explain why it's good for the game
Teaching new players how to play the game has always been a tough bit
for us, so why not add a full-on tutorial system to get people into the
know?


# To-Do:
This list is alive and will change over time.
If you are interested in coding a tutorial, know that it's very easy and
that most of the heavy lifting's done for you! You can find a [tutorial
creation guide
here](https://hackmd.io/@mRAdleXgRfmKqh97O8ixSA/BJQsmO8kT), and you can
additionally contact me on discord at any time with questions.

Also, you can find an example tutorial
[here](https://github.com/cmss13-devs/cmss13/pull/4442/files#diff-843b2f84360b9b932dfc960027992f2b5117667962bfa8da14f9a35f0179a926).

Backend:
- [x] TGUI
- [x] Add finished tutorials to save files
- [x] Communicate to players with little playtime
- [x] Suppress combat logs and similar done in tutorials

SS13:
- [x] Basics
- [x] Intents

Marine:

- [x] Basics
- [x] Medical
- [ ] Weaponry
- [ ] Comtech - Basics
- [ ] Medic - Basics
- [ ] FTL - Basics
- [ ] Smartgunner - Basics
- [ ] Specialist - Demolitionist
- [ ] Specialist - Scout
- [ ] Specialist - Pyrotechnician
- [ ] Specialist - Grenadier
- [ ] Specialist - Sniper
- [ ] Squad Leader - Basics

Xenomorph:

- [ ] Basics
- [ ] Builder Caste - Basics

# Changelog
:cl:
add: Added a tutorial system for various roles (and just general
information), find it in the lobby screen.
/:cl:

---------

Co-authored-by: fira <[email protected]>
  • Loading branch information
Zonespace27 and fira authored Dec 29, 2023
1 parent 2bc3c47 commit f377f35
Show file tree
Hide file tree
Showing 97 changed files with 2,308 additions and 140 deletions.
2 changes: 2 additions & 0 deletions code/__DEFINES/access.dm
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,8 @@ most of them are tied into map-placed objects. This should be reworked in the fu
/// Ancients only
#define ACCESS_YAUTJA_ANCIENT 392

/// Anything in a tutorial sequence that shouldn't be accessed
#define ACCESS_TUTORIAL_LOCKED 998
///Temporary, just so I can flag places I need to change
#define ACCESS_COME_BACK_TO_ME 999

Expand Down
3 changes: 3 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/living/signals_human.dm
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,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"
12 changes: 12 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/living/signals_living.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,19 @@
#define COMSIG_LIVING_PRE_COLLIDE "living_pre_collide"
#define COMPONENT_LIVING_COLLIDE_HANDLED (1<<0)

/// From /mob/living/proc/do_ghost() : (mob/dead/observer/ghost)
#define COMSIG_LIVING_GHOSTED "living_ghosted"

/// From /mob/living/carbon/human/attack_hand() : (mob/living/carbon/human/attacked_mob)
#define COMSIG_LIVING_ATTACKHAND_HUMAN "living_attackhand_human"

/// From /obj/item/reagent_container/hypospray/attack() : (obj/item/reagent_container/hypospray/injector)
#define COMSIG_LIVING_HYPOSPRAY_INJECTED "living_hypospray_injected"

///from base of mob/living/set_buckled(): (new_buckled)
#define COMSIG_LIVING_SET_BUCKLED "living_set_buckled"
///from base of mob/living/set_body_position()
#define COMSIG_LIVING_SET_BODY_POSITION "living_set_body_position"

/// from base of /mob/living/apply_status_effect(): (datum/status_effect/new_effect)
#define COMSIG_LIVING_APPLY_EFFECT "living_apply_effect"
37 changes: 37 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/signals_mob.dm
Original file line number Diff line number Diff line change
Expand Up @@ -131,5 +131,42 @@
/// From /obj/item/proc/pickup() : (obj/item/picked_up)
#define COMSIG_MOB_PICKUP_ITEM "mob_pickup_item"

/// From /obj/item/proc/attack_self() : (obj/item/used)
#define COMSIG_MOB_ITEM_ATTACK_SELF "mob_item_attack_self"

/// From /obj/item/proc/dropped() : (obj/item/dropped)
#define COMSIG_MOB_ITEM_DROPPED "mob_item_dropped"


/// From /obj/item/reagent_container/food/snacks/proc/on_Consume() : (obj/item/reagent_container/food/snacks/eaten_food)
#define COMSIG_MOB_EATEN_SNACK "mob_eaten_snack"

/// From /atom/proc/attackby() : (atom/attacked, obj/item/attacked_with)
#define COMSIG_MOB_PARENT_ATTACKBY "mob_parent_attackby"

/// From /obj/item/weapon/gun/proc/reload_into_chamber() : (obj/item/weapon/gun/empty_gun)
#define COMSIG_MOB_GUN_EMPTY "mob_gun_empty"

/// From /obj/item/weapon/gun/proc/reload() : (obj/item/weapon/gun/reloaded)
#define COMSIG_MOB_RELOADED_GUN "mob_reloaded_gun"

/// From /mob/proc/get_status_tab_items() : (list/status_list)
#define COMSIG_MOB_GET_STATUS_TAB_ITEMS "mob_get_status_tab_items"

/// From /datum/tutorial/proc/update_objective() : (new_objective)
#define COMSIG_MOB_TUTORIAL_UPDATE_OBJECTIVE "mob_tutorial_update_objective"

/// From /mob/proc/swap_hand() : ()
#define COMSIG_MOB_SWAPPED_HAND "mob_swapped_hand"

/// From /mob/proc/a_intent_change() : (new_intent)
#define COMSIG_MOB_INTENT_CHANGE "mob_intent_change"

/// From /obj/item/grab/proc/progress_passive() : (mob/living/carbon/human/grabber)
#define COMSIG_MOB_AGGRESSIVELY_GRABBED "mob_aggressively_grabbed"
#define COMSIG_MOB_AGGRESIVE_GRAB_CANCEL (1<<0)

/// Cancels all running cloaking effects on target
#define COMSIG_MOB_EFFECT_CLOAK_CANCEL "mob_effect_cloak_cancel"

#define COMSIG_MOB_END_TUTORIAL "mob_end_tutorial"
10 changes: 10 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_obj.dm
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@
/// from /obj/proc/afterbuckle()
#define COSMIG_OBJ_AFTER_BUCKLE "signal_obj_after_buckle"

/// from /obj/structure/machinery/cryopod/go_out()
#define COMSIG_CRYOPOD_GO_OUT "cryopod_go_out"

/// 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"

#define COMSIG_DROPSHIP_ADD_EQUIPMENT "dropship_add_equipment"
#define COMSIG_DROPSHIP_REMOVE_EQUIPMENT "dropship_remove_equipment"

#define COMSIG_STRUCTURE_CRATE_SQUAD_LAUNCHED "structure_crate_squad_launched"
3 changes: 0 additions & 3 deletions code/__DEFINES/dcs/signals/signals_client.dm
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,3 @@

/// Called when something is removed from a client's screen : /client/proc/remove_from_screen(screen_remove)
#define COMSIG_CLIENT_SCREEN_REMOVE "client_screen_remove"

/// When a mind is transfered to another mob at /datum/mind/proc/transfer_to()
#define COMSIG_CLIENT_MIND_TRANSFER "mind_transfer"
1 change: 1 addition & 0 deletions code/__DEFINES/mob.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#define DEFAULT_MOB_STATUS_FLAGS CANKNOCKDOWN|CANPUSH|STATUS_FLAGS_DEBILITATE
5 changes: 5 additions & 0 deletions code/__DEFINES/traits.dm
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@
#define TRAIT_HARDCORE "t_hardcore"
/// If the mob is able to use the vulture rifle or spotting scope
#define TRAIT_VULTURE_USER "t_vulture_user"
/// If the mob is currently loading a tutorial
#define TRAIT_IN_TUTORIAL "t_IN_TUTORIAL"
/// If the mob is cloaked in any form
#define TRAIT_CLOAKED "t_cloaked"

Expand Down Expand Up @@ -297,6 +299,7 @@ GLOBAL_LIST_INIT(mob_traits, list(
TRAIT_REAGENT_SCANNER,
TRAIT_ABILITY_BURROWED,
TRAIT_VULTURE_USER,
TRAIT_IN_TUTORIAL,
))

/*
Expand Down Expand Up @@ -403,6 +406,8 @@ GLOBAL_LIST(trait_name_map)
#define TRAIT_SOURCE_JOB "t_s_job"
///Status trait forced by staff
#define TRAIT_SOURCE_ADMIN "t_s_admin"
/// Status trait coming from a tutorial
#define TRAIT_SOURCE_TUTORIAL "t_s_tutorials"
///Status trait coming from equipment
#define TRAIT_SOURCE_EQUIPMENT(slot) "t_s_equipment_[slot]"
///Status trait coming from skill
Expand Down
6 changes: 6 additions & 0 deletions code/__DEFINES/tutorial.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#define TUTORIAL_ATOM_FROM_TRACKING(path, varname) var##path/##varname = tracking_atoms[##path]

#define TUTORIAL_CATEGORY_BASE "Base" // Shouldn't be used outside of base types
#define TUTORIAL_CATEGORY_SS13 "Space Station 13"
#define TUTORIAL_CATEGORY_MARINE "Marine"
#define TUTORIAL_CATEGORY_XENO "Xenomorph"
1 change: 0 additions & 1 deletion code/__HELPERS/level_traits.dm
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#define is_admin_level(z) SSmapping.level_trait(z, ZTRAIT_ADMIN)

#define is_ground_level(z) SSmapping.level_trait(z, ZTRAIT_GROUND)
Expand Down
12 changes: 12 additions & 0 deletions code/__HELPERS/unsorted.dm
Original file line number Diff line number Diff line change
Expand Up @@ -2088,3 +2088,15 @@ GLOBAL_LIST_INIT(duplicate_forbidden_vars,list(

if(NORTHWEST)
return list(NORTHWEST, NORTH, WEST)

/// Returns TRUE if the target is somewhere that the game should not interact with if possible
/// In this case, admin Zs and tutorial areas
/proc/should_block_game_interaction(atom/target)
if(is_admin_level(target.z))
return TRUE

var/area/target_area = get_area(target)
if(target_area?.block_game_interaction)
return TRUE

return FALSE
2 changes: 2 additions & 0 deletions code/_onclick/item_attack.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/obj/item/proc/attack_self(mob/user)
SHOULD_CALL_PARENT(TRUE)
SEND_SIGNAL(src, COMSIG_ITEM_ATTACK_SELF, user)
SEND_SIGNAL(user, COMSIG_MOB_ITEM_ATTACK_SELF, src)

if(flags_item & CAN_DIG_SHRAPNEL && ishuman(user))
dig_out_shrapnel(user)
Expand All @@ -11,6 +12,7 @@
/atom/proc/attackby(obj/item/W, mob/living/user,list/mods)
if(SEND_SIGNAL(src, COMSIG_PARENT_ATTACKBY, W, user, mods) & COMPONENT_NO_AFTERATTACK)
return TRUE
SEND_SIGNAL(user, COMSIG_MOB_PARENT_ATTACKBY, src, W)
return FALSE

/atom/movable/attackby(obj/item/W, mob/living/user)
Expand Down
2 changes: 1 addition & 1 deletion code/_onclick/observer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
if(ismob(target) || isVehicle(target))
if(isxeno(target) && SSticker.mode.check_xeno_late_join(src)) //if it's a xeno and all checks are alright, we are gonna try to take their body
var/mob/living/carbon/xenomorph/xeno = target
if(xeno.stat == DEAD || is_admin_level(xeno.z) || xeno.aghosted)
if(xeno.stat == DEAD || should_block_game_interaction(xeno) || xeno.aghosted)
to_chat(src, SPAN_WARNING("You cannot join as [xeno]."))
do_observe(xeno)
return FALSE
Expand Down
2 changes: 1 addition & 1 deletion code/_onclick/xeno.dm
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ so that it doesn't double up on the delays) so that it applies the delay immedia
if(alt_pressed && shift_pressed)
if(istype(target, /mob/living/carbon/xenomorph))
var/mob/living/carbon/xenomorph/xeno = target
if(!QDELETED(xeno) && xeno.stat != DEAD && !is_admin_level(xeno.z) && xeno.check_state(TRUE) && xeno.hivenumber == hivenumber)
if(!QDELETED(xeno) && xeno.stat != DEAD && !should_block_game_interaction(xeno) && xeno.check_state(TRUE) && xeno.hivenumber == hivenumber)
overwatch(xeno)
next_move = world.time + 3 // Some minimal delay so this isn't crazy spammy
return TRUE
Expand Down
1 change: 1 addition & 0 deletions code/controllers/subsystem/ticker.dm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ SUBSYSTEM_DEF(ticker)

var/totalPlayers = 0 //used for pregame stats on statpanel
var/totalPlayersReady = 0 //used for pregame stats on statpanel
var/tutorial_disabled = FALSE //zonenote

/datum/controller/subsystem/ticker/Initialize(timeofday)
load_mode()
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ammo/ammo.dm
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
SHOULD_NOT_SLEEP(TRUE)
return

/datum/ammo/proc/on_embed(mob/embedded_mob, obj/limb/target_organ)
/datum/ammo/proc/on_embed(mob/embedded_mob, obj/limb/target_organ, silent = FALSE)
return

/datum/ammo/proc/do_at_max_range(obj/projectile/P)
Expand Down
2 changes: 1 addition & 1 deletion code/datums/ammo/misc.dm
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@
accurate_range = 12
shell_speed = AMMO_SPEED_TIER_1

/datum/ammo/souto/on_embed(mob/embedded_mob, obj/limb/target_organ)
/datum/ammo/souto/on_embed(mob/embedded_mob, obj/limb/target_organ, silent = FALSE)
if(ishuman(embedded_mob) && !isyautja(embedded_mob))
if(istype(target_organ))
target_organ.embed(new can_type)
Expand Down
25 changes: 25 additions & 0 deletions code/datums/components/tutorial_status.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/datum/component/tutorial_status
dupe_mode = COMPONENT_DUPE_UNIQUE
/// What the mob's current tutorial status is, displayed in the status panel
var/tutorial_status = ""

/datum/component/tutorial_status/Initialize()
. = ..()
if(!ismob(parent))
return COMPONENT_INCOMPATIBLE

/datum/component/tutorial_status/RegisterWithParent()
..()
RegisterSignal(parent, COMSIG_MOB_TUTORIAL_UPDATE_OBJECTIVE, PROC_REF(update_objective))
RegisterSignal(parent, COMSIG_MOB_GET_STATUS_TAB_ITEMS, PROC_REF(get_status_tab_item))

/datum/component/tutorial_status/proc/update_objective(datum/source, objective_text)
SIGNAL_HANDLER

tutorial_status = objective_text

/datum/component/tutorial_status/proc/get_status_tab_item(datum/source, list/status_tab_items)
SIGNAL_HANDLER

if(tutorial_status)
status_tab_items += "Tutorial Objective: " + tutorial_status
4 changes: 2 additions & 2 deletions code/datums/datacore.dm
Original file line number Diff line number Diff line change
Expand Up @@ -206,8 +206,8 @@ GLOBAL_DATUM_INIT(data_core, /datum/datacore, new)
sleep(40)

var/list/jobs_to_check = GLOB.ROLES_CIC + GLOB.ROLES_AUXIL_SUPPORT + GLOB.ROLES_MISC + GLOB.ROLES_POLICE + GLOB.ROLES_ENGINEERING + GLOB.ROLES_REQUISITION + GLOB.ROLES_MEDICAL + GLOB.ROLES_MARINES
for(var/mob/living/carbon/human/H in GLOB.human_mob_list)
if(is_admin_level(H.z))
for(var/mob/living/carbon/human/H as anything in GLOB.human_mob_list)
if(should_block_game_interaction(H))
continue
if(H.job in jobs_to_check)
manifest_inject(H)
Expand Down
13 changes: 7 additions & 6 deletions code/datums/effects/bleeding.dm
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
if(L && istype(L))
limb = L

/datum/effects/bleeding/Destroy()
if(limb)
SEND_SIGNAL(limb, COMSIG_LIMB_STOP_BLEEDING, TRUE, FALSE)
limb.bleeding_effects_list -= src
limb = null
return ..()

/datum/effects/bleeding/validate_atom(atom/A)
if(isobj(A))
return FALSE
Expand Down Expand Up @@ -48,12 +55,6 @@
duration += damage * (blood_duration_multiplier / BLOOD_ADD_PENALTY)
blood_loss += damage / (blood_loss_divider * BLOOD_ADD_PENALTY) //Make the first hit count, adding on bleeding has a penalty

/datum/effects/bleeding/Destroy()
if(limb)
limb.bleeding_effects_list -= src
return ..()


/datum/effects/bleeding/external
var/buffer_blood_loss = 0

Expand Down
2 changes: 1 addition & 1 deletion code/datums/helper_datums/teleport.dm
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@
teleatom.visible_message(SPAN_DANGER("<B>[teleatom] bounces off of the portal!</B>"))
return 0

if(is_admin_level(destination.z))
if(should_block_game_interaction(destination))
if(length(teleatom.search_contents_for(/obj/item/storage/backpack/holding)))
teleatom.visible_message(SPAN_DANGER("<B>The Bag of Holding bounces off of the portal!</B>"))
return 0
Expand Down
2 changes: 0 additions & 2 deletions code/datums/mind.dm
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@
msg_admin_niche("[key]/[ckey] has tried to transfer to deleted [new_character].")
return

SEND_SIGNAL(current.client, COMSIG_CLIENT_MIND_TRANSFER, new_character)

if(current)
current.mind = null //remove ourself from our old body's mind variable
SSnano.nanomanager.user_transferred(current, new_character) // transfer active NanoUI instances to new user
Expand Down
1 change: 1 addition & 0 deletions code/datums/status_effects/_status_effect_helpers.dm
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@

// Create the status effect with our mob + our arguments
var/datum/status_effect/new_instance = new new_effect(arguments)
SEND_SIGNAL(src, COMSIG_LIVING_APPLY_EFFECT, new_instance)
if(!QDELETED(new_instance))
return new_instance

Expand Down
Loading

0 comments on commit f377f35

Please sign in to comment.