diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 618d4b56a440f..e1496922362d0 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -520,6 +520,7 @@ GLOBAL_LIST_INIT(xenoupgradetiers, list(XENO_UPGRADE_BASETYPE, XENO_UPGRADE_INVA #define CASTE_DO_NOT_ANNOUNCE_DEATH (1<<14) // xenos with this flag wont be announced to hive when dying #define CASTE_STAGGER_RESISTANT (1<<15) //Resistant to some forms of stagger, such as projectiles #define CASTE_HAS_WOUND_MASK (1<<16) //uses an alpha mask for wounded states +#define CASTE_EXCLUDE_STRAINS (1<<17) // denotes castes/basetypes that should be excluded from being evoable as a strain // Xeno defines that affect evolution, considering making a new var for these #define CASTE_LEADER_TYPE (1<<16) //Whether we are a leader type caste, such as the queen, shrike or ?king?, and is affected by queen ban and playtime restrictions diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm index 028a327235d06..71a8728e3f756 100644 --- a/code/__DEFINES/traits.dm +++ b/code/__DEFINES/traits.dm @@ -264,6 +264,7 @@ //regress and caste swap UI #define TRAIT_CASTE_SWAP "caste_swap" #define TRAIT_REGRESSING "regressing" +#define TRAIT_STRAIN_SWAP "strain swap" ///Pauses campaign mission timer #define CAMPAIGN_MISSION_TIMER_PAUSED "campaign_mission_timer_paused" diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm index 827b526803523..2951656facf0f 100644 --- a/code/_globalvars/bitfields.dm +++ b/code/_globalvars/bitfields.dm @@ -377,7 +377,8 @@ GLOBAL_LIST_INIT(bitfields, list( "CASTE_PLASMADRAIN_IMMUNE" = CASTE_PLASMADRAIN_IMMUNE, "CASTE_NOT_IN_BIOSCAN" = CASTE_NOT_IN_BIOSCAN, "CASTE_DO_NOT_ANNOUNCE_DEATH" = CASTE_DO_NOT_ANNOUNCE_DEATH, - "CASTE_STAGGER_RESISTANT" = CASTE_STAGGER_RESISTANT + "CASTE_STAGGER_RESISTANT" = CASTE_STAGGER_RESISTANT, + "CASTE_EXCLUDE_STRAINS" = CASTE_EXCLUDE_STRAINS, ), "can_flags" = list( "CASTE_CAN_HOLD_FACEHUGGERS" = CASTE_CAN_HOLD_FACEHUGGERS, diff --git a/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm b/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm index 6792e232bc92c..09876784ae04e 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/hunter/castedatum_hunter.dm @@ -111,7 +111,7 @@ sunder_recover = 1 // *** Flags *** // - caste_flags = CASTE_INNATE_HEALING|CASTE_INNATE_PLASMA_REGEN|CASTE_HIDE_IN_STATUS + caste_flags = CASTE_INNATE_HEALING|CASTE_INNATE_PLASMA_REGEN|CASTE_HIDE_IN_STATUS|CASTE_EXCLUDE_STRAINS can_flags = CASTE_CAN_BE_QUEEN_HEALED|CASTE_CAN_BE_GIVEN_PLASMA|CASTE_CAN_BE_LEADER|CASTE_CAN_HEAL_WITHOUT_QUEEN // *** Defense *** // diff --git a/code/modules/mob/living/carbon/xenomorph/evo_datum.dm b/code/modules/mob/living/carbon/xenomorph/evo_datum.dm index 8fa9d18e9ee35..513f8e6de1846 100644 --- a/code/modules/mob/living/carbon/xenomorph/evo_datum.dm +++ b/code/modules/mob/living/carbon/xenomorph/evo_datum.dm @@ -1,3 +1,5 @@ +//! TODO: this needs a refactor/UI rewor at some point, we've probably bolted too much onto this over time + /// Empty datum parent for use as evolution panel entrance. /datum/evolution_panel // Empty @@ -39,7 +41,7 @@ "type_path" = caste.type, "name" = caste.display_name, "abilities" = list(), - "instant_evolve" = (caste.caste_flags & CASTE_INSTANT_EVOLUTION || (HAS_TRAIT(xeno, TRAIT_CASTE_SWAP) || HAS_TRAIT(xeno, TRAIT_REGRESSING))), + "instant_evolve" = (caste.caste_flags & CASTE_INSTANT_EVOLUTION || (HAS_TRAIT(xeno, TRAIT_STRAIN_SWAP) || HAS_TRAIT(xeno, TRAIT_CASTE_SWAP) || HAS_TRAIT(xeno, TRAIT_REGRESSING))), ) for(var/ability in caste.actions) var/datum/action/ability/xeno_action/xeno_ability = ability @@ -86,3 +88,4 @@ . = ..() REMOVE_TRAIT(user, TRAIT_CASTE_SWAP, TRAIT_CASTE_SWAP) REMOVE_TRAIT(user, TRAIT_REGRESSING, TRAIT_REGRESSING) + REMOVE_TRAIT(user, TRAIT_STRAIN_SWAP, TRAIT_STRAIN_SWAP) diff --git a/code/modules/mob/living/carbon/xenomorph/evolution.dm b/code/modules/mob/living/carbon/xenomorph/evolution.dm index bd943e23dfca0..7c37a6e48e6af 100644 --- a/code/modules/mob/living/carbon/xenomorph/evolution.dm +++ b/code/modules/mob/living/carbon/xenomorph/evolution.dm @@ -16,7 +16,7 @@ set desc = "Change into another caste in the same tier." set category = "Alien" - if(world.time - (GLOB.key_to_time_of_caste_swap[key] ? GLOB.key_to_time_of_caste_swap[key] : -INFINITY) < 9000) //casteswap timer, 15 minutes + if(world.time - (GLOB.key_to_time_of_caste_swap[key] ? GLOB.key_to_time_of_caste_swap[key] : -INFINITY) < (15 MINUTES)) to_chat(src, span_warning("Your caste swap timer is not done yet.")) return @@ -24,6 +24,19 @@ ADD_TRAIT(src, TRAIT_CASTE_SWAP, TRAIT_CASTE_SWAP) GLOB.evo_panel.ui_interact(src) +/mob/living/carbon/xenomorph/verb/strain_swap() + set name = "Strain Swap" + set desc = "Change into a strain of your current caste." + set category = "Alien" + + if(world.time - (GLOB.key_to_time_of_caste_swap[key] ? GLOB.key_to_time_of_caste_swap[key] : -INFINITY) < (5 MINUTES)) // yes this is shared + to_chat(src, span_warning("Your caste swap timer is not done yet.")) + return + + SStgui.close_user_uis(src, GLOB.evo_panel) + ADD_TRAIT(src, TRAIT_STRAIN_SWAP, TRAIT_STRAIN_SWAP) + GLOB.evo_panel.ui_interact(src) + /mob/living/carbon/xenomorph/verb/regress() set name = "Regress" set desc = "Regress into a lower form." @@ -36,6 +49,8 @@ ///Creates a list of possible /datum/xeno_caste options for a caste based on their tier. /mob/living/carbon/xenomorph/proc/get_evolution_options() . = list() + if(HAS_TRAIT(src, TRAIT_STRAIN_SWAP)) + return xeno_caste.get_strain_options() if(HAS_TRAIT(src, TRAIT_CASTE_SWAP)) switch(tier) if(XENO_TIER_ZERO, XENO_TIER_FOUR) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index 8d190eec998e3..a46bd14dbf871 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -245,6 +245,30 @@ current_type = initial(current_type.parent_type) return current_type +/// basetype = list(strain1, strain2) +GLOBAL_LIST_INIT(strain_list, init_glob_strain_list()) +/proc/init_glob_strain_list() + var/list/strain_list = list() + for(var/datum/xeno_caste/root_caste AS in GLOB.xeno_caste_datums) + if(root_caste.parent_type != /datum/xeno_caste) + continue + strain_list[root_caste] = list() + for(var/datum/xeno_caste/typepath AS in subtypesof(root_caste)) + if(typepath::upgrade != XENO_UPGRADE_BASETYPE) + continue + if(typepath::caste_flags & CASTE_EXCLUDE_STRAINS) + continue + strain_list[root_caste] += typepath + return strain_list + +///returns a list of strains(xeno castedatum paths) that this caste can currently evolve to +/datum/xeno_caste/proc/get_strain_options() + var/datum/xeno_caste/root_type = type + while(initial(root_type.parent_type) != /datum/xeno_caste) + root_type = root_type::parent_type + var/list/options = GLOB.strain_list[root_type] + return options?.Copy() + /mob/living/carbon/xenomorph name = "Drone" desc = "What the hell is THAT?" diff --git a/code/modules/unit_tests/xeno_logical_scaling.dm b/code/modules/unit_tests/xeno_logical_scaling.dm index c8d7ffe7d9f18..fb285b68e635d 100644 --- a/code/modules/unit_tests/xeno_logical_scaling.dm +++ b/code/modules/unit_tests/xeno_logical_scaling.dm @@ -1,16 +1,15 @@ /datum/unit_test/xeno_logical_scaling/Run() var/list/by_xeno = list() - for(var/i in subtypesof(/datum/xeno_caste)) - var/datum/xeno_caste/caste = i + for(var/datum/xeno_caste/caste AS in subtypesof(/datum/xeno_caste)) var/typepath = initial(caste.caste_type_path) var/upgrade = initial(caste.upgrade) if(isnull(typepath)) - Fail("[i] has a null caste_type_path") + Fail("[caste] has a null caste_type_path") continue - if(upgrade == "basetype") + if(upgrade == XENO_UPGRADE_BASETYPE) continue if(isnull(upgrade)) - Fail("[i] has a null upgrade") + Fail("[caste] has a null upgrade") continue if(!("[typepath]" in by_xeno)) by_xeno["[typepath]"] = list()