diff --git a/code/__HELPERS/text.dm b/code/__HELPERS/text.dm index 967967790b28..9287197bbcc2 100644 --- a/code/__HELPERS/text.dm +++ b/code/__HELPERS/text.dm @@ -381,3 +381,7 @@ if(.) return return 0 + +/// Check if the string `haystack` begins with the string `needle`. +/proc/string_starts_with(haystack, needle) + return (copytext(haystack, 1, length(needle) + 1) == needle) diff --git a/code/controllers/subsystem/minimap.dm b/code/controllers/subsystem/minimap.dm index 410ded82f7b3..478848906047 100644 --- a/code/controllers/subsystem/minimap.dm +++ b/code/controllers/subsystem/minimap.dm @@ -585,6 +585,8 @@ SUBSYSTEM_DEF(minimaps) owner?.client?.remove_from_screen(map) minimap_displayed = FALSE + UnregisterSignal(target, COMSIG_MOVABLE_Z_CHANGED) + /** * Updates the map when the owner changes zlevel */ diff --git a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm index 460a561fa801..0e897335cf10 100644 --- a/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm +++ b/code/modules/mob/living/carbon/xenomorph/abilities/general_abilities.dm @@ -497,6 +497,7 @@ ability_name = "view tacmap" var/mob/living/carbon/xenomorph/queen/tracked_queen + var/hivenumber /datum/action/xeno_action/onclick/tacmap/Destroy() tracked_queen = null @@ -505,6 +506,7 @@ /datum/action/xeno_action/onclick/tacmap/give_to(mob/living/carbon/xenomorph/xeno) . = ..() + hivenumber = xeno.hive.hivenumber RegisterSignal(xeno.hive, COMSIG_HIVE_NEW_QUEEN, PROC_REF(handle_new_queen)) if(!xeno.hive.living_xeno_queen) @@ -516,6 +518,10 @@ handle_new_queen(new_queen = xeno.hive.living_xeno_queen) +/datum/action/xeno_action/onclick/tacmap/remove_from(mob/living/carbon/xenomorph/xeno) + . = ..() + UnregisterSignal(GLOB.hive_datum[hivenumber], COMSIG_HIVE_NEW_QUEEN) + /// handles the addition of a new queen, hiding if appropriate /datum/action/xeno_action/onclick/tacmap/proc/handle_new_queen(datum/hive_status/hive, mob/living/carbon/xenomorph/queen/new_queen) SIGNAL_HANDLER diff --git a/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/resin_whisperer.dm b/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/resin_whisperer.dm index 8bdcd1311f94..a6c493f3ada0 100644 --- a/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/resin_whisperer.dm +++ b/code/modules/mob/living/carbon/xenomorph/strains/castes/hivelord/resin_whisperer.dm @@ -23,7 +23,7 @@ hivelord.can_stack_builds = TRUE hivelord.recalculate_plasma() - hivelord.client.change_view(10, src) + hivelord.client?.change_view(10, src) hivelord.set_resin_build_order(GLOB.resin_build_order_hivelord_whisperer) for(var/datum/action/xeno_action/action in hivelord.actions) diff --git a/code/modules/unit_tests/_unit_tests.dm b/code/modules/unit_tests/_unit_tests.dm index 3967ee70e391..6f2b63e5b9e8 100644 --- a/code/modules/unit_tests/_unit_tests.dm +++ b/code/modules/unit_tests/_unit_tests.dm @@ -87,6 +87,7 @@ #include "tgui_create_message.dm" #include "timer_sanity.dm" #include "tutorials.dm" +#include "xeno_strains.dm" // Unit tests backend #include "focus_only_tests.dm" diff --git a/code/modules/unit_tests/xeno_strains.dm b/code/modules/unit_tests/xeno_strains.dm new file mode 100644 index 000000000000..6609f977efeb --- /dev/null +++ b/code/modules/unit_tests/xeno_strains.dm @@ -0,0 +1,59 @@ +/datum/unit_test/xeno_strains + +/datum/unit_test/xeno_strains/Run() + for(var/mob/living/carbon/xenomorph/caste_mob as anything in subtypesof(/mob/living/carbon/xenomorph)) + var/datum/caste_datum/caste_datum = GLOB.xeno_datum_list[initial(caste_mob.caste_type)] + if(!caste_datum) + // not really strain related but may as well have this in here + TEST_FAIL("[caste_mob] has no `/datum/caste_datum`! (Possible mismatch of `caste_type`)") + continue + + for(var/datum/xeno_strain/strain_path as anything in caste_datum.available_strains) + // Check for these, but test the strain either way. + if(!initial(strain_path.name)) + TEST_FAIL("[strain_path] has no name!") + if(!initial(strain_path.description)) + TEST_FAIL("[strain_path] has no description!") + + test_strain(caste_mob, strain_path) + +/datum/unit_test/xeno_strains/proc/test_strain(mob_path, strain_path) + var/mob/living/carbon/xenomorph/fred = allocate(mob_path) + var/datum/xeno_strain/strain = new strain_path() + + strain._add_to_xeno(fred) + + // Actions which should have been removed. + var/list/removed_actions = strain.actions_to_remove + // Exclude any actions which are in both strain lists (re-added afterwards). + removed_actions -= (removed_actions & strain.actions_to_add) + + // Actions which should have been added. + var/list/added_actions = strain.actions_to_add.Copy() + + for(var/datum/action/action as anything in fred.actions) + if(action.type in removed_actions) + TEST_FAIL("[strain.type] failed to remove [action.type] when added to a Xenomorph!") + if(action.type in added_actions) + // Remove this action from `added_actions`. + added_actions -= action.type + // If there's anything left in `added_actions`, then something wasn't given to the xeno. + if(length(added_actions)) + TEST_FAIL("[strain.type] failed to give the following actions when added to a Xenomorph: [json_encode(added_actions)]") + + // If the strain has a `/datum/behavior_delegate`, but it wasn't applied to the xeno. + if(strain.behavior_delegate_type && !istype(fred.behavior_delegate, strain.behavior_delegate_type)) + TEST_FAIL("[strain.type]'s behavior_delegate was not applied when added to a Xenomorph!") + + // If the strain doesn't have a custom icon state set. + if(!strain.icon_state_prefix) + // Check if any of the sprites in the xeno's .dmi file belong to this strain. + // If there are any, it probably means that someone just forgot to add an `icon_state_prefix` to the strain. + for(var/icon_state in icon_states(fred.icon)) + if(string_starts_with(icon_state, strain.name)) // (This won't catch everything, but it should get the easy ones.) + TEST_FAIL("[fred.icon] contains sprites for [strain.type] that aren't being used!") + break + + // If the strain *does* have a custom icon state set, but the xeno's sprite wasn't changed to it. + else if(!string_starts_with(fred.icon_state, strain.icon_state_prefix)) + TEST_FAIL("[strain.type]'s icon_state_prefix was not applied when added to a Xenomorph!") diff --git a/icons/mob/xenos/boiler.dmi b/icons/mob/xenos/boiler.dmi index 94d45c459e03..4edcf556eb11 100644 Binary files a/icons/mob/xenos/boiler.dmi and b/icons/mob/xenos/boiler.dmi differ diff --git a/icons/mob/xenos/crusher.dmi b/icons/mob/xenos/crusher.dmi index 014246dbee66..a198cc1d5cd9 100644 Binary files a/icons/mob/xenos/crusher.dmi and b/icons/mob/xenos/crusher.dmi differ diff --git a/icons/mob/xenos/drone.dmi b/icons/mob/xenos/drone.dmi index 937ddaa7b96a..a918d6fb95ab 100644 Binary files a/icons/mob/xenos/drone.dmi and b/icons/mob/xenos/drone.dmi differ diff --git a/icons/mob/xenos/facehugger.dmi b/icons/mob/xenos/facehugger.dmi index 2d8665899331..d44903adf941 100644 Binary files a/icons/mob/xenos/facehugger.dmi and b/icons/mob/xenos/facehugger.dmi differ