diff --git a/code/__DEFINES/combat.dm b/code/__DEFINES/combat.dm index 8aab6175..8acc1d8f 100644 --- a/code/__DEFINES/combat.dm +++ b/code/__DEFINES/combat.dm @@ -185,6 +185,12 @@ GLOBAL_LIST_INIT(shove_disarming_types, typecacheof(list( //We will round to this value in damage calculations. #define DAMAGE_PRECISION 0.1 +//bullet_act() return values +#define BULLET_ACT_HIT "HIT" //It's a successful hit, whatever that means in the context of the thing it's hitting. +#define BULLET_ACT_BLOCK "BLOCK" //It's a blocked hit, whatever that means in the context of the thing it's hitting. +#define BULLET_ACT_FORCE_PIERCE "PIERCE" //It pierces through the object regardless of the bullet being piercing by default. +#define BULLET_ACT_TURF "TURF" //It hit us but it should hit something on the same turf too. Usually used for turfs. + //items total mass, used to calculate their attacks' stamina costs. If not defined, the cost will be (w_class * 1.25) #define TOTAL_MASS_TINY_ITEM 1.25 #define TOTAL_MASS_SMALL_ITEM 2.5 diff --git a/code/__DEFINES/is_helpers.dm b/code/__DEFINES/is_helpers.dm index 83d603df..5bcee19f 100644 --- a/code/__DEFINES/is_helpers.dm +++ b/code/__DEFINES/is_helpers.dm @@ -1,10 +1,16 @@ // simple is_type and similar inline helpers +#if DM_VERSION < 513 #define islist(L) (istype(L, /list)) +#endif #define in_range(source, user) (get_dist(source, user) <= 1 && (get_step(source, 0)?:z) == (get_step(user, 0)?:z)) +#if DM_VERSION < 513 #define ismovableatom(A) (istype(A, /atom/movable)) +#else +#define ismovableatom(A) ismovable(A) +#endif #define isatom(A) (isloc(A)) diff --git a/code/__DEFINES/logging.dm b/code/__DEFINES/logging.dm index 8548b2be..cabc67f6 100644 --- a/code/__DEFINES/logging.dm +++ b/code/__DEFINES/logging.dm @@ -35,6 +35,7 @@ #define LOG_GAME (1 << 12) #define LOG_ADMIN_PRIVATE (1 << 13) #define LOG_ASAY (1 << 14) +#define LOG_CLONING (1 << 15) //Individual logging panel pages #define INDIVIDUAL_ATTACK_LOG (LOG_ATTACK) diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm index ec719794..0edb4239 100644 --- a/code/__DEFINES/machines.dm +++ b/code/__DEFINES/machines.dm @@ -100,6 +100,10 @@ #define NUKE_ON_TIMING 2 #define NUKE_ON_EXPLODING 3 +//cloning defines. These are flags. +#define CLONING_SUCCESS (1<<0) +#define CLONING_DELETE_RECORD (1<<1) + //these flags are used to tell the DNA modifier if a plant gene cannot be extracted or modified. #define PLANT_GENE_REMOVABLE (1<<0) #define PLANT_GENE_EXTRACTABLE (1<<1) diff --git a/code/__DEFINES/maths.dm b/code/__DEFINES/maths.dm index d344f7f0..402c3dc1 100644 --- a/code/__DEFINES/maths.dm +++ b/code/__DEFINES/maths.dm @@ -30,7 +30,11 @@ // round() acts like floor(x, 1) by default but can't handle other values #define FLOOR(x, y) ( round((x) / (y)) * (y) ) +#if DM_VERSION < 513 #define CLAMP(CLVALUE,CLMIN,CLMAX) ( max( (CLMIN), min((CLVALUE), (CLMAX)) ) ) +#else +#define CLAMP(CLVALUE,CLMIN,CLMAX) clamp(CLVALUE,CLMIN,CLMAX) +#endif // Similar to clamp but the bottom rolls around to the top and vice versa. min is inclusive, max is exclusive #define WRAP(val, min, max) ( min == max ? min : (val) - (round(((val) - (min))/((max) - (min))) * ((max) - (min))) ) @@ -39,7 +43,11 @@ #define MODULUS(x, y) ( (x) - (y) * round((x) / (y)) ) // Tangent +#if DM_VERSION < 513 #define TAN(x) (sin(x) / cos(x)) +#else +#define TAN(x) tan(x) +#endif // Cotangent #define COT(x) (1 / TAN(x)) diff --git a/code/__DEFINES/misc.dm b/code/__DEFINES/misc.dm index 98d630b7..bb6fab9f 100644 --- a/code/__DEFINES/misc.dm +++ b/code/__DEFINES/misc.dm @@ -476,7 +476,7 @@ GLOBAL_LIST_INIT(pda_reskins, list(PDA_SKIN_CLASSIC = 'icons/obj/pda.dmi', PDA_S #define PDAIMG(what) {""} //Filters -#define AMBIENT_OCCLUSION list("type"="drop_shadow","x"=0,"y"=-2,"size"=4,"border"=4,"color"="#04080FAA") +#define AMBIENT_OCCLUSION list("type"="drop_shadow","x"=0,"y"=-2,"size"=4,"color"="#04080FAA") #define EYE_BLUR(size) list("type"="blur", "size"=size) #define GRAVITY_MOTION_BLUR list("type"="motion_blur","x"=0,"y"=0) diff --git a/code/__DEFINES/typeids.dm b/code/__DEFINES/typeids.dm index ae5df258..8bfe6216 100644 --- a/code/__DEFINES/typeids.dm +++ b/code/__DEFINES/typeids.dm @@ -2,7 +2,7 @@ #define TYPEID_NULL "0" #define TYPEID_NORMAL_LIST "f" //helper macros -#define GET_TYPEID(ref) ( ( (lentext(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, lentext(ref)-6) ) ) +#define GET_TYPEID(ref) ( ( (length(ref) <= 10) ? "TYPEID_NULL" : copytext(ref, 4, length(ref)-6) ) ) #define IS_NORMAL_LIST(L) (GET_TYPEID("\ref[L]") == TYPEID_NORMAL_LIST) diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm index 99cb6c54..812ced8d 100644 --- a/code/__HELPERS/_lists.dm +++ b/code/__HELPERS/_lists.dm @@ -514,7 +514,7 @@ used_key_list[input_key] = 1 return input_key -#if DM_VERSION > 512 +#if DM_VERSION > 513 #error Remie said that lummox was adding a way to get a lists #error contents via list.values, if that is true remove this #error otherwise, update the version and bug lummox diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 30740956..b6b4efa0 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -58,6 +58,10 @@ if (CONFIG_GET(flag/log_game)) WRITE_LOG(GLOB.world_game_log, "GAME: [text]") +/proc/log_cloning(text, mob/initiator) + if(CONFIG_GET(flag/log_cloning)) + WRITE_LOG(GLOB.world_cloning_log, "CLONING: [text]") + /proc/log_access(text) if (CONFIG_GET(flag/log_access)) WRITE_LOG(GLOB.world_game_log, "ACCESS: [text]") diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 351411ce..402f0c76 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -305,9 +305,9 @@ //is in the other string at the same spot (assuming it is not a replace char). //This is used for fingerprints var/newtext = text - if(lentext(text) != lentext(compare)) + if(length(text) != length(compare)) return 0 - for(var/i = 1, i < lentext(text), i++) + for(var/i = 1, i < length(text), i++) var/a = copytext(text,i,i+1) var/b = copytext(compare,i,i+1) //if it isn't both the same letter, or if they are both the replacement character @@ -327,7 +327,7 @@ if(!text || !character) return 0 var/count = 0 - for(var/i = 1, i <= lentext(text), i++) + for(var/i = 1, i <= length(text), i++) var/a = copytext(text,i,i+1) if(a == character) count++ @@ -608,8 +608,8 @@ GLOBAL_LIST_INIT(binary, list("0","1")) continue var/buffer = "" var/early_culling = TRUE - for(var/pos = 1, pos <= lentext(string), pos++) - var/let = copytext(string, pos, (pos + 1) % lentext(string)) + for(var/pos = 1, pos <= length(string), pos++) + var/let = copytext(string, pos, (pos + 1) % length(string)) if(early_culling && !findtext(let,GLOB.is_alphanumeric)) continue early_culling = FALSE @@ -617,9 +617,9 @@ GLOBAL_LIST_INIT(binary, list("0","1")) if(!findtext(buffer,GLOB.is_alphanumeric)) continue var/punctbuffer = "" - var/cutoff = lentext(buffer) - for(var/pos = lentext(buffer), pos >= 0, pos--) - var/let = copytext(buffer, pos, (pos + 1) % lentext(buffer)) + var/cutoff = length(buffer) + for(var/pos = length(buffer), pos >= 0, pos--) + var/let = copytext(buffer, pos, (pos + 1) % length(buffer)) if(findtext(let,GLOB.is_alphanumeric)) break if(findtext(let,GLOB.is_punctuation)) @@ -629,8 +629,8 @@ GLOBAL_LIST_INIT(binary, list("0","1")) var/exclaim = FALSE var/question = FALSE var/periods = 0 - for(var/pos = lentext(punctbuffer), pos >= 0, pos--) - var/punct = copytext(punctbuffer, pos, (pos + 1) % lentext(punctbuffer)) + for(var/pos = length(punctbuffer), pos >= 0, pos--) + var/punct = copytext(punctbuffer, pos, (pos + 1) % length(punctbuffer)) if(!exclaim && findtext(punct,"!")) exclaim = TRUE if(!question && findtext(punct,"?")) @@ -652,7 +652,7 @@ GLOBAL_LIST_INIT(binary, list("0","1")) buffer = copytext(buffer, 1, cutoff) + punctbuffer if(!findtext(buffer,GLOB.is_alphanumeric)) continue - if(!buffer || lentext(buffer) > 280 || lentext(buffer) <= cullshort || buffer in accepted) + if(!buffer || length(buffer) > 280 || length(buffer) <= cullshort || buffer in accepted) continue accepted += buffer diff --git a/code/__HELPERS/text_vr.dm b/code/__HELPERS/text_vr.dm index 64e13ef6..9be806fc 100644 --- a/code/__HELPERS/text_vr.dm +++ b/code/__HELPERS/text_vr.dm @@ -9,8 +9,8 @@ return t proc/TextPreview(var/string,var/len=40) - if(lentext(string) <= len) - if(!lentext(string)) + if(length(string) <= len) + if(!length(string)) return "\[...\]" else return string diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 7e078184..1f8db38f 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -451,10 +451,15 @@ Turf and target are separate in case you want to teleport some distance from a t var/y = min(world.maxy, max(1, A.y + dy)) return locate(x,y,A.z) +#if DM_VERSION > 513 +#warn if you're getting this warning it means 513 is stable +#warn and you should remove this tidbit +#endif +#if DM_VERSION < 513 /proc/arctan(x) var/y=arcsin(x/sqrt(1+x*x)) return y - +#endif /* Gets all contents of contents and returns them all in a list. */ diff --git a/code/_globalvars/logging.dm b/code/_globalvars/logging.dm index deffd25b..bf4ff55f 100644 --- a/code/_globalvars/logging.dm +++ b/code/_globalvars/logging.dm @@ -26,6 +26,8 @@ GLOBAL_VAR(query_debug_log) GLOBAL_PROTECT(query_debug_log) GLOBAL_VAR(world_job_debug_log) GLOBAL_PROTECT(world_job_debug_log) +GLOBAL_VAR(world_cloning_log) +GLOBAL_PROTECT(world_cloning_log) GLOBAL_LIST_EMPTY(bombers) GLOBAL_PROTECT(bombers) diff --git a/code/controllers/configuration/entries/general.dm b/code/controllers/configuration/entries/general.dm index 9a1a083b..ccfa668b 100644 --- a/code/controllers/configuration/entries/general.dm +++ b/code/controllers/configuration/entries/general.dm @@ -39,6 +39,8 @@ /datum/config_entry/flag/log_game // log game events +/datum/config_entry/flag/log_cloning // log cloning actions. + /datum/config_entry/flag/log_vote // log voting /datum/config_entry/flag/log_whisper // log client whisper diff --git a/code/datums/dna.dm b/code/datums/dna.dm index 43ec9e54..466a5406 100644 --- a/code/datums/dna.dm +++ b/code/datums/dna.dm @@ -371,7 +371,7 @@ updateappearance(icon_update=0) if(LAZYLEN(mutation_index)) - dna.mutation_index = mutation_index + dna.mutation_index = mutation_index.Copy() domutcheck() if(mrace || newfeatures || ui) diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 855ede0e..e4f9d292 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -74,6 +74,23 @@ if(heal_level > 100) heal_level = 100 +/obj/machinery/clonepod/attack_hand(mob/user) + . = ..() + if(.) + return + user.examinate(src) + +/obj/machinery/clonepod/attack_ai(mob/user) + return attack_hand(user) + +/obj/machinery/clonepod/examine(mob/user) + . = ..() + . += "The linking device can be scanned with a multitool." + if(in_range(user, src) || isobserver(user)) + . += "The status display reads: Cloning speed at [speed_coeff*50]%.
Predicted amount of cellular damage: [100-heal_level]%.
" + if(efficiency > 5) + . += "Pod has been upgraded to support autoprocessing and apply beneficial mutations." + //The return of data disks?? Just for transferring between genetics machine/cloning machine. //TO-DO: Make the genetics machine accept them. /obj/item/disk/data @@ -129,41 +146,42 @@ return examine(user) //Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(ckey, clonename, ui, mutation_index, mindref, datum/species/mrace, list/features, factions, list/quirks) +/obj/machinery/clonepod/proc/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance, list/traumas, empty) if(panel_open) return FALSE if(mess || attempting) return FALSE - clonemind = locate(mindref) in SSticker.minds - if(!istype(clonemind)) //not a mind - return FALSE - if(!QDELETED(clonemind.current)) - if(clonemind.current.stat != DEAD) //mind is associated with a non-dead body + if(!empty) //Doesn't matter if we're just making a copy + clonemind = locate(mindref) in SSticker.minds + if(!istype(clonemind)) //not a mind return FALSE - if(clonemind.current.suiciding) // Mind is associated with a body that is suiciding. +// if(clonemind.last_death != last_death) //The soul has advanced, the record has not. +// return FALSE + if(!QDELETED(clonemind.current)) + if(clonemind.current.stat != DEAD) //mind is associated with a non-dead body + return NONE + if(clonemind.current.suiciding) // Mind is associated with a body that is suiciding. + return NONE + if(!clonemind.active) + // get_ghost() will fail if they're unable to reenter their body + var/mob/dead/observer/G = clonemind.get_ghost() + if(!G) + return NONE + if(G.suiciding) // The ghost came from a body that is suiciding. + return NONE + if(clonemind.damnation_type) //Can't clone the damned. + INVOKE_ASYNC(src, .proc/horrifyingsound) + mess = TRUE + icon_state = "pod_g" + update_icon() return FALSE - if(clonemind.active) //somebody is using that mind - if( ckey(clonemind.key)!=ckey ) - return FALSE - else - // get_ghost() will fail if they're unable to reenter their body - var/mob/dead/observer/G = clonemind.get_ghost() - if(!G) - return FALSE - if(G.suiciding) // The ghost came from a body that is suiciding. - return FALSE - if(clonemind.damnation_type) //Can't clone the damned. - INVOKE_ASYNC(src, .proc/horrifyingsound) - mess = TRUE - update_icon() - return FALSE attempting = TRUE //One at a time!! countdown.start() var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.hardset_dna(ui, mutation_index, H.real_name, null, mrace, features) + H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features) if(prob(50 - efficiency*10)) //Chance to give a bad mutation. H.easy_randmut(NEGATIVE+MINOR_NEGATIVE) //100% bad mutation. Can be cured with mutadone. @@ -184,15 +202,16 @@ ADD_TRAIT(H, TRAIT_NOCRITDAMAGE, "cloning") H.Unconscious(80) - clonemind.transfer_to(H) + if(!empty) + clonemind.transfer_to(H) - if(grab_ghost_when == CLONER_FRESH_CLONE) - H.grab_ghost() - to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") + if(grab_ghost_when == CLONER_FRESH_CLONE) + H.grab_ghost() + to_chat(H, "Consciousness slowly creeps over you as your body regenerates.
So this is what cloning feels like?
") - if(grab_ghost_when == CLONER_MATURE_CLONE) - H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost - to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") + if(grab_ghost_when == CLONER_MATURE_CLONE) + H.ghostize(TRUE) //Only does anything if they were still in their old body and not already a ghost + to_chat(H.get_ghost(TRUE), "Your body is beginning to regenerate in a cloning pod. You will become conscious when it is complete.") if(H) H.faction |= factions @@ -381,6 +400,7 @@ unattached_flesh.Cut() occupant = null + clonemind = null /obj/machinery/clonepod/proc/malfunction() var/mob/living/mob_occupant = occupant @@ -391,7 +411,7 @@ mess = TRUE maim_clone(mob_occupant) //Remove every bit that's grown back so far to drop later, also destroys bits that haven't grown yet update_icon() - if(mob_occupant.mind != clonemind) + if(clonemind && mob_occupant.mind != clonemind) clonemind.transfer_to(mob_occupant) mob_occupant.grab_ghost() // We really just want to make you suffer. flash_color(mob_occupant, flash_color="#960000", flash_time=100) diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 039152f5..ca5e15d1 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -39,7 +39,7 @@ if(pods) for(var/P in pods) var/obj/machinery/clonepod/pod = P - if(pod.occupant && pod.clonemind == mind) + if(pod.occupant && mind && pod.clonemind == mind) return null if(pod.is_operational() && !(pod.occupant || pod.mess)) return pod @@ -60,6 +60,9 @@ else if(!. && pod.is_operational() && !(pod.occupant || pod.mess) && pod.efficiency > 5) . = pod +/proc/grow_clone_from_record(obj/machinery/clonepod/pod, datum/data/record/R, empty) + return pod.growclone(R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mindref"], R.fields["last_death"], R.fields["blood_type"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["quirks"], R.fields["bank_account"], R.fields["traumas"], empty) + /obj/machinery/computer/cloning/process() if(!(scanner && LAZYLEN(pods) && autoprocess)) return @@ -76,8 +79,11 @@ if(pod.occupant) continue //how though? - if(pod.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["features"], R.fields["factions"], R.fields["quirks"])) + var/result = grow_clone_from_record(pod, R) + if(result & CLONING_SUCCESS) temp = "[R.fields["name"]] => Cloning cycle in progress..." + log_cloning("Cloning of [key_name(R.fields["mindref"])] automatically started via autoprocess - [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + if(result & CLONING_DELETE_RECORD) records -= R /obj/machinery/computer/cloning/proc/updatemodules(findfirstcloner) @@ -201,6 +207,7 @@ if(scanner_occupant) dat += "Start Scan" + dat += "Body-Only Scan" dat += "
[src.scanner.locked ? "Unlock Scanner" : "Lock Scanner"]" else dat += "Start Scan" @@ -228,8 +235,11 @@ if (!src.active_record) dat += "Record not found." else - dat += "

[src.active_record.fields["name"]]

" - dat += "Scan ID [src.active_record.fields["id"]] Clone
" + var/body_only = active_record.fields["body_only"] + dat += "

[active_record.fields["name"]][body_only ? " - BODY-ONLY" : ""]

" + dat += "Scan ID [active_record.fields["id"]] \ + [!body_only ? "Clone" : "" ]\ + Empty Clone
" var/obj/item/implant/health/H = locate(src.active_record.fields["imp"]) @@ -297,13 +307,14 @@ else if ((href_list["scan"]) && !isnull(scanner) && scanner.is_operational()) scantemp = "" + var/body_only = href_list["body_only"] loading = 1 src.updateUsrDialog() playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) say("Initiating scan...") spawn(20) - src.scan_occupant(scanner.occupant) + src.scan_occupant(scanner.occupant, usr, body_only) loading = 0 src.updateUsrDialog() @@ -336,9 +347,23 @@ if ((!src.active_record) || (src.menu < 3)) return if (src.menu == 3) //If we are viewing a record, confirm deletion - src.temp = "Delete record?" - src.menu = 4 - playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) + var/has_access = FALSE + if(ishuman(usr)) + var/mob/living/carbon/human/user = usr + var/obj/item/card/id/C = user.get_idcard(TRUE) + if(C) + if(check_access(C)) + has_access = TRUE + if(active_record.fields["body_only"]) //Body-only scans are not as important and can be deleted freely + has_access = TRUE + if(has_access) + temp = "Delete record?" + menu = 4 + playsound(src, 'sound/machines/terminal_prompt.ogg', 50, 0) + else + temp = "Access Denied" + menu = 2 + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else if (src.menu == 4) var/obj/item/card/id/C = usr.get_active_held_item() @@ -400,9 +425,14 @@ else if (href_list["clone"]) var/datum/data/record/C = find_record("id", href_list["clone"], records) + var/empty = href_list["empty"] //Look for that player! They better be dead! if(C) + if(C.fields["body_only"] && !empty) + temp = "Cannot initiate regular cloning with body-only scans." + playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) var/obj/machinery/clonepod/pod = GetAvailablePod() + var/success = FALSE //Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs. if(!LAZYLEN(pods)) temp = "No Clonepods detected." @@ -410,20 +440,31 @@ else if(!pod) temp = "No Clonepods available." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - else if(!CONFIG_GET(flag/revival_cloning)) + else if(!CONFIG_GET(flag/revival_cloning) && !empty) temp = "Unable to initiate cloning cycle." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else if(pod.occupant) temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) - else if(pod.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["features"], C.fields["factions"], C.fields["quirks"])) - temp = "[C.fields["name"]] => Cloning cycle in progress..." - playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) - records.Remove(C) - if(active_record == C) - active_record = null - menu = 1 else + var/result = grow_clone_from_record(pod, C, empty) + if(result & CLONING_SUCCESS) + temp = "[C.fields["name"]] => Cloning cycle in progress..." + playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) + records.Remove(C) + if(active_record == C) + active_record = null + menu = 1 + success = TRUE + if(!empty) + log_cloning("[key_name(usr)] initiated cloning of [key_name(C.fields["mindref"])] via [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + else + log_cloning("[key_name(usr)] initiated EMPTY cloning of [key_name(C.fields["mindref"])] via [src] at [AREACOORD(src)]. Pod: [pod] at [AREACOORD(pod)].") + if(result & CLONING_DELETE_RECORD) + if(active_record == C) + active_record = null + + if(!success) temp = "[C.fields["name"]] => Initialisation failure." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) @@ -439,9 +480,11 @@ src.updateUsrDialog() return -/obj/machinery/computer/cloning/proc/scan_occupant(occupant) +/obj/machinery/computer/cloning/proc/scan_occupant(occupant, mob/M, body_only) var/mob/living/mob_occupant = get_mob_or_brainmob(occupant) var/datum/dna/dna + + // Do not use unless you know what they are. if(ishuman(mob_occupant)) var/mob/living/carbon/C = mob_occupant dna = C.has_dna() @@ -453,7 +496,7 @@ scantemp = "Unable to locate valid genetic data." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return - if(mob_occupant.suiciding || mob_occupant.hellbound) + if(!body_only && (mob_occupant.suiciding || mob_occupant.hellbound)) scantemp = "Subject's brain is not responding to scanning stimuli." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return @@ -461,7 +504,7 @@ scantemp = "Subject no longer contains the fundamental materials required to create a living clone." playsound(src, 'sound/machines/terminal_alert.ogg', 50, 0) return - if ((!mob_occupant.ckey) || (!mob_occupant.client)) + if (!body_only && isnull(mob_occupant.mind)) scantemp = "Mental interface failure." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) return @@ -490,6 +533,7 @@ R.fields["blood_type"] = dna.blood_type R.fields["features"] = dna.features R.fields["factions"] = mob_occupant.faction + R.fields["body_only"] = body_only R.fields["quirks"] = list() for(var/V in mob_occupant.roundstart_quirks) var/datum/quirk/T = V @@ -498,17 +542,30 @@ if (!isnull(mob_occupant.mind)) //Save that mind so traitors can continue traitoring after cloning. R.fields["mind"] = "[REF(mob_occupant.mind)]" - //Add an implant if needed - var/obj/item/implant/health/imp - for(var/obj/item/implant/health/HI in mob_occupant.implants) - imp = HI - break - if(!imp) - imp = new /obj/item/implant/health(mob_occupant) - imp.implant(mob_occupant) - R.fields["imp"] = "[REF(imp)]" + if(!body_only) + //Add an implant if needed + var/obj/item/implant/health/imp + for(var/obj/item/implant/health/HI in mob_occupant.implants) + imp = HI + break + if(!imp) + imp = new /obj/item/implant/health(mob_occupant) + imp.implant(mob_occupant) + R.fields["imp"] = "[REF(imp)]" + var/datum/data/record/old_record = find_record("mindref", REF(mob_occupant.mind), records) + if(body_only) + old_record = find_record("UE", dna.unique_enzymes, records) //Body-only records cannot be identified by mind, so we use the DNA + if(old_record && ((old_record.fields["UI"] != dna.uni_identity) || (!old_record.fields["body_only"]))) //Never overwrite a mind-and-body record if it exists + old_record = null + if(old_record) + records -= old_record + scantemp = "Record updated." + else + scantemp = "Subject successfully scanned." src.records += R + log_cloning("[M ? key_name(M) : "Autoprocess"] added the [body_only ? "body-only " : ""]record of [key_name(mob_occupant)] to [src] at [AREACOORD(src)].") + var/obj/item/circuitboard/computer/cloning/board = circuit board.records = records scantemp = "Subject successfully scanned." diff --git a/code/game/machinery/exp_cloner.dm b/code/game/machinery/exp_cloner.dm index baa7ed27..d5388035 100644 --- a/code/game/machinery/exp_cloner.dm +++ b/code/game/machinery/exp_cloner.dm @@ -9,7 +9,7 @@ internal_radio = FALSE //Start growing a human clone in the pod! -/obj/machinery/clonepod/experimental/growclone(ckey, clonename, ui, se, datum/species/mrace, list/features, factions) +/obj/machinery/clonepod/experimental/growclone(clonename, ui, mutation_index, mindref, last_death, blood_type, datum/species/mrace, list/features, factions, list/quirks, datum/bank_account/insurance) if(panel_open) return FALSE if(mess || attempting) @@ -20,7 +20,7 @@ var/mob/living/carbon/human/H = new /mob/living/carbon/human(src) - H.hardset_dna(ui, se, H.real_name, null, mrace, features) + H.hardset_dna(ui, mutation_index, H.real_name, blood_type, mrace, features) if(efficiency > 2) var/list/unclean_mutations = (GLOB.not_good_mutations|GLOB.bad_mutations) @@ -292,7 +292,7 @@ temp = "Cloning cycle already in progress." playsound(src, 'sound/machines/terminal_prompt_deny.ogg', 50, 0) else - pod.growclone(null, mob_occupant.real_name, dna.uni_identity, dna.mutation_index, clone_species, dna.features, mob_occupant.faction) + pod.growclone(mob_occupant.real_name, dna.uni_identity, dna.mutation_index, null, null, dna.blood_type, clone_species, dna.features, mob_occupant.faction) temp = "[mob_occupant.real_name] => Cloning data sent to pod." playsound(src, 'sound/machines/terminal_prompt_confirm.ogg', 50, 0) diff --git a/code/game/machinery/newscaster.dm b/code/game/machinery/newscaster.dm index c37c607a..6bf9930e 100644 --- a/code/game/machinery/newscaster.dm +++ b/code/game/machinery/newscaster.dm @@ -515,7 +515,7 @@ GLOBAL_LIST_EMPTY(allCasters) if(href_list["set_channel_name"]) channel_name = stripped_input(usr, "Provide a Feed Channel Name", "Network Channel Handler", "", MAX_NAME_LEN) while (findtext(channel_name," ") == 1) - channel_name = copytext(channel_name,2,lentext(channel_name)+1) + channel_name = copytext(channel_name,2,length(channel_name)+1) updateUsrDialog() else if(href_list["set_channel_lock"]) c_locked = !c_locked diff --git a/code/game/machinery/status_display.dm b/code/game/machinery/status_display.dm index 877e320a..675c8bce 100644 --- a/code/game/machinery/status_display.dm +++ b/code/game/machinery/status_display.dm @@ -243,7 +243,7 @@ else line1 = "CARGO" line2 = SSshuttle.supply.getTimerStr() - if(lentext(line2) > CHARS_PER_LINE) + if(length(line2) > CHARS_PER_LINE) line2 = "Error" update_display(line1, line2) diff --git a/code/game/mecha/equipment/tools/work_tools.dm b/code/game/mecha/equipment/tools/work_tools.dm index 9ffd40b1..a2222fa2 100644 --- a/code/game/mecha/equipment/tools/work_tools.dm +++ b/code/game/mecha/equipment/tools/work_tools.dm @@ -461,3 +461,56 @@ //NC.mergeConnectedNetworksOnTurf() last_piece = NC return 1 + +//Dunno where else to put this so shrug +/obj/item/mecha_parts/mecha_equipment/ripleyupgrade + name = "Ripley MK-II Conversion Kit" + desc = "A pressurized canopy attachment kit for an Autonomous Power Loader Unit \"Ripley\" MK-I mecha, to convert it to the slower, but space-worthy MK-II design. This kit cannot be removed, once applied." + icon_state = "ripleyupgrade" + +/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/can_attach(obj/mecha/working/ripley/M) + if(M.type != /obj/mecha/working/ripley) + to_chat(loc, "This conversion kit can only be applied to APLU MK-I models.") + return FALSE + if(M.cargo.len) + to_chat(loc, "[M]'s cargo hold must be empty before this conversion kit can be applied.") + return FALSE + if(!M.maint_access) //non-removable upgrade, so lets make sure the pilot or owner has their say. + to_chat(loc, "[M] must have maintenance protocols active in order to allow this conversion kit.") + return FALSE + if(M.occupant) //We're actualy making a new mech and swapping things over, it might get weird if players are involved + to_chat(loc, "[M] must be unoccupied before this conversion kit can be applied.") + return FALSE + if(!M.cell) //Turns out things break if the cell is missing + to_chat(loc, "The conversion process requires a cell installed.") + return FALSE + return TRUE + +/obj/item/mecha_parts/mecha_equipment/ripleyupgrade/attach(obj/mecha/M) + var/obj/mecha/working/ripley/mkii/N = new /obj/mecha/working/ripley/mkii(get_turf(M),1) + if(!N) + return + QDEL_NULL(N.cell) + if (M.cell) + N.cell = M.cell + M.cell.forceMove(N) + M.cell = null + N.step_energy_drain = M.step_energy_drain //For the scanning module + N.armor = N.armor.setRating(energy = M.armor["energy"]) //for the capacitor + for(var/obj/item/mecha_parts/E in M.contents) +// if(istype(E, /obj/item/mecha_parts/concealed_weapon_bay)) //why is the bay not just a variable change who did this +// E.forceMove(N) + for(var/obj/item/mecha_parts/mecha_equipment/E in M.equipment) //Move the equipment over... + E.detach() + E.attach(N) + M.equipment -= E + N.dna_lock = M.dna_lock + N.maint_access = M.maint_access + N.strafe = M.strafe + N.obj_integrity = M.obj_integrity //This is not a repair tool + if (M.name != "\improper APLU MK-I \"Ripley\"") + N.name = M.name + M.wreckage = 0 + qdel(M) + playsound(get_turf(N),'sound/items/ratchet.ogg',50,1) + return \ No newline at end of file diff --git a/code/game/mecha/mech_fabricator.dm b/code/game/mecha/mech_fabricator.dm index 9fd16a57..46beb3a6 100644 --- a/code/game/mecha/mech_fabricator.dm +++ b/code/game/mecha/mech_fabricator.dm @@ -18,7 +18,9 @@ var/list/queue = list() var/processing_queue = 0 var/screen = "main" + var/link_on_init = TRUE var/temp + var/datum/component/remote_materials/rmat var/list/part_sets = list( "Cyborg", "Ripley", @@ -33,13 +35,11 @@ "Misc" ) -/obj/machinery/mecha_part_fabricator/Initialize() - var/datum/component/material_container/materials = AddComponent(/datum/component/material_container, - list(MAT_METAL, MAT_GLASS, MAT_SILVER, MAT_GOLD, MAT_DIAMOND, MAT_PLASMA, MAT_URANIUM, MAT_BANANIUM, MAT_TITANIUM, MAT_BLUESPACE), 0, - TRUE, /obj/item/stack, CALLBACK(src, .proc/is_insertion_ready), CALLBACK(src, .proc/AfterMaterialInsert)) - materials.precise_insertion = TRUE - stored_research = new - return ..() +/obj/machinery/mecha_part_fabricator/Initialize(mapload) + stored_research = new + rmat = AddComponent(/datum/component/remote_materials, "mechfab", mapload && link_on_init) + RefreshParts() //Recalculating local material sizes if the fab isn't linked + return ..() /obj/machinery/mecha_part_fabricator/RefreshParts() var/T = 0 @@ -47,8 +47,7 @@ //maximum stocking amount (default 300000, 600000 at T4) for(var/obj/item/stock_parts/matter_bin/M in component_parts) T += M.rating - GET_COMPONENT(materials, /datum/component/material_container) - materials.max_amount = (200000 + (T*50000)) + rmat.set_local_size((200000 + (T*50000))) //resources adjustment coefficient (1 -> 0.85 -> 0.7 -> 0.55) T = 1.15 @@ -62,6 +61,12 @@ T += Ml.rating time_coeff = round(initial(time_coeff) - (initial(time_coeff)*(T))/5,0.01) +/obj/machinery/mecha_part_fabricator/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + . += "The status display reads: Storing up to [rmat.local_size] material units.
Material consumption at [component_coeff*100]%.
Build time reduced by [100-time_coeff*100]%." + + /obj/machinery/mecha_part_fabricator/emag_act() if(obj_flags & EMAGGED) @@ -104,16 +109,22 @@ /obj/machinery/mecha_part_fabricator/proc/output_available_resources() var/output - GET_COMPONENT(materials, /datum/component/material_container) - for(var/mat_id in materials.materials) - var/datum/material/M = materials.materials[mat_id] - output += "[M.name]: [M.amount] cm³" - if(M.amount >= MINERAL_MATERIAL_AMOUNT) - output += "- Remove \[1\]" - if(M.amount >= (MINERAL_MATERIAL_AMOUNT * 10)) - output += " | \[10\]" - output += " | \[All\]" - output += "
" + var/datum/component/material_container/materials = rmat.mat_container + + if(materials) + for(var/mat_id in materials.materials) + var/datum/material/M = mat_id + var/amount = materials.materials[mat_id] + var/ref = REF(M) + output += "[M.name]: [amount] cm³" + if(amount >= MINERAL_MATERIAL_AMOUNT) + output += "- Remove \[1\]" + if(amount >= (MINERAL_MATERIAL_AMOUNT * 10)) + output += " | \[10\]" + output += " | \[50\]" + output += "
" + else + output += "No material storage connected, please contact the quartermaster.
" return output /obj/machinery/mecha_part_fabricator/proc/get_resources_w_coeff(datum/design/D) @@ -125,18 +136,29 @@ /obj/machinery/mecha_part_fabricator/proc/check_resources(datum/design/D) if(D.reagents_list.len) // No reagents storage - no reagent designs. return FALSE - GET_COMPONENT(materials, /datum/component/material_container) + var/datum/component/material_container/materials = rmat.mat_container if(materials.has_materials(get_resources_w_coeff(D))) return TRUE return FALSE /obj/machinery/mecha_part_fabricator/proc/build_part(datum/design/D) - being_built = D - desc = "It's building \a [initial(D.name)]." var/list/res_coef = get_resources_w_coeff(D) - GET_COMPONENT(materials, /datum/component/material_container) + var/datum/component/material_container/materials = rmat.mat_container + if (!materials) + say("No access to material storage, please contact the quartermaster.") + return FALSE + if (rmat.on_hold()) + say("Mineral access is on hold, please contact the quartermaster.") + return FALSE + if(!check_resources(D)) + say("Not enough resources. Queue processing stopped.") + return FALSE + being_built = D + desc = "It's building \a [initial(D.name)]." materials.use_amount(res_coef) + rmat.silo_log(src, "built", -1, "[D.name]", res_coef) + add_overlay("fab-active") use_power = ACTIVE_POWER_USE updateUsrDialog() @@ -191,13 +213,10 @@ while(D) if(stat&(NOPOWER|BROKEN)) return FALSE - if(!check_resources(D)) - say("Not enough resources. Queue processing stopped.") - temp = {"Not enough resources to build next part.
- Try again | Return"} + if(build_part(D)) + remove_from_queue(1) + else return FALSE - remove_from_queue(1) - build_part(D) D = listgetindex(queue, 1) say("Queue processing finished successfully.") @@ -379,16 +398,32 @@ break if(href_list["remove_mat"] && href_list["material"]) - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_sheets(text2num(href_list["remove_mat"]), href_list["material"]) + var/datum/material/Mat = locate(href_list["material"]) + eject_sheets(Mat, text2num(href_list["remove_mat"])) updateUsrDialog() return -/obj/machinery/mecha_part_fabricator/on_deconstruction() - GET_COMPONENT(materials, /datum/component/material_container) - materials.retrieve_all() - ..() +/obj/machinery/mecha_part_fabricator/proc/do_process_queue() + if(processing_queue || being_built) + return FALSE + processing_queue = 1 + process_queue() + processing_queue = 0 + +/obj/machinery/mecha_part_fabricator/proc/eject_sheets(eject_sheet, eject_amt) + var/datum/component/material_container/mat_container = rmat.mat_container + if (!mat_container) + say("No access to material storage, please contact the quartermaster.") + return 0 + if (rmat.on_hold()) + say("Mineral access is on hold, please contact the quartermaster.") + return 0 + var/count = mat_container.retrieve_sheets(text2num(eject_amt), eject_sheet, drop_location()) + var/list/matlist = list() + matlist[eject_sheet] = text2num(eject_amt) + rmat.silo_log(src, "ejected", -count, "sheets", matlist) + return count /obj/machinery/mecha_part_fabricator/proc/AfterMaterialInsert(type_inserted, id_inserted, amount_inserted) var/stack_name = material2name(id_inserted) @@ -417,3 +452,6 @@ return FALSE return TRUE + +/obj/machinery/mecha_part_fabricator/maint + link_on_init = FALSE \ No newline at end of file diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index 2d182516..b357e96d 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -69,6 +69,12 @@ var/melee_cooldown = 10 var/melee_can_hit = 1 + var/silicon_pilot = FALSE //set to true if an AI or MMI is piloting. + + var/enter_delay = 40 //Time taken to enter the mech + var/enclosed = TRUE //Set to false for open-cockpit mechs + var/silicon_icon_state = null //if the mech has a different icon when piloted by an AI or MMI + //Action datums var/datum/action/innate/mecha/mech_eject/eject_action = new var/datum/action/innate/mecha/mech_toggle_internals/internals_action = new @@ -116,7 +122,8 @@ icon_state += "-open" add_radio() add_cabin() - add_airtank() + if (enclosed) + add_airtank() spark_system.set_up(2, 0, src) spark_system.attach(src) smoke_system.set_up(3, src) @@ -133,6 +140,11 @@ diag_hud_set_mechcell() diag_hud_set_mechstat() +/obj/mecha/update_icon() + if (silicon_pilot && silicon_icon_state) + icon_state = silicon_icon_state + . = ..() + /obj/mecha/get_cell() return cell @@ -273,6 +285,11 @@ to_chat(user, "It's equipped with:") for(var/obj/item/mecha_parts/mecha_equipment/ME in equipment) to_chat(user, "[icon2html(ME, user)] \A [ME].") + if(!enclosed) + if(silicon_pilot) + to_chat(user, "[src] appears to be piloting itself...") + else if(occupant && occupant != user) //!silicon_pilot implied + to_chat(user, "You can see [occupant] inside.") //processing internal damage, temperature, air regulation, alert updates, lights power use. /obj/mecha/process() @@ -392,13 +409,19 @@ diag_hud_set_mechcell() diag_hud_set_mechstat() +/obj/mecha/fire_act() //Check if we should ignite the pilot of an open-canopy mech + if (occupant && !enclosed && !silicon_pilot) + if (occupant.fire_stacks < 5) + occupant.fire_stacks += 1 + occupant.IgniteMob() + /obj/mecha/proc/drop_item()//Derpfix, but may be useful in future for engineering exosuits. return /obj/mecha/Hear(message, atom/movable/speaker, message_language, raw_message, radio_freq, list/spans, message_mode) . = ..() if(speaker == occupant) - if(radio.broadcasting) + if(radio?.broadcasting) radio.talk_into(speaker, text, , spans, message_language) //flick speech bubble var/list/speech_bubble_recipients = list() @@ -474,7 +497,7 @@ . = ..() if(.) events.fireEvent("onMove",get_turf(src)) - if (internal_tank.disconnect()) // Something moved us and broke connection + if (internal_tank?.disconnect()) // Something moved us and broke connection occupant_message("Air port connection teared off!") log_message("Lost connection to gas port.") @@ -500,7 +523,7 @@ user.forceMove(get_turf(src)) to_chat(user, "You climb out from [src].") return 0 - if(internal_tank.connected_port) + if(internal_tank?.connected_port) if(world.time - last_message > 20) occupant_message("Unable to move while connected to the air system port!") last_message = world.time @@ -688,6 +711,7 @@ AI.disconnect_shell() RemoveActions(AI, TRUE) occupant = null + silicon_pilot = FALSE AI.forceMove(card) card.AI = AI AI.controlled_mech = null @@ -729,7 +753,9 @@ AI.ai_restore_power() AI.forceMove(src) occupant = AI + silicon_pilot = TRUE icon_state = initial(icon_state) + update_icon() playsound(src, 'sound/machines/windowdoor.ogg', 50, 1) if(!internal_damage) SEND_SOUND(occupant, sound('sound/mecha/nominal.ogg',volume=50)) @@ -832,7 +858,7 @@ visible_message("[user] starts to climb into [name].") - if(do_after(user, 40, target = src)) + if(do_after(user, enter_delay, target = src)) if(obj_integrity <= 0) to_chat(user, "You cannot get in the [name], it has been destroyed!") else if(occupant) @@ -905,6 +931,7 @@ var/mob/living/brainmob = mmi_as_oc.brainmob mmi_as_oc.mecha = src occupant = brainmob + silicon_pilot = TRUE brainmob.forceMove(src) //should allow relaymove brainmob.reset_perspective(src) brainmob.remote_control = src @@ -946,6 +973,7 @@ RemoveActions(occupant) occupant.gib() //If one Malf decides to steal a mech from another AI (even other Malfs!), they are destroyed, as they have nowhere to go when replaced. occupant = null + silicon_pilot = FALSE return else if(!AI.linked_core) @@ -963,6 +991,7 @@ return var/mob/living/L = occupant occupant = null //we need it null when forceMove calls Exited(). + silicon_pilot = FALSE if(mob_container.forceMove(newloc))//ejecting mob container log_message("[mob_container] moved out.") L << browse(null, "window=exosuit") diff --git a/code/game/mecha/mecha_actions.dm b/code/game/mecha/mecha_actions.dm index 7b00e208..307e8d1b 100644 --- a/code/game/mecha/mecha_actions.dm +++ b/code/game/mecha/mecha_actions.dm @@ -3,7 +3,8 @@ /obj/mecha/proc/GrantActions(mob/living/user, human_occupant = 0) if(human_occupant) eject_action.Grant(user, src) - internals_action.Grant(user, src) + if(enclosed) + internals_action.Grant(user, src) cycle_action.Grant(user, src) lights_action.Grant(user, src) stats_action.Grant(user, src) diff --git a/code/game/mecha/mecha_construction_paths.dm b/code/game/mecha/mecha_construction_paths.dm index 5b7becb5..0c87ca84 100644 --- a/code/game/mecha/mecha_construction_paths.dm +++ b/code/game/mecha/mecha_construction_paths.dm @@ -171,36 +171,29 @@ list( "key" = TOOL_WRENCH, "back_key" = TOOL_CROWBAR, - "desc" = "Internal armor is installed." + "desc" = "Outer plating is installed." ), //17 list( "key" = TOOL_WELDER, "back_key" = TOOL_WRENCH, - "desc" = "Internal armor is wrenched." + "desc" = "Outer Plating is wrenched." ), //18 list( - "key" = /obj/item/stack/sheet/plasteel, - "amount" = 5, + "key" = /obj/item/stack/rods, + "amount" = 10, "back_key" = TOOL_WELDER, - "desc" = "Internal armor is welded." + "desc" = "Outer Plating is welded." ), //19 - list( - "key" = TOOL_WRENCH, - "back_key" = TOOL_CROWBAR, - "desc" = "External armor is installed." - ), - - //20 list( "key" = TOOL_WELDER, - "back_key" = TOOL_WRENCH, - "desc" = "External armor is wrenched." + "back_key" = TOOL_WIRECUTTER, + "desc" = "Cockpit wire screen is installed." ), ) diff --git a/code/game/mecha/mecha_control_console.dm b/code/game/mecha/mecha_control_console.dm index 0188419f..37395dd4 100644 --- a/code/game/mecha/mecha_control_console.dm +++ b/code/game/mecha/mecha_control_console.dm @@ -76,7 +76,7 @@ var/answer = {"Name: [M.name] Integrity: [M.obj_integrity/M.max_integrity*100]% Cell charge: [isnull(cell_charge)?"Not found":"[M.cell.percent()]%"] -Airtank: [M.return_pressure()]kPa +Airtank: [M.internal_tank?"[round(M.return_pressure(), 0.01)]":"Not Equipped"] kPa
Pilot: [M.occupant||"None"] Location: [get_area(M)||"Unknown"] Active equipment: [M.selected||"None"] "} diff --git a/code/game/mecha/mecha_defense.dm b/code/game/mecha/mecha_defense.dm index b7d59c6a..74981f35 100644 --- a/code/game/mecha/mecha_defense.dm +++ b/code/game/mecha/mecha_defense.dm @@ -114,6 +114,9 @@ /obj/mecha/bullet_act(obj/item/projectile/Proj) //wrapper + if (!enclosed && occupant && !silicon_pilot) //allows bullets to hit the pilot of open-canopy mechs + occupant.bullet_act(Proj) //If the sides are open, the occupant can be hit + return BULLET_ACT_HIT log_message("Hit by projectile. Type: [Proj.name]([Proj.flag]).", color="red") . = ..() diff --git a/code/game/mecha/mecha_topic.dm b/code/game/mecha/mecha_topic.dm index 0bab48da..f087c67f 100644 --- a/code/game/mecha/mecha_topic.dm +++ b/code/game/mecha/mecha_topic.dm @@ -71,19 +71,24 @@ /obj/mecha/proc/get_stats_part() var/integrity = obj_integrity/max_integrity*100 var/cell_charge = get_charge() - var/datum/gas_mixture/int_tank_air = internal_tank.return_air() - var/tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None" - var/tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown" - var/cabin_pressure = round(return_pressure(),0.01) + var/datum/gas_mixture/int_tank_air = 0 + var/tank_pressure = 0 + var/tank_temperature = 0 + var/cabin_pressure = 0 + if (internal_tank) + int_tank_air = internal_tank.return_air() + tank_pressure = internal_tank ? round(int_tank_air.return_pressure(),0.01) : "None" + tank_temperature = internal_tank ? int_tank_air.temperature : "Unknown" + cabin_pressure = round(return_pressure(),0.01) . = {"[report_internal_damage()] [integrity<30?"DAMAGE LEVEL CRITICAL
":null] Integrity: [integrity]%
Powercell charge: [isnull(cell_charge)?"No powercell installed":"[cell.percent()]%"]
- Air source: [use_internal_tank?"Internal Airtank":"Environment"]
- Airtank pressure: [tank_pressure]kPa
- Airtank temperature: [tank_temperature]°K|[tank_temperature - T0C]°C
- Cabin pressure: [cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa
- Cabin temperature: [return_temperature()]°K|[return_temperature() - T0C]°C
+ Air source: [internal_tank?"[use_internal_tank?"Internal Airtank":"Environment"]":"Environment"]
+ Airtank pressure: [internal_tank?"[tank_pressure]kPa":"N/A"]
+ Airtank temperature: [internal_tank?"[tank_temperature]°K|[tank_temperature - T0C]°C":"N/A"]
+ Cabin pressure: [internal_tank?"[cabin_pressure>WARNING_HIGH_PRESSURE ? "[cabin_pressure]": cabin_pressure]kPa":"N/A"]
+ Cabin temperature: [internal_tank?"[return_temperature()]°K|[return_temperature() - T0C]°C":"N/A"]
[dna_lock?"DNA-locked:
[dna_lock] \[
Reset\]
":""]
[thrusters_action.owner ? "Thrusters: [thrusters_active ? "Enabled" : "Disabled"]
" : ""] [defense_action.owner ? "Defence Mode: [defence_mode ? "Enabled" : "Disabled"]
" : ""] @@ -100,14 +105,14 @@
Electronics
@@ -115,7 +120,7 @@