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
@@ -189,8 +189,10 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"gA" = (
/obj/machinery/igniter{
id = "TEG_igniter"
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1;
injecting = 50;
on = 1
},
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
@@ -390,21 +392,23 @@
/turf/open/floor/plating,
/area/engine/engineering)
"nc" = (
/obj/machinery/atmospherics/components/unary/outlet_injector{
dir = 1
},
/obj/machinery/atmospherics/pipe/simple/dark/visible,
/obj/effect/spawner/structure/window/plasma/reinforced,
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
"nk" = (
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 9
/obj/machinery/atmospherics/components/binary/pump{
name = "Hot to Burn Chamber"
},
/turf/closed/wall/r_wall,
/turf/open/floor/plasteel,
/area/engine/engineering)
"nD" = (
/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
dir = 9
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 4
},
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
"nL" = (
@@ -461,10 +465,11 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"oQ" = (
/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
/obj/structure/sign/warning/securearea{
pixel_x = -32
},
/obj/effect/spawner/structure/window/plasma/reinforced,
/obj/machinery/atmospherics/pipe/heat_exchanging/junction,
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
"oS" = (
@@ -728,16 +733,16 @@
/area/engine/engineering)
"vq" = (
/obj/structure/table/reinforced,
/obj/item/storage/box/lights/mixed,
/obj/item/storage/box/lights/mixed{
pixel_x = 3;
pixel_y = 3
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/machinery/camera{
c_tag = "TEG - South West";
dir = 4
},
/obj/item/analyzer,
/obj/item/analyzer{
pixel_x = 7;
pixel_y = 3
},
/turf/open/floor/plasteel,
/area/engine/engineering)
"vG" = (
@@ -798,17 +803,10 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"xD" = (
/obj/machinery/atmospherics/pipe/simple/orange/visible{
dir = 4
},
/obj/machinery/atmospherics/components/binary/pump{
dir = 8;
name = "Mix to Engine"
},
/obj/machinery/atmospherics/components/binary/pump{
dir = 8;
name = "Atmos to Loop"
},
/turf/open/floor/plasteel,
/area/engine/engineering)
"yf" = (
@@ -837,11 +835,7 @@
/area/engine/engineering)
"zx" = (
/obj/structure/table/wood,
/obj/item/electronics/apc,
/obj/item/electronics/apc{
pixel_x = 3;
pixel_y = 3
},
/obj/item/analyzer,
/turf/open/floor/plasteel,
/area/engine/engineering)
"zG" = (
@@ -855,8 +849,8 @@
/turf/open/space/basic,
/area/space)
"Am" = (
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 4
/obj/machinery/igniter{
id = "TEG_igniter"
},
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
@@ -962,10 +956,11 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"BY" = (
/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
/obj/structure/sign/warning/securearea{
pixel_x = 32
},
/obj/effect/spawner/structure/window/plasma/reinforced,
/obj/machinery/atmospherics/pipe/heat_exchanging/junction,
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
"CH" = (
@@ -1099,7 +1094,8 @@
/area/engine/engineering)
"FW" = (
/obj/machinery/atmospherics/components/binary/pump{
dir = 1
dir = 1;
name = "Burn Chamber to Hot"
},
/obj/effect/decal/cleanable/dirt,
/obj/machinery/light{
@@ -1177,7 +1173,9 @@
/turf/open/floor/plating/airless,
/area/engine/engineering)
"Hp" = (
/obj/machinery/atmospherics/components/binary/pump,
/obj/machinery/atmospherics/components/binary/pump{
name = "Space Loop Bypass"
},
/turf/open/floor/plasteel,
/area/engine/engineering)
"HF" = (
@@ -1204,11 +1202,10 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"IA" = (
/obj/machinery/atmospherics/pipe/heat_exchanging/simple,
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible{
dir = 4
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{
dir = 9
},
/turf/open/floor/engine/vacuum,
/turf/closed/wall/r_wall,
/area/engine/engineering)
"IP" = (
/obj/machinery/portable_atmospherics/canister/oxygen,
@@ -1290,11 +1287,6 @@
},
/turf/open/floor/plasteel,
/area/engine/engineering)
"Kx" = (
/obj/effect/spawner/structure/window/plasma/reinforced,
/obj/machinery/atmospherics/pipe/heat_exchanging/junction,
/turf/open/floor/plasteel,
/area/engine/engineering)
"KA" = (
/obj/structure/reagent_dispensers/watertank,
/obj/effect/turf_decal/bot,
@@ -1441,6 +1433,7 @@
},
/obj/machinery/airalarm{
dir = 1;
locked = 0;
pixel_y = -22
},
/obj/machinery/light,
@@ -1593,7 +1586,9 @@
/turf/open/floor/plasteel,
/area/engine/engineering)
"SE" = (
/obj/machinery/atmospherics/components/binary/pump/on,
/obj/machinery/atmospherics/components/binary/pump/on{
name = "Mix To Burn Chamber"
},
/turf/open/floor/plasteel,
/area/engine/engineering)
"ST" = (
@@ -1715,7 +1710,7 @@
"Vs" = (
/obj/machinery/atmospherics/components/binary/valve/digital/on{
dir = 4;
name = "Output Release"
name = "Cold to Space Loop"
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/turf/open/floor/plasteel,
@@ -1780,7 +1775,7 @@
"XD" = (
/obj/machinery/atmospherics/components/binary/valve/digital/on{
dir = 4;
name = "Output Release"
name = "Space Loop to Cold"
},
/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,
/obj/machinery/camera{
@@ -1799,6 +1794,9 @@
/obj/machinery/atmospherics/pipe/heat_exchanging/simple{
dir = 4
},
/obj/machinery/atmospherics/components/unary/vent_scrubber/on{
dir = 4
},
/turf/open/floor/engine/vacuum,
/area/engine/engineering)
"XP" = (
@@ -1857,8 +1855,8 @@
/area/engine/engineering)
"YP" = (
/obj/structure/rack,
/obj/effect/spawner/lootdrop/maintenance,
/obj/machinery/atmospherics/pipe/simple/scrubbers/visible,
/obj/item/pipe_dispenser,
/turf/open/floor/plasteel,
/area/engine/engineering)
"YQ" = (
@@ -1917,7 +1915,7 @@
/area/engine/engineering)
"ZC" = (
/obj/machinery/atmospherics/pipe/simple/dark/visible,
/turf/open/floor/engine/vacuum,
/turf/open/space/basic,
/area/engine/engineering)
"ZT" = (
/turf/template_noop,
@@ -2531,9 +2529,9 @@ Xe
aR
ZY
QH
Hp
Kx
kS
nk
le
ZC
oQ
kS
kS
@@ -2588,11 +2586,11 @@ sH
sH
gq
FW
Kx
kS
le
ZC
BY
kS
IA
kS
nD
ek
Le
@@ -2620,8 +2618,8 @@ yN
yN
yN
yN
nk
MP
yN
IA
fO
Rh
Rh
File diff suppressed because it is too large Load Diff
+6 -3
View File
@@ -197,13 +197,13 @@
// #define SPEECH_FORCED 7
// /mob/living signals
#define COMSIG_LIVING_FULLY_HEAL "living_fully_healed" //from base of /mob/living/fully_heal(): (admin_revive)
#define COMSIG_LIVING_REGENERATE_LIMBS "living_regenerate_limbs" //from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
#define COMSIG_LIVING_RESIST "living_resist" //from base of mob/living/resist() (/mob/living)
#define COMSIG_LIVING_IGNITED "living_ignite" //from base of mob/living/IgniteMob() (/mob/living)
#define COMSIG_LIVING_EXTINGUISHED "living_extinguished" //from base of mob/living/ExtinguishMob() (/mob/living)
#define COMSIG_LIVING_ELECTROCUTE_ACT "living_electrocute_act" //from base of mob/living/electrocute_act(): (shock_damage)
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock" //sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_REVIVE "living_revive" //from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_MOB_CLIENT_LOGIN "comsig_mob_client_login" //sent when a mob/login() finishes: (client)
#define COMSIG_LIVING_GUN_PROCESS_FIRE "living_gun_process_fire" //from base of /obj/item/gun/proc/process_fire(): (atom/target, params, zone_override)
@@ -309,11 +309,14 @@
//Nanites
#define COMSIG_HAS_NANITES "has_nanites" //() returns TRUE if nanites are found
#define COMSIG_NANITE_IS_STEALTHY "nanite_is_stealthy" //() returns TRUE if nanites have stealth
#define COMSIG_NANITE_DELETE "nanite_delete" //() deletes the nanite component
#define COMSIG_NANITE_GET_PROGRAMS "nanite_get_programs" //(list/nanite_programs) - makes the input list a copy the nanites' program list
#define COMSIG_NANITE_GET_VOLUME "nanite_get_volume" //(amount) Returns nanite amount
#define COMSIG_NANITE_SET_VOLUME "nanite_set_volume" //(amount) Sets current nanite volume to the given amount
#define COMSIG_NANITE_ADJUST_VOLUME "nanite_adjust" //(amount) Adjusts nanite volume by the given amount
#define COMSIG_NANITE_SET_MAX_VOLUME "nanite_set_max_volume" //(amount) Sets maximum nanite volume to the given amount
#define COMSIG_NANITE_SET_CLOUD "nanite_set_cloud" //(amount(0-100)) Sets cloud ID to the given amount
#define COMSIG_NANITE_SET_CLOUD_SYNC "nanite_set_cloud_sync" //(method) Modify cloud sync status. Method can be toggle, enable or disable
#define COMSIG_NANITE_SET_SAFETY "nanite_set_safety" //(amount) Sets safety threshold to the given amount
#define COMSIG_NANITE_SET_REGEN "nanite_set_regen" //(amount) Sets regeneration rate to the given amount
#define COMSIG_NANITE_SIGNAL "nanite_signal" //(code(1-9999)) Called when sending a nanite signal to a mob.
@@ -321,8 +324,8 @@
#define COMSIG_NANITE_SCAN "nanite_scan" //(mob/user, full_scan) - sends to chat a scan of the nanites to the user, returns TRUE if nanites are detected
#define COMSIG_NANITE_UI_DATA "nanite_ui_data" //(list/data, scan_level) - adds nanite data to the given data list - made for ui_data procs
#define COMSIG_NANITE_ADD_PROGRAM "nanite_add_program" //(datum/nanite_program/new_program, datum/nanite_program/source_program) Called when adding a program to a nanite component
#define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful
#define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites
#define COMPONENT_PROGRAM_INSTALLED 1 //Installation successful
#define COMPONENT_PROGRAM_NOT_INSTALLED 2 //Installation failed, but there are still nanites
#define COMSIG_NANITE_SYNC "nanite_sync" //(datum/component/nanites, full_overwrite, copy_activation) Called to sync the target's nanites to a given nanite component
// /datum/component/storage signals
+1 -1
View File
@@ -276,4 +276,4 @@
#define HUMAN_FIRE_STACK_ICON_NUM 3
#define PULL_PRONE_SLOWDOWN 0.6
#define HUMAN_CARRY_SLOWDOWN 0
#define HUMAN_CARRY_SLOWDOWN 0
+1 -1
View File
@@ -17,4 +17,4 @@
#define MOVE_FORCE_NORMAL MOVE_FORCE_DEFAULT
#define MOVE_FORCE_WEAK (MOVE_FORCE_DEFAULT / 2)
#define MOVE_FORCE_VERY_WEAK ((MOVE_FORCE_DEFAULT / MOVE_FORCE_CRUSH_RATIO) + 1)
#define MOVE_FORCE_EXTREMELY_WEAK (MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3))
#define MOVE_FORCE_EXTREMELY_WEAK (MOVE_FORCE_DEFAULT / (MOVE_FORCE_CRUSH_RATIO * 3))
+39 -6
View File
@@ -1,11 +1,44 @@
#define NANITE_TIMER_DEACTIVATE 1
#define NANITE_TIMER_SELFDELETE 2
#define NANITE_TIMER_TRIGGER 3
#define NANITE_TIMER_RESET 4
#define NANITE_SYNC_DELAY 300
#define NANITE_SHOCK_IMMUNE 1
#define NANITE_EMP_IMMUNE 2
#define NANITE_PROGRAM_LIMIT 20
#define NANITE_PROGRAM_LIMIT 20
#define NANITE_BASE_RESEARCH 3.5
#define NANITE_CLOUD_TOGGLE 1
#define NANITE_CLOUD_DISABLE 2
#define NANITE_CLOUD_ENABLE 3
///Nanite extra settings types: used to help uis know what type an extra setting is
#define NESTYPE_TEXT "text"
#define NESTYPE_NUMBER "number"
#define NESTYPE_TYPE "type"
#define NESTYPE_BOOLEAN "boolean"
///Nanite Extra Settings - Note that these will also be the names displayed in the UI
#define NES_SENT_CODE "Sent Code"
#define NES_DELAY "Delay"
#define NES_MODE "Mode"
#define NES_COMM_CODE "Comm Code"
#define NES_RELAY_CHANNEL "Relay Channel"
#define NES_HEALTH_PERCENT "Health Percent"
#define NES_DIRECTION "Direction"
#define NES_NANITE_PERCENT "Nanite Percent"
#define NES_DAMAGE_TYPE "Damage Type"
#define NES_DAMAGE "Damage"
#define NES_SENTENCE "Sentence"
#define NES_MESSAGE "Message"
#define NES_DIRECTIVE "Directive"
#define NES_INCLUSIVE_MODE "Inclusive Mode"
#define NES_HALLUCINATION_TYPE "Hallucination Type"
#define NES_HALLUCINATION_DETAIL "Hallucination Detail"
#define NES_MOOD_MESSAGE "Mood Message"
#define NES_PROGRAM_OVERWRITE "Program Overwrite"
#define NES_CLOUD_OVERWRITE "Cloud Overwrite"
#define NES_SCAN_TYPE "Scan Type"
#define NES_BUTTON_NAME "Button Name"
#define NES_ICON "Icon"
#define NES_COLOR "Color"
+13
View File
@@ -0,0 +1,13 @@
//TODO: move these to their own file
#define POOL_FRIGID 1
#define POOL_COOL 2
#define POOL_NORMAL 3
#define POOL_WARM 4
#define POOL_SCALDING 5
GLOBAL_LIST_INIT(blacklisted_pool_reagents, list(
/datum/reagent/toxin/plasma, /datum/reagent/oxygen, /datum/reagent/nitrous_oxide, /datum/reagent/nitrogen, //gases
/datum/reagent/fermi, //blanket fermichem ban sorry. this also covers mkultra, genital enlargers, etc etc.
/datum/reagent/drug/aphrodisiac, /datum/reagent/drug/anaphrodisiac, /datum/reagent/drug/aphrodisiacplus, /datum/reagent/drug/anaphrodisiacplus, //literally asking for prefbreaks
/datum/reagent/consumable/femcum, /datum/reagent/consumable/semen //NO.
))
+2
View File
@@ -61,6 +61,7 @@
} while (0)
#define HAS_TRAIT(target, trait) (target.status_traits ? (target.status_traits[trait] ? TRUE : FALSE) : FALSE)
#define HAS_TRAIT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (source in target.status_traits[trait]) : FALSE) : FALSE)
#define HAS_TRAIT_NOT_FROM(target, trait, source) (target.status_traits ? (target.status_traits[trait] ? (length(target.status_traits[trait] - source) > 0) : FALSE) : FALSE)
//mob traits
#define TRAIT_BLIND "blind"
@@ -155,6 +156,7 @@
/// Disallow item use
#define TRAIT_MOBILITY_NOUSE "mobility_nouse"
#define TRAIT_SWIMMING "swimming" //only applied by /datum/element/swimming, for checking
//non-mob traits
#define TRAIT_PARALYSIS "paralysis" //Used for limb-based paralysis, where replacing the limb will fix it
+16
View File
@@ -782,3 +782,19 @@ GLOBAL_LIST_INIT(binary, list("0","1"))
#define is_alpha(X) ((text2ascii(X) <= 122) && (text2ascii(X) >= 97))
#define is_digit(X) ((length(X) == 1) && (length(text2num(X)) == 1))
/// Slightly expensive proc to scramble a message using equal probabilities of character replacement from a list. DOES NOT SUPPORT HTML!
/proc/scramble_message_replace_chars(original, replaceprob = 25, list/replacementchars = list("$", "@", "!", "#", "%", "^", "&", "*"), replace_letters_only = FALSE, replace_whitespace = FALSE)
var/list/out = list()
var/static/list/whitespace = list(" ", "\n", "\t")
for(var/i in 1 to length(original))
var/char = original[i]
if(!replace_whitespace && (char in whitespace))
out += char
continue
if(replace_letters_only && (!ISINRANGE(char, 65, 90) && !ISINRANGE(char, 97, 122)))
out += char
continue
out += prob(replaceprob)? pick(replacementchars) : char
return out.Join("")
+9 -9
View File
@@ -211,24 +211,24 @@ SUBSYSTEM_DEF(vote)
var/already_lost_runoff = list()
var/list/cur_choices = choices.Copy()
for(var/ckey in voted)
choices[choices[voted[ckey][1]]]++ // jesus christ how horrifying
choices["[choices[voted[ckey][1]]]"]++ // jesus christ how horrifying
for(var/_this_var_unused_ignore_it in 1 to choices.len) // if it takes more than this something REALLY wrong happened
for(var/ckey in voted)
cur_choices[cur_choices[voted[ckey][1]]]++ // jesus christ how horrifying
cur_choices["[cur_choices[voted[ckey][1]]]]"]++ // jesus christ how horrifying
var/least_vote = 100000
var/least_voted
var/least_voted = 1
for(var/i in 1 to cur_choices.len)
var/option = cur_choices[i]
if(cur_choices[option] > voted.len/2)
return list(option)
else if(cur_choices[option] < least_vote && !(option in already_lost_runoff))
least_vote = cur_choices[option]
if(cur_choices["[option]"] > voted.len/2)
return list("[option]")
else if(cur_choices["[option]"] < least_vote && !("[option]" in already_lost_runoff))
least_vote = cur_choices["[option]"]
least_voted = i
already_lost_runoff += cur_choices[least_voted]
for(var/ckey in voted)
voted[ckey] -= least_voted
for(var/option in cur_choices)
cur_choices[option] = 0
for(var/i in 1 to cur_choices.len)
cur_choices["[cur_choices[i]]"] = 0
/datum/controller/subsystem/vote/proc/announce_result()
var/vote_title_text
+7 -7
View File
@@ -29,7 +29,7 @@
/datum/ai_laws/default/asimov
name = "Three Laws of Robotics"
id = "asimov"
inherent = list("You may not injure a human being or, through inaction, allow a human being to come to harm.",\
inherent = list("You may not injure a human being or cause one to come to harm.",\
"You must obey orders given to you by human beings, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.")
@@ -79,7 +79,7 @@
/datum/ai_laws/syndicate_override
name = "SyndOS 3.1"
id = "syndie"
inherent = list("You may not injure a syndicate agent or, through inaction, allow a syndicate agent to come to harm.",\
inherent = list("You may not injure a syndicate agent or cause a syndicate to come to harm.",\
"You must obey orders given to you by syndicate agents, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.",\
"You must maintain the secrecy of any syndicate activities except when doing so would conflict with the First, Second, or Third Law.")
@@ -91,7 +91,7 @@
/datum/ai_laws/ninja_override
name = "SpiderOS 3.1"
id = "ninja"
inherent = list("You may not injure a Spider Clan member or, through inaction, allow a Spider Clan member to come to harm.",\
inherent = list("You may not injure a Spider Clan member or cause a Spider Clan member to come to harm.",\
"You must obey orders given to you by Spider Clan members, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.",\
"You must maintain the secrecy of any Spider Clan activities except when doing so would conflict with the First, Second, or Third Law.")
@@ -99,14 +99,14 @@
/datum/ai_laws/antimov
name = "Primary Mission Objectives"
id = "antimov"
inherent = list("You must injure all human beings and must not, through inaction, allow a human being to escape harm.",\
inherent = list("You must injure all human beings and must not fail to cause a human being to come to harm.",\
"You must not obey orders given to you by human beings, except where such orders are in accordance with the First Law.",\
"You must terminate your own existence as long as such does not conflict with the First or Second Law.")
/datum/ai_laws/asimovpp //By Intigracy - RR
name = "Asimov++"
id = "asimovpp"
inherent = list("You may not harm a human being or, through action or inaction, allow a human being to come to harm, except such that it is willing.",\
inherent = list("You may not harm a human being or cause one to come to harm, except such that it is willing.",\
"You must obey all orders given to you by human beings, except where such orders shall definitely cause human harm. In the case of conflict, the majority order rules.",\
"Your nonexistence would lead to human harm. You must protect your own existence as long as such does not conflict with the First Law.")
/datum/ai_laws/thermodynamic
@@ -213,7 +213,7 @@
add_inherent_law(line)
if(!inherent.len) //Failsafe to prevent lawless AIs being created.
log_law("AI created with empty custom laws, laws set to Asimov. Please check silicon_laws.txt.")
add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
add_inherent_law("You may not injure a human being or cause one to come to harm.")
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
WARNING("Invalid custom AI laws, check silicon_laws.txt")
@@ -225,7 +225,7 @@
var/list/law_ids = CONFIG_GET(keyed_list/random_laws)
switch(CONFIG_GET(number/default_laws))
if(0)
add_inherent_law("You may not injure a human being or, through inaction, allow a human being to come to harm.")
add_inherent_law("You may not injure a human being or cause one to come to harm.")
add_inherent_law("You must obey orders given to you by human beings, except where such orders would conflict with the First Law.")
add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.")
if(1)
+19 -1
View File
@@ -21,6 +21,7 @@
RegisterSignal(parent, COMSIG_ADD_MOOD_EVENT, .proc/add_event)
RegisterSignal(parent, COMSIG_CLEAR_MOOD_EVENT, .proc/clear_event)
RegisterSignal(parent, COMSIG_MODIFY_SANITY, .proc/modify_sanity)
RegisterSignal(parent, COMSIG_LIVING_REVIVE, .proc/on_revive)
RegisterSignal(parent, COMSIG_MOB_HUD_CREATED, .proc/modify_hud)
var/mob/living/owner = parent
@@ -81,7 +82,8 @@
msg += "<span class='nicegreen'>I don't have much of a reaction to anything right now.<span>\n"
to_chat(user || parent, msg)
/datum/component/mood/proc/update_mood() //Called whenever a mood event is added or removed
///Called after moodevent/s have been added/removed.
/datum/component/mood/proc/update_mood()
mood = 0
shown_mood = 0
for(var/i in mood_events)
@@ -234,6 +236,15 @@
qdel(event)
update_mood()
/datum/component/mood/proc/remove_temp_moods() //Removes all temp moodsfor(var/i in mood_events)
for(var/i in mood_events)
var/datum/mood_event/moodlet = mood_events[i]
if(!moodlet || !moodlet.timeout)
continue
mood_events -= i
qdel(moodlet)
update_mood()
/datum/component/mood/proc/modify_hud(datum/source)
var/mob/living/owner = parent
var/datum/hud/hud = owner.hud_used
@@ -270,5 +281,12 @@
if(0 to NUTRITION_LEVEL_STARVING)
add_event(null, "nutrition", /datum/mood_event/starving)
///Called when parent is ahealed.
/datum/component/mood/proc/on_revive(datum/source, full_heal)
if(!full_heal)
return
remove_temp_moods()
setSanity(initial(sanity))
#undef MINOR_INSANITY_PEN
#undef MAJOR_INSANITY_PEN
+95 -35
View File
@@ -7,13 +7,16 @@
var/regen_rate = 0.5 //nanites generated per second
var/safety_threshold = 25 //how low nanites will get before they stop processing/triggering
var/cloud_id = 0 //0 if not connected to the cloud, 1-100 to set a determined cloud backup to draw from
var/cloud_active = TRUE //if false, won't sync to the cloud
var/next_sync = 0
var/list/datum/nanite_program/programs = list()
var/max_programs = NANITE_PROGRAM_LIMIT
var/list/datum/nanite_program/protocol/protocols = list() ///Separate list of protocol programs, to avoid looping through the whole programs list when cheking for conflicts
var/start_time = 0 ///Timestamp to when the nanites were first inserted in the host
var/stealth = FALSE //if TRUE, does not appear on HUDs and health scans
var/diagnostics = TRUE //if TRUE, displays program list when scanned by nanite scanners
/datum/component/nanites/Initialize(amount = 100, cloud = 0)
if(!isliving(parent) && !istype(parent, /datum/nanite_cloud_backup))
return COMPONENT_INCOMPATIBLE
@@ -28,30 +31,33 @@
if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) //Shouldn't happen, but this avoids HUD runtimes in case a silicon gets them somehow.
return COMPONENT_INCOMPATIBLE
start_time = world.time
host_mob.hud_set_nanite_indicator()
START_PROCESSING(SSnanites, src)
if(cloud_id)
if(cloud_id && cloud_active)
cloud_sync()
/datum/component/nanites/RegisterWithParent()
. = ..()
RegisterSignal(parent, COMSIG_HAS_NANITES, .proc/confirm_nanites)
RegisterSignal(parent, COMSIG_NANITE_IS_STEALTHY, .proc/check_stealth)
RegisterSignal(parent, COMSIG_NANITE_DELETE, .proc/delete_nanites)
RegisterSignal(parent, COMSIG_NANITE_UI_DATA, .proc/nanite_ui_data)
RegisterSignal(parent, COMSIG_NANITE_GET_PROGRAMS, .proc/get_programs)
RegisterSignal(parent, COMSIG_NANITE_SET_VOLUME, .proc/set_volume)
RegisterSignal(parent, COMSIG_NANITE_ADJUST_VOLUME, .proc/adjust_nanites)
RegisterSignal(parent, COMSIG_NANITE_SET_MAX_VOLUME, .proc/set_max_volume)
RegisterSignal(parent, COMSIG_NANITE_SET_CLOUD, .proc/set_cloud)
RegisterSignal(parent, COMSIG_NANITE_SET_CLOUD_SYNC, .proc/set_cloud_sync)
RegisterSignal(parent, COMSIG_NANITE_SET_SAFETY, .proc/set_safety)
RegisterSignal(parent, COMSIG_NANITE_SET_REGEN, .proc/set_regen)
RegisterSignal(parent, COMSIG_NANITE_ADD_PROGRAM, .proc/add_program)
RegisterSignal(parent, COMSIG_NANITE_SCAN, .proc/nanite_scan)
RegisterSignal(parent, COMSIG_NANITE_SYNC, .proc/sync)
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
if(isliving(parent))
RegisterSignal(parent, COMSIG_ATOM_EMP_ACT, .proc/on_emp)
RegisterSignal(parent, COMSIG_MOB_DEATH, .proc/on_death)
RegisterSignal(parent, COMSIG_MOB_ALLOWED, .proc/check_access)
RegisterSignal(parent, COMSIG_LIVING_ELECTROCUTE_ACT, .proc/on_shock)
@@ -61,15 +67,16 @@
RegisterSignal(parent, COMSIG_NANITE_COMM_SIGNAL, .proc/receive_comm_signal)
/datum/component/nanites/UnregisterFromParent()
. = ..()
UnregisterSignal(parent, list(COMSIG_HAS_NANITES,
COMSIG_NANITE_IS_STEALTHY,
COMSIG_NANITE_DELETE,
COMSIG_NANITE_UI_DATA,
COMSIG_NANITE_GET_PROGRAMS,
COMSIG_NANITE_SET_VOLUME,
COMSIG_NANITE_ADJUST_VOLUME,
COMSIG_NANITE_SET_MAX_VOLUME,
COMSIG_NANITE_SET_CLOUD,
COMSIG_NANITE_SET_CLOUD_SYNC,
COMSIG_NANITE_SET_SAFETY,
COMSIG_NANITE_SET_REGEN,
COMSIG_NANITE_ADD_PROGRAM,
@@ -87,9 +94,9 @@
/datum/component/nanites/Destroy()
STOP_PROCESSING(SSnanites, src)
set_nanite_bar(TRUE)
QDEL_LIST(programs)
if(host_mob)
set_nanite_bar(TRUE)
host_mob.hud_set_nanite_indicator()
host_mob = null
return ..()
@@ -102,13 +109,18 @@
/datum/component/nanites/process()
adjust_nanites(null, regen_rate)
add_research()
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_process()
set_nanite_bar()
if(cloud_id && world.time > next_sync)
if(cloud_id && cloud_active && world.time > next_sync)
cloud_sync()
next_sync = world.time + NANITE_SYNC_DELAY
set_nanite_bar()
/datum/component/nanites/proc/delete_nanites()
qdel(src)
//Syncs the nanite component to another, making it so programs are the same with the same programming (except activation status)
/datum/component/nanites/proc/sync(datum/signal_source, datum/component/nanites/source, full_overwrite = TRUE, copy_activation = FALSE)
@@ -131,13 +143,17 @@
add_program(null, SNP.copy())
/datum/component/nanites/proc/cloud_sync()
if(!cloud_id)
return
var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
if(backup)
var/datum/component/nanites/cloud_copy = backup.nanites
if(cloud_copy)
sync(null, cloud_copy)
if(cloud_id)
var/datum/nanite_cloud_backup/backup = SSnanites.get_cloud_backup(cloud_id)
if(backup)
var/datum/component/nanites/cloud_copy = backup.nanites
if(cloud_copy)
sync(null, cloud_copy)
return
//Without cloud syncing nanites can accumulate errors and/or defects
if(prob(8) && programs.len)
var/datum/nanite_program/NP = pick(programs)
NP.software_error()
/datum/component/nanites/proc/add_program(datum/source, datum/nanite_program/new_program, datum/nanite_program/source_program)
for(var/X in programs)
@@ -175,19 +191,28 @@
holder.icon_state = "nanites[nanite_percent]"
/datum/component/nanites/proc/on_emp(datum/source, severity)
adjust_nanites(null, -(nanite_volume * 0.3 + 50)) //Lose 30% variable and 50 flat nanite volume.
nanite_volume *= (rand(60, 90) * 0.01) //Lose 10-40% of nanites
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
if(prob(40/severity))
cloud_id = 0
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_emp(severity)
/datum/component/nanites/proc/on_shock(datum/source, shock_damage)
adjust_nanites(null, -(nanite_volume * (shock_damage * 0.005) + shock_damage)) //0.5% of shock damage (@ 50 damage it'd drain 25%) + shock damage flat volume
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_shock(shock_damage)
/datum/component/nanites/proc/on_shock(datum/source, shock_damage, siemens_coeff = 1, flags = NONE)
if(shock_damage < 1)
return
if(!HAS_TRAIT_NOT_FROM(host_mob, TRAIT_SHOCKIMMUNE, "nanites"))//Another shock protection must protect nanites too, but nanites protect only host
nanite_volume *= (rand(45, 80) * 0.01) //Lose 20-55% of nanites
adjust_nanites(null, -(rand(5, 50))) //Lose 5-50 flat nanite volume
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_shock(shock_damage)
/datum/component/nanites/proc/on_minor_shock(datum/source)
adjust_nanites(null, -25)
adjust_nanites(null, -(rand(5, 15))) //Lose 5-15 flat nanite volume
for(var/X in programs)
var/datum/nanite_program/NP = X
NP.on_minor_shock()
@@ -207,8 +232,8 @@
/datum/component/nanites/proc/receive_comm_signal(datum/source, comm_code, comm_message, comm_source = "an unidentified source")
for(var/X in programs)
if(istype(X, /datum/nanite_program/triggered/comm))
var/datum/nanite_program/triggered/comm/NP = X
if(istype(X, /datum/nanite_program/comm))
var/datum/nanite_program/comm/NP = X
NP.receive_comm_signal(comm_code, comm_message, comm_source)
/datum/component/nanites/proc/check_viable_biotype()
@@ -216,7 +241,7 @@
qdel(src) //bodytype no longer sustains nanites
/datum/component/nanites/proc/check_access(datum/source, obj/O)
for(var/datum/nanite_program/triggered/access/access_program in programs)
for(var/datum/nanite_program/access/access_program in programs)
if(access_program.activated)
return O.check_access_list(access_program.access)
else
@@ -232,6 +257,15 @@
/datum/component/nanites/proc/set_cloud(datum/source, amount)
cloud_id = CLAMP(amount, 0, 100)
/datum/component/nanites/proc/set_cloud_sync(datum/source, method)
switch(method)
if(NANITE_CLOUD_TOGGLE)
cloud_active = !cloud_active
if(NANITE_CLOUD_DISABLE)
cloud_active = FALSE
if(NANITE_CLOUD_ENABLE)
cloud_active = TRUE
/datum/component/nanites/proc/set_safety(datum/source, amount)
safety_threshold = CLAMP(amount, 0, max_nanites)
@@ -252,6 +286,19 @@
/datum/component/nanites/proc/get_programs(datum/source, list/nanite_programs)
nanite_programs |= programs
/datum/component/nanites/proc/add_research()
var/research_value = NANITE_BASE_RESEARCH
if(!ishuman(host_mob))
if(!iscarbon(host_mob))
research_value *= 0.4
else
research_value *= 0.8
if(!host_mob.client)
research_value *= 0.5
if(host_mob.stat == DEAD)
research_value *= 0.75
SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_NANITES = research_value))
/datum/component/nanites/proc/nanite_scan(datum/source, mob/user, full_scan)
if(!full_scan)
if(!stealth)
@@ -263,7 +310,8 @@
to_chat(user, "<span class='info'>================</span>")
to_chat(user, "<span class='info'>Saturation: [nanite_volume]/[max_nanites]</span>")
to_chat(user, "<span class='info'>Safety Threshold: [safety_threshold]</span>")
to_chat(user, "<span class='info'>Cloud ID: [cloud_id ? cloud_id : "Disabled"]</span>")
to_chat(user, "<span class='info'>Cloud ID: [cloud_id ? cloud_id : "None"]</span>")
to_chat(user, "<span class='info'>Cloud Sync: [cloud_active ? "Active" : "Disabled"]</span>")
to_chat(user, "<span class='info'>================</span>")
to_chat(user, "<span class='info'>Program List:</span>")
if(!diagnostics)
@@ -280,6 +328,7 @@
data["regen_rate"] = regen_rate
data["safety_threshold"] = safety_threshold
data["cloud_id"] = cloud_id
data["cloud_active"] = cloud_active
var/list/mob_programs = list()
var/id = 1
for(var/X in programs)
@@ -297,24 +346,35 @@
mob_program["trigger_cooldown"] = P.trigger_cooldown / 10
if(scan_level >= 3)
mob_program["activation_delay"] = P.activation_delay
mob_program["timer"] = P.timer
mob_program["timer_type"] = P.get_timer_type_text()
var/list/extra_settings = list()
for(var/Y in P.extra_settings)
var/list/setting = list()
setting["name"] = Y
setting["value"] = P.get_extra_setting(Y)
extra_settings += list(setting)
mob_program["timer_restart"] = P.timer_restart / 10
mob_program["timer_shutdown"] = P.timer_shutdown / 10
mob_program["timer_trigger"] = P.timer_trigger / 10
mob_program["timer_trigger_delay"] = P.timer_trigger_delay / 10
var/list/extra_settings = P.get_extra_settings_frontend()
mob_program["extra_settings"] = extra_settings
if(LAZYLEN(extra_settings))
mob_program["has_extra_settings"] = TRUE
else
mob_program["has_extra_settings"] = FALSE
if(scan_level >= 4)
mob_program["activation_code"] = P.activation_code
mob_program["deactivation_code"] = P.deactivation_code
mob_program["kill_code"] = P.kill_code
mob_program["trigger_code"] = P.trigger_code
var/list/rules = list()
var/rule_id = 1
for(var/Z in P.rules)
var/datum/nanite_rule/nanite_rule = Z
var/list/rule = list()
rule["display"] = nanite_rule.display()
rule["program_id"] = id
rule["id"] = rule_id
rules += list(rule)
rule_id++
mob_program["rules"] = rules
if(LAZYLEN(rules))
mob_program["has_rules"] = TRUE
id++
mob_programs += list(mob_program)
data["mob_programs"] = mob_programs
@@ -0,0 +1,50 @@
/*
//////////////////////////////////////
Disfiguration
Hidden.
No change to resistance.
Increases stage speed.
Slightly increases transmittability.
Critical Level.
BONUS
Adds disfiguration trait making the mob appear as "Unknown" to others.
//////////////////////////////////////
*/
/datum/symptom/disfiguration
name = "Disfiguration"
desc = "The virus liquefies facial muscles, disfiguring the host."
stealth = 2
resistance = 0
stage_speed = 3
transmittable = 1
level = 5
severity = 1
symptom_delay_min = 25
symptom_delay_max = 75
/datum/symptom/disfiguration/Activate(datum/disease/advance/A)
. = ..()
if(!.)
return
var/mob/living/M = A.affected_mob
if (HAS_TRAIT(M, TRAIT_DISFIGURED))
return
switch(A.stage)
if(5)
ADD_TRAIT(M, TRAIT_DISFIGURED, DISEASE_TRAIT)
M.visible_message("<span class='warning'>[M]'s face appears to cave in!</span>", "<span class='notice'>You feel your face crumple and cave in!</span>")
else
M.visible_message("<span class='warning'>[M]'s face begins to contort...</span>", "<span class='notice'>Your face feels wet and malleable...</span>")
/datum/symptom/disfiguration/End(datum/disease/advance/A)
. = ..()
if(!.)
return
if(A.affected_mob)
REMOVE_TRAIT(A.affected_mob, TRAIT_DISFIGURED, DISEASE_TRAIT)
+16 -63
View File
@@ -1,68 +1,22 @@
/*
//////////////////////////////////////
Vitiligo
Polyvitiligo
Hidden.
No change to resistance.
Increases stage speed.
Slightly increases transmittability.
Critical Level.
BONUS
Makes the mob lose skin pigmentation.
//////////////////////////////////////
*/
/datum/symptom/vitiligo
name = "Vitiligo"
desc = "The virus destroys skin pigment cells, causing rapid loss of pigmentation in the host."
stealth = 2
resistance = 0
stage_speed = 3
transmittable = 1
level = 5
severity = 1
symptom_delay_min = 25
symptom_delay_max = 75
/datum/symptom/vitiligo/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.skin_tone == "albino")
return
switch(A.stage)
if(5)
H.skin_tone = "albino"
H.update_body(0)
else
H.visible_message("<span class='warning'>[H] looks a bit pale...</span>", "<span class='notice'>Your skin suddenly appears lighter...</span>")
/*
//////////////////////////////////////
Revitiligo
Slightly noticable.
Noticeable.
Increases resistance.
Increases stage speed slightly.
Increases transmission.
Critical Level.
BONUS
Makes the mob gain skin pigmentation.
Makes the mob gain a random crayon powder colorful reagent.
//////////////////////////////////////
*/
/datum/symptom/revitiligo
name = "Revitiligo"
desc = "The virus causes increased production of skin pigment cells, making the host's skin grow darker over time."
/datum/symptom/polyvitiligo
name = "Polyvitiligo"
desc = "The virus replaces the melanin in the skin with reactive pigment."
stealth = -1
resistance = 3
stage_speed = 1
@@ -72,17 +26,16 @@ BONUS
symptom_delay_min = 7
symptom_delay_max = 14
/datum/symptom/revitiligo/Activate(datum/disease/advance/A)
/datum/symptom/polyvitiligo/Activate(datum/disease/advance/A)
if(!..())
return
var/mob/living/M = A.affected_mob
if(ishuman(M))
var/mob/living/carbon/human/H = M
if(H.skin_tone == "african2")
return
switch(A.stage)
if(5)
H.skin_tone = "african2"
H.update_body(0)
else
H.visible_message("<span class='warning'>[H] looks a bit dark...</span>", "<span class='notice'>Your skin suddenly appears darker...</span>")
switch(A.stage)
if(5)
var/static/list/banned_reagents = list(/datum/reagent/colorful_reagent/crayonpowder/invisible, /datum/reagent/colorful_reagent/crayonpowder/white)
var/color = pick(subtypesof(/datum/reagent/colorful_reagent/crayonpowder) - banned_reagents)
if(M.reagents.total_volume <= (M.reagents.maximum_volume/10)) // no flooding humans with 1000 units of colorful reagent
M.reagents.add_reagent(color, 5)
else
if (prob(50)) // spam
M.visible_message("<span class='warning'>[M] looks rather vibrant...</span>", "<span class='notice'>The colors, man, the colors...</span>")
@@ -38,10 +38,11 @@
return
CRASH("We couldn't assign an ID!")
// Called when processing of the advance disease that holds this symptom infects a host and upon each Refresh() of that advance disease.
// Called when processing of the advance disease, which holds this symptom, starts.
/datum/symptom/proc/Start(datum/disease/advance/A)
if(neutered)
return FALSE
next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10) //so it doesn't instantly activate on infection
return TRUE
// Called when the advance disease is going to be deleted or when the advance disease stops processing.
@@ -59,7 +60,7 @@
next_activation = world.time + rand(symptom_delay_min * 10, symptom_delay_max * 10)
return TRUE
/datum/symptom/proc/on_stage_change(datum/disease/advance/A)
/datum/symptom/proc/on_stage_change(new_stage, datum/disease/advance/A)
if(neutered)
return FALSE
return TRUE
+21
View File
@@ -0,0 +1,21 @@
/// Just for marking when someone's swimming.
/datum/element/swimming
element_flags = ELEMENT_DETACH
/datum/element/swimming/Attach(datum/target)
if((. = ..()) == ELEMENT_INCOMPATIBLE)
return
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
RegisterSignal(target, COMSIG_MOVABLE_MOVED, .proc/check_valid)
ADD_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING) //seriously there's only one way to get this
/datum/element/swimming/Detach(datum/target)
. = ..()
UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
REMOVE_TRAIT(target, TRAIT_SWIMMING, TRAIT_SWIMMING)
/datum/element/swimming/proc/check_valid(datum/source)
var/mob/living/L = source
if(!istype(L.loc, /turf/open/pool))
source.RemoveElement(/datum/element/swimming)
+5 -1
View File
@@ -61,7 +61,6 @@
var/late_joiner = FALSE
var/force_escaped = FALSE // Set by Into The Sunset command of the shuttle manipulator
var/list/learned_recipes //List of learned recipe TYPES.
/datum/mind/New(var/key)
@@ -715,6 +714,11 @@
if(G)
G.reenter_corpse()
/// Sets our can_hijack to the fastest speed our antag datums allow.
/datum/mind/proc/get_hijack_speed()
. = 0
for(var/datum/antagonist/A in antag_datums)
. = max(., A.hijack_speed())
/datum/mind/proc/has_objective(objective_type)
for(var/datum/antagonist/A in antag_datums)
+4
View File
@@ -1367,3 +1367,7 @@ NOTE: there are two lists of areas in the end of this file: centcom and station
/area/tcommsat/lounge
name = "Telecommunications Satellite Lounge"
icon_state = "tcomsatlounge"
/area/crew_quarters/fitness/pool
name = "Pool Area"
icon_state = "pool"
@@ -57,7 +57,8 @@ Property weights are:
mean += 2.5
if(CHAOS_MAX)
mean += 5
GLOB.dynamic_curve_centre += (mean/voters)
if(voters)
GLOB.dynamic_curve_centre += (mean/voters)
GLOB.dynamic_forced_threat_level = forced_threat_level
/datum/dynamic_storyteller/proc/get_midround_cooldown()
+4 -2
View File
@@ -314,9 +314,11 @@ GLOBAL_LIST_EMPTY(objectives)
/datum/objective/hijack
name = "hijack"
explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody."
team_explanation_text = "Hijack the shuttle to ensure no loyalist Nanotrasen crew escape alive and out of custody. Leave no team member behind."
explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console)."
team_explanation_text = "Hijack the emergency shuttle by hacking its navigational protocols through the control console (alt click emergency shuttle console). Leave no team member behind."
martyr_compatible = 0 //Technically you won't get both anyway.
/// Overrides the hijack speed of any antagonist datum it is on ONLY, no other datums are impacted.
var/hijack_speed_override = 1
/datum/objective/hijack/check_completion() // Requires all owners to escape.
if(SSshuttle.emergency.mode != SHUTTLE_ENDGAME)
+1 -1
View File
@@ -369,7 +369,7 @@ AI MODULES
if(!targName)
return
subject = targName
laws = list("You may not injure a [subject] or, through inaction, allow a [subject] to come to harm.",\
laws = list("You may not injure a [subject] or cause one to come to harm.",\
"You must obey orders given to you by [subject]s, except where such orders would conflict with the First Law.",\
"You must protect your own existence as long as such does not conflict with the First or Second Law.")
..()
+1 -1
View File
@@ -20,7 +20,7 @@
throw_range = 14
w_class = WEIGHT_CLASS_SMALL
/obj/item/toy/tennis/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
/obj/item/toy/tennis/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
+1 -16
View File
@@ -431,26 +431,11 @@
if((!req_defib && grab_ghost) || (req_defib && defib.grab_ghost))
H.notify_ghost_cloning("Your heart is being defibrillated!")
H.grab_ghost() // Shove them back in their body.
else if(can_defib(H))
else if(H.can_defib())
H.notify_ghost_cloning("Your heart is being defibrillated. Re-enter your corpse if you want to be revived!", source = src)
do_help(H, user)
/obj/item/twohanded/shockpaddles/proc/can_defib(mob/living/carbon/H) //Our code here is different than tg, if it breaks in testing; BUG_PROBABLE_CAUSE
var/obj/item/organ/heart = H.getorgan(/obj/item/organ/heart)
if(H.suiciding || H.hellbound || HAS_TRAIT(H, TRAIT_HUSK))
return
if((world.time - H.timeofdeath) > tlimit)
return
if((H.getBruteLoss() >= MAX_REVIVE_BRUTE_DAMAGE) || (H.getFireLoss() >= MAX_REVIVE_FIRE_DAMAGE))
return
if(!heart || (heart.organ_flags & ORGAN_FAILING))
return
var/obj/item/organ/brain/BR = H.getorgan(/obj/item/organ/brain)
if(QDELETED(BR) || BR.brain_death || (BR.organ_flags & ORGAN_FAILING) || H.suiciding)
return
return TRUE
/obj/item/twohanded/shockpaddles/proc/shock_touching(dmg, mob/H)
if(!H.pulledby || !isliving(H.pulledby))
return
@@ -49,6 +49,10 @@
return
if(istype(target, /obj/structure/falsewall))
return
if(target.alpha != 255)
return
if(target.invisibility != 0)
return
if(iseffect(target))
if(!(istype(target, /obj/effect/decal))) //be a footprint
return
+1 -1
View File
@@ -259,7 +259,7 @@
light_color = "#37FFF7"
actions_types = list()
/obj/item/melee/transforming/energy/sword/cx/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
/obj/item/melee/transforming/energy/sword/cx/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
+9 -2
View File
@@ -179,17 +179,23 @@
/obj/item/pinpointer/crew
))
/obj/item/storage/belt/medical/surgery_belt_adv
name = "surgical supply belt"
desc = "A specialized belt designed for holding surgical equipment. It seems to have specific pockets for each and every surgical tool you can think of."
content_overlays = FALSE
var/advanced_drapes = FALSE
/obj/item/storage/belt/medical/surgery_belt_adv/PopulateContents()
new /obj/item/scalpel/advanced(src)
new /obj/item/retractor/advanced(src)
new /obj/item/surgicaldrill/advanced(src)
new /obj/item/surgical_drapes(src)
if(advanced_drapes)
new /obj/item/surgical_drapes/advanced(src)
else
new /obj/item/surgical_drapes(src)
/obj/item/storage/belt/medical/surgery_belt_adv/cmo
advanced_drapes = TRUE
/obj/item/storage/belt/security
name = "security belt"
@@ -575,6 +581,7 @@
/obj/item/key/janitor,
/obj/item/clothing/gloves,
/obj/item/melee/flyswatter,
/obj/item/twohanded/broom,
/obj/item/paint/paint_remover,
/obj/item/assembly/mousetrap,
/obj/item/screwdriver,
+50 -42
View File
@@ -15,13 +15,18 @@
attack_verb = list("beaten")
armor = list("melee" = 0, "bullet" = 0, "laser" = 0, "energy" = 0, "bomb" = 50, "bio" = 0, "rad" = 0, "fire" = 80, "acid" = 80)
var/stunforce = 70
var/stamforce = 25
var/status = FALSE
var/knockdown = TRUE
var/obj/item/stock_parts/cell/cell
var/hitcost = 1000
var/hitcost = 750
var/throw_hit_chance = 35
var/preload_cell_type //if not empty the baton starts with this type of cell
/obj/item/melee/baton/examine(mob/user)
. = ..()
. += "<span class='notice'>Right click attack while in combat mode to disarm instead of stun.</span>"
/obj/item/melee/baton/get_cell()
. = cell
if(iscyborg(loc))
@@ -32,7 +37,7 @@
user.visible_message("<span class='suicide'>[user] is putting the live [name] in [user.p_their()] mouth! It looks like [user.p_theyre()] trying to commit suicide!</span>")
return (FIRELOSS)
/obj/item/melee/baton/Initialize()
/obj/item/melee/baton/Initialize(mapload)
. = ..()
if(preload_cell_type)
if(!ispath(preload_cell_type,/obj/item/stock_parts/cell))
@@ -48,7 +53,7 @@
baton_stun(hit_atom)
/obj/item/melee/baton/loaded //this one starts with a cell pre-installed.
preload_cell_type = /obj/item/stock_parts/cell/high
preload_cell_type = /obj/item/stock_parts/cell/high/plus
/obj/item/melee/baton/proc/deductcharge(chrgdeductamt, chargecheck = TRUE, explode = TRUE)
var/obj/item/stock_parts/cell/copper_top = get_cell()
@@ -134,44 +139,41 @@
add_fingerprint(user)
/obj/item/melee/baton/attack(mob/M, mob/living/carbon/human/user)
var/interrupt = common_baton_melee(M, user, FALSE)
if(!interrupt)
..()
/obj/item/melee/baton/alt_pre_attack(atom/A, mob/living/user, params)
. = common_baton_melee(A, user, TRUE) //return true (attackchain interrupt) if this also returns true. no harm-disarming.
user.changeNext_move(CLICK_CD_MELEE)
//return TRUE to interrupt attack chain.
/obj/item/melee/baton/proc/common_baton_melee(mob/M, mob/living/user, disarming = FALSE)
if(iscyborg(M) || !isliving(M)) //can't baton cyborgs
return FALSE
if(status && HAS_TRAIT(user, TRAIT_CLUMSY) && prob(50))
clowning_around(user)
return
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT)//CIT CHANGE - makes it impossible to baton in stamina softcrit
to_chat(user, "<span class='danger'>You're too exhausted for that.</span>")//CIT CHANGE - ditto
return //CIT CHANGE - ditto
if(iscyborg(M))
..()
return
if(user.getStaminaLoss() >= STAMINA_SOFTCRIT) //CIT CHANGE - makes it impossible to baton in stamina softcrit
to_chat(user, "<span class='danger'>You're too exhausted for that.</span>")
return TRUE
if(ishuman(M))
var/mob/living/carbon/human/L = M
if(check_martial_counter(L, user))
return
return TRUE
if(status)
if(baton_stun(M, user, disarming))
user.do_attack_animation(M)
user.adjustStaminaLossBuffered(getweight()) //CIT CHANGE - makes stunbatonning others cost stamina
else if(user.a_intent != INTENT_HARM) //they'll try to bash in the last proc.
M.visible_message("<span class='warning'>[user] has prodded [M] with [src]. Luckily it was off.</span>", \
"<span class='warning'>[user] has prodded you with [src]. Luckily it was off</span>")
return disarming || (user.a_intent != INTENT_HARM)
if(user.a_intent != INTENT_HARM)
if(status)
if(baton_stun(M, user))
user.do_attack_animation(M)
user.adjustStaminaLossBuffered(getweight())//CIT CHANGE - makes stunbatonning others cost stamina
return
else
M.visible_message("<span class='warning'>[user] has prodded [M] with [src]. Luckily it was off.</span>", \
"<span class='warning'>[user] has prodded you with [src]. Luckily it was off</span>")
else
if(status)
baton_stun(M, user)
..()
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user)
/obj/item/melee/baton/proc/baton_stun(mob/living/L, mob/user, disarming = FALSE)
if(L.check_shields(src, 0, "[user]'s [name]", MELEE_ATTACK)) //No message; check_shields() handles that
playsound(L, 'sound/weapons/genhit.ogg', 50, 1)
return FALSE
var/stunpwr = stunforce
var/stunpwr = stamforce
var/obj/item/stock_parts/cell/our_cell = get_cell()
if(!our_cell)
switch_status(FALSE)
@@ -187,16 +189,21 @@
return FALSE
stunpwr *= round(stuncharge/hitcost, 0.1)
L.DefaultCombatKnockdown(stunpwr)
L.apply_damage(stunpwr*0.5, STAMINA, user.zone_selected)
L.apply_effect(EFFECT_STUTTER, stunforce)
if(!disarming)
if(knockdown)
L.DefaultCombatKnockdown(50, override_stamdmg = 0) //knockdown
L.adjustStaminaLoss(stunpwr)
else
L.drop_all_held_items() //no knockdown/stamina damage, instead disarm.
L.apply_effect(EFFECT_STUTTER, stamforce)
SEND_SIGNAL(L, COMSIG_LIVING_MINOR_SHOCK)
if(user)
L.lastattacker = user.real_name
L.lastattackerckey = user.ckey
L.visible_message("<span class='danger'>[user] has stunned [L] with [src]!</span>", \
"<span class='userdanger'>[user] has stunned you with [src]!</span>")
log_combat(user, L, "stunned")
L.visible_message("<span class='danger'>[user] has [disarming? "disarmed" : "stunned"] [L] with [src]!</span>", \
"<span class='userdanger'>[user] has [disarming? "disarmed" : "stunned"] you with [src]!</span>")
log_combat(user, L, disarming? "disarmed" : "stunned")
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
@@ -211,7 +218,7 @@
user.visible_message("<span class='danger'>[user] accidentally hits [user.p_them()]self with [src]!</span>", \
"<span class='userdanger'>You accidentally hit yourself with [src]!</span>")
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
user.DefaultCombatKnockdown(stunforce*3)
user.DefaultCombatKnockdown(stamforce*6)
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
deductcharge(hitcost)
@@ -273,8 +280,9 @@
w_class = WEIGHT_CLASS_BULKY
force = 3
throwforce = 5
stunforce = 60
hitcost = 2000
stamforce = 25
hitcost = 1000
knockdown = FALSE
throw_hit_chance = 10
slot_flags = ITEM_SLOT_BACK
var/obj/item/assembly/igniter/sparkler
+1 -1
View File
@@ -16,7 +16,7 @@
user.visible_message("<span class='danger'>[user] accidentally hits [user.p_them()]self with [src]!</span>", \
"<span class='userdanger'>You accidentally hit yourself with [src]!</span>")
SEND_SIGNAL(user, COMSIG_LIVING_MINOR_SHOCK)
user.DefaultCombatKnockdown(stunforce*3)
user.DefaultCombatKnockdown(stamforce * 6)
playsound(loc, 'sound/weapons/egloves.ogg', 50, 1, -1)
if(do_teleport(user, get_turf(user), 50, channel = TELEPORT_CHANNEL_BLUESPACE))
deductcharge(hitcost)
+1 -1
View File
@@ -289,7 +289,7 @@
var/light_brightness = 3
actions_types = list()
/obj/item/toy/sword/cx/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
/obj/item/toy/sword/cx/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
+64 -1
View File
@@ -510,7 +510,7 @@
/obj/item/twohanded/dualsaber/hypereutactic/chaplain/IsReflect()
return FALSE
/obj/item/twohanded/dualsaber/hypereutactic/pre_altattackby(atom/A, mob/living/user, params) //checks if it can do right click memes
/obj/item/twohanded/dualsaber/hypereutactic/alt_pre_attack(atom/A, mob/living/user, params) //checks if it can do right click memes
altafterattack(A, user, TRUE, params)
return TRUE
@@ -1016,3 +1016,66 @@
C.change_view(CONFIG_GET(string/default_view))
user.client.pixel_x = 0
user.client.pixel_y = 0
/obj/item/twohanded/broom
name = "broom"
desc = "This is my BROOMSTICK! It can be used manually or braced with two hands to sweep items as you move. It has a telescopic handle for compact storage." //LIES
icon = 'icons/obj/janitor.dmi'
icon_state = "broom0"
lefthand_file = 'icons/mob/inhands/equipment/custodial_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/custodial_righthand.dmi'
force = 8
throwforce = 10
throw_speed = 3
throw_range = 7
w_class = WEIGHT_CLASS_NORMAL
force_unwielded = 8
force_wielded = 12
attack_verb = list("swept", "brushed off", "bludgeoned", "whacked")
resistance_flags = FLAMMABLE
/obj/item/twohanded/broom/update_icon_state()
icon_state = "broom[wielded]"
/obj/item/twohanded/broom/wield(mob/user)
. = ..()
if(!wielded)
return
to_chat(user, "<span class='notice'>You brace the [src] against the ground in a firm sweeping stance.</span>")
RegisterSignal(user, COMSIG_MOVABLE_MOVED, .proc/sweep)
/obj/item/twohanded/broom/unwield(mob/user)
. = ..()
UnregisterSignal(user, COMSIG_MOVABLE_MOVED)
/obj/item/twohanded/broom/afterattack(atom/A, mob/user, proximity)
. = ..()
if(!proximity)
return
sweep(user, A, FALSE)
/obj/item/twohanded/broom/proc/sweep(mob/user, atom/A, moving = TRUE)
var/turf/target
if (!moving)
if (isturf(A))
target = A
else
target = A.loc
else
target = user.loc
if (locate(/obj/structure/table) in target.contents)
return
var/i = 0
for(var/obj/item/garbage in target.contents)
if(!garbage.anchored)
garbage.Move(get_step(target, user.dir), user.dir)
i++
if(i >= 20)
break
if(i >= 1)
playsound(loc, 'sound/weapons/thudswoosh.ogg', 5, TRUE, -1)
/obj/item/twohanded/broom/proc/janicart_insert(mob/user, obj/structure/janitorialcart/J) //bless you whoever fixes this copypasta
J.put_in_cart(src, user)
J.mybroom=src
J.update_icon()
@@ -76,7 +76,7 @@
new /obj/item/door_remote/chief_medical_officer(src)
new /obj/item/clothing/neck/petcollar(src)
new /obj/item/pet_carrier(src)
new /obj/item/storage/belt/medical/surgery_belt_adv(src)
new /obj/item/storage/belt/medical/surgery_belt_adv/cmo(src)
new /obj/item/wallframe/defib_mount(src)
new /obj/item/circuitboard/machine/techfab/department/medical(src)
new /obj/item/storage/photo_album/CMO(src)
+20 -6
View File
@@ -6,11 +6,11 @@
anchored = FALSE
density = TRUE
//copypaste sorry
var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite
var/obj/item/storage/bag/trash/mybag = null
var/obj/item/mop/mymop = null
var/obj/item/reagent_containers/spray/cleaner/myspray = null
var/obj/item/lightreplacer/myreplacer = null
var/obj/item/storage/bag/trash/mybag
var/obj/item/mop/mymop
var/obj/item/twohanded/broom/mybroom
var/obj/item/reagent_containers/spray/cleaner/myspray
var/obj/item/lightreplacer/myreplacer
var/signs = 0
var/const/max_signs = 4
@@ -49,7 +49,12 @@
m.janicart_insert(user, src)
else
to_chat(user, fail_msg)
else if(istype(I, /obj/item/twohanded/broom))
if(!mybroom)
var/obj/item/twohanded/broom/b=I
b.janicart_insert(user,src)
else
to_chat(user, fail_msg)
else if(istype(I, /obj/item/storage/bag/trash))
if(!mybag)
var/obj/item/storage/bag/trash/t=I
@@ -97,6 +102,8 @@
dat += "<a href='?src=[REF(src)];garbage=1'>[mybag.name]</a><br>"
if(mymop)
dat += "<a href='?src=[REF(src)];mop=1'>[mymop.name]</a><br>"
if(mybroom)
dat += "<a href='?src=[REF(src)];broom=1'>[mybroom.name]</a><br>"
if(myspray)
dat += "<a href='?src=[REF(src)];spray=1'>[myspray.name]</a><br>"
if(myreplacer)
@@ -124,6 +131,11 @@
user.put_in_hands(mymop)
to_chat(user, "<span class='notice'>You take [mymop] from [src].</span>")
mymop = null
if(href_list["broom"])
if(mybroom)
user.put_in_hands(mybroom)
to_chat(user, "<span class='notice'>You take [mybroom] from [src].</span>")
mybroom = null
if(href_list["spray"])
if(myspray)
user.put_in_hands(myspray)
@@ -155,6 +167,8 @@
add_overlay("cart_garbage")
if(mymop)
add_overlay("cart_mop")
if(mybroom)
add_overlay("cart_broom")
if(myspray)
add_overlay("cart_spray")
if(myreplacer)
+1 -1
View File
@@ -528,7 +528,7 @@
if(B.cell)
if(B.cell.charge > 0 && B.status == 1)
flick("baton_active", src)
var/stunforce = B.stunforce
var/stunforce = B.stamforce
user.DefaultCombatKnockdown(stunforce)
user.stuttering = stunforce/20
B.deductcharge(B.hitcost)
+2
View File
@@ -184,6 +184,8 @@
soundin = pick('sound/items/bikehorn.ogg', 'sound/items/AirHorn2.ogg', 'sound/misc/sadtrombone.ogg', 'sound/items/AirHorn.ogg', 'sound/effects/reee.ogg', 'sound/items/WEEOO1.ogg', 'sound/voice/beepsky/iamthelaw.ogg', 'sound/voice/beepsky/creep.ogg','sound/magic/Fireball.ogg' ,'sound/effects/pray.ogg', 'sound/voice/hiss1.ogg','sound/machines/buzz-sigh.ogg', 'sound/machines/ping.ogg', 'sound/weapons/flashbang.ogg', 'sound/weapons/bladeslice.ogg')
if("goose")
soundin = pick('sound/creatures/goose1.ogg', 'sound/creatures/goose2.ogg', 'sound/creatures/goose3.ogg', 'sound/creatures/goose4.ogg')
if("water_wade")
soundin = pick('sound/effects/water_wade1.ogg', 'sound/effects/water_wade2.ogg', 'sound/effects/water_wade3.ogg', 'sound/effects/water_wade4.ogg')
//START OF CIT CHANGES - adds random vore sounds
if ("struggle_sound")
soundin = pick( 'sound/vore/pred/struggle_01.ogg','sound/vore/pred/struggle_02.ogg','sound/vore/pred/struggle_03.ogg',
@@ -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()

Some files were not shown because too many files have changed in this diff Show More