Files
Paradise/code/game/gamemodes/ninja_objectives.dm
PollardTheDragon 394c3b0108 Stealth and Steel: The Space Ninja (#31497)
* Space Ninja Antag Datum

* Small fixes

* Ninja outfit, ekatana, actions

* Ninja scanner and spans

* Ninja uplink implant

* Ninja bombs

* Ninja bomb flare grants

* Ninja modsuit, objective payouts

* Fixes objectives

* Ninja stealth fix, modsuit sprites and final implementations

* Lints

* Ninja scanner sprites

* Ninja Suit and Energy Shuriken Sprites

* Fixes config, Adjusts stim ability, adds shuriken printer and e-shurikens, uplink stuff

* Fixes a duplicate icon

* Attack chain

* Energy katana item sprite

* Ninja net gun

* Brazil

* Spawning ninjas, ninja on traitor panel, ninja spawn sound

* Linter

* Objective stuff

* Fixes n grey suits

* Objectives

* Trim intro sound

* Event, spawn point

* Ninja Outfits, Vox Check, Bug Fixes, Mirror at ninja spawn

* Address code review

* Oops

* Uncomments an important thingy

* Update: Gave space ninjas access to maints and an agent ID card. Updated net description.

* Update scanner examine

* Removes excess file

* Makes ninja scanner fit in belts and ninja suits. Gives them NV goggles

* Fixes modsuit sprite issue

* Energy shuriken fixes

* Scanner fix

* Printer fix

* Fixes some runtimes

* Fixes capture teleport

* Clothes rename

* Buffs energy katana, adds soft no-drop to ekatana, buffs ninja modsuit, fixes equip bug

* Adds research levels to ninja gear

* Fixes ninja capture issue

* Remaps ninja dojo

* Better cuff removal

* Forgor

* Fixes action availability

* Updates walls at dojo

* Improves ninja modsuit

* Windoors can now be opened with the katana

* Adds advanced pinpointer to ninja uplink

* Fixed energy nets sticking

* Fixes slime people ninjas

* Adds reroll to ninja capture if target is DNR. Prevents off Z-level targets

* Oop

* Adds reactor sabotage objective.

* Fixes ninja cuffs

* Removes Carp scroll from uplink. Adds Krav Implant to uplink
2026-03-25 23:11:10 +00:00

353 lines
14 KiB
Plaintext

#define NINJA_OBJECTIVE_EASY 10
#define NINJA_OBJECTIVE_NORMAL 20
#define NINJA_OBJECTIVE_HARD 40
/datum/objective/ninja
/// Can you only roll this objective once?
var/onlyone = FALSE
/// Does this objective come with special gear
var/special_equipment_path
/// Rewarded currency for objective completion
var/reward_tc = NINJA_OBJECTIVE_EASY
/datum/objective/ninja/New(text, datum/team/team_to_join, datum/mind/_owner)
. = ..()
if(special_equipment_path)
addtimer(CALLBACK(src, PROC_REF(hand_out_equipment)), 3 SECONDS, TIMER_DELETE_ME)
/datum/objective/ninja/proc/hand_out_equipment()
give_kit(special_equipment_path)
/datum/objective/ninja/is_invalid_target(datum/mind/possible_target)
. = ..()
if(possible_target.current)
var/turf/current_location = get_turf(possible_target.current)
if(current_location && !is_station_level(current_location.z))
return TARGET_INVALID_UNREACHABLE
/datum/objective/ninja/proc/complete_objective()
for(var/datum/mind/M in get_owners())
var/mob/living/carbon/human/H = M.current
if(!ishuman(H))
continue
var/obj/item/bio_chip/uplink/ninja/nuplink = locate(/obj/item/bio_chip/uplink/ninja) in H
if(!nuplink)
continue
nuplink.hidden_uplink.uses += reward_tc
completed = TRUE
/datum/objective/ninja/proc/check_objective_conditions()
return TRUE
/datum/objective/ninja/kill
name = "Kill a Target"
reward_tc = NINJA_OBJECTIVE_NORMAL
/datum/objective/ninja/kill/update_explanation_text()
if(target?.current)
explanation_text = "Kill [target.current.real_name], the [target.assigned_role]. Scan the corpse with your scanner to verify that the deed is done."
var/datum/job/target_job = SSjobs.GetJob(target.assigned_role)
if(target_job.job_department_flags & DEP_FLAG_COMMAND || target_job.job_department_flags & DEP_FLAG_SECURITY)
reward_tc = NINJA_OBJECTIVE_HARD
/datum/objective/ninja/capture
name = "Capture a Target"
reward_tc = NINJA_OBJECTIVE_NORMAL
/// The kidnapee's belongings. Set upon capture.
var/list/obj/item/victim_belongings = null
/// Temporary objects that are available to the kidnapee during their time in jail. These are deleted when the victim is returned.
var/list/obj/temp_objs = null
/// Prisoner jail timer handle. On completion, returns the prisoner back to station.
var/prisoner_timer_handle = null
/datum/objective/ninja/capture/update_explanation_text()
if(target?.current)
explanation_text = "Capture [target.current.real_name], the [target.assigned_role]. Use your energy net to capture them so that we can interrogate them at one of our many secret dojos."
var/datum/job/target_job = SSjobs.GetJob(target.assigned_role)
if(target_job.job_department_flags & DEP_FLAG_COMMAND || target_job.job_department_flags & DEP_FLAG_SECURITY)
reward_tc = NINJA_OBJECTIVE_HARD
if(target_job.job_department_flags & DEP_FLAG_SERVICE)
reward_tc = NINJA_OBJECTIVE_EASY
/datum/objective/ninja/capture/proc/handle_capture(mob/living/sucker, turf/T)
var/mob/living/carbon/human/H = sucker
// Prepare their return
prisoner_timer_handle = addtimer(CALLBACK(src, PROC_REF(handle_target_return), sucker, T), rand(3 MINUTES, 5 MINUTES), TIMER_STOPPABLE)
LAZYSET(GLOB.prisoner_belongings.prisoners, sucker, src)
// Shove all of the victim's items in the secure locker.
victim_belongings = list()
var/list/obj/item/stuff_to_transfer = list()
// Cybernetic implants get removed first (to deal with NODROP stuff)
for(var/obj/item/organ/internal/cyberimp/I in H.internal_organs)
// Greys get to keep their implant
if(isgrey(H) && istype(I, /obj/item/organ/internal/cyberimp/brain/speech_translator))
continue
// IPCs keep this implant, free of charge!
if(ismachineperson(H) && istype(I, /obj/item/organ/internal/cyberimp/arm/power_cord))
continue
// Try removing it
I = I.remove(H)
if(I)
stuff_to_transfer += I
// Skrell headpocket. They already have a check in place to limit what's placed in them.
var/obj/item/organ/internal/headpocket/C = H.get_int_organ(/obj/item/organ/internal/headpocket)
if(C?.held_item)
GLOB.prisoner_belongings.give_item(C.held_item)
victim_belongings += C.held_item
C.held_item = null
if(sucker.back) // Lets not bork modsuits in funny ways.
var/obj/modsuit_safety = sucker.back
sucker.drop_item_to_ground(modsuit_safety)
stuff_to_transfer += modsuit_safety
// Regular items get removed in second
for(var/obj/item/I in sucker)
// Keep their uniform and shoes
if(I == H.w_uniform || I == H.shoes)
continue
// Plasmamen are no use if they're crispy
if(isplasmaman(H) && I == H.head)
continue
// Any kind of implant gets potentially removed (mindshield, freedoms, etc)
if(istype(I, /obj/item/bio_chip))
if(istype(I, /obj/item/bio_chip/storage)) // Storage items are removed and placed in the confiscation locker before the implant is taken.
var/obj/item/bio_chip/storage/storage_chip = I
for(var/it in storage_chip.storage)
storage_chip.storage.remove_from_storage(it)
stuff_to_transfer += it
qdel(I)
continue
if(sucker.drop_item_to_ground(I))
stuff_to_transfer += I
// Remove accessories from the suit if present
if(length(H.w_uniform?.accessories))
for(var/obj/item/clothing/accessory/A in H.w_uniform.accessories)
H.w_uniform.detach_accessory(A, null)
H.drop_item_to_ground(A)
stuff_to_transfer += A
// Transfer it all (or drop it if not possible)
for(var/obj/item/i as anything in stuff_to_transfer)
if(GLOB.prisoner_belongings.give_item(i))
victim_belongings += i
else if(!((ABSTRACT|NODROP) in i.flags)) // Anything that can't be put on hold, just drop it on the ground
i.forceMove(T)
// Give some species the necessary to survive. Courtesy of the Syndicate.
if(istype(H))
var/obj/item/tank/internals/emergency_oxygen/tank
var/obj/item/clothing/mask/breath/mask
if(isvox(H))
tank = new /obj/item/tank/internals/emergency_oxygen/nitrogen(H)
mask = new /obj/item/clothing/mask/breath/vox(H)
else if(isplasmaman(H))
tank = new /obj/item/tank/internals/emergency_oxygen/plasma(H)
mask = new /obj/item/clothing/mask/breath(H)
if(tank)
H.equip_to_appropriate_slot(tank)
H.equip_to_appropriate_slot(mask)
tank.toggle_internals(H, TRUE)
sucker.reagents.add_reagent("mutadone", 1) // 1u of mutadone for the Hulk:tm: experience
sucker.update_icons()
// Supply them with some chow. How generous is the Syndicate?
var/obj/item/food/sliced/bread/food = new(get_turf(sucker))
food.name = "stale bread"
food.desc = "Looks like your captors care for their prisoners as much as their bread."
food.trash = null
if(prob(10))
// Mold adds a bit of spice to it
food.name = "moldy bread"
food.reagents.add_reagent("fungus", 1)
var/obj/item/reagent_containers/drinks/drinkingglass/drink = new(get_turf(sucker))
drink.reagents.add_reagent("tea", 25) // British coders beware, tea in glasses
var/obj/item/coin/antagtoken/passingtime = new(get_turf(sucker))
temp_objs = list(food, drink, passingtime)
// Narrate their kidnapping and torturing experience.
if(sucker.stat != DEAD)
// Heal them up - gets them out of crit/soft crit.
sucker.reagents.add_reagent("omnizine", 10)
to_chat(sucker, SPAN_WARNING("You feel strange..."))
sucker.Paralyse(30 SECONDS)
sucker.EyeBlind(35 SECONDS)
sucker.EyeBlurry(35 SECONDS)
sucker.AdjustConfused(35 SECONDS)
for(var/mob/living/simple_animal/hostile/guardian/G in GLOB.alive_mob_list)
if(G.summoner == sucker)
sucker.remove_guardian_actions()
to_chat(G, SPAN_DANGER("You feel your body ripped to shreds as you're forcibly removed from your summoner!"))
to_chat(sucker, SPAN_WARNING("You feel some part of you missing, you're not who you used to be..."))
G.ghostize()
qdel(G)
sleep(6 SECONDS)
to_chat(sucker, SPAN_WARNING("That portal did something to you..."))
sleep(6.5 SECONDS)
to_chat(sucker, SPAN_WARNING("Your head pounds... It feels like it's going to burst out your skull!"))
sleep(3 SECONDS)
to_chat(sucker, SPAN_WARNING("Your head pounds..."))
sleep(10 SECONDS)
to_chat(sucker, "<span class='specialnotice'>A million voices echo in your head... <i>\"Your mind held many valuable secrets - \
we thank you for providing them. Your value is expended, and you will be ransomed back to your station. We always get paid, \
so it's only a matter of time before we send you back...\"</i></span>")
to_chat(sucker, SPAN_DANGER("<font size=3>You have been kidnapped and interrogated for valuable information! You will be sent back to the station in a few minutes...</font>"))
/datum/objective/ninja/capture/proc/handle_target_return(mob/living/M, turf/T)
// Make a closet to return the target and their items neatly
var/obj/structure/closet/closet = new(T)
// Return their items
for(var/i in victim_belongings)
var/obj/item/I = GLOB.prisoner_belongings.remove_item(i)
if(!I)
continue
I.forceMove(closet)
victim_belongings = list()
// Clean up
var/obj/item/bio_chip/uplink/uplink_implant = locate() in M
uplink_implant?.hidden_uplink?.is_jammed = FALSE
QDEL_LIST_CONTENTS(temp_objs)
// Injuries due to questioning
injure_target(M)
// Return them a bit confused.
M.visible_message(SPAN_NOTICE("[M] vanishes..."))
M.forceMove(closet)
M.Paralyse(3 SECONDS)
M.EyeBlurry(5 SECONDS)
M.AdjustConfused(5 SECONDS)
M.Dizzy(70 SECONDS)
do_sparks(4, FALSE, T)
prisoner_timer_handle = null
GLOB.prisoner_belongings.prisoners[M] = null
/datum/objective/ninja/capture/proc/injure_target(mob/living/M)
var/obj/item/organ/external/injury_target
if(prob(20)) // See if they're !!!lucky!!! enough to just chop a hand or foot off first, or even !!LUCKIER!! that it chose an already amputated limb
injury_target = M.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
if(!injury_target)
return
default_damage(M)
injury_target.droplimb()
to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! Oh god, something's missing!"))
return
// Species specific punishments first
if(ismachineperson(M))
M.emp_act(EMP_HEAVY)
M.adjustBrainLoss(30)
to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! You feel like some of your components are loose!"))
return
default_damage(M) // Now that we won't accidentally kill an IPC we can make everyone take damage
if(isslimeperson(M))
injury_target = M.get_organ(pick(BODY_ZONE_PRECISE_R_HAND, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_FOOT, BODY_ZONE_PRECISE_L_FOOT))
if(!injury_target)
return
injury_target.cause_internal_bleeding()
injury_target = M.get_organ(BODY_ZONE_CHEST)
injury_target.cause_internal_bleeding()
to_chat(M, SPAN_WARNING("You were interrogated by your captors before being sent back! You feel like your inner membrane has been punctured!"))
return
if(prob(25)) // You either get broken ribs, or a broken limb and IB if you made it this far
injury_target = M.get_organ(BODY_ZONE_CHEST)
injury_target.fracture()
else
injury_target = M.get_organ(pick(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_R_LEG))
if(!injury_target)
return
injury_target.fracture()
injury_target.cause_internal_bleeding()
/datum/objective/ninja/capture/proc/default_damage(mob/living/M)
M.adjustBruteLoss(40)
M.adjustBrainLoss(25)
/datum/objective/ninja/hack_rnd
name = "Hack RnD"
explanation_text = "A client wants access to Nanotrasen's research databank. Use your scanner on their servers to give them a way inside."
needs_target = FALSE
onlyone = TRUE
/datum/objective/ninja/interrogate_ai
name = "Interrogate AI"
explanation_text = "We wish to expunge some data from their AI system. Use your scanner on an active AI core to wirelessly transfer it to us for interrogation."
needs_target = FALSE
onlyone = TRUE
reward_tc = NINJA_OBJECTIVE_HARD
/datum/objective/ninja/interrogate_ai/check_objective_conditions() // If there is no AI, you don't get the objective.
if(!length(GLOB.ai_list))
return FALSE
return TRUE
/datum/objective/ninja/steal_supermatter
name = "Steal Supermatter"
explanation_text = "Steal the supermatter crystal, using the net gun we have modified for you. The crystal will sell well to the highest bidder."
needs_target = FALSE
onlyone = TRUE
reward_tc = NINJA_OBJECTIVE_HARD
/datum/objective/ninja/steal_supermatter/check_objective_conditions() // If there is no supermatter, you don't get the objective.
return !isnull(GLOB.main_supermatter_engine)
/datum/objective/ninja/insert_spider_rod
name = "Sabotage Fission Reactor"
explanation_text = "Insert a spider-clan uranium 238 fuel rod into the station's fission reactor, then scan the reactor chamber while it is active."
needs_target = FALSE
onlyone = TRUE
reward_tc = NINJA_OBJECTIVE_HARD
special_equipment_path = /obj/item/beacon/ninja_rod_spawner
/datum/objective/ninja/insert_spider_rod/check_objective_conditions() // If there is no reactor, you don't get the objective.
return !isnull(GLOB.main_fission_reactor)
/datum/objective/ninja/bomb_department
name = "Bomb Department"
needs_target = FALSE
special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb
explanation_text = "Use the special flare provided to call down and arm a spider bomb. The target department is inscribed on the flare."
reward_tc = NINJA_OBJECTIVE_NORMAL
/datum/objective/ninja/bomb_department/emp
name = "EMP Department"
explanation_text = "Use the special flare provided to call down and arm an EMP bomb. The target department is inscribed on the flare."
special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb/emp
/datum/objective/ninja/bomb_department/spiders
name = "Spider Bomb Department"
explanation_text = "Use the special flare provided to call down and arm a Spider bomb. The target department is inscribed on the flare."
special_equipment_path = /obj/item/wormhole_jaunter/ninja_bomb/spiders
/datum/objective/ninja_exfiltrate
name = "Exfiltrate"
explanation_text = "Use your exfiltration flare to escape the station when your work is done."
needs_target = FALSE
#undef NINJA_OBJECTIVE_EASY
#undef NINJA_OBJECTIVE_NORMAL
#undef NINJA_OBJECTIVE_HARD