Merge branch 'master' of https://github.com/tgstation/tgstation into upstream-25-04a

This commit is contained in:
Waterpig
2025-04-08 18:58:45 +02:00
1089 changed files with 42417 additions and 34460 deletions
+1
View File
@@ -114,6 +114,7 @@
#include "can_see.dm"
#include "card_mismatch.dm"
#include "cardboard_cutouts.dm"
#include "cargo_crate_sanity.dm"
#include "cargo_dep_order_locations.dm"
#include "cargo_selling.dm"
#include "chain_pull_through_space.dm"
+2 -2
View File
@@ -52,14 +52,14 @@
// Check for the status effect, duh
TEST_ASSERT(dummy.is_blind(), "Dummy, [status_message], did not have the blind status effect.")
// Being more technical, we need to check for client color and screen overlays
TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome/blind), "Dummy, [status_message], did not have the monochrome client color.")
TEST_ASSERT(HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], did not have the monochrome client color.")
TEST_ASSERT(HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], did not have a blind screen overlay in their list of screens.")
/datum/unit_test/blindness/proc/check_if_not_blind(mob/living/carbon/human/dummy, status_message = "after being cured of blindness")
// Check for no status effect
TEST_ASSERT(!dummy.is_blind(), "Dummy, [status_message], still had the blindness status effect.")
// Check that the client color and screen overlay are gone
TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome/blind), "Dummy, [status_message], still had the monochrome client color.")
TEST_ASSERT(!HAS_CLIENT_COLOR(dummy, /datum/client_colour/monochrome), "Dummy, [status_message], still had the monochrome client color.")
TEST_ASSERT(!HAS_SCREEN_OVERLAY(dummy, /atom/movable/screen/fullscreen/blind), "Dummy, [status_message], still had the blind sceen overlay.")
/**
@@ -0,0 +1,42 @@
/**
* This unit test loops through all cargo crates that are available to purchase, and confirms that they're below the expected sanity minimum when sold.
* This prevents us from merging a crate that sells for more that it costs to buy.
*/
/datum/unit_test/cargo_crate_sanity
/datum/unit_test/cargo_crate_sanity/Run()
for(var/crate in subtypesof(/datum/supply_pack))
var/datum/supply_pack/new_crate = allocate(crate)
if(new_crate.test_ignored)
continue // We can safely ignore custom supply packs like the stock market or mining supply crates, or packs that have innate randomness.
if(!new_crate?.crate_type)
continue
var/obj/crate_type = allocate(new_crate.crate_type)
var/turf/open/floor/testing_floor = get_turf(crate_type)
var/datum/export_report/minimum_cost = export_item_and_contents(crate_type, delete_unsold = TRUE, dry_run = TRUE)
var/crate_value = counterlist_sum(minimum_cost.total_value)
var/obj/results = new_crate.generate(testing_floor)
var/datum/export_report/export_log = export_item_and_contents(results, apply_elastic = TRUE, delete_unsold = TRUE, export_market = EXPORT_MARKET_STATION)
// The value of the crate and all of it's contents.
var/value = counterlist_sum(export_log.total_value)
// We're selling the crate and it's contents for more value than it's supply_pack costs.
if(value > new_crate.get_cost())
TEST_FAIL("Cargo crate [new_crate.type] had a sale value of [value], Selling for more than [new_crate.get_cost()], the cost to buy")
// We're selling the crate & it's contents for less than the value of it's own crate, meaning you can buy and infinite number
if(crate_value > new_crate.get_cost())
TEST_FAIL("Cargo crate [new_crate.type] container sells for [crate_value], Selling for more than [new_crate.get_cost()], the cost to buy")
for(var/atom/stuff as anything in results.contents)
qdel(stuff)
stuff = null
qdel(results)
results = null
new_crate = null
minimum_cost = null
export_log = null
+1 -2
View File
@@ -82,8 +82,7 @@
ling.dna.mutant_bodyparts["spines"] = list(MUTANT_INDEX_NAME = "Long + Membrane", MUTANT_INDEX_COLOR_LIST = list("#886600", "#886600", "#886600")) // SKYRAT EDIT CHANGE - ORIGINAL: ling.dna.features["spines"] = "Long + Membrane"
ling.dna.body_markings["chest"] = list("Light Belly" = list("#886600", 0)) // SKYRAT EDIT CHANGE - ORIGINAL : ling.dna.features[lizard_markings] = list("Light Belly")
ling.dna.features["legs"] = DIGITIGRADE_LEGS
ling.eye_color_left = COLOR_WHITE
ling.eye_color_right = COLOR_WHITE
ling.set_eye_color(COLOR_WHITE)
ling.dna.update_ui_block(DNA_EYE_COLOR_LEFT_BLOCK)
ling.dna.update_ui_block(DNA_EYE_COLOR_RIGHT_BLOCK)
ling.set_species(/datum/species/lizard)
+8 -2
View File
@@ -5,5 +5,11 @@
for(var/datum/client_colour/colour as anything in subtypesof(/datum/client_colour))
// colours can be color matrices (lists), which initial() cannot read.
colour = new colour
if(!color_to_full_rgba_matrix(colour.colour, FALSE))
TEST_FAIL("[colour.type] has an invalid default colour value: [colour.colour]")
if (islist(colour.color))
var/list/potential_filter = colour.color
// If our list has "type" in it then its a filter, ignore that
if (potential_filter["type"])
continue
if(!color_to_full_rgba_matrix(colour.color, FALSE))
TEST_FAIL("[colour.type] has an invalid default colour value: [colour.color]")
@@ -15,27 +15,27 @@
TEST_ASSERT_EQUAL(gun.get_ammo(countchambered = TRUE), expected_ammo, "Gun spawned without a full magazine, \
when it should spawn with mag size + 1 (chambered) rounds.")
// Combat mode in melee range -> pistol whip
// Combat mode in melee range with RMB -> pistol whip
attacker.set_combat_mode(TRUE)
click_wrapper(attacker, victim)
click_wrapper(attacker, victim, list(RIGHT_CLICK = TRUE, BUTTON = RIGHT_CLICK))
TEST_ASSERT_NOTEQUAL(victim.getBruteLoss(), 0, "Victim did not take brute damage from being pistol-whipped.")
TEST_ASSERT_EQUAL(gun.get_ammo(countchambered = TRUE), expected_ammo, "The gun fired a shot when it was used for a pistol whip.")
victim.fully_heal()
// No combat mode -> point blank shot
attacker.set_combat_mode(FALSE)
// Combat mode LMB -> point blank shot
attacker.set_combat_mode(TRUE)
click_wrapper(attacker, victim)
TEST_ASSERT_NOTEQUAL(victim.getBruteLoss(), 0, "Victim did not take brute damage from being fired upon point-blank.")
TEST_ASSERT(locate(/obj/item/ammo_casing/c9mm) in attacker.loc, "The gun did not eject a casing when it was used for a point-blank shot.")
TEST_ASSERT_EQUAL(gun.get_ammo(countchambered = TRUE), expected_ammo - 1, "The gun did not fire a shot when it was used for a point-blank shot.")
victim.fully_heal()
// Combat mode in melee range with bayonet -> bayonet stab
// Combat mode RMB click in melee range with bayonet -> bayonet stab
var/obj/item/knife/combat/knife = EASY_ALLOCATE()
gun.AddComponent(/datum/component/bayonet_attachable, starting_bayonet = knife)
attacker.set_combat_mode(TRUE)
click_wrapper(attacker, victim)
click_wrapper(attacker, victim, list(RIGHT_CLICK = TRUE, BUTTON = RIGHT_CLICK))
TEST_ASSERT_NOTEQUAL(victim.getBruteLoss(), 0, "Victim did not take brute damage from being bayonet stabbed.")
victim.fully_heal()
@@ -56,5 +56,5 @@
butcher_comp.speed = 1 SECONDS
butcher.put_in_active_hand(gun, forced = TRUE)
click_wrapper(butcher, meat)
click_wrapper(butcher, meat, list(RIGHT_CLICK = TRUE, BUTTON = RIGHT_CLICK))
TEST_ASSERT(DOING_INTERACTION(butcher, meat), "The butcher did not start butchering the monkey when using a bayonetted weapon.")
@@ -149,7 +149,6 @@
/datum/fish_trait/dummy
incompatible_traits = list(/datum/fish_trait/dummy/two)
inheritability = 100
diff_traits_inheritability = 100
reagents_to_add = list(/datum/reagent/fishdummy = FISH_REAGENT_AMOUNT)
/datum/fish_trait/dummy/apply_to_fish(obj/item/fish/fish)
@@ -56,3 +56,6 @@
/// Checks icon states generated at runtime are valid
/datum/unit_test/focus_only/runtime_icon_states
/// Checks that foodtypes are the same for food whether it's spawned or crafted (with the exact required types)
/datum/unit_test/focus_only/check_foodtypes
+6 -3
View File
@@ -23,11 +23,12 @@
SSmobs.pause()
var/mob/living/carbon/human/user = allocate(/mob/living/carbon/human/consistent)
var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill)
var/obj/item/reagent_containers/applicator/pill/pill = allocate(/obj/item/reagent_containers/applicator/pill)
var/datum/reagent/drug/methamphetamine/meth = /datum/reagent/drug/methamphetamine
// Give them enough meth to be consumed in 2 metabolizations
pill.reagents.add_reagent(meth, 1.9 * initial(meth.metabolization_rate) * SSMOBS_DT)
pill.layers_remaining = 0
pill.interact_with_atom(user, user)
user.Life(SSMOBS_DT)
@@ -63,8 +64,8 @@
pill_syringe_mind.active = TRUE
pill_syringe_mind.transfer_to(pill_syringe_user)
var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill)
var/obj/item/reagent_containers/pill/pill_two = allocate(/obj/item/reagent_containers/pill)
var/obj/item/reagent_containers/applicator/pill/pill = allocate(/obj/item/reagent_containers/applicator/pill)
var/obj/item/reagent_containers/applicator/pill/pill_two = allocate(/obj/item/reagent_containers/applicator/pill)
var/obj/item/reagent_containers/syringe/syringe = allocate(/obj/item/reagent_containers/syringe)
@@ -77,6 +78,7 @@
// Let's start with stomach metabolism
pill.reagents.add_reagent(meth.type, 5)
pill.layers_remaining = 0
pill.interact_with_atom(pill_user, pill_user)
// Set the metabolism efficiency to 1.0 so it transfers all reagents to the body in one go.
@@ -105,6 +107,7 @@
// One half pill
pill_two.reagents.add_reagent(meth.type, (5 * 0.5) + 1)
pill_two.layers_remaining = 0
pill_two.interact_with_atom(pill_syringe_user, pill_syringe_user)
syringe.melee_attack_chain(pill_syringe_user, pill_syringe_user)
+12 -4
View File
@@ -1,10 +1,18 @@
/datum/unit_test/pills/Run()
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/consistent)
var/obj/item/reagent_containers/pill/iron/pill = allocate(/obj/item/reagent_containers/pill/iron)
var/obj/item/reagent_containers/applicator/pill/iron/pill_one = allocate(/obj/item/reagent_containers/applicator/pill/iron)
var/obj/item/reagent_containers/applicator/pill/iron/pill_two = allocate(/obj/item/reagent_containers/applicator/pill/tox)
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/iron), FALSE, "Human somehow has iron before taking pill")
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/iron), FALSE, "Human somehow has iron before taking the any pills")
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/toxin), FALSE, "Human somehow has iron before taking the any pills")
pill.interact_with_atom(human, human)
pill_one.layers_remaining = 0
pill_one.interact_with_atom(human, human)
human.Life(SSMOBS_DT)
TEST_ASSERT(human.has_reagent(/datum/reagent/iron), "Human doesn't have iron after taking pill")
TEST_ASSERT(human.has_reagent(/datum/reagent/iron), "Human doesn't have iron after taking the first pill")
pill_two.layers_remaining = 4
pill_two.interact_with_atom(human, human)
human.Life(SSMOBS_DT)
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/toxin), FALSE, "Human has toxin despite consuming a pill with four layers after a single tick")
+13 -11
View File
@@ -6,9 +6,9 @@
/datum/reagent/method_patch_test/expose_mob(mob/living/target, methods = PATCH, reac_volume, show_message = TRUE)
. = ..()
if(methods & PATCH)
target.health = 90
target.setBruteLoss(20)
if(methods & INJECT)
target.health = 80
target.setBruteLoss(10)
/datum/unit_test/reagent_mob_expose/Run()
// Life() is handled just by tests
@@ -17,7 +17,7 @@
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/consistent)
var/obj/item/reagent_containers/dropper/dropper = allocate(/obj/item/reagent_containers/dropper)
var/obj/item/reagent_containers/cup/glass/bottle/drink = allocate(/obj/item/reagent_containers/cup/glass/bottle)
var/obj/item/reagent_containers/pill/patch/patch = allocate(/obj/item/reagent_containers/pill/patch)
var/obj/item/reagent_containers/applicator/patch/patch = allocate(/obj/item/reagent_containers/applicator/patch)
var/obj/item/reagent_containers/syringe/syringe = allocate(/obj/item/reagent_containers/syringe)
// INGEST
@@ -34,8 +34,8 @@
TEST_ASSERT(human.fire_stacks < 0, "Human still has fire stacks after touching water")
// VAPOR
human.fully_heal(ALL)
TEST_ASSERT_NULL(human.has_status_effect(/datum/status_effect/drowsiness), "Human is drowsy at the start of testing")
drink.reagents.clear_reagents()
drink.reagents.add_reagent(/datum/reagent/nitrous_oxide, 10)
drink.reagents.trans_to(human, 10, methods = VAPOR)
TEST_ASSERT_NOTNULL(human.has_status_effect(/datum/status_effect/drowsiness), "Human is not drowsy after exposure to vapors")
@@ -46,24 +46,26 @@
TEST_ASSERT(human.fire_stacks < old_fire_stacks, "Human does not get wetter after being exposed to water by vapors")
// PATCH
human.health = 100
TEST_ASSERT_EQUAL(human.health, 100, "Human health did not set properly")
human.fully_heal(ALL)
TEST_ASSERT_EQUAL(human.getBruteLoss(), 0, "Human health did not set properly")
patch.reagents.add_reagent(/datum/reagent/method_patch_test, 1)
patch.self_delay = 0
patch.interact_with_atom(human, human)
TEST_ASSERT_EQUAL(human.health, 90, "Human health did not update after patch was applied")
patch.get_embed().process(SSdcs.wait / 10)
human.Life(SSMOBS_DT)
TEST_ASSERT_EQUAL(human.getBruteLoss(), 20, "Human health did not update after patch was applied")
// INJECT
syringe.reagents.add_reagent(/datum/reagent/method_patch_test, 1)
syringe.melee_attack_chain(human, human)
TEST_ASSERT_EQUAL(human.health, 80, "Human health did not update after injection from syringe")
TEST_ASSERT_EQUAL(human.getBruteLoss(), 10, "Human health did not update after injection from syringe")
// INHALE
TEST_ASSERT_NULL(human.has_status_effect(/datum/status_effect/hallucination), "Human is drowsy at the start of testing")
drink.reagents.clear_reagents()
human.fully_heal(ALL)
TEST_ASSERT_NULL(human.has_status_effect(/datum/status_effect/hallucination), "Human is hallucinating at the start of testing")
drink.reagents.add_reagent(/datum/reagent/nitrous_oxide, 10)
drink.reagents.trans_to(human, 10, methods = INHALE)
TEST_ASSERT_NOTNULL(human.has_status_effect(/datum/status_effect/hallucination), "Human is not drowsy after exposure to vapors")
TEST_ASSERT_NOTNULL(human.has_status_effect(/datum/status_effect/hallucination), "Human is not hallucinating after exposure to vapors")
/datum/unit_test/reagent_mob_expose/Destroy()
SSmobs.ignite()
+17 -5
View File
@@ -6,8 +6,10 @@
var/mob/living/carbon/human/human = allocate(/mob/living/carbon/human/consistent)
var/obj/item/food/hotdog/debug/fooditem = allocate(/obj/item/food/hotdog/debug)
var/obj/item/organ/stomach/belly = human.get_organ_slot(ORGAN_SLOT_STOMACH)
var/obj/item/reagent_containers/pill/pill = allocate(/obj/item/reagent_containers/pill)
var/obj/item/reagent_containers/applicator/pill/pill_one = allocate(/obj/item/reagent_containers/applicator/pill)
var/obj/item/reagent_containers/applicator/pill/pill_two = allocate(/obj/item/reagent_containers/applicator/pill)
var/datum/reagent/drug/methamphetamine/meth = /datum/reagent/drug/methamphetamine
var/datum/reagent/drug/kronkaine/krok = /datum/reagent/drug/kronkaine
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human somehow has ketchup before eating")
@@ -17,13 +19,25 @@
TEST_ASSERT_EQUAL(human.reagents.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human body has ketchup after eating it should only be in the stomach")
//Give them meth and let it kick in
pill.reagents.add_reagent(meth, 1.9 * initial(meth.metabolization_rate) * SSMOBS_DT)
pill.interact_with_atom(human, human)
pill_one.reagents.add_reagent(meth, 1.9 * initial(meth.metabolization_rate) * SSMOBS_DT)
pill_one.layers_remaining = 0
pill_one.interact_with_atom(human, human)
human.Life(SSMOBS_DT)
TEST_ASSERT(human.reagents.has_reagent(meth), "Human body does not have meth after life tick")
TEST_ASSERT(human.has_movespeed_modifier(/datum/movespeed_modifier/reagent/methamphetamine), "Human consumed meth, but did not gain movespeed modifier")
// Check if pills properly get stored in stomachs
pill_two.reagents.add_reagent(krok, 1.9 * initial(krok.metabolization_rate) * SSMOBS_DT)
pill_two.layers_remaining = 99
pill_two.interact_with_atom(human, human)
human.Life(SSMOBS_DT)
TEST_ASSERT_EQUAL(human.reagents.has_reagent(krok), FALSE, "Human body has krokaine after taking a pill despite it having 99 layers")
TEST_ASSERT(pill_two in belly.stomach_contents, "Krokaine pill did not get stored in target's stomach")
TEST_ASSERT(pill_two.loc == human, "Krokaine pill was not put in target's body")
QDEL_NULL(pill_two)
belly.Remove(human)
human.reagents.remove_all(human.reagents.total_volume)
@@ -33,8 +47,6 @@
TEST_ASSERT_EQUAL(human.has_reagent(/datum/reagent/consumable/ketchup), FALSE, "Human has ketchup without a stomach")
/datum/unit_test/stomach/Destroy()
SSmobs.ignite()
return ..()
+8 -5
View File
@@ -136,15 +136,18 @@ GLOBAL_VAR_INIT(focused_tests, focused_tests())
var/data_filename = "data/screenshots/[path_prefix]_[name].png"
fcopy(icon, data_filename)
log_test("\t[path_prefix]_[name] was found, putting in data/screenshots")
else if (fexists("code"))
// We are probably running in a local build
fcopy(icon, filename)
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
else
// We are probably running in real CI, so just pretend it worked and move on
#ifdef CIBUILDING
// We are runing in real CI, so just pretend it worked and move on
fcopy(icon, "data/screenshots_new/[path_prefix]_[name].png")
log_test("\t[path_prefix]_[name] was put in data/screenshots_new")
#else
// We are probably running in a local build
fcopy(icon, filename)
TEST_FAIL("Screenshot for [name] did not exist. One has been created.")
#endif
/// Helper for screenshot tests to take an image of an atom from all directions and insert it into one icon
/datum/unit_test/proc/get_flat_icon_for_all_directions(atom/thing, no_anim = TRUE)