Merge branch 'master' of https://github.com/SandPoot/Citadel-Station-13 into balloon-alert-port

This commit is contained in:
SandPoot
2022-01-25 14:41:34 -03:00
671 changed files with 26502 additions and 11639 deletions
+1 -1
View File
@@ -242,7 +242,7 @@
var/selectors_used = FALSE
var/list/combined_refs = list()
do
CHECK_TICK
stoplag(2)
finished = TRUE
for(var/i in running)
var/datum/SDQL2_query/query = i
+9 -6
View File
@@ -3,7 +3,7 @@
set category = "Object"
if((O.obj_flags & DANGEROUS_POSSESSION) && CONFIG_GET(flag/forbid_singulo_possession))
to_chat(usr, "[O] is too powerful for you to possess.")
to_chat(usr, "[O] is too powerful for you to possess.", confidential = TRUE)
return
var/turf/T = get_turf(O)
@@ -18,19 +18,22 @@
if(!usr.control_object) //If you're not already possessing something...
usr.name_archive = usr.real_name
usr.loc = O
usr.forceMove(O)
usr.real_name = O.name
usr.name = O.name
usr.reset_perspective(O)
usr.control_object = O
O.AddElement(/datum/element/weather_listener, /datum/weather/ash_storm, ZTRAIT_ASHSTORM, GLOB.ash_storm_sounds)
SSblackbox.record_feedback("tally", "admin_verb", 1, "Possess Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
/proc/release()
set name = "Release Obj"
set category = "Object"
//usr.loc = get_turf(usr)
if(usr.control_object && usr.name_archive) //if you have a name archived and if you are actually relassing an object
if(!usr.control_object) //lest we are banished to the nullspace realm.
return
if(usr.name_archive) //if you have a name archived
usr.real_name = usr.name_archive
usr.name_archive = ""
usr.name = usr.real_name
@@ -38,8 +41,8 @@
var/mob/living/carbon/human/H = usr
H.name = H.get_visible_name()
usr.loc = get_turf(usr.control_object)
usr.control_object.RemoveElement(/datum/element/weather_listener, /datum/weather/ash_storm, ZTRAIT_ASHSTORM, GLOB.ash_storm_sounds)
usr.forceMove(get_turf(usr.control_object))
usr.reset_perspective()
usr.control_object = null
SSblackbox.record_feedback("tally", "admin_verb", 1, "Release Object") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
@@ -79,7 +79,7 @@
. = TRUE
if("mass_apply")
if(!check_rights_for(usr.client, R_FUN))
to_chat(usr, "<span class='userdanger>Stay in your lane, jannie.</span>'")
to_chat(usr, span_userdanger("Stay in your lane, jannie."))
return
var/target_path = text2path(params["path"])
if(!target_path)
+240 -64
View File
@@ -1,33 +1,70 @@
GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist
///Public name for this antagonist. Appears for player prompts and round-end reports.
var/name = "Antagonist"
var/roundend_category = "other antagonists" //Section of roundend report, datums with same category will be displayed together, also default header for the section
var/show_in_roundend = TRUE //Set to false to hide the antagonists from roundend report
var/datum/mind/owner //Mind that owns this datum
var/silent = FALSE //Silent will prevent the gain/lose texts to show
var/can_coexist_with_others = TRUE //Whether or not the person will be able to have more than one datum
var/list/typecache_datum_blacklist = list() //List of datums this type can't coexist with
var/delete_on_mind_deletion = TRUE
///Section of roundend report, datums with same category will be displayed together, also default header for the section
var/roundend_category = "other antagonists"
///Set to false to hide the antagonists from roundend report
var/show_in_roundend = TRUE
///If false, the roundtype will still convert with this antag active
var/prevent_roundtype_conversion = TRUE
///Mind that owns this datum
var/datum/mind/owner
///Silent will prevent the gain/lose texts to show
var/silent = FALSE
///Whether or not the person will be able to have more than one datum
var/can_coexist_with_others = TRUE
///List of datums this type can't coexist with
var/list/typecache_datum_blacklist = list()
///The define string we use to identify the role for bans/player polls to spawn a random new one in.
var/job_rank
var/replace_banned = TRUE //Should replace jobbaned player with ghosts if granted.
///Should replace jobbanned player with ghosts if granted.
var/replace_banned = TRUE
///List of the objective datums that this role currently has, completing all objectives at round-end will cause this antagonist to greentext.
var/list/objectives = list()
var/antag_memory = ""//These will be removed with antag datum
var/antag_moodlet //typepath of moodlet that the mob will gain with their status
///String dialogue that is added to the player's in-round notes and memories regarding specifics of that antagonist, eg. the nuke code for nuke ops, or your unlock code for traitors.
var/antag_memory = ""
///typepath of moodlet that the mob will gain when granted this antagonist type.
var/antag_moodlet
///What is the configuration of this antagonist's hud icon, such as it's screen position and style, so thatit doesn't break other in-game hud icons.
var/antag_hud_type
///Name of the antag hud we provide to this mob.
var/antag_hud_name
/// If above 0, this is the multiplier for the speed at which we hijack the shuttle. Do not directly read, use hijack_speed().
var/hijack_speed = 0
/// If set to true, the antag will not be added to the living antag list.
var/soft_antag = FALSE
/// The battlecry this antagonist shouts when suiciding with C4/X4.
var/suicide_cry = ""
//Antag panel properties
var/show_in_antagpanel = TRUE //This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/antagpanel_category = "Uncategorized" //Antagpanel will display these together, REQUIRED
var/show_name_in_check_antagonists = FALSE //Will append antagonist name in admin listings - use for categories that share more than one antag type
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute) // Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/threat = 0 // Amount of threat this antag poses, for dynamic mode
var/show_to_ghosts = FALSE // Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
///This will hide adding this antag type in antag panel, use only for internal subtypes that shouldn't be added directly but still show if possessed by mind
var/show_in_antagpanel = TRUE
///Antagpanel will display these together, REQUIRED
var/antagpanel_category = "Uncategorized"
///Will append antagonist name in admin listings - use for categories that share more than one antag type
var/show_name_in_check_antagonists = FALSE
/// Should this antagonist be shown as antag to ghosts? Shouldn't be used for stealthy antagonists like traitors
var/show_to_ghosts = FALSE
/* CIT SPECIFIC */
/// Quirks that will be removed upon gaining this antag. Pacifist and mute are default.
var/list/blacklisted_quirks = list(/datum/quirk/nonviolent,/datum/quirk/mute)
/// Amount of threat this antag poses, for dynamic mode
var/threat = 0
var/delete_on_mind_deletion = TRUE
var/list/skill_modifiers
/* CIT SPECIFIC end */
//ANTAG UI
///name of the UI that will try to open, right now having nothing means this won't exist but in the future all should.
var/ui_name
///button to access antag interface
var/datum/action/antag_info/info_button
/datum/antagonist/New()
GLOB.antagonists += src
@@ -35,7 +72,9 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/Destroy()
GLOB.antagonists -= src
if(owner)
if(!owner)
stack_trace("Destroy()ing antagonist datum when it has no owner.")
else
LAZYREMOVE(owner.antag_datums, src)
owner = null
return ..()
@@ -55,14 +94,14 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/specialization(datum/mind/new_owner)
return src
///Called by the transfer_to() mind proc after the mind (mind.current and new_character.mind) has moved but before the player (key and client) is transfered.
///Called by the transfer_to() mind proc after the mind (mind.current and new_character.mind) has moved but before the player (key and client) is transfered.
/datum/antagonist/proc/on_body_transfer(mob/living/old_body, mob/living/new_body)
SHOULD_CALL_PARENT(TRUE)
remove_innate_effects(old_body)
if(old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
if(!soft_antag && old_body.stat != DEAD && !LAZYLEN(old_body.mind?.antag_datums))
old_body.remove_from_current_living_antags()
apply_innate_effects(new_body)
if(new_body.stat != DEAD)
if(!soft_antag && new_body.stat != DEAD)
new_body.add_to_current_living_antags()
//This handles the application of antag huds/special abilities
@@ -73,46 +112,76 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/proc/remove_innate_effects(mob/living/mob_override)
return
/// This is called when the antagonist is being mindshielded.
/datum/antagonist/proc/pre_mindshield(mob/implanter, mob/living/mob_override)
SIGNAL_HANDLER
// return COMPONENT_MINDSHIELD_PASSED
/// This is called when the antagonist is successfully mindshielded.
/datum/antagonist/proc/on_mindshield(mob/implanter, mob/living/mob_override)
SIGNAL_HANDLER
return
// Adds the specified antag hud to the player. Usually called in an antag datum file
/datum/antagonist/proc/add_antag_hud(antag_hud_type, antag_hud_name, mob/living/mob_override)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.join_hud(mob_override)
set_antag_hud(mob_override, antag_hud_name)
// Removes the specified antag hud from the player. Usually called in an antag datum file
/datum/antagonist/proc/remove_antag_hud(antag_hud_type, mob/living/mob_override)
var/datum/atom_hud/antag/hud = GLOB.huds[antag_hud_type]
hud.leave_hud(mob_override)
set_antag_hud(mob_override, null)
// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/// Handles adding and removing the clumsy mutation from clown antags. Gets called in apply/remove_innate_effects
/datum/antagonist/proc/handle_clown_mutation(mob/living/mob_override, message, removing = TRUE)
var/mob/living/carbon/human/H = mob_override
if(H && istype(H) && owner.assigned_role == "Clown")
if(removing) // They're a clown becoming an antag, remove clumsy
H.dna.remove_mutation(CLOWNMUT)
if(!silent && message)
to_chat(H, "<span class='boldnotice'>[message]</span>")
else
H.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
if(!ishuman(mob_override) || owner.assigned_role != "Clown")
return
var/mob/living/carbon/human/human_override = mob_override
if(removing) // They're a clown becoming an antag, remove clumsy
human_override.dna.remove_mutation(CLOWNMUT)
if(!silent && message)
to_chat(human_override, span_boldnotice("[message]"))
else
human_override.dna.add_mutation(CLOWNMUT) // We're removing their antag status, add back clumsy
//Assign default team and creates one for one of a kind team antagonists
/datum/antagonist/proc/create_team(datum/team/team)
return
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
///Called by the add_antag_datum() mind proc after the instanced datum is added to the mind's antag_datums list.
/datum/antagonist/proc/on_gain()
SHOULD_CALL_PARENT(TRUE)
set waitfor = FALSE
if(!(owner?.current))
return
if(!owner)
CRASH("[src] ran on_gain() without a mind")
if(!owner.current)
CRASH("[src] ran on_gain() on a mind without a mob")
if(ui_name)//in the future, this should entirely replace greet.
info_button = new(owner.current, src)
info_button.Grant(owner.current)
if(!silent)
greet()
if(ui_name)
to_chat(owner.current, span_big("You are \a [src]."))
to_chat(owner.current, span_boldnotice("For more info, read the panel. you can always come back to it using the button in the top left."))
info_button.Trigger()
apply_innate_effects()
give_antag_moodies()
remove_blacklisted_quirks()
// RegisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT, .proc/pre_mindshield)
// RegisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED, .proc/on_mindshield)
if(is_banned(owner.current) && replace_banned)
replace_banned_player()
// else if(owner.current.client?.holder && (CONFIG_GET(flag/auto_deadmin_antagonists) || owner.current.client.prefs?.toggles & DEADMIN_ANTAGONIST))
// owner.current.client.holder.auto_deadmin()
if(!soft_antag && owner.current.stat != DEAD)
owner.current.add_to_current_living_antags()
// cit skill
if(skill_modifiers)
for(var/A in skill_modifiers)
ADD_SINGLETON_SKILL_MODIFIER(owner, A, type)
@@ -120,61 +189,95 @@ GLOBAL_LIST_EMPTY(antagonists)
if(istype(M))
M.name = "[name] Training"
owner.current.AddComponent(/datum/component/activity)
if(owner.current.stat != DEAD)
owner.current.add_to_current_living_antags()
SEND_SIGNAL(owner.current, COMSIG_MOB_ANTAG_ON_GAIN, src)
/**
* Proc that checks the sent mob aganst the banlistfor this antagonist.
* Returns FALSE if no mob is sent, or the mob is not found to be banned.
*
* * mob/M: The mob that you are looking for on the banlist.
*/
/datum/antagonist/proc/is_banned(mob/M)
if(!M)
return FALSE
. = (jobban_isbanned(M, ROLE_SYNDICATE) || QDELETED(M) || (job_rank && (jobban_isbanned(M,job_rank) || QDELETED(M))))
/**
* Proc that replaces a player who cannot play a specific antagonist due to being banned via a poll, and alerts the player of their being on the banlist.
*/
/datum/antagonist/proc/replace_banned_player()
set waitfor = FALSE
var/list/mob/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", null, job_rank, 50, owner.current)
var/list/mob/dead/observer/candidates = pollCandidatesForMob("Do you want to play as a [name]?", "[name]", job_rank, 50, owner.current)
if(LAZYLEN(candidates))
var/mob/C = pick(candidates)
var/mob/dead/observer/C = pick(candidates)
to_chat(owner, "Your mob has been taken over by a ghost! Appeal your job ban if you want to avoid this in the future!")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner.current)]) to replace a jobbaned player.")
message_admins("[key_name_admin(C)] has taken control of ([key_name_admin(owner)]) to replace a jobbanned player.")
owner.current.ghostize(0)
C.transfer_ckey(owner.current, FALSE)
///Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
/**
* Called by the remove_antag_datum() and remove_all_antag_datums() mind procs for the antag datum to handle its own removal and deletion.
*/
/datum/antagonist/proc/on_removal()
SHOULD_CALL_PARENT(TRUE)
if(!owner)
CRASH("Antag datum with no owner.")
remove_innate_effects()
clear_antag_moodies()
if(owner)
LAZYREMOVE(owner.antag_datums, src)
for(var/A in skill_modifiers)
owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
if(!LAZYLEN(owner.antag_datums))
owner.current.remove_from_current_living_antags()
if(!silent && owner.current)
farewell()
LAZYREMOVE(owner.antag_datums, src)
// cit skill
for(var/A in skill_modifiers)
owner.remove_skill_modifier(GET_SKILL_MOD_ID(A, type))
// end
if(!LAZYLEN(owner.antag_datums) && !soft_antag)
owner.current.remove_from_current_living_antags()
if(info_button)
QDEL_NULL(info_button)
if(!silent && owner.current)
farewell()
// UnregisterSignal(owner, COMSIG_PRE_MINDSHIELD_IMPLANT)
// UnregisterSignal(owner, COMSIG_MINDSHIELD_IMPLANTED)
var/datum/team/team = get_team()
if(team)
team.remove_member(owner)
// we don't remove the activity component on purpose--no real point to it
qdel(src)
/**
* Proc that sends fluff or instructional messages to the player when they are given this antag datum.
* Use this proc for playing sounds, sending alerts, or helping to setup non-gameplay influencing aspects of the antagonist type.
*/
/datum/antagonist/proc/greet()
return
/**
* Proc that sends fluff or instructional messages to the player when they lose this antag datum.
* Use this proc for playing sounds, sending alerts, or otherwise informing the player that they're no longer a specific antagonist type.
*/
/datum/antagonist/proc/farewell()
return
/**
* Proc that assigns this antagonist's ascribed moodlet to the player.
*/
/datum/antagonist/proc/give_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_ADD_MOOD_EVENT, "antag_moodlet", antag_moodlet)
/**
* Proc that removes this antagonist's ascribed moodlet from the player.
*/
/datum/antagonist/proc/clear_antag_moodies()
if(!antag_moodlet)
return
SEND_SIGNAL(owner.current, COMSIG_CLEAR_MOOD_EVENT, "antag_moodlet")
/**
* Removes invalid quirks.
*/
/datum/antagonist/proc/remove_blacklisted_quirks()
var/mob/living/L = owner.current
if(istype(L))
@@ -185,16 +288,22 @@ GLOBAL_LIST_EMPTY(antagonists)
to_chat(L, "<span class='boldannounce'>[initial(Q.antag_removal_text)]</span>")
qdel(Q)
//Returns the team antagonist belongs to if any.
/**
* Proc that will return the team this antagonist belongs to, when called. Helpful with antagonists that may belong to multiple potential teams in a single round, like families.
*/
/datum/antagonist/proc/get_team()
return
//Individual roundend report
/**
* Proc that sends string information for the end-round report window to the server.
* This runs on every instance of every antagonist that exists at the end of the round.
* This is the body of the message, sandwiched between roundend_report_header and roundend_report_footer.
*/
/datum/antagonist/proc/roundend_report()
var/list/report = list()
if(!owner)
CRASH("antagonist datum without owner")
CRASH("Antagonist datum without owner")
report += printplayer(owner)
@@ -213,11 +322,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return report.Join("<br>")
//Displayed at the start of roundend_category section, default to roundend_category header
/**
* Proc that sends string data for the round-end report.
* Displayed before roundend_report and roundend_report_footer.
* Appears at start of roundend_catagory section.
*/
/datum/antagonist/proc/roundend_report_header()
return "<span class='header'>The [roundend_category] were:</span><br>"
return "<span class='header'>The [roundend_category] were:</span><br>"
//Displayed at the end of roundend_category section
/**
* Proc that sends string data for the round-end report.
* Displayed after roundend_report and roundend_report_footer.
* Appears at the end of the roundend_catagory section.
*/
/datum/antagonist/proc/roundend_report_footer()
return
@@ -226,22 +343,24 @@ GLOBAL_LIST_EMPTY(antagonists)
//Called when using admin tools to give antag status
/datum/antagonist/proc/admin_add(datum/mind/new_owner,mob/admin)
message_admins("[key_name_admin(admin)] made [new_owner.current] into [name].")
log_admin("[key_name(admin)] made [new_owner.current] into [name].")
message_admins("[key_name_admin(admin)] made [key_name_admin(new_owner)] into [name].")
log_admin("[key_name(admin)] made [key_name(new_owner)] into [name].")
new_owner.add_antag_datum(src)
//Called when removing antagonist using admin tools
/datum/antagonist/proc/admin_remove(mob/user)
if(!user)
return
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [owner.current].")
log_admin("[key_name(user)] has removed [name] antagonist status from [owner.current].")
message_admins("[key_name_admin(user)] has removed [name] antagonist status from [key_name_admin(owner)].")
log_admin("[key_name(user)] has removed [name] antagonist status from [key_name(owner)].")
on_removal()
//gamemode/proc/is_mode_antag(antagonist/A) => TRUE/FALSE
//Additional data to display in antagonist panel section
//nuke disk code, genome count, etc
/**
* Additional data to display in the antagonist panel section.
* For example, nuke disk code, genome count, etc
*/
/datum/antagonist/proc/antag_panel_data()
return ""
@@ -253,7 +372,7 @@ GLOBAL_LIST_EMPTY(antagonists)
return FALSE
return TRUE
// List if ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/// List of ["Command"] = CALLBACK(), user will be appeneded to callback arguments on execution
/datum/antagonist/proc/get_admin_commands()
. = list()
@@ -283,14 +402,19 @@ GLOBAL_LIST_EMPTY(antagonists)
return
antag_memory = new_memo
/// Gets how fast we can hijack the shuttle, return 0 for can not hijack. Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
/**
* Gets how fast we can hijack the shuttle, return 0 for can not hijack.
* Defaults to hijack_speed var, override for custom stuff like buffing hijack speed for hijack objectives or something.
*/
/datum/antagonist/proc/hijack_speed()
var/datum/objective/hijack/H = locate() in objectives
if(!isnull(H?.hijack_speed_override))
return H.hijack_speed_override
return hijack_speed
/// Gets our threat level. Defaults to threat var, override for custom stuff like different traitor goals having different threats.
/**
* Gets our threat level. Override this proc for custom functionality/dynamic threat level.
*/
/datum/antagonist/proc/threat()
. = CONFIG_GET(keyed_list/antag_threat)[lowertext(name)]
if(. == null)
@@ -300,6 +424,13 @@ GLOBAL_LIST_EMPTY(antagonists)
/datum/antagonist/custom
antagpanel_category = "Custom"
show_name_in_check_antagonists = TRUE //They're all different
var/datum/team/custom_team
/datum/antagonist/custom/create_team(datum/team/team)
custom_team = team
/datum/antagonist/custom/get_team()
return custom_team
/datum/antagonist/custom/admin_add(datum/mind/new_owner,mob/admin)
var/custom_name = stripped_input(admin, "Custom antagonist name:", "Custom antag", "Antagonist")
@@ -308,3 +439,48 @@ GLOBAL_LIST_EMPTY(antagonists)
else
return
..()
///ANTAGONIST UI STUFF
/datum/antagonist/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
ui = new(user, src, ui_name, name)
ui.open()
/datum/antagonist/ui_state(mob/user)
return GLOB.always_state
///generic helper to send objectives as data through tgui. supports smart objectives too!
/datum/antagonist/proc/get_objectives()
var/objective_count = 1
var/list/objective_data = list()
//all obj
for(var/datum/objective/objective in objectives)
objective_data += list(list(
"count" = objective_count,
"name" = objective.name,
"explanation" = objective.explanation_text,
"complete" = objective.completed,
))
objective_count++
return objective_data
//button for antags to review their descriptions/info
/datum/action/antag_info
name = "Open Antag Information:"
button_icon_state = "round_end"
var/datum/antagonist/antag_datum
/datum/action/antag_info/New(Target, datum/antagonist/antag_datum)
. = ..()
src.antag_datum = antag_datum
name += " [antag_datum.name]"
/datum/action/antag_info/Trigger()
if(antag_datum)
antag_datum.ui_interact(owner)
/datum/action/antag_info/IsAvailable()
return TRUE
@@ -23,7 +23,7 @@
return
var/obj/item/organ/lungs/lungs = owner.getorganslot(ORGAN_SLOT_LUNGS)
if((!lungs && !HAS_TRAIT(owner, TRAIT_NOBREATH)) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
if((!lungs && (HAS_TRAIT_FROM(owner, TRAIT_AUXILIARY_LUNGS, SPECIES_TRAIT) || !HAS_TRAIT(owner, TRAIT_NOBREATH))) || (lungs && (istype(lungs, /obj/item/organ/lungs/cybernetic))))
replace_lungs(lungs)
return
@@ -17,11 +17,13 @@
B.objectives += objective
M.add_antag_datum(B)
var/begin_message = "<span class='deadsay'><b>[L]</b> has been brainwashed with the following objectives: "
var/begin_message = " has been brainwashed with the following objectives: "
var/obj_message = english_list(directives)
var/end_message = "</b>.</span>"
var/end_message = "."
var/rendered = begin_message + obj_message + end_message
deadchat_broadcast(rendered, follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_REGULAR)
deadchat_broadcast(rendered, "<b>[L]</b>", follow_target = L, turf_target = get_turf(L), message_type=DEADCHAT_ANNOUNCEMENT)
if(prob(1) || SSevents.holidays && SSevents.holidays[APRIL_FOOLS])
L.say("You son of a bitch! I'm in.", forced = "That son of a bitch! They're in.")
/datum/antagonist/brainwashed
name = "Brainwashed Victim"
@@ -30,19 +32,21 @@
show_in_antagpanel = TRUE
antagpanel_category = "Other"
show_name_in_check_antagonists = TRUE
ui_name = "AntagInfoBrainwashed"
suicide_cry = "FOR... SOMEONE!!"
/datum/antagonist/brainwashed/greet()
to_chat(owner, "<span class='warning'>Your mind reels as it begins focusing on a single purpose...</span>")
to_chat(owner, "<big><span class='warning'><b>Follow the Directives, at any cost!</b></span></big>")
var/i = 1
for(var/X in objectives)
var/datum/objective/O = X
to_chat(owner, "<b>[i].</b> [O.explanation_text]")
i++
/datum/antagonist/brainwashed/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["objectives"] = get_objectives()
return data
/datum/antagonist/brainwashed/farewell()
to_chat(owner, "<span class='warning'>Your mind suddenly clears...</span>")
to_chat(owner, "<big><span class='warning'><b>You feel the weight of the Directives disappear! You no longer have to obey them.</b></span></big>")
to_chat(owner, span_warning("Your mind suddenly clears..."))
to_chat(owner, "<big>[span_warning("<b>You feel the weight of the Directives disappear! You no longer have to obey them.</b>")]</big>")
if(owner.current)
var/mob/living/owner_mob = owner.current
owner_mob.log_message("is no longer brainwashed with the objectives: [english_list(objectives)].", LOG_ATTACK)
owner.announce_objectives()
/datum/antagonist/brainwashed/admin_add(datum/mind/new_owner,mob/admin)
@@ -54,9 +58,9 @@
var/objective = stripped_input(admin, "Add an objective, or leave empty to finish.", "Brainwashing", null, MAX_MESSAGE_LEN)
if(objective)
objectives += objective
while(alert(admin,"Add another objective?","More Brainwashing","Yes","No") == "Yes")
while(tgui_alert(admin,"Add another objective?","More Brainwashing",list("Yes","No")) == "Yes")
if(alert(admin,"Confirm Brainwashing?","Are you sure?","Yes","No") == "No")
if(tgui_alert(admin,"Confirm Brainwashing?","Are you sure?",list("Yes","No")) == "No")
return
if(!LAZYLEN(objectives))
@@ -69,6 +73,7 @@
brainwash(C, objectives)
var/obj_list = english_list(objectives)
message_admins("[key_name_admin(admin)] has brainwashed [key_name_admin(C)] with the following objectives: [obj_list].")
C.log_message("has been force-brainwashed with the objective '[obj_list]' by admin [key_name(admin)]", LOG_ATTACK, log_globally = FALSE)
log_admin("[key_name(admin)] has brainwashed [key_name(C)] with the following objectives: [obj_list].")
/datum/objective/brainwashing
@@ -264,7 +264,9 @@
ui.open()
/obj/item/clockwork/slab/ui_data(mob/user) //we display a lot of data via TGUI
. = list()
. = ..()
if(!.)
return
.["recollection"] = recollecting
.["power"] = DisplayPower(get_clockwork_power())
.["power_unformatted"] = get_clockwork_power()
@@ -275,6 +277,7 @@
if(S.tier == SCRIPTURE_PERIPHERAL) // This tier is skiped because this contains basetype stuff
continue
// FUTURE IMPL: cache these perhaps?
var/list/data = list()
data["name"] = S.name
data["descname"] = S.descname
@@ -6,6 +6,7 @@
job_rank = ROLE_SERVANT_OF_RATVAR
antag_moodlet = /datum/mood_event/cult
skill_modifiers = list(/datum/skill_modifier/job/level/wiring, /datum/skill_modifier/job/level/dwarfy/blacksmithing)
ui_name = "AntagInfoClockwork"
var/datum/action/innate/hierophant/hierophant_network = new
threat = 3
var/datum/team/clockcult/clock_team
@@ -14,6 +15,12 @@
var/ignore_eligibility_check = FALSE
var/ignore_holy_water = FALSE
/datum/antagonist/clockcult/ui_data(mob/user)
. = ..()
if(!.)
return
.["HONOR_RATVAR"] = GLOB.ratvar_awakens
/datum/antagonist/clockcult/silent
name = "Silent Clock Cultist"
silent = TRUE
@@ -22,6 +29,8 @@
/datum/antagonist/clockcult/neutered
name = "Neutered Clock Cultist"
neutered = TRUE
soft_antag = TRUE
ui_name = null // no.
/datum/antagonist/clockcult/neutered/traitor
name = "Traitor Clock Cultist"
@@ -57,14 +66,6 @@
if(. && !ignore_eligibility_check)
. = is_eligible_servant(new_owner.current)
/datum/antagonist/clockcult/greet()
if(!owner.current || silent)
return
owner.current.visible_message("<span class='heavy_brass'>[owner.current]'s eyes glow a blazing yellow!</span>", null, null, 7, owner.current) //don't show the owner this message
to_chat(owner.current, "<span class='heavy_brass'>Assist your new companions in their righteous efforts. Your goal is theirs, and theirs yours. You serve the Clockwork \
Justiciar above all else. Perform his every whim without hesitation.</span>")
owner.current.playsound_local(get_turf(owner.current), 'sound/ambience/antag/clockcultalr.ogg', 70, FALSE, pressure_affected = FALSE)
/datum/antagonist/clockcult/on_gain()
var/mob/living/current = owner.current
SSticker.mode.servants_of_ratvar += owner
@@ -3,5 +3,6 @@
show_name_in_check_antagonists = TRUE
show_in_antagpanel = FALSE
threat = 2
ui_name = "AntagInfoMorph"
//It does nothing! (Besides tracking)
@@ -4,3 +4,5 @@
show_name_in_check_antagonists = TRUE
threat = 5
show_to_ghosts = TRUE
ui_name = "AntagInfoNightmare"
suicide_cry = "FOR THE DARKNESS!!"
@@ -236,6 +236,8 @@
var/obj/machinery/nuclearbomb/tracked_nuke
var/core_objective = /datum/objective/nuclear
var/memorized_code
var/list/team_discounts
var/datum/weakref/war_button_ref
/datum/team/nuclear/New()
..()
@@ -121,7 +121,7 @@
return
var/mob/living/carbon/human/H = owner.current
// Give uplink
var/obj/item/uplink_holder = owner.equip_traitor(uplink_owner = src)
var/obj/item/uplink_holder = owner.equip_traitor()
var/datum/component/uplink/uplink = uplink_holder.GetComponent(/datum/component/uplink)
uplink.telecrystals = INITIAL_CRYSTALS
// Give AI hacking board
@@ -7,37 +7,51 @@
/datum/antagonist/traitor/internal_affairs
name = "Internal Affairs Agent"
employer = "Nanotrasen"
special_role = "internal affairs agent"
suicide_cry = "FOR THE COMPANY!!"
antagpanel_category = "IAA"
var/special_role = "internal affairs agent"
var/syndicate = FALSE
var/last_man_standing = FALSE
var/list/datum/mind/targets_stolen
/datum/antagonist/traitor/internal_affairs/New()
. = ..()
LAZYADD(targets_stolen, src)
/datum/antagonist/traitor/internal_affairs/proc/give_pinpointer()
if(owner && owner.current)
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
owner.current.apply_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/apply_innate_effects()
.=..() //in case the base is used in future
if(owner && owner.current)
. = ..()
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
give_pinpointer(owner.current)
/datum/antagonist/traitor/internal_affairs/remove_innate_effects()
.=..()
if(owner && owner.current)
. = ..()
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current)
owner.current.remove_status_effect(/datum/status_effect/agent_pinpointer)
/datum/antagonist/traitor/internal_affairs/on_gain()
START_PROCESSING(SSprocessing, src)
.=..()
. = ..()
/datum/antagonist/traitor/internal_affairs/on_removal()
STOP_PROCESSING(SSprocessing,src)
.=..()
. = ..()
/datum/antagonist/traitor/internal_affairs/process()
iaa_process()
/datum/status_effect/agent_pinpointer
id = "agent_pinpointer"
duration = -1
@@ -46,6 +60,8 @@
var/minimum_range = PINPOINTER_MINIMUM_RANGE
var/range_fuzz_factor = PINPOINTER_EXTRA_RANDOM_RANGE
var/mob/scan_target = null
var/range_mid = 8
var/range_far = 16
/atom/movable/screen/alert/status_effect/agent_pinpointer
name = "Internal Affairs Integrated Pinpointer"
@@ -66,13 +82,13 @@
linked_alert.icon_state = "pinondirect"
else
linked_alert.setDir(get_dir(here, there))
switch(get_dist(here, there))
if(1 to 8)
linked_alert.icon_state = "pinonclose"
if(9 to 16)
linked_alert.icon_state = "pinonmedium"
if(16 to INFINITY)
linked_alert.icon_state = "pinonfar"
var/dist = (get_dist(here, there))
if(dist >= 1 && dist <= range_mid)
linked_alert.icon_state = "pinonclose"
else if(dist > range_mid && dist <= range_far)
linked_alert.icon_state = "pinonmedium"
else if(dist > range_far)
linked_alert.icon_state = "pinonfar"
/datum/status_effect/agent_pinpointer/proc/scan_for_target()
scan_target = null
@@ -99,37 +115,42 @@
return (istype(O, /datum/objective/assassinate/internal)||istype(O, /datum/objective/destroy/internal))
/datum/antagonist/traitor/proc/replace_escape_objective()
if(!owner || !objectives.len)
if(!owner)
CRASH("Antag datum with no owner.")
if(!objectives.len)
return
for (var/objective_ in objectives)
if(!(istype(objective_, /datum/objective/escape)||istype(objective_, /datum/objective/survive)))
for (var/objective in objectives)
if(!(istype(objective, /datum/objective/escape) || istype(objective, /datum/objective/survive)))
continue
remove_objective(objective_)
objectives -= objective
var/datum/objective/martyr/martyr_objective = new
martyr_objective.owner = owner
add_objective(martyr_objective)
/datum/antagonist/traitor/proc/reinstate_escape_objective()
if(!owner||!objectives.len)
if(!owner)
CRASH("Antag datum with no owner.")
if(!objectives.len)
return
for (var/objective_ in objectives)
if(!istype(objective_, /datum/objective/martyr))
for (var/objective in objectives)
if(!istype(objective, /datum/objective/martyr))
continue
remove_objective(objective_)
remove_objective(objective)
/datum/antagonist/traitor/internal_affairs/reinstate_escape_objective()
..()
var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
for (var/datum/objective/martyr/martyr_objective in objectives)
objectives -= martyr_objective
var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
add_objective(escape_objective)
objectives += escape_objective
/datum/antagonist/traitor/internal_affairs/proc/steal_targets(datum/mind/victim)
if(!owner.current||owner.current.stat==DEAD)
return
to_chat(owner.current, "<span class='userdanger'> Target eliminated: [victim.name]</span>")
LAZYINITLIST(targets_stolen)
to_chat(owner.current, span_userdanger("Target eliminated: [victim.name]"))
for(var/objective_ in victim.get_all_objectives())
if(istype(objective_, /datum/objective/assassinate/internal))
var/datum/objective/assassinate/internal/objective = objective_
@@ -143,7 +164,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
else if(istype(objective_, /datum/objective/destroy/internal))
var/datum/objective/destroy/internal/objective = objective_
var/datum/objective/destroy/internal/new_objective = new
@@ -156,7 +177,7 @@
add_objective(new_objective)
targets_stolen += objective.target
var/status_text = objective.check_completion() ? "neutralised" : "active"
to_chat(owner.current, "<span class='userdanger'> New target added to database: [objective.target.name] ([status_text]) </span>")
to_chat(owner.current, span_userdanger("New target added to database: [objective.target.name] ([status_text])"))
last_man_standing = TRUE
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
@@ -167,13 +188,15 @@
return
if(last_man_standing)
if(syndicate)
to_chat(owner.current,"<span class='userdanger'>All the suspected agents are dead, and no more is required of you. Die a glorious death, agent.</span>")
replace_escape_objective(owner)
to_chat(owner.current,span_userdanger("All the suspected agents are dead, and no more is required of you. Die a glorious death, agent."))
else
to_chat(owner.current,"<span class='userdanger'>All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base.</span>")
to_chat(owner.current,span_userdanger("All the other agents are dead. You have done us all a great service and shall be honorably exiled upon returning to base."))
replace_escape_objective(owner)
/datum/antagonist/traitor/internal_affairs/proc/iaa_process()
if(owner&&owner.current&&owner.current.stat!=DEAD)
if(!owner)
CRASH("Antag datum with no owner.")
if(owner.current && owner.current.stat != DEAD)
for(var/objective_ in objectives)
if(!is_internal_objective(objective_))
continue
@@ -188,12 +211,12 @@
objective.stolen = TRUE
else
if(objective.stolen)
var/fail_msg = "<span class='userdanger'>Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! </span>"
var/fail_msg = span_userdanger("Your sensors tell you that [objective.target.current.real_name], one of the targets you were meant to have killed, pulled one over on you, and is still alive - do the job properly this time! ")
if(last_man_standing)
if(syndicate)
fail_msg += "<span class='userdanger'> You no longer have permission to die. </span>"
fail_msg += span_userdanger(" You no longer have permission to die. ")
else
fail_msg += "<span class='userdanger'> The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to great shame.</span>"
fail_msg += span_userdanger(" The truth could still slip out!</font><B><font size=5 color=red> Cease any terrorist actions as soon as possible, unneeded property damage or loss of employee life will lead to your contract being terminated.")
reinstate_escape_objective(owner)
last_man_standing = FALSE
to_chat(owner.current, fail_msg)
@@ -220,29 +243,24 @@
/datum/antagonist/traitor/internal_affairs/forge_traitor_objectives()
forge_iaa_objectives()
var/objtype = !istype(traitor_kind,TRAITOR_AI) ? /datum/objective/escape : /datum/objective/survive
var/datum/objective/escape_objective = new objtype
var/datum/objective/escape_objective = new /datum/objective/escape()
escape_objective.owner = owner
add_objective(escape_objective)
/datum/antagonist/traitor/internal_affairs/proc/greet_iaa()
var/crime = pick("distribution of contraband" , "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
var/crime = pick("distribution of contraband", "embezzlement", "piloting under the influence", "dereliction of duty", "syndicate collaboration", "mutiny", "multiple homicides", "corporate espionage", "receiving bribes", "malpractice", "worship of prohibited life forms", "possession of profane texts", "murder", "arson", "insulting their manager", "grand theft", "conspiracy", "attempting to unionize", "vandalism", "gross incompetence")
to_chat(owner.current, "<span class='userdanger'>You are the [special_role].</span>")
to_chat(owner.current, span_userdanger("You are the [special_role]."))
if(syndicate)
to_chat(owner.current, "<span class='userdanger'>GREAT LEADER IS DEAD. NANOTRASEN MUST FALL.</span>")
to_chat(owner.current, "<span class='userdanger'>Your have infiltrated this vessel to cause chaos and assassinate targets known to have conspired against the Syndicate.</span>")
to_chat(owner.current, "<span class='userdanger'>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</span>")
to_chat(owner.current, "<span class='userdanger'>You have been provided with a standard uplink to accomplish your task. </span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you are a Syndicate agent. By no means reveal that your targets are being hunted.</span>")
to_chat(owner.current, span_userdanger("Your target has been framed for [crime], and you have been tasked with eliminating them to prevent them defending themselves in court."))
to_chat(owner.current, "<span class='warningplain'><B><font size=5 color=red>Any damage you cause will be a further embarrassment to Nanotrasen, so you have no limits on collateral damage.</font></B></span>")
to_chat(owner.current, span_userdanger("You have been provided with a standard uplink to accomplish your task."))
else
to_chat(owner.current, "<span class='userdanger'>CAUTION: Your legal status as a citizen of NanoTrasen will be permanently revoked upon completion of your first contract.</span>")
to_chat(owner.current, "<span class='userdanger'>Your target has been suspected of [crime], and must be removed from this plane.</span>")
to_chat(owner.current, "<span class='userdanger'>While you have a license to kill, you are to eliminate your targets with no collateral or unrelated deaths.</span>")
to_chat(owner.current, "<span class='userdanger'>For the sake of plausable deniability, you have been equipped with captured Syndicate equipment via uplink.</span>")
to_chat(owner.current, "<span class='userdanger'>By no means reveal that you, or any other NT employees, are undercover agents.</span>")
to_chat(owner.current, span_userdanger("Your target is suspected of [crime], and you have been tasked with eliminating them by any means necessary to avoid a costly and embarrassing public trial."))
to_chat(owner.current, "<span class='warningplain'><B><font size=5 color=red>While you have a license to kill, unneeded property damage or loss of employee life will lead to your contract being terminated.</font></B></span>")
to_chat(owner.current, span_userdanger("For the sake of plausible deniability, you have been equipped with an array of captured Syndicate weaponry available via uplink."))
to_chat(owner.current, "<span class='userdanger'>Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them.</span>")
to_chat(owner.current, span_userdanger("Finally, watch your back. Your target has friends in high places, and intel suggests someone may have taken out a contract of their own to protect them."))
owner.announce_objectives()
/datum/antagonist/traitor/internal_affairs/greet()
@@ -5,7 +5,7 @@
chaos = 5
threat = 5
min_players = 20
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_TC/contract_kit)
uplink_filters = list(/datum/uplink_item/stealthy_weapons/romerol_kit,/datum/uplink_item/bundles_tc/contract_kit)
/datum/traitor_class/human/martyr/forge_objectives(datum/antagonist/traitor/T)
var/datum/objective/martyr/O = new
@@ -11,6 +11,8 @@ GLOBAL_LIST_EMPTY(traitor_classes)
/// Minimum players for this to randomly roll via get_random_traitor_kind().
var/min_players = 0
var/list/uplink_filters
/// Specific tgui theme for the player's antag info panel.
var/tgui_theme = "syndicate"
/datum/traitor_class/New()
..()
@@ -5,14 +5,22 @@
job_rank = ROLE_TRAITOR
antag_moodlet = /datum/mood_event/focused
skill_modifiers = list(/datum/skill_modifier/job/level/wiring/basic)
var/special_role = ROLE_TRAITOR
hijack_speed = 0.5 //10 seconds per hijack stage by default
ui_name = "AntagInfoTraitor"
suicide_cry = "FOR THE SYNDICATE!!"
var/employer = "The Syndicate"
var/give_objectives = TRUE
var/should_give_codewords = TRUE
var/should_equip = TRUE
///special datum about what kind of employer the trator has
var/datum/traitor_class/traitor_kind
///reference to the uplink this traitor was given, if they were.
var/datum/component/uplink/uplink
var/datum/contractor_hub/contractor_hub
hijack_speed = 0.5 //10 seconds per hijack stage by default
threat = 5
/datum/antagonist/traitor/New()
@@ -45,7 +53,7 @@
/datum/antagonist/traitor/process()
traitor_kind.on_process(src)
/proc/get_random_traitor_kind(var/list/blacklist = list())
/proc/get_random_traitor_kind(list/blacklist = list())
var/list/weights = list()
for(var/C in GLOB.traitor_classes)
if(!(C in blacklist))
@@ -62,23 +70,24 @@
return choice
/datum/antagonist/traitor/on_gain()
owner.special_role = job_rank
if(owner.current && isAI(owner.current))
set_traitor_kind(TRAITOR_AI)
else
set_traitor_kind(get_random_traitor_kind())
SSticker.mode.traitors += owner
owner.special_role = special_role
finalize_traitor()
..()
uplink = owner.find_syndicate_uplink()
return ..()
/datum/antagonist/traitor/on_removal()
if(!silent && owner.current)
to_chat(owner.current,span_userdanger("You are no longer the [job_rank]!"))
//Remove malf powers.
traitor_kind.on_removal(src)
SSticker.mode.traitors -= owner
if(!silent && owner.current)
to_chat(owner.current,"<span class='userdanger'> You are no longer the [special_role]! </span>")
owner.special_role = null
. = ..()
return ..()
/datum/antagonist/traitor/proc/handle_hearing(datum/source, list/hearing_args)
var/message = hearing_args[HEARING_RAW_MESSAGE]
@@ -93,6 +102,7 @@
/datum/antagonist/traitor/proc/remove_objective(datum/objective/O)
objectives -= O
/// Generates a complete set of traitor objectives up to the traitor objective limit, including non-generic objectives such as martyr and hijack.
/datum/antagonist/traitor/proc/forge_traitor_objectives()
traitor_kind.forge_objectives(src)
@@ -151,21 +161,42 @@
H.dna.add_mutation(CLOWNMUT)
UnregisterSignal(M, COMSIG_MOVABLE_HEAR)
/datum/antagonist/traitor/ui_static_data(mob/user)
var/list/data = list()
data["phrases"] = jointext(GLOB.syndicate_code_phrase, ", ")
data["responses"] = jointext(GLOB.syndicate_code_response, ", ")
data["theme"] = traitor_kind.tgui_theme //traitor_flavor["ui_theme"]
data["code"] = uplink.unlock_code
data["intro"] = "You are from [traitor_kind.employer]." //traitor_flavor["introduction"]
data["allies"] = "Most other syndicate operatives are not to be trusted (but try not to rat them out), as they might have been assigned opposing objectives." //traitor_flavor["allies"]
data["goal"] = "We do not approve of mindless killing of innocent workers; \"get in, get done, get out\" is our motto." //traitor_flavor["goal"]
data["has_uplink"] = uplink ? TRUE : FALSE
if(uplink)
data["uplink_intro"] = "You have been provided with a standard uplink to accomplish your task." //traitor_flavor["uplink"]
data["uplink_unlock_info"] = uplink.unlock_text
data["objectives"] = get_objectives()
return data
/// Outputs this shift's codewords and responses to the antag's chat and copies them to their memory.
/datum/antagonist/traitor/proc/give_codewords()
if(!owner.current)
return
var/mob/traitor_mob=owner.current
var/mob/traitor_mob = owner.current
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var/dat = "<U><B>The Syndicate have provided you with the following codewords to identify fellow agents:</B></U>\n"
dat += "<B>Code Phrase</B>: <span class='blue'>[phrases]</span>\n"
dat += "<B>Code Response</B>: <span class='red'>[responses]</span>"
to_chat(traitor_mob, dat)
to_chat(traitor_mob, "<U><B>The Syndicate have provided you with the following codewords to identify fellow agents:</B></U>")
to_chat(traitor_mob, "<B>Code Phrase</B>: [span_blue("[phrases]")]")
to_chat(traitor_mob, "<B>Code Response</B>: [span_red("[responses]")]")
antag_memory += "<b>Code Phrase</b>: <span class='blue'>[phrases]</span><br>"
antag_memory += "<b>Code Response</b>: <span class='red'>[responses]</span><br>"
antag_memory += "<b>Code Phrase</b>: [span_blue("[phrases]")]<br>"
antag_memory += "<b>Code Response</b>: [span_red("[responses]")]<br>"
to_chat(traitor_mob, "Use the codewords during regular conversation to identify other agents. Proceed with caution, however, as everyone is a potential foe.")
to_chat(traitor_mob, span_alertwarning("You memorize the codewords, allowing you to recognise them when heard."))
/datum/antagonist/traitor/proc/add_law_zero()
var/mob/living/silicon/ai/killer = owner.current
@@ -220,44 +251,43 @@
where = "In your [equipped_slot]"
to_chat(mob, "<BR><BR><span class='info'>[where] is a folder containing <b>secret documents</b> that another Syndicate group wants. We have set up a meeting with one of their agents on station to make an exchange. Exercise extreme caution as they cannot be trusted and may be hostile.</span><BR>")
//TODO Collate
/datum/antagonist/traitor/roundend_report()
var/list/result = list()
var/traitorwin = TRUE
var/traitor_won = TRUE
result += printplayer(owner)
var/TC_uses = 0
var/uplink_true = FALSE
var/used_telecrystals = 0
var/uplink_owned = FALSE
var/purchases = ""
LAZYINITLIST(GLOB.uplink_purchase_logs_by_key)
var/datum/uplink_purchase_log/H = GLOB.uplink_purchase_logs_by_key[owner.key]
if(H)
TC_uses = H.total_spent
uplink_true = TRUE
purchases += H.generate_render(FALSE)
// Uplinks add an entry to uplink_purchase_logs_by_key on init.
var/datum/uplink_purchase_log/purchase_log = GLOB.uplink_purchase_logs_by_key[owner.key]
if(purchase_log)
used_telecrystals = purchase_log.total_spent
uplink_owned = TRUE
purchases += purchase_log.generate_render(FALSE)
var/objectives_text = ""
if(objectives.len)//If the traitor had no objectives, don't need to process this.
if(objectives.len) //If the traitor had no objectives, don't need to process this.
var/count = 1
for(var/datum/objective/objective in objectives)
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
traitorwin = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
var/completion = objective.check_completion()
if(completion >= 1)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_greentext("Success!")]"
else if(completion <= 0)
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
traitor_won = FALSE
else
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text]"
objectives_text += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
count++
if(uplink_true)
var/uplink_text = "(used [TC_uses] TC) [purchases]"
if(TC_uses==0 && traitorwin)
if(uplink_owned)
var/uplink_text = "(used [used_telecrystals] TC) [purchases]"
if((used_telecrystals == 0) && traitor_won)
var/static/icon/badass = icon('icons/badass.dmi', "badass")
uplink_text += "<BIG>[icon2html(badass, world)]</BIG>"
result += uplink_text
@@ -266,55 +296,59 @@
var/special_role_text = lowertext(name)
if(contractor_hub)
if (contractor_hub)
result += contractor_round_end()
if(traitorwin)
result += "<span class='greentext'>The [special_role_text] was successful!</span>"
if(traitor_won)
result += span_greentext("The [special_role_text] was successful!")
else
result += "<span class='redtext'>The [special_role_text] has failed!</span>"
result += span_redtext("The [special_role_text] has failed!")
SEND_SOUND(owner.current, 'sound/ambience/ambifailure.ogg')
return result.Join("<br>")
/// Proc detailing contract kit buys/completed contracts/additional info
/datum/antagonist/traitor/proc/contractor_round_end()
var result = ""
var total_spent_rep = 0
var/result = ""
var/total_spent_rep = 0
var/completed_contracts = 0
var/completed_contracts = contractor_hub.contracts_completed
var/tc_total = contractor_hub.contract_TC_payed_out + contractor_hub.contract_TC_to_redeem
for(var/datum/syndicate_contract/contract in contractor_hub.assigned_contracts)
if(contract.status == CONTRACT_STATUS_COMPLETE)
completed_contracts++
var/contractor_item_icons = "" // Icons of purchases
var/contractor_support_unit = "" // Set if they had a support unit - and shows appended to their contracts completed
for(var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items) // Get all the icons/total cost for all our items bought
/// Get all the icons/total cost for all our items bought
for (var/datum/contractor_item/contractor_purchase in contractor_hub.purchased_items)
contractor_item_icons += "<span class='tooltip_container'>\[ <i class=\"fas [contractor_purchase.item_icon]\"></i><span class='tooltip_hover'><b>[contractor_purchase.name] - [contractor_purchase.cost] Rep</b><br><br>[contractor_purchase.desc]</span> \]</span>"
total_spent_rep += contractor_purchase.cost
if(istype(contractor_purchase, /datum/contractor_item/contractor_partner)) // Special case for reinforcements, we want to show their ckey and name on round end.
/// Special case for reinforcements, we want to show their ckey and name on round end.
if (istype(contractor_purchase, /datum/contractor_item/contractor_partner))
var/datum/contractor_item/contractor_partner/partner = contractor_purchase
contractor_support_unit += "<br><b>[partner.partner_mind.key]</b> played <b>[partner.partner_mind.current.name]</b>, their contractor support unit."
if (contractor_hub.purchased_items.len)
result += "<br>(used [total_spent_rep] Rep)"
result += "<br>(used [total_spent_rep] Rep) "
result += contractor_item_icons
result += "<br>"
if(completed_contracts > 0)
if (completed_contracts > 0)
var/pluralCheck = "contract"
if(completed_contracts > 1)
if (completed_contracts > 1)
pluralCheck = "contracts"
result += "Completed <span class='greentext'>[completed_contracts]</span> [pluralCheck] for a total of \
<span class='greentext'>[tc_total] TC</span>!<br>"
result += "Completed [span_greentext("[completed_contracts]")] [pluralCheck] for a total of \
[span_greentext("[tc_total] TC")]![contractor_support_unit]<br>"
return result
/datum/antagonist/traitor/roundend_report_footer()
var/phrases = jointext(GLOB.syndicate_code_phrase, ", ")
var/responses = jointext(GLOB.syndicate_code_response, ", ")
var message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
<b>The code responses were:</b> <span class='redtext'>[responses]</span><br>"
var/message = "<br><b>The code phrases were:</b> <span class='bluetext'>[phrases]</span><br>\
<b>The code responses were:</b> [span_redtext("[responses]")]<br>"
return message
+34 -42
View File
@@ -3,12 +3,15 @@
roundend_category = "wizards/witches"
antagpanel_category = "Wizard"
job_rank = ROLE_WIZARD
antag_hud_type = ANTAG_HUD_WIZ
antag_hud_name = "wizard"
antag_moodlet = /datum/mood_event/focused
threat = 30
hijack_speed = 0.5
ui_name = "AntagInfoWizard"
suicide_cry = "FOR THE FEDERATION!!"
var/give_objectives = TRUE
var/strip = TRUE //strip before equipping
var/allow_rename = TRUE
var/hud_version = "wizard"
var/datum/team/wizard/wiz_team //Only created if wizard summons apprentices
var/move_to_lair = TRUE
var/outfit_type = /datum/outfit/wizard
@@ -50,16 +53,17 @@
wiz_team = new(owner)
wiz_team.name = "[owner.current.real_name] team"
wiz_team.master_wizard = src
update_wiz_icons_added(owner.current)
add_antag_hud(antag_hud_type, antag_hud_name, owner.current)
/datum/antagonist/wizard/proc/send_to_lair()
if(!owner || !owner.current)
if(!owner)
CRASH("Antag datum with no owner.")
if(!owner.current)
return
if(!GLOB.wizardstart.len)
SSjob.SendToLateJoin(owner.current)
to_chat(owner, "HOT INSERTION, GO GO GO")
else
owner.current.forceMove(pick(GLOB.wizardstart))
owner.current.forceMove(pick(GLOB.wizardstart))
/datum/antagonist/wizard/proc/create_objectives()
var/datum/objective/flavor/wizard/new_objective = new
@@ -79,7 +83,7 @@
/datum/antagonist/wizard/proc/equip_wizard()
if(!owner)
return
CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -91,18 +95,14 @@
H.age = wiz_age
H.equipOutfit(outfit_type)
/datum/antagonist/wizard/greet()
to_chat(owner, "<span class='boldannounce'>You are the Space Wizard!</span>")
to_chat(owner, "<B>The Space Wizards Federation has given you the following tasks:</B>")
owner.announce_objectives()
to_chat(owner, "<B>These are merely guidelines! The federation are your masters, but you forge your own path!</B>")
to_chat(owner, "You will find a list of available spells in your spell book. Choose your magic arsenal carefully.")
to_chat(owner, "The spellbook is bound to you, and others cannot use it.")
to_chat(owner, "In your pockets you will find a teleport scroll. Use it as needed.")
to_chat(owner,"<B>Remember:</B> do not forget to prepare your spells.")
/datum/antagonist/wizard/ui_static_data(mob/user)
. = ..()
var/list/data = list()
data["objectives"] = get_objectives()
return data
/datum/antagonist/wizard/farewell()
to_chat(owner, "<span class='userdanger'>You have been brainwashed! You are no longer a wizard!</span>")
to_chat(owner, span_userdanger("You have been brainwashed! You are no longer a wizard!"))
/datum/antagonist/wizard/proc/rename_wizard()
set waitfor = FALSE
@@ -111,7 +111,7 @@
var/wizard_name_second = pick(GLOB.wizard_second)
var/randomname = "[wizard_name_first] [wizard_name_second]"
var/mob/living/wiz_mob = owner.current
var/newname = reject_bad_name(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN))
var/newname = sanitize_name(reject_bad_text(stripped_input(wiz_mob, "You are the [name]. Would you like to change your name to something else?", "Name change", randomname, MAX_NAME_LEN)))
if (!newname)
newname = randomname
@@ -120,12 +120,12 @@
/datum/antagonist/wizard/apply_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_added(M, wiz_team ? TRUE : FALSE) //Don't bother showing the icon if you're solo wizard
add_antag_hud(antag_hud_type, antag_hud_name, M)
M.faction |= ROLE_WIZARD
/datum/antagonist/wizard/remove_innate_effects(mob/living/mob_override)
var/mob/living/M = mob_override || owner.current
update_wiz_icons_removed(M)
remove_antag_hud(antag_hud_type, M)
M.faction -= ROLE_WIZARD
@@ -138,7 +138,7 @@
/datum/antagonist/wizard/apprentice
name = "Wizard Apprentice"
hud_version = "apprentice"
antag_hud_name = "apprentice"
var/datum/mind/master
var/school = APPRENTICE_DESTRUCTION
outfit_type = /datum/outfit/wizard/apprentice
@@ -157,7 +157,7 @@
/datum/antagonist/wizard/apprentice/equip_wizard()
. = ..()
if(!owner)
return
CRASH("Antag datum with no owner.")
var/mob/living/carbon/human/H = owner.current
if(!istype(H))
return
@@ -169,12 +169,12 @@
if(APPRENTICE_BLUESPACE)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality bending mobility spells. You are able to cast teleport and ethereal jaunt.")
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned reality-bending mobility spells. You are able to cast teleport and ethereal jaunt.")
if(APPRENTICE_HEALING)
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/charge(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/forcewall(null))
H.put_in_hands(new /obj/item/gun/magic/staff/healing(H))
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned livesaving survival spells. You are able to cast charge and forcewall.")
to_chat(owner, "<B>Your service has not gone unrewarded, however. Studying under [master.current.real_name], you have learned life-saving survival spells. You are able to cast charge and forcewall.")
if(APPRENTICE_ROBELESS)
owner.AddSpell(new /obj/effect/proc_holder/spell/aoe_turf/knock(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/pointed/mind_transfer(null))
@@ -194,6 +194,7 @@
//Random event wizard
/datum/antagonist/wizard/apprentice/imposter
name = "Wizard Imposter"
show_in_antagpanel = FALSE
allow_rename = FALSE
move_to_lair = FALSE
@@ -224,20 +225,11 @@
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/turf_teleport/blink(null))
owner.AddSpell(new /obj/effect/proc_holder/spell/targeted/ethereal_jaunt(null))
/datum/antagonist/wizard/proc/update_wiz_icons_added(mob/living/wiz,join = TRUE)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.join_hud(wiz)
set_antag_hud(wiz, hud_version)
/datum/antagonist/wizard/proc/update_wiz_icons_removed(mob/living/wiz)
var/datum/atom_hud/antag/wizhud = GLOB.huds[ANTAG_HUD_WIZ]
wizhud.leave_hud(wiz)
set_antag_hud(wiz, null)
/datum/antagonist/wizard/academy
name = "Academy Teacher"
show_in_antagpanel = FALSE
outfit_type = /datum/outfit/wizard/academy
move_to_lair = FALSE
/datum/antagonist/wizard/academy/equip_wizard()
. = ..()
@@ -250,7 +242,7 @@
if(!istype(M))
return
var/obj/item/implant/exile/Implant = new
var/obj/item/implant/exile/Implant = new/obj/item/implant/exile(M)
Implant.implant(M)
/datum/antagonist/wizard/academy/create_objectives()
@@ -265,25 +257,25 @@
parts += printplayer(owner)
var/count = 1
var/wizardwin = 1
var/wizardwin = TRUE
for(var/datum/objective/objective in objectives)
if(objective.completable)
var/completion = objective.check_completion()
if(completion >= 1)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='greentext'><B>Success!</B></span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_greentext("Success!")]"
else if(completion <= 0)
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='redtext'>Fail.</span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] [span_redtext("Fail.")]"
wizardwin = FALSE
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
parts += "<br><B>Objective #[count]</B>: [objective.explanation_text] <span class='yellowtext'>[completion*100]%</span>"
else
parts += "<B>Objective #[count]</B>: [objective.explanation_text]"
count++
if(wizardwin)
parts += "<span class='greentext'>The wizard was successful!</span>"
parts += span_greentext("The wizard was successful!")
else
parts += "<span class='redtext'>The wizard has failed!</span>"
parts += span_redtext("The wizard has failed!")
if(owner.spell_list.len>0)
parts += "<B>[owner.name] used the following spells: </B>"
+2 -1
View File
@@ -74,7 +74,7 @@
if(spill && R.total_volume >= 5)
R.reaction(turfing ? target : target.loc, TOUCH, 1, 0)
if(!turfing)
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1))
R.trans_to(target, R.total_volume * (spill ? G.fluid_transfer_factor : 1), log = TRUE)
G.last_orgasmed = world.time
R.clear_reagents()
@@ -117,6 +117,7 @@
if(!do_after(src, mb_time, target = src) || !in_range(src, container) || !G.climaxable(src, TRUE))
return
to_chat(src,"<span class='userlove'>You used your [G.name] to fill [container].</span>")
message_admins("[src] used their [G.name] to fill [container].")
do_climax(fluid_source, container, G, FALSE)
/mob/living/carbon/human/proc/pick_climax_genitals(silent = FALSE)
+2 -2
View File
@@ -168,9 +168,9 @@
R.clear_reagents()
R.maximum_volume = fluid_max_volume
if(fluid_id)
R.add_reagent(fluid_id,amount)
R.add_reagent(fluid_id,amount, owner.get_blood_data())
else if(linked_organ?.fluid_id)
R.add_reagent(linked_organ.fluid_id,amount)
R.add_reagent(linked_organ.fluid_id,amount, owner.get_blood_data())
return TRUE
/obj/item/organ/genital/proc/update_link()
-28
View File
@@ -13,9 +13,6 @@ GLOBAL_LIST_EMPTY(asset_datums)
var/_abstract = /datum/asset
var/cached_url_mappings
/// Whether or not this asset should be loaded in the "early assets" SS
var/early = FALSE
/datum/asset/New()
GLOB.asset_datums[type] = src
register()
@@ -368,28 +365,3 @@ GLOBAL_LIST_EMPTY(asset_datums)
/datum/asset/simple/namespaced/proc/get_htmlloader(filename)
return url2htmlloader(SSassets.transport.get_asset_url(filename, assets[filename]))
/// A subtype to generate a JSON file from a list
/datum/asset/json
_abstract = /datum/asset/json
/// The filename, will be suffixed with ".json"
var/name
/datum/asset/json/send(client)
return SSassets.transport.send_assets(client, "data/[name].json")
/datum/asset/json/get_url_mappings()
return list(
"[name].json" = SSassets.transport.get_asset_url("data/[name].json"),
)
/datum/asset/json/register()
var/filename = "data/[name].json"
fdel(filename)
text2file(json_encode(generate()), filename)
SSassets.transport.register_asset(filename, fcopy_rsc(filename))
fdel(filename)
/// Returns the data that will be JSON encoded
/datum/asset/json/proc/generate()
SHOULD_CALL_PARENT(FALSE)
CRASH("generate() not implemented for [type]!")
+159 -113
View File
@@ -1,11 +1,5 @@
//DEFINITIONS FOR ASSET DATUMS START HERE.
/datum/asset/simple/tgui_common
keep_local_name = TRUE
assets = list(
"tgui-common.bundle.js" = file("tgui/public/tgui-common.bundle.js"),
)
/datum/asset/simple/tgui
keep_local_name = TRUE
assets = list(
@@ -54,9 +48,14 @@
/datum/asset/simple/radar_assets
assets = list(
"ntosradarbackground.png" = 'icons/UI_Icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/UI_Icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/UI_Icons/tgui/ntosradar_pointer_S.png'
"ntosradarbackground.png" = 'icons/ui_icons/tgui/ntosradar_background.png',
"ntosradarpointer.png" = 'icons/ui_icons/tgui/ntosradar_pointer.png',
"ntosradarpointerS.png" = 'icons/ui_icons/tgui/ntosradar_pointer_S.png'
)
/datum/asset/simple/circuit_assets
assets = list(
"grid_background.png" = 'icons/ui_icons/tgui/grid_background.png'
)
/datum/asset/spritesheet/simple/pda
@@ -91,6 +90,7 @@
"status" = 'icons/pda_icons/pda_status.png',
"dronephone" = 'icons/pda_icons/pda_dronephone.png',
"emoji" = 'icons/pda_icons/pda_emoji.png'
// "droneblacklist" = 'icons/pda_icons/pda_droneblacklist.png',
)
/datum/asset/spritesheet/simple/paper
@@ -116,7 +116,7 @@
/datum/asset/simple/irv
assets = list(
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/IRV/jquery-ui.custom-core-widgit-mouse-sortable-min.js',
"jquery-ui.custom-core-widgit-mouse-sortable-min.js" = 'html/jquery/jquery-ui.custom-core-widgit-mouse-sortable.min.js',
)
/datum/asset/group/irv
@@ -149,11 +149,10 @@
)
parents = list("changelog.html" = 'html/changelog.html')
/datum/asset/simple/jquery
legacy = TRUE
assets = list(
"jquery.min.js" = 'html/jquery.min.js',
"jquery.min.js" = 'html/jquery/jquery.min.js',
)
/datum/asset/simple/namespaced/fontawesome
@@ -223,102 +222,102 @@
/datum/asset/simple/arcade
assets = list(
"boss1.gif" = 'icons/UI_Icons/Arcade/boss1.gif',
"boss2.gif" = 'icons/UI_Icons/Arcade/boss2.gif',
"boss3.gif" = 'icons/UI_Icons/Arcade/boss3.gif',
"boss4.gif" = 'icons/UI_Icons/Arcade/boss4.gif',
"boss5.gif" = 'icons/UI_Icons/Arcade/boss5.gif',
"boss6.gif" = 'icons/UI_Icons/Arcade/boss6.gif',
"boss1.gif" = 'icons/ui_icons/arcade/boss1.gif',
"boss2.gif" = 'icons/ui_icons/arcade/boss2.gif',
"boss3.gif" = 'icons/ui_icons/arcade/boss3.gif',
"boss4.gif" = 'icons/ui_icons/arcade/boss4.gif',
"boss5.gif" = 'icons/ui_icons/arcade/boss5.gif',
"boss6.gif" = 'icons/ui_icons/arcade/boss6.gif',
)
/datum/asset/spritesheet/simple/achievements
name ="achievements"
assets = list(
"default" = 'icons/UI_Icons/Achievements/default.png',
"basemisc" = 'icons/UI_Icons/Achievements/basemisc.png',
"baseboss" = 'icons/UI_Icons/Achievements/baseboss.png',
"baseskill" = 'icons/UI_Icons/Achievements/baseskill.png',
"bbgum" = 'icons/UI_Icons/Achievements/Boss/bbgum.png',
"colossus" = 'icons/UI_Icons/Achievements/Boss/colossus.png',
"hierophant" = 'icons/UI_Icons/Achievements/Boss/hierophant.png',
"drake" = 'icons/UI_Icons/Achievements/Boss/drake.png',
"legion" = 'icons/UI_Icons/Achievements/Boss/legion.png',
"miner" = 'icons/UI_Icons/Achievements/Boss/miner.png',
"swarmer" = 'icons/UI_Icons/Achievements/Boss/swarmer.png',
"tendril" = 'icons/UI_Icons/Achievements/Boss/tendril.png',
"featofstrength" = 'icons/UI_Icons/Achievements/Misc/featofstrength.png',
"helbital" = 'icons/UI_Icons/Achievements/Misc/helbital.png',
"jackpot" = 'icons/UI_Icons/Achievements/Misc/jackpot.png',
"meteors" = 'icons/UI_Icons/Achievements/Misc/meteors.png',
"timewaste" = 'icons/UI_Icons/Achievements/Misc/timewaste.png',
"upgrade" = 'icons/UI_Icons/Achievements/Misc/upgrade.png',
"clownking" = 'icons/UI_Icons/Achievements/Misc/clownking.png',
"clownthanks" = 'icons/UI_Icons/Achievements/Misc/clownthanks.png',
"rule8" = 'icons/UI_Icons/Achievements/Misc/rule8.png',
"longshift" = 'icons/UI_Icons/Achievements/Misc/longshift.png',
"snail" = 'icons/UI_Icons/Achievements/Misc/snail.png',
"ascension" = 'icons/UI_Icons/Achievements/Misc/ascension.png',
"ashascend" = 'icons/UI_Icons/Achievements/Misc/ashascend.png',
"fleshascend" = 'icons/UI_Icons/Achievements/Misc/fleshascend.png',
"rustascend" = 'icons/UI_Icons/Achievements/Misc/rustascend.png',
"voidascend" = 'icons/UI_Icons/Achievements/Misc/voidascend.png',
"toolbox_soul" = 'icons/UI_Icons/Achievements/Misc/toolbox_soul.png',
"chem_tut" = 'icons/UI_Icons/Achievements/Misc/chem_tut.png',
"mining" = 'icons/UI_Icons/Achievements/Skills/mining.png',
"mafia" = 'icons/UI_Icons/Achievements/Mafia/mafia.png',
"town" = 'icons/UI_Icons/Achievements/Mafia/town.png',
"neutral" = 'icons/UI_Icons/Achievements/Mafia/neutral.png',
"hated" = 'icons/UI_Icons/Achievements/Mafia/hated.png',
"basemafia" ='icons/UI_Icons/Achievements/basemafia.png',
"frenching" = 'icons/UI_Icons/Achievements/Misc/frenchingthebubble.png'
"default" = 'icons/ui_icons/achievements/default.png',
"basemisc" = 'icons/ui_icons/achievements/basemisc.png',
"baseboss" = 'icons/ui_icons/achievements/baseboss.png',
"baseskill" = 'icons/ui_icons/achievements/baseskill.png',
"bbgum" = 'icons/ui_icons/achievements/Boss/bbgum.png',
"colossus" = 'icons/ui_icons/achievements/Boss/colossus.png',
"hierophant" = 'icons/ui_icons/achievements/Boss/hierophant.png',
"drake" = 'icons/ui_icons/achievements/Boss/drake.png',
"legion" = 'icons/ui_icons/achievements/Boss/legion.png',
"miner" = 'icons/ui_icons/achievements/Boss/miner.png',
"swarmer" = 'icons/ui_icons/achievements/Boss/swarmer.png',
"tendril" = 'icons/ui_icons/achievements/Boss/tendril.png',
"featofstrength" = 'icons/ui_icons/achievements/Misc/featofstrength.png',
"helbital" = 'icons/ui_icons/achievements/Misc/helbital.png',
"jackpot" = 'icons/ui_icons/achievements/Misc/jackpot.png',
"meteors" = 'icons/ui_icons/achievements/Misc/meteors.png',
"timewaste" = 'icons/ui_icons/achievements/Misc/timewaste.png',
"upgrade" = 'icons/ui_icons/achievements/Misc/upgrade.png',
"clownking" = 'icons/ui_icons/achievements/Misc/clownking.png',
"clownthanks" = 'icons/ui_icons/achievements/Misc/clownthanks.png',
"rule8" = 'icons/ui_icons/achievements/Misc/rule8.png',
"longshift" = 'icons/ui_icons/achievements/Misc/longshift.png',
"snail" = 'icons/ui_icons/achievements/Misc/snail.png',
"ascension" = 'icons/ui_icons/achievements/Misc/ascension.png',
"ashascend" = 'icons/ui_icons/achievements/Misc/ashascend.png',
"fleshascend" = 'icons/ui_icons/achievements/Misc/fleshascend.png',
"rustascend" = 'icons/ui_icons/achievements/Misc/rustascend.png',
"voidascend" = 'icons/ui_icons/achievements/Misc/voidascend.png',
"toolbox_soul" = 'icons/ui_icons/achievements/Misc/toolbox_soul.png',
"chem_tut" = 'icons/ui_icons/achievements/Misc/chem_tut.png',
"mining" = 'icons/ui_icons/achievements/Skills/mining.png',
"mafia" = 'icons/ui_icons/achievements/Mafia/mafia.png',
"town" = 'icons/ui_icons/achievements/Mafia/town.png',
"neutral" = 'icons/ui_icons/achievements/Mafia/neutral.png',
"hated" = 'icons/ui_icons/achievements/Mafia/hated.png',
"basemafia" ='icons/ui_icons/achievements/basemafia.png',
"frenching" = 'icons/ui_icons/achievements/Misc/frenchingthebubble.png'
)
/datum/asset/spritesheet/simple/pills
name = "pills"
assets = list(
"pill1" = 'icons/UI_Icons/Pills/pill1.png',
"pill2" = 'icons/UI_Icons/Pills/pill2.png',
"pill3" = 'icons/UI_Icons/Pills/pill3.png',
"pill4" = 'icons/UI_Icons/Pills/pill4.png',
"pill5" = 'icons/UI_Icons/Pills/pill5.png',
"pill6" = 'icons/UI_Icons/Pills/pill6.png',
"pill7" = 'icons/UI_Icons/Pills/pill7.png',
"pill8" = 'icons/UI_Icons/Pills/pill8.png',
"pill9" = 'icons/UI_Icons/Pills/pill9.png',
"pill10" = 'icons/UI_Icons/Pills/pill10.png',
"pill11" = 'icons/UI_Icons/Pills/pill11.png',
"pill12" = 'icons/UI_Icons/Pills/pill12.png',
"pill13" = 'icons/UI_Icons/Pills/pill13.png',
"pill14" = 'icons/UI_Icons/Pills/pill14.png',
"pill15" = 'icons/UI_Icons/Pills/pill15.png',
"pill16" = 'icons/UI_Icons/Pills/pill16.png',
"pill17" = 'icons/UI_Icons/Pills/pill17.png',
"pill18" = 'icons/UI_Icons/Pills/pill18.png',
"pill19" = 'icons/UI_Icons/Pills/pill19.png',
"pill20" = 'icons/UI_Icons/Pills/pill20.png',
"pill21" = 'icons/UI_Icons/Pills/pill21.png',
"pill22" = 'icons/UI_Icons/Pills/pill22.png',
"pill1" = 'icons/ui_icons/pills/pill1.png',
"pill2" = 'icons/ui_icons/pills/pill2.png',
"pill3" = 'icons/ui_icons/pills/pill3.png',
"pill4" = 'icons/ui_icons/pills/pill4.png',
"pill5" = 'icons/ui_icons/pills/pill5.png',
"pill6" = 'icons/ui_icons/pills/pill6.png',
"pill7" = 'icons/ui_icons/pills/pill7.png',
"pill8" = 'icons/ui_icons/pills/pill8.png',
"pill9" = 'icons/ui_icons/pills/pill9.png',
"pill10" = 'icons/ui_icons/pills/pill10.png',
"pill11" = 'icons/ui_icons/pills/pill11.png',
"pill12" = 'icons/ui_icons/pills/pill12.png',
"pill13" = 'icons/ui_icons/pills/pill13.png',
"pill14" = 'icons/ui_icons/pills/pill14.png',
"pill15" = 'icons/ui_icons/pills/pill15.png',
"pill16" = 'icons/ui_icons/pills/pill16.png',
"pill17" = 'icons/ui_icons/pills/pill17.png',
"pill18" = 'icons/ui_icons/pills/pill18.png',
"pill19" = 'icons/ui_icons/pills/pill19.png',
"pill20" = 'icons/ui_icons/pills/pill20.png',
"pill21" = 'icons/ui_icons/pills/pill21.png',
"pill22" = 'icons/ui_icons/pills/pill22.png',
)
// /datum/asset/spritesheet/simple/condiments
// name = "condiments"
// assets = list(
// CONDIMASTER_STYLE_FALLBACK = 'icons/UI_Icons/Condiments/emptycondiment.png',
// "enzyme" = 'icons/UI_Icons/Condiments/enzyme.png',
// "flour" = 'icons/UI_Icons/Condiments/flour.png',
// "mayonnaise" = 'icons/UI_Icons/Condiments/mayonnaise.png',
// "milk" = 'icons/UI_Icons/Condiments/milk.png',
// "blackpepper" = 'icons/UI_Icons/Condiments/peppermillsmall.png',
// "rice" = 'icons/UI_Icons/Condiments/rice.png',
// "sodiumchloride" = 'icons/UI_Icons/Condiments/saltshakersmall.png',
// "soymilk" = 'icons/UI_Icons/Condiments/soymilk.png',
// "soysauce" = 'icons/UI_Icons/Condiments/soysauce.png',
// "sugar" = 'icons/UI_Icons/Condiments/sugar.png',
// "ketchup" = 'icons/UI_Icons/Condiments/ketchup.png',
// "capsaicin" = 'icons/UI_Icons/Condiments/hotsauce.png',
// "frostoil" = 'icons/UI_Icons/Condiments/coldsauce.png',
// "bbqsauce" = 'icons/UI_Icons/Condiments/bbqsauce.png',
// "cornoil" = 'icons/UI_Icons/Condiments/oliveoil.png',
// CONDIMASTER_STYLE_FALLBACK = 'icons/ui_icons/condiments/emptycondiment.png',
// "enzyme" = 'icons/ui_icons/condiments/enzyme.png',
// "flour" = 'icons/ui_icons/condiments/flour.png',
// "mayonnaise" = 'icons/ui_icons/condiments/mayonnaise.png',
// "milk" = 'icons/ui_icons/condiments/milk.png',
// "blackpepper" = 'icons/ui_icons/condiments/peppermillsmall.png',
// "rice" = 'icons/ui_icons/condiments/rice.png',
// "sodiumchloride" = 'icons/ui_icons/condiments/saltshakersmall.png',
// "soymilk" = 'icons/ui_icons/condiments/soymilk.png',
// "soysauce" = 'icons/ui_icons/condiments/soysauce.png',
// "sugar" = 'icons/ui_icons/condiments/sugar.png',
// "ketchup" = 'icons/ui_icons/condiments/ketchup.png',
// "capsaicin" = 'icons/ui_icons/condiments/hotsauce.png',
// "frostoil" = 'icons/ui_icons/condiments/coldsauce.png',
// "bbqsauce" = 'icons/ui_icons/condiments/bbqsauce.png',
// "cornoil" = 'icons/ui_icons/condiments/oliveoil.png',
// )
//this exists purely to avoid meta by pre-loading all language icons.
@@ -410,9 +409,15 @@
if (machine)
item = machine
// Check for GAGS support where necessary
// var/greyscale_config = initial(item.greyscale_config)
// var/greyscale_colors = initial(item.greyscale_colors)
// if (greyscale_config && greyscale_colors)
// icon_file = SSgreyscale.GetColoredIconByType(greyscale_config, greyscale_colors)
// else
icon_file = initial(item.icon)
icon_state = initial(item.icon_state)
icon_state = initial(item.icon_state)
if(!(icon_state in icon_states(icon_file)))
warning("design [D] with icon '[icon_file]' missing state '[icon_state]'")
continue
@@ -441,7 +446,11 @@
if (!ispath(item, /atom))
continue
var/icon_file = initial(item.icon)
var/icon_file
// if (initial(item.greyscale_colors) && initial(item.greyscale_config))
// icon_file = SSgreyscale.GetColoredIconByType(initial(item.greyscale_config), initial(item.greyscale_colors))
// else
icon_file = initial(item.icon)
var/icon_state = initial(item.icon_state)
var/icon/I
@@ -543,25 +552,33 @@
// Insert(id, fish_icon, fish_icon_state)
// ..()
/datum/asset/simple/adventure
assets = list(
"default" = 'icons/ui_icons/adventure/default.png',
"grue" = 'icons/ui_icons/adventure/grue.png',
"signal_lost" ='icons/ui_icons/adventure/signal_lost.png',
"trade" = 'icons/ui_icons/adventure/trade.png',
)
/datum/asset/simple/inventory
assets = list(
"inventory-glasses.png" = 'icons/UI_Icons/inventory/glasses.png',
"inventory-head.png" = 'icons/UI_Icons/inventory/head.png',
"inventory-neck.png" = 'icons/UI_Icons/inventory/neck.png',
"inventory-mask.png" = 'icons/UI_Icons/inventory/mask.png',
"inventory-ears.png" = 'icons/UI_Icons/inventory/ears.png',
"inventory-uniform.png" = 'icons/UI_Icons/inventory/uniform.png',
"inventory-suit.png" = 'icons/UI_Icons/inventory/suit.png',
"inventory-gloves.png" = 'icons/UI_Icons/inventory/gloves.png',
"inventory-hand_l.png" = 'icons/UI_Icons/inventory/hand_l.png',
"inventory-hand_r.png" = 'icons/UI_Icons/inventory/hand_r.png',
"inventory-shoes.png" = 'icons/UI_Icons/inventory/shoes.png',
"inventory-suit_storage.png" = 'icons/UI_Icons/inventory/suit_storage.png',
"inventory-id.png" = 'icons/UI_Icons/inventory/id.png',
"inventory-belt.png" = 'icons/UI_Icons/inventory/belt.png',
"inventory-back.png" = 'icons/UI_Icons/inventory/back.png',
"inventory-pocket.png" = 'icons/UI_Icons/inventory/pocket.png',
"inventory-collar.png" = 'icons/UI_Icons/inventory/collar.png',
"inventory-glasses.png" = 'icons/ui_icons/inventory/glasses.png',
"inventory-head.png" = 'icons/ui_icons/inventory/head.png',
"inventory-neck.png" = 'icons/ui_icons/inventory/neck.png',
"inventory-mask.png" = 'icons/ui_icons/inventory/mask.png',
"inventory-ears.png" = 'icons/ui_icons/inventory/ears.png',
"inventory-uniform.png" = 'icons/ui_icons/inventory/uniform.png',
"inventory-suit.png" = 'icons/ui_icons/inventory/suit.png',
"inventory-gloves.png" = 'icons/ui_icons/inventory/gloves.png',
"inventory-hand_l.png" = 'icons/ui_icons/inventory/hand_l.png',
"inventory-hand_r.png" = 'icons/ui_icons/inventory/hand_r.png',
"inventory-shoes.png" = 'icons/ui_icons/inventory/shoes.png',
"inventory-suit_storage.png" = 'icons/ui_icons/inventory/suit_storage.png',
"inventory-id.png" = 'icons/ui_icons/inventory/id.png',
"inventory-belt.png" = 'icons/ui_icons/inventory/belt.png',
"inventory-back.png" = 'icons/ui_icons/inventory/back.png',
"inventory-pocket.png" = 'icons/ui_icons/inventory/pocket.png',
"inventory-collar.png" = 'icons/ui_icons/inventory/collar.png',
)
/// Removes all non-alphanumerics from the text, keep in mind this can lead to id conflicts
@@ -571,5 +588,34 @@
/datum/asset/simple/tutorial_advisors
assets = list(
"chem_help_advisor.gif" = 'icons/UI_Icons/Advisors/chem_help_advisor.gif',
"chem_help_advisor.gif" = 'icons/ui_icons/advisors/chem_help_advisor.gif',
)
// /datum/asset/spritesheet/moods
// name = "moods"
// var/iconinserted = 1
// /datum/asset/spritesheet/moods/register()
// for(var/i in 1 to 9)
// var/target_to_insert = "mood"+"[iconinserted]"
// Insert(target_to_insert, 'icons/hud/screen_gen.dmi', target_to_insert)
// iconinserted++
// ..()
// /datum/asset/spritesheet/moods/ModifyInserted(icon/pre_asset)
// var/blended_color
// switch(iconinserted)
// if(1)
// blended_color = "#f15d36"
// if(2 to 3)
// blended_color = "#f38943"
// if(4)
// blended_color = "#dfa65b"
// if(5)
// blended_color = "#4b96c4"
// if(6)
// blended_color = "#86d656"
// else
// blended_color = "#2eeb9a"
// pre_asset.Blend(blended_color, ICON_MULTIPLY)
// return pre_asset
@@ -115,7 +115,7 @@
if (unreceived.len)
if (unreceived.len >= ASSET_CACHE_TELL_CLIENT_AMOUNT)
to_chat(client, "Sending Resources...")
to_chat(client, "<span class='infoplain'>Sending Resources...</span>")
for (var/asset_name in unreceived)
var/new_asset_name = asset_name
+52 -11
View File
@@ -43,6 +43,7 @@
)
)
fusion_power = 3
enthalpy = -393500
/datum/gas/plasma
id = GAS_PLASMA
@@ -54,7 +55,10 @@
heat_penalty = 15
transmit_modifier = 4
powermix = 1
// no fire info cause it has its own bespoke reaction for trit generation reasons
fire_burn_rate = OXYGEN_BURN_RATE_BASE // named when plasma fires were the only fires, surely
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
fire_products = "plasma_fire"
enthalpy = FIRE_PLASMA_ENERGY_RELEASED // 3000000, 3 megajoules, 3000 kj
/datum/gas/water_vapor
id = GAS_H2O
@@ -64,6 +68,7 @@
moles_visible = MOLES_GAS_VISIBLE
fusion_power = 8
heat_penalty = 8
enthalpy = -241800 // FIRE_HYDROGEN_ENERGY_RELEASED is actually what this was supposed to be
powermix = 1
breath_reagent = /datum/reagent/water
@@ -84,6 +89,7 @@
fire_products = list(GAS_N2 = 1)
oxidation_rate = 0.5
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST + 100
enthalpy = 81600
heat_resistance = 6
/datum/gas/nitryl
@@ -95,8 +101,22 @@
flags = GAS_FLAG_DANGEROUS
fusion_power = 15
fire_products = list(GAS_N2 = 0.5)
enthalpy = 33200
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/hydrogen
id = GAS_HYDROGEN
specific_heat = 10
name = "Hydrogen"
flags = GAS_FLAG_DANGEROUS
fusion_power = 0
powermix = 1
heat_penalty = 3
transmit_modifier = 10
fire_products = list(GAS_H2O = 1)
fire_burn_rate = 2
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
/datum/gas/tritium
id = GAS_TRITIUM
specific_heat = 10
@@ -108,13 +128,11 @@
powermix = 1
heat_penalty = 10
transmit_modifier = 30
/*
these are for when we add hydrogen, trit gets to keep its hardcoded fire for legacy reasons
fire_provides = list(GAS_H2O = 2)
fire_products = list(GAS_H2O = 1)
enthalpy = 40000
fire_burn_rate = 2
fire_energy_released = FIRE_HYDROGEN_ENERGY_RELEASED
fire_radiation_released = 50 // arbitrary number, basically 60 moles of trit burning will just barely start to harm you
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST - 50
*/
/datum/gas/bz
id = GAS_BZ
@@ -124,6 +142,7 @@
fusion_power = 8
powermix = 1
heat_penalty = 5
enthalpy = FIRE_CARBON_ENERGY_RELEASED // it is a mystery
transmit_modifier = -2
radioactivity_modifier = 5
@@ -139,7 +158,8 @@
name = "Pluoxium"
fusion_power = 10
oxidation_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST * 1000 // it is VERY stable
oxidation_rate = 8
oxidation_rate = 8 // when it can oxidize, it can oxidize a LOT
enthalpy = -50000 // but it reduces the heat output a bit
powermix = -1
heat_penalty = -1
transmit_modifier = -5
@@ -172,7 +192,7 @@
alert_type = /atom/movable/screen/alert/too_much_ch4
)
)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
enthalpy = -74600
fire_temperature = FIRE_MINIMUM_TEMPERATURE_TO_EXIST
/datum/gas/methyl_bromide
@@ -192,7 +212,28 @@
alert_type = /atom/movable/screen/alert/too_much_ch3br
)
)
fire_products = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BZ = 0.5)
fire_energy_released = FIRE_CARBON_ENERGY_RELEASED
fire_burn_rate = 0.5
fire_products = list(GAS_CO2 = 1, GAS_H2O = 1.5, GAS_BROMINE = 0.5)
enthalpy = -35400
fire_burn_rate = 4 / 7 // oh no
fire_temperature = 808 // its autoignition, it apparently doesn't spark readily, so i don't put it lower
/datum/gas/bromine
id = GAS_BROMINE
specific_heat = 76
name = "Bromine"
flags = GAS_FLAG_DANGEROUS
group = GAS_GROUP_CHEMICALS
enthalpy = 193 // yeah it's small but it's good to include it
breath_reagent = /datum/reagent/bromine
/datum/gas/ammonia
id = GAS_AMMONIA
specific_heat = 35
name = "Ammonia"
flags = GAS_FLAG_DANGEROUS
group = GAS_GROUP_CHEMICALS
enthalpy = -45900
breath_reagent = /datum/reagent/ammonia
fire_products = list(GAS_H2O = 1.5, GAS_N2 = 0.5)
fire_burn_rate = 4/3
fire_temperature = 924
+24 -8
View File
@@ -15,6 +15,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
/proc/_auxtools_register_gas(datum/gas/gas) // makes sure auxtools knows stuff about this gas
/datum/auxgm
var/done_initializing = FALSE
var/list/datums = list()
var/list/specific_heats = list()
var/list/names = list()
@@ -32,30 +33,34 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
var/list/oxidation_temperatures = list()
var/list/oxidation_rates = list()
var/list/fire_temperatures = list()
var/list/fire_enthalpies = list()
var/list/enthalpies = list()
var/list/fire_products = list()
var/list/fire_burn_rates = list()
var/list/supermatter = list()
var/list/groups_by_gas = list()
var/list/groups = list()
/datum/gas
var/id = ""
var/specific_heat = 0
var/name = ""
var/gas_overlay = "" //icon_state in icons/effects/atmospherics.dmi
var/color = "#ffff"
var/moles_visible = null
var/flags = NONE //currently used by canisters
var/group = null // groups for scrubber/filter listing
var/fusion_power = 0 // How much the gas destabilizes a fusion reaction
var/breath_results = GAS_CO2 // what breathing this breathes out
var/breath_reagent = null // what breathing this adds to your reagents
var/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/datum/reagent/breath_reagent = null // what breathing this adds to your reagents
var/datum/reagent/breath_reagent_dangerous = null // what breathing this adds to your reagents IF it's above a danger threshold
var/list/breath_alert_info = null // list for alerts that pop up when you have too much/not enough of something
var/oxidation_temperature = null // temperature above which this gas is an oxidizer; null for none
var/oxidation_rate = 1 // how many moles of this can oxidize how many moles of material
var/fire_temperature = null // temperature above which gas may catch fire; null for none
var/list/fire_products = null // what results when this gas is burned (oxidizer or fuel); null for none
var/fire_energy_released = 0 // how much energy is released per mole of fuel burned
var/enthalpy = 0 // Standard enthalpy of formation in joules, used for fires
var/fire_burn_rate = 1 // how many moles are burned per product released
var/fire_radiation_released = 0 // How much radiation is released when this gas burns
var/powermix = 0 // how much this gas contributes to the supermatter's powermix ratio
var/heat_penalty = 0 // heat and waste penalty from having the supermatter crystal surrounded by this gas; negative numbers reduce
var/transmit_modifier = 0 // bonus to supermatter power generation (multiplicative, since it's % based, and divided by 10)
@@ -94,21 +99,31 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
breath_reagents[g] = gas.breath_reagent
if(gas.breath_reagent_dangerous)
breath_reagents_dangerous[g] = gas.breath_reagent_dangerous
if(gas.oxidation_temperature)
oxidation_temperatures[g] = gas.oxidation_temperature
oxidation_rates[g] = gas.oxidation_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
enthalpies[g] = gas.enthalpy
else if(gas.fire_temperature)
fire_temperatures[g] = gas.fire_temperature
fire_burn_rates[g] = gas.fire_burn_rate
if(gas.fire_products)
fire_products[g] = gas.fire_products
fire_enthalpies[g] = gas.fire_energy_released
enthalpies[g] = gas.enthalpy
if(gas.group)
if(!(gas.group in groups))
groups[gas.group] = list()
groups[gas.group] += gas
groups_by_gas[g] = gas.group
add_supermatter_properties(gas)
_auxtools_register_gas(gas)
if(done_initializing)
for(var/r in SSair.gas_reactions)
var/datum/gas_reaction/R = r
R.init_reqs()
SSair.auxtools_update_reactions()
SEND_GLOBAL_SIGNAL(COMSIG_GLOB_NEW_GAS, g)
/proc/finalize_gas_refs()
@@ -127,6 +142,7 @@ GLOBAL_LIST_INIT(nonreactive_gases, typecacheof(list(GAS_O2, GAS_N2, GAS_CO2, GA
for(var/breathing_class_path in subtypesof(/datum/breathing_class))
var/datum/breathing_class/class = new breathing_class_path
breathing_classes[breathing_class_path] = class
done_initializing = TRUE
finalize_gas_refs()
@@ -53,23 +53,68 @@
id = "vapor"
/datum/gas_reaction/water_vapor/init_reqs()
min_requirements = list(GAS_H2O = MOLES_GAS_VISIBLE)
min_requirements = list(
GAS_H2O = MOLES_GAS_VISIBLE,
"MAX_TEMP" = T0C + 40
)
/datum/gas_reaction/water_vapor/react(datum/gas_mixture/air, datum/holder)
var/turf/open/location = isturf(holder) ? holder : null
. = NO_REACTION
var/turf/open/location = holder
if(!istype(location))
return NO_REACTION
if (air.return_temperature() <= WATER_VAPOR_FREEZE)
if(location && location.freon_gas_act())
. = REACTING
return REACTING
else if(location && location.water_vapor_gas_act())
air.adjust_moles(GAS_H2O,-MOLES_GAS_VISIBLE)
. = REACTING
return REACTING
// no test cause it's entirely based on location
/datum/gas_reaction/condensation
priority = 0
name = "Condensation"
id = "condense"
exclude = TRUE
var/datum/reagent/condensing_reagent
/datum/gas_reaction/condensation/New(datum/reagent/R)
. = ..()
if(!istype(R))
return
min_requirements = list(
"MAX_TEMP" = initial(R.boiling_point)
)
min_requirements[R.get_gas()] = MOLES_GAS_VISIBLE
name = "[R.name] condensation"
id = "[R.type] condensation"
condensing_reagent = R
exclude = FALSE
/datum/gas_reaction/condensation/react(datum/gas_mixture/air, datum/holder)
. = NO_REACTION
var/turf/open/location = holder
if(!istype(location))
return
var/temperature = air.return_temperature()
var/static/datum/reagents/reagents_holder = new
reagents_holder.clear_reagents()
reagents_holder.chem_temp = temperature
var/G = condensing_reagent.get_gas()
var/amt = air.get_moles(G)
air.adjust_moles(G, -min(initial(condensing_reagent.condensation_amount), amt))
reagents_holder.add_reagent(condensing_reagent, amt)
. = REACTING
for(var/atom/movable/AM in location)
if(location.intact && AM.level == 1)
continue
reagents_holder.reaction(AM, TOUCH)
reagents_holder.reaction(location, TOUCH)
//tritium combustion: combustion of oxygen and tritium (treated as hydrocarbons). creates hotspots. exothermic
/datum/gas_reaction/tritfire
priority = -1 //fire should ALWAYS be last, but tritium fires happen before plasma fires
exclude = TRUE // generic fire now takes care of this
name = "Tritium Combustion"
id = "tritfire"
@@ -88,9 +133,9 @@
item.temperature_expose(air, temperature, CELL_VOLUME)
location.temperature_expose(air, temperature, CELL_VOLUME)
/proc/radiation_burn(turf/open/location, energy_released)
/proc/radiation_burn(turf/open/location, rad_power)
if(istype(location) && prob(10))
radiation_pulse(location, energy_released/TRITIUM_BURN_RADIOACTIVITY_FACTOR)
radiation_pulse(location, rad_power)
/datum/gas_reaction/tritfire/react(datum/gas_mixture/air, datum/holder)
var/energy_released = 0
@@ -151,6 +196,7 @@
/datum/gas_reaction/plasmafire
priority = -2 //fire should ALWAYS be last, but plasma fires happen after tritium fires
name = "Plasma Combustion"
exclude = TRUE // generic fire now takes care of this
id = "plasmafire"
/datum/gas_reaction/plasmafire/init_reqs()
@@ -300,7 +346,7 @@
fuels[fuel] *= oxidation_ratio
fuels += oxidizers
var/list/fire_products = GLOB.gas_data.fire_products
var/list/fire_enthalpies = GLOB.gas_data.fire_enthalpies
var/list/fire_enthalpies = GLOB.gas_data.enthalpies
for(var/fuel in fuels + oxidizers)
var/amt = fuels[fuel]
if(!burn_results[fuel])
@@ -535,7 +535,7 @@
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
"power" = 1,
"set_filters" = list(GAS_CO2, GAS_MIASMA),
"set_filters" = list(GAS_CO2, GAS_MIASMA, GAS_GROUP_CHEMICALS),
"scrubbing" = 1,
"widenet" = 0,
))
@@ -554,6 +554,7 @@
GAS_MIASMA,
GAS_PLASMA,
GAS_H2O,
GAS_HYDROGEN,
GAS_HYPERNOB,
GAS_NITROUS,
GAS_NITRYL,
@@ -562,7 +563,8 @@
GAS_STIMULUM,
GAS_PLUOXIUM,
GAS_METHANE,
GAS_METHYL_BROMIDE
GAS_METHYL_BROMIDE,
GAS_GROUP_CHEMICALS
),
"scrubbing" = 1,
"widenet" = 1,
@@ -582,9 +584,16 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE*2
"set_external_pressure" = ONE_ATMOSPHERE*1.4
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE/1.4
))
if(AALARM_MODE_REFILL)
for(var/device_id in A.air_scrub_names)
@@ -596,10 +605,15 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 1,
"checks" = 1,
"set_external_pressure" = ONE_ATMOSPHERE * 3
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 0,
))
if(AALARM_MODE_PANIC,
AALARM_MODE_REPLACEMENT)
for(var/device_id in A.air_scrub_names)
@@ -610,8 +624,14 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 0
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 0
))
if(AALARM_MODE_SIPHON)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
@@ -621,9 +641,14 @@
))
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"is_pressurizing" = 1,
"power" = 0
))
send_signal(device_id, list(
"is_siphoning" = 1,
"power" = 1,
"checks" = 0
))
if(AALARM_MODE_OFF)
for(var/device_id in A.air_scrub_names)
send_signal(device_id, list(
@@ -641,8 +666,12 @@
for(var/device_id in A.air_vent_names)
send_signal(device_id, list(
"power" = 1,
"checks" = 2,
"set_internal_pressure" = 0
"checks" = 0,
"is_pressurizing" = 1
))
send_signal(device_id, list(
"power" = 0,
"is_siphoning" = 1
))
/obj/machinery/airalarm/update_icon_state()
@@ -29,13 +29,16 @@ Passive gate is similar to the regular pump except:
/obj/machinery/atmospherics/components/binary/passive_gate/CtrlClick(mob/user)
if(can_interact(user))
on = !on
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_icon()
return ..()
/obj/machinery/atmospherics/components/binary/passive_gate/AltClick(mob/user)
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
update_icon()
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output set to [target_pressure] kPa")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/passive_gate/Destroy()
@@ -33,25 +33,19 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its pressure.</span>"
/obj/machinery/atmospherics/components/binary/pump/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
on = !on
update_icon()
investigate_log("Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return ..()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/pump/AltClick(mob/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
to_chat(user,"<span class='notice'>You maximize the pressure on the [src].</span>")
investigate_log("Pump, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Pump, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output set to [target_pressure] kPa")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/pump/Destroy()
SSradio.remove_object(src,frequency)
@@ -33,15 +33,20 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its pressure.</span>"
/obj/machinery/atmospherics/components/binary/volume_pump/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
on = !on
update_icon()
investigate_log("Volume Pump, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Volume Pump, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/volume_pump/AltClick(mob/user)
if(can_interact(user))
transfer_rate = MAX_TRANSFER_RATE
investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [transfer_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/binary/volume_pump/Destroy()
SSradio.remove_object(src,frequency)
return ..()
@@ -20,25 +20,19 @@
. += "<span class='notice'>You can hold <b>Alt</b> and click on it to maximize its flow rate.</span>"
/obj/machinery/atmospherics/components/trinary/filter/CtrlClick(mob/user)
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
on = !on
update_icon()
investigate_log("Filter, [src.name], turned on by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Filter, [src.name], turned [on ? "on" : "off"] by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/filter/AltClick(mob/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(user.canUseTopic(src, BE_CLOSE, FALSE,))
if(can_interact(user))
transfer_rate = MAX_TRANSFER_RATE
to_chat(user,"<span class='notice'>You maximize the flow rate on the [src].</span>")
investigate_log("Filter, [src.name], was maximized by [key_name(usr)] at [x], [y], [z], [A]", INVESTIGATE_ATMOS)
message_admins("Filter, [src.name], was maximized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [transfer_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [transfer_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/filter/proc/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
@@ -100,7 +94,10 @@
if(transfer_ratio > 0)
if(filter_type && air2.return_pressure() <= 9000)
air1.scrub_into(air2, transfer_ratio, list(filter_type))
if(filter_type in GLOB.gas_data.groups)
air1.scrub_into(air2, transfer_ratio, GLOB.gas_data.groups[filter_type])
else
air1.scrub_into(air2, transfer_ratio, list(filter_type))
if(air3.return_pressure() <= 9000)
air1.transfer_ratio_to(air3, transfer_ratio)
@@ -125,8 +122,10 @@
data["filter_types"] = list()
data["filter_types"] += list(list("name" = "Nothing", "id" = "", "selected" = !filter_type))
for(var/id in GLOB.gas_data.ids)
data["filter_types"] += list(list("name" = GLOB.gas_data.names[id], "id" = id, "selected" = (id == filter_type)))
if(!(id in GLOB.gas_data.groups_by_gas))
data["filter_types"] += list(list("name" = GLOB.gas_data.names[id], "id" = id, "selected" = (id == filter_type)))
for(var/group in GLOB.gas_data.groups)
data["filter_types"] += list(list("name" = group, "id" = group, "selected" = (group == filter_type)))
return data
/obj/machinery/atmospherics/components/trinary/filter/ui_act(action, params)
@@ -17,35 +17,27 @@
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/CtrlClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(can_interact(user))
on = !on
update_icon()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/trinary/mixer/AltClick(mob/user)
if(user.canUseTopic(src, BE_CLOSE, FALSE, NO_TK))
if(can_interact(user))
target_pressure = MAX_OUTPUT_PRESSURE
update_icon()
investigate_log("was set to [target_pressure] kPa by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "pressure output on set to [target_pressure] kPa")
update_appearance()
return ..()
//node 3 is the outlet, nodes 1 & 2 are intakes
/obj/machinery/atmospherics/components/trinary/mixer/update_icon()
cut_overlays()
/obj/machinery/atmospherics/components/trinary/mixer/update_overlays()
. = ..()
for(var/direction in GLOB.cardinals)
if(!(direction & initialize_directions))
continue
var/obj/machinery/atmospherics/node = findConnecting(direction)
var/image/cap
if(node)
cap = getpipeimage(icon, "cap", direction, node.pipe_color, piping_layer = piping_layer)
else
cap = getpipeimage(icon, "cap", direction, piping_layer = piping_layer)
add_overlay(cap)
return ..()
. += getpipeimage(icon, "cap", direction, pipe_color, piping_layer, TRUE)
/obj/machinery/atmospherics/components/trinary/mixer/update_icon_nopipes()
var/on_state = on && nodes[1] && nodes[2] && nodes[3] && is_operational()
@@ -24,13 +24,16 @@
/obj/machinery/atmospherics/components/unary/outlet_injector/CtrlClick(mob/user)
if(can_interact(user))
on = !on
update_icon()
investigate_log("was turned [on ? "on" : "off"] by [key_name(user)]", INVESTIGATE_ATMOS)
update_appearance()
return ..()
/obj/machinery/atmospherics/components/unary/outlet_injector/AltClick(mob/user)
if(can_interact(user))
volume_rate = MAX_TRANSFER_RATE
update_icon()
investigate_log("was set to [volume_rate] L/s by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "volume output set to [volume_rate] L/s")
update_appearance()
return ..()
/obj/machinery/atmospherics/components/unary/outlet_injector/Destroy()
@@ -217,16 +217,11 @@
min_temperature = max(T0C - (initial(min_temperature) + L * 15), TCMB) //73.15K with T1 stock parts
/obj/machinery/atmospherics/components/unary/thermomachine/freezer/AltClick(mob/living/user)
. = ..()
var/area/A = get_area(src)
var/turf/T = get_turf(src)
if(!istype(user) || !user.canUseTopic(src, BE_CLOSE))
if(!can_interact(user))
return
target_temperature = min_temperature
to_chat(user,"<span class='notice'>You minimize the temperature on the [src].</span>")
investigate_log("was set to [target_temperature] K by [key_name(usr)]", INVESTIGATE_ATMOS)
message_admins("[src.name] was minimized by [ADMIN_LOOKUPFLW(usr)] at [ADMIN_COORDJMP(T)], [A]")
return TRUE
investigate_log("was set to [target_temperature] K by [key_name(user)]", INVESTIGATE_ATMOS)
balloon_alert(user, "temperature reset to [target_temperature] K")
/obj/machinery/atmospherics/components/unary/thermomachine/heater
name = "heater"
@@ -178,6 +178,9 @@
var/mob/signal_sender = signal.data["user"]
if((("is_siphoning" in signal.data) && pump_direction == RELEASING) || (("is_pressurizing" in signal.data) && pump_direction == SIPHONING))
return
if("purge" in signal.data)
pressure_checks &= ~EXT_BOUND
pump_direction = SIPHONING
@@ -18,7 +18,8 @@
var/id_tag = null
var/scrubbing = SCRUBBING //0 = siphoning, 1 = scrubbing
var/filter_types = list(GAS_CO2)
var/filter_types = list(GAS_CO2, GAS_MIASMA, GAS_GROUP_CHEMICALS)
var/list/clean_filter_types = null
var/volume_rate = 200
var/widenet = 0 //is this scrubber acting on the 3x3 area around it.
var/list/turf/adjacent_turfs = list()
@@ -34,6 +35,16 @@
..()
if(!id_tag)
id_tag = assign_uid_vents()
generate_clean_filter_types()
RegisterSignal(SSdcs,COMSIG_GLOB_NEW_GAS,.proc/generate_clean_filter_types)
/obj/machinery/atmospherics/components/unary/vent_scrubber/proc/generate_clean_filter_types()
clean_filter_types = list()
for(var/id in filter_types)
if(id in GLOB.gas_data.groups)
clean_filter_types += GLOB.gas_data.groups[id]
else
clean_filter_types += id
/obj/machinery/atmospherics/components/unary/vent_scrubber/Destroy()
var/area/A = get_base_area(src)
@@ -95,7 +106,11 @@
var/list/f_types = list()
for(var/id in GLOB.gas_data.ids)
f_types += list(list("gas_id" = id, "gas_name" = GLOB.gas_data.names[id], "enabled" = (id in filter_types)))
if(!(id in GLOB.gas_data.groups_by_gas))
f_types += list(list("gas_id" = id, "gas_name" = GLOB.gas_data.names[id], "enabled" = (id in filter_types)))
for(var/group in GLOB.gas_data.groups)
f_types += list(list("gas_id" = group, "gas_name" = group, "enabled" = (group in filter_types)))
var/datum/signal/signal = new(list(
"tag" = id_tag,
@@ -147,11 +162,11 @@
var/datum/gas_mixture/environment = tile.return_air()
var/datum/gas_mixture/air_contents = airs[1]
if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE || !islist(filter_types))
if(air_contents.return_pressure() >= 50*ONE_ATMOSPHERE || !islist(clean_filter_types))
return FALSE
if(scrubbing & SCRUBBING)
environment.scrub_into(air_contents, volume_rate/environment.return_volume(), filter_types)
environment.scrub_into(air_contents, volume_rate/environment.return_volume(), clean_filter_types)
tile.air_update_turf()
@@ -205,11 +220,13 @@
if("toggle_filter" in signal.data)
filter_types ^= signal.data["toggle_filter"]
generate_clean_filter_types()
if("set_filters" in signal.data)
filter_types = list()
for(var/gas in signal.data["set_filters"])
filter_types += gas
generate_clean_filter_types()
if("init" in signal.data)
name = signal.data["init"]
+1 -1
View File
@@ -52,7 +52,7 @@
desc = "Packs of tools waiting to be used for repairing. Contains a tool and engineering vending machine refill. Requires CE access."
cost = 5500 //Powerfull
access = ACCESS_CE
contains = list(/obj/item/vending_refill/tool,
contains = list(/obj/item/vending_refill/youtool,
/obj/item/vending_refill/engivend)
crate_name = "engineering supply crate"
crate_type = /obj/structure/closet/crate/secure/engineering
+3
View File
@@ -34,6 +34,9 @@
var/last_move = 0
var/area = null
/// Timers are now handled by clients, not by doing a mess on the item and multiple people overwriting a single timer on the object, have fun.
var/tip_timer = null
/// Last time we Click()ed. No clicking twice in one tick!
var/last_click = 0
+41 -23
View File
@@ -79,19 +79,16 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
// Tgui Topic middleware
if(tgui_Topic(href_list))
if(CONFIG_GET(flag/emergency_tgui_logging))
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
return
if(href_list["reload_tguipanel"])
nuke_chat()
if(href_list["reload_statbrowser"])
src << browse(file('html/statbrowser.html'), "window=statbrowser")
// Log all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
last_activity = world.time
//Logs all hrefs
log_href("[src] (usr:[usr]\[[COORD(usr)]\]) : [hsrc ? "[hsrc] " : ""][href]")
//byond bug ID:2256651
if (asset_cache_job && (asset_cache_job in completed_asset_jobs))
to_chat(src, "<span class='danger'>An error has been detected in how your client is receiving resources. Attempting to correct.... (If you keep seeing these messages you might want to close byond and reconnect)</span>")
@@ -357,13 +354,19 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
return
// Initialize tgui panel
tgui_panel.initialize()
src << browse(file('html/statbrowser.html'), "window=statbrowser")
addtimer(CALLBACK(src, .proc/check_panel_loaded), 30 SECONDS)
tgui_panel.initialize()
if(alert_mob_dupe_login)
spawn()
alert(mob, "You have logged in already with another key this round, please log out of this one NOW or risk being banned!")
if(alert_mob_dupe_login && !holder)
var/dupe_login_message = "Your ComputerID has already logged in with another key this round, please log out of this one NOW or risk being banned!"
// if (alert_admin_multikey)
// dupe_login_message += "\nAdmins have been informed."
// message_admins(span_danger("<B>MULTIKEYING: </B></span><span class='notice'>[key_name_admin(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned."))
// log_admin_private("MULTIKEYING: [key_name(src)] has a matching CID+IP with another player and is clearly multikeying. They have been warned to leave the server or risk getting banned.")
spawn(0.5 SECONDS) //needs to run during world init, do not convert to add timer
alert(mob, dupe_login_message) //players get banned if they don't see this message, do not convert to tgui_alert (or even tg_alert) please.
to_chat(mob, span_danger(dupe_login_message))
connection_time = world.time
connection_realtime = world.realtime
@@ -1035,18 +1038,27 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(prefs && prefs.chat_toggles & CHAT_PULLR)
to_chat(src, announcement)
/client/proc/show_character_previews(mutable_appearance/MA)
/client/proc/show_character_previews(mutable_appearance/source)
LAZYINITLIST(char_render_holders)
if(!LAZYLEN(char_render_holders))
for(var/plane_master_path as anything in subtypesof(/atom/movable/screen/plane_master))
var/atom/movable/screen/plane_master/plane_master = new plane_master_path()
char_render_holders["plane_master-[plane_master.plane]"] = plane_master
plane_master.backdrop(mob)
screen |= plane_master
plane_master.screen_loc = "character_preview_map:0,CENTER"
var/pos = 0
for(var/D in GLOB.cardinals)
for(var/dir in GLOB.cardinals)
pos++
var/atom/movable/screen/O = LAZYACCESS(char_render_holders, "[D]")
if(!O)
O = new
LAZYSET(char_render_holders, "[D]", O)
screen |= O
O.appearance = MA
O.dir = D
O.screen_loc = "character_preview_map:0,[pos]"
var/atom/movable/screen/preview = char_render_holders["preview-[dir]"]
if(!preview)
preview = new
char_render_holders["preview-[dir]"] = preview
screen |= preview
preview.appearance = source
preview.dir = dir
preview.screen_loc = "character_preview_map:0,[pos]"
/client/proc/clear_character_previews()
for(var/index in char_render_holders)
@@ -1082,8 +1094,14 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
if(IsAdminAdvancedProcCall())
return
var/list/verblist = list()
verb_tabs.Cut()
for(var/thing in (verbs + mob?.verbs))
var/list/verbstoprocess = verbs.Copy()
if(mob)
verbstoprocess += mob.verbs
for(var/AM in mob.contents)
var/atom/movable/thing = AM
verbstoprocess += thing.verbs
panel_tabs.Cut() // panel_tabs get reset in init_verbs on JS side anyway
for(var/thing in verbstoprocess)
var/procpath/verb_to_init = thing
if(!verb_to_init)
continue
@@ -1091,9 +1109,9 @@ GLOBAL_LIST_INIT(blacklisted_builds, list(
continue
if(!istext(verb_to_init.category))
continue
verb_tabs |= verb_to_init.category
panel_tabs |= verb_to_init.category
verblist[++verblist.len] = list(verb_to_init.category, verb_to_init.name)
src << output("[url_encode(json_encode(verb_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
src << output("[url_encode(json_encode(panel_tabs))];[url_encode(json_encode(verblist))]", "statbrowser:init_verbs")
/client/proc/check_panel_loaded()
if(statbrowser_ready)
+4 -4
View File
@@ -61,7 +61,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
var/UI_style = null
var/outline_enabled = TRUE
var/outline_color = COLOR_BLUE_GRAY
var/outline_color = COLOR_THEME_MIDNIGHT
var/buttons_locked = FALSE
var/hotkeys = FALSE
@@ -787,7 +787,7 @@ GLOBAL_LIST_EMPTY(preferences_datums)
dat += "<h2>General Settings</h2>"
dat += "<b>UI Style:</b> <a href='?_src_=prefs;task=input;preference=ui'>[UI_style]</a><br>"
dat += "<b>Outline:</b> <a href='?_src_=prefs;preference=outline_enabled'>[outline_enabled ? "Enabled" : "Disabled"]</a><br>"
dat += "<b>Outline Color:</b> <span style='border:1px solid #161616; background-color: [outline_color];'>&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
dat += "<b>Outline Color:</b> [outline_color ? "<span style='border:1px solid #161616; background-color: [outline_color];'>" : "Theme-based (null)"]&nbsp;&nbsp;&nbsp;</span> <a href='?_src_=prefs;preference=outline_color'>Change</a><BR>"
dat += "<b>tgui Monitors:</b> <a href='?_src_=prefs;preference=tgui_lock'>[(tgui_lock) ? "Primary" : "All"]</a><br>"
dat += "<b>tgui Style:</b> <a href='?_src_=prefs;preference=tgui_fancy'>[(tgui_fancy) ? "Fancy" : "No Frills"]</a><br>"
dat += "<b>Show Runechat Chat Bubbles:</b> <a href='?_src_=prefs;preference=chat_on_map'>[chat_on_map ? "Enabled" : "Disabled"]</a><br>"
@@ -2744,8 +2744,8 @@ GLOBAL_LIST_EMPTY(preferences_datums)
outline_enabled = !outline_enabled
if("outline_color")
var/pickedOutlineColor = input(user, "Choose your outline color.", "General Preference", outline_color) as color|null
if(pickedOutlineColor)
outline_color = pickedOutlineColor
if(pickedOutlineColor != pickedOutlineColor)
outline_color = pickedOutlineColor // nullable
if("tgui_lock")
tgui_lock = !tgui_lock
if("winflash")
+1 -1
View File
@@ -47,7 +47,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car
addtimer(CALLBACK(src, .proc/force_reset_keybindings), 30) //No mob available when this is run, timer allows user choice.
if(current_version < 30)
outline_enabled = TRUE
outline_color = COLOR_BLUE_GRAY
outline_color = COLOR_THEME_MIDNIGHT
/datum/preferences/proc/update_character(current_version, savefile/S)
if(current_version < 19)
+9
View File
@@ -18,3 +18,12 @@
prefs.tip_delay = max(indelay, 0.01)
prefs.save_preferences()
to_chat(usr, "<span class='danger'>Tooltip delay set to [indelay] milliseconds.</span>")
/client/verb/toggle_hover_outline()
set name = "Toggle hover outline"
set desc = "Toggles hover-over item outline"
set category = "Preferences"
prefs.outline_enabled = !prefs.outline_enabled
prefs.save_preferences()
to_chat(usr, "<span class='danger'>Item outline [prefs.outline_enabled ? "en" : "dis"]abled.</span>")
@@ -267,9 +267,15 @@
desc = "A specialized pair of combat boots with a built-in propulsion system for rapid foward movement."
icon_state = "jetboots"
resistance_flags = FIRE_PROOF
cold_protection = FEET|LEGS
min_cold_protection_temperature = SHOES_MIN_TEMP_PROTECT
heat_protection = FEET|LEGS
max_heat_protection_temperature = SHOES_MAX_TEMP_PROTECT
pocket_storage_component_path = /datum/component/storage/concrete/pockets/shoes
actions_types = list(/datum/action/item_action/bhop)
permeability_coefficient = 0.05
unique_reskin = list("Explorer" = "miningjet",
"Jackboot" = "jetboots")
var/jumpdistance = 5 //-1 from to see the actual distance, e.g 4 goes over 3 tiles
var/jumpspeed = 3
var/recharging_rate = 60 //default 6 seconds between each dash
+5 -1
View File
@@ -20,12 +20,16 @@
/obj/item/clothing/head/helmet/space/hardsuit/Initialize()
. = ..()
soundloop = new(list(), FALSE, TRUE)
soundloop = new(src, FALSE, TRUE)
soundloop.volume = 5
START_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/Destroy()
. = ..()
if(!QDELETED(suit))
qdel(suit)
suit = null
QDEL_NULL(soundloop)
STOP_PROCESSING(SSobj, src)
/obj/item/clothing/head/helmet/space/hardsuit/attack_self(mob/user)
+23
View File
@@ -0,0 +1,23 @@
/datum/round_event_control/atmos_flux
name = "Atmospheric Flux"
typepath = /datum/round_event/atmos_flux
max_occurrences = 1
weight = 5
endWhen = 600
var/original_speed
/datum/round_event/atmos_flux
announceWhen = 1
/datum/round_event/atmos_flux/announce(fake)
priority_announce("Atmospheric flux in your sector detected. Sensors show that air may move [(SSair.share_max_steps_target > original_speed) ? "faster" : "slower"] than usual for some time.", "Atmos Alert")
/datum/round_event/atmos_flux/start()
original_speed = SSair.share_max_steps_target
if(prob(20))
SSair.share_max_steps_target = max(1, original_speed - rand(1,original_speed-1))
else
SSair.share_max_steps_target += rand(2,10)
/datum/round_event/atmos_flux/end()
SSair.share_max_steps_target = original_speed
+2 -2
View File
@@ -58,7 +58,7 @@
supernova.power_mod = min(supernova.power_mod*1.2, power)
if(activeFor > endWhen-10)
supernova.power_mod /= 4
if(prob(round(supernova.power_mod)) && prob(3) && storm_count < 5 && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
if(prob(round(supernova.power_mod)) && prob(5-storm_count) && !SSweather.get_weather_by_type(/datum/weather/rad_storm))
SSweather.run_weather(/datum/weather/rad_storm/supernova)
storm_count++
@@ -73,6 +73,6 @@
weather_duration_lower = 50
weather_duration_upper = 100
telegraph_duration = 200
radiation_intensity = 1000
radiation_intensity = 500
weather_sound = null
telegraph_message = "<span class='userdanger'>The air begins to grow very warm!</span>"
@@ -57,7 +57,11 @@ God bless America.
component_parts += new /obj/item/circuitboard/machine/deep_fryer(null)
component_parts += new /obj/item/stock_parts/micro_laser(null)
RefreshParts()
fry_loop = new(list(src), FALSE)
fry_loop = new(src, FALSE)
/obj/machinery/deepfryer/Destroy()
QDEL_NULL(fry_loop)
return ..()
/obj/machinery/deepfryer/RefreshParts()
var/oil_efficiency
@@ -13,15 +13,21 @@
/obj/machinery/grill/Initialize()
. = ..()
grill_loop = new(list(src), FALSE)
grill_loop = new(src, FALSE)
/obj/machinery/grill/Destroy()
QDEL_NULL(grill_loop)
return ..()
/obj/machinery/grill/update_icon_state()
if(grilled_item)
icon_state = "grill"
else if(grill_fuel)
return ..()
if(grill_fuel > 0)
icon_state = "grill_on"
else
icon_state = "grill_open"
return ..()
icon_state = "grill_open"
return ..()
/obj/machinery/grill/attackby(obj/item/I, mob/user)
if(istype(I, /obj/item/stack/sheet/mineral/coal) || istype(I, /obj/item/stack/sheet/mineral/wood))
@@ -60,21 +66,21 @@
return
..()
/obj/machinery/grill/process()
/obj/machinery/grill/process(delta_time)
..()
update_icon()
if(!grill_fuel)
update_appearance()
if(grill_fuel <= 0)
return
else
grill_fuel -= 1
if(prob(1))
grill_fuel -= 0.5 * delta_time
if(DT_PROB(0.5, delta_time))
var/datum/effect_system/smoke_spread/bad/smoke = new
smoke.set_up(1, loc)
smoke.start()
if(grilled_item)
grill_time += 1
grill_time += delta_time
grilled_item.reagents.add_reagent("char", 1)
grill_fuel -= 10
grill_fuel -= 5 * delta_time
grilled_item.AddComponent(/datum/component/sizzle)
/obj/machinery/grill/Exited(atom/movable/AM)
@@ -109,9 +115,9 @@
/obj/machinery/grill/on_attack_hand(mob/user, act_intent = user.a_intent, unarmed_attack_flags)
if(grilled_item)
to_chat(user, "<span class='notice'>You take out [grilled_item] from [src].</span>")
to_chat(user, span_notice("You take out [grilled_item] from [src]."))
grilled_item.forceMove(drop_location())
update_icon()
update_appearance()
return
return ..()
@@ -36,12 +36,13 @@
. = ..()
wires = new /datum/wires/microwave(src)
create_reagents(100)
soundloop = new(list(src), FALSE)
soundloop = new(src, FALSE)
/obj/machinery/microwave/Destroy()
eject()
if(wires)
QDEL_NULL(wires)
QDEL_NULL(soundloop)
. = ..()
/obj/machinery/microwave/RefreshParts()
+1 -1
View File
@@ -154,7 +154,7 @@
pop_areas += A
var/list/targets = list()
for(var/H in GLOB.network_holopads)
for(var/H in GLOB.the_station_areas)
var/area/A = get_area(H)
if(!A || findtextEx(A.name, "AI") || !(A in pop_areas) || !is_station_level(H))
continue
+15 -25
View File
@@ -53,31 +53,21 @@
/obj/item/seeds/replicapod/on_reagent_change(changetype)
if(changetype == ADD_REAGENT)
var/datum/reagent/blood/B = reagents.has_reagent(/datum/reagent/blood)
if(B)
if(B.data["mind"] && B.data["cloneable"])
mind = B.data["mind"]
ckey = B.data["ckey"]
realName = B.data["real_name"]
blood_gender = B.data["gender"]
blood_type = B.data["blood_type"]
features = B.data["features"]
factions = B.data["factions"]
quirks = B.data["quirks"]
contains_sample = TRUE
visible_message("<span class='notice'>The [src] is injected with a fresh blood sample.</span>")
else
visible_message("<span class='warning'>The [src] rejects the sample!</span>")
if(!reagents.has_reagent(/datum/reagent/blood))
mind = null
ckey = null
realName = null
blood_gender = null
blood_type = null
features = null
factions = null
contains_sample = FALSE
for(var/datum/reagent/R as anything in reagents.reagent_list)
if(R.data["mind"])
if(R.data["cloneable"])
mind = R.data["mind"]
ckey = R.data["ckey"]
realName = R.data["real_name"]
blood_gender = R.data["gender"]
blood_type = R.data["blood_type"]
features = R.data["features"]
factions = R.data["factions"]
quirks = R.data["quirks"]
contains_sample = TRUE
visible_message("<span class='notice'>The [src] is injected with a fresh blood sample.</span>")
else
visible_message("<span class='warning'>The [src] rejects the sample!</span>")
/obj/item/seeds/replicapod/get_analyzer_text()
var/text = ..()
@@ -26,7 +26,7 @@
"light blue" = COLOR_ASSEMBLY_LBLUE,
"blue" = COLOR_ASSEMBLY_BLUE,
"purple" = COLOR_ASSEMBLY_PURPLE,
"pink" = COLOR_ASSEMBLY_PINK,
"pink" = LIGHT_COLOR_PINK,
"custom" = COLOR_ASSEMBLY_WHITE
)
@@ -245,6 +245,8 @@
those who wish to spit in the eyes of God. Sacrifices outright damage for \
a reliance on backstabs and the ability to give fauna concussions on a parry."
attack_verb = list("pummeled", "punched", "jabbed", "hammer-fisted", "uppercut", "slammed")
hitsound = 'sound/weapons/resonator_blast.ogg'
sharpness = SHARP_NONE // use your survival dagger or smth
icon_state = "crusher-hands"
item_state = "crusher0-fist"
unique_reskin = list("Gauntlets" = "crusher-hands",
+96
View File
@@ -0,0 +1,96 @@
//Houses the ash tree, a lava land tree that has been burning for quite some time making a maple like sweetener.
/obj/structure/flora/ashtree
name = "ashed tree"
desc = "A once large tree now burnt like the lands around it."
layer = FLY_LAYER
gender = PLURAL //same as other tree
density = TRUE
pixel_x = -16
icon = 'icons/obj/lavaland/ash_tree.dmi'
icon_state = "ashtree"
//Are icon when we are full of honey or other sap
var/sap_icon_state = "ashtree_maple"
//What we look like when tapping
var/tabbed_icon_state = "ashtree_maple"
//amout of coal in are tree, simular to logs
var/coal_amount = 5
//Do we have sap?
var/sap = FALSE
//What reagent we have
var/sap_type = /datum/reagent/consumable/honey
//This is in seconds, and now long we wait till are tree is tapped
var/harvest_sap_time = 60
var/container_used
var/sap_amount
/obj/structure/flora/ashtree/Initialize(mapload)
..()
if(prob(50))
sap = TRUE
icon_state = sap_icon_state
desc = "A once large tree now burnt like the lands around it. This one seems to have a sap still inside."
//If we have sap, we can generate a bit of it
sap_amount = rand(5,15)
//Random coal or wood amount, so its not bog standered.
coal_amount = rand(5,15)
//If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "Honey Tree", 1, "Trees Spawned")
//So we dont lose are bowls, stolen form closet code
/obj/structure/flora/ashtree/Destroy()
dump_contents(override = FALSE)
return ..()
//Override is for not revealing the locker electronics when you open the locker, for example
/obj/structure/flora/ashtree/proc/dump_contents(override = TRUE)
var/atom/L = drop_location()
for(var/atom/movable/AM in src)
AM.forceMove(L)
/obj/structure/flora/ashtree/proc/harvest_sap()
desc = "A once large tree now burnt like the lands around it."
icon_state = "ashtree"
var/obj/item/reagent_containers/RG = container_used
//Incase someone was a dumb and used a lidded container
if(RG.is_refillable())
//Make sure that its not filling something thats full
if(!RG.reagents.holder_full())
RG.reagents.add_reagent(sap_type, min(RG.volume - RG.reagents.total_volume, sap_amount))
//We drop are used beaker and try to fill it with sap
RG.forceMove(drop_location())
SSblackbox.record_feedback("tally", "Honey Tree", 1, "Harvested Honey") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
//Proc stolen from Trees
//If you hit it with a sharp force aboe 0 item it chops it down, unlike trees tho it dosnt give wood as its already charcoal
//Also dosnt have a stump
/obj/structure/flora/ashtree/attackby(obj/item/W, mob/user, params)
if(istype(W, /obj/item/reagent_containers))
if(sap)
user.visible_message("<span class='notice'>[user] pokes [src] and places a container under the [W].</span>","<span class='notice'>You set up [src] with [W].</span>")
icon_state = tabbed_icon_state
sap = FALSE
container_used = W
//So we dont lose are bowl when cutting it down + needed for the harvest sap proc
user.transferItemToLoc(W, src)
addtimer(CALLBACK(src, .proc/harvest_sap), harvest_sap_time SECONDS)
else
to_chat(user, "<span class='notice'>There is no sap to collect.</span>")
if(coal_amount && (!(flags_1 & NODECONSTRUCT_1)))
if(!W.sharpness || !W.force)
return
if(W.hitsound)
playsound(get_turf(src), W.hitsound, 100, 0, 0)
user.visible_message("<span class='notice'>[user] begins to cut down [src] with [W].</span>","<span class='notice'>You begin to cut down [src] with [W].</span>", "You hear the sound of brittle sawing.")
//2.5 seconds with 20 force, 4 seconds with a hatchet, 10 seconds with a shard.
if(do_after(user, 500/W.force, target = src))
user.visible_message("<span class='notice'>[user] fells [src] with the [W].</span>","<span class='notice'>You fell [src] with the [W].</span>", "You hear the sound of a crumbling tree.")
playsound(get_turf(src), 'sound/effects/meteorimpact.ogg', 100 , 0, 0)
for(var/i=1 to coal_amount)
new /obj/item/stack/sheet/mineral/coal(get_turf(src))
qdel(src)//If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc!
SSblackbox.record_feedback("tally", "Honey Tree", 1, "Cutted Tree")
return ..()
+67 -67
View File
@@ -65,29 +65,30 @@
blood_volume = min(BLOOD_VOLUME_NORMAL, blood_volume + 0.5 * nutrition_ratio)
//Effects of bloodloss
var/word = pick("dizzy","woozy","faint")
var/blood_effect_volume = blood_volume + integrating_blood
switch(blood_effect_volume)
if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
if(prob(10))
to_chat(src, "<span class='warning'>You feel terribly bloated.</span>")
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
if(prob(5))
to_chat(src, "<span class='warning'>You feel [word].</span>")
adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
if(prob(5))
blur_eyes(6)
to_chat(src, "<span class='warning'>You feel very [word].</span>")
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
adjustOxyLoss(5)
if(prob(15))
Unconscious(rand(20,60))
to_chat(src, "<span class='warning'>You feel extremely [word].</span>")
if(-INFINITY to BLOOD_VOLUME_SURVIVE)
if(!HAS_TRAIT(src, TRAIT_NODEATH))
death()
if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM)) //Synths are immune to direct consequences of bloodloss, instead suffering penalties to heat exchange.
var/word = pick("dizzy","woozy","faint")
var/blood_effect_volume = blood_volume + integrating_blood
switch(blood_effect_volume)
if(BLOOD_VOLUME_MAXIMUM to BLOOD_VOLUME_EXCESS)
if(prob(10))
to_chat(src, "<span class='warning'>You feel terribly bloated.</span>")
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
if(prob(5))
to_chat(src, "<span class='warning'>You feel [word].</span>")
adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.01, 1))
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
adjustOxyLoss(round((BLOOD_VOLUME_NORMAL - blood_volume) * 0.02, 1))
if(prob(5))
blur_eyes(6)
to_chat(src, "<span class='warning'>You feel very [word].</span>")
if(BLOOD_VOLUME_SURVIVE to BLOOD_VOLUME_BAD)
adjustOxyLoss(5)
if(prob(15))
Unconscious(rand(20,60))
to_chat(src, "<span class='warning'>You feel extremely [word].</span>")
if(-INFINITY to BLOOD_VOLUME_SURVIVE)
if(!HAS_TRAIT(src, TRAIT_NODEATH))
death()
var/temp_bleed = 0
//Bleeding out
@@ -156,7 +157,7 @@
blood_volume -= amount
var/list/blood_data = get_blood_data(blood_id)
var/list/blood_data = get_blood_data()
if(iscarbon(AM))
var/mob/living/carbon/C = AM
@@ -177,54 +178,53 @@
return TRUE
/mob/living/proc/get_blood_data(blood_id)
/mob/living/proc/get_blood_data()
return
/mob/living/carbon/get_blood_data(blood_id)
if(blood_id == /datum/reagent/blood || /datum/reagent/blood/jellyblood) //actual blood reagent
var/blood_data = list()
//set the blood data
blood_data["donor"] = src
blood_data["viruses"] = list()
/mob/living/carbon/get_blood_data()
var/blood_data = list()
//set the blood data
blood_data["donor"] = src
blood_data["viruses"] = list()
for(var/thing in diseases)
var/datum/disease/D = thing
blood_data["viruses"] += D.Copy()
for(var/thing in diseases)
var/datum/disease/D = thing
blood_data["viruses"] += D.Copy()
blood_data["blood_DNA"] = dna.unique_enzymes
blood_data["bloodcolor"] = dna.species.exotic_blood_color
if(disease_resistances && disease_resistances.len)
blood_data["resistances"] = disease_resistances.Copy()
var/list/temp_chem = list()
for(var/datum/reagent/R in reagents.reagent_list)
temp_chem[R.type] = R.volume
blood_data["trace_chem"] = list2params(temp_chem)
if(mind)
blood_data["mind"] = mind
else if(last_mind)
blood_data["mind"] = last_mind
if(ckey)
blood_data["ckey"] = ckey
else if(last_mind)
blood_data["ckey"] = ckey(last_mind.key)
blood_data["blood_DNA"] = dna.unique_enzymes
blood_data["bloodcolor"] = dna.species.exotic_blood_color
if(disease_resistances && disease_resistances.len)
blood_data["resistances"] = disease_resistances.Copy()
var/list/temp_chem = list()
for(var/datum/reagent/R in reagents.reagent_list)
temp_chem[R.type] = R.volume
blood_data["trace_chem"] = list2params(temp_chem)
if(mind)
blood_data["mind"] = mind
else if(last_mind)
blood_data["mind"] = last_mind
if(ckey)
blood_data["ckey"] = ckey
else if(last_mind)
blood_data["ckey"] = ckey(last_mind.key)
if(!suiciding)
blood_data["cloneable"] = 1
blood_data["blood_type"] = dna.blood_type
blood_data["gender"] = gender
blood_data["real_name"] = real_name
blood_data["features"] = dna.features
blood_data["factions"] = faction
blood_data["quirks"] = list()
for(var/V in roundstart_quirks)
var/datum/quirk/T = V
blood_data["quirks"] += T.type
blood_data["changeling_loudness"] = 0
if(mind)
var/datum/antagonist/changeling/ling = mind.has_antag_datum(/datum/antagonist/changeling)
if(istype(ling))
blood_data["changeling_loudness"] = ling.loudfactor
return blood_data
if(!suiciding)
blood_data["cloneable"] = 1
blood_data["blood_type"] = dna.blood_type
blood_data["gender"] = gender
blood_data["real_name"] = real_name
blood_data["features"] = dna.features
blood_data["factions"] = faction
blood_data["quirks"] = list()
for(var/V in roundstart_quirks)
var/datum/quirk/T = V
blood_data["quirks"] += T.type
blood_data["changeling_loudness"] = 0
if(mind)
var/datum/antagonist/changeling/ling = mind.has_antag_datum(/datum/antagonist/changeling)
if(istype(ling))
blood_data["changeling_loudness"] = ling.loudfactor
return blood_data
//get the id of the substance this mob use as blood.
/mob/proc/get_blood_id()
@@ -6,6 +6,16 @@
key = STRIPPABLE_ITEM_BACK
item_slot = SLOT_BACK
/datum/strippable_item/mob_item_slot/back/get_alternate_action(atom/source, mob/user)
if(..() == FALSE)
return null
return get_strippable_alternate_action_internals(get_item(source), source)
/datum/strippable_item/mob_item_slot/back/alternate_action(atom/source, mob/user)
if (!..())
return null
return strippable_alternate_action_internals(get_item(source), source, user)
/datum/strippable_item/mob_item_slot/mask
key = STRIPPABLE_ITEM_MASK
item_slot = SLOT_WEAR_MASK
@@ -37,7 +37,7 @@
var/list/whatmighthappen = list()
whatmighthappen += list("avoided" = 3, "dropthing" = 1, "movetile" = 1, "shortdeaf" = 1, "flopover" = 1, "nutriloss" = 1, "selfflash" = 1, "harmies" = 1)
if(corruption >= CORRUPTION_THRESHHOLD_MAJOR)
whatmighthappen += list("longdeaf" = 1, "longknockdown" = 1, "shortlimbdisable" = 1, "shortblind" = 1, "shortstun" = 1, "shortmute" = 1, "vomit" = 1, "halluscinate" = 1)
whatmighthappen += list("longdeaf" = 1, "longknockdown" = 1, "shortlimbdisable" = 1, "shortblind" = 1, "shortstun" = 1, "shortmute" = 1, "vomit" = 1, "hallucinate" = 1, "jamcoolanthud" = 1)
if(corruption >= CORRUPTION_THRESHHOLD_CRITICAL)
whatmighthappen += list("receporgandamage" = 1, "longlimbdisable" = 1, "blindmutedeaf" = 1, "longstun" = 1, "sleep" = 1, "inducetrauma" = 1, "amplifycorrupt" = 1, "changetemp" = 1)
var/event = pickweight(whatmighthappen)
@@ -97,8 +97,10 @@
if("vomit")
to_chat(src, "<span class='notice'>Ejecting contaminant.</span>")
vomit()
if("halluscinate")
if("hallucinate")
hallucination += 20 //Doesn't give a cue
if("jamcoolanthud")
hud_used.coolant_display.jam(10)
if("receporgandamage")
adjustOrganLoss(ORGAN_SLOT_EARS, rand(10, 20))
adjustOrganLoss(ORGAN_SLOT_EYES, rand(10, 20))
@@ -159,7 +161,7 @@
/mob/living/carbon/proc/forcesleep(time = 100)
to_chat(src, "<span class='notice'>Preparations complete, powering down.</span>")
Sleeping(time, 0)
Sleeping(time)
#undef CORRUPTION_CHECK_INTERVAL
+11 -10
View File
@@ -249,16 +249,17 @@
if(DISGUST_LEVEL_DISGUSTED to INFINITY)
msg += "[t_He] look[p_s()] extremely disgusted.\n"
var/apparent_blood_volume = blood_volume
if(dna.species.use_skintones && skin_tone == "albino")
apparent_blood_volume -= 150 // enough to knock you down one tier
switch(apparent_blood_volume)
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
msg += "[t_He] [t_has] pale skin.\n"
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
msg += "<b>[t_He] look[p_s()] like pale death.</b>\n"
if(-INFINITY to BLOOD_VOLUME_BAD)
msg += "<span class='deadsay'><b>[t_He] resemble[p_s()] a crushed, empty juice pouch.</b></span>\n"
if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
var/apparent_blood_volume = blood_volume
if(dna.species.use_skintones && skin_tone == "albino")
apparent_blood_volume -= 150 // enough to knock you down one tier
switch(apparent_blood_volume)
if(BLOOD_VOLUME_OKAY to BLOOD_VOLUME_SAFE)
msg += "[t_He] [t_has] pale skin.\n"
if(BLOOD_VOLUME_BAD to BLOOD_VOLUME_OKAY)
msg += "<b>[t_He] look[p_s()] like pale death.</b>\n"
if(-INFINITY to BLOOD_VOLUME_BAD)
msg += "<span class='deadsay'><b>[t_He] resemble[p_s()] a crushed, empty juice pouch.</b></span>\n"
if(bleedsuppress)
msg += "[t_He] [t_is] embued with a power that defies bleeding.\n" // only statues and highlander sword can cause this so whatever
@@ -416,6 +416,7 @@
severity *= 0.5
var/do_not_stun = FALSE
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
hud_used?.coolant_display.jam(round(severity / 10, 1)) //Messes up the cooling system readout.
severity *= 0.5 //Robotpeople take less limb damage, but instead suffer system corruption (see carbon emp_act)
do_not_stun = TRUE
for(var/obj/item/bodypart/L in src.bodyparts)
@@ -115,13 +115,13 @@
. = ..()
if(!.)
return
if(HAS_TRAIT(src, TRAIT_NOGUNS))
to_chat(src, "<span class='warning'>You can't bring yourself to use a ranged weapon!</span>")
return FALSE
if(G.trigger_guard == TRIGGER_GUARD_NORMAL)
if(HAS_TRAIT(src, TRAIT_CHUNKYFINGERS))
to_chat(src, "<span class='warning'>Your meaty finger is much too large for the trigger guard!</span>")
balloon_alert(src, "fingers are too big!")
return FALSE
if(HAS_TRAIT(src, TRAIT_NOGUNS))
to_chat(src, span_warning("You can't bring yourself to use a ranged weapon!"))
return FALSE
/mob/living/carbon/human/proc/get_bank_account()
RETURN_TYPE(/datum/bank_account)
+17 -1
View File
@@ -30,6 +30,8 @@
/mob/living/carbon/human/PhysicalLife(seconds, times_fired)
if(!(. = ..()))
return
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM) && hud_used)
hud_used.coolant_display.update_counter(src)
//Update our name based on whether our face is obscured/disfigured
name = get_visible_name()
@@ -76,9 +78,23 @@
/mob/living/carbon/human/check_breath(datum/gas_mixture/breath)
var/L = getorganslot(ORGAN_SLOT_LUNGS)
if(breath && HAS_TRAIT(src, TRAIT_NOBREATH) && HAS_TRAIT(src, TRAIT_AUXILIARY_LUNGS)) //Something something bz and synth cooling systems interacting (in reality, this only exists to not make robot lings too strong)
var/total_moles = breath.total_moles()
var/pressure = breath.return_pressure()
#define PP_MOLES(X) ((X / total_moles) * pressure)
#define PP(air, gas) PP_MOLES(air.get_moles(gas))
var/bz_pp = PP(breath, GAS_BZ)
if(bz_pp > 1)
reagents.add_reagent(/datum/reagent/bz_metabolites,5)
else if(bz_pp > 0.1)
reagents.add_reagent(/datum/reagent/bz_metabolites,1)
#undef PP_MOLES
#undef PP
var/L = getorganslot(ORGAN_SLOT_LUNGS)
if(!L)
if(HAS_TRAIT(src, TRAIT_NOBREATH))
return
if(health >= crit_threshold)
adjustOxyLoss(HUMAN_MAX_OXYLOSS + 1)
else if(!HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
+50 -15
View File
@@ -108,6 +108,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/burnmod = 1
///multiplier for damage from cold temperature
var/coldmod = 1
///moves their safe minimum temp by this value.
var/cold_offset = 0
///moves their safe maximum temp by this value.
var/hot_offset = 0
///multiplier for damage from hot temperature
var/heatmod = 1
///multiplier for stun durations
@@ -147,6 +151,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/list/blacklisted_quirks = list() // Quirks that will be removed upon gaining this species, to be defined by species
var/list/removed_quirks = list() // Quirks that got removed due to being blacklisted, and will be restored when on_species_loss() is called
var/balance_point_values = FALSE //If true, will balance point values on species gain after removing blacklisted quirks. Use this for roundstart species with blacklisted quirks that people may attempt to use to powergame trait points.
///Punch-specific attack verb.
var/attack_verb = "punch"
@@ -331,7 +336,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/should_have_brain = TRUE
var/should_have_heart = !(NOBLOOD in species_traits)
var/should_have_lungs = !(TRAIT_NOBREATH in inherent_traits)
var/should_have_lungs = ((TRAIT_AUXILIARY_LUNGS in inherent_traits) || !(TRAIT_NOBREATH in inherent_traits))
var/should_have_appendix = !(TRAIT_NOHUNGER in inherent_traits)
var/should_have_eyes = TRUE
var/should_have_ears = TRUE
@@ -569,17 +574,39 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
for(var/obj/item/bodypart/B in C.bodyparts)
B.change_bodypart_status(initial(B.status), FALSE, TRUE)
if((TRAIT_ROBOTIC_ORGANISM in inherent_traits) && C.hud_used)
C.hud_used.coolant_display.clear()
SEND_SIGNAL(C, COMSIG_SPECIES_LOSS, src)
// shamelessly inspired by antag_datum.remove_blacklisted_quirks()
/datum/species/proc/remove_blacklisted_quirks(mob/living/carbon/C)
var/mob/living/L = C.mind?.current
if(istype(L))
for(var/q in L.roundstart_quirks)
var/datum/quirk/Q = q
if(Q.type in blacklisted_quirks)
qdel(Q)
removed_quirks += Q.type
. = 0
if(istype(C))
if(!balance_point_values)
for(var/q in C.roundstart_quirks)
var/datum/quirk/Q = q
if(Q.type in blacklisted_quirks)
removed_quirks += Q.type
. += 1
qdel(Q)
else
var/point_overhead = 0
for(var/datum/quirk/Q as anything in C.roundstart_quirks)
if(Q.type in blacklisted_quirks)
point_overhead -= Q.value
removed_quirks += Q.type
. += 1
qdel(Q)
if(point_overhead)
for(var/datum/quirk/Q as anything in C.roundstart_quirks)
if(Q.value > 0)
point_overhead -= Q.value
removed_quirks += Q.type
. += 1
qdel(Q)
if(!point_overhead)
break
// restore any quirks that we removed
/datum/species/proc/restore_quirks(mob/living/carbon/C)
@@ -1165,7 +1192,10 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
var/takes_crit_damage = !HAS_TRAIT(H, TRAIT_NOCRITDAMAGE)
if((H.health < H.crit_threshold) && takes_crit_damage)
H.adjustBruteLoss(1)
if(!HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM))
H.adjustBruteLoss(1)
else
H.adjustFireLoss(1) //Robots melt instead of taking brute.
/datum/species/proc/spec_death(gibbed, mob/living/carbon/human/H)
if(H)
@@ -1874,7 +1904,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
if(BODY_ZONE_HEAD)
if(!I.get_sharpness() && armor_block < 50)
if(prob(I.force))
if(HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
if(HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM))
H.adjustToxLoss(5, toxins_type = TOX_SYSCORRUPT) //Bonk! - Effectively 5 bonus damage
else
H.adjustOrganLoss(ORGAN_SLOT_BRAIN, 20)
@@ -2171,15 +2201,20 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
//Thermal protection (insulation) has mixed benefits in two situations (hot in hot places, cold in hot places)
if(!H.on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases
var/natural = 0
var/cooling_efficiency = 1
if(H.stat != DEAD)
natural = H.natural_bodytemperature_stabilization()
cooling_efficiency = H.get_cooling_efficiency()
if(HAS_TRAIT(H, TRAIT_ROBOTIC_ORGANISM)) //Synths by default slowly heat up and need to lose said heat to the environment or active cooling. If you have very high cooling efficiency, you instead passively cool.
H.adjust_bodytemperature(SYNTH_PASSIVE_HEAT_GAIN * (1 - cooling_efficiency), (T0C + SYNTH_MIN_PASSIVE_COOLING_TEMP), (T0C + SYNTH_MAX_PASSIVE_GAIN_TEMP))
var/thermal_protection = 1
if(loc_temp < H.bodytemperature) //Place is colder than we are
thermal_protection -= H.get_thermal_protection(loc_temp, TRUE) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //we're cold, insulation helps us retain body heat and will reduce the heat we lose to the environment
H.adjust_bodytemperature((thermal_protection+1)*natural + max(thermal_protection * (loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
H.adjust_bodytemperature((thermal_protection+1)*natural + max((thermal_protection * (loc_temp - H.bodytemperature) * cooling_efficiency) / BODYTEMP_COLD_DIVISOR, BODYTEMP_COOLING_MAX))
else //we're sweating, insulation hinders our ability to reduce heat - and it will reduce the amount of cooling you get from the environment
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
H.adjust_bodytemperature(natural*(1/(thermal_protection+1)) + max(((thermal_protection * (loc_temp - H.bodytemperature) + BODYTEMP_NORMAL - H.bodytemperature) * cooling_efficiency) / BODYTEMP_COLD_DIVISOR , BODYTEMP_COOLING_MAX)) //Extra calculation for hardsuits to bleed off heat
else //Place is hotter than we are
thermal_protection -= H.get_thermal_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to.
if(H.bodytemperature < BODYTEMP_NORMAL) //and we're cold, insulation enhances our ability to retain body heat but reduces the heat we get from the environment
@@ -2203,7 +2238,7 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.throw_alert("tempfeel", /atom/movable/screen/alert/hot, 3)
// +/- 50 degrees from 310K is the 'safe' zone, where no damage is dealt.
if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
if(H.bodytemperature > (BODYTEMP_HEAT_DAMAGE_LIMIT + hot_offset) && !HAS_TRAIT(H, TRAIT_RESISTHEAT))
//Body temperature is too hot.
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "cold")
@@ -2231,11 +2266,11 @@ GLOBAL_LIST_EMPTY(roundstart_race_names)
H.emote("scream")
H.apply_damage(burn_damage, BURN)
else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
else if(H.bodytemperature < (BODYTEMP_COLD_DAMAGE_LIMIT + cold_offset) && !HAS_TRAIT(H, TRAIT_RESISTCOLD))
SEND_SIGNAL(H, COMSIG_CLEAR_MOOD_EVENT, "hot")
SEND_SIGNAL(H, COMSIG_ADD_MOOD_EVENT, "cold", /datum/mood_event/cold)
//Apply cold slowdown
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = ((BODYTEMP_COLD_DAMAGE_LIMIT - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
H.add_or_update_variable_movespeed_modifier(/datum/movespeed_modifier/cold, multiplicative_slowdown = (((BODYTEMP_COLD_DAMAGE_LIMIT + cold_offset) - H.bodytemperature) / COLD_SLOWDOWN_FACTOR))
switch(H.bodytemperature)
if(200 to BODYTEMP_COLD_DAMAGE_LIMIT)
H.throw_alert("temp", /atom/movable/screen/alert/shiver, 1)
@@ -24,11 +24,18 @@
id = SPECIES_MAMMAL_SYNTHETIC
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE,WINGCOLOR,HORNCOLOR)
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID|MOB_BEAST
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
coldmod = 0.5
heatmod = 1.2
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
blacklisted_quirks = list(/datum/quirk/coldblooded)
balance_point_values = TRUE
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
@@ -47,5 +54,7 @@
attack_sound = 'sound/weapons/slash.ogg'
miss_sound = 'sound/weapons/slashmiss.ogg'
exotic_bloodtype = "S"
exotic_blood_color = BLOOD_COLOR_OIL
allowed_limb_ids = list("mammal","aquatic","avian", "human")
species_category = "robot"
@@ -5,7 +5,7 @@
default_color = "00FF00"
blacklisted = 0
sexes = 0
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
species_traits = list(MUTCOLORS,NOEYES,NOTRANSSTING,HAS_FLESH,HAS_BONE,HAIR,ROBOTIC_LIMBS)
hair_alpha = 210
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
@@ -13,6 +13,12 @@
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
coldmod = 0.5
heatmod = 1.2
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
blacklisted_quirks = list(/datum/quirk/coldblooded)
balance_point_values = TRUE
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
@@ -26,7 +32,7 @@
//special cybernetic organ for getting power from apcs
mutant_organs = list(/obj/item/organ/cyberimp/arm/power_cord)
exotic_bloodtype = "HF"
exotic_bloodtype = "S"
exotic_blood_color = BLOOD_COLOR_OIL
species_category = SPECIES_CATEGORY_ROBOT
@@ -4,11 +4,18 @@
say_mod = "beeps"
default_color = "00FF00"
species_traits = list(MUTCOLORS,NOTRANSSTING,EYECOLOR,LIPS,HAIR,ROBOTIC_LIMBS,HAS_FLESH,HAS_BONE)
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM)
inherent_traits = list(TRAIT_EASYDISMEMBER,TRAIT_LIMBATTACHMENT,TRAIT_NO_PROCESS_FOOD, TRAIT_ROBOTIC_ORGANISM, TRAIT_RESISTLOWPRESSURE, TRAIT_NOBREATH, TRAIT_AUXILIARY_LUNGS)
inherent_biotypes = MOB_ROBOTIC|MOB_HUMANOID
mutant_bodyparts = list("ipc_antenna" = "Synthetic Lizard - Antennae","mam_tail" = "Synthetic Lizard", "mam_snouts" = "Synthetic Lizard - Snout", "legs" = "Digitigrade", "mam_body_markings" = "Synthetic Lizard - Plates", "taur" = "None")
meat = /obj/item/reagent_containers/food/snacks/meat/slab/human/mutant/ipc
gib_types = list(/obj/effect/gibspawner/ipc, /obj/effect/gibspawner/ipc/bodypartless)
coldmod = 0.5
heatmod = 1.2
cold_offset = -125 //Can handle pretty cold environments, but it's still a slightly bad idea if you enter a room thats full of near-absolute-zero gas
blacklisted_quirks = list(/datum/quirk/coldblooded)
balance_point_values = TRUE
//Just robo looking parts.
mutant_heart = /obj/item/organ/heart/ipc
mutantlungs = /obj/item/organ/lungs/ipc
+46 -5
View File
@@ -573,9 +573,6 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
if(druggy)
adjust_drugginess(-1)
if(drunkenness)
drunkenness = max(drunkenness-1,0)
if(silent)
silent = max(silent-1, 0)
@@ -583,7 +580,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
handle_hallucinations()
if(drunkenness)
drunkenness = max(drunkenness - (drunkenness * 0.04), 0)
drunkenness *= 0.96
if(drunkenness >= 6)
SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
if(prob(25))
@@ -598,6 +595,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "drunk")
clear_alert("drunk")
sound_environment_override = SOUND_ENVIRONMENT_NONE
drunkenness = max(drunkenness - 0.2, 0)
if(mind && (mind.assigned_role == "Scientist" || mind.assigned_role == "Research Director"))
if(SSresearch.science_tech)
@@ -661,7 +659,7 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
//used in human and monkey handle_environment()
/mob/living/carbon/proc/natural_bodytemperature_stabilization()
if (HAS_TRAIT(src, TRAIT_COLDBLOODED))
if(HAS_TRAIT(src, TRAIT_COLDBLOODED) || HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
return 0 //Return 0 as your natural temperature. Species proc handle_environment() will adjust your temperature based on this.
var/body_temperature_difference = BODYTEMP_NORMAL - bodytemperature
@@ -674,6 +672,49 @@ GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put
return min(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, max(body_temperature_difference, -BODYTEMP_AUTORECOVERY_MINIMUM/4))
if(BODYTEMP_HEAT_DAMAGE_LIMIT to INFINITY)
return min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers
/mob/living/carbon/proc/get_cooling_efficiency()
if(!HAS_TRAIT(src, TRAIT_ROBOTIC_ORGANISM))
return 1
var/integration_bonus = min(blood_volume * SYNTH_INTEGRATION_COOLANT_CAP, integrating_blood * SYNTH_INTEGRATION_COOLANT_PENALTY) //Integration blood somewhat helps, though only at 40% impact and to a cap of 25% of current blood level.
var/blood_effective_volume = blood_volume + integration_bonus
var/coolant_efficiency = min(blood_effective_volume / BLOOD_VOLUME_SAFE, 1) //Low coolant is only a negative, adding more than needed will not help you.
var/environment_efficiency = get_environment_cooling_efficiency()
return min(coolant_efficiency * environment_efficiency, SYNTH_MAX_COOLING_EFFICIENCY)
/mob/living/carbon/proc/get_environment_cooling_efficiency()
var/suitlink = check_suitlinking()
if(suitlink)
return suitlink //If you are wearing full EVA or lavaland hazard gear (on lavaland), assume it has been made to accomodate your cooling needs.
var/datum/gas_mixture/environment = loc.return_air()
if(!environment)
return 0
var/pressure = environment.return_pressure()
var/heat = environment.return_temperature()
var/heat_efficiency = clamp(1 + ((bodytemperature - heat) * SYNTH_HEAT_EFFICIENCY_COEFF), 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
var/pressure_efficiency = clamp(pressure / ONE_ATMOSPHERE, 0, SYNTH_SINGLE_INFLUENCE_COOLING_EFFECT_CAP)
var/total_environment_efficiency = min(heat_efficiency * pressure_efficiency, SYNTH_TOTAL_ENVIRONMENT_EFFECT_CAP) //At best, you can get 200% total
return total_environment_efficiency
/mob/living/carbon/proc/check_suitlinking()
var/suit_item = get_item_by_slot(SLOT_WEAR_SUIT)
var/head_item = get_item_by_slot(SLOT_HEAD)
var/turf/T = get_turf(src)
if(istype(head_item, /obj/item/clothing/head/helmet/space) && istype(suit_item, /obj/item/clothing/suit/space))
return 1
if(T && is_mining_level(T.z) && istype(head_item, /obj/item/clothing/head/hooded/explorer) && istype(suit_item, /obj/item/clothing/suit/hooded/explorer))
return 1
return 0
/////////
//LIVER//
/////////
+41
View File
@@ -1,4 +1,31 @@
/**
* Run when a client is put in this mob or reconnets to byond and their client was on this mob
*
* Things it does:
* * Adds player to player_list
* * sets lastKnownIP
* * sets computer_id
* * logs the login
* * tells the world to update it's status (for player count)
* * create mob huds for the mob if needed
* * reset next_move to 1
* * parent call
* * if the client exists set the perspective to the mob loc
* * call on_log on the loc (sigh)
* * reload the huds for the mob
* * reload all full screen huds attached to this mob
* * load any global alternate apperances
* * sync the mind datum via sync_mind()
* * call any client login callbacks that exist
* * grant any actions the mob has to the client
* * calls [auto_deadmin_on_login](mob.html#proc/auto_deadmin_on_login)
* * send signal COMSIG_MOB_CLIENT_LOGIN
* * attaches the ash listener element so clients can hear weather
* client can be deleted mid-execution of this proc, chiefly on parent calls, with lag
*/
/mob/Login()
if(!client)
return FALSE
add_to_player_list()
lastKnownIP = client.address
computer_id = client.computer_id
@@ -15,6 +42,14 @@
. = ..()
if(!client)
return FALSE
// SEND_SIGNAL(src, COMSIG_MOB_LOGIN)
if (key != client.key)
key = client.key
reset_perspective(loc)
if(loc)
@@ -53,6 +88,12 @@
log_message("Client [key_name(src)] has taken ownership of mob [src]([src.type])", LOG_OWNERSHIP)
SEND_SIGNAL(src, COMSIG_MOB_CLIENT_LOGIN, client)
client.init_verbs()
if(has_field_of_vision && CONFIG_GET(flag/use_field_of_vision))
LoadComponent(/datum/component/field_of_vision, field_of_vision_type)
AddElement(/datum/element/weather_listener, /datum/weather/ash_storm, ZTRAIT_ASHSTORM, GLOB.ash_storm_sounds)
// optimized area sound effects. Enable during events (compile flag when 😳)
// AddElement(/datum/element/weather_listener, /datum/weather/long_rain, ZTRAIT_STATION, GLOB.rain_sounds)
-1
View File
@@ -4,7 +4,6 @@
SStgui.on_logout(src)
unset_machine()
remove_from_player_list()
..()
if(loc)
@@ -5,7 +5,14 @@
var/title = "Untitled Conversation"
var/datum/computer_file/program/chatclient/operator // "Administrator" of this channel. Creator starts as channel's operator,
var/list/messages = list()
var/list/clients = list()
///chat clients who are active or minimized
var/list/active_clients = list()
///chat clients who have exited out of the program.
var/list/offline_clients = list()
///clients muted by operator
var/list/muted_clients = list()
//if a channel is strong, it cannot be renamed or deleted.
var/strong = FALSE
var/password
var/static/ntnrc_uid = 0
@@ -22,6 +29,8 @@
/datum/ntnet_conversation/Destroy()
if(SSnetworks.station_network)
SSnetworks.station_network.chat_channels.Remove(src)
for(var/datum/computer_file/program/chatclient/chatterbox in (active_clients | offline_clients | muted_clients))
purge_client(chatterbox)
return ..()
/datum/ntnet_conversation/proc/add_message(message, username)
@@ -38,39 +47,70 @@
return
messages = messages.Copy(messages.len-50 ,0)
/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/C)
if(!istype(C))
/datum/ntnet_conversation/proc/add_client(datum/computer_file/program/chatclient/new_user, silent = FALSE)
if(!istype(new_user))
return
clients.Add(C)
add_status_message("[C.username] has joined the channel.")
new_user.conversations |= src
active_clients.Add(new_user)
if(!silent)
add_status_message("[new_user.username] has joined the channel.")
// No operator, so we assume the channel was empty. Assign this user as operator.
if(!operator)
changeop(C)
changeop(new_user)
/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/C)
if(!istype(C) || !(C in clients))
//Clear all of our references to a client, used for client deletion
/datum/ntnet_conversation/proc/purge_client(datum/computer_file/program/chatclient/forget)
remove_client(forget)
muted_clients -= forget
offline_clients -= forget
forget.conversations -= src
/datum/ntnet_conversation/proc/remove_client(datum/computer_file/program/chatclient/leaving)
if(!istype(leaving))
return
clients.Remove(C)
add_status_message("[C.username] has left the channel.")
if(leaving in active_clients)
active_clients.Remove(leaving)
add_status_message("[leaving.username] has left the channel.")
// Channel operator left, pick new operator
if(C == operator)
if(leaving == operator)
operator = null
if(clients.len)
var/datum/computer_file/program/chatclient/newop = pick(clients)
if(active_clients.len)
var/datum/computer_file/program/chatclient/newop = pick(active_clients)
changeop(newop)
/datum/ntnet_conversation/proc/go_offline(datum/computer_file/program/chatclient/offline)
if(!istype(offline) || !(offline in active_clients))
return
active_clients.Remove(offline)
offline_clients.Add(offline)
/datum/ntnet_conversation/proc/mute_user(datum/computer_file/program/chatclient/op, datum/computer_file/program/chatclient/muted)
if(operator != op) //sanity even if the person shouldn't be able to see the mute button
return
if(muted in muted_clients)
muted_clients.Remove(muted)
muted.computer.alert_call(muted, "You have been unmuted from [title]!", 'sound/machines/ping.ogg')
else
muted_clients.Add(muted)
muted.computer.alert_call(muted, "You have been muted from [title]!")
/datum/ntnet_conversation/proc/ping_user(datum/computer_file/program/chatclient/pinger, datum/computer_file/program/chatclient/pinged)
if(pinger in muted_clients) //oh my god fuck off
return
add_status_message("[pinger.username] pinged [pinged.username].")
pinged.computer.alert_call(pinged, "You have been pinged in [title] by [pinger.username]!", 'sound/machines/ping.ogg')
/datum/ntnet_conversation/proc/changeop(datum/computer_file/program/chatclient/newop)
if(istype(newop))
operator = newop
add_status_message("Channel operator status transferred to [newop.username].")
/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/client)
if(operator != client)
return FALSE // Not Authorised
/datum/ntnet_conversation/proc/change_title(newtitle, datum/computer_file/program/chatclient/renamer)
if(operator != renamer || strong)
return FALSE // Not Authorised or channel cannot be editted
add_status_message("[client.username] has changed channel title from [title] to [newtitle]")
add_status_message("[renamer.username] has changed channel title from [title] to [newtitle]")
title = newtitle
#undef MAX_CHANNELS
@@ -39,7 +39,7 @@
. += "It has a slot installed for an intelliCard which contains: [ai_slot.stored_card.name]"
else
. += "It has a slot installed for an intelliCard, which appears to be occupied."
. += "<span class='info'>Alt-click to eject the intelliCard.</span>"
. += span_info("Alt-click to eject the intelliCard.")
else
. += "It has a slot installed for an intelliCard."
@@ -55,7 +55,7 @@
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed[multiple_cards ? " which contain [first_ID] and [second_ID]" : ", one of which contains [first_ID ? first_ID : second_ID]"]."
else
. += "It has [multiple_slots ? "two slots" : "a slot"] for identification cards installed, [multiple_cards ? "both of which appear" : "and one of them appears"] to be occupied."
. += "<span class='info'>Alt-click [src] to eject the identification card[multiple_cards ? "s":""].</span>"
. += span_info("Alt-click [src] to eject the identification card[multiple_cards ? "s":""].")
else
. += "It has [multiple_slots ? "two slots" : "a slot"] installed for identification cards."
@@ -63,4 +63,4 @@
if(printer_slot)
. += "It has a printer installed."
if(user_is_adjacent)
. += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper].</span>"
. += "The printer's paper levels are at: [printer_slot.stored_paper]/[printer_slot.max_paper].</span>]"
@@ -12,13 +12,13 @@
rad_flags = RAD_PROTECT_CONTENTS
armor = list("melee" = 0, "bullet" = 20, "laser" = 20, "energy" = 100, "bomb" = 0, "bio" = 100, "rad" = 100, "fire" = 0, "acid" = 0)
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
var/enabled = 0 // Whether the computer is turned on.
var/screen_on = 1 // Whether the computer is active/opened/it's screen is on.
var/device_theme = "ntos" // Sets the theme for the main menu, hardware config, and file browser apps. Overridden by certain non-NT devices.
var/datum/computer_file/program/active_program = null // A currently active program running on the computer.
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0
var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged
var/last_battery_percent = 0 // Used for deciding if battery percentage has chandged
var/last_world_time = "00:00"
var/list/last_header_icons
///Looping sound for when the computer is on
@@ -26,19 +26,19 @@
///Whether or not this modular computer uses the looping sound
var/looping_sound = TRUE
var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
var/base_active_power_usage = 50 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 5 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
var/icon_state_unpowered = null // Icon state when the computer is turned off.
var/icon_state_powered = null // Icon state when the computer is turned on.
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/icon_state_unpowered = null // Icon state when the computer is turned off.
var/icon_state_powered = null // Icon state when the computer is turned on.
var/icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/display_overlays = TRUE // If FALSE, don't draw overlays on this device at all
var/max_hardware_size = 0 // Maximal hardware w_class. Tablets/PDAs have 1, laptops 2, consoles 4.
var/steel_sheet_cost = 5 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
/// List of "connection ports" in this computer and the components with which they are plugged
var/list/all_components = list()
@@ -47,11 +47,11 @@
/// Number of total expansion bays this computer has available.
var/max_bays = 0
var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
var/comp_light_luminosity = 3 //The brightness of that light
var/comp_light_color //The color of that light
var/list/idle_threads // Idle programs on background. They still receive process calls but can't be interacted with.
var/obj/physical = null // Object that represents our computer. It's used for Adjacent() and UI visibility checks.
var/has_light = FALSE //If the computer has a flashlight/LED light/what-have-you installed
var/comp_light_luminosity = 3 //The brightness of that light
var/comp_light_color //The color of that light
/obj/item/modular_computer/Initialize()
@@ -62,13 +62,12 @@
comp_light_color = "#FFFFFF"
idle_threads = list()
if(looping_sound)
soundloop = new(list(src), enabled)
update_icon()
soundloop = new(src, enabled)
update_appearance()
/obj/item/modular_computer/Destroy()
kill_program(forced = TRUE)
STOP_PROCESSING(SSobj, src)
QDEL_NULL(soundloop)
for(var/H in all_components)
var/obj/item/computer_hardware/CH = all_components[H]
if(CH.holder == src)
@@ -76,9 +75,19 @@
CH.holder = null
all_components.Remove(CH.device_type)
qdel(CH)
//Some components will actually try and interact with this, so let's do it later
QDEL_NULL(soundloop)
physical = null
return ..()
/**
* Plays a ping sound.
*
* Timers runtime if you try to make them call playsound. Yep.
*/
/obj/item/modular_computer/proc/play_ping()
playsound(loc, 'sound/machines/ping.ogg', get_clamped_volume(), FALSE, -1)
/obj/item/modular_computer/AltClick(mob/user)
..()
if(issilicon(user))
@@ -98,14 +107,34 @@
/obj/item/modular_computer/GetID()
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
if(card_slot)
return card_slot.GetID()
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/card/id/first_id = card_slot?.GetID()
var/obj/item/card/id/second_id = card_slot2?.GetID()
// If we don't have both ID slots filled, pick the one that is filled.
if(first_id)
return first_id
if(second_id)
return second_id
// Otherwise, we have no ID at all.
return ..()
/obj/item/modular_computer/RemoveID()
var/obj/item/computer_hardware/card_slot/card_slot2 = all_components[MC_CARD2]
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
return (card_slot2?.try_eject() || card_slot?.try_eject()) //Try the secondary one first.
var/removed_id = (card_slot2?.try_eject() || card_slot?.try_eject())
if(removed_id)
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
if(human_wearer.wear_id == src)
human_wearer.sec_hud_set_ID()
update_slot_icon()
return removed_id
return ..()
/obj/item/modular_computer/InsertID(obj/item/inserting_item)
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
@@ -118,7 +147,13 @@
return FALSE
if((card_slot?.try_insert(inserting_id)) || (card_slot2?.try_insert(inserting_id)))
if(ishuman(loc))
var/mob/living/carbon/human/human_wearer = loc
if(human_wearer.wear_id == src)
human_wearer.sec_hud_set_ID()
update_slot_icon()
return TRUE
return FALSE
/obj/item/modular_computer/MouseDrop(obj/over_object, src_location, over_location)
@@ -142,9 +177,8 @@
turn_on(user)
/obj/item/modular_computer/emag_act(mob/user)
. = ..()
if(!enabled)
to_chat(user, "<span class='warning'>You'd need to turn the [src] on first.</span>")
to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
obj_flags |= EMAGGED //Mostly for consistancy purposes; the programs will do their own emag handling
var/newemag = FALSE
@@ -155,36 +189,31 @@
if(app.run_emag())
newemag = TRUE
if(newemag)
to_chat(user, "<span class='notice'>You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past.</span>")
to_chat(user, span_notice("You swipe \the [src]. A console window momentarily fills the screen, with white text rapidly scrolling past."))
return TRUE
to_chat(user, "<span class='notice'>You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it.</span>")
to_chat(user, span_notice("You swipe \the [src]. A console window fills the screen, but it quickly closes itself after only a few lines are written to it."))
return FALSE
/obj/item/modular_computer/examine(mob/user)
. = ..()
if(obj_integrity <= integrity_failure * max_integrity)
. += "<span class='danger'>It is heavily damaged!</span>"
. += span_danger("It is heavily damaged!")
else if(obj_integrity < max_integrity)
. += "<span class='warning'>It is damaged.</span>"
. += span_warning("It is damaged.")
. += get_modular_computer_parts_examine(user)
/obj/item/modular_computer/update_icon_state()
if(!enabled)
icon_state = icon_state_unpowered
else
icon_state = icon_state_powered
icon_state = enabled ? icon_state_powered : icon_state_unpowered
return ..()
/obj/item/modular_computer/update_overlays()
. = ..()
if(!display_overlays)
return
if(enabled)
if(active_program)
. += active_program.program_icon_state ? active_program.program_icon_state : icon_state_menu
else
. += icon_state_menu
if(enabled)
. += active_program?.program_icon_state || icon_state_menu
if(obj_integrity <= integrity_failure * max_integrity)
. += "bsod"
. += "broken"
@@ -201,9 +230,9 @@
var/issynth = issilicon(user) // Robots and AIs get different activation messages.
if(obj_integrity <= integrity_failure * max_integrity)
if(issynth)
to_chat(user, "<span class='warning'>You send an activation signal to \the [src], but it responds with an error code. It must be damaged.</span>")
to_chat(user, span_warning("You send an activation signal to \the [src], but it responds with an error code. It must be damaged."))
else
to_chat(user, "<span class='warning'>You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again.</span>")
to_chat(user, span_warning("You press the power button, but the computer fails to boot up, displaying variety of errors before shutting down again."))
return FALSE
// If we have a recharger, enable it automatically. Lets computer without a battery work.
@@ -213,20 +242,20 @@
if(all_components[MC_CPU] && use_power()) // use_power() checks if the PC is powered
if(issynth)
to_chat(user, "<span class='notice'>You send an activation signal to \the [src], turning it on.</span>")
to_chat(user, span_notice("You send an activation signal to \the [src], turning it on."))
else
to_chat(user, "<span class='notice'>You press the power button and start up \the [src].</span>")
to_chat(user, span_notice("You press the power button and start up \the [src]."))
if(looping_sound)
soundloop.start()
enabled = 1
update_icon()
update_appearance()
ui_interact(user)
return TRUE
else // Unpowered
if(issynth)
to_chat(user, "<span class='warning'>You send an activation signal to \the [src] but it does not respond.</span>")
to_chat(user, span_warning("You send an activation signal to \the [src] but it does not respond."))
else
to_chat(user, "<span class='warning'>You press the power button but \the [src] does not respond.</span>")
to_chat(user, span_warning("You press the power button but \the [src] does not respond."))
return FALSE
// Process currently calls handle_power(), may be expanded in future if more things are added.
@@ -282,10 +311,10 @@
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
return
playsound(src, sound, 50, TRUE)
visible_message("<span class='notice'>The [src] displays a [caller.filedesc] notification: [alerttext]</span>")
visible_message(span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
var/mob/living/holder = loc
if(istype(holder))
to_chat(holder, "[icon2html(src)] <span class='notice'>The [src] displays a [caller.filedesc] notification: [alerttext]</span>")
to_chat(holder, "[icon2html(src)] [span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]")]")
// Function used by NanoUI's to obtain data for header. All relevant entries begin with "PC_"
/obj/item/modular_computer/proc/get_header_data()
@@ -349,14 +378,13 @@
// Relays kill program request to currently active program. Use this to quit current program.
/obj/item/modular_computer/proc/kill_program(forced = FALSE)
set waitfor = FALSE
if(active_program)
active_program.kill_program(forced)
active_program = null
var/mob/user = usr
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
update_icon()
update_appearance()
// Returns 0 for No Signal, 1 for Low Signal and 2 for Good Signal. 3 is for wired connection (always-on)
/obj/item/modular_computer/proc/get_ntnet_status(specific_action = 0)
@@ -370,6 +398,7 @@
if(!get_ntnet_status())
return FALSE
var/obj/item/computer_hardware/network_card/network_card = all_components[MC_NET]
return SSnetworks.station_network.add_log(text, network_card)
/obj/item/modular_computer/proc/shutdown_computer(loud = 1)
@@ -380,9 +409,9 @@
if(looping_sound)
soundloop.stop()
if(loud)
physical.visible_message("<span class='notice'>\The [src] shuts down.</span>")
physical.visible_message(span_notice("\The [src] shuts down."))
enabled = 0
update_icon()
update_appearance()
/**
* Toggles the computer's flashlight, if it has one.
@@ -412,13 +441,13 @@
if(!has_light || !color)
return FALSE
comp_light_color = color
// set_light_color(color)
set_light_color(color)
update_light()
return TRUE
/obj/item/modular_computer/screwdriver_act(mob/user, obj/item/tool)
if(!all_components.len)
to_chat(user, "<span class='warning'>This device doesn't have any components installed.</span>")
to_chat(user, span_warning("This device doesn't have any components installed."))
return
var/list/component_names = list()
for(var/h in all_components)
@@ -460,28 +489,34 @@
if(W.tool_behaviour == TOOL_WRENCH)
if(all_components.len)
to_chat(user, "<span class='warning'>Remove all components from \the [src] before disassembling it.</span>")
to_chat(user, span_warning("Remove all components from \the [src] before disassembling it."))
return
new /obj/item/stack/sheet/metal( get_turf(src.loc), steel_sheet_cost )
physical.visible_message("<span class='notice'>\The [src] is disassembled by [user].</span>")
physical.visible_message(span_notice("\The [src] is disassembled by [user]."))
relay_qdel()
qdel(src)
return
if(W.tool_behaviour == TOOL_WELDER)
if(obj_integrity == max_integrity)
to_chat(user, "<span class='warning'>\The [src] does not require repairs.</span>")
to_chat(user, span_warning("\The [src] does not require repairs."))
return
if(!W.tool_start_check(user, amount=1))
return
to_chat(user, "<span class='notice'>You begin repairing damage to \the [src]...</span>")
to_chat(user, span_notice("You begin repairing damage to \the [src]..."))
if(W.use_tool(src, user, 20, volume=50, amount=1))
obj_integrity = max_integrity
to_chat(user, "<span class='notice'>You repair \the [src].</span>")
to_chat(user, span_notice("You repair \the [src]."))
return
var/obj/item/computer_hardware/card_slot/card_slot = all_components[MC_CARD]
// Check to see if we have an ID inside, and a valid input for money
if(card_slot?.GetID() && iscash(W))
var/obj/item/card/id/id = card_slot.GetID()
id.attackby(W, user) // If we do, try and put that attacking object in
return
..()
// Used by processor to relay qdel() to machinery type.
@@ -3,19 +3,19 @@
return FALSE
if(H.w_class > max_hardware_size)
to_chat(user, "<span class='warning'>This component is too large for \the [src]!</span>")
to_chat(user, span_warning("This component is too large for \the [src]!"))
return FALSE
if(H.expansion_hw)
if(LAZYLEN(expansion_bays) >= max_bays)
to_chat(user, "<span class='warning'>All of the computer's expansion bays are filled.</span>")
to_chat(user, span_warning("All of the computer's expansion bays are filled."))
return FALSE
if(LAZYACCESS(expansion_bays, H.device_type))
to_chat(user, "<span class='warning'>The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\".</span>")
to_chat(user, span_warning("The computer immediately ejects /the [H] and flashes an error: \"Hardware Address Conflict\"."))
return FALSE
if(all_components[H.device_type])
to_chat(user, "<span class='warning'>This computer's hardware slot is already occupied by \the [all_components[H.device_type]].</span>")
to_chat(user, span_warning("This computer's hardware slot is already occupied by \the [all_components[H.device_type]]."))
return FALSE
return TRUE
@@ -32,7 +32,7 @@
LAZYSET(expansion_bays, H.device_type, H)
all_components[H.device_type] = H
to_chat(user, "<span class='notice'>You install \the [H] into \the [src].</span>")
to_chat(user, span_notice("You install \the [H] into \the [src]."))
H.holder = src
H.forceMove(src)
H.on_install(src, user)
@@ -47,14 +47,14 @@
LAZYREMOVE(expansion_bays, H.device_type)
all_components.Remove(H.device_type)
to_chat(user, "<span class='notice'>You remove \the [H] from \the [src].</span>")
to_chat(user, span_notice("You remove \the [H] from \the [src]."))
H.forceMove(get_turf(src))
H.holder = null
H.on_remove(src, user)
if(enabled && !use_power())
shutdown_computer()
update_icon()
update_appearance()
return TRUE
@@ -18,7 +18,7 @@
/obj/item/modular_computer/proc/break_apart()
if(!(flags_1 & NODECONSTRUCT_1))
physical.visible_message("<span class='notice'>\The [src] breaks apart!</span>")
physical.visible_message(span_notice("\The [src] breaks apart!"))
var/turf/newloc = get_turf(src)
new /obj/item/stack/sheet/metal(newloc, round(steel_sheet_cost/2))
for(var/C in all_components)
@@ -16,7 +16,7 @@
if(cell.use(amount * GLOB.CELLRATE))
return TRUE
else // Discharge the cell anyway.
cell.use(min(amount*GLOB.CELLRATE, cell.charge))
cell.use(min(amount * GLOB.CELLRATE, cell.charge))
return FALSE
return FALSE
@@ -13,6 +13,10 @@
ui.close()
return
// if(HAS_TRAIT(user, TRAIT_CHUNKYFINGERS))
// to_chat(user, span_warning("Your fingers are too big to use this right now!"))
// return
// Robots don't really need to see the screen, their wireless connection works as long as computer is on.
if(!screen_on && !issilicon(user))
if(ui)
@@ -30,7 +34,7 @@
// This screen simply lists available programs and user may select them.
var/obj/item/computer_hardware/hard_drive/hard_drive = all_components[MC_HDD]
if(!hard_drive || !hard_drive.stored_files || !hard_drive.stored_files.len)
to_chat(user, "<span class='danger'>\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning.</span>")
to_chat(user, span_danger("\The [src] beeps three times, it's screen displaying a \"DISK ERROR\" warning."))
return // No HDD, No HDD files list or no stored files. Something is very broken.
ui = SStgui.try_update_ui(user, src, ui)
@@ -111,15 +115,9 @@
active_program.program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
active_program = null
update_icon()
update_appearance()
if(user && istype(user))
ui_interact(user) // Re-open the UI on this computer. It should show the main screen now.
if("eject_pen")
if(istype(src, /obj/item/modular_computer/tablet))
var/obj/item/modular_computer/tablet/self = src
if(self.can_have_pen)
self.remove_pen()
return
if("PC_killprogram")
var/prog = params["name"]
@@ -132,7 +130,7 @@
return
P.kill_program(forced = TRUE)
to_chat(user, "<span class='notice'>Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed.</span>")
to_chat(user, span_notice("Program [P.filename].[P.filetype] with PID [rand(100,999)] has been killed."))
if("PC_runprogram")
var/prog = params["name"]
@@ -142,7 +140,7 @@
P = hard_drive.find_file_by_name(prog)
if(!P || !istype(P)) // Program not found or it's not executable program.
to_chat(user, "<span class='danger'>\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning.</span>")
to_chat(user, span_danger("\The [src]'s screen shows \"I/O ERROR - Unable to run program\" warning."))
return
P.computer = src
@@ -156,22 +154,22 @@
active_program = P
P.alert_pending = FALSE
idle_threads.Remove(P)
update_icon()
update_appearance()
return
var/obj/item/computer_hardware/processor_unit/PU = all_components[MC_CPU]
if(idle_threads.len > PU.max_idle_programs)
to_chat(user, "<span class='danger'>\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error.</span>")
to_chat(user, span_danger("\The [src] displays a \"Maximal CPU load reached. Unable to run another program.\" error."))
return
if(P.requires_ntnet && !get_ntnet_status(P.requires_ntnet_feature)) // The program requires NTNet connection, but we are not connected to NTNet.
to_chat(user, "<span class='danger'>\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning.</span>")
to_chat(user, span_danger("\The [src]'s screen shows \"Unable to connect to NTNet. Please retry. If problem persists contact your system administrator.\" warning."))
return
if(P.run_program(user))
active_program = P
P.alert_pending = FALSE
update_icon()
update_appearance()
return 1
if("PC_toggle_light")
@@ -185,7 +183,7 @@
if(!new_color)
return
if(color_hex2num(new_color) < 200) //Colors too dark are rejected
to_chat(user, "<span class='warning'>That color is too dark! Choose a lighter one.</span>")
to_chat(user, span_warning("That color is too dark! Choose a lighter one."))
new_color = null
return set_flashlight_color(new_color)
@@ -17,8 +17,8 @@
// No running around with open laptops in hands.
item_flags = SLOWS_WHILE_IN_HAND
screen_on = FALSE // Starts closed
var/start_open = TRUE // unless this var is set to 1
screen_on = FALSE // Starts closed
var/start_open = TRUE // unless this var is set to 1
var/icon_state_closed = "laptop-closed"
var/w_class_open = WEIGHT_CLASS_BULKY
var/slowdown_open = TRUE
@@ -44,15 +44,14 @@
/obj/item/modular_computer/laptop/update_icon_state()
if(!screen_on)
icon_state = icon_state_closed
else
. = ..()
return
return ..()
/obj/item/modular_computer/laptop/update_overlays()
if(screen_on)
return ..()
else
if(!screen_on)
cut_overlays()
icon_state = icon_state_closed
return
return ..()
/obj/item/modular_computer/laptop/attack_self(mob/user)
if(!screen_on)
@@ -68,7 +67,8 @@
try_toggle_open(usr)
/obj/item/modular_computer/laptop/MouseDrop(obj/over_object, src_location, over_location)
if(istype(over_object, /atom/movable/screen/inventory/hand) || over_object == usr)
. = ..()
if(istype(over_object, /atom/movable/screen/inventory/hand))
var/atom/movable/screen/inventory/hand/H = over_object
var/mob/M = usr
@@ -103,17 +103,17 @@
/obj/item/modular_computer/laptop/proc/toggle_open(mob/living/user=null)
if(screen_on)
to_chat(user, "<span class='notice'>You close \the [src].</span>")
to_chat(user, span_notice("You close \the [src]."))
slowdown = initial(slowdown)
w_class = initial(w_class)
else
to_chat(user, "<span class='notice'>You open \the [src].</span>")
to_chat(user, span_notice("You open \the [src]."))
slowdown = slowdown_open
w_class = w_class_open
screen_on = !screen_on
display_overlays = screen_on
update_icon()
update_appearance()
@@ -38,7 +38,7 @@
integrity_failure = machinery_computer.integrity_failure
base_active_power_usage = machinery_computer.base_active_power_usage
base_idle_power_usage = machinery_computer.base_idle_power_usage
machinery_computer.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /atom/proc/update_icon) //when we update_icon, also update the computer
machinery_computer.RegisterSignal(src, COMSIG_ATOM_UPDATED_ICON, /obj/machinery/modular_computer/proc/relay_icon_update) //when we update_icon, also update the computer
/obj/item/modular_computer/processor/relay_qdel()
qdel(machinery_computer)
@@ -47,7 +47,7 @@
if(!machinery_computer)
return
..()
machinery_computer.update_icon()
machinery_computer.update_appearance()
return
/obj/item/modular_computer/processor/attack_ghost(mob/user)
@@ -57,4 +57,4 @@
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext)
return
playsound(src, 'sound/machines/twobeep_high.ogg', 50, TRUE)
machinery_computer.visible_message("<span class='notice'>The [src] displays a [caller.filedesc] notification: [alerttext]</span>")
machinery_computer.visible_message(span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
@@ -2,9 +2,10 @@
name = "tablet computer"
icon = 'icons/obj/modular_tablet.dmi'
icon_state = "tablet-red"
icon_state_unpowered = "tablet"
icon_state_powered = "tablet"
icon_state_unpowered = "tablet-red"
icon_state_powered = "tablet-red"
icon_state_menu = "menu"
base_icon_state = "tablet"
// worn_icon_state = "tablet"
hardware_flag = PROGRAM_TABLET
max_hardware_size = 1
@@ -80,12 +81,12 @@
/obj/item/modular_computer/tablet/ui_data(mob/user)
. = ..()
.["PC_showpeneject"] = inserted_item ? 1 : 0
/obj/item/modular_computer/tablet/update_icon_state()
if(has_variants)
if(!finish_color)
finish_color = pick("red","blue","brown","green","black")
icon_state = icon_state_powered = icon_state_unpowered = "tablet-[finish_color]"
finish_color = pick("red", "blue", "brown", "green", "black")
icon_state = icon_state_powered = icon_state_unpowered = "[base_icon_state]-[finish_color]"
return ..()
/obj/item/modular_computer/tablet/syndicate_contract_uplink
name = "contractor tablet"
@@ -102,6 +103,8 @@
/// Given to Nuke Ops members.
/obj/item/modular_computer/tablet/nukeops
icon_state = "tablet-syndicate"
icon_state_powered = "tablet-syndicate"
icon_state_unpowered = "tablet-syndicate"
comp_light_luminosity = 6.3
has_variants = FALSE
device_theme = "syndicate"
@@ -109,15 +112,18 @@
/obj/item/modular_computer/tablet/nukeops/emag_act(mob/user)
if(!enabled)
to_chat(user, "<span class='warning'>You'd need to turn the [src] on first.</span>")
to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
to_chat(user, "<span class='notice'>You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\".</span>")
to_chat(user, span_notice("You swipe \the [src]. It's screen briefly shows a message reading \"MEMORY CODE INJECTION DETECTED AND SUCCESSFULLY QUARANTINED\"."))
return FALSE
/// Borg Built-in tablet interface
/obj/item/modular_computer/tablet/integrated
name = "modular interface"
icon_state = "tablet-silicon"
icon_state_powered = "tablet-silicon"
icon_state_unpowered = "tablet-silicon"
base_icon_state = "tablet-silicon"
has_light = FALSE //tablet light button actually enables/disables the borg lamp
comp_light_luminosity = 0
has_variants = FALSE
@@ -198,11 +204,13 @@
if(!caller || !caller.alert_able || caller.alert_silenced || !alerttext) //Yeah, we're checking alert_able. No, you don't get to make alerts that the user can't silence.
return
borgo.playsound_local(src, sound, 50, TRUE)
to_chat(borgo, "<span class='notice'>The [src] displays a [caller.filedesc] notification: [alerttext]</span>")
to_chat(borgo, span_notice("The [src] displays a [caller.filedesc] notification: [alerttext]"))
/obj/item/modular_computer/tablet/integrated/syndicate
icon_state = "tablet-silicon-syndicate"
icon_state_powered = "tablet-silicon-syndicate"
icon_state_unpowered = "tablet-silicon-syndicate"
device_theme = "syndicate"
@@ -20,6 +20,17 @@
install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/printer/mini)
/obj/item/modular_computer/tablet/preset/science/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
install_component(new /obj/item/computer_hardware/processor_unit/small)
install_component(new /obj/item/computer_hardware/battery(src, /obj/item/stock_parts/cell/computer))
install_component(hard_drive)
install_component(new /obj/item/computer_hardware/card_slot)
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/radio_card)
hard_drive.store_file(new /datum/computer_file/program/signaler)
/obj/item/modular_computer/tablet/preset/cargo/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = new
@@ -30,17 +41,38 @@
install_component(new /obj/item/computer_hardware/network_card)
install_component(new /obj/item/computer_hardware/printer/mini)
// hard_drive.store_file(new /datum/computer_file/program/shipping)
var/datum/computer_file/program/chatclient/chatprogram
chatprogram = new
hard_drive.store_file(chatprogram)
chatprogram.username = get_cargochat_username()
/obj/item/modular_computer/tablet/preset/cargo/proc/get_cargochat_username()
return "cargonian_[rand(1,999)]"
/obj/item/modular_computer/tablet/preset/cargo/quartermaster/get_cargochat_username()
return "quartermaster"
/obj/item/modular_computer/tablet/preset/advanced/atmos/Initialize() //This will be defunct and will be replaced when NtOS PDAs are done
. = ..()
install_component(new /obj/item/computer_hardware/sensorpackage)
/obj/item/modular_computer/tablet/preset/advanced/engineering/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive")
hard_drive.store_file(new /datum/computer_file/program/supermatter_monitor)
/obj/item/modular_computer/tablet/preset/advanced/command/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive")
install_component(new /obj/item/computer_hardware/sensorpackage)
install_component(new /obj/item/computer_hardware/card_slot/secondary)
hard_drive.store_file(new /datum/computer_file/program/budgetorders)
// hard_drive.store_file(new /datum/computer_file/program/science)
/obj/item/modular_computer/tablet/preset/advanced/command/engineering/Initialize()
. = ..()
var/obj/item/computer_hardware/hard_drive/small/hard_drive = find_hardware_by_name("solid state drive")
hard_drive.store_file(new /datum/computer_file/program/supermatter_monitor)
/// Given by the syndicate as part of the contract uplink bundle - loads in the Contractor Uplink.
/obj/item/modular_computer/tablet/syndicate_contract_uplink/preset/uplink/Initialize()
@@ -26,8 +26,6 @@
/obj/machinery/modular_computer/console/preset/proc/install_programs()
return
// ===== ENGINEERING CONSOLE =====
/obj/machinery/modular_computer/console/preset/engineering
console_department = "Engineering"
@@ -45,6 +43,7 @@
console_department = "Research"
name = "research director's console"
desc = "A stationary computer. This one comes preloaded with research programs."
_has_second_id_slot = TRUE
_has_ai = TRUE
/obj/machinery/modular_computer/console/preset/research/install_programs()
@@ -84,6 +83,18 @@
hard_drive.store_file(new/datum/computer_file/program/job_management())
hard_drive.store_file(new/datum/computer_file/program/crew_manifest())
/obj/machinery/modular_computer/console/preset/id/centcom
desc = "A stationary computer. This one comes preloaded with CentCom identification modification programs."
/obj/machinery/modular_computer/console/preset/id/centcom/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
var/datum/computer_file/program/card_mod/card_mod_centcom = new /datum/computer_file/program/card_mod()
card_mod_centcom.is_centcom = TRUE
hard_drive.store_file(new /datum/computer_file/program/chatclient())
hard_drive.store_file(card_mod_centcom)
hard_drive.store_file(new /datum/computer_file/program/job_management())
hard_drive.store_file(new /datum/computer_file/program/crew_manifest())
// ===== CIVILIAN CONSOLE =====
/obj/machinery/modular_computer/console/preset/civilian
console_department = "Civilian"
@@ -94,3 +105,79 @@
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/chatclient())
hard_drive.store_file(new/datum/computer_file/program/arcade())
// curator
/obj/machinery/modular_computer/console/preset/curator
console_department = "Civilian"
name = "curator console"
desc = "A stationary computer. This one comes preloaded with art programs."
_has_printer = TRUE
/obj/machinery/modular_computer/console/preset/curator/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
hard_drive.store_file(new/datum/computer_file/program/portrait_printer())
// ===== CARGO CHAT CONSOLES =====
/obj/machinery/modular_computer/console/preset/cargochat
name = "cargo chatroom console"
desc = "A stationary computer. This one comes preloaded with a chatroom for your cargo requests."
///chat client installed on this computer, just helpful for linking all the computers
var/datum/computer_file/program/chatclient/chatprogram
/obj/machinery/modular_computer/console/preset/cargochat/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
chatprogram = new
chatprogram.computer = cpu
hard_drive.store_file(chatprogram)
chatprogram.username = "[lowertext(console_department)]_department"
chatprogram.program_state = PROGRAM_STATE_ACTIVE
cpu.active_program = chatprogram
//ONE PER MAP PLEASE, IT MAKES A CARGOBUS FOR EACH ONE OF THESE
/obj/machinery/modular_computer/console/preset/cargochat/cargo
console_department = "Cargo"
name = "department chatroom console"
desc = "A stationary computer. This one comes preloaded with a chatroom for incoming cargo requests. You may moderate it from this computer."
/obj/machinery/modular_computer/console/preset/cargochat/cargo/install_programs()
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
//adding chat, setting it as the active window immediately
chatprogram = new
chatprogram.computer = cpu
hard_drive.store_file(chatprogram)
chatprogram.program_state = PROGRAM_STATE_ACTIVE
cpu.active_program = chatprogram
//setting up chat
chatprogram.username = "cargo_requests_operator"
var/datum/ntnet_conversation/cargochat = new
cargochat.operator = chatprogram //adding operator before joining the chat prevents an unnecessary message about switching op from showing
cargochat.add_client(chatprogram)
cargochat.title = "#cargobus"
cargochat.strong = TRUE
chatprogram.active_channel = cargochat.id
/obj/machinery/modular_computer/console/preset/cargochat/cargo/LateInitialize()
. = ..()
var/datum/ntnet_conversation/cargochat = SSnetworks.station_network.get_chat_channel_by_id(chatprogram.active_channel)
for(var/obj/machinery/modular_computer/console/preset/cargochat/cargochat_console in GLOB.machines)
if(cargochat_console == src)
continue
cargochat_console.chatprogram.active_channel = chatprogram.active_channel
cargochat.add_client(cargochat_console.chatprogram, silent = TRUE)
/obj/machinery/modular_computer/console/preset/cargochat/service
console_department = "Service"
/obj/machinery/modular_computer/console/preset/cargochat/engineering
console_department = "Engineering"
/obj/machinery/modular_computer/console/preset/cargochat/science
console_department = "Science"
/obj/machinery/modular_computer/console/preset/cargochat/security
console_department = "Security"
/obj/machinery/modular_computer/console/preset/cargochat/medical
console_department = "Medical"
@@ -4,28 +4,41 @@
name = "modular computer"
desc = "An advanced computer."
use_power = IDLE_POWER_USE
idle_power_usage = 5
var/hardware_flag = 0 // A flag that describes this device type
var/last_power_usage = 0 // Power usage during last tick
// Modular computers can run on various devices. Each DEVICE (Laptop, Console, Tablet,..)
// must have it's own DMI file. Icon states must be called exactly the same in all files, but may look differently
// If you create a program which is limited to Laptops and Consoles you don't have to add it's icon_state overlay for Tablets too, for example.
icon = null
icon_state = null
var/icon_state_unpowered = null // Icon state when the computer is turned off.
var/icon_state_powered = null // Icon state when the computer is turned on.
var/screen_icon_state_menu = "menu" // Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/screen_icon_screensaver = "standby" // Icon state overlay when the computer is powered, but not 'switched on'.
var/max_hardware_size = 0 // Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/steel_sheet_cost = 10 // Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/light_strength = 0 // Light luminosity when turned on
var/base_active_power_usage = 100 // Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_idle_power_usage = 10 // Power usage when the computer is idle and screen is off (currently only applies to laptops)
var/obj/item/modular_computer/processor/cpu = null // CPU that handles most logic while this type only handles power and other specific things.
use_power = IDLE_POWER_USE
idle_power_usage = 5
///A flag that describes this device type
var/hardware_flag = 0
///Power usage during last tick
var/last_power_usage = 0
///Icon state when the computer is turned off.
var/icon_state_unpowered = null
///Icon state when the computer is turned on.
var/icon_state_powered = null
///Icon state overlay when the computer is turned on, but no program is loaded that would override the screen.
var/screen_icon_state_menu = "menu"
///Icon state overlay when the computer is powered, but not 'switched on'.
var/screen_icon_screensaver = "standby"
///Maximal hardware size. Currently, tablets have 1, laptops 2 and consoles 3. Limits what hardware types can be installed.
var/max_hardware_size = 0
///Amount of steel sheets refunded when disassembling an empty frame of this computer.
var/steel_sheet_cost = 10
///Light luminosity when turned on
var/light_strength = 0
///Power usage when the computer is open (screen is active) and can be interacted with. Remember hardware can use power too.
var/base_active_power_usage = 100
///Power usage when the computer is idle and screen is off (currently only applies to laptops)
var/base_idle_power_usage = 10
///CPU that handles most logic while this type only handles power and other specific things.
var/obj/item/modular_computer/processor/cpu = null
/obj/machinery/modular_computer/Initialize()
. = ..()
@@ -48,32 +61,35 @@
cpu.attack_ghost(user)
/obj/machinery/modular_computer/emag_act(mob/user)
. = ..()
if(!cpu)
to_chat(user, "<span class='warning'>You'd need to turn the [src] on first.</span>")
to_chat(user, span_warning("You'd need to turn the [src] on first."))
return FALSE
return (cpu.emag_act(user))
/obj/machinery/modular_computer/update_icon()
cut_overlays()
icon_state = icon_state_powered
/obj/machinery/modular_computer/update_appearance(updates)
. = ..()
set_light(cpu?.enabled ? light_strength : 0)
if(!cpu || !cpu.enabled)
/obj/machinery/modular_computer/update_icon_state()
icon_state = (cpu?.enabled || (!(stat & NOPOWER) && cpu?.use_power())) ? icon_state_powered : icon_state_unpowered
return ..()
/obj/machinery/modular_computer/update_overlays()
. = ..()
if(!cpu?.enabled)
if (!(stat & NOPOWER) && (cpu?.use_power()))
add_overlay(screen_icon_screensaver)
else
icon_state = icon_state_unpowered
set_light(0)
. += screen_icon_screensaver
else
set_light(light_strength)
if(cpu.active_program)
add_overlay(cpu.active_program.program_icon_state ? cpu.active_program.program_icon_state : screen_icon_state_menu)
else
add_overlay(screen_icon_state_menu)
. += cpu.active_program?.program_icon_state || screen_icon_state_menu
if(cpu && cpu.obj_integrity <= cpu.integrity_failure * cpu.max_integrity)
add_overlay("bsod")
add_overlay("broken")
. += "bsod"
. += "broken"
/// Eats the "source" arg because update_icon actually expects args now.
/obj/machinery/modular_computer/proc/relay_icon_update(datum/source, updates, updated)
SIGNAL_HANDLER
return update_icon(updates)
/obj/machinery/modular_computer/AltClick(mob/user)
if(cpu)
@@ -98,17 +114,17 @@
/obj/machinery/modular_computer/proc/power_failure(malfunction = 0)
var/obj/item/computer_hardware/battery/battery_module = cpu.all_components[MC_CELL]
if(cpu?.enabled) // Shut down the computer
visible_message("<span class='danger'>\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly.</span>")
visible_message(span_danger("\The [src]'s screen flickers [battery_module ? "\"BATTERY [malfunction ? "MALFUNCTION" : "CRITICAL"]\"" : "\"EXTERNAL POWER LOSS\""] warning as it shuts down unexpectedly."))
if(cpu)
cpu.shutdown_computer(0)
stat |= NOPOWER
update_icon()
set_machine_stat(stat | NOPOWER)
update_appearance()
// Modular computers can have battery in them, we handle power in previous proc, so prevent this from messing it up for us.
/obj/machinery/modular_computer/power_change()
if(cpu?.use_power()) // If MC_CPU still has a power source, PC wouldn't go offline.
stat &= ~NOPOWER
update_icon()
set_machine_stat(stat & ~NOPOWER)
update_appearance()
return
. = ..()
@@ -116,7 +132,7 @@
if(cpu)
return cpu.screwdriver_act(user, tool)
/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/user)
/obj/machinery/modular_computer/attackby(obj/item/W as obj, mob/living/user)
if (user.a_intent == INTENT_HELP && cpu && !(flags_1 & NODECONSTRUCT_1))
return cpu.attackby(W, user)
return ..()
@@ -126,15 +142,16 @@
// Minor explosions are mostly mitigitated by casing.
/obj/machinery/modular_computer/ex_act(severity, target, origin)
if(cpu)
cpu.ex_act(severity, target, origin)
// switch(severity)
// if(EXPLODE_DEVASTATE)
// SSexplosions.high_mov_atom += cpu
// if(EXPLODE_HEAVY)
// SSexplosions.med_mov_atom += cpu
// if(EXPLODE_LIGHT)
// SSexplosions.low_mov_atom += cpu
..()
return cpu.ex_act(severity)
// switch(severity)
// if(EXPLODE_DEVASTATE)
// SSexplosions.high_mov_atom += cpu
// if(EXPLODE_HEAVY)
// SSexplosions.med_mov_atom += cpu
// if(EXPLODE_LIGHT)
// SSexplosions.low_mov_atom += cpu
return ..()
// EMPs are similar to explosions, but don't cause physical damage to the casing. Instead they screw up the components
/obj/machinery/modular_computer/emp_act(severity)
@@ -16,7 +16,8 @@
light_strength = 2
max_integrity = 300
integrity_failure = 0.5
var/console_department = "" // Used in New() to set network tag according to our area.
///Used in New() to set network tag according to our area.
var/console_department = ""
/obj/machinery/modular_computer/console/buildable/Initialize()
. = ..()
@@ -52,4 +53,4 @@
network_card.identification_string = "Unknown Console"
if(cpu)
cpu.screen_on = 1
update_icon()
update_appearance()
@@ -1,11 +1,11 @@
/datum/computer_file
var/filename = "NewFile" // Placeholder. No spacebars
var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case
var/size = 1 // File size in GQ. Integers only!
var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file.
var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means.
var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc.
var/uid // UID of this file
var/filename = "NewFile" // Placeholder. No spacebars
var/filetype = "XXX" // File full names are [filename].[filetype] so like NewFile.XXX in this case
var/size = 1 // File size in GQ. Integers only!
var/obj/item/computer_hardware/hard_drive/holder // Holder that contains this file.
var/unsendable = FALSE // Whether the file may be sent to someone via NTNet transfer or other means.
var/undeletable = FALSE // Whether the file may be deleted. Setting to TRUE prevents deletion/renaming/etc.
var/uid // UID of this file
var/static/file_uid = 0
/datum/computer_file/New()
@@ -1,10 +1,10 @@
// /data/ files store data in string format.
// They don't contain other logic for now.
/datum/computer_file/data
var/stored_data = "" // Stored data in string format.
var/stored_data = "" // Stored data in string format.
filetype = "DAT"
var/block_size = 250
var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited.
var/do_not_edit = 0 // Whether the user will be reminded that the file probably shouldn't be edited.
/datum/computer_file/data/clone()
var/datum/computer_file/data/temp = ..()
@@ -15,6 +15,8 @@
var/filedesc = "Unknown Program"
/// Short description of this program's function.
var/extended_desc = "N/A"
/// Category in the NTDownloader.
var/category = PROGRAM_CATEGORY_MISC
/// Program-specific screen icon state
var/program_icon_state = null
/// Set to 1 for program to require nonstop NTNet connection to run. If NTNet connection is lost program crashes.
@@ -25,10 +27,10 @@
var/ntnet_status = 1
/// Bitflags (PROGRAM_CONSOLE, PROGRAM_LAPTOP, PROGRAM_TABLET combination) or PROGRAM_ALL
var/usage_flags = PROGRAM_ALL
/// Whether the program can be downloaded from NTNet. Set to 0 to disable.
var/available_on_ntnet = 1
/// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to 1 to enable.
var/available_on_syndinet = 0
/// Whether the program can be downloaded from NTNet. Set to FALSE to disable.
var/available_on_ntnet = TRUE
/// Whether the program can be downloaded from SyndiNet (accessible via emagging the computer). Set to TRUE to enable.
var/available_on_syndinet = FALSE
/// Name of the tgui interface
var/tgui_id
/// Example: "something.gif" - a header image that will be rendered in computer's UI when this program is running at background. Images are taken from /icons/program_icons. Be careful not to use too large images!
@@ -64,7 +66,7 @@
// Relays icon update to the computer.
/datum/computer_file/program/proc/update_computer_icon()
if(computer)
computer.update_icon()
computer.update_appearance()
// Attempts to create a log in global ntnet datum. Returns 1 on success, 0 on fail.
/datum/computer_file/program/proc/generate_network_log(text)
@@ -72,10 +74,25 @@
return computer.add_log(text)
return 0
/**
*Runs when the device is used to attack an atom in non-combat mode.
*
*Simulates using the device to read or scan something. Tap is called by the computer during pre_attack
*and sends us all of the related info. If we return TRUE, the computer will stop the attack process
*there. What we do with the info is up to us, but we should only return TRUE if we actually perform
*an action of some sort.
*Arguments:
*A is the atom being tapped
*user is the person making the attack action
*params is anything the pre_attack() proc had in the same-named variable.
*/
/datum/computer_file/program/proc/tap(atom/A, mob/living/user, params)
return FALSE
/datum/computer_file/program/proc/is_supported_by_hardware(hardware_flag = 0, loud = 0, mob/user = null)
if(!(hardware_flag & usage_flags))
if(loud && computer && user)
to_chat(user, "<span class='danger'>\The [computer] flashes a \"Hardware Error - Incompatible software\" warning.</span>")
to_chat(user, span_danger("\The [computer] flashes a \"Hardware Error - Incompatible software\" warning."))
return FALSE
return TRUE
@@ -109,7 +126,7 @@
if(!access_to_check) // No required_access, allow it.
return TRUE
if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones.
if(!transfer && computer && (computer.obj_flags & EMAGGED)) //emags can bypass the execution locks but not the download ones.
return TRUE
if(IsAdminGhost(user))
@@ -127,14 +144,14 @@
if(!D)
if(loud)
to_chat(user, "<span class='danger'>\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning.</span>")
to_chat(user, span_danger("\The [computer] flashes an \"RFID Error - Unable to scan ID\" warning."))
return FALSE
access = D.GetAccess()
if(access_to_check in access)
return TRUE
if(loud)
to_chat(user, "<span class='danger'>\The [computer] flashes an \"Access Denied\" warning.</span>")
to_chat(user, span_danger("\The [computer] flashes an \"Access Denied\" warning."))
return FALSE
// This attempts to retrieve header data for UIs. If implementing completely new device of different type than existing ones
@@ -219,7 +236,7 @@
program_state = PROGRAM_STATE_BACKGROUND // Should close any existing UIs
computer.active_program = null
computer.update_icon()
computer.update_appearance()
ui.close()
if(user && istype(user))
@@ -13,6 +13,6 @@
/datum/computer_file/program/proc/event_networkfailure(background)
kill_program(forced = TRUE)
if(background)
computer.visible_message("<span class='danger'>\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error</span>")
computer.visible_message(span_danger("\The [computer]'s screen displays a \"Process [filename].[filetype] (PID [rand(100,999)]) terminated - Network Error\" error"))
else
computer.visible_message("<span class='danger'>\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error.</span>")
computer.visible_message(span_danger("\The [computer]'s screen briefly freezes and then shows \"NETWORK ERROR - NTNet connection lost. Please retry. If problem persists contact your system administrator.\" error."))
@@ -1,6 +1,7 @@
/datum/computer_file/program/aidiag
filename = "aidiag"
filedesc = "NT FRK"
category = PROGRAM_CATEGORY_ROBO
program_icon_state = "generic"
extended_desc = "Firmware Restoration Kit, capable of reconstructing damaged AI systems. Requires direct AI connection via intellicard slot."
size = 12
@@ -55,7 +56,7 @@
/datum/computer_file/program/aidiag/process_tick()
. = ..()
if(!restoring) //Put the check here so we don't check for an ai all the time
if(!restoring) //Put the check here so we don't check for an ai all the time
return
var/obj/item/aicard/cardhold = get_ai(2)
@@ -64,7 +65,7 @@
var/mob/living/silicon/ai/A = get_ai()
if(!A || !cardhold)
restoring = FALSE // If the AI was removed, stop the restoration sequence.
restoring = FALSE // If the AI was removed, stop the restoration sequence.
if(ai_slot)
ai_slot.locked = FALSE
return
@@ -84,7 +85,7 @@
if(A.health >= 0 && A.stat == DEAD)
A.revive(full_heal = FALSE, admin_revive = FALSE)
cardhold.update_icon()
cardhold.update_appearance()
// Finished restoring
if(A.health >= 100)
@@ -1,6 +1,7 @@
/datum/computer_file/program/contract_uplink
filename = "contractor uplink"
filedesc = "Syndicate Contractor Uplink"
category = PROGRAM_CATEGORY_MISC
program_icon_state = "assign"
extended_desc = "A standard, Syndicate issued system for handling important contracts while on the field."
size = 10
@@ -91,9 +92,9 @@
if(ishuman(user))
var/mob/living/carbon/human/H = user
if(H.put_in_hands(crystals))
to_chat(H, "<span class='notice'>Your payment materializes into your hands!</span>")
to_chat(H, span_notice("Your payment materializes into your hands!"))
else
to_chat(user, "<span class='notice'>Your payment materializes onto the floor.</span>")
to_chat(user, span_notice("Your payment materializes onto the floor."))
hard_drive.traitor_data.contractor_hub.contract_TC_payed_out += hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem
hard_drive.traitor_data.contractor_hub.contract_TC_to_redeem = 0
@@ -164,6 +165,11 @@
))
for (var/datum/syndicate_contract/contract in traitor_data.contractor_hub.assigned_contracts)
if(!contract.contract)
stack_trace("Syndiate contract with null contract objective found in [traitor_data.owner]'s contractor hub!")
contract.status = CONTRACT_STATUS_ABORTED
continue
data["contracts"] += list(list(
"target" = contract.contract.target,
"target_rank" = contract.target_rank,
@@ -1,6 +1,7 @@
/datum/computer_file/program/ntnet_dos
filename = "ntn_dos"
filedesc = "DoS Traffic Generator"
category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This advanced script can perform denial of service attacks against NTNet quantum relays. The system administrator will probably notice this. Multiple devices can run this program together against same relay for increased effect"
size = 20
@@ -1,6 +1,7 @@
/datum/computer_file/program/revelation
filename = "revelation"
filedesc = "Revelation"
category = PROGRAM_CATEGORY_MISC
program_icon_state = "hostile"
extended_desc = "This virus can destroy hard drive of system it is executed on. It may be obfuscated to look like another non-malicious program. Once armed, it will destroy the system upon next execution."
size = 13
@@ -20,13 +21,13 @@
if(computer)
if(istype(computer, /obj/item/modular_computer/tablet/integrated)) //If this is a borg's integrated tablet
var/obj/item/modular_computer/tablet/integrated/modularInterface = computer
to_chat(modularInterface.borgo,"<span class='userdanger'>SYSTEM PURGE DETECTED/</span>")
to_chat(modularInterface.borgo,span_userdanger("SYSTEM PURGE DETECTED/"))
addtimer(CALLBACK(modularInterface.borgo, /mob/living/silicon/robot/.proc/death), 2 SECONDS, TIMER_UNIQUE)
return
computer.visible_message("<span class='notice'>\The [computer]'s screen brightly flashes and loud electrical buzzing is heard.</span>")
computer.visible_message(span_notice("\The [computer]'s screen brightly flashes and loud electrical buzzing is heard."))
computer.enabled = FALSE
computer.update_icon()
computer.update_appearance()
var/obj/item/computer_hardware/hard_drive/hard_drive = computer.all_components[MC_HDD]
var/obj/item/computer_hardware/battery/battery_module = computer.all_components[MC_CELL]
var/obj/item/computer_hardware/recharger/recharger = computer.all_components[MC_CHARGE]
@@ -34,13 +35,13 @@
computer.take_damage(25, BRUTE, 0, 0)
if(battery_module && prob(25))
qdel(battery_module)
computer.visible_message("<span class='notice'>\The [computer]'s battery explodes in rain of sparks.</span>")
computer.visible_message(span_notice("\The [computer]'s battery explodes in rain of sparks."))
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
spark_system.start()
if(recharger && prob(50))
qdel(recharger)
computer.visible_message("<span class='notice'>\The [computer]'s recharger explodes in rain of sparks.</span>")
computer.visible_message(span_notice("\The [computer]'s recharger explodes in rain of sparks."))
var/datum/effect_system/spark_spread/spark_system = new /datum/effect_system/spark_spread
spark_system.start()
@@ -32,7 +32,7 @@
game_active = FALSE
program_icon_state = "arcade_off"
if(istype(computer))
computer.update_icon()
computer.update_appearance()
ticket_count += 1
// user?.mind?.adjust_experience(/datum/skill/gaming, 50)
sleep(10)
@@ -42,7 +42,7 @@
game_active = FALSE
program_icon_state = "arcade_off"
if(istype(computer))
computer.update_icon()
computer.update_appearance()
// user?.mind?.adjust_experience(/datum/skill/gaming, 10)
sleep(10)
@@ -150,20 +150,20 @@
return TRUE
if("Dispense_Tickets")
if(!printer)
to_chat(usr, "<span class='notice'>Hardware error: A printer is required to redeem tickets.</span>")
to_chat(usr, span_notice("Hardware error: A printer is required to redeem tickets."))
return
if(printer.stored_paper <= 0)
to_chat(usr, "<span class='notice'>Hardware error: Printer is out of paper.</span>")
to_chat(usr, span_notice("Hardware error: Printer is out of paper."))
return
else
computer.visible_message("<span class='notice'>\The [computer] prints out paper.</span>")
computer.visible_message(span_notice("\The [computer] prints out paper."))
if(ticket_count >= 1)
new /obj/item/stack/arcadeticket((get_turf(computer)), 1)
to_chat(usr, "<span class='notice'>[src] dispenses a ticket!</span>")
to_chat(usr, span_notice("[computer] dispenses a ticket!"))
ticket_count -= 1
printer.stored_paper -= 1
else
to_chat(usr, "<span class='notice'>You don't have any stored tickets!</span>")
to_chat(usr, span_notice("You don't have any stored tickets!"))
return TRUE
if("Start_Game")
game_active = TRUE
@@ -175,4 +175,4 @@
boss_id = rand(1,6)
pause_state = FALSE
if(istype(computer))
computer.update_icon()
computer.update_appearance()
@@ -1,6 +1,7 @@
/datum/computer_file/program/atmosscan
filename = "atmosscan"
filedesc = "AtmoZphere"
category = PROGRAM_CATEGORY_ENGI
program_icon_state = "air"
extended_desc = "A small built-in sensor reads out the atmospheric conditions around the device."
size = 4
@@ -12,7 +13,7 @@
if (!.)
return
if(!computer?.get_modular_computer_part(MC_SENSORS)) //Giving a clue to users why the program is spitting out zeros.
to_chat(user, "<span class='warning'>\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\".</span>")
to_chat(user, span_warning("\The [computer] flashes an error: \"hardware\\sensorpackage\\startup.bin -- file not found\"."))
/datum/computer_file/program/atmosscan/ui_data(mob/user)
@@ -1,6 +1,7 @@
/datum/computer_file/program/borg_monitor
filename = "siliconnect"
filedesc = "SiliConnect"
category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of station cyborgs."
@@ -9,6 +10,70 @@
size = 5
tgui_id = "NtosCyborgRemoteMonitor"
program_icon = "project-diagram"
var/emagged = FALSE ///Bool of if this app has already been emagged
var/list/loglist = list() ///A list to copy a borg's IC log list into
var/mob/living/silicon/robot/DL_source ///reference of a borg if we're downloading a log, or null if not.
var/DL_progress = -1 ///Progress of current download, 0 to 100, -1 for no current download
/datum/computer_file/program/borg_monitor/Destroy()
loglist = null
DL_source = null
return ..()
/datum/computer_file/program/borg_monitor/kill_program(forced = FALSE)
loglist = null //Not everything is saved if you close an app
DL_source = null
DL_progress = 0
return ..()
/datum/computer_file/program/borg_monitor/run_emag()
if(emagged)
return FALSE
emagged = TRUE
return TRUE
/datum/computer_file/program/borg_monitor/tap(atom/A, mob/living/user, params)
var/mob/living/silicon/robot/borgo = A
if(!istype(borgo) || !borgo.modularInterface)
return FALSE
DL_source = borgo
DL_progress = 0
var/username = "unknown user"
var/obj/item/card/id/stored_card = computer.GetID()
if(istype(stored_card) && stored_card.registered_name)
username = "user [stored_card.registered_name]"
to_chat(borgo, span_userdanger("Request received from [username] for the system log file. Upload in progress."))//Damning evidence may be contained, so warn the borg
borgo.logevent("File request by [username]: /var/logs/syslog")
return TRUE
/datum/computer_file/program/borg_monitor/process_tick()
if(!DL_source)
DL_progress = -1
return
var/turf/here = get_turf(computer)
var/turf/there = get_turf(DL_source)
if(!here.Adjacent(there))//If someone walked away, cancel the download
to_chat(DL_source, span_danger("Log upload failed: general connection error."))//Let the borg know the upload stopped
DL_source = null
DL_progress = -1
return
if(DL_progress == 100)
if(!DL_source || !DL_source.modularInterface) //sanity check, in case the borg or their modular tablet poofs somehow
loglist = list("System log of unit [DL_source.name]")
loglist += "Error -- Download corrupted."
else
loglist = DL_source.modularInterface.borglog.Copy()
loglist.Insert(1,"System log of unit [DL_source.name]")
DL_progress = -1
DL_source = null
for(var/datum/tgui/window in SStgui.open_uis_by_src[REF(src)])
window.send_full_update()
return
DL_progress += 25
/datum/computer_file/program/borg_monitor/ui_data(mob/user)
var/list/data = get_header_data()
@@ -32,15 +97,22 @@
var/list/cyborg_data = list(
name = R.name,
integ = round((R.health + 100) / 2), //mob heath is -100 to 100, we want to scale that to 0 - 100
locked_down = R.locked_down,
status = R.stat,
shell_discon = shell,
charge = R.cell ? round(R.cell.percent()) : null,
module = R.module ? "[R.module.name] Module" : "No Module Detected",
module = R.module ? "[R.module.name] Model" : "No Model Detected",
upgrades = upgrade,
ref = REF(R)
)
data["cyborgs"] += list(cyborg_data)
data["DL_progress"] = DL_progress
return data
/datum/computer_file/program/borg_monitor/ui_static_data(mob/user)
var/list/data = list()
data["borglog"] = loglist
return data
/datum/computer_file/program/borg_monitor/ui_act(action, params)
@@ -57,16 +129,16 @@
if(!ID)
return
if(R.stat == DEAD) //Dead borgs will listen to you no longer
to_chat(usr, "<span class='warn'>Error -- Could not open a connection to unit:[R]</span>")
to_chat(usr, span_warning("Error -- Could not open a connection to unit:[R]"))
var/message = stripped_input(usr, message = "Enter message to be sent to remote cyborg.", title = "Send Message")
if(!message)
return
to_chat(R, "<br><br><span class='notice'>Message from [ID] -- \"[message]\"</span><br>")
to_chat(R, "<br><br>[span_notice("Message from [ID] -- \"[message]\"")]<br>")
to_chat(usr, "Message sent to [R]: [message]")
R.logevent("Message from [ID] -- \"[message]\"")
SEND_SOUND(R, 'sound/machines/twobeep_high.ogg')
if(R.connected_ai)
to_chat(R.connected_ai, "<br><br><span class='notice'>Message from [ID] to [R] -- \"[message]\"</span><br>")
to_chat(R.connected_ai, "<br><br>[span_notice("Message from [ID] to [R] -- \"[message]\"")]<br>")
SEND_SOUND(R.connected_ai, 'sound/machines/twobeep_high.ogg')
usr.log_talk(message, LOG_PDA, tag="Cyborg Monitor Program: ID name \"[ID]\" to [R]")
@@ -82,12 +154,15 @@
/datum/computer_file/program/borg_monitor/proc/checkID()
var/obj/item/card/id/ID = computer.GetID()
if(!ID)
if(emagged)
return "STDERR:UNDF"
return FALSE
return ID.registered_name
/datum/computer_file/program/borg_monitor/syndicate
filename = "roboverlord"
filedesc = "Roboverlord"
category = PROGRAM_CATEGORY_ROBO
ui_header = "borg_mon.gif"
program_icon_state = "generic"
extended_desc = "This program allows for remote monitoring of mission-assigned cyborgs."
@@ -97,6 +172,9 @@
transfer_access = null
tgui_id = "NtosCyborgRemoteMonitorSyndicate"
/datum/computer_file/program/borg_monitor/syndicate/run_emag()
return FALSE
/datum/computer_file/program/borg_monitor/syndicate/evaluate_borg(mob/living/silicon/robot/R)
if((get_turf(computer)).z != (get_turf(R)).z)
return FALSE
@@ -1,6 +1,7 @@
/datum/computer_file/program/bounty_board
filename = "bountyboard"
filedesc = "Bounty Board Request Network"
category = PROGRAM_CATEGORY_SUPL
program_icon_state = "bountyboard"
extended_desc = "A multi-platform network for placing requests across the station, with payment across the network being possible.."
requires_ntnet = TRUE
@@ -1,41 +1,50 @@
/datum/computer_file/program/budgetorders
filename = "orderapp"
filedesc = "NT IRN"
// category = PROGRAM_CATEGORY_SUPL
category = PROGRAM_CATEGORY_SUPL
program_icon_state = "request"
extended_desc = "Nanotrasen Internal Requisition Network interface for supply purchasing using a department budget account."
requires_ntnet = TRUE
transfer_access = ACCESS_HEADS
usage_flags = PROGRAM_LAPTOP | PROGRAM_TABLET
size = 20
tgui_id = "NtosCargo"
///Are you actually placing orders with it?
var/requestonly = TRUE
///Can the tablet see or buy illegal stuff?
var/contraband_view = FALSE
var/contraband = FALSE
///Is it being bought from a personal account, or is it being done via a budget/cargo?
var/self_paid = FALSE
///Can this console approve purchase requests?
var/can_approve_requests = FALSE
///What do we say when the shuttle moves with living beings on it.
var/safety_warning = "For safety reasons, the automated supply shuttle \
cannot transport live organisms, human remains, classified nuclear weaponry, \
homing beacons or machinery housing any form of artificial intelligence."
var/safety_warning = "For safety and ethical reasons, the automated supply shuttle \
cannot transport live organisms, human remains, classified nuclear weaponry, mail, \
homing beacons, unstable eigenstates or machinery housing any form of artificial intelligence."
///If you're being raided by pirates, what do you tell the crew?
var/blockade_warning = "Bluespace instability detected. Shuttle movement impossible."
///The name of the shuttle template being used as the cargo shuttle. 'supply' is default and contains critical code. Don't change this unless you know what you're doing.
var/cargo_shuttle = "supply"
///The docking port called when returning to the station.
var/docking_home = "supply_home"
///The docking port called when leaving the station.
var/docking_away = "supply_away"
///If this console can loan the cargo shuttle. Set to false to disable.
var/stationcargo = TRUE
///The account this console processes and displays. Independent from the account the shuttle processes.
var/cargo_account = ACCOUNT_CAR
/datum/computer_file/program/budgetorders/proc/get_export_categories()
. = EXPORT_CARGO
/datum/computer_file/program/budgetorders/run_emag()
if(!contraband_view)
contraband_view = TRUE
if(!contraband)
contraband = TRUE
return TRUE
/datum/computer_file/program/budgetorders/proc/is_visible_pack(mob/user, paccess_to_check, list/access, contraband)
if(issilicon(user)) //Borgs can't buy things.
return FALSE
if((computer.obj_flags & EMAGGED) || contraband_view)
if(computer.obj_flags & EMAGGED)
return TRUE
else if(contraband) //Hide contrband when non-emagged.
return FALSE
@@ -64,11 +73,11 @@
. = ..()
var/list/data = get_header_data()
data["location"] = SSshuttle.supply.getStatusText()
var/datum/bank_account/buyer = SSeconomy.get_dep_account(ACCOUNT_CAR)
var/datum/bank_account/buyer = SSeconomy.get_dep_account(cargo_account)
var/obj/item/computer_hardware/card_slot/card_slot = computer.all_components[MC_CARD]
var/obj/item/card/id/id_card = card_slot?.GetID()
if(id_card?.registered_account)
if(ACCESS_HEADS in id_card.access)
if((ACCESS_HEADS in id_card.access) || (ACCESS_QM in id_card.access))
requestonly = FALSE
buyer = SSeconomy.get_dep_account(id_card.registered_account.account_job.paycheck_department)
can_approve_requests = TRUE
@@ -85,14 +94,14 @@
data["supplies"] = list()
for(var/pack in SSshuttle.supply_packs)
var/datum/supply_pack/P = SSshuttle.supply_packs[pack]
if(!is_visible_pack(usr, P.access , null, P.contraband))
if(!is_visible_pack(usr, P.access , null, P.contraband) || P.hidden)
continue
if(!data["supplies"][P.group])
data["supplies"][P.group] = list(
"name" = P.group,
"packs" = list()
)
if(((P.hidden || P.contraband) && !contraband_view) || (P.special && !P.special_enabled) || P.DropPodOnly)
if((P.hidden && (P.contraband && !contraband) || (P.special && !P.special_enabled) || P.DropPodOnly))
continue
data["supplies"][P.group]["packs"] += list(list(
"name" = P.name,
@@ -105,7 +114,7 @@
//Data regarding the User's capability to buy things.
data["has_id"] = id_card
data["away"] = SSshuttle.supply.getDockedId() == "supply_away"
data["away"] = SSshuttle.supply.getDockedId() == docking_away
data["self_paid"] = self_paid
data["docked"] = SSshuttle.supply.mode == SHUTTLE_IDLE
data["loan"] = !!SSshuttle.shuttle_loan
@@ -153,15 +162,15 @@
if(SSshuttle.supplyBlocked)
computer.say(blockade_warning)
return
if(SSshuttle.supply.getDockedId() == "supply_home")
if(SSshuttle.supply.getDockedId() == docking_home)
SSshuttle.supply.export_categories = get_export_categories()
SSshuttle.moveShuttle("supply", "supply_away", TRUE)
SSshuttle.moveShuttle(cargo_shuttle, docking_away, TRUE)
computer.say("The supply shuttle is departing.")
computer.investigate_log("[key_name(usr)] sent the supply shuttle away.", INVESTIGATE_CARGO)
else
computer.investigate_log("[key_name(usr)] called the supply shuttle.", INVESTIGATE_CARGO)
computer.say("The supply shuttle has been called and will arrive in [SSshuttle.supply.timeLeft(600)] minutes.")
SSshuttle.moveShuttle("supply", "supply_home", TRUE)
SSshuttle.moveShuttle(cargo_shuttle, docking_home, TRUE)
. = TRUE
if("loan")
if(!SSshuttle.shuttle_loan)
@@ -171,7 +180,9 @@
return
else if(SSshuttle.supply.mode != SHUTTLE_IDLE)
return
else if(SSshuttle.supply.getDockedId() != "supply_away")
else if(SSshuttle.supply.getDockedId() != docking_away)
return
else if(stationcargo != TRUE)
return
else
SSshuttle.shuttle_loan.loan_shuttle()
@@ -184,7 +195,7 @@
var/datum/supply_pack/pack = SSshuttle.supply_packs[id]
if(!istype(pack))
return
if(((pack.hidden || pack.contraband) && !contraband_view) || pack.DropPodOnly)
if((pack.hidden && (pack.contraband && !contraband) || pack.DropPodOnly))
return
var/name = "*None Provided*"
@@ -273,7 +284,7 @@
self_paid = !self_paid
. = TRUE
if(.)
post_signal("supply")
post_signal(cargo_shuttle)
/datum/computer_file/program/budgetorders/proc/post_signal(command)
@@ -9,6 +9,7 @@
/datum/computer_file/program/card_mod
filename = "plexagonidwriter"
filedesc = "Plexagon Access Management"
category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for programming employee ID cards to access parts of the station."
transfer_access = ACCESS_HEADS
@@ -1,6 +1,7 @@
/datum/computer_file/program/shipping
filename = "shipping"
filedesc = "GrandArk Exporter"
category = PROGRAM_CATEGORY_SUPL
program_icon_state = "shipping"
extended_desc = "A combination printer/scanner app that enables modular computers to print barcodes for easy scanning and shipping."
size = 6
@@ -8,8 +9,12 @@
program_icon = "tags"
///Account used for creating barcodes.
var/datum/bank_account/payments_acc
///The amount which the tagger will receive for the sale.
var/percent_cut = 20
///The person who tagged this will receive the sale value multiplied by this number.
var/cut_multiplier = 0.5
///Maximum value for cut_multiplier.
var/cut_max = 0.5
///Minimum value for cut_multiplier.
var/cut_min = 0.01
/datum/computer_file/program/shipping/ui_data(mob/user)
var/list/data = get_header_data()
@@ -22,7 +27,7 @@
data["paperamt"] = printer ? "[printer.stored_paper] / [printer.max_paper]" : null
data["card_owner"] = card_slot?.stored_card ? id_card.registered_name : "No Card Inserted."
data["current_user"] = payments_acc ? payments_acc.account_holder : null
data["barcode_split"] = percent_cut
data["barcode_split"] = cut_multiplier * 100
return data
/datum/computer_file/program/shipping/ui_act(action, list/params)
@@ -54,20 +59,20 @@
if("resetid")
payments_acc = null
if("setsplit")
var/potential_cut = input("How much would you like to payout to the registered card?","Percentage Profit") as num|null
percent_cut = potential_cut ? clamp(round(potential_cut, 1), 1, 50) : 20
var/potential_cut = input("How much would you like to pay out to the registered card?","Percentage Profit ([round(cut_min*100)]% - [round(cut_max*100)]%)") as num|null
cut_multiplier = potential_cut ? clamp(round(potential_cut/100, cut_min), cut_min, cut_max) : initial(cut_multiplier)
if("print")
if(!printer)
to_chat(usr, "<span class='notice'>Hardware error: A printer is required to print barcodes.</span>")
to_chat(usr, span_notice("Hardware error: A printer is required to print barcodes."))
return
if(printer.stored_paper <= 0)
to_chat(usr, "<span class='notice'>Hardware error: Printer is out of paper.</span>")
to_chat(usr, span_notice("Hardware error: Printer is out of paper."))
return
if(!payments_acc)
to_chat(usr, "<span class='notice'>Software error: Please set a current user first.</span>")
to_chat(usr, span_notice("Software error: Please set a current user first."))
return
var/obj/item/barcode/barcode = new /obj/item/barcode(get_turf(ui_host()))
barcode.payments_acc = payments_acc
barcode.percent_cut = percent_cut
barcode.cut_multiplier = cut_multiplier
printer.stored_paper--
to_chat(usr, "<span class='notice'>The computer prints out a barcode.</span>")
to_chat(usr, span_notice("The computer prints out a barcode."))
@@ -1,6 +1,7 @@
/datum/computer_file/program/crew_manifest
filename = "plexagoncrew"
filedesc = "Plexagon Crew List"
category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and printing the current crew manifest"
transfer_access = ACCESS_HEADS
@@ -44,7 +45,7 @@
[GLOB.data_core ? GLOB.data_core.get_manifest() : ""]
"}
if(!printer.print_text(contents,text("crew manifest ([])", STATION_TIME_TIMESTAMP("hh:mm:ss", world.time))))
to_chat(usr, "<span class='notice'>Hardware error: Printer was unable to print the file. It may be out of paper.</span>")
to_chat(usr, span_notice("Hardware error: Printer was unable to print the file. It may be out of paper."))
return
else
computer.visible_message("<span class='notice'>\The [computer] prints out a paper.</span>")
computer.visible_message(span_notice("\The [computer] prints out a paper."))
@@ -38,14 +38,27 @@
return
RHDD.remove_file(file)
return TRUE
if("PRG_rename")
if("PRG_renamefile")
if(!HDD)
return
var/datum/computer_file/file = HDD.find_file_by_name(params["name"])
if(!file)
return
var/newname = params["new_name"]
if(!newname)
var/newname = reject_bad_name(params["new_name"])
if(!newname || newname != params["new_name"])
playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE)
return
file.filename = newname
return TRUE
if("PRG_usbrenamefile")
if(!RHDD)
return
var/datum/computer_file/file = RHDD.find_file_by_name(params["name"])
if(!file)
return
var/newname = reject_bad_name(params["new_name"])
if(!newname || newname != params["new_name"])
playsound(computer, 'sound/machines/terminal_error.ogg', 25, FALSE)
return
file.filename = newname
return TRUE
@@ -1,6 +1,10 @@
/// The time since the last job opening was created
// GLOBAL_VAR_INIT(time_last_changed_position, 0)
/datum/computer_file/program/job_management
filename = "plexagoncore"
filedesc = "Plexagon HR Core"
category = PROGRAM_CATEGORY_CREW
program_icon_state = "id"
extended_desc = "Program for viewing and changing job slot avalibility."
transfer_access = ACCESS_HEADS
@@ -14,6 +18,7 @@
var/list/blacklisted = list(
"AI",
"Assistant",
"Prisoner",
"Cyborg",
"Captain",
"Head of Personnel",
@@ -35,21 +40,25 @@
change_position_cooldown = CONFIG_GET(number/id_console_jobslot_delay)
/datum/computer_file/program/job_management/proc/can_open_job(datum/job/job)
if(!(job?.title in blacklisted))
if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
return TRUE
if(job?.title in blacklisted)
return FALSE
if((job.total_positions <= length(GLOB.player_list) * (max_relative_positions / 100)))
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] < 0))
return TRUE
return FALSE
/datum/computer_file/program/job_management/proc/can_close_job(datum/job/job)
if(!(job?.title in blacklisted))
if(job.total_positions > job.current_positions)
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
return TRUE
if(job?.title in blacklisted)
return FALSE
if(job.total_positions > job.current_positions)
var/delta = (world.time / 10) - GLOB.time_last_changed_position
if((change_position_cooldown < delta) || (opened_positions[job.title] > 0))
return TRUE
return FALSE
/datum/computer_file/program/job_management/ui_act(action, params, datum/tgui/ui)
. = ..()
if(.)
@@ -68,9 +77,10 @@
if(!j || !can_open_job(j))
return
if(opened_positions[edit_job_target] >= 0)
GLOB.time_last_changed_position = world.time / 10 // global cd
GLOB.time_last_changed_position = world.time / 10
j.total_positions++
opened_positions[edit_job_target]++
log_game("[key_name(usr)] opened a [j.title] job position, for a total of [j.total_positions] open job slots.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_close_job")
@@ -83,21 +93,23 @@
GLOB.time_last_changed_position = world.time / 10
j.total_positions--
opened_positions[edit_job_target]--
log_game("[key_name(usr)] closed a [j.title] job position, leaving [j.total_positions] open job slots.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
if("PRG_priority")
if(length(SSjob.prioritized_jobs) >= 5)
return
var/priority_target = params["target"]
var/datum/job/j = SSjob.GetJob(priority_target)
if(!j)
if(!j || (j?.title in blacklisted))
return
if(j.total_positions <= j.current_positions)
return
if(j in SSjob.prioritized_jobs)
SSjob.prioritized_jobs -= j
else
SSjob.prioritized_jobs += j
if(length(SSjob.prioritized_jobs) < 5)
SSjob.prioritized_jobs += j
else
computer.say("Error: CentCom employment protocols restrict prioritising more than 5 jobs.")
playsound(computer, 'sound/machines/terminal_prompt_confirm.ogg', 50, FALSE)
return TRUE
@@ -22,6 +22,13 @@
var/emagged = FALSE
var/list/main_repo
var/list/antag_repo
var/list/show_categories = list(
PROGRAM_CATEGORY_CREW,
PROGRAM_CATEGORY_ENGI,
PROGRAM_CATEGORY_ROBO,
PROGRAM_CATEGORY_SUPL,
PROGRAM_CATEGORY_MISC,
)
/datum/computer_file/program/ntnetdownload/run_program()
. = ..()
@@ -146,39 +153,29 @@
var/obj/item/computer_hardware/hard_drive/hard_drive = my_computer.all_components[MC_HDD]
data["disk_size"] = hard_drive.max_capacity
data["disk_used"] = hard_drive.used_capacity
var/list/all_entries[0]
for(var/A in main_repo)
var/datum/computer_file/program/P = A
// Only those programs our user can run will show in the list
if(hard_drive.find_file_by_name(P.filename))
continue
all_entries.Add(list(list(
data["emagged"] = emagged
var/list/repo = antag_repo | main_repo
var/list/program_categories = list()
for(var/I in repo)
var/datum/computer_file/program/P = I
if(!(P.category in program_categories))
program_categories.Add(P.category)
data["programs"] += list(list(
"icon" = P.program_icon,
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
"compatibility" = check_compatibility(P),
"category" = P.category,
"installed" = !!hard_drive.find_file_by_name(P.filename),
"compatible" = check_compatibility(P),
"size" = P.size,
"access" = P.can_run(user,transfer = 1, access = access)
)))
data["hackedavailable"] = FALSE
if(emagged) // If we are running on emagged computer we have access to some "bonus" software
var/list/hacked_programs[0]
for(var/S in antag_repo)
var/datum/computer_file/program/P = S
if(hard_drive.find_file_by_name(P.filename))
continue
data["hackedavailable"] = TRUE
hacked_programs.Add(list(list(
"filename" = P.filename,
"filedesc" = P.filedesc,
"fileinfo" = P.extended_desc,
"compatibility" = check_compatibility(P),
"size" = P.size,
"access" = TRUE,
)))
data["hacked_programs"] = hacked_programs
"access" = emagged && P.available_on_syndinet ? TRUE : P.can_run(user,transfer = 1, access = access),
"verifiedsource" = P.available_on_ntnet,
))
data["downloadable_programs"] = all_entries
data["categories"] = show_categories & program_categories
return data
@@ -186,8 +183,8 @@
var/hardflag = computer.hardware_flag
if(P?.is_supported_by_hardware(hardflag,0))
return "Compatible"
return "Incompatible!"
return TRUE
return FALSE
/datum/computer_file/program/ntnetdownload/kill_program(forced)
abort_file_download()

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