Merge remote-tracking branch 'refs/remotes/Citadel-Station-13/master' into muzzled-masks

This commit is contained in:
Poojawa
2019-04-06 02:27:58 -05:00
236 changed files with 3743 additions and 1227 deletions
+2 -2
View File
@@ -133,14 +133,14 @@
return FALSE
/datum/ntnet/proc/log_data_transfer(datum/netdata/data)
logs += "[station_time_timestamp()] - [data.generate_netlog()]"
logs += "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - [data.generate_netlog()]"
if(logs.len > setting_maxlogcount)
logs = logs.Copy(logs.len - setting_maxlogcount, 0)
return
// Simplified logging: Adds a log. log_string is mandatory parameter, source is optional.
/datum/ntnet/proc/add_log(log_string, obj/item/computer_hardware/network_card/source = null)
var/log_text = "[station_time_timestamp()] - "
var/log_text = "[STATION_TIME_TIMESTAMP("hh:mm:ss")] - "
if(source)
log_text += "[source.get_network_tag()] - "
else
+10 -1
View File
@@ -538,13 +538,22 @@
message_admins("[key_name_admin(usr)] toggled OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleooclocal()
set category = "Server"
set desc="Toggle dat bitch"
set name="Toggle Local OOC"
toggle_looc()
log_admin("[key_name(usr)] toggled LOOC.")
message_admins("[key_name_admin(usr)] toggled LOOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Local OOC", "[GLOB.ooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/datum/admins/proc/toggleoocdead()
set category = "Server"
set desc="Toggle dis bitch"
set name="Toggle Dead OOC"
toggle_dooc()
log_admin("[key_name(usr)] toggled OOC.")
log_admin("[key_name(usr)] toggled Dead OOC.")
message_admins("[key_name_admin(usr)] toggled Dead OOC.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Dead OOC", "[GLOB.dooc_allowed ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
+1 -1
View File
@@ -2,7 +2,7 @@
if(!message || !subject)
return
var/F = file("[GLOB.log_directory]/[subject].html")
WRITE_FILE(F, "<small>[time_stamp()] [REF(src)] ([x],[y],[z])</small> || [src] [message]<br>")
WRITE_FILE(F, "<small>[TIME_STAMP("hh:mm:ss", FALSE)] [REF(src)] ([x],[y],[z])</small> || [src] [message]<br>")
/client/proc/investigate_show(subject in list("notes, memos, watchlist", INVESTIGATE_RESEARCH, INVESTIGATE_EXONET, INVESTIGATE_PORTAL, INVESTIGATE_SINGULO, INVESTIGATE_WIRES, INVESTIGATE_TELESCI, INVESTIGATE_GRAVITY, INVESTIGATE_RECORDS, INVESTIGATE_CARGO, INVESTIGATE_SUPERMATTER, INVESTIGATE_ATMOS, INVESTIGATE_EXPERIMENTOR, INVESTIGATE_BOTANY, INVESTIGATE_HALLUCINATIONS, INVESTIGATE_RADIATION, INVESTIGATE_CIRCUIT, INVESTIGATE_NANITES) )
set name = "Investigate"
+1
View File
@@ -24,6 +24,7 @@ GLOBAL_LIST_INIT(admin_verbs_admin, world.AVerbsAdmin())
/client/proc/game_panel, /*game panel, allows to change game-mode etc*/
/client/proc/check_ai_laws, /*shows AI and borg laws*/
/datum/admins/proc/toggleooc, /*toggles ooc on/off for everyone*/
/datum/admins/proc/toggleooclocal, /*toggles looc on/off for everyone*/
/datum/admins/proc/toggleoocdead, /*toggles ooc on/off for everyone who is dead*/
/datum/admins/proc/toggleaooc, /*toggles antag ooc on/off*/
/datum/admins/proc/toggleenter, /*toggles whether people can join the current game*/
+3 -3
View File
@@ -214,7 +214,7 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
if(heard_by_no_admins && usr && usr.ckey != initiator_ckey)
heard_by_no_admins = FALSE
send2irc(initiator_ckey, "Ticket #[id]: Answered by [key_name(usr)]")
_interactions += "[time_stamp()]: [formatted_message]"
_interactions += "[TIME_STAMP("hh:mm:ss", FALSE)]: [formatted_message]"
//Removes the ahelp verb and returns it after 2 minutes
/datum/admin_help/proc/TimeoutVerb()
@@ -416,9 +416,9 @@ GLOBAL_DATUM_INIT(ahelp_tickets, /datum/admin_help_tickets, new)
dat += "</b>[GLOB.TAB][TicketHref("Refresh", ref_src)][GLOB.TAB][TicketHref("Re-Title", ref_src, "retitle")]"
if(state != AHELP_ACTIVE)
dat += "[GLOB.TAB][TicketHref("Reopen", ref_src, "reopen")]"
dat += "<br><br>Opened at: [gameTimestamp(wtime = opened_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
dat += "<br><br>Opened at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - opened_at)] ago)"
if(closed_at)
dat += "<br>Closed at: [gameTimestamp(wtime = closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
dat += "<br>Closed at: [GAMETIMESTAMP("hh:mm:ss", closed_at)] (Approx [DisplayTimeText(world.time - closed_at)] ago)"
dat += "<br><br>"
if(initiator)
dat += "<b>Actions:</b> [FullMonty(ref_src)]<br>"
+5 -3
View File
@@ -13,6 +13,7 @@
if (new_pb && !SSdbcore.Connect())
message_admins("The Database is not connected! Panic bunker will not work until the connection is reestablished.")
SSblackbox.record_feedback("nested tally", "admin_toggle", 1, list("Toggle Panic Bunker", "[new_pb ? "Enabled" : "Disabled"]")) //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
send2irc("Panic Bunker", "[key_name(usr)] has toggled the Panic Bunker, it is now [new_pb ? "enabled" : "disabled"].")
/client/proc/addbunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -24,7 +25,8 @@
GLOB.bunker_passthrough |= ckey(ckeytobypass)
log_admin("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
message_admins("[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has added [ckeytobypass] to the current round's bunker bypass list.")
/client/proc/revokebunkerbypass(ckeytobypass as text)
set category = "Special Verbs"
@@ -36,5 +38,5 @@
GLOB.bunker_passthrough -= ckey(ckeytobypass)
log_admin("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
message_admins("[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
message_admins("[key_name_admin(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
send2irc("Panic Bunker", "[key_name(usr)] has removed [ckeytobypass] from the current round's bunker bypass list.")
@@ -72,7 +72,7 @@
name = "Cellular Emporium"
icon_icon = 'icons/obj/drinks.dmi'
button_icon_state = "changelingsting"
background_icon_state = "bg_alien"
background_icon_state = "bg_ling"
var/datum/cellular_emporium/cellular_emporium
/datum/action/innate/cellular_emporium/New(our_target)
@@ -20,10 +20,12 @@
/obj/effect/proc_holder/changeling/proc/on_purchase(mob/user, is_respec)
action.Grant(user)
if(!is_respec)
SSblackbox.record_feedback("tally", "changeling_power_purchase", 1, name)
/obj/effect/proc_holder/changeling/proc/on_refund(mob/user)
action.Remove(user)
return
/obj/effect/proc_holder/changeling/Click()
@@ -4,6 +4,9 @@
chemical_cost = 0
dna_cost = 0
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_absorb_dna"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/absorbDNA/can_sting(mob/living/carbon/user)
if(!..())
@@ -6,6 +6,9 @@
dna_cost = 2
req_human = 1
req_stat = UNCONSCIOUS
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_adrenals"
action_background_icon_state = "bg_ling"
//Recover from stuns.
/obj/effect/proc_holder/changeling/adrenaline/sting_action(mob/living/user)
@@ -8,12 +8,16 @@
chemical_cost = 0
dna_cost = 2 //Would be 1 without thermal vision
active = FALSE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_augmented_eyesight"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/augmented_eyesight/on_purchase(mob/user) //The ability starts inactive, so we should be protected from flashes.
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if (E)
E.flash_protect = 2 //Adjust the user's eyes' flash protection
to_chat(user, "We adjust our eyes to protect them from bright lights.")
action.Grant(user)
else
to_chat(user, "We can't adjust our eyes if we don't have any!")
@@ -42,6 +46,7 @@
/obj/effect/proc_holder/changeling/augmented_eyesight/on_refund(mob/user) //Get rid of X-ray vision and flash protection when the user refunds this ability
action.Remove(user)
var/obj/item/organ/eyes/E = user.getorganslot(ORGAN_SLOT_EYES)
if(E)
if (active)
@@ -6,6 +6,9 @@
loudness = 1
dna_cost = 2
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_freedom"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/biodegrade/sting_action(mob/living/carbon/human/user)
var/used = FALSE // only one form of shackles removed per use
@@ -5,6 +5,9 @@
dna_cost = 2
chemical_cost = 25
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_camouflage"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/chameleon_skin/sting_action(mob/user)
var/mob/living/carbon/human/H = user //SHOULD always be human, because req_human = 1
@@ -18,6 +21,7 @@
return TRUE
/obj/effect/proc_holder/changeling/chameleon_skin/on_refund(mob/user)
action.Remove(user)
if(user.has_dna())
var/mob/living/carbon/C = user
var/datum/mutation/human/HM = GLOB.mutations_list[CHAMELEON]
@@ -4,6 +4,9 @@
helptext = "We cannot be tracked by camera or seen by AI units while using this skill. However, humans looking at us will find us... uncanny. This ability is somewhat loud, and carries a small risk of our blood gaining violent sensitivity to heat."
dna_cost = 1
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_digital_camo"
action_background_icon_state = "bg_ling"
//Prevents AIs tracking you but makes you easily detectable to the human-eye.
/obj/effect/proc_holder/changeling/digitalcamo/sting_action(mob/user)
@@ -19,5 +22,6 @@
return TRUE
/obj/effect/proc_holder/changeling/digitalcamo/on_refund(mob/user)
action.Remove(user)
user.digitalcamo = 0
user.digitalinvis = 0
@@ -6,13 +6,16 @@
req_dna = 1
req_stat = DEAD
ignores_fakedeath = TRUE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_regenerative_stasis"
action_background_icon_state = "bg_ling"
//Fake our own death and fully heal. You will appear to be dead but regenerate fully after a short delay.
/obj/effect/proc_holder/changeling/fakedeath/sting_action(mob/living/user)
to_chat(user, "<span class='notice'>We begin our stasis, preparing energy to arise once more.</span>")
if(user.stat != DEAD)
user.emote("deathgasp")
user.tod = station_time_timestamp()
user.tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
user.fakedeath("changeling") //play dead
user.update_stat()
user.update_canmove()
@@ -25,7 +28,9 @@
var/datum/antagonist/changeling/C = user.mind.has_antag_datum(/datum/antagonist/changeling)
if(C && C.purchasedpowers)
to_chat(user, "<span class='notice'>We are ready to revive.</span>")
C.purchasedpowers += new /obj/effect/proc_holder/changeling/revive(null)
var/obj/effect/proc_holder/changeling/revive/RV = new /obj/effect/proc_holder/changeling/revive(null)
C.purchasedpowers += RV
RV.action.Grant(user)
/obj/effect/proc_holder/changeling/fakedeath/can_sting(mob/living/user)
if(user.has_trait(TRAIT_DEATHCOMA, "changeling"))
@@ -5,6 +5,9 @@
chemical_cost = 20
dna_cost = 2
req_stat = UNCONSCIOUS
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_fleshmend"
action_background_icon_state = "bg_ling"
//Starts healing you every second for 10 seconds.
//Can be used whilst unconscious.
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 2
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_explode"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/headcrab/sting_action(mob/user)
set waitfor = FALSE
@@ -5,6 +5,16 @@
helptext = "We will be able to talk with other changelings with :g. Exchanged DNA do not count towards absorb objectives."
dna_cost = 1
chemical_cost = -1
action_icon = 'icons/mob/actions/actions_xeno.dmi'
action_icon_state = "alien_whisper"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/hivemind_comms/sting_action(var/mob/living/user)
if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
to_chat(user, "<span class='warning'>The poison in the air hinders our ability to interact with the hivemind.</span>")
return
var/input = stripped_input(usr, "Please choose a message to transmit.", "Changeling Hivemind", "")
user.say(".g[input]")
/obj/effect/proc_holder/changeling/hivemind_comms/on_purchase(mob/user, is_respec)
..()
@@ -14,12 +24,15 @@
var/obj/effect/proc_holder/changeling/hivemind_upload/S1 = new
if(!changeling.has_sting(S1))
changeling.purchasedpowers+=S1
S1.action.Grant(user)
var/obj/effect/proc_holder/changeling/hivemind_download/S2 = new
if(!changeling.has_sting(S2))
changeling.purchasedpowers+=S2
S2.action.Grant(user)
var/obj/effect/proc_holder/changeling/linglink/S3 = new
if(!changeling.has_sting(S3))
changeling.purchasedpowers+=S3
S3.action.Grant(user)
// HIVE MIND UPLOAD/DOWNLOAD DNA
GLOBAL_LIST_EMPTY(hivemind_bank)
@@ -29,6 +42,9 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
desc = "Allows us to channel DNA in the airwaves to allow other changelings to absorb it."
chemical_cost = 10
dna_cost = -1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_upload"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/hivemind_upload/sting_action(var/mob/living/user)
if (user.has_trait(CHANGELING_HIVEMIND_MUTE))
@@ -63,6 +79,9 @@ GLOBAL_LIST_EMPTY(hivemind_bank)
desc = "Allows us to absorb DNA that has been channeled to the airwaves. Does not count towards absorb objectives."
chemical_cost = 10
dna_cost = -1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_download"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/hivemind_download/can_sting(mob/living/carbon/user)
if(!..())
@@ -3,6 +3,9 @@
desc = "We change into a human."
chemical_cost = 5
req_dna = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_human"
action_background_icon_state = "bg_ling"
//Transform into a human.
/obj/effect/proc_holder/changeling/humanform/sting_action(mob/living/carbon/user)
@@ -5,6 +5,9 @@
dna_cost = 1
loudness = 2
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_lesser"
action_background_icon_state = "bg_ling"
//Transform into a monkey.
/obj/effect/proc_holder/changeling/lesserform/sting_action(mob/living/carbon/human/user)
@@ -4,6 +4,9 @@
chemical_cost = 0
dna_cost = -1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_link"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/linglink/can_sting(mob/living/carbon/user)
if(!..())
@@ -5,6 +5,9 @@
chemical_cost = 0 //constant chemical drain hardcoded
dna_cost = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_mimic_voice"
action_background_icon_state = "bg_ling"
// Fake Voice
@@ -56,6 +56,7 @@
return W
/obj/effect/proc_holder/changeling/weapon/on_refund(mob/user)
action.Remove(user)
for(var/obj/item/I in user.held_items)
check_weapon(user, I)
@@ -105,6 +106,7 @@
/obj/effect/proc_holder/changeling/suit/on_refund(mob/user)
if(!ishuman(user))
return
action.Remove(user)
var/mob/living/carbon/human/H = user
check_suit(H)
@@ -141,6 +143,9 @@
req_human = 1
weapon_type = /obj/item/melee/arm_blade
weapon_name_simple = "blade"
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armblade"
action_background_icon_state = "bg_ling"
/obj/item/melee/arm_blade
name = "arm blade"
@@ -225,6 +230,9 @@
weapon_type = /obj/item/gun/magic/tentacle
weapon_name_simple = "tentacle"
silent = TRUE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_tentacle"
action_background_icon_state = "bg_ling"
/obj/item/gun/magic/tentacle
name = "tentacle"
@@ -401,6 +409,9 @@
dna_cost = 1
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_shield"
action_background_icon_state = "bg_ling"
weapon_type = /obj/item/shield/changeling
weapon_name_simple = "shield"
@@ -454,6 +465,9 @@
dna_cost = 2
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_space_suit"
action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/space/changeling
helmet_type = /obj/item/clothing/head/helmet/space/changeling
@@ -503,6 +517,9 @@
loudness = 2
req_human = 1
recharge_slowdown = 0.25
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_armor"
action_background_icon_state = "bg_ling"
suit_type = /obj/item/clothing/suit/armor/changeling
helmet_type = /obj/item/clothing/head/helmet/changeling
@@ -5,6 +5,9 @@
chemical_cost = 20
dna_cost = 1
req_stat = UNCONSCIOUS
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_anatomic_panacea"
action_background_icon_state = "bg_ling"
//Heals the things that the other regenerative abilities don't.
/obj/effect/proc_holder/changeling/panacea/sting_action(mob/user)
@@ -10,6 +10,9 @@
chemical_cost = 0 //Reduces regain rate while active.
dna_cost = 2
var/receptors_active = FALSE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_pheromone"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/pheromone_receptors/sting_action(mob/living/carbon/user)
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
@@ -8,6 +8,9 @@
chemical_cost = 10
dna_cost = 0
req_stat = UNCONSCIOUS
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_regenerate"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/regenerate/sting_action(mob/living/user)
to_chat(user, "<span class='notice'>You feel an itching, both inside and \
@@ -5,6 +5,9 @@
req_stat = DEAD
always_keep = TRUE
ignores_fakedeath = TRUE
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_revive"
action_background_icon_state = "bg_ling"
//Revive from revival stasis
/obj/effect/proc_holder/changeling/revive/sting_action(mob/living/carbon/user)
@@ -26,6 +29,7 @@
to_chat(user, "<span class='notice'>We have revived ourselves.</span>")
var/datum/antagonist/changeling/changeling = user.mind.has_antag_datum(/datum/antagonist/changeling)
changeling.purchasedpowers -= src
src.action.Remove(user)
return TRUE
/obj/effect/proc_holder/changeling/revive/can_be_used_by(mob/living/user)
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_resonant"
action_background_icon_state = "bg_ling"
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/resonant_shriek/sting_action(mob/user)
@@ -36,6 +39,9 @@
chemical_cost = 20
dna_cost = 1
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_dissonant"
action_background_icon_state = "bg_ling"
//A flashy ability, good for crowd control and sewing chaos.
/obj/effect/proc_holder/changeling/dissonant_shriek/sting_action(mob/user)
@@ -6,6 +6,9 @@
dna_cost = 1
loudness = 4
req_absorbs = 3
action_icon = 'icons/effects/effects.dmi'
action_icon_state = "spiderling"
action_background_icon_state = "bg_ling"
//Makes some spiderlings. Good for setting traps and causing general trouble.
/obj/effect/proc_holder/changeling/spiders/sting_action(mob/user)
@@ -10,6 +10,9 @@
req_human = 1
var/stacks = 0 //Increments every 5 seconds; damage increases over time
active = 0 //Whether or not you are a hedgehog
action_icon = 'icons/obj/implants.dmi'
action_icon_state = "adrenal"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/strained_muscles/sting_action(mob/living/carbon/user)
active = !active
@@ -70,6 +70,9 @@
dna_cost = 3
loudness = 1
var/datum/changelingprofile/selected_dna = null
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_transform"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/transformation/Click()
var/mob/user = usr
@@ -117,6 +120,9 @@
chemical_cost = 20
dna_cost = 1
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_fake"
action_background_icon_state = "bg_ling"
/obj/item/melee/arm_blade/false
desc = "A grotesque mass of flesh that used to be your arm. Although it looks dangerous at first, you can tell it's actually quite dull and useless."
@@ -169,6 +175,9 @@
sting_icon = "sting_extract"
chemical_cost = 25
dna_cost = 0
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_extract"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/extract_dna/can_sting(mob/user, mob/target)
if(..())
@@ -190,6 +199,9 @@
chemical_cost = 20
dna_cost = 2
loudness = 2
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_mute"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/mute/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "mute sting")
@@ -204,6 +216,9 @@
chemical_cost = 25
dna_cost = 1
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_blind"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/blind/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "blind sting")
@@ -220,6 +235,9 @@
sting_icon = "sting_lsd"
chemical_cost = 10
dna_cost = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_lsd"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/LSD/sting_action(mob/user, mob/living/carbon/target)
log_combat(user, target, "stung", "LSD sting")
@@ -238,6 +256,9 @@
chemical_cost = 15
dna_cost = 2
loudness = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_sting_cryo"
action_background_icon_state = "bg_ling"
/obj/effect/proc_holder/changeling/sting/cryo/sting_action(mob/user, mob/target)
log_combat(user, target, "stung", "cryo sting")
@@ -5,6 +5,9 @@
dna_cost = 0
req_dna = 1
req_human = 1
action_icon = 'icons/mob/actions/actions_changeling.dmi'
action_icon_state = "ling_transform"
action_background_icon_state = "bg_ling"
/obj/item/clothing/glasses/changeling
name = "flesh"
+1 -1
View File
@@ -112,7 +112,7 @@ structure_check() searches for nearby cultist structures required for the invoca
var/list/invokers = list() //people eligible to invoke the rune
if(user)
invokers += user
if(req_cultists > 1 || istype(src, /obj/effect/rune/convert))
if(req_cultists > 1 || istype(src, /obj/effect/rune/narsie) || istype(src, /obj/effect/rune/convert))
var/list/things_in_range = range(1, src)
var/obj/item/toy/plush/narplush/plushsie = locate() in things_in_range
if(istype(plushsie) && plushsie.is_invoker)
+1 -1
View File
@@ -55,7 +55,7 @@
if(specialfunctions & SHOCK)
if(D.secondsElectrified)
D.secondsElectrified = -1
LAZYADD(D.shockedby, "\[[time_stamp()]\] [key_name(usr)]")
LAZYADD(D.shockedby, "\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(usr)]")
log_combat(usr, D, "electrified")
else
D.secondsElectrified = 0
@@ -29,6 +29,7 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
var/last_share = 0
var/list/reaction_results
var/list/analyzer_results //used for analyzer feedback - not initialized until its used
var/gc_share = FALSE // Whether to call garbage_collect() on the sharer during shares, used for immutable mixtures
/datum/gas_mixture/New(volume)
gases = new
@@ -143,9 +144,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
//Performs air sharing calculations between two gas_mixtures assuming only 1 boundary length
//Returns: amount of gas exchanged (+ if sharer received)
/datum/gas_mixture/proc/after_share(datum/gas_mixture/sharer)
//called on share's sharer to let it know it just got some gases
/datum/gas_mixture/proc/temperature_share(datum/gas_mixture/sharer, conduction_coefficient)
//Performs temperature sharing calculations (via conduction) between two gas_mixtures assuming only 1 boundary length
//Returns: new temperature of the sharer
@@ -343,7 +341,8 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
if(length(cached_gases ^ sharer_gases)) //if all gases were present in both mixtures, we know that no gases are 0
garbage_collect(cached_gases - sharer_gases) //any gases the sharer had, we are guaranteed to have. gases that it didn't have we are not.
sharer.garbage_collect(sharer_gases - cached_gases) //the reverse is equally true
sharer.after_share(src, atmos_adjacent_turfs)
if (initial(sharer.gc_share))
sharer.garbage_collect()
if(temperature_delta > MINIMUM_TEMPERATURE_TO_MOVE || abs(moved_moles) > MINIMUM_MOLES_DELTA_TO_MOVE)
var/our_moles
TOTAL_MOLES(cached_gases,our_moles)
@@ -351,9 +350,6 @@ GLOBAL_LIST_INIT(gaslist_cache, init_gaslist_cache())
TOTAL_MOLES(sharer_gases,their_moles)
return (temperature_archived*(our_moles + moved_moles) - sharer.temperature_archived*(their_moles - moved_moles)) * R_IDEAL_GAS_EQUATION / volume
/datum/gas_mixture/after_share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
return
/datum/gas_mixture/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
//transfer of thermal energy (via conduction) between self and sharer
if(sharer)
@@ -1,73 +1,71 @@
//"immutable" gas mixture used for immutable calculations
//it can be changed, but any changes will ultimately be undone before they can have any effect
/datum/gas_mixture/immutable
var/initial_temperature
/datum/gas_mixture/immutable/New()
..()
garbage_collect()
/datum/gas_mixture/immutable/garbage_collect()
temperature = initial_temperature
temperature_archived = initial_temperature
gases.Cut()
/datum/gas_mixture/immutable/archive()
return 1 //nothing changes, so we do nothing and the archive is successful
/datum/gas_mixture/immutable/merge()
return 0 //we're immutable.
/datum/gas_mixture/immutable/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
. = ..(sharer, 0)
garbage_collect()
/datum/gas_mixture/immutable/after_share()
garbage_collect()
/datum/gas_mixture/immutable/react()
return 0 //we're immutable.
/datum/gas_mixture/immutable/copy()
return new type //we're immutable, so we can just return a new instance.
/datum/gas_mixture/immutable/copy_from()
return 0 //we're immutable.
/datum/gas_mixture/immutable/copy_from_turf()
return 0 //we're immutable.
/datum/gas_mixture/immutable/parse_gas_string()
return 0 //we're immutable.
/datum/gas_mixture/immutable/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
. = ..()
temperature = initial_temperature
//used by space tiles
/datum/gas_mixture/immutable/space
initial_temperature = TCMB
/datum/gas_mixture/immutable/space/heat_capacity()
return HEAT_CAPACITY_VACUUM
/datum/gas_mixture/immutable/space/remove()
return copy() //we're always empty, so we can just return a copy.
/datum/gas_mixture/immutable/space/remove_ratio()
return copy() //we're always empty, so we can just return a copy.
//used by cloners
/datum/gas_mixture/immutable/cloner
initial_temperature = T20C
/datum/gas_mixture/immutable/cloner/garbage_collect()
..()
ADD_GAS(/datum/gas/nitrogen, gases)
gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD
/datum/gas_mixture/immutable/cloner/heat_capacity()
return (MOLES_O2STANDARD + MOLES_N2STANDARD)*20 //specific heat of nitrogen is 20
//"immutable" gas mixture used for immutable calculations
//it can be changed, but any changes will ultimately be undone before they can have any effect
/datum/gas_mixture/immutable
var/initial_temperature
gc_share = TRUE
/datum/gas_mixture/immutable/New()
..()
garbage_collect()
/datum/gas_mixture/immutable/garbage_collect()
temperature = initial_temperature
temperature_archived = initial_temperature
gases.Cut()
/datum/gas_mixture/immutable/archive()
return 1 //nothing changes, so we do nothing and the archive is successful
/datum/gas_mixture/immutable/merge()
return 0 //we're immutable.
/datum/gas_mixture/immutable/share(datum/gas_mixture/sharer, atmos_adjacent_turfs = 4)
. = ..(sharer, 0)
garbage_collect()
/datum/gas_mixture/immutable/react()
return 0 //we're immutable.
/datum/gas_mixture/immutable/copy()
return new type //we're immutable, so we can just return a new instance.
/datum/gas_mixture/immutable/copy_from()
return 0 //we're immutable.
/datum/gas_mixture/immutable/copy_from_turf()
return 0 //we're immutable.
/datum/gas_mixture/immutable/parse_gas_string()
return 0 //we're immutable.
/datum/gas_mixture/immutable/temperature_share(datum/gas_mixture/sharer, conduction_coefficient, sharer_temperature, sharer_heat_capacity)
. = ..()
temperature = initial_temperature
//used by space tiles
/datum/gas_mixture/immutable/space
initial_temperature = TCMB
/datum/gas_mixture/immutable/space/heat_capacity()
return HEAT_CAPACITY_VACUUM
/datum/gas_mixture/immutable/space/remove()
return copy() //we're always empty, so we can just return a copy.
/datum/gas_mixture/immutable/space/remove_ratio()
return copy() //we're always empty, so we can just return a copy.
//used by cloners
/datum/gas_mixture/immutable/cloner
initial_temperature = T20C
/datum/gas_mixture/immutable/cloner/garbage_collect()
..()
ADD_GAS(/datum/gas/nitrogen, gases)
gases[/datum/gas/nitrogen][MOLES] = MOLES_O2STANDARD + MOLES_N2STANDARD
/datum/gas_mixture/immutable/cloner/heat_capacity()
return (MOLES_O2STANDARD + MOLES_N2STANDARD)*20 //specific heat of nitrogen is 20
@@ -221,13 +221,16 @@
if(!P)
continue
GL += P.return_air()
for(var/obj/machinery/atmospherics/components/binary/valve/V in P.other_atmosmch)
if(V.on)
PL |= V.parents[1]
PL |= V.parents[2]
for(var/obj/machinery/atmospherics/components/unary/portables_connector/C in P.other_atmosmch)
if(C.connected_device)
GL += C.portableConnectorReturnAir()
for(var/atmosmch in P.other_atmosmch)
if (istype(atmosmch, /obj/machinery/atmospherics/components/binary/valve))
var/obj/machinery/atmospherics/components/binary/valve/V = atmosmch
if(V.on)
PL |= V.parents[1]
PL |= V.parents[2]
else if (istype(atmosmch, /obj/machinery/atmospherics/components/unary/portables_connector))
var/obj/machinery/atmospherics/components/unary/portables_connector/C = atmosmch
if(C.connected_device)
GL += C.portableConnectorReturnAir()
var/total_thermal_energy = 0
var/total_heat_capacity = 0
+5
View File
@@ -41,6 +41,11 @@
return
if(QDELETED(src) || QDELETED(user))
return
if(isobserver(user))
var/mob/dead/observer/O = user
if(!O.can_reenter_round)
to_chat(user, "<span class='warning'>You are unable to reenter the round.</span>")
return
var/ghost_role = alert(latejoinercalling ? "Latejoin as [mob_name]? (This is a ghost role, and as such, it's very likely to be off-station.)" : "Become [mob_name]? (Warning, You can no longer be cloned!)",,"Yes","No")
if(ghost_role == "No" || !loc)
return
+26 -1
View File
@@ -512,6 +512,14 @@
/obj/item/storage/belt/bandolier)
crate_name = "combat shotguns crate"
/datum/supply_pack/security/armory/dragnetgun
name = "DRAGnet gun Crate"
desc = "Contains two DRAGnet gun. A Dynamic Rapid-Apprehension of the Guilty net the revolution in law enforcement technology that YOU Want! Requires Armory access to open."
cost = 3500
contains = list(/obj/item/gun/energy/e_gun/dragnet,
/obj/item/gun/energy/e_gun/dragnet)
crate_name = "anti riot net guns crate"
/datum/supply_pack/security/armory/energy
name = "Energy Guns Crate"
desc = "Contains three Energy Guns, capable of firing both nonlethal and lethal blasts of light. Requires Armory access to open."
@@ -545,7 +553,7 @@
/datum/supply_pack/security/armory/fire
name = "Incendiary Weapons Crate"
desc = "Burn, baby burn. Contains three incendiary grenades, three plasma canisters, and a flamethrower. Requires Armory access to open."
desc = "Burn, baby burn. Contains three incendiary grenades, three plasma canisters, and a flamethrower. Requires Brige access to open."
cost = 1500
access = ACCESS_HEADS
contains = list(/obj/item/flamethrower/full,
@@ -559,6 +567,16 @@
crate_type = /obj/structure/closet/crate/secure/plasma
dangerous = TRUE
/datum/supply_pack/security/armory/miniguns
name = "Personal Miniature Energy Guns"
desc = "Contains three miniature energy guns. Each gun has a disabler and a lethal option. Requires Armory access to open."
cost = 5000
contains = list(/obj/item/gun/energy/e_gun/mini,
/obj/item/gun/energy/e_gun/mini,
/obj/item/gun/energy/e_gun/mini)
crate_name = "personal energy guns crate"
crate_type = /obj/structure/closet/crate/secure/plasma
/datum/supply_pack/security/armory/laserarmor
name = "Reflector Vest Crate"
desc = "Contains two vests of highly reflective material. Each armor piece diffuses a laser's energy by over half, as well as offering a good chance to reflect the laser entirely. Requires Armory access to open."
@@ -620,6 +638,13 @@
/obj/item/clothing/gloves/combat)
crate_name = "swat crate"
/datum/supply_pack/security/armory/swattasers //Lesser AEG tbh
name = "SWAT tatical tasers Crate"
desc = "Contains two tactical energy gun, these guns are able to tase, disable and lethal as well as hold a seclight. Requires Armory access to open."
cost = 8000
contains = list(/obj/item/gun/energy/e_gun/stun,
/obj/item/gun/energy/e_gun/stun)
crate_name = "swat taser crate"
/datum/supply_pack/security/armory/wt550
name = "WT-550 Semi-Auto Rifle Crate"
+40 -54
View File
@@ -1385,48 +1385,32 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("hair_style")
var/new_hair_style
if(gender == MALE)
new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_male_list
else
new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_female_list
new_hair_style = input(user, "Choose your character's hair style:", "Character Preference") as null|anything in GLOB.hair_styles_list
if(new_hair_style)
hair_style = new_hair_style
if("next_hair_style")
if (gender == MALE)
hair_style = next_list_item(hair_style, GLOB.hair_styles_male_list)
else
hair_style = next_list_item(hair_style, GLOB.hair_styles_female_list)
hair_style = next_list_item(hair_style, GLOB.hair_styles_list)
if("previous_hair_style")
if (gender == MALE)
hair_style = previous_list_item(hair_style, GLOB.hair_styles_male_list)
else
hair_style = previous_list_item(hair_style, GLOB.hair_styles_female_list)
hair_style = previous_list_item(hair_style, GLOB.hair_styles_list)
if("facial")
var/new_facial = input(user, "Choose your character's facial-hair colour:", "Character Preference","#"+facial_hair_color) as color|null
if(new_facial)
facial_hair_color = sanitize_hexcolor(new_facial)
if("facial_hair_style")
var/new_facial_hair_style
if(gender == MALE)
new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_male_list
else
new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_female_list
if(new_facial_hair_style)
facial_hair_style = new_facial_hair_style
if("facial_hair_style")
var/new_facial_hair_style
new_facial_hair_style = input(user, "Choose your character's facial-hair style:", "Character Preference") as null|anything in GLOB.facial_hair_styles_list
if(new_facial_hair_style)
facial_hair_style = new_facial_hair_style
if("next_facehair_style")
if (gender == MALE)
facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list)
else
facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list)
if("previous_facehair_style")
if (gender == MALE)
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_male_list)
else
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_female_list)
facial_hair_style = next_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
if("previous_facehair_style")
facial_hair_style = previous_list_item(facial_hair_style, GLOB.facial_hair_styles_list)
if("underwear")
var/new_underwear
@@ -1584,14 +1568,28 @@ GLOBAL_LIST_EMPTY(preferences_datums)
features["tail_lizard"] = "None"
if("snout")
var/list/snowflake_snouts_list = list()
for(var/path in GLOB.snouts_list)
var/datum/sprite_accessory/mam_snouts/instance = GLOB.snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
snowflake_snouts_list[S.name] = path
var/new_snout
new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.snouts_list
new_snout = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snowflake_snouts_list
if(new_snout)
features["snout"] = new_snout
if("mam_snouts")
var/list/snowflake_mam_snouts_list = list()
for(var/path in GLOB.mam_snouts_list)
var/datum/sprite_accessory/mam_snouts/instance = GLOB.mam_snouts_list[path]
if(istype(instance, /datum/sprite_accessory))
var/datum/sprite_accessory/S = instance
if((!S.ckeys_allowed) || (S.ckeys_allowed.Find(user.client.ckey)))
snowflake_mam_snouts_list[S.name] = path
var/new_mam_snouts
new_mam_snouts = input(user, "Choose your character's snout:", "Character Preference") as null|anything in GLOB.mam_snouts_list
new_mam_snouts = input(user, "Choose your character's snout:", "Character Preference") as null|anything in snowflake_mam_snouts_list
if(new_mam_snouts)
features["mam_snouts"] = new_mam_snouts
@@ -1601,12 +1599,6 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if(new_horns)
features["horns"] = new_horns
if("ears")
var/new_ears
new_ears = input(user, "Choose your character's ears:", "Character Preference") as null|anything in GLOB.ears_list
if(new_ears)
features["ears"] = new_ears
if("wings")
var/new_wings
new_wings = input(user, "Choose your character's wings:", "Character Preference") as null|anything in GLOB.r_wings_list
@@ -2152,31 +2144,25 @@ GLOBAL_LIST_EMPTY(preferences_datums)
if("tail_lizard" in pref_species.default_features)
character.dna.species.mutant_bodyparts |= "tail_lizard"
else if("mam_tail" in pref_species.default_features)
if("mam_tail" in pref_species.default_features)
character.dna.species.mutant_bodyparts |= "mam_tail"
else if("xenotail" in pref_species.default_features)
if("xenotail" in pref_species.default_features)
character.dna.species.mutant_bodyparts |= "xenotail"
if("legs" in pref_species.default_features)
if(character.dna.features["legs"] == "Digitigrade Legs")
pref_species.species_traits += DIGITIGRADE
character.Digitigrade_Leg_Swap(FALSE)
if(character.dna.features["legs"] == "Normal Legs" && DIGITIGRADE in pref_species.species_traits)
pref_species.species_traits -= DIGITIGRADE
character.Digitigrade_Leg_Swap(TRUE)
else if((!"legs" in pref_species.default_features) && DIGITIGRADE in pref_species.species_traits)
if(("legs" in character.dna.species.mutant_bodyparts) && character.dna.features["legs"] == "Digitigrade Legs")
pref_species.species_traits |= DIGITIGRADE
else
pref_species.species_traits -= DIGITIGRADE
character.Digitigrade_Leg_Swap(TRUE)
if(DIGITIGRADE in pref_species.species_traits)
character.Digitigrade_Leg_Swap(FALSE)
else
character.Digitigrade_Leg_Swap(TRUE)
if(icon_updates)
character.update_body()
character.update_hair()
character.update_body_parts()
//let's be sure the character updates
character.update_body()
character.update_hair()
character.update_body_parts()
/datum/preferences/proc/get_default_name(name_id)
switch(name_id)
+2 -5
View File
@@ -24,11 +24,8 @@
return
if(!holder)
if(!GLOB.ooc_allowed)
to_chat(src, "<span class='danger'> OOC is globally muted</span>")
return
if(!GLOB.dooc_allowed && (mob.stat == DEAD))
to_chat(usr, "<span class='danger'> OOC for dead mobs has been turned off.</span>")
if(!GLOB.looc_allowed)
to_chat(src, "<span class='danger'> LOOC is globally muted</span>")
return
if(prefs.muted & MUTE_OOC)
to_chat(src, "<span class='danger'> You cannot use OOC (muted).</span>")
+10
View File
@@ -80,6 +80,16 @@
GLOB.ooc_allowed = !GLOB.ooc_allowed
to_chat(world, "<B>The OOC channel has been globally [GLOB.ooc_allowed ? "enabled" : "disabled"].</B>")
/proc/toggle_looc(toggle = null)
if(toggle != null)
if(toggle != GLOB.looc_allowed)
GLOB.looc_allowed = toggle
else
return
else
GLOB.looc_allowed = !GLOB.looc_allowed
/proc/toggle_dooc(toggle = null)
if(toggle != null)
if(toggle != GLOB.dooc_allowed)
+6 -4
View File
@@ -198,11 +198,13 @@
death(0)
/mob/living/proc/suicide_log()
log_game("[key_name(src)] committed suicide at [AREACOORD(src)] as [src.type].")
/mob/living/proc/suicide_log(ghosting)
log_game("[key_name(src)] [ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)] as [src.type].")
message_admins("[key_name(src)] [ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
/mob/living/carbon/human/suicide_log()
log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) committed suicide at [AREACOORD(src)].")
/mob/living/carbon/human/suicide_log(ghosting)
log_game("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) [is_special_character(src) ? "(ANTAG!) " : ""][ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
message_admins("[key_name(src)] (job: [src.job ? "[src.job]" : "None"]) [is_special_character(src) ? "(ANTAG!) " : ""][ghosting ? "ghosted" : "committed suicide"] at [AREACOORD(src)].")
/mob/living/proc/canSuicide()
switch(stat)
+4 -1
View File
@@ -712,7 +712,10 @@
/obj/item/clothing/suit/space/hardsuit/shielded/worn_overlays(isinhands)
. = list()
if(!isinhands)
. += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
if(taurmode >= SNEK_TAURIC)
. += mutable_appearance('modular_citadel/icons/mob/64x32_effects.dmi', shield_state, MOB_LAYER + 0.01)
else
. += mutable_appearance('icons/effects/effects.dmi', shield_state, MOB_LAYER + 0.01)
/obj/item/clothing/head/helmet/space/hardsuit/shielded
resistance_flags = FIRE_PROOF | ACID_PROOF
+5 -2
View File
@@ -36,7 +36,7 @@
if(tauric == TRUE)
center = TRUE
dimension_x = 64
else if(H.dna.features["taur"] in list("Fox", "Wolf", "Otie", "Drake", "Lab", "Shepherd", "Husky", "Eevee", "Panther", "Tajaran", "Horse", "Cow"))
else if(H.dna.features["taur"] in list("Fox","Wolf","Otie","Drake","Lab","Shepherd","Husky","Eevee","Panther","Horse","Cow","Tiger"))
taurmode = PAW_TAURIC
if(tauric == TRUE)
center = TRUE
@@ -61,7 +61,10 @@
if(damaged_clothes)
. += mutable_appearance('icons/effects/item_damage.dmi', "damaged[blood_overlay_type]")
IF_HAS_BLOOD_DNA(src)
. += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
if(taurmode >= SNEK_TAURIC)
. += mutable_appearance('modular_citadel/icons/mob/64x32_effects.dmi', "[blood_overlay_type]blood")
else
. += mutable_appearance('icons/effects/blood.dmi', "[blood_overlay_type]blood")
var/mob/living/carbon/human/M = loc
if(ishuman(M) && M.w_uniform)
var/obj/item/clothing/under/U = M.w_uniform
+1 -1
View File
@@ -116,7 +116,7 @@
// We gathered everything. Create a fork and slowly display the results to the holder of the scanner.
var/found_something = 0
add_log("<B>[station_time_timestamp()][get_timestamp()] - [target_name]</B>", 0)
add_log("<B>[STATION_TIME_TIMESTAMP("hh:mm:ss")][get_timestamp()] - [target_name]</B>", 0)
// Fingerprints
if(length(fingerprints))
+2 -2
View File
@@ -76,7 +76,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
var/skipcount = abs(error_cooldown[erroruid]) - 1
error_cooldown[erroruid] = 0
if(skipcount > 0)
SEND_TEXT(world.log, "\[[time_stamp()]] Skipped [skipcount] runtimes in [E.file],[E.line].")
SEND_TEXT(world.log, "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Skipped [skipcount] runtimes in [E.file],[E.line].")
GLOB.error_cache.log_error(E, skip_count = skipcount)
error_last_seen[erroruid] = world.time
@@ -113,7 +113,7 @@ GLOBAL_VAR_INIT(total_runtimes_skipped, 0)
if(GLOB.error_cache)
GLOB.error_cache.log_error(E, desclines)
var/main_line = "\[[time_stamp()]] Runtime in [E.file],[E.line]: [E]"
var/main_line = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Runtime in [E.file],[E.line]: [E]"
SEND_TEXT(world.log, main_line)
for(var/line in desclines)
SEND_TEXT(world.log, line)
+5 -5
View File
@@ -131,10 +131,10 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
/datum/error_viewer/error_source/New(exception/e)
if (!istype(e))
name = "\[[time_stamp()]] Uncaught exceptions"
name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Uncaught exceptions"
return
name = "<b>\[[time_stamp()]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
name = "<b>\[[TIME_STAMP("hh:mm:ss", FALSE)]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
/datum/error_viewer/error_source/show_to(user, datum/error_viewer/back_to, linear)
if (!istype(back_to))
@@ -156,15 +156,15 @@ GLOBAL_DATUM(error_cache, /datum/error_viewer/error_cache)
/datum/error_viewer/error_entry/New(exception/e, list/desclines, skip_count)
if (!istype(e))
name = "<b>\[[time_stamp()]]</b> Uncaught exception: <b>[html_encode(e.name)]</b>"
name = "<b>\[[TIME_STAMP("hh:mm:ss", FALSE)]]</b> Uncaught exception: <b>[html_encode(e.name)]</b>"
return
if(skip_count)
name = "\[[time_stamp()]] Skipped [skip_count] runtimes in [e.file],[e.line]."
name = "\[[TIME_STAMP("hh:mm:ss", FALSE)]] Skipped [skip_count] runtimes in [e.file],[e.line]."
is_skip_count = TRUE
return
name = "<b>\[[time_stamp()]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
name = "<b>\[[TIME_STAMP("hh:mm:ss", FALSE)]]</b> Runtime in <b>[e.file]</b>, line <b>[e.line]</b>: <b>[html_encode(e.name)]</b>"
exc = e
if (istype(desclines))
for (var/line in desclines)
+25 -1
View File
@@ -6,7 +6,7 @@
#define PIZZA_DELIVERY 6
#define ITS_HIP_TO 7
#define MY_GOD_JC 8
#define DELTA_CRATES 9
/datum/round_event_control/shuttle_loan
name = "Shuttle Loan"
@@ -118,6 +118,18 @@
P.info = "Cargo: We have discovered an active Syndicate bomb near our VIP shuttle's fuel lines. If you feel up to the task, we will pay you for defusing it."
P.update_icon()
bonus_points = 45000 //If you mess up, people die and the shuttle gets turned into swiss cheese
if(DELTA_CRATES)
if(prob(50))
priority_announce("Cargo: We have discovered a warehouse of DELTA locked crates, we cant store any more of them at CC can you take them for us?.", "CentCom Security Division")
else
priority_announce("A report has been downloaded and printed out at all communications consoles.", "Incoming Classified Message", 'sound/ai/commandreport.ogg') // CITADEL EDIT metabreak
for(var/obj/machinery/computer/communications/C in GLOB.machines)
if(!(C.stat & (BROKEN|NOPOWER)) && is_station_level(C.z))
var/obj/item/paper/P = new(C.loc)
P.name = "Cargo Report"
P.info = "Cargo: We have discovered a warehouse of DELTA locked crates, we cant store any more of them at CC can you take them for us?."
P.update_icon()
bonus_points = 25000 //If you mess up, people die and the shuttle gets turned into swiss cheese
/datum/round_event/shuttle_loan/proc/loan_shuttle()
priority_announce(thanks_msg, "Cargo shuttle commandeered by CentCom.")
@@ -147,6 +159,8 @@
SSshuttle.centcom_message += "Biohazard cleanup incoming."
if(MY_GOD_JC)
SSshuttle.centcom_message += "Live explosive ordnance incoming. Exercise extreme caution."
if(DELTA_CRATES)
SSshuttle.centcom_message += "DELTA Locked crates incoming. Exercise extreme caution."
/datum/round_event/shuttle_loan/tick()
if(dispatched)
@@ -291,6 +305,15 @@
else
shuttle_spawns.Add(/obj/item/paper/fluff/cargo/bomb/allyourbase)
if(DELTA_CRATES) //Delta crates can stack on eacher, and are basicly a 1/3/5 bombs
for(var/i in 1 to 7) //7 seems fair
shuttle_spawns.Add(/obj/structure/closet/crate/secure/loot)
for(var/i in 1 to 5)
var/turf/T = pick_n_take(empty_shuttle_turfs)
new /obj/structure/spider/stickyweb(T)
new /obj/effect/decal/cleanable/ash(T)
var/false_positive = 0
while(shuttle_spawns.len && empty_shuttle_turfs.len)
var/turf/T = pick_n_take(empty_shuttle_turfs)
@@ -334,3 +357,4 @@
#undef PIZZA_DELIVERY
#undef ITS_HIP_TO
#undef MY_GOD_JC
#undef DELTA_CRATES
+10 -4
View File
@@ -75,7 +75,7 @@
reagentsAmount = 250
/datum/round_event_control/vent_clog/beer
name = "Foamy beer stationwide"
name = "Clogged Vents: Beer"
typepath = /datum/round_event/vent_clog/beer
max_occurrences = 0
@@ -83,12 +83,12 @@
reagentsAmount = 100
/datum/round_event_control/vent_clog/plasma_decon
name = "Plasma decontamination"
name = "Anti-Plasma Flood"
typepath = /datum/round_event/vent_clog/plasma_decon
max_occurrences = 0
/datum/round_event_control/vent_clog/female
name = "FemCum stationwide"
name = "Clogged Vents; Girlcum"
typepath = /datum/round_event/vent_clog/female
max_occurrences = 0
@@ -96,7 +96,7 @@
reagentsAmount = 100
/datum/round_event_control/vent_clog/male
name = "Semen stationwide"
name = "Clogged Vents: Semen"
typepath = /datum/round_event/vent_clog/male
max_occurrences = 0
@@ -118,6 +118,9 @@
foam.start()
CHECK_TICK
/datum/round_event/vent_clog/male/announce()
priority_announce("The scrubbers network is experiencing a backpressure surge. Some ejaculation of contents may occur.", "Atmospherics alert")
/datum/round_event/vent_clog/male/start()
for(var/obj/machinery/atmospherics/components/unary/vent in vents)
if(vent && vent.loc)
@@ -130,6 +133,9 @@
foam.start()
CHECK_TICK
/datum/round_event/vent_clog/female/announce()
priority_announce("The scrubbers network is experiencing a backpressure squirt. Some ejection of contents may occur.", "Atmospherics alert")
/datum/round_event/vent_clog/female/start()
for(var/obj/machinery/atmospherics/components/unary/vent in vents)
if(vent && vent.loc)
@@ -184,6 +184,15 @@
tastes = list("the jungle" = 1, "bananas" = 1)
foodtype = MEAT | SUGAR
/obj/item/reagent_containers/food/snacks/monkeycube/On_Consume(mob/living/carbon/M)
if(iscarbon(M))
M.visible_message("[src] bursts out of [M]!</span>")
M.emote("scream")
M.Knockdown(40)
M.adjustBruteLoss(60)
Expand()
return ..()
/obj/item/reagent_containers/food/snacks/monkeycube/proc/Expand()
var/mob/spammer = get_mob_by_key(fingerprintslast)
var/mob/living/carbon/monkey/bananas = new(drop_location(), TRUE, spammer)
@@ -160,6 +160,15 @@
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2)
foodtype = PINEAPPLE //Over powering tast of gods fruit
/obj/item/reagent_containers/food/snacks/pizza/pineapple/anomaly
desc = "A anomaly made pizza with pineapple..."
bonus_reagents = list("nutriment" = 16, "vitamin" = 16)
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2, "good" = 10)
/obj/item/reagent_containers/food/snacks/pizzaslice/pineapple/anomaly
desc = "A slice of good tasting pizza. But has pineapple on it, what a anomaly!"
tastes = list("crust" = 1, "tomato" = 1, "cheese" = 1, "pineapple" = 6, "ham" = 2, "good" = 10)
/obj/item/reagent_containers/food/snacks/pizza/arnold
name = "\improper Arnold pizza"
desc = "Hello, you've reached Arnold's pizza shop. I'm not here now, I'm out killing pepperoni."
@@ -692,3 +692,15 @@
id = "pwr_game"
results = list("pwr_game" = 5)
required_reagents = list("sodawater" = 1, "blackcrayonpowder" = 1, "sodiumchloride" = 1)
/datum/chemical_reaction/pinkmilk
name = "Strawberry Milk"
id = "pinkmilk"
results = list("pinkmilk" = 5)
required_reagents = list("aphro+" = 1, "milk" = 1)
/datum/chemical_reaction/pinktea
name = "Strawberry Tea"
id = "pinktea"
results = list("pinktea" = 5)
required_reagents = list("aphro" = 1, "arnold_palmer" = 1, "sugar" = 1)
@@ -101,3 +101,22 @@
)
result = /obj/item/reagent_containers/food/snacks/pizza/pineapple
subcategory = CAT_PIZZA
/datum/crafting_recipe/food/pineapplepizza/anomaly
name = "Anomaly Hawaiian pizza"
reqs = list(
/obj/item/assembly/signaler/anomaly = 1,
/obj/item/reagent_containers/food/snacks/pizza/pineapple = 1
)
result = /obj/item/reagent_containers/food/snacks/pizza/pineapple/anomaly
subcategory = CAT_PIZZA
/datum/crafting_recipe/food/anomalypizzaboy
name = "Anomaly pizza box"
reqs = list(
/obj/item/pizzabox = 5,
/obj/item/assembly/signaler/anomaly = 1
)
tools = list(TOOL_WELDER, TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
result = /obj/item/pizzabox/infinite
subcategory = CAT_PIZZA
@@ -245,96 +245,41 @@ function output(message, flag) {
message = byondDecode(message).trim();
//The behemoth of filter-code (for Admin message filters)
//Note: This is proooobably hella inefficient
var filteredOut = false;
if (opts.hasOwnProperty('showMessagesFilters') && !opts.showMessagesFilters['All'].show) {
//Get this filter type (defined by class on message)
var messageHtml = $.parseHTML(message),
messageClasses;
if (opts.hasOwnProperty('filterHideAll') && opts.filterHideAll) {
var internal = false;
messageClasses = (!!$(messageHtml).attr('class') ? $(messageHtml).attr('class').split(/\s+/) : false);
if (messageClasses) {
for (var i = 0; i < messageClasses.length; i++) { //Every class
if (messageClasses[i] == 'internal') {
internal = true;
break;
}
}
}
if (!internal) {
filteredOut = 'All';
}
} else {
//If the element or it's child have any classes
if (!!$(messageHtml).attr('class') || !!$(messageHtml).children().attr('class')) {
messageClasses = $(messageHtml).attr('class').split(/\s+/);
if (!!$(messageHtml).children().attr('class')) {
messageClasses = messageClasses.concat($(messageHtml).children().attr('class').split(/\s+/));
}
var tempCount = 0;
for (var i = 0; i < messageClasses.length; i++) { //Every class
var thisClass = messageClasses[i];
$.each(opts.showMessagesFilters, function(key, val) { //Every filter
if (key !== 'All' && val.show === false && typeof val.match != 'undefined') {
for (var i = 0; i < val.match.length; i++) {
var matchClass = val.match[i];
if (matchClass == thisClass) {
filteredOut = key;
break;
}
}
}
if (filteredOut) return false;
});
if (filteredOut) break;
tempCount++;
}
} else {
if (!opts.showMessagesFilters['Misc'].show) {
filteredOut = 'Misc';
}
}
}
}
//Stuff we do along with appending a message
var atBottom = false;
if (!filteredOut) {
var bodyHeight = $('body').height();
var messagesHeight = $messages.outerHeight();
var scrollPos = $('body,html').scrollTop();
var bodyHeight = $('body').height();
var messagesHeight = $messages.outerHeight();
var scrollPos = $('body,html').scrollTop();
//Should we snap the output to the bottom?
if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
atBottom = true;
if ($('#newMessages').length) {
$('#newMessages').remove();
//Should we snap the output to the bottom?
if (bodyHeight + scrollPos >= messagesHeight - opts.scrollSnapTolerance) {
atBottom = true;
if ($('#newMessages').length) {
$('#newMessages').remove();
}
//If not, put the new messages box in
} else {
if ($('#newMessages').length) {
var messages = $('#newMessages .number').text();
messages = parseInt(messages);
messages++;
$('#newMessages .number').text(messages);
if (messages == 2) {
$('#newMessages .messageWord').append('s');
}
//If not, put the new messages box in
} else {
if ($('#newMessages').length) {
var messages = $('#newMessages .number').text();
messages = parseInt(messages);
messages++;
$('#newMessages .number').text(messages);
if (messages == 2) {
$('#newMessages .messageWord').append('s');
}
} else {
$messages.after('<a href="#" id="newMessages"><span class="number">1</span> new <span class="messageWord">message</span> <i class="icon-double-angle-down"></i></a>');
}
$messages.after('<a href="#" id="newMessages"><span class="number">1</span> new <span class="messageWord">message</span> <i class="icon-double-angle-down"></i></a>');
}
}
opts.messageCount++;
//Pop the top message off if history limit reached
if (opts.messageCount >= opts.messageLimit) {
$messages.children('div.entry:first-child').remove();
opts.messageCount--; //I guess the count should only ever equal the limit
}
//if (opts.messageCount >= opts.messageLimit) {
//$messages.children('div.entry:first-child').remove();
//opts.messageCount--; //I guess the count should only ever equal the limit
//}
// Create the element - if combining is off, we use it, and if it's on, we
// might discard it bug need to check its text content. Some messages vary
@@ -372,11 +317,6 @@ function output(message, flag) {
//Actually append the message
entry.className = 'entry';
if (filteredOut) {
entry.className += ' hidden';
entry.setAttribute('data-filter', filteredOut);
}
$last_message = trimmed_message;
$messages[0].appendChild(entry);
$(entry).find("img.icon").error(iconError);
@@ -401,7 +341,7 @@ function output(message, flag) {
}
}
if (!filteredOut && atBottom) {
if (atBottom) {
$('body,html').scrollTop($messages.outerHeight());
}
}
@@ -0,0 +1,5 @@
#define IC_TOPIC_UNHANDLED 0
#define IC_TOPIC_HANDLED 1
#define IC_TOPIC_REFRESH 2
#define IC_FLAG_ANCHORABLE 1
#define IC_FLAG_CAN_FIRE 2
@@ -0,0 +1,762 @@
#define SOURCE_TO_TARGET 0
#define TARGET_TO_SOURCE 1
#define PUMP_EFFICIENCY 0.6
#define TANK_FAILURE_PRESSURE (ONE_ATMOSPHERE*25)
#define PUMP_MAX_PRESSURE (ONE_ATMOSPHERE*24)
#define PUMP_MAX_VOLUME 100
/obj/item/integrated_circuit/atmospherics
category_text = "Atmospherics"
cooldown_per_use = 2 SECONDS
complexity = 10
size = 7
outputs = list(
"self reference" = IC_PINTYPE_SELFREF,
"pressure" = IC_PINTYPE_NUMBER
)
var/datum/gas_mixture/air_contents
var/volume = 2 //Pretty small, I know
/obj/item/integrated_circuit/atmospherics/Initialize()
air_contents = new(volume)
..()
/obj/item/integrated_circuit/atmospherics/return_air()
return air_contents
//Check if the gas container is adjacent and of the right type
/obj/item/integrated_circuit/atmospherics/proc/check_gassource(atom/gasholder)
if(!gasholder)
return FALSE
if(!gasholder.Adjacent(get_object()))
return FALSE
if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics))
return FALSE
return TRUE
//Needed in circuits where source and target types differ
/obj/item/integrated_circuit/atmospherics/proc/check_gastarget(atom/gasholder)
return check_gassource(gasholder)
// - gas pump - // **works**
/obj/item/integrated_circuit/atmospherics/pump
name = "gas pump"
desc = "Somehow moves gases between two tanks, canisters, and other gas containers."
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"source" = IC_PINTYPE_REF,
"target" = IC_PINTYPE_REF,
"target pressure" = IC_PINTYPE_NUMBER
)
activators = list(
"transfer" = IC_PINTYPE_PULSE_IN,
"on transfer" = IC_PINTYPE_PULSE_OUT
)
var/direction = SOURCE_TO_TARGET
var/target_pressure = PUMP_MAX_PRESSURE
power_draw_per_use = 20
/obj/item/integrated_circuit/atmospherics/pump/Initialize()
air_contents = new(volume)
extended_desc += " Use negative pressure to move air from target to source. \
Note that only part of the gas is moved on each transfer, \
so multiple activations will be necessary to achieve target pressure. \
The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa."
. = ..()
// This proc gets the direction of the gas flow depending on its value, by calling update target
/obj/item/integrated_circuit/atmospherics/pump/on_data_written()
var/amt = get_pin_data(IC_INPUT, 3)
update_target(amt)
/obj/item/integrated_circuit/atmospherics/pump/proc/update_target(new_amount)
if(!isnum(new_amount))
new_amount = 0
// See in which direction the gas moves
if(new_amount < 0)
direction = TARGET_TO_SOURCE
else
direction = SOURCE_TO_TARGET
target_pressure = min(round(PUMP_MAX_PRESSURE),abs(new_amount))
/obj/item/integrated_circuit/atmospherics/pump/do_work()
var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj)
var/obj/target = get_pin_data_as_type(IC_INPUT, 2, /obj)
perform_magic(source, target)
activate_pin(2)
/obj/item/integrated_circuit/atmospherics/pump/proc/perform_magic(atom/source, atom/target)
//Check if both atoms are of the right type: atmos circuits/gas tanks/canisters. If one is the same, use the circuit var
if(!check_gassource(source))
source = src
if(!check_gastarget(target))
target = src
// If both are the same, this whole proc would do nothing and just waste performance
if(source == target)
return
var/datum/gas_mixture/source_air = source.return_air()
var/datum/gas_mixture/target_air = target.return_air()
if(!source_air || !target_air)
return
// Swapping both source and target
if(direction == TARGET_TO_SOURCE)
var/temp = source_air
source_air = target_air
target_air = temp
// If what you are pumping is empty, use the circuit's storage
if(source_air.total_moles() <= 0)
source_air = air_contents
// Move gas from one place to another
move_gas(source_air, target_air)
air_update_turf()
/obj/item/integrated_circuit/atmospherics/pump/proc/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air)
// No moles = nothing to pump
if(source_air.total_moles() <= 0 || target_air.return_pressure() >= PUMP_MAX_PRESSURE)
return
// Negative Kelvin temperatures should never happen and if they do, normalize them
if(source_air.temperature < TCMB)
source_air.temperature = TCMB
var/pressure_delta = target_pressure - target_air.return_pressure()
if(pressure_delta > 0.1)
var/transfer_moles = (pressure_delta*target_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY
var/datum/gas_mixture/removed = source_air.remove(transfer_moles)
target_air.merge(removed)
// - volume pump - // **Works**
/obj/item/integrated_circuit/atmospherics/pump/volume
name = "volume pump"
desc = "Moves gases between two tanks, canisters, and other gas containers by using their volume, up to 200 L/s."
extended_desc = " Use negative volume to move air from target to source. Note that only part of the gas is moved on each transfer. Its maximum pumping volume is capped at 1000kPa."
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"source" = IC_PINTYPE_REF,
"target" = IC_PINTYPE_REF,
"transfer volume" = IC_PINTYPE_NUMBER
)
activators = list(
"transfer" = IC_PINTYPE_PULSE_IN,
"on transfer" = IC_PINTYPE_PULSE_OUT
)
direction = SOURCE_TO_TARGET
var/transfer_rate = PUMP_MAX_VOLUME
power_draw_per_use = 20
/obj/item/integrated_circuit/atmospherics/pump/volume/update_target(new_amount)
if(!isnum(new_amount))
new_amount = 0
// See in which direction the gas moves
if(new_amount < 0)
direction = TARGET_TO_SOURCE
else
direction = SOURCE_TO_TARGET
target_pressure = min(PUMP_MAX_VOLUME,abs(new_amount))
/obj/item/integrated_circuit/atmospherics/pump/volume/move_gas(datum/gas_mixture/source_air, datum/gas_mixture/target_air)
// No moles = nothing to pump
if(source_air.total_moles() <= 0)
return
// Negative Kelvin temperatures should never happen and if they do, normalize them
if(source_air.temperature < TCMB)
source_air.temperature = TCMB
if((source_air.return_pressure() < 0.01) || (target_air.return_pressure() >= PUMP_MAX_PRESSURE))
return
//The second part of the min caps the pressure built by the volume pumps to the max pump pressure
var/transfer_ratio = min(transfer_rate,target_air.volume*PUMP_MAX_PRESSURE/source_air.return_pressure())/source_air.volume
var/datum/gas_mixture/removed = source_air.remove_ratio(transfer_ratio * PUMP_EFFICIENCY)
target_air.merge(removed)
// - gas vent - // **works**
/obj/item/integrated_circuit/atmospherics/pump/vent
name = "gas vent"
extended_desc = "Use negative volume to move air from target to environment. Note that only part of the gas is moved on each transfer. Unlike the gas pump, this one keeps pumping even further to pressures of 9000 pKa and it is not advised to use it on tank circuits."
desc = "Moves gases between the environment and adjacent gas containers."
inputs = list(
"container" = IC_PINTYPE_REF,
"target pressure" = IC_PINTYPE_NUMBER
)
/obj/item/integrated_circuit/atmospherics/pump/vent/on_data_written()
var/amt = get_pin_data(IC_INPUT, 2)
update_target(amt)
/obj/item/integrated_circuit/atmospherics/pump/vent/do_work()
var/turf/source = get_turf(src)
var/obj/target = get_pin_data_as_type(IC_INPUT, 1, /obj)
perform_magic(source, target)
activate_pin(2)
/obj/item/integrated_circuit/atmospherics/pump/vent/check_gastarget(atom/gasholder)
if(!gasholder)
return FALSE
if(!gasholder.Adjacent(get_object()))
return FALSE
if(!istype(gasholder, /obj/item/tank) && !istype(gasholder, /obj/machinery/portable_atmospherics) && !istype(gasholder, /obj/item/integrated_circuit/atmospherics))
return FALSE
return TRUE
/obj/item/integrated_circuit/atmospherics/pump/vent/check_gassource(atom/target)
if(!target)
return FALSE
if(!istype(target, /turf))
return FALSE
return TRUE
// - integrated connector - // Can connect and disconnect properly
/obj/item/integrated_circuit/atmospherics/connector
name = "integrated connector"
desc = "Creates an airtight seal with standard connectors found on the floor, \
allowing the assembly to exchange gases with a pipe network."
extended_desc = "This circuit will automatically attempt to locate and connect to ports on the floor beneath it when activated. \
You <b>must</b> set a target before connecting."
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"target" = IC_PINTYPE_REF
)
activators = list(
"toggle connection" = IC_PINTYPE_PULSE_IN,
"on connected" = IC_PINTYPE_PULSE_OUT,
"on connection failed" = IC_PINTYPE_PULSE_OUT,
"on disconnected" = IC_PINTYPE_PULSE_OUT
)
var/obj/machinery/atmospherics/components/unary/portables_connector/connector
/obj/item/integrated_circuit/atmospherics/connector/Initialize()
air_contents = new(volume)
START_PROCESSING(SSobj, src)
. = ..()
//Sucks up the gas from the connector
/obj/item/integrated_circuit/atmospherics/connector/process()
set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
/obj/item/integrated_circuit/atmospherics/connector/check_gassource(atom/gasholder)
if(!gasholder)
return FALSE
if(!istype(gasholder,/obj/machinery/atmospherics/components/unary/portables_connector))
return FALSE
return TRUE
//If the assembly containing this is moved from the tile the connector pipe is in, the connection breaks
/obj/item/integrated_circuit/atmospherics/connector/ext_moved()
if(connector)
if(get_dist(get_object(), connector) > 0)
// The assembly is set as connected device and the connector handles the rest
connector.connected_device = null
connector = null
activate_pin(4)
/obj/item/integrated_circuit/atmospherics/connector/do_work()
// If there is a connection, disconnect
if(connector)
connector.connected_device = null
connector = null
activate_pin(4)
return
var/obj/machinery/atmospherics/components/unary/portables_connector/PC = locate() in get_turf(src)
// If no connector can't connect
if(!PC)
activate_pin(3)
return
connector = PC
connector.connected_device = src
activate_pin(2)
// Required for making the connector port script work
obj/item/integrated_circuit/atmospherics/connector/portableConnectorReturnAir()
return air_contents
// - gas filter - // **works**
/obj/item/integrated_circuit/atmospherics/pump/filter
name = "gas filter"
desc = "Filters one gas out of a mixture."
complexity = 20
size = 8
spawn_flags = IC_SPAWN_RESEARCH
inputs = list(
"source" = IC_PINTYPE_REF,
"filtered output" = IC_PINTYPE_REF,
"contaminants output" = IC_PINTYPE_REF,
"wanted gases" = IC_PINTYPE_LIST,
"target pressure" = IC_PINTYPE_NUMBER
)
power_draw_per_use = 30
/obj/item/integrated_circuit/atmospherics/pump/filter/on_data_written()
var/amt = get_pin_data(IC_INPUT, 5)
target_pressure = CLAMP(amt, 0, PUMP_MAX_PRESSURE)
/obj/item/integrated_circuit/atmospherics/pump/filter/do_work()
activate_pin(2)
var/obj/source = get_pin_data_as_type(IC_INPUT, 1, /obj)
var/obj/filtered = get_pin_data_as_type(IC_INPUT, 2, /obj)
var/obj/contaminants = get_pin_data_as_type(IC_INPUT, 3, /obj)
var/wanted = get_pin_data(IC_INPUT, 4)
// If there is no filtered output, this whole thing makes no sense
if(!check_gassource(filtered))
return
var/datum/gas_mixture/filtered_air = filtered.return_air()
if(!filtered_air)
return
// If no source is set, the source is possibly this circuit's content
if(!check_gassource(source))
source = src
var/datum/gas_mixture/source_air = source.return_air()
//No source air: source is this circuit
if(!source_air)
source_air = air_contents
// If no filtering tank is set, filter through itself
if(!check_gassource(contaminants))
contaminants = src
var/datum/gas_mixture/contaminated_air = contaminants.return_air()
//If there is no gas mixture datum for unfiltered, pump the contaminants back into the circuit
if(!contaminated_air)
contaminated_air = air_contents
if(contaminated_air.return_pressure() >= PUMP_MAX_PRESSURE || filtered_air.return_pressure() >= PUMP_MAX_PRESSURE)
return
var/pressure_delta = target_pressure - contaminated_air.return_pressure()
var/transfer_moles
//Negative Kelvins are an anomaly and should be normalized if encountered
if(source_air.temperature < TCMB)
source_air.temperature = TCMB
transfer_moles = (pressure_delta*contaminated_air.volume/(source_air.temperature * R_IDEAL_GAS_EQUATION))*PUMP_EFFICIENCY
//If there is nothing to transfer, just return
if(transfer_moles <= 0)
return
//This is the var that holds the currently filtered part of the gas
var/datum/gas_mixture/removed = source_air.remove(transfer_moles)
if(!removed)
return
//This is the gas that will be moved from source to filtered
var/datum/gas_mixture/filtered_out = new
for(var/filtered_gas in removed.gases)
//Get the name of the gas and see if it is in the list
if(removed.gases[filtered_gas][GAS_META][META_GAS_NAME] in wanted)
//The gas that is put in all the filtered out gases
filtered_out.temperature = removed.temperature
filtered_out.add_gas(filtered_gas)
filtered_out.gases[filtered_gas][MOLES] = removed.gases[filtered_gas][MOLES]
//The filtered out gas is entirely removed from the currently filtered gases
removed.gases[filtered_gas][MOLES] = 0
removed.garbage_collect()
//Check if the pressure is high enough to put stuff in filtered, or else just put it back in the source
var/datum/gas_mixture/target = (filtered_air.return_pressure() < target_pressure ? filtered_air : source_air)
target.merge(filtered_out)
contaminated_air.merge(removed)
/obj/item/integrated_circuit/atmospherics/pump/filter/Initialize()
air_contents = new(volume)
. = ..()
extended_desc = "Remember to properly spell and capitalize the filtered gas name. \
Note that only part of the gas is moved on each transfer, \
so multiple activations will be necessary to achieve target pressure. \
The pressure limit for circuit pumps is [round(PUMP_MAX_PRESSURE)] kPa."
// - gas mixer - // **works**
/obj/item/integrated_circuit/atmospherics/pump/mixer
name = "gas mixer"
desc = "Mixes 2 different types of gases."
complexity = 20
size = 8
spawn_flags = IC_SPAWN_RESEARCH
inputs = list(
"first source" = IC_PINTYPE_REF,
"second source" = IC_PINTYPE_REF,
"output" = IC_PINTYPE_REF,
"first source percentage" = IC_PINTYPE_NUMBER,
"target pressure" = IC_PINTYPE_NUMBER
)
power_draw_per_use = 30
/obj/item/integrated_circuit/atmospherics/pump/mixer/do_work()
activate_pin(2)
var/obj/source_1 = get_pin_data(IC_INPUT, 1)
var/obj/source_2 = get_pin_data(IC_INPUT, 2)
var/obj/gas_output = get_pin_data(IC_INPUT, 3)
if(!check_gassource(source_1))
source_1 = src
if(!check_gassource(source_2))
source_2 = src
if(!check_gassource(gas_output))
gas_output = src
if(source_1 == gas_output || source_2 == gas_output)
return
var/datum/gas_mixture/source_1_gases = source_1.return_air()
var/datum/gas_mixture/source_2_gases = source_2.return_air()
var/datum/gas_mixture/output_gases = gas_output.return_air()
if(!source_1_gases || !source_2_gases || !output_gases)
return
if(output_gases.return_pressure() >= PUMP_MAX_PRESSURE)
return
if(source_1_gases.return_pressure() <= 0 || source_2_gases.return_pressure() <= 0)
return
//This calculates how much should be sent
var/gas_percentage = round(max(min(get_pin_data(IC_INPUT, 4),100),0) / 100)
//Basically: number of moles = percentage of pressure filled up * efficiency coefficient * (pressure from both gases * volume of output) / (R * Temperature)
var/transfer_moles = (get_pin_data(IC_INPUT, 5) / max(1,output_gases.return_pressure())) * PUMP_EFFICIENCY * (source_1_gases.return_pressure() * gas_percentage + source_2_gases.return_pressure() * (1 - gas_percentage)) * output_gases.volume/ (R_IDEAL_GAS_EQUATION * max(output_gases.temperature,TCMB))
if(transfer_moles <= 0)
return
var/datum/gas_mixture/mix = source_1_gases.remove(transfer_moles * gas_percentage)
output_gases.merge(mix)
mix = source_2_gases.remove(transfer_moles * (1-gas_percentage))
output_gases.merge(mix)
// - integrated tank - // **works**
/obj/item/integrated_circuit/atmospherics/tank
name = "integrated tank"
desc = "A small tank for the storage of gases."
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
size = 4
activators = list(
"push ref" = IC_PINTYPE_PULSE_IN
)
volume = 3 //emergency tank sized
var/broken = FALSE
/obj/item/integrated_circuit/atmospherics/tank/Initialize()
air_contents = new(volume)
START_PROCESSING(SSobj, src)
extended_desc = "Take care not to pressurize it above [round(TANK_FAILURE_PRESSURE)] kPa, or else it will break."
. = ..()
/obj/item/integrated_circuit/atmospherics/tank/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/integrated_circuit/atmospherics/tank/do_work()
set_pin_data(IC_OUTPUT, 1, WEAKREF(src))
push_data()
/obj/item/integrated_circuit/atmospherics/tank/process()
var/tank_pressure = air_contents.return_pressure()
set_pin_data(IC_OUTPUT, 2, tank_pressure)
push_data()
//Check if tank broken
if(!broken && tank_pressure > TANK_FAILURE_PRESSURE)
broken = TRUE
to_chat(view(2),"<span class='notice'>The [name] ruptures, releasing its gases!</span>")
if(broken)
release()
/obj/item/integrated_circuit/atmospherics/tank/proc/release()
if(air_contents.total_moles() > 0)
playsound(loc, 'sound/effects/spray.ogg', 10, 1, -3)
var/datum/gas_mixture/expelled_gas = air_contents.remove(air_contents.total_moles())
var/turf/current_turf = get_turf(src)
var/datum/gas_mixture/exterior_gas
if(!current_turf)
return
exterior_gas = current_turf.return_air()
exterior_gas.merge(expelled_gas)
// - large integrated tank - // **works**
/obj/item/integrated_circuit/atmospherics/tank/large
name = "large integrated tank"
desc = "A less small tank for the storage of gases."
volume = 9
size = 12
spawn_flags = IC_SPAWN_RESEARCH
// - freezer tank - // **works**
/obj/item/integrated_circuit/atmospherics/tank/freezer
name = "freezer tank"
desc = "Cools the gas it contains to a preset temperature."
volume = 6
size = 8
inputs = list(
"target temperature" = IC_PINTYPE_NUMBER,
"on" = IC_PINTYPE_BOOLEAN
)
inputs_default = list("1" = 300)
spawn_flags = IC_SPAWN_RESEARCH
var/temperature = 293.15
var/heater_coefficient = 0.1
/obj/item/integrated_circuit/atmospherics/tank/freezer/on_data_written()
temperature = max(73.15,min(293.15,get_pin_data(IC_INPUT, 1)))
if(get_pin_data(IC_INPUT, 2))
power_draw_idle = 30
else
power_draw_idle = 0
/obj/item/integrated_circuit/atmospherics/tank/freezer/process()
var/tank_pressure = air_contents.return_pressure()
set_pin_data(IC_OUTPUT, 2, tank_pressure)
push_data()
//Cool the tank if the power is on and the temp is above
if(!power_draw_idle || air_contents.temperature < temperature)
return
air_contents.temperature = max(73.15,air_contents.temperature - (air_contents.temperature - temperature) * heater_coefficient)
// - heater tank - // **works**
/obj/item/integrated_circuit/atmospherics/tank/freezer/heater
name = "heater tank"
desc = "Heats the gas it contains to a preset temperature."
volume = 6
inputs = list(
"target temperature" = IC_PINTYPE_NUMBER,
"on" = IC_PINTYPE_BOOLEAN
)
spawn_flags = IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/on_data_written()
temperature = max(293.15,min(573.15,get_pin_data(IC_INPUT, 1)))
if(get_pin_data(IC_INPUT, 2))
power_draw_idle = 30
else
power_draw_idle = 0
/obj/item/integrated_circuit/atmospherics/tank/freezer/heater/process()
var/tank_pressure = air_contents.return_pressure()
set_pin_data(IC_OUTPUT, 2, tank_pressure)
push_data()
//Heat the tank if the power is on or its temperature is below what is set
if(!power_draw_idle || air_contents.temperature > temperature)
return
air_contents.temperature = min(573.15,air_contents.temperature + (temperature - air_contents.temperature) * heater_coefficient)
// - atmospheric cooler - // **works**
/obj/item/integrated_circuit/atmospherics/cooler
name = "atmospheric cooler circuit"
desc = "Cools the air around it."
volume = 6
size = 13
spawn_flags = IC_SPAWN_RESEARCH
inputs = list(
"target temperature" = IC_PINTYPE_NUMBER,
"on" = IC_PINTYPE_BOOLEAN
)
var/temperature = 293.15
var/heater_coefficient = 0.1
/obj/item/integrated_circuit/atmospherics/cooler/Initialize()
air_contents = new(volume)
START_PROCESSING(SSobj, src)
. = ..()
/obj/item/integrated_circuit/atmospherics/cooler/Destroy()
STOP_PROCESSING(SSobj, src)
. = ..()
/obj/item/integrated_circuit/atmospherics/cooler/on_data_written()
temperature = max(243.15,min(293.15,get_pin_data(IC_INPUT, 1)))
if(get_pin_data(IC_INPUT, 2))
power_draw_idle = 30
else
power_draw_idle = 0
/obj/item/integrated_circuit/atmospherics/cooler/process()
set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
push_data()
//Get the turf you're on and its gas mixture
var/turf/current_turf = get_turf(src)
if(!current_turf)
return
var/datum/gas_mixture/turf_air = current_turf.return_air()
if(!power_draw_idle || turf_air.temperature < temperature)
return
//Cool the gas
turf_air.temperature = max(243.15,turf_air.temperature - (turf_air.temperature - temperature) * heater_coefficient)
// - atmospheric heater - // **works**
/obj/item/integrated_circuit/atmospherics/cooler/heater
name = "atmospheric heater circuit"
desc = "Heats the air around it."
/obj/item/integrated_circuit/atmospherics/cooler/heater/on_data_written()
temperature = max(293.15,min(323.15,get_pin_data(IC_INPUT, 1)))
if(get_pin_data(IC_INPUT, 2))
power_draw_idle = 30
else
power_draw_idle = 0
/obj/item/integrated_circuit/atmospherics/cooler/heater/process()
set_pin_data(IC_OUTPUT, 2, air_contents.return_pressure())
push_data()
//Get the turf and its air mixture
var/turf/current_turf = get_turf(src)
if(!current_turf)
return
var/datum/gas_mixture/turf_air = current_turf.return_air()
if(!power_draw_idle || turf_air.temperature > temperature)
return
//Heat the gas
turf_air.temperature = min(323.15,turf_air.temperature + (temperature - turf_air.temperature) * heater_coefficient)
// - tank slot - // **works**
/obj/item/integrated_circuit/input/tank_slot
category_text = "Atmospherics"
cooldown_per_use = 1
name = "tank slot"
desc = "Lets you add a tank to your assembly and remove it even when the assembly is closed."
extended_desc = "It can help you extract gases easier."
complexity = 25
size = 30
inputs = list()
outputs = list(
"pressure used" = IC_PINTYPE_NUMBER,
"current tank" = IC_PINTYPE_REF
)
activators = list(
"push ref" = IC_PINTYPE_PULSE_IN,
"on insert" = IC_PINTYPE_PULSE_OUT,
"on remove" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
can_be_asked_input = TRUE
demands_object_input = TRUE
can_input_object_when_closed = TRUE
var/obj/item/tank/internals/current_tank
/obj/item/integrated_circuit/input/tank_slot/Initialize()
START_PROCESSING(SSobj, src)
. = ..()
/obj/item/integrated_circuit/input/tank_slot/process()
push_pressure()
/obj/item/integrated_circuit/input/tank_slot/attackby(var/obj/item/tank/internals/I, var/mob/living/user)
//Check if it truly is a tank
if(!istype(I,/obj/item/tank/internals))
to_chat(user,"<span class='warning'>The [I.name] doesn't seem to fit in here.</span>")
return
//Check if there is no other tank already inside
if(current_tank)
to_chat(user,"<span class='warning'>There is already a gas tank inside.</span>")
return
//The current tank is the one we just attached, its location is inside the circuit
current_tank = I
user.transferItemToLoc(I,src)
to_chat(user,"<span class='warning'>You put the [I.name] inside the tank slot.</span>")
//Set the pin to a weak reference of the current tank
push_pressure()
set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank))
push_data()
do_work(1)
/obj/item/integrated_circuit/input/tank_slot/ask_for_input(mob/user)
attack_self(user)
/obj/item/integrated_circuit/input/tank_slot/attack_self(mob/user)
//Check if no tank attached
if(!current_tank)
to_chat(user, "<span class='notice'>There is currently no tank attached.</span>")
return
//Remove tank and put in user's hands/location
to_chat(user, "<span class='notice'>You take [current_tank] out of the tank slot.</span>")
user.put_in_hands(current_tank)
current_tank = null
//Remove tank reference
push_pressure()
set_pin_data(IC_OUTPUT, 2, null)
push_data()
do_work(2)
/obj/item/integrated_circuit/input/tank_slot/do_work()
set_pin_data(IC_OUTPUT, 2, WEAKREF(current_tank))
push_data()
/obj/item/integrated_circuit/input/tank_slot/proc/push_pressure()
if(!current_tank)
set_pin_data(IC_OUTPUT, 1, 0)
return
var/datum/gas_mixture/tank_air = current_tank.return_air()
if(!tank_air)
set_pin_data(IC_OUTPUT, 1, 0)
return
set_pin_data(IC_OUTPUT, 1, tank_air.return_pressure())
push_data()
#undef SOURCE_TO_TARGET
#undef TARGET_TO_SOURCE
#undef PUMP_EFFICIENCY
#undef TANK_FAILURE_PRESSURE
#undef PUMP_MAX_PRESSURE
#undef PUMP_MAX_VOLUME
@@ -1218,4 +1218,95 @@
activate_pin(2)
else
return FALSE
return TRUE
return TRUE
//Hippie Ported Code--------------------------------------------------------------------------------------------------------
//Adding some color to cards aswell, because why not
/obj/item/card/data/attackby(obj/item/I, mob/living/user)
if(istype(I, /obj/item/integrated_electronics/detailer))
var/obj/item/integrated_electronics/detailer/D = I
detail_color = D.detail_color
update_icon()
return ..()
// -Inputlist- //
/obj/item/integrated_circuit/input/selection
name = "selection circuit"
desc = "This circuit lets you choose between different strings from a selection."
extended_desc = "This circuit lets you choose between up to 4 different values from selection of up to 8 strings that you can set. Null values are ignored and the chosen value is put out in selected."
icon_state = "addition"
can_be_asked_input = 1
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"A" = IC_PINTYPE_STRING,
"B" = IC_PINTYPE_STRING,
"C" = IC_PINTYPE_STRING,
"D" = IC_PINTYPE_STRING,
"E" = IC_PINTYPE_STRING,
"F" = IC_PINTYPE_STRING,
"G" = IC_PINTYPE_STRING,
"H" = IC_PINTYPE_STRING
)
activators = list(
"on selected" = IC_PINTYPE_PULSE_OUT
)
outputs = list(
"selected" = IC_PINTYPE_STRING
)
/obj/item/integrated_circuit/input/selection/ask_for_input(mob/user)
var/list/selection = list()
for(var/k in 1 to inputs.len)
var/I = get_pin_data(IC_INPUT, k)
if(istext(I))
selection.Add(I)
var/selected = input(user,"Choose input.","Selection") in selection
if(!selected)
return
set_pin_data(IC_OUTPUT, 1, selected)
push_data()
activate_pin(1)
// -storage examiner- // **works**
/obj/item/integrated_circuit/input/storage_examiner
name = "storage examiner circuit"
desc = "This circuit lets you scan a storage's content. (backpacks, toolboxes etc.)"
extended_desc = "The items are put out as reference, which makes it possible to interact with them. Additionally also gives the amount of items."
icon_state = "grabber"
can_be_asked_input = 1
complexity = 6
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
inputs = list(
"storage" = IC_PINTYPE_REF
)
activators = list(
"examine" = IC_PINTYPE_PULSE_IN,
"on examined" = IC_PINTYPE_PULSE_OUT
)
outputs = list(
"item amount" = IC_PINTYPE_NUMBER,
"item list" = IC_PINTYPE_LIST
)
power_draw_per_use = 85
/obj/item/integrated_circuit/input/storage_examiner/do_work()
var/obj/item/storage = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
if(!istype(storage,/obj/item/storage))
return
set_pin_data(IC_OUTPUT, 1, storage.contents.len)
var/list/regurgitated_contents = list()
for(var/obj/o in storage.contents)
regurgitated_contents.Add(WEAKREF(o))
set_pin_data(IC_OUTPUT, 2, regurgitated_contents)
push_data()
activate_pin(2)
@@ -677,3 +677,140 @@
GET_COMPONENT(materials, /datum/component/material_container)
materials.retrieve_all()
.=..()
//Hippie Ported Code--------------------------------------------------------------------------------------------------------
// - inserter circuit - //
/obj/item/integrated_circuit/manipulation/inserter
name = "inserter"
desc = "A nimble circuit that puts stuff inside a storage like a backpack and can take it out aswell."
icon_state = "grabber"
extended_desc = "This circuit accepts a reference to an object to be inserted or extracted depending on mode. If a storage is given for extraction, the extracted item will be put in the new storage. Modes: 1 insert, 0 to extract."
w_class = WEIGHT_CLASS_SMALL
size = 3
cooldown_per_use = 5
complexity = 10
inputs = list("target object" = IC_PINTYPE_REF, "target container" = IC_PINTYPE_REF,"mode" = IC_PINTYPE_NUMBER)
activators = list("pulse in" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_RESEARCH
action_flags = IC_ACTION_COMBAT
power_draw_per_use = 20
var/max_items = 10
/obj/item/integrated_circuit/manipulation/inserter/do_work()
//There shouldn't be any target required to eject all contents
var/obj/item/target_obj = get_pin_data_as_type(IC_INPUT, 1, /obj/item)
if(!target_obj)
return
var/distance = get_dist(get_turf(src),get_turf(target_obj))
if(distance > 1 || distance < 0)
return
var/obj/item/storage/container = get_pin_data_as_type(IC_INPUT, 2, /obj/item)
var/mode = get_pin_data(IC_INPUT, 3)
switch(mode)
if(1) //Not working
if(!container || !istype(container,/obj/item/storage) || !Adjacent(container))
return
GET_COMPONENT_FROM(STR, /datum/component/storage, container)
if(!STR)
return
STR.attackby(src, target_obj)
else
GET_COMPONENT_FROM(STR, /datum/component/storage, target_obj.loc)
if(!STR)
return
if(!container || !istype(container,/obj/item/storage) || !Adjacent(container))
STR.remove_from_storage(target_obj,drop_location())
else
STR.remove_from_storage(target_obj,container)
// Renamer circuit. Renames the assembly it is in. Useful in cooperation with telecomms-based circuits.
/obj/item/integrated_circuit/manipulation/renamer
name = "renamer"
desc = "A small circuit that renames the assembly it is in. Useful paired with speech-based circuits."
icon_state = "internalbm"
extended_desc = "This circuit accepts a string as input, and can be pulsed to rewrite the current assembly's name with said string. On success, it pulses the default pulse-out wire."
inputs = list("name" = IC_PINTYPE_STRING)
outputs = list("current name" = IC_PINTYPE_STRING)
activators = list("rename" = IC_PINTYPE_PULSE_IN,"get name" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
power_draw_per_use = 1
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/manipulation/renamer/do_work(var/n)
if(!assembly)
return
switch(n)
if(1)
var/new_name = get_pin_data(IC_INPUT, 1)
if(new_name)
assembly.name = new_name
else
set_pin_data(IC_OUTPUT, 1, assembly.name)
push_data()
activate_pin(3)
// - redescribing circuit - //
/obj/item/integrated_circuit/manipulation/redescribe
name = "redescriber"
desc = "Takes any string as an input and will set it as the assembly's description."
extended_desc = "Strings should can be of any length."
icon_state = "speaker"
cooldown_per_use = 10
complexity = 3
inputs = list("text" = IC_PINTYPE_STRING)
outputs = list("description" = IC_PINTYPE_STRING)
activators = list("redescribe" = IC_PINTYPE_PULSE_IN,"get description" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/manipulation/redescribe/do_work(var/n)
if(!assembly)
return
switch(n)
if(1)
assembly.desc = get_pin_data(IC_INPUT, 1)
else
set_pin_data(IC_OUTPUT, 1, assembly.desc)
push_data()
activate_pin(3)
// - repainting circuit - //
/obj/item/integrated_circuit/manipulation/repaint
name = "auto-repainter"
desc = "There's an oddly high amount of spraying cans fitted right inside this circuit."
extended_desc = "Takes a value in hexadecimal and uses it to repaint the assembly it is in."
cooldown_per_use = 10
complexity = 3
inputs = list("color" = IC_PINTYPE_COLOR)
outputs = list("current color" = IC_PINTYPE_COLOR)
activators = list("repaint" = IC_PINTYPE_PULSE_IN,"get color" = IC_PINTYPE_PULSE_IN,"pulse out" = IC_PINTYPE_PULSE_OUT)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
/obj/item/integrated_circuit/manipulation/repaint/do_work(var/n)
if(!assembly)
return
switch(n)
if(1)
assembly.detail_color = get_pin_data(IC_INPUT, 1)
assembly.update_icon()
else
set_pin_data(IC_OUTPUT, 1, assembly.detail_color)
push_data()
activate_pin(3)
@@ -385,4 +385,30 @@
else
assembly.prefered_hud_icon = "hudstat"
//update the diagnostic hud
assembly.diag_hud_set_circuitstat()
assembly.diag_hud_set_circuitstat()
//Hippie Ported Code--------------------------------------------------------------------------------------------------------
/obj/item/radio/headset/integrated
/obj/item/integrated_circuit/output/screen/large
name = "medium screen"
/obj/item/integrated_circuit/output/screen/extralarge // the subtype is called "extralarge" because tg brought back medium screens and they named the subtype /screen/large
name = "large screen"
desc = "Takes any data type as an input and displays it to the user upon examining, and to all nearby beings when pulsed."
icon_state = "screen_large"
power_draw_per_use = 40
cooldown_per_use = 10
/obj/item/integrated_circuit/output/screen/extralarge/do_work()
..()
var/obj/O = assembly ? get_turf(assembly) : loc
O.visible_message("<span class='notice'>[icon2html(O.icon, world, O.icon_state)] [stuff_to_display]</span>")
if(assembly)
assembly.investigate_log("displayed \"[html_encode(stuff_to_display)]\" with [type].", INVESTIGATE_CIRCUIT)
else
investigate_log("displayed \"[html_encode(stuff_to_display)]\" as [type].", INVESTIGATE_CIRCUIT)
@@ -538,3 +538,250 @@
reagents.handle_reactions()
set_pin_data(IC_OUTPUT, 3, reagents.chem_temp)
push_data()
//Hippie Ported Code--------------------------------------------------------------------------------------------------------
/obj/item/integrated_circuit/reagent/smoke
name = "smoke generator"
desc = "Unlike most electronics, creating smoke is completely intentional."
icon_state = "smoke"
extended_desc = "This smoke generator creates clouds of smoke on command. It can also hold liquids inside, which will go \
into the smoke clouds when activated. The reagents are consumed when the smoke is made."
ext_cooldown = 1
volume = 100
complexity = 20
cooldown_per_use = 1 SECONDS
inputs = list()
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
"self reference" = IC_PINTYPE_SELFREF
)
activators = list(
"create smoke" = IC_PINTYPE_PULSE_IN,
"on smoked" = IC_PINTYPE_PULSE_OUT,
"push ref" = IC_PINTYPE_PULSE_IN
)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 20
var/smoke_radius = 5
var/notified = FALSE
/obj/item/integrated_circuit/reagent/smoke/on_reagent_change(changetype)
//reset warning only if we have reagents now
if(changetype == ADD_REAGENT)
notified = FALSE
push_vol()
/obj/item/integrated_circuit/reagent/smoke/do_work(ord)
switch(ord)
if(1)
if(!reagents || (reagents.total_volume < IC_SMOKE_REAGENTS_MINIMUM_UNITS))
return
var/location = get_turf(src)
var/datum/effect_system/smoke_spread/chem/S = new
S.attach(location)
playsound(location, 'sound/effects/smoke.ogg', 50, 1, -3)
if(S)
S.set_up(reagents, smoke_radius, location, notified)
if(!notified)
notified = TRUE
S.start()
reagents.clear_reagents()
activate_pin(2)
if(3)
set_pin_data(IC_OUTPUT, 2, WEAKREF(src))
push_data()
// - Integrated extinguisher - //
/obj/item/integrated_circuit/reagent/extinguisher
name = "integrated extinguisher"
desc = "This circuit sprays any of its contents out like an extinguisher."
icon_state = "injector"
extended_desc = "This circuit can hold up to 30 units of any given chemicals. On each use, it sprays these reagents like a fire extinguisher."
volume = 30
complexity = 20
cooldown_per_use = 6 SECONDS
inputs = list(
"target X rel" = IC_PINTYPE_NUMBER,
"target Y rel" = IC_PINTYPE_NUMBER
)
outputs = list(
"volume" = IC_PINTYPE_NUMBER,
"self reference" = IC_PINTYPE_SELFREF
)
activators = list(
"spray" = IC_PINTYPE_PULSE_IN,
"on sprayed" = IC_PINTYPE_PULSE_OUT,
"on fail" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
power_draw_per_use = 15
var/busy = FALSE
/obj/item/integrated_circuit/reagent/extinguisher/Initialize()
.=..()
set_pin_data(IC_OUTPUT,2, src)
/obj/item/integrated_circuit/reagent/extinguisher/on_reagent_change(changetype)
push_vol()
/obj/item/integrated_circuit/reagent/extinguisher/do_work()
//Check if enough volume
set_pin_data(IC_OUTPUT, 1, reagents.total_volume)
if(!reagents || reagents.total_volume < 5 || busy)
push_data()
activate_pin(3)
return
playsound(loc, 'sound/effects/extinguish.ogg', 75, 1, -3)
//Get the tile on which the water particle spawns
var/turf/Spawnpoint = get_turf(src)
if(!Spawnpoint)
push_data()
activate_pin(3)
return
//Get direction and target turf for each water particle
var/turf/T = locate(Spawnpoint.x + get_pin_data(IC_INPUT, 1),Spawnpoint.y + get_pin_data(IC_INPUT, 2),Spawnpoint.z)
if(!T)
push_data()
activate_pin(3)
return
var/direction = get_dir(Spawnpoint, T)
var/turf/T1 = get_step(T,turn(direction, 90))
var/turf/T2 = get_step(T,turn(direction, -90))
var/list/the_targets = list(T,T1,T2)
busy = TRUE
// Create list with particles and their targets
var/list/water_particles=list()
for(var/a=0, a<5, a++)
var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water(get_turf(src))
water_particles[W] = pick(the_targets)
var/datum/reagents/R = new/datum/reagents(5)
W.reagents = R
R.my_atom = W
reagents.trans_to(W,1)
//Make em move dat ass, hun
addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, water_particles), 2)
//This whole proc is a loop
/obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles(var/list/particles, var/repetitions=0)
//Check if there's anything in here first
if(!particles || particles.len == 0)
return
// Second loop: Get all the water particles and make them move to their target
for(var/obj/effect/particle_effect/water/W in particles)
var/turf/my_target = particles[W]
if(!W)
continue
step_towards(W,my_target)
if(!W.reagents)
continue
W.reagents.reaction(get_turf(W))
for(var/A in get_turf(W))
W.reagents.reaction(A)
if(W.loc == my_target)
break
if(repetitions < 4)
repetitions++ //Can't have math operations in addtimer(CALLBACK())
addtimer(CALLBACK(src, /obj/item/integrated_circuit/reagent/extinguisher/proc/move_particles, particles, repetitions), 2)
else
push_data()
activate_pin(2)
busy = FALSE
// - Beaker Connector - //
/obj/item/integrated_circuit/input/beaker_connector
category_text = "Reagent"
cooldown_per_use = 1
name = "beaker slot"
desc = "Lets you add a beaker to your assembly and remove it even when the assembly is closed."
icon_state = "reagent_storage"
extended_desc = "It can help you extract reagents easier."
complexity = 4
inputs = list()
outputs = list(
"volume used" = IC_PINTYPE_NUMBER,
"current beaker" = IC_PINTYPE_REF
)
activators = list(
"on insert" = IC_PINTYPE_PULSE_OUT,
"on remove" = IC_PINTYPE_PULSE_OUT,
"push ref" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_DEFAULT|IC_SPAWN_RESEARCH
can_be_asked_input = TRUE
demands_object_input = TRUE
can_input_object_when_closed = TRUE
var/obj/item/reagent_containers/glass/beaker/current_beaker
/obj/item/integrated_circuit/input/beaker_connector/attackby(var/obj/item/reagent_containers/I, var/mob/living/user)
//Check if it truly is a reagent container
if(!istype(I,/obj/item/reagent_containers/glass/beaker))
to_chat(user,"<span class='warning'>The [I.name] doesn't seem to fit in here.</span>")
return
//Check if there is no other beaker already inside
if(current_beaker)
to_chat(user,"<span class='notice'>There is already a reagent container inside.</span>")
return
//The current beaker is the one we just attached, its location is inside the circuit
current_beaker = I
user.transferItemToLoc(I,src)
to_chat(user,"<span class='warning'>You put the [I.name] inside the beaker connector.</span>")
//Set the pin to a weak reference of the current beaker
push_vol()
set_pin_data(IC_OUTPUT, 2, WEAKREF(current_beaker))
push_data()
activate_pin(1)
activate_pin(3)
/obj/item/integrated_circuit/input/beaker_connector/ask_for_input(mob/user)
attack_self(user)
/obj/item/integrated_circuit/input/beaker_connector/attack_self(mob/user)
//Check if no beaker attached
if(!current_beaker)
to_chat(user, "<span class='notice'>There is currently no beaker attached.</span>")
return
//Remove beaker and put in user's hands/location
to_chat(user, "<span class='notice'>You take [current_beaker] out of the beaker connector.</span>")
user.put_in_hands(current_beaker)
current_beaker = null
//Remove beaker reference
push_vol()
set_pin_data(IC_OUTPUT, 2, null)
push_data()
activate_pin(2)
activate_pin(3)
/obj/item/integrated_circuit/input/beaker_connector/proc/push_vol()
var/beakerVolume = 0
if(current_beaker)
beakerVolume = current_beaker.reagents.total_volume
set_pin_data(IC_OUTPUT, 1, beakerVolume)
push_data()
/obj/item/reagent_containers/glass/beaker/on_reagent_change()
..()
if(istype(loc,/obj/item/integrated_circuit/input/beaker_connector))
var/obj/item/integrated_circuit/input/beaker_connector/current_circuit = loc
current_circuit.push_vol()
@@ -114,3 +114,262 @@
set_pin_data(IC_OUTPUT, 2, Yn)
push_data()
activate_pin(2)
//Hippie Ported Code--------------------------------------------------------------------------------------------------------
// - MMI Tank - //
/obj/item/integrated_circuit/input/mmi_tank
name = "man-machine interface tank"
desc = "This circuit is just a jar filled with an artificial liquid mimicking the cerebrospinal fluid."
extended_desc = "This jar can hold 1 man-machine interface and let it take control of some basic functions of the assembly."
complexity = 29
inputs = list("laws" = IC_PINTYPE_LIST)
outputs = list(
"man-machine interface" = IC_PINTYPE_REF,
"direction" = IC_PINTYPE_DIR,
"click target" = IC_PINTYPE_REF
)
activators = list(
"move" = IC_PINTYPE_PULSE_OUT,
"left" = IC_PINTYPE_PULSE_OUT,
"right" = IC_PINTYPE_PULSE_OUT,
"up" = IC_PINTYPE_PULSE_OUT,
"down" = IC_PINTYPE_PULSE_OUT,
"leftclick" = IC_PINTYPE_PULSE_OUT,
"shiftclick" = IC_PINTYPE_PULSE_OUT,
"altclick" = IC_PINTYPE_PULSE_OUT,
"ctrlclick" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 150
can_be_asked_input = TRUE
demands_object_input = TRUE
var/obj/item/mmi/installed_brain
/obj/item/integrated_circuit/input/mmi_tank/attackby(var/obj/item/mmi/O, var/mob/user)
if(!istype(O,/obj/item/mmi))
to_chat(user,"<span class='warning'>You can't put that inside.</span>")
return
if(installed_brain)
to_chat(user,"<span class='warning'>There's already a brain inside.</span>")
return
user.transferItemToLoc(O,src)
installed_brain = O
can_be_asked_input = FALSE
to_chat(user, "<span class='notice'>You gently place \the man-machine interface inside the tank.</span>")
to_chat(O, "<span class='notice'>You are slowly being placed inside the man-machine-interface tank.</span>")
O.brainmob.remote_control=src
set_pin_data(IC_OUTPUT, 1, O)
/obj/item/integrated_circuit/input/mmi_tank/attack_self(var/mob/user)
if(installed_brain)
RemoveBrain()
to_chat(user, "<span class='notice'>You slowly lift [installed_brain] out of the MMI tank.</span>")
playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
installed_brain = null
push_data()
else
to_chat(user, "<span class='notice'>You don't see any brain swimming in the tank.</span>")
/obj/item/integrated_circuit/input/mmi_tank/Destroy()
RemoveBrain()
..()
/obj/item/integrated_circuit/input/mmi_tank/relaymove(var/n,var/dir)
set_pin_data(IC_OUTPUT, 2, dir)
do_work(1)
switch(dir)
if(8) activate_pin(2)
if(4) activate_pin(3)
if(1) activate_pin(4)
if(2) activate_pin(5)
/obj/item/integrated_circuit/input/mmi_tank/do_work(var/n)
push_data()
activate_pin(n)
/obj/item/integrated_circuit/input/mmi_tank/proc/RemoveBrain()
if(installed_brain)
can_be_asked_input = TRUE
installed_brain.forceMove(drop_location())
set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
if(installed_brain.brainmob)
installed_brain.brainmob.remote_control = null
..()
//Brain changes
/mob/living/brain/var/check_bot_self = FALSE
/mob/living/brain/ClickOn(atom/A, params)
..()
if(!istype(remote_control,/obj/item/integrated_circuit/input/mmi_tank))
return
var/obj/item/integrated_circuit/input/mmi_tank/brainholder=remote_control
brainholder.set_pin_data(IC_OUTPUT, 3, A)
var/list/modifiers = params2list(params)
if(modifiers["shift"])
brainholder.do_work(7)
return
if(modifiers["alt"])
brainholder.do_work(8)
return
if(modifiers["ctrl"])
brainholder.do_work(9)
return
if(istype(A,/obj/item/electronic_assembly))
var/obj/item/electronic_assembly/CheckedAssembly = A
if(brainholder in CheckedAssembly.assembly_components)
var/obj/item/electronic_assembly/holdingassembly=A
check_bot_self=TRUE
if(holdingassembly.opened)
holdingassembly.ui_interact(src)
holdingassembly.attack_self(src)
check_bot_self=FALSE
return
brainholder.do_work(6)
/mob/living/brain/canUseTopic()
return check_bot_self
/obj/item/integrated_circuit/smart/advanced_pathfinder/proc/hippie_xor_decrypt()
var/Ps = get_pin_data(IC_INPUT, 4)
if(!Ps)
return
var/list/Pl = json_decode(XorEncrypt(hextostr(Ps, TRUE), SScircuit.cipherkey))
if(Pl&&islist(Pl))
idc.access = Pl
// - pAI connector circuit - //
/obj/item/integrated_circuit/input/pAI_connector
name = "pAI connector circuit"
desc = "This circuit lets you fit in a personal artificial intelligence to give it some form of control over the bot."
extended_desc = "You can wire various functions to it."
complexity = 29
inputs = list("laws" = IC_PINTYPE_LIST)
outputs = list(
"personal artificial intelligence" = IC_PINTYPE_REF,
"direction" = IC_PINTYPE_DIR,
"click target" = IC_PINTYPE_REF
)
activators = list(
"move" = IC_PINTYPE_PULSE_OUT,
"left" = IC_PINTYPE_PULSE_OUT,
"right" = IC_PINTYPE_PULSE_OUT,
"up" = IC_PINTYPE_PULSE_OUT,
"down" = IC_PINTYPE_PULSE_OUT,
"leftclick" = IC_PINTYPE_PULSE_OUT,
"shiftclick" = IC_PINTYPE_PULSE_OUT,
"altclick" = IC_PINTYPE_PULSE_OUT,
"ctrlclick" = IC_PINTYPE_PULSE_OUT,
"shiftctrlclick" = IC_PINTYPE_PULSE_OUT
)
spawn_flags = IC_SPAWN_RESEARCH
power_draw_per_use = 150
can_be_asked_input = TRUE
demands_object_input = TRUE
var/obj/item/paicard/installed_pai
/obj/item/integrated_circuit/input/pAI_connector/attackby(var/obj/item/paicard/O, var/mob/user)
if(!istype(O,/obj/item/paicard))
to_chat(user,"<span class='warning'>You can't put that inside.</span>")
return
if(installed_pai)
to_chat(user,"<span class='warning'>There's already a pAI connected to this.</span>")
return
user.transferItemToLoc(O,src)
installed_pai = O
can_be_asked_input = FALSE
to_chat(user, "<span class='notice'>You slowly connect the circuit's pins to the [installed_pai].</span>")
to_chat(O, "<span class='notice'>You are slowly being connected to the pAI connector.</span>")
O.pai.remote_control=src
set_pin_data(IC_OUTPUT, 1, O)
/obj/item/integrated_circuit/input/pAI_connector/attack_self(var/mob/user)
if(installed_pai)
RemovepAI()
to_chat(user, "<span class='notice'>You slowly disconnect the circuit's pins from the [installed_pai].</span>")
playsound(src, 'sound/items/Crowbar.ogg', 50, 1)
installed_pai = null
push_data()
else
to_chat(user, "<span class='notice'>The connection port is empty.</span>")
/obj/item/integrated_circuit/input/pAI_connector/relaymove(var/n,var/dir)
set_pin_data(IC_OUTPUT, 2, dir)
do_work(1)
switch(dir)
if(8) activate_pin(2)
if(4) activate_pin(3)
if(1) activate_pin(4)
if(2) activate_pin(5)
/obj/item/integrated_circuit/input/pAI_connector/do_work(var/n)
push_data()
activate_pin(n)
/obj/item/integrated_circuit/input/pAI_connector/Destroy()
RemovepAI()
..()
/obj/item/integrated_circuit/input/pAI_connector/proc/RemovepAI()
if(installed_pai)
can_be_asked_input = TRUE
installed_pai.forceMove(drop_location())
set_pin_data(IC_OUTPUT, 1, WEAKREF(null))
installed_pai.pai.remote_control = null
..()
//pAI changes
/mob/living/silicon/pai/var/check_bot_self = FALSE
/mob/living/silicon/pai/ClickOn(atom/A, params)
..()
if(!istype(remote_control,/obj/item/integrated_circuit/input/pAI_connector))
return
var/obj/item/integrated_circuit/input/pAI_connector/paiholder=remote_control
paiholder.set_pin_data(IC_OUTPUT, 3, A)
var/list/modifiers = params2list(params)
if(modifiers["shift"] && modifiers["ctrl"])
paiholder.do_work(10)
return
if(modifiers["shift"])
paiholder.do_work(7)
return
if(modifiers["alt"])
paiholder.do_work(8)
return
if(modifiers["ctrl"])
paiholder.do_work(9)
return
if(istype(A,/obj/item/electronic_assembly))
var/obj/item/electronic_assembly/CheckedAssembly = A
if(paiholder in CheckedAssembly.assembly_components)
var/obj/item/electronic_assembly/holdingassembly=A
check_bot_self=TRUE
if(holdingassembly.opened)
holdingassembly.ui_interact(src)
holdingassembly.attack_self(src)
check_bot_self=FALSE
return
paiholder.do_work(6)
/mob/living/silicon/pai/canUseTopic()
return check_bot_self
@@ -175,7 +175,7 @@
desc = "Tells you what the time is, in terms and adjusted for your local station or planet"
/obj/item/integrated_circuit/time/clock/station/get_time()
return station_time()
return STATION_TIME(FALSE)
/obj/item/integrated_circuit/time/clock/bluespace
name = "integrated clock (Bluespace Absolute Time)"
+311 -298
View File
@@ -1,298 +1,311 @@
// This is where the fun begins.
// These are the main datums that emit light.
/datum/light_source
var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
var/atom/source_atom // The atom that we belong to.
var/turf/source_turf // The turf under the above.
var/turf/pixel_turf // The turf the top_atom appears to over.
var/light_power // Intensity of the emitter light.
var/light_range // The range of the emitted light.
var/light_color // The colour of the light, string, decomposed by parse_light_color()
// Variables for keeping track of the colour.
var/lum_r
var/lum_g
var/lum_b
// The lumcount values used to apply the light.
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
var/list/turf/affecting_turfs
var/applied = FALSE // Whether we have applied our light yet or not.
var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update.
/datum/light_source/New(var/atom/owner, var/atom/top)
source_atom = owner // Set our new owner.
LAZYADD(source_atom.light_sources, src)
top_atom = top
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src)
source_turf = top_atom
pixel_turf = get_turf_pixel(top_atom) || source_turf
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
update()
return ..()
/datum/light_source/Destroy(force)
remove_lum()
if (source_atom)
LAZYREMOVE(source_atom.light_sources, src)
if (top_atom)
LAZYREMOVE(top_atom.light_sources, src)
if (needs_update)
GLOB.lighting_update_lights -= src
. = ..()
// Yes this doesn't align correctly on anything other than 4 width tabs.
// If you want it to go switch everybody to elastic tab stops.
// Actually that'd be great if you could!
#define EFFECT_UPDATE(level) \
if (needs_update == LIGHTING_NO_UPDATE) \
GLOB.lighting_update_lights += src; \
if (needs_update < level) \
needs_update = level; \
// This proc will cause the light source to update the top atom, and add itself to the update queue.
/datum/light_source/proc/update(var/atom/new_top_atom)
// This top atom is different.
if (new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
LAZYREMOVE(top_atom.light_sources, src)
top_atom = new_top_atom
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
/datum/light_source/proc/vis_update()
EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
// Decompile the hexadecimal colour into lumcounts of each perspective.
/datum/light_source/proc/parse_light_color()
if (light_color)
lum_r = GetRedPart (light_color) / 255
lum_g = GetGreenPart (light_color) / 255
lum_b = GetBluePart (light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
// Macro that applies light to a new corner.
// It is a macro in the interest of speed, yet not having to copy paste it.
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
// As such this all gets counted as a single line.
// The braces and semicolons are there to be able to do this on a single line.
#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, pixel_turf); \
. *= light_power; \
var/OLD = effect_str[C]; \
effect_str[C] = .; \
\
C.update_lumcount \
( \
(. * lum_r) - (OLD * applied_lum_r), \
(. * lum_g) - (OLD * applied_lum_g), \
(. * lum_b) - (OLD * applied_lum_b) \
);
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// This is the define used to calculate falloff.
/datum/light_source/proc/remove_lum()
applied = FALSE
var/thing
for (thing in affecting_turfs)
var/turf/T = thing
LAZYREMOVE(T.affecting_lights, src)
affecting_turfs = null
var/datum/lighting_corner/C
for (thing in effect_str)
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str = null
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
LAZYINITLIST(effect_str)
if (effect_str[C]) // Already have one.
REMOVE_CORNER(C)
effect_str[C] = 0
APPLY_CORNER(C)
UNSETEMPTY(effect_str)
/datum/light_source/proc/update_corners()
var/update = FALSE
var/atom/source_atom = src.source_atom
if (QDELETED(source_atom))
qdel(src)
return
if (source_atom.light_power != light_power)
light_power = source_atom.light_power
update = TRUE
if (source_atom.light_range != light_range)
light_range = source_atom.light_range
update = TRUE
if (!top_atom)
top_atom = source_atom
update = TRUE
if (!light_range || !light_power)
qdel(src)
return
if (isturf(top_atom))
if (source_turf != top_atom)
source_turf = top_atom
pixel_turf = source_turf
update = TRUE
else if (top_atom.loc != source_turf)
source_turf = top_atom.loc
pixel_turf = get_turf_pixel(top_atom)
update = TRUE
else
var/P = get_turf_pixel(top_atom)
if (P != pixel_turf)
pixel_turf = P
update = TRUE
if (!isturf(source_turf))
if (applied)
remove_lum()
return
if (light_range && light_power && !applied)
update = TRUE
if (source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
update = TRUE
else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
update = TRUE
if (update)
needs_update = LIGHTING_CHECK_UPDATE
applied = TRUE
else if (needs_update == LIGHTING_CHECK_UPDATE)
return //nothing's changed
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
var/thing
var/datum/lighting_corner/C
var/turf/T
if (source_turf)
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(T in view(CEILING(light_range, 1), source_turf))
for (thing in T.get_corners(source_turf))
C = thing
corners[C] = 0
turfs += T
source_turf.luminosity = oldlum
LAZYINITLIST(affecting_turfs)
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
affecting_turfs += L
for (thing in L)
T = thing
LAZYADD(T.affecting_lights, src)
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
affecting_turfs -= L
for (thing in L)
T = thing
LAZYREMOVE(T.affecting_lights, src)
LAZYINITLIST(effect_str)
if (needs_update == LIGHTING_VIS_UPDATE)
for (thing in corners - effect_str) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
else
L = corners - effect_str
for (thing in L) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for (thing in corners - L) // Existing corners
C = thing
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
L = effect_str - corners
for (thing in L) // Old, now gone, corners.
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str -= L
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
UNSETEMPTY(effect_str)
UNSETEMPTY(affecting_turfs)
#undef EFFECT_UPDATE
#undef LUM_FALLOFF
#undef REMOVE_CORNER
#undef APPLY_CORNER
// This is where the fun begins.
// These are the main datums that emit light.
/datum/light_source
var/atom/top_atom // The atom we're emitting light from (for example a mob if we're from a flashlight that's being held).
var/atom/source_atom // The atom that we belong to.
var/turf/source_turf // The turf under the above.
var/turf/pixel_turf // The turf the top_atom appears to over.
var/light_power // Intensity of the emitter light.
var/light_range // The range of the emitted light.
var/light_color // The colour of the light, string, decomposed by parse_light_color()
// Variables for keeping track of the colour.
var/lum_r
var/lum_g
var/lum_b
// The lumcount values used to apply the light.
var/tmp/applied_lum_r
var/tmp/applied_lum_g
var/tmp/applied_lum_b
var/list/datum/lighting_corner/effect_str // List used to store how much we're affecting corners.
var/list/turf/affecting_turfs
var/applied = FALSE // Whether we have applied our light yet or not.
var/needs_update = LIGHTING_NO_UPDATE // Whether we are queued for an update.
// Thanks to Lohikar for flinging this tiny bit of code at me, increasing my brain cell count from 1 to 2 in the process.
// This macro will only offset up to 1 tile, but anything with a greater offset is an outlier and probably should handle its own lighting offsets.
// Anything pixelshifted 16px or more will be considered on the next tile.
#define GET_APPROXIMATE_PIXEL_DIR(PX, PY) ((!(PX) ? 0 : ((PX >= 16 ? EAST : (PX <= -16 ? WEST : 0)))) | (!PY ? 0 : (PY >= 16 ? NORTH : (PY <= -16 ? SOUTH : 0))))
#define UPDATE_APPROXIMATE_PIXEL_TURF var/_mask = GET_APPROXIMATE_PIXEL_DIR(top_atom.pixel_x, top_atom.pixel_y); pixel_turf = _mask ? (get_step(source_turf, _mask) || source_turf) : source_turf
/datum/light_source/New(var/atom/owner, var/atom/top)
source_atom = owner // Set our new owner.
LAZYADD(source_atom.light_sources, src)
top_atom = top
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src)
source_turf = top_atom
UPDATE_APPROXIMATE_PIXEL_TURF
light_power = source_atom.light_power
light_range = source_atom.light_range
light_color = source_atom.light_color
parse_light_color()
update()
return ..()
/datum/light_source/Destroy(force)
remove_lum()
if (source_atom)
LAZYREMOVE(source_atom.light_sources, src)
if (top_atom)
LAZYREMOVE(top_atom.light_sources, src)
if (needs_update)
GLOB.lighting_update_lights -= src
. = ..()
// Yes this doesn't align correctly on anything other than 4 width tabs.
// If you want it to go switch everybody to elastic tab stops.
// Actually that'd be great if you could!
#define EFFECT_UPDATE(level) \
if (needs_update == LIGHTING_NO_UPDATE) \
GLOB.lighting_update_lights += src; \
if (needs_update < level) \
needs_update = level; \
// This proc will cause the light source to update the top atom, and add itself to the update queue.
/datum/light_source/proc/update(var/atom/new_top_atom)
// This top atom is different.
if (new_top_atom && new_top_atom != top_atom)
if(top_atom != source_atom && top_atom.light_sources) // Remove ourselves from the light sources of that top atom.
LAZYREMOVE(top_atom.light_sources, src)
top_atom = new_top_atom
if (top_atom != source_atom)
LAZYADD(top_atom.light_sources, src) // Add ourselves to the light sources of our new top atom.
EFFECT_UPDATE(LIGHTING_CHECK_UPDATE)
// Will force an update without checking if it's actually needed.
/datum/light_source/proc/force_update()
EFFECT_UPDATE(LIGHTING_FORCE_UPDATE)
// Will cause the light source to recalculate turfs that were removed or added to visibility only.
/datum/light_source/proc/vis_update()
EFFECT_UPDATE(LIGHTING_VIS_UPDATE)
// Decompile the hexadecimal colour into lumcounts of each perspective.
/datum/light_source/proc/parse_light_color()
if (light_color)
lum_r = GetRedPart (light_color) / 255
lum_g = GetGreenPart (light_color) / 255
lum_b = GetBluePart (light_color) / 255
else
lum_r = 1
lum_g = 1
lum_b = 1
// Macro that applies light to a new corner.
// It is a macro in the interest of speed, yet not having to copy paste it.
// If you're wondering what's with the backslashes, the backslashes cause BYOND to not automatically end the line.
// As such this all gets counted as a single line.
// The braces and semicolons are there to be able to do this on a single line.
//Original lighting falloff calculation. This looks the best out of the three. However, this is also the most expensive.
//#define LUM_FALLOFF(C, T) (1 - CLAMP01(sqrt((C.x - T.x) ** 2 + (C.y - T.y) ** 2 + LIGHTING_HEIGHT) / max(1, light_range)))
//Cubic lighting falloff. This has the *exact* same range as the original lighting falloff calculation, down to the exact decimal, but it looks a little unnatural due to the harsher falloff and how it's generally brighter across the board.
//#define LUM_FALLOFF(C, T) (1 - CLAMP01((((C.x - T.x) * (C.x - T.x)) + ((C.y - T.y) * (C.y - T.y)) + LIGHTING_HEIGHT) / max(1, light_range*light_range)))
//Linear lighting falloff. This resembles the original lighting falloff calculation the best, but results in lights having a slightly larger range, which is most noticable with large light sources. This also results in lights being diamond-shaped, fuck. This looks the darkest out of the three due to how lights are brighter closer to the source compared to the original falloff algorithm. This falloff method also does not at all take into account lighting height, as it acts as a flat reduction to light range with this method.
//#define LUM_FALLOFF(C, T) (1 - CLAMP01(((abs(C.x - T.x) + abs(C.y - T.y))) / max(1, light_range+1)))
//Linear lighting falloff but with an octagonal shape in place of a diamond shape. Lummox JR please add pointer support.
#define GET_LUM_DIST(DISTX, DISTY) (DISTX + DISTY + abs(DISTX - DISTY)*0.4)
#define LUM_FALLOFF(C, T) (1 - CLAMP01(max(GET_LUM_DIST(abs(C.x - T.x), abs(C.y - T.y)),LIGHTING_HEIGHT) / max(1, light_range+1)))
#define APPLY_CORNER(C) \
. = LUM_FALLOFF(C, pixel_turf); \
. *= light_power; \
var/OLD = effect_str[C]; \
effect_str[C] = .; \
\
C.update_lumcount \
( \
(. * lum_r) - (OLD * applied_lum_r), \
(. * lum_g) - (OLD * applied_lum_g), \
(. * lum_b) - (OLD * applied_lum_b) \
);
#define REMOVE_CORNER(C) \
. = -effect_str[C]; \
C.update_lumcount \
( \
. * applied_lum_r, \
. * applied_lum_g, \
. * applied_lum_b \
);
// This is the define used to calculate falloff.
/datum/light_source/proc/remove_lum()
applied = FALSE
var/thing
for (thing in affecting_turfs)
var/turf/T = thing
LAZYREMOVE(T.affecting_lights, src)
affecting_turfs = null
var/datum/lighting_corner/C
for (thing in effect_str)
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str = null
/datum/light_source/proc/recalc_corner(var/datum/lighting_corner/C)
LAZYINITLIST(effect_str)
if (effect_str[C]) // Already have one.
REMOVE_CORNER(C)
effect_str[C] = 0
APPLY_CORNER(C)
UNSETEMPTY(effect_str)
/datum/light_source/proc/update_corners()
var/update = FALSE
var/atom/source_atom = src.source_atom
if (QDELETED(source_atom))
qdel(src)
return
if (source_atom.light_power != light_power)
light_power = source_atom.light_power
update = TRUE
if (source_atom.light_range != light_range)
light_range = source_atom.light_range
update = TRUE
if (!top_atom)
top_atom = source_atom
update = TRUE
if (!light_range || !light_power)
qdel(src)
return
if (isturf(top_atom))
if (source_turf != top_atom)
source_turf = top_atom
UPDATE_APPROXIMATE_PIXEL_TURF
update = TRUE
else if (top_atom.loc != source_turf)
source_turf = top_atom.loc
UPDATE_APPROXIMATE_PIXEL_TURF
update = TRUE
if (!isturf(source_turf))
if (applied)
remove_lum()
return
if (light_range && light_power && !applied)
update = TRUE
if (source_atom.light_color != light_color)
light_color = source_atom.light_color
parse_light_color()
update = TRUE
else if (applied_lum_r != lum_r || applied_lum_g != lum_g || applied_lum_b != lum_b)
update = TRUE
if (update)
needs_update = LIGHTING_CHECK_UPDATE
applied = TRUE
else if (needs_update == LIGHTING_CHECK_UPDATE)
return //nothing's changed
var/list/datum/lighting_corner/corners = list()
var/list/turf/turfs = list()
var/thing
var/datum/lighting_corner/C
var/turf/T
if (source_turf)
var/oldlum = source_turf.luminosity
source_turf.luminosity = CEILING(light_range, 1)
for(T in view(CEILING(light_range, 1), source_turf))
for (thing in T.get_corners(source_turf))
C = thing
corners[C] = 0
turfs += T
source_turf.luminosity = oldlum
LAZYINITLIST(affecting_turfs)
var/list/L = turfs - affecting_turfs // New turfs, add us to the affecting lights of them.
affecting_turfs += L
for (thing in L)
T = thing
LAZYADD(T.affecting_lights, src)
L = affecting_turfs - turfs // Now-gone turfs, remove us from the affecting lights.
affecting_turfs -= L
for (thing in L)
T = thing
LAZYREMOVE(T.affecting_lights, src)
LAZYINITLIST(effect_str)
if (needs_update == LIGHTING_VIS_UPDATE)
for (thing in corners - effect_str) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
else
L = corners - effect_str
for (thing in L) // New corners
C = thing
LAZYADD(C.affecting, src)
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
for (thing in corners - L) // Existing corners
C = thing
if (!C.active)
effect_str[C] = 0
continue
APPLY_CORNER(C)
L = effect_str - corners
for (thing in L) // Old, now gone, corners.
C = thing
REMOVE_CORNER(C)
LAZYREMOVE(C.affecting, src)
effect_str -= L
applied_lum_r = lum_r
applied_lum_g = lum_g
applied_lum_b = lum_b
UNSETEMPTY(effect_str)
UNSETEMPTY(affecting_turfs)
#undef EFFECT_UPDATE
#undef LUM_FALLOFF
#undef GET_LUM_DIST
#undef REMOVE_CORNER
#undef APPLY_CORNER
+1 -1
View File
@@ -197,7 +197,7 @@ GLOBAL_LIST_EMPTY(silo_access_logs)
var/list/materials
/datum/ore_silo_log/New(obj/machinery/M, _action, _amount, _noun, list/mats=list())
timestamp = station_time_timestamp()
timestamp = STATION_TIME_TIMESTAMP("hh:mm:ss")
machine_name = M.name
area_name = get_area_name(M, TRUE)
action = _action
+10 -3
View File
@@ -18,6 +18,7 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
invisibility = INVISIBILITY_OBSERVER
hud_type = /datum/hud/ghost
var/can_reenter_corpse
var/can_reenter_round = TRUE
var/datum/hud/living/carbon/hud = null // hud
var/bootime = 0
var/started_as_observer //This variable is set to 1 when you enter the game as an observer.
@@ -266,6 +267,7 @@ Works together with spawning an observer, noted above.
var/mob/dead/observer/ghost = new(src) // Transfer safety to observer spawning proc.
SStgui.on_transfer(src, ghost) // Transfer NanoUIs.
ghost.can_reenter_corpse = can_reenter_corpse
ghost.can_reenter_round = (can_reenter_corpse && !suiciding)
ghost.key = key
return ghost
@@ -280,7 +282,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
// CITADEL EDIT
if(istype(loc, /obj/machinery/cryopod))
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")//darn copypaste
return
var/obj/machinery/cryopod/C = loc
@@ -293,17 +295,18 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(stat == DEAD)
ghostize(1)
else
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return //didn't want to ghost after-all
ghostize(0) //0 parameter is so we can never re-enter our body, "Charlie, you can never come baaaack~" :3
suicide_log(TRUE)
/mob/camera/verb/ghost()
set category = "OOC"
set name = "Ghost"
set desc = "Relinquish your life and enter the land of the dead."
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you may not play again this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
var/response = alert(src, "Are you -sure- you want to ghost?\n(You are alive. If you ghost whilst still alive you won't be able to re-enter this round! You can't change your mind so choose wisely!!)","Are you sure you want to ghost?","Ghost","Stay in body")
if(response != "Ghost")
return
ghostize(0)
@@ -617,6 +620,10 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
to_chat(src, "<span class='warning'>This isn't really a creature, now is it!</span>")
return 0
if(!can_reenter_round)
to_chat(src, "<span class='warning'>You are unable to re-enter the round.</span>")
return FALSE
if(can_reenter_corpse && mind && mind.current)
if(alert(src, "Your soul is still tied to your former life as [mind.current.name], if you go forward there is no going back to that life. Are you sure you wish to continue?", "Move On", "Yes", "No") == "No")
return 0
@@ -34,6 +34,7 @@
var/failed_last_breath = 0 //This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/co2overloadtime = null
var/o2overloadtime = null //for Ash walker's weaker lungs, and future atmosia hazards
var/temperature_resistance = T0C+75
var/obj/item/reagent_containers/food/snacks/meat/slab/type_of_meat = /obj/item/reagent_containers/food/snacks/meat/slab
@@ -341,8 +341,13 @@
else if(isobserver(user) && traitstring)
msg += "<span class='info'><b>Traits:</b> [traitstring]</span><br>"
if(print_flavor_text() && get_visible_name() != "Unknown")//Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
msg += "[print_flavor_text()]\n"
if(print_flavor_text())
if(get_visible_name() == "Unknown") //Are we sure we know who this is? Don't show flavor text unless we can recognize them. Prevents certain metagaming with impersonation.
msg += "...?<br>"
else if(skipface) //Sometimes we're not unknown, but impersonating someone in a hardsuit, let's not reveal our flavor text then either.
msg += "...?<br>"
else
msg += "[print_flavor_text()]\n"
msg += "*---------*</span>"
to_chat(user, msg)
@@ -84,7 +84,7 @@
var/obj/item/clothing/suit/space/space_ninja/SN = wear_suit
if(statpanel("SpiderOS"))
stat("SpiderOS Status:","[SN.s_initialized ? "Initialized" : "Disabled"]")
stat("Current Time:", "[station_time_timestamp()]")
stat("Current Time:", "[STATION_TIME_TIMESTAMP("hh:mm:ss")]")
if(SN.s_initialized)
//Suit gear
stat("Energy Charge:", "[round(SN.cell.charge/100)]%")
@@ -438,7 +438,7 @@
return
else if(!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, station_time_timestamp())
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMinorCrime(R.fields["id"], crime)
investigate_log("New Minor Crime: <strong>[t1]</strong>: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "<span class='notice'>Successfully added a minor crime.</span>")
@@ -454,7 +454,7 @@
return
else if (!istype(H.glasses, /obj/item/clothing/glasses/hud/security) && !istype(H.getorganslot(ORGAN_SLOT_HUD), /obj/item/organ/cyberimp/eyes/hud/security))
return
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, station_time_timestamp())
var/crime = GLOB.data_core.createCrimeEntry(t1, t2, allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"))
GLOB.data_core.addMajorCrime(R.fields["id"], crime)
investigate_log("New Major Crime: <strong>[t1]</strong>: [t2] | Added to [R.fields["name"]] by [key_name(usr)]", INVESTIGATE_RECORDS)
to_chat(usr, "<span class='notice'>Successfully added a major crime.</span>")
@@ -487,7 +487,7 @@
var/counter = 1
while(R.fields[text("com_[]", counter)])
counter++
R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []<BR>[]", allowed_access, station_time_timestamp(), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
R.fields[text("com_[]", counter)] = text("Made by [] on [] [], []<BR>[]", allowed_access, STATION_TIME_TIMESTAMP("hh:mm:ss"), time2text(world.realtime, "MMM DD"), GLOB.year_integer+540, t1)
to_chat(usr, "<span class='notice'>Successfully added comment.</span>")
return
to_chat(usr, "<span class='warning'>Unable to locate a data core entry for this person.</span>")
@@ -4,7 +4,9 @@
if(def_zone)
if(isbodypart(def_zone))
return checkarmor(def_zone, type)
var/obj/item/bodypart/bp = def_zone
if(bp.body_part)
return checkarmor(def_zone, type)
var/obj/item/bodypart/affecting = get_bodypart(ran_zone(def_zone))
return checkarmor(affecting, type)
//If a specific bodypart is targetted, check how that bodypart is protected and return the value.
+23 -10
View File
@@ -724,10 +724,10 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/list/colorlist = list()
colorlist.Cut()
colorlist += ReadRGB(H.dna.features["mcolor"])
colorlist += ReadRGB(H.dna.features["mcolor2"])
colorlist += ReadRGB(H.dna.features["mcolor3"])
colorlist += list(0,0,0)
colorlist += ReadRGB("[H.dna.features["mcolor"]]0")
colorlist += ReadRGB("[H.dna.features["mcolor2"]]0")
colorlist += ReadRGB("[H.dna.features["mcolor3"]]0")
colorlist += list(0,0,0, hair_alpha)
for(var/index=1, index<=colorlist.len, index++)
colorlist[index] = colorlist[index]/255
@@ -774,11 +774,10 @@ GLOBAL_LIST_EMPTY(roundstart_races)
husklist += ReadRGB("#a3a3a3")
husklist += ReadRGB("#a3a3a3")
husklist += ReadRGB("#a3a3a3")
husklist += list(0,0,0)
husklist += list(0,0,0, hair_alpha)
for(var/index=1, index<=husklist.len, index++)
husklist[index] = husklist[index]/255
accessory_overlay.color = husklist
standing += accessory_overlay
if(S.hasinner)
@@ -1143,7 +1142,15 @@ GLOBAL_LIST_EMPTY(roundstart_races)
H.add_trait(TRAIT_FAT, OBESITY)
H.update_inv_w_uniform()
H.update_inv_wear_suit()
if(H.noisy && H.nutrition <= NUTRITION_LEVEL_STARVING)
if(prob(10))
playsound(get_turf(H),"hunger_sounds",35,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
else if(H.noisy && H.nutrition <= NUTRITION_LEVEL_HUNGRY)
if(prob(10))
playsound(get_turf(H),"hunger_sounds",15,0,-5,1,ignore_walls = FALSE,channel=CHANNEL_PRED)
// nutrition decrease and satiety
if (H.nutrition > 0 && H.stat != DEAD && !H.has_trait(TRAIT_NOHUNGER))
// THEY HUNGER
@@ -1653,13 +1660,19 @@ GLOBAL_LIST_EMPTY(roundstart_races)
var/obj/item/bodypart/BP = null
if(isbodypart(def_zone))
BP = def_zone
if(damagetype == STAMINA && istype(def_zone, /obj/item/bodypart/head))
BP = H.get_bodypart(check_zone(BODY_ZONE_CHEST))
else
BP = def_zone
else
if(!def_zone)
def_zone = ran_zone(def_zone)
if(damagetype == STAMINA && def_zone == BODY_ZONE_HEAD)
def_zone = BODY_ZONE_CHEST
BP = H.get_bodypart(check_zone(def_zone))
if(!BP)
BP = H.bodyparts[1]
if(!BP)
BP = H.bodyparts[1]
switch(damagetype)
if(BRUTE)
@@ -3,8 +3,8 @@
id = "human"
default_color = "FFFFFF"
species_traits = list(EYECOLOR,HAIR,FACEHAIR,LIPS,MUTCOLORS_PARTSONLY)
mutant_bodyparts = list("mam_ears", "mam_tail", "wings", "taur") // CITADEL EDIT gives humans snowflake parts
default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None", "wings" = "None", "taur" = "None")
mutant_bodyparts = list("ears", "tail_human", "wings", "taur") // CITADEL EDIT gives humans snowflake parts
default_features = list("mcolor" = "FFF", "mcolor2" = "FFF","mcolor3" = "FFF","tail_human" = "None", "ears" = "None", "wings" = "None", "taur" = "None")
use_skintones = 1
skinned_type = /obj/item/stack/sheet/animalhide/human
disliked_food = GROSS | RAW
@@ -5,8 +5,8 @@
default_color = "00FF90"
say_mod = "chirps"
species_traits = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR,NOBLOOD)
mutant_bodyparts = list("mam_tail", "mam_ears", "taur") //CIT CHANGE
default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None") //CIT CHANGE
mutant_bodyparts = list("mam_tail", "mam_ears", "mam_snouts", "taur") //CIT CHANGE
default_features = list("mcolor" = "FFF", "mam_tail" = "None", "mam_ears" = "None", "mam_snouts" = "None", "taur" = "None") //CIT CHANGE
inherent_traits = list(TRAIT_TOXINLOVER)
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/slime
exotic_blood = "slimejelly"
@@ -91,6 +91,7 @@
id = "ashlizard"
limbs_id = "lizard"
species_traits = list(MUTCOLORS,EYECOLOR,LIPS,DIGITIGRADE)
inherent_traits = list(TRAIT_NOGUNS,TRAIT_NOBREATH)
inherent_traits = list(TRAIT_NOGUNS)
mutantlungs = /obj/item/organ/lungs/ashwalker
burnmod = 0.9
brutemod = 0.9
+15
View File
@@ -141,6 +141,7 @@
return 0
var/safe_oxy_min = 16
var/safe_oxy_max = 50
var/safe_co2_max = 10
var/safe_tox_max = 0.05
var/SA_para_min = 1
@@ -156,6 +157,19 @@
//OXYGEN
if(O2_partialpressure > safe_oxy_max) // Too much Oxygen - blatant CO2 effect copy/pasta
if(!o2overloadtime)
o2overloadtime = world.time
else if(world.time - o2overloadtime > 120)
Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
adjustOxyLoss(3)
if(world.time - o2overloadtime > 300)
adjustOxyLoss(8)
if(prob(20))
emote("cough")
throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
if(O2_partialpressure < safe_oxy_min) //Not enough oxygen
if(prob(20))
emote("gasp")
@@ -172,6 +186,7 @@
else //Enough oxygen
failed_last_breath = 0
o2overloadtime = 0 //reset our counter for this too
if(health >= crit_threshold)
adjustOxyLoss(-5)
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]
+4 -1
View File
@@ -8,6 +8,8 @@
spill_organs(no_brain, no_organs, no_bodyparts)
release_vore_contents(silent = TRUE) // return of the bomb safe internals.
if(!no_bodyparts)
spread_bodyparts(no_brain, no_organs)
@@ -36,6 +38,7 @@
buckled.unbuckle_mob(src, force = TRUE)
dust_animation()
release_vore_contents(silent = TRUE) //technically grief protection, I guess? if they're SM'd it doesn't matter seconds after anyway.
spawn_dust(just_ash)
QDEL_IN(src,5) // since this is sometimes called in the middle of movement, allow half a second for movement to finish, ghosting to happen and animation to play. Looks much nicer and doesn't cause multiple runtimes.
@@ -50,7 +53,7 @@
stat = DEAD
unset_machine()
timeofdeath = world.time
tod = station_time_timestamp()
tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
var/turf/T = get_turf(src)
for(var/obj/item/I in contents)
I.on_mob_death(src, gibbed)
+2 -1
View File
@@ -312,7 +312,8 @@
return TRUE
/mob/living/verb/succumb(whispered as null)
set hidden = TRUE
set name = "Succumb"
set category = "IC"
if (InCritical())
log_message("Has [whispered ? "whispered his final words" : "succumbed to death"] while in [InFullCritical() ? "hard":"soft"] critical with [round(health, 0.1)] points of health!", LOG_ATTACK)
adjustOxyLoss(health - HEALTH_THRESHOLD_DEAD)
+13 -1
View File
@@ -127,8 +127,18 @@
IgniteMob()
/mob/living/proc/grabbedby(mob/living/carbon/user, supress_message = 0)
if(user == src || anchored || !isturf(user.loc))
if(user == anchored || !isturf(user.loc))
return FALSE
if(user.pulling && user.grab_state == GRAB_AGGRESSIVE && user.voremode)
if(ismob(user.pulling))
var/mob/P = user.pulling
user.vore_attack(user, P, src) // User, Pulled, Predator target (which can be user, pulling, or src)
return
if(user == src) //we want to be able to self click if we're voracious
return FALSE
if(!user.pulling || user.pulling != src)
user.start_pulling(src, supress_message)
return
@@ -163,6 +173,8 @@
return 0
if(!user.pulling || user.pulling != src || user.grab_state != old_grab_state || user.a_intent != INTENT_GRAB)
return 0
if(user.voremode && user.grab_state == GRAB_AGGRESSIVE)
return 0
user.grab_state++
switch(user.grab_state)
if(GRAB_AGGRESSIVE)
+3 -1
View File
@@ -22,4 +22,6 @@
to_chat(src, "<span class='notice'>You can ventcrawl! Use alt+click on vents to quickly travel about the station.</span>")
if(ranged_ability)
ranged_ability.add_ranged_ability(src, "<span class='notice'>You currently have <b>[ranged_ability]</b> active!</span>")
ranged_ability.add_ranged_ability(src, "<span class='notice'>You currently have <b>[ranged_ability]</b> active!</span>")
if(vore_init && !vorepref_init) //Vore's been initialized, voreprefs haven't. If this triggers then that means that voreprefs failed to load due to the client being missing.
apply_vore_prefs()
@@ -63,6 +63,9 @@
..()
/mob/living/simple_animal/mouse/handle_automated_action()
if(isbelly(loc))
return
if(prob(chew_probability))
var/turf/open/floor/F = get_turf(src)
if(istype(F) && !F.intact)
@@ -100,7 +100,7 @@
if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
return
var/target_dir = get_dir(src,target)
var/static/list/cardinal_sidestep_directions = list(-90,-45,0,45,90)
var/static/list/diagonal_sidestep_directions = list(-45,0,45)
var/chosen_dir = 0
@@ -202,6 +202,8 @@
if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
return FALSE
if(isbelly(the_target.loc)) //Target's inside a gut, forget about it too
return FALSE
if(search_objects < 2)
if(isliving(the_target))
var/mob/living/L = the_target
@@ -301,7 +303,7 @@
if(target)
if(targets_from && isturf(targets_from.loc) && target.Adjacent(targets_from)) //If they're next to us, attack
MeleeAction()
else
else
if(rapid_melee > 1 && target_distance <= melee_queue_distance)
MeleeAction(FALSE)
in_melee = FALSE //If we're just preparing to strike do not enter sidestep mode
@@ -83,18 +83,12 @@
if(L.stat != DEAD)
if(!client && ranged && ranged_cooldown <= world.time)
OpenFire()
else if(L.stat >= SOFT_CRIT)
if(vore_active == TRUE && L.devourable == TRUE)
if(isliving(target) && !target.Adjacent(targets_from))
return
else
if(L.Adjacent(src) && (L.stat != CONSCIOUS))
if(vore_active && L.devourable == TRUE)
dragon_feeding(src,L)
if(L.loc == src.contents)
LoseTarget()
return 0
else
devour(L)
LoseTarget()
else
devour(L)
/mob/living/simple_animal/hostile/megafauna/proc/devour(mob/living/L)
if(!L)
+1 -1
View File
@@ -284,5 +284,5 @@
emote("deathgasp")
add_trait(TRAIT_FAKEDEATH, source)
add_trait(TRAIT_DEATHCOMA, source)
tod = station_time_timestamp()
tod = STATION_TIME_TIMESTAMP("hh:mm:ss")
update_stat()
+7 -6
View File
@@ -43,12 +43,13 @@
if(client)
client.change_view(CONFIG_GET(string/default_view)) // Resets the client.view in case it was changed.
if(client.player_details.player_actions.len)
for(var/datum/action/A in client.player_details.player_actions)
A.Grant(src)
if(client.player_details && istype(client.player_details))
if(client.player_details.player_actions.len)
for(var/datum/action/A in client.player_details.player_actions)
A.Grant(src)
for(var/foo in client.player_details.post_login_callbacks)
var/datum/callback/CB = foo
CB.Invoke()
for(var/foo in client.player_details.post_login_callbacks)
var/datum/callback/CB = foo
CB.Invoke()
log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP)
+2 -2
View File
@@ -533,8 +533,8 @@
stat(null, "Next Map: [cached.map_name]")
stat(null, "Round ID: [GLOB.round_id ? GLOB.round_id : "NULL"]")
stat(null, "Server Time: [time2text(world.timeofday, "YYYY-MM-DD hh:mm:ss")]")
stat(null, "Round Time: [worldtime2text()]")
stat(null, "Station Time: [station_time_timestamp()]")
stat(null, "Round Time: [WORLDTIME2TEXT("hh:mm:ss")]")
stat(null, "Station Time: [STATION_TIME_TIMESTAMP("hh:mm:ss")]")
stat(null, "Time Dilation: [round(SStime_track.time_dilation_current,1)]% AVG:([round(SStime_track.time_dilation_avg_fast,1)]%, [round(SStime_track.time_dilation_avg,1)]%, [round(SStime_track.time_dilation_avg_slow,1)]%)")
if(SSshuttle.emergency)
var/ETA = SSshuttle.emergency.getModeStr()
+4 -21
View File
@@ -29,27 +29,10 @@
/proc/ran_zone(zone, probability = 80)
zone = check_zone(zone)
if(prob(probability))
return zone
var/t = rand(1, 18) // randomly pick a different zone, or maybe the same one
switch(t)
if(1)
return BODY_ZONE_HEAD
if(2)
return BODY_ZONE_CHEST
if(3 to 6)
return BODY_ZONE_L_ARM
if(7 to 10)
return BODY_ZONE_R_ARM
if(11 to 14)
return BODY_ZONE_L_LEG
if(15 to 18)
return BODY_ZONE_R_LEG
zone = check_zone(zone)
else
zone = pickweight(list(BODY_ZONE_HEAD = 6, BODY_ZONE_CHEST = 6, BODY_ZONE_L_ARM = 22, BODY_ZONE_R_ARM = 22, BODY_ZONE_L_LEG = 22, BODY_ZONE_R_LEG = 22))
return zone
/proc/above_neck(zone)
@@ -491,7 +474,7 @@ It's fairly easy to fix if dealing with single letters but not so much with comp
else
colored_message = "<font color='[color]'>[message]</font>"
var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[time_stamp()]\] [key_name(src)] [loc_name(src)]" = colored_message)
var/list/timestamped_message = list("[LAZYLEN(logging[smessage_type]) + 1]\[[TIME_STAMP("hh:mm:ss", FALSE)]\] [key_name(src)] [loc_name(src)]" = colored_message)
logging[smessage_type] += timestamped_message
+6 -3
View File
@@ -96,7 +96,9 @@
mind.transfer_to(O)
var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null)
var/obj/effect/proc_holder/changeling/humanform/HF = new /obj/effect/proc_holder/changeling/humanform(null)
changeling.purchasedpowers += HF
HF.action.Grant(O)
for(var/X in internal_organs)
var/obj/item/organ/I = X
@@ -129,8 +131,9 @@
mind.transfer_to(O)
var/datum/antagonist/changeling/changeling = O.mind.has_antag_datum(/datum/antagonist/changeling)
if(changeling)
changeling.purchasedpowers += new /obj/effect/proc_holder/changeling/humanform(null)
var/obj/effect/proc_holder/changeling/humanform/HF = new /obj/effect/proc_holder/changeling/humanform(null)
changeling.purchasedpowers += HF
HF.action.Grant(O)
if (tr_flags & TR_DEFAULTMSG)
to_chat(O, "<B>You are now a monkey.</B>")
@@ -19,12 +19,12 @@
return ..()
/datum/ntnet_conversation/proc/add_message(message, username)
message = "[station_time_timestamp()] [username]: [message]"
message = "[STATION_TIME_TIMESTAMP("hh:mm:ss")] [username]: [message]"
messages.Add(message)
trim_message_list()
/datum/ntnet_conversation/proc/add_status_message(message)
messages.Add("[station_time_timestamp()] -!- [message]")
messages.Add("[STATION_TIME_TIMESTAMP("hh:mm:ss")] -!- [message]")
trim_message_list()
/datum/ntnet_conversation/proc/trim_message_list()
@@ -333,7 +333,7 @@
data["PC_programheaders"] = program_headers
data["PC_stationtime"] = station_time_timestamp()
data["PC_stationtime"] = STATION_TIME_TIMESTAMP("hh:mm:ss")
data["PC_hasheader"] = 1
data["PC_showexitprogram"] = active_program ? 1 : 0 // Hides "Exit Program" button on mainscreen
return data
@@ -159,7 +159,7 @@
<br>
[GLOB.data_core ? GLOB.data_core.get_manifest(0) : ""]
"}
if(!printer.print_text(contents,text("crew manifest ([])", station_time_timestamp())))
if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss"))))
to_chat(usr, "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>")
return
else
@@ -145,7 +145,7 @@
t = replacetext(t, "\[/i\]", "</I>")
t = replacetext(t, "\[u\]", "<U>")
t = replacetext(t, "\[/u\]", "</U>")
t = replacetext(t, "\[time\]", "[station_time_timestamp()]")
t = replacetext(t, "\[time\]", "[STATION_TIME_TIMESTAMP("hh:mm:ss")]")
t = replacetext(t, "\[date\]", "[time2text(world.realtime, "MMM DD")] [GLOB.year_integer+540]")
t = replacetext(t, "\[large\]", "<font size=\"4\">")
t = replacetext(t, "\[/large\]", "</font>")
+2 -1
View File
@@ -18,6 +18,7 @@
var/def_zone = "" //Aiming at
var/atom/movable/firer = null//Who shot it
var/suppressed = FALSE //Attack message
var/candink = FALSE //Can this projectile play the dink sound when hitting the head?
var/yo = null
var/xo = null
var/atom/original = null // the original target clicked
@@ -185,7 +186,7 @@
playsound(loc, hitsound, volume, 1, -1)
L.visible_message("<span class='danger'>[L] is hit by \a [src][organ_hit_text]!</span>", \
"<span class='userdanger'>[L] is hit by \a [src][organ_hit_text]!</span>", null, COMBAT_MESSAGE_RANGE)
if(def_zone == BODY_ZONE_HEAD)
if(candink && def_zone == BODY_ZONE_HEAD)
playsound(src, 'sound/weapons/dink.ogg', 30, 1)
L.on_hit(src)
@@ -4,6 +4,7 @@
damage = 60
damage_type = BRUTE
nodamage = FALSE
candink = TRUE
flag = "bullet"
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
@@ -5,6 +5,7 @@
knockdown = 60
forcedodge = TRUE
nodamage = TRUE
candink = FALSE
hitsound = 'sound/items/bikehorn.ogg'
icon = 'icons/obj/hydroponics/harvest.dmi'
icon_state = "banana"
@@ -49,14 +49,33 @@
"bromine",
"stable_plasma"
)
var/list/emagged_reagents = list(
"space_drugs",
"morphine",
"carpotoxin",
//these become available once upgraded.
var/list/upgrade_reagents = list(
"oil",
"ammonia"
)
var/list/upgrade_reagents2 = list(
"acetone",
"phenol",
"diethylamine"
)
var/list/upgrade_reagents3 = list(
"glycerol",
"mine_salve",
"toxin"
)
var/list/emagged_reagents = list(
"space_drugs",
"plasma",
"frostoil",
"carpotoxin",
"histamine",
"morphine"
)
var/list/saved_recipes = list()
/obj/machinery/chem_dispenser/Initialize()
@@ -74,7 +93,17 @@
..()
if(panel_open)
to_chat(user, "<span class='notice'>[src]'s maintenance hatch is open!</span>")
if(in_range(user, src) || isobserver(user))
to_chat(user, "<span class='notice'>The status display reads: <br>Recharging <b>[recharge_amount]</b> power units per interval.<br>Power efficiency increased by <b>[(powerefficiency*1000)-100]%</b>.<span>")
switch(macrotier)
if(1)
to_chat(user, "<span class='notice'>Macro granularity at <b>5u</b>.<span>")
if(2)
to_chat(user, "<span class='notice'>Macro granularity at <b>3u</b>.<span>")
if(3)
to_chat(user, "<span class='notice'>Macro granularity at <b>2u</b>.<span>")
if(4)
to_chat(user, "<span class='notice'>Macro granularity at <b>1u</b>.<span>")
/obj/machinery/chem_dispenser/process()
if (recharge_counter >= 4)
if(!is_operational())
@@ -111,7 +140,6 @@
add_overlay(beaker_overlay)
/obj/machinery/chem_dispenser/emag_act(mob/user)
if(obj_flags & EMAGGED)
to_chat(user, "<span class='warning'>[src] has no functional safeties to emag.</span>")
@@ -346,6 +374,12 @@
for(var/obj/item/stock_parts/manipulator/M in component_parts)
if (M.rating > macrotier)
macrotier = M.rating
if (M.rating > 1)
dispensable_reagents |= upgrade_reagents
if (M.rating > 2)
dispensable_reagents |= upgrade_reagents2
if (M.rating > 3)
dispensable_reagents |= upgrade_reagents3
powerefficiency = round(newpowereff, 0.01)
@@ -447,14 +481,19 @@
"tomatojuice",
"lemonjuice",
"menthol"
)
) //prevents the soda machine from obtaining chemical upgrades. .
upgrade_reagents = null
upgrade_reagents2 = null
upgrade_reagents3 = null
emagged_reagents = list(
"thirteenloko",
"changelingsting",
"whiskeycola",
"mindbreaker",
"tirizene"
)
/obj/machinery/chem_dispenser/drinks/fullupgrade //fully ugpraded stock parts, emagged
desc = "Contains a large reservoir of soft drinks. This model has had its safeties shorted out."
obj_flags = CAN_BE_HIT | EMAGGED
@@ -497,13 +536,20 @@
"creme_de_cacao",
"triple_sec",
"sake"
)
)//prevents the booze machine from obtaining chemical upgrades.
upgrade_reagents = null
upgrade_reagents2 = null
upgrade_reagents3 = null
emagged_reagents = list(
"ethanol",
"iron",
"alexander",
"clownstears",
"minttoxin",
"atomicbomb",
"fernet"
"fernet",
"aphro",
"aphro+"
)
/obj/machinery/chem_dispenser/drinks/beer/fullupgrade //fully ugpraded stock parts, emagged
@@ -528,6 +574,7 @@
name = "mutagen dispenser"
desc = "Creates and dispenses mutagen."
dispensable_reagents = list("mutagen")
upgrade_reagents = null
emagged_reagents = list("plasma")
@@ -550,7 +597,11 @@
"ammonia",
"ash",
"diethylamine")
//same as above.
upgrade_reagents = null
upgrade_reagents2 = null
upgrade_reagents3 = null
/obj/machinery/chem_dispenser/mutagensaltpeter/Initialize()
. = ..()
component_parts = list()
@@ -745,3 +745,37 @@
taste_description = "sweet pomegranates"
glass_name = "glass of grenadine"
glass_desc = "Delicious flavored syrup."
/datum/reagent/consumable/pinkmilk
name = "Strawberry Milk"
id = "pinkmilk"
description = "A drink of a bygone era of milk and artificial sweetener back on a rock."
color = "#f76aeb"//rgb(247, 106, 235)
glass_icon_state = "pinkmilk"
quality = DRINK_FANTASTIC //Love drink
taste_description = "sweet strawberry and milk cream"
glass_name = "tall glass of strawberry milk"
glass_desc = "Delicious flavored strawberry syrup mixed with milk."
/datum/reagent/consumable/tea/pinkmilk/on_mob_life(mob/living/carbon/M)
if(prob(15))
to_chat(M, "<span class = 'notice'>[pick("You cant help to smile.","You feel nostalgia all of sudden.","You remember to relax.")]</span>")
..()
. = 1
/datum/reagent/consumable/pinktea //Tiny Tim song
name = "Strawberry Tea"
id = "pinktea"
description = "A timeless classic!"
color = "#f76aeb"//rgb(247, 106, 235)
glass_icon_state = "pinktea"
quality = DRINK_FANTASTIC //Love drink
taste_description = "sweet tea with a hint of strawberry"
glass_name = "mug of strawberry tea"
glass_desc = "Delicious traditional tea flavored with strawberries."
/datum/reagent/consumable/tea/pinktea/on_mob_life(mob/living/carbon/M)
if(prob(10))
to_chat(M, "<span class = 'notice'>[pick("Diamond skies where white deer fly.","Sipping strawberry tea.","Silver raindrops drift through timeless, Neverending June.","Crystal ... pearls free, with love!","Beaming love into me.")]</span>")
..()
. = 1
@@ -1251,7 +1251,7 @@
L.remove_trait(TRAIT_SLEEPIMMUNE, id)
..()
/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M)
/datum/reagent/stimulum/on_mob_life(mob/living/carbon/M)
M.adjustStaminaLoss(-2*REM, 0)
current_cycle++
holder.remove_reagent(id, 0.99) //Gives time for the next tick of life().
@@ -3,23 +3,25 @@
id = "reagent_explosion"
var/strengthdiv = 10
var/modifier = 0
var/noexplosion = FALSE
/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, created_volume)
var/turf/T = get_turf(holder.my_atom)
var/inside_msg
if(ismob(holder.my_atom))
var/mob/M = holder.my_atom
inside_msg = " inside [ADMIN_LOOKUPFLW(M)]"
var/lastkey = holder.my_atom.fingerprintslast
var/touch_msg = "N/A"
if(lastkey)
var/mob/toucher = get_mob_by_key(lastkey)
touch_msg = "[ADMIN_LOOKUPFLW(toucher)]"
message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].")
log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." )
var/datum/effect_system/reagents_explosion/e = new()
e.set_up(modifier + round(created_volume/strengthdiv, 1), T, 0, 0)
e.start()
/datum/chemical_reaction/reagent_explosion/on_reaction(datum/reagents/holder, created_volume, turf/override)
if(!noexplosion)
var/turf/T = override || get_turf(holder.my_atom)
var/inside_msg
if(ismob(holder.my_atom))
var/mob/M = holder.my_atom
inside_msg = " inside [ADMIN_LOOKUPFLW(M)]"
var/lastkey = holder.my_atom.fingerprintslast
var/touch_msg = "N/A"
if(lastkey)
var/mob/toucher = get_mob_by_key(lastkey)
touch_msg = "[ADMIN_LOOKUPFLW(toucher)]"
message_admins("Reagent explosion reaction occurred at [ADMIN_VERBOSEJMP(T)][inside_msg]. Last Fingerprint: [touch_msg].")
log_game("Reagent explosion reaction occurred at [AREACOORD(T)]. Last Fingerprint: [lastkey ? lastkey : "N/A"]." )
var/datum/effect_system/reagents_explosion/e = new()
e.set_up(modifier + round(created_volume/strengthdiv, 1), T, 0, 0)
e.start()
holder.clear_reagents()
@@ -56,6 +58,7 @@
required_reagents = list("holywater" = 1, "potassium" = 1)
/datum/chemical_reaction/reagent_explosion/potassium_explosion/holyboom/on_reaction(datum/reagents/holder, created_volume)
var/turf/T = get_turf(holder.my_atom)
if(created_volume >= 150)
playsound(get_turf(holder.my_atom), 'sound/effects/pray.ogg', 80, 0, round(created_volume/48))
strengthdiv = 8
@@ -76,7 +79,7 @@
C.Knockdown(40)
C.adjust_fire_stacks(5)
C.IgniteMob()
..()
..(holder, created_volume, T)
/datum/chemical_reaction/blackpowder
@@ -95,8 +98,9 @@
mix_message = "<span class='boldannounce'>Sparks start flying around the black powder!</span>"
/datum/chemical_reaction/reagent_explosion/blackpowder_explosion/on_reaction(datum/reagents/holder, created_volume)
var/turf/T = get_turf(holder.my_atom)
sleep(rand(50,100))
..()
..(holder, created_volume, T)
/datum/chemical_reaction/thermite
name = "Thermite"
@@ -423,6 +427,7 @@
required_reagents = list("teslium" = 1, "water" = 1)
strengthdiv = 100
modifier = -100
noexplosion = TRUE
mix_message = "<span class='boldannounce'>The teslium starts to spark as electricity arcs away from it!</span>"
mix_sound = 'sound/machines/defib_zap.ogg'
var/tesla_flags = TESLA_MOB_DAMAGE | TESLA_OBJ_DAMAGE | TESLA_MOB_STUN
@@ -97,3 +97,6 @@
var/mutable_appearance/filling = mutable_appearance('icons/obj/reagentfillings.dmi', "dropper")
filling.color = mix_color_from_reagents(reagents.reagent_list)
add_overlay(filling)
/obj/item/reagent_containers/dropper/get_belt_overlay()
return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "pouch")
@@ -330,3 +330,6 @@
/obj/item/reagent_containers/glass/beaker/waterbottle/large/empty
list_reagents = list()
/obj/item/reagent_containers/glass/get_belt_overlay()
return mutable_appearance('icons/obj/clothing/belt_overlays.dmi', "bottle")

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