Skip to content

Commit

Permalink
Abomination Tutorial (#6442)
Browse files Browse the repository at this point in the history
# About the pull request

"This'll be a nice and simple 2 hour PR" - Me, 6 hours ago


![image](https://github.com/cmss13-devs/cmss13/assets/41448081/9ee648df-4a48-4e94-a2ac-479e8a4a77a5)

Adds a tutorial for how to play abom. You cannot roll abom without
having completed its tutorial.

# Explain why it's good for the game

More tutorials are good, and encouraging players to know the basics of a
decently complicated and rare xeno before playing it is a good idea.

# Changelog
:cl:
add: Added a tutorial for xenomorph Abominations. You must complete the
tutorial before being able to roll for Abomination.
/:cl:

---------

Co-authored-by: John Doe <[email protected]>
Co-authored-by: Drathek <[email protected]>
  • Loading branch information
3 people authored Jul 16, 2024
1 parent c87f66d commit c2266b8
Show file tree
Hide file tree
Showing 76 changed files with 460 additions and 96 deletions.
4 changes: 4 additions & 0 deletions code/__DEFINES/dcs/signals/atom/mob/living/signals_xeno.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@

/// From /datum/action/xeno_action/proc/use_ability_wrapper(): (mob/owner)
#define COMSIG_XENO_ACTION_USED "xeno_action_used"
/// From /datum/action/xeno_action/proc/use_ability_wrapper(): (mob/owner)
#define COMSIG_XENO_PRE_ACTION_USED "xeno_pre_action_used"
/// From /datum/action/xeno_action/proc/use_ability_wrapper(): (mob/owner)
#define COMSIG_XENO_FAILED_ACTION_USED "xeno_failed_action_used"
/// From /mob/living/carbon/xenomorph/proc/check_blood_splash()
#define COMSIG_XENO_DEAL_ACID_DAMAGE "xeno_deal_acid_damage"
/// From /mob/living/carbon/xenomorph/proc/recalculate_speed()
Expand Down
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals/atom/signals_item.dm
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,5 @@
#define COMSIG_CAMERA_SET_AREA "camera_manager_set_area"
#define COMSIG_CAMERA_CLEAR "camera_manager_clear_target"
#define COMSIG_CAMERA_REFRESH "camera_manager_refresh"

#define COMSIG_PRED_BRACER_DECLOAKED "pred_bracer_decloaked"
2 changes: 2 additions & 0 deletions code/__DEFINES/dcs/signals/signals_datum.dm
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#define COMSIG_ACTION_HIDDEN "action_hidden"
/// From base of /datum/action/proc/unhide_from(): (mob/owner)
#define COMSIG_ACTION_UNHIDDEN "action_unhidden"
/// From base of /datum/action/proc/action_activate() : ()
#define COMSIG_ACTION_ACTIVATED "action_activated"

///from /datum/component/bonus_damage_stack
#define COMSIG_BONUS_DAMAGE "bonus_damage"
Expand Down
7 changes: 6 additions & 1 deletion code/__HELPERS/game.dm
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
* * hive - The hive we're filling a slot for to check if the player is banished
* * sorted - Whether to sort by larva_queue_time (default TRUE) or leave unsorted
*/
/proc/get_alien_candidates(datum/hive_status/hive = null, sorted = TRUE)
/proc/get_alien_candidates(datum/hive_status/hive = null, sorted = TRUE, abomination = FALSE)
var/list/candidates = list()

for(var/mob/dead/observer/cur_obs as anything in GLOB.observer_list)
Expand Down Expand Up @@ -275,6 +275,11 @@
if(banished)
continue

if(abomination)
if(!(/datum/tutorial/xenomorph/abomination::tutorial_id in cur_obs.client.prefs.completed_tutorials))
to_chat(cur_obs, SPAN_BOLDNOTICE("You were passed over for playing as an Abomination because you have not completed its tutorial."))
continue

candidates += cur_obs

// Optionally sort by larva_queue_time
Expand Down
2 changes: 1 addition & 1 deletion code/_onclick/xeno.dm
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*/

/mob/living/carbon/xenomorph/UnarmedAttack(atom/target, proximity, click_parameters, tile_attack = FALSE, ignores_resin = FALSE)
if(body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) //No attacks while laying down
if(body_position == LYING_DOWN || HAS_TRAIT(src, TRAIT_ABILITY_BURROWED) || cannot_slash) //No attacks while laying down
return FALSE
var/mob/alt

Expand Down
1 change: 1 addition & 0 deletions code/controllers/subsystem/vote.dm
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ SUBSYSTEM_DEF(vote)
qdel(src)

/datum/action/innate/vote/action_activate()
. = ..()
owner.vote()

/datum/action/innate/vote/proc/remove_from_client()
Expand Down
9 changes: 8 additions & 1 deletion code/datums/action.dm
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@
return

/datum/action/proc/action_activate()
return
SHOULD_CALL_PARENT(TRUE)

SEND_SIGNAL(src, COMSIG_ACTION_ACTIVATED)

/// handler for when a keybind signal is received by the action, calls the action_activate proc asynchronous
/datum/action/proc/keybind_activation()
Expand Down Expand Up @@ -158,6 +160,10 @@
hidden = FALSE
L.update_action_buttons()

/proc/get_action(mob/action_mob, action_path)
for(var/datum/action/action in action_mob.actions)
if(istype(action, action_path))
return action

/datum/action/item_action
name = "Use item"
Expand All @@ -182,6 +188,7 @@
return ..()

/datum/action/item_action/action_activate()
. = ..()
if(target)
var/obj/item/I = target
I.ui_action_click(owner, holder_item)
Expand Down
1 change: 1 addition & 0 deletions code/datums/components/bad_leg.dm
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@
CRASH("No bound wound to link action")

/datum/action/human_action/rest_legs/action_activate()
. = ..()
var/mob/living/carbon/human/homan = owner
if(in_use)
to_chat(homan, SPAN_WARNING("You're already doing that!"))
Expand Down
1 change: 1 addition & 0 deletions code/datums/statistics/entities/round_stats.dm
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,7 @@
return TRUE

/datum/action/show_round_statistics/action_activate()
. = ..()
if(!can_use_action())
return

Expand Down
5 changes: 4 additions & 1 deletion code/datums/tutorial/_tutorial.dm
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ GLOBAL_LIST_EMPTY_TYPED(ongoing_tutorials, /datum/tutorial)
/datum/tutorial
/// What the tutorial is called, is player facing
var/name = "Base"
/// Internal ID of the tutorial, kept for save files
/// Internal ID of the tutorial, kept for save files. Format is "tutorialtype_specifictutorial_number". So, the first basic xeno tutorial would be "xeno_basic_1", and the 2nd marine medical tutorial would be "marine_medical_2"
var/tutorial_id = "base"
/// A short 1-2 sentence description of the tutorial itself
var/desc = ""
Expand Down Expand Up @@ -144,6 +144,8 @@ GLOBAL_LIST_EMPTY_TYPED(ongoing_tutorials, /datum/tutorial)

/// Ends the tutorial after a certain amount of time.
/datum/tutorial/proc/tutorial_end_in(time = 5 SECONDS, completed = TRUE)
if(completed)
mark_completed() // This is done because if you're calling this proc with completed == TRUE, then the tutorial's a done deal. We shouldn't penalize the player if they exit a few seconds before it actually completes.
tutorial_ending = TRUE
addtimer(CALLBACK(src, PROC_REF(end_tutorial), completed), time)

Expand Down Expand Up @@ -221,6 +223,7 @@ GLOBAL_LIST_EMPTY_TYPED(ongoing_tutorials, /datum/tutorial)
tutorial = WEAKREF(selected_tutorial)

/datum/action/tutorial_end/action_activate()
. = ..()
if(!tutorial)
return

Expand Down
247 changes: 247 additions & 0 deletions code/datums/tutorial/xenomorph/abomination.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,247 @@
/datum/tutorial/xenomorph/abomination
name = "Xenomorph - Predalien"
desc = "A tutorial to teach you how to play the \"Predalien\", also known as Abomination, xenomorph caste. Completing this is required to be able to play an Abomination."
icon_state = "predalien"
tutorial_id = "xeno_abom_1"
tutorial_template = /datum/map_template/tutorial/s7x7
starting_xenomorph_type = /mob/living/carbon/xenomorph/predalien/tutorial
/// How many marines in the kill_marines stage have been killed
var/ending_marines_killed = 0

// START OF SCRITPING

/datum/tutorial/xenomorph/abomination/start_tutorial(mob/starting_mob)
. = ..()
if(!.)
return

init_mob()
xeno.lock_evolve = TRUE

message_to_player("Welcome to the tutorial for the Abomination xenomorph. As an Abomination, you are a frontline powerhouse whose damage scales with your kill count.")
message_to_player("Your kill count scales when you kill humans with your slash attack, up to 10 kills. Ability kills do not count towards this.")

addtimer(CALLBACK(src, PROC_REF(how_to_be_abom)), 12 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/how_to_be_abom()
message_to_player("Be aware that you are kill-on-sight to all Predators forever, and will very likely need to defend yourself against multiple.")
message_to_player("Be sure to stick close to other xenomorphs or over-extend. While you may be stronger than many, you don't have enough health or armor to go out on your own.")
addtimer(CALLBACK(src, PROC_REF(feral_rush_tutorial)), 10.5 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/feral_rush_tutorial()
var/datum/action/rush = give_action(xeno, /datum/action/xeno_action/onclick/feralrush)
message_to_player("Your first unique ability is <b>Feral Rush</b>, an ability that temporarily increases your speed and your armor. Use <B>Feral Rush</b> to continue.")
update_objective("Use your Feral Rush ability.")
add_highlight(rush.button)
RegisterSignal(rush, COMSIG_XENO_ACTION_USED, PROC_REF(on_rush_used))

/datum/tutorial/xenomorph/abomination/proc/on_rush_used(datum/action/source, mob/owner)
SIGNAL_HANDLER

UnregisterSignal(source, COMSIG_XENO_ACTION_USED)
remove_highlight(source.button)
addtimer(CALLBACK(src, PROC_REF(predalien_roar_tutorial_1)), 5 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/predalien_roar_tutorial_1()
hide_action(xeno, /datum/action/xeno_action/onclick/feralrush)
xeno.cannot_slash = TRUE
message_to_player("Your next ability is <b>Roar</b>, a versatile ability that disables any motion detectors or cloaks in a medium radius around you.")
message_to_player("Additionally, it gives a slash and speed bonus to any friendly xenomorphs in range.")
addtimer(CALLBACK(src, PROC_REF(predalien_roar_tutorial_2)), 8 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/predalien_roar_tutorial_2()
var/datum/action/roar = give_action(xeno, /datum/action/xeno_action/onclick/predalien_roar)
message_to_player("One of <b>Roar</b>'s most useful abilities is uncloaking nearby Predators. Use <b>Roar</b> to uncloak the newly spawned Predator.")
update_objective("Use your Roar ability to uncloak the nearby predator.")
add_highlight(roar.button)
var/mob/living/carbon/human/pred = new(loc_from_corner(3, 3))
add_to_tracking_atoms(pred)
pred.create_hud()
arm_equipment(pred, /datum/equipment_preset/yautja/blooded)
var/obj/item/clothing/gloves/yautja/hunter/bracers = locate() in pred
if(!bracers)
message_to_player("Something has gone wrong. Please make a bug report.")
CRASH("predator spawned without bracers in tutorial")

bracers.cloaker_internal(pred, TRUE, TRUE, TRUE)
RegisterSignal(bracers, COMSIG_PRED_BRACER_DECLOAKED, PROC_REF(smash_tutorial_1))

/datum/tutorial/xenomorph/abomination/proc/smash_tutorial_1(datum/source)
SIGNAL_HANDLER

var/datum/action/roar = get_action(xeno, /datum/action/xeno_action/onclick/predalien_roar)
remove_highlight(roar.button)
update_objective("")

UnregisterSignal(source, COMSIG_PRED_BRACER_DECLOAKED)
addtimer(CALLBACK(src, PROC_REF(smash_tutorial_2)), 2.5 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/smash_tutorial_2()
hide_action(xeno, /datum/action/xeno_action/onclick/predalien_roar)
message_to_player("Good. <b>Roar</b> will be one of your primary tools for defending against Predators. Your next ability is <b>Feral Smash</b>.")
xeno.cannot_slash = FALSE

TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, pred)
remove_from_tracking_atoms(pred)
qdel(pred)

addtimer(CALLBACK(src, PROC_REF(smash_tutorial_3)), 5 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/smash_tutorial_3()
var/datum/action/smash = give_action(xeno, /datum/action/xeno_action/activable/feral_smash)
RegisterSignal(smash, COMSIG_XENO_PRE_ACTION_USED, PROC_REF(frenzy_tutorial_1))
add_highlight(smash.button)

message_to_player("<b>Feral Smash</b> is a strong lunge with a range of five tiles. It deals decent damage that scales with your kill count.")
message_to_player("Use <b>Feral Smash</b> on the marine to continue.")
update_objective("Use your Feral Smash ability on the marine.")

xeno.forceMove(loc_from_corner(0, 2))
xeno.anchored = TRUE
ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_TUTORIAL)

var/mob/living/carbon/human/marine = new(loc_from_corner(4, 2))
add_to_tracking_atoms(marine)
arm_equipment(marine, /datum/equipment_preset/uscm/private_equipped)

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_1(datum/action/source, mob/owner)
SIGNAL_HANDLER

xeno.anchored = FALSE
REMOVE_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_TUTORIAL)
RegisterSignal(source, COMSIG_XENO_ACTION_USED, PROC_REF(frenzy_tutorial_2))
RegisterSignal(source, COMSIG_XENO_FAILED_ACTION_USED, PROC_REF(frenzy_tutorial_1_fail))

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_1_fail(datum/action/source, mob/owner)
SIGNAL_HANDLER

xeno.anchored = TRUE
ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_TUTORIAL)
UnregisterSignal(source, list(COMSIG_XENO_FAILED_ACTION_USED, COMSIG_XENO_ACTION_USED))

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_2(datum/action/source, mob/owner)
SIGNAL_HANDLER

if(get_turf(xeno) == loc_from_corner(0, 2)) // xeno didn't lunge at the mob
xeno.anchored = TRUE
UnregisterSignal(source, COMSIG_XENO_ACTION_USED)
ADD_TRAIT(xeno, TRAIT_IMMOBILIZED, TRAIT_SOURCE_TUTORIAL)
return

update_objective("")
var/datum/action/smash = get_action(xeno, /datum/action/xeno_action/activable/feral_smash)
remove_highlight(smash.button)
UnregisterSignal(source, list(COMSIG_XENO_ACTION_USED, COMSIG_XENO_PRE_ACTION_USED))
addtimer(CALLBACK(src, PROC_REF(frenzy_tutorial_3)), 2 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_3()
remove_action(xeno, /datum/action/xeno_action/activable/feral_smash)
message_to_player("Good. Your final ability is <b>Feral Frenzy</b>, a strong ability that can alternate between hitting a single target or all within a large radius. However, it locks you in place while it winds up.")

TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, marine)
remove_from_tracking_atoms(marine)
qdel(marine)

addtimer(CALLBACK(src, PROC_REF(frenzy_tutorial_4)), 6 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_4()
var/mob/living/carbon/human/marine = new(loc_from_corner(4, 2))
add_to_tracking_atoms(marine)
arm_equipment(marine, /datum/equipment_preset/uscm/private_equipped)

var/datum/action/frenzy = give_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
add_highlight(frenzy.button)
message_to_player("By default, <b>Feral Frenzy</b> is on single-target mode. Use <b>Feral Frenzy</b> on the newly spawned marine.")
update_objective("Use Feral Frenzy on the marine.")

RegisterSignal(frenzy, COMSIG_XENO_ACTION_USED, PROC_REF(frenzy_tutorial_5))

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_5(datum/action/xeno_action/source, mob/owner)
SIGNAL_HANDLER

TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, marine)
if(get_dist(marine, xeno) > 1)
return

UnregisterSignal(source, COMSIG_XENO_ACTION_USED)
var/datum/action/frenzy = get_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
remove_highlight(frenzy.button)
var/datum/action/frenzy_toggle = give_action(xeno, /datum/action/xeno_action/onclick/toggle_gut_targeting)
add_highlight(frenzy_toggle.button)
message_to_player("Good, now toggle <b>Feral Frenzy</b>'s AOE mode with the newly available <b>Toggle Gutting Type</b> ability.")
update_objective("Use the Toggle Gutting Type ability to change your frenzy mode.")

RegisterSignal(frenzy_toggle, COMSIG_XENO_ACTION_USED, PROC_REF(frenzy_tutorial_6))

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_6(datum/action/xeno_action/source, mob/owner)
SIGNAL_HANDLER

UnregisterSignal(source, COMSIG_XENO_ACTION_USED)
remove_highlight(source.button)
source.plasma_cost = INFINITY // slightly scuffed way of disabling the switch button
source.update_button_icon()

message_to_player("<b>Feral Frenzy</b> has now been changed into AOE mode. Use <b>Feral Frenzy</b> again anywhere within 2 tiles of the marine.")
update_objective("Use Feral Frenzy within 2 tiles of the marine.")
TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, marine)
marine.rejuvenate()
var/datum/action/xeno_action/activable/feralfrenzy/frenzy = get_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
frenzy.targeting = AOETARGETGUT
frenzy.reduce_cooldown(frenzy.xeno_cooldown)
add_highlight(frenzy.button)

RegisterSignal(frenzy, COMSIG_XENO_ACTION_USED, PROC_REF(frenzy_tutorial_7))

/datum/tutorial/xenomorph/abomination/proc/frenzy_tutorial_7(datum/action/source)
SIGNAL_HANDLER

TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, marine)
var/datum/action/xeno_action/activable/feralfrenzy/frenzy = get_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
if(get_dist(xeno, marine) > frenzy.range)
// Not close enough to actually hit the marine
return

UnregisterSignal(frenzy, COMSIG_XENO_ACTION_USED)
remove_highlight(frenzy.button)
message_to_player("Good. As you may have noticed, the AOE version of <b>Feral Frenzy</b> takes longer to wind up, in addition to doing less overall damage.")
addtimer(CALLBACK(src, PROC_REF(kill_marines)), 6 SECONDS)

/datum/tutorial/xenomorph/abomination/proc/kill_marines()
message_to_player("To finish the tutorial, kill the three newly-spawned marines using any of your attacks or abilities.")

// Spawn/rejuv the dummies
TUTORIAL_ATOM_FROM_TRACKING(/mob/living/carbon/human, marine) // we can reuse this one though
marine.rejuvenate()
marine.forceMove(loc_from_corner(4, 2))
RegisterSignal(marine, COMSIG_MOB_DEATH, PROC_REF(kill_marines_2))

var/mob/living/carbon/human/marine_2 = new(loc_from_corner(2, 2))
arm_equipment(marine_2, /datum/equipment_preset/uscm/private_equipped)
RegisterSignal(marine_2, COMSIG_MOB_DEATH, PROC_REF(kill_marines_2))

var/mob/living/carbon/human/marine_3 = new(loc_from_corner(0, 2))
arm_equipment(marine_3, /datum/equipment_preset/uscm/private_equipped)
RegisterSignal(marine_3, COMSIG_MOB_DEATH, PROC_REF(kill_marines_2))

// Arrange the actions about how they'd be in an actual game
remove_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
remove_action(xeno, /datum/action/xeno_action/onclick/toggle_gut_targeting)

give_action(xeno, /datum/action/xeno_action/activable/tail_stab)
give_action(xeno, /datum/action/xeno_action/onclick/feralrush)
give_action(xeno, /datum/action/xeno_action/onclick/predalien_roar)
give_action(xeno, /datum/action/xeno_action/activable/feral_smash)
give_action(xeno, /datum/action/xeno_action/activable/feralfrenzy)
give_action(xeno, /datum/action/xeno_action/onclick/toggle_gut_targeting)

/datum/tutorial/xenomorph/abomination/proc/kill_marines_2(datum/source)
SIGNAL_HANDLER

if(ending_marines_killed < 2)
ending_marines_killed++
return

message_to_player("Good work. The tutorial will end shortly.")
tutorial_end_in(7 SECONDS, TRUE)

// END OF SCRIPTING
2 changes: 1 addition & 1 deletion code/datums/xeno_shields/shield_types/vanguard_shield.dm
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
return

linked_xeno.overlay_shields()
var/datum/action/xeno_action/activable/cleave/cAction = get_xeno_action_by_type(linked_xeno, /datum/action/xeno_action/activable/cleave)
var/datum/action/xeno_action/activable/cleave/cAction = get_action(linked_xeno, /datum/action/xeno_action/activable/cleave)
if (istype(cAction))
addtimer(CALLBACK(cAction, TYPE_PROC_REF(/datum/action/xeno_action/activable/cleave, remove_buff)), 7, TIMER_UNIQUE)

Expand Down
Loading

0 comments on commit c2266b8

Please sign in to comment.