From 1c326a38e1477ed830bdb6d94a9e44d8717840ed Mon Sep 17 00:00:00 2001 From: Guti <32563288+TheCaramelion@users.noreply.github.com> Date: Tue, 17 Jun 2025 20:29:06 +0200 Subject: [PATCH 01/47] Viruses now spawn with random names (#17882) * Disease descriptions * Random names --- code/datums/diseases/advance/advance.dm | 108 +++++++++++++++++- .../datums/diseases/advance/disease_preset.dm | 9 +- .../diseases/advance/symptoms/blobspores.dm | 3 + .../diseases/advance/symptoms/choking.dm | 3 + .../diseases/advance/symptoms/cockroach.dm | 3 + .../diseases/advance/symptoms/confusion.dm | 3 + .../datums/diseases/advance/symptoms/cough.dm | 2 + .../diseases/advance/symptoms/deafness.dm | 3 + .../datums/diseases/advance/symptoms/fever.dm | 5 + code/datums/diseases/advance/symptoms/fire.dm | 4 + .../diseases/advance/symptoms/flesh_eating.dm | 3 + code/datums/diseases/advance/symptoms/flip.dm | 3 + code/datums/diseases/advance/symptoms/hair.dm | 2 + .../diseases/advance/symptoms/hallucigen.dm | 3 + .../diseases/advance/symptoms/headache.dm | 2 + code/datums/diseases/advance/symptoms/heal.dm | 8 ++ .../diseases/advance/symptoms/itching.dm | 3 + .../diseases/advance/symptoms/language.dm | 3 + .../datums/diseases/advance/symptoms/light.dm | 3 + .../diseases/advance/symptoms/macrophage.dm | 3 + code/datums/diseases/advance/symptoms/mlem.dm | 3 + .../diseases/advance/symptoms/radiation.dm | 5 + .../diseases/advance/symptoms/shivering.dm | 3 + code/datums/diseases/advance/symptoms/size.dm | 4 + .../diseases/advance/symptoms/sneeze.dm | 6 + code/datums/diseases/advance/symptoms/spin.dm | 3 + .../diseases/advance/symptoms/stimulant.dm | 5 + .../diseases/advance/symptoms/symptoms.dm | 7 ++ .../datums/diseases/advance/symptoms/viral.dm | 6 + .../diseases/advance/symptoms/vision.dm | 4 + .../datums/diseases/advance/symptoms/vomit.dm | 4 + .../diseases/advance/symptoms/weakness.dm | 2 + .../diseases/advance/symptoms/weight.dm | 2 + .../datums/diseases/advance/symptoms/youth.dm | 3 + code/datums/diseases/anxiety.dm | 1 + code/datums/diseases/appendicitis.dm | 1 + code/datums/diseases/beesease.dm | 1 + code/datums/diseases/brainrot.dm | 3 +- code/datums/diseases/choreomania.dm | 1 + code/datums/diseases/cold.dm | 1 + code/datums/diseases/fake_gbs.dm | 3 +- code/datums/diseases/flu.dm | 1 + code/datums/diseases/food_poisoning.dm | 1 + code/datums/diseases/gbs.dm | 4 + code/datums/diseases/lycancoughy.dm | 1 + code/datums/diseases/magnitis.dm | 1 + .../effects/decals/Cleanable/humans.dm | 2 +- .../subtypes/animal/passive/mouse.dm | 4 +- .../reagents/reagent_containers/syringes.dm | 2 +- 49 files changed, 249 insertions(+), 11 deletions(-) diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 0b088cb74f4..2cf2d6dc256 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -17,7 +17,7 @@ GLOBAL_LIST_INIT(advance_cures, list( )) /datum/disease/advance - name = "Unknown" + name = DEVELOPER_WARNING_NAME desc = "An engineered disease which can contain a multitude of symptoms." form = "Advance Disease" agent = "advance microbes" @@ -178,7 +178,7 @@ GLOBAL_LIST_INIT(advance_cures, list( else var/datum/disease/advance/A = GLOB.archive_diseases[GetDiseaseID()] var/actual_name = A.name - if(actual_name != "Unknown") + if(actual_name != DEVELOPER_WARNING_NAME) name = actual_name @@ -266,8 +266,10 @@ GLOBAL_LIST_INIT(advance_cures, list( severity = DISEASE_HARMFUL if(4) severity = DISEASE_DANGEROUS - if(5 to INFINITY) + if(5) severity = DISEASE_BIOHAZARD + if(6 to INFINITY) + severity = DISEASE_PANDEMIC else severity = "Unknown" @@ -477,3 +479,103 @@ GLOBAL_LIST_INIT(advance_cures, list( GLOB.active_diseases += A log_admin("[key_name(src)] has contracted the virus \"[A]\"") + +/* +* Generates a random name for a disease, depending on where it comes from +*/ +/datum/disease/advance/proc/random_disease_name(var/atom/diseasesource) + + if(length(symptoms) == 1) + var/datum/symptom/main_symptom = symptoms[1] + if(istype(main_symptom) && length(main_symptom.name)) + return main_symptom.name + + // Prefixes. These need a space right after. + var/list/prefixes = list("Spacer's ", "Space ", "Infectious ","Viral ", "The ", "[capitalize(prob(50) ? pick(first_names_male) : pick(first_names_female))]'s ", "[capitalize(pick(last_names))]'s ", "Acute ") + var/list/bodies = list(pick("[capitalize(prob(50) ? pick(first_names_male) : pick(first_names_female))]", "[pick(last_names)]"), "Space", "Disease", "Noun", "Cold", "Germ", "Virus") + // These might need some space before the word, depends on what you want to add. + var/list/suffixes = list("ism", "itis", "osis", "itosis", " #[rand(1,10000)]", "-[rand(1,100)]", "s", "y", " Virus", " Bug", " Infection", " Disease", " Complex", " Syndrome", " Sickness") + + if(stealth >=2) + prefixes += "Crypto " + switch(max(resistance - (symptoms.len / 2), 1)) + if(1) + suffixes += "-alpha" + if(2) + suffixes += "-beta" + if(3) + suffixes += "-gamma" + if(4) + suffixes += "-delta" + if(5) + suffixes += "-epsilon" + if(6) + suffixes += pick("-zeta", "-eta", "-theta", "-iota") + if(7) + suffixes += pick("-kappa", "-lambda") + if(8) + suffixes += pick("-mu", "-nu", "-xi", "-omicron") + if(9) + suffixes += pick("-pi", "-rho", "-sigma", "-tau") + if(10) + suffixes += pick("-upsilon", "-phi", "-chi", "-psi") + if(11 to INFINITY) + suffixes += "-omega" + prefixes += "Robust " + switch(transmission - symptoms.len) + if(-INFINITY to 2) + prefixes += "Bloodborne " + if(3) + prefixes += list("Mucous ", "Kissing ") + if(4) + prefixes += "Contact " + suffixes += " Flu" + if(5 to INFINITY) + prefixes += "Airborne " + suffixes += " Plague" + switch(severity) + if(-INFINITY to 0) + prefixes += "Altruistic " + if(1 to 2) + prefixes += "Benign " + if(3 to 4) + prefixes += "Malignant " + if(5) + prefixes += "Deadly " + bodies += "Death" + if(6 to INFINITY) + prefixes += "Morbid " + bodies += "Death" + if(diseasesource) + if(ishuman(diseasesource)) + var/mob/living/carbon/human/H = diseasesource + prefixes += pick("[H.name]'s ", "[H.job]'s ", "[H.get_species()]'s ") + bodies += pick("[H.name]", "[H.job]", "[H.get_species()]") + if(H.get_species() == SPECIES_UNATHI || H.get_species() == SPECIES_TAJARAN) + prefixes += list("Vermin ", "Zoo", "Maintenance ") + bodies += list("Rat", "Maint") + if(ismouse(diseasesource) && !istype(diseasesource, /mob/living/simple_mob/animal/passive/mouse/white/virology)) + prefixes += list("Vermin ", "Zoo", "Maintenance ") + bodies += list("Rat", "Maint") + else switch(diseasesource.type) + if(/mob/living/simple_mob/animal/passive/mouse/white/virology) + prefixes += list("Fleming's ", "Standard ") + bodies += list("Freebie") + if(/obj/effect/decal/cleanable/blood, /obj/effect/decal/cleanable/vomit/old) + prefixes += list("Bloody ", "Maintenance ") + bodies += list("Maint") + if(/obj/item/reagent_containers/syringe/old) + prefixes += list("Junkie ", "Maintenance ") + bodies += list("Needle", "Maint") + for(var/datum/symptom/S in symptoms) + if(!S.neutered) + prefixes += S.prefixes + bodies += S.bodies + suffixes += S.suffixes + switch(rand(1, 3)) + if(1) + return "[pick(prefixes)][pick(bodies)]" + if(2) + return "[pick(prefixes)][pick(bodies)][pick(suffixes)]" + if(3) + return "[pick(bodies)][pick(suffixes)]" diff --git a/code/datums/diseases/advance/disease_preset.dm b/code/datums/diseases/advance/disease_preset.dm index 6ee6b15ce3e..6a23220cb6c 100644 --- a/code/datums/diseases/advance/disease_preset.dm +++ b/code/datums/diseases/advance/disease_preset.dm @@ -6,7 +6,7 @@ /datum/disease/advance/random/minor max_symptoms_override = 4 -/datum/disease/advance/random/New(max_symptoms, max_level = 6, min_level = 1, list/guaranteed_symptoms = setsymptom) +/datum/disease/advance/random/New(max_symptoms, max_level = 6, min_level = 1, list/guaranteed_symptoms = setsymptom, var/atom/infected) if(!max_symptoms) max_symptoms = (2 + rand(1, (VIRUS_SYMPTOM_LIMIT - 2))) if(max_symptoms_override) @@ -35,6 +35,11 @@ symptoms += new guaranteed_symptom Finalize() + if(randomname) + var/randname = random_disease_name(infected) + AssignName(randname) + name = randname + /mob/living/carbon/human/proc/give_random_dormant_disease(biohazard = 25, min_symptoms = 2, max_symptoms = 4, min_level = 4, max_level = 9, list/guaranteed_symptoms = list()) . = FALSE var/sickrisk = 1 @@ -53,7 +58,7 @@ if(prob(min(100, (biohazard * sickrisk)))) var/symptom_amt = rand(min_symptoms, max_symptoms) - var/datum/disease/advance/dormant_disease = new /datum/disease/advance/random(symptom_amt, max_level, min_level, guaranteed_symptoms) + var/datum/disease/advance/dormant_disease = new /datum/disease/advance/random(symptom_amt, max_level, min_level, guaranteed_symptoms, infected = src) dormant_disease.virus_modifiers |= DORMANT dormant_disease.spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS dormant_disease.spread_text = "None" diff --git a/code/datums/diseases/advance/symptoms/blobspores.dm b/code/datums/diseases/advance/symptoms/blobspores.dm index 29887dfc1e4..734b372c34c 100644 --- a/code/datums/diseases/advance/symptoms/blobspores.dm +++ b/code/datums/diseases/advance/symptoms/blobspores.dm @@ -37,6 +37,9 @@ BONUS var/strong_blob var/node_blob + prefixes = list("Xeno", "Sporing ") + bodies = list("Blob") + /datum/symptom/blobspores/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 14) diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index eb3b492e494..8ee41282966 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -35,6 +35,9 @@ Bonus "Transmission 8" = "Doubles the damage caused by the symptom." ) + bodies = list("Lung") + suffixes = list(" Tuberculosis") + /datum/symptom/choking/severityset(datum/disease/advance/A) . = ..() if(A.transmission >= 8) diff --git a/code/datums/diseases/advance/symptoms/cockroach.dm b/code/datums/diseases/advance/symptoms/cockroach.dm index da7481a9f0d..5638479eac8 100644 --- a/code/datums/diseases/advance/symptoms/cockroach.dm +++ b/code/datums/diseases/advance/symptoms/cockroach.dm @@ -17,6 +17,9 @@ "Transmission 8" = "When the host dies, more roaches spawn." ) + prefixes = list("Blatto") + bodies = list("Roach") + /datum/symptom/cockroach/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index 1ba1d0ad74e..29911dd7b27 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -33,6 +33,9 @@ Bonus "Stealth 4" = "The symptom remains hidden until active." ) + prefixes = list("Dizzy ") + bodies = list("Ditz") + /datum/symptom/confusion/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 6) diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index e0ef528eefe..2625530dab3 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -38,6 +38,8 @@ BONUS "Transmission 11" = "The hosts coughing will occasionally spread the virus." ) + bodies = list("Cough") + /datum/symptom/cough/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 3) diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index d32bef5f04a..cd4d7bd59da 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -33,6 +33,9 @@ Bonus "Stealth 4" = "The symptom remains hidden until active." ) + prefixes = list("Aural ") + bodies = list("Ear") + /datum/symptom/deafness/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 9) diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index bd3698eb54b..57554fefe33 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -35,12 +35,17 @@ Bonus "Resistance 10" = "Further increases fever intensity." ) + bodies = list("Fever") + suffixes = list(" Fever") + /datum/symptom/fever/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 5) severity += 1 + prefixes = list("Desert") if(A.resistance >= 10) severity += 1 + prefixes = list("Volcanic") /datum/symptom/fever/Start(datum/disease/advance/A) if(!..()) diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm index 0896422a90f..037e6cdec12 100644 --- a/code/datums/diseases/advance/symptoms/fire.dm +++ b/code/datums/diseases/advance/symptoms/fire.dm @@ -38,6 +38,10 @@ Bonus "Stealth 4" = "The symptom remains hidden until active." ) + prefixes = list("Burning ") + bodies = list("Combustion") + suffixes = list(" Combustion") + /datum/symptom/fire/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 2e181a7bafb..919102b57de 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -36,6 +36,9 @@ Bonus "Transmission 8" = "The host will bleed far more violently, loosing even more blood, and spraying infected blood everywhere." ) + prefixes = list("Bloody ", "Hemo") + bodies = list("Hemophilia") + /datum/symptom/flesh_eating/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/flip.dm b/code/datums/diseases/advance/symptoms/flip.dm index 48c01bc1f47..04ab992e2c3 100644 --- a/code/datums/diseases/advance/symptoms/flip.dm +++ b/code/datums/diseases/advance/symptoms/flip.dm @@ -27,6 +27,9 @@ BONUS level = 1 severity = 0 + prefixes = list("Acrobat's ", "Flippin' ") + bodies = list("Flip") + /datum/symptom/flip/Activate(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm index fc41c3207c8..8df7cab0f9a 100644 --- a/code/datums/diseases/advance/symptoms/hair.dm +++ b/code/datums/diseases/advance/symptoms/hair.dm @@ -28,6 +28,8 @@ BONUS symptom_delay_min = 45 symptom_delay_max = 90 + bodies = list("Bald", "Scalp") + /datum/symptom/shedding/Activate(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index 4db05f1878d..be1cb384730 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -30,6 +30,9 @@ Bonus var/fake_healthy = FALSE + prefixes = list("Narcotic ", "Narco", "Psycho-") + suffixes = list(" Psychosis") + threshold_descs = list( "Stage Speed 7" = "Increases the amount of hallucinations.", "Stealth 2" = "The virus mimics positive symptoms" diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index 94cc4bccc2d..5ed93588f32 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -34,6 +34,8 @@ BONUS "Stealth 4" = "Reduces headache frequency until later stages." ) + bodies = list("Skull", "Migraine") + /datum/symptom/headache/severityset(datum/disease/advance/A) . = ..() if(A.stage_rate >= 6) diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 78f23343f30..72ee41c65ea 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -58,6 +58,8 @@ "Stage Speed 6" = "Consumed chemicals nourish the host." ) + prefixes = list("Toxo") + /datum/symptom/heal/chem/Start(datum/disease/advance/A) if(!..()) return @@ -98,6 +100,9 @@ "Stage Speed 12" = "The disease heals brute damage incredibly fast, but deteriorates cell health and causes tumors to become more advanced. The disease will also regenerate lost limbs." ) + prefixes = list("Blood ", "Meat ", "Flesh ") + bodies = list("Giant") + /datum/symptom/growth/Start(datum/disease/advance/A) if(!..()) return @@ -165,6 +170,9 @@ "Stage Speed 10" = "Chemical metabolization is tripled instead of doubled." ) + prefixes = list("Metabolic ", "Junkie's ", "Chemical ") + bodies = list("Hunger") + /datum/symptom/heal/metabolism/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index 5eeada1d924..058cfb77f29 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -34,6 +34,9 @@ BONUS "Stage Speed 7" = "The host will scratch itself when itchin, causing superficial damage." ) + prefixes = list("Irritant ") + bodies = list("Itch") + /datum/symptom/itching/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/language.dm b/code/datums/diseases/advance/symptoms/language.dm index 7d3f95e83af..0bae1cd95ed 100644 --- a/code/datums/diseases/advance/symptoms/language.dm +++ b/code/datums/diseases/advance/symptoms/language.dm @@ -33,6 +33,9 @@ Bonus "Resistance 5" = "The host might end up speaking a completely made up language." ) + prefixes = list("Babel's ", "Tongue-Twisting ") + bodies = list("Tongue", "Language") + /datum/symptom/language/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/light.dm b/code/datums/diseases/advance/symptoms/light.dm index 0d3e5b697d3..71cb680b699 100644 --- a/code/datums/diseases/advance/symptoms/light.dm +++ b/code/datums/diseases/advance/symptoms/light.dm @@ -12,6 +12,9 @@ "Stealth 3" = "The virus causes a wider disparity between light and dark." ) + prefixes = list("Photo", "Light ") + bodies = list("Cramp") + /datum/symptom/light/severityset(datum/disease/advance/A) . = ..() if(A.stealth >= 3) diff --git a/code/datums/diseases/advance/symptoms/macrophage.dm b/code/datums/diseases/advance/symptoms/macrophage.dm index f5ea92b790f..80cd6426ee6 100644 --- a/code/datums/diseases/advance/symptoms/macrophage.dm +++ b/code/datums/diseases/advance/symptoms/macrophage.dm @@ -38,6 +38,9 @@ BONUS "Transmission 12" = "Phages will carry all diseases within the host, instead of only containing their own." ) + prefixes = list("Ambulant ", "Macro") + bodies = list("Phage") + /datum/symptom/macrophage/severityset(datum/disease/advance/A) . = ..() if(A.transmission >= 10) diff --git a/code/datums/diseases/advance/symptoms/mlem.dm b/code/datums/diseases/advance/symptoms/mlem.dm index ebd3c79eb4a..80f8836d4df 100644 --- a/code/datums/diseases/advance/symptoms/mlem.dm +++ b/code/datums/diseases/advance/symptoms/mlem.dm @@ -32,6 +32,9 @@ BONUS "Transmission 8" = "The host will spread the virus through saliva when mlemming." ) + prefixes = list("Mlemington's ", "Licking-") + bodies = list("Mlem", "Lick") + /datum/symptom/mlem/severityset(datum/disease/advance/A) . = ..() if(A.transmission >= 8) diff --git a/code/datums/diseases/advance/symptoms/radiation.dm b/code/datums/diseases/advance/symptoms/radiation.dm index 0c926c1749d..9b4a44ca015 100644 --- a/code/datums/diseases/advance/symptoms/radiation.dm +++ b/code/datums/diseases/advance/symptoms/radiation.dm @@ -13,6 +13,9 @@ "Speed 8" = "Host takes radiation damage faster." ) + prefixes = list("Gamma ") + bodies = list("Radiation") + /datum/symptom/radiation/severityset(datum/disease/advance/A) . = ..() if(A.stage_rate >= 8) @@ -65,6 +68,8 @@ "Resistance 12" = "The disease also kills off genetically damaged cells, coverting Genetic damage to Burn damage, an inefficient rate." ) + suffixes = list(" Aptosis") + /datum/symptom/radconversion/severityset(datum/disease/advance/A) . = ..() if(A.stage_rate >= 6) diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm index ec556abef70..14f6ce18ee6 100644 --- a/code/datums/diseases/advance/symptoms/shivering.dm +++ b/code/datums/diseases/advance/symptoms/shivering.dm @@ -34,6 +34,9 @@ Bonus "Stage Speed 10" = "Further increases cooling speed." ) + bodies = list("Shiver") + suffixes = list(" Shivers") + /datum/symptom/shivering/severityset(datum/disease/advance/A) . = ..() if(A.stage_rate >= 5) diff --git a/code/datums/diseases/advance/symptoms/size.dm b/code/datums/diseases/advance/symptoms/size.dm index b01336d9997..19c0c8bd574 100644 --- a/code/datums/diseases/advance/symptoms/size.dm +++ b/code/datums/diseases/advance/symptoms/size.dm @@ -43,7 +43,11 @@ BONUS min_size = RESIZE_NORMAL max_size = RESIZE_MAXIMUM + prefixes = list("Growing ", "Gigantic ") + /datum/symptom/size/shrink name = "Dwindling Malady" min_size = RESIZE_MINIMUM max_size = RESIZE_NORMAL + + prefixes = list("Shrinking ", "Micro-") diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index 2ab63957c97..1d94f0d6464 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -35,6 +35,9 @@ Bonus "Trasmission 12" = "The host may spread the disease through sneezing." ) + prefixes = list("Nasal ") + bodies = list("Cold") + /datum/symptom/sneeze/Start(datum/disease/advance/A) if(!..()) return @@ -86,6 +89,9 @@ Bonus level = 4 severity = 3 + prefixes = list("Nasal ", "Displacing ") + bodies = list("Cold", "Bluespace") + /datum/symptom/bsneeze/Activate(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/spin.dm b/code/datums/diseases/advance/symptoms/spin.dm index 95ef7569b68..319929009bb 100644 --- a/code/datums/diseases/advance/symptoms/spin.dm +++ b/code/datums/diseases/advance/symptoms/spin.dm @@ -31,6 +31,9 @@ BONUS var/bigspin = FALSE + prefixes = list("Spinning ", "Rotatory ") + bodies = list("Rotato") + /datum/symptom/spyndrome/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/stimulant.dm b/code/datums/diseases/advance/symptoms/stimulant.dm index 9378221e191..08be2e05b77 100644 --- a/code/datums/diseases/advance/symptoms/stimulant.dm +++ b/code/datums/diseases/advance/symptoms/stimulant.dm @@ -34,12 +34,17 @@ Bonus "Stage 8" = "The virus causes extreme nervousness and paranoia, resulting in occasional hallucinations, and extreme restlessness, but great overall energy." ) + prefixes = list("Gray ", "Amped ", "Nervous ") + bodies = list("Hyper") + /datum/symptom/stimulant/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 8) severity -= 1 if(A.stage_rate >= 8) severity -= 1 + prefixes = list("Gray ", "Amped ", "Paranoid ") + suffixes = list(" Madness", " Insanity") /datum/symptom/stimulant/Start(datum/disease/advance/A) if(!..()) diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index c7322cbb31b..33442444bd7 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -29,6 +29,10 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) var/neutered = FALSE var/stopped = FALSE // Used for Viral Suspended Animaton, stops a symptom but doesn't neuter it. + var/list/prefixes = list() + var/list/bodies = list() + var/list/suffixes = list() + /datum/symptom/New() var/list/S = GLOB.list_symptoms for(var/i = 1; i <= length(S); i++) @@ -53,6 +57,9 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) /datum/symptom/proc/severityset(datum/disease/advance/A) severity = initial(severity) + prefixes = initial(prefixes) + bodies = initial(bodies) + suffixes = initial(suffixes) // Called when the advance disease is going to be deleted or when the advance disease stops processing. /datum/symptom/proc/End(datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm index d8ad1fb2c37..bbd723f3ee6 100644 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ b/code/datums/diseases/advance/symptoms/viral.dm @@ -23,6 +23,8 @@ BONUS level = 4 severity = 0 + prefixes = list("Chronic ") + /* ////////////////////////////////////// Viral evolution @@ -47,6 +49,8 @@ BONUS transmission = 3 level = 4 + prefixes = list("Unstable ") + /datum/symptom/viralpower name = "Viral Power Multiplier" desc = "The vrus has more powerful symptoms. May have unpredictable effects." @@ -116,6 +120,8 @@ BONUS var/time_to_cure + prefixes = list("Spontaneous ") + /datum/symptom/viralreverse/Activate(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index 62da7032a88..5dfa8bf23ef 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -35,6 +35,10 @@ Bonus "Stealth 4" = "The symptom remains hidden until active." ) + prefixes = list("Eye ") + bodies = list("Blind") + suffixes = list(" Blindness") + /datum/symptom/visionloss/severityset(datum/disease/advance/A) . = ..() if(A.resistance >= 12) diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index 99dc0332bd3..181d2c0b839 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -37,6 +37,10 @@ Bonus "Stealth 4" = "The symptom remans hidden until active." ) + prefixes = list("Digestive ") + bodies = list("Vomit") + suffixes = list(" Emission") + /datum/symptom/vomit/Start(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/advance/symptoms/weakness.dm b/code/datums/diseases/advance/symptoms/weakness.dm index 219eff1481f..3b436c6920d 100644 --- a/code/datums/diseases/advance/symptoms/weakness.dm +++ b/code/datums/diseases/advance/symptoms/weakness.dm @@ -26,6 +26,8 @@ Bonus level = 3 severity = 3 + prefixes = list("Weakening ", "Weak's ") + /datum/symptom/weakness/Activate(datum/disease/advance/A) ..() if(prob(SYMPTOM_ACTIVATION_PROB)) diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm index 88eba6323b9..94ee0b6a696 100644 --- a/code/datums/diseases/advance/symptoms/weight.dm +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -35,6 +35,8 @@ Bonus "Stealth 2" = "The symptom is less noticeable, and does not cause starvation." ) + prefixes = list("Hungry ") + /datum/symptom/weight_loss/severityset(datum/disease/advance/A) . = ..() if(A.stealth >= 2) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm index 6597a0ad375..66b176c91a2 100644 --- a/code/datums/diseases/advance/symptoms/youth.dm +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -11,6 +11,9 @@ symptom_delay_min = 25 SECONDS symptom_delay_max = 50 SECONDS + prefixes = list("Eternal ") + bodies = list("Immortal", "Elixir") + /datum/symptom/youth/Activate(datum/disease/advance/A) if(!..()) return diff --git a/code/datums/diseases/anxiety.dm b/code/datums/diseases/anxiety.dm index 19afe869243..be986e4a5fc 100644 --- a/code/datums/diseases/anxiety.dm +++ b/code/datums/diseases/anxiety.dm @@ -1,5 +1,6 @@ /datum/disease/anxiety name = "Severe Anxiety" + medical_name = "Lepidopteric Hyperemesis" form = "Infection" max_stages = 4 spread_text = "On contact" diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm index 66e467e9614..1e6b329de03 100644 --- a/code/datums/diseases/appendicitis.dm +++ b/code/datums/diseases/appendicitis.dm @@ -1,6 +1,7 @@ /datum/disease/appendicitis form = "Condition" name = "Appendicitis" + medical_name = "Appendicitis" max_stages = 3 spread_text = "Non-contagious" disease_flags = CAN_CARRY|CAN_RESIST|CAN_NOT_POPULATE diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm index 716bd3eac1d..9764e12a24a 100644 --- a/code/datums/diseases/beesease.dm +++ b/code/datums/diseases/beesease.dm @@ -1,5 +1,6 @@ /datum/disease/beesease name = "Beesease" + medical_name = "Apidaemia" form = "Infection" max_stages = 4 spread_text = "On contact" diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index 452b4cb238c..13bc9092091 100644 --- a/code/datums/diseases/brainrot.dm +++ b/code/datums/diseases/brainrot.dm @@ -1,5 +1,6 @@ /datum/disease/brainrot name = "Brainrot" + medical_name = "Encephalonecrosis" max_stages = 4 spread_text = "On contact" spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS | DISEASE_SPREAD_CONTACT @@ -8,7 +9,7 @@ agent = "Cryptococcus Cosmosis" viable_mobtypes = list(/mob/living/carbon/human) cure_chance = 15 - desc = "This disease destroys the braincells, causing brain fever, brain necrosis and general intoxication." + desc = "Destroys the braincells, causing brain fever, brain necrosis and general intoxication." required_organs = list(/obj/item/organ/internal/brain) danger = DISEASE_HARMFUL diff --git a/code/datums/diseases/choreomania.dm b/code/datums/diseases/choreomania.dm index 835b2296800..ad6de995418 100644 --- a/code/datums/diseases/choreomania.dm +++ b/code/datums/diseases/choreomania.dm @@ -1,5 +1,6 @@ /datum/disease/choreomania name = "Choreomania" + medical_name = "Choreatic Hyperkinesia" max_stages = 3 spread_text = "Airborne" spread_flags = DISEASE_SPREAD_AIRBORNE | DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index f88ba69bc22..1f4c3216af7 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -1,5 +1,6 @@ /datum/disease/cold name = "The Cold" + medical_name = "Common Cold" max_stages = 3 spread_text = "Airborne" spread_flags = DISEASE_SPREAD_AIRBORNE diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm index 3f8b49bd772..9be83bc691f 100644 --- a/code/datums/diseases/fake_gbs.dm +++ b/code/datums/diseases/fake_gbs.dm @@ -1,5 +1,6 @@ /datum/disease/fake_gbs name = "GBS" + medical_name = "Neutered Guillain-Barré Syndrome" max_stages = 5 spread_text = "On contact" spread_flags = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS @@ -7,7 +8,7 @@ cures = list(REAGENT_ID_ADRANOL, REAGENT_ID_SULFUR) agent = "Gravitokinetic Bipotential SADS-" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) - desc = "if left untreated death will occur." + desc = "If left untreated death will occur." danger = DISEASE_BIOHAZARD // Mimics real GBS /datum/disease/fake_gbs/stage_act() diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index da6b7033380..5aa39dbe924 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -1,5 +1,6 @@ /datum/disease/flu name = "The Flu" + medical_name = "Influenza" max_stages = 3 spread_text = "Airborne" cure_text = REAGENT_SPACEACILLIN diff --git a/code/datums/diseases/food_poisoning.dm b/code/datums/diseases/food_poisoning.dm index 529a5e07dc3..a3805f8d2e3 100644 --- a/code/datums/diseases/food_poisoning.dm +++ b/code/datums/diseases/food_poisoning.dm @@ -1,5 +1,6 @@ /datum/disease/food_poisoning name = "Food Poisoning" + medical_name = "Gastroenteritis" max_stages = 3 stage_prob = 5 spread_text = "Non-Contagious" diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm index cd97f6fac8f..b024aa919bd 100644 --- a/code/datums/diseases/gbs.dm +++ b/code/datums/diseases/gbs.dm @@ -1,5 +1,7 @@ /datum/disease/gbs name = "GBS" + medical_name = "Guillain-Barré Syndrome" + desc = "If left untreated death will occur." max_stages = 5 spread_text = "On contact" spread_flags = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS @@ -40,6 +42,8 @@ /datum/disease/gbs/curable name = "Non-Contagious GBS" + medical_name = "Non-Contagious Guillain-Barré Syndrome" + desc = "If left untreated death will occur." stage_prob = 5 spread_text = "Non-contagious" spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS diff --git a/code/datums/diseases/lycancoughy.dm b/code/datums/diseases/lycancoughy.dm index 9172db1e119..e18f48378d5 100644 --- a/code/datums/diseases/lycancoughy.dm +++ b/code/datums/diseases/lycancoughy.dm @@ -1,5 +1,6 @@ /datum/disease/lycan name = "Lycancoughy" + medical_name = "Lycanthropic Hyperemesis" form = "Infection" max_stages = 4 spread_text = "On contact" diff --git a/code/datums/diseases/magnitis.dm b/code/datums/diseases/magnitis.dm index e0a80afe1d5..ecb2111664f 100644 --- a/code/datums/diseases/magnitis.dm +++ b/code/datums/diseases/magnitis.dm @@ -1,5 +1,6 @@ /datum/disease/magnitis name = "Magnitis" + medical_name = "Acquired Magnetotaxis Disorder" max_stages = 4 spread_text = "Airbone" cure_text = REAGENT_IRON diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index 3d2e7e63b32..83d286937c2 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -269,7 +269,7 @@ var/global/list/image/splatter_cache=list() //This version should be used for admin spawns and pre-mapped virus vectors (e.g. in PoIs), this version does not dry /obj/effect/decal/cleanable/mucus/mapped/Initialize(mapload) . = ..() - viruses |= new /datum/disease/advance + viruses |= new /datum/disease/advance/random(rand(3, 6), 9, 4, infected = src) /obj/effect/decal/cleanable/mucus/mapped/Destroy() viruses.Cut() diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 6ab7143359a..5391f2d7900 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -79,7 +79,7 @@ if(prob(40)) LAZYINITLIST(rat_diseases) - rat_diseases += new /datum/disease/advance/random(rand(1, 5), 9, 1) + rat_diseases += new /datum/disease/advance/random(rand(1, 5), 9, 1, infected = src) /mob/living/simple_mob/animal/passive/mouse/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE) . = ..() @@ -209,7 +209,7 @@ ..() name = initial(name) desc = initial(desc) - rat_diseases += new /datum/disease/advance/random(2, 2, 1) + rat_diseases += new /datum/disease/advance/random(2, 2, 1, infected = src) /mob/living/simple_mob/animal/passive/mouse/white/virology/Crossed(atom/movable/AM) . = ..() diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index a8f1751db58..2b5f3fe0e4e 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -489,7 +489,7 @@ /obj/item/reagent_containers/syringe/old/Initialize(mapload) . = ..() if(prob(75)) - var/datum/disease/advance/new_disease = new /datum/disease/advance/random(rand(1, 3), rand(7, 9), 2) + var/datum/disease/advance/new_disease = new /datum/disease/advance/random(rand(1, 3), rand(7, 9), 2, infected = src) src.viruses += new_disease #undef SYRINGE_DRAW From 920d741836dd14f1bbbc98c6911b7a2c75d99975 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 18:29:42 +0000 Subject: [PATCH 02/47] Automatic changelog for PR #17882 [ci skip] --- html/changelogs/AutoChangeLog-pr-17882.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17882.yml diff --git a/html/changelogs/AutoChangeLog-pr-17882.yml b/html/changelogs/AutoChangeLog-pr-17882.yml new file mode 100644 index 00000000000..2789259af2e --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17882.yml @@ -0,0 +1,5 @@ +author: "TheCaramelion" +delete-after: True +changes: + - rscadd: "Viruses now spawn with random names, depending on the symptoms, source, and random factors." + - rscadd: "Added medical names to all the common diseases" \ No newline at end of file From f07192feaa234b4e3959b2258927e6dc3b206c71 Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:11:49 +0200 Subject: [PATCH 03/47] tgui core 4.3.1 (#17874) * tgui core 4.2.3 * small panel update * sort it * some minor things * also that * 4.3.0 * . * . * 4.3.1 --- .../vorepanel_vore_subdata.dm | 9 +- code/modules/vore/eating/vorepanel_vr.dm | 4 +- tgui/packages/tgui-bench/package.json | 2 +- tgui/packages/tgui-panel/package.json | 2 +- tgui/packages/tgui-say/package.json | 2 +- .../VorePanelElements/VorePanelEditColor.tsx | 6 +- .../VorePanelEditDropdown.tsx | 6 +- .../VorePanelElements/VorePanelEditNumber.tsx | 4 +- .../VorePanelElements/VorePanelEditSwitch.tsx | 4 +- .../VoreBellySelectionAndCustomization.tsx | 42 ++++- .../VoreSelectedBellyLiquidOptions.tsx | 149 ++++++++++-------- .../VoreUserPreferencesMechanical.tsx | 2 +- .../VoreUserPreferencesSoulcatcher.tsx | 2 +- .../tgui/interfaces/VorePanel/index.tsx | 4 + .../tgui/interfaces/VorePanel/types.ts | 6 + tgui/packages/tgui/package.json | 2 +- tgui/yarn.lock | 58 ++++--- 17 files changed, 197 insertions(+), 107 deletions(-) diff --git a/code/modules/vore/eating/panel_databackend/vorepanel_vore_subdata.dm b/code/modules/vore/eating/panel_databackend/vorepanel_vore_subdata.dm index 0408e115abf..712d50eafc7 100644 --- a/code/modules/vore/eating/panel_databackend/vorepanel_vore_subdata.dm +++ b/code/modules/vore/eating/panel_databackend/vorepanel_vore_subdata.dm @@ -686,7 +686,14 @@ return autotransfer_data /datum/vore_look/proc/compile_liquid_interact_data(obj/belly/selected) - var/list/liq_interacts = list() + var/list/liquid_composition = list() + for(var/datum/reagent/reagent in selected.reagents.reagent_list) + UNTYPED_LIST_ADD(liquid_composition, list("name" = reagent.name, "volume" = reagent.volume)) + + var/list/liq_interacts = list( + "current_reagents" = liquid_composition, + "total_volume" = selected.reagents.total_volume + ) if(selected.show_liquids) var/list/liquid_addon_list = list() for(var/flag_name in selected.reagent_mode_flag_list) diff --git a/code/modules/vore/eating/vorepanel_vr.dm b/code/modules/vore/eating/vorepanel_vr.dm index 224b7cb7036..8fbbd619f8b 100644 --- a/code/modules/vore/eating/vorepanel_vr.dm +++ b/code/modules/vore/eating/vorepanel_vr.dm @@ -115,6 +115,8 @@ "%hot" = GLOB.vore_words_hot, "%snake" = GLOB.vore_words_snake, ) + data["min_belly_name"] = BELLIES_NAME_MIN + data["max_belly_name"] = BELLIES_NAME_MAX return data @@ -245,7 +247,7 @@ if(host.vore_organs.len >= BELLIES_MAX) return FALSE - var/new_name = html_encode(tgui_input_text(ui.user,"New belly's name:","New Belly")) + var/new_name = sanitize(params["val"], BELLIES_NAME_MAX, FALSE, TRUE, FALSE) if(!new_name) return FALSE diff --git a/tgui/packages/tgui-bench/package.json b/tgui/packages/tgui-bench/package.json index 172befcaeeb..9bce3a31533 100644 --- a/tgui/packages/tgui-bench/package.json +++ b/tgui/packages/tgui-bench/package.json @@ -11,7 +11,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "tgui": "workspace:*", - "tgui-core": "^4.0.3" + "tgui-core": "^4.3.1" }, "devDependencies": { "@types/react": "^19.1.0", diff --git a/tgui/packages/tgui-panel/package.json b/tgui/packages/tgui-panel/package.json index 55e41a34e6a..afbe1ad2603 100644 --- a/tgui/packages/tgui-panel/package.json +++ b/tgui/packages/tgui-panel/package.json @@ -8,7 +8,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "tgui": "workspace:*", - "tgui-core": "^4.0.3", + "tgui-core": "^4.3.1", "tgui-dev-server": "workspace:*" }, "devDependencies": { diff --git a/tgui/packages/tgui-say/package.json b/tgui/packages/tgui-say/package.json index ac308cbba5a..020f7e27fc9 100644 --- a/tgui/packages/tgui-say/package.json +++ b/tgui/packages/tgui-say/package.json @@ -7,7 +7,7 @@ "react": "^19.1.0", "react-dom": "^19.1.0", "tgui": "workspace:*", - "tgui-core": "^4.0.3" + "tgui-core": "^4.3.1" }, "devDependencies": { "@types/react": "^19.1.0", diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditColor.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditColor.tsx index 4b10510f53b..6a0eabb9754 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditColor.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditColor.tsx @@ -9,15 +9,15 @@ import { VorePanelEditNumber } from './VorePanelEditNumber'; export const VorePanelEditColor = (props: { /** Switch between Element editing and display */ editMode: boolean; - /** Our backend action on text area blur */ + /** Our backend action on click */ action: string; - /** Our secondary backend action on text area blur */ + /** Our secondary backend action on click */ subAction?: string; /** Our color value sent to byond */ value_of: BooleanLike | string; /** The displayed color of the color box */ back_color: string; - /** A number representing the alpha value for alpha inouts */ + /** A number representing the alpha value for alpha inputs */ alpha?: number; /** Optional label to show before the color box */ name_of?: string; diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditDropdown.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditDropdown.tsx index 1d78dda1cef..3445521cfed 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditDropdown.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditDropdown.tsx @@ -14,11 +14,11 @@ import type { DropdownEntry } from '../types'; export const VorePanelEditDropdown = (props: { /** Switch between Element editing and display */ editMode: boolean; - /** Our backend action on text area blur */ + /** Our backend action on selection */ action: string; - /** Our secondary backend action on text area blur */ + /** Our secondary backend action on selection */ subAction?: string; - /** Our dropdown inouts and actions */ + /** Our dropdown inputs and actions */ options: (string | DropdownEntry)[]; /** The currently shown selection */ entry: string; diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditNumber.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditNumber.tsx index f07bc61d3f7..4fdfb2a7287 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditNumber.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditNumber.tsx @@ -12,9 +12,9 @@ import { round, toFixed } from 'tgui-core/math'; export const VorePanelEditNumber = (props: { /** Switch between Element editing and display */ editMode: boolean; - /** Our backend action on text area blur */ + /** Our backend action on number change */ action: string; - /** Our secondary backend action on text area blur */ + /** Our secondary backend action on number change */ subAction?: string; /** The current displayed number */ value: number; diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditSwitch.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditSwitch.tsx index 54de874ef61..bbdcf98fb7f 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditSwitch.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelElements/VorePanelEditSwitch.tsx @@ -5,9 +5,9 @@ import { Box, Button, type Floating } from 'tgui-core/components'; export const VorePanelEditSwitch = (props: { /** Switch between Element editing and display */ editMode: boolean; - /** Our backend action on text area blur */ + /** Our backend action on click*/ action: string; - /** Our secondary backend action on text area blur */ + /** Our secondary backend action on click */ subAction?: string; /** Is the button currently active / selected */ active?: boolean; diff --git a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreBellySelectionAndCustomization.tsx b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreBellySelectionAndCustomization.tsx index 7f460e637a5..37539ce6dc9 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreBellySelectionAndCustomization.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VorePanelMainTabs/VoreBellySelectionAndCustomization.tsx @@ -29,6 +29,8 @@ export const VoreBellySelectionAndCustomization = (props: { toggleEditMode: React.Dispatch>; editMode: boolean; persist_edit_mode: BooleanLike; + minBellyName: number; + maxBellyName: number; }) => { const { act } = useBackend(); @@ -43,9 +45,13 @@ export const VoreBellySelectionAndCustomization = (props: { toggleEditMode, editMode, persist_edit_mode, + minBellyName, + maxBellyName, } = props; const [showSearch, setShowSearch] = useState(false); + const [createNewBelly, setCreateNewBelly] = useState(false); + const [currentNewName, setCurrentNewName] = useState(''); const [searchedBellies, setSearchedBellies] = useState(''); const bellySearch = createSearch( @@ -59,6 +65,16 @@ export const VoreBellySelectionAndCustomization = (props: { return { displayText: belly.name, value: belly.ref }; }); + function applyNewBelly(newName: string) { + act('newbelly', { val: newName }); + clearBellyNameInput(); + } + + function clearBellyNameInput() { + setCreateNewBelly(false); + setCurrentNewName(''); + } + return ( @@ -75,9 +91,29 @@ export const VoreBellySelectionAndCustomization = (props: { } > - act('newbelly')}> - New - + setCreateNewBelly(true)}> + {createNewBelly ? ( + { + applyNewBelly(value); + }} + onChange={(value) => setCurrentNewName(value)} + onEscape={() => clearBellyNameInput()} + onBlur={() => clearBellyNameInput()} + /> + ) : ( + <> + New + + + )} act('exportpanel')}> Export diff --git a/tgui/packages/tgui/interfaces/VorePanel/VoreSelectedBellyTabs/VoreSelectedBellyLiquidOptions.tsx b/tgui/packages/tgui/interfaces/VorePanel/VoreSelectedBellyTabs/VoreSelectedBellyLiquidOptions.tsx index 195ae6ebf6d..ed1d3b80449 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VoreSelectedBellyTabs/VoreSelectedBellyLiquidOptions.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VoreSelectedBellyTabs/VoreSelectedBellyLiquidOptions.tsx @@ -1,7 +1,7 @@ import { useBackend } from 'tgui/backend'; -import { Button, LabeledList, Section, Stack } from 'tgui-core/components'; +import { Box, Button, LabeledList, Section, Stack } from 'tgui-core/components'; -import { liquidToTooltip } from '../constants'; +import { liquidToTooltip, reagentToColor } from '../constants'; import type { bellyLiquidData } from '../types'; import { VorePanelEditCheckboxes } from '../VorePanelElements/VorePanelEditCheckboxes'; import { VorePanelEditSwitch } from '../VorePanelElements/VorePanelEditSwitch'; @@ -18,71 +18,94 @@ export const VoreSelectedBellyLiquidOptions = (props: { const { show_liq, liq_interacts } = bellyLiquidData; return ( -
- - - act('liq_set_attribute', { liq_attribute: 'b_liq_purge' }) - } - > - Purge Liquids - - - - - - - } - > - {!!show_liq && ( - - - - - - - - - - - - + + +
+ + + act('liq_set_attribute', { attribute: 'b_liq_purge' }) + } + > + Purge Liquids + - - + - - - )} -
+ } + > + {!!show_liq && ( + + + + + + + + + + + + + + + + + + + + )} +
+
+ +
+ + {liq_interacts.current_reagents + .sort((a, b) => a.volume - b.volume) + .map((reagent) => ( + + + {reagent.volume} u + + + ))} + {!!liq_interacts.current_reagents.length && } + + {liq_interacts.total_volume} u + + +
+
+
); }; diff --git a/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx b/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx index 01538581ae7..27905e89212 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx +++ b/tgui/packages/tgui/interfaces/VorePanel/VoreUserPreferencesTabs/VoreUserPreferencesMechanical.tsx @@ -109,7 +109,7 @@ export const VoreUserPreferencesMechanical = (props: { tooltipPosition="left" /> - + - + { unsaved_changes, vore_words, general_pref_data, + min_belly_name, + max_belly_name, } = data; const [editMode, setEditMode] = useState(!!persist_edit_mode); @@ -187,6 +189,8 @@ export const VorePanel = () => { toggleEditMode={setEditMode} editMode={editMode} persist_edit_mode={persist_edit_mode} + minBellyName={min_belly_name} + maxBellyName={max_belly_name} /> ); tabs[1] = our_bellies && soulcatcher && abilities && ( diff --git a/tgui/packages/tgui/interfaces/VorePanel/types.ts b/tgui/packages/tgui/interfaces/VorePanel/types.ts index ec0c820fc51..5544770d575 100644 --- a/tgui/packages/tgui/interfaces/VorePanel/types.ts +++ b/tgui/packages/tgui/interfaces/VorePanel/types.ts @@ -10,6 +10,8 @@ export type Data = { persist_edit_mode: BooleanLike; host_mobtype: hostMob | null; our_bellies?: bellyData[] | null; + min_belly_name: number; + max_belly_name: number; selected?: selectedData | null; prefs?: prefData | null; soulcatcher?: soulcatcherData | null; @@ -251,6 +253,8 @@ export type autoTransferOption = { autotransfer_blacklist_items: checkBoxEntry[]; }; +type bellyReagent = { name: string; volume: number }; + export type liqInteractData = { liq_reagent_gen: BooleanLike; liq_reagent_type: string; @@ -279,6 +283,8 @@ export type liqInteractData = { max_ingested: number; custom_ingested_color: string; custom_ingested_alpha: number; + total_volume: number; + current_reagents: bellyReagent[]; }; export type prefData = { diff --git a/tgui/packages/tgui/package.json b/tgui/packages/tgui/package.json index 30447e4e417..a9c061be71a 100644 --- a/tgui/packages/tgui/package.json +++ b/tgui/packages/tgui/package.json @@ -12,7 +12,7 @@ "marked": "^4.3.0", "react": "^19.1.0", "react-dom": "^19.1.0", - "tgui-core": "^4.0.3", + "tgui-core": "^4.3.1", "tgui-dev-server": "workspace:*" }, "devDependencies": { diff --git a/tgui/yarn.lock b/tgui/yarn.lock index 992faa3c054..72808be218f 100644 --- a/tgui/yarn.lock +++ b/tgui/yarn.lock @@ -413,6 +413,32 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react-dom@npm:^2.1.3": + version: 2.1.3 + resolution: "@floating-ui/react-dom@npm:2.1.3" + dependencies: + "@floating-ui/dom": "npm:^1.0.0" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/e88750ea2fb352264d52d502d3979f94155ce2c8ab9a50862810d0cfc8c8e49cb6bbde466d668736cb38624d089360ef97451397b647408a0eb2c1870234c19a + languageName: node + linkType: hard + +"@floating-ui/react@npm:^0.27.12": + version: 0.27.12 + resolution: "@floating-ui/react@npm:0.27.12" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.3" + "@floating-ui/utils": "npm:^0.2.9" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 10c0/da453965074bd4ded8e3de97ceb2c0833df8df2ecd9eff5ae4d336413443ea5abde5c9e37b092956901b97e7b47f9138d51d4896fa82da68e77eb0090289bf64 + languageName: node + linkType: hard + "@floating-ui/react@npm:^0.27.5": version: 0.27.7 resolution: "@floating-ui/react@npm:0.27.7" @@ -427,20 +453,6 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react@npm:^0.27.8": - version: 0.27.8 - resolution: "@floating-ui/react@npm:0.27.8" - dependencies: - "@floating-ui/react-dom": "npm:^2.1.2" - "@floating-ui/utils": "npm:^0.2.9" - tabbable: "npm:^6.0.0" - peerDependencies: - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: 10c0/901317ca85ed0b80231edc1ded1f67e685cd736ed52949b3b7ce2fa4bb52635a5676b035996e6d98f675f60891869b312beb1a2572001758d0ee2cce0efb1a77 - languageName: node - linkType: hard - "@floating-ui/utils@npm:^0.2.9": version: 0.2.9 resolution: "@floating-ui/utils@npm:0.2.9" @@ -7911,20 +7923,20 @@ __metadata: react: "npm:^19.1.0" react-dom: "npm:^19.1.0" tgui: "workspace:*" - tgui-core: "npm:^4.0.3" + tgui-core: "npm:^4.3.1" languageName: unknown linkType: soft -"tgui-core@npm:^4.0.3": - version: 4.0.3 - resolution: "tgui-core@npm:4.0.3" +"tgui-core@npm:^4.3.1": + version: 4.3.1 + resolution: "tgui-core@npm:4.3.1" dependencies: - "@floating-ui/react": "npm:^0.27.8" + "@floating-ui/react": "npm:^0.27.12" "@nozbe/microfuzz": "npm:^1.0.0" peerDependencies: react: ^19.1.0 react-dom: ^19.1.0 - checksum: 10c0/01b5a4144931bf89113b0c263db86020fbf46398eb4c982a26781a9565f41fb204e07dd9c57e9bd2f4bd6ada9029d9618560dab47b3ecfaf91ecb76295a9248f + checksum: 10c0/c5eff791c5a9580e349f65c884e24c42b091459b6eefd3707a51862d0b54969c9ac1cecfd556bbab4566dfd6ad3b1e43d2951e2114922b0668f639c6322cb087 languageName: node linkType: hard @@ -7952,7 +7964,7 @@ __metadata: react: "npm:^19.1.0" react-dom: "npm:^19.1.0" tgui: "workspace:*" - tgui-core: "npm:^4.0.3" + tgui-core: "npm:^4.3.1" tgui-dev-server: "workspace:*" languageName: unknown linkType: soft @@ -7967,7 +7979,7 @@ __metadata: react: "npm:^19.1.0" react-dom: "npm:^19.1.0" tgui: "workspace:*" - tgui-core: "npm:^4.0.3" + tgui-core: "npm:^4.3.1" vitest: "npm:^3.1.1" languageName: unknown linkType: soft @@ -8026,7 +8038,7 @@ __metadata: marked: "npm:^4.3.0" react: "npm:^19.1.0" react-dom: "npm:^19.1.0" - tgui-core: "npm:^4.0.3" + tgui-core: "npm:^4.3.1" tgui-dev-server: "workspace:*" vitest: "npm:^3.1.1" languageName: unknown From 5b89bdcd4234d1aa2fafa3389ab6a620ae80d22d Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 00:12:17 +0000 Subject: [PATCH 04/47] Automatic changelog for PR #17874 [ci skip] --- html/changelogs/AutoChangeLog-pr-17874.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17874.yml diff --git a/html/changelogs/AutoChangeLog-pr-17874.yml b/html/changelogs/AutoChangeLog-pr-17874.yml new file mode 100644 index 00000000000..a3f05192c34 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17874.yml @@ -0,0 +1,4 @@ +author: "Kashargul" +delete-after: True +changes: + - code_imp: "tgui-core 4.3.1" \ No newline at end of file From 1ce86b74f4be917cee53d57964e19eac510246ec Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:04:00 +0000 Subject: [PATCH 05/47] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-17874.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17882.yml | 5 ----- html/changelogs/archive/2025-06.yml | 7 +++++++ 3 files changed, 7 insertions(+), 9 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-17874.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17882.yml diff --git a/html/changelogs/AutoChangeLog-pr-17874.yml b/html/changelogs/AutoChangeLog-pr-17874.yml deleted file mode 100644 index a3f05192c34..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17874.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Kashargul" -delete-after: True -changes: - - code_imp: "tgui-core 4.3.1" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17882.yml b/html/changelogs/AutoChangeLog-pr-17882.yml deleted file mode 100644 index 2789259af2e..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17882.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "TheCaramelion" -delete-after: True -changes: - - rscadd: "Viruses now spawn with random names, depending on the symptoms, source, and random factors." - - rscadd: "Added medical names to all the common diseases" \ No newline at end of file diff --git a/html/changelogs/archive/2025-06.yml b/html/changelogs/archive/2025-06.yml index 0f457559d95..7f23b10e020 100644 --- a/html/changelogs/archive/2025-06.yml +++ b/html/changelogs/archive/2025-06.yml @@ -249,3 +249,10 @@ even inside a stomach. Willburd: - code_imp: Corrected CI issue with component hose connectors +2025-06-18: + Kashargul: + - code_imp: tgui-core 4.3.1 + TheCaramelion: + - rscadd: Viruses now spawn with random names, depending on the symptoms, source, + and random factors. + - rscadd: Added medical names to all the common diseases From 2dd61541c1a3710afeb49627d8d4ab0a3b53a7c1 Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:49:07 -0400 Subject: [PATCH 06/47] Merge data fix (#17858) --- code/datums/forensics_crime.dm | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/code/datums/forensics_crime.dm b/code/datums/forensics_crime.dm index 4c89d8fa481..2236c2b98f4 100644 --- a/code/datums/forensics_crime.dm +++ b/code/datums/forensics_crime.dm @@ -82,6 +82,8 @@ /datum/forensics_crime/proc/merge_prints(var/datum/forensics_crime/origin) if(!islist(origin?.fingerprints)) return + if(!fingerprints) + fingerprints = list() if(fingerprints) fingerprints |= origin.fingerprints else @@ -146,6 +148,8 @@ /datum/forensics_crime/proc/merge_hiddenprints(var/datum/forensics_crime/origin) if(!islist(origin?.fingerprintshidden)) return + if(!fingerprintshidden) + fingerprintshidden = list() if(fingerprintshidden) fingerprintshidden |= origin.fingerprintshidden else @@ -201,6 +205,8 @@ /datum/forensics_crime/proc/merge_fibres(var/datum/forensics_crime/origin) if(!islist(origin?.suit_fibres)) return + if(!suit_fibres) + suit_fibres = list() if(suit_fibres) suit_fibres |= origin.suit_fibres else @@ -262,6 +268,8 @@ /datum/forensics_crime/proc/merge_blooddna(var/datum/forensics_crime/origin, var/list/raw_list = null) // Copying from a list, blood on a mob's feet is stored as a list outside of forensics data if(raw_list) + if(!blood_DNA) + blood_DNA = list() if(blood_DNA) blood_DNA |= raw_list else @@ -270,6 +278,8 @@ // Copying from another datums if(!islist(origin?.blood_DNA)) return + if(!blood_DNA) + blood_DNA = list() if(blood_DNA) blood_DNA |= origin.blood_DNA else From 631e480efe2bbd21975d898395ce717c3a35e0f1 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:49:38 +0000 Subject: [PATCH 07/47] Automatic changelog for PR #17858 [ci skip] --- html/changelogs/AutoChangeLog-pr-17858.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17858.yml diff --git a/html/changelogs/AutoChangeLog-pr-17858.yml b/html/changelogs/AutoChangeLog-pr-17858.yml new file mode 100644 index 00000000000..1b987aeafc5 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17858.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - bugfix: "Forensics data merging on objects without data but with data on the merging object, no longer crash on merge" \ No newline at end of file From e48e0800581c06f39cb942004b72e4078e9434e6 Mon Sep 17 00:00:00 2001 From: SatinIsle <98125273+SatinIsle@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:50:46 +0100 Subject: [PATCH 08/47] Fixed janitor garage doors. (#17879) Fixed janitor shutter buttons, made a mistake when setting them up! --- maps/tether/tether-02-surface2.dmm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maps/tether/tether-02-surface2.dmm b/maps/tether/tether-02-surface2.dmm index f64d48d1ef7..e2cb6c6b264 100644 --- a/maps/tether/tether-02-surface2.dmm +++ b/maps/tether/tether-02-surface2.dmm @@ -24602,7 +24602,7 @@ /obj/effect/floor_decal/corner/purple/border, /obj/machinery/button/remote/blast_door{ id = "janitor_garage"; - name = "Desk Shutters"; + name = "Garage Doors"; pixel_x = -4; pixel_y = -26; dir = 1 @@ -26825,7 +26825,7 @@ dir = 10 }, /obj/machinery/button/remote/blast_door{ - id = "janitor_garage"; + id = "janitor_blast"; name = "Desk Shutters"; pixel_x = -22 }, From 0637041f6696ccc7e9b0b0a43fba2fb1ffb427e3 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:51:15 +0000 Subject: [PATCH 09/47] Automatic changelog for PR #17879 [ci skip] --- html/changelogs/AutoChangeLog-pr-17879.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17879.yml diff --git a/html/changelogs/AutoChangeLog-pr-17879.yml b/html/changelogs/AutoChangeLog-pr-17879.yml new file mode 100644 index 00000000000..7952cd971c4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17879.yml @@ -0,0 +1,4 @@ +author: "SatinIsle" +delete-after: True +changes: + - bugfix: "Fixed janitor shutter buttons on the tether." \ No newline at end of file From 195112b1ef577a128fc6806f445d3a1705b06ef4 Mon Sep 17 00:00:00 2001 From: eghughguhhhhhh Date: Wed, 18 Jun 2025 02:52:25 -0300 Subject: [PATCH 10/47] Embedded Examine Info (#17863) * Initial * Formatting tweaks * she be themin on my drop til til im summary:before * Convert multilingual to pref Tested, seems to work. not 200% sure cuz I never use it --- code/__defines/preferences.dm | 16 +++---- code/__defines/span.dm | 3 ++ code/controllers/subsystems/statpanel.dm | 6 ++- code/game/atoms.dm | 3 -- code/modules/client/preferences.dm | 3 -- .../client/preferences/types/game/chat.dm | 22 +++++++++ .../client/preferences_toggle_procs.dm | 35 -------------- code/modules/examine/descriptions/items.dm | 28 ++++++++--- code/modules/examine/examine.dm | 37 ++++++++++++++- code/modules/mob/say.dm | 9 ++-- .../tgui-panel/styles/tgchat/chat-dark.scss | 47 +++++++++++++++++++ .../tgui-panel/styles/tgchat/chat-light.scss | 46 ++++++++++++++++++ .../styles/tgchat/chat-vchatdark.scss | 46 ++++++++++++++++++ .../styles/tgchat/chat-vchatlight.scss | 46 ++++++++++++++++++ .../features/game_preferences/chat.tsx | 27 ++++++++++- 15 files changed, 310 insertions(+), 64 deletions(-) diff --git a/code/__defines/preferences.dm b/code/__defines/preferences.dm index a9a45c079bc..9e6725d17b0 100644 --- a/code/__defines/preferences.dm +++ b/code/__defines/preferences.dm @@ -1,16 +1,14 @@ // Modes for examine text output -#define EXAMINE_MODE_DEFAULT 0 -#define EXAMINE_MODE_INCLUDE_USAGE 1 -#define EXAMINE_MODE_SWITCH_TO_PANEL 2 +#define EXAMINE_MODE_SLIM "Slim" +#define EXAMINE_MODE_VERBOSE "Verbose" +#define EXAMINE_MODE_SWITCH_TO_PANEL "Switch To Panel" -// Should be one higher than the above -#define EXAMINE_MODE_MAX 3 // Modes for parsing multilingual speech -#define MULTILINGUAL_DEFAULT 0 -#define MULTILINGUAL_SPACE 1 -#define MULTILINGUAL_DOUBLE_DELIMITER 2 -#define MULTILINGUAL_OFF 3 +#define MULTILINGUAL_DEFAULT "Default" +#define MULTILINGUAL_SPACE "Space" +#define MULTILINGUAL_DOUBLE_DELIMITER "Double Delimiter" +#define MULTILINGUAL_OFF "Single Language" #define MULTILINGUAL_MODE_MAX 4 diff --git a/code/__defines/span.dm b/code/__defines/span.dm index b0d1dfe329b..fb72e36468d 100644 --- a/code/__defines/span.dm +++ b/code/__defines/span.dm @@ -3,6 +3,9 @@ // Adds a generic box around whatever message you're sending in chat. Really makes things stand out. #define examine_block(str) ("
" + str + "
") +//for the foldout stuff +#define span_details(title, content) ("
"+"" + title + "" + content + "
") + // Filtered both under OOC! #define span_ooc(str) ("" + str + "") #define span_aooc(str) ("" + str + "") diff --git a/code/controllers/subsystems/statpanel.dm b/code/controllers/subsystems/statpanel.dm index 03a213baaaf..6bb5cd7fb64 100644 --- a/code/controllers/subsystems/statpanel.dm +++ b/code/controllers/subsystems/statpanel.dm @@ -170,7 +170,11 @@ SUBSYSTEM_DEF(statpanels) if(description_holders["antag"]) examine_update += span_red(span_bold("[description_holders["antag"]]")) + "
" //Red, malicious antag-related text - target.stat_panel.send_message("update_examine", list("EX" = examine_update, "UPD" = target.prefs.examine_text_mode == EXAMINE_MODE_SWITCH_TO_PANEL)) + var/update_panel = FALSE + if(target.prefs?.read_preference(/datum/preference/choiced/examine_mode) == EXAMINE_MODE_SWITCH_TO_PANEL) + update_panel = TRUE + + target.stat_panel.send_message("update_examine", list("EX" = examine_update, "UPD" = update_panel)) /datum/controller/subsystem/statpanels/proc/set_tickets_tab(client/target) var/list/tickets = list() diff --git a/code/game/atoms.dm b/code/game/atoms.dm index fd3a3d5e53f..dca25e937f8 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -199,9 +199,6 @@ var/examine_text = replacetext(get_examine_desc(), "||", "") var/list/output = list("[icon2html(src,user.client)] That's [f_name] [suffix]", examine_text) - if(user.client?.prefs.examine_text_mode == EXAMINE_MODE_INCLUDE_USAGE) - output += description_info - SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) return output diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index c91bef0635e..a72c66b60fd 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -120,9 +120,6 @@ var/list/preferences_datums = list() var/lastnews // Hash of last seen lobby news content. var/lastlorenews //ID of last seen lore news article. - var/examine_text_mode = 0 // Just examine text, include usage (description_info), switch to examine panel. - var/multilingual_mode = 0 // Default behaviour, delimiter-key-space, delimiter-key-delimiter, off - // THIS IS NOT SAVED // WE JUST HAVE NOWHERE ELSE TO STORE IT var/list/action_button_screen_locs diff --git a/code/modules/client/preferences/types/game/chat.dm b/code/modules/client/preferences/types/game/chat.dm index ddeff834fb0..63f4814d5fd 100644 --- a/code/modules/client/preferences/types/game/chat.dm +++ b/code/modules/client/preferences/types/game/chat.dm @@ -61,3 +61,25 @@ savefile_key = "PAIN_FREQUENCY" default_value = FALSE savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/examine_mode + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_key = "EXAMINE_MODE" + savefile_identifier = PREFERENCE_PLAYER + +/datum/preference/choiced/examine_mode/init_possible_values() + return list(EXAMINE_MODE_SLIM,EXAMINE_MODE_VERBOSE,EXAMINE_MODE_SWITCH_TO_PANEL) + +/datum/preference/choiced/examine_mode/create_default_value() + return EXAMINE_MODE_VERBOSE + +/datum/preference/choiced/multilingual_mode + category = PREFERENCE_CATEGORY_GAME_PREFERENCES + savefile_identifier = PREFERENCE_PLAYER + savefile_key = "MULTI_LANGUAGE_YAP_MODE" + +/datum/preference/choiced/multilingual_mode/init_possible_values() + return list(MULTILINGUAL_DEFAULT,MULTILINGUAL_SPACE,MULTILINGUAL_DOUBLE_DELIMITER, MULTILINGUAL_OFF) + +/datum/preference/choiced/multilingual_mode/create_default_value() + return MULTILINGUAL_DEFAULT diff --git a/code/modules/client/preferences_toggle_procs.dm b/code/modules/client/preferences_toggle_procs.dm index 841b8110dcc..5b0c5e6b20e 100644 --- a/code/modules/client/preferences_toggle_procs.dm +++ b/code/modules/client/preferences_toggle_procs.dm @@ -13,38 +13,3 @@ to_chat(src,"You will [(prefs.be_special & role_flag) ? "now" : "no longer"] be considered for [role] events (where possible).") feedback_add_details("admin_verb","TBeSpecial") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! - -// Not attached to a pref datum because those are strict binary toggles -/client/verb/toggle_examine_mode() - set name = "Toggle Examine Mode" - set category = "Preferences.Game" - set desc = "Toggle the additional behaviour of examining things." - - prefs.examine_text_mode++ - prefs.examine_text_mode %= EXAMINE_MODE_MAX // This cycles through them because if you're already specifically being routed to the examine panel, you probably don't need to have the extra text printed to chat - switch(prefs.examine_text_mode) // ... And I only wanted to add one verb - if(EXAMINE_MODE_DEFAULT) - to_chat(src, span_filter_system("Examining things will only output the base examine text, and you will not be redirected to the examine panel automatically.")) - - if(EXAMINE_MODE_INCLUDE_USAGE) - to_chat(src, span_filter_system("Examining things will also print any extra usage information normally included in the examine panel to the chat.")) - - if(EXAMINE_MODE_SWITCH_TO_PANEL) - to_chat(src, span_filter_system("Examining things will direct you to the examine panel, where you can view extended information about the thing.")) - -/client/verb/toggle_multilingual_mode() - set name = "Toggle Multilingual Mode" - set category = "Preferences.Character" - set desc = "Toggle the behaviour of multilingual speech parsing." - - prefs.multilingual_mode++ - prefs.multilingual_mode %= MULTILINGUAL_MODE_MAX // Cycles through the various options - switch(prefs.multilingual_mode) - if(MULTILINGUAL_DEFAULT) - to_chat(src, span_filter_system("Multilingual parsing will only check for the delimiter-key combination (,0galcom-2tradeband).")) - if(MULTILINGUAL_SPACE) - to_chat(src, span_filter_system("Multilingual parsing will enforce a space after the delimiter-key combination (,0 galcom -2still galcom). The extra space will be consumed by the pattern-matching.")) - if(MULTILINGUAL_DOUBLE_DELIMITER) - to_chat(src, span_filter_system("Multilingual parsing will enforce the a language delimiter after the delimiter-key combination (,0,galcom -2 still galcom). The extra delimiter will be consumed by the pattern-matching.")) - if(MULTILINGUAL_OFF) - to_chat(src, span_filter_system("Multilingual parsing is now disabled. Entire messages will be in the language specified at the start of the message.")) diff --git a/code/modules/examine/descriptions/items.dm b/code/modules/examine/descriptions/items.dm index b6f44cc213c..0ed0581118d 100644 --- a/code/modules/examine/descriptions/items.dm +++ b/code/modules/examine/descriptions/items.dm @@ -76,16 +76,30 @@ return "an average attack speed" /obj/item/get_description_info() - var/weapon_stats = description_info + "\ -
" + var/list/weapon_stats = list() + + if(description_info) + weapon_stats += description_info if(force) - weapon_stats += "\nIf used in melee, it deals [describe_power()] [sharp ? "sharp" : "blunt"] damage, [describe_penetration()], and has [describe_speed()]." + weapon_stats += "If used in melee, it deals [describe_power()] [sharp ? "sharp" : "blunt"] damage, [describe_penetration()], and has [describe_speed()]." if(throwforce) - weapon_stats += "\nIf thrown, it would deal [describe_throwpower()] [sharp ? "sharp" : "blunt"] damage." + weapon_stats += "If thrown, it would deal [describe_throwpower()] [sharp ? "sharp" : "blunt"] damage." if(can_cleave) - weapon_stats += "\nIt is capable of hitting multiple targets with a single swing." + weapon_stats += "It is capable of hitting multiple targets with a single swing." if(reach > 1) - weapon_stats += "\nIt can attack targets up to [reach] tiles away, and can attack over certain objects." + weapon_stats += "It can attack targets up to [reach] tiles away, and can attack over certain objects." - return weapon_stats + if(weapon_stats.len < 1) + return "" + + var/assembled_string = "" + for(var/index in 1 to weapon_stats.len) + var/msg = weapon_stats[index] + if(index != weapon_stats.len) + msg += "\n" + assembled_string += msg + if(msg == description_info) //keeping the formatting as identical as I can + assembled_string += "
" + + return assembled_string diff --git a/code/modules/examine/examine.dm b/code/modules/examine/examine.dm index 9c5ff9042f6..9926a15f609 100644 --- a/code/modules/examine/examine.dm +++ b/code/modules/examine/examine.dm @@ -77,12 +77,46 @@ var/list/results = A.examine(src) if(!results || !results.len) results = list("You were unable to examine that. Tell a developer!") + + results += embedded_info(A) + var/final_string = span_infoplain("[jointext(results, "
")]") - if(ismob(A)) // mob descriptions matter more than others + if(ismob(A) || client?.prefs?.read_preference(/datum/preference/choiced/examine_mode) == EXAMINE_MODE_VERBOSE) // mob descriptions matter more than others, & it looks weird to have dropdowns outside the box. final_string = examine_block(final_string) to_chat(src, final_string) update_examine_panel(A) +/mob/proc/embedded_info(var/atom/A) + . = "" + if(!(client?.prefs?.read_preference(/datum/preference/choiced/examine_mode) == EXAMINE_MODE_VERBOSE)) + return + if(!client?.prefs?.read_preference(/datum/preference/toggle/vchat_enable)) + return //sorry oldchat + + //do pref check here + var/desc_info_temp = A.get_description_info() + if(desc_info_temp) + . += span_details("ℹ️ | Information",desc_info_temp) + var/fluff_info_temp = A.get_description_fluff() + if(fluff_info_temp && fluff_info_temp != "") + var/title = "🪐 | Flavor Information" + if(isliving(A)) //ehhh + var/mob/living/B = A + if(B.flavor_text) + title = "🔍 | Flavor Text" + + . += span_details(title,fluff_info_temp) + var/is_antagish = ((mind && mind.special_role) || isobserver(src)) //ghosts don't have minds + var/antag_info_temp = A.get_description_antag() + if(is_antagish && antag_info_temp) + . += span_details("🏴‍☠️ | Antag Information",antag_info_temp) + var/list/interaction_info = A.get_description_interaction() + if(interaction_info.len > 0) + var/temp = "" + for(var/a in interaction_info) + temp += a + "\n" + . += span_details("🛠️ | Interaction Information",temp) + /mob/proc/update_examine_panel(var/atom/A) if(client) var/is_antag = ((mind && mind.special_role) || isobserver(src)) //ghosts don't have minds @@ -90,6 +124,7 @@ SSstatpanels.set_examine_tab(client) + /mob/verb/mob_examine() set name = "Mob Examine" set desc = "Allows one to examine mobs they can see, even from inside of bellies and objects." diff --git a/code/modules/mob/say.dm b/code/modules/mob/say.dm index 451e738ffda..a40d9ccc5ba 100644 --- a/code/modules/mob/say.dm +++ b/code/modules/mob/say.dm @@ -179,6 +179,7 @@ // The first character in the selection will always be the prefix (if this is a valid language invocation) var/prefix = copytext(selection, 1, 2) var/language_key = copytext(selection, 2, 3) + var/multilingual_mode = client?.prefs?.read_preference(/datum/preference/choiced/multilingual_mode) if(is_language_prefix(prefix)) // Okay, we're definitely now trying to invoke a language (probably) // This "[]" is probably unnecessary but BYOND will runtime if a number is used @@ -187,14 +188,14 @@ L = language_keys[language_key] // MULTILINGUAL_SPACE enforces a space after the language key - if(client && (client.prefs.multilingual_mode == MULTILINGUAL_SPACE) && (text2ascii(copytext(selection, 3, 4)) != 32)) // If we're looking for a space and we don't find one + if(client && (multilingual_mode == MULTILINGUAL_SPACE) && (text2ascii(copytext(selection, 3, 4)) != 32)) // If we're looking for a space and we don't find one continue // MULTILINGUAL_DOUBLE_DELIMITER enforces a delimiter (valid prefix) after the language key - if(client && (client.prefs.multilingual_mode == MULTILINGUAL_DOUBLE_DELIMITER) && !is_language_prefix(copytext(selection, 3, 4))) + if(client && (multilingual_mode == MULTILINGUAL_DOUBLE_DELIMITER) && !is_language_prefix(copytext(selection, 3, 4))) continue - if(client && (client.prefs.multilingual_mode in list(MULTILINGUAL_DEFAULT))) + if(client && (multilingual_mode in list(MULTILINGUAL_DEFAULT))) selection = copytext(selection, 1, 3) // These modes only use two characters, not three // It's kinda silly that we have to check L != null and this isn't done for us by can_speak (it runtimes instead), but w/e @@ -211,7 +212,7 @@ prefixes[++prefixes.len] = list(get_default_language(), i, i) // If multilingualism is disabled, then after the first pass we're guaranteed to have either found a language key at the start, or else there isn't one and we're using the default for the whole message - if(client && (client.prefs.multilingual_mode == MULTILINGUAL_OFF)) + if(client && (multilingual_mode == MULTILINGUAL_OFF)) break return prefixes diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss index 73386ab9b45..d29fa79546c 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-dark.scss @@ -1486,3 +1486,50 @@ $border-width-px: $border-width * 1px; .nif { color: hsla(180, 100%, 50%, 0.801); } + +details { + width: 80%; + background: hsl(220, 5%, 11%); + margin-bottom: 0.5rem; + box-shadow: 0 0.1rem 1rem -0.5rem rgba(0, 0, 0, 0.4); + border-radius: 5px; + overflow: hidden; + border-color: #343b3d; + border: 1px solid hsl(214, 38%, 74%); +} + +summary { + padding: 1rem; + display: block; + + background: hsl(220, 5%, 11%); + padding-left: 2.2rem; + position: relative; + cursor: pointer; + user-select: none; +} + +summary:before { + content: ''; + border-width: 0.4rem; + border-style: solid; + border-color: transparent transparent transparent hsl(214, 38%, 74%); + position: absolute; + top: 1.3rem; + left: 1rem; + transform: rotate(0); + transform-origin: 0.2rem 50%; + transition: 0.25s transform ease; +} +details[open] > summary:before { + transform: rotate(90deg); +} + +details summary::-webkit-details-marker { + display: none; +} + +details > ul { + padding-bottom: 1rem; + margin-bottom: 0; +} diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss index 0da8dcfbebb..43d3676148d 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-light.scss @@ -1508,3 +1508,49 @@ $border-width-px: $border-width * 1px; .nif { color: hsl(180, 100%, 27%); } + +details { + width: 80%; + background: hsl(202, 44%, 96%); + margin-bottom: 0.5rem; + box-shadow: 0 0.1rem 1rem -0.5rem rgba(0, 0, 0, 0.4); + border-radius: 5px; + overflow: hidden; + border-color: #343b3d; + border: 1px solid hsl(215, 39%, 11%); +} + +summary { + padding: 1rem; + display: block; + background: hsl(202, 44%, 96%); + padding-left: 2.2rem; + position: relative; + cursor: pointer; + user-select: none; +} + +summary:before { + content: ''; + border-width: 0.4rem; + border-style: solid; + border-color: transparent transparent transparent hsl(215, 39%, 11%); + position: absolute; + top: 1.3rem; + left: 1rem; + transform: rotate(0); + transform-origin: 0.2rem 50%; + transition: 0.25s transform ease; +} +details[open] > summary:before { + transform: rotate(90deg); +} + +details summary::-webkit-details-marker { + display: none; +} + +details > ul { + padding-bottom: 1rem; + margin-bottom: 0; +} diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss index 674d8013ff0..d131f209f1c 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatdark.scss @@ -1483,3 +1483,49 @@ $border-width-px: $border-width * 1px; background-color: inherit; color: inherit; } + +details { + width: 80%; + background: hsl(220, 5%, 11%); + margin-bottom: 0.5rem; + box-shadow: 0 0.1rem 1rem -0.5rem rgba(0, 0, 0, 0.4); + border-radius: 5px; + overflow: hidden; + border-color: #343b3d; + border: 1px solid hsl(214, 38%, 74%); +} + +summary { + padding: 1rem; + display: block; + background: hsl(220, 5%, 11%); + padding-left: 2.2rem; + position: relative; + cursor: pointer; + user-select: none; +} + +summary:before { + content: ''; + border-width: 0.4rem; + border-style: solid; + border-color: transparent transparent transparent hsl(214, 38%, 74%); + position: absolute; + top: 1.3rem; + left: 1rem; + transform: rotate(0); + transform-origin: 0.2rem 50%; + transition: 0.25s transform ease; +} +details[open] > summary:before { + transform: rotate(90deg); +} + +details summary::-webkit-details-marker { + display: none; +} + +details > ul { + padding-bottom: 1rem; + margin-bottom: 0; +} diff --git a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss index 340c25d2649..0f901de4bb9 100644 --- a/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss +++ b/tgui/packages/tgui-panel/styles/tgchat/chat-vchatlight.scss @@ -1502,3 +1502,49 @@ $border-width-px: $border-width * 1px; background-color: inherit; color: inherit; } + +details { + width: 80%; + background: hsl(202, 44%, 96%); + margin-bottom: 0.5rem; + box-shadow: 0 0.1rem 1rem -0.5rem rgba(0, 0, 0, 0.4); + border-radius: 5px; + overflow: hidden; + border-color: #343b3d; + border: 1px solid hsl(215, 39%, 11%); +} + +summary { + padding: 1rem; + display: block; + background: hsl(202, 44%, 96%); + padding-left: 2.2rem; + position: relative; + cursor: pointer; + user-select: none; +} + +summary:before { + content: ''; + border-width: 0.4rem; + border-style: solid; + border-color: transparent transparent transparent hsl(215, 39%, 11%); + position: absolute; + top: 1.3rem; + left: 1rem; + transform: rotate(0); + transform-origin: 0.2rem 50%; + transition: 0.25s transform ease; +} +details[open] > summary:before { + transform: rotate(90deg); +} + +details summary::-webkit-details-marker { + display: none; +} + +details > ul { + padding-bottom: 1rem; + margin-bottom: 0; +} diff --git a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx index e7af497841e..7e5bb62ae78 100644 --- a/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx +++ b/tgui/packages/tgui/interfaces/PreferencesMenu/preferences/features/game_preferences/chat.tsx @@ -1,4 +1,9 @@ -import { CheckboxInput, type FeatureToggle } from '../base'; +import { + CheckboxInput, + type FeatureChoiced, + type FeatureToggle, +} from '../base'; +import { FeatureDropdownInput } from '../dropdowns'; export const CHAT_SHOWICONS: FeatureToggle = { name: 'Chat Tags', @@ -71,3 +76,23 @@ export const PAIN_FREQUENCY: FeatureToggle = { 'When enabled, reduces the amount of pain messages for minor wounds that you see.', component: CheckboxInput, }; + +export const EXAMINE_MODE: FeatureChoiced = { + name: 'Examine Mode', + category: 'CHAT', + description: + 'Choose how you want to examine items. "Verbose" will include all information found in the examine panel as foldable groups, "Switch To Panel" will switch you to the examine panel upon examining, and "Slim" will do neither. ', + component: FeatureDropdownInput, +}; + +export const MULTI_LANGUAGE_YAP_MODE: FeatureChoiced = { + name: 'Multilingual Speech Parsing Mode', + category: 'CHAT', + description: ` + Default: Multilingual parsing will only check for the delimiter-key combination (e.g., ,0galcom-2tradeband).\n + Space: Multilingual parsing will enforce a space after the delimiter-key combination (,0 galcom -2still galcom). The extra space will be consumed by the pattern-matching.\n + Double Delimiter: Multilingual parsing will enforce the language delimiter after the delimiter-key combination (,0,galcom -2 still galcom). The extra delimiter will be consumed by the pattern-matching.\n + Off: Multilingual parsing is now disabled. Entire messages will be in the language specified at the start of the message. + `, + component: FeatureDropdownInput, +}; From 9af610edd2de486939f50ecfac866e6576c2eb84 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:53:25 +0000 Subject: [PATCH 11/47] Automatic changelog for PR #17863 [ci skip] --- html/changelogs/AutoChangeLog-pr-17863.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17863.yml diff --git a/html/changelogs/AutoChangeLog-pr-17863.yml b/html/changelogs/AutoChangeLog-pr-17863.yml new file mode 100644 index 00000000000..4a0a995cd83 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17863.yml @@ -0,0 +1,6 @@ +author: "TeleTubby2" +delete-after: True +changes: + - rscadd: "Examines now embed extra information that used to be examine tab exclusive. This is a preference, so can be configured to remain as the old behavior." + - rscdel: "Removed the old verb for setting examine mode" + - refactor: "Examine Mode is now a TG preference" \ No newline at end of file From 686105efb53a03e83551689b8baed905480fb5ac Mon Sep 17 00:00:00 2001 From: eghughguhhhhhh Date: Wed, 18 Jun 2025 02:57:35 -0300 Subject: [PATCH 12/47] Makes the Voodoo doll scrungle people, various recycler fixes. (#17635) * typo fixes * ??? recommit??? * fsdfsd * rewrite pt1 * Displacement Rewrite * Resolve consent check going to wrong person, Resolve overflow issue * Few fixes. * Fix hat, ear rendering * fsdfsd * Fix newtails * fsdfsd --- .../maint_recycler/code/maint_recycler.dm | 27 +-- .../maint_recycler/code/maint_vendor.dm | 7 +- .../vendor_datums/entries/hangout_entries.dm | 10 + .../vendor_datums/entries/misc_entries.dm | 1 + .../code/vendor_datums/entries/scene_tools.dm | 4 +- .../vendor_datums/entries/weapons_and_arms.dm | 12 -- .../displacement_map_generation.dm | 19 ++ .../remote_scene_tools.dm | 148 +++++++------- .../remote_scene_tools/voodoo.dm | 183 ++++++++++++++++++ .../icons/goodies/remote_scene_tools.dmi | Bin 4301 -> 4983 bytes .../maint_recycler/icons/vendor_ads.dmi | Bin 235 -> 0 bytes vorestation.dme | 4 +- 12 files changed, 308 insertions(+), 107 deletions(-) create mode 100644 code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/displacement_map_generation.dm rename code/modules/maint_recycler/code/vendor_goodies/{ => remote_scene_tools}/remote_scene_tools.dm (62%) create mode 100644 code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/voodoo.dm delete mode 100644 code/modules/maint_recycler/icons/vendor_ads.dmi diff --git a/code/modules/maint_recycler/code/maint_recycler.dm b/code/modules/maint_recycler/code/maint_recycler.dm index 6dc88826d5c..b15b371eb7f 100644 --- a/code/modules/maint_recycler/code/maint_recycler.dm +++ b/code/modules/maint_recycler/code/maint_recycler.dm @@ -4,7 +4,7 @@ /obj/machinery/maint_recycler //fresh outta 2288 baby - name = "Decrepit Machine" + name = "\improper Decrepit Machine" //icon etc are self contained because it's dumb that they not to begin with. //it's not a MODULE if everything required for it to work is thrown everywhere. grr. @@ -196,7 +196,7 @@ if(!door_open) if(O.has_tool_quality(TOOL_CROWBAR)) if(stat & (BROKEN|NOPOWER)) - to_chat(user, span_warning("you lever \The [src]'s door open!")) + to_chat(user, span_warning("you lever \the [src]'s door open!")) open_door(user) eject_item(user) //also kick out the item they're looking for playsound(src,"sound/machines/door/airlock_creaking.ogg",4,FALSE) @@ -204,11 +204,11 @@ else to_chat(user, span_warning("\The [src]'s door won't budge!")) else - to_chat(user, span_warning("\The [src] doesn't have it's door open!")) + to_chat(user, span_warning("\The [src] doesn't have its door open!")) return if(inserted_item) - to_chat(user, span_warning("\The [src] already has [inserted_item] in it's recycling compartment!")) + to_chat(user, span_warning("\The [src] already has [inserted_item] in its recycling compartment!")) return switch(get_item_whitelist(O)) @@ -220,7 +220,7 @@ evil_act(O,user) return - to_chat(user, span_notice("you put \The [O] into \The [src]'s processing compartment!")) + to_chat(user, span_notice("You put \the [O] into \the [src]'s processing compartment!")) if(istype(O,/obj/item/holder)) var/obj/item/holder/h = O var/mob/m = h.held_mob @@ -250,7 +250,7 @@ if(prob(75)) if(get_item_whitelist(AM) == RECYCLER_ALLOWED) if(inserted_item == null) - visible_message("\The [AM] lands in \the [src].") + visible_message("\The [AM] lands in \the [src].",runemessage = "swish") AM.forceMove(src) inserted_item = AM update_icon() @@ -264,7 +264,7 @@ /obj/machinery/maint_recycler/proc/deny_act(var/obj/item/O,var/mob/user) set_screen_state("screen_deny",10) - to_chat(user, span_warning("\The [src] rejects \The [O]!")) + to_chat(user, span_warning("\The [src] rejects \the [O]!")) if(prob(99)) playsound(src, 'code/modules/maint_recycler/sfx/generaldeny.ogg', 75, 1) return @@ -282,7 +282,7 @@ if(!isRepeat) GLOB.global_announcer.autosay("HARM ALERT: Crewmember [user] recorded displaying murderous tendencies towards innocent creatures in [get_area(src)]. Please schedule psych evaluation and ensure the wellbeing of recorded victim: [h.held_mob]", "[src]", "Security") audible_message("[src] states, \"AMORAL INTENT DETECTED.\" ", "\The [src]'s screen briefly flashes to an angry red graphic!" , runemessage = ">:(") else - if(!isRepeat) GLOB.global_announcer.autosay("PROPERTY DESTRUCTION ALERT: Crewmember [user] has been recorded attempting to destroy high priority station equipment in [get_area(src)]. Please ensure the integrity of \The [O].", "[src]", "Security") + if(!isRepeat) GLOB.global_announcer.autosay("PROPERTY DESTRUCTION ALERT: Crewmember [user] has been recorded attempting to destroy high priority station equipment in [get_area(src)]. Please ensure the integrity of \the [O].", "[src]", "Security") audible_message("[src] states, \"CRIMINAL INTENT DETECTED.\" ", "\The [src]'s screen briefly flashes to an angry red graphic!" , runemessage = ">:(") playsound(src,pick(angry_sounds),80) @@ -344,7 +344,7 @@ /obj/machinery/maint_recycler/proc/eject_item_act(var/mob/user) inserted_item.forceMove(get_turf(src)) - visible_message(span_warning("[src] ejects \The [inserted_item] from it's recycling chamber!")) + visible_message(span_warning("[src] ejects \the [inserted_item] from its recycling chamber!")) inserted_item.throw_at(get_step(src,SOUTH),5,1,src) inserted_item = null; update_icon() @@ -389,7 +389,7 @@ //todo, assoc list for icon states from type if(istype(inserted_item,/mob/living/simple_mob/animal/passive/mouse)) item_overlay.icon = src.icon - item_overlay.icon_state = "hepme" //the creature deserves it's horrible end + item_overlay.icon_state = "hepme" //the creature deserves its horrible end item_overlay.vis_flags = VIS_INHERIT_ID //gotta reapply item_overlay.appearance_flags = KEEP_TOGETHER | LONG_GLIDE | PASS_MOUSE @@ -474,6 +474,7 @@ TGUI PROCS /obj/machinery/maint_recycler/tgui_close(mob/user) . = ..() + if(LAZYLEN(open_tguis) > 0) return set_on_state(FALSE) @@ -484,7 +485,7 @@ UTILITY PROCS /obj/machinery/maint_recycler/proc/credit_user(var/mob/user, var/amount) if(!user || !user.client || !user.client.prefs) return var/currentValue = user.client?.prefs?.read_preference(/datum/preference/numeric/recycler_points) - user.client?.prefs?.write_preference_by_type(/datum/preference/numeric/recycler_points, currentValue + amount) + user.client?.prefs?.write_preference_by_type(/datum/preference/numeric/recycler_points, min(currentValue + amount,999)) if(granted_points[user.key]) granted_points[user.key] += amount else @@ -496,13 +497,13 @@ UTILITY PROCS /obj/machinery/maint_recycler/proc/canRecycle(var/mob/user, var/potentialValue) if(!user.key) return FALSE - if(granted_points[user.key]+potentialValue > point_cap) return FALSE + if(granted_points[user.key]+potentialValue > point_cap) return FALSE return TRUE /obj/machinery/maint_recycler/proc/mob_consent_check(var/mob/probable_victim) if(probable_victim.key) if(probable_victim.client) //sanity check to make sure they are alright with getting squished to death - return (tgui_alert(usr,"Do you want to be put in \The [src]? Industrial machinery is pretty damn deadly, you'll probably die. to death. A fine paste.", "Welcome to the Hydralulic Press Prompt", list("OSHA is for chumps", "what the fuck? get me outta here!")) == "OSHA is for chumps") + return (tgui_alert(probable_victim,"Do you want to be put in \The [src]? Industrial machinery is pretty damn deadly, you'll probably die. to death. A fine paste.", "Welcome to the Hydralulic Press Prompt", list("OSHA is for chumps", "what the fuck? get me outta here!")) == "OSHA is for chumps") else return FALSE //no logged out users else return TRUE //mindless mobs that've never felt the gentle touch of a client are fine diff --git a/code/modules/maint_recycler/code/maint_vendor.dm b/code/modules/maint_recycler/code/maint_vendor.dm index afe268b8ce2..bfd8d1915b1 100644 --- a/code/modules/maint_recycler/code/maint_vendor.dm +++ b/code/modules/maint_recycler/code/maint_vendor.dm @@ -1,5 +1,5 @@ /obj/machinery/maint_vendor - name = "Large Decrepit Machine" + name = "\improper Large Decrepit Machine" desc = "A long since abandoned \"trash 4 cash\" rewards kiosk. Now featuring a state of the art, monochrome holographic tube display!" icon = 'code/modules/maint_recycler/icons/maint_vendor.dmi' icon_state = "default" @@ -117,7 +117,7 @@ playsound(src, 'code/modules/maint_recycler/sfx/ejectgoodies.ogg', 75, 1) used_entry.spawn_with_delay(src); - audible_message("[src] states, \"[used_entry.tagline]\" ", "\The [src]'s screen briefly flashes an $!" , runemessage = "$$$") + audible_message("[src] states, \"[used_entry.tagline]\" ", "\The [src]'s screen briefly flashes a $!" , runemessage = "$$$") if(prob(95)) set_screen_state("screen_cashout",10) else @@ -187,6 +187,7 @@ /obj/machinery/maint_vendor/tgui_close(mob/user) . = ..() + if(LAZYLEN(open_tguis) > 0) return set_on_state(FALSE) /obj/machinery/maint_vendor/tgui_data(mob/user) @@ -202,7 +203,7 @@ "desc" = entry.desc, "ad" = entry.ad_message, "icon" = entry.icon_state, - "index" = i //TODO + "index" = i )) data["items"] = items data["userBalance"] = user_balance(user) diff --git a/code/modules/maint_recycler/code/vendor_datums/entries/hangout_entries.dm b/code/modules/maint_recycler/code/vendor_datums/entries/hangout_entries.dm index c820b3e4fc8..b3d0d7b650f 100644 --- a/code/modules/maint_recycler/code/vendor_datums/entries/hangout_entries.dm +++ b/code/modules/maint_recycler/code/vendor_datums/entries/hangout_entries.dm @@ -21,6 +21,16 @@ object_type_to_spawn = /obj/item/stack/material/wood/hard{amount = 20} +/datum/maint_recycler_vendor_entry/cardboard + vendor_category = MAINTVENDOR_CONSTRUCTION + name = "C.A.R.D.B.O.A.R.D" + ad_message = "5 sheets!" + desc = "Not an acronym (maybe?), we just asked the person in charge of designating stationbounds to make this listing, we have no idea what it means!" + object_type_to_spawn = /obj/item/stack/material/cardboard{amount = 5} + item_cost = 10 //not too pricy + per_person_cap = 1 + per_round_cap = 3 //limited supply! + /datum/maint_recycler_vendor_entry/DIY_Steel vendor_category = MAINTVENDOR_CONSTRUCTION name = "steel!" diff --git a/code/modules/maint_recycler/code/vendor_datums/entries/misc_entries.dm b/code/modules/maint_recycler/code/vendor_datums/entries/misc_entries.dm index 844c3c35162..1071a4c41f5 100644 --- a/code/modules/maint_recycler/code/vendor_datums/entries/misc_entries.dm +++ b/code/modules/maint_recycler/code/vendor_datums/entries/misc_entries.dm @@ -21,6 +21,7 @@ tagline = "I HAVE 8.2 PETABYTES OF MECHHAMMER MODELS!" item_cost = 50 //even discount bin warhammer's comically expensive object_type_to_spawn = /obj/random/fromList/mecha_toys + icon_state = "copyright" /datum/maint_recycler_vendor_entry/burger //packaged 40 years ago. you should regret this. name = "TRASH 4 BURGER REDEMPTION" diff --git a/code/modules/maint_recycler/code/vendor_datums/entries/scene_tools.dm b/code/modules/maint_recycler/code/vendor_datums/entries/scene_tools.dm index 5a9447f48e6..fcdb1ca4771 100644 --- a/code/modules/maint_recycler/code/vendor_datums/entries/scene_tools.dm +++ b/code/modules/maint_recycler/code/vendor_datums/entries/scene_tools.dm @@ -104,7 +104,7 @@ /datum/maint_recycler_vendor_entry/NIF_laws name = "COMM4ND UR N1F!" ad_message = "KNEEL!" - icon_state = "law" + icon_state = "laws" desc = "Unlocks the TRUE potential of your NIF! You set laws, you follow laws! Simple as! (we are not liable for usage on other parties)" object_type_to_spawn = /obj/item/disk/nifsoft/compliance tagline = "You didn't get it from us!" @@ -113,7 +113,7 @@ /datum/maint_recycler_vendor_entry/NIFlet_laws name = "Implnt law?" ad_message = "Niflets seething!" - icon_state = "law" + icon_state = "laws" desc = "Underlings not listening to you? Someone forgetting to do whta you tell them to? No more! this implant WILL make people bend to your will! KNEEL!" object_type_to_spawn = /obj/item/implanter/compliance item_cost = 25 diff --git a/code/modules/maint_recycler/code/vendor_datums/entries/weapons_and_arms.dm b/code/modules/maint_recycler/code/vendor_datums/entries/weapons_and_arms.dm index cb134423efa..68c10c980b0 100644 --- a/code/modules/maint_recycler/code/vendor_datums/entries/weapons_and_arms.dm +++ b/code/modules/maint_recycler/code/vendor_datums/entries/weapons_and_arms.dm @@ -10,18 +10,6 @@ required_access = list(access_security) vendor_category = MAINTVENDOR_WEAPONS -/datum/maint_recycler_vendor_entry/foamforce_ammo //fun! - name = "MUNITIONS FOR GUN" - ad_message = "FOAM FLINGING PAIN!" - desc = "BIG BOX? BIG AMMO. THAT'S RIGHT. WE'LL BIG YOUR AMMO. FOAM DARTS, FOR FOAM DART GUNS - LIMITED EDITION FRAG MONTAGE BUY NOW" - object_type_to_spawn = /obj/item/toy/russian_revolver/trick_revolver - item_cost = 15 //cheap! - tagline = "to war!" - per_person_cap = 1 - per_round_cap = 3 //limited supply! - required_access = list(access_security) - vendor_category = MAINTVENDOR_WEAPONS - /datum/maint_recycler_vendor_entry/foamforce_ammo //fun! name = "MUNITIONS FOR GUN" ad_message = "FOAM FLINGING PAIN!" diff --git a/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/displacement_map_generation.dm b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/displacement_map_generation.dm new file mode 100644 index 00000000000..7eca17d5171 --- /dev/null +++ b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/displacement_map_generation.dm @@ -0,0 +1,19 @@ +//todo, figure out a better place for this +/proc/get_humanoid_displacement_map_image(var/mob/living/carbon/human/target,var/list/discarded_layer_indicies, var/icon/displacement_map, var/outlineWidth = 0, var/render_target = null) + + var/list/to_overlays = target.overlays_standing.Copy() + + for(var/layer_to_kill in discarded_layer_indicies) + to_overlays[layer_to_kill] = null + + var/image/displacement_render_image = image(icon = null, icon_state = "empty", dir = SOUTH) + displacement_render_image.appearance_flags = KEEP_TOGETHER + displacement_render_image.overlays = to_overlays + + displacement_render_image.filters += filter(type = "displace", size = 128, icon=displacement_map) //size is 128 because that's the god given standard. if you're doing something unhinged enough to need a bigger or smaller size, by all means. + if(outlineWidth) + displacement_render_image.filters += filter(type="outline", size = outlineWidth) + if(render_target) + displacement_render_image.render_target = render_target + + return displacement_render_image diff --git a/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools.dm b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/remote_scene_tools.dm similarity index 62% rename from code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools.dm rename to code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/remote_scene_tools.dm index 80140b48f43..4104a92eb2a 100644 --- a/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools.dm +++ b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/remote_scene_tools.dm @@ -1,6 +1,13 @@ -//remote stuff! like uhhhh -// the funny stickers -// the funny voodoo doll +/* +Remote scene tools! These, in effects, serve 2 purposes: + +* Forward emotes from wearer of one to wearer of its partner +* Provide simple feedback - is your partner dead, SSD, did they log out? - some are more explicit. + +why aren't these accessories? + They easily could be! I'm just lazy and don't wanna forward the "see emote" from wearables to all the accessories + +*/ /obj/item/remote_scene_tool @@ -12,11 +19,15 @@ icon_state = "sticker_inactive" name = "Bluespace Sticker" desc = "A stretchable, flexible sticker that induces a quasi-stable 4th dimensional bluespace buzzword rift, enabling moderate levels of touch between the two." - description_info = "These stickers act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other sticker! It's vauge, so use it how you want in RP! Just remember bystander consent!" + description_info = "These stickers act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other sticker! It's vague, so use it how you want in RP! Just remember bystander consent!" slot_flags = (SLOT_OCLOTHING | SLOT_ICLOTHING | SLOT_GLOVES | SLOT_MASK | SLOT_HEAD | SLOT_FEET | SLOT_ID | SLOT_BELT | SLOT_BACK | SLOT_POCKET) - + w_class = ITEMSIZE_SMALL var/mob/worn_mob = null var/last_loc + var/can_summon = TRUE + var/can_replace = TRUE + + var/replacementType = /obj/item/remote_scene_tool /obj/item/remote_scene_tool/proc/link_to(var/obj/item/remote_scene_tool/to_link) //link to a remote scene tool @@ -37,43 +48,43 @@ RegisterSignal(mob, COMSIG_MOB_LOGIN, PROC_REF(worn_mob_logged_in)) RegisterSignal(mob, COMSIG_MOB_LOGOUT, PROC_REF(worn_mob_logged_out)) - transmit_emote(src, span_notice("The [src]\'s has been put on by [mob]!")) - - + transmit_emote(src, span_notice("\The [src] has been put on by [mob]!")) /obj/item/remote_scene_tool/proc/unregister_from_mob(var/mob) if(worn_mob == null) return UnregisterSignal(worn_mob, COMSIG_MOB_LOGIN) UnregisterSignal(worn_mob, COMSIG_MOB_LOGOUT) worn_mob = null - transmit_emote(src, span_warning("The [src]\'s wearer has removed it!")) + transmit_emote(src, span_warning("\The [src]'s wearer has removed it!")) + +//called when the mob wearing this item logs out /obj/item/remote_scene_tool/proc/worn_mob_logged_out() SIGNAL_HANDLER if(!linked) return - transmit_emote(src, span_warning("The [src]\'s wearer has gone SSD!")) + transmit_emote(src, span_warning("\The [src]'s wearer has gone SSD!")) linked?.linked_updated() - //called when the mob wearing this item logs out /obj/item/remote_scene_tool/proc/worn_mob_logged_in() SIGNAL_HANDLER //called when the mob wearing this item logs in if(!linked) return - transmit_emote(src, "The [src]\'s wearer has returned from SSD!") + transmit_emote(src, "\The [src]'s wearer has returned from SSD!") linked?.linked_updated() /obj/item/remote_scene_tool/see_emote(var/mob/M as mob, var/text, var/emote_type) //to_world_log("emote: [text] from [M] to [linked]") - if(M == loc) + if(M == getWearer()) //we're the one doing the emote transmit_emote(src, text,emote_type) /obj/item/remote_scene_tool/proc/transmit_emote(var/mob/M as mob, var/text, var/emote_type) - if(ismob(linked.loc)) - var/mob/m = linked.loc + if(linked == null) return + if(ismob(linked.getWearer())) + var/mob/m = linked.getWearer() if(!m.client) return; to_chat(linked.loc, icon2html(src,m.client) + text) @@ -87,9 +98,10 @@ return FALSE if(linked.linked != src) return FALSE - if(!ismob(linked.loc)) + + var/mob/m = linked.getWearer() + if(!ismob(m)) return FALSE - var/mob/m = linked.loc if(!m.client) //no scene partner? whoopsie! return FALSE @@ -104,18 +116,21 @@ /obj/item/remote_scene_tool/proc/delayed_loc_check() //why is this delayed? because when moving stuff between slots, it considers it in a different spot, and calls the commsig multiple times - so the end //user just gets the shit spammed out of them - if(last_loc == loc) return last_loc = loc linked?.linked_updated() linked_updated() - if(ismob(loc)) - register_to_mob(loc) //handles any caching + var/mob/m = getWearer() + + + if(ismob(m)) + register_to_mob(m) //handles any caching else if(worn_mob) unregister_from_mob(worn_mob) //unregister from the mob if we aren't on it anymore + /obj/item/remote_scene_tool/proc/linked_updated() update_icon() @@ -136,13 +151,18 @@ /obj/item/remote_scene_tool/examine(mob/user) . = ..() - if(linked && ismob(linked.loc)) - . += span_notice("This is linked to [linked.loc]\'s [linked.name].") - var/mob/m = linked.loc - if(!m.client) - . += span_warning("The wearer of \The [src]'s counterpart doesn't appear to be all there!") + var/mob/living/carbon/human/lw = linked?.getWearer() + + if(lw) + . += span_notice("This is linked to [lw]'s [linked.name].") + if(!lw.client || lw.stat == UNCONSCIOUS || lw.stat == DEAD) + . += span_warning("The wearer of \the [src]'s counterpart doesn't appear to be conscious!") + else + . += span_notice("Its counterpart seems to be in \the [get_area(linked).name]") + if(!linked) . += span_warning("This is not linked to anything!") + if(!ismob(linked.loc)) . += span_warning("\The [src]'s counterpart isn't being worn or carried by anyone!") @@ -168,56 +188,32 @@ contents += RST2 calibrate_size() -/obj/item/remote_scene_tool/voodoo_doll - name = "voodoo doll" - desc = "A dubiously-cursed-esq Voodoo doll that mimics whoever is wearing it's matching necklace via built in hardlight projection." - icon = 'code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi' - icon_state = "voodoo_doll_inactive" - icon_root = "voodoo_doll" - description_info = "The Doll acts as a remote scene tool - any sort of emotes or subtles that the wearer does will go DIRECTLY to the necklace! It's vauge, so use it how you want in RP! Just remember bystander consent!" - //this is just a copy of the remote scene tool, but with a different icon - //and a different name +/obj/item/remote_scene_tool/proc/getWearer() + if(ismob(loc)) + return loc + if(istype(loc,/obj/item/clothing)) + if(ismob(loc.loc)) + return loc.loc + return null - var/last_loc_update = null +/obj/item/remote_scene_tool/verb/summon_counterpart() + set name = "Summon Counterpart" + set desc = "Forcibly moves the linked object over to you - or, if it doesn't exist, spawn a new one." + if(can_summon || (linked == null && can_replace)) + if(linked == null) + create_counterpart() + to_chat(usr,span_notice("\The [src] forms a new [linked]!")) + else + var/mob/counterpart = linked.getWearer() + if(counterpart) + counterpart.remove_from_mob(linked,get_turf(src)) + else + linked.forceMove(get_turf(src)) + to_chat(usr,span_notice("\The [linked] materializes in front of you!")) + else + to_chat(usr,span_notice("Nothing seems to happen!")) -/obj/item/remote_scene_tool/voodoo_doll/update_icon() - . = ..() - - last_loc_update = linked.loc - - overlays.Cut() - - dir = SOUTH - src.alpha = 255 - - name = initial(name) - desc = initial(desc) - - if(!linked) - return - if(!ismob(linked.loc)) - return - if(!ismob(loc)) //only show the person's icon if we're holding it. makes "found in the hall" interactions less weird - return - - var/mob/M = linked.loc - if(ismob(M)) - src.alpha = 190 //make it a bit transparent so it doesn't look like a mob holder. - name = "Voodoo doll of " + M.name - desc = "A small, marketable doll that looks suspiciously like [M]... " - overlays |= M.overlays - -/obj/item/remote_scene_tool/voodoo_necklace - name = "Gem-Encruseted Necklace" - desc = "A bitingly cold metal necklace with a swirling gem in the center. haters will say it's not magical" - icon_state = "necklace_inactive" - icon_root = "necklace" - slot_flags = SLOT_MASK - description_info = "The necklace and the associated doll act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other! It's vauge, so use it how you want in RP! Just remember bystander consent!" - -/obj/item/storage/box/remote_scene_tools/voodoo - icon_state = "voodoobox" - name = "Voodoo Supplies Box" - desc = "a horribly dusty box from 'magici-corp' with a ludicrously corny image of a voodoo doll on the front. marketed as part of a DIY magician routine, they stopped making them after someone broke their nose when the doll was dropped." - primary = /obj/item/remote_scene_tool/voodoo_doll - secondary = /obj/item/remote_scene_tool/voodoo_necklace +/obj/item/remote_scene_tool/proc/create_counterpart() + var/obj/item/remote_scene_tool/newrst = new replacementType(get_turf(src)) + link_to(newrst) + newrst.link_to(src) diff --git a/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/voodoo.dm b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/voodoo.dm new file mode 100644 index 00000000000..8a2435dee04 --- /dev/null +++ b/code/modules/maint_recycler/code/vendor_goodies/remote_scene_tools/voodoo.dm @@ -0,0 +1,183 @@ +#define DATA_SCALE "scale" +#define DATA_X_OFFSET "x" +#define DATA_Y_OFFSET "y" +#define DATA_ROTATION "rotation" + +/obj/item/remote_scene_tool/voodoo_doll + name = "voodoo doll" + desc = "A dubiously cursed-esq Voodoo doll that mimics whoever is wearing its matching necklace via built in hardlight projection." + icon = 'code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi' + icon_state = "voodoo_doll_inactive" + icon_root = "voodoo_doll" + description_info = "The Doll acts as a remote scene tool - any sort of emotes or subtles that the wearer does will go DIRECTLY to the necklace! It's vague, so use it how you want in RP! Just remember bystander consent!" + + replacementType = /obj/item/remote_scene_tool/voodoo_necklace + + //this is just a copy of the remote scene tool, but with a different icon + //and a different name + + var/underlayRotation = 45 + //TODO: make this generic so it's easier for other stuff to use. ideally there's a magical thing that just has all the various greebles set up so u can pick and choose which ones are rendered traditionally + var/list/tail_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = -2,DATA_SCALE = 1.6, DATA_ROTATION = 45) + var/list/taur_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = 2, DATA_SCALE = 1.1, DATA_ROTATION = 45) + var/list/wing_data = list( DATA_X_OFFSET = 2, DATA_Y_OFFSET = -2,DATA_SCALE = 1, DATA_ROTATION = 45) + var/list/ear_data = list( DATA_X_OFFSET = 0, DATA_Y_OFFSET = 1, DATA_SCALE = 1.5, DATA_ROTATION = 45) + var/list/hat_data = list( DATA_X_OFFSET = 0, DATA_Y_OFFSET = 3, DATA_SCALE = 1, DATA_ROTATION = 45) + var/outline_size = 1 + + var/list/discarded_layer_indicies = list( + VORE_BELLY_LAYER, //not even gonna try with this lmfao + TAIL_LOWER_LAYER, + TAIL_UPPER_LAYER, + TAIL_UPPER_LAYER_LOW, + TAIL_UPPER_LAYER_HIGH, //drawn manually + FIRE_LAYER, //not relevant + TARGETED_LAYER, //not relevant + MOB_WATER_LAYER, //not relevant + L_HAND_LAYER, //not drawn, looks like ass + R_HAND_LAYER, + EARS_LAYER, //drawn manually + WING_LAYER, + WING_LOWER_LAYER, //drawn manually + HEAD_LAYER, //drawn manually + ) + appearance_flags = KEEP_TOGETHER + var/no_fun_mode = FALSE //if true, we just draw the mob as an overlay + +/obj/item/remote_scene_tool/voodoo_necklace + name = "Gem-Encrusted Necklace" + desc = "A bitingly cold metal necklace with a swirling gem in the center. Haters will say it's not magical" + icon_state = "necklace_inactive" + icon_root = "necklace" + slot_flags = SLOT_MASK + description_info = "The necklace and the associated doll act as remote scene tools - any sort of emotes or subtles that the wearer does will go DIRECTLY to the other! It's vague, so use it how you want in RP! Just remember bystander consent!" + replacementType = /obj/item/remote_scene_tool/voodoo_doll + +/obj/item/storage/box/remote_scene_tools/voodoo + icon_state = "voodoobox" + name = "Voodoo Supplies Box" + desc = "a horribly dusty box from 'magici-corp' with a ludicrously corny image of a voodoo doll on the front." + description_fluff = "originally marketed as part of a DIY magician routine, the company making them went under due to countless lawsuits from people who broke their nose upon the doll being dropped, and tesh filing descrimination suits. they haven't been made in well over a decade." + primary =/obj/item/remote_scene_tool/voodoo_doll + secondary =/obj/item/remote_scene_tool/voodoo_necklace + +/obj/item/remote_scene_tool/voodoo_doll/verb/toggle_fun_mode() + set name = "Disable Special Rendering" + set desc = "Disables the displacement-based rendering on the doll, leaving it as just a doll at all times." + no_fun_mode = !no_fun_mode + to_chat(usr,span_notice("you turn the dial on the back of \the [src], and turn the advanced projection matrix [no_fun_mode ? "off" : "on"]")) + update_icon() //force a refresh + + +/obj/item/remote_scene_tool/voodoo_doll/Move(atom/newloc, direct, movetime) + . = ..() + dir = SOUTH //we need to do this + +/obj/item/remote_scene_tool/voodoo_doll/proc/get_outline_color(var/r,var/g,var/b,var/a, var/offset = 30) + var/new_rgba = rgb(r-offset,g-offset,b-offset,a) + return new_rgba + +/obj/item/remote_scene_tool/voodoo_doll/proc/generate_layer_matrix(var/list/data) + var/matrix/output = matrix() + output.Translate(data[DATA_X_OFFSET], data[DATA_Y_OFFSET]) + output.Turn(data[DATA_ROTATION]) + output.Scale(data[DATA_SCALE], data[DATA_SCALE]) + return output + +/obj/item/remote_scene_tool/voodoo_doll/proc/generate_layer_image(var/image/input, var/matrix/newtransform, var/outline_width = 0, var/outline_color = rgb(50,50,50)) + if(input == null) + return null + + input.dir = SOUTH + input.transform = newtransform + input.layer = FLOAT_LAYER + if(outline_width > 0) + input.filters += filter(type="outline", size = outline_width, color = outline_color) + + return input + +/obj/item/remote_scene_tool/voodoo_doll/update_icon() + . = ..() + //reset + overlays?.Cut() + underlays?.Cut() + filters = null + dir = SOUTH + name = initial(name) + desc = initial(desc) + var/matrix/rotateMatrix = matrix() + transform = rotateMatrix + + if(!linked) + return + + var/mob/owner = linked.getWearer() + if(!owner) return; + + name = "Voodoo doll of " + owner.name + desc = "A small, marketable doll that looks suspiciously like [owner]... " + + + if(istype(owner,/mob/living/carbon/human/teshari)) return //no flesh horror, sorry + + if(!ismob(loc) && !ismob(loc.loc)) return //only render the funny when we're on a humie + + if(no_fun_mode) return //no fun allowed + + if(ishuman(owner)) //TODO, refactor the fuck out of this once I port the tgui filter manager stuff over. + var/mob/living/carbon/human/buddy = owner + var/is_tail_taur = istaurtail(buddy.tail_style) + var/icon/displacement_map = icon(src.icon,is_tail_taur ? "taurdisplacement" : "displacement",SOUTH) + var/image/displacement_rendered = get_humanoid_displacement_map_image(buddy,discarded_layer_indicies,displacement_map,2,null) + + //tails need special attention because taurs + var/tailoutlinecolor = rgb(50,50,50) //default to a grey, otherwise we use a darker version of one of the tail colors + if(buddy.tail_style?.do_colouration) + tailoutlinecolor = get_outline_color(buddy.r_tail,buddy.g_tail,buddy.b_tail,buddy.a_tail) + + var/image/tail_image = buddy.get_tail_image() + if(is_tail_taur) + tail_image = generate_layer_image(buddy.get_tail_image(),generate_layer_matrix(taur_data),outline_size,tailoutlinecolor) + tail_image.filters += filter(type="alpha", flags = MASK_INVERSE) + else + tail_image = generate_layer_image(buddy.get_tail_image(),generate_layer_matrix(tail_data),outline_size,tailoutlinecolor) + + //same kind of scenario for wings + var/wingoutlinecolor = rgb(50,50,50) + if(buddy.wing_style?.do_colouration) + wingoutlinecolor = get_outline_color(buddy.r_wing,buddy.g_wing,buddy.b_wing,buddy.a_wing) + var/image/wing_image = generate_layer_image(buddy.get_wing_image(FALSE),generate_layer_matrix(wing_data),outline_size,wingoutlinecolor) + var/image/under_wing_image = generate_layer_image(buddy.get_wing_image(TRUE),generate_layer_matrix(wing_data),outline_size,wingoutlinecolor) + + //the rest are pretty simple + var/ear_icon = buddy.get_ears_overlay() + var/image/ear_image = generate_layer_image(image(icon = ear_icon),generate_layer_matrix(ear_data)) + + //hat + var/hat_icon = buddy.head?.make_worn_icon(body_type = buddy.species.get_bodytype(src), slot_name = slot_head_str, default_icon = INV_HEAD_DEF_ICON, default_layer = HEAD_LAYER) + var/image/hat_image = generate_layer_image(image(icon = hat_icon),generate_layer_matrix(hat_data)) + + + + //add them all + overlays |= displacement_rendered + overlays += ear_image + overlays += hat_image + + if(!is_tail_taur) + underlays += tail_image + else + overlays += tail_image + + underlays |= wing_image + underlays |= under_wing_image + + //filtering time + + rotateMatrix.Turn(2) //very slight blur. this is unironically the best way to do it, any of the blur filters are just way too strong at a minimum value. + transform = rotateMatrix + +#undef DATA_X_OFFSET +#undef DATA_Y_OFFSET +#undef DATA_SCALE +#undef DATA_ROTATION diff --git a/code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi b/code/modules/maint_recycler/icons/goodies/remote_scene_tools.dmi index 18d9c068cc2e0acb14635a5f16c92006f3d4a781..ec534b0a8d80ad55b88d910524fbdb77fd80a98f 100644 GIT binary patch literal 4983 zcmZ8lcQhMN`?hM8M8&EiMpdh{_Li8X1GQJxY$r#?>pz-``mlZJ@0wW``q_F=e*DFni_Gk3$oME(Q)21Hn2Fw?0=J$ z`E*Sl@RU7;o(LC1V8fkqoWJYM5he^V&ynG2G+0e)p5ri5?0!EI9On3 z6l$fA7)?>On$uKEa z@UXB3+iK_5l9yAFsgkYFBUhGUxpG;}dsq*H&18?AiBuO^U@Wb>IQj=bBZ|eS#139x zkNAEmkdkT!cS{(;n_l^1)&82RIh@K89;#jN^K+0|IqFSd&?aywwkrMM?Q7F4i2?T? z4qzkb9(^f4AMf&EoQ{s$>!yLORe0tG`fLE-7;op+C379k$MJ`Bu6K1rV!Lz#g`BMi z&zlF|NLm0{7!~r3+N8WZNM_<872B% zD0}-exGj9TQ*Q_fvO=tR)`wnui98q^V1MGql#?Xn?D$58&`mS)DXr>@tkllnsjunf zggrr$gFNlO&6Uk`k9?s>zFMnvOujdzu2)MR(tcDPl5wL5BjM!V<|)HR&CEF}^Bpxh zUWCbq2KDG$GF_}%xXLhXmW4;QZi}`qI)0{>Wyz!ScYD|wv$ATDv)k|k=6w)93UaNN z5YrnD&K)<>T$0bxtN@!0iCFTIeojaQb2&Os4-An11si_E^nDyKUcdc39g`SmIHk2= zPf9fi`8&n4w&a00V*4)D@OAk3hUSH7+1p5|}^;1*yQ7X3MtlFY^gzQQpPM3N2qyvjpNwmbSzg zM*8h5lLUbMw-8KSe}38DY-93cr@*F2_hInP6rU|*?GtsUam`fZ?Vrh0Dy4@>(V5}_2L-pgJF=5*&80F8!zO!;gw7??tnP^g8?igR&?zDt z8EdEvhe(_f9a`Q|hKP>W%l-KclHX1Mhih!qErwHuC!8IBf`jJnu>EJ-gM?$jV$U8wtL2$@Zg`+E_2RhWekW5bksI+uizd3I!ryIeVtJWO)Xq(@^uUY{)hDc#9Bf z_u72-p3xWAsHEt48+Zz^YaB;-k1CxL7FXy|=7lb{9S!qSBmX0@c{OcE&)JXa`Z@id z!balbwOD@3K#@ren>-XXxx4(>DUa1;k&)Xe%Tz)X*LxvknXIL4CJ5e)tU%wJV7mbG zK|KxEYn_Mm5hctr)&|(5(LbUm^Uj;0xQ5r5F-A z>bLrdXGDWwv+1`!Hbg1Pat^usO9y)9B4T(8zdm)u#&dAi7kK;%LQHTw?8?aj=pB#H z(98KJ0`&UccfF#fn-wiI<_mu7>!Im3%#IT-{rUr4M}D3|>>&_9fzjkQ;ZR~&B%*&L z3Wl$gWFjqxl%ppoa+y^toMs5St-P&j2nVC;Nv|b%Jdzk$3TzA19FJUUi@1)!l3f$m zC?lK2^qmd|EjRX89DOFA@@Ka5$vU{{1YOAJ%u@gGE1Zh%InL$^DCxc~V^Y+-K3zOT z@N^P-51E)tTxa903O?H}lM@?$Vv{O)Rf3O8~@XP0U@2iv9p2ShlXjOPM5eT~wsE$TaT?AVs zO*RZ-9Ak;YcGr>28par01t^ixdElk;8GAz+&t=n1czCRNO-CjL)w~M|^>)p9^xVI0 zx4pF4tB)>JcSHMI;#}El)amn|Mz5tvx4B4tCV2U4w1Dfn7p%`I}3HXB=ycF->m%o zdGcW1i6C=Q6~)An@vH}h*7ei>dg%H6lU;wZ%z|fAMXaoQx*qAwSd@D^?+cTNd-X`G z&%ru}Z_@2C>@qKj$BbSl?y1D~jDrnL{Dfo>Lvw%H6^eP8sxcRlE;nXVB)nzqm^u$jQw{nFtRqZ4CddUeOfWRv;0y^r{Iq8Y5ngKv)0bclNd9 ziwPzbi=U?ITPLB{sj^?uf94otkh?_=W#qV0d#B})3x+?OXL1SW?Kp^NIG2xn%_IW8 zdvFLxMAl=Fgs!`^Q5GyH`6aonTo11pE~*t;ex$5P=OrIk6OU01j*jqIik#k^J4t90-`m+pLKSz zWiPDeh)kx7?tAodAqHqb2awVdD@`riYtbv%p$|a~94wDque=F8!Pkf&l)@lyDCm%B z5w!sBs6{%R=8r-!t^fw6Ph4tTY8JK=sAL@gtMj`%1DIAn`#Ll_qQeBP&GBk~JJcrB4{U(Unk;a`` zL9=8`_Wc0F!M8TPOv%Nr#Sldt8SJygkuAuUNRs6fe&%v&Pzb~|mGUJCpv12S#9g?%3~JDMA2 z<}EL+8J$tj21=WJy5Q1nb2ZoF<8B^Mu%w{F<>}X*zDAjMKhCr@Hf$%;Bhx_}NAJIY zC6}Th(Z;dRlbEXN_B(x=wvL(QDL_^3lmSjz4j^c9=Fv&)@LulA(-IY9*<&;iR;1`K zDLAx$i?d<)VxaH&Jc(bm%KZsVWw^ihB9%4gMk26hvr5JWiD}{HvIhiWTnu+P-}aTb zRg9JA0-}XAC%K}b0#9QFoNJM<51-(P6Zb%?RT#vMp=8DVTFBi%Y>PDEqTTyP!M;wh zARU88-CxD6F1!u<@fhwYEZY)0))q`MGVBo-cmaL5=QaNKJ80yC3nn{tzfkXq7Gty$h0dGB0Lai@OEabh(d#JTfU7%5f$~RxZPuTcr5} z8_zODk6*@f_I>VRJYOoVEOFQL?J|?Snpa&w{4`g`L@8F#8K4$`kJd?-UKqu~@CpGb zM7aGzAA>3HSq|IIGGF-R{q=>rqqSf2lA=tE7xmo@@^Rj9Wj$IA95zZAlHBt#AzZ~N zws@NmZo8;5yKngOI+RB652USkv2bvI+_CXWww_xst1CFPJ-e7;Em%^dWxyj-vtODy zG5dx)A9u^*2EOTdzhbsis7;EFG7<1?A?^0z(mj^>{c?Z1g<%!~WBO!&=k)Q9b&c1T z_p{shg+UT1YOAxFG!E5X3)(KoczwR4+}P=_BkZPLF^(M`*5>;Yht4Od`c+M+6VAzL z+|g&|voU0K&nn%Orw0bXE4uFo?R#O_;uxsg#I(bvc6bw^z;1 zwk(6`!J8iX_A=dlRCfqlK)2YYFYf)(-s5p z$2NL2x^{A6<)owRmH@b*Ak+a<9a;QIS@0<_gv26Niqr`x@941hoxGA?E{j2WV-sL@ zEU@m}boTj47;0?y=!-MZv%WHEk|qYN-VL(Z=q`Z+6 zm*Hjl)3vQVt_T*9QsOuV#0hgXDBuA95bs^ajJgzJ8%C5455r~-eU2csqR&Is8M=5fMHTqMuWT+qULP=eEFUo z;r6`0(u;lW`e|SLx1q0cWi5?~(O|6eR(J=Mv4j8knO+7BaT-&Gc-cSkT1p8MY1<}# z8-*3Odn)l%%#ZpMK=^o7-8s-aR@L|(y2-$0HO>mio&;KlRF^b|lX7AZ^cA1$IWn!dvhJ}v>0Dv<$^xU4JQ#P~XnnTg~s2FQyI6!6*?X-)#^@k)*)FL@!By=|z)og8y;@=VS=ezA7>0*K;xCeezwvN9X|*#C{( z>#osH008c}0oT6$G-D$Z?aj5o+m3sAS!}S7IX42X5Mj^({t!mDAeJAS;!NJDB3n!j`YZdHP<e8U2vuX?{}Ypmmf$T>Yqu}*MyXZI4=Is?G^Xk3Wgl#3fr!<3npq&QYQ zzD{N?7Tf$h&IyE4Mx<=X5_UN@7v>K9Uyl+5l}Ig?=vY@YG+so6lKPQj_}&hF8|zPZ z@G``sH#9bfhmZd3rC!q?DZ#0b=);&@bGo~~C2@D&(gD^u|C+qZkt3M5<{ZAz)kOaz z-J0;}mo6G*6RX&{Y$Vkq=*9@$d(^nY4x1$@gN|2^S&cEbSb)#RG(Lw_Ph*z!?g0Qi z<~J&+^`d-fHJhtOA;7s={y(YVMVUXScch1m-4!t-#v7hZLjKemRswoIwfv|hP#DGM zL&Wn`TO6NTK72X1#M+@6{l3^S+V?Z)@Vy~C(HPIXnO`;oDt)5EXH;(}>o@uT(wtdJ!5t_) zGAWa-&6L&R`qP5C(?IzWUm+vY@vD{6D`nf9`$3X#{#NNrMR$VDT%IKxYXZQ9S20z? zxbLuA)gjczc?899IBPNLkuAA$6o+#Yo$u2a&$lr`sZoYX7dw9nJEZ?(szP=~Ajt$Q zs@deNeeAsFK1^~sI8P6Zl-VI_k@b|x-PIz4Fi(@83n_|W`2st8HKW(R<04!S8v{{t zEcdCdVOS%RMO1HKP~#BjAB zGh>{FBr~vOiN+OWo-mv+BNp+;g?8A7JDg!eOxyhrB&Eg4IrP*w%EF$e^|lY={R^%A zJP8U@f@oLKN89m761xL?G8ERmciG3j<2~Z>H%PppL4w+8+P=tUEne$2EUWj|OEB#X zA=s^dJRR0H6GjSj`j@A9iolWaXIEvh#RO+cPk}%oPa)W?V-ttNZbU_>C`J>-PsroN zurA;6SS{WeFO`!8avp)=-#=U-0h@AQjR+xFym|jwQ;2il8>vNvuF~T0KfT$LWMB;$ zc*=4*1n{oh><(Qs)K3&N+8_DiwK@kOR-t)ZTrO(9=5IB(o{^T8W`4l`2fG@0C)qxt zZfPT227DxcD(`mH7V!&GUWaie?rK^xU-QH+F`4;*`r-4h<@qZChr zfr60zz$xkXT~-)~N*_%tKHcm2-Z9O4e+a7a$TD^4Fsl@8HW&O4$Aex^C`FShbpPSJ zgQzZ24#;UMOUv2r&hFQ8RY~{{^u`83UfpF4vRo$v=gS?*soCIpJrJ;K=a`B(YU}+#}>3DrFs>r3wqhb25 zJQ(^(k!RWyH4e&E-cB00qJOxAbttpR&k($E0`X_e4Te&K*Xy42yI_ssO9qg__Wd%OZSd9QMXW&NB0T!7-aecR6!oXxVsLz5&~O^4#Z~eLOP_Z> zk#3+HBb-!Io?~UT4G#58KnY4 z&i7uuqeQI2{Qo9>{rp`>LKLcW_1HJI3LcES+E54PhFG508^q2*OfzNY(T11=1;G-T zjeG`CWN^z`i3vk;ySW-EW&j{sP&p*VAO6J7S^dF^px?{8y}0i}`T?$xsHkmy!&s{y zr>mQG^g;>PIdJOAmuVKvPJ0o-NQcoeFfgh6Ci2tHj+6m6z0nD?+SWKONrHZOPV#xX zdGy=2{LdJR9dQ>5_|QX)!lNLij^<{Q{snulYme{UQ#Ug=b{end7_OYZzozZpym|Up ze3y_CSLnimg{iZ9Pxn^))pL{Ej7}8^CcJ;WsnkTniE=GZHU+AasPs4 zlEP@ChZvkXV%RTuv$ND@lPywf~4gUHq3nd#cbSH z&La>hU{aoemOoJ>urYDHj{X~+zVDwIaOx}cxtomDw4AW99S_@)a*D%;pSoO55G-pq zum7=DB4NoG_PVW2$}rth1QMd9cKG>o4E)H>-s^dvB+f1sB}SlC%9!8}XYm072dx^W zZow9pG^nC78&BD{GzJF+*eooJykr!QXZKe3XeopKR-B11ikG-G z4DzklbkISD|E^4fgt*2yY~s(vV|~+!T}mqce_xM#%}AwL%Er$xCw91}Ikollrrjly z>A8^5=f|N&qe})+RtKnsYIoe}^g0VGtKw%S!Pg8xm^O#Z%uONDITc_;5pz1-Zg_aO z2svm!z_+Dmw?%h;^^c)tgA1?2WiM(9ZXSa){EuRyqvwTgPkUSHfH0M>Ms34lV<#dq z{QnnLS&r|Tp*I})V=m$Qxqwr^@rxG;9FX`X;g#FIznY*A_oc&40=FbjfD{ll`s0?$T}Fn}!(-4p8;hlbOCO~NcI-)SEB=wkNiGE4hXXtm7< zWBe4Uv+x!4f-q!(>NzKM2xK>sMJg-r&Bt;{G07bIK{2mjp3nNtkXqQaT?E?YiY2Z= zk0#S7ZfumPCQAiU%2b(MwgT1jCW3<0=9@dZ>eUC|3zdJ}G@~F|AvX-;=%U`$2Yt*; zibtFs!E)O1-#JQ?^9~tP(CTvR?7YItAHZP4op3fJ+fR9-6LYkBXh#{{E4mtolOqC-H(a%_av&Z?Ti^4dU*S^!9PfafC0^(X)VEVl=(dkQya)E115n&^P7sQ}$qv4t7LnD0-X57A6x-{7f0 zwecm>CsZbwtNy}Dn_|7k&1uk`MXApDBn2r|CE=4#fg|Oe_z*Z&TQEg6efWoU5x(-zq(4TWTCXxtV75L@>a|XXSXBIE7z^-6mbrMuQGU05XD4->v-X&G zJGCfQD=q%8+DRN(QF?eqfSZtT$_vjZw(xd_c6DQ``xUWaPn&|JxwKUF)6}lKPaEsh zf-g&L42`D^%0|DTa!uMi&pmVTlPc^S^X@26BmQt-arW|sP&W`Gps&3}oW?ENNA!P% zLFI))E?lSJrpd~azuQSZD+dQ=6PH2|>6nU_33OutJGmk9-#*u?L*+x8-iZIG`}-y- zwvi{u+GlZZB|l4_vez#x3=UV@aQnZ@b154I%-bbq%`6yaLHj=e+_+{0FV%5;_CJ@D BeM|rV diff --git a/code/modules/maint_recycler/icons/vendor_ads.dmi b/code/modules/maint_recycler/icons/vendor_ads.dmi deleted file mode 100644 index e607f0cb9e0013304e4316b9dd0c8029424c2ada..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235 zcmeAS@N?(olHy`uVBq!ia0vp^4nVBH!3HE3&8=$zQa)875hX6E#mPmP1tppJc?=8{ zbArPPib}tK2`>2f^@*0ZuGYCT=Yuzd8eBAf@JQ#pkLF2+qBA}_ER2JUFB^L`$RsQ4 zrLu>is8kuO#0&;Go5 r$*`8u$|1Xf?}F3<<}GY5NZ_*Oh%hy<#B!PdZDH_q^>bP0l+XkK+Y(YA diff --git a/vorestation.dme b/vorestation.dme index 92cd4faffb6..a14ae7ee9aa 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2913,8 +2913,10 @@ #include "code\modules\maint_recycler\code\vendor_datums\entries\misc_entries.dm" #include "code\modules\maint_recycler\code\vendor_datums\entries\scene_tools.dm" #include "code\modules\maint_recycler\code\vendor_datums\entries\weapons_and_arms.dm" -#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools.dm" #include "code\modules\maint_recycler\code\vendor_goodies\vendor_goodies.dm" +#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\displacement_map_generation.dm" +#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\remote_scene_tools.dm" +#include "code\modules\maint_recycler\code\vendor_goodies\remote_scene_tools\voodoo.dm" #include "code\modules\makeup\nailpolish.dm" #include "code\modules\maps\merge_conflicts.dm" #include "code\modules\maps\bapi-dmm\bapi_bindings.dm" From bfbab4e35770093136489b6ead27cda180ebcf5e Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:57:44 -0400 Subject: [PATCH 13/47] dump reagents instead of just fuel (#17885) --- code/modules/reagents/machinery/dispenser/reagent_tank.dm | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/modules/reagents/machinery/dispenser/reagent_tank.dm b/code/modules/reagents/machinery/dispenser/reagent_tank.dm index 942c2b7da49..6640820dc38 100644 --- a/code/modules/reagents/machinery/dispenser/reagent_tank.dm +++ b/code/modules/reagents/machinery/dispenser/reagent_tank.dm @@ -279,10 +279,8 @@ /obj/structure/reagent_dispensers/fueltank/proc/leak_fuel(amount) if (reagents.total_volume == 0) return - amount = min(amount, reagents.total_volume) - reagents.remove_reagent(REAGENT_ID_FUEL,amount) - new /obj/effect/decal/cleanable/liquid_fuel(src.loc, amount,1) + reagents.trans_to_turf(get_turf(src),amount) /obj/structure/reagent_dispensers/peppertank name = "Pepper Spray Refiller" From b7dc48ee83e1e75f0a54da6eaba96d350199619b Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:58:07 +0000 Subject: [PATCH 14/47] Automatic changelog for PR #17635 [ci skip] --- html/changelogs/AutoChangeLog-pr-17635.yml | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17635.yml diff --git a/html/changelogs/AutoChangeLog-pr-17635.yml b/html/changelogs/AutoChangeLog-pr-17635.yml new file mode 100644 index 00000000000..7bfc2e71d5a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17635.yml @@ -0,0 +1,8 @@ +author: "TeleTubby2" +delete-after: True +changes: + - rscadd: "Voodoo dolls now render things all scrungly" + - rscadd: "Remote scene tools have some new utility verbs" + - qol: "Remote scene tools now show the status of their counterpart's wearer on examine" + - bugfix: "Various typos have been fixed for the recycler and recycling vendor" + - bugfix: "The maint recycler doesn't nuke your points after getting to 999 anymore. whoops." \ No newline at end of file From d780e2443cfab9b3e9dc2c5769c63e6c6330f6ae Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:58:12 +0000 Subject: [PATCH 15/47] Automatic changelog for PR #17885 [ci skip] --- html/changelogs/AutoChangeLog-pr-17885.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17885.yml diff --git a/html/changelogs/AutoChangeLog-pr-17885.yml b/html/changelogs/AutoChangeLog-pr-17885.yml new file mode 100644 index 00000000000..262dd43eaac --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17885.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - bugfix: "reagent dispenser tanks no longer perform alchemy, turning all reagents into fuel spills" \ No newline at end of file From 633cea67a29a8014522014120275297c74fb0c37 Mon Sep 17 00:00:00 2001 From: Kashargul <144968721+Kashargul@users.noreply.github.com> Date: Wed, 18 Jun 2025 07:58:57 +0200 Subject: [PATCH 16/47] browse to browser continued (#17871) --- code/game/machinery/camera/camera.dm | 6 +- code/game/machinery/magnet.dm | 7 +- code/game/machinery/robot_fabricator.dm | 7 +- code/game/machinery/syndicatebeacon.dm | 6 +- code/game/machinery/syndicatebeacon_vr.dm | 6 +- .../items/weapons/implants/implantchair.dm | 5 +- code/game/objects/items/weapons/scrolls.dm | 7 +- code/modules/admin/DB ban/functions.dm | 4 +- code/modules/admin/NewBan.dm | 5 +- code/modules/admin/ToRban.dm | 6 +- code/modules/admin/admin.dm | 41 +++--- code/modules/admin/admin_report.dm | 5 +- code/modules/admin/admin_verbs.dm | 6 +- code/modules/admin/newbanjob.dm | 5 +- code/modules/admin/permissionedit.dm | 5 +- .../secrets/admin_secrets/bombing_list.dm | 5 +- .../admin/secrets/admin_secrets/list_dna.dm | 5 +- .../admin_secrets/list_fingerprints.dm | 5 +- .../admin_secrets/show_crew_manifest.dm | 4 +- .../secrets/admin_secrets/show_law_changes.dm | 5 +- .../secrets/admin_secrets/show_signalers.dm | 5 +- .../admin/verbs/check_customitem_activity.dm | 4 +- code/modules/admin/verbs/diagnostics.dm | 12 +- code/modules/admin/verbs/mapping.dm | 7 +- .../awaymissions/bluespaceartillery.dm | 7 +- code/modules/economy/money_bag.dm | 5 +- code/modules/economy/retail_scanner.dm | 9 +- .../core/special_pins/list_pin.dm | 5 +- code/modules/library/lib_items.dm | 6 +- code/modules/mentor/mentorhelp.dm | 18 ++- code/modules/mob/living/carbon/human/emote.dm | 5 +- .../living/carbon/human/human_attackhand.dm | 5 +- .../carbon/human/human_attackhand_vr.dm | 63 -------- .../mob/living/carbon/human/human_species.dm | 41 ++++++ .../living/carbon/human/human_species_vr.dm | 42 ------ .../station/station_special_abilities.dm | 7 +- .../tools/artifact_harvester.dm | 135 ------------------ vorestation.dme | 1 - 38 files changed, 203 insertions(+), 319 deletions(-) delete mode 100644 code/modules/mob/living/carbon/human/human_attackhand_vr.dm delete mode 100644 code/modules/mob/living/carbon/human/human_species_vr.dm diff --git a/code/game/machinery/camera/camera.dm b/code/game/machinery/camera/camera.dm index 1f8e32d4b44..a212588b9b5 100644 --- a/code/game/machinery/camera/camera.dm +++ b/code/game/machinery/camera/camera.dm @@ -217,7 +217,11 @@ to_chat(O, span_infoplain(span_bold("[U]") + " holds \a [itemname] up to one of your cameras ...")) else to_chat(O, span_infoplain(span_bold("[U]") + " holds \a [itemname] up to one of your cameras ...")) - O << browse(text("[][]", itemname, info), text("window=[]", itemname)) + + var/datum/browser/popup = new(O, itemname, itemname) + popup.add_head_content("[itemname]") + popup.set_content("[info]") + popup.open() else if (istype(W, /obj/item/camera_bug)) if (!src.can_use()) diff --git a/code/game/machinery/magnet.dm b/code/game/machinery/magnet.dm index 5f27c946d53..ac9853d1c2d 100644 --- a/code/game/machinery/magnet.dm +++ b/code/game/machinery/magnet.dm @@ -242,7 +242,7 @@ if(stat & (BROKEN|NOPOWER)) return user.set_machine(src) - var/dat = span_bold("Magnetic Control Console") + "

" + var/dat = "" if(!autolink) dat += {" Frequency: [frequency]
@@ -263,8 +263,9 @@ dat += "Moving: [moving ? "Enabled":"Disabled"]" - user << browse("[dat]", "window=magnet;size=400x500") - onclose(user, "magnet") + var/datum/browser/popup = new(user, "magnet", "Magnetic Control Console", 400, 500) + popup.set_content(dat) + popup.open() /obj/machinery/magnetic_controller/Topic(href, href_list) if(stat & (BROKEN|NOPOWER)) diff --git a/code/game/machinery/robot_fabricator.dm b/code/game/machinery/robot_fabricator.dm index 51a09f49a5c..57ba629d6c1 100644 --- a/code/game/machinery/robot_fabricator.dm +++ b/code/game/machinery/robot_fabricator.dm @@ -56,9 +56,10 @@ Please wait until completion...
Robot Frame (75,000 cc metal).
"} - user << browse("Robotic Fabricator Control Panel[dat]", "window=robot_fabricator") - onclose(user, "robot_fabricator") - return + var/datum/browser/popup = new(user, "robot_fabricator", "Robot Fabricator") + popup.add_head_content("Robotic Fabricator Control Panel") + popup.set_content("[dat]") + popup.open() /obj/machinery/robotic_fabricator/Topic(href, href_list) if(..()) diff --git a/code/game/machinery/syndicatebeacon.dm b/code/game/machinery/syndicatebeacon.dm index 04513432e81..9f6e8b7f0ee 100644 --- a/code/game/machinery/syndicatebeacon.dm +++ b/code/game/machinery/syndicatebeacon.dm @@ -32,8 +32,10 @@ if(!selfdestructing) dat += "

\"[pick("I want to switch teams.", "I want to work for you.", "Let me join you.", "I can be of use to you.", "You want me working for you, and here's why...", "Give me an objective.", "How's the 401k over at the Syndicate?")]\"
" dat += temptext - user << browse("[dat]", "window=syndbeacon") - onclose(user, "syndbeacon") + + var/datum/browser/popup = new(user, "syndbeacon", "Ominous Beacon") + popup.set_content(dat) + popup.open() /obj/machinery/syndicate_beacon/Topic(href, href_list) if(..()) diff --git a/code/game/machinery/syndicatebeacon_vr.dm b/code/game/machinery/syndicatebeacon_vr.dm index 356ba99d6d7..c35454278ea 100644 --- a/code/game/machinery/syndicatebeacon_vr.dm +++ b/code/game/machinery/syndicatebeacon_vr.dm @@ -16,8 +16,10 @@ if(!selfdestructing) dat += "

\"[pick("Send me some supplies!", "Transfer supplies.")]\"
" dat += temptext - user << browse("[dat]", "window=syndbeacon") - onclose(user, "syndbeacon") + + var/datum/browser/popup = new(user, "syndbeacon", "Ominous Beacon") + popup.set_content(dat) + popup.open() /obj/machinery/syndicate_beacon/virgo/Topic(href, href_list) if(href_list["betraitor"]) diff --git a/code/game/objects/items/weapons/implants/implantchair.dm b/code/game/objects/items/weapons/implants/implantchair.dm index 0f5bf2fbc83..1fc0bbd0c25 100644 --- a/code/game/objects/items/weapons/implants/implantchair.dm +++ b/code/game/objects/items/weapons/implants/implantchair.dm @@ -42,9 +42,10 @@ if(src.occupant) dat += "[src.ready ? "Implant" : "Recharging"]
" user.set_machine(src) - user << browse("[dat]", "window=implant") - onclose(user, "implant") + var/datum/browser/popup = new(user, "implant", "Implant") + popup.set_content(dat) + popup.open() /obj/machinery/implantchair/Topic(href, href_list) if((get_dist(src, usr) <= 1) || isAI(usr)) diff --git a/code/game/objects/items/weapons/scrolls.dm b/code/game/objects/items/weapons/scrolls.dm index f4d89526d90..309e66f503b 100644 --- a/code/game/objects/items/weapons/scrolls.dm +++ b/code/game/objects/items/weapons/scrolls.dm @@ -26,9 +26,10 @@ dat += span_bold("Four uses use them wisely:") + "
" dat += "Teleport
" dat += "Kind regards,
Wizards Federation

P.S. Don't forget to bring your gear, you'll need it to cast most spells.
" - user << browse("[dat]", "window=scroll") - onclose(user, "scroll") - return + + var/datum/browser/popup = new(user, "scroll", "Scroll") + popup.set_content(dat) + popup.open() /obj/item/teleportation_scroll/Topic(href, href_list) ..() diff --git a/code/modules/admin/DB ban/functions.dm b/code/modules/admin/DB ban/functions.dm index 0fe48d12636..90393cb1220 100644 --- a/code/modules/admin/DB ban/functions.dm +++ b/code/modules/admin/DB ban/functions.dm @@ -486,4 +486,6 @@ output += "" qdel(select_query) - usr << browse("[output]","window=lookupbans;size=900x700") + var/datum/browser/popup = new(owner, "lookupbans", "Lookup Bans", 900, 700) + popup.set_content(output) + popup.open() diff --git a/code/modules/admin/NewBan.dm b/code/modules/admin/NewBan.dm index 98761987fe2..6053bcaceae 100644 --- a/code/modules/admin/NewBan.dm +++ b/code/modules/admin/NewBan.dm @@ -190,7 +190,10 @@ var/savefile/Banlist dat += "" dat = "
" + span_bold("Bans:") + " " + span_blue("(U) = Unban , (E) = Edit Ban") + " - " + span_green("([count] Bans)") + "
[dat]" - usr << browse("[dat]", "window=unbanp;size=875x400") + + var/datum/browser/popup = new(owner, "unbanp", "Unban", 875, 400) + popup.set_content(dat) + popup.open() //////////////////////////////////// DEBUG //////////////////////////////////// diff --git a/code/modules/admin/ToRban.dm b/code/modules/admin/ToRban.dm index 79cf5e5fe7a..2152fa97596 100644 --- a/code/modules/admin/ToRban.dm +++ b/code/modules/admin/ToRban.dm @@ -67,7 +67,11 @@ dat = "
[dat]
" else dat = "No addresses in list." - src << browse("[dat]","window=ToRban_show") + + var/datum/browser/popup = new(src, "ToRban_show", "Torban") + popup.set_content(dat) + popup.open() + if("remove") var/savefile/F = new(TORFILE) var/choice = tgui_input_list(src,"Please select an IP address to remove from the ToR banlist:","Remove ToR ban", F.dir) diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index 57e0d4ccfb2..c7b857595e9 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -53,8 +53,7 @@ GLOBAL_VAR_INIT(floorIsLava, 0) to_chat(usr, "Error: you are not an admin!") return - var/body = "Options for [M.key]" - body += "Options panel for" + span_bold("[M]") + var/body = "Options panel for" + span_bold("[M]") if(M.client) body += " played by " + span_bold("[M.client]") body += "\[[M.client.holder ? M.client.holder.rank_names() : "Player"]\]" @@ -236,11 +235,13 @@ GLOBAL_VAR_INIT(floorIsLava, 0) else body += "[k]" - body += {"
- - "} + body += {"
"} + + var/datum/browser/popup = new(owner, "adminplayeropts", "Edit Player", 550, 515) + popup.add_head_content("Options for [M.key]") + popup.set_content(body) + popup.open() - usr << browse(body, "window=adminplayeropts;size=550x515") feedback_add_details("admin_verb","SPP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! @@ -314,7 +315,7 @@ GLOBAL_VAR_INIT(floorIsLava, 0) to_chat(usr, "Error: you are not an admin!") return var/dat - dat = text("Admin Newscaster

Admin Newscaster Unit

") + dat = text("

Admin Newscaster Unit

") switch(admincaster_screen) if(0) @@ -547,10 +548,11 @@ GLOBAL_VAR_INIT(floorIsLava, 0) //to_world("Channelname: [src.admincaster_feed_channel.channel_name] [src.admincaster_feed_channel.author]") //to_world("Msg: [src.admincaster_feed_message.author] [src.admincaster_feed_message.body]") - usr << browse("[dat]", "window=admincaster_main;size=400x600") - onclose(usr, "admincaster_main") - + var/datum/browser/popup = new(owner, "admincaster_main", "Admin Newscaster", 400, 600) + popup.add_head_content("Admin Newscaster") + popup.set_content(dat) + popup.open() /datum/admins/proc/Jobbans() if(!check_rights(R_BAN)) return @@ -562,13 +564,17 @@ GLOBAL_VAR_INIT(floorIsLava, 0) r = copytext( r, 1, findtext(r,"##") )//removes the description dat += text("[t] (unban)") dat += "" - usr << browse("[dat]", "window=ban;size=400x400") + + var/datum/browser/popup = new(owner, "ban", "Job Bans", 400, 400) + popup.add_head_content("Admin Newscaster") + popup.set_content(dat) + popup.open() /datum/admins/proc/Game() if(!check_rights(0)) return var/dat = {" -
"} + span_bold("Game Panel") + {"

\n +
"} + span_bold("Game Panel") + {"

\n Change Game Mode
"} if(GLOB.master_mode == "secret") @@ -582,11 +588,12 @@ GLOBAL_VAR_INIT(floorIsLava, 0) Create Mob

Edit Airflow Settings
Edit Phoron Settings
- Choose a default ZAS setting
+ Choose a default ZAS setting
"} - usr << browse(dat, "window=admin2;size=210x280") - return + var/datum/browser/popup = new(owner, "admin2", "Game Panel", 210, 280) + popup.set_content(dat) + popup.open() /datum/admins/proc/Secrets(var/datum/admin_secret_category/active_category = null) if(!check_rights(0)) return @@ -1255,7 +1262,9 @@ var/datum/announcement/minor/admin_min_announcer = new out += " None." out += " \[+\]
" - usr << browse("[out]", "window=edit_mode[src]") + var/datum/browser/popup = new(owner, "edit_mode[src]", "Edit Game Mode") + popup.set_content(out) + popup.open() feedback_add_details("admin_verb","SGM") diff --git a/code/modules/admin/admin_report.dm b/code/modules/admin/admin_report.dm index 0aca9bb87e6..2bc01ad38b8 100644 --- a/code/modules/admin/admin_report.dm +++ b/code/modules/admin/admin_report.dm @@ -115,8 +115,9 @@ world/New() else output += "Whoops, no reports!" - usr << browse("[output]", "window=news;size=600x400") - + var/datum/browser/popup = new(src, "news", "News", 600, 400) + popup.set_content(output) + popup.open() /client/proc/Report(mob/M as mob in world) set category = "Admin.Moderation" diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm index 6c76e569dde..017da87b67a 100644 --- a/code/modules/admin/admin_verbs.dm +++ b/code/modules/admin/admin_verbs.dm @@ -719,8 +719,10 @@ ADMIN_VERB(removetickets, R_ADMIN, "Security Tickets", "Allows one to remove tic dat += "" qdel(query) - usr << browse("[dat]", "window=library") - onclose(usr, "library") + + var/datum/browser/popup = new(src, "library", "Delete Book") + popup.set_content(dat) + popup.open() /client/proc/toggle_spawning_with_recolour() set name = "Toggle Simple/Robot recolour verb" diff --git a/code/modules/admin/newbanjob.dm b/code/modules/admin/newbanjob.dm index 70a029a71a0..477567c2d7b 100644 --- a/code/modules/admin/newbanjob.dm +++ b/code/modules/admin/newbanjob.dm @@ -232,7 +232,10 @@ var/savefile/Banlistjob dat += "" dat = "
" + span_bold("Bans:") + " " span_blue("(U) = Unban , ") + " - " + span_green("([count] Bans)") + "
[dat]" - usr << browse("[dat]", "window=unbanp;size=875x400") + + var/datum/browser/popup = new(owner, "unbanjob", "Unban Job", 875, 400) + popup.set_content(dat) + popup.open() /*/datum/admins/proc/permjobban(ckey, computerid, reason, bannedby, temp, minutes, rank) if(AddBanjob(ckey, computerid, reason, usr.ckey, 0, 0, job)) diff --git a/code/modules/admin/permissionedit.dm b/code/modules/admin/permissionedit.dm index 0ab9fe01ba3..6dd91e1e401 100644 --- a/code/modules/admin/permissionedit.dm +++ b/code/modules/admin/permissionedit.dm @@ -133,7 +133,10 @@ output += "
Search:
" if(QDELETED(usr)) return - usr << browse("[jointext(output, "")]","window=editrights;size=1000x650") + + var/datum/browser/popup = new(owner, "editrights", "Edit Rights", 1000, 650) + popup.set_content(jointext(output, "")) + popup.open() /datum/admins/proc/edit_rights_topic(list/href_list) if(!check_rights(R_PERMISSIONS)) diff --git a/code/modules/admin/secrets/admin_secrets/bombing_list.dm b/code/modules/admin/secrets/admin_secrets/bombing_list.dm index 1fb1fac6778..95f59c2bb16 100644 --- a/code/modules/admin/secrets/admin_secrets/bombing_list.dm +++ b/code/modules/admin/secrets/admin_secrets/bombing_list.dm @@ -9,4 +9,7 @@ var/dat = span_bold("Bombing List") for(var/l in GLOB.bombers) dat += text("[l]
") - user << browse("[dat]", "window=bombers") + + var/datum/browser/popup = new(user, "bombers", "Bombers") + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/secrets/admin_secrets/list_dna.dm b/code/modules/admin/secrets/admin_secrets/list_dna.dm index a4caf27b1d0..36bea6515ae 100644 --- a/code/modules/admin/secrets/admin_secrets/list_dna.dm +++ b/code/modules/admin/secrets/admin_secrets/list_dna.dm @@ -11,4 +11,7 @@ if(H.dna && H.ckey) dat += "[H][H.dna.unique_enzymes][H.dna ? H.dna.b_type : DEFAULT_BLOOD_TYPE]" dat += "" - user << browse("[dat]", "window=DNA;size=440x410") + + var/datum/browser/popup = new(user, "DNA", "DNA", 440, 410) + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm index ad86324de95..f38929c929d 100644 --- a/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm +++ b/code/modules/admin/secrets/admin_secrets/list_fingerprints.dm @@ -16,4 +16,7 @@ else if(!H.dna) dat += "[H]H.dna = null" dat += "" - user << browse("[dat]", "window=fingerprints;size=440x410") + + var/datum/browser/popup = new(user, "fingerprints", "Fingerprints", 440, 410) + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm index 9e35bc40e38..b3ac22031ce 100644 --- a/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm +++ b/code/modules/admin/secrets/admin_secrets/show_crew_manifest.dm @@ -9,4 +9,6 @@ dat += "

Crew Manifest

" dat += GLOB.data_core.get_manifest() - user << browse("[dat]", "window=manifest;size=370x420;can_close=1") + var/datum/browser/popup = new(user, "manifest", "Manifest", 370, 420) + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm index 27c6330f07b..f72d796f4b6 100644 --- a/code/modules/admin/secrets/admin_secrets/show_law_changes.dm +++ b/code/modules/admin/secrets/admin_secrets/show_law_changes.dm @@ -12,4 +12,7 @@ var/dat = span_bold("Showing last [length(GLOB.lawchanges)] law changes.") + "
" for(var/sig in GLOB.lawchanges) dat += "[sig]
" - user << browse("[dat]", "window=GLOB.lawchanges;size=800x500") + + var/datum/browser/popup = new(user, "lawchanges", "Lawcahnges", 800, 500) + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/secrets/admin_secrets/show_signalers.dm b/code/modules/admin/secrets/admin_secrets/show_signalers.dm index b96200996a2..e6663d6f62c 100644 --- a/code/modules/admin/secrets/admin_secrets/show_signalers.dm +++ b/code/modules/admin/secrets/admin_secrets/show_signalers.dm @@ -12,4 +12,7 @@ var/dat = span_bold("Showing last [length(GLOB.lastsignalers)] signalers.") + "
" for(var/sig in GLOB.lastsignalers) dat += "[sig]
" - user << browse("[dat]", "window=lastsignalers;size=800x500") + + var/datum/browser/popup = new(user, "lastsignalers", "Last Signallers", 800, 500) + popup.set_content(dat) + popup.open() diff --git a/code/modules/admin/verbs/check_customitem_activity.dm b/code/modules/admin/verbs/check_customitem_activity.dm index 2be186df2c2..5a2493a6145 100644 --- a/code/modules/admin/verbs/check_customitem_activity.dm +++ b/code/modules/admin/verbs/check_customitem_activity.dm @@ -21,7 +21,9 @@ var/inactive_keys = "None
" else dat += "Populate list (requires an active database connection)
" - usr << browse("[dat]", "window=inactive_customitems;size=600x480") + var/datum/browser/popup = new(src, "inactive_customitems", "Inactive Custom Items", 600, 480) + popup.set_content(dat) + popup.open() /proc/populate_inactive_customitems_list(var/client/C) set background = 1 diff --git a/code/modules/admin/verbs/diagnostics.dm b/code/modules/admin/verbs/diagnostics.dm index 819d72cd0f4..f8be0fb79aa 100644 --- a/code/modules/admin/verbs/diagnostics.dm +++ b/code/modules/admin/verbs/diagnostics.dm @@ -23,7 +23,7 @@ else inactive_on_main_station++ - var/output = {"AIR SYSTEMS REPORT
+ var/output = {"AIR SYSTEMS REPORT
General Processing Data
Cycle: [SSair.current_cycle]
Groups: [SSair.zones.len]
@@ -36,10 +36,12 @@ Hotspot Processing: [hotspots]

Geometry Processing Data
- Tile Update: [SSair.tiles_to_update.len]
+ Tile Update: [SSair.tiles_to_update.len]
"} - usr << browse(output,"window=airreport") + var/datum/browser/popup = new(src, "airreport", "Airreport") + popup.set_content(output) + popup.open() /client/proc/fix_next_move() set category = "Debug.Game" @@ -95,7 +97,9 @@ else output += "    [device]
" - usr << browse("[output]","window=radioreport") + var/datum/browser/popup = new(src, "radioreport", "Radioreport") + popup.set_content(output) + popup.open() feedback_add_details("admin_verb","RR") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/reload_admins() diff --git a/code/modules/admin/verbs/mapping.dm b/code/modules/admin/verbs/mapping.dm index 9db6d3042cb..70d4216a34b 100644 --- a/code/modules/admin/verbs/mapping.dm +++ b/code/modules/admin/verbs/mapping.dm @@ -78,7 +78,7 @@ GLOBAL_LIST_BOILERPLATE(all_debugging_effects, /obj/effect/debugging) for(var/obj/machinery/camera/C in cameranet.cameras) CL += C - var/output = {""} + span_bold("CAMERA ANNOMALITIES REPORT") + {"
+ var/output = span_bold("CAMERA ANNOMALITIES REPORT") + {"
"} + span_bold("The following annomalities have been detected. The ones in red need immediate attention: Some of those in black may be intentional.") + {"
    "} for(var/obj/machinery/camera/C1 in CL) @@ -102,7 +102,10 @@ GLOBAL_LIST_BOILERPLATE(all_debugging_effects, /obj/effect/debugging) output += "
  • " + span_red("Camera not connected to wall at \[[C1.x], [C1.y], [C1.z]\] ([C1.loc.loc]) Network: [C1.network]") + "
  • " output += "
" - usr << browse("[output]","window=airreport;size=1000x500") + + var/datum/browser/popup = new(src, "airreport", "Airreport", 1000, 500) + popup.set_content(output) + popup.open() feedback_add_details("admin_verb","mCRP") //If you are copy-pasting this, ensure the 2nd parameter is unique to the new proc! /client/proc/intercom_view() diff --git a/code/modules/awaymissions/bluespaceartillery.dm b/code/modules/awaymissions/bluespaceartillery.dm index d2003d34b09..b7b531c2cc4 100644 --- a/code/modules/awaymissions/bluespaceartillery.dm +++ b/code/modules/awaymissions/bluespaceartillery.dm @@ -27,9 +27,10 @@ dat += span_bold("Charge progress: [reload]/180:") + "
" dat += "Open Fire
" dat += "Deployment of weapon authorized by
[using_map.company_name] Naval Command

Remember, friendly fire is grounds for termination of your contract and life.
" - user << browse("[dat]", "window=scroll") - onclose(user, "scroll") - return + + var/datum/browser/popup = new(user, "artillery", "Artillery") + popup.set_content(dat) + popup.open() /obj/machinery/artillerycontrol/Topic(href, href_list) ..() diff --git a/code/modules/economy/money_bag.dm b/code/modules/economy/money_bag.dm index d515a1249bd..aff6d6ca7fe 100644 --- a/code/modules/economy/money_bag.dm +++ b/code/modules/economy/money_bag.dm @@ -43,7 +43,10 @@ dat += text("Phoron coins: [amt_phoron] Remove one
") if (amt_uranium) dat += text("Uranium coins: [amt_uranium] Remove one
") - user << browse("[dat]", "window=moneybag") + + var/datum/browser/popup = new(user, "moneybag", "Moneybag") + popup.set_content(dat) + popup.open() /obj/item/moneybag/attackby(obj/item/W, mob/user) ..() diff --git a/code/modules/economy/retail_scanner.dm b/code/modules/economy/retail_scanner.dm index 35c6f4baa88..02914c1cfbb 100644 --- a/code/modules/economy/retail_scanner.dm +++ b/code/modules/economy/retail_scanner.dm @@ -88,11 +88,10 @@ dat += "
" dat += "Device ID: [machine_id]" - dat = "Retail Scanner[dat]" - - user << browse(dat, "window=retail;size=350x500") - onclose(user, "retail") - + var/datum/browser/popup = new(user, "retail", "Retail", 350, 500) + popup.add_head_content("Retail Scanner") + popup.set_content(dat) + popup.open() /obj/item/retail_scanner/Topic(var/href, var/href_list) if(..()) diff --git a/code/modules/integrated_electronics/core/special_pins/list_pin.dm b/code/modules/integrated_electronics/core/special_pins/list_pin.dm index ae3ae806a30..40fcc73adf4 100644 --- a/code/modules/integrated_electronics/core/special_pins/list_pin.dm +++ b/code/modules/integrated_electronics/core/special_pins/list_pin.dm @@ -22,7 +22,10 @@ t += "#[i] | [display_data(line)] | " t += "\[Edit\] | " t += "\[Remove\]
" - user << browse("[t]", "window=list_pin_\ref[src];size=500x400") + + var/datum/browser/popup = new(user, "list_pin_\ref[src]", null, 500, 400) + popup.set_content(t) + popup.open() /datum/integrated_io/list/proc/add_to_list(mob/user, var/new_entry) if(!new_entry && user) diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index e6a55c78525..6b50dfbbf1c 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -204,9 +204,9 @@ Book Cart End to_chat(user, "This book is completely blank!") /obj/item/book/proc/display_content(mob/living/user) - if(!findtext(dat, regex("^", "Penned by [author].
"), "window=book") + var/datum/browser/popup = new(user, "book", "Penned by [author].") + popup.set_content(dat) + popup.open() /obj/item/book/attackby(obj/item/W, mob/user) if(carved) diff --git a/code/modules/mentor/mentorhelp.dm b/code/modules/mentor/mentorhelp.dm index 93c3d300a72..30967a8b80b 100644 --- a/code/modules/mentor/mentorhelp.dm +++ b/code/modules/mentor/mentorhelp.dm @@ -52,12 +52,15 @@ GLOBAL_DATUM_INIT(mhelp_tickets, /datum/mentor_help_tickets, new) title = "Resolved Tickets" if(!l2b) return - var/list/dat = list("[title]") + var/list/dat = list() dat += "Refresh

" for(var/datum/mentor_help/MH as anything in l2b) dat += span_adminnotice(span_adminhelp("Ticket #[MH.id]") + " [MH.initiator_ckey]: [MH.name]") + "
" - dat += "" - usr << browse(dat.Join(), "window=mhelp_list[state];size=600x480") + + var/datum/browser/popup = new(usr, "mhelp_list[state]", "Mentor Help List", 600, 480) + popup.add_head_content("[title]") + popup.set_content(dat.Join()) + popup.open() //Tickets statpanel /datum/mentor_help_tickets/proc/stat_entry() @@ -269,7 +272,7 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick/mticket_list) tgui_interact(usr.client.mob) /datum/mentor_help/proc/TicketPanelLegacy() - var/list/dat = list("Ticket #[id]") + var/list/dat = list() var/ref_src = "\ref[src]" dat += "

Mentor Help Ticket #[id]: [LinkedReplyName(ref_src)]

" dat += span_bold("State: ") @@ -294,8 +297,11 @@ INITIALIZE_IMMEDIATE(/obj/effect/statclick/mticket_list) dat += "
" + span_bold("Log:") + "

" for(var/I in _interactions) dat += "[I]
" - dat += "" - usr << browse(dat.Join(), "window=mhelp[id];size=620x480") + + var/datum/browser/popup = new(usr, "mhelp[id]", "Mento Help [id]", 620, 480) + popup.add_head_content("Ticket #[id]") + popup.set_content(dat.Join()) + popup.open() /datum/mentor_help/tgui_fallback(payload) if(..()) diff --git a/code/modules/mob/living/carbon/human/emote.dm b/code/modules/mob/living/carbon/human/emote.dm index a3c5bce71e5..35e1621706b 100644 --- a/code/modules/mob/living/carbon/human/emote.dm +++ b/code/modules/mob/living/carbon/human/emote.dm @@ -382,7 +382,10 @@ var/list/_simple_mob_default_emotes = list( HTML += "
" HTML +="\[Done\]" HTML += "" - src << browse(HTML, "window=flavor_changes;size=430x300") + + var/datum/browser/popup = new(src, "flavor_changes", "Change Flavortexts", 430, 300) + popup.set_content(HTML) + popup.open() /mob/living/carbon/human/proc/toggle_tail(var/setting,var/message = 0) if(!tail_style || !tail_style.ani_state) diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 2b01cc7204b..f98b62eed68 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -537,7 +537,9 @@ else dat += span_bold("Primarily [u_attack.attack_name]") + " - set default


" - src << browse("[dat]", "window=checkattack") + var/datum/browser/popup = new(src, "checkattack") + popup.set_content(dat) + popup.open() /mob/living/carbon/human/Topic(href, href_list) if(href_list["default_attk"]) @@ -555,7 +557,6 @@ /mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack) default_attack = u_attack - /mob/living/carbon/human/proc/perform_cpr(var/mob/living/carbon/human/reviver) // Check for sanity if(!istype(reviver,/mob/living/carbon/human)) diff --git a/code/modules/mob/living/carbon/human/human_attackhand_vr.dm b/code/modules/mob/living/carbon/human/human_attackhand_vr.dm deleted file mode 100644 index fc19b5e4706..00000000000 --- a/code/modules/mob/living/carbon/human/human_attackhand_vr.dm +++ /dev/null @@ -1,63 +0,0 @@ -/mob/living/carbon/human - var/datum/unarmed_attack/default_attack - -/mob/living/carbon/human/verb/check_attacks() - set name = "Check Attacks" - set category = "IC.Game" - set src = usr - - var/dat = span_bold(span_giant("Known Attacks")) + "

" - - for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) - dat += span_bold("Primarily [u_attack.attack_name] ") + "


" - - src << browse("[dat]", "window=checkattack") - return - -/mob/living/carbon/human/check_attacks() - var/dat = span_bold(span_giant("Known Attacks")) + "

" - - if(default_attack) - dat += "Current default attack: [default_attack.attack_name] - reset

" - - for(var/datum/unarmed_attack/u_attack in species.unarmed_attacks) - if(u_attack == default_attack) - dat += span_bold("Primarily [u_attack.attack_name]") + " - default - reset


" - else - dat += span_bold("Primarily [u_attack.attack_name]") + " - set default


" - - src << browse("[dat]", "window=checkattack") - -/mob/living/carbon/human/Topic(href, href_list) - if(href_list["default_attk"]) - if(href_list["default_attk"] == "reset_attk") - set_default_attack(null) - else - var/datum/unarmed_attack/u_attack = locate(href_list["default_attk"]) - if(u_attack && (u_attack in species.unarmed_attacks)) - set_default_attack(u_attack) - check_attacks() - return 1 - else - return ..() - -/mob/living/carbon/human/proc/set_default_attack(var/datum/unarmed_attack/u_attack) - default_attack = u_attack - -/datum/unarmed_attack - var/attack_name = "fist" - -/datum/unarmed_attack - bite/attack_name = "bite" - bite/sharp/attack_name = "sharp bite" - bite/strong/attack_name = "strong bite" - punch/attack_name = "punch" - kick/attack_name = "kick" - stomp/attack_name = "stomp" - stomp/weak/attack_name = "weak stomp" - light_strike/attack_name = "light hit" - diona attack_name = "tendrils" - claws/attack_name = "claws" - claws/strong/attack_name = "strong claws" - slime_glomp/attack_name = "glomp" - bite/sharp/numbing/attack_name = "numbing bite" diff --git a/code/modules/mob/living/carbon/human/human_species.dm b/code/modules/mob/living/carbon/human/human_species.dm index d4a826087d9..c685c85fbf4 100644 --- a/code/modules/mob/living/carbon/human/human_species.dm +++ b/code/modules/mob/living/carbon/human/human_species.dm @@ -3,6 +3,7 @@ status_flags = GODMODE|CANPUSH has_huds = FALSE blocks_emissive = FALSE + no_vore = TRUE //Dummies don't need bellies. /mob/living/carbon/human/dummy/Initialize(mapload) . = ..() @@ -148,3 +149,43 @@ /mob/living/carbon/human/stok/Initialize(mapload) . = ..(mapload, SPECIES_MONKEY_UNATHI) species.produceCopy(species.traits.Copy(),src,null,FALSE) + +/mob/living/carbon/human/sergal/Initialize(mapload) + h_style = "Sergal Plain" + . = ..(mapload, SPECIES_SERGAL) + +/mob/living/carbon/human/akula/Initialize(mapload) + . = ..(mapload, SPECIES_AKULA) + +/mob/living/carbon/human/nevrean/Initialize(mapload) + . = ..(mapload, SPECIES_NEVREAN) + +/mob/living/carbon/human/xenochimera/Initialize(mapload) + . = ..(mapload, SPECIES_XENOCHIMERA) + +/mob/living/carbon/human/spider/Initialize(mapload) + . = ..(mapload, SPECIES_VASILISSAN) + +/mob/living/carbon/human/vulpkanin/Initialize(mapload) + . = ..(mapload, SPECIES_VULPKANIN) + +/mob/living/carbon/human/protean/Initialize(mapload) + . = ..(mapload, SPECIES_PROTEAN) + +/mob/living/carbon/human/alraune/Initialize(mapload) + . = ..(mapload, SPECIES_ALRAUNE) + +/mob/living/carbon/human/shadekin/Initialize(mapload) + . = ..(mapload, SPECIES_SHADEKIN) + +/mob/living/carbon/human/altevian/Initialize(mapload) + . = ..(mapload, SPECIES_ALTEVIAN) + +/mob/living/carbon/human/lleill/Initialize(mapload) + . = ..(mapload, SPECIES_LLEILL) + +/mob/living/carbon/human/hanner/Initialize(mapload) + . = ..(mapload, SPECIES_HANNER) + +/mob/living/carbon/human/sparkledog/Initialize(mapload) + . = ..(mapload, SPECIES_SPARKLE) diff --git a/code/modules/mob/living/carbon/human/human_species_vr.dm b/code/modules/mob/living/carbon/human/human_species_vr.dm deleted file mode 100644 index be2d418ddb8..00000000000 --- a/code/modules/mob/living/carbon/human/human_species_vr.dm +++ /dev/null @@ -1,42 +0,0 @@ -/mob/living/carbon/human/dummy - no_vore = TRUE //Dummies don't need bellies. - -/mob/living/carbon/human/sergal/Initialize(mapload) - h_style = "Sergal Plain" - . = ..(mapload, SPECIES_SERGAL) - -/mob/living/carbon/human/akula/Initialize(mapload) - . = ..(mapload, SPECIES_AKULA) - -/mob/living/carbon/human/nevrean/Initialize(mapload) - . = ..(mapload, SPECIES_NEVREAN) - -/mob/living/carbon/human/xenochimera/Initialize(mapload) - . = ..(mapload, SPECIES_XENOCHIMERA) - -/mob/living/carbon/human/spider/Initialize(mapload) - . = ..(mapload, SPECIES_VASILISSAN) - -/mob/living/carbon/human/vulpkanin/Initialize(mapload) - . = ..(mapload, SPECIES_VULPKANIN) - -/mob/living/carbon/human/protean/Initialize(mapload) - . = ..(mapload, SPECIES_PROTEAN) - -/mob/living/carbon/human/alraune/Initialize(mapload) - . = ..(mapload, SPECIES_ALRAUNE) - -/mob/living/carbon/human/shadekin/Initialize(mapload) - . = ..(mapload, SPECIES_SHADEKIN) - -/mob/living/carbon/human/altevian/Initialize(mapload) - . = ..(mapload, SPECIES_ALTEVIAN) - -/mob/living/carbon/human/lleill/Initialize(mapload) - . = ..(mapload, SPECIES_LLEILL) - -/mob/living/carbon/human/hanner/Initialize(mapload) - . = ..(mapload, SPECIES_HANNER) - -/mob/living/carbon/human/sparkledog/Initialize(mapload) - . = ..(mapload, SPECIES_SPARKLE) diff --git a/code/modules/mob/living/carbon/human/species/station/station_special_abilities.dm b/code/modules/mob/living/carbon/human/species/station/station_special_abilities.dm index 7de1d50c005..febf1716f9f 100644 --- a/code/modules/mob/living/carbon/human/species/station/station_special_abilities.dm +++ b/code/modules/mob/living/carbon/human/species/station/station_special_abilities.dm @@ -1082,7 +1082,7 @@ to_chat(src, span_notice("You will [trait_injection_verb] your targets.")) return if(choice == "Chemical Refresher") - var/output = {""} + span_bold("Chemical Refresher!") + {"
+ var/output = {"
"} + span_bold("Options for venoms") + {"

"} + span_bold("Size Chemicals") + {"
@@ -1111,7 +1111,10 @@ You can also bite synthetics, but due to how synths work, they won't have anything injected into them.
"} - src << browse("[output]","window=chemicalrefresher") + + var/datum/browser/popup = new(src, "chemicalrefresher", "Chemical Refresher") + popup.set_content(output) + popup.open() return else var/list/targets = list() //IF IT IS NOT BROKEN. DO NOT FIX IT. AND KEEP COPYPASTING IT (Pointing Rick Dalton: "That's my code!" ~CL) diff --git a/code/modules/xenoarcheaology/tools/artifact_harvester.dm b/code/modules/xenoarcheaology/tools/artifact_harvester.dm index a3a3abcb0eb..5947cc9b45c 100644 --- a/code/modules/xenoarcheaology/tools/artifact_harvester.dm +++ b/code/modules/xenoarcheaology/tools/artifact_harvester.dm @@ -297,138 +297,3 @@ inserted_battery.battery_effect.ToggleActivate() src.visible_message(span_bold("[name]") + " states, \"Battery dump completed.\"") icon_state = "incubator" -/* //This is old and unused. -/obj/machinery/artifact_harvester/Topic(href, href_list) - - if (href_list["harvest"]) - if(!inserted_battery) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. No battery inserted.\"") - - else if(inserted_battery.stored_charge >= inserted_battery.capacity) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. battery is full.\"") - - else - - //locate artifact on analysis pad - cur_artifact = null - var/articount = 0 - var/obj/machinery/artifact/analysed - for(var/obj/A in get_turf(owned_scanner)) - analysed = A - articount++ - - if(articount <= 0) - var/message = span_bold("[src]") + " states, \"Cannot harvest. No noteworthy energy signature isolated.\"" - src.visible_message(message) - - else if(analysed && analysed.being_used) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. Source already being harvested.\"") - - else - if(articount > 1) - state("Cannot harvest. Too many artifacts on the pad.") - else if(analysed) - cur_artifact = analysed - - //if both effects are active, we can't harvest either - var/datum/component/artifact_master/ScannedMaster = analysed.GetComponent(/datum/component/artifact_master) - if(ScannedMaster && istype(ScannedMaster)) - active_effects = ScannedMaster.get_all_effects() - var/list/active_effects = ScannedMaster.artifact_id.get_active_effects() - - if(active_effects.len > 1) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. Source is emitting conflicting energy signatures.\"") - else if(!active_effects.len) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. No energy emitting from source.\"") - - else - //see if we can clear out an old effect - //delete it when the ids match to account for duplicate ids having different effects - if(inserted_battery.battery_effect && inserted_battery.stored_charge <= 0) - qdel(inserted_battery.battery_effect) - inserted_battery.battery_effect = null - - // - var/datum/artifact_effect/source_effect - var/datum/artifact_effect/active_effect = active_effects[1] - - //if we already have charge in the battery, we can only recharge it from the source artifact - if(inserted_battery.stored_charge > 0) - var/battery_matches_primary_id = 0 - if(inserted_battery.battery_effect && inserted_battery.battery_effect.artifact_id == cur_artifact.artifact_master.artifact_id) - battery_matches_primary_id = 1 - if(battery_matches_primary_id && active_effect.activated) - //we're good to recharge the primary effect! - source_effect = active_effect - - if(!source_effect) - src.visible_message(span_bold("[src]") + " states, \"Cannot harvest. Battery is charged with a different energy signature.\"") - else - //we're good to charge either - if(active_effect.activated) - //charge the primary effect - source_effect = active_effect - - if(source_effect) - harvesting = 1 - update_use_power(USE_POWER_ACTIVE) - cur_artifact.anchored = 1 - cur_artifact.being_used = 1 - icon_state = "incubator_on" - var/message = span_bold("[src]") + " states, \"Beginning energy harvesting.\"" - src.visible_message(message) - last_process = world.time - - //duplicate the artifact's effect datum - if(!inserted_battery.battery_effect) - var/effecttype = source_effect.type - var/datum/artifact_effect/E = new effecttype(inserted_battery) - - //duplicate it's unique settings - for(var/varname in list("chargelevelmax","artifact_id","effect","effectrange","trigger")) - E.vars[varname] = source_effect.vars[varname] - - //copy the new datum into the battery - inserted_battery.battery_effect = E - inserted_battery.stored_charge = 0 - - if (href_list["stopharvest"]) - if(harvesting) - if(harvesting < 0 && inserted_battery.battery_effect && inserted_battery.battery_effect.activated) - inserted_battery.battery_effect.ToggleActivate() - harvesting = 0 - cur_artifact.anchored = 0 - cur_artifact.being_used = 0 - cur_artifact = null - src.visible_message(span_bold("[name]") + " states, \"Energy harvesting interrupted.\"") - icon_state = "incubator" - - if (href_list["ejectbattery"]) - src.inserted_battery.loc = src.loc - src.inserted_battery = null - - if (href_list["drainbattery"]) - if(inserted_battery) - if(inserted_battery.battery_effect && inserted_battery.stored_charge > 0) - if(tgui_alert(usr, "This action will dump all charge, safety gear is recommended before proceeding","Warning",list("Continue","Cancel")) == "Continue") - if(!inserted_battery.battery_effect.activated) - inserted_battery.battery_effect.ToggleActivate(1) - last_process = world.time - harvesting = -1 - update_use_power(USE_POWER_ACTIVE) - icon_state = "incubator_on" - var/message = span_bold("[src]") + " states, \"Warning, battery charge dump commencing.\"" - src.visible_message(message) - else - var/message = span_bold("[src]") + " states, \"Cannot dump energy. Battery is drained of charge already.\"" - src.visible_message(message) - else - var/message = span_bold("[src]") + " states, \"Cannot dump energy. No battery inserted.\"" - src.visible_message(message) - - if(href_list["close"]) - usr << browse(null, "window=artharvester") - usr.unset_machine(src) - - updateDialog() -*/ diff --git a/vorestation.dme b/vorestation.dme index a14ae7ee9aa..304bc31fd33 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -3188,7 +3188,6 @@ #include "code\modules\mob\living\carbon\human\human_powers_vr.dm" #include "code\modules\mob\living\carbon\human\human_resist.dm" #include "code\modules\mob\living\carbon\human\human_species.dm" -#include "code\modules\mob\living\carbon\human\human_species_vr.dm" #include "code\modules\mob\living\carbon\human\inventory.dm" #include "code\modules\mob\living\carbon\human\life.dm" #include "code\modules\mob\living\carbon\human\life_vr.dm" From b9cab48c19dddcfdcf3e7367878523aa98e7063e Mon Sep 17 00:00:00 2001 From: Cameron Lennox Date: Wed, 18 Jun 2025 01:59:16 -0400 Subject: [PATCH 17/47] Fixes an inheritence issues with arglasses (#17864) --- .../preference_setup/loadout/loadout_eyes.dm | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/code/modules/client/preference_setup/loadout/loadout_eyes.dm b/code/modules/client/preference_setup/loadout/loadout_eyes.dm index f53a39b7a8c..6be257d0492 100644 --- a/code/modules/client/preference_setup/loadout/loadout_eyes.dm +++ b/code/modules/client/preference_setup/loadout/loadout_eyes.dm @@ -196,20 +196,20 @@ ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) -/datum/gear/eyes/arglasses/visor +/datum/gear/eyes/arglasses_visor display_name = "AR visor" path = /obj/item/clothing/glasses/omnihud/visor -/datum/gear/eyes/arglasses/visor/New() +/datum/gear/eyes/arglasses_visor/New() ..() gear_tweaks = list(gear_tweak_free_color_choice) -/datum/gear/eyes/arglasses/sec +/datum/gear/eyes/arglasses_sec display_name = "AR-Security glasses" path = /obj/item/clothing/glasses/omnihud/sec allowed_roles = list(JOB_SECURITY_OFFICER,JOB_HEAD_OF_SECURITY,JOB_WARDEN,JOB_DETECTIVE) -/datum/gear/eyes/arglasses/sec/New() +/datum/gear/eyes/arglasses_sec/New() ..() var/list/selector_uniforms = list( "standard AR-Security glasses"=/obj/item/clothing/glasses/omnihud/sec, @@ -217,12 +217,12 @@ ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) -/datum/gear/eyes/arglasses/sci +/datum/gear/eyes/arglasses_sci display_name = "AR-Research glasses" path = /obj/item/clothing/glasses/omnihud/rnd allowed_roles = list(JOB_RESEARCH_DIRECTOR,JOB_SCIENTIST,JOB_XENOBIOLOGIST,JOB_XENOBOTANIST,JOB_ROBOTICIST) -/datum/gear/eyes/arglasses/sci/New() +/datum/gear/eyes/arglasses_sci/New() ..() var/list/selector_uniforms = list( "standard AR-Research glasses"=/obj/item/clothing/glasses/omnihud/rnd, @@ -230,12 +230,12 @@ ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) -/datum/gear/eyes/arglasses/eng +/datum/gear/eyes/arglasses_eng display_name = "AR-Engineering glasses" path = /obj/item/clothing/glasses/omnihud/eng allowed_roles = list(JOB_ENGINEER,JOB_CHIEF_ENGINEER,JOB_ATMOSPHERIC_TECHNICIAN) -/datum/gear/eyes/arglasses/eng/New() +/datum/gear/eyes/arglasses_eng/New() ..() var/list/selector_uniforms = list( "standard AR-Engineering glasses"=/obj/item/clothing/glasses/omnihud/eng, @@ -243,12 +243,12 @@ ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) -/datum/gear/eyes/arglasses/med +/datum/gear/eyes/arglasses_med display_name = "AR-Medical glasses" path = /obj/item/clothing/glasses/omnihud/med allowed_roles = list(JOB_MEDICAL_DOCTOR,JOB_CHIEF_MEDICAL_OFFICER,JOB_CHEMIST,JOB_PARAMEDIC,JOB_GENETICIST, JOB_PSYCHIATRIST) -/datum/gear/eyes/arglasses/med/New() +/datum/gear/eyes/arglasses_med/New() ..() var/list/selector_uniforms = list( "standard AR-Medical glasses"=/obj/item/clothing/glasses/omnihud/med, @@ -256,13 +256,13 @@ ) gear_tweaks += new/datum/gear_tweak/path(sortAssoc(selector_uniforms)) -/datum/gear/eyes/arglasses/all +/datum/gear/eyes/arglasses_all display_name = "AR-Command glasses" path = /obj/item/clothing/glasses/omnihud/all cost = 2 allowed_roles = list(JOB_SITE_MANAGER,JOB_HEAD_OF_PERSONNEL) -/datum/gear/eyes/arglasses/all/New() +/datum/gear/eyes/arglasses_all/New() ..() var/list/selector_uniforms = list( "standard AR-Command glasses"=/obj/item/clothing/glasses/omnihud/all, From 2ba193f0abd8cafa02b14eebb174b8426c3b79c1 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 05:59:26 +0000 Subject: [PATCH 18/47] Automatic changelog for PR #17871 [ci skip] --- html/changelogs/AutoChangeLog-pr-17871.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17871.yml diff --git a/html/changelogs/AutoChangeLog-pr-17871.yml b/html/changelogs/AutoChangeLog-pr-17871.yml new file mode 100644 index 00000000000..f4e096e5e15 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17871.yml @@ -0,0 +1,4 @@ +author: "Kashargul" +delete-after: True +changes: + - code_imp: "more browse to datum browser" \ No newline at end of file From 2859bd38bd1f4b1b1e11ceb56292816834495c1b Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:02:54 +0000 Subject: [PATCH 19/47] Automatic changelog for PR #17864 [ci skip] --- html/changelogs/AutoChangeLog-pr-17864.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17864.yml diff --git a/html/changelogs/AutoChangeLog-pr-17864.yml b/html/changelogs/AutoChangeLog-pr-17864.yml new file mode 100644 index 00000000000..ed177b2e43a --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17864.yml @@ -0,0 +1,4 @@ +author: "Diana" +delete-after: True +changes: + - bugfix: "arglasses can be properly selected in the loadout" \ No newline at end of file From fb1aa593741e7f027a92933649ee9d5f7b34cb7c Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Wed, 18 Jun 2025 02:34:22 -0400 Subject: [PATCH 20/47] allow nondna blood smearing (#17880) * allow nondna blood smearing * use lazyor --- code/datums/forensics_crime.dm | 35 ++++---------------------- code/game/objects/effects/gibs.dm | 6 ++--- code/modules/reagents/reagents/core.dm | 3 +++ 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/code/datums/forensics_crime.dm b/code/datums/forensics_crime.dm index 2236c2b98f4..dadc3f26890 100644 --- a/code/datums/forensics_crime.dm +++ b/code/datums/forensics_crime.dm @@ -82,12 +82,7 @@ /datum/forensics_crime/proc/merge_prints(var/datum/forensics_crime/origin) if(!islist(origin?.fingerprints)) return - if(!fingerprints) - fingerprints = list() - if(fingerprints) - fingerprints |= origin.fingerprints - else - fingerprints = origin.fingerprints.Copy() + LAZYOR(fingerprints,origin.fingerprints) /// Clears data to default state, wiping all evidence /datum/forensics_crime/proc/clear_prints() @@ -148,12 +143,7 @@ /datum/forensics_crime/proc/merge_hiddenprints(var/datum/forensics_crime/origin) if(!islist(origin?.fingerprintshidden)) return - if(!fingerprintshidden) - fingerprintshidden = list() - if(fingerprintshidden) - fingerprintshidden |= origin.fingerprintshidden - else - fingerprintshidden = origin.fingerprintshidden.Copy() + LAZYOR(fingerprintshidden,origin.fingerprintshidden) ////////////////////////////////////////////////////////////////////////////////////// @@ -205,12 +195,7 @@ /datum/forensics_crime/proc/merge_fibres(var/datum/forensics_crime/origin) if(!islist(origin?.suit_fibres)) return - if(!suit_fibres) - suit_fibres = list() - if(suit_fibres) - suit_fibres |= origin.suit_fibres - else - suit_fibres = origin.suit_fibres.Copy() + LAZYOR(suit_fibres,origin.suit_fibres) /// Clears data to default state, wiping all evidence /datum/forensics_crime/proc/clear_fibres() @@ -268,22 +253,12 @@ /datum/forensics_crime/proc/merge_blooddna(var/datum/forensics_crime/origin, var/list/raw_list = null) // Copying from a list, blood on a mob's feet is stored as a list outside of forensics data if(raw_list) - if(!blood_DNA) - blood_DNA = list() - if(blood_DNA) - blood_DNA |= raw_list - else - blood_DNA = raw_list.Copy() + LAZYOR(blood_DNA,raw_list) return // Copying from another datums if(!islist(origin?.blood_DNA)) return - if(!blood_DNA) - blood_DNA = list() - if(blood_DNA) - blood_DNA |= origin.blood_DNA - else - blood_DNA = origin.blood_DNA.Copy() + LAZYOR(blood_DNA,origin.blood_DNA) /// Clears data to default state, wiping all evidence /datum/forensics_crime/proc/clear_blooddna() diff --git a/code/game/objects/effects/gibs.dm b/code/game/objects/effects/gibs.dm index 5e2f58fdc1b..8021ef414b7 100644 --- a/code/game/objects/effects/gibs.dm +++ b/code/game/objects/effects/gibs.dm @@ -47,10 +47,8 @@ gib.update_icon() gib.init_forensic_data() - if(MobDNA) - gib.add_blooddna(MobDNA,null) - else if(istype(src, /obj/effect/gibspawner/human)) // Probably a monkey - gib.add_blooddna(null,null) + gib.add_blooddna(MobDNA,null) + if(istype(location,/turf/)) var/list/directions = gibdirections[i] if(directions.len) diff --git a/code/modules/reagents/reagents/core.dm b/code/modules/reagents/reagents/core.dm index 400e04edd01..49df4025515 100644 --- a/code/modules/reagents/reagents/core.dm +++ b/code/modules/reagents/reagents/core.dm @@ -41,6 +41,9 @@ var/mob/living/carbon/alien/A = data["donor"] var/obj/effect/decal/cleanable/blood/B = blood_splatter(T, src, 1) B.add_blooddna(A.dna,A) + else + var/obj/effect/decal/cleanable/blood/B = blood_splatter(T, src, 1) + B.add_blooddna(null,null) /datum/reagent/blood/affect_ingest(var/mob/living/carbon/M, var/alien, var/removed) From a76b0c485029b56eca793ad1979810dbf0784823 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 06:34:53 +0000 Subject: [PATCH 21/47] Automatic changelog for PR #17880 [ci skip] --- html/changelogs/AutoChangeLog-pr-17880.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17880.yml diff --git a/html/changelogs/AutoChangeLog-pr-17880.yml b/html/changelogs/AutoChangeLog-pr-17880.yml new file mode 100644 index 00000000000..ee6765cf887 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17880.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - bugfix: "blood without dna from simple mobs will track onto shoes, floors and hands again" \ No newline at end of file From e84ded513df1ad74221646156b21503ae5730d77 Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Wed, 18 Jun 2025 08:12:18 -0400 Subject: [PATCH 22/47] Reduced memory usage of gene conflicts (#17886) * Reduced memory usage * standard lists * proper loop --- code/game/dna/genes/gene.dm | 23 ++++++----------------- code/game/gamemodes/setupgame.dm | 17 +++++------------ 2 files changed, 11 insertions(+), 29 deletions(-) diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index 8f20a6d348c..0ccf1172253 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -94,40 +94,29 @@ if(P == path) continue - // check if cached first... - if(!isnull(conflict_traits[P])) - if(quick_scan && conflict_traits[P]) + // Quickscan time + if(quick_scan) + if(P in conflict_traits) return TRUE continue // check trait if not. CONFLICT-O-TRON ENGAGE - conflict_traits[P] = FALSE - var/datum/trait/instance_test = GLOB.all_traits[P] if(path in instance_test.excludes) - conflict_traits[P] = TRUE + conflict_traits.Add(P) has_conflict = TRUE - // depending on scan mode we want to scan all, or only the first failure - if(quick_scan) - return TRUE continue for(var/V in linked_trait.var_changes) if(V == "flags") continue if(V in instance_test.var_changes) - conflict_traits[P] = TRUE + conflict_traits.Add(P) has_conflict = TRUE - // depending on scan mode we want to scan all, or only the first failure - if(quick_scan) - return TRUE continue for(var/V in linked_trait.var_changes_pref) if(V in instance_test.var_changes_pref) - conflict_traits[P] = TRUE + conflict_traits.Add(P) has_conflict = TRUE - // depending on scan mode we want to scan all, or only the first failure - if(quick_scan) - return TRUE continue return has_conflict diff --git a/code/game/gamemodes/setupgame.dm b/code/game/gamemodes/setupgame.dm index 63178a9cd96..45c4bfe29cf 100644 --- a/code/game/gamemodes/setupgame.dm +++ b/code/game/gamemodes/setupgame.dm @@ -41,24 +41,17 @@ if(blocks_remaining.len < 10) warning("DNA2: Blocks remaining is less than 10. The DNA_SE_LENGTH should be raised in dna.dm.") // Run conflict-o-tron on each traitgene all other traits... Lets setup an initial database of conflicts. - // Any remaining conflicts will be handled by the conflict-o-tron midround using a quicker scan flag log_world("DNA2: Checking trait gene conflicts") - var/list/compare_list = list() - for(var/datum/gene/trait/gene in GLOB.dna_genes) // was orginally all_traits, but having 300 entry lists for 50 genes at launch was pointless. the caches will fill as characters spawn with traits instead... A small tradeoff - if(gene.linked_trait) - compare_list.Add(gene.linked_trait.type) for(var/datum/gene/trait/gene in GLOB.dna_genes) - gene.has_conflict( compare_list, FALSE ) + gene.has_conflict( GLOB.all_traits, FALSE) // Check all traits beforehand to build the conflict list, so all future checks can be done with a quick contents check log_world("DNA2: Initial Conflict summary") - // Future coders: Don't worry, has_conflict() is run whenever a traitgene tries to enable itself as well, and adds to the trait conflict lists in each gene... // This is to setup the initial segments for the gene editing machines to sort gene segments with. for(var/datum/gene/trait/gene in GLOB.dna_genes) if(gene.conflict_traits.len) var/summery = "" for(var/path in gene.conflict_traits) - if(gene.conflict_traits[path]) // check if it actually conflicts - var/datum/trait/T = GLOB.all_traits[path] - if(summery != "") - summery += ", " - summery += "[T.name]" + var/datum/trait/T = GLOB.all_traits[path] + if(summery != "") + summery += ", " + summery += "[T.name]" log_world("DNA2: [gene.get_name()] - ([summery])") From c8e20e0e7b70dd7542f500d43abf8c2014de7a01 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Wed, 18 Jun 2025 12:12:53 +0000 Subject: [PATCH 23/47] Automatic changelog for PR #17886 [ci skip] --- html/changelogs/AutoChangeLog-pr-17886.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17886.yml diff --git a/html/changelogs/AutoChangeLog-pr-17886.yml b/html/changelogs/AutoChangeLog-pr-17886.yml new file mode 100644 index 00000000000..26fb2875a57 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17886.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - code_imp: "gene conflict list uses much less memory" \ No newline at end of file From d5da198430f11fee6897cfc3174406c395b0b04b Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 02:04:19 +0000 Subject: [PATCH 24/47] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-17635.yml | 8 ------- html/changelogs/AutoChangeLog-pr-17858.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17863.yml | 6 ----- html/changelogs/AutoChangeLog-pr-17864.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17871.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17879.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17880.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17885.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17886.yml | 4 ---- html/changelogs/archive/2025-06.yml | 26 ++++++++++++++++++++++ 10 files changed, 26 insertions(+), 42 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-17635.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17858.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17863.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17864.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17871.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17879.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17880.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17885.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17886.yml diff --git a/html/changelogs/AutoChangeLog-pr-17635.yml b/html/changelogs/AutoChangeLog-pr-17635.yml deleted file mode 100644 index 7bfc2e71d5a..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17635.yml +++ /dev/null @@ -1,8 +0,0 @@ -author: "TeleTubby2" -delete-after: True -changes: - - rscadd: "Voodoo dolls now render things all scrungly" - - rscadd: "Remote scene tools have some new utility verbs" - - qol: "Remote scene tools now show the status of their counterpart's wearer on examine" - - bugfix: "Various typos have been fixed for the recycler and recycling vendor" - - bugfix: "The maint recycler doesn't nuke your points after getting to 999 anymore. whoops." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17858.yml b/html/changelogs/AutoChangeLog-pr-17858.yml deleted file mode 100644 index 1b987aeafc5..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17858.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - bugfix: "Forensics data merging on objects without data but with data on the merging object, no longer crash on merge" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17863.yml b/html/changelogs/AutoChangeLog-pr-17863.yml deleted file mode 100644 index 4a0a995cd83..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17863.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "TeleTubby2" -delete-after: True -changes: - - rscadd: "Examines now embed extra information that used to be examine tab exclusive. This is a preference, so can be configured to remain as the old behavior." - - rscdel: "Removed the old verb for setting examine mode" - - refactor: "Examine Mode is now a TG preference" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17864.yml b/html/changelogs/AutoChangeLog-pr-17864.yml deleted file mode 100644 index ed177b2e43a..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17864.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Diana" -delete-after: True -changes: - - bugfix: "arglasses can be properly selected in the loadout" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17871.yml b/html/changelogs/AutoChangeLog-pr-17871.yml deleted file mode 100644 index f4e096e5e15..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17871.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Kashargul" -delete-after: True -changes: - - code_imp: "more browse to datum browser" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17879.yml b/html/changelogs/AutoChangeLog-pr-17879.yml deleted file mode 100644 index 7952cd971c4..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17879.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SatinIsle" -delete-after: True -changes: - - bugfix: "Fixed janitor shutter buttons on the tether." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17880.yml b/html/changelogs/AutoChangeLog-pr-17880.yml deleted file mode 100644 index ee6765cf887..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17880.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - bugfix: "blood without dna from simple mobs will track onto shoes, floors and hands again" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17885.yml b/html/changelogs/AutoChangeLog-pr-17885.yml deleted file mode 100644 index 262dd43eaac..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17885.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - bugfix: "reagent dispenser tanks no longer perform alchemy, turning all reagents into fuel spills" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17886.yml b/html/changelogs/AutoChangeLog-pr-17886.yml deleted file mode 100644 index 26fb2875a57..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17886.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - code_imp: "gene conflict list uses much less memory" \ No newline at end of file diff --git a/html/changelogs/archive/2025-06.yml b/html/changelogs/archive/2025-06.yml index 7f23b10e020..d18a588c648 100644 --- a/html/changelogs/archive/2025-06.yml +++ b/html/changelogs/archive/2025-06.yml @@ -256,3 +256,29 @@ - rscadd: Viruses now spawn with random names, depending on the symptoms, source, and random factors. - rscadd: Added medical names to all the common diseases +2025-06-19: + Diana: + - bugfix: arglasses can be properly selected in the loadout + Kashargul: + - code_imp: more browse to datum browser + SatinIsle: + - bugfix: Fixed janitor shutter buttons on the tether. + TeleTubby2: + - rscadd: Voodoo dolls now render things all scrungly + - rscadd: Remote scene tools have some new utility verbs + - qol: Remote scene tools now show the status of their counterpart's wearer on examine + - bugfix: Various typos have been fixed for the recycler and recycling vendor + - bugfix: The maint recycler doesn't nuke your points after getting to 999 anymore. + whoops. + - rscadd: Examines now embed extra information that used to be examine tab exclusive. + This is a preference, so can be configured to remain as the old behavior. + - rscdel: Removed the old verb for setting examine mode + - refactor: Examine Mode is now a TG preference + Willburd: + - bugfix: blood without dna from simple mobs will track onto shoes, floors and hands + again + - bugfix: Forensics data merging on objects without data but with data on the merging + object, no longer crash on merge + - bugfix: reagent dispenser tanks no longer perform alchemy, turning all reagents + into fuel spills + - code_imp: gene conflict list uses much less memory From 3cfc2c6e763d4946a54bff0d3b610b33f4bbf365 Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Thu, 19 Jun 2025 09:46:02 -0400 Subject: [PATCH 25/47] Bingle them viruses (#17881) * viruspage creation * virus unit testing * oop * tweaks * as check * use glob list * new data * scan data proper * counting * constructing lists * fix * wikicomp side * basictgui * list fix * tweaks * virus tgui page * . * . * . * unused * . * . * stack item it * genes * show gene conflicts * no static tags * fixed conflict gene report * tgui foundation for genes * handle gene conflicts once * .bingle page * . * . * import order fix * traits names must be unique --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- code/controllers/subsystems/internal_wiki.dm | 215 +++++++++++++++++- code/game/dna/genes/gene.dm | 6 +- code/modules/admin/admin.dm | 31 +-- code/modules/library/wikicomp.dm | 18 +- code/unit_tests/disease_tests.dm | 32 +++ .../{authohiss_tests.dm => trait_tests.dm} | 22 ++ .../tgui-panel/settings/settingsImExport.ts | 5 +- .../WikiCommon/WikiQuickElements.tsx | 10 +- .../WikiPages/WIkiDonationbanner.tsx | 2 +- .../WikiPages/WikiSearchPage.tsx | 6 + .../WikiPages/WikiSubPages/WikiGenePage.tsx | 52 +++++ .../WikiSubPages/WikiMaterialPage.tsx | 7 +- .../WikiPages/WikiSubPages/WikiVirusPage.tsx | 154 +++++++++++++ .../interfaces/PublicLibraryWiki/constants.ts | 63 +++++ .../interfaces/PublicLibraryWiki/index.tsx | 4 + .../interfaces/PublicLibraryWiki/types.ts | 33 +++ .../tgui/interfaces/common/RankIcon.tsx | 16 +- vorestation.dme | 3 +- 18 files changed, 642 insertions(+), 37 deletions(-) create mode 100644 code/unit_tests/disease_tests.dm rename code/unit_tests/{authohiss_tests.dm => trait_tests.dm} (65%) create mode 100644 tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiGenePage.tsx create mode 100644 tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiVirusPage.tsx diff --git a/code/controllers/subsystems/internal_wiki.dm b/code/controllers/subsystems/internal_wiki.dm index 69f3fa83433..cb9716da6df 100644 --- a/code/controllers/subsystems/internal_wiki.dm +++ b/code/controllers/subsystems/internal_wiki.dm @@ -20,6 +20,8 @@ SUBSYSTEM_DEF(internal_wiki) VAR_PRIVATE/list/drinkreact = list() VAR_PRIVATE/list/chemreact = list() VAR_PRIVATE/list/botseeds = list() + VAR_PRIVATE/list/viruses = list() + VAR_PRIVATE/list/genes = list() VAR_PRIVATE/list/foodrecipe = list() @@ -33,6 +35,8 @@ SUBSYSTEM_DEF(internal_wiki) VAR_PRIVATE/list/searchcache_chemreact = list() VAR_PRIVATE/list/searchcache_catalogs = list() VAR_PRIVATE/list/searchcache_botseeds = list() + VAR_PRIVATE/list/searchcache_viruses = list() + VAR_PRIVATE/list/searchcache_genes = list() VAR_PRIVATE/list/spoiler_entries = list() @@ -44,7 +48,7 @@ SUBSYSTEM_DEF(internal_wiki) VAR_PRIVATE/highest_cached_donator = null /datum/controller/subsystem/internal_wiki/stat_entry(msg) - msg = "P: [pages.len] | O: [ores.len] | M: [materials.len] | S: [smashers.len] | F: [foodrecipe.len] | D: [drinkreact.len] | C: [chemreact.len] | B: [botseeds.len] " + msg = "P: [pages.len] | O: [ores.len] | M: [materials.len] | S: [smashers.len] | F: [foodrecipe.len] | D: [drinkreact.len] | C: [chemreact.len] | B: [botseeds.len] | V: [viruses.len] | G: [genes.len] " return ..() /datum/controller/subsystem/internal_wiki/Initialize() @@ -53,8 +57,10 @@ SUBSYSTEM_DEF(internal_wiki) init_particle_smasher_data() init_reagent_data() init_seed_data() + init_virus_data() init_kitchen_data() init_lore_data() + init_gene_data() // Donation gag donation_goal = rand(min_donation,max_donation) donation_goal = round(donation_goal,1) @@ -126,6 +132,14 @@ SUBSYSTEM_DEF(internal_wiki) RETURN_TYPE(/datum/internal_wiki/page/seed) SHOULD_NOT_OVERRIDE(TRUE) return botseeds[search] +/datum/controller/subsystem/internal_wiki/proc/get_page_virus(var/search) + RETURN_TYPE(/datum/internal_wiki/page/virus) + SHOULD_NOT_OVERRIDE(TRUE) + return viruses[search] +/datum/controller/subsystem/internal_wiki/proc/get_page_gene(var/search) + RETURN_TYPE(/datum/internal_wiki/page/gene) + SHOULD_NOT_OVERRIDE(TRUE) + return genes[search] /datum/controller/subsystem/internal_wiki/proc/get_page_catalog(var/search) RETURN_TYPE(/datum/internal_wiki/page/catalog) SHOULD_NOT_OVERRIDE(TRUE) @@ -163,6 +177,14 @@ SUBSYSTEM_DEF(internal_wiki) RETURN_TYPE(/list) SHOULD_NOT_OVERRIDE(TRUE) return searchcache_botseeds +/datum/controller/subsystem/internal_wiki/proc/get_searchcache_viruses() + RETURN_TYPE(/list) + SHOULD_NOT_OVERRIDE(TRUE) + return searchcache_viruses +/datum/controller/subsystem/internal_wiki/proc/get_searchcache_genes() + RETURN_TYPE(/list) + SHOULD_NOT_OVERRIDE(TRUE) + return searchcache_genes /datum/controller/subsystem/internal_wiki/proc/get_catalogs() RETURN_TYPE(/list) SHOULD_NOT_OVERRIDE(TRUE) @@ -511,6 +533,37 @@ SUBSYSTEM_DEF(internal_wiki) botseeds["[S.display_name]"] = P pages.Add(P) +/datum/controller/subsystem/internal_wiki/proc/init_virus_data() + SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) + // viruses or diseases + for(var/datum/disease/D as anything in subtypesof(/datum/disease)) + if(initial(D.name) == DEVELOPER_WARNING_NAME) + continue + if(initial(D.visibility_flags) & HIDDEN_PANDEMIC) + spoiler_entries.Add(D) + continue + var/datum/internal_wiki/page/virus/P = new() + P.assemble(D) + searchcache_viruses.Add("[initial(D.medical_name)]") + viruses["[initial(D.medical_name)]"] = P + pages.Add(P) + +/datum/controller/subsystem/internal_wiki/proc/init_gene_data() + SHOULD_NOT_OVERRIDE(TRUE) + PRIVATE_PROC(TRUE) + // viruses or diseases + for(var/datum/gene/G in GLOB.dna_genes) + var/N = G.name + if(istype(G,/datum/gene/trait)) + var/datum/gene/trait/T = G + N = T.get_name() + var/datum/internal_wiki/page/gene/P = new() + P.assemble(G) + searchcache_genes.Add("[N]") + genes["[N]"] = P + pages.Add(P) + /datum/controller/subsystem/internal_wiki/proc/init_kitchen_data() SHOULD_NOT_OVERRIDE(TRUE) PRIVATE_PROC(TRUE) @@ -1311,6 +1364,166 @@ SUBSYSTEM_DEF(internal_wiki) data["name"] = catalog_record.name data["desc"] = catalog_record.desc +// VIRUSES +///////////////////////////////////////////// +/datum/internal_wiki/page/virus/assemble(var/datum/disease/D) + title = initial(D.name) + data["title"] = title + data["description"] = initial(D.desc) + data["form"] = initial(D.form) + data["agent"] = initial(D.agent) + data["danger"] = initial(D.danger) + data["max_stages"] = initial(D.max_stages) + + var/infectivity = "" + switch(initial(D.infectivity)) + if(0) + infectivity = "NA" + if(1 to 3) + infectivity = "Low" + if(4 to 7) + infectivity = "Medium" + if(8 to INFINITY) + infectivity = "High" + data["infectivity"] = infectivity + + var/resiliance = "" + switch(initial(D.cure_chance)) + if(0 to 8) + resiliance = "Extreme" + if(9 to 12) + resiliance = "High" + if(13 to 16) + resiliance = "Medium" + if(17 to INFINITY) + resiliance = "Low" + data["resiliance"] = resiliance + + var/discovery = "" + switch(initial(D.discovery_threshold)) + if(0 to 0.24) + discovery = "Extremely Elusive" + if(0.25 to 0.49) + discovery = "Difficult" + if(0.5 to 0.74) + discovery = "Moderate" + if(0.75 to 0.89) + discovery = "Easy" + if(0.9 to INFINITY) + discovery = "Trivial" + data["discovery"] = discovery + + var/spread_flags = initial(D.spread_flags) + var/spread_type = "NA" + if(spread_flags & DISEASE_SPREAD_CONTACT) + spread_type = "Contact" + else if(spread_flags & DISEASE_SPREAD_FLUIDS) + spread_type = "Fluids" + else if(spread_flags & DISEASE_SPREAD_BLOOD) + spread_type = "Blood" + else if(spread_flags & DISEASE_SPREAD_AIRBORNE) + spread_type = "Airborne" + else if(spread_flags & DISEASE_SPREAD_FALTERED) + spread_type = "Faltered" + data["spread"] = spread_type + + var/mod_flags = initial(D.virus_modifiers) + data["all_cures"] = mod_flags & NEEDS_ALL_CURES + data["aggressive"] = mod_flags & BYPASSES_IMMUNITY + var/flags = initial(D.disease_flags) + data["curable"] = flags & CURABLE + data["resistable"] = flags & CAN_RESIST + data["carriable"] = flags & CAN_CARRY + data["spread_dead"] = flags & SPREAD_DEAD + data["infect_synth"] = flags & INFECT_SYNTHETICS + +/datum/internal_wiki/page/virus/get_print() + var/body = "" + body += "Description: [data["description"]]
" + body += "
" + body += "Type: [data["form"]] - [data["agent"]]
" + body += "Hazard Level: [data["danger"]]
" + body += "Growth Stages: [data["max_stages"]]
" + body += "Curable: [(data["curable"]) ? "Yes" : "No"][!(data["all_cures"]) ? " - single treatment" : ""]
" + body += "Resistable: [(data["resistable"]) ? "Yes" : "No"]
" + body += "
" + // Transmission type + body += "Transmission: [data["spread"]] [(data["aggressive"]) ? "Aggressive" : ""]
" + if(data["carriable"]) + body += "Transmissable without symptoms
" + if(data["spread_dead"]) + body += "Transmissable from dead tissue
" + if(data["infect_synth"]) + body += "Inorganic pathogen
" + // Difficulty of discovery + body += "Discoverability: [data["discovery"]]
" + // Probability of spreading + body += "Infectivity: [data["infectivity"]]
" + // Probability of cure, 10 to 20 regularly + body += "Resiliance: [data["resiliance"]]
" + return body + +// GENES +///////////////////////////////////////////// +/datum/internal_wiki/page/gene/assemble(var/datum/gene/G) + if(istype(G,/datum/gene/trait)) + // Trait genetics + var/datum/gene/trait/T = G + title = T.get_name() + data["title"] = title + data["description"] = T.get_desc() + if(istype(T.linked_trait,/datum/trait/positive)) + if(!T.linked_trait.hidden) + data["trait_type"] = "Positive" + else + data["trait_type"] = "Super Power" // Likely eye lasers + else if(istype(T.linked_trait,/datum/trait/negative)) + if(!T.linked_trait.hidden) + data["trait_type"] = "Negative" + else + data["trait_type"] = "Disability" // Likely gibbings or such + else + if(!T.linked_trait.hidden) + data["trait_type"] = "Neutral" + else + data["trait_type"] = "Strange" // Not sure what neutrals are hidden, but just incase + // Conflicts + data["blockers"] = null + var/list/output_blockers = list() + var/list/blockers = T.conflict_traits + for(var/path in blockers) + var/datum/trait/TG = GLOB.all_traits[path] + output_blockers.Add(TG.name) + if(output_blockers.len) + data["blockers"] = output_blockers + else + // Old style gene + title = G.name + data["title"] = title + data["description"] = G.desc + data["trait_type"] = "Neutral" + data["blockers"] = null + var/list/bounds = GetDNABounds(G.block) + data["bounds_off_min"] = EncodeDNABlock(bounds[1]) // Minimum hex where gene is off + data["bounds_off_max"] = EncodeDNABlock(bounds[2]) // Maximum hex where gene is off + data["bounds_on_min"] = EncodeDNABlock(bounds[3]) // Minimum hex where gene is on + data["bounds_on_max"] = EncodeDNABlock(bounds[4]) // Maximum hex where gene is on + +/datum/internal_wiki/page/gene/get_print() + var/body = "" + body += "Description: [data["description"]]
" + body += "
" + body += "Type: [data["trait_type"]]
" + body += "Active Range: [data["bounds_on_min"]] - [data["bounds_on_max"]]
" + body += "Inactive Range: [data["bounds_off_min"]] - [data["bounds_off_max"]]
" + body += "
" + var/list/blockers = data["blockers"] + if(blockers) + body += "Suppressed By:
" + for(var/trait_name in blockers) + body += "-[trait_name]
" + return body + // MISC HELPERS //////////////////////////////////////////// /datum/internal_wiki/page/proc/print_allergens(var/list/allergens) diff --git a/code/game/dna/genes/gene.dm b/code/game/dna/genes/gene.dm index 0ccf1172253..7f61e5b7ef4 100644 --- a/code/game/dna/genes/gene.dm +++ b/code/game/dna/genes/gene.dm @@ -103,19 +103,19 @@ // check trait if not. CONFLICT-O-TRON ENGAGE var/datum/trait/instance_test = GLOB.all_traits[P] if(path in instance_test.excludes) - conflict_traits.Add(P) + conflict_traits |= P has_conflict = TRUE continue for(var/V in linked_trait.var_changes) if(V == "flags") continue if(V in instance_test.var_changes) - conflict_traits.Add(P) + conflict_traits |= P has_conflict = TRUE continue for(var/V in linked_trait.var_changes_pref) if(V in instance_test.var_changes_pref) - conflict_traits.Add(P) + conflict_traits |= P has_conflict = TRUE continue return has_conflict diff --git a/code/modules/admin/admin.dm b/code/modules/admin/admin.dm index c7b857595e9..58cf269dca1 100644 --- a/code/modules/admin/admin.dm +++ b/code/modules/admin/admin.dm @@ -89,13 +89,13 @@ GLOBAL_VAR_INIT(floorIsLava, 0) body += "\ Send back to Lobby | " var/muted = M.client.prefs.muted body += {"
"} + span_bold("Mute: ") + {" - \[IC | - OOC | - LOOC | - PRAY | - ADMINHELP | - DEADCHAT\] - (toggle all) + \[[(muted & MUTE_IC) ? span_red("IC") : span_blue("IC")] | + [(muted & MUTE_OOC) ? span_red("OOC") : span_blue("OOC")] | + [(muted & MUTE_LOOC) ? span_red("LOOC") : span_blue("LOOC")] | + [(muted & MUTE_PRAY) ? span_red("PRAY") : span_blue("PRAY")] | + [(muted & MUTE_ADMINHELP) ? span_red("ADMINHELP") : span_blue("ADMINHELP")] | + [(muted & MUTE_DEADCHAT) ? span_red("DEADCHAT") : span_blue("DEADCHAT")]\] + ([(muted & MUTE_ALL) ? span_red("toggle all") : span_blue("toggle all")]) "} body += {"

@@ -171,10 +171,13 @@ GLOBAL_VAR_INIT(floorIsLava, 0) if(istype(gene,/datum/gene/trait)) var/datum/gene/trait/T = gene tname = T.get_name() - var/bcolor="[(bstate)?"#006600":"#ff0000"]" - if(!bstate && M.dna.GetSEState(block)) // Gene isn't active, but the dna says it is... Was blocked by another gene! - bcolor="#d88d00" - body += "[bname][block]" // Traitgenes edit - show trait linked names on mouseover + if(bstate) + bname = span_green(bname) + else if(!bstate && M.dna.GetSEState(block)) // Gene isn't active, but the dna says it is... Was blocked by another gene! + bname = span_orange(bname) + else + bname = span_red(bname) + body += "[bname][block]" // Traitgenes edit - show trait linked names on mouseover else body += "[block]" body+="" @@ -231,9 +234,11 @@ GLOBAL_VAR_INIT(floorIsLava, 0) if(!f) body += " | " else f = 0 if(L in M.languages) - body += "[k]" + k = span_green(k) + body += "[k]" else - body += "[k]" + k = span_red(k) + body += "[k]" body += {"
"} diff --git a/code/modules/library/wikicomp.dm b/code/modules/library/wikicomp.dm index a74302e5c74..bd975632eaf 100644 --- a/code/modules/library/wikicomp.dm +++ b/code/modules/library/wikicomp.dm @@ -56,6 +56,8 @@ data["particle_data"] = null data["catalog_data"] = null data["ore_data"] = null + data["virus_data"] = null + data["gene_data"] = null data["sub_categories"] = null data["donated"] = SSinternal_wiki.get_donation_current() data["goal"] = SSinternal_wiki.get_donation_goal() @@ -112,6 +114,16 @@ if(P) data["ore_data"] = P.get_data() + if("Viruses") + data["search"] = SSinternal_wiki.get_searchcache_viruses() + if(P) + data["virus_data"] = P.get_data() + + if("Genes") + data["search"] = SSinternal_wiki.get_searchcache_genes() + if(P) + data["gene_data"] = P.get_data() + else data["search"] = list() @@ -203,6 +215,10 @@ new_page = SSinternal_wiki.get_page_particle(search) if(searchmode == "Ores") new_page = SSinternal_wiki.get_page_ore(search) + if(searchmode == "Viruses") + new_page = SSinternal_wiki.get_page_virus(search) + if(searchmode == "Genes") + new_page = SSinternal_wiki.get_page_gene(search) if(new_page == P) return FALSE @@ -211,7 +227,7 @@ if(P) doc_title = P.title - doc_body = P.get_print() // TODO - pass get_data() instead, as only printing should use get_print() + doc_body = P.get_print() else doc_title = "Error" doc_body = "Invalid data." diff --git a/code/unit_tests/disease_tests.dm b/code/unit_tests/disease_tests.dm new file mode 100644 index 00000000000..5327b5fdcc9 --- /dev/null +++ b/code/unit_tests/disease_tests.dm @@ -0,0 +1,32 @@ +/datum/unit_test/disease_must_be_valid + name = "DISEASE: All diseases must have valid data" + +/datum/unit_test/disease_must_be_valid/start_test() + var/failed = FALSE + var/list/used_ids = list() + + var/count = 0 + for(var/datum/disease/D as anything in subtypesof(/datum/disease)) + if(initial(D.name) == DEVELOPER_WARNING_NAME) + continue + + count++ + if(initial(D.medical_name) in used_ids) + log_unit_test("[D]: Disease - Had a reused medical name, this is used as an ID and must be unique.") + failed = TRUE + else + used_ids.Add(initial(D.medical_name)) + + if(!initial(D.name) || initial(D.name) == "") + log_unit_test("[D]: Disease - Lacks a name.") + failed = TRUE + + if(!initial(D.desc) || initial(D.desc) == "") + log_unit_test("[D]: Disease - Lacks a description.") + failed = TRUE + + if(failed) + fail("All diseases must have valid data.") + else + pass("All [count] diseases have proper data.") + return failed diff --git a/code/unit_tests/authohiss_tests.dm b/code/unit_tests/trait_tests.dm similarity index 65% rename from code/unit_tests/authohiss_tests.dm rename to code/unit_tests/trait_tests.dm index 0abc49fea67..411e61a4f59 100644 --- a/code/unit_tests/authohiss_tests.dm +++ b/code/unit_tests/trait_tests.dm @@ -1,3 +1,25 @@ +/datum/unit_test/all_traits_unique_names + name = "TRAITS: All traits shall have unique names" + +/datum/unit_test/all_traits_unique_names/start_test() + var/failed = FALSE + + var/list/used_named = list() + for(var/traitpath in GLOB.all_traits) + var/datum/trait/T = GLOB.all_traits[traitpath] + if(T.name in used_named) + log_unit_test("[T.type]: Trait - The name \"[T.name]\" is already in use.") + failed = TRUE + else + used_named.Add(T.name) + + if(failed) + fail("One or more traits shared a name.") + else + pass("All [GLOB.all_traits.len] have unique names.") + return failed + + /datum/unit_test/autohiss_shall_be_exclusive name = "TRAITS: Autohiss traits shall be exclusive" diff --git a/tgui/packages/tgui-panel/settings/settingsImExport.ts b/tgui/packages/tgui-panel/settings/settingsImExport.ts index 70771ef2fa1..5cd8f169dd9 100644 --- a/tgui/packages/tgui-panel/settings/settingsImExport.ts +++ b/tgui/packages/tgui-panel/settings/settingsImExport.ts @@ -5,7 +5,7 @@ import { importSettings } from './actions'; export function exportChatSettings( settings: Record, - pages: Record[], + pages: Record, ) { const opts: SaveFilePickerOptions = { id: `ss13-chatprefs-${Date.now()}`, @@ -18,8 +18,7 @@ export function exportChatSettings( ], }; - const pagesEntry: Record[] = []; - pagesEntry['chatPages'] = pages; + const pagesEntry = { chatPages: pages }; const exportObject = Object.assign(settings, pagesEntry); diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiCommon/WikiQuickElements.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiCommon/WikiQuickElements.tsx index d73cd69fa22..2a3eba9494a 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiCommon/WikiQuickElements.tsx +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiCommon/WikiQuickElements.tsx @@ -2,11 +2,17 @@ import { Box, LabeledList } from 'tgui-core/components'; import { zeroC } from '../constants'; -export const YesBox = (props) => { +export const YesNoBox = (props: { value: boolean }) => { + const { value } = props; + + return value ? : ; +}; + +const YesBox = (props) => { return Yes; }; -export const NoBox = (props) => { +const NoBox = (props) => { return No; }; diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WIkiDonationbanner.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WIkiDonationbanner.tsx index 58b6ddf9ae1..c4561885888 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WIkiDonationbanner.tsx +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WIkiDonationbanner.tsx @@ -107,7 +107,7 @@ const WikiDonationContent = (props: {
- {donated} / {goal}₮ + {donated}₮ / {goal}₮ {!hideButtons && donated < goal && ( diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSearchPage.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSearchPage.tsx index 70bebef56b8..edf98f6dbef 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSearchPage.tsx +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSearchPage.tsx @@ -9,10 +9,12 @@ import { WikiBotanyPage } from './WikiSubPages/WikiBotanyPage'; import { WikiCatalogPage } from './WikiSubPages/WIkiCatalogPage'; import { WikiChemistryPage } from './WikiSubPages/WikiChemistryPage'; import { WikiFoodPage } from './WikiSubPages/WikiFoodPage'; +import { WikiGenePage } from './WikiSubPages/WikiGenePage'; import { WikiMaterialPage } from './WikiSubPages/WikiMaterialPage'; import { WikiNoDataPage } from './WikiSubPages/WikiNoDataPage'; import { WikiOrePage } from './WikiSubPages/WikiOrePage'; import { WikiParticlePage } from './WikiSubPages/WikiParticlePage'; +import { WikiVirusPage } from './WikiSubPages/WikiVirusPage'; export const WikiSearchPage = ( props: { @@ -37,6 +39,8 @@ export const WikiSearchPage = ( searchmode, botany_data, ore_data, + virus_data, + gene_data, food_data, drink_data, chemistry_data, @@ -81,6 +85,8 @@ export const WikiSearchPage = ( ); tabs['Botany'] = !!botany_data && ; tabs['Ores'] = !!ore_data && ; + tabs['Viruses'] = !!virus_data && ; + tabs['Genes'] = !!gene_data && ; tabs['Materials'] = !!material_data && ( ); diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiGenePage.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiGenePage.tsx new file mode 100644 index 00000000000..b3ba07ab9bf --- /dev/null +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiGenePage.tsx @@ -0,0 +1,52 @@ +import { Box, LabeledList, Section, Stack } from 'tgui-core/components'; +import { capitalize } from 'tgui-core/string'; + +import { geneTypeToColor } from '../../constants'; +import type { GeneData } from '../../types'; +import { WikiList } from '../../WikiCommon/WikiListElements'; + +export const WikiGenePage = (props: { gene: GeneData }) => { + const { + title, + description, + trait_type, + bounds_off_min, + bounds_off_max, + bounds_on_min, + bounds_on_max, + blockers, + } = props.gene; + + return ( +
+ + + + + + {description ? description : 'No information available!'} + + + + + {trait_type} + + + + {bounds_on_min} - {bounds_on_max} + + + + + {bounds_off_min} - {bounds_off_max} + + + {!!blockers && ( + + )} + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiMaterialPage.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiMaterialPage.tsx index b706bd4616e..d91b86b4055 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiMaterialPage.tsx +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiMaterialPage.tsx @@ -5,11 +5,10 @@ import type { MaterialData } from '../../types'; import { ColorizedImage } from '../../WikiCommon/WikiColorIcon'; import { WikiSpoileredList } from '../../WikiCommon/WikiListElements'; import { - NoBox, NotAvilableBox, SupplyEntry, TemperatureBox, - YesBox, + YesNoBox, } from '../../WikiCommon/WikiQuickElements'; export const WikiMaterialPage = (props: { materials: MaterialData }) => { @@ -58,10 +57,10 @@ export const WikiMaterialPage = (props: { materials: MaterialData }) => { /> - {opacity > 0.5 ? : } + 0.5} /> - {conductive ? : } + diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiVirusPage.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiVirusPage.tsx new file mode 100644 index 00000000000..6e01b8f2fba --- /dev/null +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/WikiPages/WikiSubPages/WikiVirusPage.tsx @@ -0,0 +1,154 @@ +import { Box, Icon, LabeledList, Section, Stack } from 'tgui-core/components'; +import { capitalize } from 'tgui-core/string'; + +import { + virusDiscoveryToColor, + virusInfectivityToColor, + virusResilienceToColor, + virusSpreadToColor, + virusSpreadToIcon, + viursThreatToColor, +} from '../../constants'; +import type { VirusData } from '../../types'; +import { YesNoBox } from '../../WikiCommon/WikiQuickElements'; + +export const WikiVirusPage = (props: { virus: VirusData }) => { + const { + title, + description, + form, + agent, + danger, + infectivity, + resiliance, + max_stages, + discovery, + spread, + all_cures, + aggressive, + curable, + resistable, + carriable, + spread_dead, + infect_synth, + } = props.virus; + + return ( +
+ + + + + {description} + + + + {form} - {agent} + + + {danger} + + + {max_stages} + + + + + + + + {!all_cures ? ' - single treatment' : ''} + + + + + + + + + + + + + {spread} + + + + + {aggressive ? ( + <> + {' - Agressive '} + + + + + ) : ( + '' + )} + + + {!!carriable && ( + + + + {'>'} + + + + {'Transmissable without symptoms'} + + + + + )} + {!!spread_dead && ( + + + + {'>'} + + + + {'Transmissable from dead tissue'} + + + + + )} + {!!infect_synth && ( + + + + {'>'} + + + + {'Inorganic pathogen'} + + + + + )} + + + + + {discovery} + + + + {infectivity} + + + + {resiliance} + + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/constants.ts b/tgui/packages/tgui/interfaces/PublicLibraryWiki/constants.ts index 1fecef5b169..854ac32f83f 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/constants.ts +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/constants.ts @@ -7,6 +7,8 @@ export const WikiPages = [ 'Materials', 'Ores', 'Particle Physics', + 'Viruses', + 'Genes', ]; export const wikiAds = [ @@ -157,3 +159,64 @@ export const WikiTippOfTheDay = [ 'Your kitchen is a chemistry set too!', 'With Bingle, no sin will remain hidden from sight!', ]; + +export const viursThreatToColor = { + Beneficial: 'green', + Positive: 'olive', + 'No threat': undefined, + Minor: 'yellow', + Medium: '#f5ad42', + Harmful: 'orange', + Dangerous: 'red', + BIOHAZARD: '#b80000', + PANDEMIC: '#990202', +}; + +export const virusSpreadToColor = { + NA: 'label', + Contact: undefined, + Fluids: 'blue', + Blood: 'red', + Airborne: 'teal', + Faltered: 'grey', +}; + +export const virusSpreadToIcon = { + NA: undefined, + Contact: 'handshake', + Fluids: 'water', + Blood: 'droplet', + Airborne: 'wind', + Faltered: 'syringe', +}; + +export const virusDiscoveryToColor = { + 'Extremely Elusive': 'red', + Difficult: 'orange', + Moderate: 'yellow', + Easy: 'olive', + Trivial: undefined, +}; + +export const virusInfectivityToColor = { + High: 'red', + Medium: 'orange', + Low: 'yellow', + NA: undefined, +}; + +export const virusResilienceToColor = { + Extreme: 'red', + High: 'orange', + Medium: 'yellow', + Low: 'olive', +}; + +export const geneTypeToColor = { + Positive: 'green', + 'Super Power': 'teal', + Negative: 'red', + Disability: 'orange', + Neutral: undefined, + Strange: 'purple', +}; diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/index.tsx b/tgui/packages/tgui/interfaces/PublicLibraryWiki/index.tsx index e87367dc17c..9975148d552 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/index.tsx +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/index.tsx @@ -34,6 +34,8 @@ export const PublicLibraryWiki = (props) => { particle_data, catalog_data, ore_data, + virus_data, + gene_data, has_donated, donated, goal, @@ -148,6 +150,8 @@ export const PublicLibraryWiki = (props) => { chemistry_data={chemistry_data} botany_data={botany_data} ore_data={ore_data} + virus_data={virus_data} + gene_data={gene_data} material_data={material_data} particle_data={particle_data} catalog_data={catalog_data} diff --git a/tgui/packages/tgui/interfaces/PublicLibraryWiki/types.ts b/tgui/packages/tgui/interfaces/PublicLibraryWiki/types.ts index 3c99f720f01..2012ed00f27 100644 --- a/tgui/packages/tgui/interfaces/PublicLibraryWiki/types.ts +++ b/tgui/packages/tgui/interfaces/PublicLibraryWiki/types.ts @@ -22,6 +22,8 @@ export type PageData = { chemistry_data: ReagentData | null; drink_data: DrinkData | null; food_data: FoodData | null; + virus_data: VirusData | null; + gene_data: GeneData | null; }; export type FoodData = DrinkData & Partial<{ recipe: RedipeData }>; @@ -84,6 +86,37 @@ export type OreData = { grind_reagents: Record; } & Icon; +export type VirusData = { + title: string; + description: string | null; + form: string; + agent: string; + danger: string; + infectivity: string; + resiliance: string; + max_stages: number; + discovery: string; + spread: string; + all_cures: BooleanLike; + aggressive: BooleanLike; + curable: BooleanLike; + resistable: BooleanLike; + carriable: BooleanLike; + spread_dead: BooleanLike; + infect_synth: BooleanLike; +}; + +export type GeneData = { + title: string; + description: string | null; + trait_type: string; + blockers: string[] | null; + bounds_off_min: string; + bounds_off_max: string; + bounds_on_min: string; + bounds_on_max: string; +}; + export type ParticleData = { title: string; req_mat: string | null; diff --git a/tgui/packages/tgui/interfaces/common/RankIcon.tsx b/tgui/packages/tgui/interfaces/common/RankIcon.tsx index ae08f32484d..35e1c9a73d9 100644 --- a/tgui/packages/tgui/interfaces/common/RankIcon.tsx +++ b/tgui/packages/tgui/interfaces/common/RankIcon.tsx @@ -591,10 +591,10 @@ const rank2color = { 'Off-duty Explorer': 'white', 'Off-duty Worker': 'white', // AI / Robot - AI: 'maroon', - Cyborg: 'maroon', - Robot: 'maroon', - Drone: 'maroon', + AI: '#800000', + Cyborg: '#800000', + Robot: '#800000', + Drone: '#800000', // Clown / Mime Clown: 'green', Jester: 'green', @@ -627,11 +627,11 @@ const rank2color = { Security: 'red', Combat: 'yellow', Engineering: 'orange', - Gravekeeper: 'maroon', + Gravekeeper: '#800000', Lost: 'grey', - Protector: 'maroon', - Mechanist: 'maroon', - 'Combat Medic': 'maroon', + Protector: '#800000', + Mechanist: '#800000', + 'Combat Medic': '#800000', }; type rank_icon = { rank: string; color: string }; diff --git a/vorestation.dme b/vorestation.dme index 304bc31fd33..7d1e537e02a 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -4548,10 +4548,10 @@ #include "code\modules\xenobio\machinery\processor.dm" #include "code\modules\xgm\xgm_gas_data.dm" #include "code\modules\xgm\xgm_gas_mixture.dm" -#include "code\unit_tests\authohiss_tests.dm" #include "code\unit_tests\clothing_tests.dm" #include "code\unit_tests\cosmetic_tests.dm" #include "code\unit_tests\decl_tests.dm" +#include "code\unit_tests\disease_tests.dm" #include "code\unit_tests\genetics_tests.dm" #include "code\unit_tests\language_tests.dm" #include "code\unit_tests\loadout_tests.dm" @@ -4566,6 +4566,7 @@ #include "code\unit_tests\robot_tests.dm" #include "code\unit_tests\sqlite_tests.dm" #include "code\unit_tests\subsystem_tests.dm" +#include "code\unit_tests\trait_tests.dm" #include "code\unit_tests\unit_test.dm" #include "code\unit_tests\unit_test_vr.dm" #include "code\unit_tests\vore_tests_vr.dm" From ff07e2aa4f2dd830942fee207bb2a781bda37ea9 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 13:46:31 +0000 Subject: [PATCH 26/47] Automatic changelog for PR #17881 [ci skip] --- html/changelogs/AutoChangeLog-pr-17881.yml | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17881.yml diff --git a/html/changelogs/AutoChangeLog-pr-17881.yml b/html/changelogs/AutoChangeLog-pr-17881.yml new file mode 100644 index 00000000000..408964ad4d4 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17881.yml @@ -0,0 +1,5 @@ +author: "Kash Guti Will" +delete-after: True +changes: + - rscadd: "Adds genes and diseases to bingle's database so symptoms and genetics stuff can be looked up ingame" + - code_imp: "added unit test for virus names and trait names to prevent tgui issues" \ No newline at end of file From 3b4bcef49b46033f7579249fb878f73aaa3e5231 Mon Sep 17 00:00:00 2001 From: Cameron Lennox Date: Thu, 19 Jun 2025 18:56:26 -0400 Subject: [PATCH 27/47] Better borg modules (#17507) * Combines all the engineering borg tools into one multitool Sprites from /tg/ Combines: Wirecutters, Crowbar, Welder, Wrench, Crowbar, Screwdriver * this is a WAY better way to do it * a * dont worry about overlays anymore * Better grippers and better lists In which I go insane having to continually patch exploit after exploit in gripper code, resulting in it looking like a mad man's scribbles. * No deleting shoes when putting them on someone * empty pocket is now empty * RENAMING stuff instead of copypaste * MORE borg stuff * more bugproofing * Admin proc * this * this * Framework for TGUI * Update robot_simple_items.dm * Fix * Framework v2 * Update ModifyRobotMultiBelt.tsx * goooo * Update modify_robot.dm * modify * sub category * . * string to define * some globs * more * . * . * . * add sanely * push that real quick * . * sanity * . * . * . * . * . * . * . * Cut * . * gives to those that need * push * clean up * auto fix * . * . * . * getmodule * Update robot_simple_items.dm * . * untyped * Update stack.dm * . * . * . * . * - * . * Update _map_selection.dm * Update _map_selection.dm --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- code/__defines/robot.dm | 9 + code/_helpers/global_lists_vr.dm | 263 ++++- code/_onclick/cyborg.dm | 2 +- .../subsystems/overmap_renamer_vr.dm | 4 +- code/game/objects/items/blueprints_vr.dm | 28 +- .../objects/items/devices/denecrotizer_vr.dm | 10 +- code/game/objects/items/devices/multitool.dm | 7 - .../game/objects/items/stacks/matter_synth.dm | 14 +- code/game/objects/items/stacks/stack.dm | 2 + .../objects/items/weapons/surgery_tools.dm | 39 - .../objects/items/weapons/tools/crowbar.dm | 7 - .../items/weapons/tools/screwdriver.dm | 6 - .../items/weapons/tools/weldingtool.dm | 12 +- .../items/weapons/tools/wirecutters.dm | 6 - .../objects/items/weapons/tools/wrench.dm | 6 - .../structures/crates_lockers/largecrate.dm | 2 +- code/game/objects/structures/gargoyle.dm | 4 +- .../objects/structures/ghost_pods/event_vr.dm | 8 +- .../structures/ghost_pods/ghost_pods.dm | 5 +- .../structures/ghost_pods/ghost_pods_vr.dm | 8 +- code/game/objects/trash_eating.dm | 2 +- code/modules/admin/modify_robot.dm | 65 ++ code/modules/admin/verbs/buildmode.dm | 4 +- .../overmap_renamer/debrisfield_renamer.dm | 2 +- .../awaymissions/overmap_renamer/readme.md | 4 +- code/modules/events/roaming_wildlife.dm | 4 +- code/modules/materials/materials/glass.dm | 2 +- code/modules/mob/dead/observer/observer_vr.dm | 4 +- .../mob/living/carbon/human/death_vr.dm | 2 +- .../mob/living/carbon/human/human_defense.dm | 2 +- .../mob/living/carbon/human/stripping.dm | 21 +- code/modules/mob/living/living_defense.dm | 2 +- .../silicon/robot/dogborg/dog_sleeper.dm | 4 +- .../living/silicon/robot/drone/drone_items.dm | 430 --------- .../mob/living/silicon/robot/inventory.dm | 26 +- .../silicon/robot/robot_modules/event.dm | 12 +- .../silicon/robot/robot_modules/station.dm | 171 +--- .../silicon/robot/robot_modules/syndicate.dm | 41 +- .../silicon/robot/robot_simple_items.dm | 896 ++++++++++++++++++ .../silicon/robot/subtypes/boozeborg.dm | 4 +- .../subtypes/animal/alien animals/teppi.dm | 4 +- .../subtypes/animal/farm animals/chicken.dm | 4 +- code/modules/overmap/sectors.dm | 2 +- code/modules/power/cable.dm | 15 - .../vore/eating/bellymodes_datum_vr.dm | 4 +- code/modules/vore/eating/living_vr.dm | 2 +- code/modules/vore/eating/transforming_vr.dm | 4 +- .../artifacts/replicator_vr.dm | 4 +- code/modules/xenobio/items/extracts_vr.dm | 44 +- code/modules/xenobio/items/slimepotions_vr.dm | 2 +- icons/obj/tools_robot.dmi | Bin 0 -> 15045 bytes .../ModifyRobotTabs/ModifyRobotAccess.tsx | 3 +- .../ModifyRobotTabs/ModifyRobotComponent.tsx | 2 +- .../ModifyRobotTabs/ModifyRobotModules.tsx | 4 +- .../ModifyRobotTabs/ModifyRobotRadio.tsx | 2 +- .../ModifyRobotSpecialModules.tsx | 32 + .../ModifyRobotTabs/ModifyRobotUpgrades.tsx | 150 +-- .../SubTabs/ModifyRobotMultiBelt.tsx | 141 +++ .../{ => SubTabs}/ModifyRobotPKA.tsx | 9 +- .../tgui/interfaces/ModifyRobot/index.tsx | 274 +++--- .../tgui/interfaces/ModifyRobot/types.ts | 11 + vorestation.dme | 1 + 62 files changed, 1772 insertions(+), 1082 deletions(-) create mode 100644 code/modules/mob/living/silicon/robot/robot_simple_items.dm create mode 100644 icons/obj/tools_robot.dmi create mode 100644 tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotSpecialModules.tsx create mode 100644 tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotMultiBelt.tsx rename tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/{ => SubTabs}/ModifyRobotPKA.tsx (94%) diff --git a/code/__defines/robot.dm b/code/__defines/robot.dm index 30026857d7e..55fe9eebd19 100644 --- a/code/__defines/robot.dm +++ b/code/__defines/robot.dm @@ -1 +1,10 @@ #define DEFAULT_ROBOT_SPRITE_NAME "M-USE NanoTrasen" + +// Synthesizers +#define METAL_SYNTH "Metal Synthesizer" +#define PLASTEEL_SYNTH "Plasteel Synthesizer" +#define GLASS_SYNTH "Glass Synthesizer" +#define WOOD_SYNTH "Wood Synthesizer" +#define PLASTIC_SYNTH "Plastic Synthesizer" +#define WIRE_SYNTH "Wire Synthesizer" +#define CLOTH_SYNTH "Cloth Synthesizer" diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index a5645a4f8d9..c23b2729de9 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -2,7 +2,7 @@ * VOREStation global lists */ -var/global/list/hair_accesories_list= list()// Stores /datum/sprite_accessory/hair_accessory indexed by type +GLOBAL_LIST_EMPTY(hair_accesories_list) // Stores /datum/sprite_accessory/hair_accessory indexed by type GLOBAL_LIST_EMPTY(negative_traits) // Negative custom species traits, indexed by path GLOBAL_LIST_EMPTY(neutral_traits) // Neutral custom species traits, indexed by path GLOBAL_LIST_EMPTY(positive_traits) // Positive custom species traits, indexed by path @@ -11,11 +11,11 @@ GLOBAL_LIST_EMPTY(everyone_traits_neutral) // Neutral traits available to all sp GLOBAL_LIST_EMPTY(everyone_traits_negative) // Neutral traits available to all species, indexed by path GLOBAL_LIST_EMPTY(traits_costs) // Just path = cost list, saves time in char setup GLOBAL_LIST_EMPTY(all_traits) // All of 'em at once (same instances) -var/global/list/active_ghost_pods = list() +GLOBAL_LIST_EMPTY(active_ghost_pods) //Global vars for making the overmap_renamer subsystem. //Collects all instances by reference of visitable overmap objects of /obj/effect/overmap/visitable like the debris field. -var/global/list/visitable_overmap_object_instances = list() +GLOBAL_LIST_EMPTY(visitable_overmap_object_instances) GLOBAL_LIST_INIT(sensorpreflist, list("Off", "Binary", "Vitals", "Tracking", "No Preference")) @@ -41,7 +41,7 @@ GLOBAL_LIST_INIT(vantag_choices_list, list( VANTAG_KILL = "Be Killed")) //Blacklist to exclude items from object ingestion. Digestion blacklist located in digest_act_vr.dm -var/global/list/item_vore_blacklist = list( +GLOBAL_LIST_INIT(item_vore_blacklist, list( /obj/item/hand_tele, /obj/item/card/id/gold/captain/spare, /obj/item/gun, @@ -50,7 +50,7 @@ var/global/list/item_vore_blacklist = list( /obj/item/areaeditor/blueprints, /obj/item/clothing/head/helmet/space, /obj/item/disk/nuclear, - /obj/item/clothing/suit/storage/hooded/wintercoat/roiz) + /obj/item/clothing/suit/storage/hooded/wintercoat/roiz)) //Classic Vore sounds GLOBAL_LIST_INIT(classic_vore_sounds, list( @@ -155,7 +155,7 @@ GLOBAL_LIST_INIT(global_vore_egg_types, list( "Spotted Pink" )) -var/global/list/tf_vore_egg_types = list( +GLOBAL_LIST_INIT(tf_vore_egg_types, list( "Unathi" = /obj/item/storage/vore_egg/unathi, "Tajara" = /obj/item/storage/vore_egg/tajaran, "Akula" = /obj/item/storage/vore_egg/shark, @@ -188,9 +188,9 @@ var/global/list/tf_vore_egg_types = list( "Purple" = /obj/item/storage/vore_egg/purple, "Red" = /obj/item/storage/vore_egg/red, "Rainbow" = /obj/item/storage/vore_egg/rainbow, - "Spotted Pink" = /obj/item/storage/vore_egg/pinkspots) + "Spotted Pink" = /obj/item/storage/vore_egg/pinkspots)) -var/global/list/edible_trash = list(/obj/item/broken_device, +GLOBAL_LIST_INIT(edible_trash, list(/obj/item/broken_device, /obj/item/clothing/accessory/collar, /obj/item/communicator, /obj/item/clothing/mask, @@ -248,7 +248,7 @@ var/global/list/edible_trash = list(/obj/item/broken_device, /obj/item/roulette_ball, /obj/item/pizzabox, /obj/item/card/id - ) + )) GLOBAL_LIST_INIT(contamination_flavors, list( "Generic" = GLOB.contamination_flavors_generic, @@ -536,7 +536,7 @@ GLOBAL_LIST_EMPTY(existing_solargrubs) paths = subtypesof(/datum/sprite_accessory/hair_accessory) for(var/path in paths) var/datum/sprite_accessory/hair_accessory/instance = new path() - hair_accesories_list[path] = instance + GLOB.hair_accesories_list[path] = instance // Custom species traits paths = typesof(/datum/trait) - /datum/trait - /datum/trait/negative - /datum/trait/neutral - /datum/trait/positive @@ -595,7 +595,7 @@ GLOBAL_LIST_EMPTY(weavable_structures) GLOBAL_LIST_EMPTY(weavable_items) -var/global/list/xenobio_metal_materials_normal = list( +GLOBAL_LIST_INIT(xenobio_metal_materials_normal, list( /obj/item/stack/material/steel = 20, /obj/item/stack/material/glass = 15, /obj/item/stack/material/plastic = 12, @@ -609,9 +609,9 @@ var/global/list/xenobio_metal_materials_normal = list( /obj/item/stack/material/copper = 4, /obj/item/stack/material/tin = 4, /obj/item/stack/material/bronze = 4, - /obj/item/stack/material/aluminium = 4) + /obj/item/stack/material/aluminium = 4)) -var/global/list/xenobio_metal_materials_adv = list( +GLOBAL_LIST_INIT(xenobio_metal_materials_adv, list( /obj/item/stack/material/glass/reinforced = 15, /obj/item/stack/material/marble = 10, /obj/item/stack/material/plasteel = 10, @@ -626,9 +626,9 @@ var/global/list/xenobio_metal_materials_adv = list( /obj/item/stack/material/durasteel = 2, /obj/item/stack/material/painite = 1, /obj/item/stack/material/void_opal = 1, - /obj/item/stack/material/quartz = 1) + /obj/item/stack/material/quartz = 1)) -var/global/list/xenobio_metal_materials_weird = list( +GLOBAL_LIST_INIT(xenobio_metal_materials_weird, list( /obj/item/stack/material/cloth = 10, /obj/item/stack/material/leather = 5, /obj/item/stack/material/fiber = 5, @@ -637,29 +637,29 @@ var/global/list/xenobio_metal_materials_weird = list( /obj/item/stack/material/snowbrick = 3, /obj/item/stack/material/flint = 3, /obj/item/stack/material/stick = 3, - /obj/item/stack/material/chitin = 1) + /obj/item/stack/material/chitin = 1)) -var/global/list/xenobio_silver_materials_basic = list( +GLOBAL_LIST_INIT(xenobio_silver_materials_basic, list( /obj/item/stack/material/silver = 10, /obj/item/stack/material/uranium = 8, /obj/item/stack/material/gold = 6, /obj/item/stack/material/titanium = 4, - /obj/item/stack/material/phoron = 1) + /obj/item/stack/material/phoron = 1)) -var/global/list/xenobio_silver_materials_adv = list( +GLOBAL_LIST_INIT(xenobio_silver_materials_adv, list( /obj/item/stack/material/deuterium = 5, /obj/item/stack/material/tritium = 5, /obj/item/stack/material/osmium = 5, /obj/item/stack/material/mhydrogen = 3, /obj/item/stack/material/diamond = 2, - /obj/item/stack/material/verdantium = 1) + /obj/item/stack/material/verdantium = 1)) -var/global/list/xenobio_silver_materials_special = list( +GLOBAL_LIST_INIT(xenobio_silver_materials_special, list( /obj/item/stack/material/valhollide = 1, /obj/item/stack/material/morphium = 1, - /obj/item/stack/material/supermatter = 1) + /obj/item/stack/material/supermatter = 1)) -var/global/list/xenobio_gold_mobs_hostile = list( +GLOBAL_LIST_INIT(xenobio_gold_mobs_hostile, list( /mob/living/simple_mob/vore/alienanimals/space_jellyfish, /mob/living/simple_mob/vore/alienanimals/skeleton, /mob/living/simple_mob/vore/alienanimals/space_ghost, @@ -721,9 +721,9 @@ var/global/list/xenobio_gold_mobs_hostile = list( /mob/living/simple_mob/vore/sect_queen, /mob/living/simple_mob/vore/weretiger, /mob/living/simple_mob/vore/wolf, - /mob/living/simple_mob/vore/xeno_defanged) + /mob/living/simple_mob/vore/xeno_defanged)) -var/global/list/xenobio_gold_mobs_bosses = list( +GLOBAL_LIST_INIT(xenobio_gold_mobs_bosses, list( /mob/living/simple_mob/animal/giant_spider/broodmother, /mob/living/simple_mob/vore/leopardmander, /mob/living/simple_mob/vore/leopardmander/blue, @@ -731,9 +731,9 @@ var/global/list/xenobio_gold_mobs_bosses = list( /mob/living/simple_mob/vore/greatwolf, /mob/living/simple_mob/vore/greatwolf/black, /mob/living/simple_mob/vore/greatwolf/grey, - /mob/living/simple_mob/vore/bigdragon) + /mob/living/simple_mob/vore/bigdragon)) -var/global/list/xenobio_gold_mobs_safe = list( +GLOBAL_LIST_INIT(xenobio_gold_mobs_safe, list( /mob/living/simple_mob/vore/alienanimals/dustjumper, /mob/living/simple_mob/animal/passive/chicken, /mob/living/simple_mob/animal/passive/cow, @@ -774,9 +774,9 @@ var/global/list/xenobio_gold_mobs_safe = list( /mob/living/simple_mob/vore/redpanda, /mob/living/simple_mob/vore/sheep, /mob/living/simple_mob/vore/squirrel, - /mob/living/simple_mob/vore/solargrub) + /mob/living/simple_mob/vore/solargrub)) -var/global/list/xenobio_gold_mobs_birds = list(/mob/living/simple_mob/animal/passive/bird/black_bird, +GLOBAL_LIST_INIT(xenobio_gold_mobs_birds, list(/mob/living/simple_mob/animal/passive/bird/black_bird, /mob/living/simple_mob/animal/passive/bird/azure_tit, /mob/living/simple_mob/animal/passive/bird/european_robin, /mob/living/simple_mob/animal/passive/bird/goldcrest, @@ -796,9 +796,9 @@ var/global/list/xenobio_gold_mobs_birds = list(/mob/living/simple_mob/animal/pas /mob/living/simple_mob/animal/passive/bird/parrot/cockatiel/grey, /mob/living/simple_mob/animal/passive/bird/parrot/sulphur_cockatoo, /mob/living/simple_mob/animal/passive/bird/parrot/white_cockatoo, - /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo) //There's too dang many + /mob/living/simple_mob/animal/passive/bird/parrot/pink_cockatoo)) //There's too dang many -var/global/list/xenobio_cerulean_potions = list( +GLOBAL_LIST_INIT(xenobio_cerulean_potions, list( /obj/item/slimepotion/enhancer, /obj/item/slimepotion/stabilizer, /obj/item/slimepotion/mutator, @@ -816,9 +816,9 @@ var/global/list/xenobio_cerulean_potions = list( /obj/item/slimepotion/reinvigoration, /obj/item/slimepotion/mimic, /obj/item/slimepotion/sapience, - /obj/item/slimepotion/obedience) + /obj/item/slimepotion/obedience)) -var/global/list/xenobio_rainbow_extracts = list( +GLOBAL_LIST_INIT(xenobio_rainbow_extracts, list( /obj/item/slime_extract/grey = 2, /obj/item/slime_extract/metal = 3, /obj/item/slime_extract/blue = 3, @@ -840,13 +840,13 @@ var/global/list/xenobio_rainbow_extracts = list( /obj/item/slime_extract/ruby = 3, /obj/item/slime_extract/emerald = 3, /obj/item/slime_extract/light_pink = 1, - /obj/item/slime_extract/rainbow = 1) + /obj/item/slime_extract/rainbow = 1)) //// Wildlife lists //Listed by-type. Under each type are lists of lists that contain 'groupings' of wildlife. Sorted from 1 to 5 by threat level. -var/global/list/event_wildlife_aquatic = list( +GLOBAL_LIST_INIT(event_wildlife_aquatic, list( list( list(/mob/living/simple_mob/animal/passive/fish/koi = 1, /mob/living/simple_mob/animal/passive/fish/pike = 2, @@ -875,9 +875,9 @@ var/global/list/event_wildlife_aquatic = list( list( list(/mob/living/simple_mob/animal/sif/hooligan_crab = 1) ) - ) + )) -var/global/list/event_wildlife_roaming = list( +GLOBAL_LIST_INIT(event_wildlife_roaming, list( list( list(/mob/living/simple_mob/animal/passive/mouse/jerboa = 1, /mob/living/simple_mob/animal/passive/mouse/black = 2, @@ -931,7 +931,7 @@ var/global/list/event_wildlife_roaming = list( list(/mob/living/simple_mob/vore/aggressive/giant_snake = 1), list(/mob/living/simple_mob/vore/aggressive/corrupthound = 1) ) - ) + )) GLOBAL_LIST_INIT(selectable_speech_bubbles, list( @@ -984,15 +984,15 @@ GLOBAL_LIST_INIT(selectable_speech_bubbles, list( // These lists are, obviously, unfinished. // ALLOWING BUILDING IN AN AREA: -// If you want someone to be able to build a new area in a place, add the area to the 'BUILDABLE_AREA_TYPES' and 'blacklisted_areas' -// BUILDABLE_AREA_TYPES means they can build an area there. The blacklisted_areas means they CAN NOT EXPAND that area. No making space bigger! +// If you want someone to be able to build a new area in a place, add the area to the 'GLOB.BUILDABLE_AREA_TYPES' and 'GLOB.blacklisted_areas' +// GLOB.BUILDABLE_AREA_TYPES means they can build an area there. The GLOB.blacklisted_areas means they CAN NOT EXPAND that area. No making space bigger! // DISALLOW BUILDING/AREA MANIPULATION IN AN AREA (OR A TURF TYPE): // Likewise, if you want someone to never ever EVER be able to do anything area generation/expansion related to an area -// Then add it to SPECIALS and area_or_turf_fail_types +// Then add it to GLOB.SPECIALS and GLOB.area_or_turf_fail_types // If you want someone to -var/global/list/BUILDABLE_AREA_TYPES = list( +GLOBAL_LIST_INIT(BUILDABLE_AREA_TYPES, list( /area/space, /area/mine, // /area/surface/outside, //SC @@ -1005,9 +1005,9 @@ var/global/list/BUILDABLE_AREA_TYPES = list( /area/offmap/aerostat/surface, /area/tether_away/beach, /area/tether_away/cave, -) +)) -var/static/list/blacklisted_areas = typecacheof(list( +GLOBAL_LIST_INIT(blacklisted_areas, typecacheof(list( /area/space, /area/mine, // /area/surface/outside, //SC @@ -1022,9 +1022,9 @@ var/static/list/blacklisted_areas = typecacheof(list( /area/offmap/aerostat/surface, /area/tether_away/beach, /area/tether_away/cave - )) + ))) -var/global/list/SPECIALS = list( +GLOBAL_LIST_INIT(SPECIALS, list( /turf/space, /area/shuttle, /area/admin, @@ -1044,9 +1044,9 @@ var/global/list/SPECIALS = list( /area/submap/casino_event, /area/vr // /area/derelict //commented out, all hail derelict-rebuilders! -) +)) -var/global/list/area_or_turf_fail_types = typecacheof(list( +GLOBAL_LIST_INIT(area_or_turf_fail_types, typecacheof(list( /turf/space, /area/shuttle, /area/admin, @@ -1065,4 +1065,169 @@ var/global/list/area_or_turf_fail_types = typecacheof(list( /area/submap/virgo2, /area/submap/event, /area/submap/casino_event - )) + ))) + +//GRIPPERS!!! +#define BASIC_GRIPPER \ + /obj/item/cell, \ + /obj/item/airlock_electronics, \ + /obj/item/tracker_electronics, \ + /obj/item/module/power_control, \ + /obj/item/stock_parts, \ + /obj/item/frame, \ + /obj/item/camera_assembly, \ + /obj/item/tank, \ + /obj/item/circuitboard, \ + /obj/item/smes_coil, \ + /obj/item/fuel_assembly, \ + /obj/item/stack/tile/floor + +#define OMNI_GRIPPER \ + /obj/item + +#define MINER_GRIPPER \ + /obj/item/cell, \ + /obj/item/stock_parts + +#define SECURITY_GRIPPER \ + /obj/item/paper, \ + /obj/item/paper_bundle, \ + /obj/item/pen, \ + /obj/item/sample, \ + /obj/item/forensics/sample_kit, \ + /obj/item/taperecorder, \ + /obj/item/rectape, \ + /obj/item/uv_light + +#define PAPERWORK_GRIPPER \ + /obj/item/clipboard, \ + /obj/item/paper, \ + /obj/item/paper_bundle, \ + /obj/item/card/id, \ + /obj/item/book, \ + /obj/item/newspaper + +#define MEDICAL_GRIPPER \ + /obj/item/reagent_containers/glass, \ + /obj/item/storage/pill_bottle, \ + /obj/item/reagent_containers/pill, \ + /obj/item/reagent_containers/blood, \ + /obj/item/stack/material/phoron, \ + /obj/item/tank/anesthetic, \ + /obj/item/disk/body_record + +#define RESEARCH_GRIPPER \ + /obj/item/cell, \ + /obj/item/stock_parts, \ + /obj/item/mmi, \ + /obj/item/robot_parts, \ + /obj/item/borg/upgrade, \ + /obj/item/flash, \ + /obj/item/disk, \ + /obj/item/circuitboard, \ + /obj/item/reagent_containers/glass, \ + /obj/item/assembly/prox_sensor, \ + /obj/item/healthanalyzer, \ + /obj/item/slime_cube, \ + /obj/item/slime_crystal, \ + /obj/item/disposable_teleporter/slime, \ + /obj/item/slimepotion, \ + /obj/item/slime_extract, \ + /obj/item/reagent_containers/food/snacks/monkeycube + +#define CIRCUIT_GRIPPER \ + /obj/item/cell/device, \ + /obj/item/electronic_assembly, \ + /obj/item/assembly/electronic_assembly, \ + /obj/item/clothing/under/circuitry, \ + /obj/item/clothing/gloves/circuitry, \ + /obj/item/clothing/glasses/circuitry, \ + /obj/item/clothing/shoes/circuitry, \ + /obj/item/clothing/head/circuitry, \ + /obj/item/clothing/ears/circuitry, \ + /obj/item/clothing/suit/circuitry, \ + /obj/item/implant/integrated_circuit, \ + /obj/item/integrated_circuit + +#define SERVICE_GRIPPER \ + /obj/item/reagent_containers/glass, \ + /obj/item/reagent_containers/food, \ + /obj/item/seeds, \ + /obj/item/grown, \ + /obj/item/trash, \ + /obj/item/reagent_containers/cooking_container + +#define GRAVEYARD_GRIPPER \ + /obj/item/seeds, \ + /obj/item/grown, \ + /obj/item/material/gravemarker + +#define SCENE_GRIPPER \ + /obj/item/capture_crystal, \ + /obj/item/clothing, \ + /obj/item/implanter, \ + /obj/item/disk/nifsoft/compliance, \ + /obj/item/handcuffs, \ + /obj/item/toy, \ + /obj/item/petrifier, \ + /obj/item/dice, \ + /obj/item/casino_platinum_chip, \ + /obj/item/spacecasinocash + +#define ORGAN_GRIPPER \ + /obj/item/organ, \ + /obj/item/nif + +#define ROBOTICS_ORGAN_GRIPPER \ + /obj/item/organ/external, \ + /obj/item/organ/internal/brain, \ + /obj/item/organ/internal/cell, \ + /obj/item/organ/internal/eyes/robot, \ + /obj/item/nif + +#define EXOSUIT_GRIPPER \ + /obj/item/mecha_parts/part, \ + /obj/item/mecha_parts/micro/part, \ + /obj/item/mecha_parts/mecha_equipment, \ + /obj/item/mecha_parts/mecha_tracking, \ + /obj/item/mecha_parts/component + +#define SHEET_GRIPPER \ + /obj/item/stack/material + +GLOBAL_LIST_INIT(all_borg_multitool_options, list( + /obj/item/tool/screwdriver/cyborg, + /obj/item/tool/wrench/cyborg, + /obj/item/tool/crowbar/cyborg, + /obj/item/tool/wirecutters/cyborg, + /obj/item/multitool/cyborg, + /obj/item/weldingtool/electric/mounted/cyborg, + /obj/item/surgical/retractor/cyborg, + /obj/item/surgical/hemostat/cyborg, + /obj/item/surgical/cautery/cyborg, + /obj/item/surgical/surgicaldrill/cyborg, + /obj/item/surgical/scalpel/cyborg, + /obj/item/surgical/circular_saw/cyborg, + /obj/item/surgical/bonegel/cyborg, + /obj/item/surgical/FixOVein/cyborg, + /obj/item/surgical/bonesetter/cyborg, + /obj/item/surgical/bioregen/cyborg, + /obj/item/autopsy_scanner, + /obj/item/material/minihoe/cyborg, + /obj/item/material/knife/machete/hatchet/cyborg, + /obj/item/analyzer/plant_analyzer/cyborg, + /obj/item/material/knife/cyborg, + /obj/item/robot_harvester, + /obj/item/material/kitchen/rollingpin/cyborg, + /obj/item/reagent_containers/spray, +)) + +GLOBAL_LIST_INIT(material_synth_list, list( + METAL_SYNTH = /datum/matter_synth/metal, + PLASTEEL_SYNTH = /datum/matter_synth/plasteel, + GLASS_SYNTH = /datum/matter_synth/glass, + WOOD_SYNTH = /datum/matter_synth/wood, + PLASTIC_SYNTH = /datum/matter_synth/plastic, + WIRE_SYNTH = /datum/matter_synth/wire, + CLOTH_SYNTH = /datum/matter_synth/cloth + )) diff --git a/code/_onclick/cyborg.dm b/code/_onclick/cyborg.dm index 80d086a298c..906b90096cb 100644 --- a/code/_onclick/cyborg.dm +++ b/code/_onclick/cyborg.dm @@ -56,7 +56,7 @@ return */ - var/obj/item/W = get_active_hand() + var/obj/item/W = get_active_hand(A) // Cyborgs have no range-checking unless there is item use if(!W) diff --git a/code/controllers/subsystems/overmap_renamer_vr.dm b/code/controllers/subsystems/overmap_renamer_vr.dm index d82aadbfcbb..149ece296ee 100644 --- a/code/controllers/subsystems/overmap_renamer_vr.dm +++ b/code/controllers/subsystems/overmap_renamer_vr.dm @@ -17,9 +17,9 @@ SUBSYSTEM_DEF(overmap_renamer) /*Shouldn't be a switch statement. We want ALL of the if(map_template.name in visitable_z_leves_name_list) to fire if we end up with multiple renamable lateload overmap objects.*/ /datum/controller/subsystem/overmap_renamer/proc/update_names() - if(!visitable_overmap_object_instances || !islist(visitable_overmap_object_instances) || !length(visitable_overmap_object_instances)) + if(!GLOB.visitable_overmap_object_instances || !islist(GLOB.visitable_overmap_object_instances) || !length(GLOB.visitable_overmap_object_instances)) return - for(var/obj/effect/overmap/visitable/V in visitable_overmap_object_instances) + for(var/obj/effect/overmap/visitable/V in GLOB.visitable_overmap_object_instances) if(V.unique_identifier == "Debris Field") V.modify_descriptors() if(V.visitable_renamed) //could just if(D.modify_descriptors()), but having a var recording renaming is useful for debugging and stuff! diff --git a/code/game/objects/items/blueprints_vr.dm b/code/game/objects/items/blueprints_vr.dm index 81bf755e807..52d7104ade5 100644 --- a/code/game/objects/items/blueprints_vr.dm +++ b/code/game/objects/items/blueprints_vr.dm @@ -8,7 +8,7 @@ // Now that I've scared away half the people looking at this file, here's the relevant info: -// Banning areas: Go to global_lists_vr, jump to the BUILDABLE_AREA_TYPES and read the comments left there. +// Banning areas: Go to global_lists_vr, jump to the GLOB.BUILDABLE_AREA_TYPES and read the comments left there. @@ -339,11 +339,11 @@ if(A.outdoors) return AREA_SPACE - for (var/type in BUILDABLE_AREA_TYPES) + for (var/type in GLOB.BUILDABLE_AREA_TYPES) if ( istype(A,type) ) return AREA_SPACE - for (var/type in SPECIALS) + for (var/type in GLOB.SPECIALS) if ( istype(A,type) ) return AREA_SPECIAL return AREA_STATION @@ -471,7 +471,7 @@ to_chat(creator, span_warning("You need more paper before you can even think of editing this area!")) return - var/list/turfs = detect_room(get_turf(creator), area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) + var/list/turfs = detect_room(get_turf(creator), GLOB.area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) if(!turfs) to_chat(creator, span_warning("The new area must have a floor and not a part of a shuttle.")) return @@ -483,7 +483,7 @@ for(var/i in 1 to length(turfs)) var/area/place = get_area(turfs[i]) - if(blacklisted_areas[place.type]) + if(GLOB.blacklisted_areas[place.type]) continue if(!place.requires_power || (place.flag_check(BLUE_SHIELDED))) continue // No expanding powerless rooms etc @@ -551,7 +551,7 @@ to_chat(creator, span_warning("You need more paper before you can even think of editing this area!")) return - var/res = detect_room_ex(get_turf(creator), can_create_areas_into, area_or_turf_fail_types) + var/res = detect_room_ex(get_turf(creator), can_create_areas_into, GLOB.area_or_turf_fail_types) if(!res) to_chat(creator, span_warning("There is an area forbidden from being edited here! Use the fine-tune area creator! (3x3)")) return @@ -578,7 +578,7 @@ var/str //What the new area is named. var/can_make_new_area = 1 //If they can make a new area here or not. - var/list/nearby_turfs_to_check = detect_room(get_turf(creator), area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) //Get the nearby areas. + var/list/nearby_turfs_to_check = detect_room(get_turf(creator), GLOB.area_or_turf_fail_types, BP_MAX_ROOM_SIZE*2) //Get the nearby areas. if(!nearby_turfs_to_check) to_chat(creator, span_warning("The new area must have a floor and not a part of a shuttle.")) @@ -589,10 +589,10 @@ for(var/i in 1 to length(nearby_turfs_to_check)) var/area/place = get_area(nearby_turfs_to_check[i]) - if(blacklisted_areas[place.type]) + if(GLOB.blacklisted_areas[place.type]) if(!creator.lastarea != place) //Stops them from merging a blacklisted area to make it larger. Allows them to merge a blacklisted area into an allowed area. (Expansion!) continue - if(!BUILDABLE_AREA_TYPES[place.type]) //TODOTODOTODO + if(!GLOB.BUILDABLE_AREA_TYPES[place.type]) //TODOTODOTODO can_make_new_area = 0 if(!place.requires_power || (place.flag_check(BLUE_SHIELDED))) continue // No expanding powerless rooms etc @@ -810,14 +810,14 @@ /proc/get_new_area_type(area/A) //1 = can build in. 0 = can not build in. if (!A) A = get_area(usr) - if(A.outdoors) //ALWAYS able to build outdoors. This means if it's missed in BUILDABLE_AREA_TYPES it's fine. + if(A.outdoors) //ALWAYS able to build outdoors. This means if it's missed in GLOB.BUILDABLE_AREA_TYPES it's fine. return 1 - for (var/type in BUILDABLE_AREA_TYPES) //This works well. + for (var/type in GLOB.BUILDABLE_AREA_TYPES) //This works well. if ( istype(A,type) ) return 1 - for (var/type in SPECIALS) + for (var/type in GLOB.SPECIALS) if ( istype(A,type) ) return 0 return 0 //If it's not a buildable area, don't let them build in it. @@ -891,7 +891,7 @@ var/area/oldA = get_area(get_turf(creator)) //The old area (area currently standing in) var/str //What the new area is named. - var/list/nearby_turfs_to_check = detect_room(get_turf(creator), area_or_turf_fail_types, 70) //Get the nearby areas. + var/list/nearby_turfs_to_check = detect_room(get_turf(creator), GLOB.area_or_turf_fail_types, 70) //Get the nearby areas. if(!nearby_turfs_to_check) to_chat(creator, span_warning("The new area must have a floor and not a part of a shuttle.")) @@ -901,7 +901,7 @@ return //They can select an area they want to turn their current area into. - str = sanitizeSafe(tgui_input_text(usr, "What would you like to name the area?", "Area Name", null, MAX_NAME_LEN), MAX_NAME_LEN) + str = sanitizeSafe(tgui_input_text(creator, "What would you like to name the area?", "Area Name", null, MAX_NAME_LEN), MAX_NAME_LEN) if(isnull(str)) //They pressed cancel. to_chat(creator, span_warning("No new area made. Cancelling.")) return diff --git a/code/game/objects/items/devices/denecrotizer_vr.dm b/code/game/objects/items/devices/denecrotizer_vr.dm index b4af408bf7a..808222582c1 100644 --- a/code/game/objects/items/devices/denecrotizer_vr.dm +++ b/code/game/objects/items/devices/denecrotizer_vr.dm @@ -11,10 +11,10 @@ if(var_value) ghostjoin = TRUE - active_ghost_pods |= src + GLOB.active_ghost_pods |= src else ghostjoin = FALSE - active_ghost_pods -= src + GLOB.active_ghost_pods -= src ghostjoin_icon() . = TRUE @@ -63,7 +63,7 @@ /// Inject a ghost into this mob. Assumes you've done all sanity before this point. /mob/living/simple_mob/proc/ghost_join(mob/observer/dead/D) log_and_message_admins("joined [src] as a ghost [ADMIN_FLW(src)]", D) - active_ghost_pods -= src + GLOB.active_ghost_pods -= src // Move the ghost in if(D.mind) @@ -159,7 +159,7 @@ target.faction = user.faction target.revivedby = user.name target.ghostjoin = 1 - active_ghost_pods += target + GLOB.active_ghost_pods += target target.ghostjoin_icon() last_used = world.time charges-- @@ -189,7 +189,7 @@ log_and_message_admins("used a denecrotizer to revive a simple mob: [target]. [ADMIN_FLW(src)]", user) if(!target.mind) //if it doesn't have a mind then no one has been playing as it, and it is safe to offer to ghosts. target.ghostjoin = 1 - active_ghost_pods |= target + GLOB.active_ghost_pods |= target target.ghostjoin_icon() last_used = world.time charges-- diff --git a/code/game/objects/items/devices/multitool.dm b/code/game/objects/items/devices/multitool.dm index 65640675b22..b26bb1e4b03 100644 --- a/code/game/objects/items/devices/multitool.dm +++ b/code/game/objects/items/devices/multitool.dm @@ -67,13 +67,6 @@ return -/obj/item/multitool/cyborg - name = "multitool" - desc = "Optimised and stripped-down version of a regular multitool." - toolspeed = 0.5 - - - /datum/category_item/catalogue/anomalous/precursor_a/alien_multitool name = "Precursor Alpha Object - Pulse Tool" desc = "This ancient object appears to be an electrical tool. \ diff --git a/code/game/objects/items/stacks/matter_synth.dm b/code/game/objects/items/stacks/matter_synth.dm index 32c657134ac..66a42ccb735 100644 --- a/code/game/objects/items/stacks/matter_synth.dm +++ b/code/game/objects/items/stacks/matter_synth.dm @@ -32,23 +32,23 @@ name = "Nanite Synthesizer" /datum/matter_synth/metal - name = "Metal Synthesizer" + name = METAL_SYNTH /datum/matter_synth/plasteel - name = "Plasteel Synthesizer" + name = PLASTEEL_SYNTH max_energy = 10000 /datum/matter_synth/glass - name = "Glass Synthesizer" + name = GLASS_SYNTH /datum/matter_synth/wood - name = "Wood Synthesizer" + name = WOOD_SYNTH /datum/matter_synth/plastic - name = "Plastic Synthesizer" + name = PLASTIC_SYNTH /datum/matter_synth/wire - name = "Wire Synthesizer" + name = WIRE_SYNTH max_energy = 50 recharge_rate = 2 @@ -58,7 +58,7 @@ recharge_rate = 1 /datum/matter_synth/cloth - name = "Cloth Synthesizer" + name = CLOTH_SYNTH /datum/matter_synth/beacon name = "Beacon Synthesizer" diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm index 09d52831fac..2d7618e4544 100644 --- a/code/game/objects/items/stacks/stack.dm +++ b/code/game/objects/items/stacks/stack.dm @@ -50,6 +50,8 @@ return 1 if (src && usr && usr.machine == src) usr << browse(null, "window=stack") + if(islist(synths)) + synths.Cut() return ..() /obj/item/stack/update_icon() diff --git a/code/game/objects/items/weapons/surgery_tools.dm b/code/game/objects/items/weapons/surgery_tools.dm index 6b1e01e6e67..92681974207 100644 --- a/code/game/objects/items/weapons/surgery_tools.dm +++ b/code/game/objects/items/weapons/surgery_tools.dm @@ -225,46 +225,7 @@ throw_range = 5 attack_verb = list("attacked", "hit", "bludgeoned") -/* - * Cyborg Tools - */ -/obj/item/surgical/retractor/cyborg - icon_state = "cyborg_retractor" - toolspeed = 0.5 -/obj/item/surgical/hemostat/cyborg - icon_state = "cyborg_hemostat" - toolspeed = 0.5 - -/obj/item/surgical/cautery/cyborg - icon_state = "cyborg_cautery" - toolspeed = 0.5 - -/obj/item/surgical/surgicaldrill/cyborg - icon_state = "cyborg_drill" - toolspeed = 0.5 - -/obj/item/surgical/scalpel/cyborg - icon_state = "cyborg_scalpel" - toolspeed = 0.5 - -/obj/item/surgical/circular_saw/cyborg - icon_state = "cyborg_saw" - toolspeed = 0.5 - -/obj/item/surgical/bonegel/cyborg - toolspeed = 0.5 - -/obj/item/surgical/FixOVein/cyborg - toolspeed = 0.5 - -/obj/item/surgical/bonesetter/cyborg - icon_state = "cyborg_setter" - toolspeed = 0.5 - -/obj/item/surgical/bioregen/cyborg //VoreStation edit: let the borgs S U C C - icon_state = "cyborg_bioregen" - toolspeed = 0.5 /* * Alien Tools diff --git a/code/game/objects/items/weapons/tools/crowbar.dm b/code/game/objects/items/weapons/tools/crowbar.dm index 4662f0a1768..39344ccdca2 100644 --- a/code/game/objects/items/weapons/tools/crowbar.dm +++ b/code/game/objects/items/weapons/tools/crowbar.dm @@ -64,13 +64,6 @@ origin_tech = list(TECH_COMBAT = 4, TECH_ENGINEERING = 3) reach = 2 -/obj/item/tool/crowbar/cyborg - name = "hydraulic crowbar" - desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbars in industrial synthetics." - usesound = 'sound/items/jaws_pry.ogg' - force = 10 - toolspeed = 0.5 - /obj/item/tool/crowbar/power name = "power pryer" desc = "You shouldn't see this." diff --git a/code/game/objects/items/weapons/tools/screwdriver.dm b/code/game/objects/items/weapons/tools/screwdriver.dm index 125b5cae43f..6c7a255797f 100644 --- a/code/game/objects/items/weapons/tools/screwdriver.dm +++ b/code/game/objects/items/weapons/tools/screwdriver.dm @@ -100,12 +100,6 @@ random_color = FALSE reach = 2 -/obj/item/tool/screwdriver/cyborg - name = "powered screwdriver" - desc = "An electrical screwdriver, designed to be both precise and quick." - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.5 - /obj/item/tool/screwdriver/power name = "power screwdriver" desc = "You shouldn't see this." diff --git a/code/game/objects/items/weapons/tools/weldingtool.dm b/code/game/objects/items/weapons/tools/weldingtool.dm index 764e082b3aa..6718eb171bc 100644 --- a/code/game/objects/items/weapons/tools/weldingtool.dm +++ b/code/game/objects/items/weapons/tools/weldingtool.dm @@ -362,11 +362,6 @@ origin_tech = list(TECH_ENGINEERING = 2, TECH_PHORON = 2) matter = list(MAT_STEEL = 70, MAT_GLASS = 60) -/obj/item/weldingtool/largetank/cyborg - name = "integrated welding tool" - desc = "An advanced welder designed to be used in robotic systems." - toolspeed = 0.5 - /obj/item/weldingtool/hugetank name = "upgraded welding tool" desc = "A much larger welder with a huge tank." @@ -639,6 +634,10 @@ ..() /obj/item/weldingtool/electric/proc/get_external_power_supply() + if(istype(src.loc, /obj/item/robotic_multibelt)) //We are in a multibelt + if(istype(src.loc.loc, /mob/living/silicon/robot)) //We are in a multibelt that is in a robot! This is sanity in case someone spawns a multibelt in via admin commands. + var/mob/living/silicon/robot/R = src.loc.loc + return R.cell if(isrobot(src.loc)) var/mob/living/silicon/robot/R = src.loc return R.cell @@ -659,9 +658,6 @@ /obj/item/weldingtool/electric/mounted use_external_power = 1 -/obj/item/weldingtool/electric/mounted/cyborg - toolspeed = 0.5 - /obj/item/weldingtool/electric/mounted/exosuit var/obj/item/mecha_parts/mecha_equipment/equip_mount = null flame_intensity = 1 diff --git a/code/game/objects/items/weapons/tools/wirecutters.dm b/code/game/objects/items/weapons/tools/wirecutters.dm index aa56913a7d6..4c36b0f0ee5 100644 --- a/code/game/objects/items/weapons/tools/wirecutters.dm +++ b/code/game/objects/items/weapons/tools/wirecutters.dm @@ -81,12 +81,6 @@ origin_tech = list(TECH_MATERIAL = 5, TECH_ENGINEERING = 4) random_color = FALSE -/obj/item/tool/wirecutters/cyborg - name = "wirecutters" - desc = "This cuts wires. With science." - usesound = 'sound/items/jaws_cut.ogg' - toolspeed = 0.5 - /obj/item/tool/wirecutters/hybrid name = "strange wirecutters" desc = "This cuts wires. With " + span_purple("Science!") diff --git a/code/game/objects/items/weapons/tools/wrench.dm b/code/game/objects/items/weapons/tools/wrench.dm index aeb4b15f66b..066348c018a 100644 --- a/code/game/objects/items/weapons/tools/wrench.dm +++ b/code/game/objects/items/weapons/tools/wrench.dm @@ -19,12 +19,6 @@ pickup_sound = 'sound/items/pickup/wrench.ogg' tool_qualities = list(TOOL_WRENCH) -/obj/item/tool/wrench/cyborg - name = "automatic wrench" - desc = "An advanced robotic wrench. Can be found in industrial synthetic shells." - usesound = 'sound/items/drill_use.ogg' - toolspeed = 0.5 - /obj/item/tool/wrench/pipe name = "pipe wrench" desc = "A wrench used for plumbing. Can make a good makeshift weapon." diff --git a/code/game/objects/structures/crates_lockers/largecrate.dm b/code/game/objects/structures/crates_lockers/largecrate.dm index b27a6bc9d29..be3c7ef2310 100644 --- a/code/game/objects/structures/crates_lockers/largecrate.dm +++ b/code/game/objects/structures/crates_lockers/largecrate.dm @@ -37,7 +37,7 @@ if(!AMBLINAL.mind) AMBLINAL.ghostjoin = 1 AMBLINAL.ghostjoin_icon() - active_ghost_pods |= AMBLINAL + GLOB.active_ghost_pods |= AMBLINAL //VOREStation Add End user.visible_message(span_notice("[user] pries \the [src] open."), \ span_notice("You pry open \the [src]."), \ diff --git a/code/game/objects/structures/gargoyle.dm b/code/game/objects/structures/gargoyle.dm index 73f3f72f5c7..02d076ae3e6 100644 --- a/code/game/objects/structures/gargoyle.dm +++ b/code/game/objects/structures/gargoyle.dm @@ -252,7 +252,7 @@ if(istype(W,/obj/item/grab || /obj/item/holder)) gargoyle.vore_attackby(W, user) return - if(gargoyle.adminbus_trash || is_type_in_list(W,edible_trash) && W.trash_eatable && !is_type_in_list(W,item_vore_blacklist)) + if(gargoyle.adminbus_trash || is_type_in_list(W, GLOB.edible_trash) && W.trash_eatable && !is_type_in_list(W, GLOB.item_vore_blacklist)) to_chat(user, span_warning("You slip [W] into [gargoyle]'s [lowertext(gargoyle.vore_selected.name)] .")) user.drop_item() W.forceMove(gargoyle.vore_selected) @@ -276,7 +276,7 @@ /obj/structure/gargoyle/hitby(atom/movable/AM as mob|obj,var/speed = THROWFORCE_SPEED_DIVISOR) if(istype(AM,/obj/item) && gargoyle && gargoyle.vore_selected && gargoyle.trash_catching) var/obj/item/I = AM - if(gargoyle.adminbus_trash || is_type_in_list(I,edible_trash) && I.trash_eatable && !is_type_in_list(I,item_vore_blacklist)) + if(gargoyle.adminbus_trash || is_type_in_list(I, GLOB.edible_trash) && I.trash_eatable && !is_type_in_list(I, GLOB.item_vore_blacklist)) gargoyle.hitby(AM, speed) return else if(isliving(AM) && gargoyle) diff --git a/code/game/objects/structures/ghost_pods/event_vr.dm b/code/game/objects/structures/ghost_pods/event_vr.dm index 94d63546daf..cc73428a83e 100644 --- a/code/game/objects/structures/ghost_pods/event_vr.dm +++ b/code/game/objects/structures/ghost_pods/event_vr.dm @@ -1,5 +1,5 @@ /obj/structure/ghost_pod/proc/reset_ghostpod() //Makes the ghost pod usable again and re-adds it to the active ghost pod list if it is not on it. - active_ghost_pods |= src + GLOB.active_ghost_pods |= src used = FALSE busy = FALSE @@ -174,8 +174,7 @@ /obj/structure/ghost_pod/ghost_activated/maintpred/redgate/Initialize(mapload) . = ..() - if(!(src in active_ghost_pods)) - active_ghost_pods += src + GLOB.active_ghost_pods += src /obj/structure/ghost_pod/ghost_activated/maint_lurker name = "strange maintenance hole" @@ -260,8 +259,7 @@ /obj/structure/ghost_pod/ghost_activated/maint_lurker/Initialize(mapload) . = ..() - if(!(src in active_ghost_pods)) - active_ghost_pods += src + GLOB.active_ghost_pods += src /// redspace variant diff --git a/code/game/objects/structures/ghost_pods/ghost_pods.dm b/code/game/objects/structures/ghost_pods/ghost_pods.dm index 97bd9089d08..b1c41a8bb7c 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods.dm @@ -51,10 +51,7 @@ // Override this to create whatever mob you need. Be sure to call ..() if you don't want it to make infinite mobs. /obj/structure/ghost_pod/proc/create_occupant(var/mob/M) used = TRUE - //VOREStation Addition Start - if(src in active_ghost_pods) - active_ghost_pods -= src - //VOREStation Addition End + GLOB.active_ghost_pods -= src return TRUE diff --git a/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm b/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm index d69946073cc..ff2523d6524 100644 --- a/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm +++ b/code/game/objects/structures/ghost_pods/ghost_pods_vr.dm @@ -1,7 +1,6 @@ /obj/structure/ghost_pod/Destroy() - if(src in active_ghost_pods) - active_ghost_pods -= src - ..() + GLOB.active_ghost_pods -= src + . = ..() /obj/structure/ghost_pod var/spawn_active = FALSE @@ -47,8 +46,7 @@ create_occupant(user) /obj/structure/ghost_pod/proc/ghostpod_startup(var/notify = FALSE) - if(!(src in active_ghost_pods)) - active_ghost_pods += src + GLOB.active_ghost_pods |= src if(notify) trigger() diff --git a/code/game/objects/trash_eating.dm b/code/game/objects/trash_eating.dm index 8d087064a08..f9bf7ae8992 100644 --- a/code/game/objects/trash_eating.dm +++ b/code/game/objects/trash_eating.dm @@ -1,7 +1,7 @@ // checks for when items are consumed by trash/ore eater /obj/item/proc/on_trash_eaten(var/mob/living/user) SHOULD_CALL_PARENT(TRUE) - if(is_type_in_list(src,item_vore_blacklist) && !user.adminbus_trash) //If someone has adminbus, they can eat whatever they want. + if(is_type_in_list(src, GLOB.item_vore_blacklist) && !user.adminbus_trash) //If someone has adminbus, they can eat whatever they want. to_chat(user, span_warning("You are not allowed to eat this.")) return FALSE if(!trash_eatable) //OOC pref. This /IS/ respected, even if adminbus_trash is enabled diff --git a/code/modules/admin/modify_robot.dm b/code/modules/admin/modify_robot.dm index 6bf1b642c88..14a94da98eb 100644 --- a/code/modules/admin/modify_robot.dm +++ b/code/modules/admin/modify_robot.dm @@ -20,6 +20,7 @@ var/supplied_law = "SuppliedLaw" var/supplied_law_position = MIN_SUPPLIED_LAW_NUMBER var/list/datum/ai_laws/law_list + var/obj/item/robotic_multibelt/multibelt_holder //Currently selected multibelt. /datum/eventkit/modify_robot/New() . = ..() @@ -81,6 +82,10 @@ var/obj/item/gun/energy/kinetic_accelerator/kin = locate() in target.module.modules if(kin) .["target"]["pka"] += get_pka(kin) + for(var/obj/item/robotic_multibelt/multibelt in target.module.modules) + .["target"]["multibelt"] += list(get_mult_belt(multibelt)) + + // Radio section var/list/radio_channels = list() for(var/channel in target.radio.channels) @@ -336,6 +341,39 @@ kin.modkits.Remove(rem_kit) qdel(rem_kit) return TRUE + if("select_multibelt") + multibelt_holder = locate(params["multibelt"]) + return TRUE + if("install_tool") + if(!istype(multibelt_holder)) + return FALSE + if(istype(multibelt_holder, /obj/item/robotic_multibelt/materials)) + target.add_new_material(text2path(params["tool"])) + return TRUE + var/new_tool = text2path(params["tool"]) + if(new_tool in GLOB.all_borg_multitool_options) + multibelt_holder.cyborg_integrated_tools += new_tool //Make sure you don't add items directly to it, or you can't ever remove them. + multibelt_holder.generate_tools() + return TRUE + + if("remove_tool") + if(!istype(multibelt_holder)) + return FALSE + if(istype(multibelt_holder, /obj/item/robotic_multibelt/materials)) + var/datum/matter_synth/synth = locate(params["tool"]) + target.module.synths -= synth + qdel(synth) + target.update_material_multibelts() + return TRUE + var/obj/item/rem_tool = locate(params["tool"]) + if(multibelt_holder.selected_item == rem_tool) + multibelt_holder.dropped() //Reset to original icon. + multibelt_holder.contents -= rem_tool + multibelt_holder.cyborg_integrated_tools -= rem_tool.type + multibelt_holder.integrated_tools_by_name -= rem_tool.name + multibelt_holder.integrated_tool_images -= rem_tool.name + qdel(rem_tool) + return TRUE if("add_channel") var/selected_radio_channel = params["channel"] if(selected_radio_channel == CHANNEL_SPECIAL_OPS || selected_radio_channel == CHANNEL_RESPONSE_TEAM) @@ -694,6 +732,33 @@ pka["max_capacity"] = kin.max_mod_capacity return pka +/datum/eventkit/modify_robot/proc/get_mult_belt(var/obj/item/robotic_multibelt/mult_belt) + var/list/multi_belt_list = list() + multi_belt_list["name"] = mult_belt.name + multi_belt_list["ref"] = REF(mult_belt) + var/list/integrated_tools = list() + var/list/tools = list() + if(istype(mult_belt, /obj/item/robotic_multibelt/materials)) + for(var/datum/matter_synth/synth in target.module.synths) + integrated_tools += list(list("name" = synth.name, "ref" = "\ref[synth]")) + for(var/tool in GLOB.material_synth_list) + var/material_path = GLOB.material_synth_list[tool] + if(!target.can_install_synth(material_path)) //Don't add it to the list if we already have it! + continue + tools += list(list("name" = tool, "path" = material_path)) + else + for(var/obj/tool in mult_belt.contents) + integrated_tools += list(list("name" = tool.name, "ref" = "\ref[tool]")) + for(var/tool in GLOB.all_borg_multitool_options) + if(tool in mult_belt.cyborg_integrated_tools) //Don't add it to the list if we already have it! + continue + var/obj/item/tool_to_add = tool + tools += list(list("name" = initial(tool_to_add.name), "path" = tool_to_add)) + multi_belt_list["integrated_tools"] = integrated_tools + multi_belt_list["tools"] = tools + return multi_belt_list + + /datum/eventkit/modify_robot/proc/get_cells() var/list/cell_options = list() for(var/cell in typesof(/obj/item/cell)) diff --git a/code/modules/admin/verbs/buildmode.dm b/code/modules/admin/verbs/buildmode.dm index 4b39bfa08de..acc9faa2f48 100644 --- a/code/modules/admin/verbs/buildmode.dm +++ b/code/modules/admin/verbs/buildmode.dm @@ -860,11 +860,11 @@ GLOBAL_LIST_EMPTY(active_buildmode_holders) if(A.outdoors) return AREA_SPACE - for (var/type in BUILDABLE_AREA_TYPES) + for (var/type in GLOB.BUILDABLE_AREA_TYPES) if ( istype(A,type) ) return AREA_SPACE - for (var/type in SPECIALS) + for (var/type in GLOB.SPECIALS) if ( istype(A,type) ) return AREA_SPECIAL return AREA_STATION diff --git a/code/modules/awaymissions/overmap_renamer/debrisfield_renamer.dm b/code/modules/awaymissions/overmap_renamer/debrisfield_renamer.dm index a0621b01499..f7bdeccfddd 100644 --- a/code/modules/awaymissions/overmap_renamer/debrisfield_renamer.dm +++ b/code/modules/awaymissions/overmap_renamer/debrisfield_renamer.dm @@ -10,7 +10,7 @@ . = ..() if(!reference) // testing("First overmap descriptor, name: [name]") - for(var/obj/effect/overmap/visitable/D in visitable_overmap_object_instances) + for(var/obj/effect/overmap/visitable/D in GLOB.visitable_overmap_object_instances) if(D.unique_identifier == "Debris Field") if(D.possible_descriptors && islist(D.possible_descriptors)) D.possible_descriptors |= list(descriptors) diff --git a/code/modules/awaymissions/overmap_renamer/readme.md b/code/modules/awaymissions/overmap_renamer/readme.md index 6927ef394b0..e370bbfaebb 100644 --- a/code/modules/awaymissions/overmap_renamer/readme.md +++ b/code/modules/awaymissions/overmap_renamer/readme.md @@ -51,14 +51,14 @@ How to use - for mappers: Important procs, vars etc. contained within the following files: code\_helpers\global_lists_vr.dm - var/list/visitable_overmap_object_instances - Collects instances with reference for things like the Debris Field, Space Whale, Talon etc. We need it to call the proc on the instance + GLOBAL_LIST_EMPTY(visitable_overmap_object_instances) - Collects instances with reference for things like the Debris Field, Space Whale, Talon etc. We need it to call the proc on the instance code\modules\overmap\sectors.dm var/list/possible_descriptors - contains a list of list("name","desc","scanner_desc") var/unique_identifier - A way to check if the object in question loaded without causing a compiler error. Name them sth easy to recognize, like "Debris field" for... debris field. var/real_name - Used to handle known = FALSE overmap objects properly var/real_desc - same as real_name - /obj/effect/overmap/visitable/Initialize() - adds the object's instance with reference to visitable_overmap_object_instances + /obj/effect/overmap/visitable/Initialize() - adds the object's instance with reference to GLOB.visitable_overmap_object_instances code\modules\awaymissions\overmap_renamer\overmap_renamer.dm /obj/effect/overmap/visitable/proc/modify_descriptors() - Takes possible_descriptors from src, picks a valid one after sanitization. Gives warnings if input is invalid. diff --git a/code/modules/events/roaming_wildlife.dm b/code/modules/events/roaming_wildlife.dm index 1d855d2c210..2bb50590d94 100644 --- a/code/modules/events/roaming_wildlife.dm +++ b/code/modules/events/roaming_wildlife.dm @@ -44,9 +44,9 @@ var/list/spawn_list switch(WL.wildlife_type) if(1) - spawn_list = event_wildlife_aquatic + spawn_list = GLOB.event_wildlife_aquatic if(2) - spawn_list = event_wildlife_roaming + spawn_list = GLOB.event_wildlife_roaming if(!spawn_list) continue diff --git a/code/modules/materials/materials/glass.dm b/code/modules/materials/materials/glass.dm index b7c3ef73597..fcc679cea56 100644 --- a/code/modules/materials/materials/glass.dm +++ b/code/modules/materials/materials/glass.dm @@ -36,7 +36,7 @@ var/message = "Sheet-[used_stack.name] ([used_stack.get_amount()] sheet\s left)" var/choice = tgui_input_list(user, message, "Window Construction", window_options) - if(!choice || !used_stack || !user || used_stack.loc != user || user.stat || user.loc != T) + if(!choice || !used_stack || !user || (used_stack.loc != user && !isrobot(user)) || user.stat || user.loc != T) return 1 // Get data for building windows here. diff --git a/code/modules/mob/dead/observer/observer_vr.dm b/code/modules/mob/dead/observer/observer_vr.dm index 11da02c4d7c..3313316b624 100644 --- a/code/modules/mob/dead/observer/observer_vr.dm +++ b/code/modules/mob/dead/observer/observer_vr.dm @@ -113,12 +113,12 @@ if(!isobserver(src)) //Make sure they're an observer! return - var/input = tgui_input_list(src, "Select a ghost pod:", "Ghost Jump", observe_list_format(active_ghost_pods)) + var/input = tgui_input_list(src, "Select a ghost pod:", "Ghost Jump", observe_list_format(GLOB.active_ghost_pods)) if(!input) to_chat(src, span_filter_notice("No active ghost pods detected.")) return - var/target = observe_list_format(active_ghost_pods)[input] + var/target = observe_list_format(GLOB.active_ghost_pods)[input] if (!target)//Make sure we actually have a target return else diff --git a/code/modules/mob/living/carbon/human/death_vr.dm b/code/modules/mob/living/carbon/human/death_vr.dm index 41d63107c90..70afe4c60e8 100644 --- a/code/modules/mob/living/carbon/human/death_vr.dm +++ b/code/modules/mob/living/carbon/human/death_vr.dm @@ -35,5 +35,5 @@ /mob/living/simple_mob/death() ..() ghostjoin = 0 - active_ghost_pods -= src + GLOB.active_ghost_pods -= src ghostjoin_icon() diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index fee0d71b694..e99e33f3c2c 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -427,7 +427,7 @@ emp_act var/obj/O = AM if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //Ported from chompstation var/obj/item/I = O - if(adminbus_trash || is_type_in_list(I,edible_trash) && I.trash_eatable && !is_type_in_list(I,item_vore_blacklist)) + if(adminbus_trash || is_type_in_list(I, GLOB.edible_trash) && I.trash_eatable && !is_type_in_list(I, GLOB.item_vore_blacklist)) visible_message(span_warning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) I.throwing = 0 I.forceMove(vore_selected) diff --git a/code/modules/mob/living/carbon/human/stripping.dm b/code/modules/mob/living/carbon/human/stripping.dm index b39101d98a3..4e8aa9bd5c9 100644 --- a/code/modules/mob/living/carbon/human/stripping.dm +++ b/code/modules/mob/living/carbon/human/stripping.dm @@ -70,6 +70,10 @@ var/obj/item/held = user.get_active_hand() if(!istype(held) || is_robot_module(held)) stripping = TRUE + if(is_robot_module(held) && istype(held, /obj/item/gripper)) + var/obj/item/gripper/G = held + if(istype(G.wrapped)) + stripping = FALSE else var/obj/item/holder/holder = held if(istype(holder) && src == holder.held_mob) @@ -86,11 +90,17 @@ to_chat(user, span_warning("You cannot remove \the [src]'s [target_slot.name].")) return visible_message(span_danger("\The [user] is trying to remove \the [src]'s [target_slot.name]!")) - else + else if(!istype(held, /obj/item/gripper)) if(slot_to_strip == slot_wear_mask && istype(held, /obj/item/grenade)) visible_message(span_danger("\The [user] is trying to put \a [held] in \the [src]'s mouth!")) else visible_message(span_danger("\The [user] is trying to put \a [held] on \the [src]!")) + else + var/obj/item/gripper/G = held + if(slot_to_strip == slot_wear_mask && istype(G.wrapped, /obj/item/grenade)) + visible_message(span_danger("\The [user] is trying to put \a [G.wrapped] in \the [src]'s mouth!")) + else + visible_message(span_danger("\The [user] is trying to put \a [G.wrapped] on \the [src]!")) if(!do_after(user,HUMAN_STRIP_DELAY,src)) return @@ -103,10 +113,17 @@ to_chat(user, span_warning("You can't put someone on themselves! Stop trying to break reality!")) return - if(stripping) add_attack_logs(user,src,"Removed equipment from slot [target_slot]") unEquip(target_slot) + else if(is_robot_module(held) && istype(held, /obj/item/gripper)) + var/obj/item/gripper/G = held + var/obj/item/wrapped = G.wrapped + if(istype(wrapped)) + if(equip_to_slot_if_possible(wrapped, text2num(slot_to_strip), 0, 1, 1)) + G.wrapped = null + G.generate_icons() + G.current_pocket = pick(G.pockets) else if(user.unEquip(held)) equip_to_slot_if_possible(held, text2num(slot_to_strip), 0, 1, 1) if(held.loc != src) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index d7124c9371c..91520f0e5c1 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -261,7 +261,7 @@ var/obj/O = AM if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //ported from chompstation var/obj/item/I = O - if(adminbus_trash || is_type_in_list(I,edible_trash) && I.trash_eatable && !is_type_in_list(I,item_vore_blacklist)) + if(adminbus_trash || is_type_in_list(I, GLOB.edible_trash) && I.trash_eatable && !is_type_in_list(I, GLOB.item_vore_blacklist)) visible_message(span_warning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) I.throwing = 0 I.forceMove(vore_selected) diff --git a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm index a4bb527feb6..765bdb6045d 100644 --- a/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm +++ b/code/modules/mob/living/silicon/robot/dogborg/dog_sleeper.dm @@ -79,7 +79,7 @@ return if(compactor) - if(is_type_in_list(target,item_vore_blacklist)) + if(is_type_in_list(target, GLOB.item_vore_blacklist)) to_chat(user, span_warning("You are hard-wired to not ingest this item.")) return if(istype(target, /obj/item) || istype(target, /obj/effect/decal/remains)) @@ -176,7 +176,7 @@ ingest_living(ingesting, belly) else if (istype(ingesting, /obj/item)) var/obj/item/to_eat = ingesting - if (is_type_in_list(to_eat, item_vore_blacklist)) + if (is_type_in_list(to_eat, GLOB.item_vore_blacklist)) return if (istype(to_eat, /obj/item/holder)) //just in case var/obj/item/holder/micro = ingesting diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm index 9e8b2931c16..3d080e69729 100644 --- a/code/modules/mob/living/silicon/robot/drone/drone_items.dm +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -1,433 +1,3 @@ -//Simple borg hand. -//Limited use. -/obj/item/gripper - name = "magnetic gripper" - desc = "A simple grasping tool specialized in construction and engineering work." - description_info = "Ctrl-Clicking on the gripper will drop whatever it is holding.
\ - Using an object on the gripper will interact with the item inside it, if it exists, instead." - icon = 'icons/obj/device.dmi' - icon_state = "gripper" - - flags = NOBLUDGEON - - //Has a list of items that it can hold. - var/list/can_hold = list( - /obj/item/cell, - /obj/item/airlock_electronics, - /obj/item/tracker_electronics, - /obj/item/module/power_control, - /obj/item/stock_parts, - /obj/item/frame, - /obj/item/camera_assembly, - /obj/item/tank, - /obj/item/circuitboard, - /obj/item/smes_coil, - /obj/item/fuel_assembly - ) - - var/obj/item/wrapped = null // Item currently being held. - - var/force_holder = null // - pickup_sound = 'sound/items/pickup/device.ogg' - drop_sound = 'sound/items/drop/device.ogg' - -/obj/item/gripper/examine(mob/user) - . = ..() - if(wrapped) - . += span_notice("\The [src] is holding \the [wrapped].") - . += wrapped.examine(user) - -/obj/item/gripper/CtrlClick(mob/user) - drop_item() - return - -/obj/item/gripper/AltClick(mob/user) - drop_item() - return - -/obj/item/gripper/omni - name = "omni gripper" - desc = "A strange grasping tool that can hold anything a human can, but still maintains the limitations of application its more limited cousins have." - icon_state = "gripper-omni" - - can_hold = list(/obj/item) // Testing and Event gripper. - -// VEEEEERY limited version for mining borgs. Basically only for swapping cells and upgrading the drills. -/obj/item/gripper/miner - name = "drill maintenance gripper" - desc = "A simple grasping tool for the maintenance of heavy drilling machines." - icon_state = "gripper-mining" - - can_hold = list( - /obj/item/cell, - /obj/item/stock_parts - ) - -/obj/item/gripper/security - name = "security gripper" - desc = "A simple grasping tool for corporate security work." - icon_state = "gripper-sec" - - can_hold = list( - /obj/item/paper, - /obj/item/paper_bundle, - /obj/item/pen, - /obj/item/sample, - /obj/item/forensics/sample_kit, - /obj/item/taperecorder, - /obj/item/rectape, - /obj/item/uv_light - ) - -/obj/item/gripper/paperwork - name = "paperwork gripper" - desc = "A simple grasping tool for clerical work." - - can_hold = list( - /obj/item/clipboard, - /obj/item/paper, - /obj/item/paper_bundle, - /obj/item/card/id, - /obj/item/book, - /obj/item/newspaper - ) - -/obj/item/gripper/medical - name = "medical gripper" - desc = "A simple grasping tool for medical work." - - can_hold = list( - /obj/item/reagent_containers/glass, - /obj/item/storage/pill_bottle, - /obj/item/reagent_containers/pill, - /obj/item/reagent_containers/blood, - /obj/item/stack/material/phoron, - /obj/item/tank/anesthetic, - /obj/item/disk/body_record //Vorestation Edit: this lets you get an empty sleeve or help someone else - ) - -/obj/item/gripper/research //A general usage gripper, used for toxins/robotics/xenobio/etc - name = "scientific gripper" - icon_state = "gripper-sci" - desc = "A simple grasping tool suited to assist in a wide array of research applications." - - can_hold = list( - /obj/item/cell, - /obj/item/stock_parts, - /obj/item/mmi, - /obj/item/robot_parts, - /obj/item/borg/upgrade, - /obj/item/flash, //to build borgs, - /obj/item/disk, - /obj/item/circuitboard, - /obj/item/reagent_containers/glass, - /obj/item/assembly/prox_sensor, - /obj/item/healthanalyzer, //to build medibots, - /obj/item/slime_cube, - /obj/item/slime_crystal, - /obj/item/disposable_teleporter/slime, - /obj/item/slimepotion, - /obj/item/slime_extract, - /obj/item/reagent_containers/food/snacks/monkeycube - ) - -/obj/item/gripper/circuit - name = "circuit assembly gripper" - icon_state = "gripper-circ" - desc = "A complex grasping tool used for working with circuitry." - - can_hold = list( - /obj/item/cell/device, - /obj/item/electronic_assembly, - /obj/item/assembly/electronic_assembly, - /obj/item/clothing/under/circuitry, - /obj/item/clothing/gloves/circuitry, - /obj/item/clothing/glasses/circuitry, - /obj/item/clothing/shoes/circuitry, - /obj/item/clothing/head/circuitry, - /obj/item/clothing/ears/circuitry, - /obj/item/clothing/suit/circuitry, - /obj/item/implant/integrated_circuit, - /obj/item/integrated_circuit - - ) - -/obj/item/gripper/service //Used to handle food, drinks, and seeds. - name = "service gripper" - icon_state = "gripper" - desc = "A simple grasping tool used to perform tasks in the service sector, such as handling food, drinks, and seeds." - - can_hold = list( - /obj/item/reagent_containers/glass, - /obj/item/reagent_containers/food, - /obj/item/seeds, - /obj/item/grown, - /obj/item/trash, - /obj/item/reagent_containers/cooking_container - ) - -/obj/item/gripper/gravekeeper //Used for handling grave things, flowers, etc. - name = "grave gripper" - icon_state = "gripper" - desc = "A specialized grasping tool used in the preparation and maintenance of graves." - - can_hold = list( - /obj/item/seeds, - /obj/item/grown, - /obj/item/material/gravemarker - ) - -/obj/item/gripper/scene - name = "misc gripper" - desc = "A simple grasping tool that can hold a variety of 'general' objects..." - - can_hold = list( - /obj/item/capture_crystal, - /obj/item/clothing, - /obj/item/implanter, - /obj/item/disk/nifsoft/compliance, - /obj/item/handcuffs, - /obj/item/toy, - /obj/item/petrifier, - /obj/item/dice, - /obj/item/casino_platinum_chip, - /obj/item/spacecasinocash - ) - -/obj/item/gripper/no_use/organ - name = "organ gripper" - icon_state = "gripper-flesh" - desc = "A specialized grasping tool used to preserve and manipulate organic material." - - can_hold = list( - /obj/item/organ, - /obj/item/nif //NIFs can be slapped in during surgery - ) - -/obj/item/gripper/no_use/organ/Entered(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 1 - for(var/obj/item/organ/organ in O) - organ.preserved = 1 - ..() - -/obj/item/gripper/no_use/organ/Exited(var/atom/movable/AM) - if(istype(AM, /obj/item/organ)) - var/obj/item/organ/O = AM - O.preserved = 0 - for(var/obj/item/organ/organ in O) - organ.preserved = 0 - ..() - -/obj/item/gripper/no_use/organ/robotics - name = "robotics organ gripper" - icon_state = "gripper-flesh" - desc = "A specialized grasping tool used in robotics work." - - can_hold = list( - /obj/item/organ/external, - /obj/item/organ/internal/brain, //to insert into MMIs, - /obj/item/organ/internal/cell, - /obj/item/organ/internal/eyes/robot, - /obj/item/nif //NIFs can be slapped in during surgery - ) - -/obj/item/gripper/no_use/mech - name = "exosuit gripper" - icon_state = "gripper-mech" - desc = "A large, heavy-duty grasping tool used in construction of mechs." - - can_hold = list( - /obj/item/mecha_parts/part, - /obj/item/mecha_parts/micro/part, //VOREStation Edit: Allow construction of micromechs, - /obj/item/mecha_parts/mecha_equipment, - /obj/item/mecha_parts/mecha_tracking, - /obj/item/mecha_parts/component - ) - -/obj/item/gripper/no_use //Used when you want to hold and put items in other things, but not able to 'use' the item - -/obj/item/gripper/no_use/attack_self(mob/user as mob) - return - -/obj/item/gripper/no_use/loader //This is used to disallow building with metal. - name = "sheet loader" - desc = "A specialized loading device, designed to pick up and insert sheets of materials inside machines. Also can pick up floor tiles." - icon_state = "gripper-sheet" - - can_hold = list( - /obj/item/stack/material, - /obj/item/stack/tile - ) - -/obj/item/gripper/attack_self(mob/user as mob) - if(wrapped) - return wrapped.attack_self(user) - return ..() - -/obj/item/gripper/attackby(var/obj/item/O, var/mob/user) - if(wrapped) // We're interacting with the item inside. If you can hold a cup with 2 fingers and stick a straw in it, you could do that with a gripper and another robotic arm. - wrapped.loc = src.loc - var/resolved = wrapped.attackby(O, user) - if(QDELETED(wrapped) || wrapped.loc != src.loc) //Juuuust in case. - wrapped = null - if(!resolved && wrapped && O) - O.afterattack(wrapped,user,1) - if(QDELETED(wrapped) || wrapped.loc != src.loc) // I don't know of a nicer way to do this. - wrapped = null - if(wrapped) - wrapped.loc = src - return resolved - return ..() - -/obj/item/gripper/verb/drop_gripper_item() - - set name = "Drop Item" - set desc = "Release an item from your magnetic gripper." - set category = "Abilities.Silicon" - - drop_item() - -/obj/item/gripper/proc/drop_item() - if(!wrapped) - //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z - for(var/obj/item/thing in src.contents) - thing.loc = get_turf(src) - return - - if(wrapped.loc != src) - wrapped = null - return - - to_chat(src.loc, span_notice("You drop \the [wrapped].")) - wrapped.loc = get_turf(src) - wrapped = null - //update_icon() - -/obj/item/gripper/proc/drop_item_nm() - - if(!wrapped) - for(var/obj/item/thing in src.contents) - thing.loc = get_turf(src) - return - - if(wrapped.loc != src) - wrapped = null - return - - wrapped.loc = get_turf(src) - wrapped = null - //update_icon() - -/obj/item/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) - if(wrapped) //The force of the wrapped obj gets set to zero during the attack() and afterattack(). - force_holder = wrapped.force - wrapped.force = 0.0 - if(QDELETED(wrapped) || wrapped.loc != src.loc) //qdel check here so it doesn't duplicate/spawn ghost items - wrapped = null - else - wrapped.loc = src.loc //To ensure checks pass. - wrapped.attack(M,user) - M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe. - if(QDELETED(wrapped) || wrapped.loc != src.loc) - wrapped = null - if(wrapped) //In the event nothing happened to wrapped, go back into the gripper. - wrapped.loc = src - return 1 - return 0 - -/obj/item/gripper/afterattack(var/atom/target, var/mob/living/user, proximity, params) - - if(!proximity) - return // This will prevent them using guns at range but adminbuse can add them directly to modules, so eh. - - //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z - if(!wrapped) - for(var/obj/item/thing in src.contents) - wrapped = thing - break - - if(wrapped) //Already have an item. - //Temporary put wrapped into user so target's attackby() checks pass. - wrapped.loc = user - - //Pass the attack on to the target. This might delete/relocate wrapped. - var/resolved = target.attackby(wrapped,user) - if(!resolved && wrapped && target) - wrapped.afterattack(target,user,1) - - //wrapped's force was set to zero. This resets it to the value it had before. - if(wrapped) - wrapped.force = force_holder - force_holder = null - //If wrapped was neither deleted nor put into target, put it back into the gripper. - if(wrapped && user && (wrapped.loc == user)) - wrapped.loc = src - else - wrapped = null - return - - else if(istype(target,/obj/item)) //Check that we're not pocketing a mob. - - //...and that the item is not in a container. - if(!isturf(target.loc)) - return - - var/obj/item/I = target - - if(I.anchored) - to_chat(user,span_notice("You are unable to lift \the [I] from \the [I.loc].")) - return - - //Check if the item is blacklisted. - var/grab = 0 - for(var/typepath in can_hold) - if(istype(I,typepath)) - grab = 1 - break - - //We can grab the item, finally. - if(grab) - to_chat(user, "You collect \the [I].") - I.loc = src - wrapped = I - return - else - to_chat(user, span_danger("Your gripper cannot hold \the [target].")) - - else if(istype(target,/obj/machinery/power/apc)) - var/obj/machinery/power/apc/A = target - if(A.opened) - if(A.cell && is_type_in_list(A.cell, can_hold)) - - wrapped = A.cell - - A.cell.add_fingerprint(user) - A.cell.update_icon() - A.cell.loc = src - A.cell = null - - A.charging = 0 - A.update_icon() - - user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.") - - else if(istype(target,/mob/living/silicon/robot)) - var/mob/living/silicon/robot/A = target - if(A.opened) - if(A.cell && is_type_in_list(A.cell, can_hold)) - - wrapped = A.cell - - A.cell.add_fingerprint(user) - A.cell.update_icon() - A.update_icon() - A.cell.loc = src - A.cell = null - - user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.") - //TODO: Matter decompiler. /obj/item/matter_decompiler diff --git a/code/modules/mob/living/silicon/robot/inventory.dm b/code/modules/mob/living/silicon/robot/inventory.dm index 360baa18b63..b90544646a4 100644 --- a/code/modules/mob/living/silicon/robot/inventory.dm +++ b/code/modules/mob/living/silicon/robot/inventory.dm @@ -2,7 +2,13 @@ //as they handle all relevant stuff like adding it to the player's screen and such //Returns the thing in our active hand (whatever is in our active module-slot, in this case) -/mob/living/silicon/robot/get_active_hand() +/mob/living/silicon/robot/get_active_hand(atom/A) + if(module_active == A) //If we are interacting with the item itself (I.E swapping multibelt items) + return module_active + else if(istype(module_active, /obj/item/robotic_multibelt)) //If we are hitting something with a multibelt + var/obj/item/robotic_multibelt/belt = module_active + if(belt.selected_item) + return belt.selected_item return module_active /*-------TODOOOOOOOOOO--------*/ @@ -23,6 +29,9 @@ return if(module_state_1 == I) + if(istype(module_state_1,/obj/item/robotic_multibelt)) + var/obj/item/robotic_multibelt/toolbelt = module_state_1 + toolbelt.original_state() if(istype(module_state_1,/obj/item/borg/sight)) sight_mode &= ~module_state_1:sight_mode if (client) @@ -33,6 +42,9 @@ module_state_1 = null inv1.icon_state = "inv1" else if(module_state_2 == I) + if(istype(module_state_2,/obj/item/robotic_multibelt)) + var/obj/item/robotic_multibelt/toolbelt = module_state_2 + toolbelt.original_state() if(istype(module_state_2,/obj/item/borg/sight)) sight_mode &= ~module_state_2:sight_mode if (client) @@ -43,6 +55,9 @@ module_state_2 = null inv2.icon_state = "inv2" else if(module_state_3 == I) + if(istype(module_state_3,/obj/item/robotic_multibelt)) + var/obj/item/robotic_multibelt/toolbelt = module_state_3 + toolbelt.original_state() if(istype(module_state_3,/obj/item/borg/sight)) sight_mode &= ~module_state_3:sight_mode if (client) @@ -126,6 +141,9 @@ return 0 /mob/living/silicon/robot/proc/activated(obj/item/O) + var/belt_check = using_multibelt(O) + if(belt_check) + return belt_check if(module_state_1 == O) return 1 else if(module_state_2 == O) @@ -135,6 +153,12 @@ else return 0 +/mob/living/silicon/robot/proc/using_multibelt(obj/item/O) + for(var/obj/item/robotic_multibelt/materials/material_belt in contents) + if(material_belt.selected_item == O) + return TRUE + return FALSE + /mob/living/silicon/robot/proc/get_active_modules() return list(module_state_1, module_state_2, module_state_3) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/event.dm b/code/modules/mob/living/silicon/robot/robot_modules/event.dm index 66b0f6e062a..80da91bb609 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/event.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/event.dm @@ -22,7 +22,7 @@ src.modules += new /obj/item/tool/screwdriver/cyborg(src) src.modules += new /obj/item/tool/wrench/cyborg(src) src.modules += new /obj/item/tool/wirecutters/cyborg(src) - src.modules += new /obj/item/multitool(src) + src.modules += new /obj/item/multitool/cyborg(src) // Sci src.modules += new /obj/item/robotanalyzer(src) @@ -33,10 +33,6 @@ var/datum/matter_synth/wire = new /datum/matter_synth/wire() synths += wire - var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) - C.synths = list(wire) - src.modules += C - src.modules += new /obj/item/dogborg/sleeper/lost(src) src.modules += new /obj/item/dogborg/pounce(src) @@ -56,11 +52,7 @@ src.modules += new /obj/item/tool/wrench/cyborg(src) // For growing flowers - src.modules += new /obj/item/material/minihoe(src) - src.modules += new /obj/item/material/knife/machete/hatchet(src) - src.modules += new /obj/item/analyzer/plant_analyzer(src) - src.modules += new /obj/item/storage/bag/plants(src) - src.modules += new /obj/item/robot_harvester(src) + src.modules += new /obj/item/robotic_multibelt/botanical(src) // For digging and beautifying graves src.modules += new /obj/item/shovel(src) diff --git a/code/modules/mob/living/silicon/robot/robot_modules/station.dm b/code/modules/mob/living/silicon/robot/robot_modules/station.dm index a8c78e3e608..729cc4c980c 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/station.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/station.dm @@ -255,18 +255,8 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/healthanalyzer(src) src.modules += new /obj/item/sleevemate(src) src.modules += new /obj/item/reagent_containers/borghypo/surgeon(src) - src.modules += new /obj/item/autopsy_scanner(src) - src.modules += new /obj/item/surgical/scalpel/cyborg(src) - src.modules += new /obj/item/surgical/hemostat/cyborg(src) - src.modules += new /obj/item/surgical/retractor/cyborg(src) - src.modules += new /obj/item/surgical/cautery/cyborg(src) - src.modules += new /obj/item/surgical/bonegel/cyborg(src) - src.modules += new /obj/item/surgical/FixOVein/cyborg(src) - src.modules += new /obj/item/surgical/bonesetter/cyborg(src) - src.modules += new /obj/item/surgical/circular_saw/cyborg(src) - src.modules += new /obj/item/surgical/surgicaldrill/cyborg(src) - src.modules += new /obj/item/surgical/bioregen/cyborg(src) - src.modules += new /obj/item/gripper/no_use/organ(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) src.modules += new /obj/item/gripper/medical(src) src.modules += new /obj/item/shockpaddles/robot(src) src.modules += new /obj/item/reagent_containers/dropper(src) // Allows surgeon borg to fix necrosis @@ -327,21 +317,11 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/reagent_containers/glass/beaker/large/borg(src) src.modules += new /obj/item/reagent_containers/dropper/industrial(src) src.modules += new /obj/item/reagent_containers/syringe(src) - src.modules += new /obj/item/gripper/no_use/organ(src) src.modules += new /obj/item/gripper/medical(src) src.modules += new /obj/item/shockpaddles/robot(src) //Surgeon Modules below - src.modules += new /obj/item/autopsy_scanner(src) - src.modules += new /obj/item/surgical/scalpel/cyborg(src) - src.modules += new /obj/item/surgical/hemostat/cyborg(src) - src.modules += new /obj/item/surgical/retractor/cyborg(src) - src.modules += new /obj/item/surgical/cautery/cyborg(src) - src.modules += new /obj/item/surgical/bonegel/cyborg(src) - src.modules += new /obj/item/surgical/FixOVein/cyborg(src) - src.modules += new /obj/item/surgical/bonesetter/cyborg(src) - src.modules += new /obj/item/surgical/circular_saw/cyborg(src) - src.modules += new /obj/item/surgical/surgicaldrill/cyborg(src) - src.modules += new /obj/item/surgical/bioregen/cyborg(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) //Surgeon Modules End src.modules += new /obj/item/inflatable_dispenser/robot(src) src.modules += new /obj/item/holosign_creator/medical(src) @@ -402,18 +382,14 @@ var/global/list/robot_modules = list( /obj/item/robot_module/robot/engineering/create_equipment(var/mob/living/silicon/robot/robot) ..() + src.modules += new /obj/item/robotic_multibelt(src) + src.modules += new /obj/item/robotic_multibelt(src) src.modules += new /obj/item/borg/sight/meson(src) - src.modules += new /obj/item/weldingtool/electric/mounted/cyborg(src) - src.modules += new /obj/item/tool/screwdriver/cyborg(src) - src.modules += new /obj/item/tool/wrench/cyborg(src) - src.modules += new /obj/item/tool/wirecutters/cyborg(src) - src.modules += new /obj/item/multitool(src) src.modules += new /obj/item/t_scanner(src) src.modules += new /obj/item/analyzer(src) src.modules += new /obj/item/geiger(src) src.modules += new /obj/item/taperoll/engineering(src) - src.modules += new /obj/item/gripper(src) - src.modules += new /obj/item/gripper/circuit(src) + src.modules += new /obj/item/gripper/engineering(src) src.modules += new /obj/item/lightreplacer(src) src.modules += new /obj/item/pipe_dispenser(src) src.modules += new /obj/item/floor_painter(src) @@ -422,7 +398,6 @@ var/global/list/robot_modules = list( src.emag += new /obj/item/melee/robotic/baton/arm(src) src.modules += new /obj/item/rcd/electric/mounted/borg(src) src.modules += new /obj/item/pickaxe/plasmacutter/borg(src) - src.modules += new /obj/item/gripper/no_use/loader(src) src.modules += new /obj/item/dogborg/stasis_clamp(src) var/datum/matter_synth/metal = new /datum/matter_synth/metal(40000) @@ -439,55 +414,6 @@ var/global/list/robot_modules = list( synths += plastic synths += wire - var/obj/item/matter_decompiler/MD = new /obj/item/matter_decompiler(src) - MD.metal = metal - MD.glass = glass - src.modules += MD - - var/obj/item/stack/material/cyborg/steel/M = new (src) - M.synths = list(metal) - src.modules += M - - var/obj/item/stack/material/cyborg/glass/G = new (src) - G.synths = list(glass) - src.modules += G - - var/obj/item/stack/rods/cyborg/R = new /obj/item/stack/rods/cyborg(src) - R.synths = list(metal) - src.modules += R - - var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) - C.synths = list(wire) - src.modules += C - - var/obj/item/stack/material/cyborg/plasteel/PS = new (src) - PS.synths = list(plasteel) - src.modules += PS - - var/obj/item/stack/tile/floor/cyborg/S = new /obj/item/stack/tile/floor/cyborg(src) - S.synths = list(metal) - src.modules += S - - var/obj/item/stack/tile/roofing/cyborg/CT = new /obj/item/stack/tile/roofing/cyborg(src) - CT.synths = list(metal) - src.modules += CT - - var/obj/item/stack/material/cyborg/glass/reinforced/RG = new (src) - RG.synths = list(metal, glass) - src.modules += RG - - var/obj/item/stack/tile/wood/cyborg/WT = new /obj/item/stack/tile/wood/cyborg(src) - WT.synths = list(wood) - src.modules += WT - - var/obj/item/stack/material/cyborg/wood/W = new (src) - W.synths = list(wood) - src.modules += W - - var/obj/item/stack/material/cyborg/plastic/PL = new (src) - PL.synths = list(plastic) - src.modules += PL - var/obj/item/dogborg/sleeper/compactor/decompiler/BD = new /obj/item/dogborg/sleeper/compactor/decompiler(src) BD.metal = metal BD.glass = glass @@ -495,6 +421,7 @@ var/global/list/robot_modules = list( BD.plastic = plastic src.modules += BD + src.modules += new /obj/item/robotic_multibelt/materials(src) src.emag += new /obj/item/dogborg/pounce(src) /obj/item/robot_module/robot/security @@ -516,7 +443,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/taperoll/police(src) src.modules += new /obj/item/reagent_containers/spray/pepper(src) src.modules += new /obj/item/gripper/security(src) - src.modules += new /obj/item/ticket_printer(src) //VOREStation Add + src.modules += new /obj/item/ticket_printer(src) src.emag += new /obj/item/gun/energy/robotic/laser/rifle(src) src.modules += new /obj/item/dogborg/sleeper/K9(src) //Eat criminals. Bring them to the brig. @@ -552,6 +479,7 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/pupscrubber(src) src.modules += new /obj/item/lightreplacer(src) src.modules += new /obj/item/borg/sight/janitor(src) + src.modules += new /obj/item/reagent_containers/glass/bucket/cyborg(src) var/obj/item/reagent_containers/spray/LS = new /obj/item/reagent_containers/spray(src) src.emag += LS LS.reagents.add_reagent(REAGENT_ID_LUBE, 250) @@ -641,15 +569,8 @@ var/global/list/robot_modules = list( /obj/item/robot_module/robot/clerical/butler/create_equipment(var/mob/living/silicon/robot/robot) ..() src.modules += new /obj/item/gripper/service(src) - src.modules += new /obj/item/reagent_containers/glass/bucket(src) - src.modules += new /obj/item/material/minihoe(src) - src.modules += new /obj/item/material/knife/machete/hatchet(src) - src.modules += new /obj/item/analyzer/plant_analyzer(src) + src.modules += new /obj/item/robotic_multibelt/service(src) src.modules += new /obj/item/storage/bag/serviceborg(src) - src.modules += new /obj/item/robot_harvester(src) - src.modules += new /obj/item/material/knife(src) - src.modules += new /obj/item/material/kitchen/rollingpin(src) - src.modules += new /obj/item/multitool(src) //to freeze trays var/obj/item/rsf/M = new /obj/item/rsf(src) M.stored_matter = 30 @@ -691,15 +612,12 @@ var/global/list/robot_modules = list( /obj/item/robot_module/robot/clerical/honkborg/create_equipment(var/mob/living/silicon/robot/R) src.modules += new /obj/item/gripper/service(src) - src.modules += new /obj/item/reagent_containers/glass/bucket(src) - src.modules += new /obj/item/material/minihoe(src) - src.modules += new /obj/item/analyzer/plant_analyzer(src) - src.modules += new /obj/item/storage/bag/serviceborg(src) - src.modules += new /obj/item/robot_harvester(src) - src.modules += new /obj/item/multitool(src) + src.modules += new /obj/item/reagent_containers/glass/bucket/cyborg(src) + src.modules += new /obj/item/robotic_multibelt/botanical(src) src.modules += new /obj/item/dogborg/pounce(src) src.modules += new /obj/item/bikehorn(src) src.modules += new /obj/item/gun/launcher/confetti_cannon/robot(src) + src.modules += new /obj/item/reagent_containers/spray/waterflower(src) var/obj/item/rsf/M = new /obj/item/rsf(src) M.stored_matter = 30 @@ -776,18 +694,12 @@ var/global/list/robot_modules = list( /obj/item/robot_module/robot/research/create_equipment(var/mob/living/silicon/robot/robot) ..() src.modules += new /obj/item/portable_destructive_analyzer(src) - src.modules += new /obj/item/gripper/research(src) - src.modules += new /obj/item/gripper/circuit(src) - src.modules += new /obj/item/gripper/no_use/organ/robotics(src) - src.modules += new /obj/item/gripper/no_use/mech(src) - src.modules += new /obj/item/gripper/no_use/loader(src) src.modules += new /obj/item/robotanalyzer(src) src.modules += new /obj/item/card/robot(src) - src.modules += new /obj/item/weldingtool/electric/mounted/cyborg(src) - src.modules += new /obj/item/tool/screwdriver/cyborg(src) - src.modules += new /obj/item/tool/wrench/cyborg(src) - src.modules += new /obj/item/tool/wirecutters/cyborg(src) - src.modules += new /obj/item/multitool(src) + src.modules += new /obj/item/gripper/research(src) + src.modules += new /obj/item/robotic_multibelt(src) + src.modules += new /obj/item/robotic_multibelt(src) + src.modules += new /obj/item/robotic_multibelt/botanical(src) src.modules += new /obj/item/surgical/hemostat/cyborg(src) //Synth repair src.modules += new /obj/item/surgical/surgicaldrill/cyborg(src) //NIF repair src.modules += new /obj/item/surgical/circular_saw/cyborg(src) // Synth limb replacement @@ -813,11 +725,8 @@ var/global/list/robot_modules = list( N.synths = list(nanite) src.modules += N - var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) //Cable code, taken from engiborg, - C.synths = list(wire) - src.modules += C - src.modules += new /obj/item/dogborg/sleeper/compactor/analyzer(src) + src.modules += new /obj/item/robotic_multibelt/materials(src) src.emag += new /obj/item/dogborg/pounce(src) /obj/item/robot_module/robot/research/respawn_consumable(var/mob/living/silicon/robot/R, var/amount) @@ -871,11 +780,10 @@ var/global/list/robot_modules = list( src.modules += new /obj/item/tool/crowbar/cyborg(src) src.modules += new /obj/item/tool/wirecutters/cyborg(src) src.modules += new /obj/item/t_scanner(src) - src.modules += new /obj/item/multitool(src) + src.modules += new /obj/item/multitool/cyborg(src) src.modules += new /obj/item/lightreplacer(src) - src.modules += new /obj/item/gripper(src) + src.modules += new /obj/item/gripper/drone(src) src.modules += new /obj/item/soap(src) - src.modules += new /obj/item/gripper/no_use/loader(src) src.modules += new /obj/item/extinguisher(src) src.modules += new /obj/item/pipe_painter(src) src.modules += new /obj/item/floor_painter(src) @@ -904,44 +812,9 @@ var/global/list/robot_modules = list( MD.glass = glass MD.wood = wood MD.plastic = plastic + src.modules += new /obj/item/robotic_multibelt/materials(src) src.modules += MD - var/obj/item/stack/material/cyborg/steel/M = new (src) - M.synths = list(metal) - src.modules += M - - var/obj/item/stack/material/cyborg/glass/G = new (src) - G.synths = list(glass) - src.modules += G - - var/obj/item/stack/rods/cyborg/R = new /obj/item/stack/rods/cyborg(src) - R.synths = list(metal) - src.modules += R - - var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) - C.synths = list(wire) - src.modules += C - - var/obj/item/stack/tile/floor/cyborg/S = new /obj/item/stack/tile/floor/cyborg(src) - S.synths = list(metal) - src.modules += S - - var/obj/item/stack/material/cyborg/glass/reinforced/RG = new (src) - RG.synths = list(metal, glass) - src.modules += RG - - var/obj/item/stack/tile/wood/cyborg/WT = new /obj/item/stack/tile/wood/cyborg(src) - WT.synths = list(wood) - src.modules += WT - - var/obj/item/stack/material/cyborg/wood/W = new (src) - W.synths = list(wood) - src.modules += W - - var/obj/item/stack/material/cyborg/plastic/P = new (src) - P.synths = list(plastic) - src.modules += P - /obj/item/robot_module/drone/construction name = "construction drone module" hide_on_manifest = TRUE diff --git a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm index 3db2610165d..b89e1fef719 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules/syndicate.dm @@ -29,9 +29,6 @@ var/datum/matter_synth/cloth = new /datum/matter_synth/cloth(40000) synths += cloth - var/obj/item/stack/sandbags/cyborg/SB = new /obj/item/stack/sandbags/cyborg(src) - SB.synths += list(cloth) - var/jetpack = new/obj/item/tank/jetpack/carbondioxide(src) src.modules += jetpack robot.internals = jetpack @@ -59,6 +56,7 @@ src.modules += new /obj/item/dogborg/sleeper/K9/syndie(src) src.modules += new /obj/item/dogborg/pounce(src) + src.modules += new /obj/item/robotic_multibelt/materials(src) // 95% engi-borg and 15% roboticist. /obj/item/robot_module/robot/syndicate/mechanist @@ -102,26 +100,6 @@ N.synths = list(nanite) src.modules += N - var/obj/item/stack/material/cyborg/steel/M = new (src) - M.synths = list(metal) - src.modules += M - - var/obj/item/stack/material/cyborg/glass/G = new (src) - G.synths = list(glass) - src.modules += G - - var/obj/item/stack/rods/cyborg/rods = new /obj/item/stack/rods/cyborg(src) - rods.synths = list(metal) - src.modules += rods - - var/obj/item/stack/cable_coil/cyborg/C = new /obj/item/stack/cable_coil/cyborg(src) - C.synths = list(wire) - src.modules += C - - var/obj/item/stack/material/cyborg/glass/reinforced/RG = new (src) - RG.synths = list(metal, glass) - src.modules += RG - var/obj/item/dogborg/sleeper/compactor/syndie/MD = new /obj/item/dogborg/sleeper/compactor/syndie(src) MD.metal = metal MD.glass = glass @@ -129,6 +107,8 @@ src.modules += new /obj/item/dogborg/pounce(src) + src.modules += new /obj/item/robotic_multibelt/materials(src) + // Mediborg optimized for on-the-field healing, but can also do surgery if needed. @@ -143,19 +123,12 @@ // Surgery things. src.modules += new /obj/item/autopsy_scanner(src) - src.modules += new /obj/item/surgical/scalpel/cyborg(src) - src.modules += new /obj/item/surgical/hemostat/cyborg(src) - src.modules += new /obj/item/surgical/retractor/cyborg(src) - src.modules += new /obj/item/surgical/cautery/cyborg(src) - src.modules += new /obj/item/surgical/bonegel/cyborg(src) - src.modules += new /obj/item/surgical/FixOVein/cyborg(src) - src.modules += new /obj/item/surgical/bonesetter/cyborg(src) - src.modules += new /obj/item/surgical/circular_saw/cyborg(src) - src.modules += new /obj/item/surgical/surgicaldrill/cyborg(src) - src.modules += new /obj/item/gripper/no_use/organ(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) + src.modules += new /obj/item/robotic_multibelt/medical(src) + src.modules += new /obj/item/gripper/medical(src) + src.modules += new /obj/item/robotic_multibelt/materials(src) // General healing. - src.modules += new /obj/item/gripper/medical(src) src.modules += new /obj/item/shockpaddles/robot/combat(src) src.modules += new /obj/item/reagent_containers/dropper(src) // Allows borg to fix necrosis apparently src.modules += new /obj/item/reagent_containers/syringe(src) diff --git a/code/modules/mob/living/silicon/robot/robot_simple_items.dm b/code/modules/mob/living/silicon/robot/robot_simple_items.dm new file mode 100644 index 00000000000..21005ee2969 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/robot_simple_items.dm @@ -0,0 +1,896 @@ +// Robot toolbelts, such as screwdrivers and the like. All contained in one neat little package. +// The code for actually 'how these use the item inside of them instead of the item itself' can be found in _onclick/cyborg.dm (yes, click code, gross, I know.) +/* + * Engineering Tools + */ + +/obj/item/robotic_multibelt + name = "Robotic multitool" + desc = "An integrated toolbelt. Use CTRL-CLICK to interact with the selected item!" + + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg" + w_class = ITEMSIZE_HUGE + + var/obj/item/selected_item = null + + var/list/cyborg_integrated_tools = list( + /obj/item/tool/screwdriver/cyborg = null, + /obj/item/tool/wrench/cyborg = null, + /obj/item/tool/crowbar/cyborg = null, + /obj/item/tool/wirecutters/cyborg = null, + /obj/item/multitool/cyborg = null, + /obj/item/weldingtool/electric/mounted/cyborg = null, + ) + + var/list/integrated_tools_by_name + + var/list/integrated_tool_images + +/obj/item/robotic_multibelt/CtrlClick(mob/user) + if(selected_item) + selected_item.attack_self(user) + return + +/obj/item/robotic_multibelt/examine(mob/user) + . = ..() + if(selected_item) + . += span_notice("\The [src] is currently set to \the [selected_item].") + . += selected_item.examine(user) + +/obj/item/robotic_multibelt/proc/get_module() + var/obj/item/robot_module/module + if(isrobot(loc)) //We're in a robot (in hands) + var/mob/living/silicon/robot/our_robot = loc + module = our_robot.module + else if(istype(loc, /obj/item/robot_module)) //We're inside the module itself (in inventory) + module = loc + else //Admin spawned us outside of a module. + CRASH("Robotic Multibelt is not in a robot or module. This should not happen.") + return module + + +//'Alterate Tools' means we do special tool handling in Init +/obj/item/robotic_multibelt/Initialize(mapload, custom_handling = FALSE) + . = ..() + generate_tools() + +/obj/item/robotic_multibelt/proc/generate_tools() + integrated_tools_by_name = list() + + integrated_tool_images = list() + + for(var/path in cyborg_integrated_tools) + if(ispath(path)) //Some things like the materials printer makes its own tools and it won't be a path. + if(!cyborg_integrated_tools[path]) + cyborg_integrated_tools[path] = new path(src) + else + cyborg_integrated_tools[path] = path + var/obj/item/I = cyborg_integrated_tools[path] + I.canremove = FALSE + + for(var/tool in cyborg_integrated_tools) + var/obj/item/real_tool = cyborg_integrated_tools[tool] + integrated_tools_by_name[real_tool.name] = real_tool + integrated_tool_images[real_tool.name] = image(icon = real_tool.icon, icon_state = real_tool.icon_state) + +/obj/item/robotic_multibelt/Destroy() + selected_item = null + QDEL_LIST_ASSOC_VAL(cyborg_integrated_tools) + integrated_tools_by_name.Cut() + integrated_tool_images.Cut() + . = ..() + + +/obj/item/robotic_multibelt/attack_self(mob/user) + if(!cyborg_integrated_tools || !LAZYLEN(cyborg_integrated_tools)) + to_chat(user, "Your multibelt is empty!") + return + + var/list/options = list() + + for(var/Iname in integrated_tools_by_name) + options[Iname] = integrated_tool_images[Iname] + + var/list/choice = list() + if(length(options) == 1) + for(var/key in options) + choice = key + else + choice = show_radial_menu(user, src, options, radius = 40, require_near = TRUE) + if(!choice) + return + assume_selected_item(integrated_tools_by_name[choice]) + + ..() + +/obj/item/robotic_multibelt/proc/assume_selected_item(obj/item/chosen_item) + if(!chosen_item) + return + icon = chosen_item.icon + icon_state = chosen_item.icon_state + selected_item = chosen_item + +/obj/item/robotic_multibelt/dropped(mob/user) + ..() + //We go back to our initial values. + original_state() + +/obj/item/robotic_multibelt/proc/original_state(mob/user) + selected_item = null + icon = initial(icon) + icon_state = initial(icon_state) + +/obj/item/tool/screwdriver/cyborg + name = "powered screwdriver" + desc = "An electrical screwdriver, designed to be both precise and quick." + usesound = 'sound/items/drill_use.ogg' + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg_screwdriver" + random_color = FALSE + toolspeed = 0.5 + +/obj/item/tool/crowbar/cyborg + name = "hydraulic crowbar" + desc = "A hydraulic prying tool, compact but powerful. Designed to replace crowbars in industrial synthetics." + usesound = 'sound/items/jaws_pry.ogg' + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg_crowbar" + force = 10 + toolspeed = 0.5 + +/obj/item/weldingtool/electric/mounted/cyborg + name = "integrated electric welding tool" + desc = "An advanced welder designed to be used in robotic systems." + icon = 'icons/obj/tools_robot.dmi' + icon_state = "indwelder_cyborg" + usesound = 'sound/items/Welder2.ogg' + toolspeed = 0.5 + welding = TRUE + +/obj/item/tool/wirecutters/cyborg + name = "wirecutters" + desc = "This cuts wires. With science." + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg_cutters" + usesound = 'sound/items/jaws_cut.ogg' + random_color = FALSE + toolspeed = 0.5 + +/obj/item/tool/wrench/cyborg + name = "automatic wrench" + desc = "An advanced robotic wrench. Can be found in industrial synthetic shells." + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg_wrench" + usesound = 'sound/items/drill_use.ogg' + toolspeed = 0.5 + +/obj/item/multitool/cyborg + name = "multitool" + desc = "Optimised and stripped-down version of a regular multitool." + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_engiborg_multitool" + toolspeed = 0.5 + +/obj/item/stack/cable_coil/cyborg + name = "cable coil synthesizer" + desc = "A device that makes cable." + gender = NEUTER + matter = null + uses_charge = 1 + charge_costs = list(1) + +/obj/item/stack/cable_coil/cyborg/verb/set_colour() + set name = "Change Colour" + set category = "Object" + + var/selected_type = tgui_input_list(usr, "Pick new colour.", "Cable Colour", GLOB.possible_cable_coil_colours) + set_cable_color(selected_type, usr) + +/* + * Surgical Tools + */ + +/obj/item/robotic_multibelt/medical + name = "Robotic surgical multitool" + desc = "An integrated surgical toolbelt." + icon_state = "toolkit_medborg" + + cyborg_integrated_tools = list( + /obj/item/surgical/retractor/cyborg = null, + /obj/item/surgical/hemostat/cyborg = null, + /obj/item/surgical/cautery/cyborg = null, + /obj/item/surgical/surgicaldrill/cyborg = null, + /obj/item/surgical/scalpel/cyborg = null, + /obj/item/surgical/circular_saw/cyborg = null, + /obj/item/surgical/bonegel/cyborg = null, + /obj/item/surgical/FixOVein/cyborg = null, + /obj/item/surgical/bonesetter/cyborg = null, + /obj/item/surgical/bioregen/cyborg = null, + /obj/item/autopsy_scanner = null + ) + +/obj/item/surgical/retractor/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_retractor" + toolspeed = 0.5 + +/obj/item/surgical/hemostat/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_hemostat" + toolspeed = 0.5 + +/obj/item/surgical/cautery/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_cautery" + toolspeed = 0.5 + +/obj/item/surgical/surgicaldrill/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_drill" + toolspeed = 0.5 + +/obj/item/surgical/scalpel/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_scalpel" + toolspeed = 0.5 + +/obj/item/surgical/circular_saw/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_saw" + toolspeed = 0.5 + +/obj/item/surgical/bonegel/cyborg + toolspeed = 0.5 + +/obj/item/surgical/FixOVein/cyborg + toolspeed = 0.5 + +/obj/item/surgical/bonesetter/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "toolkit_medborg_bonesetter" + toolspeed = 0.5 + +/obj/item/surgical/bioregen/cyborg + icon_state = "cyborg_bioregen" + toolspeed = 0.5 + +//Service multibelt! +/obj/item/robotic_multibelt/service + name = "Service multitool" + desc = "An integrated service toolbelt." + icon_state = "toolkit_medborg" + + cyborg_integrated_tools = list( + /obj/item/material/minihoe/cyborg = null, + /obj/item/material/knife/machete/hatchet/cyborg = null, + /obj/item/analyzer/plant_analyzer/cyborg = null, + /obj/item/material/knife/cyborg = null, + /obj/item/robot_harvester = null, + /obj/item/material/kitchen/rollingpin/cyborg = null, + /obj/item/tool/wirecutters/cyborg = null, + /obj/item/multitool/cyborg = null, + /obj/item/reagent_containers/spray = null + ) + +//Botanical multibelt! +/obj/item/robotic_multibelt/botanical + name = "Botanical multitool" + desc = "An integrated botanical toolbelt." + icon_state = "toolkit_medborg" + + cyborg_integrated_tools = list( + /obj/item/material/minihoe/cyborg = null, + /obj/item/material/knife/machete/hatchet/cyborg = null, + /obj/item/analyzer/plant_analyzer/cyborg = null, + /obj/item/robot_harvester = null, + /obj/item/tool/wirecutters/cyborg = null, + /obj/item/multitool/cyborg = null, + /obj/item/reagent_containers/spray = null + ) + +/obj/item/material/minihoe/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "sili_cultivator" + +/obj/item/material/knife/machete/hatchet/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "sili_hatchet" + +/obj/item/analyzer/plant_analyzer/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "sili_secateur" + + +/obj/item/material/knife/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "sili_knife" + +/obj/item/material/kitchen/rollingpin/cyborg + icon = 'icons/obj/tools_robot.dmi' + icon_state = "sili_rolling_pin" + +//Admin proc to add new materials to their fabricator +/mob/living/silicon/robot/proc/add_new_material(mat_to_add) //Allows us to add a new material to the borg's synth and then make their multibelt refresh. + if(!module) + return + + if(!mat_to_add) + return + + var/datum/matter_synth/synth_path = ispath(mat_to_add) ? mat_to_add : GLOB.material_synth_list[mat_to_add] + + if(!can_install_synth(synth_path)) + return + + var/amount + switch(mat_to_add) + if(/datum/matter_synth/metal) + amount = 40000 + if(/datum/matter_synth/plasteel) + amount = 20000 + if(/datum/matter_synth/glass) + amount = 40000 + if(/datum/matter_synth/wood) + amount = 40000 + if(/datum/matter_synth/plastic) + amount = 40000 + if(/datum/matter_synth/wire) + amount = 50 + if(/datum/matter_synth/cloth) + amount = 40000 + + if(!amount) + return + + module.synths += new synth_path(amount) + update_material_multibelts() + +/mob/living/silicon/robot/proc/update_material_multibelts() + for(var/obj/item/robotic_multibelt/materials/mat_belt in module.contents) //If it's stowed in our inventory + mat_belt.generate_tools() + for(var/obj/item/robotic_multibelt/materials/mat_belt in contents) //If it's in our handstory + mat_belt.generate_tools() + +/mob/living/silicon/robot/proc/can_install_synth(var/datum/matter_synth/type_to_check) + if(!ispath(type_to_check, /datum/matter_synth)) + return FALSE + for(var/datum/matter_synth/synth in module.synths) + if(istype(synth, type_to_check)) + return FALSE + return TRUE + +/mob/living/silicon/robot/proc/remove_material(mat_to_remove) + if(!module) + return + + if(!mat_to_remove) + return + + var/datum/matter_synth/synth_path = ispath(mat_to_remove) ? mat_to_remove : GLOB.material_synth_list[mat_to_remove] + + for(var/datum/matter_synth/synth in module.synths) + if(istype(synth, synth_path)) + module.synths -= synth + qdel(synth) + update_material_multibelts() + +//The Material Dispenser Multibelt +//This thing is uh...Bulky. And took a lot of effort to get to work. + +/obj/item/robotic_multibelt/materials + name = "Robotic Material Dispenser" + desc = "An integrated material dispenser! Click once to select your material. Use Ctrl + Click to open the menu for the selected material." + icon_state = "toolkit_material" + + cyborg_integrated_tools = list() + +/obj/item/robotic_multibelt/materials/generate_tools() + var/obj/item/robot_module/module = get_module() + if(!module || !module.synths || !LAZYLEN(module.synths)) //We have a synths list and it has contents within it! + return + + var/datum/matter_synth/has_steel //Steel synth. For generating Rglass + var/datum/matter_synth/has_glass //Glass synth. For generating Rglass + for(var/datum/matter_synth/our_synth in module.synths) + if(our_synth.name == METAL_SYNTH) + has_steel = our_synth + continue + if(our_synth.name == GLASS_SYNTH) + has_glass = our_synth + continue + + var/list/possible_synths = list() + for(var/datum/matter_synth/our_synth in module.synths) + switch(our_synth.name) + if(METAL_SYNTH) + possible_synths += list(/obj/item/stack/material/cyborg/steel = list(our_synth)) + possible_synths += list(/obj/item/stack/tile/floor/cyborg = list(our_synth)) + possible_synths += list(/obj/item/stack/rods/cyborg = list(our_synth)) + possible_synths += list(/obj/item/stack/tile/roofing/cyborg = list(our_synth)) + if(has_glass) + possible_synths |= list(/obj/item/stack/material/cyborg/glass/reinforced = list(our_synth, has_glass)) + if(PLASTEEL_SYNTH) + possible_synths += list(/obj/item/stack/material/cyborg/plasteel = list(our_synth)) + if(GLASS_SYNTH) + possible_synths += list(/obj/item/stack/material/cyborg/glass = list(our_synth)) + if(has_steel) + possible_synths |= list(/obj/item/stack/material/cyborg/glass/reinforced = list(our_synth, has_steel)) + if(WOOD_SYNTH) + possible_synths += list(/obj/item/stack/tile/wood/cyborg = list(our_synth)) + possible_synths += list(/obj/item/stack/material/cyborg/wood = list(our_synth)) + if(PLASTIC_SYNTH) + possible_synths += list(/obj/item/stack/material/cyborg/plastic = list(our_synth)) + if(WIRE_SYNTH) + possible_synths += list(/obj/item/stack/cable_coil/cyborg = list(our_synth)) + if(CLOTH_SYNTH) + possible_synths += list(/obj/item/stack/sandbags/cyborg = list(our_synth)) + + for(var/obj/item/stack/our_item in cyborg_integrated_tools) + if(is_type_in_list(our_item, possible_synths)) + possible_synths -= our_item.type + else + cyborg_integrated_tools -= our_item + integrated_tools_by_name -= our_item + integrated_tool_images -= our_item + qdel(our_item) + + for(var/stack_to_add in possible_synths) + var/obj/item/stack/current_stack = new stack_to_add(src) + current_stack.synths = possible_synths[stack_to_add] + cyborg_integrated_tools += current_stack + + . = ..() + +/* + * Grippers + */ + +//Simple borg hand. +//Limited use. +//If you want to add more items to the gripper, add them to the global define list for that gripper. +/obj/item/gripper + name = "magnetic gripper" + desc = "A simple grasping tool specialized in construction and engineering work." + description_info = "Ctrl-Clicking on the gripper will interact with whatever it is holding.
\ + Alt-Clicking on the gripper will drop the item it is holding.
\ + Using an object on the gripper will interact with the item inside it, if it exists, instead." + icon = 'icons/obj/device.dmi' + icon_state = "gripper" + + flags = NOBLUDGEON + + //Has a list of items that it can hold. + var/list/can_hold = list(BASIC_GRIPPER) + + var/obj/item/wrapped = null // Item currently being held. + + var/total_pockets = 5 //How many total inventory slots we want to have in the gripper + + var/list/pockets = list() //List of the pockets we have. This is used to store the items inside of the gripper. + + var/obj/item/current_pocket = null //What pocket (or item!) we currently have selected + + var/list/pockets_by_name + + var/list/photo_images + + pickup_sound = 'sound/items/pickup/device.ogg' + drop_sound = 'sound/items/drop/device.ogg' + +/obj/item/storage/internal/gripper + max_w_class = ITEMSIZE_HUGE + max_storage_space = ITEMSIZE_COST_HUGE + +/obj/item/gripper/Initialize(mapload) + . = ..() + + if(total_pockets) + for(var/i = 1, i <= total_pockets, i++) + var/obj/new_pocket = new /obj/item/storage/internal/gripper(src) + new_pocket.name = "Pocket [i]" + pockets += new_pocket + current_pocket = pick(pockets) //Pick a random pocket! + +/obj/item/gripper/Destroy() + current_pocket = null + qdel_null(wrapped) + qdel_null(pockets) + . = ..() + + +/obj/item/gripper/examine(mob/user) + . = ..() + if(wrapped) + . += span_notice("\The [src] is holding \the [wrapped].") + . += wrapped.examine(user) + +/obj/item/gripper/CtrlClick(mob/user) + if(wrapped) + wrapped.attack_self(user) + return + +/obj/item/gripper/AltClick(mob/user) + drop_item() + return + + +//This is the code that updates our pockets and decides if they should have icons or not. +//This should be called every time we use the gripper and our wrapped item is used up. +/obj/item/gripper/proc/generate_icons() + if(LAZYLEN(pockets)) + + pockets_by_name = list() + + photo_images = list() + + for(var/obj/item/storage/internal/pocket_to_check in pockets) + if(!LAZYLEN(pocket_to_check.contents)) + pockets_by_name[pocket_to_check.name] = pocket_to_check + photo_images[pocket_to_check.name] = image(icon = 'icons/effects/effects.dmi', icon_state = "nothing") + continue + var/obj/item/pocket_content = pocket_to_check.contents[1] + pockets_by_name["[pocket_to_check.name]" + "[pocket_content.name]"] = pocket_content + photo_images["[pocket_to_check.name]" + "[pocket_content.name]"] = image(icon = pocket_content.icon, icon_state = pocket_content.icon_state) + +/obj/item/gripper/attack_self(mob/user as mob) + generate_icons() + var/list/options = list() + + for(var/Iname in pockets_by_name) + options[Iname] = photo_images[Iname] + + var/list/choice = list() + choice = show_radial_menu(user, src, options, radius = 40, require_near = TRUE, autopick_single_option = FALSE) + if(choice) + current_pocket = pockets_by_name[choice] + if(!istype(current_pocket,/obj/item/storage/internal/gripper)) //The pocket we're selecting is NOT a gripper storage + if(!istype(current_pocket.loc, /obj/item/storage/internal/gripper)) //We kept the radial menu opened, used the item, then selected it again. + current_pocket = pick(pockets) //Just pick a random pocket. + wrapped = null + else + wrapped = current_pocket + else + wrapped = null + else if(wrapped) + return wrapped.attack_self(user) + return ..() + +/obj/item/gripper/attackby(var/obj/item/O, var/mob/user) + if(wrapped) + wrapped.loc = src.loc //Place it in to the robot. + var/resolved = wrapped.attackby(O, user) + if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) + wrapped = null + if(!resolved && wrapped && O) + O.afterattack(wrapped,user,1) + if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) // I don't know of a nicer way to do this. + wrapped = null + if(wrapped && wrapped != current_pocket) + wrapped.loc = current_pocket + else if(wrapped) + for(var/obj/item/storage/internal/gripper/our_pocket in pockets) + if(LAZYLEN(our_pocket.contents)) + continue + current_pocket = our_pocket + wrapped.loc = current_pocket + + return resolved + return ..() + +/obj/item/gripper/verb/drop_gripper_item() + + set name = "Drop Item" + set desc = "Release an item from your magnetic gripper." + set category = "Abilities.Silicon" + + drop_item() + +/obj/item/gripper/proc/drop_item() + if((wrapped == current_pocket && !istype(wrapped.loc, /obj/item/storage/internal/gripper))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage + wrapped = null + current_pocket = pick(pockets) + generate_icons() + return + + to_chat(src.loc, span_notice("You drop \the [wrapped].")) + wrapped.loc = get_turf(src) + wrapped = null + generate_icons() + //update_icon() + +/obj/item/gripper/proc/drop_item_nm() + if((wrapped == current_pocket && !istype(wrapped.loc, /obj/item/storage/internal/gripper))) //We have wrapped selected as our current_pocket AND wrapped is not in a gripper storage + wrapped = null + current_pocket = pick(pockets) + return + + wrapped.loc = get_turf(src) + wrapped = null + +/obj/item/gripper/attack(mob/living/carbon/M as mob, mob/living/carbon/user as mob) + if(wrapped) //The force of the wrapped obj gets set to zero during the attack() and afterattack(). + if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage + wrapped = null //we become null + else + wrapped.attack(M,user) + M.attackby(wrapped, user) //attackby reportedly gets procced by being clicked on, at least according to Anewbe. + if(QDELETED(wrapped) || (wrapped.loc != src.loc && !istype(wrapped.loc,/obj/item/storage/internal/gripper))) //If our wrapper was deleted OR it's no longer in our internal gripper storage + wrapped = null + if(wrapped) //In the event nothing happened to wrapped, go back into the gripper. + wrapped.loc = current_pocket + return 1 + return 0 + +/obj/item/gripper/afterattack(var/atom/target, var/mob/living/user, proximity, params) + + if(!proximity) + return // This will prevent them using guns at range but adminbuse can add them directly to modules, so eh. + var/current_pocket_full = FALSE + //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + if(wrapped == current_pocket) + current_pocket_full = TRUE + if(!wrapped && current_pocket) + if(LAZYLEN(current_pocket.contents)) + wrapped = current_pocket.contents[1] + current_pocket_full = TRUE + + if(current_pocket && !LAZYLEN(current_pocket.contents)) //We have a pocket selected and it has no contents! This means we're an item OR we need to null our wrapper! + if(istype(current_pocket.loc,/obj/item/storage/internal/gripper) && !LAZYLEN(current_pocket.loc.contents)) //If our pocket is a gripper, AND we have no contents, wrapped = null + wrapped = null + else if(!istype(current_pocket.loc,/obj/item/storage/internal/gripper)) //If our pocket is an item and we are not in the gripper, wrapped = null + wrapped = null + + if(!LAZYLEN(pockets)) //Shouldn't happen, but safety first. + to_chat(user, span_danger("Your gripper has nowhere to hold \the [target].")) + return + + var/obj/item/storage/internal/gripper/selected_pocket //Find an open pocket to use in case we're trying to pick something up. + for(var/obj/item/storage/internal/gripper/available_pocket in pockets) + if(LAZYLEN(available_pocket.contents)) + continue + selected_pocket = available_pocket + break + + if(wrapped) //Already have an item. + //Temporary put wrapped into user so target's attackby() checks pass. + var/obj/previous_pocket = wrapped.loc + wrapped.loc = user + + //Pass the attack on to the target. This might delete/relocate wrapped. + var/resolved = target.attackby(wrapped,user) + if(!resolved && wrapped && target) + wrapped.afterattack(target,user,1) + + //If wrapped was neither deleted nor put into target, put it back into the gripper. + if(wrapped && user && ((wrapped.loc == user) || wrapped.loc == previous_pocket)) + wrapped.loc = previous_pocket + else + wrapped = null + return + else if(current_pocket_full) //Pocket is full. No grabbing more things. + to_chat(user, "Your gripper is currently full! You can't pick anything else up!") + return + + else if(istype(target,/obj/item)) //Check that we're not pocketing a mob. + + //...and that the item is not in a container. + if(!isturf(target.loc)) + return + + var/obj/item/I = target + + if(I.anchored) + to_chat(user,span_notice("You are unable to lift \the [I] from \the [I.loc].")) + return + + //Check if the item is blacklisted. + var/grab = 0 + for(var/typepath in can_hold) + if(istype(I,typepath)) + grab = 1 + break + + //We can grab the item, finally. + if(grab) + to_chat(user, "You collect \the [I].") + I.loc = selected_pocket + wrapped = I + return + else + to_chat(user, span_danger("Your gripper cannot hold \the [target].")) + + else if(istype(target,/obj/machinery/power/apc)) + var/obj/machinery/power/apc/A = target + if(A.opened) + if(A.cell && is_type_in_list(A.cell, can_hold)) + + wrapped = A.cell + + A.cell.add_fingerprint(user) + A.cell.update_icon() + A.cell.loc = selected_pocket + A.cell = null + + A.charging = 0 + A.update_icon() + + user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.") + + else if(istype(target,/mob/living/silicon/robot)) + var/mob/living/silicon/robot/A = target + if(A.opened) + if(A.cell && is_type_in_list(A.cell, can_hold)) + + wrapped = A.cell + + A.cell.add_fingerprint(user) + A.cell.update_icon() + A.update_icon() + A.cell.loc = current_pocket + A.cell = null + + user.visible_message(span_danger("[user] removes the power cell from [A]!"), "You remove the power cell.") + + +//Different types of grippers! + +/obj/item/gripper/engineering + name = "Engineering Gripper" + desc = "An integrated Engineering Gripper." + icon_state = "gripper-omni" + can_hold = list(BASIC_GRIPPER, CIRCUIT_GRIPPER, SHEET_GRIPPER) + +/obj/item/gripper/drone + name = "Drone Gripper" + desc = "An integrated Drone Gripper." + icon_state = "gripper-old" + can_hold = list(BASIC_GRIPPER, SHEET_GRIPPER) + +/obj/item/gripper/omni + name = "omni gripper" + desc = "A strange grasping tool that can hold anything a human can, but still maintains the limitations of application its more limited cousins have." + icon_state = "gripper-omni" + + can_hold = list(OMNI_GRIPPER) // Testing and Event gripper. + +// VEEEEERY limited version for mining borgs. Basically only for swapping cells and upgrading the drills. +/obj/item/gripper/miner + name = "drill maintenance gripper" + desc = "A simple grasping tool for the maintenance of heavy drilling machines." + icon_state = "gripper-mining" + + can_hold = list(MINER_GRIPPER) + +/obj/item/gripper/security + name = "security gripper" + desc = "A simple grasping tool for corporate security work." + icon_state = "gripper-sec" + + can_hold = list(SECURITY_GRIPPER) + +/obj/item/gripper/paperwork + name = "paperwork gripper" + desc = "A simple grasping tool for clerical work." + + can_hold = list(PAPERWORK_GRIPPER) + +/obj/item/gripper/medical + name = "medical gripper" + desc = "A simple grasping tool for medical work." + icon_state = "gripper-flesh" + + can_hold = list(BASIC_GRIPPER, ORGAN_GRIPPER, MEDICAL_GRIPPER) + +/obj/item/gripper/research //A general usage gripper, used for toxins/robotics/xenobio/etc + name = "scientific gripper" + icon_state = "gripper-sci" + desc = "A simple grasping tool suited to assist in a wide array of research applications." + + can_hold = list(BASIC_GRIPPER, CIRCUIT_GRIPPER, SHEET_GRIPPER, EXOSUIT_GRIPPER, ROBOTICS_ORGAN_GRIPPER, RESEARCH_GRIPPER) + +/obj/item/gripper/circuit + name = "circuit assembly gripper" + icon_state = "gripper-circ" + desc = "A complex grasping tool used for working with circuitry." + + can_hold = list(CIRCUIT_GRIPPER) + +/obj/item/gripper/service //Used to handle food, drinks, and seeds. + name = "service gripper" + icon_state = "gripper-sheet" + desc = "A simple grasping tool used to perform tasks in the service sector, such as handling food, drinks, and seeds." + + can_hold = list(SERVICE_GRIPPER) + +/obj/item/gripper/gravekeeper //Used for handling grave things, flowers, etc. + name = "grave gripper" + icon_state = "gripper-old" + desc = "A specialized grasping tool used in the preparation and maintenance of graves." + + can_hold = list(GRAVEYARD_GRIPPER) + +/obj/item/gripper/scene + name = "misc gripper" + desc = "A simple grasping tool that can hold a variety of 'general' objects..." + + can_hold = list(SCENE_GRIPPER) + +/obj/item/gripper/no_use/organ + name = "organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used to preserve and manipulate organic material." + + can_hold = list(ORGAN_GRIPPER) + +/obj/item/gripper/no_use/organ/Entered(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 1 + for(var/obj/item/organ/organ in O) + organ.preserved = 1 + ..() + +/obj/item/gripper/no_use/organ/Exited(var/atom/movable/AM) + if(istype(AM, /obj/item/organ)) + var/obj/item/organ/O = AM + O.preserved = 0 + for(var/obj/item/organ/organ in O) + organ.preserved = 0 + ..() + +/obj/item/gripper/no_use/organ/robotics + name = "robotics organ gripper" + icon_state = "gripper-flesh" + desc = "A specialized grasping tool used in robotics work." + + can_hold = list(ROBOTICS_ORGAN_GRIPPER) + +/obj/item/gripper/no_use/mech + name = "exosuit gripper" + icon_state = "gripper-mech" + desc = "A large, heavy-duty grasping tool used in construction of mechs." + + can_hold = list(EXOSUIT_GRIPPER) + +/obj/item/gripper/no_use //Used when you want to hold and put items in other things, but not able to 'use' the item + +/obj/item/gripper/no_use/attack_self(mob/user as mob) + return + +/obj/item/gripper/no_use/loader //This is used to disallow building with metal. + name = "sheet loader" + desc = "A specialized loading device, designed to pick up and insert sheets of materials inside machines." + icon_state = "gripper-sheet" + + can_hold = list(SHEET_GRIPPER) + +/* + * Misc tools + */ +/obj/item/reagent_containers/glass/bucket/cyborg + var/mob/living/silicon/robot/R + var/last_robot_loc + +/obj/item/reagent_containers/glass/bucket/cyborg/Initialize(mapload) + . = ..() + R = loc.loc + RegisterSignal(src, COMSIG_OBSERVER_MOVED, PROC_REF(check_loc)) + +/obj/item/reagent_containers/glass/bucket/cyborg/proc/check_loc(atom/movable/mover, atom/old_loc, atom/new_loc) + if(old_loc == R || old_loc == R.module) + last_robot_loc = old_loc + if(!istype(loc, /obj/machinery) && loc != R && loc != R.module) + if(last_robot_loc) + forceMove(last_robot_loc) + last_robot_loc = null + else + forceMove(R) + if(loc == R) + hud_layerise() + +/obj/item/reagent_containers/glass/bucket/cyborg/Destroy() + UnregisterSignal(src, COMSIG_OBSERVER_MOVED) + R = null + last_robot_loc = null + ..() diff --git a/code/modules/mob/living/silicon/robot/subtypes/boozeborg.dm b/code/modules/mob/living/silicon/robot/subtypes/boozeborg.dm index b1a1655670c..faeab034433 100644 --- a/code/modules/mob/living/silicon/robot/subtypes/boozeborg.dm +++ b/code/modules/mob/living/silicon/robot/subtypes/boozeborg.dm @@ -49,14 +49,14 @@ What Borgs are available is sadly handled in the above file in the proc /obj/item/robot_module/robot/booze/create_equipment(mob/living/silicon/robot/robot) ..() src.modules += new /obj/item/gripper/service(src) - //src.modules += new /obj/item/reagent_containers/glass/bucket(src) + //src.modules += new /obj/item/reagent_containers/glass/bucket/cyborg(src) //src.modules += new /obj/item/material/minihoe(src) //src.modules += new /obj/item/analyzer/plant_analyzer(src) //src.modules += new /obj/item/storage/bag/plants(src) //src.modules += new /obj/item/robot_harvester(src) src.modules += new /obj/item/material/knife(src) src.modules += new /obj/item/material/kitchen/rollingpin(src) - src.modules += new /obj/item/multitool(src) //to freeze trays + src.modules += new /obj/item/multitool/cyborg(src) //to freeze trays src.modules += new /obj/item/dogborg/jaws/small(src) src.modules += new /obj/item/dogborg/boop_module(src) src.modules += new /obj/item/dogborg/sleeper/compactor/brewer(src) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/teppi.dm b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/teppi.dm index b337ace184c..c1d530b94c1 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/teppi.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/alien animals/teppi.dm @@ -773,7 +773,7 @@ GLOBAL_VAR_INIT(teppi_count, 0) // How mant teppi DO we have? /mob/living/simple_mob/vore/alienanimals/teppi/Destroy() GLOB.teppi_count -- friend_zone = null - active_ghost_pods -= src + GLOB.active_ghost_pods -= src ai_holder.leader = null return ..() @@ -938,7 +938,7 @@ GLOBAL_VAR_INIT(teppi_count, 0) // How mant teppi DO we have? eye_color = baby.eye_color skin_color = baby.skin_color ghostjoin = 1 - active_ghost_pods |= src + GLOB.active_ghost_pods |= src update_icon() //This sets all the things on baby teppi when they are bred from adult teppi diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm index 28a70575f9f..d3af70f384b 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/farm animals/chicken.dm @@ -144,12 +144,10 @@ GLOBAL_VAR_INIT(chicken_count, 0) // How mant chickens DO we have? if(!stat) amount_grown += rand(1,2) if(amount_grown >= 100) - //VOREStation Edit Begin var/mob/living/simple_mob/animal/passive/chicken/C = new (src.loc) C.ghostjoin = 1 C.ghostjoin_icon() - active_ghost_pods |= C - //VOREStation Edit End + GLOB.active_ghost_pods |= C qdel(src) // Say Lists diff --git a/code/modules/overmap/sectors.dm b/code/modules/overmap/sectors.dm index 27a517e7630..24a5f8ff2fa 100644 --- a/code/modules/overmap/sectors.dm +++ b/code/modules/overmap/sectors.dm @@ -91,7 +91,7 @@ real_desc = desc desc = "Scan this to find out more information." //at the moment only used for the OM location renamer. Initializing here in case we want shuttles incl as well in future. Also proc definition convenience. - visitable_overmap_object_instances |= src + GLOB.visitable_overmap_object_instances += src for(var/i in 1 to length(levels_for_distress)) var/current = levels_for_distress[i] diff --git a/code/modules/power/cable.dm b/code/modules/power/cable.dm index ac0681c02b3..4c2fdf19be6 100644 --- a/code/modules/power/cable.dm +++ b/code/modules/power/cable.dm @@ -519,14 +519,6 @@ GLOBAL_LIST_INIT(possible_cable_coil_colours, list( tool_qualities = list(TOOL_CABLE_COIL) singular_name = "cable" -/obj/item/stack/cable_coil/cyborg - name = "cable coil synthesizer" - desc = "A device that makes cable." - gender = NEUTER - matter = null - uses_charge = 1 - charge_costs = list(1) - /obj/item/stack/cable_coil/Initialize(mapload, length = MAXCOIL, var/param_color = null) . = ..() amount = length @@ -626,13 +618,6 @@ GLOBAL_LIST_INIT(possible_cable_coil_colours, list( else to_chat(M, span_notice("You cannot do that.")) -/obj/item/stack/cable_coil/cyborg/verb/set_colour() - set name = "Change Colour" - set category = "Object" - - var/selected_type = tgui_input_list(usr, "Pick new colour.", "Cable Colour", GLOB.possible_cable_coil_colours) - set_cable_color(selected_type, usr) - // Items usable on a cable coil : // - Wirecutters : cut them duh ! // - Cable coil : merge cables diff --git a/code/modules/vore/eating/bellymodes_datum_vr.dm b/code/modules/vore/eating/bellymodes_datum_vr.dm index 78a046a446a..de21629f0e2 100644 --- a/code/modules/vore/eating/bellymodes_datum_vr.dm +++ b/code/modules/vore/eating/bellymodes_datum_vr.dm @@ -259,8 +259,8 @@ GLOBAL_LIST_INIT(digest_modes, list()) egg_contents += E if(egg_contents.len) if(!B.ownegg) - if(B.egg_type in tf_vore_egg_types) - B.egg_path = tf_vore_egg_types[B.egg_type] + if(B.egg_type in GLOB.tf_vore_egg_types) + B.egg_path = GLOB.tf_vore_egg_types[B.egg_type] B.ownegg = new B.egg_path(B) if(B.ownegg && B.egg_name) B.ownegg.egg_name = B.egg_name diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index e3fdf572ce0..9dd5c70993a 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -889,7 +889,7 @@ to_chat(src, span_notice("You are not holding anything.")) return - if(is_type_in_list(I,edible_trash) || adminbus_trash || is_type_in_list(I,edible_tech) && isSynthetic()) // adds edible tech for synth + if(is_type_in_list(I, GLOB.edible_trash) || adminbus_trash || is_type_in_list(I,edible_tech) && isSynthetic()) // adds edible tech for synth if(!I.on_trash_eaten(src)) // shows object's rejection message itself return drop_item() diff --git a/code/modules/vore/eating/transforming_vr.dm b/code/modules/vore/eating/transforming_vr.dm index 53938c23053..7c0234317f2 100644 --- a/code/modules/vore/eating/transforming_vr.dm +++ b/code/modules/vore/eating/transforming_vr.dm @@ -3,8 +3,8 @@ var/egg_path = /obj/structure/closet/secure_closet/egg var/egg_name = "odd egg" - if(O.vore_egg_type in tf_vore_egg_types) - egg_path = tf_vore_egg_types[O.vore_egg_type] + if(O.vore_egg_type in GLOB.tf_vore_egg_types) + egg_path = GLOB.tf_vore_egg_types[O.vore_egg_type] egg_name = "[O.vore_egg_type] egg" var/obj/structure/closet/secure_closet/egg/egg = new egg_path(src) diff --git a/code/modules/xenoarcheaology/artifacts/replicator_vr.dm b/code/modules/xenoarcheaology/artifacts/replicator_vr.dm index 4c456355d54..f1d418f9b06 100644 --- a/code/modules/xenoarcheaology/artifacts/replicator_vr.dm +++ b/code/modules/xenoarcheaology/artifacts/replicator_vr.dm @@ -210,7 +210,7 @@ /obj/machinery/replicator/vore/attackby(obj/item/W as obj, mob/living/user as mob) - if(!W.canremove || !user.canUnEquip(W) || W.possessed_voice || is_type_in_list(W,item_vore_blacklist)) //No armblades, no putting possessed items in it! + if(!W.canremove || !user.canUnEquip(W) || W.possessed_voice || is_type_in_list(W, GLOB.item_vore_blacklist)) //No armblades, no putting possessed items in it! to_chat(user, span_notice("You cannot put \the [W] into the machine.")) return if(istype(W, /obj/item/holder/micro)) //Are you putting a micro in it? @@ -477,7 +477,7 @@ last_process_time = world.time /obj/machinery/replicator/clothing/attackby(obj/item/W as obj, mob/living/user as mob) - if(!W.canremove || !user.canUnEquip(W) || W.possessed_voice || is_type_in_list(W,item_vore_blacklist)) //No armblades, no putting already possessed items in it! + if(!W.canremove || !user.canUnEquip(W) || W.possessed_voice || is_type_in_list(W, GLOB.item_vore_blacklist)) //No armblades, no putting already possessed items in it! to_chat(user, span_notice("You cannot put \the [W] into the machine.")) return if(istype(W, /obj/item/holder/micro) || istype(W, /obj/item/holder/mouse)) //Are you putting a micro/mouse in it? diff --git a/code/modules/xenobio/items/extracts_vr.dm b/code/modules/xenobio/items/extracts_vr.dm index ce317e2e0cb..75af8e6b935 100644 --- a/code/modules/xenobio/items/extracts_vr.dm +++ b/code/modules/xenobio/items/extracts_vr.dm @@ -134,7 +134,7 @@ /decl/chemical_reaction/instant/slime/metal_materials_basic/on_reaction(var/datum/reagents/holder) for(var/i = 1 to 3) - var/type_to_spawn = pickweight(xenobio_metal_materials_normal) + var/type_to_spawn = pickweight(GLOB.xenobio_metal_materials_normal) new type_to_spawn(get_turf(holder.my_atom), 10) ..() @@ -148,7 +148,7 @@ /decl/chemical_reaction/instant/slime/metal_materials_adv/on_reaction(var/datum/reagents/holder) for(var/i = 1 to 2) - var/type_to_spawn = pickweight(xenobio_metal_materials_adv) + var/type_to_spawn = pickweight(GLOB.xenobio_metal_materials_adv) new type_to_spawn(get_turf(holder.my_atom), 10) ..() @@ -162,7 +162,7 @@ /decl/chemical_reaction/instant/slime/metal_materials_weird/on_reaction(var/datum/reagents/holder) for(var/i = 1 to 3) - var/type_to_spawn = pickweight(xenobio_metal_materials_weird) + var/type_to_spawn = pickweight(GLOB.xenobio_metal_materials_weird) new type_to_spawn(get_turf(holder.my_atom), 5) ..() @@ -528,12 +528,12 @@ log_and_message_admins("Gold extract reaction (random mobs) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]") var/type_to_spawn var/list/all_spawnable_types = list() - all_spawnable_types += xenobio_gold_mobs_safe - all_spawnable_types += xenobio_gold_mobs_hostile - all_spawnable_types += xenobio_gold_mobs_birds + all_spawnable_types += GLOB.xenobio_gold_mobs_safe + all_spawnable_types += GLOB.xenobio_gold_mobs_hostile + all_spawnable_types += GLOB.xenobio_gold_mobs_birds for(var/j = 1, j <= 3, j++) if(prob(1)) - type_to_spawn = pickweight(xenobio_gold_mobs_bosses) + type_to_spawn = pickweight(GLOB.xenobio_gold_mobs_bosses) else type_to_spawn = pickweight(all_spawnable_types) @@ -552,7 +552,7 @@ /decl/chemical_reaction/instant/slime/gold_hostile_mob/on_reaction(var/datum/reagents/holder) log_and_message_admins("Gold extract reaction (dangerous mob) has been activated in [get_area(holder.my_atom)]. Last fingerprints: [holder.my_atom.forensic_data?.get_lastprint()]") - var/type_to_spawn = pickweight(xenobio_gold_mobs_hostile) + var/type_to_spawn = pickweight(GLOB.xenobio_gold_mobs_hostile) var/mob/living/C = new type_to_spawn(get_turf(holder.my_atom)) for(var/l = 1, l <= rand(1, 3), l++) step(C, pick(NORTH,SOUTH,EAST,WEST)) @@ -568,10 +568,10 @@ /decl/chemical_reaction/instant/slime/gold_safe_mob/on_reaction(var/datum/reagents/holder) var/type_to_spawn - if(prob(100/(xenobio_gold_mobs_safe.len + 1))) - type_to_spawn = pickweight(xenobio_gold_mobs_birds) + if(prob(100/(GLOB.xenobio_gold_mobs_safe.len + 1))) + type_to_spawn = pickweight(GLOB.xenobio_gold_mobs_birds) else - type_to_spawn = pickweight(xenobio_gold_mobs_safe) + type_to_spawn = pickweight(GLOB.xenobio_gold_mobs_safe) var/mob/living/C = new type_to_spawn(get_turf(holder.my_atom)) for(var/l = 1, l <= rand(1, 3), l++) step(C, pick(NORTH,SOUTH,EAST,WEST)) @@ -610,7 +610,7 @@ /decl/chemical_reaction/instant/slime/silver_materials_basic/on_reaction(var/datum/reagents/holder) for(var/i = 1 to 2) - var/type_to_spawn = pickweight(xenobio_silver_materials_basic) + var/type_to_spawn = pickweight(GLOB.xenobio_silver_materials_basic) new type_to_spawn(get_turf(holder.my_atom), 5) ..() @@ -623,7 +623,7 @@ required = /obj/item/slime_extract/silver /decl/chemical_reaction/instant/slime/silver_materials_adv/on_reaction(var/datum/reagents/holder) - var/type_to_spawn = pickweight(xenobio_silver_materials_adv) + var/type_to_spawn = pickweight(GLOB.xenobio_silver_materials_adv) new type_to_spawn(get_turf(holder.my_atom), 3) ..() @@ -639,15 +639,15 @@ var/type_to_spawn var/amount = 5 var/all_spawnable_types = list() - all_spawnable_types += xenobio_metal_materials_normal - all_spawnable_types += xenobio_metal_materials_adv - all_spawnable_types += xenobio_metal_materials_weird - all_spawnable_types += xenobio_silver_materials_basic - all_spawnable_types += xenobio_silver_materials_adv - all_spawnable_types += xenobio_silver_materials_special + all_spawnable_types += GLOB.xenobio_metal_materials_normal + all_spawnable_types += GLOB.xenobio_metal_materials_adv + all_spawnable_types += GLOB.xenobio_metal_materials_weird + all_spawnable_types += GLOB.xenobio_silver_materials_basic + all_spawnable_types += GLOB.xenobio_silver_materials_adv + all_spawnable_types += GLOB.xenobio_silver_materials_special for(var/i = 1 to 3) type_to_spawn = pickweight(all_spawnable_types) - if(type_to_spawn in xenobio_silver_materials_special) + if(type_to_spawn in GLOB.xenobio_silver_materials_special) amount = 1 new type_to_spawn(get_turf(holder.my_atom), amount) ..() @@ -1218,7 +1218,7 @@ required = /obj/item/slime_extract/cerulean /decl/chemical_reaction/instant/slime/cerulean_random_potion/on_reaction(var/datum/reagents/holder) - var/spawn_type = pickweight(xenobio_cerulean_potions) + var/spawn_type = pickweight(GLOB.xenobio_cerulean_potions) new spawn_type(get_turf(holder.my_atom)) ..() @@ -1633,7 +1633,7 @@ required = /obj/item/slime_extract/rainbow /decl/chemical_reaction/instant/slime/rainbow_random_extract/on_reaction(var/datum/reagents/holder) - var/spawn_type = pickweight(xenobio_rainbow_extracts) + var/spawn_type = pickweight(GLOB.xenobio_rainbow_extracts) new spawn_type(get_turf(holder.my_atom)) ..() diff --git a/code/modules/xenobio/items/slimepotions_vr.dm b/code/modules/xenobio/items/slimepotions_vr.dm index 22d96eb62f9..ec843162d89 100644 --- a/code/modules/xenobio/items/slimepotions_vr.dm +++ b/code/modules/xenobio/items/slimepotions_vr.dm @@ -163,7 +163,7 @@ to_chat(user, span_notice("You feed \the [M] the agent. It may now eventually develop proper sapience.")) M.ghostjoin = 1 - active_ghost_pods |= M + GLOB.active_ghost_pods |= M if(!M.vore_active) add_verb(M, /mob/living/simple_mob/proc/animal_nom) M.ghostjoin_icon() diff --git a/icons/obj/tools_robot.dmi b/icons/obj/tools_robot.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f7a9abe09108573cc1cedcd834e2c39e09c0b4f7 GIT binary patch literal 15045 zcmb8Wbx<7rwgrkMNYDTYJ~#w|TOecx88m2ccP9{B!r;N(-CaU(w-6kHy9IX$GQbQB zy!p<#r|MR{Q}yoqqr0l5x_`R&UVE(_uA(G^heLsbf`Wo4Cky_Bf`Ur>&w=$E1qFp9 z=(|7iV$)k)+ZAl#V&-h~#nr~q0R_b~D_K*~v5=eaLtl;9X>xT!(w>S9mqwum!D(?P z!=SU zHOOtLVMGq(O|3i0%IaRS$9$fP{T*N2#VPy17o5b5l|8Egrg6+>(Asmx**=-N23EUx zmzHs`JR_BN{vfZc@Jm3AH^X59Eh`gspUtR7Iw3OORJQ_M12yI0B;6{5@YfTM2&e5x zowa*sv-Zc|>NmA-DJjb=FJiYo{?;9dxiZaSik87DdcaT;Gke&%e5TV(CRtvd@^O_C z@6|)T4zARfp%%W;_?xFLt)UpgcU9*%A75m(eSQdYxPq<)JxOB(3Uku_ zu;?cc&2NTjquf5b{1w;~Jh36w&0R{KeuM9sK(*&}S-wk0s2gRgS!N3rwN5H%FBBaS zGimvGGya#2BR7|IR%Un$=xH!T+izdQ3Vdj53Zb027YYZwb*BwQxsA}^<6ZHxm||5L zp$M9J+0@{jLuI`z2nvLy4A_afkn4%5c ze2EdL&tpu85gYcWZHP4jGlG4TF0_p*GViQ{73)P<)g^JXe_JJMV1)A!ooD}3#OTYv zC|V}*cG!f(jD!w^1$jL;emNQHGc$*+?d{Czx1&~NUA)Q~9t$&$8n>=qh75|MV+Nb~ zpDsJHe~EtLrA;Mo7I09;?c-KWFy(($pZ=qk8+V)W%^jDzD?{m06j?}ZW|9i~k}Fw= zQ$oGEmA^^a4wE^C`5Z>1D|sKc!aLE{*4B!v9&Uw%m%M1a#B9?%8%+dF)P3Ahj9Wlj z8*a^U&em0*7aMq2a@72?c5aW1AFo`{=RLGQOCy=A@F1%q+s;AK#qZRr#Ba4-(F3I< zlrojYyJZ>$HDXkT$I|8L-~P|bvT*KP@}Q4YQ+Re_`C~Gsz6r}6V@lGoUY{sGc}7j| zD`+~?|LAIm4{NT2XE4KjbZMxhi49?)r(R)Y1guHvU{e+MfS>@^EGw?;>r)bi2|u>A zM)W@T*%6DL7qJvzylVVj=1^n-aV}o4Y&COt?>K)VNH>LvLCixDo+SjL{%j$*w-GID zq4|!??P!+SheGJHQZmj!cu>IK$!I~I}M)I`0 z;*ipZ4`{rPRDDOK*)L{XAu&(l}}>`ScsaK1fT>5&ODsi9{fPl z(1pq0`xC)DmX^_sZKbsNxQ>(c;Kp^BH?iRdeZlX3k?s2TkBPVCR$@$OLDhLzSMBBJ z#TF?r%I3AXn#F$1DgW1-6aQ2A?RgDdU|jHM^BR0=uNNC(ef47#%;7ycxf+Pw>uSu) zokAh-0=-En1J8Q)FB;o&=f@)BpK24fy$Y;7{smrZ(yYW0(DUe_M9};8m^7nq-w%qA z4)hO-AFUUU*MGbx@$2q1|1_2y3|(7DO?F&ccC^lRe5_+%4j;iB^M0w`_Vv6|OW}!q zp_{s-mSdi--@|aZB-s&>`u%V)0ka)ywnmUExFTUgvbbMw{3vQ7@xDAR=Ft;u&w__7 zU4L}UbqMVk7w>lYiPx-~5$7N#Eg8o63A<5rx|_gC;vG!Lus!v6=M8;@67^xcVeU-Y z_saeluByLi2Jm_z!RVP0mJz3rHz;B2n!BCMN?KVt^EZim+Nd8v*Gd3@_y`H}8@d&0@~ zij9Mfd_F+SFq>Q>{o7;UGjOC|$v|aJj2z%s2ByJ=Ox14cuLR7c{h6w>rGi!q3kg}C z*hA@4#2%j+oS&dto9Oy0WY4Ts)Wk1>uBOLhqw}MYAQU;P#@n_|2_Lmm|A=+M!8~{f zyNq{XwA$}R;Nj(53@Bc6jxKT^mkm0!#v~`STceCSqo^1!M+l1E0Ps=ca~fk=SO53esXLl3=lMK0O^@@B z0W6j>ykYPU{_LiQl&L`#X*_g$;k?epT%9PcDp*Xj!#X}0e>X+(HJD(NEr%GM7RyYa zbRd(7i9!rtxS^PZCyZL}|NhCl$KyG~jkg!%hPqrlI#!+xLopH41Xae(rQW@6&jOTS@xMk2^hZi+BXEuGJ#O z$J|e)D#`1m$YS=}N>EHmP{s!NuSK~Z^0r+sFjUAr&cbSx-_k%>E(xoFbI#4vWVy9C-z&1bf;3+dDzN8>k`>avAIChX*$!)*U|wv} zR84}h0C4+~2#nux(;%N9e@S7}vf-27{Nh%OUIv7#_E<73TBenql(bjrQ#!Z&CkkF~;p<4Z;gEA+z{_%7a^oD@Yq+lGOYB7k3ozqCZ)P z>T^oMKJF1?bW<{Ea(l7=_A*f|s)@qU`ZkU}`Rn&YP$se_O$(X?gWPg$@U;lOx^I-+ zsgL_hH2}%=S9LqJU94fAdL1J??jB}9Qy$XLz8Ma+MSw>lso_NAg!Hi~n4$4hbW@+z zkIcf7=)MiILVlYwStz?h!fnyjt8-<&YSlKT{RvGe4e)}J=s18r%cR6A)m%I=6Ry07 zH?{VUYKK+l^c8q2jlT4|{n!Xzh!|GoSTdlm!M(eJt$6N+K-)33g|q_7Q}$r*XWZq z+C=&`tJ_2ttTc#Lu-DUVL6*-i=pnv2)GAvr7)w#uizGya16 zf^+PKw$V4HpNFSvpicEc0jf;>zRoHjeOT74Op(_LwSmp~Bf9$2Lz_j{{WgKKgds-M z^OhJiJe3=9S>v#SOkS=?D-4x6xTcrQh1d7A^FQ}h4sPWZhEaA4ua_(NHL$UgSjSG`ez@$l)3ksglz)n?KrpqT zXs!Qizg$>iWm|J}jVS^=m)S(Oyt!ehaR07ui^JAOl@CV?HG8(S!XmR!+Sp&|=v=fb zwgiFL{XvnB<=`JMTRx*?ANfF$O3p6xHd*z6OIxhr&Z-u$IxiGB0&<&_c#3lL>sYF1 z=}dLg5OcvA#55)^L3mU&9Ujl@kA({pNPt*mPG0=o>-hS4q4@+of!V(V|9N3^MMoVh zC{Z_Ok(<1AEKB+mOyhVvn8&?^?u7qShskq@en(IQfqju$=p%bdGF7Lx5ka;mn{fx6 z)lT#|SxwiNB=;6ik8#=p`91qT9$ktu9t)Ctfl3OOZgRVhkmpL1DY(pp?sU|aua?M@ zLJ(Q+&q+c&2NRf7_azVR6_mGveHSr_h-`W={;`>CAK2>bZH;wUce2Z^$5@E2qbIt_ zmd=Os1~hJA->;zuGak%g=N*S<7jRN#&d5~rc_E6tsE&fo`R~ox+reHw30F?Diu7~v z-b>T5*xEy@XuKIC#!#wcp-9UegpzRYp};&p!_& z|7YS+!&F|tp)&(szkPP^li81FD3!9pDu;_DKy2sQW<3D5D{42 z3CVAS@|O5cdNF@B@Zbc{`CQE{#Ax}>!Aax8&H{N9=xKAGBz9$r2m4Z<5a)s>xKqFr zT^cAmy}g)1>rZR6o>OFJy9zCnZgf7UMSZ+u0cnJ78|4%a)+cY*>oLP4T^^QZY|eE(x3pRRcwRgDL#=cCvfG`Odl9DT65mX%LjX2`D3m z9dp!9;&8DY3z{bAa&@UOoOSAdyW9tt%Liy-1oN!wWrND~jUdF-WCXy07zx_*PwE&~ zsQX#leyNxHMi8dy=g{Lw<^Vlal;g^&*kOMh_=k#KA98zThQsC>}(ETYkm{(qbZfms9`;Z7>#4TA@*Sbo}E1_D0Q~iwpO_vy#pf zK2%De$_n9E%Ua#v95I@@?puGBxMxNcmoo(7KaT2UIzX>u=Tg8zj=d9cl7?nrWQI`b zxu88EDe7qUpZ;=U(GN74)t$l@%KQgvc8KSA6z@tLnbWT&rNyMLdOa7MhjehKY`-d{ z#a*ATXh|}GNv@BF6HS+dli4&5x@Z{&Dr?ntr9gxkra^EwW>WY|ja{vLz^s1}tl{oq z3_e%QiFg0aUg(R#kLQ2Eq-7x60FcI#o zmri)y?66^Isn?t#5nkq)>OQ1ooi;Hm{=&vqDMmJw#DQ22;bci_(PUt zD460j59-d>tj2=37Ns*&O5FKGGIj1yOqvcc7)|`%Jy6;mVmcDk8bl~-_S1l#MZ{Vo z@Nx@PIEm*z-HM+qUOyDpd4D^z%^*Dqv$2VBRd{{A%1ARQIU}jrQvhLM?d+Fwy&MD# zaYC#&-!nyLHsRh2ZPXZ59M|Z#uQ+FBzi+~lj219*ej2^pSVSoEpAaleH8iKpSfrOX zom_>%{TVOuwYN*GRtc{xy&ta=fz`{NQM72}Xy`+_+ZC{+sZewaRRu2 zsPl3&&ntBoAnZ%Qf!GK2*X`%h$bWQc4t;31#`;QW2z)}H><+S=dpAM;*{q>{Gx+?7 z+e+zMIZAk#4=s@;I(l1!A|-;8sLUE6zWsRl8t-{gf&SuxO|;(3Orl`{2dw?u1&WPC z*`EtA8On3lQ@pinSkzwndJYRc75L*G$;QQAgr}2;i1~W0T+P)+9*2B}1-Ru@yCyMb zP+%^V5oeE||0ekNktai*=aqRQ)$?b335q-n*1&vW(rPd5WKhaEPhX8WN2x4&0s^S% z5uW91E4UO#N<*r86aH&u>X`9-w`!_w^-B;CP-Aj^{*>o#m5WMWP*=&9fmcRVc*&{C z%fN1$c&$0hTbO?-JEUmN82rfkULkBFO#8Q-lX!a_7KXL0_o|*0qU%!h?6hodfzHdzLUg2}F#I%pE18}*%@?y85euu)^`KRmS z&E@WV}}H|qNjB25o}xj#YTaPM>F z9IlbL?%_?VL`=t_rnEMw6`H;SCW~`gG!n+jc_?nABxtSmbw1`B9@V)V(fYR|1U&CS zcgTLI%I$zFI47D?c@cBHGfG)XJ<}6T9%xBIomX~%Ft-wU^hRm5J?ly&U~oheT5p?x zS4}W-!H3HJ)5HYgHUGb+?EXBZVnJ|d|4^m_p3iIfr@gb={x76Y+`P854;mLQ@MS^Wts>-6N7uk7>WATy+_J1o??%hH&XJ5lQXSBrg};|K>>>@&mXV7d=pP(-q&WrPtn^n4qZcT8HPb8~3D`os4 zF%9(PkH3RR;`*AzT1{(+L&t?Q{G@*3*JY}auduyv6r7FCsP4G3L^&6@{Jy_`gw2=t zFx3kXH#d|i8*P^OUaln`G^IdxS>|_g2EY-VDQ%~mq{~HZ1bR@e`QD#iPsspOEiIXM zNg9e}97M!#S6yNkU&Ryk2#+d%%7JV!e4P2Dl{BY z^MKq>v`v8AIp1Eqo}RDPJ6v(>CPhefoj1N!dj}~+;x&z-5zM%fV)Mw6{nOU@WMG>4 zY_ku8>5q7!!tlUJ@wcHIs8dmLBn~?h^EwO13{o^@D{5)uS*;V^V~SNYUFYNiwn=Fr zP(cBB|CdUmRgyPs| zMcdb??g&$Ww&)$Ug$rhborXZcSdY(_1st-$LTQjv(S`{P%;osz)S^jz-Ov**X9C>p zBCu?*o+=3Quw;Zg2z*OW-B474=_WrunAdX!N;6S?CNopq8o#n7pRiCFP2qS`su!c3 z0^!f35U>SHNl7&r_oB5mnaR|OR+~s7&N*BDph3JxlHMd`XH)-O8?JI(!tU(RQguQr z2z>IIY=mD94s=yOTK>547;te!*Rezs4DEE@;?Mn{s(O4di$7!MEa_Ix4!E#S$Og{~cB5tKaj48687Fbq%W{xET4RX{)G zQTd?6z;5L0+m$xR&qkNQFmM_HVq@;SR2-U0b@cV(b32#}NZ~Z7;JW{_{Umd{1D)D$ zo)Uz>Z`vJLbn`X+n5umqmPr?YnOM8c{IuL#HRo7Y;yb*~J7hdRKEU<=(*6;C^Hg|! zZOLysi-GqXeMWrwF~e@eM;ZR@^>mP=_Y2~qtr>Weyu(eqMrS*4na|S_@!g3BauJ}HM3%nJ)e+7vJO2JX z$wfTI+SCWOn@WGW)-Kd30%0Cc1CE?{>)xSU zF$w~*_eT@z!!8N{*o+5h4E(Fvlnm&V^sf%+{Kn+6W4D`z-`bNysjZMlq0Gi(mV>S5 z-h;SWvul{Xo+8$s%jloXk+(GlLK;Oz6w-#L_szxEKS$`RJ2F(HrKNlL=~2v{k(k5S zY&C(KxyGq}Th$c;(q8@fwSrxJ zO@wVMdJ=R`Fx`;u@=K*-MGzw8`5UYGf#GmAM#gXdJGCjrph-_!_mm^oHAa*q=lu#H z5iDbBkYqG97FXm?0kPc23L=3PCc{tDO*jCYbDs3J)A*3 zRwZ9BCr86p7ahZ=vlXh!Zn>Pu1?7ISn=NMN?9}ELcYrP6i9>z65w~>r##!^Ar@$+j zVdk`>%TTJ(`0fy)AIBH z)`zVPFH;)QCjtV|^9-AO@NeMQTn|>}mifc@LfYul~~` zA{g6X`&6m#Ih2?=X?=@c#?6V3w$Q6&S7ep!OW>8Bp|rsC(NqPK5tsyf*e%s?sZ9SC zyI-DvV(dp(?vLjr>=)NkJ?1e~Kvhext5+IZBZ&rNiU;}K=8?d<_WL!Z?ZMcEtOMeo z2F)EETY1^z2gDS@&QaDCOKvi_Qv_%Jr-sdPX%jJMPuU;SRJa*gAQC2^s2 z^t4hgkYoLUOXhjY?fO?(%DcW`|J>jv?6ir$n|gUJTaV*Izj(!wZ;HOaYtoF&oB$XvYu_7XN^ zZU@tNQXnnPDXYl(4cD$!nrHZs|7sN>o{#RE*4CV#X01*_Iy(k$%#vP4SdWCA|MIsd zBYFwoug7i6iKY~a*wbn#d+W!oPDy{Pwff@I5RjIH>J3}49=XJ`RkI)(HfjZw@q$Oh zePkG;I!J-PZQ3V>`aScW(C20FWP;@Hf@ve?VJ7&^)b&#tTQ@0N4Ct68ri)Vh26glF z=A7g;`y0>ra~H{pM$^H4&JKE0$sf87UF;T^lSk9VkzwAVG|J>xsC%`|g>*zCHJ5kw zUEUC(H)dk2KFFvFyU>$VMu%ui>crD1BPD@j{l_`<@b#nFyWu^o3=-kBhwV>Rfbmy~ zRAi_H@yM9rU?XTZky)|8u^T3RjvHc=R}d0`)?qSotO&J5xAy;TX93)8ec&aiKsK^+ z!6VZjNUP)NPeO|NO-AZV8(zh^XWvl8WJ)qYGRR6I#!p>(!|G~pyr6&%@#Vra12L~x zkRA|w5-V=S&6W?={HX%N(lkV}9^o8=KL=bu?wPSimzAQARW*i2&%6O~ROGcziOld3 zsJOIh1Lby1^E7nNt1)Vym$lmk=(aYO?94etWHT5;-492ukBQ5#Cir_+E`c9XgKh-b zx;4CtfkS{p0A>4eXqV3&E?qO@BeyWo$obC2>%GrEQ@IAA3{!6MAd1f{*Fgxzq3Go;^QYcR#M&kBc-R&`3u~Dq zGHh>pqMp{M)J%p=n}2ha0KGIQ9RWnUGa`v&v&lSh-M$dKaX-$ufh1Px9UHfc{(`?S z*dzN_Y3Yzf>^+`>NDx~|)Ozu^+_*w6z~4(0CsRsCNy^^jSMO;QU$2tawOx*6bDgu9 z>=&YG))*R$F&HE^N~?>J2TW3ISYfHQo17lNk84Wx99sLGLo?20hegTvEieMv@jq?% z4vHjEk8@2u=xiJ1tSscZvrxRQ{lZaepQ18nr+nY-L=2VNd59s{foFYFZC^I zv%HGbGCGnjqz>;wGK6{Q(U$LyGD&4(X-E&liCJF$3Cd?yxz!+ntygS-_*U3j^Ub6BjNG z4_LSW68bHOaWlEN6Aq9X9561IhZ8%)0aVNn`VbKw3v2`L4e1W zxtglx^v&tY@e~SM-WojqfFim_31QvXcQ|?|dr1f7;fNlHasq8`6)gt`qxQNnO$VSW z`Ksum{#E`l+KqeoIQ`SF%SU*QFWA?)G`OAGUKAx&$Xd)gLA_d2F*XcrI2JD>cm1vL zX9YH6CJ5E!gu;(F48B}w$N2qnBFYG~a}oQerI5HIM{20s@z%j4&dbXS&Z{40`1Mra z1I3qr=Dra1S0%`705?I^vSHZ-%0#q-4n0LMeFQHz*r?TQ-Cb;_jB3z{55ne!!ZlYG%i+FEN*{> zuDC@xgzN~x>LTXAcK2L6bSy-b%7bCg`p*ERV}t$krD{jgZ?X(O>L0kXu71B)_QQLD zOZ(~c3!HWu9_n*_FctKQLzgkbR`cYB++1EhVsSp)<&Eyh>NCFP4@`AvxYQy^()N`3 zLZ~r!%f)q*n$LJQzSZ*57VV+?kL(XnzpF>3eJM^7SQ0mdku?%|beVZ&0(`8c@fZ#T z_?D#?qSD?!k7hP6?W(D>8?cn+HeeY6RJ*exv@Zjctoa*_`Ae0uaqei+Yrx!qKX=FP z3Oey{zYwl|_eri)pBl#`!yDpYNSbj--LNs1aMqQ^IG(S5;ib2XZ;aurD`?|>y1d)+ z-S#sIah+}s>i~n(gB#VrO3y(s{s8+-$(uEvI%-ZeFuj8`UB&1*<64_rJzM5B!Tg%X z2E@X3|9AHB2s(tVQM`a$ky{@EPcW zmT8O^w^aCj+{5AQirl3FWFc>I8Fwg&$V24t|BK`MZ!Gcuky4H-!^P@*T}f4#0oBQc zvd@!U5Fztf&tx(Zj>qzRs*q_8WR6|w~(TSRgLBb zUZ7y_j_09AX>ok>zAhd>h;IN+`Vqk3_MEbM1`jDO&<=k9E44EZl>&c&a_lji5l$|I7X2p_dp%oZPhmyovPi7&T32U>V z4vb!3K5euBUWWVEZbq9GnFXtyU|ea>^#e%Nq|c{+DkFg=fu|P=9@RS)+;&`UoC+_h zJCz^4{c|!btn&eKiOf3q$|ps@0W+EYM6Y)ccta)JDi_ie;yfx^Hko=)<0V_9bl{JL z^=j%m4}|RyAFZ`FYp=Jt z9p`=X_yh=IXyWW_!x#x!cQuF!uT)VHD_#Wo8scs&q(sZ)ENlmI>bj(vreA zZ#vn~Xu$l<;>y`T>K{DrKQpQXtX}2)Nu!pSbSo zG=eMRc2e{lYNNzfrwLLpg`m}!f10|4k$RPhEMYbN-rto5dJn@X9ipSmX^HSUZ04&k zUz;7iANgHxhEj}h{4@EPqoRzTh^xjDCl|40$3H8}HQLUBMO+V5C~7}rSm0f7fwt^Y zDPB<9g=lwg9m-tMQXN;$d1Qw9M)Y!D_~XzbS5K{3=rO67rdT>=&BAd-S51r!Yx7tT z4y1^eAlzK+!OnAlR2_)>`ZOraofvsiI!MjLBqATc{^#a6@|y6U(_s+!B-j$&RG%&Z zlPH^h`ah>xOl$-#ec!uhXAbHpGT)l`r^m=V#nHV?g!MVcb!fQu9gWrq!giHrA!)ABJi#a8w8 zHg|ml?85|O`z2%Rt(e zzYRJs^`zm%F8PSrMpLMNf(O>hC$mkR0emO3`03J=2TDXMQWGRV2oFk&n^U_=%}M6P zjZ&H-Oh7q9$%f(I*3W^rv4=0A8$F(JB|CfG$S>l`V zwWHLW%fz=7O;gYM0ySoH{#?mYB~`;?1$c48)1ZQ_t?lG_t@kU|9TzdZ1)4(Dn(GTG zw(}_|E%j3TajwF9!&`zcsVOPa{r!(qCx-SzeIGI-=}i&s&ZH1oBr4uH9BhS>#b^F zaqEbs@v8}t(SQ#x?)vito(WKM0!;^qY3>l%8*B`8rg+Xc&Z4^= z-{YT$YW89QBM8>;c_n4O#z?upR65L}^+nB>)vzy@Yb$vI)NODyr%SIIJI7XoV?nKm z#2TBz%!8xjhj>$_jb4$%_u4(41*XXCZDKq$Q4d5RN)y|Jj7`@3Pa*;dVYIe3e=@&c zo(nW=Zbb&evi8|0#?OB@HR2yVPr6Uhk}NWtBfvoSzT9SWN+$^Whjf%RmOzI2SDs(L z&*41rtkwc)y)HR`s*HP%?b~C7Z2LYLrOKsvt&$+vv>sLdmFDU6!l{GHUh#&;CwA4n z{?zcp1w*fB%kPMg=LS=p2XY4uzPD`AzdY)@C=g!;L9ZTfyPs^nMm)e8`}~pZ`-X}P zL8Cr(>naNTmM7Y)XPW`(yIR_)&n$#I(tb#>hwHrJSaVwyg60%s_uB#E1LNtC!MjB( zK2h9+57ksM%SP!}=P+9Xd5#Q$5~g!s+WG9eU4F!ca-a2}9$<!QBOIAVGs~fI)4UNZdnx#Yx zvQw46bt9*{3nE2qe|CNNvucZ{z#o|R=J@&jW3G6w!jR3FnVFEobg*OEUH4s)f?(JM9!m*ul9+eW`Xh+zjh}Vlp#ahgfm0K|{N)Z=&?u zibrK@eR>0Vz%os6rNzj=QXu^LRPk!>cWD!`-sA!xb<%d;3X(a=KR%PE%J2avA~hi) z!QX53YH5!zw*fD+Y*Ofh>jk~?VTkJtp`Q_X6jt`+T40nHAcD7M`#^Aybd<|U1RlSa zn48N5Ok0Qb5flhpK6t9(?eXPrRr%P&%$|hUs6}a^7YBQ4^4SS534osT6y4~3jz6QigJUr|3Ci*| z3YYzj?!zr(G(uf#_Pb&$zaW7}v7abJXA($Drm~A<%t2po!eB_ZnP;Swm4^(Wk5_0J z^%EOIcvF%oCZ7qBI9;q|fIPN7#RniqeyT_=v4930JKr;T#41t4&=!%Ptb2df zaC_WHuO#23*+Go zIy<7UYUK{ z?tb>3VpFA|Mj>;L=N6R?9-mK3m~AHgbRp15h@f2)G6UeSDeO`H4QJ)2eT5PEfg{tNHqZ0?IG1ktPR6gW(*Sb{R+{B_<60R~Hhb|QVZcmTA1 z41znuEZ>L}xfBDIqQDrlRc%^&>l~3t0s5=vws9gtU!Vy~zC&MMa2yTL`PZdmWwJCF z3F?Z8R-{!@o;;jfk(o^BzhMZN&+2!S4Q7VpQy~FP4^{k@d0%*NgIo3J!IY38);sJ| z5tt507`pvz;#t0$NJ{q{-VkPnalK*7Y?Em7!K~b@%{F>if&VKy{{KC%ig_U`Z*A$%cdJmAwb2Gr*c4~QlD9RpiJ@PHbi?` zSCULkY|y;@+S}CIUNvHdEVoC~>GB_va!^u~D2}L*Tv%~Zi2i4A)=Ne8tN1^R>R$hP z%0m1gulbL1bUX0>hl-T37oYbtAgTXEArC`a?9T0KU|XMPPr`zSU~2L z*$`mHH@H|@S*MxqKY+(9#lG}vo0-OYLER2>n)(p^`+U0N`Yw3{xKx5N?kMOVDs+!H z1IJFRf&h4256o2dWGg}~2i_skgTrAdK1ETZh+jfoyY0o1R?96a)6DQ7e7G`}$bMN} zSb~YC>q%7gntj&bhq*9J8{`F*`FCA`0xqrUOJA3jTD|U{WGcK_KL7Z=vIp5gubb&% zr)Q?#Na5-CQU8wxb-_=+P}^Hv_dhBTU??0dio;~#3a_R^ZKwf*Ehp`Bz<)(y%j6;8!OTbJG}+|hscsQ zXmg#~k3K%@etJxMp*p4f&*p^a!0PLd!@Q!@{-Z;uxM@puKU?I?ka$^7_vgGhRUtwc zW<+o^}B zQR)xwZ;dmzktBgerAFbydDjbV{3~%k!bN0UZ5S|$sx5fW=XWPoAHr*qDvmaVEuc@hDZmaSAY1zLndQ)ARB~K5#yCPC$nAW zPKWI}8!=vuh74XFJ@~j@F9n({j6A$y>et zB6qOH5espOA~*QMfDKrTn|kacaUg8S!dgo|dQcOf(29TBq%hndu9FLPSxiNHv5P%bOn`Z>YZ1G-{3%FJ+C>^A7M|Fyw_ zGV@yoS{n)H<+Zo4-d+H&cSBlNM}y?bcu;Jj(UG#>9=XXchvP!>DABT6Om6XJX!M{_ zf&zS4VshW#ShQjc^DW}e%tZ8Vh?&kIwyl^zu{^e;Y&sN%Jet@b*76Y2L8GHek*9=j z!S_72|HQ=uiWy<-ocJ9lK@xX*&EnPmi9O+)iZ2xbq~giIhD3vn)9qY)#@QFcU;ZMK z)RhzG51W|rBeM3eOKRQ1Yws=ayC3^u+EyJJBk(Gt9B3lZ?m4=x=31pdyBE$bXGg(U zfT+(7)_A6nvXO5I-s6io2hC)tPWS3D%X@5MRIjh!NjL3vc<$%H=&}4vLXU%k$XOZ9^sP%Egov%w&r*B-YiA8B z{RAVW{EPtXTPmcwe;ED$TjSr1T8vMzGqN}vUNOO~aPsSc*7SeZX64XoR3q;{fDvlA zQLgWb{*o0>q@TQTqL;z`KEWLY0b7VUr_DL#bhD1TnH9FJSly+S)dJC#rxerk z3crk$Fw~=aqzF3y4I>XF+9M76>R<-X92~dro$?SCR&qDe-ta*FUD^XSYy18R!EKEO zQv)3orr!+eo8bSaAn2SwMnIS6NUJpFTDE4QaK8I4^2~K2q6taeB>D&Q=MgdBPp@^( zA77~fHsmk`6RgorS8KD)LAd_p$f5IIS(FBwy`DN*s^6}-YdWY2W5GnWKyC<=$d1$- z9d3+oug^h`i-OKA7{L9s_*v@uVK5rJZ1x57=x;lHEvF_!on-gu?|3RyBocmkBWIoF zpYpA{knYz2DUe(Na {!target.active && } - + {!target.active && } - + {!target.active && } - +
Robot to salvage @@ -179,7 +179,7 @@ const SelectionField = (props: { > - + {capitalize(modul_option.name)} diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotRadio.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotRadio.tsx index de41174e64a..d05242112ef 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotRadio.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotRadio.tsx @@ -25,7 +25,7 @@ export const ModifyRobotRadio = (props: { target: Target }) => { return ( <> {!target.active && } - + { + const { target } = props; + const [tab, setTab] = useState(0); + + const tabs: React.JSX.Element[] = []; + + tabs[0] = ; + tabs[1] = ; + + return ( + + + + setTab(0)}> + PKA + + setTab(1)}> + Multibelt + + + + {tabs[tab]} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotUpgrades.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotUpgrades.tsx index 445b8fed605..1beaa0cd28a 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotUpgrades.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotUpgrades.tsx @@ -32,78 +32,86 @@ export const ModifyRobotUpgrades = (props: { target: Target }) => { useState(''); return ( - <> - {!target.active && } - - - + + {!target.active && ( + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + )} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); }; diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotMultiBelt.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotMultiBelt.tsx new file mode 100644 index 00000000000..19ede467584 --- /dev/null +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotMultiBelt.tsx @@ -0,0 +1,141 @@ +import { useEffect, useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { + Button, + Divider, + Image, + Input, + NoticeBox, + Section, + Stack, + Tabs, +} from 'tgui-core/components'; +import { capitalize } from 'tgui-core/string'; + +import { NoSpriteWarning } from '../../components'; +import { getModuleIcon, prepareSearch } from '../../functions'; +import type { Target } from '../../types'; + +export const ModifyRobotMultiBelt = (props: { target: Target }) => { + const { act } = useBackend(); + const { target } = props; + const { multibelt = [] } = target; + const [SearchMultibelt, setSearchMultibelt] = useState(''); + const [searchInstalledtext, setSearchInstalledtext] = useState(''); + const [selectedMultibelt, setSelectedMultibelt] = useState(0); + + const currentMultibelt = multibelt[selectedMultibelt]; + + useEffect(() => { + if (currentMultibelt) { + act('select_multibelt', { + multibelt: currentMultibelt.ref, + }); + } + }, []); + + return ( + <> + {!target.active && } + {!target.multibelt?.length ? ( + {target.name} has no Multibelt installed. + ) : ( + + + + {target.multibelt.map((_, i) => ( + { + setSelectedMultibelt(i); + act('select_multibelt', { + multibelt: multibelt[i].ref, + }); + }} + > + {i + 1}: {multibelt[i].name} + + ))} + + + +
+ + + + setSearchMultibelt(value)} + /> + + {prepareSearch( + target.multibelt[selectedMultibelt].tools, + SearchMultibelt, + ).map((tool, i) => { + return ( + + ); + })} + + + + + + + + setSearchInstalledtext(value)} + /> + + {prepareSearch( + target.multibelt[selectedMultibelt].integrated_tools, + searchInstalledtext, + ).map((tool, i) => { + return ( + + ); + })} + + +
+
+
+ )} + + ); +}; diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotPKA.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotPKA.tsx similarity index 94% rename from tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotPKA.tsx rename to tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotPKA.tsx index 8ae95ec5163..6d7a310643f 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/ModifyRobotPKA.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/ModifyRobotTabs/SubTabs/ModifyRobotPKA.tsx @@ -11,9 +11,9 @@ import { } from 'tgui-core/components'; import { capitalize } from 'tgui-core/string'; -import { NoSpriteWarning } from '../components'; -import { getModuleIcon, prepareSearch } from '../functions'; -import type { Target } from '../types'; +import { NoSpriteWarning } from '../../components'; +import { getModuleIcon, prepareSearch } from '../../functions'; +import type { Target } from '../../types'; export const ModifyRobotPKA = (props: { target: Target }) => { const { act } = useBackend(); @@ -27,7 +27,7 @@ export const ModifyRobotPKA = (props: { target: Target }) => { {!target.pka ? ( {target.name} has no PKA installed. ) : ( - + Remaining Capacity: {target.pka.capacity} @@ -67,6 +67,7 @@ export const ModifyRobotPKA = (props: { target: Target }) => { diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx index 9019b7c5ef3..22755185ccc 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx +++ b/tgui/packages/tgui/interfaces/ModifyRobot/index.tsx @@ -3,7 +3,6 @@ import { useBackend } from 'tgui/backend'; import { Window } from 'tgui/layouts'; import { Button, - Divider, Dropdown, Input, LabeledList, @@ -18,8 +17,8 @@ import { ModifyRobotNoModule } from './ModifyRobotNoModule'; import { ModifyRobotAccess } from './ModifyRobotTabs/ModifyRobotAccess'; import { ModifyRobotComponent } from './ModifyRobotTabs/ModifyRobotComponent'; import { ModifyRobotModules } from './ModifyRobotTabs/ModifyRobotModules'; -import { ModifyRobotPKA } from './ModifyRobotTabs/ModifyRobotPKA'; import { ModifyRobotRadio } from './ModifyRobotTabs/ModifyRobotRadio'; +import { ModifyRobotSpecialModules } from './ModifyRobotTabs/ModifyRobotSpecialModules'; import { ModifyRobotUpgrades } from './ModifyRobotTabs/ModifyRobotUpgrades'; import type { Data } from './types'; @@ -86,7 +85,7 @@ export const ModifyRobot = (props) => { /> ); tabs[1] = ; - tabs[2] = ; + tabs[2] = ; tabs[3] = ; tabs[4] = ( { { /> ); tabs[7] = ( -
+
{ return ( - {target ? ( - - {target.name} - {!!target.ckey && ' played by ' + target.ckey}. - - ) : ( - No target selected. Please pick one. - )} - - - - - - act('select_target', { - new_target: value, - }) - } - /> - - {!!target?.module && ( - <> + + + {target ? ( + + {target.name} + {!!target.ckey && ' played by ' + target.ckey}. + + ) : ( + No target selected. Please pick one. + )} + + + + + - setRobotName(value)} - /> - - - + /> - - - - + {!!target?.module && ( + <> + + setRobotName(value)} + /> + + + + + + + + + )} + + + {!!target?.module && ( + + + + + act('select_ai', { + new_ai: value, + }) + } + /> + + + + + + + + + )} - - - {!!target?.module && ( - - + + + + {!!target && + (!target.module ? ( + + + + ) : ( + <> - - act('select_ai', { - new_ai: value, - }) - } - /> + + setTab(0)}> + Module Manager + + setTab(1)}> + Upgrade Manager + + setTab(2)}> + Sub Modules + + setTab(3)}> + Radio Manager + + setTab(4)}> + Component Manager + + setTab(5)}> + Access Manager + + setTab(6)}> + Law Manager + + setTab(7)}> + Law Sets + + - - - - - - - - - )} - - - {!!target && - (!target.module ? ( - - ) : ( - <> - - setTab(0)}> - Module Manager - - setTab(1)}> - Upgrade Manager - - setTab(2)}> - PKA - - setTab(3)}> - Radio Manager - - setTab(4)}> - Component Manager - - setTab(5)}> - Access Manager - - setTab(6)}> - Law Manager - - setTab(7)}> - Law Sets - - - {tabs[tab]} - - ))} + {tabs[tab]} + + ))} + ); diff --git a/tgui/packages/tgui/interfaces/ModifyRobot/types.ts b/tgui/packages/tgui/interfaces/ModifyRobot/types.ts index 0efd639349d..d9a21f5d84b 100644 --- a/tgui/packages/tgui/interfaces/ModifyRobot/types.ts +++ b/tgui/packages/tgui/interfaces/ModifyRobot/types.ts @@ -66,6 +66,7 @@ export type Target = { radio_channels: string[]; availalbe_channels: string[]; pka: PKA | undefined; + multibelt: Multibelt[] | undefined; components: Component[]; active_access: Access[]; }; @@ -111,6 +112,16 @@ export type PKA = { max_capacity: number; }; +export type Multibelt = { + name: string; + tools: { + name: string; + path: string; + }[]; + ref: string; + integrated_tools: { name: string; ref: string }[]; +}; + export type InstalledCell = { name: string | null; charge: number | null; diff --git a/vorestation.dme b/vorestation.dme index 7d1e537e02a..66627a12f86 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -3308,6 +3308,7 @@ #include "code\modules\mob\living\silicon\robot\robot_items.dm" #include "code\modules\mob\living\silicon\robot\robot_movement.dm" #include "code\modules\mob\living\silicon\robot\robot_remote_control.dm" +#include "code\modules\mob\living\silicon\robot\robot_simple_items.dm" #include "code\modules\mob\living\silicon\robot\robot_ui.dm" #include "code\modules\mob\living\silicon\robot\robot_ui_module.dm" #include "code\modules\mob\living\silicon\robot\dogborg\dog_defense_modules.dm" From 833dfa52e7528bc087209d1f2bdbb163d2d5c892 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Thu, 19 Jun 2025 22:56:59 +0000 Subject: [PATCH 28/47] Automatic changelog for PR #17507 [ci skip] --- html/changelogs/AutoChangeLog-pr-17507.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17507.yml diff --git a/html/changelogs/AutoChangeLog-pr-17507.yml b/html/changelogs/AutoChangeLog-pr-17507.yml new file mode 100644 index 00000000000..c0bcea4dd20 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17507.yml @@ -0,0 +1,7 @@ +author: "Diana, Kash" +delete-after: True +changes: + - rscadd: "Borgs now have toolbelts instead of various misc tools in their inventory." + - rscadd: "Borgs now have a Material Dispenser that holds all the various materials they can make, instead of being scattered throughout your inventory" + - rscadd: "Grippers are now consolidated and have 5 inventory slots each. No more guessing 'What gripper is used for what'" + - rscdel: "tool bloat" \ No newline at end of file From c9ba2e6f550a3aac961cb1b4d46218d63a72609c Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 02:02:56 +0000 Subject: [PATCH 29/47] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-17507.yml | 7 ------- html/changelogs/AutoChangeLog-pr-17881.yml | 5 ----- html/changelogs/archive/2025-06.yml | 12 ++++++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-17507.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17881.yml diff --git a/html/changelogs/AutoChangeLog-pr-17507.yml b/html/changelogs/AutoChangeLog-pr-17507.yml deleted file mode 100644 index c0bcea4dd20..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17507.yml +++ /dev/null @@ -1,7 +0,0 @@ -author: "Diana, Kash" -delete-after: True -changes: - - rscadd: "Borgs now have toolbelts instead of various misc tools in their inventory." - - rscadd: "Borgs now have a Material Dispenser that holds all the various materials they can make, instead of being scattered throughout your inventory" - - rscadd: "Grippers are now consolidated and have 5 inventory slots each. No more guessing 'What gripper is used for what'" - - rscdel: "tool bloat" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17881.yml b/html/changelogs/AutoChangeLog-pr-17881.yml deleted file mode 100644 index 408964ad4d4..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17881.yml +++ /dev/null @@ -1,5 +0,0 @@ -author: "Kash Guti Will" -delete-after: True -changes: - - rscadd: "Adds genes and diseases to bingle's database so symptoms and genetics stuff can be looked up ingame" - - code_imp: "added unit test for virus names and trait names to prevent tgui issues" \ No newline at end of file diff --git a/html/changelogs/archive/2025-06.yml b/html/changelogs/archive/2025-06.yml index d18a588c648..ecf4df84928 100644 --- a/html/changelogs/archive/2025-06.yml +++ b/html/changelogs/archive/2025-06.yml @@ -282,3 +282,15 @@ - bugfix: reagent dispenser tanks no longer perform alchemy, turning all reagents into fuel spills - code_imp: gene conflict list uses much less memory +2025-06-20: + Diana, Kash: + - rscadd: Borgs now have toolbelts instead of various misc tools in their inventory. + - rscadd: Borgs now have a Material Dispenser that holds all the various materials + they can make, instead of being scattered throughout your inventory + - rscadd: Grippers are now consolidated and have 5 inventory slots each. No more + guessing 'What gripper is used for what' + - rscdel: tool bloat + Kash Guti Will: + - rscadd: Adds genes and diseases to bingle's database so symptoms and genetics + stuff can be looked up ingame + - code_imp: added unit test for virus names and trait names to prevent tgui issues From 5d31f14e946cce07a32316f5ce946306d1f373ae Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Fri, 20 Jun 2025 03:56:56 -0400 Subject: [PATCH 30/47] Climbable Element (#17764) * Initial work * small fix * another fix * this better? * proper type * condensing this a bit * functioning * death reporting subsystemed * fixed * cleanup * use proper sql sending * listvar * add more climbable things * moving things while climbing them is considered shaking * tabbing fix * knockdown should stop climbing * no need to bother * spaces * more climbable objects * fix * small fixes * office climbables * yet more stuff * engineering things * a few more * it's funny * fixes * additional * Moved to element * some more stragglers * unneeded * more graceful * cliffs require special handling * don't do dumb init things * unneeded sanitization, mass insert sanitizes * some small condition fixes * return if climbable * incorporeal check * stop using numbers directly * . --------- Co-authored-by: Kashargul <144968721+Kashargul@users.noreply.github.com> --- .../components/unary/heat_exchanger.dm | 4 + code/ATMOSPHERICS/pipes/tank.dm | 1 + code/__defines/dcs/signals.dm | 8 +- code/__defines/subsystems.dm | 1 + code/__defines/traits/declarations.dm | 3 + code/__defines/traits/macros.dm | 2 + code/_global_vars/traits/_traits.dm | 3 + code/controllers/subsystems/mobs.dm | 49 +++- code/datums/elements/climbable.dm | 250 ++++++++++++++++++ code/defines/procs/statistics.dm | 70 ----- code/game/machinery/OpTable.dm | 2 +- code/game/machinery/airconditioner_vr.dm | 1 + code/game/machinery/atmoalter/canister.dm | 4 + code/game/machinery/atmoalter/pump.dm | 2 + code/game/machinery/atmoalter/scrubber.dm | 4 + code/game/machinery/bioprinter.dm | 1 + code/game/machinery/computer/computer.dm | 90 +------ code/game/machinery/floodlight.dm | 1 + code/game/machinery/frame.dm | 2 + code/game/machinery/jukebox.dm | 1 + code/game/machinery/spaceheater.dm | 1 + code/game/machinery/washing_machine.dm | 1 + code/game/objects/buckling.dm | 3 + code/game/objects/items/poi_items.dm | 6 +- code/game/objects/structures.dm | 130 +-------- code/game/objects/structures/artstuff.dm | 4 + code/game/objects/structures/cliff.dm | 20 +- .../structures/crates_lockers/__closets.dm | 3 + .../crates_lockers/closets/secure/freezer.dm | 4 + .../structures/crates_lockers/crates.dm | 16 +- code/game/objects/structures/fence.dm | 4 +- code/game/objects/structures/fitness_vr.dm | 38 +-- code/game/objects/structures/gravemarker.dm | 2 +- code/game/objects/structures/janicart.dm | 5 +- code/game/objects/structures/ledges.dm | 44 +-- code/game/objects/structures/low_wall.dm | 8 +- code/game/objects/structures/mop_bucket.dm | 2 +- code/game/objects/structures/railing.dm | 63 +---- code/game/objects/structures/trash_pile_vr.dm | 1 + .../game/objects/structures/under_wardrobe.dm | 4 + code/game/objects/stumble_into_vr.dm | 4 +- code/modules/casino/casino.dm | 5 +- .../generic_objects/generic_structure.dm | 1 - code/modules/events/meteor_strike_vr.dm | 2 +- .../food/kitchen/smartfridge/drying_rack.dm | 4 + .../modules/hydroponics/beekeeping/beehive.dm | 4 + code/modules/library/lib_items.dm | 1 + code/modules/library/lib_machines.dm | 4 + code/modules/mining/drilling/drill.dm | 2 + code/modules/mining/mine_outcrops.dm | 2 +- code/modules/mining/ore_box.dm | 3 + code/modules/mob/living/carbon/human/death.dm | 4 +- .../modules/mob/living/silicon/robot/death.dm | 2 +- code/modules/multiz/stairs.dm | 3 +- code/modules/paperwork/filingcabinet.dm | 1 + code/modules/paperwork/papershredder.dm | 1 + code/modules/power/singularity/collector.dm | 1 + code/modules/power/singularity/emitter.dm | 1 + .../power/singularity/field_generator.dm | 1 + .../particle_accelerator.dm | 4 + .../particle_accelerator/particle_control.dm | 1 + code/modules/power/solar.dm | 2 + code/modules/power/tesla/coil.dm | 4 + .../machinery/dispenser/reagent_tank.dm | 8 + code/modules/reagents/machinery/pump.dm | 2 + code/modules/shieldgen/emergency_shield.dm | 4 + code/modules/shieldgen/sheldwallgen.dm | 4 + code/modules/shieldgen/shield_capacitor.dm | 4 + code/modules/shieldgen/shield_gen.dm | 3 +- code/modules/tables/flipping.dm | 5 +- code/modules/tables/interactions.dm | 10 +- code/modules/tables/tables.dm | 3 +- maps/redgate/falls/atoll_objs.dm | 6 +- vorestation.dme | 2 + 74 files changed, 480 insertions(+), 486 deletions(-) create mode 100644 code/__defines/traits/macros.dm create mode 100644 code/datums/elements/climbable.dm diff --git a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm index 078c97af8a1..9a0529d4574 100644 --- a/code/ATMOSPHERICS/components/unary/heat_exchanger.dm +++ b/code/ATMOSPHERICS/components/unary/heat_exchanger.dm @@ -11,6 +11,10 @@ var/obj/machinery/atmospherics/unary/heat_exchanger/partner = null var/update_cycle +/obj/machinery/atmospherics/unary/heat_exchanger/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/atmospherics/unary/heat_exchanger/update_icon() if(node) icon_state = "intact" diff --git a/code/ATMOSPHERICS/pipes/tank.dm b/code/ATMOSPHERICS/pipes/tank.dm index 9abf14702b1..029e3fef26c 100644 --- a/code/ATMOSPHERICS/pipes/tank.dm +++ b/code/ATMOSPHERICS/pipes/tank.dm @@ -21,6 +21,7 @@ /obj/machinery/atmospherics/pipe/tank/Initialize(mapload) icon_state = "air" . = ..() + AddElement(/datum/element/climbable) /obj/machinery/atmospherics/pipe/tank/init_dir() initialize_directions = dir diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 873e6776edd..0bfc1855b85 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -278,7 +278,6 @@ /* #define HEARING_RADIO_FREQ 5 #define HEARING_SPANS 6 #define HEARING_MESSAGE_MODE 7 */ - ///from /datum/controller/subsystem/motion_tracker/notice() (/datum/weakref/source_atom,/turf/echo_turf_location) #define COMSIG_MOVABLE_MOTIONTRACKER "move_motiontracker" @@ -431,6 +430,13 @@ ///called in /obj/update_icon() #define COMSIG_OBJ_UPDATE_ICON "obj_update_icon" +// climbable signals + +///called when a mob is mousedropped onto, or uses a verb to climb an object +#define COMSIG_CLIMBABLE_START_CLIMB "starting_climb_action" +///called when an object is considered unsafe to climb on +#define COMSIG_CLIMBABLE_SHAKE_CLIMBERS "shaking_climbing_mobs" + // /obj/machinery signals ///from /obj/machinery/obj_break(damage_flag): (damage_flag) diff --git a/code/__defines/subsystems.dm b/code/__defines/subsystems.dm index ddf81b70733..1d117736b3f 100644 --- a/code/__defines/subsystems.dm +++ b/code/__defines/subsystems.dm @@ -198,6 +198,7 @@ var/global/list/runlevel_flags = list(RUNLEVEL_LOBBY, RUNLEVEL_SETUP, RUNLEVEL_G #define FIRE_PRIORITY_TICKER 60 #define FIRE_PRIORITY_PLANETS 75 #define FIRE_PRIORITY_MACHINES 100 +#define FIRE_PRIORITY_MOBS 100 #define FIRE_PRIORITY_TGUI 110 #define FIRE_PRIORITY_PROJECTILES 150 #define FIRE_PRIORITY_STATPANEL 390 diff --git a/code/__defines/traits/declarations.dm b/code/__defines/traits/declarations.dm index 71e358701e7..bca9f5cf26c 100644 --- a/code/__defines/traits/declarations.dm +++ b/code/__defines/traits/declarations.dm @@ -9,3 +9,6 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai /// Trait given to a mob that is currently thinking (giving off the "thinking" icon), used in an IC context #define TRAIT_THINKING_IN_CHARACTER "currently_thinking_IC" + +/// Climbable trait, given and taken by the climbable element when added or removed. Exists to be easily checked via HAS_TRAIT(). +#define TRAIT_CLIMBABLE "trait_climbable" diff --git a/code/__defines/traits/macros.dm b/code/__defines/traits/macros.dm new file mode 100644 index 00000000000..d180b244426 --- /dev/null +++ b/code/__defines/traits/macros.dm @@ -0,0 +1,2 @@ +/// Trait applied by element +#define ELEMENT_TRAIT(source) "element_trait_[source]" diff --git a/code/_global_vars/traits/_traits.dm b/code/_global_vars/traits/_traits.dm index 6fab399eed5..d40f01bcc20 100644 --- a/code/_global_vars/traits/_traits.dm +++ b/code/_global_vars/traits/_traits.dm @@ -18,5 +18,8 @@ GLOBAL_LIST_INIT(radiation_levels, list( GLOBAL_LIST_INIT(traits_by_type, list( /mob = list( "TRAIT_THINKING_IN_CHARACTER" = TRAIT_THINKING_IN_CHARACTER, + ), + /obj = list( + "TRAIT_CLIMBABLE" = TRAIT_CLIMBABLE, ) )) diff --git a/code/controllers/subsystems/mobs.dm b/code/controllers/subsystems/mobs.dm index 8fec502f21c..2d8751df2f7 100644 --- a/code/controllers/subsystems/mobs.dm +++ b/code/controllers/subsystems/mobs.dm @@ -7,7 +7,7 @@ SUBSYSTEM_DEF(mobs) name = "Mobs" - priority = 100 + priority = FIRE_PRIORITY_MOBS wait = 2 SECONDS flags = SS_KEEP_TIMING|SS_NO_INIT runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME @@ -19,8 +19,10 @@ SUBSYSTEM_DEF(mobs) var/slept_mobs = 0 var/list/process_z = list() + var/list/death_list = list() + /datum/controller/subsystem/mobs/stat_entry(msg) - msg = "P: [global.mob_list.len] | S: [slept_mobs]" + msg = "P: [global.mob_list.len] | S: [slept_mobs] | D: [death_list.len]" return ..() /datum/controller/subsystem/mobs/fire(resumed = 0) @@ -30,6 +32,15 @@ SUBSYSTEM_DEF(mobs) slept_mobs = 0 for(var/level in 1 to process_z.len) process_z[level] = GLOB.living_players_by_zlevel[level].len + // Lets handle all of these while we have time, should always remain extremely small... + if(death_list.len) // Don't contact DB if this list is empty + if(CONFIG_GET(flag/sql_enabled)) + establish_db_connection() + if(!SSdbcore.IsConnected()) + log_game("SQL ERROR during death reporting. Failed to connect.") + else + SSdbcore.MassInsert(format_table_name("death"), death_list) + death_list.Cut() //cache for sanic speed (lists are references anyways) var/list/currentrun = src.currentrun @@ -92,3 +103,37 @@ SUBSYSTEM_DEF(mobs) /datum/controller/subsystem/mobs/critfail() ..() log_recent() + +/datum/controller/subsystem/mobs/proc/report_death(var/mob/living/L) + if(!L) + return + if(!L.key || !L.mind) + return + if(!ticker || !ticker.mode) + return + ticker.mode.check_win() + + // Don't bother with the rest if we've not got a DB to do anything with + if(!CONFIG_GET(flag/enable_stat_tracking) || !CONFIG_GET(flag/sql_enabled)) + return + + var/area/placeofdeath = get_area(L) + var/podname = placeofdeath ? placeofdeath.name : "Unknown area" + + var/list/data = list( + "name" = L.real_name, + "byondkey" = L.key, + "job" = L.mind.assigned_role, + "special" = L.mind.special_role, + "pod" = podname, + "tod" = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss"), + "laname" = L.lastattacker ? L.lastattacker:real_name : null, + "lakey" = L.lastattacker ? L.lastattacker:key : null, + "gender" = L.gender, + "bruteloss" = L.getBruteLoss(), + "fireloss" = L.getFireLoss(), + "brainloss" = L.brainloss, + "oxyloss" = L.getOxyLoss(), + "coord" = "[L.x], [L.y], [L.z]" + ) + death_list += list(data) diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm new file mode 100644 index 00000000000..f20dce07c7d --- /dev/null +++ b/code/datums/elements/climbable.dm @@ -0,0 +1,250 @@ +///Climbable element. Allows climbing an object using mousedrag or a verb +/datum/element/climbable + element_flags = ELEMENT_DETACH_ON_HOST_DESTROY|ELEMENT_BESPOKE + argument_hash_start_idx = 2 + + VAR_PRIVATE/list/current_climbers + VAR_PRIVATE/climb_delay = 3.5 SECONDS + VAR_PRIVATE/climb_to_adjacent_turf = FALSE // for railings + +/datum/element/climbable/Attach(obj/target, var/climb_delay = 3.5 SECONDS, var/vaulting = FALSE) + . = ..() + if(!isobj(target)) + return ELEMENT_INCOMPATIBLE + + src.climb_delay = climb_delay + src.climb_to_adjacent_turf = vaulting + + RegisterSignal(target, COMSIG_CLIMBABLE_START_CLIMB, PROC_REF(start_climb)) + RegisterSignal(target, COMSIG_CLIMBABLE_SHAKE_CLIMBERS, PROC_REF(shaken)) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(move_shaken)) + RegisterSignal(target, COMSIG_PARENT_EXAMINE, PROC_REF(on_examine)) + target.verbs += /obj/proc/climb_on + + ADD_TRAIT(target, TRAIT_CLIMBABLE, ELEMENT_TRAIT(type)) + +/datum/element/climbable/Detach(obj/source) + UnregisterSignal(source, COMSIG_CLIMBABLE_START_CLIMB) + UnregisterSignal(source, COMSIG_CLIMBABLE_SHAKE_CLIMBERS) + UnregisterSignal(source, COMSIG_MOVABLE_MOVED) + UnregisterSignal(source, COMSIG_PARENT_EXAMINE) + source.verbs -= /obj/proc/climb_on + + REMOVE_TRAIT(source, TRAIT_CLIMBABLE, ELEMENT_TRAIT(type)) + return ..() + + +/// Starts climbing the object +/datum/element/climbable/proc/start_climb(var/obj/climbed_thing, mob/user) + SIGNAL_HANDLER + var/mob/living/H = user + if(istype(H) && can_climb(climbed_thing,H)) + addtimer(CALLBACK(src, PROC_REF(do_climb), climbed_thing, user, climb_delay), 0, TIMER_DELETE_ME) // Isolate from signal handler + +/// Check if the mob is in any condition to climb the object, if the destination is blocked, and how to climb it +/datum/element/climbable/proc/can_climb(var/obj/climbed_thing, var/mob/living/user, post_climb_check=0) + if(user.is_incorporeal()) // No! Bad shadekin! + return FALSE + + var/list/climbers = LAZYACCESS(current_climbers, climbed_thing) + if (!can_touch(climbed_thing, user) || (!post_climb_check && (user in climbers))) + return FALSE + + if (!user.Adjacent(climbed_thing)) + to_chat(user, span_danger("You can't climb there, the way is blocked.")) + return FALSE + + var/obj/occupied = can_climb_turf(climbed_thing) + if(occupied) + to_chat(user, span_danger("There's \a [occupied] in the way.")) + return FALSE + + // Railings are a bit snowflakey, but needed for when you climb from their turf to their facing turf! + if(climb_to_adjacent_turf) + if(get_turf(user) == get_turf(climbed_thing)) + occupied = can_climb_neighbor_turf(climbed_thing) + if(occupied) + to_chat(user, span_danger("You can't climb there, there's \a [occupied] in the way.")) + return FALSE + return TRUE + +/// Performs the wait and any remaining checks before the climb resolves. +/datum/element/climbable/proc/do_climb(var/obj/climbed_thing, var/mob/living/user, var/delay_time) + if(QDELETED(user) || QDELETED(climbed_thing)) + return + + user.visible_message(span_warning("[user] starts climbing onto \the [climbed_thing]!")) + LAZYADDASSOCLIST(current_climbers, climbed_thing, user) + + if(do_after(user,(issmall(user) ? delay_time * 0.6 : delay_time))) + if(can_climb(climbed_thing, user, post_climb_check=1)) + climb_to(climbed_thing, user) + if(get_turf(user) == get_turf(climbed_thing)) + user.visible_message(span_warning("[user] climbs onto \the [climbed_thing]!")) + else + user.visible_message(span_warning("[user] climbed over \the [climbed_thing]!")) + else + to_chat(user, span_warning("You fail to climb onto \the [climbed_thing].")) + LAZYREMOVEASSOC(current_climbers, climbed_thing, user) + +/// Resolve the climb by moving the mob to its final destination. +/datum/element/climbable/proc/climb_to(var/obj/climbed_thing, var/mob/living/user) + if(climb_to_adjacent_turf && get_turf(user) == get_turf(climbed_thing)) + user.forceMove(get_step(climbed_thing, climbed_thing.dir)) + else + user.forceMove(get_turf(climbed_thing)) + +/// Check if a mob is capable of climbing at all +/datum/element/climbable/proc/can_touch(var/obj/climbed_thing, var/mob/user) + if (!user) + return 0 + if(!climbed_thing.Adjacent(user)) + return 0 + if (user.restrained() || user.buckled) + to_chat(user, span_notice("You need your hands and legs free for this.")) + return 0 + if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) + return 0 + if (isAI(user)) + to_chat(user, span_notice("You need hands for this.")) + return 0 + return 1 + +/// Shakes an object, called from movement so it needs to be cleaned up a bit! +/datum/element/climbable/proc/move_shaken(var/obj/climbed_thing, var/mob/user) + SIGNAL_HANDLER + shaken(climbed_thing, null) + +/// Shakes an object, if anyone is climbing it, causes them to fall off it. +/datum/element/climbable/proc/shaken(var/obj/climbed_thing, var/mob/user) + SIGNAL_HANDLER + var/list/climbers = LAZYACCESS(current_climbers, climbed_thing) + for(var/i in climbers) + + // You cannot shake yourself + if(user) // Crates pass null on open because no user + if(!LAZYLEN(climbers) || (user in climbers)) + return + user.visible_message(span_warning("\The [user] shakes \the [climbed_thing]."), span_notice("You shake \the [climbed_thing].")) + + for(var/mob/living/M in climbers) + M.Weaken(1) + to_chat(M, span_danger("You topple as you are shaken off \the [climbed_thing]!")) + climbers.Cut(1,2) + + for(var/mob/living/M in get_turf(climbed_thing)) + if(M.is_incorporeal()) + continue + if(M.lying) //No spamming this on people. + continue + + M.Weaken(3) + to_chat(M, span_danger("You topple as \the [climbed_thing] moves under you!")) + + if(prob(25)) + var/damage = rand(15,30) + var/mob/living/carbon/human/H = M + if(!istype(H)) + to_chat(H, span_danger("You land heavily!")) + M.adjustBruteLoss(damage) + continue + + var/obj/item/organ/external/affecting + + switch(pick(list("ankle","wrist","head","knee","elbow"))) + if("ankle") + affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) + if("knee") + affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) + if("wrist") + affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) + if("elbow") + affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) + if("head") + affecting = H.get_organ(BP_HEAD) + + if(affecting) + to_chat(M, span_danger("You land heavily on your [affecting.name]!")) + affecting.take_damage(damage, 0) + if(affecting.parent) + affecting.parent.add_autopsy_data("Misadventure", damage) + else + to_chat(H, span_danger("You land heavily!")) + H.adjustBruteLoss(damage) + + H.UpdateDamageIcon() + H.updatehealth() + +/datum/element/climbable/proc/on_examine(datum/source, mob/user, list/examine_texts) + SIGNAL_HANDLER + examine_texts += span_notice("It looks climbable.") + +// Cliff climbing requires climbing gear. +/datum/element/climbable/cliff/do_climb(obj/climbed_thing, mob/living/user, delay_time) + // Special snowflake handling, because north facing cliffs require half the time + var/obj/structure/cliff/C = climbed_thing + if(C.is_double_cliff) + delay_time /= 2 + . = ..(climbed_thing, user, delay_time) + +/datum/element/climbable/cliff/can_climb(var/obj/climbed_thing, var/mob/living/user, post_climb_check=0) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + var/obj/item/clothing/shoes/shoes = H.shoes + if(shoes && shoes.rock_climbing) + return ..() // Do the other checks too. + + to_chat(user, span_warning("\The [climbed_thing] is too steep to climb unassisted.")) + return FALSE + + +// Breaks if climbed while unanchored, railings. +/datum/element/climbable/unanchored_can_break/climb_to(var/obj/climbed_thing, var/mob/living/user) + . = ..() + if(!climbed_thing.anchored) + climbed_thing.take_damage(9999) // Fatboy, was originally maxhealth, but that var doesn't exist on everything + + +// Table flipping is important! +/datum/element/climbable/table/climb_to(var/obj/climbed_thing, mob/living/mover) + var/obj/structure/table/TBL = climbed_thing + if(TBL.flipped == 1 && mover.loc == TBL.loc) + var/turf/T = get_step(climbed_thing, TBL.dir) + if(T.Enter(mover)) + return T + return ..() + + + +/// Verb for climbing objects, calls same signal as mouse drop +/obj/proc/climb_on() + set name = "Climb structure" + set desc = "Climbs onto a structure." + set category = "Object" + set src in oview(1) + + SEND_SIGNAL(src, COMSIG_CLIMBABLE_START_CLIMB, usr) + +/// Checks if something is blocking our climb destination, ignores climbable objects +/proc/can_climb_turf(var/obj/climbed_thing) + var/turf/T = get_turf(climbed_thing) + if(!T || !istype(T)) + return "empty void" + if(T.density) + return T + for(var/obj/O in T.contents) + if(O && O.density && !(O.flags & ON_BORDER) && !HAS_TRAIT(O,TRAIT_CLIMBABLE)) //ON_BORDER structures are handled by the Adjacent() check. + return O + return 0 + +/// Check if the destination turf for vaulting is blocked by something. Extremely similar to above. +/proc/can_climb_neighbor_turf(var/obj/climbed_thing) + var/turf/T = get_step(climbed_thing, climbed_thing.dir) + if(!T || !istype(T)) + return 0 + if(T.density == 1) + return T + for(var/obj/O in T.contents) + if(O && O.density && !(O.flags & ON_BORDER && !(turn(O.dir, 180) & climbed_thing.dir)) && !HAS_TRAIT(O,TRAIT_CLIMBABLE)) + return O + return 0 diff --git a/code/defines/procs/statistics.dm b/code/defines/procs/statistics.dm index 01d475d2516..c2e48d42be6 100644 --- a/code/defines/procs/statistics.dm +++ b/code/defines/procs/statistics.dm @@ -26,76 +26,6 @@ if(!CONFIG_GET(flag/enable_stat_tracking)) return -/proc/sql_report_death(var/mob/living/carbon/human/H) - if(!CONFIG_GET(flag/enable_stat_tracking)) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!SSdbcore.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/datum/db_query/query = SSdbcore.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - qdel(query) - - -/proc/sql_report_cyborg_death(var/mob/living/silicon/robot/H) - if(!CONFIG_GET(flag/enable_stat_tracking)) - return - if(!H) - return - if(!H.key || !H.mind) - return - - var/area/placeofdeath = get_area(H) - var/podname = placeofdeath ? placeofdeath.name : "Unknown area" - - var/sqlname = sanitizeSQL(H.real_name) - var/sqlkey = sanitizeSQL(H.key) - var/sqlpod = sanitizeSQL(podname) - var/sqlspecial = sanitizeSQL(H.mind.special_role) - var/sqljob = sanitizeSQL(H.mind.assigned_role) - var/laname - var/lakey - if(H.lastattacker) - laname = sanitizeSQL(H.lastattacker:real_name) - lakey = sanitizeSQL(H.lastattacker:key) - var/sqltime = time2text(world.realtime, "YYYY-MM-DD hh:mm:ss") - var/coord = "[H.x], [H.y], [H.z]" - //to_world("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.bruteloss], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()])") - establish_db_connection() - if(!SSdbcore.IsConnected()) - log_game("SQL ERROR during death reporting. Failed to connect.") - else - var/datum/db_query/query = SSdbcore.NewQuery("INSERT INTO death (name, byondkey, job, special, pod, tod, laname, lakey, gender, bruteloss, fireloss, brainloss, oxyloss, coord) VALUES ('[sqlname]', '[sqlkey]', '[sqljob]', '[sqlspecial]', '[sqlpod]', '[sqltime]', '[laname]', '[lakey]', '[H.gender]', [H.getBruteLoss()], [H.getFireLoss()], [H.brainloss], [H.getOxyLoss()], '[coord]')") - if(!query.Execute()) - var/err = query.ErrorMsg() - log_game("SQL ERROR during death reporting. Error : \[[err]\]\n") - qdel(query) - - /proc/statistic_cycle() set waitfor = 0 if(!CONFIG_GET(flag/enable_stat_tracking)) diff --git a/code/game/machinery/OpTable.dm b/code/game/machinery/OpTable.dm index 66066a9de4a..81bb9f6c15e 100644 --- a/code/game/machinery/OpTable.dm +++ b/code/game/machinery/OpTable.dm @@ -105,7 +105,7 @@ take_victim(target, user) -/obj/machinery/optable/verb/climb_on() +/obj/machinery/optable/verb/climb_onto() set name = "Climb On Table" set category = "Object" set src in oview(1) diff --git a/code/game/machinery/airconditioner_vr.dm b/code/game/machinery/airconditioner_vr.dm index 5e3de80ef7f..e7592ae1778 100644 --- a/code/game/machinery/airconditioner_vr.dm +++ b/code/game/machinery/airconditioner_vr.dm @@ -23,6 +23,7 @@ /obj/machinery/power/thermoregulator/Initialize(mapload) . = ..() default_apply_parts() + AddElement(/datum/element/climbable) /obj/machinery/power/thermoregulator/examine(mob/user) . = ..() diff --git a/code/game/machinery/atmoalter/canister.dm b/code/game/machinery/atmoalter/canister.dm index 180f71b9dd1..ab2c223d622 100644 --- a/code/game/machinery/atmoalter/canister.dm +++ b/code/game/machinery/atmoalter/canister.dm @@ -23,6 +23,10 @@ var/release_log = "" var/update_flag = 0 +/obj/machinery/portable_atmospherics/canister/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/portable_atmospherics/canister/drain_power() return -1 diff --git a/code/game/machinery/atmoalter/pump.dm b/code/game/machinery/atmoalter/pump.dm index 879c78877bf..98aa44c18de 100644 --- a/code/game/machinery/atmoalter/pump.dm +++ b/code/game/machinery/atmoalter/pump.dm @@ -30,6 +30,8 @@ var/list/air_mix = StandardAirMix() src.air_contents.adjust_multi(GAS_O2, air_mix[GAS_O2], GAS_N2, air_mix[GAS_N2]) + AddElement(/datum/element/climbable) + /obj/machinery/portable_atmospherics/powered/pump/update_icon() cut_overlays() diff --git a/code/game/machinery/atmoalter/scrubber.dm b/code/game/machinery/atmoalter/scrubber.dm index 5385b2027e1..0a4a261947a 100644 --- a/code/game/machinery/atmoalter/scrubber.dm +++ b/code/game/machinery/atmoalter/scrubber.dm @@ -24,6 +24,7 @@ . = ..() if(!skip_cell) cell = new/obj/item/cell/apc(src) + AddElement(/datum/element/climbable) /obj/machinery/portable_atmospherics/powered/scrubber/emp_act(severity) if(stat & (BROKEN|NOPOWER)) @@ -173,6 +174,9 @@ name = "[name] (ID [id])" + // Not climbable! + RemoveElement(/datum/element/climbable) + /obj/machinery/portable_atmospherics/powered/scrubber/huge/attack_hand(var/mob/user as mob) to_chat(user, span_notice("You can't directly interact with this machine. Use the scrubber control console.")) diff --git a/code/game/machinery/bioprinter.dm b/code/game/machinery/bioprinter.dm index a1ee5edf953..88c76fefc11 100644 --- a/code/game/machinery/bioprinter.dm +++ b/code/game/machinery/bioprinter.dm @@ -81,6 +81,7 @@ /obj/machinery/organ_printer/Initialize(mapload) . = ..() default_apply_parts() + AddElement(/datum/element/climbable) /obj/machinery/organ_printer/examine(var/mob/user) . = ..() diff --git a/code/game/machinery/computer/computer.dm b/code/game/machinery/computer/computer.dm index 9bf22490977..f07c7964af5 100644 --- a/code/game/machinery/computer/computer.dm +++ b/code/game/machinery/computer/computer.dm @@ -9,9 +9,6 @@ idle_power_usage = 300 active_power_usage = 300 blocks_emissive = FALSE - var/climbable = TRUE - var/list/climbers - var/climb_delay = 3.5 SECONDS var/processing = 0 var/icon_keyboard = "generic_key" @@ -25,8 +22,7 @@ . = ..() power_change() update_icon() - if(climbable) - verbs += /obj/structure/proc/climb_on + AddElement(/datum/element/climbable) /obj/machinery/computer/process() if(stat & (NOPOWER|BROKEN)) @@ -37,7 +33,6 @@ if(prob(20/severity)) set_broken() ..() - /obj/machinery/computer/ex_act(severity) switch(severity) if(1.0) @@ -136,86 +131,3 @@ return attack_hand(user) return - -/obj/machinery/computer/proc/climb_on() - set name = "Climb structure" - set desc = "Climbs onto a structure." - set category = "Object" - set src in oview(1) - - do_climb(usr) - -/obj/machinery/computer/MouseDrop_T(mob/target, mob/user) - var/mob/living/H = user - if(istype(H) && can_climb(H) && target == user) - do_climb(target) - else - return ..() - -/obj/machinery/computer/proc/can_climb(var/mob/living/user, post_climb_check=0) - if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) - return 0 - - if (!user.Adjacent(src)) - to_chat(user, span_danger("You can't climb there, the way is blocked.")) - return 0 - - var/obj/occupied = turf_is_crowded() - if(occupied) - to_chat(user, span_danger("There's \a [occupied] in the way.")) - return 0 - return 1 - -/obj/machinery/computer/proc/turf_is_crowded() - var/turf/T = get_turf(src) - if(!T || !istype(T)) - return "empty void" - if(T.density) - return T - for(var/obj/O in T.contents) - if(istype(O,/obj/machinery/computer)) - var/obj/machinery/computer/S = O - if(S.climbable) continue - if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. - return O - return 0 - -/obj/machinery/computer/proc/do_climb(var/mob/living/user) - if (!can_climb(user)) - return - - user.visible_message(span_warning("[user] starts climbing onto \the [src]!")) - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) - LAZYREMOVE(climbers, user) - return - - if (!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - user.forceMove(climb_to(user)) - - if (get_turf(user) == get_turf(src)) - user.visible_message(span_warning("[user] climbs onto \the [src]!")) - LAZYREMOVE(climbers, user) - -/obj/machinery/computer/proc/climb_to(var/mob/living/user) - return get_turf(src) - - -/obj/machinery/computer/proc/can_touch(var/mob/user) - if (!user) - return 0 - if(!Adjacent(user)) - return 0 - if (user.restrained() || user.buckled) - to_chat(user, span_notice("You need your hands and legs free for this.")) - return 0 - if (user.stat || user.paralysis || user.sleeping || user.lying || user.weakened) - return 0 - if (isAI(user)) - to_chat(user, span_notice("You need hands for this.")) - return 0 - return 1 diff --git a/code/game/machinery/floodlight.dm b/code/game/machinery/floodlight.dm index d1b3a8888e1..3bd60c90748 100644 --- a/code/game/machinery/floodlight.dm +++ b/code/game/machinery/floodlight.dm @@ -16,6 +16,7 @@ /obj/machinery/floodlight/Initialize(mapload) . = ..() cell = new(src) + AddElement(/datum/element/climbable) /obj/machinery/floodlight/update_icon() cut_overlays() diff --git a/code/game/machinery/frame.dm b/code/game/machinery/frame.dm index 9315a13ccb5..d41491f22ff 100644 --- a/code/game/machinery/frame.dm +++ b/code/game/machinery/frame.dm @@ -296,6 +296,8 @@ GLOBAL_LIST(construction_frame_floor) update_icon() + AddElement(/datum/element/climbable) + /obj/structure/frame/attackby(obj/item/P as obj, mob/user as mob) if(P.has_tool_quality(TOOL_WRENCH)) if(state == FRAME_PLACED && !anchored) diff --git a/code/game/machinery/jukebox.dm b/code/game/machinery/jukebox.dm index 1e683c347ef..79835233fac 100644 --- a/code/game/machinery/jukebox.dm +++ b/code/game/machinery/jukebox.dm @@ -35,6 +35,7 @@ update_icon() if(!LAZYLEN(getTracksList())) stat |= BROKEN + AddElement(/datum/element/climbable) /obj/machinery/media/jukebox/Destroy() qdel(wires) diff --git a/code/game/machinery/spaceheater.dm b/code/game/machinery/spaceheater.dm index 801c344d0a3..06c2d1ccf9c 100644 --- a/code/game/machinery/spaceheater.dm +++ b/code/game/machinery/spaceheater.dm @@ -39,6 +39,7 @@ cell = new cell_type(src) default_apply_parts() update_icon() + AddElement(/datum/element/climbable) /obj/machinery/space_heater/RefreshParts(var/limited = 0) min_temperature = DEFAULT_MIN_TEMP diff --git a/code/game/machinery/washing_machine.dm b/code/game/machinery/washing_machine.dm index 5b4536e87a8..45fb756b921 100644 --- a/code/game/machinery/washing_machine.dm +++ b/code/game/machinery/washing_machine.dm @@ -32,6 +32,7 @@ /obj/machinery/washing_machine/Initialize(mapload) . = ..() default_apply_parts() + AddElement(/datum/element/climbable) /obj/machinery/washing_machine/AltClick() start() diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm index 5a97dc4c0b3..0404e30235a 100644 --- a/code/game/objects/buckling.dm +++ b/code/game/objects/buckling.dm @@ -38,6 +38,9 @@ if(can_buckle && istype(M)) if(user_buckle_mob(M, user)) return TRUE + if(M == user && HAS_TRAIT(src,TRAIT_CLIMBABLE)) // Buckling takes priority + SEND_SIGNAL(src, COMSIG_CLIMBABLE_START_CLIMB, user) + return TRUE /atom/movable/proc/has_buckled_mobs() return LAZYLEN(buckled_mobs) diff --git a/code/game/objects/items/poi_items.dm b/code/game/objects/items/poi_items.dm index 43d5e89d58f..ec5afbb8889 100644 --- a/code/game/objects/items/poi_items.dm +++ b/code/game/objects/items/poi_items.dm @@ -64,11 +64,15 @@ icon = 'icons/obj/closets/poireactor.dmi' closet_appearance = null catalogue_data = list(/datum/category_item/catalogue/information/objects/oldreactor) - climbable = FALSE starts_with = list( /obj/item/fuel_assembly/deuterium = 6) +/obj/structure/closet/crate/oldreactor/Initialize(mapload) + . = ..() + // Not climbable! + RemoveElement(/datum/element/climbable) + /obj/item/poi/brokenoldreactor icon_state = "poireactor_broken" name = "ruptured fission reactor rack" diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 3001cabecf4..a251a72dbbf 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -3,11 +3,8 @@ w_class = ITEMSIZE_NO_CONTAINER blocks_emissive = EMISSIVE_BLOCK_GENERIC - var/climbable - var/climb_delay = 3.5 SECONDS var/breakable var/parts - var/list/climbers var/block_turf_edges = FALSE // If true, turf edge icons will not be made on the turf this occupies. var/list/connections @@ -15,11 +12,6 @@ var/list/blend_objects = newlist() // Objects which to blend with var/list/noblend_objects = newlist() //Objects to avoid blending with (such as children of listed blend objects. -/obj/structure/Initialize(mapload) - . = ..() - if(climbable) - verbs += /obj/structure/proc/climb_on - /obj/structure/Destroy() if(parts) new parts(loc) @@ -34,12 +26,7 @@ var/mob/living/carbon/human/H = user if(H.species.can_shred(user)) attack_generic(user,1,"slices") - - if(LAZYLEN(climbers) && !(user in climbers)) - user.visible_message(span_warning("[user.name] shakes \the [src]."), \ - span_notice("You shake \the [src].")) - structure_shaken() - + SEND_SIGNAL(src, COMSIG_CLIMBABLE_SHAKE_CLIMBERS, user) return ..() /obj/structure/attack_tk() @@ -57,121 +44,6 @@ if(3.0) return -/obj/structure/proc/climb_on() - set name = "Climb structure" - set desc = "Climbs onto a structure." - set category = "Object" - set src in oview(1) - - do_climb(usr) - -/obj/structure/MouseDrop_T(mob/target, mob/user) - var/mob/living/H = user - if(istype(H) && can_climb(H) && target == user) - do_climb(target) - else - return ..() - -/obj/structure/proc/can_climb(var/mob/living/user, post_climb_check=0) - if (!climbable || !can_touch(user) || (!post_climb_check && (user in climbers))) - return 0 - - if (!user.Adjacent(src)) - to_chat(user, span_danger("You can't climb there, the way is blocked.")) - return 0 - - var/obj/occupied = turf_is_crowded() - if(occupied) - to_chat(user, span_danger("There's \a [occupied] in the way.")) - return 0 - return 1 - -/obj/structure/proc/turf_is_crowded() - var/turf/T = get_turf(src) - if(!T || !istype(T)) - return "empty void" - if(T.density) - return T - for(var/obj/O in T.contents) - if(istype(O,/obj/structure)) - var/obj/structure/S = O - if(S.climbable) continue - if(O && O.density && !(O.flags & ON_BORDER)) //ON_BORDER structures are handled by the Adjacent() check. - return O - return 0 - -/obj/structure/proc/do_climb(var/mob/living/user) - if (!can_climb(user)) - return - - user.visible_message(span_warning("[user] starts climbing onto \the [src]!")) - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? climb_delay * 0.6 : climb_delay))) - LAZYREMOVE(climbers, user) - return - - if (!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - user.forceMove(climb_to(user)) - - if (get_turf(user) == get_turf(src)) - user.visible_message(span_warning("[user] climbs onto \the [src]!")) - LAZYREMOVE(climbers, user) - -/obj/structure/proc/climb_to(var/mob/living/user) - return get_turf(src) - -/obj/structure/proc/structure_shaken() - for(var/mob/living/M in climbers) - M.Weaken(1) - to_chat(M, span_danger("You topple as you are shaken off \the [src]!")) - climbers.Cut(1,2) - - for(var/mob/living/M in get_turf(src)) - if(M.lying) return //No spamming this on people. - - M.Weaken(3) - to_chat(M, span_danger("You topple as \the [src] moves under you!")) - - if(prob(25)) - - var/damage = rand(15,30) - var/mob/living/carbon/human/H = M - if(!istype(H)) - to_chat(H, span_danger("You land heavily!")) - M.adjustBruteLoss(damage) - return - - var/obj/item/organ/external/affecting - - switch(pick(list("ankle","wrist","head","knee","elbow"))) - if("ankle") - affecting = H.get_organ(pick(BP_L_FOOT, BP_R_FOOT)) - if("knee") - affecting = H.get_organ(pick(BP_L_LEG, BP_R_LEG)) - if("wrist") - affecting = H.get_organ(pick(BP_L_HAND, BP_R_HAND)) - if("elbow") - affecting = H.get_organ(pick(BP_L_ARM, BP_R_ARM)) - if("head") - affecting = H.get_organ(BP_HEAD) - - if(affecting) - to_chat(M, span_danger("You land heavily on your [affecting.name]!")) - affecting.take_damage(damage, 0) - if(affecting.parent) - affecting.parent.add_autopsy_data("Misadventure", damage) - else - to_chat(H, span_danger("You land heavily!")) - H.adjustBruteLoss(damage) - - H.UpdateDamageIcon() - H.updatehealth() - return - /obj/structure/proc/can_touch(var/mob/user) if (!user) return 0 diff --git a/code/game/objects/structures/artstuff.dm b/code/game/objects/structures/artstuff.dm index c7e094b2919..443cb18cbfa 100644 --- a/code/game/objects/structures/artstuff.dm +++ b/code/game/objects/structures/artstuff.dm @@ -13,6 +13,10 @@ //max_integrity = 60 var/obj/item/canvas/painting = null +/obj/structure/easel/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + //Adding canvases /obj/structure/easel/attackby(obj/item/I, mob/user, params) if(istype(I, /obj/item/canvas)) diff --git a/code/game/objects/structures/cliff.dm b/code/game/objects/structures/cliff.dm index 6e13b840bca..a39aafc61b9 100644 --- a/code/game/objects/structures/cliff.dm +++ b/code/game/objects/structures/cliff.dm @@ -19,6 +19,7 @@ When mapping these in, be sure to give at least a one tile clearance, as NORTH f two tiles on initialization, and which way a cliff is facing may change during maploading. */ +#define CLIFF_CLIMB_DELAY 10 /obj/structure/cliff name = "cliff" desc = "A steep rock ledge. You might be able to climb it if you feel bold enough." @@ -31,8 +32,6 @@ two tiles on initialization, and which way a cliff is facing may change during m anchored = TRUE density = TRUE opacity = FALSE - climbable = TRUE - climb_delay = 10 SECONDS unacidable = TRUE block_turf_edges = TRUE // Don't want turf edges popping up from the cliff edge. plane = TURF_PLANE @@ -48,6 +47,7 @@ two tiles on initialization, and which way a cliff is facing may change during m /obj/structure/cliff/Initialize(mapload) . = ..() register_dangerous_to_step() + AddElement(/datum/element/climbable/cliff,CLIFF_CLIMB_DELAY SECONDS) /obj/structure/cliff/Destroy() unregister_dangerous_to_step() @@ -106,11 +106,8 @@ two tiles on initialization, and which way a cliff is facing may change during m // Now make the bottom cliff have mostly the same variables. var/obj/structure/cliff/bottom/bottom = new(T) is_double_cliff = TRUE - climb_delay /= 2 // Since there are two cliffs to climb when going north, both take half the time. - bottom.dir = dir bottom.is_double_cliff = TRUE - bottom.climb_delay = climb_delay bottom.icon_variant = icon_variant bottom.corner = corner bottom.ramp = ramp @@ -254,19 +251,10 @@ two tiles on initialization, and which way a cliff is facing may change during m sleep(5) bottom_cliff.fall_off_cliff(L) -/obj/structure/cliff/can_climb(mob/living/user, post_climb_check = FALSE) - // Cliff climbing requires climbing gear. - if(ishuman(user)) - var/mob/living/carbon/human/H = user - var/obj/item/clothing/shoes/shoes = H.shoes - if(shoes && shoes.rock_climbing) - return ..() // Do the other checks too. - - to_chat(user, span_warning("\The [src] is too steep to climb unassisted.")) - return FALSE - // This tells AI mobs to not be dumb and step off cliffs willingly. /obj/structure/cliff/is_safe_to_step(mob/living/L) if(should_fall(L)) return FALSE return ..() + +#undef CLIFF_CLIMB_DELAY diff --git a/code/game/objects/structures/crates_lockers/__closets.dm b/code/game/objects/structures/crates_lockers/__closets.dm index 683ce6b7911..afb32326aeb 100644 --- a/code/game/objects/structures/crates_lockers/__closets.dm +++ b/code/game/objects/structures/crates_lockers/__closets.dm @@ -357,6 +357,9 @@ if(!isturf(user.loc)) // are you in a container/closet/pod/etc? return if(!opened) + // Attempt to climb if not opened! + if(O == user) + SEND_SIGNAL(src, COMSIG_CLIMBABLE_START_CLIMB, user) return if(istype(O, /obj/structure/closet)) return diff --git a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm index ecba61ba485..32f15499a52 100644 --- a/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm +++ b/code/game/objects/structures/crates_lockers/closets/secure/freezer.dm @@ -1,3 +1,7 @@ +/obj/structure/closet/secure_closet/freezer/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/closet/secure_closet/freezer/kitchen name = "kitchen cabinet" req_access = list(access_kitchen) diff --git a/code/game/objects/structures/crates_lockers/crates.dm b/code/game/objects/structures/crates_lockers/crates.dm index 5170037b66f..ace35e003d6 100644 --- a/code/game/objects/structures/crates_lockers/crates.dm +++ b/code/game/objects/structures/crates_lockers/crates.dm @@ -5,7 +5,6 @@ desc = "A rectangular steel crate." icon = 'icons/obj/closets/bases/crate.dmi' closet_appearance = /decl/closet_appearance/crate - climbable = TRUE dir = 4 //Spawn facing 'forward' by default. var/points_per_crate = 5 var/rigged = 0 @@ -13,6 +12,10 @@ open_sound = 'sound/effects/crate_open.ogg' close_sound = 'sound/effects/crate_close.ogg' +/obj/structure/closet/crate/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/closet/crate/can_open() return 1 @@ -40,8 +43,7 @@ O.forceMove(get_turf(src)) src.opened = 1 - if(climbable) - structure_shaken() + SEND_SIGNAL(src, COMSIG_CLIMBABLE_SHAKE_CLIMBERS, null) update_icon() return 1 @@ -69,14 +71,6 @@ update_icon() return 1 -/obj/structure/closet/crate/MouseDrop_T(mob/target, mob/user) - // Adds climbing from drag, You can't put yourself in crates with a drag anyway... Nore anyone else actually. - var/mob/living/H = user - if(istype(H) && can_climb(H) && target == user) - do_climb(target) - else - return ..() - /obj/structure/closet/crate/verb/rotate_clockwise() set name = "Rotate Crate Clockwise" set category = "Object" diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm index 8180e59dbd7..8b2fc19ad18 100644 --- a/code/game/objects/structures/fence.dm +++ b/code/game/objects/structures/fence.dm @@ -102,11 +102,11 @@ if(MEDIUM_HOLE) visible_message(span_notice("\The [user] cuts into \the [src] some more.")) to_chat(user, span_notice("You could probably fit yourself through that hole now. Although climbing through would be much faster if you made it even bigger.")) - climbable = TRUE + AddElement(/datum/element/climbable) if(LARGE_HOLE) visible_message(span_notice("\The [user] completely cuts through \the [src].")) to_chat(user, span_notice("The hole in \the [src] is now big enough to walk through.")) - climbable = FALSE + RemoveElement(/datum/element/climbable) update_cut_status() return TRUE diff --git a/code/game/objects/structures/fitness_vr.dm b/code/game/objects/structures/fitness_vr.dm index 48635f47fc2..79905b80356 100644 --- a/code/game/objects/structures/fitness_vr.dm +++ b/code/game/objects/structures/fitness_vr.dm @@ -5,11 +5,14 @@ icon_state = "ropes" density = TRUE throwpass = TRUE - climbable = TRUE layer = WINDOW_LAYER anchored = TRUE flags = ON_BORDER +/obj/structure/fitness/boxing_ropes/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable, vaulting = TRUE) + /obj/structure/fitness/boxing_ropes/CanPass(atom/movable/mover, turf/target) if(istype(mover) && mover.checkpass(PASSTABLE)) return TRUE @@ -23,39 +26,6 @@ if(get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir return !density return TRUE -/obj/structure/fitness/boxing_ropes/do_climb(var/mob/living/user) //Sets it so that players can climb *over* the turf and will enter the the turf **this** turf is facing. - if(!can_climb(user)) - return - - user.visible_message(span_warning("[user] starts climbing onto \the [src]!")) - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? 20 : 34))) - LAZYREMOVE(climbers, user) - return - - if(!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - if(get_turf(user) == get_turf(src)) - user.forceMove(get_step(src, src.dir)) - else - user.forceMove(get_turf(src)) - - user.visible_message(span_warning("[user] climbed over \the [src]!")) - LAZYREMOVE(climbers, user) - -/obj/structure/fitness/boxing_ropes/can_climb(var/mob/living/user, post_climb_check=0) //Sets it to keep people from climbing over into the next turf if it is occupied. - if(!..()) - return 0 - - if(get_turf(user) == get_turf(src)) - var/obj/occupied = neighbor_turf_impassable() - if(occupied) - to_chat(user, span_danger("You can't climb there, there's \a [occupied] in the way.")) - return 0 - return 1 /obj/structure/fitness/boxing_ropes/bottom plane = MOB_PLANE diff --git a/code/game/objects/structures/gravemarker.dm b/code/game/objects/structures/gravemarker.dm index 33bb0656609..9028a62adfa 100644 --- a/code/game/objects/structures/gravemarker.dm +++ b/code/game/objects/structures/gravemarker.dm @@ -6,7 +6,6 @@ density = TRUE anchored = TRUE throwpass = 1 - climbable = TRUE layer = ABOVE_JUNK_LAYER @@ -27,6 +26,7 @@ if(!material) return INITIALIZE_HINT_QDEL color = material.icon_colour + AddElement(/datum/element/climbable) /obj/structure/gravemarker/examine(mob/user) . = ..() diff --git a/code/game/objects/structures/janicart.dm b/code/game/objects/structures/janicart.dm index 79bad9dcf76..d89cf47f7e6 100644 --- a/code/game/objects/structures/janicart.dm +++ b/code/game/objects/structures/janicart.dm @@ -9,7 +9,6 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) anchored = FALSE density = TRUE flags = OPENCONTAINER - climbable = TRUE //copypaste sorry var/amount_per_transfer_from_this = 5 //shit I dunno, adding this so syringes stop runtime erroring. --NeoFite var/obj/item/storage/bag/trash/mybag = null @@ -24,6 +23,10 @@ GLOBAL_LIST_BOILERPLATE(all_janitorial_carts, /obj/structure/janitorialcart) var/static/list/equippable_item_whitelist +/obj/structure/janitorialcart/Initialize(mapload, ...) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/janitorialcart/proc/equip_janicart_item(mob/user, obj/item/I) if(!equippable_item_whitelist) equippable_item_whitelist = typecacheof(list( diff --git a/code/game/objects/structures/ledges.dm b/code/game/objects/structures/ledges.dm index e56ebaee405..2bd4e8877ae 100644 --- a/code/game/objects/structures/ledges.dm +++ b/code/game/objects/structures/ledges.dm @@ -4,13 +4,16 @@ icon = 'icons/obj/ledges.dmi' density = TRUE throwpass = 1 - climbable = TRUE anchored = TRUE var/solidledge = 1 flags = ON_BORDER layer = STAIRS_LAYER icon_state = "ledge" +/obj/structure/ledge/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable, vaulting = TRUE) + /obj/structure/ledge_corner icon_state = "ledge-corner" flags = 0 @@ -19,10 +22,13 @@ icon = 'icons/obj/ledges.dmi' density = TRUE throwpass = 1 - climbable = TRUE anchored = TRUE layer = STAIRS_LAYER +/obj/structure/ledge_corner/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable, vaulting = TRUE) + /obj/structure/ledge/ledge_nub desc = "Part of a rocky ledge." icon_state = "ledge-nub" @@ -49,37 +55,3 @@ if(solidledge && get_dir(mover, target) == dir) // From here to elsewhere, can't move in our dir return FALSE return TRUE - -/obj/structure/ledge/do_climb(var/mob/living/user) - if(!can_climb(user)) - return - - user.visible_message(span_warning("[user] starts climbing onto \the [src]!")) - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? 20 : 34))) - LAZYREMOVE(climbers, user) - return - - if(!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - if(get_turf(user) == get_turf(src)) - user.forceMove(get_step(src, src.dir)) - else - user.forceMove(get_turf(src)) - - user.visible_message(span_warning("[user] climbed over \the [src]!")) - LAZYREMOVE(climbers, user) - -/obj/structure/ledge/can_climb(var/mob/living/user, post_climb_check=0) - if(!..()) - return 0 - - if(get_turf(user) == get_turf(src)) - var/obj/occupied = neighbor_turf_impassable() - if(occupied) - to_chat(user, span_danger("You can't climb there, there's \a [occupied] in the way.")) - return 0 - return 1 diff --git a/code/game/objects/structures/low_wall.dm b/code/game/objects/structures/low_wall.dm index 807ac96af84..e339a5fc0c8 100644 --- a/code/game/objects/structures/low_wall.dm +++ b/code/game/objects/structures/low_wall.dm @@ -11,7 +11,6 @@ //atom_flags = ATOM_FLAG_NO_TEMP_CHANGE | ATOM_FLAG_CLIMBABLE | ATOM_FLAG_CAN_BE_PAINTED | ATOM_FLAG_ADJACENT_EXCEPTION anchored = TRUE density = TRUE - climbable = TRUE throwpass = 1 layer = TABLE_LAYER @@ -43,6 +42,8 @@ health = material.integrity + AddElement(/datum/element/climbable) + return INITIALIZE_HINT_LATELOAD /obj/structure/low_wall/LateInitialize() @@ -123,9 +124,8 @@ /obj/structure/low_wall/MouseDrop_T(atom/movable/AM, mob/user, src_location, over_location, src_control, over_control, params) if(AM == user) - var/mob/living/H = user - if(istype(H) && can_climb(H)) - do_climb(AM) + SEND_SIGNAL(src, COMSIG_CLIMBABLE_START_CLIMB, user) + return var/obj/O = AM if(!istype(O)) return diff --git a/code/game/objects/structures/mop_bucket.dm b/code/game/objects/structures/mop_bucket.dm index d420cf0befb..5ea18d051ec 100644 --- a/code/game/objects/structures/mop_bucket.dm +++ b/code/game/objects/structures/mop_bucket.dm @@ -4,7 +4,6 @@ icon = 'icons/obj/janitor.dmi' icon_state = "mopbucket" density = TRUE - climbable = TRUE w_class = ITEMSIZE_NORMAL pressure_resistance = 5 flags = OPENCONTAINER @@ -15,6 +14,7 @@ GLOBAL_LIST_BOILERPLATE(all_mopbuckets, /obj/structure/mopbucket) /obj/structure/mopbucket/Initialize(mapload, ...) create_reagents(300) . = ..() + AddElement(/datum/element/climbable) /obj/structure/mopbucket/examine(mob/user) . = ..() diff --git a/code/game/objects/structures/railing.dm b/code/game/objects/structures/railing.dm index e52d1f3d069..55b1e95f4bf 100644 --- a/code/game/objects/structures/railing.dm +++ b/code/game/objects/structures/railing.dm @@ -5,7 +5,6 @@ icon = 'icons/obj/railing.dmi' density = TRUE throwpass = 1 - climbable = TRUE layer = WINDOW_LAYER anchored = TRUE flags = ON_BORDER @@ -27,11 +26,7 @@ // TODO - "constructed" is not passed to us. We need to find a way to do this safely. if (constructed) // player-constructed railings anchored = FALSE - if(climbable) - verbs += /obj/structure/proc/climb_on - -/obj/structure/railing/Initialize(mapload) - . = ..() + AddElement(/datum/element/climbable/unanchored_can_break, 3.4 SECONDS, TRUE) // It's a RAILING! if(src.anchored) update_icon(0) @@ -190,7 +185,7 @@ to_chat(usr, "It is fastened to the floor therefore you can't flip it!") return 0 - var/obj/occupied = neighbor_turf_impassable() + var/obj/occupied = can_climb_neighbor_turf(src) if(occupied) to_chat(usr, "You can't flip \the [src] because there's \a [occupied] in the way.") return 0 @@ -235,7 +230,7 @@ var/obj/item/grab/G = W if (isliving(G.affecting)) var/mob/living/M = G.affecting - var/obj/occupied = turf_is_crowded() + var/obj/occupied = can_climb_turf(src) if(occupied) to_chat(user, span_danger("There's \a [occupied] in the way.")) return @@ -278,55 +273,3 @@ qdel(src) return return - -// Duplicated from structures.dm, but its a bit different. -/obj/structure/railing/do_climb(var/mob/living/user) - if(!can_climb(user)) - return - - user.visible_message(span_warning("[user] starts climbing onto \the [src]!")) - LAZYDISTINCTADD(climbers, user) - - if(!do_after(user,(issmall(user) ? 20 : 34))) - LAZYREMOVE(climbers, user) - return - - if(!can_climb(user, post_climb_check=1)) - LAZYREMOVE(climbers, user) - return - - if(get_turf(user) == get_turf(src)) - user.forceMove(get_step(src, src.dir)) - else - user.forceMove(get_turf(src)) - - user.visible_message(span_warning("[user] climbed over \the [src]!")) - if(!anchored) take_damage(maxhealth) // Fatboy - LAZYREMOVE(climbers, user) - -/obj/structure/railing/can_climb(var/mob/living/user, post_climb_check=0) - if(!..()) - return 0 - - // Normal can_climb() handles climbing from adjacent turf onto our turf. But railings also allow climbing - // from our turf onto an adjacent! If that is the case we need to do checks for that too... - if(get_turf(user) == get_turf(src)) - var/obj/occupied = neighbor_turf_impassable() - if(occupied) - to_chat(user, span_danger("You can't climb there, there's \a [occupied] in the way.")) - return 0 - return 1 - -// TODO - This here might require some investigation -/obj/structure/proc/neighbor_turf_impassable() - var/turf/T = get_step(src, src.dir) - if(!T || !istype(T)) - return 0 - if(T.density == 1) - return T - for(var/obj/O in T.contents) - if(istype(O,/obj/structure)) - var/obj/structure/S = O - if(S.climbable) continue - if(O && O.density && !(O.flags & ON_BORDER && !(turn(O.dir, 180) & dir))) - return O diff --git a/code/game/objects/structures/trash_pile_vr.dm b/code/game/objects/structures/trash_pile_vr.dm index c3dd40246e0..fb2d12c1ce0 100644 --- a/code/game/objects/structures/trash_pile_vr.dm +++ b/code/game/objects/structures/trash_pile_vr.dm @@ -44,6 +44,7 @@ "trashbag", "brokecomp") mouse_nest = new(src) + AddElement(/datum/element/climbable) /obj/structure/trash_pile/Destroy() qdel(mouse_nest) diff --git a/code/game/objects/structures/under_wardrobe.dm b/code/game/objects/structures/under_wardrobe.dm index addfcdaad02..899e0a4b1ce 100644 --- a/code/game/objects/structures/under_wardrobe.dm +++ b/code/game/objects/structures/under_wardrobe.dm @@ -5,6 +5,10 @@ icon_state = "wardrobe" density = TRUE +/obj/structure/undies_wardrobe/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/undies_wardrobe/attack_hand(var/mob/user) if(!human_who_can_use_underwear(user)) to_chat(user, span_warning("Sadly there's nothing in here for you to wear.")) diff --git a/code/game/objects/stumble_into_vr.dm b/code/game/objects/stumble_into_vr.dm index db814f32c7b..075c883164f 100644 --- a/code/game/objects/stumble_into_vr.dm +++ b/code/game/objects/stumble_into_vr.dm @@ -7,7 +7,7 @@ M.stop_flying() /obj/structure/table/stumble_into(mob/living/M) - var/obj/occupied = turf_is_crowded() + var/obj/occupied = can_climb_turf(src) if(occupied) return ..() if(material) @@ -70,7 +70,7 @@ M.stop_flying() /obj/structure/railing/stumble_into(mob/living/M) - var/obj/occupied = turf_is_crowded() + var/obj/occupied = can_climb_turf(src) if(occupied) return ..() playsound(src, 'sound/misc/slip.ogg', 25, 1, -1) diff --git a/code/modules/casino/casino.dm b/code/modules/casino/casino.dm index 59e0e15397b..42267a53c94 100644 --- a/code/modules/casino/casino.dm +++ b/code/modules/casino/casino.dm @@ -12,13 +12,16 @@ icon_state = "roulette_table" density = 1 anchored = 1 - climbable = 1 layer = TABLE_LAYER throwpass = 1 var/item_place = 1 //allows items to be placed on the table, but not on benches. var/busy = 0 +/obj/structure/casino_table/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/casino_table/attackby(obj/item/W, mob/user) if(item_place) user.drop_item(src.loc) diff --git a/code/modules/eventkit/generic_objects/generic_structure.dm b/code/modules/eventkit/generic_objects/generic_structure.dm index fdad6d7726c..97f163058c2 100644 --- a/code/modules/eventkit/generic_objects/generic_structure.dm +++ b/code/modules/eventkit/generic_objects/generic_structure.dm @@ -5,7 +5,6 @@ icon_state = "bsb_off" anchored = 1 density = 1 - climbable = 0 breakable = 0 var/on = 0 var/icon_state_off = "bsb_off" diff --git a/code/modules/events/meteor_strike_vr.dm b/code/modules/events/meteor_strike_vr.dm index db234eaa3b4..4dfa9009741 100644 --- a/code/modules/events/meteor_strike_vr.dm +++ b/code/modules/events/meteor_strike_vr.dm @@ -74,7 +74,6 @@ icon = 'icons/obj/meteor.dmi' icon_state = "large" density = TRUE - climbable = TRUE /obj/structure/meteorite/Initialize(mapload) . = ..() @@ -91,6 +90,7 @@ new /obj/item/ore/diamond(src) if(91 to 100) new /obj/machinery/artifact(src) + AddElement(/datum/element/climbable) /obj/structure/meteorite/ex_act() return diff --git a/code/modules/food/kitchen/smartfridge/drying_rack.dm b/code/modules/food/kitchen/smartfridge/drying_rack.dm index 7cfaeaaccb2..03263883a05 100644 --- a/code/modules/food/kitchen/smartfridge/drying_rack.dm +++ b/code/modules/food/kitchen/smartfridge/drying_rack.dm @@ -5,6 +5,10 @@ icon_state = "drying_rack" icon_base = "drying_rack" +/obj/machinery/smartfridge/drying_rack/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/smartfridge/drying_rack/accept_check(var/obj/item/O as obj) if(istype(O, /obj/item/reagent_containers/food/snacks/)) var/obj/item/reagent_containers/food/snacks/S = O diff --git a/code/modules/hydroponics/beekeeping/beehive.dm b/code/modules/hydroponics/beekeeping/beehive.dm index 80c351fabe2..7e65fdf1087 100644 --- a/code/modules/hydroponics/beekeeping/beehive.dm +++ b/code/modules/hydroponics/beekeeping/beehive.dm @@ -12,6 +12,10 @@ var/list/frames = list() // List of frames inside. var/maxFrames = 5 +/obj/machinery/beehive/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/beehive/update_icon() cut_overlays() icon_state = "beehive" diff --git a/code/modules/library/lib_items.dm b/code/modules/library/lib_items.dm index 6b50dfbbf1c..5ea966ae0aa 100644 --- a/code/modules/library/lib_items.dm +++ b/code/modules/library/lib_items.dm @@ -26,6 +26,7 @@ if(istype(I, /obj/item/book)) I.loc = src update_icon() + AddElement(/datum/element/climbable) /obj/structure/bookcase/attackby(obj/item/O, mob/user) if(istype(O, /obj/item/book)) diff --git a/code/modules/library/lib_machines.dm b/code/modules/library/lib_machines.dm index 203ff1ec20c..3498ea12c53 100644 --- a/code/modules/library/lib_machines.dm +++ b/code/modules/library/lib_machines.dm @@ -584,6 +584,10 @@ anchored = TRUE density = TRUE +/obj/machinery/bookbinder/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/bookbinder/attackby(var/obj/O as obj, var/mob/user as mob) if(istype(O, /obj/item/paper) || istype(O, /obj/item/paper_bundle)) if(istype(O, /obj/item/paper)) diff --git a/code/modules/mining/drilling/drill.dm b/code/modules/mining/drilling/drill.dm index a26a0ae5e07..78dbb3aad2a 100644 --- a/code/modules/mining/drilling/drill.dm +++ b/code/modules/mining/drilling/drill.dm @@ -110,6 +110,7 @@ cell = new cell(src) default_apply_parts() faultreporter = new /obj/item/radio/intercom{channels=list("Supply")}(null) + AddElement(/datum/element/climbable) /obj/machinery/mining/drill/Destroy() qdel_null(faultreporter) @@ -426,6 +427,7 @@ /obj/machinery/mining/brace/Initialize(mapload) . = ..() default_apply_parts() + AddElement(/datum/element/climbable) /obj/machinery/mining/brace/RefreshParts() ..() diff --git a/code/modules/mining/mine_outcrops.dm b/code/modules/mining/mine_outcrops.dm index f05721a5a4a..36d89e95a78 100644 --- a/code/modules/mining/mine_outcrops.dm +++ b/code/modules/mining/mine_outcrops.dm @@ -4,7 +4,6 @@ icon = 'icons/obj/outcrop.dmi' density = TRUE throwpass = 1 - climbable = TRUE anchored = TRUE icon_state = "outcrop" var/mindrop = 5 @@ -15,6 +14,7 @@ . = ..() if(prob(1)) add_overlay("[initial(icon_state)]-egg") + AddElement(/datum/element/climbable) /obj/structure/outcrop/diamond name = "shiny outcrop" diff --git a/code/modules/mining/ore_box.dm b/code/modules/mining/ore_box.dm index f119a6e8d37..b17e8ddbf49 100644 --- a/code/modules/mining/ore_box.dm +++ b/code/modules/mining/ore_box.dm @@ -29,6 +29,9 @@ ORE_VERDANTIUM = 0, ORE_RUTILE = 0) +/obj/structure/ore_box/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) /obj/structure/ore_box/attackby(obj/item/W as obj, mob/user as mob) if (istype(W, /obj/item/ore)) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index a8f27e5af9f..2ffb9f9ed9c 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -96,9 +96,7 @@ if(!gibbed && !isbelly(loc)) playsound(src, pick(get_species_sound(get_gendered_sound(src))["death"]), src.species.death_volume, 1, 20, volume_channel = VOLUME_CHANNEL_DEATH_SOUNDS) - if(ticker && ticker.mode) - sql_report_death(src) - ticker.mode.check_win() + SSmobs.report_death(src) if(wearing_rig) wearing_rig.notify_ai(span_danger("Warning: user death event. Mobility control passed to integrated intelligence system.")) diff --git a/code/modules/mob/living/silicon/robot/death.dm b/code/modules/mob/living/silicon/robot/death.dm index eceacfec5a9..802629ef23c 100644 --- a/code/modules/mob/living/silicon/robot/death.dm +++ b/code/modules/mob/living/silicon/robot/death.dm @@ -18,5 +18,5 @@ var/obj/item/dogborg/sleeper/S = locate(/obj/item/dogborg/sleeper) in module //VOREStation edit. if(S) S.go_out() //VOREStation edit. remove_robot_verbs() - sql_report_cyborg_death(src) + SSmobs.report_death(src) ..(gibbed,"shudders violently for a moment, then becomes motionless, its eyes slowly darkening.") diff --git a/code/modules/multiz/stairs.dm b/code/modules/multiz/stairs.dm index 8ef370ceae6..45dee7223df 100644 --- a/code/modules/multiz/stairs.dm +++ b/code/modules/multiz/stairs.dm @@ -240,7 +240,6 @@ icon_state = "stair_u" opacity = TRUE density = TRUE // Too high to simply step up on - climbable = TRUE // But they can be climbed if the bottom is out var/obj/structure/stairs/top/top = null var/obj/structure/stairs/bottom/bottom = null @@ -250,6 +249,7 @@ if(!GetAbove(src)) warning("Stair created without level above: ([loc.x], [loc.y], [loc.z])") return INITIALIZE_HINT_QDEL + AddElement(/datum/element/climbable) /obj/structure/stairs/middle/Destroy() if(top) @@ -313,7 +313,6 @@ /obj/structure/stairs/middle/MouseDrop_T(mob/target, mob/user) . = ..() if(check_integrity()) - do_climb(user) user.forceMove(get_turf(top)) // You can't really drag things when you have to climb up the gap in the stairs yourself /obj/structure/stairs/middle/Bumped(mob/user) diff --git a/code/modules/paperwork/filingcabinet.dm b/code/modules/paperwork/filingcabinet.dm index e3a8856e21e..95628381b8a 100644 --- a/code/modules/paperwork/filingcabinet.dm +++ b/code/modules/paperwork/filingcabinet.dm @@ -30,6 +30,7 @@ if(istype(I, /obj/item/paper) || istype(I, /obj/item/folder) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle)) I.loc = src . = ..() + AddElement(/datum/element/climbable) /obj/structure/filingcabinet/attackby(obj/item/P as obj, mob/user as mob) if(istype(P, /obj/item/paper) || istype(P, /obj/item/folder) || istype(P, /obj/item/photo) || istype(P, /obj/item/paper_bundle)) diff --git a/code/modules/paperwork/papershredder.dm b/code/modules/paperwork/papershredder.dm index 23b44c0edbd..671ae2cc0d8 100644 --- a/code/modules/paperwork/papershredder.dm +++ b/code/modules/paperwork/papershredder.dm @@ -29,6 +29,7 @@ . = ..() default_apply_parts() update_icon() + AddElement(/datum/element/climbable) /obj/machinery/papershredder/attackby(var/obj/item/W, var/mob/user) diff --git a/code/modules/power/singularity/collector.dm b/code/modules/power/singularity/collector.dm index 4646c14325e..be11971b0e4 100644 --- a/code/modules/power/singularity/collector.dm +++ b/code/modules/power/singularity/collector.dm @@ -20,6 +20,7 @@ var/global/list/rad_collectors = list() /obj/machinery/power/rad_collector/Initialize(mapload) . = ..() rad_collectors += src + AddElement(/datum/element/climbable) /obj/machinery/power/rad_collector/Destroy() rad_collectors -= src diff --git a/code/modules/power/singularity/emitter.dm b/code/modules/power/singularity/emitter.dm index c7ad66d6738..616e942e0e7 100644 --- a/code/modules/power/singularity/emitter.dm +++ b/code/modules/power/singularity/emitter.dm @@ -54,6 +54,7 @@ . = ..() if(state == 2 && anchored) connect_to_network() + AddElement(/datum/element/climbable) /obj/machinery/power/emitter/Destroy() message_admins("Emitter deleted at ([x],[y],[z] - JMP)",0,1) diff --git a/code/modules/power/singularity/field_generator.dm b/code/modules/power/singularity/field_generator.dm index 80ad2083977..9dda3dd16c7 100644 --- a/code/modules/power/singularity/field_generator.dm +++ b/code/modules/power/singularity/field_generator.dm @@ -72,6 +72,7 @@ . = ..() fields = list() connected_gens = list() + AddElement(/datum/element/climbable) /obj/machinery/field_generator/process() if(Varedit_start == 1) diff --git a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm index 453bb569908..cafe061a077 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_accelerator.dm @@ -72,6 +72,10 @@ So, hopefully this is helpful if any more icons are to be added/changed/wonderin var/strength = null var/desc_holder = null +/obj/structure/particle_accelerator/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/structure/particle_accelerator/Destroy() construction_state = 0 if(master) diff --git a/code/modules/power/singularity/particle_accelerator/particle_control.dm b/code/modules/power/singularity/particle_accelerator/particle_control.dm index b292fdeb3e7..857d0cbfc47 100644 --- a/code/modules/power/singularity/particle_accelerator/particle_control.dm +++ b/code/modules/power/singularity/particle_accelerator/particle_control.dm @@ -26,6 +26,7 @@ wires = new(src) connected_parts = list() update_active_power_usage(initial(active_power_usage) * (strength + 1)) + AddElement(/datum/element/climbable) /obj/machinery/particle_accelerator/control_box/Destroy() if(active) diff --git a/code/modules/power/solar.dm b/code/modules/power/solar.dm index a2013bd9524..710de27aaa9 100644 --- a/code/modules/power/solar.dm +++ b/code/modules/power/solar.dm @@ -37,6 +37,7 @@ GLOBAL_LIST_EMPTY(solars_list) health *= 2 update_icon() connect_to_network() + AddElement(/datum/element/climbable) /obj/machinery/power/solar/Destroy() unset_control() //remove from control computer @@ -131,6 +132,7 @@ GLOBAL_LIST_EMPTY(solars_list) stat |= BROKEN unset_control() update_icon() + SEND_SIGNAL(src, COMSIG_CLIMBABLE_SHAKE_CLIMBERS, null) return diff --git a/code/modules/power/tesla/coil.dm b/code/modules/power/tesla/coil.dm index df441a51d8d..7b846896ce4 100644 --- a/code/modules/power/tesla/coil.dm +++ b/code/modules/power/tesla/coil.dm @@ -237,6 +237,10 @@ buckle_lying = FALSE circuit = /obj/item/circuitboard/grounding_rod +/obj/machinery/power/grounding_rod/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/power/grounding_rod/examine() . = ..() if(anchored) diff --git a/code/modules/reagents/machinery/dispenser/reagent_tank.dm b/code/modules/reagents/machinery/dispenser/reagent_tank.dm index 6640820dc38..11a26a2c2c5 100644 --- a/code/modules/reagents/machinery/dispenser/reagent_tank.dm +++ b/code/modules/reagents/machinery/dispenser/reagent_tank.dm @@ -97,6 +97,7 @@ /obj/structure/reagent_dispensers/watertank/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_WATER, 1000) + AddElement(/datum/element/climbable) /obj/structure/reagent_dispensers/watertank/high name = "high-capacity water tank" @@ -124,6 +125,7 @@ /obj/structure/reagent_dispensers/fueltank/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_FUEL,1000) + AddElement(/datum/element/climbable) /obj/structure/reagent_dispensers/fueltank/high name = "high-capacity fuel tank" @@ -144,6 +146,7 @@ /obj/structure/reagent_dispensers/foam/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_FIREFOAM,1000) + AddElement(/datum/element/climbable) //Helium3 /obj/structure/reagent_dispensers/he3 @@ -155,6 +158,7 @@ /obj/structure/reagent_dispensers/he3/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_HELIUM3,1000) + AddElement(/datum/element/climbable) /* * Misc @@ -344,6 +348,7 @@ if(bottle) reagents.add_reagent(REAGENT_ID_WATER,2000) update_icon() + AddElement(/datum/element/climbable) /obj/structure/reagent_dispensers/water_cooler/examine(mob/user) . = ..() @@ -482,6 +487,7 @@ /obj/structure/reagent_dispensers/beerkeg/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_BEER,1000) + AddElement(/datum/element/climbable) /obj/structure/reagent_dispensers/beerkeg/wood name = "beer keg" @@ -514,6 +520,7 @@ /obj/structure/reagent_dispensers/cookingoil/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_COOKINGOIL,5000) + AddElement(/datum/element/climbable) /obj/structure/reagent_dispensers/cookingoil/bullet_act(var/obj/item/projectile/Proj) if(Proj.get_structure_damage()) @@ -537,3 +544,4 @@ /obj/structure/reagent_dispensers/bloodbarrel/Initialize(mapload) . = ..() reagents.add_reagent(REAGENT_ID_BLOOD, 1000, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"="O-","resistances"=null,"trace_chem"=null)) + AddElement(/datum/element/climbable) diff --git a/code/modules/reagents/machinery/pump.dm b/code/modules/reagents/machinery/pump.dm index e9e77f55486..8d6fd263a4f 100644 --- a/code/modules/reagents/machinery/pump.dm +++ b/code/modules/reagents/machinery/pump.dm @@ -33,6 +33,8 @@ RefreshParts() update_icon() + AddElement(/datum/element/climbable) + /obj/machinery/pump/Destroy() QDEL_NULL(cell) . = ..() diff --git a/code/modules/shieldgen/emergency_shield.dm b/code/modules/shieldgen/emergency_shield.dm index 7f5b5fab7fa..cff5de51cc6 100644 --- a/code/modules/shieldgen/emergency_shield.dm +++ b/code/modules/shieldgen/emergency_shield.dm @@ -141,6 +141,10 @@ /turf/simulated/floor/outdoors, ) +/obj/machinery/shieldgen/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/shieldgen/Destroy() collapse_shields() . = ..() diff --git a/code/modules/shieldgen/sheldwallgen.dm b/code/modules/shieldgen/sheldwallgen.dm index 84fc3227ec3..e5bdf85a0ae 100644 --- a/code/modules/shieldgen/sheldwallgen.dm +++ b/code/modules/shieldgen/sheldwallgen.dm @@ -25,6 +25,10 @@ var/max_stored_power = 50000 //50 kW use_power = USE_POWER_OFF //Draws directly from power net. Does not use APC power. +/obj/machinery/shieldwallgen/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/shieldwallgen/attack_hand(mob/user as mob) if(state != 1) to_chat(user, span_red("The shield generator needs to be firmly secured to the floor first.")) diff --git a/code/modules/shieldgen/shield_capacitor.dm b/code/modules/shieldgen/shield_capacitor.dm index 3fdfff70d43..09eaa6cf280 100644 --- a/code/modules/shieldgen/shield_capacitor.dm +++ b/code/modules/shieldgen/shield_capacitor.dm @@ -20,6 +20,10 @@ var/obj/machinery/shield_gen/owned_gen interact_offline = TRUE +/obj/machinery/shield_capacitor/Initialize(mapload) + . = ..() + AddElement(/datum/element/climbable) + /obj/machinery/shield_capacitor/advanced name = "advanced shield capacitor" desc = "A machine that charges a shield generator. This version can store, input, and output more electricity." diff --git a/code/modules/shieldgen/shield_gen.dm b/code/modules/shieldgen/shield_gen.dm index eba5b733251..aab44dcb416 100644 --- a/code/modules/shieldgen/shield_gen.dm +++ b/code/modules/shieldgen/shield_gen.dm @@ -41,7 +41,8 @@ if(get_dir(cap, src) == cap.dir) capacitors |= cap cap.owned_gen = src - return ..() + . = ..() + AddElement(/datum/element/climbable) /obj/machinery/shield_gen/Destroy() QDEL_LIST_NULL(field) diff --git a/code/modules/tables/flipping.dm b/code/modules/tables/flipping.dm index 29c1a69a9d9..5ebd2cd5b5d 100644 --- a/code/modules/tables/flipping.dm +++ b/code/modules/tables/flipping.dm @@ -27,8 +27,7 @@ usr.visible_message(span_warning("[usr] flips \the [src]!")) - if(climbable) - structure_shaken() + SEND_SIGNAL(src, COMSIG_CLIMBABLE_SHAKE_CLIMBERS, usr) return @@ -37,7 +36,7 @@ for(var/mob/M in oview(src,0)) return 0 - var/obj/occupied = turf_is_crowded() + var/obj/occupied = can_climb_turf(src) if(occupied) to_chat(usr, span_filter_notice("There's \a [occupied] in the way.")) return 0 diff --git a/code/modules/tables/interactions.dm b/code/modules/tables/interactions.dm index 640ea43ef1c..01f4e18c483 100644 --- a/code/modules/tables/interactions.dm +++ b/code/modules/tables/interactions.dm @@ -14,14 +14,6 @@ return TRUE return FALSE -/obj/structure/table/climb_to(mob/living/mover) - if(flipped == 1 && mover.loc == loc) - var/turf/T = get_step(src, dir) - if(T.Enter(mover)) - return T - - return ..() - /obj/structure/table/Uncross(atom/movable/mover, turf/target) if(flipped == 1 && (get_dir(mover, target) == dir)) // From here to elsewhere, can't move in our dir return !density @@ -96,7 +88,7 @@ var/obj/item/grab/G = W if (isliving(G.affecting)) var/mob/living/M = G.affecting - var/obj/occupied = turf_is_crowded() + var/obj/occupied = can_climb_turf(src) if(occupied) to_chat(user, span_danger("There's \a [occupied] in the way.")) return diff --git a/code/modules/tables/tables.dm b/code/modules/tables/tables.dm index 67d89c527ce..16bdecf8eb1 100644 --- a/code/modules/tables/tables.dm +++ b/code/modules/tables/tables.dm @@ -7,7 +7,6 @@ var/list/table_icon_cache = list() desc = "It's a table, for putting things on. Or standing on, if you really want to." density = TRUE anchored = TRUE - climbable = TRUE layer = TABLE_LAYER throwpass = 1 surgery_odds = 50 //VOREStation Edit @@ -78,6 +77,8 @@ var/list/table_icon_cache = list() update_desc() update_material() + AddElement(/datum/element/climbable/table) + /obj/structure/table/Destroy() material = null reinforced = null diff --git a/maps/redgate/falls/atoll_objs.dm b/maps/redgate/falls/atoll_objs.dm index 60c24d08224..1f0150d015d 100644 --- a/maps/redgate/falls/atoll_objs.dm +++ b/maps/redgate/falls/atoll_objs.dm @@ -29,10 +29,14 @@ /obj/structure/railing/overhang/waterless icon_modifier = "wbronze_" icon_state = "wbronze_railing0" - climbable = 0 layer = MOB_LAYER + 0.3 plane = MOB_PLANE //im so sorry +/obj/structure/railing/overhang/waterless/Initialize(mapload) + . = ..() + // Not climbable! + RemoveElement(/datum/element/climbable) + /obj/structure/railing/overhang/waterless/under plane = OBJ_PLANE diff --git a/vorestation.dme b/vorestation.dme index 66627a12f86..788e6b92954 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -195,6 +195,7 @@ #include "code\__defines\dcs\signals\signals_mobs\signals_mob_main.dm" #include "code\__defines\traits\_traits.dm" #include "code\__defines\traits\declarations.dm" +#include "code\__defines\traits\macros.dm" #include "code\__defines\traits\sources.dm" #include "code\__defines\verdigris\_verdigris.dm" #include "code\__defines\verdigris\random_map.dm" @@ -588,6 +589,7 @@ #include "code\datums\diseases\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" #include "code\datums\elements\cleaning.dm" +#include "code\datums\elements\climbable.dm" #include "code\datums\elements\conflict_checking.dm" #include "code\datums\elements\footstep.dm" #include "code\datums\elements\footstep_override.dm" From 53e09bb6b7dcd88cbec4796aefc4648aed2a2100 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 07:57:25 +0000 Subject: [PATCH 31/47] Automatic changelog for PR #17764 [ci skip] --- html/changelogs/AutoChangeLog-pr-17764.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17764.yml diff --git a/html/changelogs/AutoChangeLog-pr-17764.yml b/html/changelogs/AutoChangeLog-pr-17764.yml new file mode 100644 index 00000000000..2c41d5f64f2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17764.yml @@ -0,0 +1,6 @@ +author: "Will" +delete-after: True +changes: + - code_imp: "moves sql death reporting to SSmobs as a single massinsert" + - refactor: "turns object climbing code into a component" + - qol: "many more objects are climbable, preventing softlocks, especially for engineers and miners" \ No newline at end of file From d71a532489cc37c315b4fcfd42e261a734de119d Mon Sep 17 00:00:00 2001 From: SatinIsle <98125273+SatinIsle@users.noreply.github.com> Date: Fri, 20 Jun 2025 21:00:03 +0100 Subject: [PATCH 32/47] Made backup batteries a bit more useful (#17889) Changed the level that you can use backup batteries as a synth to 150 nutrition, and it also gives you 150 nutrition back. This means you can use it as soon as you reach slowdown levels of hunger and it fills you up a decent amount. Previously you had to wait until you went down to 100 nutrition and it would only fill up to 200 max, which means you'd be back in red soon after. --- code/modules/power/cells/power_cells.dm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/modules/power/cells/power_cells.dm b/code/modules/power/cells/power_cells.dm index ffcc388833f..b6d3cccceca 100644 --- a/code/modules/power/cells/power_cells.dm +++ b/code/modules/power/cells/power_cells.dm @@ -219,7 +219,7 @@ icon = 'icons/obj/power_cells.dmi' icon_state = "backup" w_class = ITEMSIZE_SMALL - var/amount = 100 + var/amount = 150 var/used = FALSE /obj/item/fbp_backup_cell/Initialize(mapload) From 0148c9ae8b63b85796eccd6b3ec00548f8d00c66 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Fri, 20 Jun 2025 20:00:35 +0000 Subject: [PATCH 33/47] Automatic changelog for PR #17889 [ci skip] --- html/changelogs/AutoChangeLog-pr-17889.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17889.yml diff --git a/html/changelogs/AutoChangeLog-pr-17889.yml b/html/changelogs/AutoChangeLog-pr-17889.yml new file mode 100644 index 00000000000..17e5e6238f2 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17889.yml @@ -0,0 +1,4 @@ +author: "SatinIsle" +delete-after: True +changes: + - balance: "Changed the level that you can use backup batteries as a synth to 150 nutrition, and it also gives you 150 nutrition back. This means you can use it as soon as you reach slowdown levels of hunger and it fills you up a decent amount." \ No newline at end of file From b5ba4a465f70c58a10cc78d7f0da6745e63f6afa Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sat, 21 Jun 2025 02:00:32 +0000 Subject: [PATCH 34/47] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-17764.yml | 6 ------ html/changelogs/AutoChangeLog-pr-17889.yml | 4 ---- html/changelogs/archive/2025-06.yml | 11 +++++++++++ 3 files changed, 11 insertions(+), 10 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-17764.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17889.yml diff --git a/html/changelogs/AutoChangeLog-pr-17764.yml b/html/changelogs/AutoChangeLog-pr-17764.yml deleted file mode 100644 index 2c41d5f64f2..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17764.yml +++ /dev/null @@ -1,6 +0,0 @@ -author: "Will" -delete-after: True -changes: - - code_imp: "moves sql death reporting to SSmobs as a single massinsert" - - refactor: "turns object climbing code into a component" - - qol: "many more objects are climbable, preventing softlocks, especially for engineers and miners" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17889.yml b/html/changelogs/AutoChangeLog-pr-17889.yml deleted file mode 100644 index 17e5e6238f2..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17889.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "SatinIsle" -delete-after: True -changes: - - balance: "Changed the level that you can use backup batteries as a synth to 150 nutrition, and it also gives you 150 nutrition back. This means you can use it as soon as you reach slowdown levels of hunger and it fills you up a decent amount." \ No newline at end of file diff --git a/html/changelogs/archive/2025-06.yml b/html/changelogs/archive/2025-06.yml index ecf4df84928..a5fd50b53dc 100644 --- a/html/changelogs/archive/2025-06.yml +++ b/html/changelogs/archive/2025-06.yml @@ -294,3 +294,14 @@ - rscadd: Adds genes and diseases to bingle's database so symptoms and genetics stuff can be looked up ingame - code_imp: added unit test for virus names and trait names to prevent tgui issues +2025-06-21: + SatinIsle: + - balance: Changed the level that you can use backup batteries as a synth to 150 + nutrition, and it also gives you 150 nutrition back. This means you can use + it as soon as you reach slowdown levels of hunger and it fills you up a decent + amount. + Will: + - code_imp: moves sql death reporting to SSmobs as a single massinsert + - refactor: turns object climbing code into a component + - qol: many more objects are climbable, preventing softlocks, especially for engineers + and miners From 38e2bc1df1fdb0e4b173bf25aa35539b3416b08e Mon Sep 17 00:00:00 2001 From: Cameron Lennox Date: Sat, 21 Jun 2025 21:37:29 -0400 Subject: [PATCH 35/47] Mining Loadouts (#17890) * mining stuff * some missed things in borg pr --- code/__defines/borg_overlays.dm | 2 +- code/_helpers/global_lists_vr.dm | 2 + code/datums/outfits/jobs/cargo.dm | 2 +- .../equipment_vendor.dm | 77 ++++++++++++++----- .../mob/living/carbon/human/human_defense.dm | 2 +- code/modules/mob/living/living_defense.dm | 2 +- 6 files changed, 63 insertions(+), 24 deletions(-) diff --git a/code/__defines/borg_overlays.dm b/code/__defines/borg_overlays.dm index 54d1ec327e6..f281b6ee492 100644 --- a/code/__defines/borg_overlays.dm +++ b/code/__defines/borg_overlays.dm @@ -56,6 +56,6 @@ /* /datum/robot_sprite/combat/fluff/foopwotch/handle_extra_icon_updates(var/mob/living/silicon/robot/ourborg) //Make sure the path is correct! ..() //THIS IS ESSENTIAL. IF YOU FORGET THIS LINE, YOU WILL NOT GET THE NORMAL SPRITES. - if(ourborg.has_active_type(/obj/item/weapon/combat_borgblade)) + if(ourborg.has_active_type(/obj/item/combat_borgblade)) ourborg.add_overlay("[sprite_icon_state]-dagger") */ diff --git a/code/_helpers/global_lists_vr.dm b/code/_helpers/global_lists_vr.dm index c23b2729de9..7286506a4bb 100644 --- a/code/_helpers/global_lists_vr.dm +++ b/code/_helpers/global_lists_vr.dm @@ -1073,6 +1073,7 @@ GLOBAL_LIST_INIT(area_or_turf_fail_types, typecacheof(list( /obj/item/airlock_electronics, \ /obj/item/tracker_electronics, \ /obj/item/module/power_control, \ + /obj/item/bluespace_crystal, \ /obj/item/stock_parts, \ /obj/item/frame, \ /obj/item/camera_assembly, \ @@ -1112,6 +1113,7 @@ GLOBAL_LIST_INIT(area_or_turf_fail_types, typecacheof(list( /obj/item/storage/pill_bottle, \ /obj/item/reagent_containers/pill, \ /obj/item/reagent_containers/blood, \ + /obj/item/nif, \ /obj/item/stack/material/phoron, \ /obj/item/tank/anesthetic, \ /obj/item/disk/body_record diff --git a/code/datums/outfits/jobs/cargo.dm b/code/datums/outfits/jobs/cargo.dm index 8bfb269012a..8dc1d6052b5 100644 --- a/code/datums/outfits/jobs/cargo.dm +++ b/code/datums/outfits/jobs/cargo.dm @@ -31,7 +31,7 @@ satchel_one = /obj/item/storage/backpack/satchel/eng id_type = /obj/item/card/id/cargo/miner pda_type = /obj/item/pda/shaftminer - backpack_contents = list(/obj/item/tool/crowbar = 1, /obj/item/storage/bag/ore = 1) + backpack_contents = list(/obj/item/tool/crowbar = 1, /obj/item/storage/bag/ore = 1, /obj/item/mining_voucher = 1) flags = OUTFIT_HAS_BACKPACK|OUTFIT_EXTENDED_SURVIVAL headset = /obj/item/radio/headset/miner diff --git a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm index 72ea39e71a1..ca21e2ed817 100644 --- a/code/modules/mining/ore_redemption_machine/equipment_vendor.dm +++ b/code/modules/mining/ore_redemption_machine/equipment_vendor.dm @@ -248,27 +248,25 @@ inserted_id = null if("purchase") if(!inserted_id) - flick(icon_deny, src) //VOREStation Add + flick(icon_deny, src) return var/category = params["cat"] // meow var/name = params["name"] if(!(category in prize_list) || !(name in prize_list[category])) // Not trying something that's not in the list, are you? - flick(icon_deny, src) //VOREStation Add + flick(icon_deny, src) return var/datum/data/mining_equipment/prize = prize_list[category][name] if(prize.cost > get_points(inserted_id)) // shouldn't be able to access this since the button is greyed out, but.. to_chat(ui.user, span_danger("You have insufficient points.")) - flick(icon_deny, src) //VOREStation Add + flick(icon_deny, src) return remove_points(inserted_id, prize.cost) - //VOREStation Edit Start var/obj/I = new prize.equipment_path(loc) I.persist_storable = FALSE - //VOREStation Edit End - flick(icon_vend, src) //VOREStation Add + flick(icon_vend, src) else - flick(icon_deny, src) //VOREStation Add + flick(icon_deny, src) return FALSE add_fingerprint() @@ -308,23 +306,62 @@ * * redeemer - The person holding it */ /obj/machinery/mineral/equipment_vendor/proc/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer) - var/selection = tgui_input_list(redeemer, "Pick your equipment", "Mining Voucher Redemption", list("Kinetic Accelerator", "Resonator", "Mining Drone", "Advanced Scanner", "Crusher")) - if(!selection || !Adjacent(redeemer) || voucher.loc != redeemer) - return - //VOREStation Edit Start - Uncommented these + to_chat(redeemer, "You insert your voucher into the machine!") + var/selection = tgui_input_list(redeemer, "Pick your equipment.", "Mining Voucher Redemption", list("Kinetic Accelerator + KA Addon", "Resonator + Advanced Ore Scanner", "Survival Pistol & Machete + Survival Addon","1000 Points")) var/drop_location = drop_location() + if(!Adjacent(redeemer)) + to_chat(redeemer, "You must stay near the machine to use it.") + return + if(!selection) + to_chat(redeemer, "You decide not to redeem anything for now.") + return switch(selection) - if("Kinetic Accelerator") + + if("Kinetic Accelerator + KA Addon") //1250-2100 points worth + var/addon_selection = tgui_input_list(redeemer, "Pick your addon", "Mining Voucher Redemption", list("Cooldown", "Range","Holster")) //Just the basics. Nothing too crazy. + if(!addon_selection) + to_chat(redeemer, "You must select an addon.") + return new /obj/item/gun/energy/kinetic_accelerator(drop_location) - if("Resonator") + switch(addon_selection) + if("Cooldown") + new /obj/item/borg/upgrade/modkit/cooldown(drop_location) + if("Range") + new /obj/item/borg/upgrade/modkit/range(drop_location) + if("Holster") + new /obj/item/clothing/accessory/holster/waist/kinetic_accelerator(drop_location) + + + if("Resonator + Advanced Ore Scanner") //1400 points worth new /obj/item/resonator(drop_location) - //VOREStation Edit End - // if("Mining Drone") - // new /obj/item/storage/box/drone_kit(drop_location) - // if("Advanced Scanner") - // new /obj/item/t_scanner/adv_mining_scanner(drop_location) - // if("Crusher") - // new /obj/item/twohanded/required/mining_hammer(drop_location) + new /obj/item/mining_scanner/advanced(drop_location) + qdel(voucher) + + if("Survival Pistol & Machete + Survival Addon") // ~3000-3500 points worth. + var/addon_selection = tgui_input_list(redeemer, "Pick your survival addon", "Mining Voucher Redemption", list("Shelter Capsule", "Glucose", "Panacea", "Trauma", "Medipens")) //Just the basics. Nothing too crazy. + if(!addon_selection) + to_chat(redeemer, "You must select an addon.") + return + new /obj/item/gun/energy/phasegun/pistol(drop_location) //1500 + new /obj/item/material/knife/machete(drop_location) //1000 + switch(addon_selection) + if("Shelter Capsule") + new /obj/item/survivalcapsule(drop_location) //500 + if("Glucose") + new /obj/item/reagent_containers/hypospray/autoinjector/biginjector/glucose(drop_location) //500 + if("Panacea") + new /obj/item/reagent_containers/hypospray/autoinjector/biginjector/purity(drop_location) //500 + if("Trauma") + new /obj/item/reagent_containers/hypospray/autoinjector/biginjector/brute(drop_location) //500 + if("Medipens") + var/obj/item/storage/box/medbox = new /obj/item/storage/box(drop_location) //1000 + new /obj/item/reagent_containers/hypospray/autoinjector/burn(medbox) + new /obj/item/reagent_containers/hypospray/autoinjector/detox(medbox) + new /obj/item/reagent_containers/hypospray/autoinjector/oxy(medbox) + new /obj/item/reagent_containers/hypospray/autoinjector/trauma(medbox) + if("1000 Points") //1000 points + var/obj/item/card/mining_point_card/new_card = new(drop_location) + new_card.mine_points = 1000 qdel(voucher) /obj/machinery/mineral/equipment_vendor/proc/new_prize(var/name, var/path, var/cost) // Generic proc for adding new entries. Good for abusing for FUN and PROFIT. diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index e99e33f3c2c..10cf0bf2897 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -428,7 +428,7 @@ emp_act if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //Ported from chompstation var/obj/item/I = O if(adminbus_trash || is_type_in_list(I, GLOB.edible_trash) && I.trash_eatable && !is_type_in_list(I, GLOB.item_vore_blacklist)) - visible_message(span_warning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) + visible_message(span_vwarning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) I.throwing = 0 I.forceMove(vore_selected) return diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 91520f0e5c1..54c6a2429b5 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -262,7 +262,7 @@ if(stat != DEAD && istype(O,/obj/item) && trash_catching && vore_selected) //ported from chompstation var/obj/item/I = O if(adminbus_trash || is_type_in_list(I, GLOB.edible_trash) && I.trash_eatable && !is_type_in_list(I, GLOB.item_vore_blacklist)) - visible_message(span_warning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) + visible_message(span_vwarning("[I] is thrown directly into [src]'s [lowertext(vore_selected.name)]!")) I.throwing = 0 I.forceMove(vore_selected) return From eea8fef1eeb49db7c1b79d36bf2b3933b08733b9 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:37:58 +0000 Subject: [PATCH 36/47] Automatic changelog for PR #17890 [ci skip] --- html/changelogs/AutoChangeLog-pr-17890.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17890.yml diff --git a/html/changelogs/AutoChangeLog-pr-17890.yml b/html/changelogs/AutoChangeLog-pr-17890.yml new file mode 100644 index 00000000000..7acbd85de23 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17890.yml @@ -0,0 +1,4 @@ +author: "Diana" +delete-after: True +changes: + - rscadd: "Miners now start with a voucher that can be redeemed for various loadouts at the ore vendor." \ No newline at end of file From 1a114201221aaeb9efd841785d1b415348070924 Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Sat, 21 Jun 2025 21:39:56 -0400 Subject: [PATCH 37/47] handle stairs (#17899) --- code/datums/elements/climbable.dm | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/code/datums/elements/climbable.dm b/code/datums/elements/climbable.dm index f20dce07c7d..1d99bd1a3c3 100644 --- a/code/datums/elements/climbable.dm +++ b/code/datums/elements/climbable.dm @@ -111,16 +111,15 @@ return 1 /// Shakes an object, called from movement so it needs to be cleaned up a bit! -/datum/element/climbable/proc/move_shaken(var/obj/climbed_thing, var/mob/user) +/datum/element/climbable/proc/move_shaken(obj/climbed_thing, atom/oldloc, direction, forced, list/old_locs, momentum_change) SIGNAL_HANDLER - shaken(climbed_thing, null) + if(!forced) // Don't perform this if going up stairs + shaken(climbed_thing, null) /// Shakes an object, if anyone is climbing it, causes them to fall off it. /datum/element/climbable/proc/shaken(var/obj/climbed_thing, var/mob/user) SIGNAL_HANDLER var/list/climbers = LAZYACCESS(current_climbers, climbed_thing) - for(var/i in climbers) - // You cannot shake yourself if(user) // Crates pass null on open because no user if(!LAZYLEN(climbers) || (user in climbers)) @@ -137,6 +136,8 @@ continue if(M.lying) //No spamming this on people. continue + if(M.pulling == climbed_thing) // Pulling stuff up stairs can get weird + continue M.Weaken(3) to_chat(M, span_danger("You topple as \the [climbed_thing] moves under you!")) From 165eaebc4781bfe23d5592162d339d5bc6a072d9 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:40:24 +0000 Subject: [PATCH 38/47] Automatic changelog for PR #17899 [ci skip] --- html/changelogs/AutoChangeLog-pr-17899.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17899.yml diff --git a/html/changelogs/AutoChangeLog-pr-17899.yml b/html/changelogs/AutoChangeLog-pr-17899.yml new file mode 100644 index 00000000000..175947f7227 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17899.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - qol: "climbable objects being pulled up or down stairs no longer knock you around" \ No newline at end of file From 2033cdfd9ad818a9effd3d96102146f515ae7247 Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Sat, 21 Jun 2025 21:46:46 -0400 Subject: [PATCH 39/47] Sinks can act as endless sources and drains for reagent hoses (#17897) --- .../components/reagent_hose/connector.dm | 40 +++++++++++++++++++ code/game/objects/structures/watercloset.dm | 5 +++ 2 files changed, 45 insertions(+) diff --git a/code/datums/components/reagent_hose/connector.dm b/code/datums/components/reagent_hose/connector.dm index d909da9c0cd..bd07f4b3b09 100644 --- a/code/datums/components/reagent_hose/connector.dm +++ b/code/datums/components/reagent_hose/connector.dm @@ -219,3 +219,43 @@ /datum/component/hose_connector/output/handle_pump(var/datum/reagents/connected_to) ASSERT(connected_to) connected_to.trans_to_holder(reagents, reagents.maximum_volume) + +/// Endless source, produces a reagent and pumps it out forever. Does not require attached object to have reagents. +/datum/component/hose_connector/endless_source + name = "source connector" + flow_direction = HOSE_OUTPUT + var/reagent_id = null + +/datum/component/hose_connector/endless_source/Initialize() + . = ..() + name = initial(name) + +/datum/component/hose_connector/endless_source/connected_reagents() + if(!carrier) + return null + return reagents // Ourselves, not our carrier + +/datum/component/hose_connector/endless_source/handle_pump(var/datum/reagents/connected_to) + ASSERT(connected_to) + connected_to.add_reagent(reagent_id,5) + +/datum/component/hose_connector/endless_source/water + reagent_id = REAGENT_ID_WATER + +/// Endless drain, removes reagents from existance +/datum/component/hose_connector/endless_drain + name = "drain connector" + flow_direction = HOSE_INPUT + +/datum/component/hose_connector/endless_drain/Initialize() + . = ..() + name = initial(name) + +/datum/component/hose_connector/endless_drain/connected_reagents() + if(!carrier) + return null + return reagents // Ourselves, not our carrier + +/datum/component/hose_connector/endless_drain/handle_pump(var/datum/reagents/connected_to) + ASSERT(connected_to) + connected_to.clear_reagents() diff --git a/code/game/objects/structures/watercloset.dm b/code/game/objects/structures/watercloset.dm index 0b4ca498a73..9cb17b244a1 100644 --- a/code/game/objects/structures/watercloset.dm +++ b/code/game/objects/structures/watercloset.dm @@ -500,6 +500,11 @@ anchored = TRUE var/busy = 0 //Something's being washed at the moment +/obj/structure/sink/Initialize(mapload) + . = ..() + AddComponent(/datum/component/hose_connector/endless_source/water) + AddComponent(/datum/component/hose_connector/endless_drain) + /obj/structure/sink/MouseDrop_T(var/obj/item/thing, var/mob/user) ..() if(!istype(thing) || !thing.is_open_container()) From e2fd72dbdbe7c9072c555601cee5a0b8352d50ee Mon Sep 17 00:00:00 2001 From: Will <7099514+Willburd@users.noreply.github.com> Date: Sat, 21 Jun 2025 21:47:14 -0400 Subject: [PATCH 40/47] gripper shows information on examine for robots (#17898) --- code/game/atoms.dm | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/code/game/atoms.dm b/code/game/atoms.dm index dca25e937f8..fdc9ea757c9 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -196,8 +196,18 @@ else f_name += "oil-stained [name][infix]." + var/borg = "" // Borg grippers say if the item can be gripped + if(isrobot(user) && isitem(src)) + borg = "None of your grippers can hold this." + var/mob/living/silicon/robot/R = user + if(R.module?.modules) + for(var/obj/item/gripper/G in R.module.modules) + if(is_type_in_list(src,G.can_hold)) + borg = span_boldnotice("\The [G]") + span_notice(" can hold this.") + break + var/examine_text = replacetext(get_examine_desc(), "||", "") - var/list/output = list("[icon2html(src,user.client)] That's [f_name] [suffix]", examine_text) + var/list/output = list("[icon2html(src,user.client)] That's [f_name] [suffix] [borg]", examine_text) SEND_SIGNAL(src, COMSIG_PARENT_EXAMINE, user, output) return output From cd6efc98efc40da2a03bf776ccb44e266c32abcb Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:47:18 +0000 Subject: [PATCH 41/47] Automatic changelog for PR #17897 [ci skip] --- html/changelogs/AutoChangeLog-pr-17897.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17897.yml diff --git a/html/changelogs/AutoChangeLog-pr-17897.yml b/html/changelogs/AutoChangeLog-pr-17897.yml new file mode 100644 index 00000000000..390047b65a7 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17897.yml @@ -0,0 +1,4 @@ +author: "Willburd" +delete-after: True +changes: + - rscadd: "Hoses can connect to sinks for endless water, or as an endless drain" \ No newline at end of file From c5006b5ae952c035da074e7f5643696c13935b70 Mon Sep 17 00:00:00 2001 From: eghughguhhhhhh Date: Sat, 21 Jun 2025 22:47:41 -0300 Subject: [PATCH 42/47] FUCK (#17892) --- code/modules/eventkit/collector_event/event_collector.dm | 2 +- code/modules/eventkit/generic_objects/generic_structure.dm | 2 +- code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/modules/eventkit/collector_event/event_collector.dm b/code/modules/eventkit/collector_event/event_collector.dm index ecadaa1ddbc..abf6dad33bc 100644 --- a/code/modules/eventkit/collector_event/event_collector.dm +++ b/code/modules/eventkit/collector_event/event_collector.dm @@ -65,7 +65,7 @@ GLOBAL_LIST_INIT(event_collector_blockers,list()) //ditto . = ..() GLOB.event_collectors |= src -/obj/strucutre/event_collector/Destroy() +/obj/structure/event_collector/Destroy() GLOB.event_collectors -= src . = ..() diff --git a/code/modules/eventkit/generic_objects/generic_structure.dm b/code/modules/eventkit/generic_objects/generic_structure.dm index 97f163058c2..8a79e114195 100644 --- a/code/modules/eventkit/generic_objects/generic_structure.dm +++ b/code/modules/eventkit/generic_objects/generic_structure.dm @@ -12,7 +12,7 @@ var/wrenchable = 0 var/activatable_hand = 1 var/togglable = 1 - var/text_activated = "The strucutre turns on." + var/text_activated = "The structure turns on." var/text_deactivated = "The structure turns off." var/effect = 0 var/object = 0 diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm index 35e7ed6d3f4..89e94e9eee6 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/deathclaw.dm @@ -5,7 +5,7 @@ Originally the Deathclaw was a top secret genetics project that was run by ancestral Zorren which was \ lost to time. While it is not immediately evident in their body structure, these creatures bare a \ subtle genetic connection to Zorren, however, this connection is marred by the other genes that \ - have been grafted onto the DNA strucutre of the Deathclaw. The creatures are known to attack humans \ + have been grafted onto the DNA structure of the Deathclaw. The creatures are known to attack humans \ and other animals regularly to protect their territory or to hunt for food. It is speculated that \ they escaped roughly around the time as whatever calamity befell the Zorren many centuries ago \ as sighting of these beasts in the wild began around that time according to recovered Zorren texts. \ From af30d490e2baf9aadab3738fcfe378569e1bb5eb Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:47:44 +0000 Subject: [PATCH 43/47] Automatic changelog for PR #17898 [ci skip] --- html/changelogs/AutoChangeLog-pr-17898.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17898.yml diff --git a/html/changelogs/AutoChangeLog-pr-17898.yml b/html/changelogs/AutoChangeLog-pr-17898.yml new file mode 100644 index 00000000000..314e83d2a4f --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17898.yml @@ -0,0 +1,4 @@ +author: "Will" +delete-after: True +changes: + - qol: "Borgs can now examine items to know if any of their grippers can hold it." \ No newline at end of file From 8a53a4e46f634becb8c17059a0b7d15c4f39ce02 Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 01:48:10 +0000 Subject: [PATCH 44/47] Automatic changelog for PR #17892 [ci skip] --- html/changelogs/AutoChangeLog-pr-17892.yml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 html/changelogs/AutoChangeLog-pr-17892.yml diff --git a/html/changelogs/AutoChangeLog-pr-17892.yml b/html/changelogs/AutoChangeLog-pr-17892.yml new file mode 100644 index 00000000000..8e459c59f88 --- /dev/null +++ b/html/changelogs/AutoChangeLog-pr-17892.yml @@ -0,0 +1,4 @@ +author: "TeleTubby2" +delete-after: True +changes: + - spellcheck: "removed all strucutres from the game" \ No newline at end of file From ac0842401560f7b1504a565dcb8fe805b016e01b Mon Sep 17 00:00:00 2001 From: "vorestation-ci[bot]" <199609141+vorestation-ci[bot]@users.noreply.github.com> Date: Sun, 22 Jun 2025 02:18:31 +0000 Subject: [PATCH 45/47] Automatic changelog compile [ci skip] --- html/changelogs/AutoChangeLog-pr-17890.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17892.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17897.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17898.yml | 4 ---- html/changelogs/AutoChangeLog-pr-17899.yml | 4 ---- html/changelogs/archive/2025-06.yml | 11 +++++++++++ 6 files changed, 11 insertions(+), 20 deletions(-) delete mode 100644 html/changelogs/AutoChangeLog-pr-17890.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17892.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17897.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17898.yml delete mode 100644 html/changelogs/AutoChangeLog-pr-17899.yml diff --git a/html/changelogs/AutoChangeLog-pr-17890.yml b/html/changelogs/AutoChangeLog-pr-17890.yml deleted file mode 100644 index 7acbd85de23..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17890.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Diana" -delete-after: True -changes: - - rscadd: "Miners now start with a voucher that can be redeemed for various loadouts at the ore vendor." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17892.yml b/html/changelogs/AutoChangeLog-pr-17892.yml deleted file mode 100644 index 8e459c59f88..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17892.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "TeleTubby2" -delete-after: True -changes: - - spellcheck: "removed all strucutres from the game" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17897.yml b/html/changelogs/AutoChangeLog-pr-17897.yml deleted file mode 100644 index 390047b65a7..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17897.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - rscadd: "Hoses can connect to sinks for endless water, or as an endless drain" \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17898.yml b/html/changelogs/AutoChangeLog-pr-17898.yml deleted file mode 100644 index 314e83d2a4f..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17898.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Will" -delete-after: True -changes: - - qol: "Borgs can now examine items to know if any of their grippers can hold it." \ No newline at end of file diff --git a/html/changelogs/AutoChangeLog-pr-17899.yml b/html/changelogs/AutoChangeLog-pr-17899.yml deleted file mode 100644 index 175947f7227..00000000000 --- a/html/changelogs/AutoChangeLog-pr-17899.yml +++ /dev/null @@ -1,4 +0,0 @@ -author: "Willburd" -delete-after: True -changes: - - qol: "climbable objects being pulled up or down stairs no longer knock you around" \ No newline at end of file diff --git a/html/changelogs/archive/2025-06.yml b/html/changelogs/archive/2025-06.yml index a5fd50b53dc..537eb976aa4 100644 --- a/html/changelogs/archive/2025-06.yml +++ b/html/changelogs/archive/2025-06.yml @@ -305,3 +305,14 @@ - refactor: turns object climbing code into a component - qol: many more objects are climbable, preventing softlocks, especially for engineers and miners +2025-06-22: + Diana: + - rscadd: Miners now start with a voucher that can be redeemed for various loadouts + at the ore vendor. + TeleTubby2: + - spellcheck: removed all strucutres from the game + Will: + - qol: Borgs can now examine items to know if any of their grippers can hold it. + Willburd: + - qol: climbable objects being pulled up or down stairs no longer knock you around + - rscadd: Hoses can connect to sinks for endless water, or as an endless drain From 656617ab5505725b8504f422250e74b7c1e75b7e Mon Sep 17 00:00:00 2001 From: TheToaster98 <51209769+TheToaster98@users.noreply.github.com> Date: Sun, 22 Jun 2025 19:55:15 +0200 Subject: [PATCH 46/47] Adds fluff item: Tally's voodoo doll (#17891) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixed the fucky wucky * Changed Returns * I forgor the if 💀 Co-authored-by: ShadowLarkens --------- Co-authored-by: VM_USER Co-authored-by: ShadowLarkens --- .../loadout/loadout_fluffitems.dm | 6 ++++ .../vore/fluffstuff/custom_items_vr.dm | 30 ++++++++++++++++++ icons/vore/custom_remote_scene_tools.dmi | Bin 0 -> 1759 bytes 3 files changed, 36 insertions(+) create mode 100644 icons/vore/custom_remote_scene_tools.dmi diff --git a/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm b/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm index f75ae256d5f..f62080a6fcb 100644 --- a/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm +++ b/code/modules/client/preference_setup/loadout/loadout_fluffitems.dm @@ -229,6 +229,12 @@ ckeywhitelist = list("bricker98") character_name = list("Nettie Stough") +/datum/gear/fluff/tally_doll + path = /obj/item/remote_scene_tool/tally_necklace + display_name = "Custom Collar" + ckeywhitelist = list("bricker98") + character_name = list("Talenya Lapushkina") + /datum/gear/fluff/xin_sovietuniform path = /obj/item/clothing/under/soviet display_name = "Xin's Soviet Uniform" diff --git a/code/modules/vore/fluffstuff/custom_items_vr.dm b/code/modules/vore/fluffstuff/custom_items_vr.dm index 6b6cfbbd35a..9483f89a7e3 100644 --- a/code/modules/vore/fluffstuff/custom_items_vr.dm +++ b/code/modules/vore/fluffstuff/custom_items_vr.dm @@ -1580,3 +1580,33 @@ icon_override = 'icons/vore/custom_clothes_vr.dmi' icon_state = "kintacts" item_state = "kintacts_mob" + +//Bricker98: Talenya Lapushkina +/obj/item/remote_scene_tool/tally_necklace //A reskinned sticker for the collar, using a modified golden collar sprite + name = "link bell collar" + desc = "A collar with a seemingly simple golden bell that contains advanced bluespace tech inside, allowing it to link to, and even recall, a matching doll." + icon = 'icons/vore/custom_remote_scene_tools.dmi' + icon_override = 'icons/vore/custom_remote_scene_tools.dmi' + icon_state = "collar_inactive" + icon_root = "collar" + item_state = "on_mob_collar" + slot_flags = SLOT_MASK | SLOT_OCLOTHING + replacementType = /obj/item/remote_scene_tool/tally_doll + +/obj/item/remote_scene_tool/tally_necklace/mob_can_equip(var/mob/living/carbon/human/H, slot, disable_warning = 0) + if(..()) + if(H.ckey != "bricker98") + if(!disable_warning) + to_chat(H, span_warning("The collar doesn't fit you!")) + return FALSE + return TRUE + +/obj/item/remote_scene_tool/tally_doll //A reskinned sticker for the doll, using a custom sprite + name = "Talenya's voodoo doll" + desc = "A cute custom plushie made to look like Talenya! With her bell and glassy beads for eyes, all the clothing articles are removable, it is incredibly detailed!" + icon = 'icons/vore/custom_remote_scene_tools.dmi' + icon_state = "doll_inactive" + icon_root = "doll" + slot_flags = NONE + can_summon = FALSE + can_replace = FALSE diff --git a/icons/vore/custom_remote_scene_tools.dmi b/icons/vore/custom_remote_scene_tools.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f371eef52be273e52f4b5aeaf798f74aeb4a056c GIT binary patch literal 1759 zcma)-eKgaHAIHB~o^C^#h)hHt3Uxd`Z3!EyxyNIdbf>j`M3_zHB82PGny2biTbpRb zLb@1|QT*=6u5r0}&NPJ?yK5ddbp7rdI#~1RYuO7Vk#4s*+XdPU+4&!)ek|=S4L%%X=>j)5f9YFr%g(H! zO3wn_^X~_tckWZvqbeUB44 z_*np86c|t}pW4?00LmJE-k!lk>Jl|F`cMdj!@F923{t9hE-o+dy*Fjv%U^5J%TG&Q ztfKR>LW}WO6W4?&^o*@4&1H3mG8wB!5XA`V=#UzinyfzJdn<+?)IN38%-?eYf-%sU z^nZ7qH$`0%UB0s3FXB>7@i$IzFaM0^wa;bpTJ0M0Z=3E{)7ogsZc~avg3yTn>WWT^ z%02DDXnQvomuhl~2x>tMNSENcuTl0ZXvDCz+sk%u(PP<3B6jbb>&kBu zjUuvs=MOD3q9~h-28Ay;Yrl24w9v+2H%%ZWKskQ{kGvQvr@hSNH-UjjCsdc)T&0j& zCs3<($Wo^H&gbLOClU;2bS-Uw*WgS{^&FZHr=}~&cA4LQaa7b25-^y` zW9GpRZ?r6FTPQxQVCNu*xzj^U;19x#0~cLavSnX8biZbW3{s+CQX|5$@YsrUW5!ra zQ*w26J$%={RX~ZhX=+naj zL$B)WW)|xzxU<)g8(5!^3;mfy5HE*Kq~*Lg;L?#g2j}ob=1eBjq*b*?-w>t8Yu`pUVdZ8)#gfN9 zE*SR4*D<;RZG1w#x-90W$yi)_pQo*k9Pw?gq(CiMUVwTg#pCflb_Ry=*VBW7wBtmF za!WMf?>=OVJo3pXY^g1bZcCO_+}|3Kufr$T7Vd&ddM_WphAuSb3_+zZ!~jN)s^ZOp zfCiv$nN+JBFqflc>?^;nVY|nb9@uXkk%CD4Jdwew7#ZBqI!%aYK4E-{ncnNV)AmTS zb9flSuL(F>CVZXx-k^Hk;H19&2)}j7;0+6{RS>S3TGvcKB)t`o)#u9~bMBMPHS~cPgZP%JP@-ecV`zpb#VR0m7+>hy=$6 z?u|7GK%8#Ws|n+J80`xCB@9}5373)X&@C(46zUOnMk@b>PfJOzJd;;yY4MYcxK0KC zLgL-oMv#O8wVEO%cU7Vqszp9y6Y$wkKw z6UmltgXyo%Uhe|$?^F1{RDB+(8RCGS-))~S)`-3(QpOJ1y31Sj_jiha*e(}=@}5~* us6MNKaJ0YVoa6SGaR0fb|JB}KKG#7{dYZ@-W$N#KEWpnP Date: Sun, 22 Jun 2025 14:34:31 -0400 Subject: [PATCH 47/47] Map Swap (#17901) --- maps/~map_system/_map_selection.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/maps/~map_system/_map_selection.dm b/maps/~map_system/_map_selection.dm index d0770a55a3b..89f466da444 100644 --- a/maps/~map_system/_map_selection.dm +++ b/maps/~map_system/_map_selection.dm @@ -5,8 +5,8 @@ /* FOR LIVE SERVER */ /*********************/ -#define USE_MAP_TETHER -//#define USE_MAP_STELLARDELIGHT +//#define USE_MAP_TETHER +#define USE_MAP_STELLARDELIGHT //#define USE_MAP_GROUNDBASE // Debug