diff --git a/code/ATMOSPHERICS/components/unary/vent_pump.dm b/code/ATMOSPHERICS/components/unary/vent_pump.dm index 2d8c9ee1163..b60f2436d14 100644 --- a/code/ATMOSPHERICS/components/unary/vent_pump.dm +++ b/code/ATMOSPHERICS/components/unary/vent_pump.dm @@ -308,8 +308,11 @@ radio_filter_in = frequency==1439?(RADIO_FROM_AIRALARM):null radio_filter_out = frequency==1439?(RADIO_TO_AIRALARM):null if(frequency) - radio_connection = register_radio(src, frequency, frequency, radio_filter_in) - src.broadcast_status() + set_frequency(frequency) + +/obj/machinery/atmospherics/unary/vent_pump/proc/set_frequency(new_frequency) + radio_connection = register_radio(src, frequency, new_frequency, radio_filter_in) + frequency = new_frequency /obj/machinery/atmospherics/unary/vent_pump/receive_signal(datum/signal/signal) if(stat & (NOPOWER|BROKEN)) @@ -407,6 +410,20 @@ else to_chat(user, span_warning("You need more welding fuel to complete this task.")) return 1 + if(W.has_tool_quality(TOOL_MULTITOOL)) + var/choice = tgui_alert(user, "[src] has an ID of \"[id_tag]\" and a frequency of [frequency]. What would you like to change?", "[src] ID", list("ID Tag", "Frequency", "Nothing")) + switch(choice) + if("ID Tag") + var/new_id = tgui_input_text(user, "[src] has an ID of \"[id_tag]\". What would you like it to be?", "[src] ID", id_tag, 30, FALSE, TRUE) + if(new_id) + id_tag = new_id + + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + return TRUE else ..() diff --git a/code/__defines/diseases.dm b/code/__defines/diseases.dm index 30f1d6252be..97f00252284 100644 --- a/code/__defines/diseases.dm +++ b/code/__defines/diseases.dm @@ -18,7 +18,6 @@ #define CONTACT_GENERAL (1<<5) #define AIRBORNE (1<<6) - //Severity Defines #define NONTHREAT "No threat" #define MINOR "Minor" diff --git a/code/__defines/faction.dm b/code/__defines/faction.dm index 766c2d51aac..a264a643ad3 100644 --- a/code/__defines/faction.dm +++ b/code/__defines/faction.dm @@ -111,6 +111,7 @@ #define FACTION_WOLFTAUR "wolftaur" #define FACTION_WORM "worm" #define FACTION_XENO "xeno" +#define FACTION_CORGI "corgi" #define FACTION_PLANTS "plants" diff --git a/code/__defines/gamemode.dm b/code/__defines/gamemode.dm index 990ddc1fa09..74121881ce1 100644 --- a/code/__defines/gamemode.dm +++ b/code/__defines/gamemode.dm @@ -40,11 +40,12 @@ //VOREStation Add #define BE_LOSTDRONE 0x10000 #define BE_MAINTPRED 0x20000 -#define BE_MORPH 0x40000 -#define BE_CORGI 0x80000 -#define BE_CURSEDSWORD 0x100000 -#define BE_SURVIVOR 0x200000 -#define BE_EVENT 0x400000 +#define BE_MAINTLURKER 0x40000 +#define BE_MORPH 0x80000 +#define BE_CORGI 0x100000 +#define BE_CURSEDSWORD 0x200000 +#define BE_SURVIVOR 0x400000 +#define BE_EVENT 0x800000 //VOREStation Add End var/list/be_special_flags = list( @@ -67,6 +68,7 @@ var/list/be_special_flags = list( //VOREStation Add "Lost Drone" = BE_LOSTDRONE, "Maint Pred" = BE_MAINTPRED, + "Maint Lurker" = BE_MAINTLURKER, "Morph" = BE_MORPH, "Corgi" = BE_CORGI, "Cursed Sword" = BE_CURSEDSWORD, diff --git a/code/__defines/jobs.dm b/code/__defines/jobs.dm index 70e38c6876d..a4560b36dff 100644 --- a/code/__defines/jobs.dm +++ b/code/__defines/jobs.dm @@ -420,3 +420,4 @@ #define JOB_XENOMORPH "Xenomorph" #define JOB_BORER "Borer" #define JOB_EMERGENCY_RESPONSE_TEAM "Emergency Response Team" +#define JOB_MAINT_LURKER "Maintenance Lurker" diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 77e1e2d74ab..b6b6cffd657 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -328,6 +328,7 @@ #define SPECIES_XENO_HUNTER "Xenomorph Hunter" #define SPECIES_XENO_SENTINEL "Xenomorph Sentinel" #define SPECIES_XENO_QUEEN "Xenomorph Queen" +#define SPECIES_XENOMORPH_HYBRID "Xenomorph Hybrid" // Misc species. Mostly unused but might as well be complete. #define SPECIES_SHADOW "Shadow" @@ -456,3 +457,6 @@ #define VISIBLE_GENDER_FORCE_PLURAL 1 // Used by get_visible_gender to return PLURAL #define VISIBLE_GENDER_FORCE_IDENTIFYING 2 // Used by get_visible_gender to return the mob's identifying gender #define VISIBLE_GENDER_FORCE_BIOLOGICAL 3 // Used by get_visible_gender to return the mob's biological gender + +// Default name for accesories +#define DEVELOPER_WARNING_NAME "you should not see this..." diff --git a/code/__defines/talksounds.dm b/code/__defines/talksounds.dm new file mode 100644 index 00000000000..4f01b4fb2f5 --- /dev/null +++ b/code/__defines/talksounds.dm @@ -0,0 +1,63 @@ + + +var/list/talk_sound_map = rlist( + list( + "beep-boop", + "goon speak 1", + "goon speak 2", + "goon speak 3", + "goon speak 4", + "goon speak blub", + "goon speak bottalk", + "goon speak buwoo", + "goon speak cow", + "goon speak lizard", + "goon speak pug", + "goon speak pugg", + "goon speak roach", + "goon speak skelly", + // "xeno speak" // Does not exist on virgo + ), + list( + talk_sound, + goon_speak_one_sound, + goon_speak_two_sound, + goon_speak_three_sound, + goon_speak_four_sound, + goon_speak_blub_sound, + goon_speak_bottalk_sound, + goon_speak_buwoo_sound, + goon_speak_cow_sound, + goon_speak_lizard_sound, + goon_speak_pug_sound, + goon_speak_pugg_sound, + goon_speak_roach_sound, + goon_speak_skelly_sound, + // xeno_speak_sound // Does not exist on virgo + ) + ) + +/proc/get_talk_sound(var/voice_sound) + if(!voice_sound) + return talk_sound_map[1] + return talk_sound_map[2][voice_sound] + +/proc/rlist(var/list/keys,var/list/values) //short for reversible list generator + var/list/rlist = list(list(),list(),FALSE,0) + var/i = 0 + for(i = 1, i <= LAZYLEN(keys), i++) + to_chat(world,keys[i]) + rlist[1] += keys[i] + rlist[2][keys[i]] = values[i] + rlist += TRUE + rlist += i + return rlist + +/proc/arlist(var/list/altlist) + var/list/rlist = list(list(),list(),FALSE,0) + var/i = 0 + for(i = 1, i <= LAZYLEN(altlist), i++) + rlist[(i % 2) +1] += altlist[i] + rlist += TRUE + rlist += i/2 + return rlist diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 6eda375458b..e5790c3bca2 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -3,8 +3,8 @@ GLOBAL_LIST_EMPTY(archive_diseases) GLOBAL_LIST_INIT(advance_cures, list( "sodiumchloride", "sugar", "orangejuice", "spaceacillin", "glucose", "ethanol", - "dyloteane", "impedrezene", "hepanephrodaxon", - "gold", "silver" + "leporazine", "impedrezene", "hepanephrodaxon", + "silver", "gold" )) /datum/disease/advance @@ -236,6 +236,13 @@ GLOBAL_LIST_INIT(advance_cures, list( Refresh(1) return +/datum/disease/advance/proc/PickyEvolve(var/list/datum/symptom/D) + var/s = safepick(D) + if(s) + AddSymptom(new s) + Refresh(1) + return + // Randomly remove a symptom. /datum/disease/advance/proc/Devolve() if(length(symptoms) > 1) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm new file mode 100644 index 00000000000..21a44b2e9f4 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -0,0 +1,38 @@ +/* +////////////////////////////////////// + +Deafness + + Slightly hidden. + Lowers resistance. + Increases stage speed slightly. + Decreases transmittablity. + Intense Level. + +Bonus + Causes intermittent loss of hearing. + +////////////////////////////////////// +*/ + +/datum/symptom/deafness + name = "Deafness" + stealth = 1 + resistance = -2 + stage_speed = 1 + transmittable = -3 + level = 4 + severity = 3 + +/datum/symptom/deafness/Activate(datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + + switch(A.stage) + if(3, 4) + to_chat(M, span_warning("[pick("you hear a ringing in your ear.", "You ears pop.")]")) + if(5) + to_chat(M, span_userdanger("You ear pop and begin ringing loudly!")) + M.ear_deaf += 20 + return diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index e91efab1c7c..00a5a273e7b 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -6,7 +6,7 @@ Necrotizing Fasciitis (AKA Flesh-Eating Disease) Very very noticable. Lowers resistance tremendously. No changes to stage speed. - Decreases transmittablity temrendously. + Decreases transmittablity tremendously. Fatal Level. Bonus diff --git a/code/datums/diseases/advance/symptoms/flip.dm b/code/datums/diseases/advance/symptoms/flip.dm new file mode 100644 index 00000000000..e210e4b3bfe --- /dev/null +++ b/code/datums/diseases/advance/symptoms/flip.dm @@ -0,0 +1,33 @@ +/* +////////////////////////////////////// + +Flippinov + + Slightly hidden. + No change to resistance. + Increases stage speed. + Little transmittable. + Low Level. + +BONUS + Makes the host FLIP. + Should be used for buffing your disease. + +////////////////////////////////////// +*/ + +/datum/symptom/spyndrome + name = "Flippinov" + stealth = 2 + resistance = 0 + stage_speed = 3 + transmittable = 1 + level = 1 + severity = 1 + +/datum/symptom/spyndrome/Activate(datum/disease/advance/A) + ..() + + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/L = A.affected_mob + L.emote("flip") diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm new file mode 100644 index 00000000000..1a6c2ff109c --- /dev/null +++ b/code/datums/diseases/advance/symptoms/hair.dm @@ -0,0 +1,56 @@ +/* +////////////////////////////////////// +Alopecia + + Noticable. + Decreases resistance slightly. + Reduces stage speed slightly. + Transmittable. + Intense Level. + +BONUS + Makes the mob lose hair. + +////////////////////////////////////// +*/ + +/datum/symptom/shedding + name = "Alopecia" + stealth = -1 + resistance = -1 + stage_speed = -1 + transmittable = 2 + level = 4 + severity = 1 + +/datum/symptom/shedding/Activate(datum/disease/advance/A) + ..() + if(!prob(SYMPTOM_ACTIVATION_PROB)) + return + if(ishuman(A.affected_mob)) + return + var/mob/living/carbon/human/H = A.affected_mob + var/obj/item/organ/external/head/head_organ = H.get_organ(BP_HEAD) + if(!istype(head_organ)) + return + to_chat(H, span_warning("[pick("Your scalp itches.", "Your skin feels flakey.")]")) + + switch(A.stage) + if(3, 4) + if(H.h_style != "Bald" && H.h_style != "Balding Hair") + to_chat(H, span_warning("Your hair starts to fall out in clumps...")) + addtimer(CALLBACK(src, PROC_REF(change_hair), H, null, "Balding Hair"), 5 SECONDS) + if(5) + if(H.h_style != "Shaved" && H.h_style != "Bald") + to_chat(H, span_warning("Your hair starts to fall out in clumps...")) + addtimer(CALLBACK(src, PROC_REF(change_hair), H, "Shaved", "Bald"), 5 SECONDS) + return + +/datum/symptom/shedding/proc/change_hair(var/mob/living/carbon/human/H, f_style, h_style) + if(!H) + return + if(f_style) + H.f_style = f_style + if(h_style) + H.h_style = h_style + return diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 1138d4adba9..340b88d6b4c 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -130,7 +130,7 @@ Bonus Very high level. Bonus - Heals brain damage, treats radiation. + Heals clone damage, treats radiation. ////////////////////////////////////// */ diff --git a/code/datums/diseases/advance/symptoms/hematophagy.dm b/code/datums/diseases/advance/symptoms/hematophagy.dm new file mode 100644 index 00000000000..53e5aa842d4 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/hematophagy.dm @@ -0,0 +1,42 @@ +/* +////////////////////////////////////// + +Hematophagy + + Little bit hidden. + Decreases resistance slightly. + Decreases stage speed tremendously. + Slightly increased transmittablity. + Intense level. + +BONUS + The host must feed on BLOOD + +////////////////////////////////////// +*/ + +/datum/symptom/hematophagy + name = "Hematophagy" + stealth = 1 + resistance = -1 + resistance = -4 + transmittable = 1 + level = 4 + +/datum/symptom/hematophagy/Start(datum/disease/advance/A) + if(ishuman(A.affected_mob)) + var/mob/living/carbon/human/H = A.affected_mob + + H.species.organic_food_coeff = 0 + H.species.bloodsucker = TRUE + + add_verb(H, /mob/living/carbon/human/proc/bloodsuck) + +/datum/symptom/hematophagy/End(datum/disease/advance/A) + if(ishuman(A.affected_mob)) + var/mob/living/carbon/human/H = A.affected_mob + + H.species.organic_food_coeff = initial(H.species.organic_food_coeff) + H.species.bloodsucker = initial(H.species.bloodsucker) + + remove_verb(H, /mob/living/carbon/human/proc/bloodsuck) diff --git a/code/datums/diseases/advance/symptoms/photosensitivity.dm b/code/datums/diseases/advance/symptoms/photosensitivity.dm new file mode 100644 index 00000000000..5908bfb259c --- /dev/null +++ b/code/datums/diseases/advance/symptoms/photosensitivity.dm @@ -0,0 +1,41 @@ +/* +////////////////////////////////////// + +Photosensitivity + + Noticable. + Increases resistance. + Increases stage speed slightly. + Decreases transmittablity tremendously. + Fatal Level. + +Bonus + Deals brute damage over time. + +////////////////////////////////////// +*/ + +/datum/symptom/photosensitivity + name = "Photosensitivity" + stealth = -2 + resistance = 2 + stage_speed = 1 + transmittable = -4 + level = 6 + severity = 5 + +/datum/symptom/photosensitivity/Activate(datum/disease/advance/A) + ..() + + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/carbon/human/H = A.affected_mob + var/turf/T = get_turf(H) + switch(A.stage) + if(3) + if(T.get_lumcount() > 0.5) + to_chat(H, span_danger("Your skin itches under the light...")) + if(4, 5) + if(T.get_lumcount() > 0.5) + to_chat(H, span_danger("Your skin feels like burning!")) + H.adjustFireLoss(T.get_lumcount()) + return diff --git a/code/datums/diseases/advance/symptoms/pica.dm b/code/datums/diseases/advance/symptoms/pica.dm new file mode 100644 index 00000000000..ddd7e282d85 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/pica.dm @@ -0,0 +1,46 @@ +/* +////////////////////////////////////// + +Pica + + Not noticable or unnoticable. + Decreases resistance. + Increases stage speed. + Little transmittable. + Low Level. + +BONUS + The host gains hunger for any kind of object. + +////////////////////////////////////// +*/ + +/datum/symptom/pica + name = "Pica" + stealth = 0 + resistance = -2 + stage_speed = 3 + transmittable = 1 + level = 1 + severity = 1 + +/datum/symptom/pica/Start(datum/disease/advance/A) + add_verb(A.affected_mob, /mob/living/proc/eat_trash) + add_verb(A.affected_mob, /mob/living/proc/toggle_trash_catching) + +/datum/symptom/pica/End(datum/disease/advance/A) + remove_verb(A.affected_mob, /mob/living/proc/eat_trash) + remove_verb(A.affected_mob, /mob/living/proc/toggle_trash_catching) + +/datum/symptom/pica/Activate(datum/disease/advance/A) + ..() + + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/list/nearby = oview(5, A.affected_mob) + to_chat(A.affected_mob, span_warning("You could go fo a bite of [pick(nearby)]...")) + else if (prob(SYMPTOM_ACTIVATION_PROB)) + if(ishuman(A.affected_mob)) + var/mob/living/carbon/human/H = A.affected_mob + var/list/item = H.get_equipped_items() + to_chat(H, span_warning("[pick(item)] looks oddly [pick("delicious", "tasty", "scrumptious", "inviting")]...")) + return diff --git a/code/datums/diseases/advance/symptoms/size.dm b/code/datums/diseases/advance/symptoms/size.dm new file mode 100644 index 00000000000..a0a71fe1b71 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/size.dm @@ -0,0 +1,98 @@ +/* +////////////////////////////////////// + +Mass Revectoring + + Very noticeable. + Increases resistance slightly. + Increases stage speed. + Decreases transmittablity + Intense level. + +BONUS + Changes the size of the host. + +////////////////////////////////////// +*/ + +/datum/symptom/size + name = "Mass Revectoring" + stealth = -4 + resistance = 1 + stage_speed = 2 + transmittable = -2 + level = 4 + severity = 1 + +/datum/symptom/size/Activate(datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + + switch(A.stage) + if(4, 5) + M.emote("twitch") + Resize(M, rand(25, 200)) + +/datum/symptom/size/proc/Resize(mob/living/M, var/size) + M.resize(size+M.size_multiplier/100) + +/* +////////////////////////////////////// + +Enlargement Disorder + + Very noticeable. + Increases resistance slightly. + Increases stage speed. + Decreases transmittablity + Intense level. + +BONUS + Grows the host to bigger sizes + +////////////////////////////////////// +*/ + +/datum/symptom/size/grow + name = "Enlargement Disorder" + +/datum/symptom/size/Activate(datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + + switch(A.stage) + if(4, 5) + M.emote("twitch") + Resize(M, rand(100, 200)) + +/* +////////////////////////////////////// + +Dwindling Malady + + Very noticeable. + Increases resistance slightly. + Increases stage speed. + Decreases transmittablity + Intense level. + +BONUS + Shrinks the host to small sizes + +////////////////////////////////////// +*/ + +/datum/symptom/size/shrink + name = "Dwindling Malady" + +/datum/symptom/size/Activate(datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + + switch(A.stage) + if(4, 5) + M.emote("twitch") + Resize(M, rand(25, 100)) diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 63f2ab00e19..529596945b8 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -42,3 +42,78 @@ Bonus icky.viruses |= A.Copy() return + +/* +////////////////////////////////////// + +Bluespace Sneezing + + Very Noticable. + Resistant + Doesn't increase stage speed. + Little transmittable. + Low Level. + +Bonus + Forces a spread type of AIRBORNE + with extra range AND teleports the mob + +////////////////////////////////////// +*/ + +/datum/symptom/sneeze/bluespace + name = "Bluespace Sneezing" + stealth = -2 + resistance = 3 + stage_speed = 0 + transmittable = 1 + level = 4 + severity = 1 + +/datum/symptom/sneeze/bluespace/Activate(datum/disease/advance/A) + ..() + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + M.emote("sniff") + else + SneezeTeleport(A, M) + A.spread(A.stage) + if(prob(30)) + var/obj/effect/decal/cleanable/mucus/icky = new(get_turf(M)) + icky.viruses |= A.Copy() + + return + +/datum/symptom/sneeze/bluespace/proc/SneezeTeleport(datum/disease/advance/A, var/mob/living/M) + var/list/destination = list() + var/mob/living/carbon/human/H = M + var/place + + for(var/mob/living/carbon/human/B in range(A.stage, M)) + if(B.can_be_drop_pred && H.can_be_drop_prey && H.devourable) + destination += B.vore_selected + + for(var/turf/T in range(A.stage, H)) + if(istype(T, /turf/space)) // No danger, this is just a fun/vore symptom + continue + destination += T + + if(isemptylist(destination)) + to_chat(H, span_warning("You go to sneeze, but can't.")) + return FALSE + + place = safepick(destination) + + var/mob/living/carbon/human/unlucky = locate() in place + + if(unlucky) + if(unlucky.can_be_drop_pred && H.can_be_drop_prey && H.devourable) + place = unlucky.vore_selected + else if(unlucky.devourable && unlucky.can_be_drop_prey && H.can_be_drop_pred) + unlucky.forceMove(H.vore_selected) + + H.emote("sneeze") + do_teleport(H, place) + return TRUE diff --git a/code/datums/diseases/advance/symptoms/spin.dm b/code/datums/diseases/advance/symptoms/spin.dm index 995be34071b..055505dba95 100644 --- a/code/datums/diseases/advance/symptoms/spin.dm +++ b/code/datums/diseases/advance/symptoms/spin.dm @@ -16,7 +16,7 @@ BONUS ////////////////////////////////////// */ -/datum/symptom/mlem +/datum/symptom/spyndrome name = "Spyndrome" stealth = 2 resistance = 0 @@ -24,9 +24,8 @@ BONUS transmittable = 1 level = 1 severity = 1 - var/list/directions = list(2,4,1,8,2,4,1,8,2,4,1,8,2,4,1,8,2,4,1,8) -/datum/symptom/mlem/Activate(var/datum/disease/advance/A) +/datum/symptom/spyndrome/Activate(var/datum/disease/advance/A) ..() if(prob(SYMPTOM_ACTIVATION_PROB)) @@ -34,7 +33,5 @@ BONUS to_chat(viewers(A.affected_mob), span_warning("[A.affected_mob.name] struggles violently against their restraints!")) else to_chat(viewers(A.affected_mob), span_warning("[A.affected_mob.name] spins around violently!")) - for(var/D in directions) - A.affected_mob.dir = D - A.affected_mob.dir = pick(2,4,1,8) + A.affected_mob.emote("spin") return diff --git a/code/datums/diseases/advance/symptoms/stimulant.dm b/code/datums/diseases/advance/symptoms/stimulant.dm new file mode 100644 index 00000000000..4a427d4036f --- /dev/null +++ b/code/datums/diseases/advance/symptoms/stimulant.dm @@ -0,0 +1,43 @@ +/* +////////////////////////////////////// + +Healing + + No change to stealth. + Slightly decreases resistance. + Increases stage speed. + Decreases transmittablity considerably. + Moderate Level. + +Bonus + Heals toxins in the affected mob's blood stream. + +////////////////////////////////////// +*/ + +/datum/symptom/stimulant + name = "Overactive Adrenal Gland" + stealth = 0 + resistance = -1 + stage_speed = 2 + transmittable = -3 + level = 3 + severity = 1 + +/datum/symptom/stimulant/Activate(datum/disease/advance/A) + ..() + + if(prob(SYMPTOM_ACTIVATION_PROB)) + var/mob/living/L = A.affected_mob + to_chat(L, span_notice("You feel a rush of energy inside you!")) + switch(A.stage) + if(1, 2) + L.jitteriness += 5 + if(3, 4) + L.jitteriness += 10 + else + if(L.reagents.get_reagent_amount("hyperzine" < 10)) + L.reagents.add_reagent("hyperzine", 5) + if(prob(30)) + L.jitteriness += 15 + return diff --git a/code/datums/diseases/advance/symptoms/synthetic_infection.dm b/code/datums/diseases/advance/symptoms/synthetic_infection.dm index cb17d88c812..b34ce074818 100644 --- a/code/datums/diseases/advance/symptoms/synthetic_infection.dm +++ b/code/datums/diseases/advance/symptoms/synthetic_infection.dm @@ -1,7 +1,7 @@ /* ////////////////////////////////////// -Sneezing +Synthetic Infection Slightly hidden. Increases resistance. @@ -23,7 +23,6 @@ Bonus transmittable = 1 level = 5 severity = 3 - id = "synthetic_infection" /datum/symptom/infect_synthetics/Start(datum/disease/advance/A) A.infect_synthetics = TRUE diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm new file mode 100644 index 00000000000..ab7f5383b31 --- /dev/null +++ b/code/datums/diseases/appendicitis.dm @@ -0,0 +1,45 @@ +/datum/disease/appendicitis + form = "Condition" + name = "Appendicitis" + max_stages = 3 + spread_text = "Non-contagius" + spread_flags = NON_CONTAGIOUS + cure_text = "Surgery" + agent = "Shitty Appendix" + viable_mobtypes = list(/mob/living/carbon/human) + desc = "If left untreated the subject will become very weak, and may vomit often." + severity = MINOR + disease_flags = CAN_CARRY|CAN_CARRY + visibility_flags = HIDDEN_PANDEMIC + required_organs = list(/obj/item/organ/internal/appendix) + bypasses_immunity = TRUE + virus_heal_resistant = TRUE + +/datum/disease/appendicitis/stage_act() + if(!..()) + return + switch(stage) + if(1) + if(prob(5)) + affected_mob.adjustToxLoss(1) + if(2) + var/obj/item/organ/internal/appendix/A = affected_mob.internal_organs_by_name[O_APPENDIX] + if(A) + A.inflamed = TRUE + if(prob(3)) + to_chat(affected_mob, span_warning("You feel a stabbing pain in your abdomen!")) + affected_mob.custom_emote(VISIBLE_MESSAGE, "winces painfully.") + affected_mob.Stun(rand(4, 6)) + affected_mob.adjustToxLoss(1) + if(3) + if(prob(1)) + to_chat(affected_mob, span_danger("Your abdomen is a world of pain!")) + affected_mob.custom_emote(VISIBLE_MESSAGE, "winces painfully.") + affected_mob.Weaken(10) + if(prob(1)) + affected_mob.vomit(95) + if(prob(5)) + to_chat(affected_mob, span_warning("You feel a stabbing pain in your abdomen!")) + affected_mob.custom_emote(VISIBLE_MESSAGE, "winces painfully.") + affected_mob.Stun(rand(4, 6)) + affected_mob.adjustToxLoss(2) diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm new file mode 100644 index 00000000000..ca36f57d872 --- /dev/null +++ b/code/datums/diseases/fake_gbs.dm @@ -0,0 +1,32 @@ +/datum/disease/fake_gbs + name = "GBS" + max_stages = 5 + spread_text = "On contact" + spread_flags = CONTACT_GENERAL + cure_text = "Adranol & Sulfur" + cures = list("adranol", "sulfur") + agent = "Gravitokinetic Bipotential SADS-" + viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) + desc = "if left untreated death will occur." + severity = BIOHAZARD // Mimics real GBS + +/datum/disease/fake_gbs/stage_act() + if(!..()) + return FALSE + switch(stage) + if(2) + if(prob(1)) + affected_mob.emote("sneeze") + if(3) + if(prob(5)) + affected_mob.emote("cough") + else if(prob(5)) + affected_mob.emote("gasp") + if(prob(10)) + to_chat(span_danger("You're starting to feel very weak...")) + if(4) + if(prob(10)) + affected_mob.emote("cough") + if(5) + if(prob(10)) + affected_mob.emote("cough") diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm new file mode 100644 index 00000000000..ea2c755e279 --- /dev/null +++ b/code/datums/diseases/gbs.dm @@ -0,0 +1,49 @@ +/datum/disease/gbs + name = "GBS" + max_stages = 5 + spread_text = "On contact" + spread_flags = CONTACT_GENERAL + cure_text = "Adranol & Sulfur" + cures = list("adranol", "sulfur") + cure_chance = 15 + agent = "Gravitokinetic Bipotential SADS+" + viable_mobtypes = list(/mob/living/carbon/human) + severity = BIOHAZARD + +/datum/disease/gbs/stage_act() + if(!..()) + return FALSE + switch(stage) + if(2) + if(prob(45)) + affected_mob.adjustToxLoss(5) + if(prob(1)) + affected_mob.emote("sneeze") + if(3) + if(prob(5)) + affected_mob.emote("cough") + else if(prob(5)) + affected_mob.emote("gasp") + if(prob(10)) + to_chat(affected_mob, span_danger("You're starting to feel very weak...")) + if(4) + if(prob(10)) + affected_mob.emote("cough") + affected_mob.adjustToxLoss(5) + if(5) + to_chat(affected_mob, span_danger("Your body feels as if it's trying to rip itself open...")) + if(prob(50)) + affected_mob.delayed_gib() + else + return + +/datum/disease/gbs/curable + name = "Non-Contagious GBS" + stage_prob = 5 + spread_text = "Non-contagious" + spread_flags = NON_CONTAGIOUS + cure_text = "Cryoxadone" + cures = list("cryoxadone") + cure_chance = 10 + agent = "gibbis" + disease_flags = CURABLE diff --git a/code/datums/ghost_query_vr.dm b/code/datums/ghost_query_vr.dm index 75356029be3..8f29b14bb35 100644 --- a/code/datums/ghost_query_vr.dm +++ b/code/datums/ghost_query_vr.dm @@ -7,5 +7,11 @@ /datum/ghost_query/maints_pred role_name = "Maintenance Predator" be_special_flag = BE_MAINTPRED - question = "It appears a predatory critter is lurking in the maintenance. Do you want to play as it? ((You get to choose type of critter. Expect to be treated as vore predator.))" - cutoff_number = 1 \ No newline at end of file + question = "It appears a predatory critter is lurking in maintenance. Do you want to play as it? ((You get to choose type of critter. Expect to be treated as vore predator.))" + cutoff_number = 1 + +/datum/ghost_query/maints_lurker + role_name = "Maintenance Lurker" + be_special_flag = BE_MAINTLURKER + question = "It appears a strange individual is lurking in maintenance. Do you want to play as them? ((You can spawn as your currently loaded character slot. Expect to be treated as vore predator.))" + cutoff_number = 1 diff --git a/code/datums/outfits/misc.dm b/code/datums/outfits/misc.dm index ef36b85038f..13620cbf9a9 100644 --- a/code/datums/outfits/misc.dm +++ b/code/datums/outfits/misc.dm @@ -63,4 +63,16 @@ /decl/hierarchy/outfit/zaddat name = "Zaddat Suit" suit = /obj/item/clothing/suit/space/void/zaddat/ - mask = /obj/item/clothing/mask/gas/zaddat \ No newline at end of file + mask = /obj/item/clothing/mask/gas/zaddat + +/decl/hierarchy/outfit/maint_lurker + name = "Maintenance Lurker Outfit" + id_slot = slot_wear_id + id_type = /obj/item/card/id/civilian/lurker + pda_slot = slot_belt + pda_type = /obj/item/pda + id_pda_assignment = "NO DATA" + + headset = /obj/item/radio/headset + headset_alt = /obj/item/radio/headset/alt + headset_earbud = /obj/item/radio/headset/earbud diff --git a/code/datums/wires/airlock.dm b/code/datums/wires/airlock.dm index 229412e93f0..1c22cf8a7c4 100644 --- a/code/datums/wires/airlock.dm +++ b/code/datums/wires/airlock.dm @@ -8,6 +8,7 @@ holder_type = /obj/machinery/door/airlock wire_count = 12 proper_name = "Airlock" + tgui_template = "WiresAirlock" /datum/wires/airlock/interactable(mob/user) var/obj/machinery/door/airlock/A = holder @@ -41,6 +42,39 @@ . += "The 'Check Timing Mechanism' light is [(A.normalspeed == 0 && haspower) ? "on" : "off"]." . += "The IDScan light is [(A.aiDisabledIdScanner == 0 && haspower) ? "on" : "off."]" +/datum/wires/airlock/tgui_data(mob/user) + var/list/data = ..() + + var/obj/machinery/door/airlock/A = holder + data["id_tag"] = A.id_tag + data["frequency"] = A.radio_connection ? A.frequency : null + data["min_freq"] = RADIO_LOW_FREQ + data["max_freq"] = RADIO_HIGH_FREQ + + return data + +/datum/wires/airlock/tgui_act(action, list/params) + . = ..() + if(.) + return + + var/obj/machinery/door/airlock/A = holder + + switch(action) + if("set_id_tag") + var/new_id = tgui_input_text(usr, "Enter a new ID tag for [A]", "[A] ID Tag", A.id_tag, 30, FALSE, TRUE) + if(new_id) + A.id_tag = new_id + return TRUE + + if("set_frequency") + A.set_frequency(sanitize_frequency(text2num(params["freq"]), RADIO_LOW_FREQ, RADIO_HIGH_FREQ)) + return TRUE + + if("clear_frequency") + A.set_frequency(null) + return TRUE + /datum/wires/airlock/on_cut(wire, mend) var/obj/machinery/door/airlock/A = holder switch(wire) diff --git a/code/datums/wires/wires.dm b/code/datums/wires/wires.dm index 03ffdb8ecbf..b0b0e738cf9 100644 --- a/code/datums/wires/wires.dm +++ b/code/datums/wires/wires.dm @@ -9,6 +9,8 @@ /// The display name for the TGUI window. For example, given the var is "APC"... /// When the TGUI window is opened, "wires" will be appended to it's title, and it would become "APC wires". var/proper_name = "Unknown" + /// The template to use for TGUI. + var/tgui_template = "Wires" /// The total number of wires that our holder atom has. var/wire_count = NONE /// A list of all wires. For a list of valid wires defines that can go here, see `code/__DEFINES/wires.dm` @@ -92,7 +94,7 @@ /datum/wires/tgui_interact(mob/user, datum/tgui/ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "Wires", "[proper_name] wires") + ui = new(user, src, tgui_template, "[proper_name] wires") ui.open() /datum/wires/tgui_state(mob/user) diff --git a/code/game/jobs/job/civilian.dm b/code/game/jobs/job/civilian.dm index 0604b872e4e..34883ef8b61 100644 --- a/code/game/jobs/job/civilian.dm +++ b/code/game/jobs/job/civilian.dm @@ -350,8 +350,8 @@ title_blurb = "A " + JOB_ALT_COMEDIAN + " will focus on making people laugh with the power of wit! Telling jokes, stand-up comedy, you are here to make others smile!" /datum/alt_title/tragedian - title = JOB_ALT_ARTIST - title_blurb = "A " + JOB_ALT_ARTIST + " will focus on making people think about life and world around them! Life is a tragedy, and who's better to convey its emotions than you?" + title = JOB_ALT_TRAGEDIAN + title_blurb = "A " + JOB_ALT_TRAGEDIAN + " will focus on making people think about life and world around them! Life is a tragedy, and who's better to convey its emotions than you?" /datum/alt_title/artist title = JOB_ALT_ARTIST diff --git a/code/game/jobs/job/special.dm b/code/game/jobs/job/special.dm index 2d5f291ffce..cb54e541170 100644 --- a/code/game/jobs/job/special.dm +++ b/code/game/jobs/job/special.dm @@ -108,3 +108,25 @@ return list(access_maint_tunnels, access_entertainment, access_tomfoolery, access_mime) else return list(access_entertainment, access_tomfoolery, access_mime) + +/obj/item/card/id/civilian/lurker + desc = "A rather old and scuffed-looking ID. It seems legit, but something also seems very off somehow. Say, what's the issue/expiry date..?" + assignment = "NO DATA" + rank = JOB_MAINT_LURKER + +/datum/job/maint_lurker + title = JOB_MAINT_LURKER + departments = list(DEPARTMENT_CIVILIAN) + faction = FACTION_STATION + total_positions = -1 + spawn_positions = -1 + supervisors = "nobody save your own common sense and good judgement, though Security may get upset if you're not careful" + selection_color = "#515151" + account_allowed = FALSE //let's not give the game away by creating accounts that someone might stumble across + job_description = "You are a maintenance lurker! You shouldn't be able to see this description anyway! Go prank the CMO or eat the RD's hat or something instead of looking at this." + whitelist_only = 1 + latejoin_only = 1 + requestable = FALSE + outfit_type = /decl/hierarchy/outfit/maint_lurker + access = list(access_maint_tunnels) + minimal_access = list(access_maint_tunnels) diff --git a/code/game/machinery/air_alarm.dm b/code/game/machinery/air_alarm.dm index 522a883efee..4a3b362db29 100644 --- a/code/game/machinery/air_alarm.dm +++ b/code/game/machinery/air_alarm.dm @@ -801,7 +801,7 @@ apply_danger_level(0) update_icon() -/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user as mob) +/obj/machinery/alarm/attackby(obj/item/W as obj, mob/user) add_fingerprint(user) if(alarm_deconstruction_screwdriver(user, W)) return @@ -809,10 +809,10 @@ return if(istype(W, /obj/item/card/id) || istype(W, /obj/item/pda))// trying to unlock the interface with an ID card - togglelock() + togglelock(user) return ..() -/obj/machinery/alarm/verb/togglelock(mob/user as mob) +/obj/machinery/alarm/proc/togglelock(mob/user) if(stat & (NOPOWER|BROKEN)) to_chat(user, "It does nothing.") return @@ -824,9 +824,9 @@ to_chat(user, span_warning("Access denied.")) return -/obj/machinery/alarm/AltClick() +/obj/machinery/alarm/AltClick(mob/user) ..() - togglelock() + togglelock(user) /obj/machinery/alarm/power_change() ..() diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm index 5301aa56613..9ebdc878cdb 100644 --- a/code/game/machinery/doors/airlock_control.dm +++ b/code/game/machinery/doors/airlock_control.dm @@ -125,10 +125,12 @@ return /obj/machinery/door/airlock/proc/set_frequency(new_frequency) + radio_connection = null radio_controller.remove_object(src, frequency) + frequency = new_frequency + if(new_frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency, RADIO_AIRLOCK) + radio_connection = radio_controller.add_object(src, new_frequency, RADIO_AIRLOCK) /obj/machinery/door/airlock/Initialize() @@ -152,6 +154,7 @@ anchored = TRUE power_channel = ENVIRON + circuit = /obj/item/circuitboard/airlock_cycling var/id_tag var/master_tag @@ -165,6 +168,9 @@ var/previousPressure /obj/machinery/airlock_sensor/update_icon() + if(panel_open) + icon_state = "airlock_sensor_open" + return if(on) if(alert) icon_state = "airlock_sensor_alert" @@ -216,6 +222,45 @@ radio_controller.remove_object(src,frequency) return ..() +/obj/machinery/airlock_sensor/examine(mob/user, infix, suffix) + . = ..() + if(in_range(src, user)) + . += "It has a master tag of \"[master_tag]\"." + . += "It has an ID tag of \"[id_tag]\"." + . += "It has a frequency of [frequency]." + . += "It has a command of \"[command]\"." + if(panel_open) + . += "It's panel is open." + +/obj/machinery/airlock_sensor/attackby(obj/item/I, mob/user) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(I.has_tool_quality(TOOL_MULTITOOL)) + var/choice = tgui_alert(user, "What would you like to configure?", "[src] Configuration", list("Master Tag", "ID Tag", "Frequency", "Command", "None")) + switch(choice) + if("Master Tag") + var/new_value = tgui_input_text(user, "The current master tag is \"[master_tag]\", what would you like it to be?", "[src] Master Tag", master_tag, 30, encode = TRUE) + if(new_value) + master_tag = new_value + if("ID Tag") + var/new_value = tgui_input_text(user, "The current id tag is \"[id_tag]\", what would you like it to be?", "[src] ID Tag", id_tag, 30, encode = TRUE) + if(new_value) + id_tag = new_value + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + if("Command") + var/new_value = tgui_input_text(user, "The current command is \"[command]\", what would you like it to be? Valid options include: cycle, cycle_interior, cycle_exterior.", "[src] command", command, encode = TRUE) + if(new_value) + command = new_value + + return TRUE + return ..() + /obj/machinery/airlock_sensor/airlock_interior command = "cycle_interior" @@ -237,6 +282,7 @@ anchored = TRUE power_channel = ENVIRON + circuit = /obj/item/circuitboard/airlock_cycling var/master_tag var/frequency = 1449 @@ -246,18 +292,50 @@ var/on = 1 - /obj/machinery/access_button/update_icon() - if(on) + if(panel_open) + icon_state = "access_button_open" + else if(on) icon_state = "access_button_standby" else icon_state = "access_button_off" +/obj/machinery/access_button/examine(mob/user, infix, suffix) + . = ..() + if(in_range(src, user)) + . += "It has a master tag of \"[master_tag]\"." + . += "It has a frequency of \"[frequency]\"." + . += "It is sending a command of \"[command]\"." + if(panel_open) + . += "It's panel is open." + /obj/machinery/access_button/attackby(obj/item/I as obj, mob/user as mob) //Swiping ID on the access button if (istype(I, /obj/item/card/id) || istype(I, /obj/item/pda)) attack_hand(user) return + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + if(I.has_tool_quality(TOOL_MULTITOOL)) + var/choice = tgui_alert(user, "What would you like to change?", "[src] Settings", list("Tag", "Frequency", "Command", "None")) + switch(choice) + if("Tag") + var/new_id = tgui_input_text(user, "[src] has an master tag of \"[master_tag]\". What would you like it to be?", "[src] ID", master_tag, 30, FALSE, TRUE) + if(new_id) + master_tag = new_id + if("Frequency") + var/new_frequency = tgui_input_number(user, "[src] has a frequency of [frequency]. What would you like it to be?", "[src] frequency", frequency, RADIO_HIGH_FREQ, RADIO_LOW_FREQ) + if(new_frequency) + new_frequency = sanitize_frequency(new_frequency, RADIO_LOW_FREQ, RADIO_HIGH_FREQ) + set_frequency(new_frequency) + if("Command") + var/new_command = tgui_input_text(user, "[src] has a command of \"[command]\". Valid options include: cycle, cycle_interior, cycle_exterior", "[src] command", command, encode = TRUE) + if(new_command) + command = new_command + + return TRUE ..() /obj/machinery/access_button/attack_hand(mob/user) diff --git a/code/game/machinery/embedded_controller/airlock_controllers.dm b/code/game/machinery/embedded_controller/airlock_controllers.dm index 879b532b08a..55e7d97ed20 100644 --- a/code/game/machinery/embedded_controller/airlock_controllers.dm +++ b/code/game/machinery/embedded_controller/airlock_controllers.dm @@ -17,6 +17,8 @@ valid_actions = list("cycle_ext", "cycle_int", "force_ext", "force_int", "abort", "purge", "secure") layer = ABOVE_WINDOW_LAYER + var/deconstructable = FALSE + /obj/machinery/embedded_controller/radio/airlock/Destroy() // TODO - Leshana - Implement dummy terminals //for(var/thing in dummy_terminals) @@ -30,12 +32,75 @@ if(!allowed(user)) return min(STATUS_UPDATE, .) +/obj/machinery/embedded_controller/radio/airlock/attackby(obj/item/I, mob/user) + if(deconstructable) + if(default_deconstruction_screwdriver(user, I)) + return + if(default_deconstruction_crowbar(user, I)) + return + . = ..() + +/obj/machinery/embedded_controller/radio/airlock/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) + var/list/data = ..() + + data["panel_open"] = panel_open + data["tags"] = null + + data["frequency"] = null + data["min_freq"] = null + data["max_freq"] = null + + if(panel_open) + var/datum/embedded_program/airlock/airlock_program = program + data["tags"] = airlock_program.get_all_tags() + + data["frequency"] = frequency + data["min_freq"] = RADIO_LOW_FREQ + data["max_freq"] = RADIO_HIGH_FREQ + + return data + +/obj/machinery/embedded_controller/radio/airlock/tgui_act(action, params) + . = ..() + if(.) + return + + if(!panel_open) + return + + switch(action) + if("edit_tag") + var/datum/embedded_program/airlock/airlock_program = program + + var/tag = params["tag"] + var/current = airlock_program.get_tag(tag) + var/new_tag = tgui_input_text(usr, "What would you like to set [tag] to?", "New [tag]?", current, 30, FALSE, TRUE) + + if(new_tag) + airlock_program.set_tag(tag, new_tag) + return TRUE + + if("set_frequency") + set_frequency(sanitize_frequency(text2num(params["freq"]), RADIO_LOW_FREQ, RADIO_HIGH_FREQ)) + return TRUE + + +/obj/machinery/embedded_controller/radio/airlock/update_icon() + cut_overlays() + if(panel_open) + add_overlay("airlock_control_open") + //Advanced airlock controller for when you want a more versatile airlock controller - useful for turning simple access control rooms into airlocks /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller name = "Advanced Airlock Controller" + deconstructable = TRUE + circuit = /obj/item/circuitboard/airlock_cycling /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller/tgui_data(mob/user) - . = list( + var/list/data = ..() + + // Deliberately not using UNTYPED_LIST_ADD, we want this to be appended + data += list( "chamber_pressure" = round(program.memory["chamber_sensor_pressure"]), "external_pressure" = round(program.memory["external_sensor_pressure"]), "internal_pressure" = round(program.memory["internal_sensor_pressure"]), @@ -45,15 +110,22 @@ "internalTemplateName" = "AirlockConsoleAdvanced", ) + return data + //Airlock controller for airlock control - most airlocks on the station use this /obj/machinery/embedded_controller/radio/airlock/airlock_controller name = "Airlock Controller" tag_secure = 1 valid_actions = list("cycle_ext", "cycle_int", "force_ext", "force_int", "abort") + deconstructable = TRUE + circuit = /obj/item/circuitboard/airlock_cycling /obj/machinery/embedded_controller/radio/airlock/airlock_controller/tgui_data(mob/user) - . = list( + var/list/data = ..() + + // Deliberately not using UNTYPED_LIST_ADD, we want this to be appended + data += list( "chamber_pressure" = round(program.memory["chamber_sensor_pressure"]), "exterior_status" = program.memory["exterior_status"], "interior_status" = program.memory["interior_status"], @@ -61,6 +133,8 @@ "internalTemplateName" = "AirlockConsoleSimple", ) + return data + //Access controller for door control - used in virology and the like /obj/machinery/embedded_controller/radio/airlock/access_controller icon = 'icons/obj/airlock_machines.dmi' @@ -69,7 +143,8 @@ name = "Access Controller" tag_secure = 1 valid_actions = list("cycle_ext_door", "cycle_int_door", "force_ext", "force_int") - + deconstructable = TRUE + circuit = /obj/item/circuitboard/airlock_cycling /obj/machinery/embedded_controller/radio/airlock/access_controller/update_icon() if(on && program) @@ -81,9 +156,14 @@ icon_state = "access_control_off" /obj/machinery/embedded_controller/radio/airlock/access_controller/tgui_data(mob/user) - . = list( + var/list/data = ..() + + // Deliberately not using UNTYPED_LIST_ADD, we want this to be appended + data += list( "exterior_status" = program.memory["exterior_status"], "interior_status" = program.memory["interior_status"], "processing" = program.memory["processing"], "internalTemplateName" = "DoorAccessConsole", ) + + return data diff --git a/code/game/machinery/embedded_controller/airlock_program.dm b/code/game/machinery/embedded_controller/airlock_program.dm index 90558cf6227..93d8b7ed71e 100644 --- a/code/game/machinery/embedded_controller/airlock_program.dm +++ b/code/game/machinery/embedded_controller/airlock_program.dm @@ -414,6 +414,55 @@ send an additional command to open the door again. if(doorCommand) signalDoor(doorTag, doorCommand) +/datum/embedded_program/airlock/proc/get_all_tags() + return list( + "id_tag" = id_tag, + "tag_exterior_door" = tag_exterior_door, + "tag_interior_door" = tag_interior_door, + "tag_airpump" = tag_airpump, + "tag_chamber_sensor" = tag_chamber_sensor, + "tag_exterior_sensor" = tag_exterior_sensor, + "tag_interior_sensor" = tag_interior_sensor, + "tag_airlock_mech_sensor" = tag_airlock_mech_sensor, + "tag_shuttle_mech_sensor" = tag_shuttle_mech_sensor + ) + +/datum/embedded_program/airlock/proc/get_tag(tag_name) + switch(tag_name) + if("id_tag") . = id_tag + if("tag_exterior_door") . = tag_exterior_door + if("tag_interior_door") . = tag_interior_door + if("tag_airpump") . = tag_airpump + if("tag_chamber_sensor") . = tag_chamber_sensor + if("tag_exterior_sensor") . = tag_exterior_sensor + if("tag_interior_sensor") . = tag_interior_sensor + if("tag_airlock_mech_sensor") . = tag_airlock_mech_sensor + if("tag_shuttle_mech_sensor") . = tag_shuttle_mech_sensor + +/datum/embedded_program/airlock/proc/set_tag(tag_name, new_tag) + switch(tag_name) + if("id_tag") + id_tag = new_tag + if("tag_exterior_door") + tag_exterior_door = new_tag + signalDoor(tag_exterior_door, "update") + if("tag_interior_door") + tag_interior_door = new_tag + signalDoor(tag_interior_door, "update") + if("tag_airpump") + tag_airpump = new_tag + if("tag_chamber_sensor") + tag_chamber_sensor = new_tag + if("tag_exterior_sensor") + tag_exterior_sensor = new_tag + if("tag_interior_sensor") + tag_interior_sensor = new_tag + if("tag_airlock_mech_sensor") + tag_airlock_mech_sensor = new_tag + if("tag_shuttle_mech_sensor") + tag_shuttle_mech_sensor = new_tag + + #undef SKIPCYCLE_MARGIN #undef MIN_TARGET_PRESSURE @@ -424,4 +473,4 @@ send an additional command to open the door again. #undef TARGET_NONE #undef TARGET_INOPEN -#undef TARGET_OUTOPEN \ No newline at end of file +#undef TARGET_OUTOPEN diff --git a/code/game/machinery/embedded_controller/construction.dm b/code/game/machinery/embedded_controller/construction.dm new file mode 100644 index 00000000000..0a75ae3f0d5 --- /dev/null +++ b/code/game/machinery/embedded_controller/construction.dm @@ -0,0 +1,44 @@ +/obj/item/circuitboard/airlock_cycling + name = T_BOARD("cycling airlock button") + build_path = /obj/machinery/access_button + board_type = new /datum/frame/frame_types/button + matter = list(MAT_STEEL = 50, MAT_GLASS = 50) + +/datum/design/circuit/airlock_cycling + name = "Machine Design (Cycling Airlock Board)" + desc = "The circuit board for cycling airlock parts." + id = "airlock_cycling" + build_path = /obj/item/circuitboard/airlock_cycling + req_tech = list(TECH_ENGINEERING = 2, TECH_DATA = 2) + sort_string = "MAAAD" + +/obj/item/circuitboard/airlock_cycling/attackby(obj/item/I as obj, mob/user as mob) + if(I.has_tool_quality(TOOL_MULTITOOL)) + var/result = tgui_input_list( + user, + "What do you want to reconfigure the board to?", + "Multitool-Circuitboard interface", + list( + "Button", + "Sensor", + "Controller - Standard", + "Controller - Advanced", + "Controller - Access", + )) + switch(result) + if("Button") + name = T_BOARD("cycling airlock button") + build_path = /obj/machinery/access_button + if("Sensor") + name = T_BOARD("cycling airlock sensor") + build_path = /obj/machinery/airlock_sensor + if("Controller - Standard") + name = T_BOARD("cycling airlock controller (simple)") + build_path = /obj/machinery/embedded_controller/radio/airlock/airlock_controller + if("Controller - Advanced") + name = T_BOARD("cycling airlock controller (advanced)") + build_path = /obj/machinery/embedded_controller/radio/airlock/advanced_airlock_controller + if("Controller - Access") + name = T_BOARD("cycling airlock controller (access)") + build_path = /obj/machinery/embedded_controller/radio/airlock/access_controller + return diff --git a/code/game/machinery/embedded_controller/embedded_controller_base.dm b/code/game/machinery/embedded_controller/embedded_controller_base.dm index f534ccd832a..c92cbdd650e 100644 --- a/code/game/machinery/embedded_controller/embedded_controller_base.dm +++ b/code/game/machinery/embedded_controller/embedded_controller_base.dm @@ -17,6 +17,11 @@ qdel(program) // the program will clear the ref in its Destroy return ..() +/obj/machinery/embedded_controller/examine(mob/user, infix, suffix) + . = ..() + if(in_range(src, user)) + . += "It has an ID tag of \"[program?.id_tag]\"" + /obj/machinery/embedded_controller/proc/post_signal(datum/signal/signal, comm_line) return 0 @@ -36,6 +41,7 @@ if(LAZYLEN(valid_actions)) if(action in valid_actions) program.receive_user_command(action) + return TRUE if(ui.user) add_fingerprint(ui.user) diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index ab1fe6aa501..003fad71082 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -31,6 +31,14 @@ type = replacetext(type, " ", "_") return "[type]_[state]" +/datum/frame/frame_types/button + name = "Button" + frame_class = FRAME_CLASS_ALARM + frame_size = 1 + frame_style = FRAME_STYLE_WALL + x_offset = 24 + y_offset = 24 + /datum/frame/frame_types/computer name = "Computer" icon_override = 'icons/obj/stock_parts_vr.dmi' //VOREStation Edit @@ -208,14 +216,6 @@ circuit = /obj/machinery/atmospheric_field_generator frame_size = 3 -/datum/frame/frame_types/electrochromic_button - name = "Electrochromic Window Button" - frame_class = FRAME_CLASS_ALARM - frame_size = 1 - frame_style = FRAME_STYLE_WALL - x_offset = 24 - y_offset = 24 - ////////////////////////////// // Frame Object (Structure) ////////////////////////////// diff --git a/code/game/machinery/pandemic.dm b/code/game/machinery/pandemic.dm index cd51b2a9fc7..a3521facbf6 100644 --- a/code/game/machinery/pandemic.dm +++ b/code/game/machinery/pandemic.dm @@ -1,6 +1,7 @@ /obj/machinery/computer/pandemic name = "PanD.E.M.I.C 2200" desc = "Used to work with viruses." + circuit = /obj/item/circuitboard/pandemic density = TRUE anchored = TRUE icon = 'icons/obj/pandemic.dmi' @@ -325,7 +326,7 @@ printing = 1 var/obj/item/paper/P = new /obj/item/paper(loc) visible_message(span_notice("[src] rattles and prints out a sheet of paper.")) - // playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1) + playsound(loc, 'sound/machines/printer.ogg', 50, 1) P.info = "
Releasing Virus
" P.info += "
" diff --git a/code/game/objects/items/devices/personal_shield_generator_vr.dm b/code/game/objects/items/devices/personal_shield_generator_vr.dm index f231ccebfea..35498064e35 100644 --- a/code/game/objects/items/devices/personal_shield_generator_vr.dm +++ b/code/game/objects/items/devices/personal_shield_generator_vr.dm @@ -539,7 +539,7 @@ //Misc belts. Admin-spawn only atm. /obj/item/personal_shield_generator/belt/adminbus - desc = "You should not see this. You REALLY should not see this. If you do, you have either been blessed or are about to be the target of some sick prank." + desc = DEVELOPER_WARNING_NAME + " You REALLY should not see this. If you do, you have either been blessed or are about to be the target of some sick prank." modifier_type = /datum/modifier/shield_projection/admin generator_hit_cost = 0 generator_active_cost = 0 diff --git a/code/game/objects/items/falling_object_vr.dm b/code/game/objects/items/falling_object_vr.dm index fdc140b6b4b..c8583db3c6c 100644 --- a/code/game/objects/items/falling_object_vr.dm +++ b/code/game/objects/items/falling_object_vr.dm @@ -1,5 +1,5 @@ /obj/effect/falling_effect - name = "you should not see this" + name = DEVELOPER_WARNING_NAME desc = "no data" invisibility = 101 anchored = TRUE diff --git a/code/game/objects/items/weapons/circuitboards/frame.dm b/code/game/objects/items/weapons/circuitboards/frame.dm index 10b9b00771a..02f011ab67e 100644 --- a/code/game/objects/items/weapons/circuitboards/frame.dm +++ b/code/game/objects/items/weapons/circuitboards/frame.dm @@ -84,7 +84,7 @@ /obj/item/circuitboard/electrochromic name = T_BOARD("electrochromic button") build_path = /obj/machinery/button/windowtint - board_type = new /datum/frame/frame_types/electrochromic_button + board_type = new /datum/frame/frame_types/button matter = list(MAT_STEEL = 50, "glass" = 50) //Computer diff --git a/code/game/objects/items/weapons/circuitboards/machinery/pandemic.dm b/code/game/objects/items/weapons/circuitboards/machinery/pandemic.dm new file mode 100644 index 00000000000..2865260d8a2 --- /dev/null +++ b/code/game/objects/items/weapons/circuitboards/machinery/pandemic.dm @@ -0,0 +1,16 @@ +#ifndef T_BOARD +#error T_BOARD macro is not defined but we need it! +#endif + +/obj/item/circuitboard/pandemic + name = T_BOARD("pandemic") + build_path = /obj/machinery/computer/pandemic + board_type = new /datum/frame/frame_types/computer + origin_tech = list(TECH_DATA = 2, TECH_BIO = 2) + req_components = list( + /obj/item/stock_parts/matter_bin = 2, + /obj/item/stock_parts/scanning_module = 1, + /obj/item/stock_parts/manipulator = 1, + /obj/item/stack/cable_coil = 5, + /obj/item/stock_parts/capacitor = 1 + ) diff --git a/code/game/objects/items/weapons/id cards/id_stacks.dm b/code/game/objects/items/weapons/id cards/id_stacks.dm index 3158d93a9bf..1e0f49133e1 100644 --- a/code/game/objects/items/weapons/id cards/id_stacks.dm +++ b/code/game/objects/items/weapons/id cards/id_stacks.dm @@ -336,3 +336,9 @@ /obj/item/card/id/syndicate/officer name = "Syndicate Officer ID" initial_sprite_stack = list("base-stamp-dark", "top-syndicate", "stamp-s", "pips-gold", "stripe-gold") + +//Special + +/obj/item/card/id/civilian/lurker + name = "Outdated ID" + initial_sprite_stack = list("base-stamp", "stamp-silhouette", "top-olive", "digested") diff --git a/code/game/objects/mob_spawner_vr.dm b/code/game/objects/mob_spawner_vr.dm index 463caa43a69..0169683e010 100644 --- a/code/game/objects/mob_spawner_vr.dm +++ b/code/game/objects/mob_spawner_vr.dm @@ -133,7 +133,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. name = "Corgi Lazy Spawner" desc = "This is a proof of concept, not sure why you would use this one" spawn_delay = 3 MINUTES - mob_faction = "Corgi" + mob_faction = FACTION_CORGI spawn_types = list( /mob/living/simple_mob/animal/passive/dog/corgi = 75, /mob/living/simple_mob/animal/passive/dog/corgi/puppy = 50 @@ -150,7 +150,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. spawn_delay = 10 MINUTES range = 10 simultaneous_spawns = 1 - mob_faction = "wild animal" + mob_faction = FACTION_WILD_ANIMAL total_spawns = -1 destructible = 0 anchored = TRUE @@ -167,7 +167,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. spawn_delay = 10 MINUTES range = 10 simultaneous_spawns = 1 - mob_faction = "xeno" + mob_faction = FACTION_XENO total_spawns = -1 destructible = 1 health = 50 @@ -186,7 +186,7 @@ It also makes it so a ghost wont know where all the goodies/mobs are. spawn_delay = 10 MINUTES range = 10 simultaneous_spawns = 1 - mob_faction = "xeno" + mob_faction = FACTION_XENO total_spawns = 1 destructible = 1 health = 50 diff --git a/code/game/objects/random/mob.dm b/code/game/objects/random/mob.dm index e00c10a6db9..5d1a0f5ecbc 100644 --- a/code/game/objects/random/mob.dm +++ b/code/game/objects/random/mob.dm @@ -174,7 +174,7 @@ overwrite_hostility = 1 - mob_faction = "malf_drone" + mob_faction = FACTION_MALF_DRONE mob_returns_home = 1 mob_wander = 1 mob_wander_distance = 5 @@ -203,7 +203,7 @@ overwrite_hostility = 1 - mob_faction = "malf_drone" + mob_faction = FACTION_MALF_DRONE mob_returns_home = 1 mob_wander = 1 mob_wander_distance = 5 @@ -220,7 +220,7 @@ desc = "This is a random hivebot." icon_state = "robot" - mob_faction = "hivebot" + mob_faction = FACTION_HIVEBOT /obj/random/mob/robotic/hivebot/item_to_spawn() return pick(prob(10);/mob/living/simple_mob/mechanical/hivebot, @@ -292,7 +292,7 @@ desc = "This is a random PoI mercenary." icon_state = "humanoid" - mob_faction = "syndicate" + mob_faction = FACTION_SYNDICATE mob_returns_home = 1 mob_wander_distance = 7 // People like to wander, and these people probably have a lot of stuff to guard. diff --git a/code/game/objects/structures/curtains.dm b/code/game/objects/structures/curtains.dm index 73a0beb8935..3ab4c032c21 100644 --- a/code/game/objects/structures/curtains.dm +++ b/code/game/objects/structures/curtains.dm @@ -26,6 +26,14 @@ toggle() ..() +/obj/structure/curtain/attack_ai(mob/user) + if(!Adjacent(user)) + return + if(!isrobot((user))) + return + playsound(src, "rustle", 15, 1, -5) + toggle() + /obj/structure/curtain/proc/toggle() set_opacity(!opacity) if(opacity) diff --git a/code/game/objects/structures/ghost_pods/event_vr.dm b/code/game/objects/structures/ghost_pods/event_vr.dm index 7e718aab891..fb46fe77b1c 100644 --- a/code/game/objects/structures/ghost_pods/event_vr.dm +++ b/code/game/objects/structures/ghost_pods/event_vr.dm @@ -108,7 +108,7 @@ //newPred.movement_cooldown = 0 // The "needless artificial speed cap" exists for a reason if(M.mind) M.mind.transfer_to(newPred) - to_chat(M, span_notice("You are [newPred], somehow having gotten aboard the station in search of food. \ + to_chat(M, span_notice("You are " + span_bold(newPred) + ", somehow having gotten aboard the station in search of food. \ You are wary of environment around you, but you do feel rather peckish. Stick around dark, secluded places to avoid danger or, \ if you are cute enough, try to make friends with this place's inhabitants.")) to_chat(M, span_critical("Please be advised, this role is NOT AN ANTAGONIST.")) @@ -144,7 +144,7 @@ var/mob/living/simple_mob/vore/morph/newMorph = new /mob/living/simple_mob/vore/morph(get_turf(src)) if(M.mind) M.mind.transfer_to(newMorph) - to_chat(M, span_notice("You are a Morph, somehow having gotten aboard the station in your wandering. \ + to_chat(M, span_notice("You are a " + span_bold("Morph") + ", somehow having gotten aboard the station in your wandering. \ You are wary of environment around you, but your primal hunger still calls for you to find prey. Seek a convincing disguise, \ using your amorphous form to traverse vents to find and consume weak prey.")) to_chat(M, span_notice("You can use shift + click on objects to disguise yourself as them, but your strikes are nearly useless when you are disguised. \ @@ -172,3 +172,82 @@ ..() if(!(src in active_ghost_pods)) active_ghost_pods += src + +/obj/structure/ghost_pod/ghost_activated/maint_lurker + name = "strange maintenance hole" + desc = "This is my hole! It was made for me!" + icon = 'icons/effects/effects.dmi' + icon_state = "tunnel_hole" + icon_state_opened = "tunnel_hole" + density = FALSE + ghost_query_type = /datum/ghost_query/maints_lurker + anchored = TRUE + invisibility = INVISIBILITY_OBSERVER + spawn_active = TRUE + +//override the standard attack_ghost proc for custom messages +/obj/structure/ghost_pod/ghost_activated/maint_lurker/attack_ghost(var/mob/observer/dead/user) + if(jobban_isbanned(user, JOB_GHOSTROLES)) + to_chat(user, span_warning("You cannot use this spawnpoint because you are banned from playing ghost roles.")) + return + + //No whitelist + if(!is_alien_whitelisted(user, GLOB.all_species[user.client.prefs.species])) + to_chat(user, span_warning("You cannot use this spawnpoint to spawn as a species you are not whitelisted for!")) + return + + //No OOC notes/FT + if(not_has_ooc_text(user)) + //to_chat(user, span_warning("You must have proper out-of-character notes and flavor text configured for your current character slot to use this spawnpoint.")) + return + + var/choice = tgui_alert(user, "Using this spawner will spawn you as your currently loaded character slot in a special role. It should not be used with characters you regularly play on station. Are you absolutely sure you wish to continue?", "Maint Lurker Spawner", list("Yes", "No")) + + if(!choice || choice == "No") + return + + create_occupant(user) + +/obj/structure/ghost_pod/ghost_activated/maint_lurker/create_occupant(var/mob/M) + ..() + + var/picked_ckey = M.ckey + var/picked_slot = M.client.prefs.default_slot + + var/mob/living/carbon/human/new_character = new(src.loc) + if(!new_character) + to_chat(M, span_warning("Something went wrong and spawning failed. Please check your character slot doesn't have any obvious errors, then either try again or send an adminhelp!")) + reset_ghostpod() + return + log_and_message_admins("successfully used a Maintenance Lurker spawnpoint and became their loaded character.") + + M.client.prefs.copy_to(new_character) + new_character.dna.ResetUIFrom(new_character) + new_character.sync_organ_dna() + new_character.key = M.key + new_character.mind.loaded_from_ckey = picked_ckey + new_character.mind.loaded_from_slot = picked_slot + + job_master.EquipRank(new_character, JOB_MAINT_LURKER, 1) + + for(var/lang in new_character.client.prefs.alternate_languages) + var/datum/language/chosen_language = GLOB.all_languages[lang] + if(chosen_language) + if(is_lang_whitelisted(src,chosen_language) || (new_character.species && (chosen_language.name in new_character.species.secondary_langs))) + new_character.add_language(lang) + + new_character.regenerate_icons() + + new_character.update_transform() + + to_chat(new_character, span_notice("You are a " + span_bold(JOB_MAINT_LURKER) + ", a loose end... you have no special advantages compared to the rest of the crew, so be cautious! You have spawned with an ID that will allow you free access to maintenance areas along with any of your chosen loadout items that are not role restricted, and can make use of anything you can find in maintenance.")) + to_chat(new_character, span_critical("Please be advised, this role is " + span_bold("NOT AN ANTAGONIST."))) + to_chat(new_character, span_notice("Whoever or whatever your chosen character slot is, your role is to facilitate roleplay focused around that character; this role is not free license to attack and murder people without provocation or explicit out-of-character consent. You should probably be cautious around high-traffic and highly sensitive areas (e.g. Telecomms) as Security personnel would be well within their rights to treat you as a trespasser. That said, good luck!")) + + new_character.visible_message(span_warning("[new_character] appears to crawl out of somewhere.")) + qdel(src) + +/obj/structure/ghost_pod/ghost_activated/maint_lurker/Initialize() + ..() + if(!(src in active_ghost_pods)) + active_ghost_pods += src diff --git a/code/game/objects/structures/mirror.dm b/code/game/objects/structures/mirror.dm index d3caa34c3ed..447f02eeeba 100644 --- a/code/game/objects/structures/mirror.dm +++ b/code/game/objects/structures/mirror.dm @@ -11,7 +11,7 @@ var/glass = 1 var/datum/tgui_module/appearance_changer/mirror/M -/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0, mob/user as mob) +/obj/structure/mirror/Initialize(mapload, var/dir, var/building = 0) M = new(src, null) if(building) glass = 0 @@ -24,12 +24,18 @@ QDEL_NULL(M) . = ..() -/obj/structure/mirror/attack_hand(mob/user as mob) +/obj/structure/mirror/attack_hand(mob/user) if(!glass) return if(shattered) return - if(ishuman(user)) - M.tgui_interact(user) + M.tgui_interact(user) + +/obj/structure/mirror/attack_ai(mob/user) + if(!glass) return + if(shattered) return + if(!Adjacent(user)) return + + M.tgui_interact(user) /obj/structure/mirror/proc/shatter() if(!glass) return diff --git a/code/game/turfs/simulated/wall_attacks.dm b/code/game/turfs/simulated/wall_attacks.dm index cfc998f72a1..df6aa9320b6 100644 --- a/code/game/turfs/simulated/wall_attacks.dm +++ b/code/game/turfs/simulated/wall_attacks.dm @@ -92,6 +92,13 @@ toggle_open(user) return 0 +/turf/simulated/wall/attack_ai(var/mob/user) + if(!Adjacent(user)) + return + if(!isrobot((user))) + return + var/rotting = (locate(/obj/effect/overlay/wallrot) in src) + try_touch(user, rotting) /turf/simulated/wall/attack_hand(var/mob/user) diff --git a/code/modules/admin/admin_verb_lists_vr.dm b/code/modules/admin/admin_verb_lists_vr.dm index fac337afb8a..de613980d0a 100644 --- a/code/modules/admin/admin_verb_lists_vr.dm +++ b/code/modules/admin/admin_verb_lists_vr.dm @@ -196,7 +196,8 @@ var/list/admin_verbs_spawn = list( /client/proc/create_gm_message, /client/proc/remove_gm_message, /client/proc/AdminCreateVirus, - /client/proc/ReleaseVirus + /client/proc/ReleaseVirus, + /client/proc/spawn_reagent ) var/list/admin_verbs_server = list( @@ -283,7 +284,8 @@ var/list/admin_verbs_debug = list( /client/proc/admin_give_modifier, /client/proc/simple_DPS, /datum/admins/proc/view_feedback, - /client/proc/stop_sounds + /client/proc/stop_sounds, + /client/proc/spawn_reagent ) var/list/admin_verbs_paranoid_debug = list( diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 7bd3b1e4c15..6b4bad16849 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -535,3 +535,18 @@ set category = "Debug.Misc" src.stat_panel.send_message("create_debug") + +/client/proc/spawn_reagent() + set name = "Spawn Reagent" + set category = "Debug.Game" + + if(!check_rights(R_ADMIN|R_EVENT)) return + var/datum/reagent/R = tgui_input_list(usr, "Select a reagent to spawn", "Reagent Spawner", subtypesof(/datum/reagent)) + if(!R) + return + + var/obj/item/reagent_containers/glass/bottle/B = new(usr.loc) + + B.icon_state = "bottle-1" + B.reagents.add_reagent(R.id, 60) + B.name = "[B.name] of [R.name]" diff --git a/code/modules/admin/verbs/randomverbs.dm b/code/modules/admin/verbs/randomverbs.dm index bc0547c4b5a..91c8b5ef93a 100644 --- a/code/modules/admin/verbs/randomverbs.dm +++ b/code/modules/admin/verbs/randomverbs.dm @@ -662,12 +662,14 @@ Traitors and the like can also be revived with the previous role mostly intact. //New message handling post_comm_message(customname, replacetext(input, "\n", "
")) - switch(tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No"))) - if("Yes") - command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); - if("No") - to_world(span_red("New [using_map.company_name] Update available at all communication consoles.")) - world << sound('sound/AI/commandreport.ogg') + var/confirm = tgui_alert(usr, "Should this be announced to the general population?","Show world?",list("Yes","No")) + if(!confirm) + return + if(confirm == "Yes") + command_announcement.Announce(input, customname, new_sound = 'sound/AI/commandreport.ogg', msg_sanitized = 1); + else + to_world(span_red("New [using_map.company_name] Update available at all communication consoles.")) + world << sound('sound/AI/commandreport.ogg') log_admin("[key_name(src)] has created a command report: [input]") message_admins("[key_name_admin(src)] has created a command report", 1) diff --git a/code/modules/client/preference_setup/antagonism/02_candidacy.dm b/code/modules/client/preference_setup/antagonism/02_candidacy.dm index d4000472749..5c1c58fc72f 100644 --- a/code/modules/client/preference_setup/antagonism/02_candidacy.dm +++ b/code/modules/client/preference_setup/antagonism/02_candidacy.dm @@ -20,10 +20,11 @@ var/global/list/special_roles = list( //keep synced with the defines BE_* in set //VOREStation Add "lost drone" = 1, // 16 "maint pred" = 1, // 17 - "morph" = 1, // 18 - "corgi" = 1, // 19 - "cursed sword" = 1, // 20 - "Ship Survivor" = 1, // 21 + "maint lurker" = 1, // 18 + "morph" = 1, // 19 + "corgi" = 1, // 20 + "cursed sword" = 1, // 21 + "Ship Survivor" = 1, // 22 //VOREStation Add End ) diff --git a/code/modules/client/preference_setup/vore/02_size.dm b/code/modules/client/preference_setup/vore/02_size.dm index 26cfdc197b5..447b3298371 100644 --- a/code/modules/client/preference_setup/vore/02_size.dm +++ b/code/modules/client/preference_setup/vore/02_size.dm @@ -68,35 +68,7 @@ if(!pref.voice_sound) character.voice_sounds_list = talk_sound else - switch(pref.voice_sound) - if("beep-boop") - character.voice_sounds_list = talk_sound - if("goon speak 1") - character.voice_sounds_list = goon_speak_one_sound - if("goon speak 2") - character.voice_sounds_list = goon_speak_two_sound - if("goon speak 3") - character.voice_sounds_list = goon_speak_three_sound - if("goon speak 4") - character.voice_sounds_list = goon_speak_four_sound - if("goon speak blub") - character.voice_sounds_list = goon_speak_blub_sound - if("goon speak bottalk") - character.voice_sounds_list = goon_speak_bottalk_sound - if("goon speak buwoo") - character.voice_sounds_list = goon_speak_buwoo_sound - if("goon speak cow") - character.voice_sounds_list = goon_speak_cow_sound - if("goon speak lizard") - character.voice_sounds_list = goon_speak_lizard_sound - if("goon speak pug") - character.voice_sounds_list = goon_speak_pug_sound - if("goon speak pugg") - character.voice_sounds_list = goon_speak_pugg_sound - if("goon speak roach") - character.voice_sounds_list = goon_speak_roach_sound - if("goon speak skelly") - character.voice_sounds_list = goon_speak_skelly_sound + character.voice_sounds_list = get_talk_sound(pref.voice_sound) character.custom_speech_bubble = pref.custom_speech_bubble /datum/category_item/player_setup_item/vore/size/content(var/mob/user) diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index f2d0f5b2da4..915d2307791 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -482,3 +482,243 @@ var/list/preferences_datums = list() save_character() attempt_vr(user.client?.prefs_vr,"load_vore","") ShowChoices(user) + +/datum/preferences/proc/vanity_copy_to(var/mob/living/carbon/human/character, var/copy_name, var/copy_flavour = TRUE, var/copy_ooc_notes = FALSE, var/convert_to_prosthetics = FALSE) + //snowflake copy_to, does not copy anything but the vanity things + //does not check if the name is the same, do that in any proc that calls this proc + /* + name, nickname, flavour, OOC notes + gender, sex + custom species name, custom bodytype, weight, scale, scaling center, sound type, sound freq + custom say verbs + ears, wings, tail, hair, facial hair + ears colors, wings colors, tail colors + body color, prosthetics (if they're a protean) (convert to DSI if protean and not prosthetic), eye color, hair color etc + markings + custom synth markings toggle, custom synth color toggle + digitigrade + blood color + */ + if (copy_name) + if(CONFIG_GET(flag/humans_need_surnames)) + var/firstspace = findtext(real_name, " ") + var/name_length = length(real_name) + if(!firstspace) //we need a surname + real_name += " [pick(last_names)]" + else if(firstspace == name_length) + real_name += "[pick(last_names)]" + character.real_name = real_name + character.name = character.real_name + if(character.dna) + character.dna.real_name = character.real_name + character.nickname = nickname + character.gender = biological_gender + character.identifying_gender = identifying_gender + + character.r_eyes = r_eyes + character.g_eyes = g_eyes + character.b_eyes = b_eyes + character.h_style = h_style + character.r_hair = r_hair + character.g_hair = g_hair + character.b_hair = b_hair + character.r_grad = r_grad + character.g_grad = g_grad + character.b_grad = b_grad + character.f_style = f_style + character.r_facial = r_facial + character.g_facial = g_facial + character.b_facial = b_facial + character.r_skin = r_skin + character.g_skin = g_skin + character.b_skin = b_skin + character.s_tone = s_tone + character.h_style = h_style + character.grad_style= grad_style + character.f_style = f_style + character.grad_style= grad_style + character.b_type = b_type + character.synth_color = synth_color + character.r_synth = r_synth + character.g_synth = g_synth + character.b_synth = b_synth + character.synth_markings = synth_markings + + var/list/ear_styles = get_available_styles(global.ear_styles_list) + character.ear_style = ear_styles[ear_style] + character.r_ears = r_ears + character.b_ears = b_ears + character.g_ears = g_ears + character.r_ears2 = r_ears2 + character.b_ears2 = b_ears2 + character.g_ears2 = g_ears2 + character.r_ears3 = r_ears3 + character.b_ears3 = b_ears3 + character.g_ears3 = g_ears3 + + character.ear_secondary_style = ear_styles[ear_secondary_style] + character.ear_secondary_colors = SANITIZE_LIST(ear_secondary_colors) + + var/list/tail_styles = get_available_styles(global.tail_styles_list) + character.tail_style = tail_styles[tail_style] + character.r_tail = r_tail + character.b_tail = b_tail + character.g_tail = g_tail + character.r_tail2 = r_tail2 + character.b_tail2 = b_tail2 + character.g_tail2 = g_tail2 + character.r_tail3 = r_tail3 + character.b_tail3 = b_tail3 + character.g_tail3 = g_tail3 + + var/list/wing_styles = get_available_styles(global.wing_styles_list) + character.wing_style = wing_styles[wing_style] + character.r_wing = r_wing + character.b_wing = b_wing + character.g_wing = g_wing + character.r_wing2 = r_wing2 + character.b_wing2 = b_wing2 + character.g_wing2 = g_wing2 + character.r_wing3 = r_wing3 + character.b_wing3 = b_wing3 + character.g_wing3 = g_wing3 + + character.set_gender(biological_gender) + + // Destroy/cyborgize organs and limbs. + if (convert_to_prosthetics) //should only really be run for proteans + var/list/organs_to_edit = list() + for (var/name in list(BP_TORSO, BP_HEAD, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/O = character.organs_by_name[name] + if (O) + var/x = organs_to_edit.Find(O.parent_organ) + if (x == 0) + organs_to_edit += name + else + organs_to_edit.Insert(x+(O.robotic == ORGAN_NANOFORM ? 1 : 0), name) + for(var/name in organs_to_edit) + var/status = organ_data[name] + var/obj/item/organ/external/O = character.organs_by_name[name] + if(O) + if(status == "amputated") + continue + else if(status == "cyborg") + O.robotize(rlimb_data[name]) + else + var/bodytype + var/datum/species/selected_species = GLOB.all_species[species] + if(selected_species.selects_bodytype) + bodytype = custom_base + else + bodytype = selected_species.get_bodytype() + var/dsi_company = GLOB.dsi_to_species[bodytype] + if (!dsi_company) + dsi_company = "DSI - Adaptive" + O.robotize(dsi_company) + + for(var/N in character.organs_by_name) + var/obj/item/organ/external/O = character.organs_by_name[N] + O.markings.Cut() + + var/priority = 0 + for(var/M in body_markings) + priority += 1 + var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[M] + + for(var/BP in mark_datum.body_parts) + var/obj/item/organ/external/O = character.organs_by_name[BP] + if(O) + O.markings[M] = list("color" = body_markings[M][BP]["color"], "datum" = mark_datum, "priority" = priority, "on" = body_markings[M][BP]["on"]) + character.markings_len = priority + + var/list/last_descriptors = list() + if(islist(body_descriptors)) + last_descriptors = body_descriptors.Copy() + body_descriptors = list() + + var/datum/species/mob_species = GLOB.all_species[species] + if(LAZYLEN(mob_species.descriptors)) + for(var/entry in mob_species.descriptors) + var/datum/mob_descriptor/descriptor = mob_species.descriptors[entry] + if(istype(descriptor)) + if(isnull(last_descriptors[entry])) + body_descriptors[entry] = descriptor.default_value // Species datums have initial default value. + else + body_descriptors[entry] = CLAMP(last_descriptors[entry], 1, LAZYLEN(descriptor.standalone_value_descriptors)) + character.descriptors = body_descriptors + + if (copy_flavour) + character.flavor_texts["general"] = flavor_texts["general"] + character.flavor_texts["head"] = flavor_texts["head"] + character.flavor_texts["face"] = flavor_texts["face"] + character.flavor_texts["eyes"] = flavor_texts["eyes"] + character.flavor_texts["torso"] = flavor_texts["torso"] + character.flavor_texts["arms"] = flavor_texts["arms"] + character.flavor_texts["hands"] = flavor_texts["hands"] + character.flavor_texts["legs"] = flavor_texts["legs"] + character.flavor_texts["feet"] = flavor_texts["feet"] + if (copy_ooc_notes) + character.ooc_notes = metadata + + character.weight = weight_vr + character.weight_gain = weight_gain + character.weight_loss = weight_loss + character.fuzzy = fuzzy + character.offset_override = offset_override + character.voice_freq = voice_freq + character.resize(size_multiplier, animate = FALSE, ignore_prefs = TRUE) + + var/list/traits_to_copy = list(/datum/trait/neutral/tall, + /datum/trait/neutral/taller, + /datum/trait/neutral/short, + /datum/trait/neutral/shorter, + /datum/trait/neutral/obese, + /datum/trait/neutral/fat, + /datum/trait/neutral/thin, + /datum/trait/neutral/thinner, + /datum/trait/neutral/micro_size_down, + /datum/trait/neutral/micro_size_up) + //reset all the above trait vars + if (character.species) + character.species.micro_size_mod = 0 + character.species.icon_scale_x = 1 + character.species.icon_scale_y = 1 + for (var/trait in neu_traits) + if (trait in traits_to_copy) + var/datum/trait/instance = all_traits[trait] + if (!instance) + continue + for (var/to_edit in instance.var_changes) + character.species.vars[to_edit] = instance.var_changes[to_edit] + character.update_transform() + if(!voice_sound) + character.voice_sounds_list = talk_sound + else + character.voice_sounds_list = get_talk_sound(voice_sound) + + character.species?.blood_color = blood_color + + var/datum/species/selected_species = GLOB.all_species[species] + var/bodytype_selected + if(selected_species.selects_bodytype) + bodytype_selected = custom_base + else + bodytype_selected = selected_species.get_bodytype(character) + + character.dna.base_species = bodytype_selected + character.species.base_species = bodytype_selected + character.species.vanity_base_fit = bodytype_selected + if (istype(character.species, /datum/species/shapeshifter)) + wrapped_species_by_ref["\ref[character]"] = bodytype_selected + + character.custom_species = custom_species + character.custom_say = lowertext(trim(custom_say)) + character.custom_ask = lowertext(trim(custom_ask)) + character.custom_whisper = lowertext(trim(custom_whisper)) + character.custom_exclaim = lowertext(trim(custom_exclaim)) + + character.digitigrade = selected_species.digi_allowed ? digitigrade : 0 + + character.dna.ResetUIFrom(character) + character.force_update_limbs() + character.regenerate_icons() diff --git a/code/modules/clothing/spacesuits/rig/modules/protean.dm b/code/modules/clothing/spacesuits/rig/modules/protean.dm new file mode 100644 index 00000000000..f1e78a5e074 --- /dev/null +++ b/code/modules/clothing/spacesuits/rig/modules/protean.dm @@ -0,0 +1,209 @@ +/* +A collection of Protean rigsuit modules, intended to encourage Symbiotic relations with a host. +All of these should require someone else to be wearing the Protean to function. +These should come standard with the Protean rigsuit, unless you want them to work for some upgrades. +*/ + + +//This rig module feeds nutrition directly from the wearer to the Protean, to help them stay charged while worn. +/obj/item/rig_module/protean + permanent = 1 + +/obj/item/rig_module/protean/syphon + name = "Protean Metabolic Syphon" + desc = "This should never be outside of a RIG." + icon_state = "flash" + interface_name = "Protean Metabolic Syphon" + interface_desc = "Toggle to drain nutrition/power from the user directly into the Protean's own energy stores." + toggleable = 1 + activate_string = "Enable Syphon" + deactivate_string = "Disable Syphon" + +/obj/item/rig_module/protean/syphon/activate() + if(!..()) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + if(H) + to_chat(usr, span_boldnotice("You activate the suit's energy syphon.")) + to_chat(H, span_warning("Your suit begins to sap at your own energy stores.")) + active = 1 + else + return 0 + +/obj/item/rig_module/protean/syphon/deactivate(var/forced) + if(!..()) + return 0 + if(forced) + active = 0 + return + var/mob/living/carbon/human/H = holder.wearer + if(H) + to_chat(usr, span_boldnotice("You deactivate the suit's energy syphon.")) + to_chat(H, span_warning("Your suit ceases from sapping your own energy.")) + active = 0 + else + return 0 + +/obj/item/rig_module/protean/syphon/process() + if(active) + var/mob/living/carbon/human/H = holder.wearer + var/mob/living/P = holder?:myprotean + if(istype(H.species, /datum/species/protean)) + to_chat(H, span_warning("Your Protean modules do not function on yourself.")) + deactivate(1) + else + P = P?:humanform + if((H.nutrition >= 100) && (P.nutrition <= 5000)) + H.nutrition -= 10 + P.nutrition += 10 + +//This rig module allows a worn Protean to toggle and configure its armor settings. +/obj/item/rig_module/protean/armor + name = "Protean Adaptive Armor" + desc = "This should never be outside of a RIG." + interface_name = "Protean Adaptive Armor" + interface_desc = "Adjusts the proteans deployed armor values to fit the needs of the wearer." + usable = 1 + toggleable = 1 + activate_string = "Enable Armor" + deactivate_string = "Disable Armor" + engage_string = "Configure Armor" + var/list/armor_settings = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0) + var/armor_weight_ratio = 0.01 //This amount of slowdown per 1% of armour. 3 slowdown at the max armour. + +/obj/item/rig_module/protean/armor/engage() + var/armor_chosen = input(usr, "Which armor to adjust?", "Protean Armor") as null|anything in armor_settings + if(armor_chosen) + var/armorvalue = tgui_input_number(usr, "Set armour reduction value (Max of 60%)", "Protean Armor",0,60) + if(isnum(armorvalue)) + armor_settings[armor_chosen] = armorvalue + interface_desc = initial(interface_desc) + slowdown = 0 + for(var/entry in armor_settings) //This is dumb and ugly but I dont feel like rewriting rig TGUI just to make this a pretty list + interface_desc += " [entry]: [armor_settings[entry]]" + slowdown += armor_settings[entry]*armor_weight_ratio + interface_desc += " Slowdown: [slowdown]" + +/obj/item/rig_module/protean/armor/activate() + if(holder?:assimilated_rig) + to_chat(usr, span_bolddanger("Armor module non-functional while a RIG is assimilated.")) + return + if(!..(1)) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + if(H) + var/list/temparmor = list("bio" = 100, "rad" = 100) + temparmor = armor_settings + temparmor + to_chat(usr, span_boldnotice("You signal the suit to harden.")) + to_chat(H, span_notice("Your suit hardens in response to physical trauma.")) + holder.armor = temparmor.Copy() + for(var/obj/item/piece in list(holder.gloves,holder.helmet,holder.boots,holder.chest)) + piece.armor = temparmor.Copy() + holder.slowdown = slowdown + active = 1 + else + return 0 + +/obj/item/rig_module/protean/armor/deactivate(var/forced) + if(!..(1)) + return 0 + if(forced) + holder.armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100) + for(var/obj/item/piece in list(holder.gloves,holder.helmet,holder.boots,holder.chest)) + piece.armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100) + holder.slowdown = initial(slowdown) + active = 0 + return + var/mob/living/carbon/human/H = holder.wearer + if(H) + to_chat(usr, span_boldnotice("You signal the suit to relax.")) + to_chat(H, span_warning("Your suit softens.")) + holder.armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100) + for(var/obj/item/piece in list(holder.gloves,holder.helmet,holder.boots,holder.chest)) + piece.armor = list("melee" = 0, "bullet" = 0, "laser" = 0,"energy" = 0, "bomb" = 0, "bio" = 100, "rad" = 100) + holder.slowdown = initial(slowdown) + active = 0 + else + return 0 + +/obj/item/rig_module/protean/armor/process() + if(active) + var/mob/living/carbon/human/H = holder.wearer + if(istype(H.species, /datum/species/protean)) + to_chat(H, span_warning("Your Protean modules do not function on yourself.")) + deactivate(1) + + +//This rig module lets a Protean expend its metal stores to heal its host +/obj/item/rig_module/protean/healing + name = "Protean Restorative Nanites" + desc = "This should never be outside of a RIG." + interface_name = "Protean Restorative Nanites" + interface_desc = "Utilises stored steel from the Protean to slowly heal and repair the wearer." + toggleable = 1 + activate_string = "Enable Healing" + deactivate_string = "Disable Healing" + var/datum/modifier/healing + +/obj/item/rig_module/protean/healing/activate() + if(!..(1)) + return 0 + + var/mob/living/carbon/human/H = holder.wearer + var/mob/living/P = holder?:myprotean + if(H && P) + if(istype(H.species, /datum/species/protean)) + to_chat(H, span_warning("Your Protean modules do not function on yourself.")) + return 0 + var/obj/item/organ/internal/nano/refactory/R = P.nano_get_refactory() + if(R.get_stored_material(MAT_STEEL) >= 100) + healing = holder.wearer.add_modifier(/datum/modifier/protean/steel, origin = R) + to_chat(usr, span_boldnotice("You activate the suit's restorative nanites.")) + to_chat(H, span_warning("Your suit begins mending your injuries.")) + active = 1 + return 1 + return 0 + +/obj/item/rig_module/protean/healing/deactivate() + if(!..(1)) + return 0 + var/mob/living/carbon/human/H = holder.wearer + if(H) + to_chat(usr, span_boldnotice("You deactivate the suit's restorative nanites.")) + to_chat(H, span_warning("Your suit is no longer mending your injuries.")) + active = 0 + if(healing) + healing.expire() + healing = null + return 1 + else + return 0 + +/obj/item/rig_module/protean/healing/process() + if(active) + var/mob/living/carbon/human/H = holder.wearer + var/mob/living/P = holder?:myprotean + if((istype(H.species, /datum/species/protean)) || !H || !P) + to_chat(H, span_warning("Your Protean modules do not function on yourself.")) + deactivate() + return + var/obj/item/organ/internal/nano/refactory/R = P.nano_get_refactory() + if((!R.get_stored_material(MAT_STEEL))) + to_chat(H, span_warning("Your [holder] is out of steel.")) + deactivate() + return + +/obj/item/rig_module/protean/healing/accepts_item(var/obj/item/stack/material/steel/S, var/mob/living/user) + + if(!istype(S) || !istype(user)) + return 0 + + var/mob/living/P = holder?:myprotean + var/obj/item/organ/internal/nano/refactory/R = P?.nano_get_refactory() + + if(R?.add_stored_material(S.material.name,1*S.perunit) && S.use(1)) + to_chat(user, span_boldnotice("You directly feed some steel to the [holder].")) + return 1 + return 0 diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index 653ea9a58a1..6dba8b8f05a 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -100,6 +100,11 @@ // Action button actions_types = list(/datum/action/item_action/hardsuit_interface) + // Protean + var/protean = 0 + var/obj/item/storage/backpack/rig_storage + permeability_coefficient = 0 //Protect the squishies, after all this shit should be waterproof. + /obj/item/rig/New() ..() @@ -538,7 +543,8 @@ offline = 0 if(istype(wearer) && !wearer.wearing_rig) wearer.wearing_rig = src - slowdown = initial(slowdown) + if(!istype(src,/obj/item/rig/protean)) // Stupid snowflake protean special check for rig assimilation code + slowdown = initial(slowdown) if(offline) if(offline == 1) @@ -914,15 +920,24 @@ wearer.lay_down() to_chat(user, span_notice("\The [wearer] is now [wearer.resting ? "resting" : "getting up"].")) -/obj/item/rig/proc/forced_move(var/direction, var/mob/user) +/obj/item/rig/proc/forced_move(var/direction, var/mob/user, var/ai_moving = TRUE) // Why is all this shit in client/Move()? Who knows? if(world.time < wearer_move_delay) return - if(!wearer || !wearer.loc || !ai_can_move_suit(user, check_user_module = 1)) + if(!wearer || !wearer.loc) // Removed some stuff for protean living hardsuit return +// Added this for protean living hardsuit + wearer_move_delay = world.time + 2 + if(ai_moving) + if(!ai_can_move_suit(user, check_user_module = 1)) + return + // AIs are a bit slower than regular and ignore move intent. + // Moved this to where it's relevant + wearer_move_delay = world.time + ai_controlled_move_delay + //This is sota the goto stop mobs from moving var if(wearer.transforming || !wearer.canmove) return @@ -953,9 +968,6 @@ to_chat(src, span_notice("Your host is pinned to a wall by [wearer.pinned[1]]!")) return 0 - // AIs are a bit slower than regular and ignore move intent. - wearer_move_delay = world.time + ai_controlled_move_delay - if(istype(wearer.buckled, /obj/vehicle)) //manually set move_delay for vehicles so we don't inherit any mob movement penalties //specific vehicle move delays are set in code\modules\vehicles\vehicle.dm @@ -980,7 +992,10 @@ wearer_move_delay += 2 return wearer.buckled.relaymove(wearer,direction) - cell.use(200) //Arbitrary, TODO + var/power_cost = 200 + if(!ai_moving) + power_cost = 20 + cell.use(power_cost) //Arbitrary, TODO wearer.Move(get_step(get_turf(wearer),direction),direction) // This returns the rig if you are contained inside one, but not if you are wearing it diff --git a/code/modules/economy/vending_machines.dm b/code/modules/economy/vending_machines.dm index 4663e9bb54f..dcd6b42cd6d 100644 --- a/code/modules/economy/vending_machines.dm +++ b/code/modules/economy/vending_machines.dm @@ -689,6 +689,7 @@ /obj/item/multitool = 4, /obj/item/cell/high = 10, /obj/item/airlock_electronics = 10, + /obj/item/circuitboard/airlock_cycling = 20, /obj/item/module/power_control = 10, /obj/item/circuitboard/airalarm = 10, /obj/item/circuitboard/firealarm = 10, diff --git a/code/modules/env_message/env_message.dm b/code/modules/env_message/env_message.dm index fc5ea527b31..69e1364b7b4 100644 --- a/code/modules/env_message/env_message.dm +++ b/code/modules/env_message/env_message.dm @@ -8,7 +8,7 @@ var/global/list/env_messages = list() mouse_opacity = TRUE anchored = TRUE var/list/message_list = list() - var/combined_message = "You should not see this" + var/combined_message = DEVELOPER_WARNING_NAME /obj/effect/env_message/Initialize(mapload) .=..() diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index f07c1a7ff13..be8ee51f25f 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -3,7 +3,8 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) var/datum/disease/chosen_disease var/list/disease_blacklist = list( /datum/disease/advance, - /datum/disease/food_poisoning + /datum/disease/food_poisoning, + /datum/disease/gbs // Terrible. It's only in the code to scare people. ) var/static/list/transmissable_symptoms = list() var/static/list/diseases_minor = list() diff --git a/code/modules/events/maint_lurker.dm b/code/modules/events/maint_lurker.dm new file mode 100644 index 00000000000..e82950fefbc --- /dev/null +++ b/code/modules/events/maint_lurker.dm @@ -0,0 +1,22 @@ +/datum/event/maintenance_lurker + startWhen = 1 + endWhen = 30 + +/datum/event/maintenance_lurker/start() + + var/obj/effect/landmark/spawnspot = null + var/list/possibleSpawnspots = list() + for(var/obj/effect/landmark/L in landmarks_list) + if(L.name == "maint_pred") + possibleSpawnspots += L + if(possibleSpawnspots.len) + spawnspot = pick(possibleSpawnspots) + else + kill() // To prevent fake announcements + return + + if(!spawnspot) + kill() // To prevent fake announcements + return + + new /obj/structure/ghost_pod/ghost_activated/maint_lurker(get_turf(spawnspot)) diff --git a/code/modules/food/drinkingglass/glass_boxes.dm b/code/modules/food/drinkingglass/glass_boxes.dm index 20dd616ee11..978f964bd77 100644 --- a/code/modules/food/drinkingglass/glass_boxes.dm +++ b/code/modules/food/drinkingglass/glass_boxes.dm @@ -81,4 +81,16 @@ /obj/item/storage/box/glasses/coffeemug name = "box of coffee mugs" - starts_with = list(/obj/item/reagent_containers/food/drinks/glass2/coffeemug = 7) \ No newline at end of file + starts_with = list(/obj/item/reagent_containers/food/drinks/glass2/coffeemug = 7) + +/obj/item/storage/box/glasses/coffeecup_tall + name = "box of tall cups" + starts_with = list(/obj/item/reagent_containers/food/drinks/tall = 7) + +/obj/item/storage/box/glasses/coffeecup_grande + name = "box of grande cups" + starts_with = list(/obj/item/reagent_containers/food/drinks/grande = 7) + +/obj/item/storage/box/glasses/coffeecup_venti + name = "box of venti cups" + starts_with = list(/obj/item/reagent_containers/food/drinks/venti = 7) diff --git a/code/modules/food/food/drinks/cup.dm b/code/modules/food/food/drinks/cup.dm index 1c7f7b326d4..a427b619292 100644 --- a/code/modules/food/food/drinks/cup.dm +++ b/code/modules/food/food/drinks/cup.dm @@ -17,7 +17,12 @@ icon_state = "cup_brown" if(R.cup_name) - name = R.cup_name + var/prefix = " " + for(var/datum/reagent/S in reagents.reagent_list) + if(S.cup_prefix) + var/current_prefix = prefix + prefix = "[current_prefix][S.cup_prefix] " + name = "cup of[prefix][R.cup_name]" else name = "Cup of.. what?" diff --git a/code/modules/food/food/drinks/drinkingglass.dm b/code/modules/food/food/drinks/drinkingglass.dm index 3d48ab9068a..2e7eff18506 100644 --- a/code/modules/food/food/drinks/drinkingglass.dm +++ b/code/modules/food/food/drinks/drinkingglass.dm @@ -67,7 +67,12 @@ icon_state = "cup_brown" if(R.cup_name) - name = R.cup_name + var/prefix = " " + for(var/datum/reagent/S in reagents.reagent_list) + if(S.cup_prefix) + var/current_prefix = prefix + prefix = "[current_prefix][S.cup_prefix] " + name = "cup of[prefix][R.cup_name]" else name = "Cup of.. what?" @@ -160,4 +165,148 @@ reagents.add_reagent("nutriment", 30) reagents.add_reagent("iron", 10) reagents.add_reagent("protein", 15) - reagents.add_reagent("water", 45) \ No newline at end of file + reagents.add_reagent("water", 45) + + +////////////////Fancy coffee cups + +/obj/item/reagent_containers/food/drinks/tall + name = "tall cup" + desc = "A larger coffee cup." + icon_state = "tall_cup_empty" + amount_per_transfer_from_this = 5 + volume = 40 + center_of_mass = list("x"=16, "y"=16) + +/obj/item/reagent_containers/food/drinks/tall/on_reagent_change() + if (!length(reagents?.reagent_list)) + icon_state = "tall_cup_empty" + name = "tall cup" + desc = "A larger coffee cup." + center_of_mass = list("x"=16, "y"=16) + return + var/datum/reagent/R = reagents.get_master_reagent() + + if(R.cup_icon_state) + icon_state = "tall_[R.cup_icon_state]" + else + icon_state = "tall_cup_brown" + + if(R.cup_name) + var/prefix = " " + for(var/datum/reagent/S in reagents.reagent_list) + if(S.cup_prefix) + var/current_prefix = prefix + prefix = "[current_prefix][S.cup_prefix] " + name = "tall[prefix][R.cup_name]" + else + name = "tall.. what?" + + if(R.cup_desc) + desc = R.cup_desc + else + desc = "You can't really tell what this is." + + if(R.cup_center_of_mass) + center_of_mass = R.cup_center_of_mass + else + center_of_mass = list("x"=16, "y"=16) + + if(R.price_tag) + price_tag = R.price_tag + else + price_tag = null + +/obj/item/reagent_containers/food/drinks/grande + name = "grande cup" + desc = "A much taller coffee cup for people who really need their coffee." + icon_state = "grande_cup_empty" + amount_per_transfer_from_this = 5 + volume = 50 + center_of_mass = list("x"=16, "y"=16) + +/obj/item/reagent_containers/food/drinks/grande/on_reagent_change() + if (!length(reagents?.reagent_list)) + icon_state = "grande_cup_empty" + name = "grande cup" + desc = "A much taller coffee cup for people who really need their coffee." + center_of_mass = list("x"=16, "y"=16) + return + var/datum/reagent/R = reagents.get_master_reagent() + + if(R.cup_icon_state) + icon_state = "grande_[R.cup_icon_state]" + else + icon_state = "grande_cup_brown" + + if(R.cup_name) + var/prefix = " " + for(var/datum/reagent/S in reagents.reagent_list) + if(S.cup_prefix) + var/current_prefix = prefix + prefix = "[current_prefix][S.cup_prefix] " + name = "grande[prefix][R.cup_name]" + else + name = "grande.. what?" + + if(R.cup_desc) + desc = R.cup_desc + else + desc = "You can't really tell what this is." + + if(R.cup_center_of_mass) + center_of_mass = R.cup_center_of_mass + else + center_of_mass = list("x"=16, "y"=16) + + if(R.price_tag) + price_tag = R.price_tag + else + price_tag = null + +/obj/item/reagent_containers/food/drinks/venti + name = "venti cup" + desc = "A huge coffee cup for people who literally cannot function without it." + icon_state = "venti_cup_empty" + amount_per_transfer_from_this = 5 + volume = 60 + center_of_mass = list("x"=16, "y"=16) + +/obj/item/reagent_containers/food/drinks/venti/on_reagent_change() + if (!length(reagents?.reagent_list)) + icon_state = "venti_cup_empty" + name = "venti cup" + desc = "A huge coffee cup for people who literally cannot function without it." + center_of_mass = list("x"=16, "y"=16) + return + var/datum/reagent/R = reagents.get_master_reagent() + + if(R.cup_icon_state) + icon_state = "venti_[R.cup_icon_state]" + else + icon_state = "venti_cup_brown" + + if(R.cup_name) + var/prefix = " " + for(var/datum/reagent/S in reagents.reagent_list) + if(S.cup_prefix) + var/current_prefix = prefix + prefix = "[current_prefix][S.cup_prefix] " + name = "venti[prefix][R.cup_name]" + else + name = "venti.. what?" + + if(R.cup_desc) + desc = R.cup_desc + else + desc = "You can't really tell what this is." + + if(R.cup_center_of_mass) + center_of_mass = R.cup_center_of_mass + else + center_of_mass = list("x"=16, "y"=16) + + if(R.price_tag) + price_tag = R.price_tag + else + price_tag = null diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index 7910d1594bb..84b14551ba0 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -373,3 +373,26 @@ var/list/holder_mob_icon_cache = list() icon = 'icons/mob/holder_complex.dmi' var/list/generate_for_slots = list(slot_l_hand_str, slot_r_hand_str, slot_back_str) slot_flags = SLOT_BACK + +/obj/item/holder/proc/sync(var/mob/living/M) + dir = 2 + overlays.Cut() + if(M.item_state) + item_state = M.item_state + color = M.color + name = M.name + desc = M.desc + overlays |= M.overlays + +/obj/item/holder/protoblob + slot_flags = SLOT_HEAD | SLOT_OCLOTHING | SLOT_HOLSTER | SLOT_ICLOTHING | SLOT_ID | SLOT_EARS + w_class = ITEMSIZE_TINY + allowed = list(/obj/item/gun,/obj/item/flashlight,/obj/item/tank,/obj/item/suit_cooling_unit,/obj/item/melee/baton) + item_icons = list( + slot_l_hand_str = 'icons/mob/lefthand_holder.dmi', + slot_r_hand_str = 'icons/mob/righthand_holder.dmi', + slot_head_str = 'icons/mob/head.dmi', + slot_w_uniform_str = 'icons/mob/uniform.dmi', + slot_wear_suit_str = 'icons/mob/suit.dmi', + slot_r_ear_str = 'icons/mob/ears.dmi', + slot_l_ear_str = 'icons/mob/ears.dmi') diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm index 44626f5e0e2..a41f45ef356 100644 --- a/code/modules/mob/language/language.dm +++ b/code/modules/mob/language/language.dm @@ -296,9 +296,9 @@ if(href_list["default_lang"]) if(href_list["default_lang"] == "reset") if (species_language) - set_default_language(GLOB.all_languages[species_language]) + apply_default_language(GLOB.all_languages[species_language]) else - set_default_language(GLOB.all_languages[LANGUAGE_GIBBERISH]) + apply_default_language(GLOB.all_languages[LANGUAGE_GIBBERISH]) else var/datum/language/L = locate(href_list["default_lang"]) if(L && (L in languages)) diff --git a/code/modules/mob/living/carbon/human/human_helpers_vr.dm b/code/modules/mob/living/carbon/human/human_helpers_vr.dm index 7d02ca95a05..28eea3e81e9 100644 --- a/code/modules/mob/living/carbon/human/human_helpers_vr.dm +++ b/code/modules/mob/living/carbon/human/human_helpers_vr.dm @@ -126,11 +126,155 @@ var/static/icon/ingame_hud_med_vr = icon('icons/mob/hud_med_vr.dmi') //does not really need to happen, that kinda thing will only happen when putting another person's limb onto your own body return sorted +/mob/living/carbon/human/proc/transform_into_other_human(var/mob/living/carbon/human/character, var/copy_name, var/copy_flavour = TRUE, var/convert_to_prosthetics = FALSE) + /* + name, nickname, flavour, OOC notes + gender, sex + custom species name, custom bodytype, weight, scale, scaling center, sound type, sound freq + custom say verbs + ears, wings, tail, hair, facial hair + ears colors, wings colors, tail colors + body color, prosthetics (if they're a protean) (convert to DSI if protean and not prosthetic), eye color, hair color etc + markings + custom synth markings toggle, custom synth color toggle + digitigrade + blood color + */ + if (copy_name) + name = character.name + nickname = character.nickname + gender = character.gender + identifying_gender = character.identifying_gender + + r_eyes = character.r_eyes + g_eyes = character.g_eyes + b_eyes = character.b_eyes + h_style = character.h_style + r_hair = character.r_hair + g_hair = character.g_hair + b_hair = character.b_hair + r_grad = character.r_grad + g_grad = character.g_grad + b_grad = character.b_grad + f_style = character.f_style + r_facial = character.r_facial + g_facial = character.g_facial + b_facial = character.b_facial + r_skin = character.r_skin + g_skin = character.g_skin + b_skin = character.b_skin + s_tone = character.s_tone + h_style = character.h_style + grad_style = character.grad_style + f_style = character.f_style + grad_style = character.grad_style + b_type = character.b_type + synth_color = character.synth_color + r_synth = character.r_synth + g_synth = character.g_synth + b_synth = character.b_synth + synth_markings = character.synth_markings + + ear_style = character.ear_style + r_ears = character.r_ears + b_ears = character.b_ears + g_ears = character.g_ears + r_ears2 = character.r_ears2 + b_ears2 = character.b_ears2 + g_ears2 = character.g_ears2 + r_ears3 = character.r_ears3 + b_ears3 = character.b_ears3 + g_ears3 = character.g_ears3 + + tail_style = character.tail_style + r_tail = character.r_tail + b_tail = character.b_tail + g_tail = character.g_tail + r_tail2 = character.r_tail2 + b_tail2 = character.b_tail2 + g_tail2 = character.g_tail2 + r_tail3 = character.r_tail3 + b_tail3 = character.b_tail3 + g_tail3 = character.g_tail3 + + wing_style = character.wing_style + r_wing = character.r_wing + b_wing = character.b_wing + g_wing = character.g_wing + r_wing2 = character.r_wing2 + b_wing2 = character.b_wing2 + g_wing2 = character.g_wing2 + r_wing3 = character.r_wing3 + b_wing3 = character.b_wing3 + g_wing3 = character.g_wing3 + var/bodytype = character.species?.get_bodytype() + if (convert_to_prosthetics) //should only really be run for proteans + var/list/organs_to_edit = list() + for (var/name in list(BP_TORSO, BP_HEAD, BP_GROIN, BP_L_ARM, BP_R_ARM, BP_L_HAND, BP_R_HAND, BP_L_LEG, BP_R_LEG, BP_L_FOOT, BP_R_FOOT)) + var/obj/item/organ/external/O = character.organs_by_name[name] + if (O) + var/x = organs_to_edit.Find(O.parent_organ) + if (x == 0) + organs_to_edit += name + else + organs_to_edit.Insert(x+(O.robotic == ORGAN_NANOFORM ? 1 : 0), name) + for(var/name in organs_to_edit) + var/obj/item/organ/external/I = character.organs_by_name[name] + var/obj/item/organ/external/O = organs_by_name[name] + if(O) + if(I.robotic >= ORGAN_ROBOT) + O.robotize(I.model) + else + var/dsi_company = GLOB.dsi_to_species[bodytype] + if (!dsi_company) + dsi_company = "DSI - Adaptive" + O.robotize(dsi_company) + for(var/N in character.organs_by_name) + var/obj/item/organ/external/O = organs_by_name[N] + var/obj/item/organ/external/I = character.organs_by_name[N] + O.markings = I.markings.Copy() + markings_len = character.markings_len + descriptors = character.descriptors?.Copy() + if (copy_flavour) + flavor_texts = character.flavor_texts?.Copy() + weight = character.weight + weight_gain = character.weight_gain + weight_loss = character.weight_loss + fuzzy = character.fuzzy + offset_override = character.offset_override + voice_freq = character.voice_freq + if (species && character.species) + species.micro_size_mod = character.species.micro_size_mod + species.icon_scale_x = character.species.icon_scale_x + species.icon_scale_y = character.species.icon_scale_y + update_transform() + resize(character.size_multiplier, animate = TRUE, ignore_prefs = TRUE) + voice_sounds_list = character.voice_sounds_list + + species?.blood_color = character.species?.blood_color + + dna?.base_species = bodytype + species?.base_species = bodytype + species?.vanity_base_fit = bodytype + if (istype(species, /datum/species/shapeshifter)) + wrapped_species_by_ref["\ref[src]"] = bodytype + + custom_species = character.custom_species + custom_say = character.custom_say + custom_ask = character.custom_ask + custom_whisper = character.custom_whisper + custom_exclaim = character.custom_exclaim + + digitigrade = character.digitigrade + + dna?.ResetUIFrom(src) + force_update_limbs() + regenerate_icons() diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index 863e6cd0a99..09fc23d4c73 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -225,6 +225,8 @@ var/rarity_value = 1 // Relative rarity/collector value for this species. var/economic_modifier = 2 // How much money this species makes + var/vanity_base_fit //when shapeshifting using vanity_copy_to, this allows you to have add something so they can go back to their original species fit + var/vore_belly_default_variant = "H" // Determines the organs that the species spawns with and diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm index 69eee283882..ade1ef0fc8a 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift.dm @@ -16,7 +16,7 @@ var/list/wrapped_species_by_ref = list() //var/default_form = SPECIES_HUMAN //VOREStation edit /datum/species/shapeshifter/get_valid_shapeshifter_forms(var/mob/living/carbon/human/H) - return valid_transform_species + return list(vanity_base_fit)|valid_transform_species /datum/species/shapeshifter/get_icobase(var/mob/living/carbon/human/H, var/get_deform) if(!H) return ..(null, get_deform) diff --git a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm index 5a0d5ce3a63..6dc8616210c 100644 --- a/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm +++ b/code/modules/mob/living/carbon/human/species/species_shapeshift_vr.dm @@ -58,7 +58,7 @@ /mob/living/carbon/human/proc/shapeshifter_select_secondary_ears() set name = "Select Secondary Ears" - set category = "Abilities" + set category = "Abilities.Shapeshift" if(stat || world.time < last_special) return @@ -78,12 +78,13 @@ ear_secondary_style = ear_styles_list[pretty_ear_styles[new_ear_style]] // Handle color picks - var/list/new_colors = list() - for(var/channel in 1 to ear_secondary_style.get_color_channel_count()) - var/channel_name = GLOB.fancy_sprite_accessory_color_channel_names[channel] - var/default = LAZYACCESS(ear_secondary_colors, channel) || "#ffffff" - var/new_color = input(usr, "Pick [channel_name]", "Ear Color ([channel_name])", default) as color | null - new_colors += new_color || default + if(ear_secondary_style) + var/list/new_colors = list() + for(var/channel in 1 to ear_secondary_style.get_color_channel_count()) + var/channel_name = GLOB.fancy_sprite_accessory_color_channel_names[channel] + var/default = LAZYACCESS(ear_secondary_colors, channel) || "#ffffff" + var/new_color = input(usr, "Pick [channel_name]", "Ear Color ([channel_name])", default) as color | null + new_colors += new_color || default update_hair() diff --git a/code/modules/mob/living/carbon/human/species/station/prommie_blob.dm b/code/modules/mob/living/carbon/human/species/station/prommie_blob.dm index 0b7e542bc98..ad51161bc97 100644 --- a/code/modules/mob/living/carbon/human/species/station/prommie_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/prommie_blob.dm @@ -12,13 +12,15 @@ //appearance_flags = RADIATION_GLOWS shock_resist = 0 // Lets not be immune to zaps. friendly = list("nuzzles", "glomps", "snuggles", "cuddles", "squishes") // lets be cute :3 - melee_damage_upper = 0 - melee_damage_lower = 0 + harm_intent_damage = 3 + melee_damage_lower = 5 + melee_damage_upper = 5 player_msg = "You're a little squisher! Your cuteness level has increased tenfold." heat_damage_per_tick = 20 // Hot and cold are bad, but cold is AS bad for prommies as it is for slimes. cold_damage_per_tick = 20 //glow_range = 0 //glow_intensity = 0 + has_hands = 1 // brings in line with Proteans' own blob form. var/mob/living/carbon/human/humanform var/datum/modifier/healing @@ -86,6 +88,13 @@ humanform.species.update_misc_tabs(src) /mob/living/simple_mob/slime/promethean/handle_special() // Should disable default slime healing, we'll use nutrition based heals instead. +// They already heal from their carbon form while even in slime form, but this is for a small bonus healing for being unformed. + adjustOxyLoss(-0.2) + adjustToxLoss(-0.2) + adjustFireLoss(-0.2) + adjustCloneLoss(-0.2) + adjustBruteLoss(-0.2) + adjustHalLoss(-6) // HalLoss ticks down FAST if(rad_glow) rad_glow = CLAMP(rad_glow,0,250) set_light(max(1,min(5,rad_glow/15)), max(1,min(10,rad_glow/25)), color) @@ -368,25 +377,14 @@ drop_from_inventory(H) things_to_drop -= H - for(var/obj/item/I in things_to_drop) //rip hoarders - drop_from_inventory(I) - - if(w_uniform && istype(w_uniform,/obj/item/clothing)) //No webbings tho. We do this after in case a suit was in the way - var/obj/item/clothing/uniform = w_uniform - if(LAZYLEN(uniform.accessories)) - for(var/obj/item/clothing/accessory/A in uniform.accessories) - if(is_type_in_list(A, disallowed_protean_accessories)) - uniform.remove_accessory(null,A) //First param is user, but adds fingerprints and messages - //Size update blob.transform = matrix()*size_multiplier blob.size_multiplier = size_multiplier - if(l_hand) blob.prev_left_hand = l_hand //Won't save them if dropped above, but necessary if handdrop is disabled. - if(r_hand) blob.prev_right_hand = r_hand + if(l_hand) drop_from_inventory(l_hand) + if(r_hand) drop_from_inventory(r_hand) //Put our owner in it (don't transfer var/mind) - blob.Weaken(2) blob.transforming = TRUE blob.ckey = ckey blob.ooc_notes = ooc_notes @@ -394,6 +392,7 @@ blob.ooc_notes_dislikes = ooc_notes_dislikes blob.transforming = FALSE blob.name = name + blob.real_name = real_name blob.nutrition = nutrition blob.color = rgb(r_skin, g_skin, b_skin) playsound(src.loc, "sound/effects/slime_squish.ogg", 15) @@ -411,6 +410,18 @@ remove_verb(blob, /mob/living/proc/ventcrawl) // Absolutely not. remove_verb(blob, /mob/living/simple_mob/proc/set_name) // We already have a name. temporary_form = blob + + var/obj/item/radio/R = null + if(isradio(l_ear)) + R = l_ear + if(isradio(r_ear)) + R = r_ear + if(R) + blob.mob_radio = R + R.forceMove(blob) + if(wear_id) + blob.myid = wear_id.GetID() + //Mail them to nullspace moveToNullspace() @@ -460,7 +471,6 @@ forceMove(reform_spot) //Put our owner in it (don't transfer var/mind) - Weaken(2) playsound(src.loc, "sound/effects/slime_squish.ogg", 15) transforming = TRUE ckey = blob.ckey @@ -493,8 +503,15 @@ B.owner = src //vore_organs.Cut() - if(blob.prev_left_hand) put_in_l_hand(blob.prev_left_hand) //The restore for when reforming. - if(blob.prev_right_hand) put_in_r_hand(blob.prev_right_hand) + + if(blob.l_hand) blob.drop_from_inventory(blob.l_hand) + if(blob.r_hand) blob.drop_from_inventory(blob.r_hand) + + if(blob.mob_radio) + blob.mob_radio.forceMove(src) + blob.mob_radio = null + if(blob.myid) + blob.myid = null Life(1) //Fix my blindness right meow //Has to be moved up here, there exists a circumstance where blob could be deleted without vore organs moving right. @@ -522,3 +539,8 @@ else if(humanform.say_understands(other, speaking)) //So they're speaking something other than promethean or sign, let's just ask our original mob if it understands return TRUE else return FALSE + +/mob/living/simple_mob/slime/promethean/character_directory_species() + if (humanform) + return "[humanform.custom_species ? humanform.custom_species : (humanform.species ? humanform.species.name : "Promethean Blob")]" + return "Promethean Blob" diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index 40d366f7d55..961fc177bce 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -5,9 +5,9 @@ tt_desc = "Animated nanogoop" icon = 'icons/mob/species/protean/protean.dmi' icon_state = "to_puddle" - icon_living = "puddle2" - icon_rest = "rest" - icon_dead = "puddle" + icon_living = "puddle0-eyes" //Null icon, since we're made of overlays now. + icon_rest = "puddle0-eyes" + icon_dead = "puddle0-eyes" faction = FACTION_NEUTRAL maxHealth = 200 @@ -16,11 +16,11 @@ show_stat_health = FALSE //We will do it ourselves - response_help = "pets" - response_disarm = "gently pushes aside" - response_harm = "hits" + response_help = "pets the" + response_disarm = "gently pushes aside the " + response_harm = "hits the" - harm_intent_damage = 2 + harm_intent_damage = 3 melee_damage_lower = 5 melee_damage_upper = 5 attacktext = list("slashed") @@ -34,22 +34,32 @@ min_n2 = 0 max_n2 = 0 minbodytemp = 0 - maxbodytemp = 900 - movement_cooldown = -0.5 // Should mean that the little blurb about being quicker in blobform rings true. May need further adjusting. + maxbodytemp = 1100 + movement_cooldown = 0 + hunger_rate = 0 var/mob/living/carbon/human/humanform var/obj/item/organ/internal/nano/refactory/refactory var/datum/modifier/healing - var/obj/prev_left_hand - var/obj/prev_right_hand - var/human_brute = 0 var/human_burn = 0 - player_msg = "In this form, you can move a little faster, your health will regenerate as long as you have metal in you, and you can ventcrawl!" + player_msg = "In this form, your health will regenerate as long as you have metal in you." - can_buckle = TRUE //Blobsurfing + can_buckle = 1 + buckle_lying = 1 + mount_offset_x = 0 + mount_offset_y = 0 + has_hands = 1 + shock_resist = 1 + nameset = 1 + holder_type = /obj/item/holder/protoblob + var/hiding = 0 + vore_icons = 1 + vore_active = 1 + + plane = ABOVE_MOB_PLANE //Necessary for overlay based icons /datum/say_list/protean_blob speak = list("Blrb?","Sqrsh.","Glrsh!") @@ -63,14 +73,88 @@ humanform = H updatehealth() refactory = locate() in humanform.internal_organs - add_verb(src, /mob/living/proc/ventcrawl) - add_verb(src, /mob/living/proc/hide) + add_verb(src,/mob/living/proc/ventcrawl) + add_verb(src,/mob/living/proc/usehardsuit) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_partswap) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_regenerate) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_metalnom) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_blobform) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_rig_transform) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/appearance_switch) + add_verb(src,/mob/living/simple_mob/protean_blob/proc/nano_latch) else update_icon() + add_verb(src,/mob/living/simple_mob/proc/animal_mount) + add_verb(src,/mob/living/proc/toggle_rider_reins) + +//Hidden verbs for macro hotkeying +/mob/living/simple_mob/protean_blob/proc/nano_partswap() + set name = "Ref - Single Limb" + set desc = "Allows you to replace and reshape your limbs as you see fit." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_partswap() + +/mob/living/simple_mob/protean_blob/proc/nano_regenerate() + set name = "Total Reassembly (wip)" + set desc = "Completely reassemble yourself from whatever save slot you have loaded in preferences. Assuming you meet the requirements." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_regenerate() + +/mob/living/simple_mob/protean_blob/proc/nano_blobform() + set name = "Toggle Blobform" + set desc = "Switch between amorphous and humanoid forms." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_blobform() + +/mob/living/simple_mob/protean_blob/proc/nano_metalnom() + set name = "Ref - Store Metals" + set desc = "If you're holding a stack of material, you can consume some and store it for later." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_metalnom() + +/mob/living/simple_mob/protean_blob/proc/nano_rig_transform() + set name = "Modify Form - Hardsuit" + set desc = "Allows a protean to retract its mass into its hardsuit module at will." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_rig_transform() + +/mob/living/simple_mob/protean_blob/proc/appearance_switch() + set name = "Switch Blob Appearance" + set desc = "Allows a protean blob to switch its outwards appearance." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.appearance_switch() + +/mob/living/simple_mob/protean_blob/proc/nano_latch() + set name = "Latch/Unlatch host" + set desc = "Allows a protean to forcibly latch or unlatch from a host." + //set category = "Abilities.Protean" + set hidden = 1 + humanform.nano_latch() /mob/living/simple_mob/protean_blob/Login() - . = ..() - copy_from_prefs_vr(bellies = FALSE) //Load vore prefs + ..() + plane_holder.set_vis(VIS_AUGMENTED, 1) + plane_holder.set_vis(VIS_CH_HEALTH_VR, 1) + plane_holder.set_vis(VIS_CH_ID, 1) + plane_holder.set_vis(VIS_CH_STATUS_R, 1) + plane_holder.set_vis(VIS_CH_BACKUP, 1) //Gonna need these so we can see the status of our host. Could probably write it so this only happens when worn, but eeehhh + if(!riding_datum) + riding_datum = new /datum/riding/simple_mob/protean_blob(src) + +/datum/riding/simple_mob/protean_blob/handle_vehicle_layer() + ridden.layer = OBJ_LAYER + +/mob/living/simple_mob/protean_blob/MouseDrop_T() + return + +/mob/living/simple_mob/protean_blob/runechat_y_offset(width, height) + return (..()) - (20*size_multiplier) /mob/living/simple_mob/protean_blob/Destroy() humanform = null @@ -90,60 +174,44 @@ /mob/living/simple_mob/protean_blob/speech_bubble_appearance() return "synthetic" -/mob/living/simple_mob/protean_blob/get_available_emotes() - return global._robot_default_emotes.Copy() - /mob/living/simple_mob/protean_blob/init_vore() return //Don't make a random belly, don't waste your time /mob/living/simple_mob/protean_blob/isSynthetic() return TRUE // yup +/mob/living/simple_mob/protean_blob/get_available_emotes() + var/list/fulllist = global._robot_default_emotes.Copy() + fulllist |= global._human_default_emotes //they're living nanites, they can make whatever sounds they want + return fulllist + /mob/living/simple_mob/protean_blob/update_misc_tabs() . = ..() if(humanform) humanform.species.update_misc_tabs(src) -/mob/living/simple_mob/protean_blob/update_icon() - if(humanform) - //Still have a refactory - if(istype(refactory)) - icon_living = "puddle2" - - //Else missing one - else - icon_living = "puddle1" - - //Not human-based - else - icon_living = "puddle0" - - ..() - /mob/living/simple_mob/protean_blob/updatehealth() + if(humanform.nano_dead_check(src)) + return if(!humanform) return ..() //Set the max maxHealth = humanform.getMaxHealth()*2 //HUMANS, and their 'double health', bleh. - //Set us to their health, but, human health ignores robolimbs so we do it 'the hard way' human_brute = humanform.getActualBruteLoss() human_burn = humanform.getActualFireLoss() - health = maxHealth - humanform.getOxyLoss() - humanform.getToxLoss() - humanform.getCloneLoss() - human_brute - human_burn + health = maxHealth - humanform.getOxyLoss() - humanform.getToxLoss() - humanform.getCloneLoss() - humanform.getBruteLoss() - humanform.getFireLoss() //Alive, becoming dead if((stat < DEAD) && (health <= 0)) - death() + humanform.death() + + nutrition = humanform.nutrition //Overhealth if(health > getMaxHealth()) health = getMaxHealth() - //Grab any other interesting values - confused = humanform.confused - radiation = humanform.radiation - paralysis = humanform.paralysis - //Update our hud if we have one if(healths) if(stat != DEAD) @@ -174,14 +242,12 @@ return ..() /mob/living/simple_mob/protean_blob/adjustBruteLoss(var/amount,var/include_robo) - amount *= 1.5 if(humanform) return humanform.adjustBruteLoss(amount) else return ..() /mob/living/simple_mob/protean_blob/adjustFireLoss(var/amount,var/include_robo) - amount *= 1.5 if(humanform) return humanform.adjustFireLoss(amount) else @@ -211,6 +277,12 @@ else return ..() +/mob/living/simple_mob/protean_blob/adjust_nutrition(amount) + if(humanform) + return humanform.adjust_nutrition(amount) + else + return ..() + /mob/living/simple_mob/protean_blob/emp_act(severity) if(humanform) return humanform.emp_act(severity) @@ -224,8 +296,10 @@ return ..() /mob/living/simple_mob/protean_blob/rad_act(severity) + if(istype(loc, /obj/item/rig)) + return //Don't irradiate us while we're in rig mode if(humanform) - return humanform.ex_act(severity) + return humanform.rad_act(severity) else return ..() @@ -235,38 +309,55 @@ else return ..() -/mob/living/simple_mob/protean_blob/death(gibbed, deathmessage = "dissolves away, leaving only a few spare parts!") +/mob/living/simple_mob/protean_blob/death(gibbed, deathmessage = "Coalesces inwards, retreating into their core componants") if(humanform) humanform.death(gibbed, deathmessage) else animate(src, alpha = 0, time = 2 SECONDS) sleep(2 SECONDS) - if(!QDELETED(src)) // Human's handle death should have taken us, but maybe we were adminspawned or something without a human counterpart - qdel(src) - /mob/living/simple_mob/protean_blob/Life() . = ..() - if(. && istype(refactory) && humanform) - if(!healing && (human_brute || human_burn) && refactory.get_stored_material(MAT_STEEL) >= 100) - healing = humanform.add_modifier(/datum/modifier/protean/steel, origin = refactory) - else if(healing && !(human_brute || human_burn)) + if(!humanform.nano_dead_check(src)) + if(. && istype(refactory) && humanform) + if(!healing && (human_brute || human_burn) && refactory.get_stored_material(MAT_STEEL) >= 100) + healing = humanform.add_modifier(/datum/modifier/protean/steel, origin = refactory) + else if(healing && !(human_brute || human_burn)) + healing.expire() + healing = null + else + if(healing) healing.expire() healing = null /mob/living/simple_mob/protean_blob/lay_down() + var/obj/item/rig/rig = src.get_rig() + if(rig) + rig.force_rest(src) + return ..() - if(resting) - animate(src,alpha = 40,time = 1 SECOND) + +/mob/living/simple_mob/protean_blob/verb/prot_hide() + set name = "Hide Self" + set desc = "Disperses your mass into a thin veil, making a trap to snatch prey with, or simply hide." + set category = "Abilities.Protean" + + if(!hiding) + cut_overlays() + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "hide" + sleep(7) mouse_opacity = 0 plane = ABOVE_OBJ_PLANE + hiding = 1 else + icon = 'icons/mob/species/protean/protean.dmi' mouse_opacity = 1 icon_state = "wake" - animate(src,alpha = 255,time = 1 SECOND) - plane = MOB_PLANE + plane = initial(plane) sleep(7) update_icon() + hiding = 0 //Potential glob noms if(can_be_drop_pred) //Toggleable in vore panel var/list/potentials = living_mobs(0) @@ -276,21 +367,15 @@ if(target.buckled) target.buckled.unbuckle_mob(target, force = TRUE) target.forceMove(vore_selected) - to_chat(target,span_vwarning("\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!")) + to_chat(target,span_warning("\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!")) + update_canmove() -/mob/living/simple_mob/protean_blob/attack_target(var/atom/A) - if(refactory && istype(A,/obj/item/stack/material)) - var/obj/item/stack/material/S = A - var/substance = S.material.name - var allowed = FALSE - for(var/material in PROTEAN_EDIBLE_MATERIALS) - if(material == substance) allowed = TRUE - if(!allowed) - return - if(refactory.add_stored_material(S.material.name,1*S.perunit) && S.use(1)) - visible_message(span_infoplain(span_bold("[name]") + " gloms over some of \the [S], absorbing it.")) +/mob/living/simple_mob/protean_blob/update_canmove() + if(hiding) + canmove = 0 + return canmove else - return ..() + ..() /mob/living/simple_mob/protean_blob/attackby(var/obj/item/O, var/mob/user) if(refactory && istype(O,/obj/item/stack/material)) @@ -306,104 +391,115 @@ else return ..() +/mob/living/simple_mob/protean_blob/attack_hand(mob/living/L) + if(L.get_effective_size() >= (src.get_effective_size() + 0.5) ) + src.get_scooped(L) + else + ..() + +/mob/living/simple_mob/protean_blob/MouseDrop(var/atom/over_object) + if(ishuman(over_object) && usr == src && src.Adjacent(over_object)) + var/mob/living/carbon/human/H = over_object + get_scooped(H, TRUE) + else + return ..() + /mob/living/simple_mob/protean_blob/MouseEntered(location,control,params) if(resting) return ..() -var/global/list/disallowed_protean_accessories = list( - /obj/item/clothing/accessory/holster, - /obj/item/clothing/accessory/storage, - /obj/item/clothing/accessory/armor - ) +/mob/living/simple_mob/protean_blob/character_directory_species() + if (humanform) + return "[humanform.custom_species ? humanform.custom_species : (humanform.species ? humanform.species.name : "Protean")]" + return "Protean" // Helpers - Unsafe, WILL perform change. /mob/living/carbon/human/proc/nano_intoblob(force) - if(!force && !isturf(loc)) + if(!force && !isturf(loc) && !loc == /obj/item/rig/protean) to_chat(src,span_warning("You can't change forms while inside something.")) return + to_chat(src, span_notice("You rapidly disassociate your form.")) + if(force || do_after(src,20,exclusive = TASK_ALL_EXCLUSIVE)) + handle_grasp() //It's possible to blob out before some key parts of the life loop. This results in things getting dropped at null. TODO: Fix the code so this can be done better. + remove_micros(src, src) //Living things don't fare well in roblobs. + if(buckled) + buckled.unbuckle_mob() + if(LAZYLEN(buckled_mobs)) + for(var/buckledmob in buckled_mobs) + riding_datum.force_dismount(buckledmob) + if(pulledby) + pulledby.stop_pulling() + stop_pulling() - handle_grasp() //It's possible to blob out before some key parts of the life loop. This results in things getting dropped at null. TODO: Fix the code so this can be done better. - remove_micros(src, src) //Living things don't fare well in roblobs. - if(buckled) - buckled.unbuckle_mob() - if(LAZYLEN(buckled_mobs)) - for(var/buckledmob in buckled_mobs) - riding_datum.force_dismount(buckledmob) - if(pulledby) - pulledby.stop_pulling() - stop_pulling() + var/client/C = client - //Record where they should go - var/atom/creation_spot = drop_location() + //Record where they should go + var/atom/creation_spot = drop_location() - //Create our new blob - var/mob/living/simple_mob/protean_blob/blob = new(creation_spot,src) + //Create our new blob + var/mob/living/simple_mob/protean_blob/blob = new(creation_spot,src) - //Drop all our things - var/list/things_to_drop = contents.Copy() - var/list/things_to_not_drop = list(w_uniform,nif,l_store,r_store,wear_id,l_ear,r_ear) //And whatever else we decide for balancing. + //Size update + blob.transform = matrix()*size_multiplier + blob.size_multiplier = size_multiplier - /* No for now, because insta-pepperspray or flash on unblob - if(l_hand && l_hand.w_class <= ITEMSIZE_SMALL) //Hands but only if small or smaller - things_to_not_drop += l_hand - if(r_hand && r_hand.w_class <= ITEMSIZE_SMALL) - things_to_not_drop += r_hand - */ + //dir update + blob.dir = dir - things_to_drop -= things_to_not_drop //Crunch the lists - things_to_drop -= organs //Mah armbs - things_to_drop -= internal_organs //Mah sqeedily spooch + if(l_hand) drop_l_hand() + if(r_hand) drop_r_hand() - for(var/obj/item/I in things_to_drop) //rip hoarders - if(I.protean_drop_whitelist) - continue - drop_from_inventory(I) + //Put our owner in it (don't transfer var/mind) + blob.ckey = ckey + blob.ooc_notes = ooc_notes + blob.ooc_notes_likes = ooc_notes_likes + blob.ooc_notes_dislikes = ooc_notes_dislikes + temporary_form = blob + var/obj/item/radio/R = null + if(isradio(l_ear)) + R = l_ear + if(isradio(r_ear)) + R = r_ear + if(R) + blob.mob_radio = R + R.forceMove(blob) + if(wear_id) + blob.myid = wear_id.GetID() - if(w_uniform && istype(w_uniform,/obj/item/clothing)) //No webbings tho. We do this after in case a suit was in the way - var/obj/item/clothing/uniform = w_uniform - if(LAZYLEN(uniform.accessories)) - for(var/obj/item/clothing/accessory/A in uniform.accessories) - if(is_type_in_list(A, disallowed_protean_accessories)) - uniform.remove_accessory(null,A) //First param is user, but adds fingerprints and messages + //Mail them to nullspace + moveToNullspace() - //Size update - blob.transform = matrix()*size_multiplier - blob.size_multiplier = size_multiplier + //Message + blob.visible_message(span_infoplain(span_bold("[src.name]") + " collapses into a gooey blob!")) - if(l_hand) blob.prev_left_hand = l_hand //Won't save them if dropped above, but necessary if handdrop is disabled. - if(r_hand) blob.prev_right_hand = r_hand + //Duration of the to_puddle iconstate that the blob starts with + sleep(13) + blob.update_icon() //Will remove the collapse anim - //Put our owner in it (don't transfer var/mind) - blob.ckey = ckey - blob.ooc_notes = ooc_notes - blob.ooc_notes_likes = ooc_notes_likes - blob.ooc_notes_dislikes = ooc_notes_dislikes - temporary_form = blob + //Transfer vore organs + blob.vore_organs = vore_organs.Copy() + blob.vore_selected = vore_selected + for(var/obj/belly/B as anything in vore_organs) + B.forceMove(blob) + B.owner = blob + vore_organs.Cut() - //Mail them to nullspace - moveToNullspace() + //We can still speak our languages! + blob.languages = languages.Copy() + blob.name = real_name + blob.real_name = real_name + blob.voice_name = name - //Message - blob.visible_message(span_infoplain(span_bold("[src.name]") + " collapses into a gooey blob!")) + blob.update_icon(1) - //Duration of the to_puddle iconstate that the blob starts with - sleep(13) - blob.update_icon() //Will remove the collapse anim + //Flip them to the protean panel + addtimer(CALLBACK(src, PROC_REF(nano_set_panel), C), 4) - //Transfer vore organs - blob.vore_organs = vore_organs - blob.vore_selected = vore_selected - for(var/obj/belly/B as anything in vore_organs) - B.forceMove(blob) - B.owner = blob - - //We can still speak our languages! - blob.languages = languages.Copy() - - - //Return our blob in case someone wants it - return blob + //Return our blob in case someone wants it + return blob + else + to_chat(src, span_warning("You must remain still to blobform!")) //For some reason, there's no way to force drop all the mobs grabbed. This ought to fix that. And be moved elsewhere. Call with caution, doesn't handle cycles. /proc/remove_micros(var/src, var/mob/root) @@ -412,74 +508,126 @@ var/global/list/disallowed_protean_accessories = list( if(istype(I, /obj/item/holder)) root.remove_from_mob(I) +/mob/living/proc/usehardsuit() + set name = "Utilize Hardsuit Interface" + set desc = "Allows a protean blob to open hardsuit interface." + set category = "Abilities.Protean" + + if(istype(loc, /obj/item/rig/protean)) + var/obj/item/rig/protean/prig = loc + to_chat(src, "You attempt to interface with the [prig].") + prig.tgui_interact(src) + else + to_chat(src, "You are not in RIG form.") + /mob/living/carbon/human/proc/nano_outofblob(var/mob/living/simple_mob/protean_blob/blob, force) if(!istype(blob)) return - + if(blob.loc == /obj/item/rig/protean) + return if(!force && !isturf(blob.loc)) to_chat(blob,span_warning("You can't change forms while inside something.")) return + to_chat(src, span_notice("You rapidly reassemble your form.")) + if(force || do_after(blob,20,exclusive = TASK_ALL_EXCLUSIVE)) + if(buckled) + buckled.unbuckle_mob() + if(LAZYLEN(buckled_mobs)) + for(var/buckledmob in buckled_mobs) + riding_datum.force_dismount(buckledmob) + if(pulledby) + pulledby.stop_pulling() + stop_pulling() - if(buckled) - buckled.unbuckle_mob() - if(LAZYLEN(buckled_mobs)) - for(var/buckledmob in buckled_mobs) - riding_datum.force_dismount(buckledmob) - if(pulledby) - pulledby.stop_pulling() - stop_pulling() + var/client/C = blob.client - //Stop healing if we are - if(blob.healing) - blob.healing.expire() + //Stop healing if we are + if(blob.healing) + blob.healing.expire() - //Play the animation - blob.icon_state = "from_puddle" + if(blob.mob_radio) + blob.mob_radio.forceMove(src) + blob.mob_radio = null + if(blob.myid) + blob.myid = null - //Message - blob.visible_message(span_infoplain(span_bold("[src.name]") + " reshapes into a humanoid appearance!")) + //Play the animation + blob.icon_state = "from_puddle" - //Duration of above animation - sleep(8) + //Message + blob.visible_message(span_infoplain(span_bold("[src.name]") + " reshapes into a humanoid appearance!")) - //Record where they should go - var/atom/reform_spot = blob.drop_location() + //Size update + resize(blob.size_multiplier, FALSE, TRUE, ignore_prefs = TRUE) - //Size update - resize(blob.size_multiplier, FALSE, ignore_prefs = TRUE) + //Duration of above animation + sleep(8) - //Move them back where the blob was - forceMove(reform_spot) + //Record where they should go + var/atom/reform_spot = blob.drop_location() - //Put our owner in it (don't transfer var/mind) - ckey = blob.ckey - ooc_notes = blob.ooc_notes // Lets give the protean any updated notes from blob form. - ooc_notes_likes = blob.ooc_notes_likes - ooc_notes_dislikes = blob.ooc_notes_dislikes - temporary_form = null + //dir update + dir = blob.dir - //Transfer vore organs - vore_selected = blob.vore_selected - for(var/obj/belly/B as anything in blob.vore_organs) - B.forceMove(src) - B.owner = src + //Move them back where the blob was + forceMove(reform_spot) - if(blob.prev_left_hand) put_in_l_hand(blob.prev_left_hand) //The restore for when reforming. - if(blob.prev_right_hand) put_in_r_hand(blob.prev_right_hand) + if(blob.l_hand) blob.drop_l_hand() + if(blob.r_hand) blob.drop_r_hand() - Life(1) //Fix my blindness right meow //Has to be moved up here, there exists a circumstance where blob could be deleted without vore organs moving right. + //Put our owner in it (don't transfer var/mind) + ckey = blob.ckey + ooc_notes = blob.ooc_notes // Lets give the protean any updated notes from blob form. + ooc_notes_likes = blob.ooc_notes_likes + ooc_notes_dislikes = blob.ooc_notes_dislikes + temporary_form = null - //Get rid of friend blob - qdel(blob) + //Transfer vore organs + vore_organs = blob.vore_organs.Copy() + vore_selected = blob.vore_selected + for(var/obj/belly/B as anything in blob.vore_organs) + B.forceMove(src) + B.owner = src + languages = blob.languages.Copy() - //Return ourselves in case someone wants it - return src + Life(1) //Fix my blindness right meow //Has to be moved up here, there exists a circumstance where blob could be deleted without vore organs moving right. -/mob/living/simple_mob/protean_blob/CanStumbleVore(mob/living/target) - if(target == humanform) - return FALSE - return ..() + //Get rid of friend blob + qdel(blob) + //Flip them to the protean panel + addtimer(CALLBACK(src, PROC_REF(nano_set_panel), C), 4) + + //Return ourselves in case someone wants it + return src + else + to_chat(src, span_warning("You must remain still to reshape yourself!")) + +/mob/living/carbon/human/proc/nano_set_panel(var/client/C) + if(C) + C.statpanel = "Protean" + +/mob/living/simple_mob/protean_blob/ClickOn(var/atom/A, var/params) + if(istype(loc, /obj/item/rig/protean)) + HardsuitClickOn(A) + ..() + +/mob/living/simple_mob/protean_blob/can_use_rig() + return 1 + +/mob/living/simple_mob/protean_blob/HardsuitClickOn(var/atom/A, var/alert_ai = 0) + if(istype(loc, /obj/item/rig/protean)) + var/obj/item/rig/protean/prig = loc + if(istype(prig) && !prig.offline && prig.selected_module) + if(!prig.ai_can_move_suit(src)) + return 0 + prig.selected_module.engage(A, alert_ai) + if(ismob(A)) // No instant mob attacking - though modules have their own cooldowns + setClickCooldown(get_attack_speed()) + return 1 + return 0 + +//Don't eat yourself, idiot /mob/living/simple_mob/protean_blob/CanStumbleVore(mob/living/target) if(target == humanform) return FALSE @@ -491,3 +639,110 @@ var/global/list/disallowed_protean_accessories = list( if(PB.humanform == src) return FALSE return ..() + +/mob/living/simple_mob/protean_blob/handle_mutations_and_radiation() + humanform.handle_mutations_and_radiation() + +/mob/living/simple_mob/protean_blob/update_icon() + ..() + if(humanform) + vis_height = 32 + cut_overlays() + var/list/wide_icons = list( + "lizard", + "rat", + "wolf" + ) + var/list/tall_icons = list( + "drake", + "teppi", + "panther" + ) + var/list/big_icons = list( + "robodrgn" + ) + var/datum/species/protean/S = humanform.species + icon = 'icons/mob/species/protean/protean.dmi' + default_pixel_x = 0 + pixel_x = 0 + vore_capacity = 1 + if(S.blob_appearance == "dragon") + vore_capacity = 2 + icon = 'icons/mob/vore128x64.dmi' + mount_offset_y = 32 + mount_offset_x = -16 + var/image/I = image(icon, "[S.dragon_overlays[1]][resting? "-rest" : (vore_fullness? "-[vore_fullness]" : null)]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[1]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + + I = image(icon, "[S.dragon_overlays[2]][resting? "-rest" : null]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[2]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + + I = image(icon, "[S.dragon_overlays[3]][resting? "-rest" : null]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[3]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + + I = image(icon, "[S.dragon_overlays[4]][resting? "-rest" : null]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[4]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + + I = image(icon, "[S.dragon_overlays[5]][resting? "-rest" : null]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[5]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + + I = image(icon, "[S.dragon_overlays[6]][resting? "-rest" : null]", pixel_x = -48) + I.color = S.dragon_overlays[S.dragon_overlays[6]] + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = PLANE_LIGHTING_ABOVE + add_overlay(I) + qdel(I) + + //You know technically I could just put all the icons into the 128x64.dmi file and off-set them to fit.. + if(S.blob_appearance in wide_icons) + icon = 'icons/mob/species/protean/protean64x32.dmi' + default_pixel_x = -16 + pixel_x = -16 + if(S.blob_appearance in tall_icons) + icon = 'icons/mob/species/protean/protean64x64.dmi' + default_pixel_x = -16 + pixel_x = -16 + vis_height = 64 + if(S.blob_appearance in big_icons) + icon = 'icons/mob/species/protean/protean128x64.dmi' + default_pixel_x = -48 + pixel_x = -48 + vis_height = 64 + var/image/I = image(icon, S.blob_appearance+"[resting? "_rest":null][vore_fullness? "-[vore_fullness]" : null]") + I.color = S.blob_color_1 + I.appearance_flags |= (RESET_COLOR|PIXEL_SCALE) + I.plane = MOB_PLANE + I.layer = MOB_LAYER + add_overlay(I) + qdel(I) + eye_layer = image(icon, "[S.blob_appearance][resting? "_rest" : null]-eyes") + eye_layer.appearance_flags = appearance_flags + eye_layer.color = S.blob_color_2 + eye_layer.plane = PLANE_LIGHTING_ABOVE + add_overlay(eye_layer) + qdel(eye_layer) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm index 9a7953ce2f0..39a0d63fd43 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm @@ -1,3 +1,4 @@ +//TODO: Replace ventcrawl with morphing. /mob/living/simple_mob/vore/hostile/morph #define PER_LIMB_STEEL_COST SHEET_MATERIAL_AMOUNT //// // One-part Refactor @@ -5,33 +6,39 @@ /mob/living/carbon/human/proc/nano_partswap() set name = "Ref - Single Limb" set desc = "Allows you to replace and reshape your limbs as you see fit." - set category = "Abilities" - set hidden = TRUE + //set category = "Abilities.Protean" + set hidden = 1 + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + if(nano_dead_check(caller)) + to_chat(caller, span_warning("You need to be repaired first before you can act!")) + return if(stat) - to_chat(src,span_warning("You must be awake and standing to perform this action!")) + to_chat(caller,span_warning("You must be awake and standing to perform this action!")) return - if(!isturf(loc)) - to_chat(src,span_warning("You need more space to perform this action!")) + if(!isturf(caller.loc)) + to_chat(caller,span_warning("You need more space to perform this action!")) return var/obj/item/organ/internal/nano/refactory/refactory = nano_get_refactory() //Missing the organ that does this if(!istype(refactory)) - to_chat(src,span_warning("You don't have a working refactory module!")) + to_chat(caller,span_warning("You don't have a working refactory module!")) return - var/choice = tgui_input_list(src,"Pick the bodypart to change:", "Refactor - One Bodypart", species.has_limbs) + var/choice = tgui_input_list(caller,"Pick the bodypart to change:", "Refactor - One Bodypart", species.has_limbs) if(!choice) return //Organ is missing, needs restoring if(!organs_by_name[choice] || istype(organs_by_name[choice], /obj/item/organ/external/stump)) //allows limb stumps to regenerate like removed limbs. if(refactory.get_stored_material(MAT_STEEL) < PER_LIMB_STEEL_COST) - to_chat(src,span_warning("You're missing that limb, and need to store at least [PER_LIMB_STEEL_COST] steel to regenerate it.")) + to_chat(caller,span_warning("You're missing that limb, and need to store at least [PER_LIMB_STEEL_COST] steel to regenerate it.")) return - var/regen = tgui_alert(src,"That limb is missing, do you want to regenerate it in exchange for [PER_LIMB_STEEL_COST] steel?","Regenerate limb?",list("Yes","No")) + var/regen = tgui_alert(caller,"That limb is missing, do you want to regenerate it in exchange for [PER_LIMB_STEEL_COST] steel?","Regenerate limb?",list("Yes","No")) if(regen != "Yes") return if(!refactory.use_stored_material(MAT_STEEL,PER_LIMB_STEEL_COST)) @@ -40,10 +47,13 @@ var/obj/item/organ/external/oldlimb = organs_by_name[choice] oldlimb.removed() qdel(oldlimb) - - var/mob/living/simple_mob/protean_blob/blob = nano_intoblob() - active_regen = TRUE - if(do_after(blob,5 SECONDS)) + var/mob/living/simple_mob/protean_blob/blob + if(!temporary_form) + blob = nano_intoblob() + else + blob = temporary_form + active_regen = 1 + if(do_after(blob,50,exclusive = TASK_ALL_EXCLUSIVE)) var/list/limblist = species.has_limbs[choice] var/limbpath = limblist["path"] var/obj/item/organ/external/new_eo = new limbpath(src) @@ -51,8 +61,7 @@ new_eo.robotize(synthetic ? synthetic.company : null) //Use the base we started with new_eo.sync_colour_to_human(src) regenerate_icons() - active_regen = FALSE - nano_outofblob(blob) + active_regen = 0 return //Organ exists, let's reshape it @@ -68,7 +77,7 @@ usable_manufacturers[company] = M if(!usable_manufacturers.len) return - var/manu_choice = tgui_input_list(src, "Which manufacturer do you wish to mimic for this limb?", "Manufacturer for [choice]", usable_manufacturers) + var/manu_choice = tgui_input_list(caller, "Which manufacturer do you wish to mimic for this limb?", "Manufacturer for [choice]", usable_manufacturers) if(!manu_choice) return //Changed mind @@ -78,177 +87,199 @@ return //Lost it meanwhile eo.robotize(manu_choice) - visible_message(span_infoplain(span_bold("[src]") + "'s [choice] loses its shape, then reforms.")) update_icons_body() -//// -// Full Refactor -//// -/mob/living/carbon/human/proc/nano_regenerate() //fixed the proc, it used to leave active_regen true. - set name = "Ref - Whole Body" - set desc = "Allows you to regrow limbs and replace organs, given you have enough materials." - set category = "Abilities" - set hidden = TRUE - - if(stat) - to_chat(src,span_warning("You must be awake and standing to perform this action!")) +/mob/living/carbon/human/proc/nano_regenerate() + set name = "Total Reassembly" + set desc = "Fully repair yourself or reload your appearance from whatever character slot you have loaded." + //set category = "Abilities.Protean" + set hidden = 1 + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + var/input = tgui_alert(caller,{"Do you want to rebuild or reassemble yourself? + Rebuilding will cost 10,000 steel and will rebuild all of your limbs as well as repair all damage over a 40s period. + Reassembling costs no steel and will copy the appearance data of your currently loaded save slot."},"Reassembly",list("Rebuild","Reassemble","Cancel")) + if(input == "Cancel" || !input) return - - if(!isturf(loc)) - to_chat(src,span_warning("You need more space to perform this action!")) - return - - var/obj/item/organ/internal/nano/refactory/refactory = nano_get_refactory() - //Missing the organ that does this - if(!istype(refactory)) - to_chat(src,span_warning("You don't have a working refactory module!")) - return - - //Already regenerating - if(active_regen) - to_chat(src, span_warning("You are already refactoring!")) - return - - var/swap_not_rebuild = tgui_alert(src,"Do you want to rebuild, or reshape?","Rebuild or Reshape",list("Reshape","Cancel","Rebuild")) - if(!swap_not_rebuild || swap_not_rebuild == "Cancel") - return - if(swap_not_rebuild == "Reshape") - var/list/usable_manufacturers = list() - for(var/company in chargen_robolimbs) - var/datum/robolimb/M = chargen_robolimbs[company] - if(!(BP_TORSO in M.parts)) - continue - if(species?.base_species in M.species_cannot_use) - continue - if(M.whitelisted_to && !(ckey in M.whitelisted_to)) - continue - usable_manufacturers[company] = M - if(!usable_manufacturers.len) - return - var/manu_choice = tgui_input_list(src, "Which manufacturer do you wish to mimic?", "Manufacturer", usable_manufacturers) - - if(!manu_choice) - return //Changed mind - if(!organs_by_name[BP_TORSO]) - return //Ain't got a torso! - - var/obj/item/organ/external/torso = organs_by_name[BP_TORSO] - to_chat(src, span_danger("Remain still while the process takes place! It will take 5 seconds.")) - visible_message(span_infoplain(span_bold("[src]") + "'s form collapses into an amorphous blob of black ichor...")) - - var/mob/living/simple_mob/protean_blob/blob = nano_intoblob() - active_regen = TRUE - if(do_after(blob,5 SECONDS)) - synthetic = usable_manufacturers[manu_choice] - torso.robotize(manu_choice) //Will cascade to all other organs. - regenerate_icons() - visible_message(span_infoplain(span_bold("[src]") + "'s form reshapes into a new one...")) - active_regen = FALSE - nano_outofblob(blob) - return - - //Not enough resources (AND spends the resources, should be the last check) - if(!refactory.use_stored_material(MAT_STEEL,refactory.max_storage)) - to_chat(src, span_warning("You need to be maxed out on normal metal to do this!")) - return - - var/delay_length = round(active_regen_delay * species.active_regen_mult) - to_chat(src, span_danger("Remain still while the process takes place! It will take [delay_length/10] seconds.")) - visible_message(span_infoplain(span_bold("[src]") + "'s form begins to shift and ripple as if made of oil...")) - active_regen = TRUE - - var/mob/living/simple_mob/protean_blob/blob = nano_intoblob() - if(do_after(blob, delay_length, null, 0)) - if(stat != DEAD && refactory) - var/list/holder = refactory.materials - species.create_organs(src) - var/obj/item/organ/external/torso = organs_by_name[BP_TORSO] - torso.robotize() //synthetic wasn't defined here. - LAZYCLEARLIST(blood_DNA) - LAZYCLEARLIST(feet_blood_DNA) - blood_color = null - feet_blood_color = null - regenerate_icons() //Probably worth it, yeah. - var/obj/item/organ/internal/nano/refactory/new_refactory = locate() in internal_organs - if(!new_refactory) - log_debug("[src] protean-regen'd but lacked a refactory when done.") - else - new_refactory.materials = holder - to_chat(src, span_notice("Your refactoring is complete.")) //Guarantees the message shows no matter how bad the timing. - to_chat(blob, span_notice("Your refactoring is complete!")) + if(input == "Rebuild") + var/obj/item/organ/internal/nano/refactory/refactory = nano_get_refactory() + if(refactory.get_stored_material(MAT_STEEL) >= 10000) + to_chat(caller, span_notify("You begin to rebuild. You will need to remain still.")) + if(do_after(caller, 400,exclusive = TASK_ALL_EXCLUSIVE)) + if(species?:OurRig) //Unsafe, but we should only ever be using this with a Protean + species?:OurRig?:make_alive(src,1) //Re-using this proc + refactory.use_stored_material(MAT_STEEL,refactory.get_stored_material(MAT_STEEL)) //Use all of our steel + else + to_chat(caller, span_userdanger("Somehow, you are missing your protean rig. You are unable to rebuild without one.")) else - to_chat(src, span_critical("Your refactoring has failed.")) - to_chat(blob, span_critical("Your refactoring has failed!")) + to_chat(caller, span_warning("You do not have enough steel stored for this operation.")) else - to_chat(src, span_critical("Your refactoring is interrupted.")) - to_chat(blob, span_critical("Your refactoring is interrupted!")) - active_regen = FALSE - nano_outofblob(blob) + input = tgui_alert(caller,{"Include Flavourtext?"},"Reassembly",list("Yes","No","Cancel")) + if(input == "Cancel" || !input) + return + var/flavour = 0 + if(input == "Yes") + flavour = 1 + input = tgui_alert(caller,{"Include OOC notes?"},"Reassembly",list("Yes","No","Cancel")) + if(input == "Cancel" || !input) + return + var/oocnotes = 0 + if(input == "Yes") + oocnotes = 1 + to_chat(caller, span_notify("You begin to reassemble. You will need to remain still.")) + caller.visible_message(span_notify("[caller] rapidly contorts and shifts!"), span_danger("You begin to reassemble.")) + if(do_after(caller, 40,exclusive = TASK_ALL_EXCLUSIVE)) + if(caller.client.prefs) //Make sure we didn't d/c + var/obj/item/rig/protean/Rig = species?:OurRig + caller.client.prefs.vanity_copy_to(src, FALSE, flavour, oocnotes, TRUE) + species?:OurRig = Rig //Get a reference to our Rig and put it back after reassembling + caller.visible_message(span_notify("[caller] adopts a new form!"), span_danger("You have reassembled.")) +/mob/living/carbon/human/proc/nano_copy_body() + set name = "Copy Form" + set desc = "If you are aggressively grabbing someone, with their consent, you can turn into a copy of them. (Without their name)." + //set category = "Abilities.Protean" + set hidden = 1 + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + + var/grabbing_but_not_enough + var/mob/living/carbon/human/victim = null + for(var/obj/item/grab/G in caller) + if(G.state < GRAB_AGGRESSIVE) + grabbing_but_not_enough = TRUE + return + else + victim = G.affecting + if (!victim) + if (grabbing_but_not_enough) + to_chat(caller, span_warning("You need a better grip to do that!")) + else + to_chat(caller, span_notice("You need to be aggressively grabbing someone before you can copy their form.")) + return + if (!istype(victim)) + to_chat(caller, span_warning("You can only perform this on human mobs!")) + return + if (!victim.client) + to_chat(caller, span_notice("The person you try this on must have a client!")) + return + + + to_chat(caller, span_notice("Waiting for other person's consent.")) + var/consent = tgui_alert(victim, "Allow [src] to copy what you look like?", "Consent", list("Yes", "No")) + if (consent != "Yes") + to_chat(caller, span_notice("They declined your request.")) + return + + var/input = tgui_alert(caller,{"Copy [victim]'s flavourtext?"},"Copy Form",list("Yes","No","Cancel")) + if(input == "Cancel" || !input) + return + var/flavour = 0 + if(input == "Yes") + flavour = 1 + + var/checking = FALSE + for(var/obj/item/grab/G in caller) + if(G.affecting == victim && G.state >= GRAB_AGGRESSIVE) + checking = TRUE + if (!checking) + to_chat(caller, span_warning("You lost your grip on [victim]!")) + return + + to_chat(caller, span_notify("You begin to reassemble into [victim]. You will need to remain still.")) + caller.visible_message(span_notify("[caller] rapidly contorts and shifts!"), span_danger("You begin to reassemble into [victim].")) + if(do_after(caller, 40,exclusive = TASK_ALL_EXCLUSIVE)) + checking = FALSE + for(var/obj/item/grab/G in caller) + if(G.affecting == victim && G.state >= GRAB_AGGRESSIVE) + checking = TRUE + if (!checking) + to_chat(caller, span_warning("You lost your grip on [victim]!")) + return + if(caller.client) //Make sure we didn't d/c + var/obj/item/rig/protean/Rig = species?:OurRig + transform_into_other_human(victim, FALSE, flavour, TRUE) + species?:OurRig = Rig //Get a reference to our Rig and put it back after reassembling + caller.visible_message(span_notify("[caller] adopts the form of [victim]!"), span_danger("You have reassembled into [victim].")) + //// // Storing metal //// /mob/living/carbon/human/proc/nano_metalnom() set name = "Ref - Store Metals" set desc = "If you're holding a stack of material, you can consume some and store it for later." - set category = "Abilities" - set hidden = TRUE + //set category = "Abilities.Protean" + set hidden = 1 + + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + if(nano_dead_check(caller)) + to_chat(caller, span_warning("You need to be repaired first before you can act!")) + return var/obj/item/organ/internal/nano/refactory/refactory = nano_get_refactory() //Missing the organ that does this if(!istype(refactory)) - to_chat(src,span_warning("You don't have a working refactory module!")) + to_chat(caller,span_warning("You don't have a working refactory module!")) return - var/held = get_active_hand() + var/held = caller.get_active_hand() if(!istype(held,/obj/item/stack/material)) - to_chat(src,span_warning("You aren't holding a stack of materials in your active hand...!")) + to_chat(caller,span_warning("You aren't holding a stack of materials in your active hand!")) return var/obj/item/stack/material/matstack = held var/substance = matstack.material.name - var allowed = FALSE + var allowed = 0 for(var/material in PROTEAN_EDIBLE_MATERIALS) - if(material == substance) allowed = TRUE + if(material == substance) allowed = 1 if(!allowed) - to_chat(src,span_warning("You can't process [substance]!")) - return //Only a few things matter, the rest are best not cluttering the lists. + to_chat(caller,span_warning("You can't process [substance]!")) + return - var/howmuch = tgui_input_number(src,"How much do you want to store? (0-[matstack.get_amount()])","Select amount",null,matstack.get_amount(),0) - if(!howmuch || matstack != get_active_hand() || howmuch > matstack.get_amount()) + var/howmuch = tgui_input_number(caller,"How much do you want to store? (0-[matstack.get_amount()])","Select amount",null,matstack.get_amount()) + if(!howmuch || matstack != caller.get_active_hand() || howmuch > matstack.get_amount()) return //Quietly fail var/actually_added = refactory.add_stored_material(substance,howmuch*matstack.perunit) matstack.use(CEILING((actually_added/matstack.perunit), 1)) if(actually_added && actually_added < howmuch) - to_chat(src,span_warning("Your refactory module is now full, so only [actually_added] units were stored.")) - visible_message(span_notice("[src] nibbles some of the [substance] right off the stack!")) + to_chat(caller,span_warning("Your refactory module is now full, so only [actually_added] units were stored.")) + visible_message(span_notice("[caller] nibbles some of the [substance] right off the stack!")) else if(actually_added) - to_chat(src,span_notice("You store [actually_added] units of [substance].")) - visible_message(span_notice("[src] devours some of the [substance] right off the stack!")) + to_chat(caller,span_notice("You store [actually_added] units of [substance].")) + visible_message(span_notice("[caller] devours some of the [substance] right off the stack!")) else - to_chat(src,span_notice("You're completely capped out on [substance]!")) + to_chat(caller,span_notice("You're completely capped out on [substance]!")) //// // Blob Form //// -/mob/living/carbon/human/proc/nano_blobform() +/mob/living/carbon/human/proc/nano_blobform(var/forced) set name = "Toggle Blobform" set desc = "Switch between amorphous and humanoid forms." - set category = "Abilities" - set hidden = TRUE + //set category = "Abilities.Protean" + set hidden = 1 + if(nano_dead_check(src)) + return + if(forced) + if(temporary_form) + nano_outofblob(temporary_form, forced) + else + nano_intoblob(forced) + return var/atom/movable/to_locate = temporary_form || src - if(!isturf(to_locate.loc)) + if(!isturf(to_locate.loc) && !forced) to_chat(to_locate,span_warning("You need more space to perform this action!")) return - //Blob form if(temporary_form) - if(health < maxHealth*0.5) - to_chat(temporary_form,span_warning("You need to regenerate more nanites first!")) - else if(temporary_form.stat) + if(temporary_form.stat) to_chat(temporary_form,span_warning("You can only do this while not stunned.")) else nano_outofblob(temporary_form) @@ -257,6 +288,9 @@ else if(stat) to_chat(src,span_warning("You can only do this while not stunned.")) return + else if(handcuffed) + to_chat(src, span_warning("You can't do this while handcuffed!")) + return else nano_intoblob() @@ -266,63 +300,300 @@ /mob/living/carbon/human/proc/nano_change_fitting() set name = "Change Species Fit" set desc = "Tweak your shape to change what suits you fit into (and their sprites!)." - set category = "Abilities" + set category = "Abilities.Protean" if(stat) to_chat(src,span_warning("You must be awake and standing to perform this action!")) return - var/new_species = tgui_input_list(src, "Please select a species to emulate.", "Shapeshifter Body", GLOB.playable_species) + var/new_species = tgui_input_list(src, "Please select a species to emulate.", "Shapeshifter Body", list(species?.vanity_base_fit)|species?.get_valid_shapeshifter_forms()) if(new_species) species?.base_species = new_species // Really though you better have a species regenerate_icons() //Expensive, but we need to recrunch all the icons we're wearing //// -// Change size +// Rig Transform //// -/mob/living/carbon/human/proc/nano_set_size() - set name = "Adjust Volume" - set category = "Abilities" - set hidden = TRUE +/mob/living/carbon/human/proc/nano_rig_transform(var/forced) + set name = "Modify Form - Hardsuit" + set desc = "Allows a protean to retract its mass into its hardsuit module at will." + //set category = "Abilities.Protean" + set hidden = 1 - var/mob/living/user = temporary_form || src - - var/obj/item/organ/internal/nano/refactory/refactory = nano_get_refactory() - //Missing the organ that does this - if(!istype(refactory)) - to_chat(user,span_warning("You don't have a working refactory module!")) + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + if(nano_dead_check(src)) + to_chat(caller, span_warning("You need to be repaired first before you can act!")) return + to_chat(caller, span_notice("You rapidly condense into your module.")) + if(forced || do_after(caller,20,exclusive = TASK_ALL_EXCLUSIVE)) + if(!temporary_form) //If you're human, force you into blob form before rig'ing + nano_blobform(forced) + spawn(2) - var/nagmessage = "Adjust your mass to be a size between 25 to 200% (or between 1 to 600% in dorms area). Up-sizing consumes metal, downsizing returns metal." - var/new_size = tgui_input_number(user, nagmessage, "Pick a Size", user.size_multiplier*100, 600, 1) - if(!new_size || !size_range_check(new_size)) - return + if(istype(src.species, /datum/species/protean)) + var/datum/species/protean/S = src.species + var/mob/living/simple_mob/protean_blob/P = temporary_form + if(S.OurRig) //Do we even have a RIG? + if(P.loc == S.OurRig) //we're inside our own RIG + if(ismob(S.OurRig.loc)) + var/mob/m = S.OurRig.loc + m.drop_from_inventory(S.OurRig) + if(S.OurRig.wearer) //We're being worn. Engulf em', if prefs align.. otherwise just drop off. + var/mob/living/carbon/human/victim = S.OurRig.wearer + if(P.can_be_drop_pred && victim.devourable && victim.can_be_drop_prey) + if(P.vore_selected) + perform_the_nom(P,victim,P,P.vore_selected,1) + P.forceMove(get_turf(S.OurRig)) + S.OurRig.forceMove(src) + S.OurRig.myprotean = src + src.equip_to_slot_if_possible(S.OurRig, slot_back) + S.OurRig.Moved() + P.has_hands = 1 + else //We're not in our own RIG + if(P.stat || P.resting && !forced) + to_chat(P,span_warning("You can only do this while not stunned.")) + else + if(P.l_hand) + P.drop_l_hand() + if(P.r_hand) + P.drop_r_hand() + P.has_hands = 0 + S.OurRig.myprotean = P + src.drop_from_inventory(S.OurRig) + P.forceMove(S.OurRig) + S.OurRig.canremove = 1 + P.reset_view() + else //Make one if not + to_chat(temporary_form, span_warning("Somehow, your RIG got disconnected from your species. This may have been caused by an admin heal. A new one has been created for you, contact a coder.")) + new /obj/item/rig/protean(src,src) + else + to_chat(caller, span_warning("You must remain still to condense!")) - var/size_factor = new_size/100 - - //Will be: -1.75 for 200->25, and 1.75 for 25->200 - var/sizediff = size_factor - user.size_multiplier - - //Negative if shrinking, positive if growing - //Will be (PLSC*2)*-1.75 to 1.75 - //For 2000 PLSC that's -7000 to 7000 - var/cost = (PER_LIMB_STEEL_COST*2)*sizediff - - //Sizing up - if(cost > 0) - if(refactory.use_stored_material(MAT_STEEL,cost)) - user.resize(size_factor, ignore_prefs = TRUE) +/mob/living/carbon/human/proc/appearance_switch() + set name = "Switch Blob Appearance" + set desc = "Allows a protean blob to switch its outwards appearance." + //set category = "Abilities.Protean" + set hidden = 1 + var/datum/species/protean/S = src.species + var/mob/living/caller = src + if(temporary_form) + caller = temporary_form + var/list/icon_choices = list( + "Primary" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "primary"), + "Highlight" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "highlight"), + "puddle1" = image(icon = 'icons/mob/species/protean/protean_powers.dmi', icon_state = "blob"), + "puddle0" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "puddle"), + "shadow" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "shadow"), + "clean" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "clean"), + "swarm" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "swarm"), + "slime" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "slime"), + "chaos" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "chaos"), + "cloud" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "cloud"), + /*"catslug" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "catslug"), + "cat" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "cat"), + "mouse" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "mouse"), + "rabbit" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "rabbit"), + "bear" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "bear"), + "fen" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "fen"), + "fox" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "fox"), + "raptor" = image(icon = 'icons/mob/species/protean/protean.dmi', icon_state = "raptor"), + "rat" = image(icon = 'icons/mob/species/protean/protean64x32.dmi', icon_state = "rat", pixel_x = -16), + "lizard" = image(icon = 'icons/mob/species/protean/protean64x32.dmi', icon_state = "lizard", pixel_x = -16), + "wolf" = image(icon = 'icons/mob/species/protean/protean64x32.dmi', icon_state = "wolf", pixel_x = -16), + //"drake" = image(icon = 'modular_chomp/icons/mob/species/protean/protean64x64.dmi', icon_state = "drake", pixel_x = -16), + "teppi" = image(icon = 'icons/mob/species/protean/protean64x64.dmi', icon_state = "teppi", pixel_x = -16), + "panther" = image(icon = 'icons/mob/species/protean/protean64x64.dmi', icon_state = "panther", pixel_x = -16), + "robodrgn" = image(icon = 'icons/mob/species/protean/protean128x64.dmi', icon_state = "robodrgn", pixel_x = -48), + "Dragon" = image(icon = 'icons/mob/bigdragon_small.dmi', icon_state = "dragon_small")*/ + ) + var/blobstyle = show_radial_menu(caller, caller, icon_choices, require_near = TRUE, tooltips = FALSE) + if(!blobstyle || QDELETED(caller) || caller.incapacitated()) + return FALSE + switch(blobstyle) + if("Dragon") //Fuck it, we ball + var/list/options = list("Underbelly","Body","Ears","Mane","Horns","Eyes") + for(var/option in options) + LAZYSET(options, option, image('icons/effects/bigdragon_labels.dmi', option)) + var/choice = show_radial_menu(caller, caller, options, radius = 60) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return FALSE + . = TRUE + var/list/underbelly_styles = list( + "dragon_underSmooth", + "dragon_underPlated" + ) + var/list/body_styles = list( + "dragon_bodySmooth", + "dragon_bodyScaled" + ) + var/list/ear_styles = list( + "dragon_earsNormal" + ) + var/list/mane_styles = list( + "dragon_maneNone", + "dragon_maneShaggy", + "dragon_maneDorsalfin" + ) + var/list/horn_styles = list( + "dragon_hornsPointy", + "dragon_hornsCurved", + "dragon_hornsCurved2", + "dragon_hornsJagged", + "dragon_hornsCrown", + "dragon_hornsSkull" + ) + var/list/eye_styles = list( + "dragon_eyesNormal" + ) + switch(choice) + if("Underbelly") + options = underbelly_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 4, pixel_x = -48) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick underbelly color:","Underbelly Color", S.dragon_overlays[1]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[1] = choice + S.dragon_overlays[S.dragon_overlays[1]] = new_color + if("Body") + options = body_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 4, pixel_x = -48) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick body color:","Body Color", S.dragon_overlays[2]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[2] = choice + S.dragon_overlays[S.dragon_overlays[2]] = new_color + if("Ears") + options = ear_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 4, pixel_x = -76, pixel_y = -50) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick ear color:","Ear Color", S.dragon_overlays[3]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[3] = choice + S.dragon_overlays[S.dragon_overlays[3]] = new_color + if("Mane") + options = mane_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 4, pixel_x = -76, pixel_y = -50) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick mane color:","Mane Color", S.dragon_overlays[4]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[4] = choice + S.dragon_overlays[S.dragon_overlays[4]] = new_color + if("Horns") + options = horn_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 4, pixel_x = -86, pixel_y = -50) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick horn color:","Horn Color", S.dragon_overlays[5]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[5] = choice + S.dragon_overlays[S.dragon_overlays[5]] = new_color + if("Eyes") + options = eye_styles + for(var/option in options) + var/image/I = image('icons/mob/vore128x64.dmi', option, dir = 2, pixel_x = -48, pixel_y = -50) + LAZYSET(options, option, I) + choice = show_radial_menu(caller, caller, options, radius = 90) + if(!choice || QDELETED(caller) || caller.incapacitated()) + return 0 + var/new_color = input("Pick eye color:","Eye Color", S.dragon_overlays[6]) as null|color + if(!new_color) + return 0 + S.dragon_overlays[6] = choice + S.dragon_overlays[S.dragon_overlays[6]] = new_color + S.blob_appearance = "dragon" + if("Primary") + var/new_color = input("Pick primary color:","Protean Primary", "#FF0000") as null|color + if(!new_color) + return + S.blob_color_1 = new_color + if("Highlight") + var/new_color = input("Pick highlight color:","Protean Highlight", "#FF0000") as null|color + if(!new_color) + return + S.blob_color_2 = new_color else - to_chat(user,span_warning("That size change would cost [cost] steel, which you don't have.")) - //Sizing down (or not at all) - else if(cost <= 0) - cost = abs(cost) - var/actually_added = refactory.add_stored_material(MAT_STEEL,cost) - user.resize(size_factor, ignore_prefs = TRUE) - if(actually_added != cost) - to_chat(user,span_warning("Unfortunately, [cost-actually_added] steel was lost due to lack of storage space.")) + S.blob_appearance = blobstyle + if(temporary_form) + if(blobstyle) + temporary_form.update_icon() + if(istype(temporary_form.loc, /obj/item/holder/protoblob)) + var/obj/item/holder/protoblob/PB = temporary_form.loc + PB.item_state = S.blob_appearance - user.visible_message(span_notice("Black mist swirls around [user] as they change size.")) +/mob/living/carbon/human/proc/nano_latch() + set name = "Latch/Unlatch host" + set desc = "Allows a protean to forcibly latch or unlatch from a host." + //set category = "Abilities.Protean" + set hidden = 1 + var/mob/living/caller = src + var/mob/living/carbon/human/target + var/datum/species/protean/S = src.species + if(nano_dead_check(src)) + return + if(temporary_form) + caller = temporary_form + if(caller.loc == S.OurRig) + target = S.OurRig.wearer + if(target) + target.drop_from_inventory(S.OurRig) + to_chat(caller, span_notice("You detach from your host.")) + else + to_chat(caller, span_warning("You aren't being worn, dummy.")) + return + var/obj/held_item = caller.get_active_hand() + if(istype(held_item,/obj/item/grab)) + var/obj/item/grab/G = held_item + if(istype(G.affecting, /mob/living/carbon/human)) + target = G.affecting + if(istype(target.species, /datum/species/protean)) + to_chat(caller, span_danger("You can't latch onto a fellow Protean!")) + return + if(G.loc == caller && G.state >= GRAB_AGGRESSIVE) + caller.visible_message(span_warning("[caller] is attempting to latch onto [target]!"), span_danger("You attempt to latch onto [target]!")) + if(do_after(caller, 50, target,exclusive = TASK_ALL_EXCLUSIVE)) + if(G.loc == caller && G.state >= GRAB_AGGRESSIVE) + target.drop_from_inventory(target.back) + caller.visible_message(span_danger("[caller] latched onto [target]!"), span_danger("You latch yourself onto [target]!")) + target.Weaken(3) + nano_rig_transform(1) + spawn(5) //Have to give time for the above proc to resolve + //S.OurRig.forceMove(target) + target.equip_to_slot(S.OurRig, slot_back) + S.OurRig.Moved() + spawn(1) //Same here :( + S.OurRig.wearer = target + else + to_chat(caller, span_warning("You need a more aggressive grab to do this!")) + else + to_chat(caller, span_warning("You can only latch onto humanoid mobs!")) + else + to_chat(caller, span_warning("You need to be grabbing a humanoid mob aggressively to latch onto them.")) /// /// /// A helper to reuse /mob/living/proc/nano_get_refactory(obj/item/organ/internal/nano/refactory/R) @@ -340,7 +611,18 @@ /mob/living/carbon/human/nano_get_refactory() return ..(locate(/obj/item/organ/internal/nano/refactory) in internal_organs) +//I hate this whole bit but I want proteans to be able to "die" and still be "alive" in their blob as a suit +/mob/living/carbon/human/proc/nano_dead_check(var/mob/living/caller) + if(istype(src.species, /datum/species/protean)) + var/datum/species/protean/S = src.species + if(S.pseudodead) + return 1 + return 0 +/mob/living/carbon/human/proc/nano_set_dead(var/num) + if(istype(src.species, /datum/species/protean)) + var/datum/species/protean/S = src.species + S.pseudodead = num /// /// /// Ability objects for stat panel /obj/effect/protean_ability @@ -371,20 +653,20 @@ /obj/effect/protean_ability/proc/do_ability(var/mob/living/L) if(istype(L)) call(L,to_call)() - return FALSE + return 0 /// The actual abilities /obj/effect/protean_ability/into_blob ability_name = "Toggle Blobform" - desc = "Discard your shape entirely, changing to a low-energy blob that can fit into small spaces. You'll consume steel to repair yourself in this form." + desc = "Discard your shape entirely, changing to a low-energy blob. You'll consume steel to repair yourself in this form." icon_state = "blob" to_call = /mob/living/carbon/human/proc/nano_blobform /obj/effect/protean_ability/change_volume ability_name = "Change Volume" - desc = "Alter your size by consuming steel to produce additional nanites, or regain steel by reducing your size and reclaiming them." + desc = "Alter your size between 25% and 200%." icon_state = "volume" - to_call = /mob/living/carbon/human/proc/nano_set_size + to_call = /mob/living/proc/set_size /obj/effect/protean_ability/reform_limb ability_name = "Ref - Single Limb" @@ -393,15 +675,80 @@ to_call = /mob/living/carbon/human/proc/nano_partswap /obj/effect/protean_ability/reform_body - ability_name = "Ref - Whole Body" - desc = "Rebuild your entire body into whatever design you want, assuming you have 10,000 metal." + ability_name = "Total Reassembly" + desc = "Fully repair yourself or reload your appearance from whatever character slot you have loaded." icon_state = "body" to_call = /mob/living/carbon/human/proc/nano_regenerate /obj/effect/protean_ability/metal_nom ability_name = "Ref - Store Metals" - desc = "Store the metal you're holding. Your refactory can only store steel, and all other metals will be converted into nanites ASAP for various effects." + desc = "Store the metal you're holding. Your refactory can only store steel." icon_state = "metal" to_call = /mob/living/carbon/human/proc/nano_metalnom +/obj/effect/protean_ability/hardsuit + ability_name = "Hardsuit Transform" + desc = "Coalesce your nanite swarm into their control module, allowing others to wear you." + icon_state = "rig" + to_call = /mob/living/carbon/human/proc/nano_rig_transform + +/obj/effect/protean_ability/appearance_switch + ability_name = "Blob Appearance" + desc = "Toggle your blob appearance. Also affects your worn appearance." + icon_state = "switch" + to_call = /mob/living/carbon/human/proc/appearance_switch + +/obj/effect/protean_ability/latch_host + ability_name = "Latch Host" + desc = "Forcibly latch or unlatch your RIG from a host mob." + icon_state = "latch" + to_call = /mob/living/carbon/human/proc/nano_latch + +/obj/effect/protean_ability/copy_form + ability_name = "Copy Form" + desc = "If you are aggressively grabbing someone, with their consent, you can turn into a copy of them. (Without their name)." + icon_state = "copy_form" + to_call = /mob/living/carbon/human/proc/nano_copy_body + #undef PER_LIMB_STEEL_COST + +/mob/living/carbon/human/proc/chest_transparency_toggle() + set name = "transparency toggle (chest only)" + set category = "Abilities.Protean" + if(stat || world.time < last_special) + return + last_special = world.time + 50 + for(var/obj/item/organ/external/proteanlimbs as anything in src.organs) + if(proteanlimbs.organ_tag == BP_HEAD) + continue + proteanlimbs.transparent = !proteanlimbs.transparent + visible_message(span_notice("\The [src]'s internal composition seems to change.")) + update_icons_body() + update_hair() + +/obj/effect/protean_ability/chest_transparency + ability_name = "body transparency toggle (All but head)" + desc = "Makes everything but your head transparent!" + icon = 'icons/obj/slimeborg/slimecore.dmi' + icon_state = "core" + to_call = /mob/living/carbon/human/proc/chest_transparency_toggle + +/mob/living/carbon/human/proc/transparency_toggle() + set name = "Toggle Transparency" + set category = "Abilities.Protean" + if(stat || world.time < last_special) + return + last_special = world.time + 50 + for(var/obj/item/organ/external/proteanlimbs as anything in src.organs) + proteanlimbs.transparent = !proteanlimbs.transparent + + visible_message(span_notice("\The [src]'s internal composition seems to change.")) + update_icons_body() + update_hair() + +/obj/effect/protean_ability/transparency_for_entire_body + ability_name = "Toggle Transparency" + desc = "transparency toggle for your entire body" + icon = 'icons/obj/slimeborg/slimecore.dmi' + icon_state = "core" + to_call = /mob/living/carbon/human/proc/transparency_toggle diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm new file mode 100644 index 00000000000..79eaa0a9224 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig.dm @@ -0,0 +1,609 @@ +/* + proteans +*/ +/obj/item/rig/protean + name = "nanosuit control cluster" + suit_type = "nanomachine" + icon = 'icons/obj/rig_modules_vr.dmi' + default_mob_icon = null //Actually having a forced sprite for Proteans is ugly af. I'm not gonna make this a toggle + icon_state = "nanomachine_rig" + armor = list(melee = 0, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 100, rad = 100) + siemens_coefficient= 1 + slowdown = 0 + offline_slowdown = 0 + seal_delay = 0 + var/mob/living/myprotean + initial_modules = list(/obj/item/rig_module/protean/syphon, /obj/item/rig_module/protean/armor, /obj/item/rig_module/protean/healing) + + helm_type = /obj/item/clothing/head/helmet/space/rig/protean //These are important for sprite pointers + boot_type = /obj/item/clothing/shoes/magboots/rig/protean + chest_type = /obj/item/clothing/suit/space/rig/protean + glove_type = /obj/item/clothing/gloves/gauntlets/rig/protean + protean = 1 + offline_vision_restriction = 0 + open = 1 + cell_type = /obj/item/cell/protean + var/dead = 0 + //interface_path = "RIGSuit_protean" + //ai_interface_path = "RIGSuit_protean" + var/sealed = 0 + var/assimilated_rig + var/can_assimilate_rig = FALSE + +/obj/item/rig/protean/relaymove(mob/user, var/direction) + if(user.stat || user.stunned) + return + forced_move(direction, user, 0) + +/obj/item/rig/protean/check_suit_access(mob/living/user) + if(user == myprotean) + return 1 + return ..() + +/obj/item/rig/protean/digest_act(atom/movable/item_storage = null) + return 0 + +/obj/item/rig/protean/ex_act(severity) + return + +/obj/item/rig/protean/New(var/newloc, var/mob/living/carbon/human/P) + if(P) + var/datum/species/protean/S = P.species + S.OurRig = src + if(P.back) + addtimer(CALLBACK(src, PROC_REF(AssimilateBag), P, 1, P.back), 3) + myprotean = P + else + to_chat(P, span_notice("You should have spawned with a backpack to assimilate into your RIG. Try clicking it with a backpack.")) + ..(newloc) + +/obj/item/rig/protean/Destroy() + if(myprotean) + var/mob/living/carbon/human/P = myprotean + var/datum/species/protean/S = P?.species + S?.OurRig = null + myprotean = null + . = ..() + + +/obj/item/rig/proc/AssimilateBag(var/mob/living/carbon/human/P, var/spawned, var/obj/item/storage/backpack/B) + if(istype(B,/obj/item/storage/backpack)) + if(spawned) + B = P.back + P.unEquip(P.back) + if(QDELETED(B)) // for mannequins or such + return + B.forceMove(src) + rig_storage = B + P.drop_item(B) + to_chat(P, span_notice("[B] has been integrated into the [src].")) + if(spawned) //This feels very dumb to have a second if but I'm lazy + P.equip_to_slot_if_possible(src, slot_back) + src.Moved() + else + to_chat(P,span_warning("Your rigsuit can only assimilate a backpack into itself. If you are seeing this message, and you do not have a rigsuit, tell a coder.")) + +/obj/item/rig/protean/verb/RemoveBag() + set name = "Remove Stored Bag" + set category = "Object" + + if(rig_storage) + usr.put_in_hands(rig_storage) + rig_storage = null + else + to_chat(usr, "This Rig does not have a bag installed. Use a bag on it to install one.") + +/obj/item/rig/protean/attack_hand(mob/user as mob) + if (src.loc == user) + if(rig_storage) + src.rig_storage.open(user) + else + ..() + for(var/mob/M in range(1)) + if (M.s_active == src) + src.rig_storage.close(M) + src.add_fingerprint(user) + return + +/obj/item/clothing/head/helmet/space/rig/protean + name = "mass" + desc = "A helmet-shaped clump of nanomachines." + light_overlay = "should not use a light overlay" + species_restricted = list(SPECIES_PROTEAN, SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_XENOMORPH_HYBRID) + sprite_sheets = list( + SPECIES_PROTEAN = 'icons/mob/head.dmi', + SPECIES_HUMAN = 'icons/mob/head.dmi', + SPECIES_TAJ = 'icons/mob/species/tajaran/helmet.dmi', + SPECIES_SKRELL = 'icons/mob/species/skrell/helmet.dmi', + SPECIES_UNATHI = 'icons/mob/species/unathi/helmet.dmi', + SPECIES_XENOHYBRID = 'icons/mob/species/unathi/helmet.dmi', + SPECIES_AKULA = 'icons/mob/species/akula/helmet.dmi', + SPECIES_SERGAL = 'icons/mob/species/sergal/helmet.dmi', + SPECIES_NEVREAN = 'icons/mob/species/sergal/helmet.dmi', + SPECIES_VULPKANIN = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/species/fox/helmet.dmi', + SPECIES_FENNEC = 'icons/mob/species/vulpkanin/helmet.dmi', + SPECIES_PROMETHEAN = 'icons/mob/species/skrell/helmet.dmi', + SPECIES_TESHARI = 'icons/mob/species/teshari/helmet.dmi', + SPECIES_VASILISSAN = 'icons/mob/species/skrell/helmet.dmi', + SPECIES_VOX = 'icons/mob/species/vox/head.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/species/xenomorph_hybrid/helmet.dmi' + ) + + sprite_sheets_obj = list( + SPECIES_PROTEAN = 'icons/mob/head.dmi', + SPECIES_HUMAN = 'icons/mob/head.dmi', + SPECIES_TAJ = 'icons/mob/head.dmi', + SPECIES_SKRELL = 'icons/mob/head.dmi', + SPECIES_UNATHI = 'icons/mob/head.dmi', + SPECIES_XENOHYBRID = 'icons/mob/head.dmi', + SPECIES_AKULA = 'icons/mob/head.dmi', + SPECIES_SERGAL = 'icons/mob/head.dmi', + SPECIES_NEVREAN = 'icons/mob/head.dmi', + SPECIES_VULPKANIN = 'icons/mob/head.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/head.dmi', + SPECIES_FENNEC = 'icons/mob/head.dmi', + SPECIES_PROMETHEAN = 'icons/mob/head.dmi', + SPECIES_TESHARI = 'icons/mob/head.dmi', + SPECIES_VASILISSAN = 'icons/mob/head.dmi', + SPECIES_VOX = 'icons/mob/head.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/head.dmi' + ) + icon = 'icons/mob/head.dmi' + default_worn_icon = 'icons/mob/head.dmi' + +/obj/item/clothing/gloves/gauntlets/rig/protean + name = "mass" + desc = "Glove-shaped clusters of nanomachines." + siemens_coefficient= 0 + species_restricted = list(SPECIES_PROTEAN, SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_XENOMORPH_HYBRID) + sprite_sheets = list( + SPECIES_PROTEAN = 'icons/mob/hands.dmi', + SPECIES_HUMAN = 'icons/mob/hands.dmi', + SPECIES_TAJ = 'icons/mob/hands.dmi', + SPECIES_SKRELL = 'icons/mob/hands.dmi', + SPECIES_UNATHI = 'icons/mob/hands.dmi', + SPECIES_XENOHYBRID = 'icons/mob/hands.dmi', + SPECIES_AKULA = 'icons/mob/hands.dmi', + SPECIES_SERGAL = 'icons/mob/hands.dmi', + SPECIES_NEVREAN = 'icons/mob/hands.dmi', + SPECIES_VULPKANIN = 'icons/mob/hands.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/hands.dmi', + SPECIES_FENNEC = 'icons/mob/hands.dmi', + SPECIES_PROMETHEAN = 'icons/mob/hands.dmi', + SPECIES_TESHARI = 'icons/mob/species/teshari/hands.dmi', + SPECIES_VASILISSAN = 'icons/mob/hands.dmi', + SPECIES_VOX = 'icons/mob/species/vox/gloves.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/species/xenomorph_hybrid/gloves.dmi' + ) + + sprite_sheets_obj = list( + SPECIES_HUMAN = 'icons/mob/hands.dmi', + SPECIES_TAJ = 'icons/mob/hands.dmi', + SPECIES_SKRELL = 'icons/mob/hands.dmi', + SPECIES_UNATHI = 'icons/mob/hands.dmi', + SPECIES_XENOHYBRID = 'icons/mob/hands.dmi', + SPECIES_AKULA = 'icons/mob/hands.dmi', + SPECIES_SERGAL = 'icons/mob/hands.dmi', + SPECIES_NEVREAN = 'icons/mob/hands.dmi', + SPECIES_VULPKANIN = 'icons/mob/hands.dmi', + SPECIES_ZORREN_HIGH = 'icons/mob/hands.dmi', + SPECIES_FENNEC = 'icons/mob/hands.dmi', + SPECIES_PROMETHEAN = 'icons/mob/hands.dmi', + SPECIES_TESHARI = 'icons/mob/hands.dmi', + SPECIES_VASILISSAN = 'icons/mob/hands.dmi', + SPECIES_VOX = 'icons/mob/hands.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/hands.dmi' + ) + icon = 'icons/mob/hands.dmi' + default_worn_icon = 'icons/mob/hands.dmi' + +/obj/item/clothing/shoes/magboots/rig/protean + name = "mass" + desc = "Boot-shaped clusters of nanomachines." + species_restricted = list(SPECIES_PROTEAN, SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_XENOMORPH_HYBRID) + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/teshari/feet.dmi', + SPECIES_VOX = 'icons/mob/species/vox/shoes.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/species/xenomorph_hybrid/shoes.dmi' + ) + sprite_sheets_obj = list() + icon = 'icons/mob/feet.dmi' + default_worn_icon = 'icons/mob/feet.dmi' + +/obj/item/clothing/suit/space/rig/protean + name = "mass" + desc = "A body-hugging mass of nanomachines." + can_breach = 0 + species_restricted = list(SPECIES_PROTEAN, SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_TAJ, SPECIES_UNATHI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_PROMETHEAN, SPECIES_XENOHYBRID, SPECIES_VOX, SPECIES_TESHARI, SPECIES_VASILISSAN, SPECIES_XENOMORPH_HYBRID) + allowed = list( + /obj/item/gun, + /obj/item/flashlight, + /obj/item/tank, + /obj/item/suit_cooling_unit, + /obj/item/melee/baton, + /obj/item/storage/backpack, + ) + sprite_sheets = list( + SPECIES_TESHARI = 'icons/mob/species/teshari/suit.dmi', + SPECIES_VOX = 'icons/mob/species/vox/suit.dmi', + SPECIES_XENOMORPH_HYBRID = 'icons/mob/species/xenomorph_hybrid/suit.dmi' + ) + + sprite_sheets_obj = list() + icon = 'icons/mob/spacesuit.dmi' + default_worn_icon = 'icons/mob/spacesuit.dmi' + +//Copy pasted most of this proc from base because I don't feel like rewriting the base proc with a shit load of exceptions +/obj/item/rig/protean/attackby(obj/item/W as obj, mob/living/user as mob) + if(!istype(user)) + return 0 + if(dead) + switch(dead) + if(1) + if(W.is_screwdriver()) + playsound(src, W.usesound, 50, 1) + if(do_after(user,50,src,exclusive = TASK_ALL_EXCLUSIVE)) + to_chat(user, span_notice("You unscrew the maintenace panel on the [src].")) + dead +=1 + return + if(2) + if(istype(W, /obj/item/protean_reboot))//placeholder + if(do_after(user,50,src,exclusive = TASK_ALL_EXCLUSIVE)) + playsound(src, 'sound/items/Deconstruct.ogg', 50, 1) + to_chat(user, span_notice("You carefully slot [W] in the [src].")) + dead +=1 + qdel(W) + return + if(3) + if(istype(W, /obj/item/stack/nanopaste)) + if(do_after(user,50,src,exclusive = TASK_ALL_EXCLUSIVE)) + playsound(src, 'sound/effects/ointment.ogg', 50, 1) + to_chat(user, span_notice("You slather the interior confines of the [src] with the [W].")) + dead +=1 + W?:use(1) + return + if(4) + if(istype(W, /obj/item/shockpaddles)) + if(W?:can_use(user)) + to_chat(user, span_notice("You hook up the [W] to the contact points in the maintenance assembly")) + if(do_after(user,50,src,exclusive = TASK_ALL_EXCLUSIVE)) + playsound(src, 'sound/machines/defib_charge.ogg', 50, 0) + if(do_after(user,10,src)) + playsound(src, 'sound/machines/defib_zap.ogg', 50, 1, -1) + playsound(src, 'sound/machines/defib_success.ogg', 50, 0) + new /obj/effect/gibspawner/robot(src.loc) + src.atom_say("Contact received! Reassembly nanites calibrated. Estimated time to resucitation: 1 minute 30 seconds") + addtimer(CALLBACK(src, PROC_REF(make_alive), myprotean?:humanform), 900) + return + if(istype(W,/obj/item/rig)) + if(!assimilated_rig) + AssimilateRig(user,W) + if(istype(W,/obj/item/tank)) //Todo, some kind of check for suits without integrated air supplies. + if(air_supply) + to_chat(user, "\The [src] already has a tank installed.") + return + + if(!user.unEquip(W)) + return + + air_supply = W + W.forceMove(src) + to_chat(user, "You slot [W] into [src] and tighten the connecting valve.") + return + + // Check if this is a hardsuit upgrade or a modification. + else if(istype(W,/obj/item/rig_module)) + if(!installed_modules) + installed_modules = list() + if(installed_modules.len) + for(var/obj/item/rig_module/installed_mod in installed_modules) + if(!installed_mod.redundant && istype(installed_mod,W)) + to_chat(user, "The hardsuit already has a module of that class installed.") + return 1 + + var/obj/item/rig_module/mod = W + to_chat(user, "You begin installing \the [mod] into \the [src].") + if(!do_after(user,40)) + return + if(!user || !W) + return + if(!user.unEquip(mod)) + return + to_chat(user, "You install \the [mod] into \the [src].") + installed_modules |= mod + mod.forceMove(src) + mod.installed(src) + update_icon() + return 1 + else if(W.is_wrench()) + if(!air_supply) + to_chat(user, "There is no tank to remove.") + return + + if(user.r_hand && user.l_hand) + air_supply.forceMove(get_turf(user)) + else + user.put_in_hands(air_supply) + to_chat(user, "You detach and remove \the [air_supply].") + air_supply = null + return + else if(W.is_screwdriver()) + var/list/possible_removals = list() + for(var/obj/item/rig_module/module in installed_modules) + if(module.permanent) + continue + possible_removals[module.name] = module + + if(!possible_removals.len) + to_chat(user, "There are no installed modules to remove.") + return + + var/removal_choice = tgui_input_list(usr, "Which module would you like to remove?", "Removal Choice", possible_removals) + if(!removal_choice) + return + + var/obj/item/rig_module/removed = possible_removals[removal_choice] + to_chat(user, "You detach \the [removed] from \the [src].") + removed.forceMove(get_turf(src)) + removed.removed() + installed_modules -= removed + update_icon() + return + for(var/obj/item/rig_module/module in installed_modules) + if(module.accepts_item(W,user)) //Item is handled in this proc + return + if(rig_storage) + var/obj/item/storage/backpack = rig_storage + if(backpack.can_be_inserted(W, 1)) + backpack.handle_item_insertion(W) + else + if(istype(W,/obj/item/storage/backpack)) + AssimilateBag(user,0,W) + ..() + +/obj/item/rig/protean/proc/make_alive(var/mob/living/carbon/human/H, var/partial) + if(H) + H.setToxLoss(0) + H.setOxyLoss(0) + H.setCloneLoss(0) + H.setBrainLoss(0) + H.SetParalysis(0) + H.SetStunned(0) + H.SetWeakened(0) + H.blinded = 0 + H.SetBlinded(0) + H.eye_blurry = 0 + H.ear_deaf = 0 + H.ear_damage = 0 + H.heal_overall_damage(H.getActualBruteLoss(), H.getActualFireLoss(), 1) + for(var/I in H.organs_by_name) + if(!H.organs_by_name[I] || istype(H.organs_by_name[I], /obj/item/organ/external/stump)) + if(H.organs_by_name[I]) + var/obj/item/organ/external/oldlimb = H.organs_by_name[I] + oldlimb.removed() + qdel(oldlimb) + var/list/organ_data = H.species.has_limbs[I] + var/limb_path = organ_data["path"] + var/obj/item/organ/external/new_eo = new limb_path(H) + new_eo.robotize(H.synthetic ? H.synthetic.company : null) + new_eo.sync_colour_to_human(H) + if(!partial) + dead_mob_list.Remove(H) + living_mob_list += H + H.tod = null + H.timeofdeath = 0 + H.set_stat(CONSCIOUS) + if(istype(H.species, /datum/species/protean)) + var/datum/species/protean/S + S = H.species + S.pseudodead = 0 + to_chat(myprotean, span_notice("You have finished reconstituting.")) + playsound(src.loc, 'sound/machines/ding.ogg', 50, 1) + dead = 0 + +/obj/item/rig/protean/take_hit(damage, source, is_emp=0) + return //We don't do that here + +/obj/item/rig/protean/emp_act(severity_class) + return //Same here + +/obj/item/rig/protean/cut_suit() + return //nope + +/obj/item/rig/protean/force_rest(var/mob/user) + wearer.lay_down() + to_chat(user, span_notice("\The [wearer] is now [wearer.resting ? "resting" : "getting up"].")) + +/obj/item/cell/protean + name = "Protean power cell" + desc = "Something terrible must have happened if you're managing to see this." + maxcharge = 10000 + charge_amount = 100 + var/mob/living/carbon/human/charger + +/obj/item/cell/protean/Initialize() //ChompEDIT New --> Initialize + charge = maxcharge + update_icon() + addtimer(CALLBACK(src, PROC_REF(search_for_protean)), 60) + +/obj/item/cell/protean/proc/search_for_protean() + if(istype(src.loc, /obj/item/rig/protean)) + var/obj/item/rig/protean/prig = src.loc + charger = prig.wearer + if(charger) + START_PROCESSING(SSobj, src) + +/obj/item/cell/protean/process() + var/C = charge + if(charger) + if((world.time >= last_use + charge_delay) && charger.nutrition > 100) + give(charge_amount) + charger.nutrition -= ((1/200)*(charge - C)) //Take nutrition relative to charge. Change the 1/200 if you want to alter the nutrition to charge ratio + else + return PROCESS_KILL + + +/obj/item/rig/protean/equipped(mob/living/carbon/human/M) + ..() + if(dead) + canremove = 1 + else + canremove = 0 + +/obj/item/rig/protean/ai_can_move_suit(mob/user, check_user_module = 0, check_for_ai = 0) + if(check_for_ai) + return 0 //We don't do that here. + if(offline || !cell || !cell.charge || locked_down) + if(user) + to_chat(user, span_warning("Your host rig is unpowered and unresponsive.")) + return 0 + if(!wearer || (wearer.back != src && wearer.belt != src)) + if(user) + to_chat(user, span_warning("Your host rig is not being worn.")) + return 0 + return 1 + +/obj/item/rig/protean/toggle_seals(mob/living/carbon/human/M, instant) + M = src.wearer + ..() + +/obj/item/rig/protean/toggle_cooling(mob/user) + user = src.wearer + ..() + +/obj/item/rig/protean/toggle_piece(piece, mob/living/carbon/human/H, deploy_mode, forced) + H = src.wearer + ..() + +/obj/item/rig/protean/get_description_interaction() + if(dead) + var/list/results = list() + switch(dead) + if(1) + results += "Use a screwdriver to start repairs." + if(2) + results += "Insert a Protean Reboot Programmer, printed from a protolathe." + if(3) + results += "Use some Nanopaste." + if(4) + results += "Use either a defib or jumper cables to start the reboot sequence." + return results + +//Effectively a round about way of letting a Protean wear other rigs. +/obj/item/rig/protean/proc/AssimilateRig(mob/user, var/obj/item/rig/R) + if(!can_assimilate_rig) + to_chat(user, span_warning("You can not place a rig into \the [src]")) + return + if(!R || assimilated_rig) + return + if(istype(R, /obj/item/rig/protean)) + to_chat(user, span_warning("The world is not ready for such a technological singularity.")) + return + to_chat(user, span_notice("You assimilate the [R] into the [src]. Mimicking its stats and appearance.")) + for(var/obj/item/piece in list(gloves,helmet,boots,chest)) + piece.armor = R.armor.Copy() + piece.max_pressure_protection = R.max_pressure_protection + piece.max_heat_protection_temperature = R.max_heat_protection_temperature + //I dislike this piece of code, but not every rig has the full set of parts + if(R.gloves) + gloves.sprite_sheets = R.gloves.sprite_sheets.Copy() + gloves.sprite_sheets_obj = R.gloves.sprite_sheets.Copy() + gloves.icon = R.gloves.icon + gloves.icon_state = R.gloves.icon_state + gloves.default_worn_icon = R.gloves.default_worn_icon + if(R.helmet) + helmet.sprite_sheets = R.helmet.sprite_sheets.Copy() + helmet.sprite_sheets_obj = R.helmet.sprite_sheets.Copy() + helmet.icon = R.helmet.icon + helmet.icon_state = R.helmet.icon_state + helmet.default_worn_icon = R.helmet.default_worn_icon + if(R.boots) + boots.sprite_sheets = R.boots.sprite_sheets.Copy() + boots.sprite_sheets_obj = R.boots.sprite_sheets.Copy() + boots.icon = R.boots.icon + boots.icon_state = R.boots.icon_state + boots.default_worn_icon = R.boots.default_worn_icon + if(R.chest) + chest.sprite_sheets = R.chest.sprite_sheets.Copy() + chest.sprite_sheets_obj = R.chest.sprite_sheets.Copy() + chest.icon = R.chest.icon + chest.icon_state = R.chest.icon_state + chest.default_worn_icon = R.chest.default_worn_icon + + suit_state = R.suit_state + name = R.name + icon = R.icon + icon_state = R.icon_state + user.drop_item(R) + contents += R + assimilated_rig = R + slowdown = (initial(R.slowdown) *0.5) + offline_slowdown = slowdown + +/obj/item/rig/protean/verb/RemoveRig() + set name = "Remove Assimilated Rig" + set category = "Object" + + if(assimilated_rig) + for(var/obj/item/piece in list(gloves,helmet,boots,chest)) + piece.armor = armor.Copy() + piece.max_pressure_protection = initial(piece.max_pressure_protection) + piece.max_heat_protection_temperature = initial(piece.max_heat_protection_temperature) + piece.icon_state = src.icon_state + piece.icon = initial(piece.icon) + piece.default_worn_icon = initial(piece.default_worn_icon) + + //Byond at this time does not support initial() on lists + //So we have to create a new rig, just so we can copy the lists we're after + //If someone figures out a smarter way to do this, please tell me + var/obj/item/rig/tempRig = new /obj/item/rig/protean() + gloves.sprite_sheets = tempRig.gloves.sprite_sheets.Copy() + gloves.sprite_sheets_obj = tempRig.gloves.sprite_sheets.Copy() + helmet.sprite_sheets = tempRig.helmet.sprite_sheets.Copy() + helmet.sprite_sheets_obj = tempRig.helmet.sprite_sheets.Copy() + boots.sprite_sheets = tempRig.boots.sprite_sheets.Copy() + boots.sprite_sheets_obj = tempRig.boots.sprite_sheets.Copy() + chest.sprite_sheets = tempRig.chest.sprite_sheets.Copy() + chest.sprite_sheets_obj = tempRig.chest.sprite_sheets.Copy() + slowdown = initial(slowdown) + name = tempRig.name + icon = tempRig.icon // Reset the icon back to its original + icon_state = tempRig.icon_state + suit_state = icon_state + offline_slowdown = initial(offline_slowdown) + usr.put_in_hands(assimilated_rig) + assimilated_rig = null + qdel(tempRig) + else + to_chat(usr, "[src] has not assimilated a RIG. Use one on it to assimilate.") + +/obj/item/rig/protean/MouseDrop(obj/over_object as obj) + if(!canremove) + return + + if (isliving(usr) || isobserver(usr)) + + if (istype(usr.loc,/obj/mecha)) // stops inventory actions in a mech. why? + return + + if (!( istype(over_object, /obj/screen) )) + return ..() + + if (!(src.loc == usr) || (src.loc && src.loc.loc == usr)) + return + + if (( usr.restrained() ) || ( usr.stat )) + return + + if ((src.loc == usr) && !(istype(over_object, /obj/screen)) && !usr.unEquip(src)) + return + + switch(over_object.name) + if("r_hand") + usr.unEquip(src) + usr.put_in_r_hand(src) + if("l_hand") + usr.unEquip(src) + usr.put_in_l_hand(src) + src.add_fingerprint(usr) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig_tgui.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig_tgui.dm new file mode 100644 index 00000000000..6c90baed522 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_rig_tgui.dm @@ -0,0 +1,128 @@ +/* +It's a bit snowflake, but some rigsuit rewriting was necessary to achieved what I wanted +for protean rigsuits, and rolling these changes into the base RIGsuit code would definitely create +merge conflicts down the line. +So here it sits, snowflake code for a single item. +*/ + +/obj/item/rig/protean/tgui_state(mob/user) + return GLOB.tgui_always_state + +/obj/item/rig/protean/tgui_data(mob/user) + var/list/data = list() + + if(selected_module) + data["primarysystem"] = "[selected_module.interface_name]" + else + data["primarysystem"] = null + + if(loc != user) + data["ai"] = TRUE + else + data["ai"] = FALSE + + data["cooling"] = cooling_on + data["sealed"] = !sealed + data["sealing"] = sealing + data["helmet"] = (helmet ? "[helmet.name]" : "None.") + data["gauntlets"] = (gloves ? "[gloves.name]" : "None.") + data["boots"] = (boots ? "[boots.name]" : "None.") + data["chest"] = (chest ? "[chest.name]" : "None.") + + data["helmetDeployed"] = (helmet && helmet.loc == loc) + data["gauntletsDeployed"] = (gloves && gloves.loc == loc) + data["bootsDeployed"] = (boots && boots.loc == loc) + data["chestDeployed"] = (chest && chest.loc == loc) + + data["charge"] = cell ? round(cell.charge,1) : 0 + data["maxcharge"] = cell ? cell.maxcharge : 0 + data["chargestatus"] = cell ? FLOOR((cell.charge/cell.maxcharge)*50, 1) : 0 + + data["emagged"] = subverted + data["coverlock"] = locked + data["interfacelock"] = interface_locked + data["aicontrol"] = control_overridden + data["aioverride"] = ai_override_enabled + data["securitycheck"] = security_check_enabled + data["malf"] = malfunction_delay + + var/list/module_list = list() + if(!canremove && !sealing) + var/i = 1 + for(var/obj/item/rig_module/module in installed_modules) + var/list/module_data = list( + "index" = i, + "name" = "[module.interface_name]", + "desc" = "[module.interface_desc]", + "can_use" = module.usable, + "can_select" = module.selectable, + "can_toggle" = module.toggleable, + "is_active" = module.active, + "engagecost" = module.use_power_cost*10, + "activecost" = module.active_power_cost*10, + "passivecost" = module.passive_power_cost*10, + "engagestring" = module.engage_string, + "activatestring" = module.activate_string, + "deactivatestring" = module.deactivate_string, + "damage" = module.damage + ) + + if(module.charges && module.charges.len) + module_data["charges"] = list() + var/datum/rig_charge/selected = module.charges["[module.charge_selected]"] + module_data["realchargetype"] = module.charge_selected + module_data["chargetype"] = selected ? "[selected.display_name]" : "none" + + for(var/chargetype in module.charges) + var/datum/rig_charge/charge = module.charges[chargetype] + module_data["charges"] += list(list("caption" = "[charge.display_name] ([charge.charges])", "index" = "[chargetype]")) + + module_list += list(module_data) + i++ + + if(module_list.len) + data["modules"] = module_list + else + data["modules"] = list() + + return data +/* +/obj/item/rig/protean/tgui_act(action, params) + switch(action) + if("toggle_seals") + toggle_seals(wearer) + . = TRUE + if("toggle_cooling") + toggle_cooling(wearer) + . = TRUE + if("toggle_ai_control") + to_chat(usr, span_notice("Proteans cannot be AI controlled.")) + . = TRUE + if("toggle_suit_lock") + locked = !locked + . = TRUE + if("toggle_piece") + toggle_piece(params["piece"], wearer) + . = TRUE + if("interact_module") + var/module_index = text2num(params["module"]) + + if(module_index > 0 && module_index <= installed_modules.len) + var/obj/item/rig_module/module = installed_modules[module_index] + switch(params["module_mode"]) + if("select") + selected_module = module + . = TRUE + if("engage") + module.engage() + . = TRUE + if("toggle") + if(module.active) + module.deactivate() + else + module.activate() + . = TRUE + if("select_charge_type") + module.charge_selected = params["charge_type"] + . = TRUE +*/ diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index 2825cdf8022..484ccdbc8b2 100755 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -1,5 +1,4 @@ -#define DAM_SCALE_FACTOR 0.01 -#define METAL_PER_TICK 100 +#define METAL_PER_TICK SHEET_MATERIAL_AMOUNT/20 /datum/species/protean name = SPECIES_PROTEAN name_plural = "Proteans" @@ -7,7 +6,7 @@ " + span_italics("VERY") + " advanced civilizations have the option of 'nanoswarm' bodies. Effectively a single robot body comprised \ of millions of tiny nanites working in concert to maintain cohesion." show_ssd = "totally quiescent" - death_message = "rapidly loses cohesion, dissolving into a cloud of gray dust..." + death_message = "rapidly loses cohesion, retreating into their hardened control module..." knockout_message = "collapses inwards, forming a disordered puddle of gray goo." remains_type = /obj/effect/decal/cleanable/ash @@ -26,50 +25,52 @@ num_alternate_languages = 3 species_language = LANGUAGE_EAL assisted_langs = list(LANGUAGE_ROOTLOCAL, LANGUAGE_ROOTGLOBAL, LANGUAGE_VOX) + speech_bubble_appearance = "synthetic" color_mult = TRUE breath_type = null poison_type = null - virus_immune = 1 - blood_volume = 0 - min_age = 18 - max_age = 200 - brute_mod = 0.8 - burn_mod = 1.5 - oxy_mod = 0 - item_slowdown_mod = 1.33 + // male_scream_sound = null + // female_scream_sound = null - cold_level_1 = 280 //Default 260 - Lower is better - cold_level_2 = 220 //Default 200 - cold_level_3 = 130 //Default 120 - - heat_level_1 = 320 //Default 360 - heat_level_2 = 370 //Default 400 - heat_level_3 = 600 //Default 1000 + virus_immune = 1 + blood_volume = 0 + min_age = 18 + max_age = 200 + oxy_mod = 0 + //radiation_mod = 0 //Can't be assed with fandangling rad protections while blob formed/suited + siemens_coefficient = 2 + brute_mod = 0.8 + burn_mod = 1.5 + emp_dmg_mod = 0.8 + emp_sensitivity = EMP_BLIND | EMP_DEAFEN | EMP_BRUTE_DMG | EMP_BURN_DMG + item_slowdown_mod = 1.5 //Gentle encouragement to let others wear you hazard_low_pressure = -1 //Space doesn't bother them - hazard_high_pressure = 200 //They can cope with slightly higher pressure - //Cold/heat does affect them, but it's done in special ways below cold_level_1 = -INFINITY cold_level_2 = -INFINITY cold_level_3 = -INFINITY - heat_level_1 = INFINITY - heat_level_2 = INFINITY - heat_level_3 = INFINITY + heat_level_1 = 420 + heat_level_2 = 480 + heat_level_3 = 1100 - body_temperature = 290 + body_temperature = 290 - siemens_coefficient = 1.5 //Very bad zappy times - rarity_value = 5 + rarity_value = 5 + + // species_sounds = "Robotic" + + // crit_mod = 4 //Unable to go crit + var/obj/item/rig/protean/OurRig genders = list(MALE, FEMALE, PLURAL, NEUTER) has_organ = list( O_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain/nano, O_ORCH = /obj/item/organ/internal/nano/orchestrator, - O_FACT = /obj/item/organ/internal/nano/refactory + O_FACT = /obj/item/organ/internal/nano/refactory, ) has_limbs = list( BP_TORSO = list("path" = /obj/item/organ/external/chest/unbreakable/nano), @@ -85,7 +86,7 @@ BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/unbreakable/nano) ) - heat_discomfort_strings = list("You feel too warm.") + heat_discomfort_strings = list("WARNING: Temperature exceeding acceptable thresholds!.") cold_discomfort_strings = list("You feel too cool.") //These verbs are hidden, for hotkey use only @@ -94,7 +95,11 @@ /mob/living/carbon/human/proc/nano_partswap, /mob/living/carbon/human/proc/nano_metalnom, /mob/living/carbon/human/proc/nano_blobform, - /mob/living/carbon/human/proc/nano_set_size, + /mob/living/carbon/human/proc/nano_rig_transform, + /mob/living/carbon/human/proc/nano_copy_body, + /mob/living/carbon/human/proc/appearance_switch, + /mob/living/carbon/human/proc/nano_latch, + /mob/living/proc/set_size, /mob/living/carbon/human/proc/nano_change_fitting, //These verbs are displayed normally, /mob/living/carbon/human/proc/shapeshifter_select_hair, /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, @@ -105,12 +110,29 @@ /mob/living/carbon/human/proc/shapeshifter_select_tail, /mob/living/carbon/human/proc/shapeshifter_select_ears, /mob/living/carbon/human/proc/shapeshifter_select_secondary_ears, - ) + /mob/living/proc/flying_toggle, + /mob/living/proc/flying_vore_toggle, + /mob/living/proc/start_wings_hovering, + ) //removed fetish verbs, since non-customs can pick neutral traits now. Also added flight, cause shapeshifter can grow wings. var/global/list/abilities = list() var/monochromatic = FALSE //IGNORE ME + var/blob_appearance = "puddle1" + var/blob_color_1 = "#363636" + var/blob_color_2 = "#ba3636" + var/list/dragon_overlays = list( + "dragon_underSmooth" = "#FFFFFF", + "dragon_bodySmooth" = "#FFFFFF", + "dragon_earsNormal" = "#FFFFFF", + "dragon_maneShaggy" = "#FFFFFF", + "dragon_hornsPointy" = "#FFFFFF", + "dragon_eyesNormal" = "#FFFFFF" + ) + + var/pseudodead = 0 + /datum/species/protean/New() ..() if(!LAZYLEN(abilities)) @@ -127,20 +149,62 @@ if(saved_nif) saved_nif.quick_implant(H) +/datum/species/protean/get_race_key() + var/datum/species/real = GLOB.all_species[base_species] + return real.race_key + /datum/species/protean/get_bodytype(var/mob/living/carbon/human/H) if(!H || base_species == name) return ..() var/datum/species/S = GLOB.all_species[base_species] return S.get_bodytype(H) +/datum/species/protean/get_icobase(var/mob/living/carbon/human/H, var/get_deform) + if(!H || base_species == name) return ..(null, get_deform) + var/datum/species/S = GLOB.all_species[base_species] + return S.get_icobase(H, get_deform) + /datum/species/protean/get_valid_shapeshifter_forms(var/mob/living/carbon/human/H) - return GLOB.playable_species + var/static/list/protean_shapeshifting_forms = GLOB.playable_species.Copy() - SPECIES_PROMETHEAN + return protean_shapeshifting_forms + +/datum/species/protean/get_tail(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_tail(H) + +/datum/species/protean/get_tail_animation(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_tail_animation(H) + +/datum/species/protean/get_tail_hair(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_tail_hair(H) + +/datum/species/protean/get_blood_mask(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_blood_mask(H) + +/datum/species/protean/get_damage_mask(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_damage_mask(H) + +/datum/species/protean/get_damage_overlays(var/mob/living/carbon/human/H) + if(!H || base_species == name) return ..() + var/datum/species/S = GLOB.all_species[base_species] + return S.get_damage_overlays(H) /datum/species/protean/handle_post_spawn(var/mob/living/carbon/human/H) ..() H.synth_color = TRUE /datum/species/protean/equip_survival_gear(var/mob/living/carbon/human/H) - var/obj/item/stack/material/steel/metal_stack = new(null, 3) + ..() + var/obj/item/stack/material/steel/metal_stack = new() + metal_stack.set_amount(5) var/obj/item/clothing/accessory/permit/nanotech/permit = new() permit.set_name(H.real_name) @@ -161,6 +225,8 @@ else H.nif.durability = 25 + new /obj/item/rig/protean(H,H) + /datum/species/protean/hug(var/mob/living/carbon/human/H, var/mob/living/target) return ..() //Wut @@ -172,18 +238,22 @@ /datum/species/protean/handle_death(var/mob/living/carbon/human/H) if(!H) - return // Iono! - + return //No body? + if(OurRig.dead) + return + OurRig.dead = 1 + var/mob/temp = H if(H.temporary_form) - H.forceMove(H.temporary_form.drop_location()) - H.ckey = H.temporary_form.ckey - QDEL_NULL(H.temporary_form) - - to_chat(H, span_warning("You died as a Protean. Please sit out of the round for at least 60 minutes before respawning, to represent the time it would take to ship a new-you to the station.")) - - spawn(1) - if(H) - H.gib() + temp = H.temporary_form + playsound(temp, 'sound/voice/borg_deathsound.ogg', 50, 1) + temp.visible_message(span_bold("[temp.name]") + " shudders and retreats inwards, coalescing into a single core componant!") + to_chat(temp, span_warning("You've died as a Protean! While dead, you will be locked to your core RIG control module until you can be repaired. Instructions to your revival can be found in the Examine tab when examining your module.")) + if(H.temporary_form) + if(!istype(H.temporary_form.loc, /obj/item/rig/protean)) + H.nano_rig_transform(1) + else + H.nano_rig_transform(1) + pseudodead = 1 /datum/species/protean/handle_environment_special(var/mob/living/carbon/human/H) if((H.getActualBruteLoss() + H.getActualFireLoss()) > H.maxHealth*0.5 && isturf(H.loc)) //So, only if we're not a blob (we're in nullspace) or in someone (or a locker, really, but whatever) @@ -325,16 +395,26 @@ material_name = MAT_STEEL /datum/modifier/protean/steel/tick() - holder.adjustBruteLoss(-1,include_robo = TRUE) //Modified by species resistances - holder.adjustFireLoss(-0.5,include_robo = TRUE) //Modified by species resistances - var/mob/living/carbon/human/H = holder - for(var/obj/item/organ/O as anything in H.internal_organs) - // Fix internal damage - if(O.damage > 0) - O.damage = max(0,O.damage-0.1) - // If not damaged, but dead, fix it - else if(O.status & ORGAN_DEAD) - O.status &= ~ORGAN_DEAD //Unset dead if we repaired it entirely + //Heal a random damaged limb by 1,1 per tick + holder.adjustBruteLoss(-1,include_robo = TRUE) + holder.adjustFireLoss(-1,include_robo = TRUE) + holder.adjustToxLoss(-1) + + var/mob/living/carbon/human/H + if(ishuman(holder)) + H = holder + + //Then heal every damaged limb by a smaller amount + if(H) + for(var/obj/item/organ/external/O in H.organs) + O.heal_damage(0.5, 0.5, 0, 1) + + //Heal the organs a little bit too, as a treat + for(var/obj/item/organ/O as anything in H.internal_organs) + if(O.damage > 0) + O.damage = max(0,O.damage-0.3) + else if(O.status & ORGAN_DEAD) + O.status &= ~ORGAN_DEAD //Unset dead if we repaired it entirely // PAN Card /obj/item/clothing/accessory/permit/nanotech @@ -357,6 +437,4 @@ . = ..() . += validstring . += registring - -#undef DAM_SCALE_FACTOR #undef METAL_PER_TICK diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm index fb8f73b9529..8ecb4b9d44f 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities_vr.dm @@ -170,7 +170,7 @@ update_canmove() weakened = 2 - revive_ready = world.time + 10 MINUTES //set the cooldown CHOMPEdit: Reduced this to 10 minutes, you're playing with fire if you're reviving that often. + revive_ready = world.time + 10 MINUTES //set the cooldown, Reduced this to 10 minutes, you're playing with fire if you're reviving that often. /datum/modifier/resleeving_sickness/chimera //near identical to the regular version, just with different flavortexts name = "imperfect regeneration" diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm index d5df6193332..6880142471e 100644 --- a/code/modules/mob/living/death.dm +++ b/code/modules/mob/living/death.dm @@ -28,3 +28,9 @@ S.sharer_died(gibbed) . = ..() + +/mob/living/proc/delayed_gib() + visible_message(span_danger(span_bold("[src]") + " starts convulsing violently!"), span_danger("You feel as if your body is tearing itself apart!")) + Weaken(30) + make_jittery(1000) + addtimer(CALLBACK(src, PROC_REF(gib)), rand(2 SECONDS, 10 SECONDS)) diff --git a/code/modules/mob/living/inventory.dm b/code/modules/mob/living/inventory.dm index 8f412c4c1b5..2ddea51a17f 100644 --- a/code/modules/mob/living/inventory.dm +++ b/code/modules/mob/living/inventory.dm @@ -25,6 +25,17 @@ else newitem.forceMove(S) return S + + if(istype(src.back,/obj/item/rig)) //This would be much cooler if we had componentized storage datums + var/obj/item/rig/R = src.back + if(R.rig_storage) + var/obj/item/storage/backpack = R.rig_storage + if(backpack.can_be_inserted(newitem, 1)) + if(user_initiated) + backpack.handle_item_insertion(newitem) + else + newitem.forceMove(src.back) + return backpack return 0 //Returns the thing in our active hand diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index a81282f3646..cbedd7d3923 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -613,6 +613,11 @@ L += D.wrapped if(istype(D.wrapped, /obj/item/storage)) //this should never happen L += get_contents(D.wrapped) + + for(var/obj/item/rig/R in src.contents) //Check rigsuit storage for items + if(R.rig_storage) + L += get_contents(R.rig_storage) + return L /mob/living/proc/check_contents_for(A) diff --git a/code/modules/mob/living/living_vr.dm b/code/modules/mob/living/living_vr.dm index fe8b80b5357..57d054e4248 100644 --- a/code/modules/mob/living/living_vr.dm +++ b/code/modules/mob/living/living_vr.dm @@ -140,50 +140,8 @@ set desc = "Sets your voice style!" set category = "OOC.Game Settings" - var/list/possible_voice_types = list( - "beep-boop", - "goon speak 1", - "goon speak 2", - "goon speak 3", - "goon speak 4", - "goon speak blub", - "goon speak bottalk", - "goon speak buwoo", - "goon speak cow", - "goon speak lizard", - "goon speak pug", - "goon speak pugg", - "goon speak roach", - "goon speak skelly") + var/list/possible_voice_types = get_talk_sound() var/choice = tgui_input_list(usr, "Which set of sounds would you like to use for your character's speech sounds?", "Voice Sounds", possible_voice_types) if(!choice) voice_sounds_list = talk_sound - switch(choice) - if("beep-boop") - voice_sounds_list = talk_sound - if("goon speak 1") - voice_sounds_list = goon_speak_one_sound - if("goon speak 2") - voice_sounds_list = goon_speak_two_sound - if("goon speak 3") - voice_sounds_list = goon_speak_three_sound - if("goon speak 4") - voice_sounds_list = goon_speak_four_sound - if("goon speak blub") - voice_sounds_list = goon_speak_blub_sound - if("goon speak bottalk") - voice_sounds_list = goon_speak_bottalk_sound - if("goon speak buwoo") - voice_sounds_list = goon_speak_buwoo_sound - if("goon speak cow") - voice_sounds_list = goon_speak_cow_sound - if("goon speak lizard") - voice_sounds_list = goon_speak_lizard_sound - if("goon speak pug") - voice_sounds_list = goon_speak_pug_sound - if("goon speak pugg") - voice_sounds_list = goon_speak_pugg_sound - if("goon speak roach") - voice_sounds_list = goon_speak_roach_sound - if("goon speak skelly") - voice_sounds_list = goon_speak_skelly_sound + voice_sounds_list = get_talk_sound(choice) diff --git a/code/modules/mob/living/login.dm b/code/modules/mob/living/login.dm index 94819c56c9b..ab322eb9bcc 100644 --- a/code/modules/mob/living/login.dm +++ b/code/modules/mob/living/login.dm @@ -29,35 +29,7 @@ if(!voice_sounds_list.len || !voice_sounds_list) if(client.prefs.voice_sound) var/prefsound = client.prefs.voice_sound - switch(prefsound) - if("beep-boop") - voice_sounds_list = talk_sound - if("goon speak 1") - voice_sounds_list = goon_speak_one_sound - if("goon speak 2") - voice_sounds_list = goon_speak_two_sound - if("goon speak 3") - voice_sounds_list = goon_speak_three_sound - if("goon speak 4") - voice_sounds_list = goon_speak_four_sound - if("goon speak blub") - voice_sounds_list = goon_speak_blub_sound - if("goon speak bottalk") - voice_sounds_list = goon_speak_bottalk_sound - if("goon speak buwoo") - voice_sounds_list = goon_speak_buwoo_sound - if("goon speak cow") - voice_sounds_list = goon_speak_cow_sound - if("goon speak lizard") - voice_sounds_list = goon_speak_lizard_sound - if("goon speak pug") - voice_sounds_list = goon_speak_pug_sound - if("goon speak pugg") - voice_sounds_list = goon_speak_pugg_sound - if("goon speak roach") - voice_sounds_list = goon_speak_roach_sound - if("goon speak skelly") - voice_sounds_list = goon_speak_skelly_sound + voice_sounds_list = get_talk_sound(prefsound) else voice_sounds_list = talk_sound //VOREStation Add End diff --git a/code/modules/mob/living/silicon/pai/life.dm b/code/modules/mob/living/silicon/pai/life.dm index c506924923c..683c1ffac8e 100644 --- a/code/modules/mob/living/silicon/pai/life.dm +++ b/code/modules/mob/living/silicon/pai/life.dm @@ -34,6 +34,7 @@ to_chat(src, span_green("Communication circuit reinitialized. Speech and messaging functionality restored.")) handle_statuses() + handle_sleeping() if(health <= 0) card.death_damage() diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index c7615e92e61..b5c4dd522ff 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -136,12 +136,21 @@ return 0 // This one takes an object's type instead of an instance, as above. -/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare) +/mob/living/silicon/robot/proc/has_active_type(var/type_to_compare, var/explicit = FALSE) var/list/active_modules = list(module_state_1, module_state_2, module_state_3) - if(is_path_in_list(type_to_compare, active_modules)) + if(is_type_in_modules(type_to_compare, active_modules, explicit)) return TRUE return FALSE +/mob/living/silicon/robot/proc/is_type_in_modules(var/type, var/list/modules, var/explicit = FALSE) + for(var/atom/module in modules) + if(explicit && isatom(module)) + if(module.type == type) + return TRUE + else if(istype(module, type)) + return TRUE + return FALSE + //Helper procs for cyborg modules on the UI. //These are hackish but they help clean up code elsewhere. diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index d10451a5201..b95612426a0 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -343,6 +343,8 @@ if(module) var/list/module_sprites = SSrobot_sprites.get_module_sprites(module, src) if(module_sprites.len == 1 || !client) + if(!module_sprites.len) + return sprite_datum = module_sprites[1] sprite_datum.do_equipment_glamour(module) return diff --git a/code/modules/mob/living/simple_mob/simple_mob.dm b/code/modules/mob/living/simple_mob/simple_mob.dm index 2eff632c3a4..9f919a2b4b7 100644 --- a/code/modules/mob/living/simple_mob/simple_mob.dm +++ b/code/modules/mob/living/simple_mob/simple_mob.dm @@ -387,3 +387,6 @@ else to_chat(usr, "You stop sensing creatures beyond the walls.") sight -= SEE_MOBS + +/mob/living/simple_mob/proc/character_directory_species() + return "simplemob" diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm index abd7cdf105d..a1335771f85 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/corrupt_hounds.dm @@ -59,6 +59,8 @@ say_list_type = /datum/say_list/corrupthound ai_holder_type = /datum/ai_holder/simple_mob/melee/evasive/corrupthound + has_langs = list(LANGUAGE_GALCOM, LANGUAGE_EAL) //Robots speak in beep, yes? + max_buckled_mobs = 1 //Yeehaw can_buckle = TRUE buckle_movable = TRUE diff --git a/code/modules/mob/new_player/sprite_accessories_ear.dm b/code/modules/mob/new_player/sprite_accessories_ear.dm index 6f7c3263787..2b5f60c892e 100644 --- a/code/modules/mob/new_player/sprite_accessories_ear.dm +++ b/code/modules/mob/new_player/sprite_accessories_ear.dm @@ -6,14 +6,14 @@ //////////////////////////// */ /datum/sprite_accessory/ears - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/human_races/sprite_accessories/ears.dmi' do_colouration = 0 // Set to 1 to blend (ICON_ADD) hair color color_blend_mode = ICON_ADD // Only appliciable if do_coloration = 1 var/extra_overlay // Icon state of an additional overlay to blend in. var/extra_overlay2 - var/desc = "You should not see this..." + var/desc = DEVELOPER_WARNING_NAME em_block = TRUE //species_allowed = list(SPECIES_EVENT1, SPECIES_EVENT2, SPECIES_EVENT3) //Removing Polaris whitelits, ones we need are defined in our files diff --git a/code/modules/mob/new_player/sprite_accessories_ear_vr.dm b/code/modules/mob/new_player/sprite_accessories_ear_vr.dm index 67cfbed5eb1..aacf98605f9 100644 --- a/code/modules/mob/new_player/sprite_accessories_ear_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_ear_vr.dm @@ -6,7 +6,7 @@ //////////////////////////// */ /datum/sprite_accessory/ears - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/ears_vr.dmi' do_colouration = 0 // Set to 1 to blend (ICON_ADD) hair color species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER) //This lets all races use diff --git a/code/modules/mob/new_player/sprite_accessories_tail.dm b/code/modules/mob/new_player/sprite_accessories_tail.dm index fd9ad113829..858fe01b106 100644 --- a/code/modules/mob/new_player/sprite_accessories_tail.dm +++ b/code/modules/mob/new_player/sprite_accessories_tail.dm @@ -6,7 +6,7 @@ //////////////////////////// */ /datum/sprite_accessory/tail - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/human_races/sprite_accessories/tails.dmi' do_colouration = 0 //Set to 1 to enable coloration using the tail color. @@ -16,7 +16,7 @@ var/extra_overlay2 //Tertiary. var/show_species_tail = 0 // If false, do not render species' tail. var/clothing_can_hide = 1 // If true, clothing with HIDETAIL hides it - var/desc = "You should not see this..." + var/desc = DEVELOPER_WARNING_NAME var/ani_state // State when wagging/animated var/extra_overlay_w // Wagging state for extra overlay var/extra_overlay2_w // Tertiary wagging. diff --git a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm index e516eb2ac6e..8b510e65021 100644 --- a/code/modules/mob/new_player/sprite_accessories_tail_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_tail_vr.dm @@ -6,7 +6,7 @@ //////////////////////////// */ /datum/sprite_accessory/tail - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/tails_vr.dmi' var/offset_x = 0 var/offset_y = 0 @@ -1439,7 +1439,7 @@ //LONG TAILS ARE NOT TAUR BUTTS >:O /datum/sprite_accessory/tail/longtail - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/taurs_vr.dmi' offset_x = -16 do_colouration = 1 // Yes color, using tail color diff --git a/code/modules/mob/new_player/sprite_accessories_taur.dm b/code/modules/mob/new_player/sprite_accessories_taur.dm index 6dc5cbb0116..b72a372b35c 100644 --- a/code/modules/mob/new_player/sprite_accessories_taur.dm +++ b/code/modules/mob/new_player/sprite_accessories_taur.dm @@ -128,7 +128,7 @@ // Taur sprites are now a subtype of tail since they are mutually exclusive anyway. /datum/sprite_accessory/tail/taur - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/human_races/sprite_accessories/taurs.dmi' do_colouration = 1 // Yes color, using tail color color_blend_mode = ICON_MULTIPLY // The sprites for taurs are designed for ICON_MULTIPLY diff --git a/code/modules/mob/new_player/sprite_accessories_taur_vr.dm b/code/modules/mob/new_player/sprite_accessories_taur_vr.dm index f9525966645..8d570d15cf4 100644 --- a/code/modules/mob/new_player/sprite_accessories_taur_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_taur_vr.dm @@ -9,7 +9,7 @@ // Taur sprites are now a subtype of tail since they are mutually exclusive anyway. /datum/sprite_accessory/tail/taur - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/taurs_vr.dmi' do_colouration = 1 // Yes color, using tail color color_blend_mode = ICON_MULTIPLY // The sprites for taurs are designed for ICON_MULTIPLY @@ -888,7 +888,7 @@ suit_sprites = 'icons/mob/taursuits_noodle_vr.dmi' clip_mask_state = "taur_clip_mask_noodle" icon_sprite_tag = "noodle" -/* CHOMPEdit - removed as a sprite accessory of the same name already exists for us, and having this here stops it from registering as a sprite accessory. + /datum/sprite_accessory/tail/taur/sect_drone name = "Sect Drone (Taur)" icon_state = "sect_drone" @@ -909,7 +909,7 @@ msg_owner_grab_fail = "You step down onto %prey, squishing them and forcing them down to the ground!" msg_prey_grab_fail = "%owner steps down and squishes you with their leg, forcing you down to the ground!" -*/ + /datum/sprite_accessory/tail/taur/sect_drone/fat name = "Fat Sect Drone (Taur)" icon_state = "fat_sect_drone" diff --git a/code/modules/mob/new_player/sprite_accessories_vr.dm b/code/modules/mob/new_player/sprite_accessories_vr.dm index c9da7d34a22..0a440d4a23f 100644 --- a/code/modules/mob/new_player/sprite_accessories_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_vr.dm @@ -575,13 +575,13 @@ // No, this isn't a character creation option, but... I guess in the future it could be, if anyone wants that? /datum/sprite_accessory/hair_accessory - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/hair_accessories_vr.dmi' do_colouration = 0 // Set to 1 to blend (ICON_ADD) hair color var/ignores_lighting = 0 // Whether or not this hair accessory will ignore lighting and glow in the dark. color_blend_mode = ICON_ADD // Only appliciable if do_coloration = 1 - var/desc = "You should not see this..." + var/desc = DEVELOPER_WARNING_NAME /datum/sprite_accessory/hair_accessory/verie_hair_glow name = "veries hair glow" diff --git a/code/modules/mob/new_player/sprite_accessories_wing.dm b/code/modules/mob/new_player/sprite_accessories_wing.dm index 3b13156b9d2..3f2f2efd69f 100644 --- a/code/modules/mob/new_player/sprite_accessories_wing.dm +++ b/code/modules/mob/new_player/sprite_accessories_wing.dm @@ -6,7 +6,7 @@ //////////////////////////// */ /datum/sprite_accessory/wing - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/human_races/sprite_accessories/wings.dmi' do_colouration = 0 //Set to 1 to enable coloration using the tail color. @@ -16,7 +16,7 @@ var/extra_overlay2 //Tertiary. var/clothing_can_hide = 1 // If true, clothing with HIDETAIL hides it. If the clothing is bulky enough to hide a tail, it should also hide wings. // var/show_species_tail = 1 // Just so // TODO - Seems not needed ~Leshana - var/desc = "You should not see this..." + var/desc = DEVELOPER_WARNING_NAME var/ani_state // State when flapping/animated var/extra_overlay_w // Flapping state for extra overlay var/extra_overlay2_w diff --git a/code/modules/mob/new_player/sprite_accessories_wing_large_vr.dm b/code/modules/mob/new_player/sprite_accessories_wing_large_vr.dm index e516d6b0853..66263ad850b 100644 --- a/code/modules/mob/new_player/sprite_accessories_wing_large_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_wing_large_vr.dm @@ -2,7 +2,7 @@ /datum/sprite_accessory/wing/large - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/wings96_vr.dmi' wing_offset = 32 diff --git a/code/modules/mob/new_player/sprite_accessories_wing_vr.dm b/code/modules/mob/new_player/sprite_accessories_wing_vr.dm index d26b57177bd..6610a2e8ea8 100644 --- a/code/modules/mob/new_player/sprite_accessories_wing_vr.dm +++ b/code/modules/mob/new_player/sprite_accessories_wing_vr.dm @@ -6,7 +6,7 @@ //////////////////////////// */ /datum/sprite_accessory/wing - name = "You should not see this..." + name = DEVELOPER_WARNING_NAME icon = 'icons/mob/vore/wings_vr.dmi' do_colouration = 0 //Set to 1 to enable coloration using the tail color. species_allowed = list(SPECIES_HUMAN, SPECIES_SKRELL, SPECIES_UNATHI, SPECIES_TAJ, SPECIES_TESHARI, SPECIES_NEVREAN, SPECIES_AKULA, SPECIES_SERGAL, SPECIES_FENNEC, SPECIES_ZORREN_HIGH, SPECIES_VULPKANIN, SPECIES_XENOCHIMERA, SPECIES_XENOHYBRID, SPECIES_VASILISSAN, SPECIES_RAPALA, SPECIES_PROTEAN, SPECIES_ALRAUNE, SPECIES_WEREBEAST, SPECIES_SHADEKIN, SPECIES_SHADEKIN_CREW, SPECIES_ALTEVIAN, SPECIES_LLEILL, SPECIES_HANNER) //This lets all races use diff --git a/code/modules/organs/internal/appendix.dm b/code/modules/organs/internal/appendix.dm index a941d5aadcf..456f8f157ce 100644 --- a/code/modules/organs/internal/appendix.dm +++ b/code/modules/organs/internal/appendix.dm @@ -9,12 +9,9 @@ /mob/living/carbon/human/proc/appendicitis() if(stat == DEAD) return 0 - var/obj/item/organ/internal/appendix/A = internal_organs_by_name[O_APPENDIX] - if(istype(A) && !A.inflamed) - A.inflamed = 1 - return 1 + ForceContractDisease(new /datum/disease/appendicitis) return 0 - +/* /obj/item/organ/internal/appendix/process() ..() @@ -47,7 +44,7 @@ owner.adjustToxLoss(25) groin.wounds += W inflamed = 1 - +*/ /obj/item/organ/internal/appendix/removed() if(inflamed) icon_state = "[initial(icon_state)]inflamed" diff --git a/code/modules/organs/robolimbs_vr.dm b/code/modules/organs/robolimbs_vr.dm index 8fa9a8e3cf9..a3956a2bf9c 100644 --- a/code/modules/organs/robolimbs_vr.dm +++ b/code/modules/organs/robolimbs_vr.dm @@ -1,3 +1,7 @@ +GLOBAL_LIST_INIT(dsi_to_species, list(SPECIES_TAJARAN = "DSI - Tajaran", SPECIES_UNATHI = "DSI - Lizard", SPECIES_SERGAL = "DSI - Sergal", SPECIES_NEVREAN = "DSI - Nevrean", \ + SPECIES_VULPKANIN = "DSI - Vulpkanin", SPECIES_AKULA = "DSI - Akula", SPECIES_VASILISSAN = "DSI - Vasilissan", SPECIES_ZORREN = "DSI - Zorren",\ + SPECIES_TESHARI = "DSI - Teshari", SPECIES_FENNEC = "DSI - Fennec")) + //CitRP Port var/const/cyberbeast_monitor_styles = "blank=cyber_blank;\ default=cyber_default;\ diff --git a/code/modules/organs/subtypes/nano.dm b/code/modules/organs/subtypes/nano.dm index 0c750a70d63..023ccc8005a 100644 --- a/code/modules/organs/subtypes/nano.dm +++ b/code/modules/organs/subtypes/nano.dm @@ -2,81 +2,84 @@ /obj/item/organ/external/chest/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 50 // <-- This is different from the rest + max_damage = 70 // <-- This is different from the rest min_broken_damage = 1000 - vital = TRUE // <-- This is different from the rest + vital = TRUE model = "protean" /obj/item/organ/external/groin/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 30 // <-- This is different from the rest + max_damage = 70 min_broken_damage = 1000 //Multiple vital = FALSE model = "protean" /obj/item/organ/external/head/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 30 + max_damage = 70 min_broken_damage = 1000 //Inheritance vital = FALSE model = "protean" /obj/item/organ/external/arm/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 //Please vital = FALSE model = "protean" /obj/item/organ/external/arm/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/leg/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/leg/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/hand/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/hand/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/foot/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" /obj/item/organ/external/foot/right/unbreakable/nano robotic = ORGAN_NANOFORM encased = FALSE - max_damage = 20 + max_damage = 40 min_broken_damage = 1000 vital = FALSE model = "protean" +/obj/item/organ/external/head/unbreakable/nano/disfigure() + return //No way to repair disfigured prots + // // // Internal Organs /obj/item/organ/internal/nano robotic = ORGAN_ROBOT @@ -180,9 +183,11 @@ icon = initial(icon) icon_state = "posi1" stored_mmi.icon_state = "posi1" - stored_mmi.brainmob.languages = owner.languages +/obj/item/organ/internal/mmi_holder/posibrain/nano/emp_act() + return //Proteans handle EMP's differently + // The 'out on the ground' object, not the organ holder /obj/item/mmi/digital/posibrain/nano name = "protean posibrain" @@ -209,3 +214,19 @@ /obj/item/mmi/digital/posibrain/nano/transfer_identity() . = ..() icon_state = "posi1" + +/obj/item/organ/internal/nano/digest_act(atom/movable/item_storage = null) + return FALSE + +/datum/design/item/protean_reboot + name = "Protean Reboot Programmer" + id = "protean_reboot" + materials = list(MAT_STEEL = 2000, MAT_GLASS = 1000, MAT_PLASTEEL = 10000) + build_path = /obj/item/protean_reboot + sort_string = "JVAAZ" + +/obj/item/protean_reboot + name = "Protean Reboot Programmer" + desc = "A small, highly specialized programmer used to form the basis of a Protean swarm. A necessary component in reconstituting a Protean who has lost total body cohesion." + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "reboot" diff --git a/code/modules/power/apc.dm b/code/modules/power/apc.dm index f66a2afe203..16199e4b8c0 100644 --- a/code/modules/power/apc.dm +++ b/code/modules/power/apc.dm @@ -685,7 +685,7 @@ GLOBAL_LIST_EMPTY(apcs) // attack with hand - remove cell (if cover open) or interact with the APC -/obj/machinery/power/apc/verb/togglelock(mob/user as mob) +/obj/machinery/power/apc/proc/togglelock(mob/user) if(emagged) to_chat(user, "The panel is unresponsive.") else if(opened) @@ -873,7 +873,7 @@ GLOBAL_LIST_EMPTY(apcs) // to_world("[area.power_equip]") area.power_change() -/obj/machinery/power/apc/proc/can_use(mob/user as mob, var/loud = 0) //used by attack_hand() and Topic() +/obj/machinery/power/apc/proc/can_use(mob/user, var/loud = 0) //used by attack_hand() and Topic() if(!user.client) return 0 if(isobserver(user) && is_admin(user)) //This is to allow nanoUI interaction by ghost admins. diff --git a/code/modules/reagents/machinery/dispenser/cartridge_presets.dm b/code/modules/reagents/machinery/dispenser/cartridge_presets.dm index 9dfc3ac0d98..4f1098c6072 100644 --- a/code/modules/reagents/machinery/dispenser/cartridge_presets.dm +++ b/code/modules/reagents/machinery/dispenser/cartridge_presets.dm @@ -117,10 +117,14 @@ spawn_reagent = "lemonjuice" /obj/item/reagent_containers/chem_disp_cartridge/grapesoda spawn_reagent = "grapesoda" +/obj/item/reagent_containers/chem_disp_cartridge/pineapple + spawn_reagent = "pineapplejuice" // Bar, coffee /obj/item/reagent_containers/chem_disp_cartridge/coffee spawn_reagent = "coffee" +/obj/item/reagent_containers/chem_disp_cartridge/drip_coffee + spawn_reagent = "drip_coffee" /obj/item/reagent_containers/chem_disp_cartridge/cafe_latte spawn_reagent = "cafe_latte" /obj/item/reagent_containers/chem_disp_cartridge/soy_latte @@ -129,6 +133,8 @@ spawn_reagent = "hot_coco" /obj/item/reagent_containers/chem_disp_cartridge/milk spawn_reagent = "milk" +/obj/item/reagent_containers/chem_disp_cartridge/milk_foam + spawn_reagent = "milk_foam" /obj/item/reagent_containers/chem_disp_cartridge/cream spawn_reagent = "cream" /obj/item/reagent_containers/chem_disp_cartridge/mint @@ -144,6 +150,48 @@ /obj/item/reagent_containers/chem_disp_cartridge/decafchai spawn_reagent = "chaiteadecaf" +// syrups +/obj/item/reagent_containers/chem_disp_cartridge/syrup_pumpkin + spawn_reagent = "syrup_pumpkin" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_caramel + spawn_reagent = "syrup_caramel" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_scaramel + spawn_reagent = "syrup_salted_caramel" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_irish + spawn_reagent = "syrup_irish" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_almond + spawn_reagent = "syrup_almond" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_cinnamon + spawn_reagent = "syrup_cinnamon" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_pistachio + spawn_reagent = "syrup_pistachio" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_vanilla + spawn_reagent = "syrup_vanilla" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_toffee + spawn_reagent = "syrup_toffee" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_cherry + spawn_reagent = "syrup_cherry" +/obj/item/reagent_containers/chem_disp_cartridge/grenadine + spawn_reagent = "grenadine" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_butterscotch + spawn_reagent = "syrup_butterscotch" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_chocolate + spawn_reagent = "syrup_chocolate" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_wchocolate + spawn_reagent = "syrup_white_chocolate" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_strawberry + spawn_reagent = "syrup_strawberry" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_coconut + spawn_reagent = "syrup_coconut" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_ginger + spawn_reagent = "syrup_ginger" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_gingerbread + spawn_reagent = "syrup_gingerbread" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_peppermint + spawn_reagent = "syrup_peppermint" +/obj/item/reagent_containers/chem_disp_cartridge/syrup_birthday + spawn_reagent = "syrup_birthday" + // ERT /obj/item/reagent_containers/chem_disp_cartridge/inaprov spawn_reagent = "inaprovaline" diff --git a/code/modules/reagents/machinery/dispenser/dispenser2_energy.dm b/code/modules/reagents/machinery/dispenser/dispenser2_energy.dm index dfb16c3391e..2495018f5ec 100644 --- a/code/modules/reagents/machinery/dispenser/dispenser2_energy.dm +++ b/code/modules/reagents/machinery/dispenser/dispenser2_energy.dm @@ -47,7 +47,7 @@ /obj/machinery/chemical_dispenser/bar_soft dispense_reagents = list( "water", "ice", "coffee", "cream", "tea", "icetea", "cola", "spacemountainwind", "dr_gibb", "space_up", "tonic", - "sodawater", "lemonjuice", "lemon_lime", "sugar", "orangejuice", "limejuice", "watermelonjuice", "thirteenloko", "grapesoda" + "sodawater", "lemonjuice", "lemon_lime", "sugar", "orangejuice", "limejuice", "watermelonjuice", "thirteenloko", "grapesoda", "pineapplejuice" ) /obj/machinery/chemical_dispenser/bar_alc @@ -58,6 +58,13 @@ /obj/machinery/chemical_dispenser/bar_coffee dispense_reagents = list( - "coffee", "cafe_latte", "soy_latte", "hot_coco", "milk", "cream", "tea", "ice", - "orangejuice", "lemonjuice", "limejuice", "berryjuice", "mint", "decaf", "greentea" + "coffee", "cafe_latte", "soy_latte", "hot_coco", "milk", "cream", "tea", "ice", "water", + "orangejuice", "lemonjuice", "limejuice", "berryjuice", "mint", "decaf", "greentea", "milk_foam", "drip_coffee" + ) + +/obj/machinery/chemical_dispenser/bar_syrup + dispense_reagents = list( + "syrup_pumpkin", "syrup_caramel", "syrup_salted_caramel", "syrup_irish", "syrup_almond", "syrup_cinnamon", "syrup_pistachio", + "syrup_vanilla", "syrup_toffee", "grenadine", "syrup_cherry", "syrup_butterscotch", "syrup_chocolate", "syrup_white_chocolate", "syrup_strawberry", + "syrup_coconut", "syrup_ginger", "syrup_gingerbread", "syrup_peppermint", "syrup_birthday" ) diff --git a/code/modules/reagents/machinery/dispenser/dispenser_presets.dm b/code/modules/reagents/machinery/dispenser/dispenser_presets.dm index 65523e16f35..49fd9f5a223 100644 --- a/code/modules/reagents/machinery/dispenser/dispenser_presets.dm +++ b/code/modules/reagents/machinery/dispenser/dispenser_presets.dm @@ -85,7 +85,8 @@ /obj/item/reagent_containers/chem_disp_cartridge/lime, /obj/item/reagent_containers/chem_disp_cartridge/watermelon, /obj/item/reagent_containers/chem_disp_cartridge/lemon, - /obj/item/reagent_containers/chem_disp_cartridge/grapesoda + /obj/item/reagent_containers/chem_disp_cartridge/grapesoda, + /obj/item/reagent_containers/chem_disp_cartridge/pineapple ) /obj/machinery/chemical_dispenser/bar_alc @@ -130,11 +131,14 @@ /obj/machinery/chemical_dispenser/bar_coffee/full spawn_cartridges = list( /obj/item/reagent_containers/chem_disp_cartridge/coffee, + /obj/item/reagent_containers/chem_disp_cartridge/drip_coffee, /obj/item/reagent_containers/chem_disp_cartridge/cafe_latte, /obj/item/reagent_containers/chem_disp_cartridge/soy_latte, /obj/item/reagent_containers/chem_disp_cartridge/hot_coco, /obj/item/reagent_containers/chem_disp_cartridge/milk, /obj/item/reagent_containers/chem_disp_cartridge/cream, + /obj/item/reagent_containers/chem_disp_cartridge/milk_foam, + /obj/item/reagent_containers/chem_disp_cartridge/water, /obj/item/reagent_containers/chem_disp_cartridge/sugar, /obj/item/reagent_containers/chem_disp_cartridge/tea, /obj/item/reagent_containers/chem_disp_cartridge/ice, @@ -148,3 +152,34 @@ /obj/item/reagent_containers/chem_disp_cartridge/chaitea, /obj/item/reagent_containers/chem_disp_cartridge/decafchai ) + +/obj/machinery/chemical_dispenser/bar_syrup + name = "syrup dispenser" + desc = "Allow your customers to test your patience to the extremes." + icon_state = "syrup_dispenser" + ui_title = "Syrup Dispenser" + accept_drinking = 1 + +/obj/machinery/chemical_dispenser/bar_syrup/full + spawn_cartridges = list( + /obj/item/reagent_containers/chem_disp_cartridge/syrup_pumpkin, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_caramel, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_scaramel, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_irish, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_almond, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_cinnamon, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_pistachio, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_vanilla, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_toffee, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_cherry, + /obj/item/reagent_containers/chem_disp_cartridge/grenadine, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_butterscotch, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_chocolate, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_wchocolate, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_strawberry, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_coconut, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_ginger, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_gingerbread, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_peppermint, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_birthday + ) diff --git a/code/modules/reagents/machinery/dispenser/supply.dm b/code/modules/reagents/machinery/dispenser/supply.dm index 32dea5167e2..e13cca8c129 100644 --- a/code/modules/reagents/machinery/dispenser/supply.dm +++ b/code/modules/reagents/machinery/dispenser/supply.dm @@ -38,6 +38,16 @@ containername = "coffee dispenser crate" group = "Reagents" +/datum/supply_pack/syrup_dispenser + name = "Syrup dispenser" + contains = list( + /obj/machinery/chemical_dispenser/bar_syrup{anchored = FALSE} + ) + cost = 25 + containertype = /obj/structure/largecrate + containername = "Syrup dispenser crate" + group = "Reagents" + /datum/supply_pack/reagents name = "Chemistry dispenser refill" contains = list( @@ -123,7 +133,8 @@ /obj/item/reagent_containers/chem_disp_cartridge/lime, /obj/item/reagent_containers/chem_disp_cartridge/watermelon, /obj/item/reagent_containers/chem_disp_cartridge/lemon, - /obj/item/reagent_containers/chem_disp_cartridge/grapesoda + /obj/item/reagent_containers/chem_disp_cartridge/grapesoda, + /obj/item/reagent_containers/chem_disp_cartridge/pineapple ) cost = 50 containertype = /obj/structure/closet/crate @@ -134,11 +145,14 @@ name = "Coffee machine dispenser refill" contains = list( /obj/item/reagent_containers/chem_disp_cartridge/coffee, + /obj/item/reagent_containers/chem_disp_cartridge/drip_coffee, /obj/item/reagent_containers/chem_disp_cartridge/cafe_latte, /obj/item/reagent_containers/chem_disp_cartridge/soy_latte, /obj/item/reagent_containers/chem_disp_cartridge/hot_coco, /obj/item/reagent_containers/chem_disp_cartridge/milk, /obj/item/reagent_containers/chem_disp_cartridge/cream, + /obj/item/reagent_containers/chem_disp_cartridge/milk_foam, + /obj/item/reagent_containers/chem_disp_cartridge/water, /obj/item/reagent_containers/chem_disp_cartridge/sugar, /obj/item/reagent_containers/chem_disp_cartridge/tea, /obj/item/reagent_containers/chem_disp_cartridge/ice, @@ -148,13 +162,44 @@ /obj/item/reagent_containers/chem_disp_cartridge/lime, /obj/item/reagent_containers/chem_disp_cartridge/berry, /obj/item/reagent_containers/chem_disp_cartridge/greentea, - /obj/item/reagent_containers/chem_disp_cartridge/decaf + /obj/item/reagent_containers/chem_disp_cartridge/decaf, + /obj/item/reagent_containers/chem_disp_cartridge/chaitea, + /obj/item/reagent_containers/chem_disp_cartridge/decafchai ) cost = 50 containertype = /obj/structure/closet/crate containername = "coffee drinks crate" group = "Reagents" +/datum/supply_pack/syrup_reagents + name = "Syrup machine dispenser refill" + contains = list( + /obj/item/reagent_containers/chem_disp_cartridge/syrup_pumpkin, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_caramel, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_scaramel, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_irish, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_almond, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_cinnamon, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_pistachio, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_vanilla, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_toffee, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_cherry, + /obj/item/reagent_containers/chem_disp_cartridge/grenadine, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_butterscotch, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_chocolate, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_wchocolate, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_strawberry, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_coconut, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_ginger, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_gingerbread, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_peppermint, + /obj/item/reagent_containers/chem_disp_cartridge/syrup_birthday + ) + cost = 50 + containertype = /obj/structure/closet/crate + containername = "Syrup crate" + group = "Reagents" + /datum/supply_pack/dispenser_cartridges name = "Empty dispenser cartridges" contains = list( diff --git a/code/modules/reagents/reactions/instant/drinks.dm b/code/modules/reagents/reactions/instant/drinks.dm index 75e15d188cc..4736a12f057 100644 --- a/code/modules/reagents/reactions/instant/drinks.dm +++ b/code/modules/reagents/reactions/instant/drinks.dm @@ -82,6 +82,90 @@ required_reagents = list("ice" = 1, "coffee" = 2) result_amount = 3 +/decl/chemical_reaction/instant/drinks/icecoffee/alt + name = "Iced Drip Coffee" + id = "icecoffee" + result = "icecoffee" + required_reagents = list("ice" = 1, "drip_coffee" = 2) + result_amount = 3 + +/decl/chemical_reaction/instant/drinks/blackeye + name = "Black Eye Coffee" + id = "black_eye" + result = "black_eye" + required_reagents = list("drip_coffee" = 1, "coffee" = 1) + result_amount = 1 + +/decl/chemical_reaction/instant/drinks/americano + name = "Americano" + id = "americano" + result = "americano" + required_reagents = list("water" = 1, "long_black" = 2) + result_amount = 3 + +/decl/chemical_reaction/instant/drinks/long_black + name = "Long Black Coffee" + id = "long_black" + result = "long_black" + required_reagents = list("water" = 1, "coffee" = 1) + result_amount = 2 + +/decl/chemical_reaction/instant/drinks/macchiato + name = "Macchiato" + id = "macchiato" + result = "macchiato" + required_reagents = list("milk" = 1, "coffee" = 2) + result_amount = 3 + +/decl/chemical_reaction/instant/drinks/cortado + name = "Cortado" + id = "cortado" + result = "cortado" + required_reagents = list("macchiato" = 3, "milk_foam" = 1) // 2 coffee, 1 milk, 1 milk foam + result_amount = 4 + +/decl/chemical_reaction/instant/drinks/breve + name = "Breve" + id = "breve" + result = "breve" + required_reagents = list("cortado" = 4, "cream" = 1) // 2 coffee, 1 milk, 1 milk foam, 1 cream + result_amount = 5 + +/decl/chemical_reaction/instant/drinks/cappuccino + name = "Cappuccino" + id = "cappuccino" + result = "cappuccino" + required_reagents = list("milk" = 1, "milk_foam" = 1, "cortado" = 4) // 2 coffee, 2 milk, 2 milk foam + result_amount = 6 + +/decl/chemical_reaction/instant/drinks/flat_white + name = "Flat White Coffee" + id = "flat_white" + result = "flat_white" + required_reagents = list("milk" = 2, "drip_coffee" = 1) // 2 drip coffee, 4 milk I'M SORRY THAT ITS DRIP COFFEE, otherwise it just gets in the way of all other reactions + result_amount = 3 + +/decl/chemical_reaction/instant/drinks/mocha + name = "Mocha" + id = "mocha" + result = "mocha" + required_reagents = list("milk" = 1, "cream" = 1, "milk_foam" = 1, "hot_coco" = 2, "breve" = 5) // 2 coffee, 2 milk, 2 cream, 2 milk foam and 2 hot coco + result_amount = 10 + +/decl/chemical_reaction/instant/drinks/mocha/alt //incase they use cream before milk + name = "Mocha" + id = "mocha" + result = "mocha" + required_reagents = list("cream" = 2, "hot_coco" = 2, "cappuccino" = 6) // 2 coffee, 2 milk, 2 cream, 2 milk foam and 2 hot coco + result_amount = 10 + +/decl/chemical_reaction/instant/drinks/vienna + name = "Vienna" + id = "vienna" + result = "vienna" + required_reagents = list("cream" = 2, "coffee" = 1) + result_amount = 3 + /decl/chemical_reaction/instant/drinks/nuka_cola name = "Nuclear Cola" id = "nuka_cola" @@ -499,7 +583,7 @@ name = "Cafe Latte" id = "cafe_latte" result = "cafe_latte" - required_reagents = list("coffee" = 1, "milk" = 1) + required_reagents = list("flat_white" = 1, "milk" = 1) result_amount = 2 /decl/chemical_reaction/instant/drinks/acidspit diff --git a/code/modules/reagents/reactions/instant/virology.dm b/code/modules/reagents/reactions/instant/virology.dm index f8d50383267..26576c23d6c 100644 --- a/code/modules/reagents/reactions/instant/virology.dm +++ b/code/modules/reagents/reactions/instant/virology.dm @@ -40,6 +40,13 @@ required_reagents = list("inaprovaline" = 1, "mutagenvirusfood" = 1) result_amount = 2 +/decl/chemical_reaction/instant/virus_food_size + name = "sizeoxadone virus food" + id = "sizeoxadonevirusfood" + result = "sizevirusfood" + required_reagents = list("sizeoxadone" = 1, "phoronvirusfood" = 1) + result_amount = 2 + /decl/chemical_reaction/instant/mix_virus name = "Mix Virus" id = "mixvirus" @@ -48,6 +55,9 @@ var/level_min = 0 var/level_max = 2 +/decl/chemical_reaction/instant/mix_virus/picky + var/list/datum/symptom/symptoms + /decl/chemical_reaction/instant/mix_virus/on_reaction(datum/reagents/holder) var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list if(B && B.data) @@ -55,6 +65,13 @@ if(D) D.Evolve(level_min, level_max) +/decl/chemical_reaction/instant/mix_virus/picky/on_reaction(datum/reagents/holder) + var/datum/reagent/blood/B = locate(/datum/reagent/blood) in holder.reagent_list + if(B && B.data) + var/datum/disease/advance/D = locate(/datum/disease/advance) in B.data["viruses"] + if(D) + D.PickyEvolve(symptoms) + /decl/chemical_reaction/instant/mix_virus/mix_virus_2 name = "Mix Virus 2" id = "mixvirus2" @@ -111,6 +128,17 @@ level_min = 1 level_max = 1 +/decl/chemical_reaction/instant/mix_virus/picky/size + name = "Mix Virus Size" + id = "mixvirussize" + required_reagents = list("sizevirusfood" = 1) + symptoms = list( + /datum/symptom/macrophage, + /datum/symptom/size, + /datum/symptom/size/grow, + /datum/symptom/size/shrink + ) + /decl/chemical_reaction/instant/mix_virus/rem_virus name = "Devolve Virus" id = "remvirus" diff --git a/code/modules/reagents/reagents/_reagents.dm b/code/modules/reagents/reagents/_reagents.dm index b5ee4d9d731..a8865bc4d48 100644 --- a/code/modules/reagents/reagents/_reagents.dm +++ b/code/modules/reagents/reagents/_reagents.dm @@ -33,6 +33,7 @@ var/cup_name = null var/cup_desc = null var/cup_center_of_mass = null + var/cup_prefix = null var/color = "#000000" var/color_weight = 1 diff --git a/code/modules/reagents/reagents/dispenser.dm b/code/modules/reagents/reagents/dispenser.dm index dcdc0f7c50b..185fa25dc12 100644 --- a/code/modules/reagents/reagents/dispenser.dm +++ b/code/modules/reagents/reagents/dispenser.dm @@ -86,6 +86,7 @@ taste_description = "pure alcohol" reagent_state = LIQUID color = "#404030" + cup_prefix = "alcoholic" ingest_met = REM * 2 diff --git a/code/modules/reagents/reagents/food_drinks.dm b/code/modules/reagents/reagents/food_drinks.dm index 258d19b89d5..43dad05e8b2 100644 --- a/code/modules/reagents/reagents/food_drinks.dm +++ b/code/modules/reagents/reagents/food_drinks.dm @@ -271,6 +271,7 @@ id = "glucose" taste_description = "sweetness" color = "#FFFFFF" + cup_prefix = "sweetened" injectable = 1 @@ -314,6 +315,7 @@ color = "#EDB91F" taste_description = "cheese" allergen_type = ALLERGEN_DAIRY //Cheese is made from dairy + cup_prefix = "cheesy" /datum/reagent/nutriment/protein/egg name = "egg yolk" @@ -321,6 +323,7 @@ taste_description = "egg" color = "#FFFFAA" allergen_type = ALLERGEN_EGGS //Eggs contain egg + cup_prefix = "eggy" /datum/reagent/nutriment/protein/murk name = "murkfin protein" @@ -343,6 +346,7 @@ taste_description = "sweetness" nutriment_factor = 10 color = "#FFFF00" + cup_prefix = "honey" /datum/reagent/nutriment/honey/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -373,6 +377,7 @@ nutriment_factor = 10 color = "#FFFFFF" allergen_type = ALLERGEN_EGGS //Mayo is made from eggs + cup_prefix = "mayo" /datum/reagent/nutriment/yeast name = "Yeast" @@ -436,6 +441,7 @@ nutriment_factor = 5 color = "#302000" allergen_type = ALLERGEN_CHOCOLATE + cup_prefix = "coco" /datum/reagent/nutriment/chocolate name = "Chocolate" @@ -446,6 +452,7 @@ nutriment_factor = 5 taste_mult = 1.3 allergen_type = ALLERGEN_CHOCOLATE + cup_prefix = "chocolate" /datum/reagent/nutriment/instantjuice name = "Juice Powder" @@ -461,6 +468,7 @@ description = "Dehydrated, powdered grape juice." taste_description = "dry grapes" color = "#863333" + cup_prefix = "grape" /datum/reagent/nutriment/instantjuice/orange name = "Orange Juice Powder" @@ -468,6 +476,7 @@ description = "Dehydrated, powdered orange juice." taste_description = "dry oranges" color = "#e78108" + cup_prefix = "orange" /datum/reagent/nutriment/instantjuice/watermelon name = "Watermelon Juice Powder" @@ -475,6 +484,7 @@ description = "Dehydrated, powdered watermelon juice." taste_description = "dry sweet watermelon" color = "#b83333" + cup_prefix = "melon" /datum/reagent/nutriment/instantjuice/apple name = "Apple Juice Powder" @@ -482,6 +492,7 @@ description = "Dehydrated, powdered apple juice." taste_description = "dry sweet apples" color = "#c07c40" + cup_prefix = "apple" /datum/reagent/nutriment/soysauce name = "Soy Sauce" @@ -493,6 +504,7 @@ nutriment_factor = 2 color = "#792300" allergen_type = ALLERGEN_BEANS //Soy (beans) + cup_prefix = "umami" /datum/reagent/nutriment/vinegar name = "Vinegar" @@ -502,6 +514,7 @@ reagent_state = LIQUID nutriment_factor = 5 color = "#54410C" + cup_prefix = "acidic" /datum/reagent/nutriment/ketchup name = "Ketchup" @@ -512,6 +525,7 @@ nutriment_factor = 5 color = "#731008" allergen_type = ALLERGEN_FRUIT //Tomatoes are a fruit. + cup_prefix = "tomato" /datum/reagent/nutriment/mustard name = "Mustard" @@ -521,6 +535,7 @@ reagent_state = LIQUID nutriment_factor = 5 color = "#E3BD00" + cup_prefix = "mustard" /datum/reagent/nutriment/barbecue name = "Barbeque Sauce" @@ -530,6 +545,7 @@ reagent_state = LIQUID nutriment_factor = 5 color = "#4F330F" + cup_prefix = "barbecue" /datum/reagent/nutriment/rice name = "Rice" @@ -562,6 +578,7 @@ nutriment_factor = 30 color = "#4F3500" allergen_type = ALLERGEN_SEEDS //Peanuts(seeds) + cup_prefix = "peanut butter" /datum/reagent/nutriment/vanilla name = "Vanilla Extract" @@ -572,6 +589,7 @@ reagent_state = LIQUID nutriment_factor = 2 color = "#0F0A00" + cup_prefix = "vanilla" /datum/reagent/nutriment/durian name = "Durian Paste" @@ -618,6 +636,7 @@ taste_description = "sugar" nutriment_factor = 1 color = "#FF00FF" + cup_prefix = "sprinkled" /datum/reagent/nutriment/mint name = "Mint" @@ -626,6 +645,7 @@ taste_description = "mint" reagent_state = LIQUID color = "#CF3600" + cup_prefix = "minty" /datum/reagent/lipozine // The anti-nutriment. name = "Lipozine" @@ -650,6 +670,7 @@ color = "#FFFFFF" overdose = REAGENTS_OVERDOSE ingest_met = REM + cup_prefix = "salty" /datum/reagent/sodiumchloride/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -669,6 +690,7 @@ reagent_state = SOLID ingest_met = REM color = "#000000" + cup_prefix = "peppery" /datum/reagent/enzyme name = "Universal Enzyme" @@ -686,6 +708,7 @@ description = "An exotic blend of spices for cooking. Definitely not worms." reagent_state = SOLID color = "#e08702" + cup_prefix = "spicy" /datum/reagent/browniemix name = "Brownie Mix" @@ -759,6 +782,7 @@ reagent_state = LIQUID ingest_met = REM color = "#B31008" + cup_prefix = "hot" /datum/reagent/capsaicin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -798,6 +822,7 @@ touch_met = 50 // Get rid of it quickly ingest_met = REM color = "#B31008" + cup_prefix = "dangerously hot" /datum/reagent/condensedcapsaicin/affect_blood(var/mob/living/carbon/M, var/alien, var/removed) if(alien == IS_DIONA) @@ -986,6 +1011,7 @@ glass_name = "banana juice" glass_desc = "The raw essence of a banana. HONK!" allergen_type = ALLERGEN_FRUIT //Bananas are fruit + cup_prefix = "banana" /datum/reagent/drink/juice/berry name = "Berry Juice" @@ -997,6 +1023,7 @@ glass_name = "berry juice" glass_desc = "Berry juice. Or maybe it's jam. Who cares?" allergen_type = ALLERGEN_FRUIT //Berries are fruit + cup_prefix = "berry" /datum/reagent/drink/juice/pineapple name = "Pineapple Juice" @@ -1008,6 +1035,7 @@ glass_name = "pineapple juice" glass_desc = "Pineapple juice. Or maybe it's spineapple. Who cares?" allergen_type = ALLERGEN_FRUIT //Pineapples are fruit + cup_prefix = "pineapple" /datum/reagent/drink/juice/carrot name = "Carrot juice" @@ -1019,6 +1047,7 @@ glass_name = "carrot juice" glass_desc = "It is just like a carrot but without crunching." allergen_type = ALLERGEN_VEGETABLE //Carrots are vegetables + cup_prefix = "carrot" /datum/reagent/drink/juice/carrot/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -1033,6 +1062,7 @@ glass_name = "lettuce juice" glass_desc = "This is just lettuce water. Fresh but boring." + cup_prefix = "lettuce" /datum/reagent/drink/juice name = "Grape Juice" @@ -1045,6 +1075,7 @@ glass_name = "grape juice" glass_desc = "It's grrrrrape!" allergen_type = ALLERGEN_FRUIT //Grapes are fruit + cup_prefix = "grape" /datum/reagent/drink/juice/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -1079,6 +1110,7 @@ glass_name = "lemon juice" glass_desc = "Sour..." allergen_type = ALLERGEN_FRUIT //Lemons are fruit + cup_prefix = "lemon" /datum/reagent/drink/juice/apple @@ -1092,6 +1124,7 @@ glass_name = "apple juice" glass_desc = "An earth favorite." allergen_type = ALLERGEN_FRUIT //Apples are fruit + cup_prefix = "apple" /datum/reagent/drink/juice/lime name = "Lime Juice" @@ -1104,6 +1137,7 @@ glass_name = "lime juice" glass_desc = "A glass of sweet-sour lime juice" allergen_type = ALLERGEN_FRUIT //Limes are fruit + cup_prefix = "lime" /datum/reagent/drink/juice/lime/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -1121,6 +1155,7 @@ glass_name = "orange juice" glass_desc = "Vitamins! Yay!" allergen_type = ALLERGEN_FRUIT //Oranges are fruit + cup_prefix = "orange" /datum/reagent/drink/juice/orange/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) ..() @@ -1138,6 +1173,7 @@ glass_name = "poison berry juice" glass_desc = "A glass of deadly juice." + cup_prefix = "poison" /datum/reagent/drink/juice/potato name = "Potato Juice" @@ -1151,6 +1187,7 @@ glass_name = "potato juice" glass_desc = "Juice from a potato. Bleh." allergen_type = ALLERGEN_VEGETABLE //Potatoes are vegetables + cup_prefix = "potato" /datum/reagent/drink/juice/turnip name = "Turnip Juice" @@ -1164,6 +1201,7 @@ glass_name = "turnip juice" glass_desc = "Juice of the turnip. A step below the potato." allergen_type = ALLERGEN_VEGETABLE //Turnips are vegetables + cup_prefix = "turnip" /datum/reagent/drink/juice/tomato name = "Tomato Juice" @@ -1172,6 +1210,7 @@ taste_description = "tomatoes" color = "#731008" sugary = FALSE + cup_prefix = "tomato" glass_name = "tomato juice" glass_desc = "Are you sure this is tomato juice?" @@ -1189,6 +1228,7 @@ description = "Delicious juice made from watermelon." taste_description = "sweet watermelon" color = "#B83333" + cup_prefix = "melon" glass_name = "watermelon juice" glass_desc = "Delicious juice made from watermelon." @@ -1207,7 +1247,7 @@ glass_desc = "White and nutritious goodness!" cup_icon_state = "cup_cream" - cup_name = "cup of milk" + cup_name = "milk" cup_desc = "White and nutritious goodness!" allergen_type = ALLERGEN_DAIRY //Milk is dairy @@ -1219,7 +1259,7 @@ color = "#74533b" cup_icon_state = "cup_brown" - cup_name = "cup of chocolate milk" + cup_name = "chocolate milk" cup_desc = "Deliciously fattening!" glass_name = "chocolate milk" @@ -1254,7 +1294,7 @@ glass_desc = "Ewwww..." cup_icon_state = "cup_cream" - cup_name = "cup of cream" + cup_name = "cream" cup_desc = "Ewwww..." allergen_type = ALLERGEN_DAIRY //Cream is dairy @@ -1269,10 +1309,26 @@ glass_desc = "White and nutritious soy goodness!" cup_icon_state = "cup_cream" - cup_name = "cup of milk" + cup_name = "milk" cup_desc = "White and nutritious goodness!" allergen_type = ALLERGEN_BEANS //Would be made from soy beans +/datum/reagent/drink/milk/foam + name = "Milk Foam" + id = "milk_foam" + description = "Light and airy foamed milk." + taste_description = "airy milk" + color = "#eeebdf" + + glass_name = "foam" + glass_desc = "Fluffy..." + + cup_icon_state = "cup_cream" + cup_name = "foam" + cup_desc = "Fluffy..." + allergen_type = ALLERGEN_DAIRY //Cream is dairy + + /datum/reagent/drink/tea name = "Tea" id = "tea" @@ -1288,7 +1344,7 @@ glass_desc = "Tasty black tea, it has antioxidants, it's good for you!" cup_icon_state = "cup_tea" - cup_name = "cup of tea" + cup_name = "tea" cup_desc = "Tasty black tea, it has antioxidants, it's good for you!" allergen_type = ALLERGEN_STIMULANT //Black tea strong enough to have significant caffeine content @@ -1310,7 +1366,7 @@ glass_name = "cup of decaf tea" glass_desc = "Tasty black tea, it has antioxidants, it's good for you, and won't keep you up at night!" - cup_name = "cup of decaf tea" + cup_name = "decaf tea" cup_desc = "Tasty black tea, it has antioxidants, it's good for you, and won't keep you up at night!" allergen_type = null //Certified cat-safe! @@ -1328,7 +1384,7 @@ glass_special = list(DRINK_ICE) cup_icon_state = "cup_tea" - cup_name = "cup of iced tea" + cup_name = "iced tea" cup_desc = "No relation to a certain rap artist/ actor." /datum/reagent/drink/tea/icetea/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) @@ -1352,7 +1408,7 @@ /datum/reagent/drink/tea/icetea/decaf name = "Decaf Iced Tea" glass_name = "decaf iced tea" - cup_name = "cup of decaf iced tea" + cup_name = "decaf iced tea" id = "iceteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1369,13 +1425,13 @@ glass_name = "mint tea" glass_desc = "A tasty mixture of mint and tea. It's apparently good for you!" - cup_name = "cup of mint tea" + cup_name = "mint tea" cup_desc = "A tasty mixture of mint and tea. It's apparently good for you!" /datum/reagent/drink/tea/minttea/decaf name = "Decaf Mint Tea" glass_name = "decaf mint tea" - cup_name = "cup of decaf mint tea" + cup_name = "decaf mint tea" id = "mintteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1392,14 +1448,14 @@ glass_name = "lemon tea" glass_desc = "A tasty mixture of lemon and tea. It's apparently good for you!" - cup_name = "cup of lemon tea" + cup_name = "lemon tea" cup_desc = "A tasty mixture of lemon and tea. It's apparently good for you!" allergen_type = ALLERGEN_FRUIT | ALLERGEN_STIMULANT //Made with lemon juice, still tea /datum/reagent/drink/tea/lemontea/decaf name = "Decaf Lemon Tea" glass_name = "decaf lemon tea" - cup_name = "cup of decaf lemon tea" + cup_name = "decaf lemon tea" id = "lemonteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1416,14 +1472,14 @@ glass_name = "lime tea" glass_desc = "A tasty mixture of lime and tea. It's apparently good for you!" - cup_name = "cup of lime tea" + cup_name = "lime tea" cup_desc = "A tasty mixture of lime and tea. It's apparently good for you!" allergen_type = ALLERGEN_FRUIT | ALLERGEN_STIMULANT //Made with lime juice, still tea /datum/reagent/drink/tea/limetea/decaf name = "Decaf Lime Tea" glass_name = "decaf lime tea" - cup_name = "cup of decaf lime tea" + cup_name = "decaf lime tea" id = "limeteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1440,14 +1496,14 @@ glass_name = "orange tea" glass_desc = "A tasty mixture of orange and tea. It's apparently good for you!" - cup_name = "cup of orange tea" + cup_name = "orange tea" cup_desc = "A tasty mixture of orange and tea. It's apparently good for you!" allergen_type = ALLERGEN_FRUIT | ALLERGEN_STIMULANT //Made with orange juice, still tea /datum/reagent/drink/tea/orangetea/decaf name = "Decaf orange Tea" glass_name = "decaf orange tea" - cup_name = "cup of decaf orange tea" + cup_name = "decaf orange tea" id = "orangeteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1464,14 +1520,14 @@ glass_name = "berry tea" glass_desc = "A tasty mixture of berries and tea. It's apparently good for you!" - cup_name = "cup of berry tea" + cup_name = "berry tea" cup_desc = "A tasty mixture of berries and tea. It's apparently good for you!" allergen_type = ALLERGEN_FRUIT | ALLERGEN_STIMULANT //Made with berry juice, still tea /datum/reagent/drink/tea/berrytea/decaf name = "Decaf Berry Tea" glass_name = "decaf berry tea" - cup_name = "cup of decaf berry tea" + cup_name = "decaf berry tea" id = "berryteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1488,7 +1544,7 @@ glass_name = "green tea" glass_desc = "A subtle blend of green tea. It's apparently good for you!" - cup_name = "cup of green tea" + cup_name = "green tea" cup_desc = "A subtle blend of green tea. It's apparently good for you!" /datum/reagent/drink/tea/chaitea @@ -1501,14 +1557,14 @@ glass_name = "chai tea" glass_desc = "A milky tea spiced with cinnamon and cloves." - cup_name = "cup of chai tea" + cup_name = "chai tea" cup_desc = "A milky tea spiced with cinnamon and cloves." allergen_type = ALLERGEN_STIMULANT|ALLERGEN_DAIRY //Made with milk and tea. /datum/reagent/drink/tea/chaitea/decaf name = "Decaf Chai Tea" glass_name = "decaf chai tea" - cup_name = "cup of decaf chai tea" + cup_name = "decaf chai tea" id = "chaiteadecaf" adj_dizzy = 0 adj_drowsy = 0 @@ -1529,7 +1585,7 @@ overdose = 45 cup_icon_state = "cup_coffee" - cup_name = "cup of coffee" + cup_name = "coffee" cup_desc = "Don't drop it, or you'll send scalding liquid and ceramic shards everywhere." glass_name = "coffee" @@ -1604,7 +1660,7 @@ glass_name = "soy latte" cup_icon_state = "cup_latte" - cup_name = "cup of soy latte" + cup_name = "soy latte" cup_desc = "A nice and refreshing beverage while you are reading." allergen_type = ALLERGEN_COFFEE|ALLERGEN_BEANS //Soy(beans) and coffee @@ -1624,7 +1680,7 @@ glass_desc = "A nice, strong and refreshing beverage while you are reading." cup_icon_state = "cup_latte" - cup_name = "cup of cafe latte" + cup_name = "cafe latte" cup_desc = "A nice and refreshing beverage while you are reading." allergen_type = ALLERGEN_COFFEE|ALLERGEN_DAIRY //Cream and coffee @@ -1642,7 +1698,7 @@ adj_temp = 25 cup_icon_state = "cup_coffee" - cup_name = "cup of decaf" + cup_name = "decaf" cup_desc = "Basically just brown, bitter water." glass_name = "decaf coffee" @@ -1663,10 +1719,186 @@ glass_desc = "Made with love! And cocoa beans." cup_icon_state = "cup_coco" - cup_name = "cup of hot chocolate" + cup_name = "hot chocolate" cup_desc = "Made with love! And cocoa beans." allergen_type = ALLERGEN_CHOCOLATE +/datum/reagent/drink/coffee/blackeye + name = "Black Eye Coffee" + id = "black_eye" + description = "Coffee but with more coffee for that extra coffee kick." + taste_description = "very concentrated coffee" + color = "#241001" + adj_temp = 5 + + glass_desc = "Coffee but with more coffee for that extra coffee kick." + glass_name = "black eye coffee" + + cup_icon_state = "cup_coffee" + cup_name = "black eye coffee" + cup_desc = "Coffee but with more coffee for that extra coffee kick." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/drip + name = "Drip Coffee" + id = "drip_coffee" + description = "Coffee made by soaking beans in hot water and allowing it seep through." + taste_description = "very concentrated coffee" + color = "#3d1a00" + adj_temp = 5 + + glass_desc = "Coffee made by soaking beans in hot water and allowing it seep through." + glass_name = "drip coffee" + + cup_icon_state = "cup_coffee" + cup_name = "drip brewed coffee" + cup_desc = "Coffee made by soaking beans in hot water and allowing it seep through." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/americano + name = "Americano" + id = "americano" + description = "A traditional coffee that is more dilute and perfect for a gentle start to the day." + taste_description = "pleasant coffee" + color = "#6d3205" + adj_temp = 5 + + glass_desc = "A traditional coffee that is more dilute and perfect for a gentle start to the day." + glass_name = "americano" + + cup_icon_state = "cup_coffee" + cup_name = "americano" + cup_desc = "A traditional coffee that is more dilute and perfect for a gentle start to the day." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/long_black + name = "Long Black Coffee" + id = "long_black" + description = "A traditional coffee with a little more kick." + taste_description = "modestly bitter coffee" + color = "#6d3205" + adj_temp = 5 + + glass_desc = "A traditional coffee with a little more kick." + glass_name = "long_black" + + cup_icon_state = "cup_coffee" + cup_name = "long black coffee" + cup_desc = "A traditional coffee with a little more kick." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/macchiato + name = "Macchiato" + id = "macchiato" + description = "A coffee mixed with steamed milk, it has swirling patterns on top." + taste_description = "milky coffee" + color = "#ad5817" + adj_temp = 5 + + glass_desc = "A coffee mixed with steamed milk, it has swirling patterns on top." + glass_name = "macchiato" + + cup_icon_state = "cup_latte" + cup_name = "macchiato" + cup_desc = "A coffee mixed with steamed milk, it has swirling patterns on top." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/cortado + name = "Cortado" + id = "cortado" + description = "Espresso mixed with equal parts milk and a layer of foam on top." + taste_description = "milky coffee" + color = "#ad5817" + adj_temp = 5 + + glass_desc = "Espresso mixed with equal parts milk and a layer of foam on top." + glass_name = "macchiato" + + cup_icon_state = "cup_latte" + cup_name = "cortado" + cup_desc = "Espresso mixed with equal parts milk and a layer of foam on top." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/breve + name = "Breve" + id = "breve" + description = "Espresso topped with half-and-half, with a layer of foam on top." + taste_description = "creamy coffee" + color = "#d1905e" + adj_temp = 5 + + glass_desc = "Espresso topped with half-and-half, with a layer of foam on top." + glass_name = "breve" + + cup_icon_state = "cup_cream" + cup_name = "breve" + cup_desc = "Espresso topped with half-and-half, with a layer of foam on top." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/cappuccino + name = "Cappuccino" + id = "cappuccino" + description = "Espresso with a large portion of milk and a hefty layer of foam." + taste_description = "classic coffee" + color = "#d1905e" + adj_temp = 5 + + glass_desc = "Espresso with a large portion of milk and a hefty layer of foam." + glass_name = "cappuccino" + + cup_icon_state = "cup_cream" + cup_name = "cappuccino" + cup_desc = "Espresso with a large portion of milk and a hefty layer of foam." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/flat_white + name = "Flat White Coffee" + id = "flat_white" + description = "A very milky coffee that is particularly light and airy." + taste_description = "very milky coffee" + color = "#ed9f64" + adj_temp = 5 + + glass_desc = "A very milky coffee that is particularly light and airy." + glass_name = "flat_white" + + cup_icon_state = "cup_latte" + cup_name = "flat white coffee" + cup_desc = "A very milky coffee that is particularly light and airy." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/mocha + name = "Mocha" + id = "mocha" + description = "A chocolate and coffee mix topped with a lot of milk and foam." + taste_description = "chocolatey coffee" + color = "#984201" + adj_temp = 5 + + glass_desc = "A chocolate and coffee mix topped with a lot of milk and foam." + glass_name = "mocha" + + cup_icon_state = "cup_cream" + cup_name = "mocha" + cup_desc = "A chocolate and coffee mix topped with a lot of milk and foam." + allergen_type = ALLERGEN_COFFEE + +/datum/reagent/drink/coffee/vienna + name = "Vienna" + id = "vienna" + description = "A very sweet espresso topped with a lot of whipped cream." + taste_description = "super sweet and creamy coffee" + color = "#8e7059" + adj_temp = 5 + + glass_desc = "A very sweet espresso topped with a lot of whipped cream." + glass_name = "vienna" + + cup_icon_state = "cup_cream" + cup_name = "vienna" + cup_desc = "A very sweet espresso topped with a lot of whipped cream." + allergen_type = ALLERGEN_COFFEE + /datum/reagent/drink/soda/sodawater name = "Soda Water" id = "sodawater" @@ -1676,6 +1908,7 @@ adj_dizzy = -5 adj_drowsy = -3 adj_temp = -5 + cup_prefix = "fizzy" glass_name = "soda water" glass_desc = "Soda water. Why not make a scotch and soda?" @@ -1688,6 +1921,7 @@ taste_description = "grape soda" color = "#421C52" adj_drowsy = -3 + cup_prefix = "grape soda" glass_name = "grape soda" glass_desc = "Looks like a delicious drink!" @@ -1700,6 +1934,7 @@ description = "It tastes strange but at least the quinine keeps the Space Malaria at bay." taste_description = "tart and fresh" color = "#619494" + cup_prefix = "tonic" adj_dizzy = -5 adj_drowsy = -3 @@ -1716,6 +1951,7 @@ taste_description = "lemonade" color = "#FFFF00" adj_temp = -5 + cup_prefix = "lemonade" glass_name = "lemonade" glass_desc = "Oh the nostalgia..." @@ -1729,6 +1965,7 @@ taste_description = "watermelon" color = "#FFB3BB" adj_temp = -5 + cup_prefix = "melonade" glass_name = "melonade" glass_desc = "Oh the.. nostalgia?" @@ -1742,6 +1979,7 @@ taste_description = "apples" color = "#FFD1B3" adj_temp = -5 + cup_prefix = "appleade" glass_name = "appleade" glass_desc = "Applejuice, improved." @@ -1755,6 +1993,7 @@ taste_description = "sweet`n`sour pineapples" color = "#FFFF00" adj_temp = -5 + cup_prefix = "pineappleade" glass_name = "pineappleade" glass_desc = "Pineapple, juiced up." @@ -2537,6 +2776,192 @@ glass_special = list(DRINK_FIZZ) allergen_type = ALLERGEN_FRUIT //made with several fruit juices +/datum/reagent/drink/syrup + name = "syrup" + id = "syrup" + description = "A generic, sugary syrup." + taste_description = "sweetness" + color = "#fffbe8" + cup_prefix = "extra sweet" + + glass_name = "syrup" + glass_desc = "That is just way too much syrup to drink on its own." + allergen_type = ALLERGEN_SUGARS + + overdose = 45 + +/datum/reagent/drink/syrup/overdose(var/mob/living/carbon/M, var/alien) + if(alien == IS_DIONA) + return + M.make_dizzy(1) + +/datum/reagent/drink/syrup/pumpkin + name = "pumpkin spice syrup" + id = "syrup_pumpkin" + description = "A sugary syrup that tastes of pumpkin spice." + taste_description = "pumpkin spice" + color = "#e0b439" + cup_prefix = "pumpkin spice" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_FRUIT + +/datum/reagent/drink/syrup/caramel + name = "caramel syrup" + id = "syrup_caramel" + description = "A sugary syrup that tastes of caramel." + taste_description = "caramel" + color = "#b47921" + cup_prefix = "caramel" + +/datum/reagent/drink/syrup/scaramel + name = "salted caramel syrup" + id = "syrup_salted_caramel" + description = "A sugary syrup that tastes of salted caramel." + taste_description = "salty caramel" + color = "#9f6714" + cup_prefix = "salted caramel" + +/datum/reagent/drink/syrup/irish + name = "irish cream syrup" + id = "syrup_irish" + description = "A sugary syrup that tastes of a light, sweet cream." + taste_description = "creaminess" + color = "#ead3b0" + cup_prefix = "irish" + +/datum/reagent/drink/syrup/almond + name = "almond syrup" + id = "syrup_almond" + description = "A sugary syrup that tastes of almonds." + taste_description = "almonds" + color = "#ffb64a" + cup_prefix = "almond" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_SEEDS + +/datum/reagent/drink/syrup/cinnamon + name = "cinnamon syrup" + id = "syrup_cinnamon" + description = "A sugary syrup that tastes of cinnamon." + taste_description = "cinnamon" + color = "#ec612a" + cup_prefix = "cinnamon" + +/datum/reagent/drink/syrup/pistachio + name = "pistachio syrup" + id = "syrup_pistachio" + description = "A sugary syrup that tastes of pistachio." + taste_description = "pistachio" + color = "#c9eb59" + cup_prefix = "pistachio" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_SEEDS + +/datum/reagent/drink/syrup/vanilla + name = "vanilla syrup" + id = "syrup_vanilla" + description = "A sugary syrup that tastes of vanilla." + taste_description = "vanilla" + color = "#eaebd1" + cup_prefix = "vanilla" + +/datum/reagent/drink/syrup/toffee + name = "toffee syrup" + id = "syrup_toffee" + description = "A sugary syrup that tastes of toffee." + taste_description = "toffee" + color = "#aa7143" + cup_prefix = "toffee" + +/datum/reagent/drink/syrup/cherry + name = "cherry syrup" + id = "syrup_cherry" + description = "A sugary syrup that tastes of cherries." + taste_description = "cherries" + color = "#ff0000" + cup_prefix = "cherry" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_FRUIT + +/datum/reagent/drink/syrup/butterscotch + name = "butterscotch syrup" + id = "syrup_butterscotch" + description = "A sugary syrup that tastes of butterscotch." + taste_description = "butterscotch" + color = "#e6924e" + cup_prefix = "butterscotch" + +/datum/reagent/drink/syrup/chocolate + name = "chocolate syrup" + id = "syrup_chocolate" + description = "A sugary syrup that tastes of chocolate." + taste_description = "chocolate" + color = "#873600" + cup_prefix = "chocolate" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_CHOCOLATE + +/datum/reagent/drink/syrup/wchocolate + name = "white chocolate syrup" + id = "syrup_white_chocolate" + description = "A sugary syrup that tastes of white chocolate." + taste_description = "white chocolate" + color = "#c4c6a5" + cup_prefix = "white chocolate" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_CHOCOLATE + +/datum/reagent/drink/syrup/strawberry + name = "strawberry syrup" + id = "syrup_strawberry" + description = "A sugary syrup that tastes of strawberries." + taste_description = "strawberries" + color = "#ff2244" + cup_prefix = "strawberry" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_FRUIT + +/datum/reagent/drink/syrup/coconut + name = "coconut syrup" + id = "syrup_coconut" + description = "A sugary syrup that tastes of coconut." + taste_description = "coconut" + color = "#ffffff" + cup_prefix = "coconut" + + allergen_type = ALLERGEN_SUGARS|ALLERGEN_FRUIT + +/datum/reagent/drink/syrup/ginger + name = "ginger syrup" + id = "syrup_ginger" + description = "A sugary syrup that tastes of ginger." + taste_description = "ginger" + color = "#d09740" + cup_prefix = "ginger" + +/datum/reagent/drink/syrup/gingerbread + name = "gingerbread syrup" + id = "syrup_gingerbread" + description = "A sugary syrup that tastes of gingerbread." + taste_description = "gingerbread" + color = "#b6790f" + cup_prefix = "gingerbread" + +/datum/reagent/drink/syrup/peppermint + name = "peppermint syrup" + id = "syrup_peppermint" + description = "A sugary syrup that tastes of peppermint." + taste_description = "peppermint" + color = "#9ce06e" + cup_prefix = "peppermint" + +/datum/reagent/drink/syrup/birthday_cake + name = "birthday cake syrup" + id = "syrup_birthday" + description = "A sugary syrup that tastes of an overload of sweetness." + taste_description = "far too much sugar" + color = "#ff00e6" + cup_prefix = "birthday cake" /* Alcohol */ diff --git a/code/modules/reagents/reagents/virology.dm b/code/modules/reagents/reagents/virology.dm index de6e77e3049..95badd98511 100644 --- a/code/modules/reagents/reagents/virology.dm +++ b/code/modules/reagents/reagents/virology.dm @@ -46,3 +46,9 @@ id = "weakphoronvirusfood" description = "Mutates viruses when mixed in blood. This one seems to have been weakened, but still strong." color = "#CEC3C6" + +/datum/reagent/toxin/phoron/phoronvirusfood/sizevirusfood + name = "Sizeoxadone virus food" + id = "sizevirusfood" + description = "Mutates virus when mixed in blood. This is a strange size mix..." + color = "#88AFDD" diff --git a/code/modules/research/designs/circuits/circuits.dm b/code/modules/research/designs/circuits/circuits.dm index 972ee1b1598..bb9d2a5342a 100644 --- a/code/modules/research/designs/circuits/circuits.dm +++ b/code/modules/research/designs/circuits/circuits.dm @@ -123,6 +123,13 @@ CIRCUITS BELOW build_path = /obj/item/circuitboard/crew sort_string = "FAGAI" +/datum/design/circuit/pandemic + name = "PanD.E.M.I.C 2200" + id = "pandemic" + req_tech = list(TECH_DATA = 2, TECH_BIO = 2) + build_path = /obj/item/circuitboard/pandemic + sort_string = "FAGAJ" + /datum/design/circuit/teleconsole name = "teleporter control console" id = "teleconsole" diff --git a/code/modules/research/prosfab_designs.dm b/code/modules/research/prosfab_designs.dm index 8bf0beb7429..0c88d946888 100644 --- a/code/modules/research/prosfab_designs.dm +++ b/code/modules/research/prosfab_designs.dm @@ -83,6 +83,7 @@ H.b_skin = 0 H.dna.ResetUIFrom(H) + H.allow_spontaneous_tf = TRUE // Allows vore customization of synthmorphs H.real_name = "Synthmorph #[rand(100,999)]" H.name = H.real_name H.dir = 2 diff --git a/code/modules/tgui/modules/appearance_changer.dm b/code/modules/tgui/modules/appearance_changer.dm index 3a07f583e4f..5e410b9e6c6 100644 --- a/code/modules/tgui/modules/appearance_changer.dm +++ b/code/modules/tgui/modules/appearance_changer.dm @@ -28,7 +28,7 @@ var/list/markings = null /datum/tgui_module/appearance_changer/New( - var/host, + host, mob/living/carbon/human/H, check_species_whitelist = 1, list/species_whitelist = list(), @@ -57,309 +57,323 @@ local_skybox.screen_loc = "[map_name]:CENTER,CENTER" cam_plane_masters += local_skybox + owner = H cam_background = new cam_background.assigned_map = map_name cam_background.del_on_map_removal = FALSE - update_active_camera_screen() - - if(customize_usr) - if(ishuman(usr)) - H = usr - owner = H - if(owner) - owner.AddComponent(/datum/component/recursive_move) - RegisterSignal(owner, COMSIG_OBSERVER_MOVED, PROC_REF(update_active_camera_screen)) check_whitelist = check_species_whitelist whitelist = species_whitelist blacklist = species_blacklist +/datum/tgui_module/appearance_changer/tgui_close(mob/user) + . = ..() + if(owner == user || !customize_usr) + close_ui() + UnregisterSignal(owner, COMSIG_OBSERVER_MOVED) + owner = null + last_camera_turf = null + cut_data() + /datum/tgui_module/appearance_changer/Destroy() - UnregisterSignal(owner, COMSIG_OBSERVER_MOVED) - last_camera_turf = null qdel(cam_screen) QDEL_LIST(cam_plane_masters) qdel(cam_background) - cut_data() return ..() /datum/tgui_module/appearance_changer/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state) if(..()) return TRUE - var/mob/living/carbon/human/target = owner - if(customize_usr) - if(!ishuman(ui.user)) - return TRUE - target = ui.user - switch(action) if("race") - if(can_change(target, APPEARANCE_RACE) && (params["race"] in valid_species)) - if(target.change_species(params["race"])) + if(can_change(owner, APPEARANCE_RACE) && (params["race"] in valid_species)) + if(owner.change_species(params["race"])) if(params["race"] == "Custom Species") - target.custom_species = sanitize(tgui_input_text(target, "Input custom species name:", + owner.custom_species = sanitize(tgui_input_text(ui.user, "Input custom species name:", "Custom Species Name", null, MAX_NAME_LEN), MAX_NAME_LEN) cut_data() - generate_data(target) + generate_data(ui.user, owner) changed_hook(APPEARANCECHANGER_CHANGED_RACE) return 1 if("gender") - if(can_change(target, APPEARANCE_GENDER) && (params["gender"] in get_genders(target))) - if(target.change_gender(params["gender"])) + if(can_change(owner, APPEARANCE_GENDER) && (params["gender"] in get_genders(owner))) + if(owner.change_gender(params["gender"])) cut_data() - generate_data(target) + generate_data(ui.user, owner) changed_hook(APPEARANCECHANGER_CHANGED_GENDER) return 1 if("gender_id") - if(can_change(target, APPEARANCE_GENDER) && (params["gender_id"] in all_genders_define_list)) - target.identifying_gender = params["gender_id"] + if(can_change(owner, APPEARANCE_GENDER) && (params["gender_id"] in all_genders_define_list)) + owner.identifying_gender = params["gender_id"] changed_hook(APPEARANCECHANGER_CHANGED_GENDER_ID) return 1 if("skin_tone") - if(can_change_skin_tone(target)) - var/new_s_tone = tgui_input_number(target, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -target.s_tone + 35, 220, 1) - if(isnum(new_s_tone) && can_still_topic(target, state)) + if(can_change_skin_tone(owner)) + var/new_s_tone = tgui_input_number(ui.user, "Choose your character's skin-tone:\n(Light 1 - 220 Dark)", "Skin Tone", -owner.s_tone + 35, 220, 1) + if(isnum(new_s_tone) && can_still_topic(owner, state)) new_s_tone = 35 - max(min( round(new_s_tone), 220),1) changed_hook(APPEARANCECHANGER_CHANGED_SKINTONE) - return target.change_skin_tone(new_s_tone) + return owner.change_skin_tone(new_s_tone) if("skin_color") - if(can_change_skin_color(target)) - var/new_skin = input(target, "Choose your character's skin colour: ", "Skin Color", rgb(target.r_skin, target.g_skin, target.b_skin)) as color|null - if(new_skin && can_still_topic(target, state)) + if(can_change_skin_color(owner)) + var/new_skin = input(ui.user, "Choose your character's skin colour: ", "Skin Color", rgb(owner.r_skin, owner.g_skin, owner.b_skin)) as color|null + if(new_skin && can_still_topic(owner, state)) var/r_skin = hex2num(copytext(new_skin, 2, 4)) var/g_skin = hex2num(copytext(new_skin, 4, 6)) var/b_skin = hex2num(copytext(new_skin, 6, 8)) - if(target.change_skin_color(r_skin, g_skin, b_skin)) - update_dna(target) + if(owner.change_skin_color(r_skin, g_skin, b_skin)) + update_dna(ui.user, owner) changed_hook(APPEARANCECHANGER_CHANGED_SKINCOLOR) return 1 if("hair") - if(can_change(target, APPEARANCE_HAIR) && (params["hair"] in valid_hairstyles)) - if(target.change_hair(params["hair"])) - update_dna(target) + if(can_change(owner, APPEARANCE_HAIR) && (params["hair"] in valid_hairstyles)) + if(owner.change_hair(params["hair"])) + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return 1 if("hair_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select hair color.", "Hair Color", rgb(target.r_hair, target.g_hair, target.b_hair)) as color|null - if(new_hair && can_still_topic(target, state)) + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select hair color.", "Hair Color", rgb(owner.r_hair, owner.g_hair, owner.b_hair)) as color|null + if(new_hair && can_still_topic(owner, state)) var/r_hair = hex2num(copytext(new_hair, 2, 4)) var/g_hair = hex2num(copytext(new_hair, 4, 6)) var/b_hair = hex2num(copytext(new_hair, 6, 8)) - if(target.change_hair_color(r_hair, g_hair, b_hair)) - update_dna(target) + if(owner.change_hair_color(r_hair, g_hair, b_hair)) + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("facial_hair") - if(can_change(target, APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles)) - if(target.change_facial_hair(params["facial_hair"])) - update_dna(target) + if(can_change(owner, APPEARANCE_FACIAL_HAIR) && (params["facial_hair"] in valid_facial_hairstyles)) + if(owner.change_facial_hair(params["facial_hair"])) + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_F_HAIRSTYLE) return 1 if("facial_hair_color") - if(can_change(target, APPEARANCE_FACIAL_HAIR_COLOR)) - var/new_facial = input(target, "Please select facial hair color.", "Facial Hair Color", rgb(target.r_facial, target.g_facial, target.b_facial)) as color|null - if(new_facial && can_still_topic(target, state)) + if(can_change(owner, APPEARANCE_FACIAL_HAIR_COLOR)) + var/new_facial = input(ui.user, "Please select facial hair color.", "Facial Hair Color", rgb(owner.r_facial, owner.g_facial, owner.b_facial)) as color|null + if(new_facial && can_still_topic(owner, state)) var/r_facial = hex2num(copytext(new_facial, 2, 4)) var/g_facial = hex2num(copytext(new_facial, 4, 6)) var/b_facial = hex2num(copytext(new_facial, 6, 8)) - if(target.change_facial_hair_color(r_facial, g_facial, b_facial)) - update_dna(target) + if(owner.change_facial_hair_color(r_facial, g_facial, b_facial)) + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_F_HAIRCOLOR) return 1 if("eye_color") - if(can_change(target, APPEARANCE_EYE_COLOR)) - var/new_eyes = input(target, "Please select eye color.", "Eye Color", rgb(target.r_eyes, target.g_eyes, target.b_eyes)) as color|null - if(new_eyes && can_still_topic(target, state)) + if(can_change(owner, APPEARANCE_EYE_COLOR)) + var/new_eyes = input(ui.user, "Please select eye color.", "Eye Color", rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes)) as color|null + if(new_eyes && can_still_topic(owner, state)) var/r_eyes = hex2num(copytext(new_eyes, 2, 4)) var/g_eyes = hex2num(copytext(new_eyes, 4, 6)) var/b_eyes = hex2num(copytext(new_eyes, 6, 8)) - if(target.change_eye_color(r_eyes, g_eyes, b_eyes)) - update_dna(target) + if(owner.change_eye_color(r_eyes, g_eyes, b_eyes)) + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_EYES) return 1 // VOREStation Add - Ears/Tails/Wings/Markings if("ear") - if(can_change(target, APPEARANCE_ALL_HAIR)) + if(can_change(owner, APPEARANCE_ALL_HAIR)) var/datum/sprite_accessory/ears/instance = locate(params["ref"]) if(params["clear"]) instance = null if(!istype(instance) && !params["clear"]) return FALSE - target.ear_style = instance - target.update_hair() - update_dna(target) + owner.ear_style = instance + owner.update_hair() + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if("ear_secondary") - if(can_change(target, APPEARANCE_ALL_HAIR)) + if(can_change(owner, APPEARANCE_ALL_HAIR)) var/datum/sprite_accessory/ears/instance = locate(params["ref"]) if(params["clear"]) instance = null if(!istype(instance) && !params["clear"]) return FALSE - target.ear_secondary_style = instance - if(!islist(target.ear_secondary_colors)) - target.ear_secondary_colors = list() - if(length(target.ear_secondary_colors) < instance.get_color_channel_count()) - target.ear_secondary_colors.len = instance.get_color_channel_count() - target.update_hair() - update_dna(target) + owner.ear_secondary_style = instance + if(!islist(owner.ear_secondary_colors)) + owner.ear_secondary_colors = list() + if(instance && length(owner.ear_secondary_colors) < instance.get_color_channel_count()) + owner.ear_secondary_colors.len = instance.get_color_channel_count() + owner.update_hair() + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if("ears_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select ear color.", "Ear Color", rgb(target.r_ears, target.g_ears, target.b_ears)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_ears = hex2num(copytext(new_hair, 2, 4)) - target.g_ears = hex2num(copytext(new_hair, 4, 6)) - target.b_ears = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_hair() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select ear color.", "Ear Color", rgb(owner.r_ears, owner.g_ears, owner.b_ears)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_ears = hex2num(copytext(new_hair, 2, 4)) + owner.g_ears = hex2num(copytext(new_hair, 4, 6)) + owner.b_ears = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_hair() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("ears2_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select secondary ear color.", "2nd Ear Color", rgb(target.r_ears2, target.g_ears2, target.b_ears2)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_ears2 = hex2num(copytext(new_hair, 2, 4)) - target.g_ears2 = hex2num(copytext(new_hair, 4, 6)) - target.b_ears2 = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_hair() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select secondary ear color.", "2nd Ear Color", rgb(owner.r_ears2, owner.g_ears2, owner.b_ears2)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_ears2 = hex2num(copytext(new_hair, 2, 4)) + owner.g_ears2 = hex2num(copytext(new_hair, 4, 6)) + owner.b_ears2 = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_hair() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("ears_secondary_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) + if(can_change(owner, APPEARANCE_HAIR_COLOR)) var/channel = params["channel"] - if(channel > length(target.ear_secondary_colors)) + if(channel > length(owner.ear_secondary_colors)) return TRUE - var/existing = LAZYACCESS(target.ear_secondary_colors, channel) || "#ffffff" - var/new_color = input(target, "Please select ear color.", "2nd Ear Color", existing) as color|null - if(new_color && can_still_topic(target, state)) - target.ear_secondary_colors[channel] = new_color - update_dna(target) - target.update_hair() + var/existing = LAZYACCESS(owner.ear_secondary_colors, channel) || "#ffffff" + var/new_color = input(ui.user, "Please select ear color.", "2nd Ear Color", existing) as color|null + if(new_color && can_still_topic(owner, state)) + owner.ear_secondary_colors[channel] = new_color + update_dna(owner) + owner.update_hair() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return TRUE if("tail") - if(can_change(target, APPEARANCE_ALL_HAIR)) + if(can_change(owner, APPEARANCE_ALL_HAIR)) var/datum/sprite_accessory/tail/instance = locate(params["ref"]) if(params["clear"]) instance = null if(!istype(instance) && !params["clear"]) return FALSE - target.tail_style = instance - target.update_tail_showing() - update_dna(target) + owner.tail_style = instance + owner.update_tail_showing() + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if("tail_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select tail color.", "Tail Color", rgb(target.r_tail, target.g_tail, target.b_tail)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_tail = hex2num(copytext(new_hair, 2, 4)) - target.g_tail = hex2num(copytext(new_hair, 4, 6)) - target.b_tail = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_tail_showing() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select tail color.", "Tail Color", rgb(owner.r_tail, owner.g_tail, owner.b_tail)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_tail = hex2num(copytext(new_hair, 2, 4)) + owner.g_tail = hex2num(copytext(new_hair, 4, 6)) + owner.b_tail = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_tail_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("tail2_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select secondary tail color.", "2nd Tail Color", rgb(target.r_tail2, target.g_tail2, target.b_tail2)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_tail2 = hex2num(copytext(new_hair, 2, 4)) - target.g_tail2 = hex2num(copytext(new_hair, 4, 6)) - target.b_tail2 = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_tail_showing() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select secondary tail color.", "2nd Tail Color", rgb(owner.r_tail2, owner.g_tail2, owner.b_tail2)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_tail2 = hex2num(copytext(new_hair, 2, 4)) + owner.g_tail2 = hex2num(copytext(new_hair, 4, 6)) + owner.b_tail2 = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_tail_showing() + changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) + return 1 + if("tail3_color") + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select secondary tail color.", "3rd Tail Color", rgb(owner.r_tail3, owner.g_tail3, owner.b_tail3)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_tail3 = hex2num(copytext(new_hair, 2, 4)) + owner.g_tail3 = hex2num(copytext(new_hair, 4, 6)) + owner.b_tail3 = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_tail_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("wing") - if(can_change(target, APPEARANCE_ALL_HAIR)) + if(can_change(owner, APPEARANCE_ALL_HAIR)) var/datum/sprite_accessory/wing/instance = locate(params["ref"]) if(params["clear"]) instance = null if(!istype(instance) && !params["clear"]) return FALSE - target.wing_style = instance - target.update_wing_showing() - update_dna(target) + owner.wing_style = instance + owner.update_wing_showing() + update_dna(owner) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if("wing_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select wing color.", "Wing Color", rgb(target.r_wing, target.g_wing, target.b_wing)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_wing = hex2num(copytext(new_hair, 2, 4)) - target.g_wing = hex2num(copytext(new_hair, 4, 6)) - target.b_wing = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_wing_showing() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select wing color.", "Wing Color", rgb(owner.r_wing, owner.g_wing, owner.b_wing)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_wing = hex2num(copytext(new_hair, 2, 4)) + owner.g_wing = hex2num(copytext(new_hair, 4, 6)) + owner.b_wing = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_wing_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("wing2_color") - if(can_change(target, APPEARANCE_HAIR_COLOR)) - var/new_hair = input(target, "Please select secondary wing color.", "2nd Wing Color", rgb(target.r_wing2, target.g_wing2, target.b_wing2)) as color|null - if(new_hair && can_still_topic(target, state)) - target.r_wing2 = hex2num(copytext(new_hair, 2, 4)) - target.g_wing2 = hex2num(copytext(new_hair, 4, 6)) - target.b_wing2 = hex2num(copytext(new_hair, 6, 8)) - update_dna(target) - target.update_wing_showing() + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select secondary wing color.", "2nd Wing Color", rgb(owner.r_wing2, owner.g_wing2, owner.b_wing2)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_wing2 = hex2num(copytext(new_hair, 2, 4)) + owner.g_wing2 = hex2num(copytext(new_hair, 4, 6)) + owner.b_wing2 = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_wing_showing() + changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) + return 1 + if("wing3_color") + if(can_change(owner, APPEARANCE_HAIR_COLOR)) + var/new_hair = input(ui.user, "Please select secondary wing color.", "3rd Wing Color", rgb(owner.r_wing3, owner.g_wing3, owner.b_wing3)) as color|null + if(new_hair && can_still_topic(owner, state)) + owner.r_wing3 = hex2num(copytext(new_hair, 2, 4)) + owner.g_wing3 = hex2num(copytext(new_hair, 4, 6)) + owner.b_wing3 = hex2num(copytext(new_hair, 6, 8)) + update_dna(owner) + owner.update_wing_showing() changed_hook(APPEARANCECHANGER_CHANGED_HAIRCOLOR) return 1 if("marking") - if(can_change(target, APPEARANCE_ALL_HAIR)) + if(can_change(owner, APPEARANCE_ALL_HAIR)) var/todo = params["todo"] var/name_marking = params["name"] switch (todo) if (0) //delete if (name_marking) var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking] - if (target.remove_marking(mark_datum)) + if (owner.remove_marking(mark_datum)) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if (1) //add var/list/usable_markings = markings.Copy() ^ body_marking_styles_list.Copy() - var/new_marking = tgui_input_list(target, "Choose a body marking:", "New Body Marking", usable_markings) - if(new_marking && can_still_topic(target, state)) + var/new_marking = tgui_input_list(ui.user, "Choose a body marking:", "New Body Marking", usable_markings) + if(new_marking && can_still_topic(owner, state)) var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[new_marking] - if (target.add_marking(mark_datum)) + if (owner.add_marking(mark_datum)) changed_hook(APPEARANCECHANGER_CHANGED_HAIRSTYLE) return TRUE if (2) //move up var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking] - if (target.change_priority_of_marking(mark_datum, FALSE)) + if (owner.change_priority_of_marking(mark_datum, FALSE)) return TRUE if (3) //move down var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking] - if (target.change_priority_of_marking(mark_datum, TRUE)) + if (owner.change_priority_of_marking(mark_datum, TRUE)) return TRUE if (4) //color var/current = markings[name_marking] ? markings[name_marking] : "#000000" - var/marking_color = input(target, "Please select marking color", "Marking color", current) as color|null - if(marking_color && can_still_topic(target, state)) + var/marking_color = input(ui.user, "Please select marking color", "Marking color", current) as color|null + if(marking_color && can_still_topic(owner, state)) var/datum/sprite_accessory/marking/mark_datum = body_marking_styles_list[name_marking] - if (target.change_marking_color(mark_datum, marking_color)) + if (owner.change_marking_color(mark_datum, marking_color)) return TRUE // VOREStation Add End return FALSE /datum/tgui_module/appearance_changer/tgui_interact(mob/user, datum/tgui/ui = null, datum/tgui/parent_ui = null, datum/tgui_state/custom_state) - var/mob/living/carbon/human/target = owner - if(customize_usr) + if(customize_usr && !owner) if(!ishuman(user)) return TRUE - target = user + owner = user - if(!target || !target.species) + if(!owner || !owner.species) return ui = SStgui.try_update_ui(user, src, ui) - update_active_camera_screen() if(!ui) + owner.AddComponent(/datum/component/recursive_move) + RegisterSignal(owner, COMSIG_OBSERVER_MOVED, PROC_REF(update_active_camera_screen)) // Register map objects user.client.register_map_obj(cam_screen) for(var/plane in cam_plane_masters) @@ -370,19 +384,20 @@ ui.open() if(custom_state) ui.set_state(custom_state) + update_active_camera_screen() /datum/tgui_module/appearance_changer/tgui_static_data(mob/user) var/list/data = ..() - generate_data(user) + generate_data(user, owner) - if(can_change(user, APPEARANCE_RACE)) + if(can_change(owner, APPEARANCE_RACE)) var/species[0] for(var/specimen in valid_species) species[++species.len] = list("specimen" = specimen) data["species"] = species - if(can_change(user, APPEARANCE_HAIR)) + if(can_change(owner, APPEARANCE_HAIR)) var/hair_styles[0] for(var/hair_style in valid_hairstyles) hair_styles[++hair_styles.len] = list("hairstyle" = hair_style) @@ -393,7 +408,7 @@ data["wing_styles"] = valid_wingstyles // VOREStation Add End - if(can_change(user, APPEARANCE_FACIAL_HAIR)) + if(can_change(owner, APPEARANCE_FACIAL_HAIR)) var/facial_hair_styles[0] for(var/facial_hair_style in valid_facial_hairstyles) facial_hair_styles[++facial_hair_styles.len] = list("facialhairstyle" = facial_hair_style) @@ -404,24 +419,18 @@ /datum/tgui_module/appearance_changer/tgui_data(mob/user, datum/tgui/ui, datum/tgui_state/state) var/list/data = ..() - generate_data(user) + generate_data(user, owner) - var/mob/living/carbon/human/target = owner - if(customize_usr) - if(!ishuman(ui.user)) - return TRUE - target = ui.user + data["name"] = owner.name + data["specimen"] = owner.species.name + data["gender"] = owner.gender + data["gender_id"] = owner.identifying_gender + data["change_race"] = can_change(owner, APPEARANCE_RACE) - data["name"] = target.name - data["specimen"] = target.species.name - data["gender"] = target.gender - data["gender_id"] = target.identifying_gender - data["change_race"] = can_change(target, APPEARANCE_RACE) - - data["change_gender"] = can_change(target, APPEARANCE_GENDER) + data["change_gender"] = can_change(owner, APPEARANCE_GENDER) if(data["change_gender"]) var/genders[0] - for(var/gender in get_genders(target)) + for(var/gender in get_genders(owner)) genders[++genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender) data["genders"] = genders var/id_genders[0] @@ -429,56 +438,58 @@ id_genders[++id_genders.len] = list("gender_name" = gender2text(gender), "gender_key" = gender) data["id_genders"] = id_genders - data["change_hair"] = can_change(target, APPEARANCE_HAIR) + data["change_hair"] = can_change(owner, APPEARANCE_HAIR) if(data["change_hair"]) - data["hair_style"] = target.h_style + data["hair_style"] = owner.h_style // VOREStation Add - Ears/Tails/Wings - data["ear_style"] = target.ear_style - data["ear_secondary_style"] = target.ear_secondary_style?.name - data["tail_style"] = target.tail_style - data["wing_style"] = target.wing_style + data["ear_style"] = owner.ear_style + data["ear_secondary_style"] = owner.ear_secondary_style?.name + data["tail_style"] = owner.tail_style + data["wing_style"] = owner.wing_style var/list/markings_data[0] - markings = target.get_prioritised_markings() + markings = owner.get_prioritised_markings() for (var/marking in markings) markings_data[++markings_data.len] = list("marking_name" = marking, "marking_color" = markings[marking]["color"] ? markings[marking]["color"] : "#000000") //too tired to add in another submenu for bodyparts here data["markings"] = markings_data // VOREStation Add End - data["change_facial_hair"] = can_change(target, APPEARANCE_FACIAL_HAIR) + data["change_facial_hair"] = can_change(owner, APPEARANCE_FACIAL_HAIR) if(data["change_facial_hair"]) - data["facial_hair_style"] = target.f_style + data["facial_hair_style"] = owner.f_style - data["change_skin_tone"] = can_change_skin_tone(target) - data["change_skin_color"] = can_change_skin_color(target) + data["change_skin_tone"] = can_change_skin_tone(owner) + data["change_skin_color"] = can_change_skin_color(owner) if(data["change_skin_color"]) - data["skin_color"] = rgb(target.r_skin, target.g_skin, target.b_skin) + data["skin_color"] = rgb(owner.r_skin, owner.g_skin, owner.b_skin) - data["change_eye_color"] = can_change(target, APPEARANCE_EYE_COLOR) + data["change_eye_color"] = can_change(owner, APPEARANCE_EYE_COLOR) if(data["change_eye_color"]) - data["eye_color"] = rgb(target.r_eyes, target.g_eyes, target.b_eyes) + data["eye_color"] = rgb(owner.r_eyes, owner.g_eyes, owner.b_eyes) - data["change_hair_color"] = can_change(target, APPEARANCE_HAIR_COLOR) + data["change_hair_color"] = can_change(owner, APPEARANCE_HAIR_COLOR) if(data["change_hair_color"]) - data["hair_color"] = rgb(target.r_hair, target.g_hair, target.b_hair) + data["hair_color"] = rgb(owner.r_hair, owner.g_hair, owner.b_hair) // VOREStation Add - Ears/Tails/Wings - data["ears_color"] = rgb(target.r_ears, target.g_ears, target.b_ears) - data["ears2_color"] = rgb(target.r_ears2, target.g_ears2, target.b_ears2) + data["ears_color"] = rgb(owner.r_ears, owner.g_ears, owner.b_ears) + data["ears2_color"] = rgb(owner.r_ears2, owner.g_ears2, owner.b_ears2) // secondary ear colors - var/list/ear_secondary_color_channels = target.ear_secondary_colors || list() - ear_secondary_color_channels.len = target.ear_secondary_style?.get_color_channel_count() || 0 + var/list/ear_secondary_color_channels = owner.ear_secondary_colors || list() + ear_secondary_color_channels.len = owner.ear_secondary_style?.get_color_channel_count() || 0 data["ear_secondary_colors"] = ear_secondary_color_channels - data["tail_color"] = rgb(target.r_tail, target.g_tail, target.b_tail) - data["tail2_color"] = rgb(target.r_tail2, target.g_tail2, target.b_tail2) - data["wing_color"] = rgb(target.r_wing, target.g_wing, target.b_wing) - data["wing2_color"] = rgb(target.r_wing2, target.g_wing2, target.b_wing2) + data["tail_color"] = rgb(owner.r_tail, owner.g_tail, owner.b_tail) + data["tail2_color"] = rgb(owner.r_tail2, owner.g_tail2, owner.b_tail2) + data["tail3_color"] = rgb(owner.r_tail3, owner.g_tail3, owner.b_tail3) + data["wing_color"] = rgb(owner.r_wing, owner.g_wing, owner.b_wing) + data["wing2_color"] = rgb(owner.r_wing2, owner.g_wing2, owner.b_wing2) + data["wing3_color"] = rgb(owner.r_wing3, owner.g_wing3, owner.b_wing3) // VOREStation Add End - data["change_facial_hair_color"] = can_change(target, APPEARANCE_FACIAL_HAIR_COLOR) + data["change_facial_hair_color"] = can_change(owner, APPEARANCE_FACIAL_HAIR_COLOR) if(data["change_facial_hair_color"]) - data["facial_hair_color"] = rgb(target.r_facial, target.g_facial, target.b_facial) + data["facial_hair_color"] = rgb(owner.r_facial, owner.g_facial, owner.b_facial) return data /datum/tgui_module/appearance_changer/tgui_static_data(mob/user) @@ -512,30 +523,18 @@ local_skybox.set_position("CENTER", "CENTER", (world.maxx>>1) - newturf.x, (world.maxy>>1) - newturf.y) */ -/datum/tgui_module/appearance_changer/proc/update_dna(mob/target) - if(customize_usr && !ishuman(target)) - return TRUE - var/mob/living/carbon/human/H = target - if(H && (flags & APPEARANCE_UPDATE_DNA)) - H.update_dna() +/datum/tgui_module/appearance_changer/proc/update_dna(mob/living/carbon/human/target) + if(target && (flags & APPEARANCE_UPDATE_DNA)) + target.update_dna() -/datum/tgui_module/appearance_changer/proc/can_change(mob/target, var/flag) - if(customize_usr && !ishuman(target)) - return TRUE - var/mob/living/carbon/human/H = target - return H && (flags & flag) +/datum/tgui_module/appearance_changer/proc/can_change(mob/living/carbon/human/target, var/flag) + return target && (flags & flag) -/datum/tgui_module/appearance_changer/proc/can_change_skin_tone(mob/target) - if(customize_usr && !ishuman(target)) - return TRUE - var/mob/living/carbon/human/H = target - return H && (flags & APPEARANCE_SKIN) && H.species.appearance_flags & HAS_SKIN_TONE +/datum/tgui_module/appearance_changer/proc/can_change_skin_tone(mob/living/carbon/human/target) + return target && (flags & APPEARANCE_SKIN) &&target.species.appearance_flags & HAS_SKIN_TONE -/datum/tgui_module/appearance_changer/proc/can_change_skin_color(mob/target) - if(customize_usr && !ishuman(target)) - return TRUE - var/mob/living/carbon/human/H = target - return H && (flags & APPEARANCE_SKIN) && H.species.appearance_flags & HAS_SKIN_COLOR +/datum/tgui_module/appearance_changer/proc/can_change_skin_color(mob/living/carbon/human/target) + return target && (flags & APPEARANCE_SKIN) && target.species.appearance_flags & HAS_SKIN_COLOR /datum/tgui_module/appearance_changer/proc/cut_data() // Making the assumption that the available species remain constant @@ -547,14 +546,9 @@ valid_wingstyles.Cut() // VOREStation Add End -/datum/tgui_module/appearance_changer/proc/generate_data(mob/user) - var/mob/living/carbon/human/target = owner - if(customize_usr) - if(!ishuman(user)) - return TRUE - target = user - if(!target) - return +/datum/tgui_module/appearance_changer/proc/generate_data(mob/user, mob/living/carbon/human/target) + if(!ishuman(target)) + return TRUE if(!LAZYLEN(valid_species)) valid_species = target.generate_valid_species(check_whitelist, whitelist, blacklist) @@ -598,13 +592,10 @@ ))) // VOREStation Add End -/datum/tgui_module/appearance_changer/proc/get_genders(mob/target) - if(customize_usr && !ishuman(target)) - return TRUE - var/mob/living/carbon/human/H = target - var/datum/species/S = H.species +/datum/tgui_module/appearance_changer/proc/get_genders(mob/living/carbon/human/target) + var/datum/species/S = target.species var/list/possible_genders = S.genders - if(!H.internal_organs_by_name["cell"]) + if(!target.internal_organs_by_name["cell"]) return possible_genders possible_genders = possible_genders.Copy() possible_genders |= NEUTER @@ -616,6 +607,8 @@ // VOREStation Add - Ears/Tails/Wings /datum/tgui_module/appearance_changer/proc/can_use_sprite(datum/sprite_accessory/X, mob/living/carbon/human/target, mob/user) + if(X.name == DEVELOPER_WARNING_NAME) + return FALSE if(!isnull(X.species_allowed) && !(target.species.name in X.species_allowed) && (!istype(target.species, /datum/species/custom))) // Letting custom species access wings/ears/tails. return FALSE diff --git a/code/modules/ventcrawl/ventcrawl.dm b/code/modules/ventcrawl/ventcrawl.dm index d25bd2517d2..f16deca1dac 100644 --- a/code/modules/ventcrawl/ventcrawl.dm +++ b/code/modules/ventcrawl/ventcrawl.dm @@ -12,7 +12,8 @@ var/list/ventcrawl_machinery = list( /obj/machinery/camera, /obj/belly, /obj/screen, - /atom/movable/emissive_blocker + /atom/movable/emissive_blocker, + /obj/item/rig/protean ) //VOREStation Edit : added /obj/belly, to this list, CI is complaining about this in his indentation check. Added mob_headset for those with radios so there's no weirdness. //mob/living/simple_mob/borer, //VORESTATION AI TEMPORARY REMOVAL REPLACE BACK IN LIST WHEN RESOLVED //VOREStation Edit @@ -66,11 +67,11 @@ var/list/ventcrawl_machinery = list( /mob/living/proc/is_allowed_vent_crawl_item(var/obj/carried_item) //Ability master easy test for allowed (cheaper than istype) if(carried_item == ability_master) - return 1 + return TRUE if(isanimal(src)) var/mob/living/simple_mob/S = src - if(carried_item == S.myid) //VOREStation Edit - return 1 //VOREStation Edit + if(carried_item == S.myid) + return TRUE //Try to find it in our allowed list (istype includes subtypes) var/listed = FALSE for(var/test_type in can_enter_vent_with) @@ -80,23 +81,23 @@ var/list/ventcrawl_machinery = list( //Only allow it if it's "IN" the mob, not equipped on/being held. //Disabled, as it's very annoying that, for example, Pun Pun has no way to ventcrawl with his suit if given the verb, since the list of allowed items is ignored for worn items. if(listed/* && !get_inventory_slot(carried_item)*/) - return 1 + return TRUE /mob/living/carbon/is_allowed_vent_crawl_item(var/obj/item/carried_item) if(carried_item in internal_organs) - return 1 + return TRUE return ..() /mob/living/carbon/human/is_allowed_vent_crawl_item(var/obj/item/carried_item) if(carried_item in organs) - return 1 + return TRUE if(species.name == SPECIES_REPLICANT_CREW) if(istype(carried_item, /obj/item/clothing/under)) - return 1 //Allow them to not vent crawl naked + return TRUE //Allow them to not vent crawl naked if(istype(carried_item, /obj/item)) var/obj/item/I = carried_item - if(I.w_class <= 2) - return 1 //Allow them to carry items that fit in pockets + if(I.w_class <= ITEMSIZE_SMALL) + return TRUE //Allow them to carry items that fit in pockets return ..() /mob/living/proc/ventcrawl_carry() @@ -106,6 +107,29 @@ var/list/ventcrawl_machinery = list( return FALSE return TRUE +/mob/living/simple_mob/protean_blob/ventcrawl_carry() + for(var/atom/A in contents) + if(!is_allowed_vent_crawl_item(A)) + to_chat(src, span_warning("You can't carry \the [A] while ventcrawling!")) + return FALSE + if(humanform) + for(var/atom/B in humanform.get_contents()) + if(!is_allowed_vent_crawl_item(B)) + to_chat(src, span_warning("You can't carry \the [B] while ventcrawling!")) + return FALSE + return TRUE + +/mob/living/simple_mob/protean_blob/is_allowed_vent_crawl_item(var/obj/item/carried_item) + if((carried_item in humanform.organs) || (carried_item in humanform.internal_organs)) + return TRUE + if(istype(carried_item, /obj/item/clothing/under)) + return TRUE //Allow jumpsuits only + if(istype(carried_item, /obj/item)) + var/obj/item/I = carried_item + if(I.w_class <= ITEMSIZE_SMALL) + return TRUE //Allow them to carry items that fit in pockets + return ..() + /mob/living/AltClickOn(var/atom/A) if(is_type_in_list(A,ventcrawl_machinery)) handle_ventcrawl(A) @@ -129,7 +153,7 @@ var/list/ventcrawl_machinery = list( return pipe /mob/living/carbon/alien/ventcrawl_carry() - return 1 + return TRUE /mob/living/var/ventcrawl_layer = 3 diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index 3a44a1414b3..b690ab20f29 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -891,6 +891,9 @@ var/global/list/belly_colorable_only_fullscreens = list("a_synth_flesh_mono", if(!istype(H)) return + if(!H.allow_spontaneous_tf) + return + var/datum/tgui_module/appearance_changer/vore/V = new(host, H) V.tgui_interact(user) return TRUE diff --git a/code/modules/vore/resizing/resize_vr.dm b/code/modules/vore/resizing/resize_vr.dm index 9e684ad8a55..80ae9f8b720 100644 --- a/code/modules/vore/resizing/resize_vr.dm +++ b/code/modules/vore/resizing/resize_vr.dm @@ -69,7 +69,7 @@ /atom/movable/proc/has_large_resize_bounds() var/area/A = get_area(src) //Get the atom's area to check for size limit. - return A.flag_check(AREA_ALLOW_LARGE_SIZE) + return A ? A.flag_check(AREA_ALLOW_LARGE_SIZE) : FALSE /proc/is_extreme_size(size) return (size < RESIZE_MINIMUM || size > RESIZE_MAXIMUM) @@ -135,6 +135,9 @@ if(!resizable && !ignore_prefs) return 1 . = ..() + if(!ishuman(temporary_form) && isliving(temporary_form)) + var/mob/living/temp_form = temporary_form + temp_form.resize(new_size, animate, uncapped, ignore_prefs, aura_animation) if(LAZYLEN(hud_list) && has_huds) var/new_y_offset = vis_height * (size_multiplier - 1) for(var/index = 1 to hud_list.len) diff --git a/icons/mob/ears.dmi b/icons/mob/ears.dmi new file mode 100644 index 00000000000..3cbae70a3d8 Binary files /dev/null and b/icons/mob/ears.dmi differ diff --git a/icons/mob/feet.dmi b/icons/mob/feet.dmi new file mode 100644 index 00000000000..ccc2345e87b Binary files /dev/null and b/icons/mob/feet.dmi differ diff --git a/icons/mob/hands.dmi b/icons/mob/hands.dmi new file mode 100644 index 00000000000..c5434d96948 Binary files /dev/null and b/icons/mob/hands.dmi differ diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 3e8d60a5798..983a4f95b25 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/human_face_vr.dmi b/icons/mob/human_face_vr.dmi index 5da8720d000..372ce05bf45 100644 Binary files a/icons/mob/human_face_vr.dmi and b/icons/mob/human_face_vr.dmi differ diff --git a/icons/mob/lefthand_holder.dmi b/icons/mob/lefthand_holder.dmi new file mode 100644 index 00000000000..e5cded14f13 Binary files /dev/null and b/icons/mob/lefthand_holder.dmi differ diff --git a/icons/mob/righthand_holder.dmi b/icons/mob/righthand_holder.dmi new file mode 100644 index 00000000000..edc2e8d4d32 Binary files /dev/null and b/icons/mob/righthand_holder.dmi differ diff --git a/icons/mob/spacesuit.dmi b/icons/mob/spacesuit.dmi new file mode 100644 index 00000000000..7fb16ee9cc2 Binary files /dev/null and b/icons/mob/spacesuit.dmi differ diff --git a/icons/mob/species/akula/helmet.dmi b/icons/mob/species/akula/helmet.dmi new file mode 100644 index 00000000000..816728e67f1 Binary files /dev/null and b/icons/mob/species/akula/helmet.dmi differ diff --git a/icons/mob/species/akula/suit.dmi b/icons/mob/species/akula/suit.dmi new file mode 100644 index 00000000000..6bb1a3c22f0 Binary files /dev/null and b/icons/mob/species/akula/suit.dmi differ diff --git a/icons/mob/species/fox/helmet.dmi b/icons/mob/species/fox/helmet.dmi new file mode 100644 index 00000000000..662cf2b5389 Binary files /dev/null and b/icons/mob/species/fox/helmet.dmi differ diff --git a/icons/mob/species/fox/suit.dmi b/icons/mob/species/fox/suit.dmi new file mode 100644 index 00000000000..6aaae8e1533 Binary files /dev/null and b/icons/mob/species/fox/suit.dmi differ diff --git a/icons/mob/species/protean/protean.dmi b/icons/mob/species/protean/protean.dmi index f644aa051a8..bbabb24a84d 100644 Binary files a/icons/mob/species/protean/protean.dmi and b/icons/mob/species/protean/protean.dmi differ diff --git a/icons/mob/species/protean/protean128x64.dmi b/icons/mob/species/protean/protean128x64.dmi new file mode 100644 index 00000000000..3a40f0c2092 Binary files /dev/null and b/icons/mob/species/protean/protean128x64.dmi differ diff --git a/icons/mob/species/protean/protean64x32.dmi b/icons/mob/species/protean/protean64x32.dmi new file mode 100644 index 00000000000..e5244c77bf5 Binary files /dev/null and b/icons/mob/species/protean/protean64x32.dmi differ diff --git a/icons/mob/species/protean/protean64x64.dmi b/icons/mob/species/protean/protean64x64.dmi new file mode 100644 index 00000000000..1a6fe757e81 Binary files /dev/null and b/icons/mob/species/protean/protean64x64.dmi differ diff --git a/icons/mob/species/protean/protean_powers.dmi b/icons/mob/species/protean/protean_powers.dmi index febd3522a03..efcd821a813 100644 Binary files a/icons/mob/species/protean/protean_powers.dmi and b/icons/mob/species/protean/protean_powers.dmi differ diff --git a/icons/mob/species/sergal/helmet.dmi b/icons/mob/species/sergal/helmet.dmi new file mode 100644 index 00000000000..3db73dd843c Binary files /dev/null and b/icons/mob/species/sergal/helmet.dmi differ diff --git a/icons/mob/species/sergal/suit.dmi b/icons/mob/species/sergal/suit.dmi new file mode 100644 index 00000000000..cb43d464232 Binary files /dev/null and b/icons/mob/species/sergal/suit.dmi differ diff --git a/icons/mob/species/skrell/helmet.dmi b/icons/mob/species/skrell/helmet.dmi new file mode 100644 index 00000000000..0d78858afd9 Binary files /dev/null and b/icons/mob/species/skrell/helmet.dmi differ diff --git a/icons/mob/species/skrell/suit.dmi b/icons/mob/species/skrell/suit.dmi new file mode 100644 index 00000000000..74d3cb4f021 Binary files /dev/null and b/icons/mob/species/skrell/suit.dmi differ diff --git a/icons/mob/species/tajaran/helmet.dmi b/icons/mob/species/tajaran/helmet.dmi new file mode 100644 index 00000000000..4ea01ddf643 Binary files /dev/null and b/icons/mob/species/tajaran/helmet.dmi differ diff --git a/icons/mob/species/tajaran/suit.dmi b/icons/mob/species/tajaran/suit.dmi new file mode 100644 index 00000000000..fc9e06c4cab Binary files /dev/null and b/icons/mob/species/tajaran/suit.dmi differ diff --git a/icons/mob/species/teshari/feet.dmi b/icons/mob/species/teshari/feet.dmi new file mode 100644 index 00000000000..3eeebe76dfd Binary files /dev/null and b/icons/mob/species/teshari/feet.dmi differ diff --git a/icons/mob/species/teshari/hands.dmi b/icons/mob/species/teshari/hands.dmi new file mode 100644 index 00000000000..cf74d73796c Binary files /dev/null and b/icons/mob/species/teshari/hands.dmi differ diff --git a/icons/mob/species/teshari/helmet.dmi b/icons/mob/species/teshari/helmet.dmi new file mode 100644 index 00000000000..124701a5266 Binary files /dev/null and b/icons/mob/species/teshari/helmet.dmi differ diff --git a/icons/mob/species/teshari/suit.dmi b/icons/mob/species/teshari/suit.dmi index 09d72fa4612..55b76f109c0 100644 Binary files a/icons/mob/species/teshari/suit.dmi and b/icons/mob/species/teshari/suit.dmi differ diff --git a/icons/mob/species/unathi/helmet.dmi b/icons/mob/species/unathi/helmet.dmi new file mode 100644 index 00000000000..10980c26b88 Binary files /dev/null and b/icons/mob/species/unathi/helmet.dmi differ diff --git a/icons/mob/species/unathi/suit.dmi b/icons/mob/species/unathi/suit.dmi new file mode 100644 index 00000000000..7bd66e6277a Binary files /dev/null and b/icons/mob/species/unathi/suit.dmi differ diff --git a/icons/mob/species/vox/gloves.dmi b/icons/mob/species/vox/gloves.dmi new file mode 100644 index 00000000000..043d409ab4f Binary files /dev/null and b/icons/mob/species/vox/gloves.dmi differ diff --git a/icons/mob/species/vox/head.dmi b/icons/mob/species/vox/head.dmi new file mode 100644 index 00000000000..5bfd81ffea7 Binary files /dev/null and b/icons/mob/species/vox/head.dmi differ diff --git a/icons/mob/species/vox/shoes.dmi b/icons/mob/species/vox/shoes.dmi new file mode 100644 index 00000000000..14a1c8ff2e2 Binary files /dev/null and b/icons/mob/species/vox/shoes.dmi differ diff --git a/icons/mob/species/vox/suit.dmi b/icons/mob/species/vox/suit.dmi new file mode 100644 index 00000000000..58ecc85ca4a Binary files /dev/null and b/icons/mob/species/vox/suit.dmi differ diff --git a/icons/mob/species/vulpkanin/helmet.dmi b/icons/mob/species/vulpkanin/helmet.dmi new file mode 100644 index 00000000000..39cf0a26a5c Binary files /dev/null and b/icons/mob/species/vulpkanin/helmet.dmi differ diff --git a/icons/mob/species/vulpkanin/suit.dmi b/icons/mob/species/vulpkanin/suit.dmi new file mode 100644 index 00000000000..1242aaf5b07 Binary files /dev/null and b/icons/mob/species/vulpkanin/suit.dmi differ diff --git a/icons/mob/species/xenomorph_hybrid/gloves.dmi b/icons/mob/species/xenomorph_hybrid/gloves.dmi new file mode 100644 index 00000000000..d723ac8a71d Binary files /dev/null and b/icons/mob/species/xenomorph_hybrid/gloves.dmi differ diff --git a/icons/mob/species/xenomorph_hybrid/helmet.dmi b/icons/mob/species/xenomorph_hybrid/helmet.dmi new file mode 100644 index 00000000000..9d1d68c6a7d Binary files /dev/null and b/icons/mob/species/xenomorph_hybrid/helmet.dmi differ diff --git a/icons/mob/species/xenomorph_hybrid/shoes.dmi b/icons/mob/species/xenomorph_hybrid/shoes.dmi new file mode 100644 index 00000000000..f52f13a184b Binary files /dev/null and b/icons/mob/species/xenomorph_hybrid/shoes.dmi differ diff --git a/icons/mob/species/xenomorph_hybrid/suit.dmi b/icons/mob/species/xenomorph_hybrid/suit.dmi new file mode 100644 index 00000000000..adf54281828 Binary files /dev/null and b/icons/mob/species/xenomorph_hybrid/suit.dmi differ diff --git a/icons/mob/suit.dmi b/icons/mob/suit.dmi new file mode 100644 index 00000000000..35400348492 Binary files /dev/null and b/icons/mob/suit.dmi differ diff --git a/icons/mob/uniform.dmi b/icons/mob/uniform.dmi index 9c210eb641c..0b5fd53fb22 100644 Binary files a/icons/mob/uniform.dmi and b/icons/mob/uniform.dmi differ diff --git a/icons/obj/airlock_machines.dmi b/icons/obj/airlock_machines.dmi index 1f22e8e1989..9a24724c815 100644 Binary files a/icons/obj/airlock_machines.dmi and b/icons/obj/airlock_machines.dmi differ diff --git a/icons/obj/card_alt_vr.dmi b/icons/obj/card_alt_vr.dmi index d18adbb882f..b9e2db290c2 100644 Binary files a/icons/obj/card_alt_vr.dmi and b/icons/obj/card_alt_vr.dmi differ diff --git a/icons/obj/chemical.dmi b/icons/obj/chemical.dmi index 2dbb310fded..1ea17855596 100644 Binary files a/icons/obj/chemical.dmi and b/icons/obj/chemical.dmi differ diff --git a/icons/obj/drinks.dmi b/icons/obj/drinks.dmi index d15f3417d0e..2cfbcc9b50d 100644 Binary files a/icons/obj/drinks.dmi and b/icons/obj/drinks.dmi differ diff --git a/icons/obj/rig_modules_vr.dmi b/icons/obj/rig_modules_vr.dmi index 334a8e881ff..b09eb0d54c7 100644 Binary files a/icons/obj/rig_modules_vr.dmi and b/icons/obj/rig_modules_vr.dmi differ diff --git a/icons/obj/slimeborg/slimecore.dmi b/icons/obj/slimeborg/slimecore.dmi new file mode 100644 index 00000000000..761a44804cb Binary files /dev/null and b/icons/obj/slimeborg/slimecore.dmi differ diff --git a/icons/obj/stock_parts.dmi b/icons/obj/stock_parts.dmi index 30baa36e3cd..dcb032ca655 100644 Binary files a/icons/obj/stock_parts.dmi and b/icons/obj/stock_parts.dmi differ diff --git a/icons/obj/stock_parts_vr.dmi b/icons/obj/stock_parts_vr.dmi index 8e48a4c0a30..6a90bf44852 100644 Binary files a/icons/obj/stock_parts_vr.dmi and b/icons/obj/stock_parts_vr.dmi differ diff --git a/maps/groundbase/gb-z1.dmm b/maps/groundbase/gb-z1.dmm index 331732a24a0..9cd2c0d3307 100644 --- a/maps/groundbase/gb-z1.dmm +++ b/maps/groundbase/gb-z1.dmm @@ -1820,6 +1820,15 @@ /obj/machinery/atmospherics/pipe/simple/hidden/supply{ dir = 4 }, +/obj/structure/closet/walllocker_double/hydrant/south, +/obj/item/hand_labeler, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup_tall, +/obj/item/storage/box/glasses/coffeecup_grande, +/obj/item/storage/box/glasses/coffeecup_venti, /turf/simulated/floor/lino, /area/groundbase/civilian/cafe) "dW" = ( @@ -5875,8 +5884,12 @@ /obj/random/mug, /obj/random/mug, /obj/item/reagent_containers/food/drinks/shaker{ - pixel_x = 5; - pixel_y = 6 + pixel_x = -7; + pixel_y = 18 + }, +/obj/machinery/chemical_dispenser/bar_syrup/full{ + dir = 8; + pixel_x = 7 }, /turf/simulated/floor/lino, /area/groundbase/civilian/cafe) diff --git a/maps/groundbase/groundbase_events.dm b/maps/groundbase/groundbase_events.dm index 905ce89875f..e12d41100dd 100644 --- a/maps/groundbase/groundbase_events.dm +++ b/maps/groundbase/groundbase_events.dm @@ -70,6 +70,7 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Drone Pod Drop", /datum/event/drone_pod_drop, 40, list(ASSIGNMENT_SCIENTIST = 40), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/morph_spawn, 75, list(ASSIGNMENT_ANY = 5), 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Predator", /datum/event/maintenance_predator, 100, list(ASSIGNMENT_ANY = 5), 0), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Lurker", /datum/event/maintenance_lurker, 100, list(ASSIGNMENT_ANY = 5), 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Roaming Wildlife", /datum/event/roaming_wildlife, 20, list(ASSIGNMENT_SECURITY = 20, ASSIGNMENT_MEDICAL = 5), min_jobs = list(ASSIGNMENT_SECURITY = 2)), ) add_disabled_events(list( diff --git a/maps/southern_cross/job/outfits.dm b/maps/southern_cross/job/outfits.dm index f2ef7db2ee6..95db358a216 100644 --- a/maps/southern_cross/job/outfits.dm +++ b/maps/southern_cross/job/outfits.dm @@ -54,9 +54,9 @@ Keep outfits simple. Spawn with basic uniforms and minimal gear. Gear instead go id_pda_assignment = JOB_PILOT flags = OUTFIT_HAS_BACKPACK|OUTFIT_COMPREHENSIVE_SURVIVAL - headset = /obj/item/radio/headset/pilot/alt - headset_alt = /obj/item/radio/headset/pilot/alt - headset_earbud = /obj/item/radio/headset/pilot/alt + headset = /obj/item/radio/headset/alt/pilot + headset_alt = /obj/item/radio/headset/alt/pilot + headset_earbud = /obj/item/radio/headset/alt/pilot /decl/hierarchy/outfit/job/medical/sar name = OUTFIT_JOB_NAME("Field Medic") //VOREStation Edit diff --git a/maps/stellar_delight/stellar_delight3.dmm b/maps/stellar_delight/stellar_delight3.dmm index 4a75c431248..2e51d9e5443 100644 --- a/maps/stellar_delight/stellar_delight3.dmm +++ b/maps/stellar_delight/stellar_delight3.dmm @@ -1563,7 +1563,6 @@ pixel_y = 12 }, /obj/structure/table/woodentable, -/obj/item/hand_labeler, /obj/machinery/alarm/angled, /obj/machinery/status_display{ pixel_x = -32 @@ -9920,19 +9919,11 @@ icon_state = "0-2" }, /obj/structure/table/woodentable, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, /obj/item/reagent_containers/glass/rag, +/obj/machinery/chemical_dispenser/bar_syrup/full{ + pixel_y = 11; + pixel_x = -1 + }, /turf/simulated/floor/lino, /area/stellardelight/deck3/cafe) "JJ" = ( @@ -11410,6 +11401,13 @@ /obj/machinery/door/window{ req_one_access = list(25) }, +/obj/machinery/button/remote/blast_door{ + desc = "A remote control-switch for shutters."; + id = "cafe"; + name = "Cafe Shutters"; + pixel_y = 28; + req_one_access = list(25) + }, /turf/simulated/floor/lino, /area/stellardelight/deck3/cafe) "Oz" = ( @@ -14112,13 +14110,18 @@ /turf/simulated/floor, /area/maintenance/stellardelight/deck3/aftstarroom) "Yp" = ( -/obj/machinery/button/remote/blast_door{ - desc = "A remote control-switch for shutters."; - id = "cafe"; - name = "Cafe Shutters"; - pixel_y = 28; - req_one_access = list(25) - }, +/obj/structure/closet/walllocker_double/kitchen/north, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/hand_labeler, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup_tall, +/obj/item/storage/box/glasses/coffeecup_grande, +/obj/item/storage/box/glasses/coffeecup_venti, /turf/simulated/floor/lino, /area/stellardelight/deck3/cafe) "Ys" = ( diff --git a/maps/stellar_delight/stellar_delight_events.dm b/maps/stellar_delight/stellar_delight_events.dm index 9511d67a048..d524b2bfcde 100644 --- a/maps/stellar_delight/stellar_delight_events.dm +++ b/maps/stellar_delight/stellar_delight_events.dm @@ -86,6 +86,7 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Drone Pod Drop", /datum/event/drone_pod_drop, 40, list(ASSIGNMENT_SCIENTIST = 40), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/morph_spawn, 75, list(ASSIGNMENT_ANY = 5), 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Predator", /datum/event/maintenance_predator, 100, list(ASSIGNMENT_ANY = 5), 0), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Lurker", /datum/event/maintenance_lurker, 100, list(ASSIGNMENT_ANY = 5), 0), ) add_disabled_events(list( new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 30), 1), diff --git a/maps/tether/tether-01-surface1.dmm b/maps/tether/tether-01-surface1.dmm index 2768b3e8c66..52a2a55bdcf 100644 --- a/maps/tether/tether-01-surface1.dmm +++ b/maps/tether/tether-01-surface1.dmm @@ -17639,6 +17639,16 @@ /obj/machinery/alarm{ pixel_y = 22 }, +/obj/structure/closet/walllocker_double/kitchen/east, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/reagent_containers/food/drinks/glass2/square, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup, +/obj/item/storage/box/glasses/coffeecup_tall, +/obj/item/storage/box/glasses/coffeecup_grande, +/obj/item/storage/box/glasses/coffeecup_venti, /turf/simulated/floor/tiled, /area/crew_quarters/visitor_dining) "aEb" = ( @@ -18425,24 +18435,16 @@ /area/crew_quarters/visitor_dining) "aFC" = ( /obj/structure/table/marble, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, -/obj/item/reagent_containers/food/drinks/glass2/square{ - pixel_x = 8; - pixel_y = 8 - }, /obj/machinery/floor_light/prebuilt{ on = 1 }, /obj/machinery/atmospherics/unary/vent_pump/on{ dir = 8 }, +/obj/machinery/chemical_dispenser/bar_syrup/full{ + dir = 8; + pixel_x = 8 + }, /turf/simulated/floor/tiled, /area/crew_quarters/visitor_dining) "aFD" = ( @@ -19024,7 +19026,8 @@ "aGy" = ( /obj/structure/table/marble, /obj/machinery/chemical_dispenser/bar_coffee/full{ - dir = 8 + dir = 8; + pixel_x = 1 }, /obj/machinery/floor_light/prebuilt{ on = 1 diff --git a/maps/tether/tether_events.dm b/maps/tether/tether_events.dm index 5ed863881a3..543e6002f0a 100644 --- a/maps/tether/tether_events.dm +++ b/maps/tether/tether_events.dm @@ -86,6 +86,7 @@ new /datum/event_meta(EVENT_LEVEL_MODERATE, "Drone Pod Drop", /datum/event/drone_pod_drop, 40, list(ASSIGNMENT_SCIENTIST = 40), 1), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Morph Spawn", /datum/event/morph_spawn, 75, list(ASSIGNMENT_ANY = 5), 0), new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Predator", /datum/event/maintenance_predator, 100, list(ASSIGNMENT_ANY = 5), 0), + new /datum/event_meta(EVENT_LEVEL_MODERATE, "Maintenance Lurker", /datum/event/maintenance_lurker, 100, list(ASSIGNMENT_ANY = 5), 0), ) add_disabled_events(list( new /datum/event_meta(EVENT_LEVEL_MODERATE, "Appendicitis", /datum/event/spontaneous_appendicitis, 0, list(ASSIGNMENT_MEDICAL = 30), 1), diff --git a/sound/voice/borg_deathsound.ogg b/sound/voice/borg_deathsound.ogg new file mode 100644 index 00000000000..bb11022abec Binary files /dev/null and b/sound/voice/borg_deathsound.ogg differ diff --git a/tgui/packages/common/string.ts b/tgui/packages/common/string.ts index d6f328750c4..29dee190f03 100644 --- a/tgui/packages/common/string.ts +++ b/tgui/packages/common/string.ts @@ -130,7 +130,7 @@ export function toTitleCase(str: string): string { return currentStr; } -const TRANSLATE_REGEX = /&(nbsp|amp|quot|lt|gt|apos);/g; +const TRANSLATE_REGEX = /&(nbsp|amp|quot|lt|gt|apos|trade);/g; const TRANSLATIONS = { amp: '&', apos: "'", @@ -138,6 +138,7 @@ const TRANSLATIONS = { lt: '<', nbsp: ' ', quot: '"', + trade: '™', } as const; /** diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx index e9e3472a18b..8785a469602 100644 --- a/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/AppearanceChangerDetails.tsx @@ -19,8 +19,11 @@ export const AppearanceChangerColors = (props) => { ears2_color, tail_color, tail2_color, + tail3_color, wing_color, wing2_color, + wing3_color, + ear_secondary_colors, } = data; return ( @@ -67,13 +70,17 @@ export const AppearanceChangerColors = (props) => { {data.ear_secondary_colors.map((color, index) => ( - + + + + ))} @@ -85,6 +92,12 @@ export const AppearanceChangerColors = (props) => { Change Secondary Tail Color + + + + @@ -95,6 +108,12 @@ export const AppearanceChangerColors = (props) => { Change Secondary Wing Color + + + + ) : null} {change_facial_hair_color ? ( diff --git a/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts index aae93386f16..21401487721 100644 --- a/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts +++ b/tgui/packages/tgui/interfaces/AppearanceChanger/types.ts @@ -42,8 +42,10 @@ export type Data = { ears2_color: string; tail_color: string; tail2_color: string; + tail3_color: string; wing_color: string; wing2_color: string; + wing3_color: string; facial_hair_styles: { facialhairstyle: string }[]; hair_styles: { hairstyle: string }[]; ear_secondary_style: string; diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleAdvanced.tsx b/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleAdvanced.tsx index 0977fc9d54d..15e7f9b2c89 100644 --- a/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleAdvanced.tsx +++ b/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleAdvanced.tsx @@ -1,6 +1,7 @@ import { useBackend } from '../../backend'; import { Box, Button, Section } from '../../components'; import { StandardControls, StatusDisplay } from './EmbeddedControllerHelpers'; +import { PanelOpen } from './PanelOpen'; import { AirlockConsoleAdvancedData } from './types'; /** @@ -12,8 +13,13 @@ import { AirlockConsoleAdvancedData } from './types'; export const AirlockConsoleAdvanced = (props) => { const { act, data } = useBackend(); - const { external_pressure, chamber_pressure, internal_pressure, processing } = - data; + const { + external_pressure, + chamber_pressure, + internal_pressure, + processing, + panel_open, + } = data; const pressure_range = { external_pressure, @@ -59,27 +65,31 @@ export const AirlockConsoleAdvanced = (props) => { return ( <> -
- - - - - - - - -
+ {panel_open ? ( + + ) : ( +
+ + + + + + + + +
+ )} ); }; diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleSimple.tsx b/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleSimple.tsx index bed9c734727..c97e0854cc6 100644 --- a/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleSimple.tsx +++ b/tgui/packages/tgui/interfaces/EmbeddedController/AirlockConsoleSimple.tsx @@ -1,6 +1,7 @@ import { useBackend } from '../../backend'; import { Box, Button, Section } from '../../components'; import { StandardControls, StatusDisplay } from './EmbeddedControllerHelpers'; +import { PanelOpen } from './PanelOpen'; import { AirlockConsoleSimpleData } from './types'; /** @@ -12,8 +13,13 @@ import { AirlockConsoleSimpleData } from './types'; export const AirlockConsoleSimple = (props) => { const { act, data } = useBackend(); - const { exterior_status, chamber_pressure, processing, interior_status } = - data; + const { + exterior_status, + chamber_pressure, + processing, + interior_status, + panel_open, + } = data; const status_range = { interior_status, exterior_status }; @@ -37,19 +43,23 @@ export const AirlockConsoleSimple = (props) => { return ( <> -
- - - - -
+ {panel_open ? ( + + ) : ( +
+ + + + +
+ )} ); }; diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/DoorAccessConsole.tsx b/tgui/packages/tgui/interfaces/EmbeddedController/DoorAccessConsole.tsx index 9de6aa36c5f..bd4963fde33 100644 --- a/tgui/packages/tgui/interfaces/EmbeddedController/DoorAccessConsole.tsx +++ b/tgui/packages/tgui/interfaces/EmbeddedController/DoorAccessConsole.tsx @@ -1,5 +1,6 @@ import { useBackend } from '../../backend'; import { Button, LabeledList, Section } from '../../components'; +import { PanelOpen } from './PanelOpen'; import { DoorAccessConsoleData } from './types'; /** @@ -9,7 +10,7 @@ import { DoorAccessConsoleData } from './types'; export const DoorAccessConsole = (props) => { const { act, data } = useBackend(); - const { interior_status, exterior_status } = data; + const { interior_status, exterior_status, panel_open } = data; const interiorOpen = interior_status.state === 'open' || exterior_status.state === 'closed'; @@ -17,39 +18,42 @@ export const DoorAccessConsole = (props) => { exterior_status.state === 'open' || interior_status.state === 'closed'; return ( -
- {/* Interior Button */} - - {/* Exterior Button */} - - - } - > - - - {exterior_status.state === 'closed' ? 'Locked' : 'Open'} - - - {interior_status.state === 'closed' ? 'Locked' : 'Open'} - - -
+ <> +
+ {/* Interior Button */} + + {/* Exterior Button */} + + + } + > + + + {exterior_status.state === 'closed' ? 'Locked' : 'Open'} + + + {interior_status.state === 'closed' ? 'Locked' : 'Open'} + + +
+ {!!panel_open && } + ); }; diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/PanelOpen.tsx b/tgui/packages/tgui/interfaces/EmbeddedController/PanelOpen.tsx new file mode 100644 index 00000000000..c02bbb78073 --- /dev/null +++ b/tgui/packages/tgui/interfaces/EmbeddedController/PanelOpen.tsx @@ -0,0 +1,57 @@ +import { round, toFixed } from 'common/math'; +import { useBackend } from 'tgui/backend'; +import { + Box, + Button, + LabeledList, + NumberInput, + Section, +} from 'tgui-core/components'; + +import { PanelOpenData } from './types'; + +export const PanelOpen = (props) => { + const { act, data } = useBackend(); + + const { panel_open, tags, frequency, min_freq, max_freq } = data; + + if (!panel_open || !tags) { + return null; + } + + return ( +
+ + + toFixed(val, 1)} + onDrag={(freq) => + act('set_frequency', { freq: round(freq * 10, 0) }) + } + /> + + {Object.entries(tags).map(([tag, value]) => ( + act('edit_tag', { tag })}> + Edit + + } + > + {value || Unset} + + ))} + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/index.tsx b/tgui/packages/tgui/interfaces/EmbeddedController/index.tsx index eb4504e3510..e7f289bd1cb 100644 --- a/tgui/packages/tgui/interfaces/EmbeddedController/index.tsx +++ b/tgui/packages/tgui/interfaces/EmbeddedController/index.tsx @@ -98,7 +98,7 @@ export const EmbeddedController = (props) => { } return ( - + {Component} ); diff --git a/tgui/packages/tgui/interfaces/EmbeddedController/types.ts b/tgui/packages/tgui/interfaces/EmbeddedController/types.ts index d6b9c374873..fc343d0a632 100644 --- a/tgui/packages/tgui/interfaces/EmbeddedController/types.ts +++ b/tgui/packages/tgui/interfaces/EmbeddedController/types.ts @@ -44,7 +44,7 @@ export type AirlockConsoleAdvancedData = { purge: BooleanLike; secure: BooleanLike; internalTemplateName: string; -}; +} & PanelOpenData; export type AirlockConsoleSimpleData = { chamber_pressure: number; @@ -52,7 +52,7 @@ export type AirlockConsoleSimpleData = { interior_status: status; processing: BooleanLike; internalTemplateName: string; -}; +} & PanelOpenData; export type AirlockConsolePhoronData = { chamber_pressure: number; @@ -68,7 +68,7 @@ export type DoorAccessConsoleData = { interior_status: status; processing: BooleanLike; internalTemplateName: string; -}; +} & PanelOpenData; export type EscapePodConsoleData = { docking_status: string; @@ -80,3 +80,11 @@ export type EscapePodConsoleData = { }; export type status = { state: string; lock: string }; + +export type PanelOpenData = { + panel_open: BooleanLike; + tags: Record | null; + frequency: number; + min_freq: number; + max_freq: number; +}; diff --git a/tgui/packages/tgui/interfaces/PanDEMIC.tsx b/tgui/packages/tgui/interfaces/PanDEMIC.tsx index ff8c718529c..967486a8c94 100644 --- a/tgui/packages/tgui/interfaces/PanDEMIC.tsx +++ b/tgui/packages/tgui/interfaces/PanDEMIC.tsx @@ -185,7 +185,10 @@ const StrainInformationSection = ({ {selectedStrain && selectedStrain.symptoms.length > 0 && ( - + <> + + + )} ); @@ -220,6 +223,39 @@ const StrainSymptomsSection = ({ strain }: { strain: Strain }) => { ); }; +const TotalStats = ({ strain }: { strain: Strain }) => { + const symptoms = strain.symptoms; + + const TotalStats = symptoms.reduce( + (totals, symptom) => ({ + stealth: totals.stealth + symptom.stealth, + resistance: totals.resistance + symptom.resistance, + stageSpeed: totals.stageSpeed + symptom.stageSpeed, + transmissibility: totals.transmissibility + symptom.transmissibility, + }), + { stealth: 0, resistance: 0, stageSpeed: 0, transmissibility: 0 }, + ); + + return ( +
+ + + Stealth + Resistance + Stage Speed + Transmissibility + + + {TotalStats.stealth} + {TotalStats.resistance} + {TotalStats.stageSpeed} + {TotalStats.transmissibility} + +
+
+ ); +}; + const StrainInformation = ({ strain, strainIndex, diff --git a/tgui/packages/tgui/interfaces/Wires.tsx b/tgui/packages/tgui/interfaces/Wires.tsx index e197b966182..7b771bf3942 100644 --- a/tgui/packages/tgui/interfaces/Wires.tsx +++ b/tgui/packages/tgui/interfaces/Wires.tsx @@ -1,10 +1,9 @@ import { BooleanLike } from 'common/react'; +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Box, Button, LabeledList, Section } from 'tgui-core/components'; -import { useBackend } from '../backend'; -import { Box, Button, LabeledList, Section } from '../components'; -import { Window } from '../layouts'; - -type Data = { +export type WireData = { wires: { seen_color: string; color_name: string; @@ -17,71 +16,88 @@ type Data = { }; export const Wires = (props) => { - const { act, data } = useBackend(); + const { data } = useBackend(); + const { wires = [] } = data; + + return ( + + + + + + + ); +}; + +export const WiresWires = (props) => { + const { act, data } = useBackend(); const { wires = [] } = data; const statuses = data.status || []; return ( - - -
- - {wires.map((wire) => ( - - - - - - } - > - {!!wire.wire && ({wire.wire})} - - ))} - -
- - {!!statuses.length && ( -
- {statuses.map((status) => ( - - {status} - - ))} -
- )} -
-
+
+ + {wires.map((wire) => ( + + + + + + } + > + {!!wire.wire && ({wire.wire})} + + ))} + +
); }; + +export const WiresStatus = (props) => { + const { act, data } = useBackend(); + + const { wires = [] } = data; + const statuses = data.status || []; + + return statuses.length ? ( +
+ {statuses.map((status) => ( + + {status} + + ))} +
+ ) : null; +}; diff --git a/tgui/packages/tgui/interfaces/WiresAirlock.tsx b/tgui/packages/tgui/interfaces/WiresAirlock.tsx new file mode 100644 index 00000000000..eda4886a3bb --- /dev/null +++ b/tgui/packages/tgui/interfaces/WiresAirlock.tsx @@ -0,0 +1,81 @@ +import { round } from 'common/math'; +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { + Box, + Button, + LabeledList, + NumberInput, + Section, +} from 'tgui-core/components'; + +import { WireData, WiresStatus, WiresWires } from './Wires'; + +type WiresAirlockData = WireData & { + id_tag: string; + frequency: number | null; + min_freq: number; + max_freq: number; +}; + +export const WiresAirlock = (props) => { + const { act, data } = useBackend(); + const { wires = [], id_tag, frequency, min_freq, max_freq } = data; + + return ( + + +
+ + act('set_id_tag')}> + Edit + + } + > + {id_tag || None Set} + + act('clear_frequency')}> + Clear + + ) : null + } + > + {frequency ? ( + val.toFixed(1)} + onChange={(freq) => + act('set_frequency', { freq: round(freq * 10, 0) }) + } + /> + ) : ( + + )} + + +
+ + +
+
+ ); +}; diff --git a/vorestation.dme b/vorestation.dme index 057b7057863..7e4854302ef 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -137,6 +137,7 @@ #include "code\__defines\statpanel.dm" #include "code\__defines\subsystems.dm" #include "code\__defines\supply.dm" +#include "code\__defines\talksounds.dm" #include "code\__defines\targeting.dm" #include "code\__defines\text.dm" #include "code\__defines\tgs.config.dm" @@ -464,13 +465,16 @@ #include "code\datums\diseases\_disease.dm" #include "code\datums\diseases\_MobProcs.dm" #include "code\datums\diseases\anxiety.dm" +#include "code\datums\diseases\appendicitis.dm" #include "code\datums\diseases\beesease.dm" #include "code\datums\diseases\brainrot.dm" #include "code\datums\diseases\choreomania.dm" #include "code\datums\diseases\cold.dm" #include "code\datums\diseases\cold9.dm" +#include "code\datums\diseases\fake_gbs.dm" #include "code\datums\diseases\flu.dm" #include "code\datums\diseases\food_poisoning.dm" +#include "code\datums\diseases\gbs.dm" #include "code\datums\diseases\lycancoughy.dm" #include "code\datums\diseases\magnitis.dm" #include "code\datums\diseases\advance\advance.dm" @@ -479,23 +483,30 @@ #include "code\datums\diseases\advance\symptoms\confusion.dm" #include "code\datums\diseases\advance\symptoms\cough.dm" #include "code\datums\diseases\advance\symptoms\damage_converter.dm" +#include "code\datums\diseases\advance\symptoms\deafness.dm" #include "code\datums\diseases\advance\symptoms\dizzy.dm" #include "code\datums\diseases\advance\symptoms\fever.dm" #include "code\datums\diseases\advance\symptoms\fire.dm" #include "code\datums\diseases\advance\symptoms\flesh_eating.dm" +#include "code\datums\diseases\advance\symptoms\flip.dm" +#include "code\datums\diseases\advance\symptoms\hair.dm" #include "code\datums\diseases\advance\symptoms\hallucigen.dm" #include "code\datums\diseases\advance\symptoms\headache.dm" #include "code\datums\diseases\advance\symptoms\heal.dm" +#include "code\datums\diseases\advance\symptoms\hematophagy.dm" #include "code\datums\diseases\advance\symptoms\itching.dm" #include "code\datums\diseases\advance\symptoms\language.dm" #include "code\datums\diseases\advance\symptoms\macrophage.dm" #include "code\datums\diseases\advance\symptoms\mlem.dm" #include "code\datums\diseases\advance\symptoms\necrotic_agent.dm" -#include "code\datums\diseases\advance\symptoms\oxygen.dm" +#include "code\datums\diseases\advance\symptoms\photosensitivity.dm" +#include "code\datums\diseases\advance\symptoms\pica.dm" #include "code\datums\diseases\advance\symptoms\sensory.dm" #include "code\datums\diseases\advance\symptoms\shivering.dm" +#include "code\datums\diseases\advance\symptoms\size.dm" #include "code\datums\diseases\advance\symptoms\sneeze.dm" #include "code\datums\diseases\advance\symptoms\spin.dm" +#include "code\datums\diseases\advance\symptoms\stimulant.dm" #include "code\datums\diseases\advance\symptoms\symptoms.dm" #include "code\datums\diseases\advance\symptoms\synthetic_infection.dm" #include "code\datums\diseases\advance\symptoms\telepathy.dm" @@ -1077,6 +1088,7 @@ #include "code\game\machinery\embedded_controller\airlock_docking_controller.dm" #include "code\game\machinery\embedded_controller\airlock_docking_controller_multi.dm" #include "code\game\machinery\embedded_controller\airlock_program.dm" +#include "code\game\machinery\embedded_controller\construction.dm" #include "code\game\machinery\embedded_controller\docking_program.dm" #include "code\game\machinery\embedded_controller\docking_program_multi.dm" #include "code\game\machinery\embedded_controller\embedded_controller_base.dm" @@ -1471,6 +1483,7 @@ #include "code\game\objects\items\weapons\circuitboards\machinery\mech_recharger.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\mining_drill.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\pacman.dm" +#include "code\game\objects\items\weapons\circuitboards\machinery\pandemic.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\papershredder.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\power.dm" #include "code\game\objects\items\weapons\circuitboards\machinery\recharge_station.dm" @@ -2223,6 +2236,7 @@ #include "code\modules\clothing\spacesuits\rig\rig_verbs.dm" #include "code\modules\clothing\spacesuits\rig\rig_wiring.dm" #include "code\modules\clothing\spacesuits\rig\modules\modules.dm" +#include "code\modules\clothing\spacesuits\rig\modules\protean.dm" #include "code\modules\clothing\spacesuits\rig\modules\specific\ai_container.dm" #include "code\modules\clothing\spacesuits\rig\modules\specific\chem_dispenser.dm" #include "code\modules\clothing\spacesuits\rig\modules\specific\cleaner_launcher.dm" @@ -2417,6 +2431,7 @@ #include "code\modules\events\infestation.dm" #include "code\modules\events\ion_storm.dm" #include "code\modules\events\jellyfish_migration.dm" +#include "code\modules\events\maint_lurker.dm" #include "code\modules\events\maintenance_predator_vr.dm" #include "code\modules\events\meteor_strike_vr.dm" #include "code\modules\events\meteors.dm" @@ -3097,6 +3112,8 @@ #include "code\modules\mob\living\carbon\human\species\station\protean_vr\_protean_defines.dm" #include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_blob.dm" #include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_powers.dm" +#include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_rig.dm" +#include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_rig_tgui.dm" #include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_species.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\_traits.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\negative.dm"