diff --git a/code/datums/actions/ninja.dm b/code/datums/actions/ninja.dm
deleted file mode 100644
index c0314b9c48..0000000000
--- a/code/datums/actions/ninja.dm
+++ /dev/null
@@ -1,51 +0,0 @@
-/datum/action/item_action/initialize_ninja_suit
- name = "Toggle ninja suit"
-
-/datum/action/item_action/ninjasmoke
- name = "Smoke Bomb"
- desc = "Blind your enemies momentarily with a well-placed smoke bomb."
- button_icon_state = "smoke"
- icon_icon = 'icons/mob/actions/actions_spells.dmi'
-
-/datum/action/item_action/ninjaboost
- name = "Adrenaline Boost"
- desc = "Inject a secret chemical that will counteract all movement-impairing effect."
- button_icon_state = "repulse"
- icon_icon = 'icons/mob/actions/actions_spells.dmi'
- required_mobility_flags = NONE
-
-/datum/action/item_action/ninjapulse
- name = "EM Burst (25E)"
- desc = "Disable any nearby technology with an electro-magnetic pulse."
- button_icon_state = "emp"
- icon_icon = 'icons/mob/actions/actions_spells.dmi'
-
-/datum/action/item_action/ninjastar
- name = "Create Throwing Stars (1E)"
- desc = "Creates some throwing stars"
- button_icon_state = "throwingstar"
- icon_icon = 'icons/obj/items_and_weapons.dmi'
-
-/datum/action/item_action/ninjanet
- name = "Energy Net (20E)"
- desc = "Captures a fallen opponent in a net of energy. Will teleport them to a holding facility after 30 seconds."
- button_icon_state = "energynet"
- icon_icon = 'icons/effects/effects.dmi'
-
-/datum/action/item_action/ninja_sword_recall
- name = "Recall Energy Katana (Variable Cost)"
- desc = "Teleports the Energy Katana linked to this suit to its wearer, cost based on distance."
- button_icon_state = "energy_katana"
- icon_icon = 'icons/obj/items_and_weapons.dmi'
-
-/datum/action/item_action/ninja_stealth
- name = "Toggle Stealth"
- desc = "Toggles stealth mode on and off."
- button_icon_state = "ninja_cloak"
- icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
-
-/datum/action/item_action/toggle_glove
- name = "Toggle interaction"
- desc = "Switch between normal interaction and drain mode."
- button_icon_state = "s-ninjan"
- icon_icon = 'icons/obj/clothing/gloves.dmi'
diff --git a/code/game/area/areas/ruins/_ruins.dm b/code/game/area/areas/ruins/_ruins.dm
index 17ba4f7721..6699e94129 100644
--- a/code/game/area/areas/ruins/_ruins.dm
+++ b/code/game/area/areas/ruins/_ruins.dm
@@ -8,6 +8,7 @@
dynamic_lighting = DYNAMIC_LIGHTING_FORCED
ambientsounds = RUINS
sound_environment = SOUND_ENVIRONMENT_STONEROOM
+ var/valid_territory = FALSE // hey so what if we did not allow things like cult summons to appear on ruins
/area/ruin/unpowered
diff --git a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
index 7ce6e74cc1..61732b30eb 100644
--- a/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
+++ b/code/game/gamemodes/dynamic/dynamic_rulesets_midround.dm
@@ -669,7 +669,6 @@
var/mob/living/carbon/human/Ninja = create_space_ninja(spawn_loc)
Mind.transfer_to(Ninja)
var/datum/antagonist/ninja/ninjadatum = new
- ninjadatum.helping_station = pick(TRUE,FALSE)
Mind.add_antag_datum(ninjadatum)
if(Ninja.mind != Mind) //something has gone wrong!
diff --git a/code/game/machinery/_machinery.dm b/code/game/machinery/_machinery.dm
index 133fec28b3..e05991dd8a 100644
--- a/code/game/machinery/_machinery.dm
+++ b/code/game/machinery/_machinery.dm
@@ -582,3 +582,14 @@ Class Procs:
/obj/machinery/rust_heretic_act()
take_damage(500, BRUTE, "melee", 1)
+
+/**
+ * Alerts the AI that a hack is in progress.
+ *
+ * Sends all AIs a message that a hack is occurring. Specifically used for space ninja tampering as this proc was originally in the ninja files.
+ * However, the proc may also be used elsewhere.
+ */
+/obj/machinery/proc/AI_notify_hack()
+ var/alertstr = "Network Alert: Hacking attempt detected[get_area(src)?" in [get_area_name(src, TRUE)]":". Unable to pinpoint location"]."
+ for(var/mob/living/silicon/ai/AI in GLOB.player_list)
+ to_chat(AI, alertstr)
diff --git a/code/game/objects/items/weaponry.dm b/code/game/objects/items/weaponry.dm
index fe63e313de..902b1eaa13 100644
--- a/code/game/objects/items/weaponry.dm
+++ b/code/game/objects/items/weaponry.dm
@@ -615,9 +615,9 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
name = "throwing star"
desc = "An ancient weapon still used to this day, due to its ease of lodging itself into its victim's body parts."
icon_state = "throwingstar"
- item_state = "eshield0"
- lefthand_file = 'icons/mob/inhands/equipment/shields_lefthand.dmi'
- righthand_file = 'icons/mob/inhands/equipment/shields_righthand.dmi'
+ item_state = "throwingstar"
+ lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
+ righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
force = 2
throwforce = 10 //10 + 2 (WEIGHT_CLASS_SMALL) * 4 (EMBEDDED_IMPACT_PAIN_MULTIPLIER) = 18 damage on hit due to guaranteed embedding
throw_speed = 4
@@ -630,8 +630,10 @@ for further reading, please see: https://github.com/tgstation/tgstation/pull/301
resistance_flags = FIRE_PROOF
/obj/item/throwing_star/stamina
- name = "shock throwing star"
+ name = "energy throwing star"
desc = "An aerodynamic disc designed to cause excruciating pain when stuck inside fleeing targets, hopefully without causing fatal harm."
+ icon_state = "energystar"
+ item_state = "energystar"
throwforce = 5
embedding = list("pain_chance" = 5, "embed_chance" = 100, "fall_chance" = 0, "jostle_chance" = 10, "pain_stam_pct" = 0.8, "jostle_pain_mult" = 3)
diff --git a/code/modules/admin/verbs/one_click_antag.dm b/code/modules/admin/verbs/one_click_antag.dm
index 2379ee22bc..6c43d61cce 100644
--- a/code/modules/admin/verbs/one_click_antag.dm
+++ b/code/modules/admin/verbs/one_click_antag.dm
@@ -266,8 +266,8 @@
return TRUE
/datum/admins/proc/makeSpaceNinja()
- new /datum/round_event/ghost_role/ninja()
- return 1
+ new /datum/round_event/ghost_role/space_ninja()
+ return TRUE
// DEATH SQUADS
/datum/admins/proc/makeDeathsquad()
diff --git a/code/modules/antagonists/ninja/ninja.dm b/code/modules/antagonists/ninja/ninja.dm
deleted file mode 100644
index 414f7dd6b0..0000000000
--- a/code/modules/antagonists/ninja/ninja.dm
+++ /dev/null
@@ -1,146 +0,0 @@
-/datum/antagonist/ninja
- name = "Ninja"
- antagpanel_category = "Ninja"
- job_rank = ROLE_NINJA
- show_name_in_check_antagonists = TRUE
- show_to_ghosts = TRUE
- antag_moodlet = /datum/mood_event/focused
- threat = 8
- var/helping_station = FALSE
- var/give_objectives = TRUE
- var/give_equipment = TRUE
-
-/datum/antagonist/ninja/threat()
- return helping_station ? -(..()) : ..()
-
-/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
- var/mob/living/M = mob_override || owner.current
- update_ninja_icons_added(M)
-
-/datum/antagonist/ninja/remove_innate_effects(mob/living/mob_override)
- var/mob/living/M = mob_override || owner.current
- update_ninja_icons_removed(M)
-
-/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/H = owner.current)
- return H.equipOutfit(/datum/outfit/ninja)
-
-/datum/antagonist/ninja/proc/addMemories()
- antag_memory += "I am an elite mercenary assassin of the mighty Spider Clan. A SPACE NINJA!
"
- antag_memory += "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by clicking the initialize UI button, to use abilities like stealth)!
"
- antag_memory += "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.
"
-
-/datum/antagonist/ninja/proc/addObjectives(quantity = 6)
- var/list/possible_targets = list()
- for(var/datum/mind/M in SSticker.minds)
- if(M.current && M.current.stat != DEAD)
- if(ishuman(M.current))
- if(M.special_role)
- possible_targets[M] = 0 //bad-guy
- else if(M.assigned_role in GLOB.command_positions)
- possible_targets[M] = 1 //good-guy
-
- var/list/possible_objectives = list(1,2,3,4)
-
- while(objectives.len < quantity)
- switch(pick_n_take(possible_objectives))
- if(1) //research
- var/datum/objective/download/O = new /datum/objective/download()
- O.owner = owner
- O.gen_amount_goal()
- objectives += O
-
- if(2) //steal
- var/datum/objective/steal/special/O = new /datum/objective/steal/special()
- O.owner = owner
- O.find_target()
- objectives += O
-
- if(3) //protect/kill
- if(!possible_targets.len) continue
- var/index = rand(1,possible_targets.len)
- var/datum/mind/M = possible_targets[index]
- var/is_bad_guy = possible_targets[M]
- possible_targets.Cut(index,index+1)
-
- if(is_bad_guy ^ helping_station) //kill (good-ninja + bad-guy or bad-ninja + good-guy)
- var/datum/objective/assassinate/once/O = new /datum/objective/assassinate()
- O.owner = owner
- O.target = M
- O.explanation_text = "Slay \the [M.current.real_name], the [M.assigned_role]. You may let [M.p_they()] live, if they come back from death."
- objectives += O
- else //protect
- var/datum/objective/protect/O = new /datum/objective/protect()
- O.owner = owner
- O.target = M
- O.explanation_text = "Protect \the [M.current.real_name], the [M.assigned_role], from harm."
- objectives += O
- if(4) //flavor
- var/datum/objective/flavor/O = helping_station ? new /datum/objective/flavor/ninja_helping : new /datum/objective/flavor/ninja_syndie
- O.owner = owner
- O.forge_objective()
- objectives += O
- else
- break
- var/datum/objective/O = new /datum/objective/survive()
- O.owner = owner
- objectives += O
-
-/proc/remove_ninja(mob/living/L)
- if(!L || !L.mind)
- return FALSE
- var/datum/antagonist/datum = L.mind.has_antag_datum(/datum/antagonist/ninja)
- datum.on_removal()
- return TRUE
-
-/proc/is_ninja(mob/living/M)
- return M && M.mind && M.mind.has_antag_datum(/datum/antagonist/ninja)
-
-
-/datum/antagonist/ninja/greet()
- SEND_SOUND(owner.current, sound('sound/effects/ninja_greeting.ogg'))
- to_chat(owner.current, "I am an elite mercenary assassin of the mighty Spider Clan. A SPACE NINJA!")
- to_chat(owner.current, "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!")
- to_chat(owner.current, "Officially, [helping_station?"Nanotrasen":"The Syndicate"] are my employer.")
- owner.announce_objectives()
- return
-
-/datum/antagonist/ninja/on_gain()
- if(give_objectives)
- addObjectives()
- addMemories()
- if(give_equipment)
- equip_space_ninja(owner.current)
- . = ..()
-
-/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
- var/adj
- switch(input("What kind of ninja?", "Ninja") as null|anything in list("Random","Syndicate","Nanotrasen","No objectives"))
- if("Random")
- helping_station = pick(TRUE,FALSE)
- adj = ""
- if("Syndicate")
- helping_station = FALSE
- adj = "syndie"
- if("Nanotrasen")
- helping_station = TRUE
- adj = "friendly"
- if("No objectives")
- give_objectives = FALSE
- adj = "objectiveless"
- else
- return
- new_owner.assigned_role = ROLE_NINJA
- new_owner.special_role = ROLE_NINJA
- new_owner.add_antag_datum(src)
- message_admins("[key_name_admin(admin)] has [adj] ninja'ed [new_owner.current].")
- log_admin("[key_name(admin)] has [adj] ninja'ed [new_owner.current].")
-
-/datum/antagonist/ninja/proc/update_ninja_icons_added(var/mob/living/carbon/human/ninja)
- var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
- ninjahud.join_hud(ninja)
- set_antag_hud(ninja, "ninja")
-
-/datum/antagonist/ninja/proc/update_ninja_icons_removed(var/mob/living/carbon/human/ninja)
- var/datum/atom_hud/antag/ninjahud = GLOB.huds[ANTAG_HUD_NINJA]
- ninjahud.leave_hud(ninja)
- set_antag_hud(ninja, null)
diff --git a/code/modules/antagonists/nukeop/equipment/pinpointer.dm b/code/modules/antagonists/nukeop/equipment/pinpointer.dm
index d3fe235f49..5b48951ed5 100644
--- a/code/modules/antagonists/nukeop/equipment/pinpointer.dm
+++ b/code/modules/antagonists/nukeop/equipment/pinpointer.dm
@@ -68,6 +68,11 @@
desc = "An integrated tracking device, jury-rigged to search for living Syndicate operatives."
flags_1 = NONE
+/obj/item/pinpointer/spider_cyborg // Cyborg pinpointers just look for a random space ninja.
+ name = "cyborg spider clan pinpointer"
+ desc = "An integrated tracking device, jury-rigged to search for living Spider Clan members."
+ flags_1 = NONE
+
/obj/item/pinpointer/syndicate_cyborg/Initialize()
. = ..()
@@ -85,3 +90,20 @@
if(closest_operative)
target = closest_operative
..()
+
+/obj/item/pinpointer/spider_cyborg/Initialize()
+ . = ..()
+ ADD_TRAIT(src, TRAIT_NODROP, CYBORG_ITEM_TRAIT)
+
+/obj/item/pinpointer/spider_cyborg/scan_for_target()
+ target = null
+ var/list/possible_targets = list()
+ var/turf/here = get_turf(src)
+ for(var/V in get_antag_minds(/datum/antagonist/ninja))
+ var/datum/mind/M = V
+ if(ishuman(M.current) && M.current.stat != DEAD)
+ possible_targets |= M.current
+ var/mob/living/closest_weeaboo = get_closest_atom(/mob/living/carbon/human, possible_targets, here)
+ if(closest_weeaboo)
+ target = closest_weeaboo
+ ..()
diff --git a/code/modules/antagonists/space_ninja/space_ninja.dm b/code/modules/antagonists/space_ninja/space_ninja.dm
new file mode 100644
index 0000000000..1deb37eb56
--- /dev/null
+++ b/code/modules/antagonists/space_ninja/space_ninja.dm
@@ -0,0 +1,131 @@
+/datum/antagonist/ninja
+ name = "Space Ninja"
+ antagpanel_category = "Space Ninja"
+ job_rank = ROLE_NINJA
+ antag_hud_type = ANTAG_HUD_NINJA
+ antag_hud_name = "space_ninja"
+ show_name_in_check_antagonists = TRUE
+ show_to_ghosts = TRUE
+ antag_moodlet = /datum/mood_event/focused
+ ///Whether or not this ninja will obtain objectives
+ var/give_objectives = TRUE
+ ///Whether or not this ninja receives the standard equipment
+ var/give_equipment = TRUE
+
+/proc/is_ninja(mob/living/M)
+ return M && M.mind && M.mind.has_antag_datum(/datum/antagonist/ninja)
+
+/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
+ var/mob/living/ninja = mob_override || owner.current
+ add_antag_hud(antag_hud_type, antag_hud_name, ninja)
+
+/datum/antagonist/ninja/remove_innate_effects(mob/living/mob_override)
+ var/mob/living/ninja = mob_override || owner.current
+ remove_antag_hud(antag_hud_type, ninja)
+
+/**
+ * Proc that equips the space ninja outfit on a given individual. By default this is the owner of the antagonist datum.
+ *
+ * Proc that equips the space ninja outfit on a given individual. By default this is the owner of the antagonist datum.
+ * Arguments:
+ * * ninja - The human to receive the gear
+ * * Returns a proc call on the given human which will equip them with all the gear.
+ */
+/datum/antagonist/ninja/proc/equip_space_ninja(mob/living/carbon/human/ninja = owner.current)
+ return ninja.equipOutfit(/datum/outfit/ninja)
+
+/**
+ * Proc that adds the proper memories to the antag datum
+ *
+ * Proc that adds the ninja starting memories to the owner of the antagonist datum.
+ */
+/datum/antagonist/ninja/proc/addMemories()
+ antag_memory += "I am an elite mercenary of the mighty Spider Clan. A SPACE NINJA!
"
+ antag_memory += "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by clicking the initialize UI button, to use abilities like stealth)!
"
+
+/datum/objective/cyborg_hijack
+ explanation_text = "Use your gloves to convert at least one cyborg to aide you in sabotaging the station."
+
+/datum/objective/door_jack
+ ///How many doors that need to be opened using the gloves to pass the objective
+ var/doors_required = 0
+
+/datum/objective/plant_explosive
+ var/area/detonation_location
+
+/datum/objective/security_scramble
+ explanation_text = "Use your gloves on a security console to set everyone to arrest at least once. Note that the AI will be alerted once you begin!"
+
+/datum/objective/terror_message
+ explanation_text = "Use your gloves on a communication console in order to bring another threat to the station. Note that the AI will be alerted once you begin!"
+
+/**
+ * Proc that adds all the ninja's objectives to the antag datum.
+ *
+ * Proc that adds all the ninja's objectives to the antag datum. Called when the datum is gained.
+ */
+/datum/antagonist/ninja/proc/addObjectives()
+ //Cyborg Hijack: Flag set to complete in the DrainAct in ninjaDrainAct.dm
+ var/datum/objective/hijack = new /datum/objective/cyborg_hijack()
+ objectives += hijack
+
+ //Research stealing
+ var/datum/objective/download/research = new /datum/objective/download()
+ research.owner = owner
+ research.gen_amount_goal()
+ objectives += research
+
+ //Door jacks, flag will be set to complete on when the last door is hijacked
+ var/datum/objective/door_jack/doorobjective = new /datum/objective/door_jack()
+ doorobjective.doors_required = rand(15,40)
+ doorobjective.explanation_text = "Use your gloves to doorjack [doorobjective.doors_required] airlocks on the station."
+ objectives += doorobjective
+
+ //Explosive plant, the bomb will register its completion on priming
+ var/datum/objective/plant_explosive/bombobjective = new /datum/objective/plant_explosive()
+ for(var/sanity in 1 to 100) // 100 checks at most.
+ var/area/selected_area = pick(GLOB.sortedAreas)
+ if(!is_station_level(selected_area.z) || !selected_area.area_flags == VALID_TERRITORY)
+ continue
+ bombobjective.detonation_location = selected_area
+ break
+ if(bombobjective.detonation_location)
+ bombobjective.explanation_text = "Detonate your starter bomb in [bombobjective.detonation_location]. Note that the bomb will not work anywhere else!"
+ objectives += bombobjective
+
+ //Security Scramble, set to complete upon using your gloves on a security console
+ var/datum/objective/securityobjective = new /datum/objective/security_scramble()
+ objectives += securityobjective
+
+ //Message of Terror, set to complete upon using your gloves a communication console
+ var/datum/objective/communicationobjective = new /datum/objective/terror_message()
+ objectives += communicationobjective
+
+ //Survival until end
+ var/datum/objective/survival = new /datum/objective/survive()
+ survival.owner = owner
+ objectives += survival
+
+/datum/antagonist/ninja/greet()
+ SEND_SOUND(owner.current, sound('sound/effects/ninja_greeting.ogg'))
+ to_chat(owner.current, "I am an elite mercenary of the mighty Spider Clan. A SPACE NINJA!")
+ to_chat(owner.current, "Surprise is my weapon. Shadows are my armor. Without them, I am nothing. (//initialize your suit by right clicking on it, to use abilities like stealth)!")
+ owner.announce_objectives()
+
+/datum/antagonist/ninja/on_gain()
+ if(give_objectives)
+ addObjectives()
+ addMemories()
+ if(give_equipment)
+ equip_space_ninja(owner.current)
+
+ owner.current.mind.assigned_role = ROLE_NINJA
+ owner.current.mind.special_role = ROLE_NINJA
+ return ..()
+
+/datum/antagonist/ninja/admin_add(datum/mind/new_owner,mob/admin)
+ new_owner.assigned_role = ROLE_NINJA
+ new_owner.special_role = ROLE_NINJA
+ new_owner.add_antag_datum(src)
+ message_admins("[key_name_admin(admin)] has ninja'ed [key_name_admin(new_owner)].")
+ log_admin("[key_name(admin)] has ninja'ed [key_name(new_owner)].")
diff --git a/code/modules/clothing/glasses/phantomthief.dm b/code/modules/clothing/glasses/phantomthief.dm
index 6d209ec695..ba3ea2830e 100644
--- a/code/modules/clothing/glasses/phantomthief.dm
+++ b/code/modules/clothing/glasses/phantomthief.dm
@@ -3,8 +3,8 @@
desc = "A cheap, Syndicate-branded paper face mask. They'll never see it coming."
mob_overlay_icon = 'icons/mob/clothing/mask.dmi'
icon = 'icons/obj/clothing/masks.dmi'
- icon_state = "s-ninja"
- item_state = "s-ninja"
+ icon_state = "ninjaOLD"
+ item_state = "ninjaOLD"
/obj/item/clothing/glasses/phantomthief/ComponentInitialize()
. = ..()
diff --git a/code/modules/events/space_ninja.dm b/code/modules/events/space_ninja.dm
new file mode 100644
index 0000000000..0a24fecd44
--- /dev/null
+++ b/code/modules/events/space_ninja.dm
@@ -0,0 +1,51 @@
+/datum/round_event_control/space_ninja
+ name = "Spawn Space Ninja"
+ typepath = /datum/round_event/ghost_role/space_ninja
+ max_occurrences = 1
+ weight = 10
+ earliest_start = 20 MINUTES
+ min_players = 15
+
+/datum/round_event/ghost_role/space_ninja
+ minimum_required = 1
+ role_name = "Space Ninja"
+
+/datum/round_event/ghost_role/space_ninja/spawn_role()
+ var/list/spawn_locs = list()
+ for(var/obj/effect/landmark/carpspawn/carp_spawn in GLOB.landmarks_list)
+ if(!isturf(carp_spawn.loc))
+ stack_trace("Carp spawn found not on a turf: [carp_spawn.type] on [isnull(carp_spawn.loc) ? "null" : carp_spawn.loc.type]")
+ continue
+ spawn_locs += carp_spawn.loc
+ if(!spawn_locs.len)
+ message_admins("No valid spawn locations found, aborting...")
+ return MAP_ERROR
+
+ //selecting a candidate player
+ var/list/candidates = get_candidates(ROLE_NINJA, null, ROLE_NINJA)
+ if(!candidates.len)
+ return NOT_ENOUGH_PLAYERS
+
+ var/mob/dead/selected_candidate = pick(candidates)
+ var/key = selected_candidate.key
+
+ //spawn the ninja and assign the candidate
+ var/mob/living/carbon/human/ninja = create_space_ninja(pick(spawn_locs))
+ ninja.key = key
+ ninja.mind.add_antag_datum(/datum/antagonist/ninja)
+ spawned_mobs += ninja
+ message_admins("[ADMIN_LOOKUPFLW(ninja)] has been made into a space ninja by an event.")
+ log_game("[key_name(ninja)] was spawned as a ninja by an event.")
+
+ return SUCCESSFUL_SPAWN
+
+
+//=======//NINJA CREATION PROCS//=======//
+
+/proc/create_space_ninja(spawn_loc)
+ var/mob/living/carbon/human/new_ninja = new(spawn_loc)
+ var/datum/preferences/random_human_options = new()//Randomize appearance for the ninja.
+ random_human_options.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]"
+ random_human_options.copy_to(new_ninja)
+ new_ninja.dna.update_dna_identity()
+ return new_ninja
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index e3af31ae96..d13a0f1149 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -82,34 +82,6 @@
. += ""
. += "Chemical Storage: [changeling.chem_charges]/[changeling.chem_storage]"
. += "Absorbed DNA: [changeling.absorbedcount]"
- //NINJACODE
- if(istype(wear_suit, /obj/item/clothing/suit/space/space_ninja)) //Only display if actually a ninja.
- var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
- . += "SpiderOS Status: [SN.s_initialized ? "Initialized" : "Disabled"]"
- . += "Current Time: [STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)]"
- if(SN.s_initialized)
- //Suit gear
- . += "Energy Charge: [round(SN.cell.charge/100)]%"
- . += "Smoke Bombs: \Roman [SN.s_bombs]"
- //Ninja status
- . += "Fingerprints: [md5(dna.uni_identity)]"
- . += "Unique Identity: [dna.unique_enzymes]"
- . += "Overall Status: [stat > 1 ? "dead" : "[health]% healthy"]"
- . += "Nutrition Status: [nutrition]"
- . += "Oxygen Loss: [getOxyLoss()]"
- . += "Toxin Levels: [getToxLoss()]"
- . += "Burn Severity: [getFireLoss()]"
- . += "Brute Trauma: [getBruteLoss()]"
- . += "Radiation Levels: [radiation] rad"
- . += "Body Temperature: [bodytemperature-T0C] degrees C ([bodytemperature*1.8-459.67] degrees F)"
-
- //Diseases
- if(length(diseases))
- . += "Viruses:"
- for(var/thing in diseases)
- var/datum/disease/D = thing
- . += "* [D.name], Type: [D.spread_text], Stage: [D.stage]/[D.max_stages], Possible Cure: [D.cure_text]"
-
/mob/living/carbon/human/show_inv(mob/user)
user.set_machine(src)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 9658247da3..7ab826cae3 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -811,6 +811,32 @@
Help the operatives secure the disk at all costs!"
set_module = /obj/item/robot_module/saboteur
+/mob/living/silicon/robot/modules/syndicate/spider// used for space ninja and their cyborg hacking special objective
+ bubble_icon = "spider"
+ playstyle_string = "You are a Spider Clan assault cyborg!
\
+ You are armed with powerful offensive tools to aid you in your mission: obey your ninja masters by any means necessary. \
+ Your cyborg LMG will slowly produce ammunition from your power supply, and your pinpointer will find and locate fellow members of the Spider Clan."
+ set_module = /obj/item/robot_module/syndicate/spider
+
+/mob/living/silicon/robot/modules/syndicate_medical/spider// ditto
+ bubble_icon = "spider"
+ var/playstyle_string = "You are a Spider Clan medical cyborg!
\
+ You are armed with powerful medical tools to aid you in your mission: obey your ninja masters by any means necessary. \
+ Your hypospray will produce Restorative Nanites, a wonder-drug that will heal most types of bodily damages, including clone and brain damage. It also produces morphine for offense. \
+ Your defibrillator paddles can revive allies through their hardsuits, or can be used on harm intent to shock enemies! \
+ Your energy saw functions as a circular saw, but can be activated to deal more damage, and your pinpointer will find and locate fellow members of the Spider Clan."
+ set_module = /obj/item/robot_module/syndicate_medical/spider
+
+/mob/living/silicon/robot/modules/saboteur/spider// ditto
+ bubble_icon = "spider"
+ var/playstyle_string = "You are a Spider Clan saboteur cyborg!
\
+ You are armed with robust engineering tools to aid you in your mission: obey your ninja masters by any means necessary. \
+ Your destination tagger will allow you to stealthily traverse the disposal network across the station. \
+ Your welder will can be used to repair yourself or any allied silicon units, while serving as an impromptu melee weapon. \
+ Your cyborg chameleon projector allows you to assume the appearance and registered name of a Nanotrasen engineering borg, and undertake covert actions on the station, \
+ be aware that almost any physical contact or incidental damage will break your camouflage."
+ set_module = /obj/item/robot_module/saboteur/spider
+
/mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname)
if(!connected_ai)
return
diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm
index 7ecc714b56..b9c6f4e2c4 100644
--- a/code/modules/mob/living/silicon/robot/robot_modules.dm
+++ b/code/modules/mob/living/silicon/robot/robot_modules.dm
@@ -1034,6 +1034,7 @@
/obj/item/extinguisher/mini,
/obj/item/crowbar/cyborg,
/obj/item/reagent_containers/borghypo/syndicate,
+ /obj/item/gripper/medical,
/obj/item/shockpaddles/syndicate,
/obj/item/healthanalyzer/advanced,
/obj/item/surgical_drapes/advanced,
@@ -1147,3 +1148,73 @@
max_energy = 30
recharge_rate = 1
name = "Wrapping Paper Storage"
+
+/obj/item/robot_module/syndicate/spider// used for space ninja and their cyborg hacking special objective
+ name = "Spider Assault"
+ basic_modules = list(
+ /obj/item/assembly/flash/cyborg,
+ /obj/item/extinguisher/mini,
+ /obj/item/crowbar/cyborg,
+ /obj/item/melee/transforming/energy/sword/cyborg,
+ /obj/item/gun/energy/printer,
+ /obj/item/pinpointer/spider_cyborg)
+
+ cyborg_base_icon = "spider_sec"
+
+/obj/item/robot_module/syndicate_medical/spider// ditto
+ name = "Spider Medical"
+ basic_modules = list(
+ /obj/item/assembly/flash/cyborg,
+ /obj/item/extinguisher/mini,
+ /obj/item/crowbar/cyborg,
+ /obj/item/reagent_containers/borghypo/syndicate,
+ /obj/item/gripper/medical,
+ /obj/item/shockpaddles/syndicate,
+ /obj/item/healthanalyzer/advanced,
+ /obj/item/surgical_drapes/advanced,
+ /obj/item/retractor,
+ /obj/item/hemostat,
+ /obj/item/cautery,
+ /obj/item/surgicaldrill,
+ /obj/item/scalpel,
+ /obj/item/bonesetter,
+ /obj/item/stack/medical/bone_gel,
+ /obj/item/melee/transforming/energy/sword/cyborg/saw,
+ /obj/item/roller/robo,
+ /obj/item/stack/medical/gauze/cyborg,
+ /obj/item/gun/medbeam,
+ /obj/item/organ_storage,
+ /obj/item/pinpointer/spider_cyborg)
+
+ cyborg_base_icon = "spider_medical"
+
+/obj/item/robot_module/saboteur/spider// ditto
+ name = "Spider Saboteur"
+ basic_modules = list(
+ /obj/item/assembly/flash/cyborg,
+ /obj/item/borg/sight/thermal,
+ /obj/item/construction/rcd/borg/syndicate,
+ /obj/item/pipe_dispenser,
+ /obj/item/restraints/handcuffs/cable/zipties,
+ /obj/item/extinguisher,
+ /obj/item/weldingtool/largetank/cyborg,
+ /obj/item/screwdriver/nuke,
+ /obj/item/wrench/cyborg,
+ /obj/item/crowbar/cyborg,
+ /obj/item/wirecutters/cyborg,
+ /obj/item/multitool/cyborg,
+ /obj/item/storage/part_replacer/cyborg,
+ /obj/item/holosign_creator/atmos,
+ /obj/item/gripper,
+ /obj/item/lightreplacer/cyborg,
+ /obj/item/stack/sheet/metal/cyborg,
+ /obj/item/stack/sheet/glass/cyborg,
+ /obj/item/stack/sheet/rglass/cyborg,
+ /obj/item/stack/rods/cyborg,
+ /obj/item/stack/tile/plasteel/cyborg,
+ /obj/item/destTagger/borg,
+ /obj/item/stack/cable_coil/cyborg,
+ /obj/item/borg_chameleon,
+ /obj/item/pinpointer/spider_cyborg)
+
+ cyborg_base_icon = "spider_engi"
diff --git a/code/modules/ninja/__ninjaDefines.dm b/code/modules/ninja/__ninjaDefines.dm
index d6cdb55840..3f5db48407 100644
--- a/code/modules/ninja/__ninjaDefines.dm
+++ b/code/modules/ninja/__ninjaDefines.dm
@@ -1,23 +1,24 @@
-
-/*
-
-Contents:
-- Definitions, because the original Ninja code has so much magic.
-
-*/
-
-
//ninjacost() specificCheck defines
#define N_STEALTH_CANCEL 1
-#define N_SMOKE_BOMB 2
-#define N_ADRENALINE 3
+#define N_ADRENALINE 2
//ninjaDrainAct() defines for non numerical returns
-//While not strictly needed, it's nicer than them just returning "twat"
-//Which was my original intention.
#define INVALID_DRAIN "INVALID" //This one is if the drain proc needs to cancel, eg missing variables, etc, it's important.
-
#define DRAIN_RD_HACK_FAILED "RDHACKFAIL"
#define DRAIN_MOB_SHOCK "MOBSHOCK"
#define DRAIN_MOB_SHOCK_FAILED "MOBSHOCKFAIL"
+
+
+//Tells whether or not someone is a space ninja
+#define IS_SPACE_NINJA(ninja) (ninja.mind && ninja.mind.has_antag_datum(/datum/antagonist/ninja))
+
+//Defines for the suit's unique abilities
+#define IS_NINJA_SUIT_INITIALIZATION(action) (istype(action, /datum/action/item_action/initialize_ninja_suit))
+#define IS_NINJA_SUIT_STATUS(action) (istype(action, /datum/action/item_action/ninjastatus))
+#define IS_NINJA_SUIT_BOOST(action) (istype(action, /datum/action/item_action/ninjaboost))
+#define IS_NINJA_SUIT_EMP(action) (istype(action, /datum/action/item_action/ninjapulse))
+#define IS_NINJA_SUIT_STAR_CREATION(action) (istype(action, /datum/action/item_action/ninjastar))
+#define IS_NINJA_SUIT_NET_CREATION(action) (istype(action, /datum/action/item_action/ninjanet))
+#define IS_NINJA_SUIT_SWORD_RECALL(action) (istype(action, /datum/action/item_action/ninja_sword_recall))
+#define IS_NINJA_SUIT_STEALTH(action) (istype(action, /datum/action/item_action/ninja_stealth))
diff --git a/code/modules/ninja/energy_katana.dm b/code/modules/ninja/energy_katana.dm
index 00cfa94893..cf895924b0 100644
--- a/code/modules/ninja/energy_katana.dm
+++ b/code/modules/ninja/energy_katana.dm
@@ -1,3 +1,14 @@
+/**
+ * # Energy Katana
+ *
+ * The space ninja's katana.
+ *
+ * The katana that only space ninja spawns with. Comes with 30 force and throwforce, along with a signature special jaunting system.
+ * Upon clicking on a tile with the dash on, the user will teleport to that tile, assuming their target was not dense.
+ * The katana has 3 dashes stored at maximum, and upon using the dash, it will return 20 seconds after it was used.
+ * It also has a special feature where if it is tossed at a space ninja who owns it (determined by the ninja suit), the ninja will catch the katana instead of being hit by it.
+ *
+ */
/obj/item/energy_katana
name = "energy katana"
desc = "A katana infused with strong energy."
@@ -5,16 +16,16 @@
item_state = "energy_katana"
lefthand_file = 'icons/mob/inhands/weapons/swords_lefthand.dmi'
righthand_file = 'icons/mob/inhands/weapons/swords_righthand.dmi'
- force = 40
- throwforce = 20
+ force = 30
+ throwforce = 30
block_chance = 50
armour_penetration = 50
w_class = WEIGHT_CLASS_NORMAL
hitsound = 'sound/weapons/bladeslice.ogg'
attack_verb = list("attacked", "slashed", "stabbed", "sliced", "torn", "ripped", "diced", "cut")
- block_chance = 50
slot_flags = ITEM_SLOT_BELT
sharpness = SHARP_EDGED
+ obj_flags = UNIQUE_RENAME // here is a shitpost and i cannot wait for ninjas naming their sword very rude things
max_integrity = 200
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
var/datum/effect_system/spark_spread/spark_system
@@ -34,13 +45,19 @@
/obj/item/energy_katana/afterattack(atom/target, mob/user, proximity_flag, click_parameters)
. = ..()
- if(dash_toggled)
- jaunt.Teleport(user, target)
- if(proximity_flag && (isobj(target) || issilicon(target)))
- spark_system.start()
- playsound(user, "sparks", 50, 1)
- playsound(user, 'sound/weapons/blade1.ogg', 50, 1)
- target.emag_act(user)
+ if(!ishuman(user))
+ return
+ var/mob/living/carbon/human/H = user
+ var/obj/item/clothing/suit/space/space_ninja/ninja_suit = H.wear_suit
+ if(!istype(ninja_suit))// do you actually have a ninja suit on
+ to_chat(user, "ERROR: garments incompatible with carbon material jaunt. Please suit up in compatible garments.")
+ return
+ if(!dash_toggled || Adjacent(target) || target.density)// is where you are going not a wall
+ return
+ if(!ninja_suit.s_coold == 0)// are you not currently on the ability cooldown
+ to_chat(user, "ERROR: suit is on cooldown.")
+ return
+ jaunt.Teleport(user, target)// okay you can now go my good sir
/obj/item/energy_katana/pickup(mob/living/user)
. = ..()
@@ -58,16 +75,30 @@
//To throw it at the ninja
/obj/item/energy_katana/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum)
if(ishuman(hit_atom))
- var/mob/living/carbon/human/H = hit_atom
- if(istype(H.wear_suit, /obj/item/clothing/suit/space/space_ninja))
- var/obj/item/clothing/suit/space/space_ninja/SN = H.wear_suit
- if(SN.energyKatana == src)
- returnToOwner(H, 0, 1)
+ var/mob/living/carbon/human/hit_human = hit_atom
+ if(istype(hit_human.wear_suit, /obj/item/clothing/suit/space/space_ninja))
+ var/obj/item/clothing/suit/space/space_ninja/ninja_suit = hit_human.wear_suit
+ if(ninja_suit.energyKatana == src)
+ returnToOwner(hit_human, 0, 1)
return
..()
-/obj/item/energy_katana/proc/returnToOwner(mob/living/carbon/human/user, doSpark = 1, caught = 0)
+/obj/item/energy_katana/Destroy()
+ QDEL_NULL(spark_system)
+ QDEL_NULL(jaunt)
+ return ..()
+
+/**
+ * Proc called when the katana is recalled to its space ninja.
+ *
+ * Proc called when space ninja is hit with its suit's katana or the recall ability is used.
+ * Arguments:
+ * * user - To whom the katana is returning to.
+ * * doSpark - whether or not the katana will spark when it returns.
+ * * caught - boolean for whether or not the katana was caught or was teleported back.
+ */
+/obj/item/energy_katana/proc/returnToOwner(mob/living/carbon/human/user, doSpark = TRUE, caught = FALSE)
if(!istype(user))
return
forceMove(get_turf(user))
@@ -94,13 +125,8 @@
if(msg)
to_chat(user, "[msg]")
-
-/obj/item/energy_katana/Destroy()
- QDEL_NULL(spark_system)
- return ..()
-
/datum/action/innate/dash/ninja
current_charges = 3
max_charges = 3
- charge_rate = 30
+ charge_rate = 200
recharge_sound = null
diff --git a/code/modules/ninja/ninja_event.dm b/code/modules/ninja/ninja_event.dm
deleted file mode 100644
index bc3c445731..0000000000
--- a/code/modules/ninja/ninja_event.dm
+++ /dev/null
@@ -1,93 +0,0 @@
-//Note to future generations: I didn't write this god-awful code I just ported it to the event system and tried to make it less moon-speaky.
-//Don't judge me D; ~Carn //Maximum judging occuring - Remie.
-// Tut tut Remie, let's keep our comments constructive. - coiax
-
-/*
-
-Contents:
-- The Ninja "Random" Event
-- Ninja creation code
-*/
-
-/datum/round_event_control/ninja
- name = "Space Ninja"
- typepath = /datum/round_event/ghost_role/ninja
- max_occurrences = 1
- earliest_start = 40 MINUTES
- gamemode_blacklist = list("dynamic")
- min_players = 15
-
-/datum/round_event/ghost_role/ninja
- var/success_spawn = 0
- role_name = "space ninja"
- minimum_required = 1
-
- var/helping_station
- var/spawn_loc
- var/give_objectives = TRUE
-
-/datum/round_event/ghost_role/ninja/setup()
- helping_station = rand(0,1)
-
-/datum/round_event/ghost_role/ninja/kill()
- if(!success_spawn && control)
- control.occurrences--
- return ..()
-
-/datum/round_event/ghost_role/ninja/spawn_role()
- //selecting a spawn_loc
- if(!spawn_loc)
- var/list/spawn_locs = list()
- for(var/obj/effect/landmark/carpspawn/L in GLOB.landmarks_list)
- if(isturf(L.loc))
- spawn_locs += L.loc
- for(var/obj/effect/landmark/loneopspawn/L in GLOB.landmarks_list)
- if(isturf(L.loc))
- spawn_locs += L.loc
- if(!spawn_locs.len)
- return kill()
- spawn_loc = pick(spawn_locs)
- if(!spawn_loc)
- return MAP_ERROR
-
- //selecting a candidate player
- var/list/candidates = get_candidates(ROLE_NINJA, null, ROLE_NINJA)
- if(!candidates.len)
- return NOT_ENOUGH_PLAYERS
-
- var/mob/dead/selected_candidate = pick_n_take(candidates)
- var/key = selected_candidate.key
-
- //Prepare ninja player mind
- var/datum/mind/Mind = new /datum/mind(key)
- Mind.assigned_role = ROLE_NINJA
- Mind.special_role = ROLE_NINJA
- Mind.active = 1
-
- //spawn the ninja and assign the candidate
- var/mob/living/carbon/human/Ninja = create_space_ninja(spawn_loc)
- Mind.transfer_to(Ninja)
- var/datum/antagonist/ninja/ninjadatum = new
- ninjadatum.helping_station = pick(TRUE,FALSE)
- Mind.add_antag_datum(ninjadatum)
-
- if(Ninja.mind != Mind) //something has gone wrong!
- stack_trace("Ninja created with incorrect mind")
-
- spawned_mobs += Ninja
- message_admins("[ADMIN_LOOKUPFLW(Ninja)] has been made into a ninja by an event.")
- log_game("[key_name(Ninja)] was spawned as a ninja by an event.")
- success_spawn = TRUE
-
- return SUCCESSFUL_SPAWN
-
-
-//=======//NINJA CREATION PROCS//=======//
-
-/proc/create_space_ninja(spawn_loc)
- var/mob/living/carbon/human/new_ninja = new(spawn_loc)
- var/datum/preferences/A = new()//Randomize appearance for the ninja.
- A.real_name = "[pick(GLOB.ninja_titles)] [pick(GLOB.ninja_names)]"
- A.copy_to(new_ninja)
- new_ninja.dna.update_dna_identity()
- return new_ninja
diff --git a/code/modules/ninja/ninja_explosive.dm b/code/modules/ninja/ninja_explosive.dm
new file mode 100644
index 0000000000..e87ff2b6d6
--- /dev/null
+++ b/code/modules/ninja/ninja_explosive.dm
@@ -0,0 +1,45 @@
+/**
+ * # Spider Charge
+ *
+ * A unique version of c4 possessed only by the space ninja. Has a stronger blast radius.
+ * Can only be detonated by space ninjas with the bombing objective. Can only be set up where the objective says it can.
+ * When it primes, the space ninja responsible will have their objective set to complete.
+ *
+ */
+/obj/item/grenade/plastic/c4/ninja
+ name = "spider charge"
+ desc = "A modified C-4 charge supplied to you by the Spider Clan. Its explosive power has been juiced up, but only works in one specific area."
+ icon_state = "plasticspider0"
+ item_state = "plasticspider"
+ boom_sizes = list(2, 4, 6)
+ var/mob/detonator = null
+
+/obj/item/grenade/plastic/c4/ninja/afterattack(atom/movable/AM, mob/user, flag)
+ var/datum/antagonist/ninja/ninja_antag = user.mind.has_antag_datum(/datum/antagonist/ninja)
+ if(!ninja_antag)
+ to_chat(user, "You fiddle with the charge, but nothing happens.")
+ return
+ var/datum/objective/plant_explosive/objective = locate() in ninja_antag.objectives
+ if(!objective)
+ to_chat(user, "You can't seem to activate the charge. It's location-locked, but you don't know where to detonate it.")
+ return
+ if(objective.detonation_location != get_area(user))
+ to_chat(user, "This isn't the location you're supposed to use this!")
+ return
+ detonator = user
+ return ..()
+
+/obj/item/grenade/plastic/c4/ninja/prime(mob/living/lanced_by)
+ . = ..()
+ anchored = TRUE
+ //Since we already did the checks in afterattack, the denonator must be a ninja with the bomb objective.
+ if(!detonator)
+ return
+ var/datum/antagonist/ninja/ninja_antag = detonator.mind.has_antag_datum(/datum/antagonist/ninja)
+ var/datum/objective/plant_explosive/objective = locate() in ninja_antag.objectives
+ objective.completed = TRUE
+
+/obj/item/grenade/plastic/c4/ninja/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/screwdriver))
+ to_chat(user, "The wire panel is locked in place, clearly you aren't supposed to be opening it!")
+ return
diff --git a/code/modules/ninja/outfit.dm b/code/modules/ninja/outfit.dm
index 983ef0585a..a6faa9a3d5 100644
--- a/code/modules/ninja/outfit.dm
+++ b/code/modules/ninja/outfit.dm
@@ -8,10 +8,10 @@
ears = /obj/item/radio/headset
shoes = /obj/item/clothing/shoes/space_ninja
gloves = /obj/item/clothing/gloves/space_ninja
- back = /obj/item/tank/jetpack/carbondioxide
- l_pocket = /obj/item/grenade/plastic/x4
- r_pocket = /obj/item/tank/internals/emergency_oxygen
- internals_slot = SLOT_R_STORE
+ l_pocket = /obj/item/grenade/plastic/c4/ninja
+ r_pocket = /obj/item/hypospray/mkii/CMO/combat
+ suit_store = /obj/item/tank/internals/oxygen
+ internals_slot = SLOT_S_STORE
belt = /obj/item/energy_katana
implants = list(/obj/item/implant/explosive)
@@ -21,5 +21,3 @@
var/obj/item/clothing/suit/space/space_ninja/S = H.wear_suit
if(istype(H.belt, belt))
S.energyKatana = H.belt
- S.randomize_param()
-
diff --git a/code/modules/ninja/suit/gloves.dm b/code/modules/ninja/suit/gloves.dm
index a06b753402..15abf0936e 100644
--- a/code/modules/ninja/suit/gloves.dm
+++ b/code/modules/ninja/suit/gloves.dm
@@ -1,25 +1,13 @@
-
-
-
-/*
- Dear ninja gloves
-
- This isn't because I like you
- this is because your father is a bastard
-
- ...
- I guess you're a little cool.
- -Sayu
-
-
- see ninjaDrainAct.dm for ninjadrain_act()
- Touch() simply calls this on it's target now
- Ninja's electricuting people when?
- -Remie
-
-*/
-
-
+/**
+ * # Ninja Gloves
+ *
+ * Space ninja's gloves. Gives access to a number of special interactions.
+ *
+ * Gloves only found from space ninjas. Allows the wearer to access special interactions with various objects.
+ * These interactions are detailed in ninjaDrainAct.dm in the suit file.
+ * These interactions are toggled by an action tied to the gloves. The interactions will not activate if the user is also not wearing a ninja suit.
+ *
+ */
/obj/item/clothing/gloves/space_ninja
desc = "These nano-enhanced gloves insulate from electricity and provide fire resistance."
name = "ninja gloves"
@@ -32,11 +20,24 @@
max_heat_protection_temperature = GLOVES_MAX_TEMP_PROTECT
strip_delay = 120
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- var/draining = 0
- var/candrain = 0
+ actions_types = list(/datum/action/item_action/toggle_glove)
+ var/datum/action/innate/doorjack/ninja/hack
+ ///Whether or not we're currently draining something
+ var/draining = FALSE
+ ///Whether or not we can currently drain something
+ var/candrain = FALSE
+ ///Minimum amount of power we can drain in a single drain action
var/mindrain = 200
+ ///Maximum amount of power we can drain in a single drain action
var/maxdrain = 400
-
+ ///Whether or not the security console hack was used to set everyone to arrest
+ var/security_console_hack_success = FALSE
+ ///Whether or not the communication console hack was used to summon another antagonist
+ var/communication_console_hack_success = FALSE
+ ///How many times the gloves have been used to force open doors.
+ var/door_hack_counter = 0
+ ///Whether or not the cyborg hack was used to syndify a cyborg
+ var/borg_hack_success = FALSE
var/stunforce = 100
/obj/item/clothing/gloves/space_ninja/Touch(atom/A,proximity)
@@ -45,9 +46,9 @@
if(!ishuman(loc))
return FALSE //Only works while worn
- var/mob/living/carbon/human/H = loc
+ var/mob/living/carbon/human/wearer = loc
- var/obj/item/clothing/suit/space/space_ninja/suit = H.wear_suit
+ var/obj/item/clothing/suit/space/space_ninja/suit = wearer.wear_suit
if(!istype(suit))
return FALSE
if(isturf(A))
@@ -56,17 +57,17 @@
if(!proximity)
return FALSE
- A.add_fingerprint(H)
+ A.add_fingerprint(wearer)
draining = TRUE
- . = A.ninjadrain_act(suit,H,src)
+ . = A.ninjadrain_act(suit,wearer,src)
draining = FALSE
if(isnum(.)) //Numerical values of drained handle their feedback here, Alpha values handle it themselves (Research hacking)
if(.)
- to_chat(H, "Gained [DisplayEnergy(.)] of energy from [A].")
+ to_chat(wearer, "Gained [DisplayEnergy(.)] of energy from [A].")
else
- to_chat(H, "\The [A] has run dry of energy, you must find another source!")
+ to_chat(wearer, "\The [A] has run dry of energy, you must find another source!")
. = INTERRUPT_UNARMED_ATTACK
else
. = FALSE //as to not cancel attack_hand()
@@ -75,12 +76,13 @@
. = ..()
REMOVE_TRAIT(src, TRAIT_NODROP, NINJA_SUIT_TRAIT)
-/obj/item/clothing/gloves/space_ninja/proc/toggledrain()
- var/mob/living/carbon/human/U = loc
- to_chat(U, "You [candrain?"disable":"enable"] special interaction.")
- candrain=!candrain
-
/obj/item/clothing/gloves/space_ninja/examine(mob/user)
. = ..()
if(HAS_TRAIT_FROM(src, TRAIT_NODROP, NINJA_SUIT_TRAIT))
. += "The energy drain mechanism is [candrain?"active":"inactive"]."
+
+/obj/item/clothing/gloves/space_ninja/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/toggle_glove))
+ toggledrain()
+ return TRUE
+ return FALSE
diff --git a/code/modules/ninja/suit/head.dm b/code/modules/ninja/suit/head.dm
index 9a65dabc1e..99dde98f09 100644
--- a/code/modules/ninja/suit/head.dm
+++ b/code/modules/ninja/suit/head.dm
@@ -1,14 +1,19 @@
-
-
+/**
+ * # Ninja Hood
+ *
+ * Space ninja's hood. Provides armor and blocks AI tracking.
+ *
+ * A hood that only exists as a part of space ninja's starting kit. Provides armor equal of space ninja's suit and disallows an AI to track the wearer.
+ *
+ */
/obj/item/clothing/head/helmet/space/space_ninja
desc = "What may appear to be a simple black garment is in fact a highly sophisticated nano-weave helmet. Standard issue ninja gear."
name = "ninja hood"
icon_state = "s-ninja"
item_state = "s-ninja_mask"
- armor = list("melee" = 60, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 25, "fire" = 100, "acid" = 100)
- strip_delay = 12
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 20,"energy" = 30, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 100, "acid" = 100)
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- blockTracking = 1//Roughly the only unique thing about this helmet.
+ blockTracking = TRUE//Roughly the only unique thing about this helmet.
flags_inv = HIDEEARS|HIDEEYES|HIDEFACE|HIDEHAIR|HIDEFACIALHAIR
/obj/item/clothing/head/helmet/space/space_ninja/dropped(mob/user)
diff --git a/code/modules/ninja/suit/mask.dm b/code/modules/ninja/suit/mask.dm
index 421c13f9eb..111f76ae65 100644
--- a/code/modules/ninja/suit/mask.dm
+++ b/code/modules/ninja/suit/mask.dm
@@ -1,15 +1,11 @@
-
-/*
-
-Contents:
-- The Ninja Space Mask
-- Ninja Space Mask speech modification
-
-*/
-
-
-
-
+/**
+ * # Ninja Mask
+ *
+ * Space ninja's mask. Makes you sound like a real anime girl. Barely able to be considered a real upside.
+ *
+ * A mask which only spawns as a part of space ninja's starting kit. Functions as a gas mask.
+ *
+ */
/obj/item/clothing/mask/gas/space_ninja
name = "ninja mask"
desc = "A close-fitting mask that acts both as an air filter and a post-modern fashion statement."
diff --git a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm b/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm
deleted file mode 100644
index c0ee68cdee..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/energy_net_nets.dm
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-It will teleport people to a holding facility after 30 seconds. (Check the process() proc to change where teleport goes)
-It is possible to destroy the net by the occupant or someone else.
-*/
-
-/obj/structure/energy_net
- name = "energy net"
- desc = "It's a net made of green energy."
- icon = 'icons/effects/effects.dmi'
- icon_state = "energynet"
-
- density = TRUE//Can't pass through.
- opacity = 0//Can see through.
- mouse_opacity = MOUSE_OPACITY_ICON//So you can hit it with stuff.
- anchored = TRUE//Can't drag/grab the net.
- layer = ABOVE_ALL_MOB_LAYER
- max_integrity = 50 //How much health it has.
- can_buckle = 1
- buckle_lying = 0
- buckle_prevents_pull = TRUE
- var/mob/living/carbon/affecting //Who it is currently affecting, if anyone.
- var/mob/living/carbon/master //Who shot web. Will let this person know if the net was successful or failed.
- var/check = 15//30 seconds before teleportation. Could be extended I guess.
- var/success = FALSE
-
-
-/obj/structure/energy_net/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
- switch(damage_type)
- if(BRUTE)
- playsound(src, 'sound/weapons/slash.ogg', 80, 1)
- if(BURN)
- playsound(src, 'sound/weapons/slash.ogg', 80, 1)
-
-/obj/structure/energy_net/Destroy()
- if(!success)
- if(!QDELETED(affecting))
- affecting.visible_message("[affecting.name] was recovered from the energy net!", "You were recovered from the energy net!", "You hear a grunt.")
- if(!QDELETED(master))//As long as they still exist.
- to_chat(master, "ERROR: unable to initiate transport protocol. Procedure terminated.")
- return ..()
-
-/obj/structure/energy_net/process()
- if(QDELETED(affecting)||affecting.loc!=loc)
- qdel(src)//Get rid of the net.
- return
-
- if(check>0)
- check--
- return
-
- success = TRUE
- qdel(src)
- if(ishuman(affecting))
- var/mob/living/carbon/human/H = affecting
- for(var/obj/item/W in H)
- if(W == H.w_uniform || W == H.shoes)
- continue//So all they're left with are shoes and uniform.
- H.dropItemToGround(W)
- H.dna.species.give_important_for_life(H) // After we remove items, at least give them what they need to live.
-/*
- var/datum/antagonist/antag_datum
- for(var/datum/antagonist/ninja/AD in GLOB.antagonists) //Because only ninjas get capture objectives; They're not doable without the suit.
- if(AD.owner == master)
- antag_datum = AD
- break
- for(var/datum/objective/capture/capture in antag_datum)
- if(istype(affecting, /mob/living/carbon/human)) //Humans.
- if(affecting.stat == DEAD)//Dead folks are worth less.
- capture.captured_amount+=0.5
- continue
- capture.captured_amount+=1
- if(istype(affecting, /mob/living/carbon/monkey)) //Monkeys are almost worthless, you failure.
- capture.captured_amount+=0.1
- if(istype(affecting, /mob/living/carbon/alien/larva)) //Larva are important for research.
- if(affecting.stat == DEAD)
- capture.captured_amount+=0.5
- continue
- capture.captured_amount+=1
- if(istype(affecting, /mob/living/carbon/alien/humanoid)) //Aliens are worth twice as much as humans.
- if(istype(affecting, /mob/living/carbon/alien/humanoid/royal/queen)) //Queens are worth three times as much as humans.
- if(affecting.stat == DEAD)
- capture.captured_amount+=1.5
- else
- capture.captured_amount+=3
- continue
- if(affecting.stat == DEAD)
- capture.captured_amount+=1
- continue
- capture.captured_amount+=2
-*/
-
- affecting.revive(1, 1) //Basically a revive and full heal, including limbs/organs
- //In case people who have been captured dead want to hang out at the holding area
-
- playsound(affecting, 'sound/effects/sparks4.ogg', 50, 1)
- new /obj/effect/temp_visual/dir_setting/ninja/phase/out(affecting.drop_location(), affecting.dir)
-
- visible_message("[affecting] suddenly vanishes!")
- affecting.forceMove(pick(GLOB.holdingfacility)) //Throw mob in to the holding facility.
- to_chat(affecting, "You appear in a strange place!")
-
- if(!QDELETED(master))//As long as they still exist.
- to_chat(master, "SUCCESS: transport procedure of [affecting] complete.")
- do_sparks(5, FALSE, affecting)
- playsound(affecting, 'sound/effects/phasein.ogg', 25, 1)
- playsound(affecting, 'sound/effects/sparks2.ogg', 50, 1)
- new /obj/effect/temp_visual/dir_setting/ninja/phase(affecting.drop_location(), affecting.dir)
-
-/obj/structure/energy_net/attack_alien(mob/living/carbon/alien/humanoid/user)
- if(attack_generic(user, 15, BRUTE, "melee", 0)) //Aliens normally deal 60 damage to structures. They'd one-shot nets without this.
- playsound(src.loc, 'sound/weapons/slash.ogg', 100, 1)
-
-/obj/structure/energy_net/user_buckle_mob(mob/living/M, mob/living/user)
- return//We only want our target to be buckled
-
-/obj/structure/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/living/user)
- return//The net must be destroyed to free the target
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm
deleted file mode 100644
index 89f5d20e70..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_adrenaline.dm
+++ /dev/null
@@ -1,19 +0,0 @@
-//Wakes the user so they are able to do their thing. Also injects a decent dose of radium.
-//Movement impairing would indicate drugs and the like.
-/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost()
-
- if(!ninjacost(0,N_ADRENALINE))
- var/mob/living/carbon/human/H = affecting
- H.do_adrenaline(150, TRUE, 0, 0, TRUE, list(/datum/reagent/medicine/inaprovaline = 3, /datum/reagent/medicine/synaptizine = 10, /datum/reagent/medicine/omnizine = 10), "You feel a sudden surge of energy!")
-
- H.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost")
-
- a_boost--
- to_chat(H, "There are [a_boost] adrenaline boosts remaining.")
- s_coold = 3
- addtimer(CALLBACK(src, .proc/ninjaboost_after), 70)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost_after()
- var/mob/living/carbon/human/H = affecting
- H.reagents.add_reagent(/datum/reagent/radium, a_transfer)
- to_chat(H, "You are beginning to feel the after-effect of the injection.")
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm
deleted file mode 100644
index 12fea51815..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_cost_check.dm
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
-//Cost function for suit Procs/Verbs/Abilities
-/obj/item/clothing/suit/space/space_ninja/proc/ninjacost(cost = 0, specificCheck = 0)
- var/mob/living/carbon/human/H = affecting
- var/actualCost = cost*10
- if(cost && cell.charge < actualCost)
- to_chat(H, "Not enough energy.")
- return 1
- else
- //This shit used to be handled individually on every proc.. why even bother with a universal check proc then?
- cell.charge-=(actualCost)
-
- switch(specificCheck)
- if(N_STEALTH_CANCEL)
- cancel_stealth()//Get rid of it.
- if(N_SMOKE_BOMB)
- if(!s_bombs)
- to_chat(H, "There are no more smoke bombs remaining.")
- return 1
- if(N_ADRENALINE)
- if(!a_boost)
- to_chat(H, "You do not have any more adrenaline boosters.")
- return 1
- return (s_coold)//Returns the value of the variable which counts down to zero.
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm
deleted file mode 100644
index 29dc78e5f3..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_empulse.dm
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-//Disables nearby tech equipment.
-/obj/item/clothing/suit/space/space_ninja/proc/ninjapulse()
-
- if(!ninjacost(250,N_STEALTH_CANCEL))
- var/mob/living/carbon/human/H = affecting
- playsound(H.loc, 'sound/effects/empulse.ogg', 60, 2)
- empulse_using_range(H, 9) //Procs sure are nice. Slightly weaker than wizard's disable tch.
- s_coold = 2
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm
deleted file mode 100644
index 61355ca89b..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_net.dm
+++ /dev/null
@@ -1,44 +0,0 @@
-
-//Allows the ninja to kidnap people
-/obj/item/clothing/suit/space/space_ninja/proc/ninjanet()
- var/mob/living/carbon/human/H = affecting
- var/mob/living/carbon/C
-
- //If there's only one valid target, let's actually try to capture it, rather than forcing
- //the user to fiddle with the dialog displaying a list of one
- //Also, let's make this smarter and not list mobs you can't currently net.
- var/list/candidates
- for(var/mob/M in oview(H))
- if(!M.client)//Monkeys without a client can still step_to() and bypass the net. Also, netting inactive people is lame.
- continue
- for(var/obj/structure/energy_net/E in get_turf(M))//Check if they are already being affected by an energy net.
- if(E.affecting == M)
- continue
- LAZYADD(candidates, M)
-
- if(!LAZYLEN(candidates))
- return FALSE
-
- if(candidates.len == 1)
- C = candidates[1]
- else
- C = input("Select who to capture:","Capture who?",null) as null|mob in candidates
-
-
- if(QDELETED(C)||!(C in oview(H)))
- return FALSE
-
- if(!ninjacost(200,N_STEALTH_CANCEL))
- H.Beam(C,"n_beam",time=15)
- H.say("Get over here!", forced = "ninja net")
- var/obj/structure/energy_net/E = new /obj/structure/energy_net(C.drop_location())
- E.affecting = C
- E.master = H
- H.visible_message("[H] caught [C] with an energy net!","You caught [C] with an energy net!")
-
- if(C.buckled)
- C.buckled.unbuckle_mob(affecting,TRUE)
- E.buckle_mob(C, TRUE) //No moving for you!
- //The person can still try and attack the net when inside.
-
- START_PROCESSING(SSobj, E)
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm
deleted file mode 100644
index 3c19162048..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_smoke.dm
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-//Smoke bomb
-/obj/item/clothing/suit/space/space_ninja/proc/ninjasmoke()
-
- if(!ninjacost(0,N_SMOKE_BOMB))
- var/mob/living/carbon/human/H = affecting
- var/datum/effect_system/smoke_spread/bad/smoke = new
- smoke.set_up(4, H.loc)
- smoke.start()
- playsound(H.loc, 'sound/effects/bamf.ogg', 50, 2)
- s_bombs--
- to_chat(H, "There are [s_bombs] smoke bombs remaining.")
- s_coold = 1
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm
deleted file mode 100644
index 57faad9493..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_stars.dm
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-//Creates a throwing star
-/obj/item/clothing/suit/space/space_ninja/proc/ninjastar()
- if(!ninjacost(10))
- var/mob/living/carbon/human/H = affecting
- var/obj/item/throwing_star/ninja/N = new(H)
- if(H.put_in_hands(N))
- to_chat(H, "A throwing star has been created in your hand!")
- else
- qdel(N)
- H.throw_mode_on() //So they can quickly throw it.
-
-
-/obj/item/throwing_star/ninja
- name = "ninja throwing star"
- throwforce = 20
- embedding = list("pain_mult" = 6, "embed_chance" = 100, "fall_chance" = 0, "embed_chance_turf_mod" = 15)
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm
deleted file mode 100644
index 56c50078c1..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_stealth.dm
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/*
-
-Contents:
-- Stealth Verbs
-
-*/
-
-
-/obj/item/clothing/suit/space/space_ninja/proc/toggle_stealth()
- if(!affecting || stealth_cooldown > world.time)
- return
- if(stealth)
- cancel_stealth()
- else
- if(cell.charge <= 0)
- to_chat(affecting, "You don't have enough power to enable Stealth!")
- return
- stealth = TRUE
- stealth_cooldown = world.time + 5 SECONDS
- animate(affecting, alpha = 15, time = 3 SECONDS)
- affecting.visible_message("[affecting.name] vanishes into thin air!", \
- "You are now mostly invisible to normal detection.")
- addtimer(CALLBACK(src, .proc/enable_signals), 3 SECONDS)
-
-/obj/item/clothing/suit/space/space_ninja/proc/enable_signals()
- if(!affecting)
- return
- RegisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE), .proc/reduce_stealth)
- RegisterSignal(affecting, COMSIG_MOVABLE_BUMP, .proc/bumping_stealth)
-
-/obj/item/clothing/suit/space/space_ninja/proc/reduce_stealth(datum/source)
- affecting.alpha = min(affecting.alpha + 40, 100)
-
-/obj/item/clothing/suit/space/space_ninja/proc/bumping_stealth(datum/source, atom/A)
- if(isliving(A))
- affecting.alpha = min(affecting.alpha + 20, 100)
-
-/obj/item/clothing/suit/space/space_ninja/proc/cancel_stealth()
- if(!affecting || !stealth)
- return FALSE
- stealth = !stealth
- stealth_cooldown = world.time + 5 SECONDS
- UnregisterSignal(affecting, list(COMSIG_MOB_ITEM_ATTACK, COMSIG_MOB_ATTACK_RANGED, COMSIG_HUMAN_MELEE_UNARMED_ATTACK, COMSIG_MOB_ATTACK_HAND, COMSIG_MOB_THROW, COMSIG_PARENT_ATTACKBY, COMSIG_MOVABLE_BUMP, COMSIG_MOVABLE_TELEPORTED, COMSIG_LIVING_GUN_PROCESS_FIRE))
- animate(affecting, alpha = 255, time = 3 SECONDS)
- affecting.visible_message("[affecting.name] appears from thin air!", \
- "You are now visible.")
- return TRUE
-
-/obj/item/clothing/suit/space/space_ninja/proc/stealth()
- if(!s_busy)
- toggle_stealth()
- else
- to_chat(affecting, "Stealth does not appear to work!")
diff --git a/code/modules/ninja/suit/n_suit_verbs/ninja_sword_recall.dm b/code/modules/ninja/suit/n_suit_verbs/ninja_sword_recall.dm
deleted file mode 100644
index 7a192c427c..0000000000
--- a/code/modules/ninja/suit/n_suit_verbs/ninja_sword_recall.dm
+++ /dev/null
@@ -1,39 +0,0 @@
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninja_sword_recall()
- var/mob/living/carbon/human/H = affecting
-
- var/cost = 0
- var/inview = 1
-
- if(!energyKatana)
- to_chat(H, "Could not locate Energy Katana!")
- return
-
- if(energyKatana in H)
- return
-
- var/distance = get_dist(H,energyKatana)
-
- if(!(energyKatana in view(H)))
- cost = distance //Actual cost is cost x 10, so 5 turfs is 50 cost.
- inview = 0
-
- if(!ninjacost(cost))
- if(iscarbon(energyKatana.loc))
- var/mob/living/carbon/C = energyKatana.loc
- C.transferItemToLoc(energyKatana, get_turf(energyKatana), TRUE)
-
- //Somebody swollowed my sword, probably the clown doing a circus act.
- if(energyKatana in C.stomach_contents)
- C.stomach_contents -= energyKatana
- else
- energyKatana.forceMove(get_turf(energyKatana))
-
- if(inview) //If we can see the katana, throw it towards ourselves, damaging people as we go.
- energyKatana.spark_system.start()
- playsound(H, "sparks", 50, 1)
- H.visible_message("\the [energyKatana] flies towards [H]!","You hold out your hand and \the [energyKatana] flies towards you!")
- energyKatana.throw_at(H, distance+1, energyKatana.throw_speed,H)
-
- else //Else just TP it to us.
- energyKatana.returnToOwner(H,1)
diff --git a/code/modules/ninja/suit/ninjaDrainAct.dm b/code/modules/ninja/suit/ninjaDrainAct.dm
index af0ca809da..4f287fc81b 100644
--- a/code/modules/ninja/suit/ninjaDrainAct.dm
+++ b/code/modules/ninja/suit/ninjaDrainAct.dm
@@ -1,274 +1,326 @@
-
-/*
-
-Contents:
-- Assorted ninjadrain_act() procs
-- What is Object Oriented Programming
-
-They *could* go in their appropriate files, but this is supposed to be modular
-
-*/
-
-
-//Needs to return the amount drained from the atom, if no drain on a power object, return FALSE, otherwise, return a define.
-/atom/proc/ninjadrain_act()
+/**
+ * Atom level proc for space ninja's glove interactions.
+ *
+ * Proc which only occurs when space ninja uses his gloves on an atom.
+ * Does nothing by default, but effects will vary.
+ * Arguments:
+ * * ninja_suit - The offending space ninja's suit.
+ * * ninja - The human mob wearing the suit.
+ * * ninja_gloves - The offending space ninja's gloves.
+ */
+/atom/proc/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
return INVALID_DRAIN
-
-
-
//APC//
-/obj/machinery/power/apc/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/obj/machinery/power/apc/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
- var/maxcapacity = 0 //Safety check for batteries
+ var/maxcapacity = FALSE //Safety check for batteries
var/drain = 0 //Drain amount from batteries
-
- . = 0
+ var/drain_total = 0
if(cell && cell.charge)
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, loc)
- while(G.candrain && cell.charge> 0 && !maxcapacity)
- drain = rand(G.mindrain, G.maxdrain)
+ while(ninja_gloves.candrain && cell.charge> 0 && !maxcapacity)
+ drain = rand(ninja_gloves.mindrain, ninja_gloves.maxdrain)
if(cell.charge < drain)
drain = cell.charge
- if(S.cell.charge + drain > S.cell.maxcharge)
- drain = S.cell.maxcharge - S.cell.charge
- maxcapacity = 1//Reached maximum battery capacity.
+ if(ninja_suit.cell.charge + drain > ninja_suit.cell.maxcharge)
+ drain = ninja_suit.cell.maxcharge - ninja_suit.cell.charge
+ maxcapacity = TRUE//Reached maximum battery capacity.
- if (do_after(H,10, target = src))
+ if (do_after(ninja ,10, target = src))
spark_system.start()
playsound(loc, "sparks", 50, 1)
cell.charge -= drain
- S.cell.charge += drain
- . += drain
+ ninja_suit.cell.charge += drain
+ drain_total += drain
else
break
if(!(obj_flags & EMAGGED))
- flick("apc-spark", G)
+ flick("apc-spark", ninja_gloves)
playsound(loc, "sparks", 50, 1)
obj_flags |= EMAGGED
locked = FALSE
update_icon()
-
-
-
+ return drain_total
//SMES//
-/obj/machinery/power/smes/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/obj/machinery/power/smes/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
var/maxcapacity = 0 //Safety check for batteries
- var/drain = 0 //Drain amount from batteries
-
- . = 0
+ var/drain = FALSE //Drain amount from batteries
+ var/drain_total = 0
if(charge)
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, loc)
- while(G.candrain && charge > 0 && !maxcapacity)
- drain = rand(G.mindrain, G.maxdrain)
+ while(ninja_gloves.candrain && charge > 0 && !maxcapacity)
+ drain = rand(ninja_gloves.mindrain, ninja_gloves.maxdrain)
if(charge < drain)
drain = charge
- if(S.cell.charge + drain > S.cell.maxcharge)
- drain = S.cell.maxcharge - S.cell.charge
- maxcapacity = 1
+ if(ninja_suit.cell.charge + drain > ninja_suit.cell.maxcharge)
+ drain = ninja_suit.cell.maxcharge - ninja_suit.cell.charge
+ maxcapacity = TRUE
- if (do_after(H,10, target = src))
+ if (do_after(ninja,10, target = src))
spark_system.start()
playsound(loc, "sparks", 50, 1)
charge -= drain
- S.cell.charge += drain
- . += drain
+ ninja_suit.cell.charge += drain
+ drain_total += drain
else
break
+ return drain_total
//CELL//
-/obj/item/stock_parts/cell/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/obj/item/stock_parts/cell/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
- . = 0
+ var/drain_total = 0
if(charge)
- if(G.candrain && do_after(H,30, target = src))
- . = charge
- if(S.cell.charge + charge > S.cell.maxcharge)
- S.cell.charge = S.cell.maxcharge
+ if(ninja_gloves.candrain && do_after(ninja,30, target = src))
+ drain_total = charge
+ if(ninja_suit.cell.charge + charge > ninja_suit.cell.maxcharge)
+ ninja_suit.cell.charge = ninja_suit.cell.maxcharge
else
- S.cell.charge += charge
+ ninja_suit.cell.charge += charge
charge = 0
corrupt()
update_icon()
-/obj/machinery/proc/AI_notify_hack()
- var/turf/location = get_turf(src)
- var/alertstr = "Network Alert: Hacking attempt detected[location?" in [location]":". Unable to pinpoint location"]."
- for(var/mob/living/silicon/ai/AI in GLOB.player_list)
- to_chat(AI, alertstr)
+ return drain_total
//RDCONSOLE//
-/obj/machinery/computer/rdconsole/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/obj/machinery/computer/rdconsole/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
. = DRAIN_RD_HACK_FAILED
- to_chat(H, "Hacking \the [src]...")
+ to_chat(ninja, "Hacking \the [src]...")
AI_notify_hack()
if(stored_research)
- to_chat(H, "Copying files...")
- if(do_after(H, S.s_delay, target = src) && G.candrain && src)
- stored_research.copy_research_to(S.stored_research)
- to_chat(H, "Data analyzed. Process finished.")
+ to_chat(ninja, "Copying files...")
+ if(do_after(ninja, ninja_suit.s_delay, target = src) && ninja_gloves.candrain && src)
+ stored_research.copy_research_to( ninja_suit.stored_research)
+ to_chat(ninja, "Data analyzed. Process finished.")
//RD SERVER//
-//Shamelessly copypasted from above, since these two used to be the same proc, but with MANY colon operators
-/obj/machinery/rnd/server/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+//obj/machinery/rnd/server/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
. = DRAIN_RD_HACK_FAILED
- to_chat(H, "Hacking \the [src]...")
+ to_chat(ninja, "Hacking \the [src]...")
AI_notify_hack()
if(stored_research)
- to_chat(H, "Copying files...")
- if(do_after(H, S.s_delay, target = src) && G.candrain && src)
- stored_research.copy_research_to(S.stored_research)
- to_chat(H, "Data analyzed. Process finished.")
+ to_chat(ninja, "Copying files...")
+ if(do_after(ninja, ninja_suit.s_delay, target = src) && ninja_gloves.candrain && src)
+ stored_research.copy_research_to(ninja_suit.stored_research)
+ to_chat(ninja, "Data analyzed. Process finished.")
+//SECURITY CONSOLE//
+/obj/machinery/computer/secure_data/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
+ return INVALID_DRAIN
+ if(ninja_gloves.security_console_hack_success)
+ return
+ to_chat(ninja, "Hacking \the [src]...")
+ AI_notify_hack()
+ if(do_after(ninja, ninja_suit.s_longdelay, target = src) && ninja_gloves.candrain && src)
+ for(var/datum/data/record/rec in sortRecord(GLOB.data_core.general, sortBy, order))
+ for(var/datum/data/record/security_record in GLOB.data_core.security)
+ security_record.fields["criminal"] = "*Arrest*"
+ var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja)
+ if(!ninja_antag)
+ return
+ ninja_gloves.security_console_hack_success = TRUE
+ var/datum/objective/security_scramble/objective = locate() in ninja_antag.objectives
+ if(objective)
+ objective.completed = TRUE
+ to_chat(ninja, "Security record corruption malware uploaded. Process finished; objective completed.")
-//WIRE//
-/obj/structure/cable/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+//COMMUNICATIONS CONSOLE//
+/obj/machinery/computer/communications/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
+ return INVALID_DRAIN
+ if(ninja_gloves.communication_console_hack_success)
+ return
+ to_chat(ninja, "Hacking \the [src]...")
+ AI_notify_hack()
+ if(do_after(ninja, ninja_suit.s_longdelay, target = src) && ninja_gloves.candrain && src)
+ var/announcement_pick = rand(0, 1)
+ switch(announcement_pick)
+ if(0)
+ priority_announce("Attention crew, it appears that something on your station has caused an unexpected disruption with the station's airlock network.", "[command_name()] High-Priority Update")
+ var/datum/round_event_control/grey_tide/greytide_event = new/datum/round_event_control/grey_tide
+ greytide_event.runEvent()
+ if(1)
+ priority_announce("Attention crew, it appears that something on your station has made unexpected communication with a syndicate ship in nearby space.", "[command_name()] High-Priority Update")
+ var/datum/round_event_control/pirates/pirate_event = new/datum/round_event_control/pirates
+ pirate_event.runEvent()
+ ninja_gloves.communication_console_hack_success = TRUE
+ var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja)
+ if(!ninja_antag)
+ return
+ var/datum/objective/terror_message/objective = locate() in ninja_antag.objectives
+ if(objective)
+ objective.completed = TRUE
+ to_chat(ninja, "Signal decryption malware uploaded. Process finished; objective completed.")
+
+//AIRLOCK//
+/obj/machinery/door/airlock/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
- var/maxcapacity = 0 //Safety check
+ if(!operating && density && hasPower() && !(obj_flags & EMAGGED))
+ emag_act()
+ ninja_gloves.door_hack_counter++
+ var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja)
+ if(!ninja_antag)
+ return
+ var/datum/objective/door_jack/objective = locate() in ninja_antag.objectives
+ if(objective && objective.doors_required <= ninja_gloves.door_hack_counter)
+ objective.completed = TRUE
+
+//WIRE//
+/obj/structure/cable/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
+ return INVALID_DRAIN
+
+ var/maxcapacity = FALSE //Safety check
var/drain = 0 //Drain amount
- . = 0
+ var/drain_total = 0
- var/datum/powernet/PN = powernet
- while(G.candrain && !maxcapacity && src)
- drain = (round((rand(G.mindrain, G.maxdrain))/2))
+ var/datum/powernet/wire_powernet = powernet
+ while(ninja_gloves.candrain && !maxcapacity && src)
+ drain = (round((rand(ninja_gloves.mindrain, ninja_gloves.maxdrain))/2))
var/drained = 0
- if(PN && do_after(H,10, target = src))
+ if(wire_powernet && do_after(ninja,10, target = src))
drained = min(drain, delayed_surplus())
add_delayedload(drained)
if(drained < drain)//if no power on net, drain apcs
- for(var/obj/machinery/power/terminal/T in PN.nodes)
- if(istype(T.master, /obj/machinery/power/apc))
- var/obj/machinery/power/apc/AP = T.master
+ for(var/obj/machinery/power/terminal/affected_terminal in wire_powernet.nodes)
+ if(istype(affected_terminal.master, /obj/machinery/power/apc))
+ var/obj/machinery/power/apc/AP = affected_terminal.master
if(AP.operating && AP.cell && AP.cell.charge > 0)
AP.cell.charge = max(0, AP.cell.charge - 5)
drained += 5
else
break
- S.cell.charge += drained
- if(S.cell.charge > S.cell.maxcharge)
- . += (drained-(S.cell.charge - S.cell.maxcharge))
- S.cell.charge = S.cell.maxcharge
- maxcapacity = 1
+ ninja_suit.cell.charge += drained
+ if(ninja_suit.cell.charge > ninja_suit.cell.maxcharge)
+ . += (drained-(ninja_suit.cell.charge - ninja_suit.cell.maxcharge))
+ ninja_suit.cell.charge = ninja_suit.cell.maxcharge
+ maxcapacity = TRUE
else
- . += drained
- S.spark_system.start()
+ drain_total += drained
+ ninja_suit.spark_system.start()
+
+ return drain_total
//MECH//
-/obj/mecha/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/obj/mecha/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
- var/maxcapacity = 0 //Safety check
+ var/maxcapacity = FALSE //Safety check
var/drain = 0 //Drain amount
- . = 0
+ var/drain_total = 0
occupant_message("Warning: Unauthorized access through sub-route 4, block H, detected.")
if(get_charge())
- while(G.candrain && cell.charge > 0 && !maxcapacity)
- drain = rand(G.mindrain,G.maxdrain)
+ while(ninja_gloves.candrain && cell.charge > 0 && !maxcapacity)
+ drain = rand(ninja_gloves.mindrain, ninja_gloves.maxdrain)
if(cell.charge < drain)
drain = cell.charge
- if(S.cell.charge + drain > S.cell.maxcharge)
- drain = S.cell.maxcharge - S.cell.charge
- maxcapacity = 1
- if (do_after(H,10, target = src))
+ if(ninja_suit.cell.charge + drain > ninja_suit.cell.maxcharge)
+ drain = ninja_suit.cell.maxcharge - ninja_suit.cell.charge
+ maxcapacity = TRUE
+ if (do_after(ninja, 10, target = src))
spark_system.start()
playsound(loc, "sparks", 50, 1)
cell.use(drain)
- S.cell.charge += drain
- . += drain
+ ninja_suit.cell.give(drain)
+ drain_total += drain
else
break
+ return drain_total
+
//BORG//
-/mob/living/silicon/robot/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/mob/living/silicon/robot/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves || (ROLE_NINJA in faction))
return INVALID_DRAIN
+ if(ninja_gloves.borg_hack_success)
+ return
+ to_chat(src, "Warni-***BZZZZZZZZZRT*** UPLOADING SPYDERPATCHER VERSION 9.5.2...")
+ if (do_after(ninja, 60, target = src))
+ spark_system.start()
+ playsound(loc, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ to_chat(src, "UPLOAD COMPLETE. NEW CYBORG MODULE DETECTED. INSTALLING...")
+ faction = list(ROLE_NINJA)
+ bubble_icon = "syndibot"
+ UnlinkSelf()
+ ionpulse = TRUE
+ laws = new /datum/ai_laws/ninja_override()
+ module.transform_to(pick(/obj/item/robot_module/syndicate/spider, /obj/item/robot_module/syndicate_medical/spider, /obj/item/robot_module/saboteur/spider))
- var/maxcapacity = 0 //Safety check
- var/drain = 0 //Drain amount
- . = 0
-
- to_chat(src, "Warning: Unauthorized access through sub-route 12, block C, detected.")
-
- if(cell && cell.charge)
- while(G.candrain && cell.charge > 0 && !maxcapacity)
- drain = rand(G.mindrain,G.maxdrain)
- if(cell.charge < drain)
- drain = cell.charge
- if(S.cell.charge+drain > S.cell.maxcharge)
- drain = S.cell.maxcharge - S.cell.charge
- maxcapacity = 1
- if (do_after(H,10))
- spark_system.start()
- playsound(loc, "sparks", 50, 1)
- cell.charge -= drain
- S.cell.charge += drain
- . += drain
- else
- break
-
+ var/datum/antagonist/ninja/ninja_antag = ninja.mind.has_antag_datum(/datum/antagonist/ninja)
+ if(!ninja_antag)
+ return
+ ninja_gloves.borg_hack_success = TRUE
+ var/datum/objective/cyborg_hijack/objective = locate() in ninja_antag.objectives
+ if(objective)
+ objective.completed = TRUE
//CARBON MOBS//
-/mob/living/carbon/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/S, mob/living/carbon/human/H, obj/item/clothing/gloves/space_ninja/G)
- if(!S || !H || !G)
+/mob/living/carbon/ninjadrain_act(obj/item/clothing/suit/space/space_ninja/ninja_suit, mob/living/carbon/human/ninja, obj/item/clothing/gloves/space_ninja/ninja_gloves)
+ if(!ninja_suit || !ninja || !ninja_gloves)
return INVALID_DRAIN
. = DRAIN_MOB_SHOCK_FAILED
//Default cell = 10,000 charge, 10,000/1000 = 10 uses without charging/upgrading
- if(S.cell && S.cell.charge && S.cell.use(500))
+ if(ninja_suit.cell?.charge && ninja_suit.cell.use(1000))
. = DRAIN_MOB_SHOCK
//Got that electric touch
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread()
spark_system.set_up(5, 0, loc)
playsound(src, "sparks", 50, 1)
- visible_message("[H] electrocutes [src] with [H.p_their()] touch!", "[H] electrocutes you with [H.p_their()] touch!")
- electrocute_act(15, H, flags = SHOCK_NOSTUN)
+ visible_message("[ninja] electrocutes [src] with [ninja.p_their()] touch!", "[ninja] electrocutes you with [ninja.p_their()] touch!")
+ electrocute_act(15, ninja, flags = SHOCK_NOSTUN)
- DefaultCombatKnockdown(G.stunforce, override_hardstun = 0)
- apply_effect(EFFECT_STUTTER, G.stunforce)
+ DefaultCombatKnockdown(ninja_gloves.stunforce, override_hardstun = 0)
+ apply_effect(EFFECT_STUTTER, ninja_gloves.stunforce)
SEND_SIGNAL(src, COMSIG_LIVING_MINOR_SHOCK)
-
- set_last_attacker(H)
- log_combat(H, src, "stunned")
+ lastattacker = ninja.real_name
+ lastattackerckey = ninja.ckey
+ log_combat(ninja, src, "stunned")
+ set_last_attacker(ninja)
+ log_combat(ninja, src, "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm b/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm
new file mode 100644
index 0000000000..bc0828242e
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/energy_net_nets.dm
@@ -0,0 +1,46 @@
+/**
+ * # Energy Net
+ *
+ * Energy net which ensnares prey until it is destroyed. Used by space ninjas.
+ *
+ * Energy net which keeps its target from moving until it is destroyed. Used to send
+ * players to a holding area in which they could never leave, but such feature has since
+ * been removed.
+ */
+/obj/structure/energy_net
+ name = "energy net"
+ desc = "It's a net made of green energy."
+ icon = 'icons/effects/effects.dmi'
+ icon_state = "energynet"
+
+
+ density = TRUE//Can't pass through.
+ opacity = FALSE //Can see through.
+ mouse_opacity = MOUSE_OPACITY_ICON//So you can hit it with stuff.
+ anchored = TRUE//Can't drag/grab the net.
+ layer = ABOVE_ALL_MOB_LAYER
+ max_integrity = 60 //How much health it has.
+ can_buckle = 1
+ buckle_lying = 0
+ buckle_prevents_pull = TRUE
+ ///The creature currently caught in the net
+ var/mob/living/affecting
+
+/obj/structure/energy_net/play_attack_sound(damage, damage_type = BRUTE, damage_flag = 0)
+ if(damage_type == BRUTE || damage_type == BURN)
+ playsound(src, 'sound/weapons/slash.ogg', 80, TRUE)
+
+/obj/structure/energy_net/Destroy()
+ if(!QDELETED(affecting))
+ affecting.visible_message("[affecting.name] is recovered from the energy net!", "You are recovered from the energy net!", "You hear a grunt.")
+ affecting = null
+ return ..()
+
+/obj/structure/energy_net/attack_paw(mob/user)
+ return attack_hand()
+
+/obj/structure/energy_net/user_buckle_mob(mob/living/M, mob/user, check_loc = TRUE)
+ return//We only want our target to be buckled
+
+/obj/structure/energy_net/user_unbuckle_mob(mob/living/buckled_mob, mob/living/user)
+ return//The net must be destroyed to free the target
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm
new file mode 100644
index 0000000000..0b64f35f0c
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_adrenaline.dm
@@ -0,0 +1,38 @@
+//Wakes the user so they are able to do their thing. Also injects a decent dose of radium.
+//Movement impairing would indicate drugs and the like.
+
+/datum/action/item_action/ninjaboost
+ check_flags = NONE
+ name = "Adrenaline Boost"
+ desc = "Inject a secret chemical that will counteract all movement-impairing effect."
+ button_icon_state = "adrenal"
+ icon_icon = 'icons/obj/implants.dmi'
+ required_mobility_flags = NONE
+
+/**
+ * Proc called to activate space ninja's adrenaline.
+ *
+ * Proc called to use space ninja's adrenaline. Gets the ninja out of almost any stun.
+ * Also makes them shout MGS references when used. After a bit, it injects the user with
+ * radium by calling a different proc.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost()
+ if(ninjacost(0,N_ADRENALINE))
+ return
+ var/mob/living/carbon/human/ninja = affecting
+ ninja.do_adrenaline(150, TRUE, 0, 0, TRUE, list(/datum/reagent/medicine/epinephrine = 3, /datum/reagent/medicine/regen_jelly = 10, /datum/reagent/medicine/stimulants = 2), "You feel a sudden surge of energy!")
+ ninja.say(pick("A CORNERED FOX IS MORE DANGEROUS THAN A JACKAL!","HURT ME MOOORRREEE!","IMPRESSIVE!"), forced = "ninjaboost")
+ a_boost = FALSE
+ to_chat(ninja, "You have used the adrenaline boost.")
+ addtimer(CALLBACK(src, .proc/ninjaboost_after), 70)
+
+/**
+ * Proc called to inject the ninja with radium.
+ *
+ * Used after 7 seconds of using the ninja's adrenaline.
+ * Injects the user with how much radium the suit needs to refill an adrenaline boost.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjaboost_after()
+ var/mob/living/carbon/human/ninja = affecting
+ ninja.reagents.add_reagent(/datum/reagent/radium, a_transfer * 0.25)
+ to_chat(ninja, "You are beginning to feel the after-effect of the injection.")
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_cost_check.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_cost_check.dm
new file mode 100644
index 0000000000..7fb9d9dbf6
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_cost_check.dm
@@ -0,0 +1,28 @@
+/**
+ * Proc called to check if the ninja can afford an ability's cost.
+ *
+ * Proc which determine whether or not a space ninja can afford to use a specific ability.
+ * It can also cancel stealth if the ability requested it.
+ * Arguments:
+ * * cost - the energy cost of the ability
+ * * specificCheck - Determines if the check is a normal one, an adrenaline one, or a stealth cancel check.
+ * * Returns TRUE or the current cooldown timer if we can't perform the ability, and FALSE if we can.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjacost(cost = 0, specificCheck = 0)
+ var/mob/living/carbon/human/ninja = affecting
+ var/actualCost = cost*10
+ if(cost && cell.charge < actualCost)
+ to_chat(ninja, "Not enough energy!")
+ return TRUE
+ else
+ //This shit used to be handled individually on every proc.. why even bother with a universal check proc then?
+ cell.charge-=(actualCost)
+
+ switch(specificCheck)
+ if(N_STEALTH_CANCEL)
+ cancel_stealth()//Get rid of it.
+ if(N_ADRENALINE)
+ if(!a_boost)
+ to_chat(ninja, "You do not have any more adrenaline boosters!")
+ return TRUE
+ return (s_coold)//Returns the value of the variable which counts down to zero.
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_empulse.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_empulse.dm
new file mode 100644
index 0000000000..ea2341a132
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_empulse.dm
@@ -0,0 +1,21 @@
+//Disables nearby tech equipment.
+
+/datum/action/item_action/ninjapulse
+ name = "EM Burst (50E)"
+ desc = "Disable any nearby technology with an electro-magnetic pulse."
+ button_icon_state = "emp"
+ icon_icon = 'icons/mob/actions/actions_spells.dmi'
+
+/**
+ * Proc called to allow the ninja to EMP the nearby area.
+ *
+ * Proc called to allow the ninja to EMP the nearby area. By default, costs 500E, which is half of the default battery's max charge.
+ * Also affects the ninja as well.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjapulse()
+ if(ninjacost(500,N_STEALTH_CANCEL))
+ return
+ var/mob/living/carbon/human/H = affecting
+ playsound(H.loc, 'sound/effects/empulse.ogg', 60, 2)
+ empulse(H, 4, 6) //Procs sure are nice. Slightly weaker than wizard's disable tch.
+ s_coold = 4
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_glove_toggle.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_glove_toggle.dm
new file mode 100644
index 0000000000..ec5bf078a5
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_glove_toggle.dm
@@ -0,0 +1,15 @@
+/datum/action/item_action/toggle_glove
+ name = "Toggle interaction"
+ desc = "Switch between normal interaction and drain mode."
+ button_icon_state = "s-ninjan"
+ icon_icon = 'icons/obj/clothing/gloves.dmi'
+
+/**
+ * Proc called to toggle the ninja glove's special abilities.
+ *
+ * Used to toggle whether or not the ninja glove's abilities will activate on touch.
+ */
+/obj/item/clothing/gloves/space_ninja/proc/toggledrain()
+ var/mob/living/carbon/human/ninja = loc
+ to_chat(ninja, "You [candrain?"disable":"enable"] special interaction.")
+ candrain=!candrain
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_net.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_net.dm
new file mode 100644
index 0000000000..ab52f3e018
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_net.dm
@@ -0,0 +1,36 @@
+/datum/action/item_action/ninjanet
+ name = "Energy Net (40E)"
+ desc = "Captures a fallen opponent in a net of energy."
+ button_icon_state = "energynet"
+ icon_icon = 'icons/effects/effects.dmi'
+
+/**
+ * Proc called to ensnare a person in a energy net.
+ *
+ * Used to ensnare a target in an energy net, preventing them from moving until the net is broken.
+ * Costs 40E, which is 40% of the default battery's max charge. Intended as a means of reliably locking down an opponent when ninja stars won't suffice.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjanet()
+ var/mob/living/carbon/human/ninja = affecting
+ var/mob/living/net_target = input("Select who to capture:","Capture who?",null) as null|mob in sortNames(oview(ninja))
+
+ if(QDELETED(net_target)||!(net_target in oview(ninja)))
+ return
+
+ if(locate(/obj/structure/energy_net) in get_turf(net_target))//Check if they are already being affected by an energy net.
+ to_chat(ninja, "[net_target.p_they(TRUE)] are already trapped inside an energy net!")
+ return
+ for(var/turf/between_turf in getline(get_turf(ninja), get_turf(net_target)))
+ if(between_turf.density)//Don't want them shooting nets through walls. It's kind of cheesy.
+ to_chat(ninja, "You may not use an energy net through solid obstacles!")
+ return
+ if(!ninjacost(400,N_STEALTH_CANCEL))
+ ninja.Beam(net_target, "n_beam", time = 15)
+ ninja.say("Get over here!", forced = "ninja net")
+ var/obj/structure/energy_net/net = new /obj/structure/energy_net(net_target.drop_location())
+ net.affecting = net_target
+ ninja.visible_message("[ninja] caught [net_target] with an energy net!","You caught [net_target] with an energy net!")
+
+ if(net_target.buckled)
+ net_target.buckled.unbuckle_mob(affecting,TRUE)
+ net.buckle_mob(net_target, TRUE) //No moving for you!
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_stars.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_stars.dm
new file mode 100644
index 0000000000..ef897d5bb9
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_stars.dm
@@ -0,0 +1,46 @@
+/datum/action/item_action/ninjastar
+ name = "Create Throwing Stars (1E)"
+ desc = "Creates a throwing star in your hand, if possible."
+ button_icon_state = "throwingstar"
+ icon_icon = 'icons/obj/items_and_weapons.dmi'
+
+/**
+ * Proc called to create a ninja star in the ninja's hands.
+ *
+ * Called to create a ninja star in the wearer's hand. The ninja
+ * star doesn't do much up-front damage, but deals stamina damage
+ * as the target moves around, forcing a finish or flee scenario.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjastar()
+ if(ninjacost(10))
+ return
+ var/mob/living/carbon/human/ninja = affecting
+ var/obj/item/throwing_star/stamina/ninja/ninja_star = new(ninja)
+ if(ninja.put_in_hands(ninja_star))
+ to_chat(ninja, "A throwing star has been created in your hand!")
+ else
+ qdel(ninja_star)
+ to_chat(ninja, "You can't create a throwing star, your hands are full!")
+ ninja.throw_mode_on() //So they can quickly throw it.
+
+/**
+ * # Ninja Throwing Star
+ *
+ * a throwing star which specifically makes sure you know it came from a real ninja.
+ *
+ * The most important item in the entire codebase, as without it we would all cease to exist.
+ * Inherits everything that makes it interesting the stamina throwing star, but the most
+ * important change made is that its name specifically has the prefix, 'ninja' in it.
+ * This provides the detective role with information to play off of by ensuring that his
+ * assumption that a space ninja is aboard the ship to be true when he find 20 of these in
+ * the captain's back. Along with this, its throwforce is 10 instead of the 5 of the stamina
+ * throwing star, meaning it'll do a little more damage than the stamina throwing star does as well.
+ * Changes to this item need to be approved by all maintainers, so if you do change it, make sure
+ * you go through the proper channels, lest you get permabanned. Do I make myself clear?
+ */
+/obj/item/throwing_star/stamina/ninja
+ name = "ninja throwing star"
+ icon_state = "ninjastar"
+ item_state = "ninjastar"
+ throwforce = 10
+ embedding = list("pain_chance" = 5, "embed_chance" = 100, "fall_chance" = 10, "jostle_chance" = 10, "pain_stam_pct" = 2.5, "jostle_pain_mult" = 3)
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_status_read.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_status_read.dm
new file mode 100644
index 0000000000..0bedd31c72
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_status_read.dm
@@ -0,0 +1,39 @@
+/datum/action/item_action/ninjastatus
+ check_flags = NONE
+ name = "Status Readout"
+ desc = "Gives a detailed readout about your current status."
+ button_icon_state = "health"
+ icon_icon = 'icons/obj/device.dmi'
+ var/action_background_icon_state = "bg_default_on"
+
+/**
+ * Proc called to put a status readout to the ninja in chat.
+ *
+ * Called put some information about the ninja's current status into chat.
+ * This information used to be displayed constantly on the status tab screen
+ * when the suit was on, but was turned into this as to remove the code from
+ * human.dm
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninjastatus()
+ var/mob/living/carbon/human/ninja = affecting
+ var/list/info_list = list()
+ info_list += "SpiderOS Status: [s_initialized ? "Initialized" : "Disabled"]\n"
+ //Ninja status
+ info_list += "Fingerprints: [md5(ninja.dna.uni_identity)]\n"
+ info_list += "Unique Identity: [ninja.dna.unique_enzymes]\n"
+ info_list += "Overall Status: [ninja.stat > 1 ? "dead" : "[ninja.health]% healthy"]\n"
+ info_list += "Nutrition Status: [ninja.nutrition]\n"
+ info_list += "Oxygen Loss: [ninja.getOxyLoss()]\n"
+ info_list += "Toxin Levels: [ninja.getToxLoss()]\n"
+ info_list += "Burn Severity: [ninja.getFireLoss()]\n"
+ info_list += "Brute Trauma: [ninja.getBruteLoss()]\n"
+ info_list += "Radiation Levels: [ninja.radiation] rad\n"
+ info_list += "Body Temperature: [ninja.bodytemperature-T0C] degrees C ([ninja.bodytemperature*1.8-459.67] degrees F)\n"
+
+ //Diseases
+ if(length(ninja.diseases))
+ info_list += "Viruses:"
+ for(var/datum/disease/ninja_disease in ninja.diseases)
+ info_list += "* [ninja_disease.name], Type: [ninja_disease.spread_text], Stage: [ninja_disease.stage]/[ninja_disease.max_stages], Possible Cure: [ninja_disease.cure_text]\n"
+
+ to_chat(ninja, "[info_list.Join()]")
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_stealth.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_stealth.dm
new file mode 100644
index 0000000000..079f0c53e9
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_stealth.dm
@@ -0,0 +1,46 @@
+/datum/action/item_action/ninja_stealth
+ name = "Toggle Stealth"
+ desc = "Toggles stealth mode on and off."
+ button_icon_state = "ninja_cloak"
+ icon_icon = 'icons/mob/actions/actions_minor_antag.dmi'
+
+/**
+ * Proc called to toggle ninja stealth.
+ *
+ * Proc called to toggle whether or not the ninja is in stealth mode.
+ * If cancelling, calls a separate proc in case something else needs to quickly cancel stealth.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/toggle_stealth()
+ var/mob/living/carbon/human/ninja = affecting
+ if(!ninja)
+ return
+ if(stealth)
+ cancel_stealth()
+ else
+ if(cell.charge <= 0)
+ to_chat(ninja, "You don't have enough power to enable Stealth!")
+ return
+ stealth = !stealth
+ animate(ninja, alpha = 20,time = 12)
+ ninja.visible_message("[ninja.name] vanishes into thin air!", \
+ "You are now mostly invisible to normal detection.")
+
+/**
+ * Proc called to cancel stealth.
+ *
+ * Called to cancel the stealth effect if it is ongoing.
+ * Does nothing otherwise.
+ * Arguments:
+ * * Returns false if either the ninja no longer exists or is already visible, returns true if we successfully made the ninja visible.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/cancel_stealth()
+ var/mob/living/carbon/human/ninja = affecting
+ if(!ninja)
+ return FALSE
+ if(stealth)
+ stealth = !stealth
+ animate(ninja, alpha = 255, time = 12)
+ ninja.visible_message("[ninja.name] appears from thin air!", \
+ "You are now visible.")
+ return TRUE
+ return FALSE
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_suit_initialisation.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_suit_initialisation.dm
new file mode 100644
index 0000000000..c1a3c77814
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_suit_initialisation.dm
@@ -0,0 +1,123 @@
+#define NINJA_LOCK_PHASE 1
+#define NINJA_ICON_GENERATE_PHASE 3
+#define NINJA_COMPLETE_PHASE 6
+#define NINJA_DEINIT_LOGOFF_PHASE 1
+#define NINJA_DEINIT_STEALTH_PHASE 5
+
+GLOBAL_LIST_INIT(ninja_initialize_messages, list(
+ "Now initializing...",
+ "Securing external locking mechanism...\nNeural-net established.",
+ "Extending neural-net interface...\nNow monitoring brain wave pattern...",
+ "Linking neural-net interface...\nPattern GREEN, continuing operation.",
+ "VOID-shift device status: ONLINE.\nCLOAK-tech device status: ONLINE.",
+ "Primary system status: ONLINE.\nBackup system status: ONLINE.\nCurrent energy capacity: ",
+ "All systems operational. Welcome to SpiderOS, "
+))
+
+GLOBAL_LIST_INIT(ninja_deinitialize_messages, list(
+ "Now de-initializing...",
+ "Shutting down SpiderOS.",
+ "Primary system status: OFFLINE.\nBackup system status: OFFLINE.",
+ "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.",
+ "Disconnecting neural-net interface...Success.",
+ "Disengaging neural-net interface... Success.",
+ "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED."
+))
+
+/datum/action/item_action/initialize_ninja_suit
+ name = "Toggle Ninja Suit"
+
+/**
+ * Toggles the ninja suit on/off
+ *
+ * Attempts to initialize or deinitialize the ninja suit
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/toggle_on_off()
+ . = TRUE
+ if(s_busy)
+ to_chat(loc, "ERROR: You cannot use this function at this time.")
+ return FALSE
+ s_busy = TRUE
+ if(s_initialized)
+ deinitialize()
+ else
+ ninitialize()
+
+/**
+ * Initializes the ninja suit
+ *
+ * Initializes the ninja suit through seven phases, each of which calls this proc with an incremented phase
+ * Arguments:
+ * * delay - The delay between each phase of initialization
+ * * ninja - The human who is being affected by the suit
+ * * phase - The phase of initialization
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninitialize(delay = s_delay, mob/living/carbon/human/ninja = loc, phase = 0)
+ if(!ninja || !ninja.mind)
+ s_busy = FALSE
+ return
+ if (phase > NINJA_LOCK_PHASE && (ninja.stat == DEAD || ninja.health <= 0))
+ to_chat(ninja, "FÄAL �Rr�R: 344--93#�&&21 BR��N |/|/aV� PATT$RN RED\nA-A-aB�rT�NG...")
+ unlock_suit(ninja)
+ s_busy = FALSE
+ return
+
+ var/message = GLOB.ninja_initialize_messages[phase + 1]
+ switch(phase)
+ if (NINJA_LOCK_PHASE)
+ if(!lock_suit(ninja))//To lock the suit onto wearer.
+ s_busy = FALSE
+ return
+ if (NINJA_COMPLETE_PHASE - 1)
+ message += "[DisplayEnergy(cell.charge)]."
+ if (NINJA_COMPLETE_PHASE)
+ message += "[ninja.real_name]."
+ ninja.regenerate_icons()
+ s_initialized = TRUE
+ s_busy = FALSE
+
+ to_chat(ninja, "[message]")
+ playsound(ninja, 'sound/effects/sparks1.ogg', 10, TRUE)
+
+ if (phase < NINJA_COMPLETE_PHASE)
+ addtimer(CALLBACK(src, .proc/ninitialize, delay, ninja, phase + 1), delay)
+
+/**
+ * Deinitializes the ninja suit
+ *
+ * Deinitializes the ninja suit through eight phases, each of which calls this proc with an incremented phase
+ * Arguments:
+ * * delay - The delay between each phase of deinitialization
+ * * ninja - The human who is being affected by the suit
+ * * phase - The phase of deinitialization
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/deinitialize(delay = s_delay, mob/living/carbon/human/ninja = affecting == loc ? affecting : null, phase = 0)
+ if (!ninja || !ninja.mind)
+ s_busy = FALSE
+ return
+ if (phase == 0 && alert("Are you certain you wish to remove the suit? This will take time and remove all abilities.",,"Yes","No") == "No")
+ s_busy = FALSE
+ return
+
+ var/message = GLOB.ninja_deinitialize_messages[phase + 1]
+ switch(phase)
+ if(NINJA_DEINIT_LOGOFF_PHASE)
+ message = "Logging off, [ninja.real_name]. " + message
+ if(NINJA_DEINIT_STEALTH_PHASE)
+ cancel_stealth()
+ to_chat(ninja, "[message]")
+ playsound(ninja, 'sound/items/deconstruct.ogg', 10, TRUE)
+
+ if (phase < NINJA_COMPLETE_PHASE)
+ addtimer(CALLBACK(src, .proc/deinitialize, delay, ninja, phase + 1), delay)
+ else
+ unlock_suit(ninja)
+ ninja.regenerate_icons()
+ s_initialized = FALSE
+ s_busy = FALSE
+
+#undef NINJA_LOCK_PHASE
+#undef NINJA_ICON_GENERATE_PHASE
+#undef NINJA_COMPLETE_PHASE
+#undef NINJA_DEINIT_LOGOFF_PHASE
+#undef NINJA_DEINIT_STEALTH_PHASE
diff --git a/code/modules/ninja/suit/ninja_equipment_actions/ninja_sword_recall.dm b/code/modules/ninja/suit/ninja_equipment_actions/ninja_sword_recall.dm
new file mode 100644
index 0000000000..dd6ca61f88
--- /dev/null
+++ b/code/modules/ninja/suit/ninja_equipment_actions/ninja_sword_recall.dm
@@ -0,0 +1,48 @@
+/datum/action/item_action/ninja_sword_recall
+ name = "Recall Energy Katana (Variable Cost)"
+ desc = "Teleports the Energy Katana linked to this suit to its wearer, cost based on distance."
+ button_icon_state = "energy_katana"
+ icon_icon = 'icons/obj/items_and_weapons.dmi'
+
+/**
+ * Proc called to recall the ninja's sword.
+ *
+ * Called to summon the ninja's katana back to them
+ * If the katana can see the ninja, it will throw itself towards them.
+ * If not, the katana will teleport itself to the ninja.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/ninja_sword_recall()
+ var/mob/living/carbon/human/ninja = affecting
+ var/cost = 0
+ var/inview = TRUE
+
+ if(!energyKatana)
+ to_chat(ninja, "Could not locate Energy Katana!")
+ return
+
+ if(energyKatana in ninja)
+ return
+
+ var/distance = get_dist(ninja,energyKatana)
+
+ if(!(energyKatana in view(ninja)))
+ cost = distance //Actual cost is cost x 10, so 5 turfs is 50 cost.
+ inview = FALSE
+
+ if(!ninjacost(cost))
+ if(iscarbon(energyKatana.loc))
+ var/mob/living/carbon/sword_holder = energyKatana.loc
+ sword_holder.transferItemToLoc(energyKatana, get_turf(energyKatana), TRUE)
+
+ else
+ energyKatana.forceMove(get_turf(energyKatana))
+
+ if(inview) //If we can see the katana, throw it towards ourselves, damaging people as we go.
+ energyKatana.spark_system.start()
+ playsound(ninja, "sparks", 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ ninja.visible_message("\the [energyKatana] flies towards [ninja]!","You hold out your hand and \the [energyKatana] flies towards you!")
+ energyKatana.throw_at(ninja, distance+1, energyKatana.throw_speed, ninja)
+
+ else //Else just TP it to us.
+ energyKatana.returnToOwner(ninja, 1)
+ s_coold = 4
diff --git a/code/modules/ninja/suit/shoes.dm b/code/modules/ninja/suit/shoes.dm
index f2227b5f9f..6fba60a6ef 100644
--- a/code/modules/ninja/suit/shoes.dm
+++ b/code/modules/ninja/suit/shoes.dm
@@ -1,3 +1,12 @@
+/**
+ * # Ninja Shoes
+ *
+ * Space ninja's shoes. Gives him armor on his feet.
+ *
+ * Space ninja's ninja shoes. How mousey. Gives him slip protection and protection against attacks.
+ * Also are temperature resistant.
+ *
+ */
/obj/item/clothing/shoes/space_ninja
name = "ninja shoes"
desc = "A pair of running shoes. Excellent for running and even better for smashing skulls."
@@ -5,8 +14,7 @@
permeability_coefficient = 0.01
clothing_flags = NOSLIP
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
- armor = list("melee" = 60, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 100, "acid" = 100)
- strip_delay = 120
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 20,"energy" = 30, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 100, "acid" = 100)
cold_protection = FEET
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET
diff --git a/code/modules/ninja/suit/suit.dm b/code/modules/ninja/suit/suit.dm
index 15071a186f..66d9a85290 100644
--- a/code/modules/ninja/suit/suit.dm
+++ b/code/modules/ninja/suit/suit.dm
@@ -1,16 +1,13 @@
-
-/*
-
-Contents:
-- The Ninja Space Suit
-- Ninja Space Suit Procs
-
-*/
-
-
-// /obj/item/clothing/suit/space/space_ninja
-
-
+/**
+ * # Ninja Suit
+ *
+ * Space ninja's suit. Provides him with most of his powers.
+ *
+ * Space ninja's suit. Gives space ninja all his iconic powers, which are mostly kept in
+ * the folder ninja_equipment_actions. Has a lot of unique stuff going on, so make sure to check
+ * the variables. Check suit_attackby to see radium interaction, disk copying, and cell replacement.
+ *
+ */
/obj/item/clothing/suit/space/space_ninja
name = "ninja suit"
desc = "A unique, vacuum-proof suit of nano-enhanced armor designed specifically for Spider Clan assassins."
@@ -19,12 +16,11 @@ Contents:
allowed = list(/obj/item/gun, /obj/item/ammo_box, /obj/item/ammo_casing, /obj/item/melee/baton, /obj/item/restraints/handcuffs, /obj/item/tank/internals, /obj/item/stock_parts/cell)
slowdown = 1
resistance_flags = LAVA_PROOF | ACID_PROOF
- armor = list("melee" = 60, "bullet" = 50, "laser" = 30,"energy" = 15, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 100, "acid" = 100)
- strip_delay = 12
+ armor = list("melee" = 40, "bullet" = 30, "laser" = 20,"energy" = 30, "bomb" = 30, "bio" = 30, "rad" = 30, "fire" = 100, "acid" = 100)
- actions_types = list(/datum/action/item_action/initialize_ninja_suit, /datum/action/item_action/ninjasmoke, /datum/action/item_action/ninjaboost, /datum/action/item_action/ninjapulse, /datum/action/item_action/ninjastar, /datum/action/item_action/ninja_sword_recall, /datum/action/item_action/ninja_stealth, /datum/action/item_action/toggle_glove)
+ actions_types = list(/datum/action/item_action/initialize_ninja_suit, /datum/action/item_action/ninjastatus, /datum/action/item_action/ninjaboost, /datum/action/item_action/ninjapulse, /datum/action/item_action/ninjastar, /datum/action/item_action/ninjanet, /datum/action/item_action/ninja_sword_recall, /datum/action/item_action/ninja_stealth)
- //Important parts of the suit.
+ ///The person wearing the suit
var/mob/living/carbon/human/affecting = null
var/obj/item/stock_parts/cell/cell
var/datum/effect_system/spark_spread/spark_system
@@ -37,28 +33,38 @@ Contents:
var/obj/item/clothing/shoes/space_ninja/n_shoes
var/obj/item/clothing/gloves/space_ninja/n_gloves
- //Main function variables.
- var/s_initialized = 0//Suit starts off.
- var/s_coold = 0//If the suit is on cooldown. Can be used to attach different cooldowns to abilities. Ticks down every second based on suit ntick().
- var/s_cost = 5//Base energy cost each ntick.
- var/s_acost = 25//Additional cost for additional powers active.
- var/s_delay = 40//How fast the suit does certain things, lower is faster. Can be overridden in specific procs. Also determines adverse probability.
- var/a_transfer = 20//How much radium is used per adrenaline boost.
- var/a_maxamount = 7//Maximum number of adrenaline boosts.
- var/s_maxamount = 20//Maximum number of smoke bombs.
+ ///The space ninja's mask.
+ var/obj/item/clothing/mask/gas/space_ninja/n_mask
- //Support function variables.
+ ///Whether or not the suit is currently booted up. Starts off.
+ var/s_initialized = FALSE//Suit starts off.
+ ///The suit's current cooldown. If not 0, blocks usage of most abilities, and decrements its value by 1 every process
+ var/s_coold = 0
+ ///How much energy the suit expends in a single process
+ var/s_cost = 1
+ ///Additional energy cost for cloaking per process
+ var/s_acost = 20
+ ///How fast the suit is at certain actions, like draining power from things
+ var/s_delay = 40
+ ///How fast the suit is at certain riskier actions, like hacking communications consoles.
+ var/s_longdelay = 200
+ ///Units of radium required to refill the adrenaline boost
+ var/a_transfer = 20//How much radium is required to refill the adrenaline boost.
+ ///Whether or not the suit is currently in stealth mode.
var/stealth = FALSE//Stealth off.
- var/stealth_cooldown = 0
- var/s_busy = FALSE//Is the suit busy with a process? Like AI hacking. Used for safety functions.
-
- //Ability function variables.
- var/s_bombs = 10//Number of smoke bombs.
- var/a_boost = 3//Number of adrenaline boosters.
-
-
-/obj/item/clothing/suit/space/space_ninja/get_cell()
- return cell
+ ///Whether or not the wearer is in the middle of an action, like hacking.
+ var/s_busy = FALSE
+ ///Whether or not the adrenaline boost ability is available
+ var/a_boost = TRUE
+/obj/item/clothing/suit/space/space_ninja/examine(mob/user)
+ . = ..()
+ if(!s_initialized)
+ return
+ if(!user == affecting)
+ return
+ . += "All systems operational. Current energy capacity: [DisplayEnergy(cell.charge)].\n"+\
+ "The CLOAK-tech device is [stealth?"active":"inactive"].\n"+\
+ "[a_boost?"An adrenaline boost is available to use.":"There is no adrenaline boost available. Try refilling the suit with 20 units of radium."]"
/obj/item/clothing/suit/space/space_ninja/Initialize()
. = ..()
@@ -77,114 +83,157 @@ Contents:
cell.name = "black power cell"
cell.icon_state = "bscell"
-/obj/item/clothing/suit/space/space_ninja/Destroy()
- if(affecting)
- unlock_suit()
- return ..()
+/obj/item/clothing/suit/space/space_ninja/Initialize(mapload)
+ START_PROCESSING(SSobj, src)
+ return ..()
-//Simply deletes all the attachments and self, killing all related procs.
+/obj/item/clothing/suit/space/space_ninja/Destroy()
+ STOP_PROCESSING(SSobj, src)
+ return ..()
+
+// Power usage
+/obj/item/clothing/suit/space/space_ninja/process(delta_time)
+ var/mob/living/carbon/human/user = src.loc
+ if(!user || !ishuman(user) || !(user.wear_suit == src))
+ return
+
+ // Check for energy usage
+ if(s_initialized)
+ if(!affecting)
+ terminate() // Kills the suit and attached objects.
+ else if(cell.charge > 0)
+ if(s_coold > 0)
+ s_coold = max(s_coold - delta_time, 0) // Checks for ability s_cooldown first.
+ cell.charge -= s_cost * delta_time // s_cost is the default energy cost each ntick, usually 5.
+ if(stealth) // If stealth is active.
+ cell.charge -= s_acost * delta_time
+ else
+ cell.charge = 0
+ cancel_stealth()
+
+/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/user, action)
+ if(IS_NINJA_SUIT_INITIALIZATION(action))
+ toggle_on_off()
+ return TRUE
+ if(!s_initialized)
+ to_chat(user, "ERROR: suit offline. Please activate suit.")
+ return FALSE
+ if(s_coold > 0)
+ to_chat(user, "ERROR: suit is on cooldown.")
+ return FALSE
+ if(IS_NINJA_SUIT_STATUS(action))
+ ninjastatus()
+ return TRUE
+ if(IS_NINJA_SUIT_BOOST(action))
+ ninjaboost()
+ return TRUE
+ if(IS_NINJA_SUIT_EMP(action))
+ ninjapulse()
+ return TRUE
+ if(IS_NINJA_SUIT_STAR_CREATION(action))
+ ninjastar()
+ return TRUE
+ if(IS_NINJA_SUIT_NET_CREATION(action))
+ ninjanet()
+ return TRUE
+ if(IS_NINJA_SUIT_SWORD_RECALL(action))
+ ninja_sword_recall()
+ return TRUE
+ if(IS_NINJA_SUIT_STEALTH(action))
+ toggle_stealth()
+ return TRUE
+ return FALSE
+
+/obj/item/clothing/suit/space/space_ninja/run_block(mob/living/carbon/human/owner, atom/movable/hitby, attack_text = "the attack", final_block_chance = 0, damage = 0, attack_type = ATTACK_TYPE_MELEE)
+ . = ..()
+ if(stealth)
+ cancel_stealth()
+ s_coold = 5
+
+/**
+ * Proc called to lock the important gear pieces onto space ninja's body.
+ *
+ * Called during the suit startup to lock all gear pieces onto space ninja.
+ * Terminates if a gear piece is not being worn. Also gives the ninja the inability to use firearms.
+ * If the person in the suit isn't a ninja when this is called, this proc just gibs them instead.
+ * Arguments:
+ * * ninja - The person wearing the suit.
+ * * Returns false if the locking fails due to lack of all suit parts, and true if it succeeds.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/lock_suit(mob/living/carbon/human/ninja)
+ if(!istype(ninja))
+ return FALSE
+ if(!IS_SPACE_NINJA(ninja))
+ to_chat(ninja, "fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAUHORIZED USÈ DETÈCeD\nCoMMÈNCING SUB-R0UIN3 13...\nTÈRMInATING U-U-USÈR...")
+ ninja.gib()
+ return FALSE
+ if(!istype(ninja.head, /obj/item/clothing/head/helmet/space/space_ninja))
+ to_chat(ninja, "ERROR: 100113 UNABLE TO LOCATE HEAD GEAR\nABORTING...")
+ return FALSE
+ if(!istype(ninja.shoes, /obj/item/clothing/shoes/space_ninja))
+ to_chat(ninja, "ERROR: 122011 UNABLE TO LOCATE FOOT GEAR\nABORTING...")
+ return FALSE
+ if(!istype(ninja.gloves, /obj/item/clothing/gloves/space_ninja))
+ to_chat(ninja, "ERROR: 110223 UNABLE TO LOCATE HAND GEAR\nABORTING...")
+ return FALSE
+ affecting = ninja
+ ADD_TRAIT(src, TRAIT_NODROP, NINJA_SUIT_TRAIT) //colons make me go all |=
+ slowdown = 0
+ icon_state = "s-ninjan"
+ n_hood = ninja.head
+ ADD_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_shoes = ninja.shoes
+ ADD_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_shoes.slowdown--
+ n_gloves = ninja.gloves
+ ADD_TRAIT(n_gloves, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_gloves.icon_state = "s-ninjan"
+ n_gloves.item_state = "s-ninjan"
+ n_mask = ninja.wear_mask
+ n_mask.icon_state = "s-ninjan"
+ n_mask.item_state = "s-ninjan"
+
+ ADD_TRAIT(ninja, TRAIT_NOGUNS, NINJA_SUIT_TRAIT)
+ return TRUE
+
+/**
+ * Proc called to unlock all the gear off space ninja's body.
+ *
+ * Proc which is essentially the opposite of lock_suit. Lets you take off all the suit parts.
+ * Also gets rid of the objection to using firearms from the wearer.
+ * Arguments:
+ * * ninja - The person wearing the suit.
+ */
+/obj/item/clothing/suit/space/space_ninja/proc/unlock_suit(mob/living/carbon/human/ninja)
+ affecting = null
+ REMOVE_TRAIT(src, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ slowdown = 1
+ icon_state = "s-ninja"
+ if(n_hood)//Should be attached, might not be attached.
+ REMOVE_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_hood.icon_state = "s-ninja"
+ if(n_shoes)
+ REMOVE_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_shoes.slowdown++
+ if(n_gloves)
+ n_gloves.icon_state = "s-ninja"
+ n_gloves.item_state = "s-ninja"
+ REMOVE_TRAIT(n_gloves, TRAIT_NODROP, NINJA_SUIT_TRAIT)
+ n_gloves.candrain = FALSE
+ n_gloves.draining = FALSE
+ if(n_mask)
+ n_mask.icon_state = "s-ninja"
+ n_mask.item_state = "s-ninja"
+
+ REMOVE_TRAIT(ninja, TRAIT_NOGUNS, NINJA_SUIT_TRAIT)
+
+/**
+ * Proc used to delete all the attachments and itself.
+ *
+ * Can be called to entire rid of the suit pieces and the suit itself.
+ */
/obj/item/clothing/suit/space/space_ninja/proc/terminate()
qdel(n_hood)
qdel(n_gloves)
qdel(n_shoes)
qdel(src)
-
-//Randomizes suit parameters.
-/obj/item/clothing/suit/space/space_ninja/proc/randomize_param()
- s_cost = rand(1,20)
- s_acost = rand(20,100)
- s_delay = rand(10,100)
- s_bombs = rand(5,20)
- a_boost = rand(1,7)
-
-//This proc prevents the suit from being taken off.
-/obj/item/clothing/suit/space/space_ninja/proc/lock_suit(mob/living/carbon/human/H)
- if(!istype(H))
- return FALSE
- if(!is_ninja(H))
- to_chat(H, "fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAUHORIZED USÈ DETÈCeD\nCoMMÈNCING SUB-R0UIN3 13...\nTÈRMInATING U-U-USÈR...")
- H.gib()
- return FALSE
- if(!istype(H.head, /obj/item/clothing/head/helmet/space/space_ninja))
- to_chat(H, "ERROR: 100113 UNABLE TO LOCATE HEAD GEAR\nABORTING...")
- return FALSE
- if(!istype(H.shoes, /obj/item/clothing/shoes/space_ninja))
- to_chat(H, "ERROR: 122011 UNABLE TO LOCATE FOOT GEAR\nABORTING...")
- return FALSE
- if(!istype(H.gloves, /obj/item/clothing/gloves/space_ninja))
- to_chat(H, "ERROR: 110223 UNABLE TO LOCATE HAND GEAR\nABORTING...")
- return FALSE
- affecting = H
- ADD_TRAIT(src, TRAIT_NODROP, NINJA_SUIT_TRAIT) //colons make me go all |=
- n_hood = H.head
- ADD_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- n_shoes = H.shoes
- ADD_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- n_shoes.slowdown = -0.25
- n_gloves = H.gloves
- ADD_TRAIT(n_gloves, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- ADD_TRAIT(affecting, TRAIT_TASED_RESISTANCE, NINJA_SUIT_TRAIT)
- return TRUE
-
-/obj/item/clothing/suit/space/space_ninja/proc/lockIcons(mob/living/carbon/human/H)
- icon_state = H.dna.features["body_model"] == FEMALE ? "s-ninjanf" : "s-ninjan"
- H.gloves.icon_state = "s-ninjan"
- H.gloves.item_state = "s-ninjan"
-
-
-//This proc allows the suit to be taken off.
-/obj/item/clothing/suit/space/space_ninja/proc/unlock_suit()
- affecting = null
- REMOVE_TRAIT(src, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- icon_state = "s-ninja"
- if(n_hood)//Should be attached, might not be attached.
- REMOVE_TRAIT(n_hood, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- if(n_shoes)
- REMOVE_TRAIT(n_shoes, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- n_shoes.slowdown = 0.25
- if(n_gloves)
- n_gloves.icon_state = "s-ninja"
- n_gloves.item_state = "s-ninja"
- REMOVE_TRAIT(n_gloves, TRAIT_NODROP, NINJA_SUIT_TRAIT)
- n_gloves.candrain=0
- n_gloves.draining=0
- REMOVE_TRAIT(affecting, TRAIT_TASED_RESISTANCE, NINJA_SUIT_TRAIT)
-
-
-/obj/item/clothing/suit/space/space_ninja/examine(mob/user)
- . = ..()
- if(s_initialized && user == affecting)
- . += "All systems operational. Current energy capacity: [DisplayEnergy(cell.charge)].\n\
- The CLOAK-tech device is [stealth?"active":"inactive"].\n\
- There are [s_bombs] smoke bomb\s remaining.\n\
- There are [a_boost] adrenaline booster\s remaining."
-
-/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/user, action)
- if(istype(action, /datum/action/item_action/initialize_ninja_suit))
- toggle_on_off()
- return TRUE
- if(!s_initialized)
- to_chat(user, "ERROR: suit offline. Please activate suit.")
- return FALSE
- if(istype(action, /datum/action/item_action/ninjasmoke))
- ninjasmoke()
- return TRUE
- if(istype(action, /datum/action/item_action/ninjaboost))
- ninjaboost()
- return TRUE
- if(istype(action, /datum/action/item_action/ninjapulse))
- ninjapulse()
- return TRUE
- if(istype(action, /datum/action/item_action/ninjastar))
- ninjastar()
- return TRUE
- if(istype(action, /datum/action/item_action/ninja_sword_recall))
- ninja_sword_recall()
- return TRUE
- if(istype(action, /datum/action/item_action/ninja_stealth))
- stealth()
- return TRUE
- if(istype(action, /datum/action/item_action/toggle_glove))
- n_gloves.toggledrain()
- return TRUE
- return FALSE
diff --git a/code/modules/ninja/suit/suit_attackby.dm b/code/modules/ninja/suit/suit_attackby.dm
index ed920a57d1..75d438a35f 100644
--- a/code/modules/ninja/suit/suit_attackby.dm
+++ b/code/modules/ninja/suit/suit_attackby.dm
@@ -1,52 +1,44 @@
-
-
-/obj/item/clothing/suit/space/space_ninja/attackby(obj/item/I, mob/U, params)
- if(U!=affecting)//Safety, in case you try doing this without wearing the suit/being the person with the suit.
+/obj/item/clothing/suit/space/space_ninja/attackby(obj/item/I, mob/ninja, params)
+ if(ninja!=affecting)//Safety, in case you try doing this without wearing the suit/being the person with the suit.
return ..()
- if(istype(I, /obj/item/reagent_containers/glass))//If it's a glass beaker.
- if(I.reagents.has_reagent(/datum/reagent/radium, a_transfer) && a_boost < a_maxamount)
- I.reagents.remove_reagent(/datum/reagent/radium, a_transfer)
- a_boost++;
- to_chat(U, "There are now [a_boost] adrenaline boosts remaining.")
- return
- if(I.reagents.has_reagent(/datum/reagent/smoke_powder, a_transfer) && s_bombs < s_maxamount)
- I.reagents.remove_reagent(/datum/reagent/smoke_powder, a_transfer)
- s_bombs++;
- to_chat(U, "There are now [s_bombs] smoke bombs remaining.")
- return
+ if(istype(I, /obj/item/reagent_containers/glass) && I.reagents.has_reagent(/datum/reagent/radium, a_transfer) && a_boost != TRUE)//If it's a glass beaker, and what we're transferring is radium.
+ I.reagents.remove_reagent(/datum/reagent/radium, a_transfer)
+ a_boost = TRUE;
+ to_chat(ninja, "The suit's adrenaline boost is now reloaded.")
+ return
else if(istype(I, /obj/item/stock_parts/cell))
var/obj/item/stock_parts/cell/CELL = I
if(CELL.maxcharge > cell.maxcharge && n_gloves && n_gloves.candrain)
- to_chat(U, "Higher maximum capacity detected.\nUpgrading...")
- if (n_gloves && n_gloves.candrain && do_after(U,s_delay, target = src))
- U.transferItemToLoc(CELL, src)
+ to_chat(ninja, "Higher maximum capacity detected.\nUpgrading...")
+ if (n_gloves?.candrain && do_after(ninja,s_delay, target = src))
+ ninja.transferItemToLoc(CELL, src)
CELL.charge = min(CELL.charge+cell.charge, CELL.maxcharge)
var/obj/item/stock_parts/cell/old_cell = cell
old_cell.charge = 0
- U.put_in_hands(old_cell)
- old_cell.add_fingerprint(U)
+ ninja.put_in_hands(old_cell)
+ old_cell.add_fingerprint(ninja)
old_cell.corrupt()
old_cell.update_icon()
cell = CELL
- to_chat(U, "Upgrade complete. Maximum capacity: [round(cell.maxcharge/100)]%")
+ to_chat(ninja, "Upgrade complete. Maximum capacity: [round(cell.maxcharge/100)]%")
else
- to_chat(U, "Procedure interrupted. Protocol terminated.")
+ to_chat(ninja, "Procedure interrupted. Protocol terminated.")
return
else if(istype(I, /obj/item/disk/tech_disk))//If it's a data disk, we want to copy the research on to the suit.
var/obj/item/disk/tech_disk/TD = I
var/has_research = 0
if(has_research)//If it has something on it.
- to_chat(U, "Research information detected, processing...")
- if(do_after(U,s_delay, target = src))
+ to_chat(ninja, "Research information detected, processing...")
+ if(do_after(ninja,s_delay, target = src))
TD.stored_research.copy_research_to(stored_research)
- to_chat(U, "Data analyzed and updated. Disk erased.")
+ to_chat(ninja, "Data analyzed and updated. Disk erased.")
else
- to_chat(U, "ERROR: Procedure interrupted. Process terminated.")
+ to_chat(ninja, "ERROR: Procedure interrupted. Process terminated.")
else
- to_chat(U, "No research information detected.")
+ to_chat(ninja, "No research information detected.")
return
return ..()
diff --git a/code/modules/ninja/suit/suit_initialisation.dm b/code/modules/ninja/suit/suit_initialisation.dm
deleted file mode 100644
index 3d80282fe7..0000000000
--- a/code/modules/ninja/suit/suit_initialisation.dm
+++ /dev/null
@@ -1,95 +0,0 @@
-/obj/item/clothing/suit/space/space_ninja/proc/toggle_on_off()
- if(s_busy)
- to_chat(loc, "ERROR: You cannot use this function at this time.")
- return FALSE
- if(s_initialized)
- deinitialize()
- else
- ninitialize()
- . = TRUE
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize(delay = s_delay, mob/living/carbon/human/U = loc)
- if(!U.mind)
- return //Not sure how this could happen.
- s_busy = TRUE
- to_chat(U, "Now initializing...")
- addtimer(CALLBACK(src, .proc/ninitialize_two, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_two(delay, mob/living/carbon/human/U)
- if(!lock_suit(U))//To lock the suit onto wearer.
- s_busy = FALSE
- return
- to_chat(U, "Securing external locking mechanism...\nNeural-net established.")
- addtimer(CALLBACK(src, .proc/ninitialize_three, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_three(delay, mob/living/carbon/human/U)
- to_chat(U, "Extending neural-net interface...\nNow monitoring brain wave pattern...")
- addtimer(CALLBACK(src, .proc/ninitialize_four, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_four(delay, mob/living/carbon/human/U)
- if(U.stat == DEAD|| U.health <= 0)
- to_chat(U, "FÄAL �Rr�R: 344--93#�&&21 BR��N |/|/aV� PATT$RN RED\nA-A-aB�rT�NG...")
- unlock_suit()
- s_busy = FALSE
- return
- lockIcons(U)//Check for icons.
- U.regenerate_icons()
- to_chat(U, "Linking neural-net interface...\nPattern\green GREEN, continuing operation.")
- addtimer(CALLBACK(src, .proc/ninitialize_five, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_five(delay, mob/living/carbon/human/U)
- to_chat(U, "VOID-shift device status: ONLINE.\nCLOAK-tech device status: ONLINE.")
- addtimer(CALLBACK(src, .proc/ninitialize_six, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_six(delay, mob/living/carbon/human/U)
- to_chat(U, "Primary system status: ONLINE.\nBackup system status: ONLINE.\nCurrent energy capacity: [DisplayEnergy(cell.charge)].")
- addtimer(CALLBACK(src, .proc/ninitialize_seven, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/ninitialize_seven(delay, mob/living/carbon/human/U)
- to_chat(U, "All systems operational. Welcome to SpiderOS, [U.real_name].")
- s_initialized = TRUE
- START_PROCESSING(SSprocessing, src)
- s_busy = FALSE
-
-
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize(delay = s_delay)
- if(affecting==loc)
- var/mob/living/carbon/human/U = affecting
- if(alert("Are you certain you wish to remove the suit? This will take time and remove all abilities.",,"Yes","No")=="No")
- return
- s_busy = TRUE
- addtimer(CALLBACK(src, .proc/deinitialize_two, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_two(delay, mob/living/carbon/human/U)
- to_chat(U, "Now de-initializing...")
- addtimer(CALLBACK(src, .proc/deinitialize_three, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_three(delay, mob/living/carbon/human/U)
- to_chat(U, "Logging off, [U.real_name]. Shutting down SpiderOS.")
- addtimer(CALLBACK(src, .proc/deinitialize_four, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_four(delay, mob/living/carbon/human/U)
- to_chat(U, "Primary system status: OFFLINE.\nBackup system status: OFFLINE.")
- addtimer(CALLBACK(src, .proc/deinitialize_five, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_five(delay, mob/living/carbon/human/U)
- to_chat(U, "VOID-shift device status: OFFLINE.\nCLOAK-tech device status: OFFLINE.")
- cancel_stealth()//Shutdowns stealth.
- addtimer(CALLBACK(src, .proc/deinitialize_six, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_six(delay, mob/living/carbon/human/U)
- to_chat(U, "Disconnecting neural-net interface...\greenSuccess.")
- addtimer(CALLBACK(src, .proc/deinitialize_seven, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_seven(delay, mob/living/carbon/human/U)
- to_chat(U, "Disengaging neural-net interface...\greenSuccess.")
- addtimer(CALLBACK(src, .proc/deinitialize_eight, delay, U), delay)
-
-/obj/item/clothing/suit/space/space_ninja/proc/deinitialize_eight(delay, mob/living/carbon/human/U)
- to_chat(U, "Unsecuring external locking mechanism...\nNeural-net abolished.\nOperation status: FINISHED.")
- unlock_suit()
- U.regenerate_icons()
- s_initialized = FALSE
- STOP_PROCESSING(SSprocessing, src)
- s_busy = FALSE
diff --git a/code/modules/ninja/suit/suit_process.dm b/code/modules/ninja/suit/suit_process.dm
deleted file mode 100644
index 5c1276e29a..0000000000
--- a/code/modules/ninja/suit/suit_process.dm
+++ /dev/null
@@ -1,17 +0,0 @@
-/obj/item/clothing/suit/space/space_ninja/process()
- if(!affecting || !s_initialized)
- return PROCESS_KILL
-
- if(cell.charge > 0)
- if(s_coold)
- s_coold--//Checks for ability s_cooldown first.
-
- cell.charge -= s_cost//s_cost is the default energy cost each tick, usually 5.
- if(stealth && stealth_cooldown <= world.time)//If stealth is active.
- cell.charge -= s_acost
- affecting.alpha = max(affecting.alpha - 10, 15)
-
- else
- cell.charge = 0
- if(stealth)
- cancel_stealth()
diff --git a/html/changelogs/AutoChangeLog-pr-14219.yml b/html/changelogs/AutoChangeLog-pr-14219.yml
new file mode 100644
index 0000000000..1bef269e5a
--- /dev/null
+++ b/html/changelogs/AutoChangeLog-pr-14219.yml
@@ -0,0 +1,4 @@
+author: "zeroisthebiggay"
+delete-after: True
+changes:
+ - balance: "The Spider Clan has recently taken up the Space Ninja project again along with the Syndicate. Space Ninjas have been drastically changed as a result, becoming much weaker and more stealth oriented. As a result of cutting costs per ninja, more ninjas were able to be hired. Expect to see them around more often."
diff --git a/icons/mob/clothing/belt.dmi b/icons/mob/clothing/belt.dmi
index a40a8a72dd..2338fa37a0 100644
Binary files a/icons/mob/clothing/belt.dmi and b/icons/mob/clothing/belt.dmi differ
diff --git a/icons/mob/clothing/feet.dmi b/icons/mob/clothing/feet.dmi
index df40636049..4b732f13fe 100644
Binary files a/icons/mob/clothing/feet.dmi and b/icons/mob/clothing/feet.dmi differ
diff --git a/icons/mob/clothing/feet_digi.dmi b/icons/mob/clothing/feet_digi.dmi
index 7215875f2c..e82ae468e2 100644
Binary files a/icons/mob/clothing/feet_digi.dmi and b/icons/mob/clothing/feet_digi.dmi differ
diff --git a/icons/mob/clothing/head.dmi b/icons/mob/clothing/head.dmi
index 1596672ac3..3b6af60828 100644
Binary files a/icons/mob/clothing/head.dmi and b/icons/mob/clothing/head.dmi differ
diff --git a/icons/mob/clothing/head_muzzled.dmi b/icons/mob/clothing/head_muzzled.dmi
index 2fce92ee81..d3f05135c1 100644
Binary files a/icons/mob/clothing/head_muzzled.dmi and b/icons/mob/clothing/head_muzzled.dmi differ
diff --git a/icons/mob/clothing/mask.dmi b/icons/mob/clothing/mask.dmi
index 075464ace5..17a49ea751 100644
Binary files a/icons/mob/clothing/mask.dmi and b/icons/mob/clothing/mask.dmi differ
diff --git a/icons/mob/clothing/mask_muzzled.dmi b/icons/mob/clothing/mask_muzzled.dmi
index 9da3545c65..9f483c8d47 100644
Binary files a/icons/mob/clothing/mask_muzzled.dmi and b/icons/mob/clothing/mask_muzzled.dmi differ
diff --git a/icons/mob/clothing/suit.dmi b/icons/mob/clothing/suit.dmi
index b4ecbdfb2a..48759bf81c 100644
Binary files a/icons/mob/clothing/suit.dmi and b/icons/mob/clothing/suit.dmi differ
diff --git a/icons/mob/clothing/suit_digi.dmi b/icons/mob/clothing/suit_digi.dmi
index 80259e2832..13840507a6 100644
Binary files a/icons/mob/clothing/suit_digi.dmi and b/icons/mob/clothing/suit_digi.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_lefthand.dmi b/icons/mob/inhands/weapons/swords_lefthand.dmi
index 4be0df2300..62e48f626b 100644
Binary files a/icons/mob/inhands/weapons/swords_lefthand.dmi and b/icons/mob/inhands/weapons/swords_lefthand.dmi differ
diff --git a/icons/mob/inhands/weapons/swords_righthand.dmi b/icons/mob/inhands/weapons/swords_righthand.dmi
index 57308ffb76..658e7304df 100644
Binary files a/icons/mob/inhands/weapons/swords_righthand.dmi and b/icons/mob/inhands/weapons/swords_righthand.dmi differ
diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi
index 082bfb3c3e..21af40ff8f 100644
Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ
diff --git a/icons/mob/talk.dmi b/icons/mob/talk.dmi
index 0a56321037..beb38f01cc 100644
Binary files a/icons/mob/talk.dmi and b/icons/mob/talk.dmi differ
diff --git a/icons/obj/clothing/gloves.dmi b/icons/obj/clothing/gloves.dmi
index 9df096d19b..4d2ddcf6bd 100644
Binary files a/icons/obj/clothing/gloves.dmi and b/icons/obj/clothing/gloves.dmi differ
diff --git a/icons/obj/clothing/hats.dmi b/icons/obj/clothing/hats.dmi
index 7d4ae79957..3ebd15a511 100644
Binary files a/icons/obj/clothing/hats.dmi and b/icons/obj/clothing/hats.dmi differ
diff --git a/icons/obj/clothing/masks.dmi b/icons/obj/clothing/masks.dmi
index 1e2449329d..b6839de219 100644
Binary files a/icons/obj/clothing/masks.dmi and b/icons/obj/clothing/masks.dmi differ
diff --git a/icons/obj/clothing/suits.dmi b/icons/obj/clothing/suits.dmi
index 77d1d026b2..ab6cea5a3b 100644
Binary files a/icons/obj/clothing/suits.dmi and b/icons/obj/clothing/suits.dmi differ
diff --git a/icons/obj/grenade.dmi b/icons/obj/grenade.dmi
index d6dd4c1b00..e2f06621e4 100644
Binary files a/icons/obj/grenade.dmi and b/icons/obj/grenade.dmi differ
diff --git a/icons/obj/items_and_weapons.dmi b/icons/obj/items_and_weapons.dmi
index 39f1845f43..454e174550 100644
Binary files a/icons/obj/items_and_weapons.dmi and b/icons/obj/items_and_weapons.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index c4de2a1799..204b69a11b 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -463,7 +463,6 @@
#include "code\datums\achievements\misc_scores.dm"
#include "code\datums\achievements\skill_achievements.dm"
#include "code\datums\actions\beam_rifle.dm"
-#include "code\datums\actions\ninja.dm"
#include "code\datums\atmosphere\_atmosphere.dm"
#include "code\datums\atmosphere\planetary.dm"
#include "code\datums\brain_damage\brain_trauma.dm"
@@ -1738,7 +1737,6 @@
#include "code\modules\antagonists\morph\morph.dm"
#include "code\modules\antagonists\morph\morph_antag.dm"
#include "code\modules\antagonists\nightmare\nightmare.dm"
-#include "code\modules\antagonists\ninja\ninja.dm"
#include "code\modules\antagonists\nukeop\clownop.dm"
#include "code\modules\antagonists\nukeop\nukeop.dm"
#include "code\modules\antagonists\nukeop\equipment\borgchameleon.dm"
@@ -1761,6 +1759,7 @@
#include "code\modules\antagonists\slaughter\slaughter.dm"
#include "code\modules\antagonists\slaughter\slaughter_antag.dm"
#include "code\modules\antagonists\slaughter\slaughterevent.dm"
+#include "code\modules\antagonists\space_ninja\space_ninja.dm"
#include "code\modules\antagonists\space_dragon\space_dragon.dm"
#include "code\modules\antagonists\survivalist\survivalist.dm"
#include "code\modules\antagonists\swarmer\swarmer.dm"
@@ -2133,6 +2132,7 @@
#include "code\modules\events\radiation_storm.dm"
#include "code\modules\events\sentience.dm"
#include "code\modules\events\shuttle_loan.dm"
+#include "code\modules\events\space_ninja.dm"
#include "code\modules\events\space_dragon.dm"
#include "code\modules\events\spacevine.dm"
#include "code\modules\events\spider_infestation.dm"
@@ -2977,7 +2977,7 @@
#include "code\modules\newscaster\wanted_message.dm"
#include "code\modules\ninja\__ninjaDefines.dm"
#include "code\modules\ninja\energy_katana.dm"
-#include "code\modules\ninja\ninja_event.dm"
+#include "code\modules\ninja\ninja_explosive.dm"
#include "code\modules\ninja\outfit.dm"
#include "code\modules\ninja\suit\gloves.dm"
#include "code\modules\ninja\suit\head.dm"
@@ -2986,17 +2986,17 @@
#include "code\modules\ninja\suit\shoes.dm"
#include "code\modules\ninja\suit\suit.dm"
#include "code\modules\ninja\suit\suit_attackby.dm"
-#include "code\modules\ninja\suit\suit_initialisation.dm"
-#include "code\modules\ninja\suit\suit_process.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\energy_net_nets.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_adrenaline.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_cost_check.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_empulse.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_net.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_smoke.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_stars.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_stealth.dm"
-#include "code\modules\ninja\suit\n_suit_verbs\ninja_sword_recall.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\energy_net_nets.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_adrenaline.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_cost_check.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_empulse.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_glove_toggle.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_net.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_stars.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_status_read.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_stealth.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_suit_initialisation.dm"
+#include "code\modules\ninja\suit\ninja_equipment_actions\ninja_sword_recall.dm"
#include "code\modules\NTNet\netdata.dm"
#include "code\modules\NTNet\network.dm"
#include "code\modules\NTNet\relays.dm"