This commit is contained in:
Ghommie
2020-02-22 00:55:32 +01:00
1009 changed files with 20505 additions and 13193 deletions
+72
View File
@@ -544,6 +544,70 @@
cooldown = world.time
owner.playsound_local(box, 'sound/misc/box_deploy.ogg', 50, TRUE)
/datum/action/item_action/removeAPCs
name = "Relinquish APC"
desc = "Let go of an APC, relinquish control back to the station."
icon_icon = 'icons/obj/implants.dmi'
button_icon_state = "hijackx"
/datum/action/item_action/removeAPCs/Trigger()
var/list/areas = list()
for (var/area/a in owner.siliconaccessareas)
areas[a.name] = a
var/removeAPC = input("Select an APC to remove:","Remove APC Control",1) as null|anything in areas
if (!removeAPC)
return
var/area/area = areas[removeAPC]
var/obj/machinery/power/apc/apc = area.get_apc()
if (!apc || !(area in owner.siliconaccessareas))
return
apc.hijacker = null
apc.update_icon()
apc.set_hijacked_lighting()
owner.toggleSiliconAccessArea(area)
/datum/action/item_action/accessAPCs
name = "Access APC Interface"
desc = "Open the APC's interface."
icon_icon = 'icons/obj/implants.dmi'
button_icon_state = "hijacky"
/datum/action/item_action/accessAPCs/Trigger()
var/list/areas = list()
for (var/area/a in owner.siliconaccessareas)
areas[a.name] = a
var/accessAPC = input("Select an APC to access:","Access APC Interface",1) as null|anything in areas
if (!accessAPC)
return
var/area/area = areas[accessAPC]
var/obj/machinery/power/apc/apc = area.get_apc()
if (!apc || !(area in owner.siliconaccessareas))
return
apc.ui_interact(owner)
/datum/action/item_action/stealthmodetoggle
name = "Toggle Stealth Mode"
desc = "Toggles the stealth mode on the hijack implant."
icon_icon = 'icons/obj/implants.dmi'
button_icon_state = "hijackz"
/datum/action/item_action/stealthmodetoggle/Trigger()
var/obj/item/implant/hijack/H = target
if (!istype(H))
return
if (H.stealthcooldown > world.time)
to_chat(owner,"<span class='warning'>The hijack implant's stealth mode toggle is still rebooting!</span>")
return
H.stealthmode = !H.stealthmode
for (var/area/area in H.imp_in.siliconaccessareas)
var/obj/machinery/power/apc/apc = area.get_apc()
if (apc)
apc.set_hijacked_lighting()
apc.update_icon()
H.stealthcooldown = world.time + 15 SECONDS
H.toggle_eyes()
to_chat(owner,"<span class='notice'>You toggle the hijack implant's stealthmode [H.stealthmode ? "on" : "off"].</span>")
/datum/action/item_action/flash
name = "Flash"
@@ -756,3 +820,11 @@
target.layer = old_layer
target.plane = old_plane
current_button.appearance_cache = target.appearance
/proc/get_action_of_type(mob/M, var/action_type)
if(!M.actions || !ispath(action_type, /datum/action))
return
for(var/datum/action/A in M.actions)
if(istype(A, action_type))
return A
return
+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)
+2 -1
View File
@@ -61,7 +61,8 @@
/datum/beam/proc/recalculate_in(time)
if(timing_id)
deltimer(timing_id)
timing_id = addtimer(CALLBACK(src, .proc/recalculate), time, TIMER_STOPPABLE)
if(!finished)
timing_id = addtimer(CALLBACK(src, .proc/recalculate), time, TIMER_STOPPABLE)
/datum/beam/proc/after_calculate()
if((sleep_time == null) || finished) //Does not automatically recalculate.
+47 -23
View File
@@ -64,29 +64,48 @@
/datum/component/personal_crafting/proc/check_contents(datum/crafting_recipe/R, list/contents)
/**
* Check that the contents of the recipe meet the requirements.
*
* user: The /mob that initated the crafting.
* R: The /datum/crafting_recipe being attempted.
* contents: List of items to search for R's reqs.
*/
/datum/component/personal_crafting/proc/check_contents(mob/user, datum/crafting_recipe/R, list/contents)
var/list/item_instances = contents["instances"]
contents = contents["other"]
main_loop:
for(var/A in R.reqs)
var/needed_amount = R.reqs[A]
for(var/B in contents)
if(ispath(B, A))
if (R.blacklist.Find(B))
continue
if(contents[B] >= R.reqs[A])
continue main_loop
else
needed_amount -= contents[B]
if(needed_amount <= 0)
continue main_loop
else
continue
var/list/requirements_list = list()
// Process all requirements
for(var/requirement_path in R.reqs)
// Check we have the appropriate amount available in the contents list
var/needed_amount = R.reqs[requirement_path]
for(var/content_item_path in contents)
// Right path and not blacklisted
if(!ispath(content_item_path, requirement_path) || R.blacklist.Find(requirement_path))
continue
needed_amount -= contents[content_item_path]
if(needed_amount <= 0)
break
if(needed_amount > 0)
return FALSE
for(var/A in R.chem_catalysts)
if(contents[A] < R.chem_catalysts[A])
// Store the instances of what we will use for R.check_requirements() for requirement_path
var/list/instances_list = list()
for(var/instance_path in item_instances)
if(ispath(instance_path, requirement_path))
instances_list += item_instances[instance_path]
requirements_list[requirement_path] = instances_list
for(var/requirement_path in R.chem_catalysts)
if(contents[requirement_path] < R.chem_catalysts[requirement_path])
return FALSE
return TRUE
return R.check_requirements(user, requirements_list)
/datum/component/personal_crafting/proc/get_environment(mob/user)
. = list()
@@ -110,9 +129,14 @@
. = list()
.["tool_behaviour"] = list()
.["other"] = list()
.["instances"] = list()
for(var/obj/item/I in get_environment(user))
if(I.flags_1 & HOLOGRAM_1)
continue
if(.["instances"][I.type])
.["instances"][I.type] += I
else
.["instances"][I.type] = list(I)
if(istype(I, /obj/item/stack))
var/obj/item/stack/S = I
.["other"][I.type] += S.amount
@@ -161,11 +185,11 @@
/datum/component/personal_crafting/proc/construct_item(mob/user, datum/crafting_recipe/R)
var/list/contents = get_surroundings(user)
var/send_feedback = 1
if(check_contents(R, contents))
if(check_contents(user, R, contents))
if(check_tools(user, R, contents))
if(do_after(user, R.time, target = user))
contents = get_surroundings(user)
if(!check_contents(R, contents))
if(!check_contents(user, R, contents))
return ", missing component."
if(!check_tools(user, R, contents))
return ", missing tool."
@@ -341,7 +365,7 @@
if((R.category != cur_category) || (R.subcategory != cur_subcategory))
continue
craftability["[REF(R)]"] = check_contents(R, surroundings)
craftability["[REF(R)]"] = check_contents(user, R, surroundings)
data["craftability"] = craftability
return data
@@ -13,3 +13,9 @@
desc = "A classic rifle stock that doubles as a grip, roughly carved out of wood."
icon = 'icons/obj/improvised.dmi'
icon_state = "riflestock"
/obj/item/weaponcrafting/silkstring
name = "silkstring"
desc = "A long pice of silk looks like cable coil."
icon = 'icons/obj/improvised.dmi'
icon_state = "silkstring"
@@ -1,10 +1,3 @@
/datum/crafting_recipe/bonearmor
name = "Bone Armor"
result = /obj/item/clothing/suit/armor/bone
time = 30
reqs = list(/obj/item/stack/sheet/bone = 6)
category = CAT_PRIMAL
/datum/crafting_recipe/bonetalisman
name = "Bone Talisman"
result = /obj/item/clothing/accessory/talisman
@@ -29,13 +22,6 @@
/obj/item/stack/sheet/sinew = 1)
category = CAT_PRIMAL
/datum/crafting_recipe/skullhelm
name = "Skull Helmet"
result = /obj/item/clothing/head/helmet/skull
time = 30
reqs = list(/obj/item/stack/sheet/bone = 4)
category = CAT_PRIMAL
/datum/crafting_recipe/goliathcloak
name = "Goliath Cloak"
result = /obj/item/clothing/suit/hooded/cloak/goliath
@@ -62,13 +48,6 @@
/obj/item/stack/sheet/sinew = 2)
category = CAT_PRIMAL
/datum/crafting_recipe/bonedagger
name = "Bone Dagger"
result = /obj/item/kitchen/knife/combat/bone
time = 20
reqs = list(/obj/item/stack/sheet/bone = 2)
category = CAT_PRIMAL
/datum/crafting_recipe/bonespear
name = "Bone Spear"
result = /obj/item/twohanded/bonespear
@@ -110,4 +89,31 @@
parts = list(/obj/item/bodypart/head = 1,
/obj/item/twohanded/bonespear = 1)
result = /obj/structure/headpike/bone
category = CAT_PRIMAL
/datum/crafting_recipe/quiver
name = "Quiver"
always_availible = FALSE
result = /obj/item/storage/belt/quiver
time = 80
reqs = list(/obj/item/stack/sheet/leather = 3,
/obj/item/stack/sheet/sinew = 4)
category = CAT_PRIMAL
/datum/crafting_recipe/bone_bow
name = "Bone Bow"
result = /obj/item/gun/ballistic/bow/ashen
time = 200
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 8,
/obj/item/stack/sheet/sinew = 4)
category = CAT_PRIMAL
/datum/crafting_recipe/bow_tablet
name = "Sandstone Bow Making Manual"
result = /obj/item/book/granter/crafting_recipe/bone_bow
time = 600 //Scribing
always_availible = FALSE
reqs = list(/obj/item/stack/rods = 1,
/obj/item/stack/sheet/mineral/sandstone = 4)
category = CAT_PRIMAL
@@ -8,6 +8,12 @@
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/pin_removal/check_requirements(mob/user, list/collected_requirements)
var/obj/item/gun/G = collected_requirements[/obj/item/gun][1]
if (G.no_pin_required || !G.pin)
return FALSE
return TRUE
/datum/crafting_recipe/strobeshield
name = "Strobe Shield"
result = /obj/item/assembly/flash/shield
@@ -193,6 +199,16 @@
//////////////////
/datum/crafting_recipe/pipebow
name = "Pipe Bow"
result = /obj/item/gun/ballistic/bow/pipe
reqs = list(/obj/item/pipe = 5,
/obj/item/stack/sheet/plastic = 15,
/obj/item/weaponcrafting/silkstring = 10)
time = 450
category = CAT_WEAPONRY
subcategory = CAT_WEAPON
/datum/crafting_recipe/smartdartgun
name = "Smart dartgun"
result = /obj/item/gun/syringe/dart
@@ -272,6 +288,37 @@
///AMMO CRAFTING//
//////////////////
/datum/crafting_recipe/arrow
name = "Arrow"
result = /obj/item/ammo_casing/caseless/arrow
time = 40
reqs = list(/obj/item/stack/sheet/mineral/wood = 1,
/obj/item/weaponcrafting/silkstring = 1,
/obj/item/stack/rods = 3) // 1 metal sheet is worth 1.5 arrows
category = CAT_WEAPONRY
subcategory = CAT_AMMO
/datum/crafting_recipe/bone_arrow
name = "Bone Arrow"
result = /obj/item/ammo_casing/caseless/arrow/bone
time = 40
always_availible = FALSE
reqs = list(/obj/item/stack/sheet/bone = 1,
/obj/item/stack/sheet/sinew = 1,
/obj/item/ammo_casing/caseless/arrow/ashen = 1)
category = CAT_WEAPONRY
subcategory = CAT_AMMO
/datum/crafting_recipe/ashen_arrow
name = "Harden Arrow"
result = /obj/item/ammo_casing/caseless/arrow/ashen
tools = list(/obj/structure/bonfire)
time = 20
always_availible = FALSE
reqs = list(/obj/item/ammo_casing/caseless/arrow = 1)
category = CAT_WEAPONRY
subcategory = CAT_AMMO
/datum/crafting_recipe/smartdart
name = "Medical smartdart"
result = /obj/item/reagent_containers/syringe/dart
+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
+97 -37
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
@@ -25,33 +28,36 @@
if(isliving(parent))
host_mob = parent
if(!(MOB_ORGANIC in host_mob.mob_biotypes) && !(MOB_UNDEAD in host_mob.mob_biotypes)) //Shouldn't happen, but this avoids HUD runtimes in case a silicon gets them somehow.
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,16 +232,16 @@
/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()
if(!(MOB_ORGANIC in host_mob.mob_biotypes) && !(MOB_UNDEAD in host_mob.mob_biotypes))
if(!(host_mob.mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD)))
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
@@ -56,7 +56,7 @@
/obj/item/scalpel, /obj/item/reagent_containers/syringe, /obj/item/dnainjector,
/obj/item/reagent_containers/hypospray/medipen, /obj/item/reagent_containers/dropper,
/obj/item/implanter, /obj/item/screwdriver, /obj/item/weldingtool/mini,
/obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol
/obj/item/firing_pin, /obj/item/gun/ballistic/automatic/pistol, /obj/item/gun/ballistic/automatic/magrifle/pistol
))
/datum/component/storage/concrete/pockets/shoes/clown/Initialize()
+9 -4
View File
@@ -41,6 +41,7 @@
var/obj/screen/storage/boxes //storage display object
var/obj/screen/close/closer //close button object
var/current_maxscreensize
var/allow_big_nesting = FALSE //allow storage objects of the same or greater size.
@@ -362,11 +363,15 @@
break
closer.screen_loc = "[screen_start_x + cols]:[screen_pixel_x],[screen_start_y]:[screen_pixel_y]"
/datum/component/storage/proc/show_to(mob/M)
/datum/component/storage/proc/show_to(mob/M, set_screen_size = TRUE)
if(!M.client)
return FALSE
var/list/cview = getviewsize(M.client.view)
var/maxallowedscreensize = cview[1]-8
if(set_screen_size)
current_maxscreensize = maxallowedscreensize
else if(current_maxscreensize)
maxallowedscreensize = current_maxscreensize
if(M.active_storage != src && (M.stat == CONSCIOUS))
for(var/obj/item/I in accessible_items())
if(I.on_found(M))
@@ -547,14 +552,14 @@
return
A.add_fingerprint(M)
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE)
/datum/component/storage/proc/user_show_to_mob(mob/M, force = FALSE, ghost = FALSE)
var/atom/A = parent
if(!istype(M))
return FALSE
A.add_fingerprint(M)
if(!force && (check_locked(null, M) || !M.CanReach(parent, view_only = TRUE)))
return FALSE
show_to(M)
show_to(M, !ghost)
/datum/component/storage/proc/mousedrop_receive(datum/source, atom/movable/O, mob/M)
if(isitem(O))
@@ -665,7 +670,7 @@
return can_be_inserted(I, silent, M)
/datum/component/storage/proc/show_to_ghost(datum/source, mob/dead/observer/M)
return user_show_to_mob(M, TRUE)
return user_show_to_mob(M, TRUE, TRUE)
/datum/component/storage/proc/signal_show_attempt(datum/source, mob/showto, force = FALSE)
return user_show_to_mob(showto, force)
+26 -16
View File
@@ -25,6 +25,8 @@ GLOBAL_LIST_EMPTY(uplinks)
var/unlock_code
var/failsafe_code
var/datum/ui_state/checkstate
var/compact_mode = FALSE
var/debug = FALSE
/datum/component/uplink/Initialize(_owner, _lockable = TRUE, _enabled = FALSE, datum/game_mode/_gamemode, starting_tc = 20, datum/ui_state/_checkstate)
if(!isitem(parent))
@@ -121,7 +123,7 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 450, 750, master_ui, state)
ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
@@ -138,29 +140,35 @@ GLOBAL_LIST_EMPTY(uplinks)
var/list/data = list()
data["telecrystals"] = telecrystals
data["lockable"] = lockable
data["compact_mode"] = compact_mode
return data
/datum/component/uplink/ui_static_data(mob/user)
var/list/data = list()
data["categories"] = list()
for(var/category in uplink_items)
var/list/cat = list(
"name" = category,
"items" = (category == selected_cat ? list() : null))
if(category == selected_cat)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
for(var/item in uplink_items[category])
var/datum/uplink_item/I = uplink_items[category][item]
if(I.limited_stock == 0)
continue
if(I.restricted_roles.len)
var/is_inaccessible = TRUE
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role || debug)
is_inaccessible = FALSE
if(is_inaccessible)
continue
if(I.restricted_roles.len)
var/is_inaccessible = 1
for(var/R in I.restricted_roles)
if(R == user.mind.assigned_role)
is_inaccessible = 0
if(is_inaccessible)
continue
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
cat["items"] += list(list(
"name" = I.name,
"cost" = I.cost,
"desc" = I.desc,
))
data["categories"] += list(cat)
return data
@@ -188,6 +196,8 @@ GLOBAL_LIST_EMPTY(uplinks)
SStgui.close_uis(src)
if("select")
selected_cat = params["category"]
if("compact_toggle")
compact_mode = !compact_mode
return TRUE
/datum/component/uplink/proc/MakePurchase(mob/user, datum/uplink_item/U)
@@ -294,4 +304,4 @@ GLOBAL_LIST_EMPTY(uplinks)
if(!T)
return
explosion(T,1,2,3)
qdel(parent) //Alternatively could brick the uplink.
qdel(parent) //Alternatively could brick the uplink.
+2 -6
View File
@@ -17,14 +17,10 @@
if(HasDisease(D))
return FALSE
var/can_infect = FALSE
for(var/host_type in D.infectable_biotypes)
if(host_type in mob_biotypes)
can_infect = TRUE
break
if(!can_infect)
if(!(D.infectable_biotypes & mob_biotypes))
return FALSE
if(!(type in D.viable_mobtypes))
return FALSE
+1 -1
View File
@@ -30,7 +30,7 @@
var/list/required_organs = list()
var/needs_all_cures = TRUE
var/list/strain_data = list() //dna_spread special bullshit
var/list/infectable_biotypes = list(MOB_ORGANIC) //if the disease can spread on organics, synthetics, or undead
var/infectable_biotypes = MOB_ORGANIC //if the disease can spread on organics, synthetics, or undead
var/process_dead = FALSE //if this ticks while the host is dead
var/copy_type = null //if this is null, copies will use the type of the instance being copied
@@ -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)
@@ -448,9 +448,11 @@
symptom_delay_max = 1
passive_message = "<span class='notice'>Your skin glows faintly for a moment.</span>"
var/cellular_damage = FALSE
threshold_desc = "<b>Transmission 6:</b> Additionally heals cellular damage and toxin lovers.<br>\
<b>Resistance 7:</b> Increases healing speed."
threshold_desc = list(
"Transmission 6" = "Additionally heals cellular damage and toxin lovers.",
"Resistance 7" = "Increases healing speed.",
)
/datum/symptom/heal/radiation/Start(datum/disease/advance/A)
if(!..())
return
+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>")
@@ -14,7 +14,7 @@
/datum/symptom/undead_adaptation/OnRemove(datum/disease/advance/A)
A.process_dead = FALSE
A.infectable_biotypes -= MOB_UNDEAD
A.infectable_biotypes &= ~MOB_UNDEAD
/datum/symptom/inorganic_adaptation
name = "Inorganic Biology"
@@ -27,7 +27,8 @@
severity = 0
/datum/symptom/inorganic_adaptation/OnAdd(datum/disease/advance/A)
A.infectable_biotypes |= MOB_INORGANIC
A.infectable_biotypes |= MOB_MINERAL //Mineral covers plasmamen and golems.
/datum/symptom/inorganic_adaptation/OnRemove(datum/disease/advance/A)
A.infectable_biotypes -= MOB_INORGANIC
A.infectable_biotypes &= ~MOB_MINERAL
@@ -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
+1 -1
View File
@@ -10,7 +10,7 @@
viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
desc = "If left untreated subject will regurgitate bees."
severity = DISEASE_SEVERITY_MEDIUM
infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD) //bees nesting in corpses
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD //bees nesting in corpses
/datum/disease/beesease/stage_act()
..()
+1 -1
View File
@@ -10,7 +10,7 @@
permeability_mod = 0.75
desc = "This disease disrupts the magnetic field of your body, making it act as if a powerful magnet. Injections of iron help stabilize the field."
severity = DISEASE_SEVERITY_MEDIUM
infectable_biotypes = list(MOB_ORGANIC, MOB_ROBOTIC)
infectable_biotypes = MOB_ORGANIC|MOB_ROBOTIC
process_dead = TRUE
/datum/disease/magnitis/stage_act()
+1 -1
View File
@@ -11,7 +11,7 @@
viable_mobtypes = list(/mob/living/carbon/human)
desc = "Subject is possessed by the vengeful spirit of a parrot. Call the priest."
severity = DISEASE_SEVERITY_MEDIUM
infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_INORGANIC, MOB_ROBOTIC)
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC|MOB_MINERAL
bypasses_immunity = TRUE //2spook
var/mob/living/simple_animal/parrot/Poly/ghost/parrot
+2 -2
View File
@@ -168,7 +168,7 @@
stage4 = list("<span class='danger'>Your skin feels very loose.</span>", "<span class='danger'>You can feel... something...inside you.</span>")
stage5 = list("<span class='danger'>Your skin feels as if it's about to burst off!</span>")
new_form = /mob/living/silicon/robot
infectable_biotypes = list(MOB_ORGANIC, MOB_UNDEAD, MOB_ROBOTIC)
infectable_biotypes = MOB_ORGANIC|MOB_UNDEAD|MOB_ROBOTIC
bantype = "Cyborg"
/datum/disease/transformation/robot/stage_act()
@@ -284,7 +284,7 @@
stage4 = list("<span class='danger'>You're ravenous.</span>")
stage5 = list("<span class='danger'>You have become a morph.</span>")
new_form = /mob/living/simple_animal/hostile/morph
infectable_biotypes = list(MOB_ORGANIC, MOB_INORGANIC, MOB_UNDEAD) //magic!
infectable_biotypes = MOB_ORGANIC|MOB_MINERAL|MOB_UNDEAD //magic!
/datum/disease/transformation/gondola
name = "Gondola Transformation"
+1 -1
View File
@@ -45,4 +45,4 @@
cleaned_human.clean_blood()
cleaned_human.wash_cream()
cleaned_human.regenerate_icons()
to_chat(cleaned_human, "<span class='danger'>[src] cleans your face!</span>")
to_chat(cleaned_human, "<span class='danger'>[AM] cleans your face!</span>")
+184
View File
@@ -0,0 +1,184 @@
/datum/element/mob_holder
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
var/worn_state
var/alt_worn
var/right_hand
var/left_hand
var/inv_slots
var/proctype //if present, will be invoked on headwear generation.
/datum/element/mob_holder/Attach(datum/target, _worn_state, _alt_worn, _right_hand, _left_hand, _inv_slots = NONE, _proctype)
. = ..()
if(!isliving(target))
return ELEMENT_INCOMPATIBLE
worn_state = _worn_state
alt_worn = _alt_worn
right_hand = _right_hand
left_hand = _left_hand
inv_slots = _inv_slots
proctype = _proctype
RegisterSignal(target, COMSIG_CLICK_ALT, .proc/mob_try_pickup)
RegisterSignal(target, COMSIG_PARENT_EXAMINE, .proc/on_examine)
/datum/element/mob_holder/Detach(datum/source, force)
. = ..()
UnregisterSignal(source, COMSIG_CLICK_ALT)
UnregisterSignal(source, COMSIG_PARENT_EXAMINE)
/datum/element/mob_holder/proc/on_examine(mob/living/source, mob/user, list/examine_list)
if(ishuman(user) && !istype(source.loc, /obj/item/clothing/head/mob_holder))
examine_list += "<span class='notice'>Looks like [source.p_they(TRUE)] can be picked up with <b>Alt+Click</b>!</span>"
/datum/element/mob_holder/proc/mob_try_pickup(mob/living/source, mob/user)
if(!ishuman(user) || !user.Adjacent(source) || user.incapacitated())
return FALSE
if(user.get_active_held_item())
to_chat(user, "<span class='warning'>Your hands are full!</span>")
return FALSE
if(source.buckled)
to_chat(user, "<span class='warning'>[source] is buckled to something!</span>")
return FALSE
if(source == user)
to_chat(user, "<span class='warning'>You can't pick yourself up.</span>")
return FALSE
source.visible_message("<span class='warning'>[user] starts picking up [source].</span>", \
"<span class='userdanger'>[user] starts picking you up!</span>")
if(!do_after(user, 20, target = source) || source.buckled)
return FALSE
source.visible_message("<span class='warning'>[user] picks up [source]!</span>", \
"<span class='userdanger'>[user] picks you up!</span>")
to_chat(user, "<span class='notice'>You pick [source] up.</span>")
source.drop_all_held_items()
var/obj/item/clothing/head/mob_holder/holder = new(get_turf(source), source, worn_state, alt_worn, right_hand, left_hand, inv_slots)
if(proctype)
INVOKE_ASYNC(src, proctype, source, holder, user)
user.put_in_hands(holder)
return TRUE
/datum/element/mob_holder/proc/drone_worn_icon(mob/living/simple_animal/drone/D, obj/item/clothing/head/mob_holder/holder, mob/user)
var/new_state = "[D.visualAppearence]_hat"
holder.item_state = new_state
holder.icon_state = new_state
//The item itself,
/obj/item/clothing/head/mob_holder
name = "bugged mob"
desc = "Yell at coderbrush."
icon = null
alternate_worn_icon = 'icons/mob/animals_held.dmi'
righthand_file = 'icons/mob/animals_held_rh.dmi'
lefthand_file = 'icons/mob/animals_held_lh.dmi'
icon_state = ""
w_class = WEIGHT_CLASS_BULKY
var/mob/living/held_mob
/obj/item/clothing/head/mob_holder/Initialize(mapload, mob/living/target, worn_state, alt_worn, right_hand, left_hand, slots = NONE)
. = ..()
if(target)
assimilate(target)
if(alt_worn)
alternate_worn_icon = alt_worn
if(worn_state)
item_state = worn_state
icon_state = worn_state
if(left_hand)
lefthand_file = left_hand
if(right_hand)
righthand_file = right_hand
slot_flags = slots
/obj/item/clothing/head/mob_holder/proc/assimilate(mob/living/target)
target.setDir(SOUTH)
held_mob = target
target.forceMove(src)
var/image/I = new //work around to retain the same appearance to the mob idependently from inhands/worn states.
I.appearance = target.appearance
I.layer = FLOAT_LAYER //So it doesn't get screwed up by layer overrides.
I.plane = FLOAT_PLANE //Same as above but for planes.
I.override = TRUE
add_overlay(I)
name = target.name
desc = target.desc
switch(target.mob_size)
if(MOB_SIZE_TINY)
w_class = WEIGHT_CLASS_TINY
if(MOB_SIZE_SMALL)
w_class = WEIGHT_CLASS_NORMAL
if(MOB_SIZE_LARGE)
w_class = WEIGHT_CLASS_HUGE
/obj/item/clothing/head/mob_holder/Destroy()
if(held_mob)
release()
return ..()
/obj/item/clothing/head/mob_holder/examine(mob/user)
return held_mob?.examine(user) || ..()
/obj/item/clothing/head/mob_holder/Exited(atom/movable/AM, atom/newloc)
. = ..()
if(AM == held_mob)
held_mob.reset_perspective()
held_mob = null
QDEL_IN(src, 1) //To avoid a qdel loop.
/obj/item/clothing/head/mob_holder/Entered(atom/movable/AM, atom/newloc)
. = ..()
if(AM != held_mob)
var/destination = loc
if(isliving(loc)) //the mob is held or worn, drop things on the floor
destination = get_turf(loc)
AM.forceMove(destination)
/obj/item/clothing/head/mob_holder/dropped()
. = ..()
if(held_mob && isturf(loc))//don't release on soft-drops
release()
/obj/item/clothing/head/mob_holder/proc/release()
if(held_mob)
var/mob/living/L = held_mob
held_mob = null
L.forceMove(get_turf(L))
L.reset_perspective()
L.setDir(SOUTH)
qdel(src)
/obj/item/clothing/head/mob_holder/relaymove(mob/user)
return
/obj/item/clothing/head/mob_holder/container_resist()
if(isliving(loc))
var/mob/living/L = loc
L.visible_message("<span class='warning'>[held_mob] escapes from [L]!</span>", "<span class='warning'>[held_mob] escapes your grip!</span>")
release()
/obj/item/clothing/head/mob_holder/assume_air(datum/gas_mixture/env)
var/atom/location = loc
if(!loc)
return //null
var/turf/T = get_turf(loc)
while(location != T)
location = location.loc
if(ismob(location))
return location.loc.assume_air(env)
return location.assume_air(env)
/obj/item/clothing/head/mob_holder/remove_air(amount)
var/atom/location = loc
if(!loc)
return //null
var/turf/T = get_turf(loc)
while(location != T)
location = location.loc
if(ismob(location))
return location.loc.remove_air(amount)
return location.remove_air(amount)
+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
@@ -1,6 +1,6 @@
/datum/element/wuv //D'awwwww
element_flags = ELEMENT_BESPOKE
element_flags = ELEMENT_BESPOKE|ELEMENT_DETACH
id_arg_index = 2
//the for the me emote proc call when petted.
var/pet_emote
@@ -30,6 +30,10 @@
RegisterSignal(target, COMSIG_MOB_ATTACK_HAND, .proc/on_attack_hand)
/datum/element/wuv/Detach(datum/source, force)
. = ..()
UnregisterSignal(source, COMSIG_MOB_ATTACK_HAND)
/datum/element/wuv/proc/on_attack_hand(datum/source, mob/user)
var/mob/living/L = source
+4 -2
View File
@@ -27,7 +27,8 @@
for(var/line in testmerge)
var/datum/tgs_revision_information/test_merge/tm = line
msg += "Test merge active of PR #[tm.number] commit [tm.commit]"
msg += "Test merge active of PR #[tm.number] commit [tm.pull_request_commit]"
SSblackbox.record_feedback("associative", "testmerged_prs", 1, list("number" = "[tm.number]", "commit" = "[tm.pull_request_commit]", "title" = "[tm.title]", "author" = "[tm.author]"))
if(commit && commit != originmastercommit)
msg += "HEAD: [commit]"
@@ -75,7 +76,8 @@
else if(!pc)
msg += "No commit information"
if(world.TgsAvailable())
msg += "Server tools version: [world.TgsVersion()]"
var/datum/tgs_version/version = world.TgsVersion()
msg += "Server tools version: [version.raw_parameter]"
// Game mode odds
msg += "<br><b>Current Informational Settings:</b>"
-62
View File
@@ -1,62 +0,0 @@
/datum/topic_input
var/href
var/list/href_list
/datum/topic_input/New(thref,list/thref_list)
href = thref
href_list = thref_list.Copy()
return
/datum/topic_input/proc/get(i)
return listgetindex(href_list,i)
/datum/topic_input/proc/getAndLocate(i)
var/t = get(i)
if(t)
t = locate(t)
if (istext(t))
t = null
return t || null
/datum/topic_input/proc/getNum(i)
var/t = get(i)
if(t)
t = text2num(t)
return isnum(t) ? t : null
/datum/topic_input/proc/getObj(i)
var/t = getAndLocate(i)
return isobj(t) ? t : null
/datum/topic_input/proc/getMob(i)
var/t = getAndLocate(i)
return ismob(t) ? t : null
/datum/topic_input/proc/getTurf(i)
var/t = getAndLocate(i)
return isturf(t) ? t : null
/datum/topic_input/proc/getAtom(i)
return getType(i, /atom)
/datum/topic_input/proc/getArea(i)
var/t = getAndLocate(i)
return isarea(t) ? t : null
/datum/topic_input/proc/getStr(i)//params should always be text, but...
var/t = get(i)
return istext(t) ? t : null
/datum/topic_input/proc/getType(i,type)
var/t = getAndLocate(i)
return istype(t,type) ? t : null
/datum/topic_input/proc/getPath(i)
var/t = get(i)
if(t)
t = text2path(t)
return ispath(t) ? t : null
/datum/topic_input/proc/getList(i)
var/t = getAndLocate(i)
return islist(t) ? t : null
+6 -6
View File
@@ -12,7 +12,12 @@
/mob/camera/aiEye/remote/holo/setLoc()
. = ..()
var/obj/machinery/holopad/H = origin
H.move_hologram(eye_user, loc)
H?.move_hologram(eye_user, loc)
/obj/machinery/holopad/remove_eye_control(mob/living/user)
if(user.client)
user.reset_perspective(null)
user.remote_control = null
//this datum manages it's own references
@@ -54,11 +59,6 @@
/datum/holocall/Destroy()
QDEL_NULL(hangup)
var/user_good = !QDELETED(user)
if(user_good)
user.reset_perspective()
user.remote_control = null
if(!QDELETED(eye))
QDEL_NULL(eye)
+1 -1
View File
@@ -32,7 +32,7 @@ GLOBAL_LIST_INIT(huds, list(
/datum/atom_hud
var/list/atom/hudatoms = list() //list of all atoms which display this hud
var/list/mob/hudusers = list() //list with all mobs who can see the hud
var/list/hudusers = list() //list with all mobs who can see the hud
var/list/hud_icons = list() //these will be the indexes for the atom's hud_list
var/list/next_time_allowed = list() //mobs associated with the next time this hud can be added to them
+6 -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)
@@ -458,6 +457,7 @@
var/list/allowed_types = list(
/datum/objective/assassinate,
/datum/objective/assassinate/once,
/datum/objective/maroon,
/datum/objective/debrain,
/datum/objective/protect,
@@ -714,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)
@@ -201,51 +201,52 @@
/datum/mood_event/vampcandle
description = "<span class='umbra'>Something is making your mind feel... loose...</span>\n"
mood_change = -10
mood_change = -15
timeout = 1 MINUTES
/datum/mood_event/drankblood_bad
description = "<span class='boldwarning'>I drank the blood of a lesser creature. Disgusting.</span>\n"
mood_change = -4
timeout = 900
timeout = 8 MINUTES
/datum/mood_event/drankblood_dead
description = "<span class='boldwarning'>I drank dead blood. I am better than this.</span>\n"
mood_change = -7
timeout = 900
timeout = 10 MINUTES
/datum/mood_event/drankblood_synth
description = "<span class='boldwarning'>I drank synthetic blood. What is wrong with me?</span>\n"
mood_change = -7
timeout = 900
timeout = 15 MINUTES
/datum/mood_event/drankkilled
description = "<span class='boldwarning'>I drank from my victim until they died. I feel...less human.</span>\n"
mood_change = -12
timeout = 6000
timeout = 25 MINUTES
/datum/mood_event/madevamp
description = "<span class='boldwarning'>A soul has been cursed to undeath by my own hand.</span>\n"
mood_change = -10
timeout = 10000
timeout = 30 MINUTES
/datum/mood_event/vampatefood
description = "<span class='boldwarning'>Mortal nourishment no longer sustains me. I feel unwell.</span>\n"
mood_change = -6
timeout = 1000
timeout = 10 MINUTES
/datum/mood_event/daylight_1
description = "<span class='boldwarning'>I slept poorly in a makeshift coffin during the day.</span>\n"
mood_change = -3
timeout = 1000
timeout = 10 MINUTES
/datum/mood_event/nanite_sadness
description = "<span class='warning robot'>+++++++HAPPINESS SUPPRESSION+++++++</span>\n"
mood_change = -7
/datum/mood_event/daylight_2
description = "<span class='boldwarning'>I have been scorched by the unforgiving rays of the sun.</span>\n"
mood_change = -6
timeout = 1200
timeout = 15 MINUTES
/datum/mood_event/nanite_sadness
description = "<span class='warning robot'>+++++++HAPPINESS SUPPRESSION+++++++</span>\n"
mood_change = -7
/datum/mood_event/nanite_sadness/add_effects(message)
description = "<span class='warning robot'>+++++++[message]+++++++</span>\n"
+5 -5
View File
@@ -37,22 +37,22 @@
/datum/map_template/ruin/station/box/engine/teg
id = "engine_teg"
suffix = "Box/Engine/engine_tesla.dmm"
suffix = "Box/Engine/engine_teg.dmm"
name = "Box TEG"
/datum/map_template/ruin/station/box/engine/empty
id = "engine_empty"
suffix = "Box/Engine/engine_tesla.dmm"
suffix = "Box/Engine/empty.dmm"
name = "Box Empty"
/datum/map_template/ruin/station/box/engine/am
id = "engine_am"
suffix = "Box/Engine/engine_tesla.dmm"
suffix = "Box/Engine/engine_am.dmm"
name = "Box Antimatter"
/datum/map_template/ruin/station/box/engine/budget
id = "engine_budget"
suffix = "Box/Engine/engine_tesla.dmm"
suffix = "Box/Engine/budget.dmm"
name = "Box P.A.C.M.A.N"
// Lavaland
@@ -60,4 +60,4 @@
/datum/map_template/ruin/station/lavaland/mining_base
id = "mining_public_01"
suffix = "Lavaland/Mining_Station/Mining_Station_Public_01.dmm"
name = "Public Mining Base"
name = "Public Mining Base"
+41 -19
View File
@@ -33,7 +33,6 @@
if(owner.getStaminaLoss())
owner.adjustStaminaLoss(-0.3) //reduce stamina loss by 0.3 per tick, 6 per 2 seconds
//UNCONSCIOUS
/datum/status_effect/incapacitating/unconscious
id = "unconscious"
@@ -80,11 +79,11 @@
desc = "You've fallen asleep. Wait a bit and you should wake up. Unless you don't, considering how helpless you are."
icon_state = "asleep"
/datum/status_effect/no_combat_mode/
/datum/status_effect/no_combat_mode
id = "no_combat_mode"
blocks_combatmode = TRUE
alert_type = null
status_type = STATUS_EFFECT_REPLACE
blocks_combatmode = TRUE
/datum/status_effect/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration))
@@ -113,31 +112,54 @@
icon = 'icons/mob/actions/bloodsucker.dmi'
icon_state = "power_mez"
/datum/status_effect/no_combat_mode/electrode
/datum/status_effect/electrode
id = "tased"
var/slowdown = 1.5
var/slowdown_priority = 50 //to make sure the stronger effect overrides
var/affect_crawl = FALSE
var/nextmove_modifier = 1
var/stamdmg_per_ds = 1 //a 20 duration would do 20 stamdmg, disablers do 24 or something
var/last_tick = 0 //fastprocess processing speed is a goddamn sham, don't trust it.
/datum/status_effect/no_combat_mode/electrode/on_creation(mob/living/new_owner, set_duration)
/datum/status_effect/electrode/on_creation(mob/living/new_owner, set_duration)
if(isnum(set_duration)) //TODO, figure out how to grab from subtype
duration = set_duration
. = ..()
last_tick = world.time
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
C.add_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]", TRUE, priority = slowdown_priority, override = TRUE, multiplicative_slowdown = slowdown, blacklisted_movetypes = affect_crawl? NONE : CRAWLING)
/datum/status_effect/electrode/on_remove()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.remove_movespeed_modifier("[MOVESPEED_ID_TASED_STATUS]_[id]")
. = ..()
/datum/status_effect/electrode/tick()
var/diff = world.time - last_tick
if(owner)
owner.adjustStaminaLoss(max(0, stamdmg_per_ds * diff)) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
last_tick = world.time
/datum/status_effect/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
return nextmove_modifier
/datum/status_effect/electrode/no_combat_mode
id = "tased_strong"
slowdown = 8
slowdown_priority = 100
nextmove_modifier = 2
blocks_combatmode = TRUE
/datum/status_effect/electrode/no_combat_mode/on_creation(mob/living/new_owner, set_duration)
. = ..()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
if(C.combatmode)
C.toggle_combat_mode(TRUE)
C.add_movespeed_modifier(MOVESPEED_ID_TASED_STATUS, TRUE, override = TRUE, multiplicative_slowdown = 8)
/datum/status_effect/no_combat_mode/electrode/on_remove()
if(iscarbon(owner))
var/mob/living/carbon/C = owner
C.remove_movespeed_modifier(MOVESPEED_ID_TASED_STATUS)
. = ..()
/datum/status_effect/no_combat_mode/electrode/tick()
if(owner)
owner.adjustStaminaLoss(5) //if you really want to try to stamcrit someone with a taser alone, you can, but it'll take time and good timing.
/datum/status_effect/no_combat_mode/electrode/nextmove_modifier() //why is this a proc. its no big deal since this doesnt get called often at all but literally w h y
return 2
//OTHER DEBUFFS
/datum/status_effect/his_wrath //does minor damage over time unless holding His Grace
+1
View File
@@ -8,6 +8,7 @@
var/gain_text
var/lose_text
var/medical_record_text //This text will appear on medical records for the trait. Not yet implemented
var/antag_removal_text // Text will be given to the quirk holder if they get an antag that has it blacklisted.
var/mood_quirk = FALSE //if true, this quirk affects mood and is unavailable if moodlets are disabled
var/mob_trait //if applicable, apply and remove this mob trait
var/mob/living/quirk_holder
+8 -13
View File
@@ -6,6 +6,7 @@
value = -2
gain_text = "<span class='danger'>You feel your vigor slowly fading away.</span>"
lose_text = "<span class='notice'>You feel vigorous again.</span>"
antag_removal_text = "Your antagonistic nature has removed your blood deficiency."
medical_record_text = "Patient requires regular treatment for blood loss due to low production of blood."
/datum/quirk/blooddeficiency/on_process()
@@ -38,6 +39,8 @@
var/obj/item/heirloom
var/where
GLOBAL_LIST_EMPTY(family_heirlooms)
/datum/quirk/family_heirloom/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/heirloom_type
@@ -76,6 +79,7 @@
/obj/item/lighter,
/obj/item/dice/d20)
heirloom = new heirloom_type(get_turf(quirk_holder))
GLOB.family_heirlooms += heirloom
var/list/slots = list(
"in your left pocket" = SLOT_L_STORE,
"in your right pocket" = SLOT_R_STORE,
@@ -140,9 +144,8 @@
/datum/quirk/nearsighted/on_spawn()
var/mob/living/carbon/human/H = quirk_holder
var/obj/item/clothing/glasses/regular/glasses = new(get_turf(H))
H.put_in_hands(glasses)
H.equip_to_slot(glasses, SLOT_GLASSES)
H.regenerate_icons() //this is to remove the inhand icon, which persists even if it's not in their hands
if(!H.equip_to_slot_if_possible(glasses, SLOT_GLASSES))
H.put_in_hands(glasses)
/datum/quirk/nyctophobia
name = "Nyctophobia"
@@ -188,11 +191,7 @@
gain_text = "<span class='danger'>You feel repulsed by the thought of violence!</span>"
lose_text = "<span class='notice'>You think you can defend yourself again.</span>"
medical_record_text = "Patient is unusually pacifistic and cannot bring themselves to cause physical harm."
/datum/quirk/nonviolent/on_process()
if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
to_chat(quirk_holder, "<span class='boldannounce'>Your antagonistic nature has caused you to renounce your pacifism.</span>")
qdel(src)
antag_removal_text = "Your antagonistic nature has caused you to renounce your pacifism."
/datum/quirk/paraplegic
name = "Paraplegic"
@@ -355,6 +354,7 @@
gain_text = "<span class='danger'>You find yourself unable to speak!</span>"
lose_text = "<span class='notice'>You feel a growing strength in your vocal chords.</span>"
medical_record_text = "Functionally mute, patient is unable to use their voice in any capacity."
antag_removal_text = "Your antagonistic nature has caused your voice to be heard."
var/datum/brain_trauma/severe/mute/mute
/datum/quirk/mute/add()
@@ -366,11 +366,6 @@
var/mob/living/carbon/human/H = quirk_holder
H?.cure_trauma_type(mute, TRAUMA_RESILIENCE_ABSOLUTE)
/datum/quirk/mute/on_process()
if(quirk_holder.mind && LAZYLEN(quirk_holder.mind.antag_datums))
to_chat(quirk_holder, "<span class='boldannounce'>Your antagonistic nature has caused your voice to be heard.</span>")
qdel(src)
/datum/quirk/unstable
name = "Unstable"
desc = "Due to past troubles, you are unable to recover your sanity if you lose it. Be very careful managing your mood!"
+9 -1
View File
@@ -61,7 +61,7 @@
if(A.z in impacted_z_levels)
impacted_areas |= A
weather_duration = rand(weather_duration_lower, weather_duration_upper)
START_PROCESSING(SSweather, src)
START_PROCESSING(SSweather, src) //The reason this doesn't start and stop at main stage is because processing list is also used to see active running weathers (for example, you wouldn't want two ash storms starting at once.)
update_areas()
for(var/M in GLOB.player_list)
var/turf/mob_turf = get_turf(M)
@@ -107,6 +107,14 @@
STOP_PROCESSING(SSweather, src)
update_areas()
/datum/weather/process()
if(aesthetic || (stage != MAIN_STAGE))
return
for(var/i in GLOB.mob_living_list)
var/mob/living/L = i
if(can_weather_act(L))
weather_act(L)
/datum/weather/proc/can_weather_act(mob/living/L) //Can this weather impact a mob?
var/turf/mob_turf = get_turf(L)
if(mob_turf && !(mob_turf.z in impacted_z_levels))
+1 -1
View File
@@ -52,7 +52,7 @@
/datum/wires/airlock/interactable(mob/user)
var/obj/machinery/door/airlock/A = holder
if(!issilicon(user) && A.isElectrified() && A.shock(user, 100))
if(!A.hasSiliconAccessInArea(user) && A.isElectrified() && A.shock(user, 100))
return FALSE
if(A.panel_open)
return TRUE
+1 -1
View File
@@ -12,7 +12,7 @@
/datum/wires/vending/interactable(mob/user)
var/obj/machinery/vending/V = holder
if(!issilicon(user) && V.seconds_electrified && V.shock(user, 100))
if(!V.hasSiliconAccessInArea(user) && V.seconds_electrified && V.shock(user, 100))
return FALSE
if(V.panel_open)
return TRUE