diff --git a/code/__DEFINES/configuration.dm b/code/__DEFINES/configuration.dm
index 9915563cab..8c9b270f43 100644
--- a/code/__DEFINES/configuration.dm
+++ b/code/__DEFINES/configuration.dm
@@ -24,3 +24,11 @@
#define POLICYCONFIG_ON_PYROCLASTIC_SENTIENT "PYROCLASTIC_SLIME"
/// Displayed to pAIs on spawn
#define POLICYCONFIG_PAI "PAI_SPAWN"
+/// displayed to lavaland elites on spawn, overriding the default if set
+#define POLICYCONFIG_ELITE_SPAWN "ELITE_FAUNA_SPAWN"
+/// displayed to lavaland elites on sentience potion, overriding the default if set
+#define POLICYCONFIG_ELITE_SENTIENCE "ELITE_FAUNA_SENTIENCE"
+/// displayed to lavaland elites on winning the fight, overriding the default if set
+#define POLICYCONFIG_ELITE_WIN "ELITE_FAUNA_WIN"
+/// displayed to prisoners on spawn, overriding the default if set
+#define POLICYCONFIG_JOB_PRISONER "JOB_PRISONER"
diff --git a/code/_globalvars/lists/typecache.dm b/code/_globalvars/lists/typecache.dm
index 1ec3de0bc5..d380816bbf 100644
--- a/code/_globalvars/lists/typecache.dm
+++ b/code/_globalvars/lists/typecache.dm
@@ -12,3 +12,16 @@ GLOBAL_LIST_INIT(typecache_stack, typecacheof(/obj/item/stack))
GLOBAL_LIST_INIT(typecache_machine_or_structure, typecacheof(list(/obj/machinery, /obj/structure)))
GLOBAL_LIST_INIT(freezing_objects, typecacheof(list(/obj/structure/closet/crate/freezer, /obj/structure/closet/secure_closet/freezer, /obj/structure/bodycontainer, /obj/item/autosurgeon, /obj/machinery/smartfridge/organ))) //list of all cold objects, that freeze organs when inside
+
+GLOBAL_LIST_EMPTY(typecaches)
+
+/**
+ * Makes a typecache of a single typecache
+ *
+ * Obviously in BYOND we don't have the efficiency around here to have proper enforcement so
+ * If you use this you better know what you're doing. The list you get back is globally cached and if it's modified, you might break multiple things.
+ */
+/proc/single_path_typecache_immutable(path)
+ if(!GLOB.typecaches[path])
+ GLOB.typecaches[path] = typecacheof(path)
+ return GLOB.typecaches[path]
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/dice.dm b/code/game/objects/items/dice.dm
index bbc493672f..53b0860def 100644
--- a/code/game/objects/items/dice.dm
+++ b/code/game/objects/items/dice.dm
@@ -17,6 +17,11 @@
/obj/item/dice/d100
)
+/obj/item/storage/dice/ComponentInitialize()
+ . = ..()
+ var/datum/component/storage/STR = GetComponent(/datum/component/storage)
+ STR.can_hold = single_path_typecache_immutable(/obj/item/dice)
+
/obj/item/storage/dice/PopulateContents()
new /obj/item/dice/d4(src)
new /obj/item/dice/d6(src)
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/game/objects/obj_defense.dm b/code/game/objects/obj_defense.dm
index 78a5c9ece9..15fd102878 100644
--- a/code/game/objects/obj_defense.dm
+++ b/code/game/objects/obj_defense.dm
@@ -22,7 +22,7 @@
//returns the damage value of the attack after processing the obj's various armor protections
/obj/proc/run_obj_armor(damage_amount, damage_type, damage_flag = 0, attack_dir, armour_penetration = 0)
- if(damage_flag == MELEE && damage_amount < damage_deflection)
+ if(damage_flag == "melee" && damage_amount < damage_deflection) // TODO: Refactor armor datums and types entirely jfc
return 0
switch(damage_type)
if(BRUTE)
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/jobs/job_types/prisoner.dm b/code/modules/jobs/job_types/prisoner.dm
index 2ad2f32537..48c7afe304 100644
--- a/code/modules/jobs/job_types/prisoner.dm
+++ b/code/modules/jobs/job_types/prisoner.dm
@@ -1,4 +1,4 @@
-datum/job/prisoner
+/datum/job/prisoner
title = "Prisoner"
flag = PRISONER
department_head = list("The Security Team")
@@ -13,6 +13,15 @@ datum/job/prisoner
display_order = JOB_DISPLAY_ORDER_PRISONER
+/datum/job/prisoner/after_spawn(mob/living/carbon/human/H, mob/M)
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/policy = policies[POLICYCONFIG_JOB_PRISONER]
+ if(policy)
+ var/mob/found = (M?.client && M) || (H?.client && H)
+ to_chat(found, "
!!READ THIS!!
The following is server-specific policy configuration and overrides anything said above if conflicting.")
+ to_chat(found, "
")
+ to_chat(found, "[policy]")
+
/datum/outfit/job/prisoner
name = "Prisoner"
jobtype = /datum/job/prisoner
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/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
index d677f59440..de6b858f79 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining_mobs/elites/elite.dm
@@ -176,6 +176,12 @@ While using this makes the system rely on OnFire, it still gives options for tim
elitemind = pick(candidates)
elitemind.playsound_local(get_turf(elitemind), 'sound/effects/magic.ogg', 40, 0)
to_chat(elitemind, "You have been chosen to play as a Lavaland Elite.\nIn a few seconds, you will be summoned on Lavaland as a monster to fight your activator, in a fight to the death.\nYour attacks can be switched using the buttons on the top left of the HUD, and used by clicking on targets or tiles similar to a gun.\nWhile the opponent might have an upper hand with powerful mining equipment and tools, you have great power normally limited by AI mobs.\nIf you want to win, you'll have to use your powers in creative ways to ensure the kill. It's suggested you try using them all as soon as possible.\nShould you win, you'll receive extra information regarding what to do after. Good luck!")
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/policy = policies[POLICYCONFIG_ELITE_SPAWN]
+ if(policy)
+ to_chat(elitemind, "
!!READ THIS!!
The following is server-specific policy configuration and overrides anything said above if conflicting.")
+ to_chat(elitemind, "
")
+ to_chat(elitemind, "[policy]")
addtimer(CALLBACK(src, .proc/spawn_elite, elitemind), 100)
else
visible_message("The stirring stops, and nothing emerges. Perhaps try again later.")
@@ -304,6 +310,12 @@ While using this makes the system rely on OnFire, it still gives options for tim
to_chat(mychild, "As the life in the activator's eyes fade, the forcefield around you dies out and you feel your power subside.\nDespite this inferno being your home, you feel as if you aren't welcome here anymore.\nWithout any guidance, your purpose is now for you to decide.")
to_chat(mychild, "Your max health has been halved, but can now heal by standing on your tumor. Note, it's your only way to heal.\nBear in mind, if anyone interacts with your tumor, you'll be resummoned here to carry out another fight. In such a case, you will regain your full max health.\nAlso, be weary of your fellow inhabitants, they likely won't be happy to see you!")
to_chat(mychild, "Note that you are a lavaland monster, and thus not allied to the station. You should not cooperate or act friendly with any station crew unless under extreme circumstances!")
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/policy = policies[POLICYCONFIG_ELITE_WIN]
+ if(policy)
+ to_chat(mychild, "
!!READ THIS!!
The following is server-specific policy configuration and overrides anything said above if conflicting.")
+ to_chat(mychild, "
")
+ to_chat(mychild, "[policy]")
/obj/item/tumor_shard
name = "tumor shard"
@@ -332,6 +344,12 @@ While using this makes the system rely on OnFire, it still gives options for tim
E.playsound_local(get_turf(E), 'sound/effects/magic.ogg', 40, 0)
to_chat(E, "You have been revived by [user]. While you can't speak to them, you owe [user] a great debt. Assist [user.p_them()] in achieving [user.p_their()] goals, regardless of risk.Note that you now share the loyalties of [user]. You are expected not to intentionally sabotage their faction unless commanded to!")
+ var/list/policies = CONFIG_GET(keyed_list/policy)
+ var/policy = policies[POLICYCONFIG_ELITE_SENTIENCE]
+ if(policy)
+ to_chat(E, "
!!READ THIS!!
The following is server-specific policy configuration and overrides anything said above if conflicting.")
+ to_chat(E, "
")
+ to_chat(E, "[policy]")
E.maxHealth = E.maxHealth * 0.5
E.health = E.maxHealth
E.desc = "[E.desc] However, this one appears appears less wild in nature, and calmer around people."
diff --git a/code/modules/movespeed/modifiers/mobs.dm b/code/modules/movespeed/modifiers/mobs.dm
index 7e857a4e0d..153d7dd973 100644
--- a/code/modules/movespeed/modifiers/mobs.dm
+++ b/code/modules/movespeed/modifiers/mobs.dm
@@ -113,6 +113,7 @@
multiplicative_slowdown = CRAWLING_ADD_SLOWDOWN
movetypes = CRAWLING
flags = IGNORE_NOSLOW
+ priority = 20000
/datum/movespeed_modifier/mob_config_speedmod
variable = TRUE
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/code/modules/surgery/bodyparts/parts.dm b/code/modules/surgery/bodyparts/parts.dm
index 3a08a764d3..e66f14da93 100644
--- a/code/modules/surgery/bodyparts/parts.dm
+++ b/code/modules/surgery/bodyparts/parts.dm
@@ -59,13 +59,13 @@
one though."
icon_state = "default_human_l_arm"
attack_verb = list("slapped", "punched")
- max_damage = 150
+ max_damage = 50
disable_threshold = 75
max_stamina_damage = 50
body_zone = BODY_ZONE_L_ARM
body_part = ARM_LEFT
aux_icons = list(BODY_ZONE_PRECISE_L_HAND = HANDS_PART_LAYER, "l_hand_behind" = BODY_BEHIND_LAYER)
- body_damage_coeff = 0.25
+ body_damage_coeff = 0.75
held_index = 1
px_x = -6
px_y = 0
@@ -121,12 +121,12 @@
among humans missing their right arm."
icon_state = "default_human_r_arm"
attack_verb = list("slapped", "punched")
- max_damage = 150
+ max_damage = 50
disable_threshold = 75
body_zone = BODY_ZONE_R_ARM
body_part = ARM_RIGHT
aux_icons = list(BODY_ZONE_PRECISE_R_HAND = HANDS_PART_LAYER, "r_hand_behind" = BODY_BEHIND_LAYER)
- body_damage_coeff = 0.25
+ body_damage_coeff = 0.75
held_index = 2
px_x = 6
px_y = 0
@@ -184,11 +184,11 @@
luck. In this instance, it probably would not have helped."
icon_state = "default_human_l_leg"
attack_verb = list("kicked", "stomped")
- max_damage = 150
+ max_damage = 50
disable_threshold = 75
body_zone = BODY_ZONE_L_LEG
body_part = LEG_LEFT
- body_damage_coeff = 0.25
+ body_damage_coeff = 0.75
px_x = -2
px_y = 12
stam_heal_tick = STAM_RECOVERY_LIMB
@@ -243,11 +243,10 @@
// alternative spellings of 'pokey' are availible
icon_state = "default_human_r_leg"
attack_verb = list("kicked", "stomped")
- max_damage = 150
- disable_threshold = 75
+ max_damage = 50
body_zone = BODY_ZONE_R_LEG
body_part = LEG_RIGHT
- body_damage_coeff = 0.25
+ body_damage_coeff = 0.75
px_x = 2
px_y = 12
max_stamina_damage = 50
diff --git a/html/changelog.html b/html/changelog.html
index dabd4e7704..6cf78fb190 100644
--- a/html/changelog.html
+++ b/html/changelog.html
@@ -50,6 +50,70 @@
-->