Merge remote-tracking branch 'citadel/master' into mobility_flags

This commit is contained in:
kevinz000
2020-02-21 13:35:26 -07:00
180 changed files with 4392 additions and 2132 deletions
@@ -14,7 +14,8 @@ GLOBAL_LIST_EMPTY(antagonists)
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
var/can_hijack = HIJACK_NEUTRAL //If these antags are alone on shuttle hijack happens.
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
var/hijack_speed = 0
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
@@ -229,6 +230,13 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
/datum/antagonist/proc/hijack_speed()
var/datum/objective/hijack/H = locate() in objectives
if(!isnull(H?.hijack_speed_override))
return H.hijack_speed_override
return hijack_speed
//This one is created by admin tools for custom objectives
/datum/antagonist/custom
antagpanel_category = "Custom"
@@ -286,10 +286,10 @@
var/mob/living/L = owner.current
level_bloodcost = maxBloodVolume * 0.2
//If the blood volume of the bloodsucker is lower than the cost to level up, return and inform the bloodsucker
//TODO: Make this into a radial, or perhaps a tgui next UI
// Purchase Power Prompt
var/list/options = list()
var/list/options = list()
for(var/pickedpower in typesof(/datum/action/bloodsucker))
var/datum/action/bloodsucker/power = pickedpower
// If I don't own it, and I'm allowed to buy it.
@@ -14,6 +14,9 @@
message_Trigger = ""//"Whom will you subvert to your will?"
bloodsucker_can_buy = TRUE
must_be_capacitated = TRUE
var/list/hit //current hit, set while power is in use as we can't pass the list as an extra calling argument in registersignal.
/// If set, uses this speed in deciseconds instead of world.tick_lag
var/speed_override
/datum/action/bloodsucker/targeted/haste/CheckCanUse(display_error)
. = ..()
@@ -43,42 +46,46 @@
return TRUE
/datum/action/bloodsucker/targeted/haste/FireTargetedPower(atom/A)
// set waitfor = FALSE <---- DONT DO THIS!We WANT this power to hold up ClickWithPower(), so that we can unlock the power when it's done.
// This is a non-async proc to make sure the power is "locked" until this finishes.
hit = list()
RegisterSignal(owner, COMSIG_MOVABLE_MOVED, .proc/on_move)
var/mob/living/user = owner
var/turf/T = isturf(A) ? A : get_turf(A)
// Pulled? Not anymore.
owner.pulledby = null
// Step One: Heatseek toward Target's Turf
walk_to(owner, T, 0, 0.01, 20) // NOTE: this runs in the background! to cancel it, you need to use walk(owner.current,0), or give them a new path.
user.pulledby?.stop_pulling()
// Go to target turf
// DO NOT USE WALK TO.
playsound(get_turf(owner), 'sound/weapons/punchmiss.ogg', 25, 1, -1)
var/safety = 20
while(get_turf(owner) != T && safety > 0 && !(isliving(target) && target.Adjacent(owner)))
user.mobility_flags = NONE
safety --
// Did I get knocked down?
if(owner && owner.incapacitated(ignore_restraints=TRUE, ignore_grab=TRUE))// owner.incapacitated())
// We're gonna cancel. But am I on the ground? Spin me!
if(!CHECK_MOBILITY(user, MOBILITY_STAND))
var/send_dir = get_dir(owner, T)
new /datum/forced_movement(owner, get_ranged_target_turf(owner, send_dir, 1), 1, FALSE)
owner.spin(10)
var/safety = get_dist(user, T) * 3 + 1
var/consequetive_failures = 0
var/speed = isnull(speed_override)? world.tick_lag : speed_override
while(--safety && (get_turf(user) != T))
var/success = step_towards(user, T) //This does not try to go around obstacles.
if(!success)
success = step_to(user, T) //this does
if(!success)
if(++consequetive_failures >= 3) //if 3 steps don't work
break //just stop
else
consequetive_failures = 0
if(user.resting)
user.setDir(turn(user.dir, 90)) //down? spin2win :^)
if(user.incapacitated(ignore_restraints = TRUE, ignore_grab = TRUE)) //actually down? stop.
break
// Spin/Stun people we pass.
//var/mob/living/newtarget = locate(/mob/living) in oview(1, owner)
var/list/mob/living/foundtargets = list()
for(var/mob/living/newtarget in oview(1, owner))
if (newtarget && newtarget != target && !(newtarget in foundtargets))//!newtarget.IsKnockdown())
if (rand(0, 5) < level_current)
playsound(get_turf(newtarget), "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
newtarget.DefaultCombatKnockdown(10 + level_current * 5)
if(newtarget.IsStun())
newtarget.spin(10,1)
if (rand(0,4))
newtarget.drop_all_held_items()
foundtargets += newtarget
sleep(1)
user?.update_mobility() //Let the poor guy move again
if(success) //don't sleep if we failed to move.
sleep(speed)
UnregisterSignal(owner, COMSIG_MOVABLE_MOVED)
hit = null
user.update_canmove()
/datum/action/bloodsucker/targeted/haste/DeactivatePower(mob/living/user = owner, mob/living/target)
..() // activate = FALSE
user.update_mobility()
user.update_canmove()
/datum/action/bloodsucker/targeted/haste/proc/on_move()
for(var/mob/living/L in dview(1, get_turf(owner)))
if(!hit[L] && (L != owner))
hit[L] = TRUE
playsound(L, "sound/weapons/punch[rand(1,4)].ogg", 15, 1, -1)
L.Knockdown(10 + level_current * 5, override_hardstun = 0.1)
L.spin(10, 1)
@@ -54,8 +54,8 @@
REMOVE_TRAIT(user, TRAIT_VIRUSIMMUNE, "bloodsucker")
var/obj/item/organ/heart/vampheart/H = user.getorganslot(ORGAN_SLOT_HEART)
var/obj/item/organ/eyes/vassal/bloodsucker/E = user.getorganslot(ORGAN_SLOT_EYES)
E.flash_protect = 0
E.flash_protect = 0
// WE ARE ALIVE! //
bloodsuckerdatum.poweron_masquerade = TRUE
while(bloodsuckerdatum && ContinueActive(user))
@@ -5,7 +5,6 @@
var/special_role = ROLE_BROTHER
var/datum/team/brother_team/team
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_HIJACKER
/datum/antagonist/brother/create_team(datum/team/brother_team/new_team)
if(!new_team)
@@ -5,49 +5,49 @@ is currently following.
*/
GLOBAL_LIST_INIT(disease_ability_singletons, list(
new /datum/disease_ability/action/cough,
new /datum/disease_ability/action/sneeze,
new /datum/disease_ability/action/infect,
new /datum/disease_ability/symptom/mild/cough,
new /datum/disease_ability/symptom/mild/sneeze,
new /datum/disease_ability/symptom/medium/shedding,
new /datum/disease_ability/symptom/medium/beard,
new /datum/disease_ability/symptom/medium/hallucigen,
new /datum/disease_ability/symptom/medium/choking,
new /datum/disease_ability/symptom/medium/confusion,
new /datum/disease_ability/symptom/medium/vomit,
new /datum/disease_ability/symptom/medium/voice_change,
new /datum/disease_ability/symptom/medium/visionloss,
new /datum/disease_ability/symptom/medium/deafness,
new /datum/disease_ability/symptom/powerful/narcolepsy,
new /datum/disease_ability/symptom/medium/fever,
new /datum/disease_ability/symptom/medium/shivering,
new /datum/disease_ability/symptom/medium/headache,
new /datum/disease_ability/symptom/medium/nano_boost,
new /datum/disease_ability/symptom/medium/nano_destroy,
new /datum/disease_ability/symptom/medium/viraladaptation,
new /datum/disease_ability/symptom/medium/viralevolution,
new /datum/disease_ability/symptom/medium/vitiligo,
new /datum/disease_ability/symptom/medium/revitiligo,
new /datum/disease_ability/symptom/medium/itching,
new /datum/disease_ability/symptom/medium/heal/weight_loss,
new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
new /datum/disease_ability/symptom/medium/heal/mind_restoration,
new /datum/disease_ability/symptom/powerful/fire,
new /datum/disease_ability/symptom/powerful/flesh_eating,
// new /datum/disease_ability/symptom/powerful/genetic_mutation,
new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
new /datum/disease_ability/symptom/powerful/heal/starlight,
new /datum/disease_ability/symptom/powerful/heal/oxygen,
new /datum/disease_ability/symptom/powerful/heal/chem,
new /datum/disease_ability/symptom/powerful/heal/metabolism,
new /datum/disease_ability/symptom/powerful/heal/dark,
new /datum/disease_ability/symptom/powerful/heal/water,
new /datum/disease_ability/symptom/powerful/heal/plasma,
new /datum/disease_ability/symptom/powerful/heal/radiation,
new /datum/disease_ability/symptom/powerful/heal/coma,
new /datum/disease_ability/symptom/powerful/youth
))
new /datum/disease_ability/action/cough,
new /datum/disease_ability/action/sneeze,
new /datum/disease_ability/action/infect,
new /datum/disease_ability/symptom/mild/cough,
new /datum/disease_ability/symptom/mild/sneeze,
new /datum/disease_ability/symptom/medium/shedding,
new /datum/disease_ability/symptom/medium/beard,
new /datum/disease_ability/symptom/medium/hallucigen,
new /datum/disease_ability/symptom/medium/choking,
new /datum/disease_ability/symptom/medium/confusion,
new /datum/disease_ability/symptom/medium/vomit,
new /datum/disease_ability/symptom/medium/voice_change,
new /datum/disease_ability/symptom/medium/visionloss,
new /datum/disease_ability/symptom/medium/deafness,
new /datum/disease_ability/symptom/powerful/narcolepsy,
new /datum/disease_ability/symptom/medium/fever,
new /datum/disease_ability/symptom/medium/shivering,
new /datum/disease_ability/symptom/medium/headache,
new /datum/disease_ability/symptom/medium/nano_boost,
new /datum/disease_ability/symptom/medium/nano_destroy,
new /datum/disease_ability/symptom/medium/viraladaptation,
new /datum/disease_ability/symptom/medium/viralevolution,
new /datum/disease_ability/symptom/medium/disfiguration,
new /datum/disease_ability/symptom/medium/polyvitiligo,
new /datum/disease_ability/symptom/medium/itching,
new /datum/disease_ability/symptom/medium/heal/weight_loss,
new /datum/disease_ability/symptom/medium/heal/sensory_restoration,
new /datum/disease_ability/symptom/medium/heal/mind_restoration,
new /datum/disease_ability/symptom/powerful/fire,
new /datum/disease_ability/symptom/powerful/flesh_eating,
new /datum/disease_ability/symptom/powerful/genetic_mutation,
new /datum/disease_ability/symptom/powerful/inorganic_adaptation,
new /datum/disease_ability/symptom/powerful/heal/starlight,
new /datum/disease_ability/symptom/powerful/heal/oxygen,
new /datum/disease_ability/symptom/powerful/heal/chem,
new /datum/disease_ability/symptom/powerful/heal/metabolism,
new /datum/disease_ability/symptom/powerful/heal/dark,
new /datum/disease_ability/symptom/powerful/heal/water,
new /datum/disease_ability/symptom/powerful/heal/plasma,
new /datum/disease_ability/symptom/powerful/heal/radiation,
new /datum/disease_ability/symptom/powerful/heal/coma,
new /datum/disease_ability/symptom/powerful/youth
))
/datum/disease_ability
var/name
@@ -57,7 +57,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/short_desc = ""
var/long_desc = ""
var/stat_block = ""
var/threshold_block = list()
var/threshold_block = ""
var/category = ""
var/list/symptoms
@@ -76,7 +76,7 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
resistance += initial(S.resistance)
stage_speed += initial(S.stage_speed)
transmittable += initial(S.transmittable)
threshold_block += initial(S.threshold_desc)
threshold_block += "<br><br>[initial(S.threshold_desc)]"
stat_block = "Resistance: [resistance]<br>Stealth: [stealth]<br>Stage Speed: [stage_speed]<br>Transmissibility: [transmittable]<br><br>"
if(symptoms.len == 1) //lazy boy's dream
name = initial(S.name)
@@ -106,10 +106,8 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
for(var/T in symptoms)
var/datum/symptom/S = new T()
SD.symptoms += S
S.OnAdd(SD)
if(SD.processing)
if(S.Start(SD))
S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
S.Start(SD)
SD.Refresh()
for(var/T in actions)
var/datum/action/A = new T()
@@ -136,7 +134,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
var/datum/symptom/S = locate(T) in SD.symptoms
if(S)
SD.symptoms -= S
S.OnRemove(SD)
if(SD.processing)
S.End(SD)
qdel(S)
@@ -296,7 +293,6 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
cost = 8
category = "Symptom (Strong+)"
/******MILD******/
/datum/disease_ability/symptom/mild/cough
@@ -377,11 +373,11 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/medium/viralevolution
symptoms = list(/datum/symptom/viralevolution)
/datum/disease_ability/symptom/medium/vitiligo
symptoms = list(/datum/symptom/vitiligo)
/datum/disease_ability/symptom/medium/polyvitiligo
symptoms = list(/datum/symptom/polyvitiligo)
/datum/disease_ability/symptom/medium/revitiligo
symptoms = list(/datum/symptom/revitiligo)
/datum/disease_ability/symptom/medium/disfiguration
symptoms = list(/datum/symptom/disfiguration)
/datum/disease_ability/symptom/medium/itching
symptoms = list(/datum/symptom/itching)
@@ -409,11 +405,9 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/flesh_eating
symptoms = list(/datum/symptom/flesh_eating)
/*
/datum/disease_ability/symptom/powerful/genetic_mutation
symptoms = list(/datum/symptom/genetic_mutation)
cost = 8
*/
/datum/disease_ability/symptom/powerful/inorganic_adaptation
symptoms = list(/datum/symptom/inorganic_adaptation)
@@ -457,4 +451,4 @@ GLOBAL_LIST_INIT(disease_ability_singletons, list(
/datum/disease_ability/symptom/powerful/heal/coma
symptoms = list(/datum/symptom/heal/coma)
short_desc = "Cause victims to fall into a healing coma when hurt."
long_desc = "Cause victims to fall into a healing coma when hurt."
long_desc = "Cause victims to fall into a healing coma when hurt."
-1
View File
@@ -12,7 +12,6 @@
var/list/name_source
show_in_antagpanel = FALSE
antag_moodlet = /datum/mood_event/focused
can_hijack = HIJACK_PREVENT
/datum/antagonist/ert/on_gain()
update_name()
@@ -3,7 +3,7 @@
var/obj/item/claymore/highlander/sword
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
hijack_speed = 2 //if you kill everyone and actually haev a hand to hijack with, you win??
/datum/antagonist/highlander/apply_innate_effects(mob/living/mob_override)
var/mob/living/L = owner.current || mob_override
-7
View File
@@ -8,11 +8,6 @@
var/give_objectives = TRUE
var/give_equipment = TRUE
/datum/antagonist/ninja/New()
if(helping_station)
can_hijack = HIJACK_PREVENT
. = ..()
/datum/antagonist/ninja/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_ninja_icons_added(M)
@@ -135,8 +130,6 @@
adj = "objectiveless"
else
return
if(helping_station)
can_hijack = HIJACK_PREVENT
new_owner.assigned_role = ROLE_NINJA
new_owner.special_role = ROLE_NINJA
new_owner.add_antag_datum(src)
@@ -8,7 +8,6 @@
var/always_new_team = FALSE //If not assigned a team by default ops will try to join existing ones, set this to TRUE to always create new team.
var/send_to_spawnpoint = TRUE //Should the user be moved to default spawnpoint.
var/nukeop_outfit = /datum/outfit/syndicate
can_hijack = HIJACK_HIJACKER //Alternative way to wipe out the station.
/datum/antagonist/nukeop/proc/update_synd_icons_added(mob/living/M)
var/datum/atom_hud/antag/opshud = GLOB.huds[ANTAG_HUD_OPS]
@@ -4,7 +4,6 @@
show_in_antagpanel = FALSE
var/datum/objective/mission
var/datum/team/ert/ert_team
can_hijack = HIJACK_PREVENT
/datum/antagonist/official/greet()
to_chat(owner, "<B><font size=3 color=red>You are a CentCom Official.</font></B>")
@@ -13,7 +13,7 @@
var/should_give_codewords = TRUE
var/should_equip = TRUE
var/traitor_kind = TRAITOR_HUMAN //Set on initial assignment
can_hijack = HIJACK_HIJACKER
hijack_speed = 0.5 //10 seconds per hijack stage by default
/datum/antagonist/traitor/on_gain()
if(owner.current && isAI(owner.current))
@@ -60,6 +60,7 @@
message = GLOB.syndicate_code_response_regex.Replace(message, "<span class='red'>$1</span>")
hearing_args[HEARING_RAW_MESSAGE] = message
// needs to be refactored to base /datum/antagonist sometime..
/datum/antagonist/traitor/proc/add_objective(datum/objective/O)
objectives += O
@@ -2,7 +2,6 @@
name = "Wishgranter Avatar"
show_in_antagpanel = FALSE
show_name_in_check_antagonists = TRUE
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wishgranter/proc/forge_objectives()
var/datum/objective/hijack/hijack = new
@@ -12,7 +12,6 @@
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
var/wiz_age = WIZARD_AGE_MIN /* Wizards by nature cannot be too young. */
can_hijack = HIJACK_HIJACKER
/datum/antagonist/wizard/on_gain()
register()
@@ -53,7 +53,8 @@
H.adjust_blurriness(1)
H.visible_message("<span class='warning'>[H] is creamed by [src]!</span>", "<span class='userdanger'>You've been creamed by [src]!</span>")
playsound(H, "desceration", 50, TRUE)
reagents.trans_to(H,15) //Cream pie combat
if(!H.is_mouth_covered())
reagents.trans_to(H,15) //Cream pie combat
if(!H.creamed) // one layer at a time
H.add_overlay(creamoverlay)
H.creamed = TRUE
+4 -7
View File
@@ -122,13 +122,10 @@
to_chat(usr, "<span class='warning'>Error: Insufficient credits for [prize.equipment_name] on [I]!</span>")
flick(icon_deny, src)
else
if (I.mining_points -= prize.cost)
to_chat(usr, "<span class='notice'>[src] clanks to life briefly before vending [prize.equipment_name]!</span>")
new prize.equipment_path(src.loc)
SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]"))
else
to_chat(usr, "<span class='warning'>Error: Transaction failure, please try again later!</span>")
flick(icon_deny, src)
I.mining_points -= prize.cost
to_chat(usr, "<span class='notice'>[src] clanks to life briefly before vending [prize.equipment_name]!</span>")
new prize.equipment_path(src.loc)
SSblackbox.record_feedback("nested tally", "mining_equipment_bought", 1, list("[type]", "[prize.equipment_path]"))
else
to_chat(usr, "<span class='warning'>Error: An ID with a registered account is required!</span>")
flick(icon_deny, src)
+21 -1
View File
@@ -834,12 +834,31 @@
update_inv_handcuffed()
update_hud_handcuffed()
/mob/living/carbon/proc/can_defib()
var/tlimit = DEFIB_TIME_LIMIT * 10
var/obj/item/organ/heart = getorgan(/obj/item/organ/heart)
if(suiciding || hellbound || HAS_TRAIT(src, TRAIT_HUSK))
return
if((world.time - timeofdeath) > tlimit)
return
if((getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE) || (getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE))
return
if(!heart || (heart.organ_flags & ORGAN_FAILING))
return
var/obj/item/organ/brain/BR = getorgan(/obj/item/organ/brain)
if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || suiciding)
return
return TRUE
/mob/living/carbon/fully_heal(admin_revive = FALSE)
if(reagents)
reagents.clear_reagents()
var/obj/item/organ/brain/B = getorgan(/obj/item/organ/brain)
if(B)
B.brain_death = FALSE
for(var/O in internal_organs)
var/obj/item/organ/organ = O
organ.setOrganDamage(0)
for(var/thing in diseases)
var/datum/disease/D = thing
if(D.severity != DISEASE_SEVERITY_POSITIVE)
@@ -852,7 +871,8 @@
qdel(R)
update_handcuffed()
if(reagents)
reagents.addiction_list = list()
for(var/addi in reagents.addiction_list)
reagents.remove_addiction(addi)
cure_all_traumas(TRAUMA_RESILIENCE_MAGIC)
..()
// heal ears after healing traits, since ears check TRAIT_DEAF trait
@@ -803,7 +803,7 @@
hud_used.staminas?.update_icon_state()
hud_used.staminabuffer?.update_icon_state()
/mob/living/carbon/human/fully_heal(admin_revive = 0)
/mob/living/carbon/human/fully_heal(admin_revive = FALSE)
if(admin_revive)
regenerate_limbs()
regenerate_organs()
@@ -135,7 +135,7 @@
RegisterSignal(owner, COMSIG_CLICK_SHIFT, .proc/examinate_check)
RegisterSignal(src, COMSIG_ATOM_HEARER_IN_VIEW, .proc/include_owner)
RegisterSignal(owner, COMSIG_LIVING_REGENERATE_LIMBS, .proc/unlist_head)
RegisterSignal(owner, COMSIG_LIVING_FULLY_HEAL, .proc/retrieve_head)
RegisterSignal(owner, COMSIG_LIVING_REVIVE, .proc/retrieve_head)
/obj/item/dullahan_relay/proc/examinate_check(atom/source, mob/user)
if(user.client.eye == src)
@@ -148,8 +148,9 @@
/obj/item/dullahan_relay/proc/unlist_head(datum/source, noheal = FALSE, list/excluded_limbs)
excluded_limbs |= BODY_ZONE_HEAD // So we don't gib when regenerating limbs.
/obj/item/dullahan_relay/proc/retrieve_head(datum/source, admin_revive = FALSE)
if(admin_revive) //retrieving the owner's head for ahealing purposes.
//Retrieving the owner's head for better ahealing.
/obj/item/dullahan_relay/proc/retrieve_head(datum/source, full_heal, admin_revive)
if(admin_revive)
var/obj/item/bodypart/head/H = loc
var/turf/T = get_turf(owner)
if(H && istype(H) && T && !(H in owner.GetAllContents()))
@@ -213,6 +213,16 @@
PDA.f_lum = 0
PDA.update_icon()
visible_message("<span class='danger'>The light in [PDA] shorts out!</span>")
else if(istype(O, /obj/item/gun))
var/obj/item/gun/weapon = O
if(weapon.gun_light)
var/obj/item/flashlight/seclite/light = weapon.gun_light
light.forceMove(get_turf(weapon))
light.burn()
weapon.gun_light = null
weapon.update_gunlight()
QDEL_NULL(weapon.alight)
visible_message("<span class='danger'>[light] on [O] flickers out and disintegrates!</span>")
else
visible_message("<span class='danger'>[O] is disintegrated by [src]!</span>")
O.burn()
+2 -8
View File
@@ -481,7 +481,8 @@
med_hud_set_status()
//proc used to ressuscitate a mob
/mob/living/proc/revive(full_heal = 0, admin_revive = 0)
/mob/living/proc/revive(full_heal = FALSE, admin_revive = FALSE)
SEND_SIGNAL(src, COMSIG_LIVING_REVIVE, full_heal, admin_revive)
if(full_heal)
fully_heal(admin_revive)
if(stat == DEAD && can_be_revived()) //in some cases you can't revive (e.g. no brain)
@@ -526,11 +527,6 @@
fire_stacks = 0
confused = 0
update_mobility()
var/datum/component/mood/mood = GetComponent(/datum/component/mood)
if (mood)
QDEL_LIST_ASSOC_VAL(mood.mood_events)
mood.sanity = SANITY_GREAT
mood.update_mood()
//Heal all organs
if(iscarbon(src))
var/mob/living/carbon/C = src
@@ -538,8 +534,6 @@
for(var/organ in C.internal_organs)
var/obj/item/organ/O = organ
O.setOrganDamage(0)
SEND_SIGNAL(src, COMSIG_LIVING_FULLY_HEAL, admin_revive)
//proc called by revive(), to check if we can actually ressuscitate the mob (we don't want to revive him and have him instantly die again)
/mob/living/proc/can_be_revived()
+1 -2
View File
@@ -2,7 +2,6 @@
name = "pAI"
icon = 'icons/mob/pai.dmi'
icon_state = "repairbot"
mouse_opacity = MOUSE_OPACITY_OPAQUE
density = FALSE
pass_flags = PASSTABLE | PASSMOB
mob_size = MOB_SIZE_TINY
@@ -75,7 +74,7 @@
var/emitteroverloadcd = 100
var/radio_short = FALSE
var/radio_short_cooldown = 5 MINUTES
var/radio_short_cooldown = 3 MINUTES
var/radio_short_timerid
mobility_flags = NONE
@@ -56,7 +56,8 @@
if(P.stun)
fold_in(force = TRUE)
visible_message("<span class='warning'>The electrically-charged projectile disrupts [src]'s holomatrix, forcing [src] to fold in!</span>")
return ..()
. = ..()
return BULLET_ACT_FORCE_PIERCE
/mob/living/silicon/pai/stripPanelUnequip(obj/item/what, mob/who, where) //prevents stripping
to_chat(src, "<span class='warning'>Your holochassis stutters and warps intensely as you attempt to interact with the object, forcing you to cease lest the field fail.</span>")
@@ -596,8 +596,8 @@
/obj/item/robot_module/peacekeeper/do_transform_animation()
..()
to_chat(loc, "<span class='userdanger'>Under ASIMOV/CREWSIMOV, you are an enforcer of the PEACE and preventer of HUMAN/CREW HARM. \
You are not a security module and you are expected to follow orders and prevent harm above all else. Space law means nothing to you.</span>")
to_chat(loc, "<span class='userdanger'>Under ASIMOV/CREWSIMOV, you are an enforcer of the PEACE. \
You are not a security module and you are expected to follow orders to the best of your abilities without causing harm. Space law means nothing to you.</span>")
/obj/item/robot_module/peacekeeper/be_transformed_to(obj/item/robot_module/old_module)
var/mob/living/silicon/robot/R = loc
@@ -0,0 +1,91 @@
//shameless copies of carps.
/mob/living/simple_animal/hostile/shark
name = "Space Shark"
desc = "The best terror of the seas, next to the kraken."
icon_state = "shark"
icon_living = "shark"
icon = 'icons/mob/sharks.dmi'
icon_dead = "shark_dead"
icon_gib = "carp_gib"
environment_smash = 0
speak_chance = 0
turns_per_move = 3
butcher_results = list(/obj/item/reagent_containers/food/snacks/carpmeat = 3)
response_help = "pets"
response_disarm = "gently pushes aside"
response_harm = "hits"
speed = 0
maxHealth = 75
health = 75
harm_intent_damage = 18
melee_damage_lower = 18
melee_damage_upper = 18
attacktext = "maims"
attack_sound = 'sound/weapons/bite.ogg'
gold_core_spawnable = 1
//Space shark aren't affected by cold.
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
minbodytemp = 0
maxbodytemp = 1500
faction = list("shark")
/mob/living/simple_animal/hostile/shark/Process_Spacemove(var/movement_dir = 0)
return 1 //No drifting in space for space sharks....either!
/mob/living/simple_animal/hostile/shark/FindTarget()
. = ..()
if(.)
emote("me", 1, "growls at [.]!")
/mob/living/simple_animal/hostile/shark/AttackingTarget()
. =..()
var/mob/living/carbon/L = .
if(istype(L))
if(prob(25))
L.Knockdown(20)
L.visible_message("<span class='danger'>\the [src] knocks down \the [L]!</span>")
/mob/living/simple_animal/hostile/shark/laser
name = "Laser-Shark"
desc = "NOW we've jumped the shark."
icon_state = "lasershark"
icon_living = "lasershark"
icon_dead = "lasershark_dead"
icon_gib = "carp_gib"
ranged = 1
retreat_distance = 3
minimum_distance = 0 //Between shots they can and will close in to nash
projectiletype = /obj/item/projectile/beam/laser/heavylaser
projectilesound = 'sound/weapons/lasercannonfire.ogg'
maxHealth = 50
health = 50
/mob/living/simple_animal/hostile/shark/kawaii
name = "Kawaii Shark"
desc = "Senpai~ Notice me.."
icon_state = "kawaiishark"
icon_living = "kawaiishark"
icon_dead = "kawaiishark_dead"
speak = list("Oh Senpai","Notice me senpai!","Oh my...","Kawaii~")
speak_emote = list("lovingly says","says")
speak_chance = 2
turns_per_move = 3
butcher_results = list(/mob/living/simple_animal/butterfly = 3)
maxHealth = 50
health = 50
maxbodytemp = INFINITY
harm_intent_damage = 0
melee_damage_lower = 0
melee_damage_upper = 0
attacktext = "violently hugs"
vision_range = 0
/mob/living/simple_animal/hostile/shark/kawaii/death()
say("Senpai, you noticed~!")
LoseAggro()
..()
walk(src, 0)
+422
View File
@@ -0,0 +1,422 @@
#define POOL_NO_OVERDOSE_MEDICINE_MAX 5 //max units of no-overdose medicine to allow mobs to have through duplication
//Originally stolen from paradise. Credits to tigercat2000.
//Modified a lot by Kokojo and Tortellini Tony for hippiestation.
//Heavily refactored by tgstation
/obj/machinery/pool
icon = 'icons/obj/machines/pool.dmi'
anchored = TRUE
resistance_flags = INDESTRUCTIBLE
/obj/machinery/pool/controller
name = "\improper Pool Controller"
desc = "An advanced substance generation and fluid tank management system that can refill the contents of a pool to a completely different substance in minutes."
icon_state = "poolc_3"
density = TRUE
use_power = TRUE
idle_power_usage = 75
/// How far it scans for pool objects
var/scan_range = 6
/// Is pool mist currently on?
var/mist_state = FALSE
/// Linked mist effects
var/list/obj/effect/mist/linked_mist = list()
/// Pool turfs
var/list/turf/open/pool/linked_turfs = list()
/// All mobs in pool
var/list/mob/living/mobs_in_pool = list()
/// Is the pool bloody?
var/bloody = 0
/// Last time we process_reagents()'d
var/last_reagent_process = 0
/// Maximum amount we will take from a beaker
var/max_beaker_transfer = 100
/// Minimum amount of a reagent for it to work on us
var/min_reagent_amount = 10
/// ADMINBUS ONLY - WHETHER OR NOT WE HAVE NOREACT ;)
var/noreact_reagents = FALSE
/// how fast in deciseconds between reagent processes
var/reagent_tick_interval = 5
/// Can we use unsafe temperatures
var/temperature_unlocked = FALSE
/// See __DEFINES/pool.dm, temperature defines
var/temperature = POOL_NORMAL
/// Whether or not the pool can be drained
var/drainable = FALSE
// Whether or not the pool is drained
var/drained = FALSE
/// Pool drain
var/obj/machinery/pool/drain/linked_drain
/// Pool filter
var/obj/machinery/pool/filter/linked_filter
/// Next world.time you can interact with settings
var/interact_delay = 0
/// Airlock style shocks
var/shocked = FALSE
/// Old reagent color, used to determine if update_color needs to reset colors.
var/old_rcolor
/// Just to prevent spam
var/draining = FALSE
/// Reagent blacklisting
var/respect_reagent_blacklist = TRUE
/obj/machinery/pool/controller/examine(mob/user)
. = ..()
. += "<span class='boldnotice'>Alt click to drain reagents.</span>"
/obj/machinery/pool/controller/Initialize()
. = ..()
START_PROCESSING(SSfastprocess, src)
create_reagents(1000)
if(noreact_reagents)
reagents.reagents_holder_flags |= NO_REACT
wires = new /datum/wires/poolcontroller(src)
scan_things()
/obj/machinery/pool/controller/Destroy()
STOP_PROCESSING(SSprocessing, src)
linked_drain = null
linked_filter = null
linked_turfs.Cut()
mobs_in_pool.Cut()
return ..()
/obj/machinery/pool/controller/proc/scan_things()
var/list/cached = range(scan_range, src)
for(var/turf/open/pool/W in cached)
linked_turfs += W
W.controller = src
for(var/obj/machinery/pool/drain/pooldrain in cached)
linked_drain = pooldrain
linked_drain.controller = src
break
for(var/obj/machinery/pool/filter/F in cached)
linked_filter = F
linked_filter.controller = src
break
/obj/machinery/pool/controller/emag_act(mob/user)
. = ..()
if(!(obj_flags & EMAGGED)) //If it is not already emagged, emag it.
to_chat(user, "<span class='warning'>You disable the [src]'s safety features.</span>")
do_sparks(5, TRUE, src)
obj_flags |= EMAGGED
temperature_unlocked = TRUE
drainable = TRUE
log_game("[key_name(user)] emagged [src]")
message_admins("[key_name_admin(user)] emagged [src]")
else
to_chat(user, "<span class='warning'>The interface on [src] is already too damaged to short it again.</span>")
return
/obj/machinery/pool/controller/AltClick(mob/user)
. = ..()
if(!isliving(user) || !user.Adjacent(src) || !user.CanReach(src) || user.IsStun() || user.IsKnockdown() || user.incapacitated())
return FALSE
visible_message("<span class='boldwarning'>[user] starts to drain [src]!</span>")
draining = TRUE
if(!do_after(user, 50, target = src))
draining = FALSE
return TRUE
reagents.remove_all(INFINITY)
visible_message("<span class='boldnotice'>[user] drains [src].</span>")
say("Reagents cleared.")
update_color()
draining = FALSE
return TRUE
/obj/machinery/pool/controller/attackby(obj/item/W, mob/user)
if(shocked && !(stat & NOPOWER))
shock(user,50)
if(stat & (BROKEN))
return
if(istype(W,/obj/item/reagent_containers))
if(W.reagents.total_volume) //check if there's reagent
user.visible_message("<span class='boldwarning'>[user] is feeding [src] some chemicals from [W].</span>")
if(do_after(user, 50, target = src))
for(var/datum/reagent/R in W.reagents.reagent_list)
if(R.type in GLOB.blacklisted_pool_reagents)
to_chat(user, "[src] cannot accept [R.name].")
return
if(R.reagent_state == SOLID)
to_chat(user, "The pool cannot accept reagents in solid form!.")
return
reagents.clear_reagents()
// This also reacts them. No nitroglycerin deathpools, sorry gamers :(
W.reagents.trans_to(reagents, max_beaker_transfer)
user.visible_message("<span class='notice'>[src] makes a slurping noise.</span>", "<span class='notice'>All of the contents of [W] are quickly suctioned out by the machine!</span")
updateUsrDialog()
var/list/reagent_names = list()
var/list/rejected = list()
for(var/datum/reagent/R in reagents.reagent_list)
if((R.volume >= min_reagent_amount) && (!respect_reagent_blacklist || R.can_synth))
reagent_names += R.name
else
reagents.remove_reagent(R.type, INFINITY)
rejected += R.name
if(length(reagent_names))
reagent_names = english_list(reagent_names)
var/msg = "POOL: [key_name(user)] has changed [src]'s chems to [reagent_names]"
log_game(msg)
message_admins(msg)
if(length(rejected))
rejected = english_list(rejected)
to_chat(user, "<span class='warning'>[src] rejects the following chemicals as they do not have at least [min_reagent_amount] units of volume: [rejected]</span>")
update_color()
else
to_chat(user, "<span class='notice'>[src] beeps unpleasantly as it rejects the beaker. Why are you trying to feed it an empty beaker?</span>")
return
else if(panel_open && is_wire_tool(W))
wires.interact(user)
else
return ..()
/obj/machinery/pool/controller/screwdriver_act(mob/living/user, obj/item/W)
. = ..()
if(.)
return TRUE
cut_overlays()
panel_open = !panel_open
to_chat(user, "You [panel_open ? "open" : "close"] the maintenance panel.")
W.play_tool_sound(src)
if(panel_open)
add_overlay("wires")
return TRUE
//procs
/obj/machinery/pool/controller/proc/shock(mob/user, prb)
if(stat & (BROKEN|NOPOWER)) // unpowered, no shock
return FALSE
if(!prob(prb))
return FALSE
var/datum/effect_system/spark_spread/s = new /datum/effect_system/spark_spread
s.set_up(5, 1, src)
s.start()
if(electrocute_mob(user, get_area(src), src, 0.7))
return TRUE
else
return FALSE
/obj/machinery/pool/controller/proc/process_reagents()
if(last_reagent_process > world.time + reagent_tick_interval)
return
if(!length(reagents.reagent_list))
return
for(var/turf/open/pool/W in linked_turfs)
for(var/mob/living/carbon/human/swimee in W)
for(var/datum/reagent/R in reagents.reagent_list)
if(R.reagent_state == SOLID)
R.reagent_state = LIQUID
if(!swimee.reagents.has_reagent(POOL_NO_OVERDOSE_MEDICINE_MAX))
swimee.reagents.add_reagent(R.type, 0.5) //osmosis
reagents.reaction(swimee, VAPOR, 0.03) //3 percent. Need to find a way to prevent this from stacking chems at some point like the above.
for(var/obj/objects in W)
if(W.reagents)
W.reagents.reaction(objects, VAPOR, 1)
last_reagent_process = world.time
/obj/machinery/pool/controller/process()
updateUsrDialog()
if(stat & (NOPOWER|BROKEN))
return
if(drained)
return
process_pool()
process_reagents()
/obj/machinery/pool/controller/proc/process_pool()
if(drained)
return
for(var/mob/living/M in mobs_in_pool)
switch(temperature) //Apply different effects based on what the temperature is set to.
if(POOL_SCALDING) //Scalding
M.adjust_bodytemperature(50,0,500)
if(POOL_WARM) //Warm
M.adjust_bodytemperature(20,0,360) //Heats up mobs till the termometer shows up
//Normal temp does nothing, because it's just room temperature water.
if(POOL_COOL)
M.adjust_bodytemperature(-20,250) //Cools mobs till the termometer shows up
if(POOL_FRIGID) //Freezing
M.adjust_bodytemperature(-60) //cool mob at -35k per cycle, less would not affect the mob enough.
if(M.bodytemperature <= 50 && !M.stat)
M.apply_status_effect(/datum/status_effect/freon)
if(ishuman(M))
var/mob/living/carbon/human/drownee = M
if(!drownee || drownee.stat == DEAD)
return
if(drownee.resting && !drownee.internal)
if(drownee.stat != CONSCIOUS)
drownee.adjustOxyLoss(9)
else
drownee.adjustOxyLoss(4)
if(prob(35))
to_chat(drownee, "<span class='danger'>You're drowning!</span>")
/obj/machinery/pool/controller/proc/set_bloody(state)
if(bloody == state)
return
bloody = state
update_color()
/obj/machinery/pool/controller/proc/update_color()
if(drained)
return
var/rcolor
if(reagents.reagent_list.len)
rcolor = mix_color_from_reagents(reagents.reagent_list)
if(rcolor == old_rcolor)
return // small performance upgrade hopefully?
old_rcolor = rcolor
for(var/X in linked_turfs)
var/turf/open/pool/color1 = X
if(bloody)
if(rcolor)
var/thecolor = BlendRGB(rgb(150, 20, 20), rcolor, 0.5)
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
else
var/thecolor = rgb(150, 20, 20)
color1.watereffect.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(thecolor, FIXED_COLOUR_PRIORITY)
else if(!bloody && rcolor)
color1.watereffect.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
color1.watertop.add_atom_colour(rcolor, FIXED_COLOUR_PRIORITY)
else
color1.watereffect.remove_atom_colour(FIXED_COLOUR_PRIORITY)
color1.watertop.remove_atom_colour(FIXED_COLOUR_PRIORITY)
/obj/machinery/pool/controller/proc/update_temp()
if(mist_state)
if(temperature < POOL_SCALDING)
mist_off()
else
if(temperature == POOL_SCALDING)
mist_on()
update_icon()
/obj/machinery/pool/controller/update_icon()
. = ..()
icon_state = "poolc_[temperature]"
/obj/machinery/pool/controller/proc/CanUpTemp(mob/user)
if(temperature == POOL_WARM && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature < POOL_WARM)
return TRUE
return FALSE
/obj/machinery/pool/controller/proc/CanDownTemp(mob/user)
if(temperature == POOL_COOL && (temperature_unlocked || issilicon(user) || IsAdminGhost(user)) || temperature > POOL_COOL)
return TRUE
return FALSE
/obj/machinery/pool/controller/Topic(href, href_list)
if(..())
return
if(interact_delay > world.time)
return
if(href_list["IncreaseTemp"])
if(CanUpTemp(usr))
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
temperature++
update_temp()
var/msg = "POOL: [key_name(usr)] increased [src]'s pool temperature at [COORD(src)] to [temperature]"
log_game(msg)
message_admins(msg)
interact_delay = world.time + 15
if(href_list["DecreaseTemp"])
if(CanDownTemp(usr))
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
temperature--
update_temp()
var/msg = "POOL: [key_name(usr)] decreased [src]'s pool temperature at [COORD(src)] to [temperature]"
log_game(msg)
message_admins(msg)
interact_delay = world.time + 15
if(href_list["Activate Drain"])
if((drainable || issilicon(usr) || IsAdminGhost(usr)) && !linked_drain.active)
var/msg = "POOL: [key_name(usr)] activated [src]'s pool drain in [linked_drain.filling? "FILLING" : "DRAINING"] mode at [COORD(src)]"
log_game(msg)
message_admins(msg)
visible_message("<span class='warning'>[usr] presses a button on [src].</span>")
mist_off()
interact_delay = world.time + 60
linked_drain.active = TRUE
linked_drain.cycles_left = 75
if(!linked_drain.filling)
new /obj/effect/whirlpool(linked_drain.loc)
temperature = POOL_NORMAL
else
new /obj/effect/waterspout(linked_drain.loc)
temperature = POOL_NORMAL
update_temp()
bloody = FALSE
updateUsrDialog()
/obj/machinery/pool/controller/proc/temp2text()
switch(temperature)
if(POOL_FRIGID)
return "<span class='boldwarning'>Frigid</span>"
if(POOL_COOL)
return "<span class='boldnotice'>Cool</span>"
if(POOL_NORMAL)
return "<span class='notice'>Normal</span>"
if(POOL_WARM)
return "<span class='boldnotice'>Warm</span>"
if(POOL_SCALDING)
return "<span class='boldwarning'>Scalding</span>"
else
return "Outside of possible range."
/obj/machinery/pool/controller/ui_interact(mob/user)
. = ..()
if(.)
return
if(shocked && !(stat & NOPOWER))
shock(user,50)
if(panel_open && !isAI(user))
return wires.interact(user)
if(stat & (NOPOWER|BROKEN))
return
var/datum/browser/popup = new(user, "Pool Controller", name, 300, 450)
var/dat = ""
if(interact_delay > world.time)
dat += "<span class='notice'>[round((interact_delay - world.time)/10, 0.1)] seconds left until [src] can operate again.</span><BR>"
dat += text({"
<h3>Temperature</h3>
<div class='statusDisplay'>
<B>Current temperature:</B> [temp2text()]<BR>
[CanUpTemp(user) ? "<a href='?src=\ref[src];IncreaseTemp=1'>Increase Temperature</a><br>" : "<span class='linkOff'>Increase Temperature</span><br>"]
[CanDownTemp(user) ? "<a href='?src=\ref[src];DecreaseTemp=1'>Decrease Temperature</a><br>" : "<span class='linkOff'>Decrease Temperature</span><br>"]
</div>
<h3>Drain</h3>
<div class='statusDisplay'>
<B>Drain status:</B> [(issilicon(user) || IsAdminGhost(user) || drainable) ? "<span class='bad'>Enabled</span>" : "<span class='good'>Disabled</span>"]
<br><b>Pool status:</b> "})
if(!drained)
dat += "<span class='good'>Full</span><BR>"
else
dat += "<span class='bad'>Drained</span><BR>"
if((issilicon(user) || IsAdminGhost(user) || drainable) && !linked_drain.active)
dat += "<a href='?src=\ref[src];Activate Drain=1'>[drained ? "Fill" : "Drain"] Pool</a><br>"
popup.set_content(dat)
popup.open()
/obj/machinery/pool/controller/proc/reset(wire)
switch(wire)
if(WIRE_SHOCK)
if(!wires.is_cut(wire))
shocked = FALSE
/obj/machinery/pool/controller/proc/mist_on() //Spawn /obj/effect/mist (from the shower) on all linked pool tiles
if(mist_state)
return
mist_state = TRUE
for(var/X in linked_turfs)
var/turf/open/pool/W = X
if(W.filled)
var/M = new /obj/effect/mist(W)
linked_mist += M
/obj/machinery/pool/controller/proc/mist_off() //Delete all /obj/effect/mist from all linked pool tiles.
for(var/M in linked_mist)
qdel(M)
mist_state = FALSE
+160
View File
@@ -0,0 +1,160 @@
/obj/machinery/pool/drain
name = "drain"
icon_state = "drain"
desc = "A suction system to remove the contents of the pool, and sometimes small objects. Do not insert fingers."
anchored = TRUE
/// Active/on?
var/active = FALSE
/// Filling or draining
var/filling = FALSE
/// Drain item suction range
var/item_suction_range = 2
/// Fill mode knock away range
var/fill_push_range = 6
/// Drain mode suction range
var/drain_suck_range = 6
/// Parent controller
var/obj/machinery/pool/controller/controller
/// Cycles left for fill/drain while active
var/cycles_left = 0
/// Mobs we are swirling around
var/list/whirling_mobs
/// Suck in once per x ticks
var/suck_in_once_per = 3
var/cooldown
/obj/machinery/pool/drain/Initialize()
START_PROCESSING(SSfastprocess, src)
whirling_mobs = list()
return ..()
/obj/machinery/pool/drain/Destroy()
STOP_PROCESSING(SSfastprocess, src)
controller.linked_drain = null
controller = null
whirling_mobs = null
return ..()
/obj/machinery/pool/drain/proc/is_in_our_pool(atom/A)
. = FALSE
if(istype(A.loc, /turf/open/pool))
var/turf/open/pool/P = A.loc
if(P.controller == controller)
. = TRUE
// This should probably start using move force sometime in the future but I'm lazy.
/obj/machinery/pool/drain/process()
if(!filling)
for(var/obj/item/I in range(min(item_suction_range, 10), src))
if(!I.anchored && (I.w_class <= WEIGHT_CLASS_SMALL))
step_towards(I, src)
if((I.w_class <= WEIGHT_CLASS_TINY) && (get_dist(I, src) == 0))
I.forceMove(controller.linked_filter)
if(active)
if(filling)
if(cycles_left-- > 0)
playsound(src, 'sound/effects/fillingwatter.ogg', 100, TRUE)
for(var/obj/O in orange(min(fill_push_range, 10), src))
if(!O.anchored && is_in_our_pool(O))
step_away(O, src)
for(var/mob/M in orange(min(fill_push_range, 10), src)) //compiler fastpath apparently?
if(!M.anchored && isliving(M) && is_in_our_pool(M))
step_away(M, src)
else
for(var/turf/open/pool/P in controller.linked_turfs)
P.filled = TRUE
P.update_icon()
for(var/obj/effect/waterspout/S in range(1, src))
qdel(S)
controller.drained = FALSE
if(controller.bloody < 1000)
controller.bloody /= 2
else
controller.bloody /= 4
controller.update_color()
filling = FALSE
active = FALSE
else
if(cycles_left-- > 0)
playsound(src, 'sound/effects/pooldrain.ogg', 100, TRUE)
playsound(src, "water_wade", 60, TRUE)
for(var/obj/O in orange(min(drain_suck_range, 10), src))
if(!O.anchored && is_in_our_pool(O))
step_towards(O, src)
for(var/mob/M in orange(min(drain_suck_range, 10), src))
if(isliving(M) && !M.anchored && is_in_our_pool(M))
if(!(cycles_left % suck_in_once_per))
step_towards(M, src)
whirl_mob(M)
if(ishuman(M) && (get_dist(M, src) <= 1))
var/mob/living/carbon/human/H = M
playsound(src, pick('sound/misc/crack.ogg','sound/misc/crunch.ogg'), 50, TRUE)
if(H.lying) //down for any reason
H.adjustBruteLoss(2)
to_chat(H, "<span class='danger'>You're caught in the drain!</span>")
else
H.apply_damage(2.5, BRUTE, pick(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) //drain should only target the legs
to_chat(H, "<span class='danger'>Your legs are caught in the drain!</span>")
else
for(var/turf/open/pool/P in controller.linked_turfs)
P.filled = FALSE
P.update_icon()
for(var/obj/effect/whirlpool/W in range(1, src))
qdel(W)
controller.drained = TRUE
controller.mist_off()
active = FALSE
filling = TRUE
/// dangerous proc don't fuck with, admins
/obj/machinery/pool/drain/proc/whirl_mob(mob/living/L, duration = 8, delay = 1)
set waitfor = FALSE
if(whirling_mobs[L])
return
whirling_mobs[L] = TRUE
for(var/i in 1 to min(duration, 100))
L.setDir(turn(L.dir, 90))
sleep(delay)
if(QDELETED(L))
break
if(QDELETED(src))
return
whirling_mobs -= L
/obj/machinery/pool/filter
name = "Filter"
icon_state = "filter"
desc = "The part of the pool where all the IDs, ATV keys, and pens, and other dangerous things get trapped."
var/obj/machinery/pool/controller/controller
/obj/machinery/pool/filter/Destroy()
controller.linked_filter = null
controller = null
return ..()
/obj/machinery/pool/filter/emag_act(mob/living/user)
. = ..()
if(!(obj_flags & EMAGGED))
to_chat(user, "<span class='warning'>You disable the [src]'s shark filter! Run!</span>")
obj_flags |= EMAGGED
do_sparks(5, TRUE, src)
icon_state = "filter_b"
addtimer(CALLBACK(src, /obj/machinery/pool/filter/proc/spawn_shark), 50)
var/msg = "[key_name(user)] emagged the pool filter and spawned a shark"
log_game(msg)
message_admins(msg)
/obj/machinery/pool/filter/proc/spawn_shark()
if(prob(50))
new /mob/living/simple_animal/hostile/shark(loc)
else
if(prob(50))
new /mob/living/simple_animal/hostile/shark/kawaii(loc)
else
new /mob/living/simple_animal/hostile/shark/laser(loc)
/obj/machinery/pool/filter/attack_hand(mob/user)
to_chat(user, "You search the filter.")
for(var/obj/O in contents)
O.forceMove(loc)
+29
View File
@@ -0,0 +1,29 @@
/obj/effect/splash
name = "splash"
desc = "Wataaa!."
icon = 'icons/turf/pool.dmi'
icon_state = "splash"
layer = ABOVE_ALL_MOB_LAYER
/obj/effect/whirlpool
name = "Whirlpool"
icon = 'icons/effects/96x96.dmi'
icon_state = "whirlpool"
layer = 5
anchored = TRUE
mouse_opacity = 0
pixel_x = -32
pixel_y = -32
alpha = 90
/obj/effect/waterspout
name = "Waterspout"
icon = 'icons/effects/96x96.dmi'
icon_state = "waterspout"
color = "#3399AA"
layer = 5
anchored = TRUE
mouse_opacity = 0
pixel_x = -32
pixel_y = -32
alpha = 120
+191
View File
@@ -0,0 +1,191 @@
/turf/open/pool
icon = 'icons/turf/pool.dmi'
name = "poolwater"
desc = "You're safer here than in the deep."
icon_state = "pool_tile"
heat_capacity = INFINITY
var/filled = TRUE
var/next_splash = 0
var/obj/machinery/pool/controller/controller
var/obj/effect/overlay/water/watereffect
var/obj/effect/overlay/water/top/watertop
/turf/open/pool/Initialize(mapload)
. = ..()
update_icon()
/turf/open/pool/Destroy()
if(controller)
controller.linked_turfs -= src
controller = null
QDEL_NULL(watereffect)
QDEL_NULL(watertop)
return ..()
/turf/open/pool/update_icon()
. = ..()
if(!filled)
name = "drained pool"
desc = "No diving!"
QDEL_NULL(watereffect)
QDEL_NULL(watertop)
else
name = "poolwater"
desc = "You're safer here than in the deep."
watereffect = new /obj/effect/overlay/water(src)
watertop = new /obj/effect/overlay/water/top(src)
/obj/effect/overlay/water
name = "water"
icon = 'icons/turf/pool.dmi'
icon_state = "bottom"
density = FALSE
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
layer = ABOVE_MOB_LAYER
anchored = TRUE
resistance_flags = INDESTRUCTIBLE
/obj/effect/overlay/water/top
icon_state = "top"
layer = BELOW_MOB_LAYER
// Mousedrop hook to normal turfs to get out of pools.
/turf/open/MouseDrop_T(atom/from, mob/user)
// I could make this /open/floor and not have the !istype but ehh - kev
if(isliving(from) && HAS_TRAIT(from, TRAIT_SWIMMING) && isliving(user) && ((user == from) || user.CanReach(from)) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && !istype(src, /turf/open/pool))
var/mob/living/L = from
//The element only exists if you're on water and a living mob, so let's skip those checks.
var/pre_msg
var/post_msg
if(user == from)
pre_msg = "<span class='notice'>[L] is getting out of the pool.</span>"
post_msg = "<span class='notice'>[L] gets out of the pool.</span>"
else
pre_msg = "<span class='notice'>[L] is being pulled out of the pool by [user].</span>"
post_msg = "<span class='notice'>[user] pulls [L] out of the pool.</span>"
L.visible_message(pre_msg)
if(do_mob(user, L, 20))
L.visible_message(post_msg)
L.forceMove(src)
else
return ..()
// Exit check
/turf/open/pool/Exit(atom/movable/AM, atom/newloc)
if(!AM.has_gravity(src))
return ..()
if(isliving(AM) || isstructure(AM))
if(AM.throwing)
return ..() //WHEEEEEEEEEEE
if(istype(AM, /obj/structure) && isliving(AM.pulledby))
return ..() //people pulling stuff out of pool
if(!ishuman(AM))
return ..() //human weak, monkey (and anyone else) ook ook eek eek strong
if(isliving(AM) && (locate(/obj/structure/pool/ladder) in src))
return ..() //climbing out
return istype(newloc, /turf/open/pool)
return ..()
// Exited logic
/turf/open/pool/Exited(atom/A, atom/newLoc)
. = ..()
if(isliving(A))
var/turf/open/pool/P = newLoc
if(!istype(P) || (P.controller != controller))
controller?.mobs_in_pool -= A
// Entered logic
/turf/open/pool/Entered(atom/movable/AM, atom/oldloc)
if(istype(AM, /obj/effect/decal/cleanable))
var/obj/effect/decal/cleanable/C = AM
if(prob(C.bloodiness))
controller.set_bloody(TRUE)
QDEL_IN(AM, 25)
animate(AM, alpha = 10, time = 20)
return ..()
if(!AM.has_gravity(src))
return ..()
if(isliving(AM))
var/mob/living/victim = AM
if(!HAS_TRAIT(victim, TRAIT_SWIMMING)) //poor guy not swimming time to dunk them!
victim.AddElement(/datum/element/swimming)
controller.mobs_in_pool += victim
if(locate(/obj/structure/pool/ladder) in src) //safe climbing
return
if(iscarbon(AM)) //FUN TIME!
var/mob/living/carbon/H = victim
if(filled)
if (H.wear_mask && H.wear_mask.flags_cover & MASKCOVERSMOUTH)
H.visible_message("<span class='danger'>[H] falls in the water!</span>",
"<span class='userdanger'>You fall in the water!</span>")
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
H.Knockdown(20)
return
else
H.Knockdown(60)
H.adjustOxyLoss(5)
H.emote("cough")
H.visible_message("<span class='danger'>[H] falls in and takes a drink!</span>",
"<span class='userdanger'>You fall in and swallow some water!</span>")
playsound(src, 'sound/effects/splash.ogg', 60, TRUE, 1)
else if(!H.head || !(H.head.armor.getRating("melee") > 20))
if(prob(75))
H.visible_message("<span class='danger'>[H] falls in the drained pool!</span>",
"<span class='userdanger'>You fall in the drained pool!</span>")
H.adjustBruteLoss(7)
H.Knockdown(80)
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
else
H.visible_message("<span class='danger'>[H] falls in the drained pool, and cracks his skull!</span>",
"<span class='userdanger'>You fall in the drained pool, and crack your skull!</span>")
H.apply_damage(15, BRUTE, "head")
H.Knockdown(200) // This should hurt. And it does.
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
playsound(src, 'sound/misc/crack.ogg', 100, TRUE)
else
H.visible_message("<span class='danger'>[H] falls in the drained pool, but had an helmet!</span>",
"<span class='userdanger'>You fall in the drained pool, but you had an helmet!</span>")
H.Knockdown(40)
playsound(src, 'sound/effects/woodhit.ogg', 60, TRUE, 1)
else if(filled)
victim.adjustStaminaLoss(1)
playsound(src, "water_wade", 20, TRUE)
return ..()
/turf/open/pool/MouseDrop_T(atom/from, mob/user)
. = ..()
if(!isliving(from))
return
var/mob/living/victim = from
if(user.stat || user.lying || !Adjacent(user) || !from.Adjacent(user) || !iscarbon(user) || !victim.has_gravity(src) || HAS_TRAIT(victim, TRAIT_SWIMMING))
return
var/victimname = victim == user? "themselves" : "[victim]"
var/starttext = victim == user? "[user] is descending into [src]." : "[user] is lowering [victim] into [src]."
user.visible_message("<span class='notice'>[starttext]</span>")
if(do_mob(user, victim, 20))
user.visible_message("<span class='notice'>[user] lowers [victimname] into [src].</span>")
victim.AddElement(/datum/element/swimming) //make sure they have it so they don't fall/whatever
victim.forceMove(src)
/turf/open/pool/attackby(obj/item/W, mob/living/user)
if(istype(W, /obj/item/mop) && filled)
W.reagents.add_reagent("water", 5)
to_chat(user, "<span class='notice'>You wet [W] in [src].</span>")
playsound(loc, 'sound/effects/slosh.ogg', 25, TRUE)
else
return ..()
/turf/open/pool/attack_hand(mob/living/user)
. = ..()
if(.)
return
if((user.loc != src) && !user.IsStun() && !user.IsKnockdown() && !user.incapacitated() && Adjacent(user) && HAS_TRAIT(user, TRAIT_SWIMMING) && filled && (next_splash < world.time))
playsound(src, 'sound/effects/watersplash.ogg', 8, TRUE, 1)
next_splash = world.time + 25
var/obj/effect/splash/S = new(src)
animate(S, alpha = 0, time = 8)
QDEL_IN(S, 10)
for(var/mob/living/carbon/human/H in src)
if(!H.wear_mask && (H.stat == CONSCIOUS))
H.emote("cough")
H.adjustStaminaLoss(4)
+32
View File
@@ -0,0 +1,32 @@
//Pool noodles
/obj/item/toy/poolnoodle
icon = 'icons/obj/toy.dmi'
icon_state = "noodle"
name = "pool noodle"
desc = "A strange, bulky, bendable toy that can annoy people."
force = 0
color = "#000000"
w_class = WEIGHT_CLASS_SMALL
throwforce = 1
throw_speed = 10 //weeee
hitsound = 'sound/weapons/tap.ogg'
attack_verb = list("flogged", "poked", "jabbed", "slapped", "annoyed")
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
/obj/item/toy/poolnoodle/attack(target as mob, mob/living/user as mob)
. = ..()
user.spin(prob(20)? 16 : 8, 1)
/obj/item/toy/poolnoodle/red
item_state = "noodlered"
color = "#ff4c4c"
/obj/item/toy/poolnoodle/blue
item_state = "noodleblue"
color = "#3232ff"
/obj/item/toy/poolnoodle/yellow
item_state = "noodleyellow"
color = "#ffff66"
+159
View File
@@ -0,0 +1,159 @@
/obj/structure/pool
name = "pool"
icon = 'icons/obj/machines/pool.dmi'
anchored = TRUE
resistance_flags = UNACIDABLE|INDESTRUCTIBLE
/obj/structure/pool/ladder
name = "Ladder"
icon_state = "ladder"
desc = "Are you getting in or are you getting out?."
layer = ABOVE_MOB_LAYER
dir = EAST
/obj/structure/pool/ladder/attack_hand(mob/living/user)
. = ..()
if(.)
return
if(!HAS_TRAIT(user, TRAIT_SWIMMING))
if(user.CanReach(src))
user.AddElement(/datum/element/swimming)
user.forceMove(get_step(src, dir))
else
if(user.loc == loc)
user.forceMove(get_step(src, turn(dir, 180))) //If this moves them out the element cleans up after itself.
/obj/structure/pool/Rboard
name = "JumpBoard"
density = FALSE
icon_state = "boardright"
desc = "The less-loved portion of the jumping board."
dir = EAST
/obj/structure/pool/Lboard
name = "JumpBoard"
icon_state = "boardleft"
desc = "Get on there to jump!"
layer = FLY_LAYER
dir = WEST
var/jumping = FALSE
var/timer
/obj/structure/pool/Lboard/proc/backswim()
if(jumping)
for(var/mob/living/jumpee in loc) //hackzors.
playsound(jumpee, 'sound/effects/splash.ogg', 60, TRUE, 1)
if(!HAS_TRAIT(jumpee, TRAIT_SWIMMING))
jumpee.AddElement(/datum/element/swimming)
jumpee.Stun(2)
/obj/structure/pool/Lboard/proc/reset_position(mob/user, initial_layer, initial_px, initial_py)
user.layer = initial_layer
user.pixel_x = initial_px
user.pixel_y = initial_py
/obj/structure/pool/Lboard/attack_hand(mob/living/user)
if(iscarbon(user))
var/mob/living/carbon/jumper = user
if(jumping)
to_chat(user, "<span class='notice'>Someone else is already making a jump!</span>")
return
var/turf/T = get_turf(src)
if(HAS_TRAIT(user, TRAIT_SWIMMING))
return
else
if(Adjacent(jumper))
jumper.visible_message("<span class='notice'>[user] climbs up \the [src]!</span>", \
"<span class='notice'>You climb up \the [src] and prepares to jump!</span>")
jumper.Stun(40)
jumping = TRUE
var/original_layer = jumper.layer
var/original_px = jumper.pixel_x
var/original_py = jumper.pixel_y
jumper.layer = RIPPLE_LAYER
jumper.pixel_x = 3
jumper.pixel_y = 7
jumper.dir = WEST
jumper.AddElement(/datum/element/swimming)
sleep(1)
jumper.forceMove(T)
addtimer(CALLBACK(src, .proc/dive, jumper, original_layer, original_px, original_py), 10)
/obj/structure/pool/Lboard/proc/dive(mob/living/carbon/jumper, original_layer, original_px, original_py)
switch(rand(1, 100))
if(1 to 20)
jumper.visible_message("<span class='notice'>[jumper] goes for a small dive!</span>", \
"<span class='notice'>You go for a small dive.</span>")
sleep(15)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(21 to 40)
jumper.visible_message("<span class='notice'>[jumper] goes for a dive!</span>", \
"<span class='notice'>You're going for a dive!</span>")
sleep(20)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 2, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(41 to 60)
jumper.visible_message("<span class='notice'>[jumper] goes for a long dive! Stay far away!</span>", \
"<span class='notice'>You're going for a long dive!!</span>")
sleep(25)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 3, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(61 to 80)
jumper.visible_message("<span class='notice'>[jumper] goes for an awesome dive! Don't stand in [jumper.p_their()] way!</span>", \
"<span class='notice'>You feel like this dive will be awesome</span>")
sleep(30)
backswim()
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 4, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
if(81 to 91)
sleep(20)
backswim()
jumper.visible_message("<span class='danger'>[jumper] misses [jumper.p_their()] step!</span>", \
"<span class='userdanger'>You misstep!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 0, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
jumper.Knockdown(100)
jumper.adjustBruteLoss(10)
if(91 to 100)
jumper.visible_message("<span class='notice'>[jumper] is preparing for the legendary dive! Can [jumper.p_they()] make it?</span>", \
"<span class='userdanger'>You start preparing for a legendary dive!</span>")
jumper.SpinAnimation(7,1)
sleep(30)
if(prob(75))
backswim()
jumper.visible_message("<span class='notice'>[jumper] fails!</span>", \
"<span class='userdanger'>You can't quite do it!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 1, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
else
jumper.fire_stacks = min(1,jumper.fire_stacks + 1)
jumper.IgniteMob()
sleep(5)
backswim()
jumper.visible_message("<span class='danger'>[jumper] bursts into flames of pure awesomness!</span>", \
"<span class='userdanger'>No one can stop you now!</span>")
var/atom/throw_target = get_edge_target_turf(src, dir)
jumper.throw_at(throw_target, 6, 1, callback = CALLBACK(src, .proc/on_finish_jump, jumper))
addtimer(CALLBACK(src, .proc/togglejumping), 35)
reset_position(jumper, original_layer, original_px, original_py)
/obj/structure/pool/Lboard/proc/togglejumping()
jumping = FALSE
/obj/structure/pool/Lboard/proc/on_finish_jump(mob/living/victim)
if(istype(victim.loc, /turf/open/pool))
var/turf/open/pool/P = victim.loc
if(!P.filled) //you dun fucked up now
to_chat(victim, "<span class='warning'>That was stupid of you..</span>")
victim.visible_message("<span class='danger'>[victim] smashes into the ground!</span>")
victim.apply_damage(50)
victim.Knockdown(200)
+59
View File
@@ -0,0 +1,59 @@
#define POOL_WIRE_DRAIN "drain"
#define POOL_WIRE_TEMP "temp"
/datum/wires/poolcontroller
holder_type = /obj/machinery/pool/controller
proper_name = "Pool"
/datum/wires/poolcontroller/New(atom/holder)
wires = list(
POOL_WIRE_DRAIN, WIRE_SHOCK, WIRE_ZAP, POOL_WIRE_TEMP
)
add_duds(3)
..()
/datum/wires/poolcontroller/interactable(mob/user)
var/obj/machinery/pool/controller/P = holder
if(P.panel_open)
return TRUE
/datum/wires/poolcontroller/get_status()
var/obj/machinery/pool/controller/P = holder
var/list/status = list()
status += "The blue light is [P.drainable ? "on" : "off"]."
status += "The red light is [P.temperature_unlocked ? "on" : "off"]."
status += "The yellow light is [P.shocked ? "on" : "off"]."
return status
/datum/wires/poolcontroller/on_pulse(wire)
var/obj/machinery/pool/controller/P = holder
switch(wire)
if(POOL_WIRE_DRAIN)
P.drainable = FALSE
if(POOL_WIRE_TEMP)
P.temperature_unlocked = FALSE
if(WIRE_SHOCK)
P.shocked = !P.shocked
addtimer(CALLBACK(P, /obj/machinery/autolathe.proc/reset, wire), 60)
/datum/wires/poolcontroller/on_cut(wire, mend)
var/obj/machinery/pool/controller/P = holder
switch(wire)
if(POOL_WIRE_DRAIN)
if(mend)
P.drainable = FALSE
else
P.drainable = TRUE
if(POOL_WIRE_TEMP)
if(mend)
P.temperature_unlocked = FALSE
else
P.temperature_unlocked = TRUE
if(WIRE_ZAP)
P.shock(usr, 50)
if(WIRE_SHOCK)
if(mend)
P.stat &= ~NOPOWER
else
P.stat |= NOPOWER
@@ -346,6 +346,7 @@ GLOBAL_DATUM(main_supermatter_engine, /obj/machinery/power/supermatter_crystal)
// Pass all the gas related code an empty gas container
removed = new()
damage = min(damage_archived + (DAMAGE_HARDCAP * explosion_point),damage) // hardcap any direct damage taken before doing atmos damage
damage_archived = damage
if(!removed || !removed.total_moles() || isspaceturf(T)) //we're in space or there is no gas to process
if(takes_damage)
+11 -3
View File
@@ -77,6 +77,8 @@
/datum/reagents/Destroy()
. = ..()
//We're about to delete all reagents, so lets cleanup
addiction_list.Cut()
var/list/cached_reagents = reagent_list
for(var/reagent in cached_reagents)
var/datum/reagent/R = reagent
@@ -332,9 +334,7 @@
if(R.addiction_stage3_end to R.addiction_stage4_end)
need_mob_update += R.addiction_act_stage4(C)
if(R.addiction_stage4_end to INFINITY)
to_chat(C, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_addiction")
cached_addictions.Remove(R)
remove_addiction(R)
else
SEND_SIGNAL(C, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
addiction_tick++
@@ -344,6 +344,12 @@
C.update_stamina()
update_total()
/datum/reagents/proc/remove_addiction(datum/reagent/R)
to_chat(my_atom, "<span class='notice'>You feel like you've gotten over your need for [R.name].</span>")
SEND_SIGNAL(my_atom, COMSIG_CLEAR_MOOD_EVENT, "[R.type]_overdose")
addiction_list.Remove(R)
qdel(R)
//Signals that metabolization has stopped, triggering the end of trait-based effects
/datum/reagents/proc/end_metabolization(mob/living/carbon/C, keep_liverless = TRUE)
var/list/cached_reagents = reagent_list
@@ -762,6 +768,8 @@
R.metabolizing = FALSE
R.on_mob_end_metabolize(M)
R.on_mob_delete(M)
//Clear from relevant lists
addiction_list -= R
qdel(R)
reagent_list -= R
update_total()
@@ -168,7 +168,7 @@ GLOBAL_LIST_INIT(food_reagents, build_reagents_to_food()) //reagentid = related
/obj/item/paper/secretrecipe
name = "old recipe"
var/recipe_id = "secretsauce"
var/recipe_id = /datum/reagent/consumable/secretsauce
/obj/item/paper/secretrecipe/examine(mob/user) //Extra secret
if(isobserver(user))
@@ -53,7 +53,7 @@
SEND_SIGNAL(A, COMSIG_COMPONENT_CLEAN_ACT, CLEAN_MEDIUM)
return
/obj/item/reagent_containers/rag/pre_altattackby(mob/living/M, mob/living/user, params)
/obj/item/reagent_containers/rag/alt_pre_attack(mob/living/M, mob/living/user, params)
if(istype(M) && user.a_intent == INTENT_HELP)
user.changeNext_move(CLICK_CD_MELEE)
if(M.on_fire)
@@ -289,6 +289,16 @@
////////////Janitor Designs//////////////
/////////////////////////////////////////
/datum/design/broom
name = "Broom"
desc = "Just your everyday standard broom."
id = "broom"
build_type = PROTOLATHE
materials = list(MAT_METAL = 1000, MAT_GLASS = 600)
build_path = /obj/item/twohanded/broom
category = list("Equipment")
departmental_flags = DEPARTMENTAL_FLAG_SERVICE
/datum/design/mop
name = "Mop"
desc = "Just your everyday standard mop."
+57 -18
View File
@@ -50,7 +50,7 @@
name = "Host Scan"
desc = "The nanites display a detailed readout of a body scan to the host."
id = "selfscan_nanites"
program_type = /datum/nanite_program/triggered/self_scan
program_type = /datum/nanite_program/self_scan
category = list("Utility Nanites")
/datum/design/nanites/dermal_button
@@ -67,7 +67,6 @@
program_type = /datum/nanite_program/stealth
category = list("Utility Nanites")
/datum/design/nanites/reduced_diagnostics
name = "Reduced Diagnostics"
desc = "Disables some high-cost diagnostics in the nanites, making them unable to communicate their program list to portable scanners. \
@@ -80,7 +79,7 @@
name = "Subdermal ID"
desc = "The nanites store the host's ID access rights in a subdermal magnetic strip. Updates when triggered, copying the host's current access."
id = "access_nanites"
program_type = /datum/nanite_program/triggered/access
program_type = /datum/nanite_program/access
category = list("Utility Nanites")
/datum/design/nanites/relay
@@ -106,9 +105,9 @@
/datum/design/nanites/emp
name = "Electromagnetic Resonance"
desc = "The nanites cause an elctromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
desc = "The nanites cause an electromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
id = "emp_nanites"
program_type = /datum/nanite_program/triggered/emp
program_type = /datum/nanite_program/emp
category = list("Utility Nanites")
/datum/design/nanites/spreading
@@ -119,6 +118,13 @@
program_type = /datum/nanite_program/spreading
category = list("Utility Nanites")
/datum/design/nanites/nanite_sting
name = "Nanite Sting"
desc = "When triggered, projects a nearly invisible spike of nanites that attempts to infect a nearby non-host with a copy of the host's nanites cluster."
id = "nanite_sting_nanites"
program_type = /datum/nanite_program/nanite_sting
category = list("Utility Nanites")
/datum/design/nanites/mitosis
name = "Mitosis"
desc = "The nanites gain the ability to self-replicate, using bluespace to power the process, instead of drawing from a template. This rapidly speeds up the replication rate,\
@@ -197,7 +203,7 @@
name = "Defibrillation"
desc = "The nanites, when triggered, send a defibrillating shock to the host's heart."
id = "defib_nanites"
program_type = /datum/nanite_program/triggered/defib
program_type = /datum/nanite_program/defib
category = list("Medical Nanites")
@@ -242,7 +248,7 @@
name = "Adrenaline Burst"
desc = "The nanites cause a burst of adrenaline when triggered, waking the host from stuns and temporarily increasing their speed."
id = "adrenaline_nanites"
program_type = /datum/nanite_program/triggered/adrenaline
program_type = /datum/nanite_program/adrenaline
category = list("Augmentation Nanites")
/datum/design/nanites/mindshield
@@ -366,21 +372,21 @@
name = "Heart-Stopper"
desc = "Stops the host's heart when triggered; restarts it if triggered again."
id = "heartstop_nanites"
program_type = /datum/nanite_program/triggered/heart_stop
program_type = /datum/nanite_program/heart_stop
category = list("Weaponized Nanites")
/datum/design/nanites/explosive
name = "Chain Detonation"
desc = "Blows up all the nanites inside the host in a chain reaction when triggered."
id = "explosive_nanites"
program_type = /datum/nanite_program/triggered/explosive
program_type = /datum/nanite_program/explosive
category = list("Weaponized Nanites")
/datum/design/nanites/mind_control
name = "Mind Control"
desc = "The nanites imprint an absolute directive onto the host's brain while they're active."
id = "mindcontrol_nanites"
program_type = /datum/nanite_program/triggered/comm/mind_control
program_type = /datum/nanite_program/comm/mind_control
category = list("Weaponized Nanites")
////////////////////SUPPRESSION NANITES//////////////////////////////////////
@@ -389,21 +395,21 @@
name = "Electric Shock"
desc = "The nanites shock the host when triggered. Destroys a large amount of nanites!"
id = "shock_nanites"
program_type = /datum/nanite_program/triggered/shocking
program_type = /datum/nanite_program/shocking
category = list("Suppression Nanites")
/datum/design/nanites/stun
name = "Neural Shock"
desc = "The nanites pulse the host's nerves when triggered, inapacitating them for a short period."
id = "stun_nanites"
program_type = /datum/nanite_program/triggered/stun
program_type = /datum/nanite_program/stun
category = list("Suppression Nanites")
/datum/design/nanites/sleepy
name = "Sleep Induction"
desc = "The nanites cause rapid narcolepsy when triggered."
id = "sleep_nanites"
program_type = /datum/nanite_program/triggered/sleepy
program_type = /datum/nanite_program/sleepy
category = list("Suppression Nanites")
/datum/design/nanites/paralyzing
@@ -445,21 +451,21 @@
name = "Skull Echo"
desc = "The nanites echo a synthesized message inside the host's skull."
id = "voice_nanites"
program_type = /datum/nanite_program/triggered/comm/voice
program_type = /datum/nanite_program/comm/voice
category = list("Suppression Nanites")
/datum/design/nanites/speech
name = "Forced Speech"
desc = "The nanites force the host to say a pre-programmed sentence when triggered."
id = "speech_nanites"
program_type = /datum/nanite_program/triggered/comm/speech
program_type = /datum/nanite_program/comm/speech
category = list("Suppression Nanites")
/datum/design/nanites/hallucination
name = "Hallucination"
desc = "The nanites make the host see and hear things that aren't real."
id = "hallucination_nanites"
program_type = /datum/nanite_program/triggered/comm/hallucination
program_type = /datum/nanite_program/comm/hallucination
category = list("Suppression Nanites")
/datum/design/nanites/good_mood
@@ -513,9 +519,42 @@
program_type = /datum/nanite_program/sensor/voice
category = list("Sensor Nanites")
/datum/design/nanites/sensor__nanite_volume
/datum/design/nanites/sensor_nanite_volume
name = "Nanite Volume Sensor"
desc = "The nanites receive a signal when the nanite supply is above/below a certain percentage."
id = "sensor_nanite_volume"
program_type = /datum/nanite_program/sensor/nanite_volume
category = list("Sensor Nanites")
category = list("Sensor Nanites")
////////////////////NANITE PROTOCOLS//////////////////////////////////////
//Note about the category name: The UI cuts the last 8 characters from the category name to remove the " Nanites" in the other categories
//Because of this, Protocols was getting cut down to "P", so i had to add some padding
/*
/datum/design/nanites/kickstart
name = "Kickstart Protocol"
desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation."
id = "kickstart_nanites"
program_type = /datum/nanite_program/protocol/kickstart
category = list("Protocols_Nanites")
/datum/design/nanites/factory
name = "Factory Protocol"
desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time. The factory decays if the protocol is not active."
id = "factory_nanites"
program_type = /datum/nanite_program/protocol/factory
category = list("Protocols_Nanites")
/datum/design/nanites/tinker
name = "Tinker Protocol"
desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream to speed up the replication process."
id = "tinker_nanites"
program_type = /datum/nanite_program/protocol/tinker
category = list("Protocols_Nanites")
/datum/design/nanites/offline
name = "Offline Production Protocol"
desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
id = "offline_nanites"
program_type = /datum/nanite_program/protocol/offline
category = list("Protocols_Nanites")
*/
@@ -0,0 +1,20 @@
/datum/nanite_extra_setting
var/setting_type
var/value
/datum/nanite_extra_setting/proc/get_value()
return value
/datum/nanite_extra_setting/proc/set_value(value)
src.value = value
/datum/nanite_extra_setting/proc/get_copy()
return
//I made the choice to send the name as part of the parameter instead of storing it directly on
//this datum as a way of avoiding duplication of data between the containing assoc list
//and this datum.
//Also make sure to double wrap the list when implementing this as
//+= is interpreted as a combine on lists, so the outer list gets unwrapped
/datum/nanite_extra_setting/proc/get_frontend_list(name)
return
@@ -0,0 +1,27 @@
/datum/nanite_extra_setting/boolean
setting_type = NESTYPE_BOOLEAN
var/true_text
var/false_text
/datum/nanite_extra_setting/boolean/New(initial, true_text, false_text)
value = initial
src.true_text = true_text
src.false_text = false_text
/datum/nanite_extra_setting/boolean/set_value(value)
if(isnull(value))
src.value = !src.value
return
. = ..()
/datum/nanite_extra_setting/boolean/get_copy()
return new /datum/nanite_extra_setting/boolean(value, true_text, false_text)
/datum/nanite_extra_setting/boolean/get_frontend_list(name)
return list(list(
"name" = name,
"type" = setting_type,
"value" = value,
"true_text" = true_text,
"false_text" = false_text
))
@@ -0,0 +1,32 @@
/datum/nanite_extra_setting/number
setting_type = NESTYPE_NUMBER
var/min
var/max
var/unit = ""
/datum/nanite_extra_setting/number/New(initial, min, max, unit)
value = initial
src.min = min
src.max = max
if(unit)
src.unit = unit
/datum/nanite_extra_setting/number/set_value(value)
if(istext(value))
value = text2num(value)
if(!value || !isnum(value))
return
src.value = CLAMP(value, min, max)
/datum/nanite_extra_setting/number/get_copy()
return new /datum/nanite_extra_setting/number(value, min, max, unit)
/datum/nanite_extra_setting/number/get_frontend_list(name)
return list(list(
"name" = name,
"type" = setting_type,
"value" = value,
"min" = min,
"max" = max,
"unit" = unit
))
@@ -0,0 +1,18 @@
/datum/nanite_extra_setting/text
setting_type = NESTYPE_TEXT
/datum/nanite_extra_setting/text/New(initial)
value = initial
/datum/nanite_extra_setting/text/set_value(value)
src.value = trim(value)
/datum/nanite_extra_setting/text/get_copy()
return new /datum/nanite_extra_setting/text(value)
/datum/nanite_extra_setting/text/get_frontend_list(name)
return list(list(
"name" = name,
"type" = setting_type,
"value" = value
))
@@ -0,0 +1,18 @@
/datum/nanite_extra_setting/type
setting_type = NESTYPE_TYPE
var/list/types
/datum/nanite_extra_setting/type/New(initial, types)
value = initial
src.types = types
/datum/nanite_extra_setting/type/get_copy()
return new /datum/nanite_extra_setting/type(value, types)
/datum/nanite_extra_setting/type/get_frontend_list(name)
return list(list(
"name" = name,
"type" = setting_type,
"value" = value,
"types" = types
))
+32 -58
View File
@@ -77,7 +77,7 @@
return
occupant.AddComponent(/datum/component/nanites, 100)
/obj/machinery/nanite_chamber/proc/install_program(datum/nanite_program/NP)
/obj/machinery/nanite_chamber/proc/remove_nanites(datum/nanite_program/NP)
if(stat & (NOPOWER|BROKEN))
return
if((stat & MAINT) || panel_open)
@@ -88,74 +88,46 @@
var/locked_state = locked
locked = TRUE
//TODO COMPUTERY MACHINE SOUNDS
set_busy(TRUE, "Initializing installation protocol...", "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Connecting to nanite framework...", "[initial(icon_state)]_active"),20)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Installing program...", "[initial(icon_state)]_falling"),35)
addtimer(CALLBACK(src, .proc/complete_installation, locked_state, NP),55)
//TODO OMINOUS MACHINE SOUNDS
set_busy(TRUE, "Initializing cleanup protocol...", "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Analyzing host bio-structure...", "[initial(icon_state)]_active"),20)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Pinging nanites...", "[initial(icon_state)]_active"),40)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Initiating graceful self-destruct sequence...", "[initial(icon_state)]_active"),70)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Removing debris...", "[initial(icon_state)]_falling"),110)
addtimer(CALLBACK(src, .proc/complete_removal, locked_state),130)
/obj/machinery/nanite_chamber/proc/complete_installation(locked_state, datum/nanite_program/NP)
/obj/machinery/nanite_chamber/proc/complete_removal(locked_state)
//TODO MACHINE DING
locked = locked_state
set_busy(FALSE)
if(!occupant)
return
SEND_SIGNAL(occupant, COMSIG_NANITE_DELETE)
SEND_SIGNAL(occupant, COMSIG_NANITE_ADD_PROGRAM, NP.copy())
/obj/machinery/nanite_chamber/proc/uninstall_program(datum/nanite_program/NP)
if(stat & (NOPOWER|BROKEN))
return
if((stat & MAINT) || panel_open)
return
if(!occupant || busy)
return
var/locked_state = locked
locked = TRUE
//TODO COMPUTERY MACHINE SOUNDS
set_busy(TRUE, "Initializing uninstallation protocol...", "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Connecting to nanite framework...", "[initial(icon_state)]_active"),20)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "Uninstalling program...", "[initial(icon_state)]_falling"),35)
addtimer(CALLBACK(src, .proc/complete_uninstallation, locked_state, NP),55)
/obj/machinery/nanite_chamber/proc/complete_uninstallation(locked_state, datum/nanite_program/NP)
//TODO MACHINE DING
locked = locked_state
set_busy(FALSE)
if(!occupant)
return
qdel(NP)
/obj/machinery/nanite_chamber/update_icon()
cut_overlays()
if((stat & MAINT) || panel_open)
add_overlay("maint")
else if(!(stat & (NOPOWER|BROKEN)))
if(busy || locked)
add_overlay("red")
if(locked)
add_overlay("bolted")
else
add_overlay("green")
/obj/machinery/nanite_chamber/update_icon_state()
//running and someone in there
if(occupant)
if(busy)
icon_state = busy_icon_state
else
icon_state = initial(icon_state)+ "_occupied"
return
icon_state = initial(icon_state) + "_occupied"
else
//running
icon_state = initial(icon_state) + (state_open ? "_open" : "")
//running
icon_state = initial(icon_state)+ (state_open ? "_open" : "")
/obj/machinery/nanite_chamber/update_overlays()
. = ..()
/obj/machinery/nanite_chamber/power_change()
..()
update_icon()
if((stat & MAINT) || panel_open)
. += "maint"
else if(!(stat & (NOPOWER|BROKEN)))
if(busy || locked)
. += "red"
if(locked)
. += "bolted"
else
. += "green"
/obj/machinery/nanite_chamber/proc/toggle_open(mob/user)
if(panel_open)
@@ -182,7 +154,7 @@
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the door of [src]!</span>", \
"<span class='notice'>You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a metallic creaking from [src].</span>")
"<span class='hear'>You hear a metallic creaking from [src].</span>")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked || busy)
return
@@ -231,6 +203,8 @@
toggle_open(user)
/obj/machinery/nanite_chamber/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) || !Adjacent(target) || !user.Adjacent(target) || !iscarbon(target))
return
close_machine(target)
if(close_machine(target))
log_combat(user, target, "inserted", null, "into [src].")
add_fingerprint(user)
@@ -5,30 +5,13 @@
var/obj/item/disk/nanite_program/disk
circuit = /obj/item/circuitboard/computer/nanite_chamber_control
icon_screen = "nanite_chamber_control"
ui_x = 380
ui_y = 570
/obj/machinery/computer/nanite_chamber_control/Initialize()
. = ..()
find_chamber()
/obj/machinery/computer/nanite_chamber_control/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
if(disk)
eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "<span class='notice'>You insert [N] into [src]</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
disk = N
else
..()
/obj/machinery/computer/nanite_chamber_control/proc/eject(mob/living/user)
if(!disk)
return
if(!istype(user) || !Adjacent(user) || !user.put_in_active_hand(disk))
disk.forceMove(drop_location())
disk = null
/obj/machinery/computer/nanite_chamber_control/proc/find_chamber()
for(var/direction in GLOB.cardinals)
var/C = locate(/obj/machinery/nanite_chamber, get_step(src, direction))
@@ -43,41 +26,13 @@
..()
/obj/machinery/computer/nanite_chamber_control/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_chamber_control", name, 550, 800, master_ui, state)
ui = new(user, src, ui_key, "nanite_chamber_control", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/nanite_chamber_control/ui_data()
var/list/data = list()
if(disk)
data["has_disk"] = TRUE
var/list/disk_data = list()
var/datum/nanite_program/P = disk.program
if(P)
data["has_program"] = TRUE
disk_data["name"] = P.name
disk_data["desc"] = P.desc
disk_data["activated"] = P.activated
disk_data["activation_delay"] = P.activation_delay
disk_data["timer"] = P.timer
disk_data["activation_code"] = P.activation_code
disk_data["deactivation_code"] = P.deactivation_code
disk_data["kill_code"] = P.kill_code
disk_data["trigger_code"] = P.trigger_code
disk_data["timer_type"] = P.get_timer_type_text()
var/list/extra_settings = list()
for(var/X in P.extra_settings)
var/list/setting = list()
setting["name"] = X
setting["value"] = P.get_extra_setting(X)
extra_settings += list(setting)
disk_data["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
disk_data["has_extra_settings"] = TRUE
data["disk"] = disk_data
if(!chamber)
data["status_msg"] = "No chamber detected."
@@ -97,6 +52,7 @@
data["status_msg"] = chamber.busy_message
return data
data["status_msg"] = null
data["scan_level"] = chamber.scan_level
data["locked"] = chamber.locked
data["occupant_name"] = chamber.occupant.name
@@ -113,46 +69,32 @@
chamber.locked = !chamber.locked
chamber.update_icon()
. = TRUE
if("eject")
eject(usr)
. = TRUE
if("set_safety")
var/threshold = input("Set safety threshold (0-500):", name, null) as null|num
var/threshold = text2num(params["value"])
if(!isnull(threshold))
chamber.set_safety(CLAMP(round(threshold, 1),0,500))
playsound(src, "terminal_type", 25, 0)
investigate_log("[key_name(chamber.occupant)]'s nanites' safety threshold was set to [threshold] by [key_name(usr)].", INVESTIGATE_NANITES)
playsound(src, "terminal_type", 25, FALSE)
chamber.occupant.investigate_log("'s nanites' safety threshold was set to [threshold] by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
if("set_cloud")
var/cloud_id = input("Set cloud ID (1-100, 0 to disable):", name, null) as null|num
var/cloud_id = text2num(params["value"])
if(!isnull(cloud_id))
chamber.set_cloud(CLAMP(round(cloud_id, 1),0,100))
playsound(src, "terminal_type", 25, 0)
investigate_log("[key_name(chamber.occupant)]'s nanites' cloud id was set to [cloud_id] by [key_name(usr)].", INVESTIGATE_NANITES)
playsound(src, "terminal_type", 25, FALSE)
chamber.occupant.investigate_log("'s nanites' cloud id was set to [cloud_id] by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
if("connect_chamber")
find_chamber()
. = TRUE
if("remove_nanites")
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
chamber.remove_nanites()
log_combat(usr, chamber.occupant, "cleared nanites from", null, "via [src]")
chamber.occupant.investigate_log("'s nanites were cleared by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
if("nanite_injection")
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
chamber.inject_nanites()
investigate_log("[key_name(chamber.occupant)] was injected with nanites by [key_name(usr)] using a nanite chamber.", INVESTIGATE_NANITES)
log_combat(usr, chamber.occupant, "injected", null, "with nanites via [src]")
chamber.occupant.investigate_log("was injected with nanites by [key_name(usr)] via [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
. = TRUE
if("add_program")
if(!disk || !chamber || !chamber.occupant)
return
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, 0)
chamber.install_program(disk.program)
investigate_log("Program of type [disk.program.type] was installed into [key_name(chamber.occupant)]'s nanites with a nanite chamber by [key_name(usr)].", INVESTIGATE_NANITES)
. = TRUE
if("remove_program")
if(!chamber || !chamber.occupant)
return
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, 0)
var/list/nanite_programs = list()
SEND_SIGNAL(chamber.occupant, COMSIG_NANITE_GET_PROGRAMS, nanite_programs)
if(LAZYLEN(nanite_programs))
var/datum/nanite_program/P = nanite_programs[text2num(params["program_id"])]
chamber.uninstall_program(P)
investigate_log("Program of type [P.type] was uninstalled from [key_name(chamber.occupant)]'s nanites with a nanite chamber by [key_name(usr)].", INVESTIGATE_NANITES)
. = TRUE
@@ -4,9 +4,13 @@
circuit = /obj/item/circuitboard/computer/nanite_cloud_controller
icon = 'icons/obj/machines/research.dmi'
icon_state = "nanite_cloud_controller"
ui_x = 375
ui_y = 700
var/obj/item/disk/nanite_program/disk
var/list/datum/nanite_cloud_backup/cloud_backups = list()
var/current_view = 0 //0 is the main menu, any other number is the page of the backup with that ID
var/new_backup_id = 1
/obj/machinery/computer/nanite_cloud_controller/Destroy()
QDEL_LIST(cloud_backups) //rip backups
@@ -19,8 +23,8 @@
if(disk)
eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "<span class='notice'>You insert [N] into [src]</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
to_chat(user, "<span class='notice'>You insert [N] into [src].</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
disk = N
else
..()
@@ -50,13 +54,14 @@
investigate_log("[key_name(user)] created a new nanite cloud backup with id #[cloud_id]", INVESTIGATE_NANITES)
/obj/machinery/computer/nanite_cloud_controller/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_cloud_control", name, 600, 800, master_ui, state)
ui = new(user, src, ui_key, "nanite_cloud_control", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/computer/nanite_cloud_controller/ui_data()
var/list/data = list()
if(disk)
data["has_disk"] = TRUE
var/list/disk_data = list()
@@ -71,24 +76,28 @@
disk_data["trigger_cooldown"] = P.trigger_cooldown / 10
disk_data["activated"] = P.activated
disk_data["activation_delay"] = P.activation_delay
disk_data["timer"] = P.timer
disk_data["activation_code"] = P.activation_code
disk_data["deactivation_code"] = P.deactivation_code
disk_data["kill_code"] = P.kill_code
disk_data["trigger_code"] = P.trigger_code
disk_data["timer_type"] = P.get_timer_type_text()
disk_data["timer_restart"] = P.timer_restart / 10
disk_data["timer_shutdown"] = P.timer_shutdown / 10
disk_data["timer_trigger"] = P.timer_trigger / 10
disk_data["timer_trigger_delay"] = P.timer_trigger_delay / 10
var/list/extra_settings = list()
for(var/X in P.extra_settings)
var/list/setting = list()
setting["name"] = X
setting["value"] = P.get_extra_setting(X)
extra_settings += list(setting)
var/list/extra_settings = P.get_extra_settings_frontend()
disk_data["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
disk_data["has_extra_settings"] = TRUE
if(istype(P, /datum/nanite_program/sensor))
var/datum/nanite_program/sensor/sensor = P
if(sensor.can_rule)
disk_data["can_rule"] = TRUE
data["disk"] = disk_data
else
data["has_disk"] = FALSE
data["new_backup_id"] = new_backup_id
data["current_view"] = current_view
if(current_view)
@@ -108,19 +117,30 @@
cloud_program["trigger_cost"] = P.trigger_cost
cloud_program["trigger_cooldown"] = P.trigger_cooldown / 10
cloud_program["activated"] = P.activated
cloud_program["activation_delay"] = P.activation_delay
cloud_program["timer"] = P.timer
cloud_program["timer_type"] = P.get_timer_type_text()
cloud_program["timer_restart"] = P.timer_restart / 10
cloud_program["timer_shutdown"] = P.timer_shutdown / 10
cloud_program["timer_trigger"] = P.timer_trigger / 10
cloud_program["timer_trigger_delay"] = P.timer_trigger_delay / 10
cloud_program["activation_code"] = P.activation_code
cloud_program["deactivation_code"] = P.deactivation_code
cloud_program["kill_code"] = P.kill_code
cloud_program["trigger_code"] = P.trigger_code
var/list/extra_settings = list()
for(var/X in P.extra_settings)
var/list/setting = list()
setting["name"] = X
setting["value"] = P.get_extra_setting(X)
extra_settings += list(setting)
var/list/rules = list()
var/rule_id = 1
for(var/X in P.rules)
var/datum/nanite_rule/nanite_rule = X
var/list/rule = list()
rule["display"] = nanite_rule.display()
rule["program_id"] = id
rule["id"] = rule_id
rules += list(rule)
rule_id++
cloud_program["rules"] = rules
if(LAZYLEN(rules))
cloud_program["has_rules"] = TRUE
var/list/extra_settings = P.get_extra_settings_frontend()
cloud_program["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
cloud_program["has_extra_settings"] = TRUE
@@ -147,17 +167,20 @@
if("set_view")
current_view = text2num(params["view"])
. = TRUE
if("update_new_backup_value")
var/backup_value = text2num(params["value"])
new_backup_id = backup_value
if("create_backup")
var/cloud_id = input("Choose a cloud ID (1-100):", name, null) as null|num
var/cloud_id = new_backup_id
if(!isnull(cloud_id))
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
cloud_id = CLAMP(round(cloud_id, 1),1,100)
generate_backup(cloud_id, usr)
. = TRUE
if("delete_backup")
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
qdel(backup)
investigate_log("[key_name(usr)] deleted the nanite cloud backup #[current_view]", INVESTIGATE_NANITES)
. = TRUE
@@ -165,7 +188,7 @@
if(disk && disk.program)
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
var/datum/component/nanites/nanites = backup.nanites
nanites.add_program(null, disk.program.copy())
investigate_log("[key_name(usr)] uploaded program [disk.program.name] to cloud #[current_view]", INVESTIGATE_NANITES)
@@ -173,12 +196,37 @@
if("remove_program")
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, FALSE)
var/datum/component/nanites/nanites = backup.nanites
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
investigate_log("[key_name(usr)] deleted program [P.name] from cloud #[current_view]", INVESTIGATE_NANITES)
qdel(P)
. = TRUE
if("add_rule")
if(disk && disk.program && istype(disk.program, /datum/nanite_program/sensor))
var/datum/nanite_program/sensor/rule_template = disk.program
if(!rule_template.can_rule)
return
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
var/datum/component/nanites/nanites = backup.nanites
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
var/datum/nanite_rule/rule = rule_template.make_rule(P)
investigate_log("[key_name(usr)] added rule [rule.display()] to program [P.name] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
if("remove_rule")
var/datum/nanite_cloud_backup/backup = get_backup(current_view)
if(backup)
playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0)
var/datum/component/nanites/nanites = backup.nanites
var/datum/nanite_program/P = nanites.programs[text2num(params["program_id"])]
var/datum/nanite_rule/rule = P.rules[text2num(params["rule_id"])]
rule.remove()
investigate_log("[key_name(usr)] removed rule [rule.display()] from program [P.name] in cloud #[current_view]", INVESTIGATE_NANITES)
. = TRUE
/datum/nanite_cloud_backup
var/cloud_id = 0
@@ -193,4 +241,4 @@
/datum/nanite_cloud_backup/Destroy()
storage.cloud_backups -= src
SSnanites.cloud_backups -= src
return ..()
return ..()
@@ -1,157 +0,0 @@
/obj/item/nanite_hijacker
name = "nanite remote control" //fake name
desc = "A device that can load nanite programming disks, edit them at will, and imprint them to nanites remotely."
w_class = WEIGHT_CLASS_SMALL
icon = 'icons/obj/device.dmi'
icon_state = "nanite_remote"
item_flags = NOBLUDGEON
var/obj/item/disk/nanite_program/disk
var/datum/nanite_program/program
/obj/item/nanite_hijacker/AltClick(mob/user)
. = ..()
if(!user.canUseTopic(src, BE_CLOSE))
return
if(disk)
eject()
return TRUE
/obj/item/nanite_hijacker/examine(mob/user)
. = ..()
if(disk)
. += "<span class='notice'>Alt-click [src] to eject the disk.</span>"
/obj/item/nanite_hijacker/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
var/obj/item/disk/nanite_program/N = I
if(disk)
eject()
if(user.transferItemToLoc(N, src))
to_chat(user, "<span class='notice'>You insert [N] into [src]</span>")
disk = N
program = N.program
else
..()
/obj/item/nanite_hijacker/proc/eject(mob/living/user)
if(!disk)
return
if(!istype(user) || !Adjacent(user) || !user.put_in_hand(disk))
disk.forceMove(drop_location())
disk = null
program = null
/obj/item/nanite_hijacker/afterattack(atom/target, mob/user, etc)
if(!disk || !disk.program)
return
if(isliving(target))
var/success = SEND_SIGNAL(target, COMSIG_NANITE_ADD_PROGRAM, program.copy())
switch(success)
if(NONE)
to_chat(user, "<span class='notice'>You don't detect any nanites in [target].</span>")
if(COMPONENT_PROGRAM_INSTALLED)
to_chat(user, "<span class='notice'>You insert the currently loaded program into [target]'s nanites.</span>")
if(COMPONENT_PROGRAM_NOT_INSTALLED)
to_chat(user, "<span class='warning'>You try to insert the currently loaded program into [target]'s nanites, but the installation fails.</span>")
//Same UI as the nanite programmer, as it pretty much does the same
/obj/item/nanite_hijacker/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_programmer", "Internal Nanite Programmer", 420, 800, master_ui, state)
ui.open()
/obj/item/nanite_hijacker/ui_data()
var/list/data = list()
data["has_disk"] = istype(disk)
data["has_program"] = istype(program)
if(program)
data["name"] = program.name
data["desc"] = program.desc
data["use_rate"] = program.use_rate
data["can_trigger"] = program.can_trigger
data["trigger_cost"] = program.trigger_cost
data["trigger_cooldown"] = program.trigger_cooldown / 10
data["activated"] = program.activated
data["activation_delay"] = program.activation_delay
data["timer"] = program.timer
data["activation_code"] = program.activation_code
data["deactivation_code"] = program.deactivation_code
data["kill_code"] = program.kill_code
data["trigger_code"] = program.trigger_code
data["timer_type"] = program.get_timer_type_text()
var/list/extra_settings = list()
for(var/X in program.extra_settings)
var/list/setting = list()
setting["name"] = X
setting["value"] = program.get_extra_setting(X)
extra_settings += list(setting)
data["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
data["has_extra_settings"] = TRUE
return data
/obj/item/nanite_hijacker/ui_act(action, params)
if(..())
return
switch(action)
if("eject")
eject(usr)
. = TRUE
if("toggle_active")
program.activated = !program.activated //we don't use the activation procs since we aren't in a mob
if(program.activated)
program.activation_delay = 0
. = TRUE
if("set_code")
var/new_code = input("Set code (0000-9999):", name, null) as null|num
if(!isnull(new_code))
new_code = CLAMP(round(new_code, 1),0,9999)
else
return
var/target_code = params["target_code"]
switch(target_code)
if("activation")
program.activation_code = CLAMP(round(new_code, 1),0,9999)
if("deactivation")
program.deactivation_code = CLAMP(round(new_code, 1),0,9999)
if("kill")
program.kill_code = CLAMP(round(new_code, 1),0,9999)
if("trigger")
program.trigger_code = CLAMP(round(new_code, 1),0,9999)
. = TRUE
if("set_extra_setting")
program.set_extra_setting(usr, params["target_setting"])
. = TRUE
if("set_activation_delay")
var/delay = input("Set activation delay in seconds (0-1800):", name, program.activation_delay) as null|num
if(!isnull(delay))
delay = CLAMP(round(delay, 1),0,1800)
program.activation_delay = delay
if(delay)
program.activated = FALSE
. = TRUE
if("set_timer")
var/timer = input("Set timer in seconds (10-3600):", name, program.timer) as null|num
if(!isnull(timer))
if(!timer == 0)
timer = CLAMP(round(timer, 1),10,3600)
program.timer = timer
. = TRUE
if("set_timer_type")
var/new_type = input("Choose the timer effect","Timer Effect") as null|anything in list("Deactivate","Self-Delete","Trigger","Reset Activation Timer")
if(new_type)
switch(new_type)
if("Deactivate")
program.timer_type = NANITE_TIMER_DEACTIVATE
if("Self-Delete")
program.timer_type = NANITE_TIMER_SELFDELETE
if("Trigger")
program.timer_type = NANITE_TIMER_TRIGGER
if("Reset Activation Timer")
program.timer_type = NANITE_TIMER_RESET
. = TRUE
@@ -6,4 +6,4 @@
icon_state = "nanite_remote"
/obj/item/nanite_injector/attack_self(mob/user)
user.AddComponent(/datum/component/nanites, 150)
user.AddComponent(/datum/component/nanites, 150)
@@ -7,18 +7,21 @@
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
ui_x = 500
ui_y = 700
var/obj/item/disk/nanite_program/disk
var/datum/techweb/linked_techweb
var/current_category = "Main"
var/detail_view = FALSE
var/detail_view = TRUE
var/categories = list(
list(name = "Utility Nanites"),
list(name = "Medical Nanites"),
list(name = "Sensor Nanites"),
list(name = "Augmentation Nanites"),
list(name = "Suppression Nanites"),
list(name = "Weaponized Nanites")
list(name = "Weaponized Nanites"),
//list(name = "Protocols") B.E.P.I.S Content, which we dont have
)
/obj/machinery/nanite_program_hub/Initialize()
@@ -31,8 +34,8 @@
if(disk)
eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "<span class='notice'>You insert [N] into [src]</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
to_chat(user, "<span class='notice'>You insert [N] into [src].</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
disk = N
else
..()
@@ -45,10 +48,9 @@
disk = null
/obj/machinery/nanite_program_hub/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_program_hub", name, 500, 700, master_ui, state)
ui.set_autoupdate(FALSE) //to avoid making the whole program list every second
ui = new(user, src, ui_key, "nanite_program_hub", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nanite_program_hub/ui_data()
@@ -62,25 +64,31 @@
disk_data["name"] = P.name
disk_data["desc"] = P.desc
data["disk"] = disk_data
else
data["has_disk"] = FALSE
data["detail_view"] = detail_view
data["category"] = current_category
if(current_category != "Main")
var/list/program_list = list()
for(var/i in linked_techweb.researched_designs)
var/datum/design/nanites/D = SSresearch.techweb_design_by_id(i)
if(!istype(D))
continue
if(current_category in D.category)
var/list/program_design = list()
program_design["id"] = D.id
program_design["name"] = D.name
program_design["desc"] = D.desc
program_list += list(program_design)
data["program_list"] = program_list
else
data["categories"] = categories
return data
/obj/machinery/nanite_program_hub/ui_static_data(mob/user)
var/list/data = list()
data["programs"] = list()
for(var/i in linked_techweb.researched_designs)
var/datum/design/nanites/D = SSresearch.techweb_design_by_id(i)
if(!istype(D))
continue
var/cat_name = D.category[1] //just put them in the first category fuck it
if(isnull(data["programs"][cat_name]))
data["programs"][cat_name] = list()
var/list/program_design = list()
program_design["id"] = D.id
program_design["name"] = D.name
program_design["desc"] = D.desc
data["programs"][cat_name] += list(program_design)
if(!length(data["programs"]))
data["programs"] = null
return data
@@ -101,11 +109,10 @@
qdel(disk.program)
disk.program = new downloaded.program_type
disk.name = "[initial(disk.name)] \[[disk.program.name]\]"
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, 0)
playsound(src, 'sound/machines/terminal_prompt.ogg', 25, FALSE)
. = TRUE
if("set_category")
var/new_category = params["category"]
current_category = new_category
if("refresh")
update_static_data(usr)
. = TRUE
if("toggle_details")
detail_view = !detail_view
@@ -9,6 +9,9 @@
use_power = IDLE_POWER_USE
anchored = TRUE
density = TRUE
flags_1 = HEAR_1
ui_x = 420
ui_y = 550
/obj/machinery/nanite_programmer/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/disk/nanite_program))
@@ -17,7 +20,7 @@
eject(user)
if(user.transferItemToLoc(N, src))
to_chat(user, "<span class='notice'>You insert [N] into [src]</span>")
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, 0)
playsound(src, 'sound/machines/terminal_insert_disc.ogg', 50, FALSE)
disk = N
program = N.program
else
@@ -32,9 +35,9 @@
program = null
/obj/machinery/nanite_programmer/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.default_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_programmer", name, 600, 800, master_ui, state)
ui = new(user, src, ui_key, "nanite_programmer", name, ui_x, ui_y, master_ui, state)
ui.open()
/obj/machinery/nanite_programmer/ui_data()
@@ -50,20 +53,16 @@
data["trigger_cooldown"] = program.trigger_cooldown / 10
data["activated"] = program.activated
data["activation_delay"] = program.activation_delay
data["timer"] = program.timer
data["activation_code"] = program.activation_code
data["deactivation_code"] = program.deactivation_code
data["kill_code"] = program.kill_code
data["trigger_code"] = program.trigger_code
data["timer_type"] = program.get_timer_type_text()
data["timer_restart"] = program.timer_restart / 10
data["timer_shutdown"] = program.timer_shutdown / 10
data["timer_trigger"] = program.timer_trigger / 10
data["timer_trigger_delay"] = program.timer_trigger_delay / 10
var/list/extra_settings = list()
for(var/X in program.extra_settings)
var/list/setting = list()
setting["name"] = X
setting["value"] = program.get_extra_setting(X)
extra_settings += list(setting)
var/list/extra_settings = program.get_extra_settings_frontend()
data["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
data["has_extra_settings"] = TRUE
@@ -78,20 +77,12 @@
eject(usr)
. = TRUE
if("toggle_active")
playsound(src, "terminal_type", 25, 0)
playsound(src, "terminal_type", 25, FALSE)
program.activated = !program.activated //we don't use the activation procs since we aren't in a mob
if(program.activated)
program.activation_delay = 0
. = TRUE
if("set_code")
var/new_code = input("Set code (0000-9999):", name, null) as null|num
if(!isnull(new_code))
playsound(src, "terminal_type", 25, 0)
new_code = CLAMP(round(new_code, 1),0,9999)
else
return
playsound(src, "terminal_type", 25, 0)
var/new_code = text2num(params["code"])
playsound(src, "terminal_type", 25, FALSE)
var/target_code = params["target_code"]
switch(target_code)
if("activation")
@@ -104,37 +95,44 @@
program.trigger_code = CLAMP(round(new_code, 1),0,9999)
. = TRUE
if("set_extra_setting")
program.set_extra_setting(usr, params["target_setting"])
playsound(src, "terminal_type", 25, 0)
program.set_extra_setting(params["target_setting"], params["value"])
playsound(src, "terminal_type", 25, FALSE)
. = TRUE
if("set_activation_delay")
var/delay = input("Set activation delay in seconds (0-1800):", name, program.activation_delay) as null|num
if(!isnull(delay))
playsound(src, "terminal_type", 25, 0)
delay = CLAMP(round(delay, 1),0,1800)
program.activation_delay = delay
if(delay)
program.activated = FALSE
. = TRUE
if("set_timer")
var/timer = input("Set timer in seconds (10-3600):", name, program.timer) as null|num
if("set_restart_timer")
var/timer = text2num(params["delay"])
if(!isnull(timer))
playsound(src, "terminal_type", 25, 0)
if(!timer == 0)
timer = CLAMP(round(timer, 1),10,3600)
program.timer = timer
playsound(src, "terminal_type", 25, FALSE)
timer = CLAMP(round(timer, 1), 0, 3600)
timer *= 10 //convert to deciseconds
program.timer_restart = timer
. = TRUE
if("set_timer_type")
var/new_type = input("Choose the timer effect","Timer Effect") as null|anything in list("Deactivate","Self-Delete","Trigger","Reset Activation Timer")
if(new_type)
playsound(src, "terminal_type", 25, 0)
switch(new_type)
if("Deactivate")
program.timer_type = NANITE_TIMER_DEACTIVATE
if("Self-Delete")
program.timer_type = NANITE_TIMER_SELFDELETE
if("Trigger")
program.timer_type = NANITE_TIMER_TRIGGER
if("Reset Activation Timer")
program.timer_type = NANITE_TIMER_RESET
. = TRUE
if("set_shutdown_timer")
var/timer = text2num(params["delay"])
if(!isnull(timer))
playsound(src, "terminal_type", 25, FALSE)
timer = CLAMP(round(timer, 1), 0, 3600)
timer *= 10 //convert to deciseconds
program.timer_shutdown = timer
. = TRUE
if("set_trigger_timer")
var/timer = text2num(params["delay"])
if(!isnull(timer))
playsound(src, "terminal_type", 25, FALSE)
timer = CLAMP(round(timer, 1), 0, 3600)
timer *= 10 //convert to deciseconds
program.timer_trigger = timer
. = TRUE
if("set_timer_trigger_delay")
var/timer = text2num(params["delay"])
if(!isnull(timer))
playsound(src, "terminal_type", 25, FALSE)
timer = CLAMP(round(timer, 1), 0, 3600)
timer *= 10 //convert to deciseconds
program.timer_trigger_delay = timer
. = TRUE
/obj/machinery/nanite_programmer/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode, atom/movable/source)
. = ..()
var/static/regex/when = regex("(?:^\\W*when|when\\W*$)", "i") //starts or ends with when
if(findtext(raw_message, when) && !istype(speaker, /obj/machinery/nanite_programmer))
say("When you code it!!")
+131 -75
View File
@@ -11,7 +11,7 @@
var/trigger_cost = 0 //Amount of nanites required to trigger
var/trigger_cooldown = 50 //Deciseconds required between each trigger activation
var/next_trigger = 0 //World time required for the next trigger activation
var/timer_counter = 0 //Counts up while active. Used for the timer and the activation delay.
var/program_flags = NONE
var/passive_enabled = FALSE //If the nanites have an on/off-style effect, it's tracked by this var
@@ -28,9 +28,17 @@
//The following vars are customizable
var/activated = TRUE //If FALSE, the program won't process, disables passive effects, can't trigger and doesn't consume nanites
var/activation_delay = 0 //Seconds before the program self-activates.
var/timer = 0 //Seconds before the timer effect activates. Starts counting AFTER the activation delay
var/timer_type = NANITE_TIMER_DEACTIVATE //What happens when the timer runs out
var/timer_restart = 0 //When deactivated, the program will wait X deciseconds before self-reactivating. Also works if the program begins deactivated.
var/timer_shutdown = 0 //When activated, the program will wait X deciseconds before self-deactivating. Also works if the program begins activated.
var/timer_trigger = 0 //[Trigger only] While active, the program will attempt to trigger once every x deciseconds.
var/timer_trigger_delay = 0 //[Trigger only] While active, the program will delay trigger signals by X deciseconds.
//Indicates the next world.time tick where these timers will act
var/timer_restart_next = 0
var/timer_shutdown_next = 0
var/timer_trigger_next = 0
var/timer_trigger_delay_next = 0
//Signal codes, these handle remote input to the nanites. If set to 0 they'll ignore signals.
var/activation_code = 0 //Code that activates the program [1-9999]
@@ -39,19 +47,19 @@
var/trigger_code = 0 //Code that triggers the program (if available) [1-9999]
//Extra settings
//Must be listed in text form, with the same title they'll be displayed in the programmer UI
//Changing these values is handled by set_extra_setting()
//Viewing these values is handled by get_extra_setting()
//Copying these values is handled by copy_extra_settings_to()
///Don't ever override this or I will come to your house and stand menacingly behind a bush
var/list/extra_settings = list()
/datum/nanite_program/triggered
use_rate = 0
trigger_cost = 5
trigger_cooldown = 50
can_trigger = TRUE
//Rules
//Rules that automatically manage if the program's active without requiring separate sensor programs
var/list/datum/nanite_rule/rules = list()
/datum/nanite_program/New()
. = ..()
register_extra_settings()
/datum/nanite_program/Destroy()
extra_settings = null
if(host_mob)
if(activated)
deactivate()
@@ -64,39 +72,60 @@
/datum/nanite_program/proc/copy()
var/datum/nanite_program/new_program = new type()
new_program.activated = activated
new_program.activation_delay = activation_delay
new_program.timer = timer
new_program.timer_type = timer_type
new_program.activation_code = activation_code
new_program.deactivation_code = deactivation_code
new_program.kill_code = kill_code
new_program.trigger_code = trigger_code
copy_extra_settings_to(new_program)
copy_programming(new_program, TRUE)
return new_program
/datum/nanite_program/proc/copy_programming(datum/nanite_program/target, copy_activated = TRUE)
if(copy_activated)
target.activated = activated
target.activation_delay = activation_delay
target.timer = timer
target.timer_type = timer_type
target.timer_restart = timer_restart
target.timer_shutdown = timer_shutdown
target.timer_trigger = timer_trigger
target.timer_trigger_delay = timer_trigger_delay
target.activation_code = activation_code
target.deactivation_code = deactivation_code
target.kill_code = kill_code
target.trigger_code = trigger_code
copy_extra_settings_to(target)
/datum/nanite_program/proc/set_extra_setting(user, setting)
target.rules = list()
for(var/R in rules)
var/datum/nanite_rule/rule = R
rule.copy_to(target)
if(istype(target,src))
copy_extra_settings_to(target)
///Register extra settings by overriding this.
///extra_settings[name] = new typepath() for each extra setting
/datum/nanite_program/proc/register_extra_settings()
return
/datum/nanite_program/proc/get_extra_setting(setting)
return
///You can override this if you need to have special behavior after setting certain settings.
/datum/nanite_program/proc/set_extra_setting(setting, value)
var/datum/nanite_extra_setting/ES = extra_settings[setting]
return ES.set_value(value)
///You probably shouldn't be overriding this one, but I'm not a cop.
/datum/nanite_program/proc/get_extra_setting_value(setting)
var/datum/nanite_extra_setting/ES = extra_settings[setting]
return ES.get_value()
///Used for getting information about the extra settings to the frontend
/datum/nanite_program/proc/get_extra_settings_frontend()
var/list/out = list()
for(var/name in extra_settings)
var/datum/nanite_extra_setting/ES = extra_settings[name]
out += ES.get_frontend_list(name)
return out
///Copy of the list instead of direct reference for obvious reasons
/datum/nanite_program/proc/copy_extra_settings_to(datum/nanite_program/target)
return
var/list/copy_list = list()
for(var/ns_name in extra_settings)
var/datum/nanite_extra_setting/extra_setting = extra_settings[ns_name]
copy_list[ns_name] = extra_setting.get_copy()
target.extra_settings = copy_list
/datum/nanite_program/proc/on_add(datum/component/nanites/_nanites)
nanites = _nanites
@@ -105,10 +134,12 @@
/datum/nanite_program/proc/on_mob_add()
host_mob = nanites.host_mob
if(activated) //apply activation effects if it starts active
if(activated) //apply activation effects depending on initial status; starts the restart and shutdown timers
activate()
else
deactivate()
datum/nanite_program/proc/on_mob_remove()
/datum/nanite_program/proc/on_mob_remove()
return
/datum/nanite_program/proc/toggle()
@@ -119,36 +150,35 @@ datum/nanite_program/proc/on_mob_remove()
/datum/nanite_program/proc/activate()
activated = TRUE
timer_counter = activation_delay
if(timer_shutdown)
timer_shutdown_next = world.time + timer_shutdown
/datum/nanite_program/proc/deactivate()
if(passive_enabled)
disable_passive_effect()
activated = FALSE
if(timer_restart)
timer_restart_next = world.time + timer_restart
/datum/nanite_program/proc/on_process()
timer_counter++
if(activation_delay)
if(activated && timer_counter < activation_delay)
deactivate()
else if(!activated && timer_counter >= activation_delay)
activate()
if(!activated)
if(timer_restart_next && world.time > timer_restart_next)
activate()
timer_restart_next = 0
return
if(timer && timer_counter > timer)
if(timer_type == NANITE_TIMER_DEACTIVATE)
deactivate()
return
else if(timer_type == NANITE_TIMER_SELFDELETE)
qdel(src)
return
else if(can_trigger && timer_type == NANITE_TIMER_TRIGGER)
trigger()
timer_counter = activation_delay
else if(timer_type == NANITE_TIMER_RESET)
timer_counter = 0
if(timer_shutdown_next && world.time > timer_shutdown_next)
deactivate()
timer_shutdown_next = 0
if(timer_trigger && world.time > timer_trigger_next)
trigger()
timer_trigger_next = world.time + timer_trigger
if(timer_trigger_delay_next && world.time > timer_trigger_delay_next)
trigger(delayed = TRUE)
timer_trigger_delay_next = 0
if(check_conditions() && consume_nanites(use_rate))
if(!passive_enabled)
enable_passive_effect()
@@ -160,6 +190,10 @@ datum/nanite_program/proc/on_mob_remove()
//If false, disables active and passive effects, but doesn't consume nanites
//Can be used to avoid consuming nanites for nothing
/datum/nanite_program/proc/check_conditions()
for(var/R in rules)
var/datum/nanite_rule/rule = R
if(!rule.check_rule())
return FALSE
return TRUE
//Constantly procs as long as the program is active
@@ -174,15 +208,25 @@ datum/nanite_program/proc/on_mob_remove()
/datum/nanite_program/proc/disable_passive_effect()
passive_enabled = FALSE
/datum/nanite_program/proc/trigger()
//Checks conditions then fires the nanite trigger effect
/datum/nanite_program/proc/trigger(delayed = FALSE, comm_message)
if(!can_trigger)
return
if(!activated)
return FALSE
return
if(timer_trigger_delay && !delayed)
timer_trigger_delay_next = world.time + timer_trigger_delay
return
if(world.time < next_trigger)
return FALSE
return
if(!consume_nanites(trigger_cost))
return FALSE
return
next_trigger = world.time + trigger_cooldown
return TRUE
on_trigger(comm_message)
//Nanite trigger effect, requires can_trigger to be used
/datum/nanite_program/proc/on_trigger(comm_message)
return
/datum/nanite_program/proc/consume_nanites(amount, force = FALSE)
return nanites.consume_nanites(amount, force)
@@ -234,24 +278,36 @@ datum/nanite_program/proc/on_mob_remove()
/datum/nanite_program/proc/receive_signal(code, source)
if(activation_code && code == activation_code && !activated)
activate()
host_mob.investigate_log("[host_mob]'s [name] nanite program was activated by [source] with code [code].", INVESTIGATE_NANITES)
host_mob.investigate_log("'s [name] nanite program was activated by [source] with code [code].", INVESTIGATE_NANITES)
else if(deactivation_code && code == deactivation_code && activated)
deactivate()
host_mob.investigate_log("[host_mob]'s [name] nanite program was deactivated by [source] with code [code].", INVESTIGATE_NANITES)
if(kill_code && code == kill_code)
host_mob.investigate_log("[host_mob]'s [name] nanite program was deleted by [source] with code [code].", INVESTIGATE_NANITES)
qdel(src)
else if(can_trigger && trigger_code && code == trigger_code)
host_mob.investigate_log("'s [name] nanite program was deactivated by [source] with code [code].", INVESTIGATE_NANITES)
if(can_trigger && trigger_code && code == trigger_code)
trigger()
host_mob.investigate_log("[host_mob]'s [name] nanite program was triggered by [source] with code [code].", INVESTIGATE_NANITES)
host_mob.investigate_log("'s [name] nanite program was triggered by [source] with code [code].", INVESTIGATE_NANITES)
if(kill_code && code == kill_code)
host_mob.investigate_log("'s [name] nanite program was deleted by [source] with code [code].", INVESTIGATE_NANITES)
qdel(src)
///A nanite program containing a behaviour protocol. Only one protocol of each class can be active at once.
//Currently unused due to us lacking the B.E.P.I.S
/datum/nanite_program/protocol
name = "Nanite Protocol"
var/protocol_class = NONE
/datum/nanite_program/protocol/check_conditions()
. = ..()
for(var/protocol in nanites.protocols)
var/datum/nanite_program/protocol/P = protocol
if(P != src && P.activated && P.protocol_class == protocol_class)
return FALSE
/datum/nanite_program/protocol/on_add(datum/component/nanites/_nanites)
..()
nanites.protocols += src
/datum/nanite_program/protocol/Destroy()
if(nanites)
nanites.protocols -= src
return ..()
/datum/nanite_program/proc/get_timer_type_text()
switch(timer_type)
if(NANITE_TIMER_DEACTIVATE)
return "Deactivate"
if(NANITE_TIMER_SELFDELETE)
return "Self-Delete"
if(NANITE_TIMER_TRIGGER)
return "Trigger"
if(NANITE_TIMER_RESET)
return "Reset Activation Timer"
@@ -18,23 +18,16 @@
var/mob/living/carbon/human/H = host_mob
H.physiology.stun_mod *= 2
/datum/nanite_program/triggered/adrenaline
/datum/nanite_program/adrenaline
name = "Adrenaline Burst"
desc = "The nanites cause a burst of adrenaline when triggered, waking the host from stuns and temporarily increasing their speed."
can_trigger = TRUE
trigger_cost = 25
trigger_cooldown = 1200
rogue_types = list(/datum/nanite_program/toxic, /datum/nanite_program/nerve_decay)
/datum/nanite_program/triggered/adrenaline/trigger()
if(!..())
return
to_chat(host_mob, "<span class='notice'>You feel a sudden surge of energy!</span>")
host_mob.SetAllImmobility(0, FALSE)
host_mob.SetUnconscious(0, FALSE)
host_mob.adjustStaminaLoss(-10) //stimulants give stamina heal now
host_mob.set_resting(FALSE, TRUE, FALSE)
host_mob.update_mobility()
host_mob.reagents.add_reagent(/datum/reagent/medicine/stimulants, 1.5)
/datum/nanite_program/adrenaline/on_trigger()
host_mob.do_adrenaline(-10, TRUE, TRUE, FALSE, TRUE, list(/datum/reagent/medicine/stimulants = 1.5), "<span class='notice'>You feel a sudden surge of energy!</span>", FALSE, FALSE, FALSE)
/datum/nanite_program/hardening
name = "Dermal Hardening"
@@ -119,7 +112,7 @@
/datum/nanite_program/mindshield/enable_passive_effect()
. = ..()
if(!host_mob.mind.has_antag_datum(/datum/antagonist/rev)) //won't work if on a rev, to avoid having implanted revs
if(!host_mob.mind.has_antag_datum(/datum/antagonist/rev, TRUE)) //won't work if on a rev, to avoid having implanted revs.
ADD_TRAIT(host_mob, TRAIT_MINDSHIELD, "nanites")
host_mob.sec_hud_set_implants()
@@ -23,7 +23,7 @@
if(!parts.len)
return
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(0.5/parts.len, 0.5/parts.len, null, BODYPART_ORGANIC))
if(L.heal_damage(0.5/parts.len, 0.5/parts.len))
host_mob.update_damage_overlays()
else
host_mob.adjustBruteLoss(-0.5, TRUE)
@@ -48,12 +48,12 @@
/datum/nanite_program/purging
name = "Blood Purification"
desc = "The nanites purge toxins and chemicals from the host's bloodstream, however it is dangerous to slimepeople biology due to inaccuracy."
desc = "The nanites purge toxins and chemicals from the host's bloodstream, however it is dangerous to slime biology due to inaccuracy."
use_rate = 1
rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
/datum/nanite_program/purging/check_conditions()
var/foreign_reagent = LAZYLEN(host_mob.reagents.reagent_list)
var/foreign_reagent = length(host_mob.reagents?.reagent_list)
if(!host_mob.getToxLoss() && !foreign_reagent)
return FALSE
return ..()
@@ -73,18 +73,17 @@
rogue_types = list(/datum/nanite_program/brain_decay)
/datum/nanite_program/brain_heal/check_conditions()
var/problems = FALSE
if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob
for(var/X in C.get_traumas())
var/datum/brain_trauma/BT = X
if(BT.resilience <= TRAUMA_RESILIENCE_BASIC)
return ..()
if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN))
return ..()
return FALSE
if(length(C.get_traumas()))
problems = TRUE
if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN) > 0)
problems = TRUE
return problems ? ..() : FALSE
/datum/nanite_program/brain_heal/active_effect()
host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1, TRUE)
host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -1)
if(iscarbon(host_mob) && prob(10))
var/mob/living/carbon/C = host_mob
C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_BASIC)
@@ -137,7 +136,7 @@
return
var/update = FALSE
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(1.5/parts.len, 1.5/parts.len, null, BODYPART_ROBOTIC)) //much faster than organic healing
if(L.heal_damage(1.5/parts.len, 1.5/parts.len, null, TRUE, FALSE)) //much faster than organic healing
update = TRUE
if(update)
host_mob.update_damage_overlays()
@@ -148,7 +147,7 @@
/datum/nanite_program/purging_advanced
name = "Selective Blood Purification"
desc = "The nanites purge toxins and dangerous chemicals from the host's bloodstream, while ignoring beneficial chemicals. \
The added processing power required to analyze the chemicals severely increases the nanite consumption rate. Due to added complexity, it is safe with slimepeople biology."
The added processing power required to analyze the chemicals severely increases the nanite consumption rate. This program is safe with slime biology due to the increased precision."
use_rate = 2
rogue_types = list(/datum/nanite_program/suffocating, /datum/nanite_program/necrotic)
@@ -181,7 +180,7 @@
return
var/update = FALSE
for(var/obj/item/bodypart/L in parts)
if(L.heal_damage(3/parts.len, 3/parts.len))
if(L.heal_damage(3/parts.len, 3/parts.len, FALSE))
update = TRUE
if(update)
host_mob.update_damage_overlays()
@@ -196,70 +195,54 @@
rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
/datum/nanite_program/brain_heal_advanced/check_conditions()
var/problems = FALSE
if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob
for(var/X in C.get_traumas())
var/datum/brain_trauma/BT = X
if(BT.resilience <= TRAUMA_RESILIENCE_LOBOTOMY)
return ..()
if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN))
return ..()
return FALSE
if(length(C.get_traumas()))
problems = TRUE
if(host_mob.getOrganLoss(ORGAN_SLOT_BRAIN) > 0)
problems = TRUE
return problems ? ..() : FALSE
/datum/nanite_program/brain_heal_advanced/active_effect()
host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -2, TRUE)
host_mob.adjustOrganLoss(ORGAN_SLOT_BRAIN, -2)
if(iscarbon(host_mob) && prob(10))
var/mob/living/carbon/C = host_mob
C.cure_trauma_type(resilience = TRAUMA_RESILIENCE_LOBOTOMY)
/datum/nanite_program/triggered/defib
/datum/nanite_program/defib
name = "Defibrillation"
desc = "The nanites shock the host's heart when triggered, bringing them back to life if the body can sustain it."
can_trigger = TRUE
trigger_cost = 25
trigger_cooldown = 120
rogue_types = list(/datum/nanite_program/triggered/shocking)
/datum/nanite_program/triggered/defib/trigger()
if(!..())
return
rogue_types = list(/datum/nanite_program/shocking)
/datum/nanite_program/defib/on_trigger(comm_message)
host_mob.notify_ghost_cloning("Your heart is being defibrillated by nanites. Re-enter your corpse if you want to be revived!")
addtimer(CALLBACK(src, .proc/zap), 50)
/datum/nanite_program/triggered/defib/proc/check_revivable()
/datum/nanite_program/defib/proc/check_revivable()
if(!iscarbon(host_mob)) //nonstandard biology
return FALSE
var/mob/living/carbon/C = host_mob
if(C.suiciding || HAS_TRAIT(C, TRAIT_NOCLONE) || C.hellbound) //can't revive
return FALSE
if((world.time - C.timeofdeath) > 1800) //too late
return FALSE
if((C.getBruteLoss() > 180) || (C.getFireLoss() > 180) || !C.can_be_revived()) //too damaged
return FALSE
if(!C.getorgan(/obj/item/organ/heart)) //what are we even shocking
return FALSE
var/obj/item/organ/brain/BR = C.getorgan(/obj/item/organ/brain)
if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || C.suiciding)
return FALSE
if(C.get_ghost())
return FALSE
return TRUE
return C.can_defib()
/datum/nanite_program/triggered/defib/proc/zap()
/datum/nanite_program/defib/proc/zap()
var/mob/living/carbon/C = host_mob
playsound(C, 'sound/machines/defib_charge.ogg', 50, 0)
playsound(C, 'sound/machines/defib_charge.ogg', 50, FALSE)
sleep(30)
playsound(C, 'sound/machines/defib_zap.ogg', 50, 0)
playsound(C, 'sound/machines/defib_zap.ogg', 50, FALSE)
if(check_revivable())
playsound(C, 'sound/machines/defib_success.ogg', 50, 0)
playsound(C, 'sound/machines/defib_success.ogg', 50, FALSE)
C.set_heartattack(FALSE)
C.revive()
C.revive(full_heal = FALSE, admin_revive = FALSE)
C.emote("gasp")
C.Jitter(100)
SEND_SIGNAL(C, COMSIG_LIVING_MINOR_SHOCK)
var/tplus = world.time - C.timeofdeath
if(tplus > 600)
C.adjustOrganLoss(ORGAN_SLOT_BRAIN, max(0, ((1800 - tplus) / 1800 * 150)), 150)
log_game("[C] has been successfully defibrillated by nanites.")
else
playsound(C, 'sound/machines/defib_failed.ogg', 50, 0)
playsound(C, 'sound/machines/defib_failed.ogg', 50, FALSE)
@@ -0,0 +1,107 @@
//Replication Protocols
/datum/nanite_program/protocol/kickstart
name = "Kickstart Protocol"
desc = "Replication Protocol: the nanites focus on early growth, heavily boosting replication rate for a few minutes after the initial implantation."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
var/boost_duration = 1200
/datum/nanite_program/protocol/kickstart/check_conditions()
if(!(world.time < nanites.start_time + boost_duration))
return FALSE
return ..()
/datum/nanite_program/protocol/kickstart/active_effect()
nanites.adjust_nanites(null, 3.5)
/datum/nanite_program/protocol/factory
name = "Factory Protocol"
desc = "Replication Protocol: the nanites build a factory matrix within the host, gradually increasing replication speed over time. \
The factory decays if the protocol is not active, or if the nanites are disrupted by shocks or EMPs."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
var/factory_efficiency = 0
var/max_efficiency = 1000 //Goes up to 2 bonus regen per tick after 16 minutes and 40 seconds
/datum/nanite_program/protocol/factory/on_process()
if(!activated || !check_conditions())
factory_efficiency = max(0, factory_efficiency - 5)
..()
/datum/nanite_program/protocol/factory/on_emp(severity)
..()
factory_efficiency = max(0, factory_efficiency - 300)
/datum/nanite_program/protocol/factory/on_shock(shock_damage)
..()
factory_efficiency = max(0, factory_efficiency - 200)
/datum/nanite_program/protocol/factory/on_minor_shock()
..()
factory_efficiency = max(0, factory_efficiency - 100)
/datum/nanite_program/protocol/factory/active_effect()
factory_efficiency = min(factory_efficiency + 1, max_efficiency)
nanites.adjust_nanites(null, round(0.002 * factory_efficiency, 0.1))
/datum/nanite_program/protocol/tinker
name = "Tinker Protocol"
desc = "Replication Protocol: the nanites learn to use metallic material in the host's bloodstream to speed up the replication process."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
var/boost = 2
var/list/valid_reagents = list(
/datum/reagent/iron,
/datum/reagent/copper,
/datum/reagent/gold,
/datum/reagent/silver,
/datum/reagent/mercury,
/datum/reagent/aluminium,
/datum/reagent/silicon)
/datum/nanite_program/protocol/tinker/check_conditions()
if(!nanites.host_mob.reagents)
return FALSE
var/found_reagent = FALSE
var/datum/reagents/R = nanites.host_mob.reagents
for(var/VR in valid_reagents)
if(R.has_reagent(VR, 0.5))
R.remove_reagent(VR, 0.5)
found_reagent = TRUE
break
if(!found_reagent)
return FALSE
return ..()
/datum/nanite_program/protocol/tinker/active_effect()
nanites.adjust_nanites(null, boost)
/datum/nanite_program/protocol/offline
name = "Offline Production Protocol"
desc = "Replication Protocol: while the host is asleep or otherwise unconcious, the nanites exploit the reduced interference to replicate more quickly."
use_rate = 0
rogue_types = list(/datum/nanite_program/necrotic)
protocol_class = NANITE_PROTOCOL_REPLICATION
var/boost = 3
/datum/nanite_program/protocol/offline/check_conditions()
var/is_offline = FALSE
if(nanites.host_mob.IsSleeping() || nanites.host_mob.IsUnconscious())
is_offline = TRUE
if(nanites.host_mob.stat == DEAD || HAS_TRAIT(nanites.host_mob, TRAIT_DEATHCOMA))
is_offline = TRUE
if(nanites.host_mob.InCritical() && !HAS_TRAIT(nanites.host_mob, TRAIT_NOSOFTCRIT))
is_offline = TRUE
if(nanites.host_mob.InFullCritical() && !HAS_TRAIT(nanites.host_mob, TRAIT_NOHARDCRIT))
is_offline = TRUE
if(!is_offline)
return FALSE
return ..()
/datum/nanite_program/protocol/offline/active_effect()
nanites.adjust_nanites(null, boost)
@@ -2,71 +2,41 @@
name = "Sensor Nanites"
desc = "These nanites send a signal code when a certain condition is met."
unique = FALSE
extra_settings = list("Sent Code")
var/can_rule = FALSE
var/sent_code = 0
/datum/nanite_program/sensor/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
/datum/nanite_program/sensor/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
/datum/nanite_program/sensor/copy_extra_settings_to(datum/nanite_program/sensor/target)
target.sent_code = sent_code
/datum/nanite_program/sensor/register_extra_settings()
extra_settings[NES_SENT_CODE] = new /datum/nanite_extra_setting/number(0, 1, 9999)
/datum/nanite_program/sensor/proc/check_event()
return FALSE
/datum/nanite_program/sensor/proc/send_code()
if(activated)
SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, sent_code, "a [name] program")
var/datum/nanite_extra_setting/ES = extra_settings[NES_SENT_CODE]
SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, ES.value, "a [name] program")
/datum/nanite_program/sensor/active_effect()
if(sent_code && check_event())
if(check_event())
send_code()
/datum/nanite_program/sensor/proc/make_rule(datum/nanite_program/target)
return
/datum/nanite_program/sensor/repeat
name = "Signal Repeater"
desc = "When triggered, sends another signal to the nanites, optionally with a delay."
can_trigger = TRUE
trigger_cost = 0
trigger_cooldown = 10
extra_settings = list("Sent Code","Delay")
var/spent = FALSE
var/delay = 0
/datum/nanite_program/sensor/repeat/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Delay")
var/new_delay = input(user, "Set the delay in seconds:", name, null) as null|num
if(isnull(new_delay))
return
delay = (CLAMP(round(new_delay, 1), 0, 3600)) * 10 //max 1 hour
/datum/nanite_program/sensor/repeat/register_extra_settings()
. = ..()
extra_settings[NES_DELAY] = new /datum/nanite_extra_setting/number(0, 0, 3600, "s")
/datum/nanite_program/sensor/repeat/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Delay")
return "[delay/10] seconds"
/datum/nanite_program/sensor/repeat/copy_extra_settings_to(datum/nanite_program/sensor/repeat/target)
target.sent_code = sent_code
target.delay = delay
/datum/nanite_program/sensor/repeat/trigger()
if(!..())
return
addtimer(CALLBACK(src, .proc/send_code), delay)
/datum/nanite_program/sensor/repeat/on_trigger(comm_message)
var/datum/nanite_extra_setting/ES = extra_settings[NES_DELAY]
addtimer(CALLBACK(src, .proc/send_code), ES.get_value() * 10)
/datum/nanite_program/sensor/relay_repeat
name = "Relay Signal Repeater"
@@ -74,98 +44,46 @@
can_trigger = TRUE
trigger_cost = 0
trigger_cooldown = 10
extra_settings = list("Sent Code","Relay Channel","Delay")
var/spent = FALSE
var/delay = 0
var/relay_channel = 0
/datum/nanite_program/sensor/relay_repeat/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Relay Channel")
var/new_channel = input(user, "Set the relay channel (1-9999):", name, null) as null|num
if(isnull(new_channel))
return
relay_channel = CLAMP(round(new_channel, 1), 1, 9999)
if(setting == "Delay")
var/new_delay = input(user, "Set the delay in seconds:", name, null) as null|num
if(isnull(new_delay))
return
delay = (CLAMP(round(new_delay, 1), 0, 3600)) * 10 //max 1 hour
/datum/nanite_program/sensor/relay_repeat/register_extra_settings()
. = ..()
extra_settings[NES_RELAY_CHANNEL] = new /datum/nanite_extra_setting/number(1, 1, 9999)
extra_settings[NES_DELAY] = new /datum/nanite_extra_setting/number(0, 0, 3600, "s")
/datum/nanite_program/sensor/relay_repeat/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Relay Channel")
return relay_channel
if(setting == "Delay")
return "[delay/10] seconds"
/datum/nanite_program/sensor/relay_repeat/copy_extra_settings_to(datum/nanite_program/sensor/relay_repeat/target)
target.sent_code = sent_code
target.delay = delay
target.relay_channel = relay_channel
/datum/nanite_program/sensor/relay_repeat/trigger()
if(!..())
return
addtimer(CALLBACK(src, .proc/send_code), delay)
/datum/nanite_program/sensor/relay_repeat/on_trigger(comm_message)
var/datum/nanite_extra_setting/ES = extra_settings[NES_DELAY]
addtimer(CALLBACK(src, .proc/send_code), ES.get_value() * 10)
/datum/nanite_program/sensor/relay_repeat/send_code()
if(activated && relay_channel)
var/datum/nanite_extra_setting/relay = extra_settings[NES_RELAY_CHANNEL]
if(activated && relay.get_value())
for(var/X in SSnanites.nanite_relays)
var/datum/nanite_program/relay/N = X
N.relay_signal(sent_code, relay_channel, "a [name] program")
var/datum/nanite_extra_setting/code = extra_settings[NES_SENT_CODE]
N.relay_signal(code.get_value(), relay.get_value(), "a [name] program")
/datum/nanite_program/sensor/health
name = "Health Sensor"
desc = "The nanites receive a signal when the host's health is above/below a target percentage."
extra_settings = list("Sent Code","Health Percent","Direction")
can_rule = TRUE
var/spent = FALSE
var/percent = 50
var/direction = "Above"
/datum/nanite_program/sensor/health/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Health Percent")
var/new_percent = input(user, "Set the health percentage:", name, null) as null|num
if(isnull(new_percent))
return
percent = CLAMP(round(new_percent, 1), -99, 100)
if(setting == "Direction")
if(direction == "Above")
direction = "Below"
else
direction = "Above"
/datum/nanite_program/sensor/health/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Health Percent")
return "[percent]%"
if(setting == "Direction")
return direction
/datum/nanite_program/sensor/health/copy_extra_settings_to(datum/nanite_program/sensor/health/target)
target.sent_code = sent_code
target.percent = percent
target.direction = direction
/datum/nanite_program/sensor/health/register_extra_settings()
. = ..()
extra_settings[NES_HEALTH_PERCENT] = new /datum/nanite_extra_setting/number(50, -99, 100, "%")
extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
/datum/nanite_program/sensor/health/check_event()
var/health_percent = host_mob.health / host_mob.maxHealth * 100
var/datum/nanite_extra_setting/percent = extra_settings[NES_HEALTH_PERCENT]
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/detected = FALSE
if(direction == "Above")
if(health_percent >= percent)
if(direction.get_value())
if(health_percent >= percent.get_value())
detected = TRUE
else
if(health_percent < percent)
if(health_percent < percent.get_value())
detected = TRUE
if(detected)
@@ -177,9 +95,18 @@
spent = FALSE
return FALSE
/datum/nanite_program/sensor/health/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/health/rule = new(target)
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/datum/nanite_extra_setting/percent = extra_settings[NES_HEALTH_PERCENT]
rule.above = direction.get_value()
rule.threshold = percent.get_value()
return rule
/datum/nanite_program/sensor/crit
name = "Critical Health Sensor"
desc = "The nanites receive a signal when the host first reaches critical health."
can_rule = TRUE
var/spent = FALSE
/datum/nanite_program/sensor/crit/check_event()
@@ -192,61 +119,44 @@
spent = FALSE
return FALSE
/datum/nanite_program/sensor/crit/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/crit/rule = new(target)
return rule
/datum/nanite_program/sensor/death
name = "Death Sensor"
desc = "The nanites receive a signal when they detect the host is dead."
can_rule = TRUE
var/spent = FALSE
/datum/nanite_program/sensor/death/on_death()
send_code()
/datum/nanite_program/sensor/death/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/death/rule = new(target)
return rule
/datum/nanite_program/sensor/nanite_volume
name = "Nanite Volume Sensor"
desc = "The nanites receive a signal when the nanite supply is above/below a certain percentage."
extra_settings = list("Sent Code","Nanite Percent","Direction")
can_rule = TRUE
var/spent = FALSE
var/percent = 50
var/direction = "Above"
/datum/nanite_program/sensor/nanite_volume/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Nanite Percent")
var/new_percent = input(user, "Set the nanite percentage:", name, null) as null|num
if(isnull(new_percent))
return
percent = CLAMP(round(new_percent, 1), 1, 100)
if(setting == "Direction")
if(direction == "Above")
direction = "Below"
else
direction = "Above"
/datum/nanite_program/sensor/nanite_volume/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Nanite Percent")
return "[percent]%"
if(setting == "Direction")
return direction
/datum/nanite_program/sensor/nanite_volume/copy_extra_settings_to(datum/nanite_program/sensor/nanite_volume/target)
target.sent_code = sent_code
target.percent = percent
target.direction = direction
/datum/nanite_program/sensor/nanite_volume/register_extra_settings()
. = ..()
extra_settings[NES_NANITE_PERCENT] = new /datum/nanite_extra_setting/number(50, -99, 100, "%")
extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
/datum/nanite_program/sensor/nanite_volume/check_event()
var/nanite_percent = (nanites.nanite_volume - nanites.safety_threshold)/(nanites.max_nanites - nanites.safety_threshold)*100
var/datum/nanite_extra_setting/percent = extra_settings[NES_NANITE_PERCENT]
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/detected = FALSE
if(direction == "Above")
if(nanite_percent >= percent)
if(direction.get_value())
if(nanite_percent >= percent.get_value())
detected = TRUE
else
if(nanite_percent < percent)
if(nanite_percent < percent.get_value())
detected = TRUE
if(detected)
@@ -258,75 +168,51 @@
spent = FALSE
return FALSE
/datum/nanite_program/sensor/nanite_volume/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/nanites/rule = new(target)
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/datum/nanite_extra_setting/percent = extra_settings[NES_NANITE_PERCENT]
rule.above = direction.get_value()
rule.threshold = percent.get_value()
return rule
/datum/nanite_program/sensor/damage
name = "Damage Sensor"
desc = "The nanites receive a signal when a host's specific damage type is above/below a target value."
extra_settings = list("Sent Code","Damage Type","Damage","Direction")
can_rule = TRUE
var/spent = FALSE
var/damage_type = "Brute"
var/damage = 50
var/direction = "Above"
/datum/nanite_program/sensor/damage/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Damage")
var/new_damage = input(user, "Set the damage threshold:", name, null) as null|num
if(isnull(new_damage))
return
damage = CLAMP(round(new_damage, 1), 0, 500)
if(setting == "Damage Type")
var/list/damage_types = list("Brute","Burn","Toxin","Oxygen","Cellular")
var/new_damage_type = input("Choose the damage type", name) as null|anything in damage_types
if(!new_damage_type)
return
damage_type = new_damage_type
if(setting == "Direction")
if(direction == "Above")
direction = "Below"
else
direction = "Above"
/datum/nanite_program/sensor/damage/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Damage")
return damage
if(setting == "Damage Type")
return damage_type
if(setting == "Direction")
return direction
/datum/nanite_program/sensor/damage/copy_extra_settings_to(datum/nanite_program/sensor/damage/target)
target.sent_code = sent_code
target.damage = damage
target.damage_type = damage_type
target.direction = direction
/datum/nanite_program/sensor/damage/register_extra_settings()
. = ..()
extra_settings[NES_DAMAGE_TYPE] = new /datum/nanite_extra_setting/type(BRUTE, list(BRUTE, BURN, TOX, OXY, CLONE))
extra_settings[NES_DAMAGE] = new /datum/nanite_extra_setting/number(50, 0, 500)
extra_settings[NES_DIRECTION] = new /datum/nanite_extra_setting/boolean(TRUE, "Above", "Below")
/datum/nanite_program/sensor/damage/check_event()
var/reached_threshold = FALSE
var/check_above = (direction == "Above")
var/datum/nanite_extra_setting/type = extra_settings[NES_DAMAGE_TYPE]
var/datum/nanite_extra_setting/damage = extra_settings[NES_DAMAGE]
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/check_above = direction.get_value()
var/damage_amt = 0
switch(damage_type)
if("Brute")
switch(type.get_value())
if(BRUTE)
damage_amt = host_mob.getBruteLoss()
if("Burn")
if(BURN)
damage_amt = host_mob.getFireLoss()
if("Toxin")
if(TOX)
damage_amt = host_mob.getToxLoss()
if("Oxygen")
if(OXY)
damage_amt = host_mob.getOxyLoss()
if("Cellular")
if(CLONE)
damage_amt = host_mob.getCloneLoss()
if(damage_amt >= damage)
if(check_above)
if(check_above)
if(damage_amt >= damage.get_value())
reached_threshold = TRUE
else
if(damage_amt < damage.get_value())
reached_threshold = TRUE
else if(!check_above)
reached_threshold = TRUE
if(reached_threshold)
if(!spent)
@@ -337,14 +223,25 @@
spent = FALSE
return FALSE
/datum/nanite_program/sensor/damage/make_rule(datum/nanite_program/target)
var/datum/nanite_rule/damage/rule = new(target)
var/datum/nanite_extra_setting/direction = extra_settings[NES_DIRECTION]
var/datum/nanite_extra_setting/damage_type = extra_settings[NES_DAMAGE_TYPE]
var/datum/nanite_extra_setting/damage = extra_settings[NES_DAMAGE]
rule.above = direction.get_value()
rule.threshold = damage.get_value()
rule.damage_type = damage_type.get_value()
return rule
/datum/nanite_program/sensor/voice
name = "Voice Sensor"
desc = "Sends a signal when the nanites hear a determined word or sentence."
extra_settings = list("Sent Code","Sentence","Inclusive Mode")
var/spent = FALSE
var/sentence = ""
var/inclusive = TRUE
/datum/nanite_program/sensor/voice/register_extra_settings()
. = ..()
extra_settings[NES_SENTENCE] = new /datum/nanite_extra_setting/text("")
extra_settings[NES_INCLUSIVE_MODE] = new /datum/nanite_extra_setting/boolean(TRUE, "Inclusive", "Exclusive")
/datum/nanite_program/sensor/voice/on_mob_add()
. = ..()
@@ -353,45 +250,14 @@
/datum/nanite_program/sensor/voice/on_mob_remove()
UnregisterSignal(host_mob, COMSIG_MOVABLE_HEAR, .proc/on_hear)
/datum/nanite_program/sensor/voice/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Sentence")
var/new_sentence = stripped_input(user, "Choose the sentence that triggers the sensor.", "Sentence", sentence, MAX_MESSAGE_LEN)
if(!new_sentence)
return
sentence = new_sentence
if(setting == "Inclusive Mode")
var/new_inclusive = input("Should the sensor detect the sentence if contained within another sentence?", name) as null|anything in list("Inclusive","Exclusive")
if(!new_inclusive)
return
inclusive = (new_inclusive == "Inclusive")
/datum/nanite_program/sensor/voice/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Sentence")
return sentence
if(setting == "Inclusive Mode")
if(inclusive)
return "Inclusive"
else
return "Exclusive"
/datum/nanite_program/sensor/voice/copy_extra_settings_to(datum/nanite_program/sensor/voice/target)
target.sent_code = sent_code
target.sentence = sentence
target.inclusive = inclusive
/datum/nanite_program/sensor/voice/proc/on_hear(datum/source, list/hearing_args)
if(!sentence)
var/datum/nanite_extra_setting/sentence = extra_settings[NES_SENTENCE]
var/datum/nanite_extra_setting/inclusive = extra_settings[NES_INCLUSIVE_MODE]
if(!sentence.get_value())
return
if(inclusive)
if(inclusive.get_value())
if(findtextEx(hearing_args[HEARING_RAW_MESSAGE], sentence))
send_code()
else
if(hearing_args[HEARING_RAW_MESSAGE] == sentence)
send_code()
send_code()
@@ -1,15 +1,14 @@
//Programs that are generally useful for population control and non-harmful suppression.
/datum/nanite_program/triggered/sleepy
/datum/nanite_program/sleepy
name = "Sleep Induction"
desc = "The nanites cause rapid narcolepsy when triggered."
can_trigger = TRUE
trigger_cost = 15
trigger_cooldown = 1200
rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
/datum/nanite_program/triggered/sleepy/trigger()
if(!..())
return
/datum/nanite_program/sleepy/on_trigger(comm_message)
to_chat(host_mob, "<span class='warning'>You start to feel very sleepy...</span>")
host_mob.drowsyness += 20
addtimer(CALLBACK(host_mob, /mob/living.proc/Sleeping, 200), rand(60,200))
@@ -31,31 +30,31 @@
. = ..()
to_chat(host_mob, "<span class='notice'>Your muscles relax, and you can move again.</span>")
/datum/nanite_program/triggered/shocking
/datum/nanite_program/shocking
name = "Electric Shock"
desc = "The nanites shock the host when triggered. Destroys a large amount of nanites!"
can_trigger = TRUE
trigger_cost = 10
trigger_cooldown = 300
program_flags = NANITE_SHOCK_IMMUNE
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/triggered/shocking/trigger()
if(!..())
return
/datum/nanite_program/shocking/on_trigger(comm_message)
host_mob.electrocute_act(rand(5,10), "shock nanites", TRUE, TRUE)
/datum/nanite_program/triggered/stun
/datum/nanite_program/stun
name = "Neural Shock"
desc = "The nanites pulse the host's nerves when triggered, inapacitating them for a short period."
can_trigger = TRUE
trigger_cost = 4
trigger_cooldown = 300
rogue_types = list(/datum/nanite_program/triggered/shocking, /datum/nanite_program/nerve_decay)
rogue_types = list(/datum/nanite_program/shocking, /datum/nanite_program/nerve_decay)
/datum/nanite_program/triggered/stun/trigger()
if(!..())
return
playsound(host_mob, "sparks", 75, 1, -1)
host_mob.DefaultCombatKnockdown(80)
playsound(host_mob, "sparks", 75, TRUE, -1)
/datum/nanite_program/pacifying
name = "Pacification"
@@ -115,64 +114,50 @@
host_mob.cure_fakedeath("nanites")
//Can receive transmissions from a nanite communication remote for customized messages
/datum/nanite_program/triggered/comm
/datum/nanite_program/comm
can_trigger = TRUE
var/comm_code = 0
var/comm_message = ""
/datum/nanite_program/triggered/comm/proc/receive_comm_signal(signal_comm_code, comm_message, comm_source)
/datum/nanite_program/comm/register_extra_settings()
extra_settings[NES_COMM_CODE] = new /datum/nanite_extra_setting/number(0, 0, 9999)
/datum/nanite_program/comm/proc/receive_comm_signal(signal_comm_code, comm_message, comm_source)
if(!activated || !comm_code)
return
if(signal_comm_code == comm_code)
host_mob.investigate_log("'s [name] nanite program was messaged by [comm_source] with comm code [signal_comm_code] and message '[comm_message]'.", INVESTIGATE_NANITES)
trigger(comm_message)
/datum/nanite_program/triggered/comm/speech
/datum/nanite_program/comm/speech
name = "Forced Speech"
desc = "The nanites force the host to say a pre-programmed sentence when triggered."
unique = FALSE
trigger_cost = 3
trigger_cooldown = 20
rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
var/static/list/blacklist = list(
"*surrender",
"*collapse"
)
extra_settings = list("Sentence","Comm Code")
var/sentence = ""
/datum/nanite_program/comm/speech/register_extra_settings()
. = ..()
extra_settings[NES_SENTENCE] = new /datum/nanite_extra_setting/text("")
/datum/nanite_program/triggered/comm/speech/set_extra_setting(user, setting)
if(setting == "Sentence")
var/new_sentence = stripped_input(user, "Choose the sentence that the host will be forced to say.", "Sentence", sentence, MAX_MESSAGE_LEN)
if(!new_sentence)
return
if(new_sentence[1] == "*") //emotes are abusable, like surrender
return
sentence = new_sentence
if(setting == "Comm Code")
var/new_code = input(user, "Set the communication code (1-9999) or set to 0 to disable external signals.", name, null) as null|num
if(isnull(new_code))
return
comm_code = CLAMP(round(new_code, 1), 0, 9999)
/datum/nanite_program/triggered/comm/speech/get_extra_setting(setting)
if(setting == "Sentence")
return sentence
if(setting == "Comm Code")
return comm_code
/datum/nanite_program/triggered/comm/speech/copy_extra_settings_to(datum/nanite_program/triggered/comm/speech/target)
target.sentence = sentence
target.comm_code = comm_code
/datum/nanite_program/triggered/comm/speech/trigger(comm_message)
if(!..())
return
/datum/nanite_program/comm/speech/on_trigger(comm_message)
var/sent_message = comm_message
if(!comm_message)
sent_message = sentence
var/datum/nanite_extra_setting/sentence = extra_settings[NES_SENTENCE]
sent_message = sentence.get_value()
if(sent_message in blacklist)
return
if(host_mob.stat == DEAD)
return
to_chat(host_mob, "<span class='warning'>You feel compelled to speak...</span>")
host_mob.say(sent_message, forced = "nanite speech")
/datum/nanite_program/triggered/comm/voice
/datum/nanite_program/comm/voice
name = "Skull Echo"
desc = "The nanites echo a synthesized message inside the host's skull."
unique = FALSE
@@ -180,56 +165,50 @@
trigger_cooldown = 20
rogue_types = list(/datum/nanite_program/brain_misfire, /datum/nanite_program/brain_decay)
extra_settings = list("Message","Comm Code")
var/message = ""
/datum/nanite_program/comm/voice/register_extra_settings()
. = ..()
extra_settings[NES_MESSAGE] = new /datum/nanite_extra_setting/text("")
/datum/nanite_program/triggered/comm/voice/set_extra_setting(user, setting)
if(setting == "Message")
var/new_message = stripped_input(user, "Choose the message sent to the host.", "Message", message, MAX_MESSAGE_LEN)
if(!new_message)
return
message = new_message
if(setting == "Comm Code")
var/new_code = input(user, "Set the communication code (1-9999) or set to 0 to disable external signals.", name, null) as null|num
if(isnull(new_code))
return
comm_code = CLAMP(round(new_code, 1), 0, 9999)
/datum/nanite_program/triggered/comm/voice/get_extra_setting(setting)
if(setting == "Message")
return message
if(setting == "Comm Code")
return comm_code
/datum/nanite_program/triggered/comm/voice/copy_extra_settings_to(datum/nanite_program/triggered/comm/voice/target)
target.message = message
target.comm_code = comm_code
/datum/nanite_program/triggered/comm/voice/trigger(comm_message)
if(!..())
return
/datum/nanite_program/comm/voice/on_trigger(comm_message)
var/sent_message = comm_message
if(!comm_message)
sent_message = message
var/datum/nanite_extra_setting/message_setting = extra_settings[NES_MESSAGE]
sent_message = message_setting.get_value()
if(host_mob.stat == DEAD)
return
to_chat(host_mob, "<i>You hear a strange, robotic voice in your head...</i> \"<span class='robot'>[sent_message]</span>\"")
/datum/nanite_program/triggered/comm/hallucination
/datum/nanite_program/comm/hallucination
name = "Hallucination"
desc = "The nanites make the host hallucinate something when triggered."
trigger_cost = 4
trigger_cooldown = 80
unique = FALSE
rogue_types = list(/datum/nanite_program/brain_misfire)
extra_settings = list("Hallucination Type", "Comm Code")
var/hal_type
var/hal_details
/datum/nanite_program/triggered/comm/hallucination/trigger(comm_message)
if(!..())
return
/datum/nanite_program/comm/hallucination/register_extra_settings()
. = ..()
var/list/options = list(
"Message",
"Battle",
"Sound",
"Weird Sound",
"Station Message",
"Health",
"Alert",
"Fire",
"Shock",
"Plasma Flood",
"Random"
)
extra_settings[NES_HALLUCINATION_TYPE] = new /datum/nanite_extra_setting/type("Message", options)
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/text("")
/datum/nanite_program/comm/hallucination/on_trigger(comm_message)
var/datum/nanite_extra_setting/hal_setting = extra_settings[NES_HALLUCINATION_TYPE]
var/hal_type = hal_setting.get_value()
var/datum/nanite_extra_setting/hal_detail_setting = extra_settings[NES_HALLUCINATION_DETAIL]
var/hal_details = hal_detail_setting.get_value()
if(comm_message && (hal_type != "Message")) //Triggered via comm remote, but not set to a message hallucination
return
var/sent_message = comm_message //Comm remotes can send custom hallucination messages for the chat hallucination
@@ -239,7 +218,9 @@
if(!iscarbon(host_mob))
return
var/mob/living/carbon/C = host_mob
if(!hal_type)
if(hal_details == "random")
hal_details = null
if(hal_type == "Random")
C.hallucination += 15
else
switch(hal_type)
@@ -254,6 +235,13 @@
if("Station Message")
new /datum/hallucination/stationmessage(C, TRUE, hal_details)
if("Health")
switch(hal_details)
if("critical")
hal_details = SCREWYHUD_CRIT
if("dead")
hal_details = SCREWYHUD_DEAD
if("healthy")
hal_details = SCREWYHUD_HEALTHY
new /datum/hallucination/hudscrew(C, TRUE, hal_details)
if("Alert")
new /datum/hallucination/fake_alert(C, TRUE, hal_details)
@@ -264,127 +252,40 @@
if("Plasma Flood")
new /datum/hallucination/fake_flood(C, TRUE)
/datum/nanite_program/triggered/comm/hallucination/set_extra_setting(user, setting)
if(setting == "Comm Code")
var/new_code = input(user, "(Only for Message) Set the communication code (1-9999) or set to 0 to disable external signals.", name, null) as null|num
if(isnull(new_code))
return
comm_code = CLAMP(round(new_code, 1), 0, 9999)
if(setting == "Hallucination Type")
var/list/possible_hallucinations = list("Random","Message","Battle","Sound","Weird Sound","Station Message","Health","Alert","Fire","Shock","Plasma Flood")
var/hal_type_choice = input("Choose the hallucination type", name) as null|anything in possible_hallucinations
if(!hal_type_choice)
return
switch(hal_type_choice)
if("Random")
hal_type = null
hal_details = null
/datum/nanite_program/comm/hallucination/set_extra_setting(setting, value)
. = ..()
if(setting == NES_HALLUCINATION_TYPE)
switch(value)
if("Message")
hal_type = "Message"
var/hal_chat = stripped_input(user, "Choose the message the host will hear, or leave empty for random messages.", "Message", hal_details, MAX_MESSAGE_LEN)
if(hal_chat)
hal_details = hal_chat
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/text("")
if("Battle")
hal_type = "Battle"
var/sound_list = list("random","laser","disabler","esword","gun","stunprod","harmbaton","bomb")
var/hal_choice = input("Choose the hallucination battle type", name) as null|anything in sound_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
hal_details = hal_choice
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","laser","disabler","esword","gun","stunprod","harmbaton","bomb"))
if("Sound")
hal_type = "Sound"
var/sound_list = list("random","airlock","airlock pry","console","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack")
var/hal_choice = input("Choose the hallucination sound", name) as null|anything in sound_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
hal_details = hal_choice
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","airlock","airlock pry","console","explosion","far explosion","mech","glass","alarm","beepsky","mech","wall decon","door hack"))
if("Weird Sound")
hal_type = "Weird Sound"
var/sound_list = list("random","phone","hallelujah","highlander","laughter","hyperspace","game over","creepy","tesla")
var/hal_choice = input("Choose the hallucination sound", name) as null|anything in sound_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
hal_details = hal_choice
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","phone","hallelujah","highlander","laughter","hyperspace","game over","creepy","tesla"))
if("Station Message")
hal_type = "Station Message"
var/msg_list = list("random","ratvar","shuttle dock","blob alert","malf ai","meteors","supermatter")
var/hal_choice = input("Choose the hallucination station message", name) as null|anything in msg_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
hal_details = hal_choice
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","ratvar","shuttle dock","blob alert","malf ai","meteors","supermatter"))
if("Health")
hal_type = "Health"
var/health_list = list("random","critical","dead","healthy")
var/hal_choice = input("Choose the health status", name) as null|anything in health_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
switch(hal_choice)
if("critical")
hal_details = SCREWYHUD_CRIT
if("dead")
hal_details = SCREWYHUD_DEAD
if("healthy")
hal_details = SCREWYHUD_HEALTHY
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","critical","dead","healthy"))
if("Alert")
hal_type = "Alert"
var/alert_list = list("random","not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","gravity","fire","locked","hacked","temphot","tempcold","pressure")
var/hal_choice = input("Choose the alert", name) as null|anything in alert_list
if(!hal_choice || hal_choice == "random")
hal_details = null
else
hal_details = hal_choice
if("Fire")
hal_type = "Fire"
if("Shock")
hal_type = "Shock"
if("Plasma Flood")
hal_type = "Plasma Flood"
/datum/nanite_program/triggered/comm/hallucination/get_extra_setting(setting)
if(setting == "Hallucination Type")
if(!hal_type)
return "Random"
else
return hal_type
if(setting == "Comm Code")
return comm_code
/datum/nanite_program/triggered/comm/hallucination/copy_extra_settings_to(datum/nanite_program/triggered/comm/hallucination/target)
target.hal_type = hal_type
target.hal_details = hal_details
target.comm_code = comm_code
extra_settings[NES_HALLUCINATION_DETAIL] = new /datum/nanite_extra_setting/type("random", list("random","not_enough_oxy","not_enough_tox","not_enough_co2","too_much_oxy","too_much_co2","too_much_tox","newlaw","nutrition","charge","gravity","fire","locked","hacked","temphot","tempcold","pressure"))
else
extra_settings.Remove(NES_HALLUCINATION_DETAIL)
/datum/nanite_program/good_mood
name = "Happiness Enhancer"
desc = "The nanites synthesize serotonin inside the host's brain, creating an artificial sense of happiness."
use_rate = 0.1
rogue_types = list(/datum/nanite_program/brain_decay)
extra_settings = list("Mood Message")
var/message = "HAPPINESS ENHANCEMENT"
/datum/nanite_program/good_mood/set_extra_setting(user, setting)
if(setting == "Mood Message")
var/new_message = stripped_input(user, "Choose the message visible on the mood effect.", "Message", message, MAX_NAME_LEN)
if(!new_message)
return
message = new_message
/datum/nanite_program/good_mood/get_extra_setting(setting)
if(setting == "Mood Message")
return message
/datum/nanite_program/good_mood/copy_extra_settings_to(datum/nanite_program/good_mood/target)
target.message = message
/datum/nanite_program/good_mood/register_extra_settings()
. = ..()
extra_settings[NES_MOOD_MESSAGE] = new /datum/nanite_extra_setting/text("HAPPINESS ENHANCEMENT")
/datum/nanite_program/good_mood/enable_passive_effect()
. = ..()
SEND_SIGNAL(host_mob, COMSIG_ADD_MOOD_EVENT, "nanite_happy", /datum/mood_event/nanite_happiness, message)
SEND_SIGNAL(host_mob, COMSIG_ADD_MOOD_EVENT, "nanite_happy", /datum/mood_event/nanite_happiness, get_extra_setting_value(NES_MOOD_MESSAGE))
/datum/nanite_program/good_mood/disable_passive_effect()
. = ..()
@@ -395,26 +296,14 @@
desc = "The nanites suppress the production of serotonin inside the host's brain, creating an artificial state of depression."
use_rate = 0.1
rogue_types = list(/datum/nanite_program/brain_decay)
extra_settings = list("Mood Message")
var/message = "HAPPINESS SUPPRESSION"
/datum/nanite_program/bad_mood/set_extra_setting(user, setting)
if(setting == "Mood Message")
var/new_message = stripped_input(user, "Choose the message visible on the mood effect.", "Message", message, MAX_NAME_LEN)
if(!new_message)
return
message = new_message
/datum/nanite_program/bad_mood/get_extra_setting(setting)
if(setting == "Mood Message")
return message
/datum/nanite_program/bad_mood/copy_extra_settings_to(datum/nanite_program/bad_mood/target)
target.message = message
/datum/nanite_program/bad_mood/register_extra_settings()
. = ..()
extra_settings[NES_MOOD_MESSAGE] = new /datum/nanite_extra_setting/text("HAPPINESS SUPPRESSION")
/datum/nanite_program/bad_mood/enable_passive_effect()
. = ..()
SEND_SIGNAL(host_mob, COMSIG_ADD_MOOD_EVENT, "nanite_sadness", /datum/mood_event/nanite_sadness, message)
SEND_SIGNAL(host_mob, COMSIG_ADD_MOOD_EVENT, "nanite_sadness", /datum/mood_event/nanite_sadness, get_extra_setting_value(NES_MOOD_MESSAGE))
/datum/nanite_program/bad_mood/disable_passive_effect()
. = ..()
@@ -1,82 +1,29 @@
//Programs that interact with other programs or nanites directly, or have other special purposes.
/datum/nanite_program/viral
name = "Viral Replica"
desc = "The nanites constantly send encrypted signals attempting to forcefully copy their own programming into other nanite clusters."
desc = "The nanites constantly send encrypted signals attempting to forcefully copy their own programming into other nanite clusters, also overriding or disabling their cloud sync."
use_rate = 0.5
rogue_types = list(/datum/nanite_program/toxic)
extra_settings = list("Program Overwrite","Cloud Overwrite")
var/pulse_cooldown = 0
var/sync_programs = TRUE
var/sync_overwrite = FALSE
var/overwrite_cloud = FALSE
var/set_cloud = 0
/datum/nanite_program/viral/set_extra_setting(user, setting)
if(setting == "Program Overwrite")
var/overwrite_type = input("Choose what to do with the target's programs", name) as null|anything in list("Overwrite","Add To","Ignore")
if(!overwrite_type)
return
switch(overwrite_type)
if("Ignore") //Do not affect programs (if you only want to set the cloud ID)
sync_programs = FALSE
sync_overwrite = FALSE
if("Add To") //Add to existing programs (so the target does not notice theirs are missing)
sync_programs = TRUE
sync_overwrite = FALSE
if("Overwrite") //Replace target's programs with the source
sync_programs = TRUE
sync_overwrite = TRUE
if(setting == "Cloud Overwrite")
var/overwrite_type = input("Choose what to do with the target's Cloud ID", name) as null|anything in list("Overwrite","Disable","Keep")
if(!overwrite_type)
return
switch(overwrite_type)
if("Keep") //Don't change the cloud ID
overwrite_cloud = FALSE
set_cloud = 0
if("Disable") //Set the cloud ID to disabled
overwrite_cloud = TRUE
set_cloud = 0
if("Overwrite") //Set the cloud ID to what we choose
var/new_cloud = input(user, "Choose the Cloud ID to set on infected nanites (1-100)", name, null) as null|num
if(isnull(new_cloud))
return
overwrite_cloud = TRUE
set_cloud = CLAMP(round(new_cloud, 1), 1, 100)
/datum/nanite_program/viral/get_extra_setting(setting)
if(setting == "Program Overwrite")
if(!sync_programs)
return "Ignore"
else if(sync_overwrite)
return "Overwrite"
else
return "Add To"
if(setting == "Cloud Overwrite")
if(!overwrite_cloud)
return "None"
else if(set_cloud == 0)
return "Disable"
else
return set_cloud
/datum/nanite_program/viral/copy_extra_settings_to(datum/nanite_program/viral/target)
target.overwrite_cloud = overwrite_cloud
target.set_cloud = set_cloud
target.sync_programs = sync_programs
target.sync_overwrite = sync_overwrite
/datum/nanite_program/viral/register_extra_settings()
extra_settings[NES_PROGRAM_OVERWRITE] = new /datum/nanite_extra_setting/type("Add To", list("Overwrite", "Add To", "Ignore"))
extra_settings[NES_CLOUD_OVERWRITE] = new /datum/nanite_extra_setting/number(0, 0, 100)
/datum/nanite_program/viral/active_effect()
if(world.time < pulse_cooldown)
return
var/datum/nanite_extra_setting/program = extra_settings[NES_PROGRAM_OVERWRITE]
var/datum/nanite_extra_setting/cloud = extra_settings[NES_CLOUD_OVERWRITE]
for(var/mob/M in orange(host_mob, 5))
if(SEND_SIGNAL(M, COMSIG_NANITE_IS_STEALTHY))
continue
if(sync_programs)
SEND_SIGNAL(M, COMSIG_NANITE_SYNC, nanites, sync_overwrite)
if(overwrite_cloud)
SEND_SIGNAL(M, COMSIG_NANITE_SET_CLOUD, set_cloud)
switch(program.get_value())
if("Overwrite")
SEND_SIGNAL(M, COMSIG_NANITE_SYNC, nanites, TRUE)
if("Add To")
SEND_SIGNAL(M, COMSIG_NANITE_SYNC, nanites, FALSE)
SEND_SIGNAL(M, COMSIG_NANITE_SET_CLOUD, cloud.get_value())
pulse_cooldown = world.time + 75
/datum/nanite_program/monitoring
@@ -94,38 +41,23 @@
SSnanites.nanite_monitored_mobs -= host_mob
host_mob.hud_set_nanite_indicator()
/datum/nanite_program/triggered/self_scan
/datum/nanite_program/self_scan
name = "Host Scan"
desc = "The nanites display a detailed readout of a body scan to the host."
unique = FALSE
can_trigger = TRUE
trigger_cost = 3
trigger_cooldown = 50
rogue_types = list(/datum/nanite_program/toxic)
extra_settings = list("Scan Type")
var/scan_type = "Medical"
/datum/nanite_program/self_scan/register_extra_settings()
extra_settings[NES_SCAN_TYPE] = new /datum/nanite_extra_setting/type("Medical", list("Medical", "Chemical", "Nanite"))
/datum/nanite_program/triggered/self_scan/set_extra_setting(user, setting)
if(setting == "Scan Type")
var/list/scan_types = list("Medical","Chemical","Nanite")
var/new_scan_type = input("Choose the scan type", name) as null|anything in scan_types
if(!new_scan_type)
return
scan_type = new_scan_type
/datum/nanite_program/triggered/self_scan/get_extra_setting(setting)
if(setting == "Scan Type")
return scan_type
/datum/nanite_program/triggered/self_scan/copy_extra_settings_to(datum/nanite_program/triggered/self_scan/target)
target.scan_type = scan_type
/datum/nanite_program/triggered/self_scan/trigger()
if(!..())
return
/datum/nanite_program/self_scan/on_trigger(comm_message)
if(host_mob.stat == DEAD)
return
switch(scan_type)
var/datum/nanite_extra_setting/NS = extra_settings[NES_SCAN_TYPE]
switch(NS.get_value())
if("Medical")
healthscan(host_mob, host_mob)
if("Chemical")
@@ -162,28 +94,13 @@
. = ..()
nanites.diagnostics = TRUE
/datum/nanite_program/relay
name = "Relay"
desc = "The nanites receive and relay long-range nanite signals."
rogue_types = list(/datum/nanite_program/toxic)
extra_settings = list("Relay Channel")
var/relay_channel = 1
/datum/nanite_program/relay/set_extra_setting(user, setting)
if(setting == "Relay Channel")
var/new_channel = input(user, "Set the relay channel (1-9999):", name, null) as null|num
if(isnull(new_channel))
return
relay_channel = CLAMP(round(new_channel, 1), 1, 9999)
/datum/nanite_program/relay/get_extra_setting(setting)
if(setting == "Relay Channel")
return relay_channel
/datum/nanite_program/relay/copy_extra_settings_to(datum/nanite_program/relay/target)
target.relay_channel = relay_channel
/datum/nanite_program/relay/register_extra_settings()
extra_settings[NES_RELAY_CHANNEL] = new /datum/nanite_extra_setting/number(1, 1, 9999)
/datum/nanite_program/relay/enable_passive_effect()
. = ..()
@@ -198,7 +115,8 @@
return
if(!host_mob)
return
if(relay_code != relay_channel)
var/datum/nanite_extra_setting/NS = extra_settings[NES_RELAY_CHANNEL]
if(relay_code != NS.get_value())
return
SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, code, source)
@@ -207,7 +125,8 @@
return
if(!host_mob)
return
if(relay_code != relay_channel)
var/datum/nanite_extra_setting/NS = extra_settings[NES_RELAY_CHANNEL]
if(relay_code != NS.get_value())
return
SEND_SIGNAL(host_mob, COMSIG_NANITE_COMM_SIGNAL, comm_code, comm_message)
@@ -228,14 +147,64 @@
host_mob.nutrition -= 0.5
nanites.adjust_nanites(src, 0.5)
/datum/nanite_program/triggered/access
/datum/nanite_program/research
name = "Distributed Computing"
desc = "The nanites aid the research servers by performing a portion of its calculations, increasing research point generation."
use_rate = 0.2
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/research/active_effect()
if(!iscarbon(host_mob))
return
var/points = 1
if(!host_mob.client) //less brainpower
points *= 0.25
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points))
/datum/nanite_program/researchplus
name = "Neural Network"
desc = "The nanites link the host's brains together forming a neural research network, that becomes more efficient with the amount of total hosts."
use_rate = 0.3
rogue_types = list(/datum/nanite_program/brain_decay)
/datum/nanite_program/researchplus/enable_passive_effect()
. = ..()
if(!iscarbon(host_mob))
return
if(host_mob.client)
SSnanites.neural_network_count++
else
SSnanites.neural_network_count += 0.25
/datum/nanite_program/researchplus/disable_passive_effect()
. = ..()
if(!iscarbon(host_mob))
return
if(host_mob.client)
SSnanites.neural_network_count--
else
SSnanites.neural_network_count -= 0.25
/datum/nanite_program/researchplus/active_effect()
if(!iscarbon(host_mob))
return
var/mob/living/carbon/C = host_mob
var/points = round(SSnanites.neural_network_count / 12, 0.1)
if(!C.client) //less brainpower
points *= 0.25
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points))
/datum/nanite_program/access
name = "Subdermal ID"
desc = "The nanites store the host's ID access rights in a subdermal magnetic strip. Updates when triggered, copying the host's current access."
can_trigger = TRUE
trigger_cost = 3
trigger_cooldown = 30
rogue_types = list(/datum/nanite_program/skin_decay)
var/access = list()
//Syncs the nanites with the cumulative current mob's access level. Can potentially wipe existing access.
/datum/nanite_program/triggered/access/trigger()
/datum/nanite_program/access/on_trigger(comm_message)
var/list/new_access = list()
var/obj/item/current_item
current_item = host_mob.get_active_held_item()
@@ -284,19 +253,42 @@
SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
infectee.investigate_log("was infected by spreading nanites by [key_name(host_mob)] at [AREACOORD(infectee)].", INVESTIGATE_NANITES)
/datum/nanite_program/nanite_sting
name = "Nanite Sting"
desc = "When triggered, projects a nearly invisible spike of nanites that attempts to infect a nearby non-host with a copy of the host's nanites cluster."
can_trigger = TRUE
trigger_cost = 5
trigger_cooldown = 100
rogue_types = list(/datum/nanite_program/glitch, /datum/nanite_program/toxic)
/datum/nanite_program/nanite_sting/on_trigger(comm_message)
var/list/mob/living/target_hosts = list()
for(var/mob/living/L in oview(1, host_mob))
if(!(L.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)) || SEND_SIGNAL(L, COMSIG_HAS_NANITES) || !L.Adjacent(host_mob))
continue
target_hosts += L
if(!target_hosts.len)
consume_nanites(-5)
return
var/mob/living/infectee = pick(target_hosts)
if(prob(100 - (infectee.get_permeability_protection() * 100)))
//unlike with Infective Exo-Locomotion, this can't take over existing nanites, because Nanite Sting only targets non-hosts.
infectee.AddComponent(/datum/component/nanites, 5)
SEND_SIGNAL(infectee, COMSIG_NANITE_SYNC, nanites)
infectee.investigate_log("was infected by a nanite cluster by [key_name(host_mob)] at [AREACOORD(infectee)].", INVESTIGATE_NANITES)
to_chat(infectee, "<span class='warning'>You feel a tiny prick.</span>")
/datum/nanite_program/mitosis
name = "Mitosis"
desc = "The nanites gain the ability to self-replicate, using bluespace to power the process, instead of drawing from a template. This rapidly speeds up the replication rate,\
but it causes occasional software errors due to faulty copies. Not compatible with cloud sync."
desc = "The nanites gain the ability to self-replicate, using bluespace to power the process. Becomes more effective the more nanites are already in the host.\
The replication has also a chance to corrupt the nanite programming due to copy faults - cloud sync is highly recommended."
use_rate = 0
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/mitosis/active_effect()
if(nanites.cloud_id)
return
var/rep_rate = round(nanites.nanite_volume / 50, 1) //0.5 per 50 nanite volume
rep_rate *= 0.5
nanites.adjust_nanites(rep_rate)
nanites.adjust_nanites(null, rep_rate)
if(prob(rep_rate))
var/datum/nanite_program/fault = pick(nanites.programs)
if(fault == src)
@@ -306,56 +298,22 @@
/datum/nanite_program/dermal_button
name = "Dermal Button"
desc = "Displays a button on the host's skin, which can be used to send a signal to the nanites."
extra_settings = list("Sent Code","Button Name","Icon","Color")
unique = FALSE
var/datum/action/innate/nanite_button/button
var/button_name = "Button"
var/icon = "power"
var/color = "green"
var/sent_code = 0
/datum/nanite_program/dermal_button/set_extra_setting(user, setting)
if(setting == "Sent Code")
var/new_code = input(user, "Set the sent code (1-9999):", name, null) as null|num
if(isnull(new_code))
return
sent_code = CLAMP(round(new_code, 1), 1, 9999)
if(setting == "Button Name")
var/new_button_name = stripped_input(user, "Choose the name for the button.", "Button Name", button_name, MAX_NAME_LEN)
if(!new_button_name)
return
button_name = new_button_name
if(setting == "Icon")
var/new_icon = input("Select the icon to display on the button:", name) as null|anything in list("one","two","three","four","five","plus","minus","power")
if(!new_icon)
return
icon = new_icon
if(setting == "Color")
var/new_color = input("Select the color of the button's icon:", name) as null|anything in list("green","red","yellow","blue")
if(!new_color)
return
color = new_color
/datum/nanite_program/dermal_button/get_extra_setting(setting)
if(setting == "Sent Code")
return sent_code
if(setting == "Button Name")
return button_name
if(setting == "Icon")
return capitalize(icon)
if(setting == "Color")
return capitalize(color)
/datum/nanite_program/dermal_button/copy_extra_settings_to(datum/nanite_program/dermal_button/target)
target.sent_code = sent_code
target.button_name = button_name
target.icon = icon
target.color = color
/datum/nanite_program/dermal_button/register_extra_settings()
extra_settings[NES_SENT_CODE] = new /datum/nanite_extra_setting/number(1, 1, 9999)
extra_settings[NES_BUTTON_NAME] = new /datum/nanite_extra_setting/text("Button")
extra_settings[NES_ICON] = new /datum/nanite_extra_setting/type("power", list("one","two","three","four","five","plus","minus","power"))
extra_settings[NES_COLOR] = new /datum/nanite_extra_setting/type("green", list("green","red","yellow","blue"))
/datum/nanite_program/dermal_button/enable_passive_effect()
. = ..()
var/datum/nanite_extra_setting/bn_name = extra_settings[NES_BUTTON_NAME]
var/datum/nanite_extra_setting/bn_icon = extra_settings[NES_ICON]
var/datum/nanite_extra_setting/bn_color = extra_settings[NES_COLOR]
if(!button)
button = new(src, button_name, icon, color)
button = new(src, bn_name.get_value(), bn_icon.get_value(), bn_color.get_value())
button.target = host_mob
button.Grant(host_mob)
@@ -372,7 +330,8 @@
if(activated)
host_mob.visible_message("<span class='notice'>[host_mob] presses a button on [host_mob.p_their()] forearm.</span>",
"<span class='notice'>You press the nanite button on your forearm.</span>", null, 2)
SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, sent_code, "a [name] program")
var/datum/nanite_extra_setting/sent_code = extra_settings[NES_SENT_CODE]
SEND_SIGNAL(host_mob, COMSIG_NANITE_SIGNAL, sent_code.get_value(), "a [name] program")
/datum/action/innate/nanite_button
name = "Button"
@@ -389,51 +348,3 @@
/datum/action/innate/nanite_button/Activate()
program.press()
/datum/nanite_program/research
name = "Distributed Computing"
desc = "The nanites aid the research servers by performing a portion of its calculations, increasing research point generation."
use_rate = 0.2
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/research/active_effect()
if(!iscarbon(host_mob))
return
var/points = 1
if(!host_mob.client) //less brainpower
points *= 0.25
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points))
/datum/nanite_program/researchplus
name = "Neural Network"
desc = "The nanites link the host's brains together forming a neural research network, that becomes more efficient with the amount of total hosts."
use_rate = 0.3
rogue_types = list(/datum/nanite_program/brain_decay)
/datum/nanite_program/researchplus/enable_passive_effect()
. = ..()
if(!iscarbon(host_mob))
return
if(host_mob.client)
SSnanites.neural_network_count++
else
SSnanites.neural_network_count += 0.25
/datum/nanite_program/researchplus/disable_passive_effect()
. = ..()
if(!iscarbon(host_mob))
return
if(host_mob.client)
SSnanites.neural_network_count--
else
SSnanites.neural_network_count -= 0.25
/datum/nanite_program/researchplus/active_effect()
if(!iscarbon(host_mob))
return
var/mob/living/carbon/C = host_mob
var/points = round(SSnanites.neural_network_count / 12, 0.1)
if(!C.client) //less brainpower
points *= 0.25
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = points))
@@ -73,21 +73,20 @@
. = ..()
to_chat(host_mob, "<span class='warning'>Your blood cools down, and the pain gradually fades.</span>")
/datum/nanite_program/triggered/explosive
/datum/nanite_program/explosive
name = "Chain Detonation"
desc = "Detonates all the nanites inside the host in a chain reaction when triggered."
can_trigger = TRUE
trigger_cost = 25 //plus every idle nanite left afterwards
trigger_cooldown = 100 //Just to avoid double-triggering
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/triggered/explosive/trigger()
if(!..())
return
/datum/nanite_program/explosive/on_trigger(comm_message)
host_mob.visible_message("<span class='warning'>[host_mob] starts emitting a high-pitched buzzing, and [host_mob.p_their()] skin begins to glow...</span>",\
"<span class='userdanger'>You start emitting a high-pitched buzzing, and your skin begins to glow...</span>")
addtimer(CALLBACK(src, .proc/boom), CLAMP((nanites.nanite_volume * 0.35), 25, 150))
/datum/nanite_program/triggered/explosive/proc/boom()
/datum/nanite_program/explosive/proc/boom()
var/nanite_amount = nanites.nanite_volume
var/heavy_range = FLOOR(nanite_amount/100, 1) - 1
var/light_range = FLOOR(nanite_amount/50, 1) - 1
@@ -96,16 +95,15 @@
//TODO make it defuse if triggered again
/datum/nanite_program/triggered/heart_stop
/datum/nanite_program/heart_stop
name = "Heart-Stopper"
desc = "Stops the host's heart when triggered; restarts it if triggered again."
can_trigger = TRUE
trigger_cost = 12
trigger_cooldown = 10
rogue_types = list(/datum/nanite_program/nerve_decay)
/datum/nanite_program/triggered/heart_stop/trigger()
if(!..())
return
/datum/nanite_program/heart_stop/on_trigger(comm_message)
if(iscarbon(host_mob))
var/mob/living/carbon/C = host_mob
var/obj/item/organ/heart/heart = C.getorganslot(ORGAN_SLOT_HEART)
@@ -115,16 +113,16 @@
else
heart.Restart()
/datum/nanite_program/triggered/emp
/datum/nanite_program/emp
name = "Electromagnetic Resonance"
desc = "The nanites cause an elctromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
desc = "The nanites cause an electromagnetic pulse around the host when triggered. Will corrupt other nanite programs!"
can_trigger = TRUE
trigger_cost = 10
trigger_cooldown = 50
program_flags = NANITE_EMP_IMMUNE
rogue_types = list(/datum/nanite_program/toxic)
/datum/nanite_program/triggered/emp/trigger()
if(!..())
return
/datum/nanite_program/emp/on_trigger(comm_message)
empulse(host_mob, 1, 2)
/datum/nanite_program/pyro/active_effect()
@@ -148,7 +146,7 @@
/datum/nanite_program/cryo
name = "Cryogenic Treatment"
desc = "The nanites rapidly skin heat through the host's skin, lowering their temperature."
desc = "The nanites rapidly sink heat through the host's skin, lowering their temperature."
use_rate = 1
rogue_types = list(/datum/nanite_program/skin_decay, /datum/nanite_program/pyro)
@@ -160,55 +158,33 @@
/datum/nanite_program/cryo/active_effect()
host_mob.adjust_bodytemperature(-rand(15,25), 50)
/datum/nanite_program/triggered/comm/mind_control
/datum/nanite_program/comm/mind_control
name = "Mind Control"
desc = "The nanites imprint an absolute directive onto the host's brain for one minute when triggered."
trigger_cost = 30
trigger_cooldown = 1800
rogue_types = list(/datum/nanite_program/brain_decay, /datum/nanite_program/brain_misfire)
extra_settings = list("Directive","Comm Code")
var/directive = "..."
/datum/nanite_program/comm/mind_control/register_extra_settings()
. = ..()
extra_settings[NES_DIRECTIVE] = new /datum/nanite_extra_setting/text("...")
/datum/nanite_program/triggered/comm/mind_control/set_extra_setting(user, setting)
if(setting == "Directive")
var/new_directive = stripped_input(user, "Choose the directive to imprint with mind control.", "Directive", directive, MAX_MESSAGE_LEN)
if(!new_directive)
return
directive = new_directive
if(setting == "Comm Code")
var/new_code = input(user, "Set the communication code (1-9999) or set to 0 to disable external signals.", name, null) as null|num
if(isnull(new_code))
return
comm_code = CLAMP(round(new_code, 1), 0, 9999)
/datum/nanite_program/triggered/comm/mind_control/get_extra_setting(setting)
if(setting == "Directive")
return directive
if(setting == "Comm Code")
return comm_code
/datum/nanite_program/triggered/comm/mind_control/copy_extra_settings_to(datum/nanite_program/triggered/comm/mind_control/target)
target.directive = directive
target.comm_code = comm_code
/datum/nanite_program/triggered/comm/mind_control/trigger(comm_message)
if(!..())
return
/datum/nanite_program/comm/mind_control/on_trigger(comm_message)
if(host_mob.stat == DEAD)
return
var/sent_directive = comm_message
if(!comm_message)
sent_directive = directive
var/datum/nanite_extra_setting/ES = extra_settings[NES_DIRECTIVE]
sent_directive = ES.get_value()
brainwash(host_mob, sent_directive)
log_game("A mind control nanite program brainwashed [key_name(host_mob)] with the objective '[directive]'.")
log_game("A mind control nanite program brainwashed [key_name(host_mob)] with the objective '[sent_directive]'.")
addtimer(CALLBACK(src, .proc/end_brainwashing), 600)
/datum/nanite_program/triggered/comm/mind_control/proc/end_brainwashing()
/datum/nanite_program/comm/mind_control/proc/end_brainwashing()
if(host_mob.mind && host_mob.mind.has_antag_datum(/datum/antagonist/brainwashed))
host_mob.mind.remove_antag_datum(/datum/antagonist/brainwashed)
log_game("[key_name(host_mob)] is no longer brainwashed by nanites.")
/datum/nanite_program/triggered/comm/mind_control/disable_passive_effect()
/datum/nanite_program/comm/mind_control/disable_passive_effect()
. = ..()
end_brainwashing()
+30 -76
View File
@@ -18,6 +18,7 @@
var/last_id = 0
var/code = 0
var/relay_code = 0
var/current_program_name = "Program"
/obj/item/nanite_remote/examine(mob/user)
. = ..()
@@ -35,10 +36,8 @@
update_icon()
else
to_chat(user, "<span class='warning'>Access denied.</span>")
return TRUE
/obj/item/nanite_remote/emag_act(mob/user)
. = ..()
if(obj_flags & EMAGGED)
return
to_chat(user, "<span class='warning'>You override [src]'s ID lock.</span>")
@@ -46,33 +45,31 @@
if(locked)
locked = FALSE
update_icon()
return TRUE
/obj/item/nanite_remote/update_icon()
/obj/item/nanite_remote/update_overlays()
. = ..()
cut_overlays()
if(obj_flags & EMAGGED)
add_overlay("nanite_remote_emagged")
. += "nanite_remote_emagged"
if(locked)
add_overlay("nanite_remote_locked")
. += "nanite_remote_locked"
/obj/item/nanite_remote/afterattack(atom/target, mob/user, etc)
switch(mode)
if(REMOTE_MODE_OFF)
return
if(REMOTE_MODE_SELF)
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites in your bloodstream.<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites in your bloodstream.</span>")
signal_mob(user, code, key_name(user))
if(REMOTE_MODE_TARGET)
if(isliving(target) && (get_dist(target, get_turf(src)) <= 7))
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside [target].<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside [target].</span>")
signal_mob(target, code, key_name(user))
if(REMOTE_MODE_AOE)
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside every host around you.<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside every host around you.</span>")
for(var/mob/living/L in view(user, 7))
signal_mob(L, code, key_name(user))
if(REMOTE_MODE_RELAY)
to_chat(user, "<span class='notice'>You activate [src], signaling all connected relay nanites.<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling all connected relay nanites.</span>")
signal_relay(code, relay_code, key_name(user))
/obj/item/nanite_remote/proc/signal_mob(mob/living/M, code, source)
@@ -84,9 +81,9 @@
N.relay_signal(code, relay_code, source)
/obj/item/nanite_remote/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_remote", name, 420, 800, master_ui, state)
ui = new(user, src, ui_key, "nanite_remote", name, 420, 500, master_ui, state)
ui.open()
/obj/item/nanite_remote/ui_data()
@@ -96,6 +93,7 @@
data["mode"] = mode
data["locked"] = locked
data["saved_settings"] = saved_settings
data["program_name"] = current_program_name
return data
@@ -106,7 +104,7 @@
if("set_code")
if(locked)
return
var/new_code = input("Set code (0000-9999):", name, code) as null|num
var/new_code = text2num(params["code"])
if(!isnull(new_code))
new_code = CLAMP(round(new_code, 1),0,9999)
code = new_code
@@ -114,21 +112,21 @@
if("set_relay_code")
if(locked)
return
var/new_code = input("Set relay code (0000-9999):", name, code) as null|num
var/new_code = text2num(params["code"])
if(!isnull(new_code))
new_code = CLAMP(round(new_code, 1),0,9999)
relay_code = new_code
. = TRUE
if("update_name")
current_program_name = params["name"]
. = TRUE
if("save")
if(locked)
return
var/code_name = stripped_input(usr, "Set the setting name", "Set Name", null , 15)
if(!code_name)
return
var/new_save = list()
new_save["id"] = last_id + 1
last_id++
new_save["name"] = code_name
new_save["name"] = current_program_name
new_save["code"] = code
new_save["mode"] = mode
new_save["relay_code"] = relay_code
@@ -172,7 +170,6 @@
name = "nanite communication remote"
desc = "A device that can send text messages to specific programs."
icon_state = "nanite_comm_remote"
var/comm_code = 0
var/comm_message = ""
/obj/item/nanite_remote/comm/afterattack(atom/target, mob/user, etc)
@@ -180,42 +177,38 @@
if(REMOTE_MODE_OFF)
return
if(REMOTE_MODE_SELF)
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites in your bloodstream.<span>")
signal_mob(user, comm_code, comm_message)
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites in your bloodstream.</span>")
signal_mob(user, code, comm_message)
if(REMOTE_MODE_TARGET)
if(isliving(target) && (get_dist(target, get_turf(src)) <= 7))
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside [target].<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside [target].</span>")
signal_mob(target, code, comm_message, key_name(user))
if(REMOTE_MODE_AOE)
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside every host around you.<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling the nanites inside every host around you.</span>")
for(var/mob/living/L in view(user, 7))
signal_mob(L, code, comm_message, key_name(user))
if(REMOTE_MODE_RELAY)
to_chat(user, "<span class='notice'>You activate [src], signaling all connected relay nanites.<span>")
to_chat(user, "<span class='notice'>You activate [src], signaling all connected relay nanites.</span>")
signal_relay(code, relay_code, comm_message, key_name(user))
/obj/item/nanite_remote/comm/signal_mob(mob/living/M, code, source)
SEND_SIGNAL(M, COMSIG_NANITE_COMM_SIGNAL, comm_code, comm_message)
SEND_SIGNAL(M, COMSIG_NANITE_COMM_SIGNAL, code, comm_message)
/obj/item/nanite_remote/comm/signal_relay(code, relay_code, source)
for(var/X in SSnanites.nanite_relays)
var/datum/nanite_program/relay/N = X
N.relay_comm_signal(comm_code, relay_code, comm_message)
/obj/item/nanite_remote/comm/ui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/ui_state/state = GLOB.hands_state)
SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "nanite_comm_remote", name, 420, 800, master_ui, state)
ui.open()
N.relay_comm_signal(code, relay_code, comm_message)
/obj/item/nanite_remote/comm/ui_data()
var/list/data = list()
data["comm_code"] = comm_code
data["comms"] = TRUE
data["code"] = code
data["relay_code"] = relay_code
data["comm_message"] = comm_message
data["message"] = comm_message
data["mode"] = mode
data["locked"] = locked
data["saved_settings"] = saved_settings
data["program_name"] = current_program_name
return data
@@ -223,56 +216,17 @@
if(..())
return
switch(action)
if("set_comm_code")
if(locked)
return
var/new_code = input("Set comm code (0000-9999):", name, code) as null|num
if(!isnull(new_code))
new_code = CLAMP(round(new_code, 1),0,9999)
comm_code = new_code
. = TRUE
if("set_message")
if(locked)
return
var/new_message = stripped_input(usr, "Set the message (Max 300 characters):", "Set Message", null , 300)
var/new_message = html_encode(params["value"])
if(!new_message)
return
comm_message = new_message
. = TRUE
if("comm_save")
if(locked)
return
var/code_name = stripped_input(usr, "Set the setting name", "Set Name", null , 15)
if(!code_name)
return
var/new_save = list()
new_save["id"] = last_id + 1
last_id++
new_save["name"] = code_name
new_save["code"] = comm_code
new_save["mode"] = mode
new_save["relay_code"] = relay_code
new_save["message"] = comm_message
saved_settings += list(new_save)
. = TRUE
if("comm_load")
var/code_id = params["save_id"]
var/list/setting
for(var/list/X in saved_settings)
if(X["id"] == text2num(code_id))
setting = X
break
if(setting)
comm_code = setting["code"]
mode = setting["mode"]
relay_code = setting["relay_code"]
comm_message = setting["message"]
. = TRUE
#undef REMOTE_MODE_OFF
#undef REMOTE_MODE_SELF
#undef REMOTE_MODE_TARGET
#undef REMOTE_MODE_AOE
#undef REMOTE_MODE_RELAY
#undef REMOTE_MODE_RELAY
@@ -31,7 +31,7 @@
program_type = /datum/nanite_program/relay
/obj/item/disk/nanite_program/emp
program_type = /datum/nanite_program/triggered/emp
program_type = /datum/nanite_program/emp
/obj/item/disk/nanite_program/spreading
program_type = /datum/nanite_program/spreading
@@ -91,16 +91,16 @@
program_type = /datum/nanite_program/suffocating
/obj/item/disk/nanite_program/heart_stop
program_type = /datum/nanite_program/triggered/heart_stop
program_type = /datum/nanite_program/heart_stop
/obj/item/disk/nanite_program/explosive
program_type = /datum/nanite_program/triggered/explosive
program_type = /datum/nanite_program/explosive
/obj/item/disk/nanite_program/shock
program_type = /datum/nanite_program/triggered/shocking
program_type = /datum/nanite_program/shocking
/obj/item/disk/nanite_program/sleepy
program_type = /datum/nanite_program/triggered/sleepy
program_type = /datum/nanite_program/sleepy
/obj/item/disk/nanite_program/paralyzing
program_type = /datum/nanite_program/paralyzing
@@ -130,7 +130,7 @@
program_type = /datum/nanite_program/pacifying
/obj/item/disk/nanite_program/stun
program_type = /datum/nanite_program/triggered/stun
program_type = /datum/nanite_program/stun
/obj/item/disk/nanite_program/dermal_button
program_type = /datum/nanite_program/dermal_button
@@ -149,3 +149,4 @@
/obj/item/disk/nanite_program/bad_mood
program_type = /datum/nanite_program/bad_mood
+54 -28
View File
@@ -32,7 +32,7 @@
busy_icon_state = working_icon
update_icon()
/obj/machinery/public_nanite_chamber/proc/inject_nanites()
/obj/machinery/public_nanite_chamber/proc/inject_nanites(mob/living/attacker)
if(stat & (NOPOWER|BROKEN))
return
if((stat & MAINT) || panel_open)
@@ -47,46 +47,67 @@
set_busy(TRUE, "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_active"),20)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_falling"),60)
addtimer(CALLBACK(src, .proc/complete_injection, locked_state),80)
addtimer(CALLBACK(src, .proc/complete_injection, locked_state, attacker),80)
/obj/machinery/public_nanite_chamber/proc/complete_injection(locked_state)
/obj/machinery/public_nanite_chamber/proc/complete_injection(locked_state, mob/living/attacker)
//TODO MACHINE DING
locked = locked_state
set_busy(FALSE)
if(!occupant)
return
if(attacker)
occupant.investigate_log("was injected with nanites by [key_name(attacker)] using [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
log_combat(attacker, occupant, "injected", null, "with nanites via [src]")
occupant.AddComponent(/datum/component/nanites, 75, cloud_id)
/obj/machinery/public_nanite_chamber/update_icon()
cut_overlays()
/obj/machinery/public_nanite_chamber/proc/change_cloud(mob/living/attacker)
if(stat & (NOPOWER|BROKEN))
return
if((stat & MAINT) || panel_open)
add_overlay("maint")
return
if(!occupant || busy)
return
else if(!(stat & (NOPOWER|BROKEN)))
if(busy || locked)
add_overlay("red")
if(locked)
add_overlay("bolted")
else
add_overlay("green")
var/locked_state = locked
locked = TRUE
set_busy(TRUE, "[initial(icon_state)]_raising")
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_active"),20)
addtimer(CALLBACK(src, .proc/set_busy, TRUE, "[initial(icon_state)]_falling"),40)
addtimer(CALLBACK(src, .proc/complete_cloud_change, locked_state, attacker),60)
/obj/machinery/public_nanite_chamber/proc/complete_cloud_change(locked_state, mob/living/attacker)
locked = locked_state
set_busy(FALSE)
if(!occupant)
return
if(attacker)
occupant.investigate_log("had their nanite cloud ID changed into [cloud_id] by [key_name(attacker)] using [src] at [AREACOORD(src)].", INVESTIGATE_NANITES)
SEND_SIGNAL(occupant, COMSIG_NANITE_SET_CLOUD, cloud_id)
/obj/machinery/public_nanite_chamber/update_icon_state()
//running and someone in there
if(occupant)
if(busy)
icon_state = busy_icon_state
else
icon_state = initial(icon_state)+ "_occupied"
return
else
//running
icon_state = initial(icon_state)+ (state_open ? "_open" : "")
//running
icon_state = initial(icon_state)+ (state_open ? "_open" : "")
/obj/machinery/public_nanite_chamber/power_change()
/obj/machinery/public_nanite_chamber/update_overlays()
. = ..()
update_icon()
if((stat & MAINT) || panel_open)
. += "maint"
else if(!(stat & (NOPOWER|BROKEN)))
if(busy || locked)
. += "red"
if(locked)
. += "bolted"
else
. += "green"
/obj/machinery/public_nanite_chamber/proc/toggle_open(mob/user)
if(panel_open)
@@ -94,7 +115,7 @@
return
if(state_open)
close_machine()
close_machine(null, user)
return
else if(locked)
@@ -113,7 +134,7 @@
user.last_special = world.time + CLICK_CD_BREAKOUT
user.visible_message("<span class='notice'>You see [user] kicking against the door of [src]!</span>", \
"<span class='notice'>You lean on the back of [src] and start pushing the door open... (this will take about [DisplayTimeText(breakout_time)].)</span>", \
"<span class='italics'>You hear a metallic creaking from [src].</span>")
"<span class='hear'>You hear a metallic creaking from [src].</span>")
if(do_after(user,(breakout_time), target = src))
if(!user || user.stat != CONSCIOUS || user.loc != src || state_open || !locked || busy)
return
@@ -122,7 +143,7 @@
"<span class='notice'>You successfully break out of [src]!</span>")
open_machine()
/obj/machinery/public_nanite_chamber/close_machine(mob/living/carbon/user)
/obj/machinery/public_nanite_chamber/close_machine(mob/living/carbon/user, mob/living/attacker)
if(!state_open)
return FALSE
@@ -130,15 +151,18 @@
. = TRUE
addtimer(CALLBACK(src, .proc/try_inject_nanites), 30) //If someone is shoved in give them a chance to get out before the injection starts
addtimer(CALLBACK(src, .proc/try_inject_nanites, attacker), 30) //If someone is shoved in give them a chance to get out before the injection starts
/obj/machinery/public_nanite_chamber/proc/try_inject_nanites()
/obj/machinery/public_nanite_chamber/proc/try_inject_nanites(mob/living/attacker)
if(occupant)
var/mob/living/L = occupant
if(SEND_SIGNAL(L, COMSIG_HAS_NANITES))
var/datum/component/nanites/nanites = L.GetComponent(/datum/component/nanites)
if(nanites && nanites.cloud_id != cloud_id)
change_cloud(attacker)
return
if(L.mob_biotypes & (MOB_ORGANIC | MOB_UNDEAD))
inject_nanites()
inject_nanites(attacker)
/obj/machinery/public_nanite_chamber/open_machine()
if(state_open)
@@ -173,6 +197,8 @@
toggle_open(user)
/obj/machinery/public_nanite_chamber/MouseDrop_T(mob/target, mob/user)
if(user.stat || user.lying || !Adjacent(user) || !user.Adjacent(target) || !iscarbon(target) || !user.IsAdvancedToolUser())
if(!user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK) || !Adjacent(target) || !user.Adjacent(target) || !iscarbon(target))
return
close_machine(target)
if(close_machine(target, user))
log_combat(user, target, "inserted", null, "into [src].")
add_fingerprint(user)
+151
View File
@@ -0,0 +1,151 @@
/datum/nanite_rule
var/name = "Generic Condition"
var/desc = "When triggered, the program is active"
var/datum/nanite_program/program
/datum/nanite_rule/New(datum/nanite_program/new_program)
program = new_program
if(LAZYLEN(new_program.rules) <= 5) //Avoid infinite stacking rules
new_program.rules += src
else
qdel(src)
/datum/nanite_rule/proc/remove()
program.rules -= src
program = null
qdel(src)
/datum/nanite_rule/proc/check_rule()
return TRUE
/datum/nanite_rule/proc/display()
return name
/datum/nanite_rule/proc/copy_to(datum/nanite_program/new_program)
new type(new_program)
/datum/nanite_rule/health
name = "Health"
desc = "Checks the host's health status."
var/threshold = 50
var/above = TRUE
/datum/nanite_rule/health/check_rule()
var/health_percent = program.host_mob.health / program.host_mob.maxHealth * 100
if(above)
if(health_percent >= threshold)
return TRUE
else
if(health_percent < threshold)
return TRUE
return FALSE
/datum/nanite_rule/health/display()
return "[name] [above ? ">" : "<"] [threshold]%"
/datum/nanite_rule/health/copy_to(datum/nanite_program/new_program)
var/datum/nanite_rule/health/rule = new(new_program)
rule.above = above
rule.threshold = threshold
//TODO allow inversion
/datum/nanite_rule/crit
name = "Crit"
desc = "Checks if the host is in critical condition."
/datum/nanite_rule/crit/check_rule()
if(program.host_mob.InCritical())
return TRUE
return FALSE
/datum/nanite_rule/death
name = "Death"
desc = "Checks if the host is dead."
/datum/nanite_rule/death/check_rule()
if(program.host_mob.stat == DEAD || HAS_TRAIT(program.host_mob, TRAIT_FAKEDEATH))
return TRUE
return FALSE
/datum/nanite_rule/cloud_sync
name = "Cloud Sync"
desc = "Checks if the nanites have cloud sync enabled or disabled."
var/check_type = "Enabled"
/datum/nanite_rule/cloud_sync/check_rule()
if(check_type == "Enabled")
return program.nanites.cloud_active
else
return !program.nanites.cloud_active
/datum/nanite_rule/cloud_sync/copy_to(datum/nanite_program/new_program)
var/datum/nanite_rule/cloud_sync/rule = new(new_program)
rule.check_type = check_type
/datum/nanite_rule/cloud_sync/display()
return "[name]:[check_type]"
/datum/nanite_rule/nanites
name = "Nanite Volume"
desc = "Checks the host's nanite volume."
var/threshold = 50
var/above = TRUE
/datum/nanite_rule/nanites/check_rule()
var/nanite_percent = (program.nanites.nanite_volume - program.nanites.safety_threshold)/(program.nanites.max_nanites - program.nanites.safety_threshold)*100
if(above)
if(nanite_percent >= threshold)
return TRUE
else
if(nanite_percent < threshold)
return TRUE
return FALSE
/datum/nanite_rule/nanites/copy_to(datum/nanite_program/new_program)
var/datum/nanite_rule/nanites/rule = new(new_program)
rule.above = above
rule.threshold = threshold
/datum/nanite_rule/nanites/display()
return "[name] [above ? ">" : "<"] [threshold]%"
/datum/nanite_rule/damage
name = "Damage"
desc = "Checks the host's damage."
var/threshold = 50
var/above = TRUE
var/damage_type = BRUTE
/datum/nanite_rule/damage/check_rule()
var/damage_amt = 0
switch(damage_type)
if(BRUTE)
damage_amt = program.host_mob.getBruteLoss()
if(BURN)
damage_amt = program.host_mob.getFireLoss()
if(TOX)
damage_amt = program.host_mob.getToxLoss()
if(OXY)
damage_amt = program.host_mob.getOxyLoss()
if(CLONE)
damage_amt = program.host_mob.getCloneLoss()
if(above)
if(damage_amt >= threshold)
return TRUE
else
if(damage_amt < threshold)
return TRUE
return FALSE
/datum/nanite_rule/damage/copy_to(datum/nanite_program/new_program)
var/datum/nanite_rule/damage/rule = new(new_program)
rule.above = above
rule.threshold = threshold
rule.damage_type = damage_type
/datum/nanite_rule/damage/display()
return "[damage_type] [above ? ">" : "<"] [threshold]"
+1 -1
View File
@@ -602,7 +602,7 @@
display_name = "Basic Tools"
description = "Basic mechanical, electronic, surgical and botanical tools."
prereq_ids = list("base")
design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop")
design_ids = list("screwdriver", "wrench", "wirecutters", "crowbar", "multitool", "welding_tool", "tscanner", "analyzer", "cable_coil", "pipe_painter", "airlock_painter", "scalpel", "circular_saw", "surgicaldrill", "retractor", "cautery", "hemostat", "cultivator", "plant_analyzer", "shovel", "spade", "hatchet", "mop", "broom")
research_costs = list(TECHWEB_POINT_TYPE_GENERIC = 500)
export_price = 5000
+100 -32
View File
@@ -3,13 +3,38 @@
#define ENGINES_STARTED (SSshuttle.emergency.mode == SHUTTLE_IGNITING)
#define IS_DOCKED (SSshuttle.emergency.mode == SHUTTLE_DOCKED || (ENGINES_STARTED))
#define NOT_BEGUN 0
#define STAGE_1 1
#define STAGE_2 2
#define STAGE_3 3
#define STAGE_4 4
#define HIJACKED 5
/obj/machinery/computer/emergency_shuttle
name = "emergency shuttle console"
desc = "For shuttle control."
icon_screen = "shuttle"
icon_keyboard = "tech_key"
resistance_flags = INDESTRUCTIBLE
var/auth_need = 3
var/list/authorized = list()
var/hijack_last_stage_increase = 0
var/hijack_stage_time = 50
var/hijack_stage_cooldown = 50
var/hijack_flight_time_increase = 300
var/hijack_completion_flight_time_set = 100 //How long in deciseconds to set shuttle's timer after hijack is done.
var/hijack_hacking = FALSE
var/hijack_announce = TRUE
/obj/machinery/computer/emergency_shuttle/examine(mob/user)
. = ..()
if(hijack_announce)
. += "<span class='danger'>Security systems present on console. Any unauthorized tampering will result in an emergency announcement.</span>"
if(user?.mind?.get_hijack_speed())
. += "<span class='danger'>Alt click on this to attempt to hijack the shuttle. This will take multiple tries (current: stage [SSshuttle.emergency.hijack_status]/[HIJACKED]).</span>"
. += "<span class='notice'>It will take you [(hijack_stage_time * user.mind.get_hijack_speed()) / 10] seconds to reprogram a stage of the shuttle's navigational firmware, and the console will undergo automated timed lockout for [hijack_stage_cooldown/10] seconds after each stage.</span>"
if(hijack_announce)
. += "<span class='warning'>It is probably best to fortify your position as to be uninterrupted during the attempt, given the automatic announcements..</span>"
/obj/machinery/computer/emergency_shuttle/attackby(obj/item/I, mob/user,params)
if(istype(I, /obj/item/card/id))
@@ -133,6 +158,71 @@
[TIME_LEFT] seconds", system_error, alert=TRUE)
. = TRUE
/obj/machinery/computer/emergency_shuttle/proc/increase_hijack_stage()
var/obj/docking_port/mobile/emergency/shuttle = SSshuttle.emergency
shuttle.hijack_status++
if(hijack_announce)
announce_hijack_stage()
hijack_last_stage_increase = world.time
say("Navigational protocol error! Rebooting systems.")
if(shuttle.mode == SHUTTLE_ESCAPE)
if(shuttle.hijack_status == HIJACKED)
shuttle.setTimer(hijack_completion_flight_time_set)
else
shuttle.setTimer(shuttle.timeLeft(1) + hijack_flight_time_increase) //give the guy more time to hijack if it's already in flight.
return shuttle.hijack_status
/obj/machinery/computer/emergency_shuttle/AltClick(user)
attempt_hijack_stage(user)
/obj/machinery/computer/emergency_shuttle/proc/attempt_hijack_stage(mob/living/user)
if(!user.CanReach(src))
return
if(!user?.mind?.get_hijack_speed())
to_chat(user, "<span class='warning'>You manage to open a user-mode shell on [src], and hundreds of lines of debugging output fly through your vision. It is probably best to leave this alone.</span.")
return
if(hijack_hacking == TRUE)
return
if(SSshuttle.emergency.hijack_status >= HIJACKED)
to_chat(user, "<span class='warning'>The emergency shuttle is already loaded with a corrupt navigational payload. What more do you want from it?</span>")
return
if(hijack_last_stage_increase >= world.time + hijack_stage_cooldown)
say("Error - Catastrophic software error detected. Input is currently on timeout.")
return
hijack_hacking = TRUE
to_chat(user, "<span class='boldwarning'>You [SSshuttle.emergency.hijack_status == NOT_BEGUN? "begin" : "continue"] to override [src]'s navigational protocols.</span>")
say("Software override initiated.")
. = FALSE
if(do_after(user, hijack_stage_time * (1 / user.mind.get_hijack_speed()), target = src))
increase_hijack_stage()
. = TRUE
to_chat(user, "<span class='notice'>You reprogram some of [src]'s programming, putting it on timeout for [hijack_stage_cooldown/10] seconds.</span>")
hijack_hacking = FALSE
/obj/machinery/computer/emergency_shuttle/proc/announce_hijack_stage()
var/msg
switch(SSshuttle.emergency.hijack_status)
if(NOT_BEGUN)
return
if(STAGE_1)
var/datum/species/S = new
msg = "AUTHENTICATING - FAIL. AUTHENTICATING - FAIL. AUTHENTICATING - FAI###### Welcome, technician JOHN DOE."
qdel(S)
if(STAGE_2)
msg = "Warning: Navigational route fails \"IS_AUTHORIZED\". Please try againNN[scramble_message_replace_chars("againagainagainagainagain", 70)]."
if(STAGE_3)
var/hex = ""
for(var/i in 1 to 8)
hex += num2hex(rand(1,16))
msg = "CRC mismatch at 0x[hex] in calculated route buffer. Full reset initiated of FTL_NAVIGATION_SERVICES. Memory decrypted for automatic repair."
if(STAGE_4)
msg = "~ACS_directive module_load(cyberdyne.exploit.nanotrasen.shuttlenav)... NT key mismatch. Confirm load? Y...###Reboot complete. $SET transponder_state = 0; System link initiated with connected engines..."
if(HIJACKED)
msg = "<font color='red'>SYSTEM OVERRIDE - Resetting course to \[[scramble_message_replace_chars("###########", 100)]\] \
([scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]/[scramble_message_replace_chars("#######", 100)]) \
{AUTH - ROOT (uid: 0)}.</font>[SSshuttle.emergency.mode == SHUTTLE_ESCAPE? "Diverting from existing route - Bluespace exit in [hijack_completion_flight_time_set/10] seconds." : ""]"
minor_announce(scramble_message_replace_chars(msg, replaceprob = 10), "Emergency Shuttle", TRUE)
/obj/machinery/computer/emergency_shuttle/emag_act(mob/user)
. = ..()
@@ -185,6 +275,7 @@
dir = EAST
port_direction = WEST
var/sound_played = 0 //If the launch sound has been sent to all players on the shuttle itself
var/hijack_status = NOT_BEGUN
/obj/docking_port/mobile/emergency/canDock(obj/docking_port/stationary/S)
return SHUTTLE_CAN_DOCK //If the emergency shuttle can't move, the whole game breaks, so it will force itself to land even if it has to crush a few departments in the process
@@ -250,37 +341,7 @@
priority_announce("The emergency shuttle has been recalled.[SSshuttle.emergencyLastCallLoc ? " Recall signal traced. Results can be viewed on any communications console." : "" ]", null, "shuttlerecalled", "Priority")
/obj/docking_port/mobile/emergency/proc/is_hijacked()
var/has_people = FALSE
var/hijacker_present = FALSE
for(var/mob/living/player in GLOB.player_list)
if(player.mind)
if(player.stat != DEAD)
if(issilicon(player)) //Borgs are technically dead anyways
continue
if(isanimal(player)) //animals don't count
continue
if(isbrain(player)) //also technically dead
continue
if(shuttle_areas[get_area(player)])
has_people = TRUE
var/location = get_turf(player.mind.current)
//Non-antag present. Can't hijack.
if(!(player.mind.has_antag_datum(/datum/antagonist)) && !istype(location, /turf/open/floor/plasteel/shuttle/red) && !istype(location, /turf/open/floor/mineral/plastitanium/red/brig))
return FALSE
//Antag present, doesn't stop but let's see if we actually want to hijack
var/prevent = FALSE
for(var/datum/antagonist/A in player.mind.antag_datums)
if(A.can_hijack == HIJACK_HIJACKER)
hijacker_present = TRUE
prevent = FALSE
break //If we have both prevent and hijacker antags assume we want to hijack.
else if(A.can_hijack == HIJACK_PREVENT)
prevent = TRUE
if(prevent)
return FALSE
return has_people && hijacker_present
return hijack_status == HIJACKED
/obj/docking_port/mobile/emergency/proc/ShuttleDBStuff()
set waitfor = FALSE
@@ -422,7 +483,7 @@
mode = SHUTTLE_ESCAPE
launch_status = ENDGAME_LAUNCHED
setTimer(SSshuttle.emergencyEscapeTime)
priority_announce("The Emergency Shuttle preparing for direct jump. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
priority_announce("The Emergency Shuttle is preparing for direct jump. Estimate [timeLeft(600)] minutes until the shuttle docks at Central Command.", null, null, "Priority")
/obj/docking_port/mobile/pod
@@ -567,3 +628,10 @@
#undef ENGINES_START_TIME
#undef ENGINES_STARTED
#undef IS_DOCKED
#undef NOT_BEGUN
#undef STAGE_1
#undef STAGE_2
#undef STAGE_3
#undef STAGE_4
#undef HIJACKED
@@ -2,7 +2,7 @@
name = "enhancement surgery"
var/bioware_target = BIOWARE_GENERIC
/datum/surgery/advanced/bioware/can_start(mob/user, mob/living/carbon/human/target)
/datum/surgery/advanced/bioware/can_start(mob/user, mob/living/carbon/human/target, obj/item/tool)
if(!..())
return FALSE
if(!istype(target))
@@ -16,7 +16,7 @@
target_mobtypes = list(/mob/living/carbon/human)
possible_locs = list(BODY_ZONE_HEAD)
/datum/surgery/advanced/brainwashing/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/brainwashing/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(!..())
return FALSE
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
+1 -1
View File
@@ -12,7 +12,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_HEAD)
requires_bodypart_type = 0
/datum/surgery/advanced/lobotomy/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/lobotomy/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(!..())
return FALSE
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
@@ -8,7 +8,7 @@
/datum/surgery_step/bionecrosis,
/datum/surgery_step/close)
possible_locs = list(BODY_ZONE_HEAD)
/datum/surgery/advanced/necrotic_revival/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/necrotic_revival/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
. = ..()
var/obj/item/organ/zombie_infection/ZI = target.getorganslot(ORGAN_SLOT_ZOMBIE)
if(ZI)
@@ -11,7 +11,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_HEAD)
requires_bodypart_type = 0
/datum/surgery/advanced/pacify/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/pacify/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
. = ..()
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
if(!B)
+1 -1
View File
@@ -12,7 +12,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_HEAD)
requires_bodypart_type = 0
/datum/surgery/advanced/revival/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/revival/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(!..())
return FALSE
if(target.stat != DEAD)
@@ -10,7 +10,7 @@
target_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
possible_locs = list(BODY_ZONE_CHEST)
/datum/surgery/advanced/viral_bonding/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/advanced/viral_bonding/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(!..())
return FALSE
if(!LAZYLEN(target.diseases))
+1 -1
View File
@@ -15,7 +15,7 @@
name = "fix brain"
implements = list(TOOL_HEMOSTAT = 85, TOOL_SCREWDRIVER = 35, /obj/item/pen = 15) //don't worry, pouring some alcohol on their open brain will get that chance to 100
time = 120 //long and complicated
/datum/surgery/brain_surgery/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/brain_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/brain/B = target.getorganslot(ORGAN_SLOT_BRAIN)
if(!B)
return FALSE
+1 -1
View File
@@ -6,7 +6,7 @@
lying_required = FALSE
ignore_clothes = TRUE
/datum/surgery/core_removal/can_start(mob/user, mob/living/target)
/datum/surgery/core_removal/can_start(mob/user, mob/living/target, obj/item/tool)
if(target.stat == DEAD)
return 1
return 0
+1 -1
View File
@@ -4,7 +4,7 @@
/datum/surgery_step/incise_heart, /datum/surgery_step/coronary_bypass, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
/datum/surgery/coronary_bypass/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/coronary_bypass/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/heart/H = target.getorganslot(ORGAN_SLOT_HEART)
if(H)
if(H.damage > 60 && !H.operated)
@@ -15,7 +15,7 @@
requires_tech = FALSE
var/value_multiplier = 1
/datum/surgery/advanced/experimental_dissection/can_start(mob/user, mob/living/target)
/datum/surgery/advanced/experimental_dissection/can_start(mob/user, mob/living/target, obj/item/tool)
. = ..()
if(HAS_TRAIT_FROM(target, TRAIT_DISSECTED,"[name]"))
return FALSE
+1 -1
View File
@@ -9,7 +9,7 @@
name = "fix eyes"
implements = list(TOOL_HEMOSTAT = 100, TOOL_SCREWDRIVER = 45, /obj/item/pen = 25)
time = 64
/datum/surgery/eye_surgery/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/eye_surgery/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/eyes/E = target.getorganslot(ORGAN_SLOT_EYES)
if(!E)
to_chat(user, "It's hard to do surgery on someone's eyes when [target.p_they()] [target.p_do()]n't have any.")
+2 -2
View File
@@ -34,7 +34,7 @@
continue
if(S.lying_required && !(M.lying))
continue
if(!S.can_start(user, M))
if(!S.can_start(user, M, I))
continue
for(var/path in S.target_mobtypes)
if(istype(M, path))
@@ -64,7 +64,7 @@
return
if(S.lying_required && !(M.lying))
return
if(!S.can_start(user, M))
if(!S.can_start(user, M, I))
return
if(S.ignore_clothes || get_location_accessible(M, selected_zone))
+1 -1
View File
@@ -2,7 +2,7 @@
name = "Lipoplasty"
steps = list(/datum/surgery_step/incise, /datum/surgery_step/clamp_bleeders, /datum/surgery_step/cut_fat, /datum/surgery_step/remove_fat, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
/datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/lipoplasty/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(HAS_TRAIT(target, TRAIT_FAT))
return 1
return 0
+1 -1
View File
@@ -4,7 +4,7 @@
/datum/surgery_step/lobectomy, /datum/surgery_step/close)
possible_locs = list(BODY_ZONE_CHEST)
/datum/surgery/lobectomy/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/lobectomy/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
var/obj/item/organ/lungs/L = target.getorganslot(ORGAN_SLOT_LUNGS)
if(L)
if(L.damage > 60 && !L.operated)
@@ -5,7 +5,7 @@
possible_locs = list(BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_HEAD)
requires_bodypart = FALSE //need a missing limb
requires_bodypart_type = 0
/datum/surgery/prosthetic_replacement/can_start(mob/user, mob/living/carbon/target)
/datum/surgery/prosthetic_replacement/can_start(mob/user, mob/living/carbon/target, obj/item/tool)
if(!iscarbon(target))
return 0
var/mob/living/carbon/C = target
+14 -15
View File
@@ -37,7 +37,7 @@
return ..()
/datum/surgery/proc/can_start(mob/user, mob/living/patient) //FALSE to not show in list
/datum/surgery/proc/can_start(mob/user, mob/living/patient, obj/item/tool) //FALSE to not show in list
. = TRUE
if(replaced_by == /datum/surgery)
return FALSE
@@ -55,27 +55,26 @@
if(requires_tech)
. = FALSE
var/list/advanced_surgeries = list()
if(iscyborg(user))
var/mob/living/silicon/robot/R = user
var/obj/item/surgical_processor/SP = locate() in R.module.modules
if(SP)
if (replaced_by in SP.advanced_surgeries)
return .
if(type in SP.advanced_surgeries)
return TRUE
advanced_surgeries |= SP.advanced_surgeries
var/turf/T = get_turf(patient)
var/obj/structure/table/optable/table = locate(/obj/structure/table/optable, T)
if(table)
if(!table.computer)
return .
if(table.computer.stat & (NOPOWER|BROKEN))
return .
if(replaced_by in table.computer.advanced_surgeries)
return FALSE
if(type in table.computer.advanced_surgeries)
return TRUE
if(table?.computer && !CHECK_BITFIELD(table.computer.stat, NOPOWER|BROKEN))
advanced_surgeries |= table.computer.advanced_surgeries
if(istype(tool, /obj/item/surgical_drapes/advanced))
var/obj/item/surgical_drapes/advanced/A = tool
advanced_surgeries |= A.get_advanced_surgeries()
if(replaced_by in advanced_surgeries)
return FALSE
if(type in advanced_surgeries)
return TRUE
/datum/surgery/proc/next_step(mob/user, intent)
if(step_in_progress)
+20
View File
@@ -287,6 +287,26 @@
if(!attempt_initiate_surgery(src, M, user))
..()
/obj/item/surgical_drapes/advanced
name = "smart surgical drapes"
desc = "A quite quirky set of drapes with wireless synchronization to the station's research networks, with an integrated display allowing users to execute advanced surgeries without the aid of an operating computer."
var/datum/techweb/linked_techweb
/obj/item/surgical_drapes/advanced/Initialize(mapload)
. = ..()
linked_techweb = SSresearch.science_tech
/obj/item/surgical_drapes/advanced/proc/get_advanced_surgeries()
. = list()
if(!linked_techweb)
return
for(var/subtype in subtypesof(/datum/design/surgery))
var/datum/design/surgery/prototype = subtype
var/id = initial(prototype.id)
if(id in linked_techweb.researched_designs)
prototype = SSresearch.techweb_design_by_id(id)
. |= prototype.surgery
/obj/item/organ_storage //allows medical cyborgs to manipulate organs without hands
name = "organ storage bag"
desc = "A container for holding body parts."
+8 -7
View File
@@ -266,16 +266,17 @@
/obj/item/cartridge/janitor = 3,
/obj/item/clothing/gloves/color/black = 2,
/obj/item/clothing/head/soft/purple = 2,
/obj/item/twohanded/broom = 2,
/obj/item/paint/paint_remover = 2,
/obj/item/melee/flyswatter = 1,
/obj/item/melee/flyswatter = 2,
/obj/item/flashlight = 2,
/obj/item/caution = 8,
/obj/item/holosign_creator = 1,
/obj/item/lightreplacer = 1,
/obj/item/soap = 1,
/obj/item/storage/bag/trash = 1,
/obj/item/clothing/shoes/galoshes = 1,
/obj/item/watertank/janitor = 1,
/obj/item/holosign_creator = 2,
/obj/item/lightreplacer = 2,
/obj/item/soap = 2,
/obj/item/storage/bag/trash = 2,
/obj/item/clothing/shoes/galoshes = 2,
/obj/item/watertank/janitor = 2,
/obj/item/storage/belt/janitor = 2,
/obj/item/screwdriver = 2,
/obj/item/stack/cable_coil/random = 4)
+3
View File
@@ -384,6 +384,9 @@
if(!istype(tasted))
return
if(!tasted.client?.prefs_vr.lickable)
return
if(src == stat)
return
+1
View File
@@ -40,6 +40,7 @@ GLOBAL_LIST_EMPTY(vore_preferences_datums)
var/digestable = FALSE
var/devourable = FALSE
var/feeding = FALSE
var/lickable = FALSE
// var/allowmobvore = TRUE
var/list/belly_prefs = list()
var/vore_taste = "nothing in particular"
+19 -17
View File
@@ -242,22 +242,13 @@
dat += "<a href='?src=\ref[src];applyprefs=1'>Reload Slot Prefs</a>"
dat += "<HR>"
switch(user.digestable)
if(TRUE)
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledg=1'>Toggle Digestable (Currently: ON)</a>"
if(FALSE)
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledg=1'>Toggle Digestable (Currently: OFF)</a>"
switch(user.devourable)
if(TRUE)
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledvor=1'>Toggle Devourable (Currently: ON)</a>"
if(FALSE)
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledvor=1'>Toggle Devourable (Currently: OFF)</a>"
switch(user.feeding)
if(TRUE)
dat += "<br><a style='background:#173d15;' href='?src=\ref[src];toggledfeed=1'>Toggle Feeding (Currently: ON)</a>"
if(FALSE)
dat += "<br><a style='background:#990000;' href='?src=\ref[src];toggledfeed=1'>Toggle Feeding (Currently: OFF)</a>"
var/pref_on = "#173d15"
var/pref_off = "#990000"
dat += "<br><a style='background:[user.digestable ? pref_on : pref_off];' href='?src=\ref[src];toggledg=1'>Toggle Digestable (Currently: [user.digestable ? "ON" : "OFF"])</a>"
dat += "<br><a style='background:[user.devourable ? pref_on : pref_off];' href='?src=\ref[src];toggledvor=1'>Toggle Devourable (Currently: [user.devourable ? "ON" : "OFF"])</a>"
dat += "<br><a style='background:[user.feeding ? pref_on : pref_off];' href='?src=\ref[src];toggledfeed=1'>Toggle Feeding (Currently: [user.feeding ? "ON" : "OFF"])</a>"
if(user.client.prefs_vr)
dat += "<br><a style='background:[user.client.prefs_vr.lickable ? pref_on : pref_off];' href='?src=\ref[src];toggledlickable=1'>Toggle Licking (Currently: [user.client.prefs_vr.lickable ? "ON" : "OFF"])</a>"
//Returns the dat html to the vore_look
return dat
@@ -734,5 +725,16 @@
if(user.client.prefs_vr)
user.client.prefs_vr.feeding = user.feeding
if(href_list["toggledlickable"])
if(user.client.prefs_vr)
var/choice = alert(user, "This button is to toggle your ability to be licked. Being licked is currently: [user.client.prefs_vr.lickable ? "Allowed" : "Prevented"]", "", "Allow Licking", "Cancel", "Prevent Licking")
switch(choice)
if("Cancel")
return FALSE
if("Allow Licking")
user.client.prefs_vr.lickable = TRUE
if("Prevent Licking")
user.client.prefs_vr.lickable = FALSE
//Refresh when interacted with, returning 1 makes vore_look.Topic update
return TRUE
return TRUE