From caf18a019764446426848035870eba4f9053a68d Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 19:15:21 +0000 Subject: [PATCH 01/17] refactors Initialize() and recalculate_actions() --- .../mob/living/carbon/xenomorph/Xenomorph.dm | 202 +++++++++--------- .../living/carbon/xenomorph/castes/Carrier.dm | 5 + .../living/carbon/xenomorph/castes/Queen.dm | 1 + .../living/carbon/xenomorph/castes/Runner.dm | 6 + 4 files changed, 108 insertions(+), 106 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 51cab73e80e6..110e6f82c788 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -347,143 +347,144 @@ var/atom/movable/vis_obj/xeno_wounds/wound_icon_holder var/atom/movable/vis_obj/xeno_pack/backpack_icon_holder -/mob/living/carbon/xenomorph/Initialize(mapload, mob/living/carbon/xenomorph/oldXeno, h_number) - var/area/A = get_area(src) - if(A && A.statistic_exempt) +/mob/living/carbon/xenomorph/Initialize(mapload, mob/living/carbon/xenomorph/old_xeno, hivenumber) + + var/datum/hive_status/hive + if(hivenumber) + hive = GLOB.hive_datum[hivenumber] + if(hive) + hive.add_xeno(src) + + var/area/current_area = get_area(src) + if(current_area && current_area.statistic_exempt) statistic_exempt = TRUE wound_icon_holder = new(null, src) vis_contents += wound_icon_holder - if(oldXeno) - set_movement_intent(oldXeno.m_intent) - hivenumber = oldXeno.hivenumber - nicknumber = oldXeno.nicknumber - life_kills_total = oldXeno.life_kills_total - life_damage_taken_total = oldXeno.life_damage_taken_total - evolution_stored = oldXeno.evolution_stored - if(oldXeno.iff_tag) - iff_tag = oldXeno.iff_tag - iff_tag.forceMove(src) - oldXeno.iff_tag = null - else if (h_number) - hivenumber = h_number - set_languages(list(LANGUAGE_XENOMORPH, LANGUAGE_HIVEMIND)) - if(oldXeno) - for(var/datum/language/L in oldXeno.languages) - add_language(L.name)//Make sure to keep languages (mostly for event Queens that know English) - // Well, not yet, technically - var/datum/hive_status/in_hive = GLOB.hive_datum[hivenumber] - if(in_hive) - in_hive.add_xeno(src) - // But now we are! + ///Handle transferring things from the old Xeno if we have one in the case of evolve, devolve etc. + if(old_xeno) + src.hivenumber = old_xeno.hivenumber + src.nicknumber = old_xeno.nicknumber + src.life_kills_total = old_xeno.life_kills_total + src.life_damage_taken_total = old_xeno.life_damage_taken_total + src.evolution_stored = old_xeno.evolution_stored + + for(var/datum/language/language in old_xeno.languages) + add_language(language.name)//Make sure to keep languages (mostly for event Queens that know English) + + //Carry over intents & targeted limb to the new Xeno + set_movement_intent(old_xeno.m_intent) + a_intent_change(old_xeno.a_intent) + if(src.client) + a_select_zone(old_xeno.zone_selected, src.client) + + //We are hiding, let's keep hiding if we can! + if(old_xeno.layer == XENO_HIDING_LAYER) + for(var/datum/action/xeno_action/onclick/xenohide/hide in actions) + if(istype(hide)) + layer = XENO_HIDING_LAYER + hide.button.icon_state = "template_active" - for(var/T in in_hive.hive_inherant_traits) - ADD_TRAIT(src, T, TRAIT_SOURCE_HIVE) + //If we're holding things drop them + for(var/obj/item/item in old_xeno.contents) //Drop stuff + old_xeno.drop_inv_item_on_ground(item) + old_xeno.empty_gut() + + //Set leader to the new mob + if(IS_XENO_LEADER(old_xeno)) + hive.replace_hive_leader(old_xeno, src) + + if(old_xeno.iff_tag) + iff_tag = old_xeno.iff_tag + iff_tag.forceMove(src) + old_xeno.iff_tag = null + + for(var/trait in hive.hive_inherant_traits) + ADD_TRAIT(src, trait, TRAIT_SOURCE_HIVE) mutators.xeno = src + //Set caste stuff if(caste_type && GLOB.xeno_datum_list[caste_type]) caste = GLOB.xeno_datum_list[caste_type] - else - to_world("something went very wrong") - return - update_icon_source() + //Fire immunity signals + if (caste.fire_immunity != FIRE_IMMUNITY_NONE) + if(caste.fire_immunity & FIRE_IMMUNITY_NO_IGNITE) + RegisterSignal(src, COMSIG_LIVING_PREIGNITION, PROC_REF(fire_immune)) - acid_splash_cooldown = caste.acid_splash_cooldown + RegisterSignal(src, list(COMSIG_LIVING_FLAMER_CROSSED, COMSIG_LIVING_FLAMER_FLAMED), PROC_REF(flamer_crossed_immune)) + else + UnregisterSignal(src, list( + COMSIG_LIVING_PREIGNITION, + COMSIG_LIVING_FLAMER_CROSSED, + COMSIG_LIVING_FLAMER_FLAMED + )) - if (caste.fire_immunity != FIRE_IMMUNITY_NONE) - if(caste.fire_immunity & FIRE_IMMUNITY_NO_IGNITE) - RegisterSignal(src, COMSIG_LIVING_PREIGNITION, PROC_REF(fire_immune)) - RegisterSignal(src, list( - COMSIG_LIVING_FLAMER_CROSSED, - COMSIG_LIVING_FLAMER_FLAMED, - ), PROC_REF(flamer_crossed_immune)) - else - UnregisterSignal(src, list( - COMSIG_LIVING_PREIGNITION, - COMSIG_LIVING_FLAMER_CROSSED, - COMSIG_LIVING_FLAMER_FLAMED, - )) + if(caste.spit_types && length(caste.spit_types)) + ammo = GLOB.ammo_list[caste.spit_types[1]] - recalculate_everything() + acid_splash_cooldown = caste.acid_splash_cooldown + + if(caste.adjust_size_x != 1) + var/matrix/matrix = matrix() + matrix.Scale(caste.adjust_size_x, caste.adjust_size_y) + apply_transform(matrix) + + behavior_delegate = new caste.behavior_delegate_type() + behavior_delegate.bound_xeno = src + behavior_delegate.add_to_xeno() + resin_build_order = caste.resin_build_order + + job = caste.caste_type // Used for tracking the caste playtime + + else + CRASH("Attempted to create a new xenomorph [src] without caste datum.") if(mob_size < MOB_SIZE_BIG) mob_flags |= SQUEEZE_UNDER_VEHICLES + // More setup stuff for names, abilities etc + update_icon_source() generate_name() + add_inherent_verbs() + add_abilities() + create_reagents(100) + regenerate_icons() + toggle_xeno_mobhud() //This is a verb, but fuck it, it just werks + toggle_xeno_hostilehud() + recalculate_everything() - if(isqueen(src)) - SStracking.set_leader("hive_[hivenumber]", src) + //Begin SStracking SStracking.start_tracking("hive_[hivenumber]", src) . = ..() + + GLOB.living_xeno_list += src + GLOB.xeno_mob_list += src + //WO GAMEMODE if(SSticker?.mode?.hardcore) hardcore = 1 //Prevents healing and queen evolution time_of_birth = world.time - add_inherent_verbs() - add_abilities() - recalculate_actions() - + //Minimap if(z) INVOKE_NEXT_TICK(src, PROC_REF(add_minimap_marker)) + //Sight sight |= SEE_MOBS see_invisible = SEE_INVISIBLE_LIVING see_in_dark = 12 + if(client) set_lighting_alpha_from_prefs(client) else lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE - if(caste && caste.spit_types && caste.spit_types.len) - ammo = GLOB.ammo_list[caste.spit_types[1]] - - create_reagents(100) - - GLOB.living_xeno_list += src - GLOB.xeno_mob_list += src - - if(caste && caste.adjust_size_x != 1) - var/matrix/M = matrix() - M.Scale(caste.adjust_size_x, caste.adjust_size_y) - apply_transform(M) - - if(caste) - behavior_delegate = new caste.behavior_delegate_type() - behavior_delegate.bound_xeno = src - behavior_delegate.add_to_xeno() - resin_build_order = caste.resin_build_order - else - CRASH("Xenomorph [src] has no caste datum! Tell the devs!") - - regenerate_icons() - toggle_xeno_mobhud() //This is a verb, but fuck it, it just werks - toggle_xeno_hostilehud() - - if(oldXeno) - a_intent_change(oldXeno.a_intent)//Keep intent - - if(oldXeno.layer == XENO_HIDING_LAYER) - //We are hiding, let's keep hiding if we can! - for(var/datum/action/xeno_action/onclick/xenohide/hide in actions) - if(istype(hide)) - layer = XENO_HIDING_LAYER - hide.button.icon_state = "template_active" - - for(var/obj/item/W in oldXeno.contents) //Drop stuff - oldXeno.drop_inv_item_on_ground(W) - - oldXeno.empty_gut() - - if(IS_XENO_LEADER(oldXeno)) - hive.replace_hive_leader(oldXeno, src) - // Only handle free slots if the xeno is not in tdome if(!is_admin_level(z)) var/selected_caste = GLOB.xeno_datum_list[caste_type]?.type @@ -491,13 +492,11 @@ if(round_statistics && !statistic_exempt) round_statistics.track_new_participant(faction, 1) - generate_name() // This can happen if a xeno gets made before the game starts if (hive && hive.hive_ui) hive.hive_ui.update_all_xeno_data() - job = caste.caste_type // Used for tracking the caste playtime Decorate() RegisterSignal(src, COMSIG_MOB_SCREECH_ACT, PROC_REF(handle_screech_act)) @@ -928,15 +927,6 @@ recalculate_acid() recalculate_weeds() pull_multiplier = mutators.pull_multiplier - if(isrunner(src)) - //Xeno runners need a small nerf to dragging speed mutator - pull_multiplier = 1 - (1 - mutators.pull_multiplier) * 0.85 - if(is_zoomed) - zoom_out() - if(iscarrier(src)) - var/mob/living/carbon/xenomorph/carrier/carrier = src - carrier.huggers_max = caste.huggers_max - carrier.eggs_max = caste.eggs_max need_weeds = mutators.need_weeds diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm index c13555cba12c..2439e78faf69 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Carrier.dm @@ -92,6 +92,11 @@ var/eggs_max = 0 var/laid_egg = 0 +/mob/living/carbon/xenomorph/carrier/recalculate_actions() + ..() + huggers_max = caste.huggers_max + eggs_max = caste.eggs_max + /mob/living/carbon/xenomorph/carrier/update_icons() . = ..() if (mutation_type == CARRIER_NORMAL) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm index b83b33e2eee5..3748ff5b1124 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm @@ -389,6 +389,7 @@ /mob/living/carbon/xenomorph/queen/Initialize() . = ..() + SStracking.set_leader("hive_[hivenumber]", src) if(!is_admin_level(z))//so admins can safely spawn Queens in Thunderdome for tests. xeno_message(SPAN_XENOANNOUNCE("A new Queen has risen to lead the Hive! Rejoice!"),3,hivenumber) notify_ghosts(header = "New Queen", message = "A new Queen has risen.", source = src, action = NOTIFY_ORBIT) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm index 77e4291ee84b..9fcee5134a48 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Runner.dm @@ -73,6 +73,12 @@ if (pass_flags_container) pass_flags_container.flags_pass |= PASS_FLAGS_CRAWLER +/mob/living/carbon/xenomorph/runner/recalculate_actions() + ..() + //Xeno runners need a small nerf to dragging speed mutator + pull_multiplier = 1 - (1 - mutators.pull_multiplier) * 0.85 + if(is_zoomed) + zoom_out() /datum/behavior_delegate/runner_base name = "Base Runner Behavior Delegate" From c13ebcbfd3f62a5eaf1e8e71888d35435ea4f5d9 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 19:45:36 +0000 Subject: [PATCH 02/17] refactor set_hive_and_update() --- .../mob/living/carbon/xenomorph/Xenomorph.dm | 21 +++++++------------ .../living/carbon/xenomorph/castes/Larva.dm | 3 +++ .../living/carbon/xenomorph/castes/Queen.dm | 5 +++++ 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 110e6f82c788..42421cc59921 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -808,23 +808,17 @@ /mob/living/carbon/xenomorph/proc/set_hive_and_update(new_hivenumber = XENO_HIVE_NORMAL) var/datum/hive_status/new_hive = GLOB.hive_datum[new_hivenumber] if(!new_hive) - return + return FALSE - for(var/T in _status_traits) // They can't keep getting away with this!!! - REMOVE_TRAIT(src, T, TRAIT_SOURCE_HIVE) + for(var/trait in _status_traits) // They can't keep getting away with this!!! + REMOVE_TRAIT(src, trait, TRAIT_SOURCE_HIVE) new_hive.add_xeno(src) - for(var/T in new_hive.hive_inherant_traits) - ADD_TRAIT(src, T, TRAIT_SOURCE_HIVE) + for(var/trait in new_hive.hive_inherant_traits) + ADD_TRAIT(src, trait, TRAIT_SOURCE_HIVE) - if(istype(src, /mob/living/carbon/xenomorph/larva)) - var/mob/living/carbon/xenomorph/larva/L = src - L.update_icons() // larva renaming done differently - else - generate_name() - if(istype(src, /mob/living/carbon/xenomorph/queen)) - update_living_queens() + generate_name() lock_evolve = FALSE banished = FALSE @@ -835,6 +829,8 @@ // Update the hive status UI new_hive.hive_ui.update_all_xeno_data() + return TRUE + //*********************************************************// //********************Mutator functions********************// //*********************************************************// @@ -929,7 +925,6 @@ pull_multiplier = mutators.pull_multiplier need_weeds = mutators.need_weeds - /mob/living/carbon/xenomorph/proc/recalculate_acid() if(caste) acid_level = caste.acid_level diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index 82d80752ec54..ff50479cb397 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -176,3 +176,6 @@ /mob/living/carbon/xenomorph/larva/is_xeno_grabbable() return TRUE + +/mob/living/carbon/xenomorph/larva/generate_name() + update_icons() diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm index 3748ff5b1124..4116f451da5e 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm @@ -445,6 +445,11 @@ //Update linked data so they show up properly change_real_name(src, name) +/mob/living/carbon/xenomorph/queen/set_hive_and_update(new_hivenumber) + if(!..()) + return FALSE + update_living_queens() + /mob/living/carbon/xenomorph/queen/proc/make_combat_effective() queen_aged = TRUE From 0189d624835d29f78618d92f7b969cb61b48f138 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 22:20:36 +0000 Subject: [PATCH 03/17] refactors xeno numbering, adds 999 numbers to all hives --- .../mob/living/carbon/xenomorph/Xenomorph.dm | 27 +++++----- .../living/carbon/xenomorph/castes/Larva.dm | 51 ++++++++++--------- .../living/carbon/xenomorph/castes/Queen.dm | 8 +-- .../living/carbon/xenomorph/xeno_defines.dm | 5 ++ 4 files changed, 52 insertions(+), 39 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 42421cc59921..9d26b24b2e18 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -552,16 +552,7 @@ /mob/living/carbon/xenomorph/proc/generate_name() //We don't have a nicknumber yet, assign one to stick with us if(!nicknumber) - var/tempnumber = rand(1, 999) - var/list/numberlist = list() - for(var/mob/living/carbon/xenomorph/X in GLOB.xeno_mob_list) - numberlist += X.nicknumber - - while(tempnumber in numberlist) - tempnumber = rand(1, 999) - - nicknumber = tempnumber - + generate_and_set_nicknumber() // Even if we don't have the hive datum we usually still have the hive number var/datum/hive_status/in_hive = hive if(!in_hive) @@ -570,9 +561,8 @@ //Im putting this in here, because this proc gets called when a player inhabits a SSD xeno and it needs to go somewhere (sorry) hud_set_marks() - handle_name(in_hive) + //handle_name(in_hive) -> look at queen handle_name() also -/mob/living/carbon/xenomorph/proc/handle_name(datum/hive_status/in_hive) var/name_prefix = in_hive.prefix var/name_client_prefix = "" var/name_client_postfix = "" @@ -1093,3 +1083,16 @@ . = ..() if(!resting) // !resting because we dont wanna prematurely update wounds if they're just trying to rest update_wounds() + +///Generate a new unused nicknumber for the current hive, if hive doesn't exist return 0 +/mob/living/carbon/xenomorph/proc/generate_and_set_nicknumber() + if(!hive) + //If hive doesn't exist make it 0 + nicknumber = 0 + return + var/datum/hive_status/hive_status = hive + if(length(hive_status.available_nicknumbers)) + nicknumber = pick(hive_status.available_nicknumbers) + else + //If we somehow use all 999 numbers fallback on 0 + nicknumber = 0 diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index ff50479cb397..1dc4715e5fbe 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -101,30 +101,13 @@ //Larva code is just a mess, so let's get it over with /mob/living/carbon/xenomorph/larva/update_icons() - var/progress = "" //Naming convention, three different names var/state = "" //Icon convention, two different sprite sets - var/name_prefix = "" - - if(hive) - name_prefix = hive.prefix - color = hive.color - - if(evolution_stored >= evolution_threshold) - progress = "Mature " - else if(evolution_stored < evolution_threshold / 2) //We're still bloody - progress = "Bloody " + if(evolution_stored < evolution_threshold / 2) //We're still bloody state = "Bloody " - else - progress = "" - - name = "[name_prefix][progress]Larva ([nicknumber])" - - if(istype(src,/mob/living/carbon/xenomorph/larva/predalien)) state = "Predalien " //Sort of a hack. - - //Update linked data so they show up properly - change_real_name(src, name) + if(istype(src,/mob/living/carbon/xenomorph/larva/predalien)) + state = "Predalien " //Sort of a hack. <- BIRD: Refactor this out if(stat == DEAD) icon_state = "[state_override || state]Larva Dead" else if(handcuffed || legcuffed) @@ -141,9 +124,6 @@ /mob/living/carbon/xenomorph/larva/alter_ghost(mob/dead/observer/ghost) ghost.icon_state = "[caste.caste_type]" -/mob/living/carbon/xenomorph/larva/handle_name() - return - /mob/living/carbon/xenomorph/larva/start_pulling(atom/movable/AM) return @@ -177,5 +157,28 @@ /mob/living/carbon/xenomorph/larva/is_xeno_grabbable() return TRUE +///Larva name generation, set nickname = (number between 1 & 999) which isn't taken by any other xenos in GLOB.xeno_mob_list /mob/living/carbon/xenomorph/larva/generate_name() - update_icons() + if(!nicknumber) + generate_and_set_nicknumber() + + var/progress = "" //Naming convention, three different names + var/name_prefix = "" // Prefix for hive + + if(hive) + name_prefix = hive.prefix + color = hive.color + + if(evolution_stored >= evolution_threshold) + progress = "Mature " + else if(evolution_stored < evolution_threshold / 2) //We're still bloody + progress = "Bloody " + + name = "[name_prefix][progress]Larva ([nicknumber])" + + //Update linked data so they show up properly + change_real_name(src, name) + //Update the hive status UI + if(hive) + var/datum/hive_status/hive_status = hive + hive_status.hive_ui.update_xeno_info() diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm index 4116f451da5e..159909669984 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Queen.dm @@ -410,8 +410,10 @@ AddComponent(/datum/component/footstep, 2 , 35, 11, 4, "alien_footstep_large") -/mob/living/carbon/xenomorph/queen/handle_name(datum/hive_status/in_hive) - var/name_prefix = in_hive.prefix +/mob/living/carbon/xenomorph/queen/generate_name() + if(!nicknumber) + generate_and_set_nicknumber() + var/name_prefix = hive.prefix if(queen_aged) age_xeno() switch(age) @@ -440,7 +442,7 @@ name_client_prefix = "[(client.xeno_prefix||client.xeno_postfix) ? client.xeno_prefix : "XX"]-" name_client_postfix = client.xeno_postfix ? ("-"+client.xeno_postfix) : "" full_designation = "[name_client_prefix][nicknumber][name_client_postfix]" - color = in_hive.color + color = hive.color //Update linked data so they show up properly change_real_name(src, name) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm index f1fff4fb765e..b4ffe41c3830 100644 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm @@ -365,6 +365,8 @@ var/datum/tacmap/xeno/tacmap var/minimap_type = MINIMAP_FLAG_XENO + var/list/available_nicknumbers = list() + /datum/hive_status/New() mutators.hive = src hive_ui = new(src) @@ -376,6 +378,9 @@ if(hivenumber != XENO_HIVE_NORMAL) return + for(var/number in 1 to 999) + available_nicknumbers += number + RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup)) /datum/hive_status/proc/post_setup() From 05526282c1a04358df4416810f1b593995b9cd82 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 22:42:38 +0000 Subject: [PATCH 04/17] removes istype predalien larva --- code/modules/mob/living/carbon/xenomorph/castes/Larva.dm | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index 1dc4715e5fbe..cf85a85b2a36 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -106,8 +106,6 @@ if(evolution_stored < evolution_threshold / 2) //We're still bloody state = "Bloody " - if(istype(src,/mob/living/carbon/xenomorph/larva/predalien)) - state = "Predalien " //Sort of a hack. <- BIRD: Refactor this out if(stat == DEAD) icon_state = "[state_override || state]Larva Dead" else if(handcuffed || legcuffed) From a2047818da00280084767bf03799641f590d4697 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 23:06:13 +0000 Subject: [PATCH 05/17] organises file structure, moves all defines into xeno_defines.dm, separate files for caste_datum and hive_status --- code/__DEFINES/job.dm | 8 - code/__DEFINES/mobs.dm | 27 - code/__DEFINES/{xeno.dm => xeno_defines.dm} | 38 + .../carbon/xenomorph/castes/caste_datum.dm | 183 ++ .../living/carbon/xenomorph/hive_status.dm | 1391 +++++++++++++-- .../living/carbon/xenomorph/hive_status_ui.dm | 216 +++ .../carbon/xenomorph/xeno_client_procs.dm | 48 + .../living/carbon/xenomorph/xeno_defines.dm | 1573 ----------------- .../mob/living/carbon/xenomorph/xeno_marks.dm | 60 + colonialmarines.dme | 7 +- 10 files changed, 1777 insertions(+), 1774 deletions(-) rename code/__DEFINES/{xeno.dm => xeno_defines.dm} (95%) create mode 100644 code/modules/mob/living/carbon/xenomorph/castes/caste_datum.dm create mode 100644 code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm create mode 100644 code/modules/mob/living/carbon/xenomorph/xeno_client_procs.dm delete mode 100644 code/modules/mob/living/carbon/xenomorph/xeno_defines.dm create mode 100644 code/modules/mob/living/carbon/xenomorph/xeno_marks.dm diff --git a/code/__DEFINES/job.dm b/code/__DEFINES/job.dm index 56062cb0213b..03b2071a8e66 100644 --- a/code/__DEFINES/job.dm +++ b/code/__DEFINES/job.dm @@ -358,14 +358,6 @@ var/global/list/job_command_roles = JOB_COMMAND_ROLES_LIST #define JOB_PLAYTIME_TIER_3 (70 HOURS) #define JOB_PLAYTIME_TIER_4 (175 HOURS) -#define XENO_NO_AGE -1 -#define XENO_YOUNG 0 -#define XENO_NORMAL 1 -#define XENO_MATURE 2 -#define XENO_ELDER 3 -#define XENO_ANCIENT 4 -#define XENO_PRIME 5 - /// For monthly time tracking #define JOB_OBSERVER "Observer" #define TIMELOCK_JOB(role_id, hours) new/datum/timelock(role_id, hours, role_id) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 072738184807..8b6b58f584f5 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -117,33 +117,6 @@ #define CANSLOW (1<<19) #define NO_PERMANENT_DAMAGE (1<<20) -// ============================= -// hive types - -#define XENO_HIVE_NORMAL "xeno_hive_normal" -#define XENO_HIVE_CORRUPTED "xeno_hive_corrupted" -#define XENO_HIVE_ALPHA "xeno_hive_alpha" -#define XENO_HIVE_BRAVO "xeno_hive_bravo" -#define XENO_HIVE_CHARLIE "xeno_hive_charlie" -#define XENO_HIVE_DELTA "xeno_hive_delta" -#define XENO_HIVE_FERAL "xeno_hive_feral" -#define XENO_HIVE_TAMED "xeno_hive_tamed" -#define XENO_HIVE_MUTATED "xeno_hive_mutated" -#define XENO_HIVE_FORSAKEN "xeno_hive_forsaken" -#define XENO_HIVE_YAUTJA "xeno_hive_yautja" -#define XENO_HIVE_RENEGADE "xeno_hive_renegade" - -#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA, XENO_HIVE_RENEGADE) - -//================================================= - -// ============================= -// slowdowns -#define XENO_SLOWED_AMOUNT 0.7 -#define XENO_SUPERSLOWED_AMOUNT 1.5 -#define HUMAN_SLOWED_AMOUNT 2 -#define HUMAN_SUPERSLOWED_AMOUNT 4 - // Adds onto HUMAN_*****_AMOUNT #define YAUTJA_SLOWED_AMOUNT -1.25 // 0.75s slowdown #define YAUTJA_SUPERSLOWED_AMOUNT -3 // 1s slowdown diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno_defines.dm similarity index 95% rename from code/__DEFINES/xeno.dm rename to code/__DEFINES/xeno_defines.dm index a0a4c927d3d9..9134759bca0b 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno_defines.dm @@ -1,3 +1,41 @@ +// Xeno ages + +#define XENO_NO_AGE -1 +#define XENO_YOUNG 0 +#define XENO_NORMAL 1 +#define XENO_MATURE 2 +#define XENO_ELDER 3 +#define XENO_ANCIENT 4 +#define XENO_PRIME 5 + + +// ============================= +// hive types + +#define XENO_HIVE_NORMAL "xeno_hive_normal" +#define XENO_HIVE_CORRUPTED "xeno_hive_corrupted" +#define XENO_HIVE_ALPHA "xeno_hive_alpha" +#define XENO_HIVE_BRAVO "xeno_hive_bravo" +#define XENO_HIVE_CHARLIE "xeno_hive_charlie" +#define XENO_HIVE_DELTA "xeno_hive_delta" +#define XENO_HIVE_FERAL "xeno_hive_feral" +#define XENO_HIVE_TAMED "xeno_hive_tamed" +#define XENO_HIVE_MUTATED "xeno_hive_mutated" +#define XENO_HIVE_FORSAKEN "xeno_hive_forsaken" +#define XENO_HIVE_YAUTJA "xeno_hive_yautja" +#define XENO_HIVE_RENEGADE "xeno_hive_renegade" + +#define ALL_XENO_HIVES list(XENO_HIVE_NORMAL, XENO_HIVE_CORRUPTED, XENO_HIVE_ALPHA, XENO_HIVE_BRAVO, XENO_HIVE_CHARLIE, XENO_HIVE_DELTA, XENO_HIVE_FERAL, XENO_HIVE_TAMED, XENO_HIVE_MUTATED, XENO_HIVE_FORSAKEN, XENO_HIVE_YAUTJA, XENO_HIVE_RENEGADE) + +//================================================= + +// ============================= +// slowdowns +#define XENO_SLOWED_AMOUNT 0.7 +#define XENO_SUPERSLOWED_AMOUNT 1.5 +#define HUMAN_SLOWED_AMOUNT 2 +#define HUMAN_SUPERSLOWED_AMOUNT 4 + #define XENOCON_THRESHOLD 6000 #define TUNNEL_MOVEMENT_XENO_DELAY 20 diff --git a/code/modules/mob/living/carbon/xenomorph/castes/caste_datum.dm b/code/modules/mob/living/carbon/xenomorph/castes/caste_datum.dm new file mode 100644 index 000000000000..f32202540ce2 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/castes/caste_datum.dm @@ -0,0 +1,183 @@ +// Actual caste datum basedef +/datum/caste_datum + var/caste_type = "" + var/display_name = "" + var/tier = 0 + var/dead_icon = "Drone Dead" + var/language = LANGUAGE_XENOMORPH + var/melee_damage_lower = 10 + var/melee_damage_upper = 20 + ///allows fine tuning melee damage to vehicles per caste. + var/melee_vehicle_damage = 10 + var/evasion = XENO_EVASION_NONE + + var/speed = XENO_SPEED_TIER_10 + + var/plasma_max = 10 + var/plasma_gain = 5 + + var/crystal_max = 0 + + var/max_health = XENO_UNIVERSAL_HPMULT * 100 + ///Are they allowed to evolve (and have their evolution progress group) + var/evolution_allowed = 1 + ///Threshold to next evolution + var/evolution_threshold = 0 + /// whether they can get evo points without needing an ovi queen + var/evolve_without_queen = FALSE + ///This is where you add castes to evolve into. "Separated", "by", "commas" + var/list/evolves_to = list() + /// what caste or castes to de-evolve to. + var/list/deevolves_to = list() + ///If they can use consoles, etc. Set on Queen + var/is_intelligent = 0 + var/caste_desc = null + + // Tackles + var/tackle_min = 2 + var/tackle_max = 6 + var/tackle_chance = 35 + var/tacklestrength_min = 2 + var/tacklestrength_max = 3 + + ///Chance of deflecting projectiles. + var/armor_deflection = 0 + var/fire_immunity = FIRE_IMMUNITY_NONE + var/fire_intensity_resistance = 0 + + ///Delay timer for spitting + var/spit_delay = 60 + + /// Windup for spits + var/spit_windup = FALSE + + ///The strength of our aura. Zero means we can't emit one + var/aura_strength = 0 + + ///"Evolving" removed for the time being + var/aura_allowed = list("frenzy", "warding", "recovery") + + ///Adjust pixel size. 0.x is smaller, 1.x is bigger, percentage based. + var/adjust_size_x = 1 + var/adjust_size_y = 1 + + ///list of datum projectile types the xeno can use. + var/list/spit_types + + var/attack_delay = 0 //Bonus or pen to time in between attacks. + makes slashes slower. + + var/agility_speed_increase = 0 // this opens up possibilities for balancing + + // The type of mutator delegate to instantiate on the base caste. Will + // be replaced when the Xeno chooses a strain. + var/behavior_delegate_type = /datum/behavior_delegate + + // Resin building-related vars + /// Default build time and build distance + var/build_time_mult = BUILD_TIME_MULT_XENO + var/max_build_dist = 0 + + // Carrier vars // + + /// if a hugger is held in hand, won't attempt to leap and kill itself + var/hugger_nurturing = FALSE + var/huggers_max = 0 + var/throwspeed = 0 + var/hugger_delay = 0 + var/eggs_max = 0 + var/egg_cooldown = 30 + ///Armor but for explosions + var/xeno_explosion_resistance = 0 + + //Queen vars + var/can_hold_facehuggers = 0 + var/can_hold_eggs = CANNOT_HOLD_EGGS + + var/can_be_queen_healed = TRUE + var/can_be_revived = TRUE + + var/can_vent_crawl = 1 + + var/caste_luminosity = 0 + + /// if fire_immunity is set to be vulnerable, how much will fire damage be multiplied. Defines in xeno.dm + var/fire_vulnerability_mult = 0 + + var/burrow_cooldown = 5 SECONDS + var/tunnel_cooldown = 100 + var/widen_cooldown = 10 SECONDS + ///Big strong ability, big cooldown. + var/tremor_cooldown = 30 SECONDS + ///whether the xeno heals even outside weeds. + var/innate_healing = FALSE + + var/acid_level = 0 + var/weed_level = WEED_LEVEL_STANDARD + ///Time it takes between acid splash retaliate procs. Variable per caste, for if we want future castes that are acid bombs + var/acid_splash_cooldown = 3 SECONDS + + // regen vars + + var/heal_delay_time = 0 SECONDS + var/heal_resting = 1 + var/heal_standing = 0.4 + var/heal_knocked_out = 0.33 + + var/list/resin_build_order + var/minimum_xeno_playtime = 0 + +// cannot evolve to this caste until the round has been going on for this amount of time + // IMPORTANT: this is ROUND_TIME, not world.time + var/minimum_evolve_time = 1 MINUTES + /// Iconstate for the xeno on the minimap + var/minimap_icon = "xeno" + ///The iconstate for leadered xenos on the minimap, added as overlay + var/minimap_leadered_overlay = "xenoleader" + + var/royal_caste = FALSE + + +/datum/caste_datum/can_vv_modify() + return FALSE + +/datum/caste_datum/New() + . = ..() + + //Initialise evolution and upgrade thresholds in one place, once and for all + evolution_threshold = 0 + if(evolution_allowed) + switch(tier) + if(0) + evolution_threshold = 60 + if(1) + evolution_threshold = 200 + if(2) + evolution_threshold = 500 + //Other tiers (T3, Queen, etc.) can't evolve anyway + + resin_build_order = GLOB.resin_build_order_drone + +/datum/caste_datum/proc/get_caste_requirement(client/client) + return minimum_xeno_playtime - client.get_total_xeno_playtime() + +/datum/caste_datum/proc/get_minimap_icon() + var/image/background = mutable_appearance('icons/ui_icons/map_blips.dmi', "background") + background.color = MINIMAP_ICON_BACKGROUND_XENO + + var/iconstate = minimap_icon ? minimap_icon : "unknown" + var/mutable_appearance/icon = image('icons/ui_icons/map_blips.dmi', icon_state = iconstate) + icon.appearance_flags = RESET_COLOR + background.overlays += icon + + return background + +/datum/caste_datum/proc/can_play_caste(client/client) + if(!CONFIG_GET(flag/use_timelocks)) + return TRUE + + var/total_xeno_playtime = client.get_total_xeno_playtime() + + if(minimum_xeno_playtime && total_xeno_playtime < minimum_xeno_playtime) + return FALSE + + return TRUE diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index 4fe1be51bfff..8adb23fd8390 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -1,216 +1,1279 @@ -/datum/hive_status_ui - var/name = "Hive Status" +/datum/hive_status + var/name = "Normal Hive" - // Data to pass when rendering the UI (not static) - var/total_xenos - var/list/xeno_counts - var/list/tier_slots - var/list/xeno_vitals - var/list/xeno_keys - var/list/xeno_info - var/hive_location - var/burrowed_larva - var/evilution_level + // Used for the faction of the xenomorph. Not recommended to modify. + var/internal_faction - var/data_initialized = FALSE + /// Short Hive ID as string used in stats reporting + var/reporting_id = "normal" - var/datum/hive_status/assoc_hive = null + var/hivenumber = XENO_HIVE_NORMAL + var/mob/living/carbon/xenomorph/queen/living_xeno_queen + var/egg_planting_range = 15 + var/slashing_allowed = XENO_SLASH_ALLOWED //This initial var allows the queen to turn on or off slashing. Slashing off means harm intent does much less damage. + var/construction_allowed = NORMAL_XENO //Who can place construction nodes for special structures + var/destruction_allowed = XENO_LEADER //Who can destroy special structures + var/unnesting_allowed = TRUE + var/hive_orders = "" //What orders should the hive have + var/color = null + var/ui_color = null // Color for hive status collapsible buttons and xeno count list + var/prefix = "" + var/queen_leader_limit = 2 + var/list/open_xeno_leader_positions = list(1, 2) // Ordered list of xeno leader positions (indexes in xeno_leader_list) that are not occupied + var/list/xeno_leader_list[2] // Ordered list (i.e. index n holds the nth xeno leader) + var/stored_larva = 0 -/datum/hive_status_ui/New(datum/hive_status/hive) - assoc_hive = hive - update_all_data() - START_PROCESSING(SShive_status, src) + ///used by /datum/hive_status/proc/increase_larva_after_burst() to support non-integer increases to larva + var/partial_larva = 0 + /// Assoc list of free slots available to specific castes + var/list/free_slots = list( + /datum/caste_datum/burrower = 1, + /datum/caste_datum/hivelord = 1, + /datum/caste_datum/carrier = 1 + ) + /// Assoc list of slots currently used by specific castes (for calculating free_slot usage) + var/list/used_slots = list() + /// list of living tier2 xenos + var/list/tier_2_xenos = list() + /// list of living tier3 xenos + var/list/tier_3_xenos = list() + /// list of living xenos + var/list/totalXenos = list() + /// list of previously living xenos (hardrefs currently) + var/list/total_dead_xenos = list() + var/xeno_queen_timer + var/isSlotOpen = TRUE //Set true for starting alerts only after the hive has reached its full potential + var/allowed_nest_distance = 15 //How far away do we allow nests from an ovied Queen. Default 15 tiles. + var/obj/effect/alien/resin/special/pylon/core/hive_location = null //Set to ref every time a core is built, for defining the hive location + var/crystal_stored = 0 //How much stockpiled material is stored for the hive to use. -/datum/hive_status_ui/process() - update_xeno_vitals() - update_xeno_info(FALSE) - SStgui.update_uis(src) + var/datum/mutator_set/hive_mutators/mutators = new + var/tier_slot_multiplier = 1 + var/larva_gestation_multiplier = 1 + var/bonus_larva_spawn_chance = 1 + var/hijack_burrowed_surge = FALSE //at hijack, start spawning lots of burrowed + /// how many burrowed is going to spawn during larva surge + var/hijack_burrowed_left = 0 -// Updates the list tracking how many xenos there are in each tier, and how many there are in total -/datum/hive_status_ui/proc/update_xeno_counts(send_update = TRUE) - xeno_counts = assoc_hive.get_xeno_counts() + var/ignore_slots = FALSE + var/dynamic_evolution = TRUE + var/evolution_rate = 3 // Only has use if dynamic_evolution is false + var/evolution_bonus = 0 - total_xenos = 0 - for(var/counts in xeno_counts) - for(var/caste in counts) - total_xenos += counts[caste] + var/allow_no_queen_actions = FALSE + var/allow_no_queen_evo = FALSE + var/evolution_without_ovipositor = TRUE //Temporary for the roundstart. + /// Set to false if you want to prevent evolutions into Queens + var/allow_queen_evolve = TRUE + /// Set to true if you want to prevent bursts and spawns of new xenos. Will also prevent healing if the queen no longer exists + var/hardcore = FALSE + /// Set to false if you want to prevent getting burrowed larva from latejoin marines + var/latejoin_burrowed = TRUE - if(send_update) - SStgui.update_uis(src) + var/list/hive_inherant_traits - xeno_counts[1] -= "Queen" // don't show queen in the amount of xenos + // Cultist Info + var/mob/living/carbon/leading_cult_sl - // Also update the amount of T2/T3 slots - tier_slots = assoc_hive.get_tier_slots() + //List of how many maximum of each special structure you can have + var/list/hive_structures_limit = list( + XENO_STRUCTURE_CORE = 1, + XENO_STRUCTURE_CLUSTER = 8, + XENO_STRUCTURE_POOL = 1, + XENO_STRUCTURE_EGGMORPH = 6, + XENO_STRUCTURE_EVOPOD = 2, + XENO_STRUCTURE_RECOVERY = 6, + XENO_STRUCTURE_PYLON = 2, + ) -// Updates the hive location using the area name of the defined hive location turf -/datum/hive_status_ui/proc/update_hive_location(send_update = TRUE) - if(!assoc_hive.hive_location) + var/global/list/hive_structure_types = list( + XENO_STRUCTURE_CORE = /datum/construction_template/xenomorph/core, + XENO_STRUCTURE_CLUSTER = /datum/construction_template/xenomorph/cluster, + XENO_STRUCTURE_EGGMORPH = /datum/construction_template/xenomorph/eggmorph, + XENO_STRUCTURE_RECOVERY = /datum/construction_template/xenomorph/recovery + ) + + var/list/list/hive_structures = list() //Stringref list of structures that have been built + var/list/list/hive_constructions = list() //Stringref list of structures that are being built + + var/datum/hive_status_ui/hive_ui + var/datum/mark_menu_ui/mark_ui + var/datum/hive_faction_ui/faction_ui + + var/list/tunnels = list() + + var/list/allies = list() + + var/list/resin_marks = list() + + var/list/banished_ckeys = list() + + var/hivecore_cooldown = FALSE + + var/need_round_end_check = FALSE + + //Joining as Facehugger vars + /// When can huggers join the round + var/hugger_timelock = 15 MINUTES + /// How many huggers can the hive support + var/playable_hugger_limit = 0 + /// Minimum number of huggers available at any hive size + var/playable_hugger_minimum = 2 + /// This number divides the total xenos counted for slots to give the max number of facehuggers + var/playable_hugger_max_divisor = 4 + + /// How many lesser drones the hive can support + var/lesser_drone_limit = 0 + /// Slots available for lesser drones will never go below this number + var/lesser_drone_minimum = 2 + /// This number divides the total xenos counted for slots to give the max number of lesser drones + var/playable_lesser_drones_max_divisor = 3 + + var/datum/tacmap/xeno/tacmap + var/minimap_type = MINIMAP_FLAG_XENO + + var/list/available_nicknumbers = list() + +/datum/hive_status/New() + mutators.hive = src + hive_ui = new(src) + mark_ui = new(src) + faction_ui = new(src) + tacmap = new(src, minimap_type) + if(!internal_faction) + internal_faction = name + if(hivenumber != XENO_HIVE_NORMAL) return - hive_location = strip_improper(get_area_name(assoc_hive.hive_location)) + for(var/number in 1 to 999) + available_nicknumbers += number + + RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup)) - if(send_update) - SStgui.update_uis(src) +/datum/hive_status/proc/post_setup() + SIGNAL_HANDLER -// Updates the sorted list of all xenos that we use as a key for all other information -/datum/hive_status_ui/proc/update_xeno_keys(send_update = TRUE) - xeno_keys = assoc_hive.get_xeno_keys() + setup_evolution_announcements() + setup_pylon_limits() - if(send_update) - SStgui.update_uis(src) +/datum/hive_status/proc/setup_evolution_announcements() + for(var/time in GLOB.xeno_evolve_times) + if(time == "0") + continue -// Mildly related to the above, but only for when xenos are removed from the hive -// If a xeno dies, we don't have to regenerate all xeno info and sort it again, just remove them from the data list -/datum/hive_status_ui/proc/xeno_removed(mob/living/carbon/xenomorph/X) - if(!xeno_keys) + addtimer(CALLBACK(src, PROC_REF(announce_evolve_available), GLOB.xeno_evolve_times[time]), text2num(time)) + +/// Sets up limits on pylons in New() for potential futureproofing with more static comms +/datum/hive_status/proc/setup_pylon_limits() + hive_structures_limit[XENO_STRUCTURE_PYLON] = length(GLOB.all_static_telecomms_towers) || 2 + +/datum/hive_status/proc/announce_evolve_available(list/datum/caste_datum/available_castes) + + var/list/castes_available = list() + for(var/datum/caste_datum/current_caste as anything in available_castes) + castes_available += initial(current_caste.caste_type) + + var/castes = castes_available.Join(", ") + xeno_message(SPAN_XENOANNOUNCE("The Hive is now strong enough to support: [castes]")) + xeno_maptext("The Hive can now support: [castes]", "Hive Strengthening") + + +// Adds a xeno to this hive +/datum/hive_status/proc/add_xeno(mob/living/carbon/xenomorph/X) + if(!X || !istype(X)) return - for(var/index in 1 to length(xeno_keys)) - var/list/info = xeno_keys[index] - if(info["nicknumber"] == X.nicknumber) + // If the xeno is part of another hive, they should be removed from that one first + if(X.hive && X.hive != src) + X.hive.remove_xeno(X, TRUE) - // tried Remove(), didn't work. *shrug* - xeno_keys[index] = null - xeno_keys -= null - return + // Already in the hive + if(X in totalXenos) + return + + // Can only have one queen. + if(isqueen(X)) + if(!living_xeno_queen && !is_admin_level(X.z)) // Don't consider xenos in admin level + set_living_xeno_queen(X) + + X.hivenumber = hivenumber + X.hive = src + + X.set_faction(internal_faction) + + if(X.hud_list) + X.hud_update() + + var/area/A = get_area(X) + if(!is_admin_level(X.z) || (A.flags_atom & AREA_ALLOW_XENO_JOIN)) + totalXenos += X + if(X.tier == 2) + tier_2_xenos += X + else if(X.tier == 3) + tier_3_xenos += X - SStgui.update_uis(src) + // Xenos are a fuckfest of cross-dependencies of different datums that are initialized at different times + // So don't even bother trying updating UI here without large refactors -// Updates the list of xeno names, strains and references -/datum/hive_status_ui/proc/update_xeno_info(send_update = TRUE) - xeno_info = assoc_hive.get_xeno_info() +// Removes the xeno from the hive +/datum/hive_status/proc/remove_xeno(mob/living/carbon/xenomorph/xeno, hard = FALSE, light_mode = FALSE) + if(!xeno || !istype(xeno)) + return + + // Make sure the xeno was in the hive in the first place + if(!(xeno in totalXenos)) + return - if(send_update) - SStgui.update_uis(src) + // This might be a redundant check now that Queen/Destroy() checks, but doesn't hurt to double check + if(living_xeno_queen == xeno) + var/mob/living/carbon/xenomorph/queen/next_queen = null + for(var/mob/living/carbon/xenomorph/queen/queen in totalXenos) + if(!is_admin_level(queen.z) && queen != src && !QDELETED(queen)) + next_queen = queen + break -// Updates vital information about xenos such as health and location. Only info that should be updated regularly -/datum/hive_status_ui/proc/update_xeno_vitals() - xeno_vitals = assoc_hive.get_xeno_vitals() + set_living_xeno_queen(next_queen) // either null or a queen -// Updates how many buried larva there are -/datum/hive_status_ui/proc/update_burrowed_larva(send_update = TRUE) - burrowed_larva = assoc_hive.stored_larva - if(SSxevolution) - evilution_level = SSxevolution.get_evolution_boost_power(assoc_hive.hivenumber) + // We allow "soft" removals from the hive (the xeno still retains information about the hive) + // This is so that xenos can add themselves back to the hive if they should die or otherwise go "on leave" from the hive + if(hard) + xeno.hivenumber = 0 + xeno.hive = null +#ifndef UNIT_TESTS // Since this is a hard ref, we shouldn't confuse create_and_destroy else - evilution_level = 1 - if(send_update) - SStgui.update_uis(src) - -// Updates all data except burrowed larva -/datum/hive_status_ui/proc/update_all_xeno_data(send_update = TRUE) - update_xeno_counts(FALSE) - update_xeno_vitals() - update_xeno_keys(FALSE) - update_xeno_info(FALSE) - - if(send_update) - SStgui.update_uis(src) - -// Updates all data, including burrowed larva -/datum/hive_status_ui/proc/update_all_data() - data_initialized = TRUE - update_all_xeno_data(FALSE) - update_burrowed_larva(FALSE) - SStgui.update_uis(src) - -/datum/hive_status_ui/ui_state(mob/user) - return GLOB.hive_state[assoc_hive.internal_faction] - -/datum/hive_status_ui/ui_status(mob/user, datum/ui_state/state) - . = ..() - if(isobserver(user)) - return UI_INTERACTIVE + total_dead_xenos += xeno +#endif + + totalXenos -= xeno + if(xeno.tier == 2) + tier_2_xenos -= xeno + else if(xeno.tier == 3) + tier_3_xenos -= xeno + + // Only handle free slots if the xeno is not in tdome + if(!is_admin_level(xeno.z)) + var/selected_caste = GLOB.xeno_datum_list[xeno.caste_type]?.type + if(used_slots[selected_caste]) + used_slots[selected_caste]-- + + if(!light_mode) + hive_ui.update_xeno_counts() + hive_ui.xeno_removed(xeno) + +/datum/hive_status/proc/set_living_xeno_queen(mob/living/carbon/xenomorph/queen/queen) + if(!queen) + mutators.reset_mutators() + SStracking.delete_leader("hive_[hivenumber]") + SStracking.stop_tracking("hive_[hivenumber]", living_xeno_queen) + SShive_status.wait = 10 SECONDS + else + SStracking.set_leader("hive_[hivenumber]", queen) + SShive_status.wait = 2 SECONDS + + SEND_SIGNAL(src, COMSIG_HIVE_NEW_QUEEN, queen) + living_xeno_queen = queen + + recalculate_hive() + +/datum/hive_status/proc/recalculate_hive() + if (!living_xeno_queen) + queen_leader_limit = 0 //No leaders for a Hive without a Queen! + else + queen_leader_limit = 4 + mutators.leader_count_boost + + if (xeno_leader_list.len > queen_leader_limit) + var/diff = 0 + for (var/i in queen_leader_limit + 1 to xeno_leader_list.len) + if(!open_xeno_leader_positions.Remove(i)) + remove_hive_leader(xeno_leader_list[i]) + diff++ + xeno_leader_list.len -= diff // Changing the size of xeno_leader_list needs to go at the end or else it won't iterate through the list properly + else if (xeno_leader_list.len < queen_leader_limit) + for (var/i in xeno_leader_list.len + 1 to queen_leader_limit) + open_xeno_leader_positions += i + xeno_leader_list.len++ + + + tier_slot_multiplier = mutators.tier_slot_multiplier + larva_gestation_multiplier = mutators.larva_gestation_multiplier + bonus_larva_spawn_chance = mutators.bonus_larva_spawn_chance + + hive_ui.update_all_data() + +/datum/hive_status/proc/add_hive_leader(mob/living/carbon/xenomorph/xeno) + if(!xeno) + return FALSE //How did this even happen? + if(!open_xeno_leader_positions.len) + return FALSE //Too many leaders already (no available xeno leader positions) + if(xeno.hive_pos != NORMAL_XENO) + return FALSE //Already on the list + var/leader_num = open_xeno_leader_positions[1] + xeno_leader_list[leader_num] = xeno + xeno.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + xeno.handle_xeno_leader_pheromones() + xeno.hud_update() // To add leader star + open_xeno_leader_positions -= leader_num + + xeno.update_minimap_icon() + + give_action(xeno, /datum/action/xeno_action/activable/info_marker) + + hive_ui.update_xeno_keys() + return TRUE + +/datum/hive_status/proc/remove_hive_leader(mob/living/carbon/xenomorph/xeno, light_mode = FALSE) + if(!istype(xeno) || !IS_XENO_LEADER(xeno)) + return FALSE + + var/leader_num = GET_XENO_LEADER_NUM(xeno) + + xeno_leader_list[leader_num] = null + + if(!light_mode) // Don't run side effects during deletions. Better yet, replace all this by signals someday + xeno.hive_pos = NORMAL_XENO + xeno.handle_xeno_leader_pheromones() + xeno.hud_update() // To remove leader star + + // Need to maintain ascending order of open_xeno_leader_positions + for (var/i in 1 to queen_leader_limit) + if (i > open_xeno_leader_positions.len || open_xeno_leader_positions[i] > leader_num) + open_xeno_leader_positions.Insert(i, leader_num) + break + + if(!light_mode) + hive_ui.update_xeno_keys() -/datum/hive_status_ui/ui_data(mob/user) - . = list() - .["total_xenos"] = total_xenos - .["xeno_counts"] = xeno_counts - .["tier_slots"] = tier_slots - .["xeno_keys"] = xeno_keys - .["xeno_info"] = xeno_info - .["xeno_vitals"] = xeno_vitals - .["queen_location"] = get_area_name(assoc_hive.living_xeno_queen) - .["hive_location"] = hive_location - .["burrowed_larva"] = burrowed_larva - .["evilution_level"] = evilution_level + for(var/obj/effect/alien/resin/marker/leaderless_mark in resin_marks) //no resin_mark limit abuse + if(leaderless_mark.createdby == xeno.nicknumber) + qdel(leaderless_mark) - var/mob/living/carbon/xenomorph/queen/Q = user - .["is_in_ovi"] = istype(Q) && Q.ovipositor + xeno.update_minimap_icon() -/datum/hive_status_ui/ui_static_data(mob/user) - . = list() - .["user_ref"] = REF(user) - .["hive_color"] = assoc_hive.ui_color - .["hive_name"] = assoc_hive.name + remove_action(xeno, /datum/action/xeno_action/activable/info_marker) -/datum/hive_status_ui/proc/open_hive_status(mob/user) - if(!user) + return TRUE + +/datum/hive_status/proc/replace_hive_leader(mob/living/carbon/xenomorph/original, mob/living/carbon/xenomorph/replacement) + if(!replacement || replacement.hive_pos != NORMAL_XENO) + return remove_hive_leader(original) + + var/leader_num = GET_XENO_LEADER_NUM(original) + + xeno_leader_list[leader_num] = replacement + + original.hive_pos = NORMAL_XENO + original.handle_xeno_leader_pheromones() + original.hud_update() // To remove leader star + remove_action(original, /datum/action/xeno_action/activable/info_marker) + + replacement.hive_pos = XENO_LEADER_HIVE_POS(leader_num) + replacement.handle_xeno_leader_pheromones() + replacement.hud_update() // To add leader star + give_action(replacement, /datum/action/xeno_action/activable/info_marker) + + hive_ui.update_xeno_keys() + +/datum/hive_status/proc/handle_xeno_leader_pheromones() + for(var/mob/living/carbon/xenomorph/L in xeno_leader_list) + L.handle_xeno_leader_pheromones() + +/* + * Helper procs for the Hive Status UI + * These are all called by the hive status UI manager to update its data + */ + +// Returns a list of how many of each caste of xeno there are, sorted by tier +/datum/hive_status/proc/get_xeno_counts() + // Every caste is manually defined here so you get + var/list/xeno_counts = list( + // Yes, Queen is technically considered to be tier 0 + list(XENO_CASTE_LARVA = 0, "Queen" = 0), + list(XENO_CASTE_DRONE = 0, XENO_CASTE_RUNNER = 0, XENO_CASTE_SENTINEL = 0, XENO_CASTE_DEFENDER = 0), + list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0), + list(XENO_CASTE_BOILER = 0, XENO_CASTE_CRUSHER = 0, XENO_CASTE_PRAETORIAN = 0, XENO_CASTE_RAVAGER = 0) + ) + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + //don't show xenos in the thunderdome when admins test stuff. + if(is_admin_level(X.z)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(X.caste && X.counts_for_slots) + xeno_counts[X.caste.tier+1][X.caste.caste_type]++ + + return xeno_counts + +// Returns a sorted list of some basic info (stuff that's needed for sorting) about all the xenos in the hive +// The idea is that we sort this list, and use it as a "key" for all the other information (especially the nicknumber) +// in the hive status UI. That way we can minimize the amount of sorts performed by only calling this when xenos are created/disposed +/datum/hive_status/proc/get_xeno_keys() + var/list/xenos[totalXenos.len] + + var/index = 1 + var/useless_slots = 0 + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(is_admin_level(X.z)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + useless_slots++ + continue + + // Insert without doing list merging + xenos[index++] = list( + "nicknumber" = X.nicknumber, + "tier" = X.tier, // This one is only important for sorting + "is_leader" = (IS_XENO_LEADER(X)), + "is_queen" = istype(X.caste, /datum/caste_datum/queen), + "caste_type" = X.caste_type + ) + + // Clear nulls from the xenos list + xenos.len -= useless_slots + + // Make it all nice and fancy by sorting the list before returning it + var/list/sorted_keys = sort_xeno_keys(xenos) + if(length(sorted_keys)) + return sorted_keys + return xenos + +// This sorts the xeno info list by multiple criteria. Prioritized in order: +// 1. Queen +// 2. Leaders +// 3. Tier +// It uses a slightly modified insertion sort to accomplish this +/datum/hive_status/proc/sort_xeno_keys(list/xenos) + if(!length(xenos)) + return + + var/list/sorted_list = xenos.Copy() + + if(!length(sorted_list)) return - // Update absolutely all data - if(!data_initialized) - update_all_data() + for(var/index in 2 to length(sorted_list)) + var/j = index + + while(j > 1) + var/current = sorted_list[j] + var/prev = sorted_list[j-1] + + // Queen comes first, always + if(current["is_queen"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // don't muck up queen's slot + if(prev["is_queen"]) + j-- + continue + + // Leaders before normal xenos + if(!prev["is_leader"] && current["is_leader"]) + sorted_list.Swap(j-1, j) + j-- + continue + + // Make sure we're only comparing leaders to leaders and non-leaders to non-leaders when sorting + // This means we get leaders sorted first, then non-leaders sorted + // Sort by tier first, higher tiers over lower tiers, and then by name alphabetically + + // Could not think of an elegant way to write this + if(!(current["is_leader"]^prev["is_leader"])\ + && (prev["tier"] < current["tier"]\ + || prev["tier"] == current["tier"] && prev["caste_type"] > current["caste_type"]\ + )) + sorted_list.Swap(j-1, j) - tgui_interact(user) + j-- -/datum/hive_status_ui/tgui_interact(mob/user, datum/tgui/ui) - if(!assoc_hive) + return sorted_list + +// Returns a list with some more info about all xenos in the hive +/datum/hive_status/proc/get_xeno_info() + var/list/xenos = list() + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(is_admin_level(X.z)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + var/xeno_name = X.name + // goddamn fucking larvas with their weird ass maturing system + // its name updates with its icon, unlike other castes which only update the mature/elder, etc. prefix on evolve + if(istype(X, /mob/living/carbon/xenomorph/larva)) + xeno_name = "Larva ([X.nicknumber])" + xenos["[X.nicknumber]"] = list( + "name" = xeno_name, + "strain" = X.mutation_type, + "ref" = "\ref[X]" + ) + + return xenos + +/datum/hive_status/proc/set_hive_location(obj/effect/alien/resin/special/pylon/core/C) + if(!C || C == hive_location) return + var/area/A = get_area(C) + xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as \the [A]."), 3, hivenumber) + hive_location = C + hive_ui.update_hive_location() + +// Returns a list of xeno healths and locations +/datum/hive_status/proc/get_xeno_vitals() + var/list/xenos = list() + + for(var/mob/living/carbon/xenomorph/X in totalXenos) + if(is_admin_level(X.z)) + var/area/A = get_area(X) + if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + continue + + if(!(X in GLOB.living_xeno_list)) + continue + + var/area/A = get_area(X) + var/area_name = "Unknown" + if(A) + area_name = A.name + + xenos["[X.nicknumber]"] = list( + "health" = round((X.health / X.maxHealth) * 100, 1), + "area" = area_name, + "is_ssd" = (!X.client) + ) + + return xenos + +#define TIER_3 "3" +#define TIER_2 "2" +#define OPEN_SLOTS "open_slots" +#define GUARANTEED_SLOTS "guaranteed_slots" + +// Returns an assoc list of open slots and guaranteed slots left +/datum/hive_status/proc/get_tier_slots() + var/list/slots = list( + TIER_3 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + TIER_2 = list( + OPEN_SLOTS = 0, + GUARANTEED_SLOTS = list(), + ), + ) + + var/used_tier_2_slots = length(tier_2_xenos) + var/used_tier_3_slots = length(tier_3_xenos) + + for(var/caste_path in free_slots) + var/slots_free = free_slots[caste_path] + var/slots_used = used_slots[caste_path] + var/datum/caste_datum/current_caste = caste_path + if(slots_used) + // Don't count any free slots in use + switch(initial(current_caste.tier)) + if(2) + used_tier_2_slots -= min(slots_used, slots_free) + if(3) + used_tier_3_slots -= min(slots_used, slots_free) + if(slots_free <= slots_used) + continue + // Display any free slots available + switch(initial(current_caste.tier)) + if(2) + slots[TIER_2][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + if(3) + slots[TIER_3][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used + + var/burrowed_factor = min(stored_larva, sqrt(4*stored_larva)) + var/effective_total = round(burrowed_factor) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + if(xeno.counts_for_slots) + effective_total++ + + // Tier 3 slots are always 20% of the total xenos in the hive + slots[TIER_3][OPEN_SLOTS] = max(0, Ceiling(0.20*effective_total/tier_slot_multiplier) - used_tier_3_slots) + // Tier 2 slots are between 30% and 50% of the hive, depending + // on how many T3s there are. + slots[TIER_2][OPEN_SLOTS] = max(0, Ceiling(0.5*effective_total/tier_slot_multiplier) - used_tier_2_slots - used_tier_3_slots) + + return slots + +#undef TIER_3 +#undef TIER_2 +#undef OPEN_SLOTS +#undef GUARANTEED_SLOTS + +/datum/hive_status/proc/can_build_structure(structure_name) + if(!structure_name || !hive_structures_limit[structure_name]) + return FALSE + var/total_count = 0 + if(hive_structures[structure_name]) + total_count += hive_structures[structure_name].len + if(hive_constructions[structure_name]) + total_count += hive_constructions[structure_name].len + if(total_count >= hive_structures_limit[structure_name]) + return FALSE + return TRUE + +/datum/hive_status/proc/has_structure(structure_name) + if(!structure_name) + return FALSE + if(hive_structures[structure_name] && hive_structures[structure_name].len) + return TRUE + return FALSE + +/datum/hive_status/proc/add_construction(obj/effect/alien/resin/construction/S) + if(!S || !S.template) + return FALSE + var/name_ref = initial(S.template.name) + if(!hive_constructions[name_ref]) + hive_constructions[name_ref] = list() + if(hive_constructions[name_ref].len >= hive_structures_limit[name_ref]) + return FALSE + hive_constructions[name_ref] += src + return TRUE + +/datum/hive_status/proc/remove_construction(obj/effect/alien/resin/construction/S) + if(!S || !S.template) + return FALSE + var/name_ref = initial(S.template.name) + hive_constructions[name_ref] -= src + return TRUE + +/datum/hive_status/proc/add_special_structure(obj/effect/alien/resin/special/S) + if(!S) + return FALSE + var/name_ref = initial(S.name) + if(!hive_structures[name_ref]) + hive_structures[name_ref] = list() + if(hive_structures[name_ref].len >= hive_structures_limit[name_ref]) + return FALSE + hive_structures[name_ref] += S + return TRUE + +/datum/hive_status/proc/remove_special_structure(obj/effect/alien/resin/special/S) + if(!S) + return FALSE + var/name_ref = initial(S.name) + hive_structures[name_ref] -= S + return TRUE + +/datum/hive_status/proc/has_special_structure(name_ref) + if(!name_ref || !hive_structures[name_ref] || !hive_structures[name_ref].len) + return 0 + return hive_structures[name_ref].len - ui = SStgui.try_update_ui(user, src, ui) - if (!ui) - ui = new(user, src, "HiveStatus", "[assoc_hive.name] Status") - ui.open() - ui.set_autoupdate(FALSE) +/datum/hive_status/proc/abandon_on_hijack() + var/area/hijacked_dropship = get_area(living_xeno_queen) + var/shipside_humans_weighted_count = 0 + var/xenos_count = 0 + for(var/name_ref in hive_structures) + for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref]) + if(get_area(S) == hijacked_dropship) + continue + S.hijack_delete = TRUE + hive_structures[name_ref] -= S + qdel(S) + for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) + if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z)) + if(isfacehugger(xeno) || islesserdrone(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + continue + if(xeno.hunter_data.hunted && !isqueen(xeno)) + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, seperating you from her hive! You must defend yourself from the headhunter before you can enter hibernation...")) + xeno.set_hive_and_update(XENO_HIVE_FORSAKEN) + else + to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) + if(xeno.stomach_contents.len) + xeno.devour_timer = 0 + xeno.handle_stomach_contents() + qdel(xeno) + stored_larva++ + continue + if(xeno.tier >= 1) + xenos_count++ + for(var/i in GLOB.alive_mob_list) + var/mob/living/potential_host = i + if(!(potential_host.status_flags & XENO_HOST)) + continue + if(!is_ground_level(potential_host.z) || get_area(potential_host) == hijacked_dropship) + continue + var/obj/item/alien_embryo/A = locate() in potential_host + if(A && A.hivenumber != hivenumber) + continue + for(var/obj/item/alien_embryo/embryo in potential_host) + embryo.hivenumber = XENO_HIVE_FORSAKEN + potential_host.update_med_icon() + for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list) + if(!(isspecieshuman(current_human) || isspeciessynth(current_human))) + continue + var/datum/job/job = RoleAuthority.roles_for_mode[current_human.job] + if(!job) + continue + var/turf/turf = get_turf(current_human) + if(is_mainship_level(turf?.z)) + shipside_humans_weighted_count += RoleAuthority.calculate_role_weight(job) + hijack_burrowed_surge = TRUE + hijack_burrowed_left = max(n_ceil(shipside_humans_weighted_count * 0.5) - xenos_count, 5) + hivecore_cooldown = FALSE + xeno_message(SPAN_XENOBOLDNOTICE("The weeds have recovered! A new hive core can be built!"),3,hivenumber) -/datum/hive_status_ui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) +/datum/hive_status/proc/free_respawn(client/C) + stored_larva++ + if(!hive_location || !hive_location.spawn_burrowed_larva(C.mob)) + stored_larva-- + else + hive_ui.update_burrowed_larva() + +/datum/hive_status/proc/respawn_on_turf(client/xeno_client, turf/spawning_turf) + var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) + if(isnull(new_xeno)) + return FALSE + + if(!SSticker.mode.transfer_xeno(xeno_client.mob, new_xeno)) + qdel(new_xeno) + return FALSE + + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly emerges from a dead husk!"), + SPAN_XENOANNOUNCE("The hive has no core! You manage to emerge from your old husk as a larva!")) + msg_admin_niche("[key_name(new_xeno)] respawned at \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") + playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) + if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xeno.client) + + hive_ui.update_burrowed_larva() + +/datum/hive_status/proc/do_buried_larva_spawn(mob/xeno_candidate) + var/spawning_area + if(hive_location) + spawning_area = hive_location + else if(living_xeno_queen) + spawning_area = living_xeno_queen + else for(var/mob/living/carbon/xenomorpheus as anything in totalXenos) + if(islarva(xenomorpheus) || isxeno_builder(xenomorpheus)) //next to xenos that should be in a safe spot + spawning_area = xenomorpheus + if(!spawning_area) + spawning_area = pick(totalXenos) // FUCK IT JUST GO ANYWHERE + var/list/turf_list + for(var/turf/open/open_turf in orange(3, spawning_area)) + LAZYADD(turf_list, open_turf) + var/turf/open/spawning_turf = pick(turf_list) + + var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) + if(isnull(new_xeno)) + return FALSE + + if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno)) + qdel(new_xeno) + return FALSE + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of \the [spawning_turf]!"), + SPAN_XENODANGER("You burrow out of \the [spawning_turf] and awaken from your slumber. For the Hive!")) + msg_admin_niche("[key_name(new_xeno)] burrowed out from \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") + playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) + to_chat(new_xeno, SPAN_XENOANNOUNCE("You are a xenomorph larva awakened from slumber!")) + if(new_xeno.client) + if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) + window_flash(new_xeno.client) + + stored_larva-- + hive_ui.update_burrowed_larva() + +/mob/living/proc/ally_of_hivenumber(hivenumber) + var/datum/hive_status/indexed_hive = GLOB.hive_datum[hivenumber] + if(!indexed_hive) + return FALSE + + return indexed_hive.is_ally(src) + +/datum/hive_status/proc/is_ally(mob/living/living_mob) + if(isxeno(living_mob)) + var/mob/living/carbon/xenomorph/zenomorf = living_mob + if(zenomorf.hivenumber == hivenumber) + return !zenomorf.banished + + if(!living_mob.faction) + return FALSE + + return faction_is_ally(living_mob.faction) + +/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = FALSE) + if(faction == internal_faction) + return TRUE + if(!ignore_queen_check && !living_xeno_queen) + return FALSE + + return allies[faction] + +/datum/hive_status/proc/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + if(HAS_TRAIT(src, TRAIT_NO_HIVE_DELAY)) + return FALSE + return TRUE + +/datum/hive_status/proc/update_hugger_limit() + var/countable_xeno_iterator = 0 + for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) + if(cycled_xeno.counts_for_slots) + countable_xeno_iterator++ + + playable_hugger_limit = max(Floor(countable_xeno_iterator / playable_hugger_max_divisor), playable_hugger_minimum) + +/datum/hive_status/proc/can_spawn_as_hugger(mob/dead/observer/user) + if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) + return FALSE + if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned + to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) + return FALSE + if(world.time < hugger_timelock) + to_chat(user, SPAN_WARNING("The hive cannot support facehuggers yet...")) + return FALSE + if(world.time - user.timeofdeath < JOIN_AS_FACEHUGGER_DELAY) + var/time_left = round((user.timeofdeath + JOIN_AS_FACEHUGGER_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a facehugger until 3 minutes have passed ([time_left] seconds remaining).")) + return FALSE + if(totalXenos.len <= 0) + //This is to prevent people from joining as Forsaken Huggers on the pred ship + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + for(var/mob_name in banished_ckeys) + if(banished_ckeys[mob_name] == user.ckey) + to_chat(user, SPAN_WARNING("You are banished from the [name], you may not rejoin unless the Queen re-admits you or dies.")) + return FALSE + + update_hugger_limit() + + var/current_hugger_count = 0 + for(var/mob/mob as anything in totalXenos) + if(isfacehugger(mob)) + current_hugger_count++ + if(playable_hugger_limit <= current_hugger_count) + to_chat(user, SPAN_WARNING("\The [GLOB.hive_datum[hivenumber]] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]")) + return FALSE + + if(tgui_alert(user, "Are you sure you want to become a facehugger?", "Confirmation", list("Yes", "No")) != "Yes") + return FALSE + + if(!user.client) + return FALSE + + return TRUE + +/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/A) + var/mob/living/carbon/xenomorph/facehugger/hugger = new /mob/living/carbon/xenomorph/facehugger(A.loc, null, hivenumber) + user.mind.transfer_to(hugger, TRUE) + hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!")) + playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE) + hugger.generate_name() + hugger.timeofdeath = user.timeofdeath // Keep old death time + +/datum/hive_status/proc/update_lesser_drone_limit() + var/countable_xeno_iterator = 0 + for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) + if(cycled_xeno.counts_for_slots) + countable_xeno_iterator++ + + lesser_drone_limit = max(Floor(countable_xeno_iterator / playable_lesser_drones_max_divisor), lesser_drone_minimum) + +/datum/hive_status/proc/can_spawn_as_lesser_drone(mob/dead/observer/user, obj/effect/alien/resin/special/pylon/spawning_pylon) + if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) + return FALSE + + if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned + to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) + return FALSE + + if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY) + var/time_left = round((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10) + to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining).")) + return FALSE + + if(totalXenos.len <= 0) + to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) + return FALSE + + if(!living_xeno_queen) + to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!")) + return FALSE + + if(spawning_pylon.lesser_drone_spawns < 1) + to_chat(user, SPAN_WARNING("The selected core or pylon does not have enough power for a lesser drone!")) + return FALSE + + update_lesser_drone_limit() + + var/current_lesser_drone_count = 0 + for(var/mob/mob as anything in totalXenos) + if(islesserdrone(mob)) + current_lesser_drone_count++ + + if(lesser_drone_limit <= current_lesser_drone_count) + to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]")) + return FALSE + + if(!user.client) + return FALSE + + return TRUE + +///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst +/datum/hive_status/proc/increase_larva_after_burst() + var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst) + partial_larva += extra_per_burst + convert_partial_larva_to_full_larva() + +///Called after times when partial larva are added to process them to stored larva +/datum/hive_status/proc/convert_partial_larva_to_full_larva() + for(var/i = 1 to partial_larva) + partial_larva-- + stored_larva++ + +/datum/hive_status/corrupted + name = "Corrupted Hive" + reporting_id = "corrupted" + hivenumber = XENO_HIVE_CORRUPTED + prefix = "Corrupted " + color = "#80ff80" + ui_color ="#4d994d" + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + + var/list/defectors = list() + +/datum/hive_status/corrupted/add_xeno(mob/living/carbon/xenomorph/xeno) + . = ..() + xeno.add_language(LANGUAGE_ENGLISH) + +/datum/hive_status/corrupted/remove_xeno(mob/living/carbon/xenomorph/xeno, hard) + . = ..() + xeno.remove_language(LANGUAGE_ENGLISH) + +/datum/hive_status/corrupted/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + if(!faction_is_ally(FACTION_MARINE, TRUE)) + return TRUE + return FALSE + +/datum/hive_status/alpha + name = "Alpha Hive" + reporting_id = "alpha" + hivenumber = XENO_HIVE_ALPHA + prefix = "Alpha " + color = "#ff4040" + ui_color = "#992626" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/hive_status/bravo + name = "Bravo Hive" + reporting_id = "bravo" + hivenumber = XENO_HIVE_BRAVO + prefix = "Bravo " + color = "#ffff80" + ui_color = "#99994d" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/hive_status/charlie + name = "Charlie Hive" + reporting_id = "charlie" + hivenumber = XENO_HIVE_CHARLIE + prefix = "Charlie " + color = "#bb40ff" + ui_color = "#702699" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/hive_status/delta + name = "Delta Hive" + reporting_id = "delta" + hivenumber = XENO_HIVE_DELTA + prefix = "Delta " + color = "#8080ff" + ui_color = "#4d4d99" + latejoin_burrowed = FALSE + + dynamic_evolution = FALSE + +/datum/hive_status/feral + name = "Feral Hive" + reporting_id = "feral" + hivenumber = XENO_HIVE_FERAL + prefix = "Feral " + color = "#828296" + ui_color = "#828296" + + construction_allowed = XENO_NOBODY + destruction_allowed = XENO_NOBODY + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + latejoin_burrowed = FALSE + +/datum/hive_status/forsaken + name = "Forsaken Hive" + reporting_id = "forsaken" + hivenumber = XENO_HIVE_FORSAKEN + prefix = "Forsaken " + color = "#cc8ec4" + ui_color = "#cc8ec4" + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + +/datum/hive_status/forsaken/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + return FALSE + +/datum/hive_status/yautja + name = "Hellhound Pack" + reporting_id = "hellhounds" + hivenumber = XENO_HIVE_YAUTJA + internal_faction = FACTION_YAUTJA + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + latejoin_burrowed = FALSE + + need_round_end_check = TRUE + +/datum/hive_status/yautja/can_delay_round_end(mob/living/carbon/xenomorph/xeno) + return FALSE + +/datum/hive_status/mutated + name = "Mutated Hive" + reporting_id = "mutated" + hivenumber = XENO_HIVE_MUTATED + prefix = "Mutated " + color = "#6abd99" + ui_color = "#6abd99" + + hive_inherant_traits = list(TRAIT_XENONID, TRAIT_NO_COLOR) + latejoin_burrowed = FALSE + +/datum/hive_status/corrupted/tamed + name = "Tamed Hive" + reporting_id = "tamed" + hivenumber = XENO_HIVE_TAMED + prefix = "Tamed " + color = "#80ff80" + + dynamic_evolution = FALSE + allow_no_queen_actions = TRUE + allow_no_queen_evo = TRUE + allow_queen_evolve = FALSE + ignore_slots = TRUE + latejoin_burrowed = FALSE + + var/mob/living/carbon/human/leader + var/list/allied_factions + +/datum/hive_status/corrupted/tamed/New() . = ..() - if(.) + hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 + hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 + +/datum/hive_status/corrupted/tamed/proc/make_leader(mob/living/carbon/human/H) + if(!istype(H)) return - switch(action) - if("give_plasma") - var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list - var/mob/living/carbon/xenomorph/xenoSrc = ui.user + if(leader) + UnregisterSignal(leader, COMSIG_PARENT_QDELETING) - if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) - return + leader = H + RegisterSignal(leader, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdelete)) - if(xenoSrc.stat == DEAD) - return +/datum/hive_status/corrupted/tamed/proc/handle_qdelete(mob/living/carbon/human/H) + SIGNAL_HANDLER - var/datum/action/xeno_action/A = get_xeno_action_by_type(xenoSrc, /datum/action/xeno_action/activable/queen_give_plasma) - A?.use_ability_wrapper(xenoTarget) + if(H == leader) + leader = null - if("heal") - var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list - var/mob/living/carbon/xenomorph/xenoSrc = ui.user + var/list/faction_groups = H.faction_group + if(faction_groups) + allied_factions = faction_groups.Copy() + if(!(H.faction in allied_factions)) + allied_factions += H.faction - if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) - return +/datum/hive_status/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/X) + . = ..() + X.faction_group = allied_factions + +/datum/hive_status/corrupted/tamed/remove_xeno(mob/living/carbon/xenomorph/X, hard) + . = ..() + X.faction_group = list(X.faction) - if(xenoSrc.stat == DEAD) - return +/datum/hive_status/corrupted/tamed/is_ally(mob/living/carbon/C) + if(leader) + if(C.faction in leader.faction_group) + return TRUE - var/datum/action/xeno_action/A = get_xeno_action_by_type(xenoSrc, /datum/action/xeno_action/activable/queen_heal) - A?.use_ability_wrapper(xenoTarget, TRUE) + if(C.faction == leader.faction) + return TRUE + else + if(C.faction in allied_factions) + return TRUE + + return ..() - if("overwatch") - var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list - var/mob/living/carbon/xenomorph/xenoSrc = ui.user +/datum/hive_status/corrupted/renegade + name = "Renegade Hive" + reporting_id = "renegade" + hivenumber = XENO_HIVE_RENEGADE + prefix = "Renegade " + color = "#9c7a4d" + ui_color ="#80705c" - if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) - return + dynamic_evolution = FALSE + allow_queen_evolve = FALSE + allow_no_queen_evo = TRUE + latejoin_burrowed = FALSE - if(xenoSrc.stat == DEAD) - if(isobserver(xenoSrc)) - var/mob/dead/observer/O = xenoSrc - O.ManualFollow(xenoTarget) - return +/datum/hive_status/corrupted/renegade/New() + . = ..() + hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 + hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 + for(var/faction in FACTION_LIST_HUMANOID) //renegades allied to all humanoids, but it mostly affects structures. Their ability to attack humanoids and other xenos (including of the same hive) depends on iff settings + allies[faction] = TRUE + +/datum/hive_status/corrupted/renegade/can_spawn_as_hugger(mob/dead/observer/user) + to_chat(user, SPAN_WARNING("The [name] cannot support facehuggers.")) + return FALSE + +/datum/hive_status/corrupted/renegade/proc/iff_protection_check(mob/living/carbon/xenomorph/xeno, mob/living/carbon/attempt_harm_mob) + if(xeno == attempt_harm_mob) + return TRUE //you cannot hurt yourself... + if(!xeno.iff_tag) + return FALSE //can attack anyone if you don't have iff tag + if(isxeno(attempt_harm_mob)) + var/mob/living/carbon/xenomorph/target_xeno = attempt_harm_mob + if(!target_xeno.iff_tag) + return FALSE //can attack any xeno who don't have iff tag + for(var/faction in xeno.iff_tag.faction_groups) + if(faction in target_xeno.iff_tag.faction_groups) + return TRUE //cannot attack xenos with same iff setting + return FALSE + for(var/faction in xeno.iff_tag.faction_groups) + if(faction in attempt_harm_mob.faction_group) + return TRUE //cannot attack mob if iff is set to at least one of its factions + return FALSE + +/datum/hive_status/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE) + return ..() + +/datum/hive_status/proc/on_queen_death() //break alliances on queen's death + if(allow_no_queen_actions || living_xeno_queen) + return + var/broken_alliances = FALSE + for(var/faction in allies) + if(!allies[faction]) + continue + change_stance(faction, FALSE) + broken_alliances = TRUE + + + if(broken_alliances) + xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber) + +/datum/hive_status/proc/change_stance(faction, should_ally) + if(faction == name) + return + if(allies[faction] == should_ally) + return + allies[faction] = should_ally + + if(living_xeno_queen) + if(allies[faction]) + xeno_message(SPAN_XENOANNOUNCE("Your Queen set up an alliance with [faction]!"), 3, hivenumber) + else + xeno_message(SPAN_XENOANNOUNCE("Your Queen broke the alliance with [faction]!"), 3, hivenumber) + + for(var/number in GLOB.hive_datum) + var/datum/hive_status/target_hive = GLOB.hive_datum[number] + if(target_hive.name != faction) + continue + if(!target_hive.living_xeno_queen && !target_hive.allow_no_queen_actions) + return + if(allies[faction]) + xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]set up an alliance with us!"), 3, target_hive.hivenumber) + return + + xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]broke the alliance with us!"), 3, target_hive.hivenumber) + if(target_hive.allies[name]) //autobreak alliance on betrayal + target_hive.change_stance(name, FALSE) + + +/datum/hive_status/corrupted/change_stance(faction, should_ally) + . = ..() + if(allies[faction]) + return + if(!(faction in FACTION_LIST_HUMANOID)) + return + + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) // handle defecting xenos on betrayal + if(!xeno.iff_tag) + continue + if(!(faction in xeno.iff_tag.faction_groups)) + continue + if(xeno in defectors) + continue + if(xeno.caste_type == XENO_CASTE_QUEEN) + continue + INVOKE_ASYNC(src, PROC_REF(give_defection_choice), xeno, faction) + addtimer(CALLBACK(src, PROC_REF(handle_defectors), faction), 11 SECONDS) + +/datum/hive_status/corrupted/proc/give_defection_choice(mob/living/carbon/xenomorph/xeno, faction) + if(tgui_alert(xeno, "Your Queen has broken the alliance with the [faction]. The device inside your carapace begins to suppress your connection with the Hive. Do you remove it and stay loyal to her?", "Alliance broken!", list("Stay loyal", "Obey the talls"), 10 SECONDS) == "Obey the talls") + if(!xeno.iff_tag) + to_chat(xeno, SPAN_XENOWARNING("It's too late now. The device is gone and your service to the Queen continues.")) + return + defectors += xeno + xeno.set_hive_and_update(XENO_HIVE_RENEGADE) + to_chat(xeno, SPAN_XENOANNOUNCE("You lost the connection with your Hive. Now you have no Queen, only your masters.")) + to_chat(xeno, SPAN_NOTICE("Your instincts have changed, you seem compelled to protect [english_list(xeno.iff_tag.faction_groups, "no one")].")) + return + xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the Hive!")) + xeno.adjustBruteLoss(50) + xeno.iff_tag.forceMove(get_turf(xeno)) + xeno.iff_tag = null + +/datum/hive_status/corrupted/proc/handle_defectors(faction) + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) + if(!xeno.iff_tag) + continue + if(xeno in defectors) + continue + if(!(faction in xeno.iff_tag.faction_groups)) + continue + xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the hive!")) + xeno.adjustBruteLoss(50) + xeno.iff_tag.forceMove(get_turf(xeno)) + xeno.iff_tag = null + if(!length(defectors)) + return - if(!xenoSrc.check_state(TRUE)) - return + xeno_message(SPAN_XENOANNOUNCE("You sense that [english_list(defectors)] turned their backs against their sisters and the Queen in favor of their slavemasters!"), 3, hivenumber) + defectors.Cut() - xenoSrc.overwatch(xenoTarget) +//Xeno Resin Mark Shit, the very best place for it too :0) +//Defines at the bottom of this list here will show up at the top in the mark menu diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm b/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm new file mode 100644 index 000000000000..4fe1be51bfff --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/hive_status_ui.dm @@ -0,0 +1,216 @@ +/datum/hive_status_ui + var/name = "Hive Status" + + // Data to pass when rendering the UI (not static) + var/total_xenos + var/list/xeno_counts + var/list/tier_slots + var/list/xeno_vitals + var/list/xeno_keys + var/list/xeno_info + var/hive_location + var/burrowed_larva + var/evilution_level + + var/data_initialized = FALSE + + var/datum/hive_status/assoc_hive = null + +/datum/hive_status_ui/New(datum/hive_status/hive) + assoc_hive = hive + update_all_data() + START_PROCESSING(SShive_status, src) + +/datum/hive_status_ui/process() + update_xeno_vitals() + update_xeno_info(FALSE) + SStgui.update_uis(src) + +// Updates the list tracking how many xenos there are in each tier, and how many there are in total +/datum/hive_status_ui/proc/update_xeno_counts(send_update = TRUE) + xeno_counts = assoc_hive.get_xeno_counts() + + total_xenos = 0 + for(var/counts in xeno_counts) + for(var/caste in counts) + total_xenos += counts[caste] + + if(send_update) + SStgui.update_uis(src) + + xeno_counts[1] -= "Queen" // don't show queen in the amount of xenos + + // Also update the amount of T2/T3 slots + tier_slots = assoc_hive.get_tier_slots() + +// Updates the hive location using the area name of the defined hive location turf +/datum/hive_status_ui/proc/update_hive_location(send_update = TRUE) + if(!assoc_hive.hive_location) + return + + hive_location = strip_improper(get_area_name(assoc_hive.hive_location)) + + if(send_update) + SStgui.update_uis(src) + +// Updates the sorted list of all xenos that we use as a key for all other information +/datum/hive_status_ui/proc/update_xeno_keys(send_update = TRUE) + xeno_keys = assoc_hive.get_xeno_keys() + + if(send_update) + SStgui.update_uis(src) + +// Mildly related to the above, but only for when xenos are removed from the hive +// If a xeno dies, we don't have to regenerate all xeno info and sort it again, just remove them from the data list +/datum/hive_status_ui/proc/xeno_removed(mob/living/carbon/xenomorph/X) + if(!xeno_keys) + return + + for(var/index in 1 to length(xeno_keys)) + var/list/info = xeno_keys[index] + if(info["nicknumber"] == X.nicknumber) + + // tried Remove(), didn't work. *shrug* + xeno_keys[index] = null + xeno_keys -= null + return + + SStgui.update_uis(src) + +// Updates the list of xeno names, strains and references +/datum/hive_status_ui/proc/update_xeno_info(send_update = TRUE) + xeno_info = assoc_hive.get_xeno_info() + + if(send_update) + SStgui.update_uis(src) + +// Updates vital information about xenos such as health and location. Only info that should be updated regularly +/datum/hive_status_ui/proc/update_xeno_vitals() + xeno_vitals = assoc_hive.get_xeno_vitals() + +// Updates how many buried larva there are +/datum/hive_status_ui/proc/update_burrowed_larva(send_update = TRUE) + burrowed_larva = assoc_hive.stored_larva + if(SSxevolution) + evilution_level = SSxevolution.get_evolution_boost_power(assoc_hive.hivenumber) + else + evilution_level = 1 + if(send_update) + SStgui.update_uis(src) + +// Updates all data except burrowed larva +/datum/hive_status_ui/proc/update_all_xeno_data(send_update = TRUE) + update_xeno_counts(FALSE) + update_xeno_vitals() + update_xeno_keys(FALSE) + update_xeno_info(FALSE) + + if(send_update) + SStgui.update_uis(src) + +// Updates all data, including burrowed larva +/datum/hive_status_ui/proc/update_all_data() + data_initialized = TRUE + update_all_xeno_data(FALSE) + update_burrowed_larva(FALSE) + SStgui.update_uis(src) + +/datum/hive_status_ui/ui_state(mob/user) + return GLOB.hive_state[assoc_hive.internal_faction] + +/datum/hive_status_ui/ui_status(mob/user, datum/ui_state/state) + . = ..() + if(isobserver(user)) + return UI_INTERACTIVE + +/datum/hive_status_ui/ui_data(mob/user) + . = list() + .["total_xenos"] = total_xenos + .["xeno_counts"] = xeno_counts + .["tier_slots"] = tier_slots + .["xeno_keys"] = xeno_keys + .["xeno_info"] = xeno_info + .["xeno_vitals"] = xeno_vitals + .["queen_location"] = get_area_name(assoc_hive.living_xeno_queen) + .["hive_location"] = hive_location + .["burrowed_larva"] = burrowed_larva + .["evilution_level"] = evilution_level + + var/mob/living/carbon/xenomorph/queen/Q = user + .["is_in_ovi"] = istype(Q) && Q.ovipositor + +/datum/hive_status_ui/ui_static_data(mob/user) + . = list() + .["user_ref"] = REF(user) + .["hive_color"] = assoc_hive.ui_color + .["hive_name"] = assoc_hive.name + +/datum/hive_status_ui/proc/open_hive_status(mob/user) + if(!user) + return + + // Update absolutely all data + if(!data_initialized) + update_all_data() + + tgui_interact(user) + +/datum/hive_status_ui/tgui_interact(mob/user, datum/tgui/ui) + if(!assoc_hive) + return + + ui = SStgui.try_update_ui(user, src, ui) + if (!ui) + ui = new(user, src, "HiveStatus", "[assoc_hive.name] Status") + ui.open() + ui.set_autoupdate(FALSE) + +/datum/hive_status_ui/ui_act(action, list/params, datum/tgui/ui, datum/ui_state/state) + . = ..() + if(.) + return + + switch(action) + if("give_plasma") + var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenoSrc = ui.user + + if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) + return + + if(xenoSrc.stat == DEAD) + return + + var/datum/action/xeno_action/A = get_xeno_action_by_type(xenoSrc, /datum/action/xeno_action/activable/queen_give_plasma) + A?.use_ability_wrapper(xenoTarget) + + if("heal") + var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenoSrc = ui.user + + if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) + return + + if(xenoSrc.stat == DEAD) + return + + var/datum/action/xeno_action/A = get_xeno_action_by_type(xenoSrc, /datum/action/xeno_action/activable/queen_heal) + A?.use_ability_wrapper(xenoTarget, TRUE) + + if("overwatch") + var/mob/living/carbon/xenomorph/xenoTarget = locate(params["target_ref"]) in GLOB.living_xeno_list + var/mob/living/carbon/xenomorph/xenoSrc = ui.user + + if(QDELETED(xenoTarget) || xenoTarget.stat == DEAD || is_admin_level(xenoTarget.z)) + return + + if(xenoSrc.stat == DEAD) + if(isobserver(xenoSrc)) + var/mob/dead/observer/O = xenoSrc + O.ManualFollow(xenoTarget) + return + + if(!xenoSrc.check_state(TRUE)) + return + + xenoSrc.overwatch(xenoTarget) diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_client_procs.dm b/code/modules/mob/living/carbon/xenomorph/xeno_client_procs.dm new file mode 100644 index 000000000000..0de15315796f --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/xeno_client_procs.dm @@ -0,0 +1,48 @@ +/client/var/cached_xeno_playtime + +/// playtime for all castes +/client/proc/get_total_xeno_playtime(skip_cache = FALSE) + if(cached_xeno_playtime && !skip_cache) + return cached_xeno_playtime + + var/total_xeno_playtime = 0 + + for(var/caste in RoleAuthority.castes_by_name) + total_xeno_playtime += get_job_playtime(src, caste) + + total_xeno_playtime += get_job_playtime(src, JOB_XENOMORPH) + + if(player_entity) + var/past_xeno_playtime = player_entity.get_playtime(STATISTIC_XENO) + if(past_xeno_playtime) + total_xeno_playtime += past_xeno_playtime + + + cached_xeno_playtime = total_xeno_playtime + + return total_xeno_playtime + +/// playtime for drone and drone evolution castes +/client/proc/get_total_drone_playtime() + var/total_drone_playtime = 0 + + var/list/drone_evo_castes = list(XENO_CASTE_DRONE, XENO_CASTE_CARRIER, XENO_CASTE_BURROWER, XENO_CASTE_HIVELORD, XENO_CASTE_QUEEN) + + for(var/caste in RoleAuthority.castes_by_name) + if(!(caste in drone_evo_castes)) + continue + total_drone_playtime += get_job_playtime(src, caste) + + return total_drone_playtime + +/// playtime for t3 castes and queen +/client/proc/get_total_t3_playtime() + var/total_t3_playtime = 0 + var/datum/caste_datum/caste + for(var/caste_name in RoleAuthority.castes_by_name) + caste = RoleAuthority.castes_by_name[caste_name] + if(caste.tier < 3) + continue + total_t3_playtime += get_job_playtime(src, caste_name) + + return total_t3_playtime diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm b/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm deleted file mode 100644 index b4ffe41c3830..000000000000 --- a/code/modules/mob/living/carbon/xenomorph/xeno_defines.dm +++ /dev/null @@ -1,1573 +0,0 @@ -// Actual caste datum basedef -/datum/caste_datum - var/caste_type = "" - var/display_name = "" - var/tier = 0 - var/dead_icon = "Drone Dead" - var/language = LANGUAGE_XENOMORPH - var/melee_damage_lower = 10 - var/melee_damage_upper = 20 - ///allows fine tuning melee damage to vehicles per caste. - var/melee_vehicle_damage = 10 - var/evasion = XENO_EVASION_NONE - - var/speed = XENO_SPEED_TIER_10 - - var/plasma_max = 10 - var/plasma_gain = 5 - - var/crystal_max = 0 - - var/max_health = XENO_UNIVERSAL_HPMULT * 100 - ///Are they allowed to evolve (and have their evolution progress group) - var/evolution_allowed = 1 - ///Threshold to next evolution - var/evolution_threshold = 0 - /// whether they can get evo points without needing an ovi queen - var/evolve_without_queen = FALSE - ///This is where you add castes to evolve into. "Separated", "by", "commas" - var/list/evolves_to = list() - /// what caste or castes to de-evolve to. - var/list/deevolves_to = list() - ///If they can use consoles, etc. Set on Queen - var/is_intelligent = 0 - var/caste_desc = null - - // Tackles - var/tackle_min = 2 - var/tackle_max = 6 - var/tackle_chance = 35 - var/tacklestrength_min = 2 - var/tacklestrength_max = 3 - - ///Chance of deflecting projectiles. - var/armor_deflection = 0 - var/fire_immunity = FIRE_IMMUNITY_NONE - var/fire_intensity_resistance = 0 - - ///Delay timer for spitting - var/spit_delay = 60 - - /// Windup for spits - var/spit_windup = FALSE - - ///The strength of our aura. Zero means we can't emit one - var/aura_strength = 0 - - ///"Evolving" removed for the time being - var/aura_allowed = list("frenzy", "warding", "recovery") - - ///Adjust pixel size. 0.x is smaller, 1.x is bigger, percentage based. - var/adjust_size_x = 1 - var/adjust_size_y = 1 - - ///list of datum projectile types the xeno can use. - var/list/spit_types - - var/attack_delay = 0 //Bonus or pen to time in between attacks. + makes slashes slower. - - var/agility_speed_increase = 0 // this opens up possibilities for balancing - - // The type of mutator delegate to instantiate on the base caste. Will - // be replaced when the Xeno chooses a strain. - var/behavior_delegate_type = /datum/behavior_delegate - - // Resin building-related vars - /// Default build time and build distance - var/build_time_mult = BUILD_TIME_MULT_XENO - var/max_build_dist = 0 - - // Carrier vars // - - /// if a hugger is held in hand, won't attempt to leap and kill itself - var/hugger_nurturing = FALSE - var/huggers_max = 0 - var/throwspeed = 0 - var/hugger_delay = 0 - var/eggs_max = 0 - var/egg_cooldown = 30 - ///Armor but for explosions - var/xeno_explosion_resistance = 0 - - //Queen vars - var/can_hold_facehuggers = 0 - var/can_hold_eggs = CANNOT_HOLD_EGGS - - var/can_be_queen_healed = TRUE - var/can_be_revived = TRUE - - var/can_vent_crawl = 1 - - var/caste_luminosity = 0 - - /// if fire_immunity is set to be vulnerable, how much will fire damage be multiplied. Defines in xeno.dm - var/fire_vulnerability_mult = 0 - - var/burrow_cooldown = 5 SECONDS - var/tunnel_cooldown = 100 - var/widen_cooldown = 10 SECONDS - ///Big strong ability, big cooldown. - var/tremor_cooldown = 30 SECONDS - ///whether the xeno heals even outside weeds. - var/innate_healing = FALSE - - var/acid_level = 0 - var/weed_level = WEED_LEVEL_STANDARD - ///Time it takes between acid splash retaliate procs. Variable per caste, for if we want future castes that are acid bombs - var/acid_splash_cooldown = 3 SECONDS - - // regen vars - - var/heal_delay_time = 0 SECONDS - var/heal_resting = 1 - var/heal_standing = 0.4 - var/heal_knocked_out = 0.33 - - var/list/resin_build_order - var/minimum_xeno_playtime = 0 - -// cannot evolve to this caste until the round has been going on for this amount of time - // IMPORTANT: this is ROUND_TIME, not world.time - var/minimum_evolve_time = 1 MINUTES - /// Iconstate for the xeno on the minimap - var/minimap_icon = "xeno" - ///The iconstate for leadered xenos on the minimap, added as overlay - var/minimap_leadered_overlay = "xenoleader" - - var/royal_caste = FALSE - - -/datum/caste_datum/can_vv_modify() - return FALSE - -/datum/caste_datum/New() - . = ..() - - //Initialise evolution and upgrade thresholds in one place, once and for all - evolution_threshold = 0 - if(evolution_allowed) - switch(tier) - if(0) - evolution_threshold = 60 - if(1) - evolution_threshold = 200 - if(2) - evolution_threshold = 500 - //Other tiers (T3, Queen, etc.) can't evolve anyway - - resin_build_order = GLOB.resin_build_order_drone - -/client/var/cached_xeno_playtime - -/// playtime for all castes -/client/proc/get_total_xeno_playtime(skip_cache = FALSE) - if(cached_xeno_playtime && !skip_cache) - return cached_xeno_playtime - - var/total_xeno_playtime = 0 - - for(var/caste in RoleAuthority.castes_by_name) - total_xeno_playtime += get_job_playtime(src, caste) - - total_xeno_playtime += get_job_playtime(src, JOB_XENOMORPH) - - if(player_entity) - var/past_xeno_playtime = player_entity.get_playtime(STATISTIC_XENO) - if(past_xeno_playtime) - total_xeno_playtime += past_xeno_playtime - - - cached_xeno_playtime = total_xeno_playtime - - return total_xeno_playtime - -/// playtime for drone and drone evolution castes -/client/proc/get_total_drone_playtime() - var/total_drone_playtime = 0 - - var/list/drone_evo_castes = list(XENO_CASTE_DRONE, XENO_CASTE_CARRIER, XENO_CASTE_BURROWER, XENO_CASTE_HIVELORD, XENO_CASTE_QUEEN) - - for(var/caste in RoleAuthority.castes_by_name) - if(!(caste in drone_evo_castes)) - continue - total_drone_playtime += get_job_playtime(src, caste) - - return total_drone_playtime - -/// playtime for t3 castes and queen -/client/proc/get_total_t3_playtime() - var/total_t3_playtime = 0 - var/datum/caste_datum/caste - for(var/caste_name in RoleAuthority.castes_by_name) - caste = RoleAuthority.castes_by_name[caste_name] - if(caste.tier < 3) - continue - total_t3_playtime += get_job_playtime(src, caste_name) - - return total_t3_playtime - -/datum/caste_datum/proc/can_play_caste(client/client) - if(!CONFIG_GET(flag/use_timelocks)) - return TRUE - - var/total_xeno_playtime = client.get_total_xeno_playtime() - - if(minimum_xeno_playtime && total_xeno_playtime < minimum_xeno_playtime) - return FALSE - - return TRUE - -/datum/caste_datum/proc/get_caste_requirement(client/client) - return minimum_xeno_playtime - client.get_total_xeno_playtime() - -/datum/caste_datum/proc/get_minimap_icon() - var/image/background = mutable_appearance('icons/ui_icons/map_blips.dmi', "background") - background.color = MINIMAP_ICON_BACKGROUND_XENO - - var/iconstate = minimap_icon ? minimap_icon : "unknown" - var/mutable_appearance/icon = image('icons/ui_icons/map_blips.dmi', icon_state = iconstate) - icon.appearance_flags = RESET_COLOR - background.overlays += icon - - return background - -/datum/hive_status - var/name = "Normal Hive" - - // Used for the faction of the xenomorph. Not recommended to modify. - var/internal_faction - - /// Short Hive ID as string used in stats reporting - var/reporting_id = "normal" - - var/hivenumber = XENO_HIVE_NORMAL - var/mob/living/carbon/xenomorph/queen/living_xeno_queen - var/egg_planting_range = 15 - var/slashing_allowed = XENO_SLASH_ALLOWED //This initial var allows the queen to turn on or off slashing. Slashing off means harm intent does much less damage. - var/construction_allowed = NORMAL_XENO //Who can place construction nodes for special structures - var/destruction_allowed = XENO_LEADER //Who can destroy special structures - var/unnesting_allowed = TRUE - var/hive_orders = "" //What orders should the hive have - var/color = null - var/ui_color = null // Color for hive status collapsible buttons and xeno count list - var/prefix = "" - var/queen_leader_limit = 2 - var/list/open_xeno_leader_positions = list(1, 2) // Ordered list of xeno leader positions (indexes in xeno_leader_list) that are not occupied - var/list/xeno_leader_list[2] // Ordered list (i.e. index n holds the nth xeno leader) - var/stored_larva = 0 - - ///used by /datum/hive_status/proc/increase_larva_after_burst() to support non-integer increases to larva - var/partial_larva = 0 - /// Assoc list of free slots available to specific castes - var/list/free_slots = list( - /datum/caste_datum/burrower = 1, - /datum/caste_datum/hivelord = 1, - /datum/caste_datum/carrier = 1 - ) - /// Assoc list of slots currently used by specific castes (for calculating free_slot usage) - var/list/used_slots = list() - /// list of living tier2 xenos - var/list/tier_2_xenos = list() - /// list of living tier3 xenos - var/list/tier_3_xenos = list() - /// list of living xenos - var/list/totalXenos = list() - /// list of previously living xenos (hardrefs currently) - var/list/total_dead_xenos = list() - var/xeno_queen_timer - var/isSlotOpen = TRUE //Set true for starting alerts only after the hive has reached its full potential - var/allowed_nest_distance = 15 //How far away do we allow nests from an ovied Queen. Default 15 tiles. - var/obj/effect/alien/resin/special/pylon/core/hive_location = null //Set to ref every time a core is built, for defining the hive location - var/crystal_stored = 0 //How much stockpiled material is stored for the hive to use. - - var/datum/mutator_set/hive_mutators/mutators = new - var/tier_slot_multiplier = 1 - var/larva_gestation_multiplier = 1 - var/bonus_larva_spawn_chance = 1 - var/hijack_burrowed_surge = FALSE //at hijack, start spawning lots of burrowed - /// how many burrowed is going to spawn during larva surge - var/hijack_burrowed_left = 0 - - var/ignore_slots = FALSE - var/dynamic_evolution = TRUE - var/evolution_rate = 3 // Only has use if dynamic_evolution is false - var/evolution_bonus = 0 - - var/allow_no_queen_actions = FALSE - var/allow_no_queen_evo = FALSE - var/evolution_without_ovipositor = TRUE //Temporary for the roundstart. - /// Set to false if you want to prevent evolutions into Queens - var/allow_queen_evolve = TRUE - /// Set to true if you want to prevent bursts and spawns of new xenos. Will also prevent healing if the queen no longer exists - var/hardcore = FALSE - /// Set to false if you want to prevent getting burrowed larva from latejoin marines - var/latejoin_burrowed = TRUE - - var/list/hive_inherant_traits - - // Cultist Info - var/mob/living/carbon/leading_cult_sl - - //List of how many maximum of each special structure you can have - var/list/hive_structures_limit = list( - XENO_STRUCTURE_CORE = 1, - XENO_STRUCTURE_CLUSTER = 8, - XENO_STRUCTURE_POOL = 1, - XENO_STRUCTURE_EGGMORPH = 6, - XENO_STRUCTURE_EVOPOD = 2, - XENO_STRUCTURE_RECOVERY = 6, - XENO_STRUCTURE_PYLON = 2, - ) - - var/global/list/hive_structure_types = list( - XENO_STRUCTURE_CORE = /datum/construction_template/xenomorph/core, - XENO_STRUCTURE_CLUSTER = /datum/construction_template/xenomorph/cluster, - XENO_STRUCTURE_EGGMORPH = /datum/construction_template/xenomorph/eggmorph, - XENO_STRUCTURE_RECOVERY = /datum/construction_template/xenomorph/recovery - ) - - var/list/list/hive_structures = list() //Stringref list of structures that have been built - var/list/list/hive_constructions = list() //Stringref list of structures that are being built - - var/datum/hive_status_ui/hive_ui - var/datum/mark_menu_ui/mark_ui - var/datum/hive_faction_ui/faction_ui - - var/list/tunnels = list() - - var/list/allies = list() - - var/list/resin_marks = list() - - var/list/banished_ckeys = list() - - var/hivecore_cooldown = FALSE - - var/need_round_end_check = FALSE - - //Joining as Facehugger vars - /// When can huggers join the round - var/hugger_timelock = 15 MINUTES - /// How many huggers can the hive support - var/playable_hugger_limit = 0 - /// Minimum number of huggers available at any hive size - var/playable_hugger_minimum = 2 - /// This number divides the total xenos counted for slots to give the max number of facehuggers - var/playable_hugger_max_divisor = 4 - - /// How many lesser drones the hive can support - var/lesser_drone_limit = 0 - /// Slots available for lesser drones will never go below this number - var/lesser_drone_minimum = 2 - /// This number divides the total xenos counted for slots to give the max number of lesser drones - var/playable_lesser_drones_max_divisor = 3 - - var/datum/tacmap/xeno/tacmap - var/minimap_type = MINIMAP_FLAG_XENO - - var/list/available_nicknumbers = list() - -/datum/hive_status/New() - mutators.hive = src - hive_ui = new(src) - mark_ui = new(src) - faction_ui = new(src) - tacmap = new(src, minimap_type) - if(!internal_faction) - internal_faction = name - if(hivenumber != XENO_HIVE_NORMAL) - return - - for(var/number in 1 to 999) - available_nicknumbers += number - - RegisterSignal(SSdcs, COMSIG_GLOB_POST_SETUP, PROC_REF(post_setup)) - -/datum/hive_status/proc/post_setup() - SIGNAL_HANDLER - - setup_evolution_announcements() - setup_pylon_limits() - -/datum/hive_status/proc/setup_evolution_announcements() - for(var/time in GLOB.xeno_evolve_times) - if(time == "0") - continue - - addtimer(CALLBACK(src, PROC_REF(announce_evolve_available), GLOB.xeno_evolve_times[time]), text2num(time)) - -/// Sets up limits on pylons in New() for potential futureproofing with more static comms -/datum/hive_status/proc/setup_pylon_limits() - hive_structures_limit[XENO_STRUCTURE_PYLON] = length(GLOB.all_static_telecomms_towers) || 2 - -/datum/hive_status/proc/announce_evolve_available(list/datum/caste_datum/available_castes) - - var/list/castes_available = list() - for(var/datum/caste_datum/current_caste as anything in available_castes) - castes_available += initial(current_caste.caste_type) - - var/castes = castes_available.Join(", ") - xeno_message(SPAN_XENOANNOUNCE("The Hive is now strong enough to support: [castes]")) - xeno_maptext("The Hive can now support: [castes]", "Hive Strengthening") - - -// Adds a xeno to this hive -/datum/hive_status/proc/add_xeno(mob/living/carbon/xenomorph/X) - if(!X || !istype(X)) - return - - // If the xeno is part of another hive, they should be removed from that one first - if(X.hive && X.hive != src) - X.hive.remove_xeno(X, TRUE) - - // Already in the hive - if(X in totalXenos) - return - - // Can only have one queen. - if(isqueen(X)) - if(!living_xeno_queen && !is_admin_level(X.z)) // Don't consider xenos in admin level - set_living_xeno_queen(X) - - X.hivenumber = hivenumber - X.hive = src - - X.set_faction(internal_faction) - - if(X.hud_list) - X.hud_update() - - var/area/A = get_area(X) - if(!is_admin_level(X.z) || (A.flags_atom & AREA_ALLOW_XENO_JOIN)) - totalXenos += X - if(X.tier == 2) - tier_2_xenos += X - else if(X.tier == 3) - tier_3_xenos += X - - // Xenos are a fuckfest of cross-dependencies of different datums that are initialized at different times - // So don't even bother trying updating UI here without large refactors - -// Removes the xeno from the hive -/datum/hive_status/proc/remove_xeno(mob/living/carbon/xenomorph/xeno, hard = FALSE, light_mode = FALSE) - if(!xeno || !istype(xeno)) - return - - // Make sure the xeno was in the hive in the first place - if(!(xeno in totalXenos)) - return - - // This might be a redundant check now that Queen/Destroy() checks, but doesn't hurt to double check - if(living_xeno_queen == xeno) - var/mob/living/carbon/xenomorph/queen/next_queen = null - for(var/mob/living/carbon/xenomorph/queen/queen in totalXenos) - if(!is_admin_level(queen.z) && queen != src && !QDELETED(queen)) - next_queen = queen - break - - set_living_xeno_queen(next_queen) // either null or a queen - - // We allow "soft" removals from the hive (the xeno still retains information about the hive) - // This is so that xenos can add themselves back to the hive if they should die or otherwise go "on leave" from the hive - if(hard) - xeno.hivenumber = 0 - xeno.hive = null -#ifndef UNIT_TESTS // Since this is a hard ref, we shouldn't confuse create_and_destroy - else - total_dead_xenos += xeno -#endif - - totalXenos -= xeno - if(xeno.tier == 2) - tier_2_xenos -= xeno - else if(xeno.tier == 3) - tier_3_xenos -= xeno - - // Only handle free slots if the xeno is not in tdome - if(!is_admin_level(xeno.z)) - var/selected_caste = GLOB.xeno_datum_list[xeno.caste_type]?.type - if(used_slots[selected_caste]) - used_slots[selected_caste]-- - - if(!light_mode) - hive_ui.update_xeno_counts() - hive_ui.xeno_removed(xeno) - -/datum/hive_status/proc/set_living_xeno_queen(mob/living/carbon/xenomorph/queen/queen) - if(!queen) - mutators.reset_mutators() - SStracking.delete_leader("hive_[hivenumber]") - SStracking.stop_tracking("hive_[hivenumber]", living_xeno_queen) - SShive_status.wait = 10 SECONDS - else - SStracking.set_leader("hive_[hivenumber]", queen) - SShive_status.wait = 2 SECONDS - - SEND_SIGNAL(src, COMSIG_HIVE_NEW_QUEEN, queen) - living_xeno_queen = queen - - recalculate_hive() - -/datum/hive_status/proc/recalculate_hive() - if (!living_xeno_queen) - queen_leader_limit = 0 //No leaders for a Hive without a Queen! - else - queen_leader_limit = 4 + mutators.leader_count_boost - - if (xeno_leader_list.len > queen_leader_limit) - var/diff = 0 - for (var/i in queen_leader_limit + 1 to xeno_leader_list.len) - if(!open_xeno_leader_positions.Remove(i)) - remove_hive_leader(xeno_leader_list[i]) - diff++ - xeno_leader_list.len -= diff // Changing the size of xeno_leader_list needs to go at the end or else it won't iterate through the list properly - else if (xeno_leader_list.len < queen_leader_limit) - for (var/i in xeno_leader_list.len + 1 to queen_leader_limit) - open_xeno_leader_positions += i - xeno_leader_list.len++ - - - tier_slot_multiplier = mutators.tier_slot_multiplier - larva_gestation_multiplier = mutators.larva_gestation_multiplier - bonus_larva_spawn_chance = mutators.bonus_larva_spawn_chance - - hive_ui.update_all_data() - -/datum/hive_status/proc/add_hive_leader(mob/living/carbon/xenomorph/xeno) - if(!xeno) - return FALSE //How did this even happen? - if(!open_xeno_leader_positions.len) - return FALSE //Too many leaders already (no available xeno leader positions) - if(xeno.hive_pos != NORMAL_XENO) - return FALSE //Already on the list - var/leader_num = open_xeno_leader_positions[1] - xeno_leader_list[leader_num] = xeno - xeno.hive_pos = XENO_LEADER_HIVE_POS(leader_num) - xeno.handle_xeno_leader_pheromones() - xeno.hud_update() // To add leader star - open_xeno_leader_positions -= leader_num - - xeno.update_minimap_icon() - - give_action(xeno, /datum/action/xeno_action/activable/info_marker) - - hive_ui.update_xeno_keys() - return TRUE - -/datum/hive_status/proc/remove_hive_leader(mob/living/carbon/xenomorph/xeno, light_mode = FALSE) - if(!istype(xeno) || !IS_XENO_LEADER(xeno)) - return FALSE - - var/leader_num = GET_XENO_LEADER_NUM(xeno) - - xeno_leader_list[leader_num] = null - - if(!light_mode) // Don't run side effects during deletions. Better yet, replace all this by signals someday - xeno.hive_pos = NORMAL_XENO - xeno.handle_xeno_leader_pheromones() - xeno.hud_update() // To remove leader star - - // Need to maintain ascending order of open_xeno_leader_positions - for (var/i in 1 to queen_leader_limit) - if (i > open_xeno_leader_positions.len || open_xeno_leader_positions[i] > leader_num) - open_xeno_leader_positions.Insert(i, leader_num) - break - - if(!light_mode) - hive_ui.update_xeno_keys() - - for(var/obj/effect/alien/resin/marker/leaderless_mark in resin_marks) //no resin_mark limit abuse - if(leaderless_mark.createdby == xeno.nicknumber) - qdel(leaderless_mark) - - xeno.update_minimap_icon() - - remove_action(xeno, /datum/action/xeno_action/activable/info_marker) - - return TRUE - -/datum/hive_status/proc/replace_hive_leader(mob/living/carbon/xenomorph/original, mob/living/carbon/xenomorph/replacement) - if(!replacement || replacement.hive_pos != NORMAL_XENO) - return remove_hive_leader(original) - - var/leader_num = GET_XENO_LEADER_NUM(original) - - xeno_leader_list[leader_num] = replacement - - original.hive_pos = NORMAL_XENO - original.handle_xeno_leader_pheromones() - original.hud_update() // To remove leader star - remove_action(original, /datum/action/xeno_action/activable/info_marker) - - replacement.hive_pos = XENO_LEADER_HIVE_POS(leader_num) - replacement.handle_xeno_leader_pheromones() - replacement.hud_update() // To add leader star - give_action(replacement, /datum/action/xeno_action/activable/info_marker) - - hive_ui.update_xeno_keys() - -/datum/hive_status/proc/handle_xeno_leader_pheromones() - for(var/mob/living/carbon/xenomorph/L in xeno_leader_list) - L.handle_xeno_leader_pheromones() - -/* - * Helper procs for the Hive Status UI - * These are all called by the hive status UI manager to update its data - */ - -// Returns a list of how many of each caste of xeno there are, sorted by tier -/datum/hive_status/proc/get_xeno_counts() - // Every caste is manually defined here so you get - var/list/xeno_counts = list( - // Yes, Queen is technically considered to be tier 0 - list(XENO_CASTE_LARVA = 0, "Queen" = 0), - list(XENO_CASTE_DRONE = 0, XENO_CASTE_RUNNER = 0, XENO_CASTE_SENTINEL = 0, XENO_CASTE_DEFENDER = 0), - list(XENO_CASTE_HIVELORD = 0, XENO_CASTE_BURROWER = 0, XENO_CASTE_CARRIER = 0, XENO_CASTE_LURKER = 0, XENO_CASTE_SPITTER = 0, XENO_CASTE_WARRIOR = 0), - list(XENO_CASTE_BOILER = 0, XENO_CASTE_CRUSHER = 0, XENO_CASTE_PRAETORIAN = 0, XENO_CASTE_RAVAGER = 0) - ) - - for(var/mob/living/carbon/xenomorph/X in totalXenos) - //don't show xenos in the thunderdome when admins test stuff. - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) - continue - - if(X.caste && X.counts_for_slots) - xeno_counts[X.caste.tier+1][X.caste.caste_type]++ - - return xeno_counts - -// Returns a sorted list of some basic info (stuff that's needed for sorting) about all the xenos in the hive -// The idea is that we sort this list, and use it as a "key" for all the other information (especially the nicknumber) -// in the hive status UI. That way we can minimize the amount of sorts performed by only calling this when xenos are created/disposed -/datum/hive_status/proc/get_xeno_keys() - var/list/xenos[totalXenos.len] - - var/index = 1 - var/useless_slots = 0 - for(var/mob/living/carbon/xenomorph/X in totalXenos) - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) - useless_slots++ - continue - - // Insert without doing list merging - xenos[index++] = list( - "nicknumber" = X.nicknumber, - "tier" = X.tier, // This one is only important for sorting - "is_leader" = (IS_XENO_LEADER(X)), - "is_queen" = istype(X.caste, /datum/caste_datum/queen), - "caste_type" = X.caste_type - ) - - // Clear nulls from the xenos list - xenos.len -= useless_slots - - // Make it all nice and fancy by sorting the list before returning it - var/list/sorted_keys = sort_xeno_keys(xenos) - if(length(sorted_keys)) - return sorted_keys - return xenos - -// This sorts the xeno info list by multiple criteria. Prioritized in order: -// 1. Queen -// 2. Leaders -// 3. Tier -// It uses a slightly modified insertion sort to accomplish this -/datum/hive_status/proc/sort_xeno_keys(list/xenos) - if(!length(xenos)) - return - - var/list/sorted_list = xenos.Copy() - - if(!length(sorted_list)) - return - - for(var/index in 2 to length(sorted_list)) - var/j = index - - while(j > 1) - var/current = sorted_list[j] - var/prev = sorted_list[j-1] - - // Queen comes first, always - if(current["is_queen"]) - sorted_list.Swap(j-1, j) - j-- - continue - - // don't muck up queen's slot - if(prev["is_queen"]) - j-- - continue - - // Leaders before normal xenos - if(!prev["is_leader"] && current["is_leader"]) - sorted_list.Swap(j-1, j) - j-- - continue - - // Make sure we're only comparing leaders to leaders and non-leaders to non-leaders when sorting - // This means we get leaders sorted first, then non-leaders sorted - // Sort by tier first, higher tiers over lower tiers, and then by name alphabetically - - // Could not think of an elegant way to write this - if(!(current["is_leader"]^prev["is_leader"])\ - && (prev["tier"] < current["tier"]\ - || prev["tier"] == current["tier"] && prev["caste_type"] > current["caste_type"]\ - )) - sorted_list.Swap(j-1, j) - - j-- - - return sorted_list - -// Returns a list with some more info about all xenos in the hive -/datum/hive_status/proc/get_xeno_info() - var/list/xenos = list() - - for(var/mob/living/carbon/xenomorph/X in totalXenos) - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) - continue - - var/xeno_name = X.name - // goddamn fucking larvas with their weird ass maturing system - // its name updates with its icon, unlike other castes which only update the mature/elder, etc. prefix on evolve - if(istype(X, /mob/living/carbon/xenomorph/larva)) - xeno_name = "Larva ([X.nicknumber])" - xenos["[X.nicknumber]"] = list( - "name" = xeno_name, - "strain" = X.mutation_type, - "ref" = "\ref[X]" - ) - - return xenos - -/datum/hive_status/proc/set_hive_location(obj/effect/alien/resin/special/pylon/core/C) - if(!C || C == hive_location) - return - var/area/A = get_area(C) - xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as \the [A]."), 3, hivenumber) - hive_location = C - hive_ui.update_hive_location() - -// Returns a list of xeno healths and locations -/datum/hive_status/proc/get_xeno_vitals() - var/list/xenos = list() - - for(var/mob/living/carbon/xenomorph/X in totalXenos) - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) - continue - - if(!(X in GLOB.living_xeno_list)) - continue - - var/area/A = get_area(X) - var/area_name = "Unknown" - if(A) - area_name = A.name - - xenos["[X.nicknumber]"] = list( - "health" = round((X.health / X.maxHealth) * 100, 1), - "area" = area_name, - "is_ssd" = (!X.client) - ) - - return xenos - -#define TIER_3 "3" -#define TIER_2 "2" -#define OPEN_SLOTS "open_slots" -#define GUARANTEED_SLOTS "guaranteed_slots" - -// Returns an assoc list of open slots and guaranteed slots left -/datum/hive_status/proc/get_tier_slots() - var/list/slots = list( - TIER_3 = list( - OPEN_SLOTS = 0, - GUARANTEED_SLOTS = list(), - ), - TIER_2 = list( - OPEN_SLOTS = 0, - GUARANTEED_SLOTS = list(), - ), - ) - - var/used_tier_2_slots = length(tier_2_xenos) - var/used_tier_3_slots = length(tier_3_xenos) - - for(var/caste_path in free_slots) - var/slots_free = free_slots[caste_path] - var/slots_used = used_slots[caste_path] - var/datum/caste_datum/current_caste = caste_path - if(slots_used) - // Don't count any free slots in use - switch(initial(current_caste.tier)) - if(2) - used_tier_2_slots -= min(slots_used, slots_free) - if(3) - used_tier_3_slots -= min(slots_used, slots_free) - if(slots_free <= slots_used) - continue - // Display any free slots available - switch(initial(current_caste.tier)) - if(2) - slots[TIER_2][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used - if(3) - slots[TIER_3][GUARANTEED_SLOTS][initial(current_caste.caste_type)] = slots_free - slots_used - - var/burrowed_factor = min(stored_larva, sqrt(4*stored_larva)) - var/effective_total = round(burrowed_factor) - for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) - if(xeno.counts_for_slots) - effective_total++ - - // Tier 3 slots are always 20% of the total xenos in the hive - slots[TIER_3][OPEN_SLOTS] = max(0, Ceiling(0.20*effective_total/tier_slot_multiplier) - used_tier_3_slots) - // Tier 2 slots are between 30% and 50% of the hive, depending - // on how many T3s there are. - slots[TIER_2][OPEN_SLOTS] = max(0, Ceiling(0.5*effective_total/tier_slot_multiplier) - used_tier_2_slots - used_tier_3_slots) - - return slots - -#undef TIER_3 -#undef TIER_2 -#undef OPEN_SLOTS -#undef GUARANTEED_SLOTS - -/datum/hive_status/proc/can_build_structure(structure_name) - if(!structure_name || !hive_structures_limit[structure_name]) - return FALSE - var/total_count = 0 - if(hive_structures[structure_name]) - total_count += hive_structures[structure_name].len - if(hive_constructions[structure_name]) - total_count += hive_constructions[structure_name].len - if(total_count >= hive_structures_limit[structure_name]) - return FALSE - return TRUE - -/datum/hive_status/proc/has_structure(structure_name) - if(!structure_name) - return FALSE - if(hive_structures[structure_name] && hive_structures[structure_name].len) - return TRUE - return FALSE - -/datum/hive_status/proc/add_construction(obj/effect/alien/resin/construction/S) - if(!S || !S.template) - return FALSE - var/name_ref = initial(S.template.name) - if(!hive_constructions[name_ref]) - hive_constructions[name_ref] = list() - if(hive_constructions[name_ref].len >= hive_structures_limit[name_ref]) - return FALSE - hive_constructions[name_ref] += src - return TRUE - -/datum/hive_status/proc/remove_construction(obj/effect/alien/resin/construction/S) - if(!S || !S.template) - return FALSE - var/name_ref = initial(S.template.name) - hive_constructions[name_ref] -= src - return TRUE - -/datum/hive_status/proc/add_special_structure(obj/effect/alien/resin/special/S) - if(!S) - return FALSE - var/name_ref = initial(S.name) - if(!hive_structures[name_ref]) - hive_structures[name_ref] = list() - if(hive_structures[name_ref].len >= hive_structures_limit[name_ref]) - return FALSE - hive_structures[name_ref] += S - return TRUE - -/datum/hive_status/proc/remove_special_structure(obj/effect/alien/resin/special/S) - if(!S) - return FALSE - var/name_ref = initial(S.name) - hive_structures[name_ref] -= S - return TRUE - -/datum/hive_status/proc/has_special_structure(name_ref) - if(!name_ref || !hive_structures[name_ref] || !hive_structures[name_ref].len) - return 0 - return hive_structures[name_ref].len - -/datum/hive_status/proc/abandon_on_hijack() - var/area/hijacked_dropship = get_area(living_xeno_queen) - var/shipside_humans_weighted_count = 0 - var/xenos_count = 0 - for(var/name_ref in hive_structures) - for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref]) - if(get_area(S) == hijacked_dropship) - continue - S.hijack_delete = TRUE - hive_structures[name_ref] -= S - qdel(S) - for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) - if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z)) - if(isfacehugger(xeno) || islesserdrone(xeno)) - to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) - if(xeno.stomach_contents.len) - xeno.devour_timer = 0 - xeno.handle_stomach_contents() - qdel(xeno) - continue - if(xeno.hunter_data.hunted && !isqueen(xeno)) - to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, seperating you from her hive! You must defend yourself from the headhunter before you can enter hibernation...")) - xeno.set_hive_and_update(XENO_HIVE_FORSAKEN) - else - to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) - if(xeno.stomach_contents.len) - xeno.devour_timer = 0 - xeno.handle_stomach_contents() - qdel(xeno) - stored_larva++ - continue - if(xeno.tier >= 1) - xenos_count++ - for(var/i in GLOB.alive_mob_list) - var/mob/living/potential_host = i - if(!(potential_host.status_flags & XENO_HOST)) - continue - if(!is_ground_level(potential_host.z) || get_area(potential_host) == hijacked_dropship) - continue - var/obj/item/alien_embryo/A = locate() in potential_host - if(A && A.hivenumber != hivenumber) - continue - for(var/obj/item/alien_embryo/embryo in potential_host) - embryo.hivenumber = XENO_HIVE_FORSAKEN - potential_host.update_med_icon() - for(var/mob/living/carbon/human/current_human as anything in GLOB.alive_human_list) - if(!(isspecieshuman(current_human) || isspeciessynth(current_human))) - continue - var/datum/job/job = RoleAuthority.roles_for_mode[current_human.job] - if(!job) - continue - var/turf/turf = get_turf(current_human) - if(is_mainship_level(turf?.z)) - shipside_humans_weighted_count += RoleAuthority.calculate_role_weight(job) - hijack_burrowed_surge = TRUE - hijack_burrowed_left = max(n_ceil(shipside_humans_weighted_count * 0.5) - xenos_count, 5) - hivecore_cooldown = FALSE - xeno_message(SPAN_XENOBOLDNOTICE("The weeds have recovered! A new hive core can be built!"),3,hivenumber) - -/datum/hive_status/proc/free_respawn(client/C) - stored_larva++ - if(!hive_location || !hive_location.spawn_burrowed_larva(C.mob)) - stored_larva-- - else - hive_ui.update_burrowed_larva() - -/datum/hive_status/proc/respawn_on_turf(client/xeno_client, turf/spawning_turf) - var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) - if(isnull(new_xeno)) - return FALSE - - if(!SSticker.mode.transfer_xeno(xeno_client.mob, new_xeno)) - qdel(new_xeno) - return FALSE - - new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly emerges from a dead husk!"), - SPAN_XENOANNOUNCE("The hive has no core! You manage to emerge from your old husk as a larva!")) - msg_admin_niche("[key_name(new_xeno)] respawned at \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") - playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) - if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) - window_flash(new_xeno.client) - - hive_ui.update_burrowed_larva() - -/datum/hive_status/proc/do_buried_larva_spawn(mob/xeno_candidate) - var/spawning_area - if(hive_location) - spawning_area = hive_location - else if(living_xeno_queen) - spawning_area = living_xeno_queen - else for(var/mob/living/carbon/xenomorpheus as anything in totalXenos) - if(islarva(xenomorpheus) || isxeno_builder(xenomorpheus)) //next to xenos that should be in a safe spot - spawning_area = xenomorpheus - if(!spawning_area) - spawning_area = pick(totalXenos) // FUCK IT JUST GO ANYWHERE - var/list/turf_list - for(var/turf/open/open_turf in orange(3, spawning_area)) - LAZYADD(turf_list, open_turf) - var/turf/open/spawning_turf = pick(turf_list) - - var/mob/living/carbon/xenomorph/larva/new_xeno = spawn_hivenumber_larva(spawning_turf, hivenumber) - if(isnull(new_xeno)) - return FALSE - - if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno)) - qdel(new_xeno) - return FALSE - new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of \the [spawning_turf]!"), - SPAN_XENODANGER("You burrow out of \the [spawning_turf] and awaken from your slumber. For the Hive!")) - msg_admin_niche("[key_name(new_xeno)] burrowed out from \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") - playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) - to_chat(new_xeno, SPAN_XENOANNOUNCE("You are a xenomorph larva awakened from slumber!")) - if(new_xeno.client) - if(new_xeno.client?.prefs?.toggles_flashing & FLASH_POOLSPAWN) - window_flash(new_xeno.client) - - stored_larva-- - hive_ui.update_burrowed_larva() - -/mob/living/proc/ally_of_hivenumber(hivenumber) - var/datum/hive_status/indexed_hive = GLOB.hive_datum[hivenumber] - if(!indexed_hive) - return FALSE - - return indexed_hive.is_ally(src) - -/datum/hive_status/proc/is_ally(mob/living/living_mob) - if(isxeno(living_mob)) - var/mob/living/carbon/xenomorph/zenomorf = living_mob - if(zenomorf.hivenumber == hivenumber) - return !zenomorf.banished - - if(!living_mob.faction) - return FALSE - - return faction_is_ally(living_mob.faction) - -/datum/hive_status/proc/faction_is_ally(faction, ignore_queen_check = FALSE) - if(faction == internal_faction) - return TRUE - if(!ignore_queen_check && !living_xeno_queen) - return FALSE - - return allies[faction] - -/datum/hive_status/proc/can_delay_round_end(mob/living/carbon/xenomorph/xeno) - if(HAS_TRAIT(src, TRAIT_NO_HIVE_DELAY)) - return FALSE - return TRUE - -/datum/hive_status/proc/update_hugger_limit() - var/countable_xeno_iterator = 0 - for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) - if(cycled_xeno.counts_for_slots) - countable_xeno_iterator++ - - playable_hugger_limit = max(Floor(countable_xeno_iterator / playable_hugger_max_divisor), playable_hugger_minimum) - -/datum/hive_status/proc/can_spawn_as_hugger(mob/dead/observer/user) - if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) - return FALSE - if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned - to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) - return FALSE - if(world.time < hugger_timelock) - to_chat(user, SPAN_WARNING("The hive cannot support facehuggers yet...")) - return FALSE - if(world.time - user.timeofdeath < JOIN_AS_FACEHUGGER_DELAY) - var/time_left = round((user.timeofdeath + JOIN_AS_FACEHUGGER_DELAY - world.time) / 10) - to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a facehugger until 3 minutes have passed ([time_left] seconds remaining).")) - return FALSE - if(totalXenos.len <= 0) - //This is to prevent people from joining as Forsaken Huggers on the pred ship - to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) - return FALSE - for(var/mob_name in banished_ckeys) - if(banished_ckeys[mob_name] == user.ckey) - to_chat(user, SPAN_WARNING("You are banished from the [name], you may not rejoin unless the Queen re-admits you or dies.")) - return FALSE - - update_hugger_limit() - - var/current_hugger_count = 0 - for(var/mob/mob as anything in totalXenos) - if(isfacehugger(mob)) - current_hugger_count++ - if(playable_hugger_limit <= current_hugger_count) - to_chat(user, SPAN_WARNING("\The [GLOB.hive_datum[hivenumber]] cannot support more facehuggers! Limit: [current_hugger_count]/[playable_hugger_limit]")) - return FALSE - - if(tgui_alert(user, "Are you sure you want to become a facehugger?", "Confirmation", list("Yes", "No")) != "Yes") - return FALSE - - if(!user.client) - return FALSE - - return TRUE - -/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/A) - var/mob/living/carbon/xenomorph/facehugger/hugger = new /mob/living/carbon/xenomorph/facehugger(A.loc, null, hivenumber) - user.mind.transfer_to(hugger, TRUE) - hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!")) - playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE) - hugger.generate_name() - hugger.timeofdeath = user.timeofdeath // Keep old death time - -/datum/hive_status/proc/update_lesser_drone_limit() - var/countable_xeno_iterator = 0 - for(var/mob/living/carbon/xenomorph/cycled_xeno as anything in totalXenos) - if(cycled_xeno.counts_for_slots) - countable_xeno_iterator++ - - lesser_drone_limit = max(Floor(countable_xeno_iterator / playable_lesser_drones_max_divisor), lesser_drone_minimum) - -/datum/hive_status/proc/can_spawn_as_lesser_drone(mob/dead/observer/user, obj/effect/alien/resin/special/pylon/spawning_pylon) - if(!GLOB.hive_datum || ! GLOB.hive_datum[hivenumber]) - return FALSE - - if(jobban_isbanned(user, JOB_XENOMORPH)) // User is jobbanned - to_chat(user, SPAN_WARNING("You are banned from playing aliens and cannot spawn as a xenomorph.")) - return FALSE - - if(world.time - user.timeofdeath < JOIN_AS_LESSER_DRONE_DELAY) - var/time_left = round((user.timeofdeath + JOIN_AS_LESSER_DRONE_DELAY - world.time) / 10) - to_chat(user, SPAN_WARNING("You ghosted too recently. You cannot become a lesser drone until 30 seconds have passed ([time_left] seconds remaining).")) - return FALSE - - if(totalXenos.len <= 0) - to_chat(user, SPAN_WARNING("The hive has fallen, you can't join it!")) - return FALSE - - if(!living_xeno_queen) - to_chat(user, SPAN_WARNING("The selected hive does not have a Queen!")) - return FALSE - - if(spawning_pylon.lesser_drone_spawns < 1) - to_chat(user, SPAN_WARNING("The selected core or pylon does not have enough power for a lesser drone!")) - return FALSE - - update_lesser_drone_limit() - - var/current_lesser_drone_count = 0 - for(var/mob/mob as anything in totalXenos) - if(islesserdrone(mob)) - current_lesser_drone_count++ - - if(lesser_drone_limit <= current_lesser_drone_count) - to_chat(user, SPAN_WARNING("[GLOB.hive_datum[hivenumber]] cannot support more lesser drones! Limit: [current_lesser_drone_count]/[lesser_drone_limit]")) - return FALSE - - if(!user.client) - return FALSE - - return TRUE - -///Called by /obj/item/alien_embryo when a host is bursting to determine extra larva per burst -/datum/hive_status/proc/increase_larva_after_burst() - var/extra_per_burst = CONFIG_GET(number/extra_larva_per_burst) - partial_larva += extra_per_burst - convert_partial_larva_to_full_larva() - -///Called after times when partial larva are added to process them to stored larva -/datum/hive_status/proc/convert_partial_larva_to_full_larva() - for(var/i = 1 to partial_larva) - partial_larva-- - stored_larva++ - -/datum/hive_status/corrupted - name = "Corrupted Hive" - reporting_id = "corrupted" - hivenumber = XENO_HIVE_CORRUPTED - prefix = "Corrupted " - color = "#80ff80" - ui_color ="#4d994d" - latejoin_burrowed = FALSE - - need_round_end_check = TRUE - - var/list/defectors = list() - -/datum/hive_status/corrupted/add_xeno(mob/living/carbon/xenomorph/xeno) - . = ..() - xeno.add_language(LANGUAGE_ENGLISH) - -/datum/hive_status/corrupted/remove_xeno(mob/living/carbon/xenomorph/xeno, hard) - . = ..() - xeno.remove_language(LANGUAGE_ENGLISH) - -/datum/hive_status/corrupted/can_delay_round_end(mob/living/carbon/xenomorph/xeno) - if(!faction_is_ally(FACTION_MARINE, TRUE)) - return TRUE - return FALSE - -/datum/hive_status/alpha - name = "Alpha Hive" - reporting_id = "alpha" - hivenumber = XENO_HIVE_ALPHA - prefix = "Alpha " - color = "#ff4040" - ui_color = "#992626" - latejoin_burrowed = FALSE - - dynamic_evolution = FALSE - -/datum/hive_status/bravo - name = "Bravo Hive" - reporting_id = "bravo" - hivenumber = XENO_HIVE_BRAVO - prefix = "Bravo " - color = "#ffff80" - ui_color = "#99994d" - latejoin_burrowed = FALSE - - dynamic_evolution = FALSE - -/datum/hive_status/charlie - name = "Charlie Hive" - reporting_id = "charlie" - hivenumber = XENO_HIVE_CHARLIE - prefix = "Charlie " - color = "#bb40ff" - ui_color = "#702699" - latejoin_burrowed = FALSE - - dynamic_evolution = FALSE - -/datum/hive_status/delta - name = "Delta Hive" - reporting_id = "delta" - hivenumber = XENO_HIVE_DELTA - prefix = "Delta " - color = "#8080ff" - ui_color = "#4d4d99" - latejoin_burrowed = FALSE - - dynamic_evolution = FALSE - -/datum/hive_status/feral - name = "Feral Hive" - reporting_id = "feral" - hivenumber = XENO_HIVE_FERAL - prefix = "Feral " - color = "#828296" - ui_color = "#828296" - - construction_allowed = XENO_NOBODY - destruction_allowed = XENO_NOBODY - dynamic_evolution = FALSE - allow_no_queen_actions = TRUE - allow_no_queen_evo = TRUE - allow_queen_evolve = FALSE - ignore_slots = TRUE - latejoin_burrowed = FALSE - -/datum/hive_status/forsaken - name = "Forsaken Hive" - reporting_id = "forsaken" - hivenumber = XENO_HIVE_FORSAKEN - prefix = "Forsaken " - color = "#cc8ec4" - ui_color = "#cc8ec4" - - dynamic_evolution = FALSE - allow_no_queen_actions = TRUE - allow_no_queen_evo = TRUE - allow_queen_evolve = FALSE - ignore_slots = TRUE - latejoin_burrowed = FALSE - - need_round_end_check = TRUE - -/datum/hive_status/forsaken/can_delay_round_end(mob/living/carbon/xenomorph/xeno) - return FALSE - -/datum/hive_status/yautja - name = "Hellhound Pack" - reporting_id = "hellhounds" - hivenumber = XENO_HIVE_YAUTJA - internal_faction = FACTION_YAUTJA - - dynamic_evolution = FALSE - allow_no_queen_actions = TRUE - allow_no_queen_evo = TRUE - allow_queen_evolve = FALSE - ignore_slots = TRUE - latejoin_burrowed = FALSE - - need_round_end_check = TRUE - -/datum/hive_status/yautja/can_delay_round_end(mob/living/carbon/xenomorph/xeno) - return FALSE - -/datum/hive_status/mutated - name = "Mutated Hive" - reporting_id = "mutated" - hivenumber = XENO_HIVE_MUTATED - prefix = "Mutated " - color = "#6abd99" - ui_color = "#6abd99" - - hive_inherant_traits = list(TRAIT_XENONID, TRAIT_NO_COLOR) - latejoin_burrowed = FALSE - -/datum/hive_status/corrupted/tamed - name = "Tamed Hive" - reporting_id = "tamed" - hivenumber = XENO_HIVE_TAMED - prefix = "Tamed " - color = "#80ff80" - - dynamic_evolution = FALSE - allow_no_queen_actions = TRUE - allow_no_queen_evo = TRUE - allow_queen_evolve = FALSE - ignore_slots = TRUE - latejoin_burrowed = FALSE - - var/mob/living/carbon/human/leader - var/list/allied_factions - -/datum/hive_status/corrupted/tamed/New() - . = ..() - hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 - hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 - -/datum/hive_status/corrupted/tamed/proc/make_leader(mob/living/carbon/human/H) - if(!istype(H)) - return - - if(leader) - UnregisterSignal(leader, COMSIG_PARENT_QDELETING) - - leader = H - RegisterSignal(leader, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdelete)) - -/datum/hive_status/corrupted/tamed/proc/handle_qdelete(mob/living/carbon/human/H) - SIGNAL_HANDLER - - if(H == leader) - leader = null - - var/list/faction_groups = H.faction_group - if(faction_groups) - allied_factions = faction_groups.Copy() - if(!(H.faction in allied_factions)) - allied_factions += H.faction - -/datum/hive_status/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/X) - . = ..() - X.faction_group = allied_factions - -/datum/hive_status/corrupted/tamed/remove_xeno(mob/living/carbon/xenomorph/X, hard) - . = ..() - X.faction_group = list(X.faction) - -/datum/hive_status/corrupted/tamed/is_ally(mob/living/carbon/C) - if(leader) - if(C.faction in leader.faction_group) - return TRUE - - if(C.faction == leader.faction) - return TRUE - else - if(C.faction in allied_factions) - return TRUE - - return ..() - -/datum/hive_status/corrupted/renegade - name = "Renegade Hive" - reporting_id = "renegade" - hivenumber = XENO_HIVE_RENEGADE - prefix = "Renegade " - color = "#9c7a4d" - ui_color ="#80705c" - - dynamic_evolution = FALSE - allow_queen_evolve = FALSE - allow_no_queen_evo = TRUE - latejoin_burrowed = FALSE - -/datum/hive_status/corrupted/renegade/New() - . = ..() - hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 - hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 - for(var/faction in FACTION_LIST_HUMANOID) //renegades allied to all humanoids, but it mostly affects structures. Their ability to attack humanoids and other xenos (including of the same hive) depends on iff settings - allies[faction] = TRUE - -/datum/hive_status/corrupted/renegade/can_spawn_as_hugger(mob/dead/observer/user) - to_chat(user, SPAN_WARNING("The [name] cannot support facehuggers.")) - return FALSE - -/datum/hive_status/corrupted/renegade/proc/iff_protection_check(mob/living/carbon/xenomorph/xeno, mob/living/carbon/attempt_harm_mob) - if(xeno == attempt_harm_mob) - return TRUE //you cannot hurt yourself... - if(!xeno.iff_tag) - return FALSE //can attack anyone if you don't have iff tag - if(isxeno(attempt_harm_mob)) - var/mob/living/carbon/xenomorph/target_xeno = attempt_harm_mob - if(!target_xeno.iff_tag) - return FALSE //can attack any xeno who don't have iff tag - for(var/faction in xeno.iff_tag.faction_groups) - if(faction in target_xeno.iff_tag.faction_groups) - return TRUE //cannot attack xenos with same iff setting - return FALSE - for(var/faction in xeno.iff_tag.faction_groups) - if(faction in attempt_harm_mob.faction_group) - return TRUE //cannot attack mob if iff is set to at least one of its factions - return FALSE - -/datum/hive_status/corrupted/renegade/faction_is_ally(faction, ignore_queen_check = TRUE) - return ..() - -/datum/hive_status/proc/on_queen_death() //break alliances on queen's death - if(allow_no_queen_actions || living_xeno_queen) - return - var/broken_alliances = FALSE - for(var/faction in allies) - if(!allies[faction]) - continue - change_stance(faction, FALSE) - broken_alliances = TRUE - - - if(broken_alliances) - xeno_message(SPAN_XENOANNOUNCE("With the death of the Queen, all alliances have been broken."), 3, hivenumber) - -/datum/hive_status/proc/change_stance(faction, should_ally) - if(faction == name) - return - if(allies[faction] == should_ally) - return - allies[faction] = should_ally - - if(living_xeno_queen) - if(allies[faction]) - xeno_message(SPAN_XENOANNOUNCE("Your Queen set up an alliance with [faction]!"), 3, hivenumber) - else - xeno_message(SPAN_XENOANNOUNCE("Your Queen broke the alliance with [faction]!"), 3, hivenumber) - - for(var/number in GLOB.hive_datum) - var/datum/hive_status/target_hive = GLOB.hive_datum[number] - if(target_hive.name != faction) - continue - if(!target_hive.living_xeno_queen && !target_hive.allow_no_queen_actions) - return - if(allies[faction]) - xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]set up an alliance with us!"), 3, target_hive.hivenumber) - return - - xeno_message(SPAN_XENOANNOUNCE("You sense that [name] [living_xeno_queen ? "Queen " : ""]broke the alliance with us!"), 3, target_hive.hivenumber) - if(target_hive.allies[name]) //autobreak alliance on betrayal - target_hive.change_stance(name, FALSE) - - -/datum/hive_status/corrupted/change_stance(faction, should_ally) - . = ..() - if(allies[faction]) - return - if(!(faction in FACTION_LIST_HUMANOID)) - return - - for(var/mob/living/carbon/xenomorph/xeno in totalXenos) // handle defecting xenos on betrayal - if(!xeno.iff_tag) - continue - if(!(faction in xeno.iff_tag.faction_groups)) - continue - if(xeno in defectors) - continue - if(xeno.caste_type == XENO_CASTE_QUEEN) - continue - INVOKE_ASYNC(src, PROC_REF(give_defection_choice), xeno, faction) - addtimer(CALLBACK(src, PROC_REF(handle_defectors), faction), 11 SECONDS) - -/datum/hive_status/corrupted/proc/give_defection_choice(mob/living/carbon/xenomorph/xeno, faction) - if(tgui_alert(xeno, "Your Queen has broken the alliance with the [faction]. The device inside your carapace begins to suppress your connection with the Hive. Do you remove it and stay loyal to her?", "Alliance broken!", list("Stay loyal", "Obey the talls"), 10 SECONDS) == "Obey the talls") - if(!xeno.iff_tag) - to_chat(xeno, SPAN_XENOWARNING("It's too late now. The device is gone and your service to the Queen continues.")) - return - defectors += xeno - xeno.set_hive_and_update(XENO_HIVE_RENEGADE) - to_chat(xeno, SPAN_XENOANNOUNCE("You lost the connection with your Hive. Now you have no Queen, only your masters.")) - to_chat(xeno, SPAN_NOTICE("Your instincts have changed, you seem compelled to protect [english_list(xeno.iff_tag.faction_groups, "no one")].")) - return - xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the Hive!")) - xeno.adjustBruteLoss(50) - xeno.iff_tag.forceMove(get_turf(xeno)) - xeno.iff_tag = null - -/datum/hive_status/corrupted/proc/handle_defectors(faction) - for(var/mob/living/carbon/xenomorph/xeno in totalXenos) - if(!xeno.iff_tag) - continue - if(xeno in defectors) - continue - if(!(faction in xeno.iff_tag.faction_groups)) - continue - xeno.visible_message(SPAN_XENOWARNING("[xeno] rips out [xeno.iff_tag]!"), SPAN_XENOWARNING("You rip out [xeno.iff_tag]! For the hive!")) - xeno.adjustBruteLoss(50) - xeno.iff_tag.forceMove(get_turf(xeno)) - xeno.iff_tag = null - if(!length(defectors)) - return - - xeno_message(SPAN_XENOANNOUNCE("You sense that [english_list(defectors)] turned their backs against their sisters and the Queen in favor of their slavemasters!"), 3, hivenumber) - defectors.Cut() - -//Xeno Resin Mark Shit, the very best place for it too :0) -//Defines at the bottom of this list here will show up at the top in the mark menu -/datum/xeno_mark_define - var/name = "xeno_declare" - var/icon_state = "empty" - var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to eachother" - -/datum/xeno_mark_define/fortify - name = "Fortify" - desc = "Fortify this area!" - icon_state = "fortify" - -/datum/xeno_mark_define/weeds - name = "Need Weeds" - desc = "Need weeds here!" - icon_state = "weed" - -/datum/xeno_mark_define/nest - name = "Nest" - desc = "Nest enemies here!" - icon_state = "nest" - -/datum/xeno_mark_define/hosts - name = "Hosts" - desc = "Hosts here!" - icon_state = "hosts" - -/datum/xeno_mark_define/aide - name = "Aide" - desc = "Aide here!" - icon_state = "aide" - -/datum/xeno_mark_define/defend - name = "Defend" - desc = "Defend the hive here!" - icon_state = "defend" - -/datum/xeno_mark_define/danger - name = "Danger Warning" - desc = "Caution, danger here!" - icon_state = "danger" - -/datum/xeno_mark_define/rally - name = "Rally" - desc = "Group up here!" - icon_state = "rally" - -/datum/xeno_mark_define/hold - name = "Hold" - desc = "Hold this area!" - icon_state = "hold" - -/datum/xeno_mark_define/ambush - name = "Ambush" - desc = "Ambush the enemy here!" - icon_state = "ambush" -/datum/xeno_mark_define/attack - name = "Attack" - desc = "Attack the enemy here!" - icon_state = "attack" - - - diff --git a/code/modules/mob/living/carbon/xenomorph/xeno_marks.dm b/code/modules/mob/living/carbon/xenomorph/xeno_marks.dm new file mode 100644 index 000000000000..e3f168f26de0 --- /dev/null +++ b/code/modules/mob/living/carbon/xenomorph/xeno_marks.dm @@ -0,0 +1,60 @@ +// Xeno mark defines + +/datum/xeno_mark_define + var/name = "xeno_declare" + var/icon_state = "empty" + var/desc = "Xenos make psychic markers with this meaning as positional lasting communication to eachother" + +/datum/xeno_mark_define/fortify + name = "Fortify" + desc = "Fortify this area!" + icon_state = "fortify" + +/datum/xeno_mark_define/weeds + name = "Need Weeds" + desc = "Need weeds here!" + icon_state = "weed" + +/datum/xeno_mark_define/nest + name = "Nest" + desc = "Nest enemies here!" + icon_state = "nest" + +/datum/xeno_mark_define/hosts + name = "Hosts" + desc = "Hosts here!" + icon_state = "hosts" + +/datum/xeno_mark_define/aide + name = "Aide" + desc = "Aide here!" + icon_state = "aide" + +/datum/xeno_mark_define/defend + name = "Defend" + desc = "Defend the hive here!" + icon_state = "defend" + +/datum/xeno_mark_define/danger + name = "Danger Warning" + desc = "Caution, danger here!" + icon_state = "danger" + +/datum/xeno_mark_define/rally + name = "Rally" + desc = "Group up here!" + icon_state = "rally" + +/datum/xeno_mark_define/hold + name = "Hold" + desc = "Hold this area!" + icon_state = "hold" + +/datum/xeno_mark_define/ambush + name = "Ambush" + desc = "Ambush the enemy here!" + icon_state = "ambush" +/datum/xeno_mark_define/attack + name = "Attack" + desc = "Attack the enemy here!" + icon_state = "attack" diff --git a/colonialmarines.dme b/colonialmarines.dme index 9ef2ad37c605..ce2c1f713810 100644 --- a/colonialmarines.dme +++ b/colonialmarines.dme @@ -116,7 +116,7 @@ #include "code\__DEFINES\vv.dm" #include "code\__DEFINES\weapon_stats.dm" #include "code\__DEFINES\weather.dm" -#include "code\__DEFINES\xeno.dm" +#include "code\__DEFINES\xeno_defines.dm" #include "code\__DEFINES\dcs\flags.dm" #include "code\__DEFINES\dcs\helpers.dm" #include "code\__DEFINES\dcs\signals\signals_admin.dm" @@ -1913,6 +1913,7 @@ #include "code\modules\mob\living\carbon\xenomorph\Facehuggers.dm" #include "code\modules\mob\living\carbon\xenomorph\hive_faction.dm" #include "code\modules\mob\living\carbon\xenomorph\hive_status.dm" +#include "code\modules\mob\living\carbon\xenomorph\hive_status_ui.dm" #include "code\modules\mob\living\carbon\xenomorph\life.dm" #include "code\modules\mob\living\carbon\xenomorph\login.dm" #include "code\modules\mob\living\carbon\xenomorph\mark_menu.dm" @@ -1920,8 +1921,9 @@ #include "code\modules\mob\living\carbon\xenomorph\resin_constructions.dm" #include "code\modules\mob\living\carbon\xenomorph\say.dm" #include "code\modules\mob\living\carbon\xenomorph\update_icons.dm" -#include "code\modules\mob\living\carbon\xenomorph\xeno_defines.dm" +#include "code\modules\mob\living\carbon\xenomorph\xeno_client_procs.dm" #include "code\modules\mob\living\carbon\xenomorph\xeno_helpers.dm" +#include "code\modules\mob\living\carbon\xenomorph\xeno_marks.dm" #include "code\modules\mob\living\carbon\xenomorph\xeno_tackle_counter.dm" #include "code\modules\mob\living\carbon\xenomorph\xeno_verbs.dm" #include "code\modules\mob\living\carbon\xenomorph\XenoAttacks.dm" @@ -1987,6 +1989,7 @@ #include "code\modules\mob\living\carbon\xenomorph\castes\Boiler.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Burrower.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Carrier.dm" +#include "code\modules\mob\living\carbon\xenomorph\castes\caste_datum.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Crusher.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Defender.dm" #include "code\modules\mob\living\carbon\xenomorph\castes\Drone.dm" From 959257cbd1e263bb34b866fddfeb31680e6e83d8 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 23:09:00 +0000 Subject: [PATCH 06/17] moves some defines back to mobs.dm --- code/__DEFINES/mobs.dm | 7 +++++++ code/__DEFINES/xeno_defines.dm | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 8b6b58f584f5..3417d3624419 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -117,6 +117,13 @@ #define CANSLOW (1<<19) #define NO_PERMANENT_DAMAGE (1<<20) +// ============================= +// slowdowns +#define XENO_SLOWED_AMOUNT 0.7 +#define XENO_SUPERSLOWED_AMOUNT 1.5 +#define HUMAN_SLOWED_AMOUNT 2 +#define HUMAN_SUPERSLOWED_AMOUNT 4 + // Adds onto HUMAN_*****_AMOUNT #define YAUTJA_SLOWED_AMOUNT -1.25 // 0.75s slowdown #define YAUTJA_SUPERSLOWED_AMOUNT -3 // 1s slowdown diff --git a/code/__DEFINES/xeno_defines.dm b/code/__DEFINES/xeno_defines.dm index 9134759bca0b..47fdee20be66 100644 --- a/code/__DEFINES/xeno_defines.dm +++ b/code/__DEFINES/xeno_defines.dm @@ -29,13 +29,6 @@ //================================================= -// ============================= -// slowdowns -#define XENO_SLOWED_AMOUNT 0.7 -#define XENO_SUPERSLOWED_AMOUNT 1.5 -#define HUMAN_SLOWED_AMOUNT 2 -#define HUMAN_SUPERSLOWED_AMOUNT 4 - #define XENOCON_THRESHOLD 6000 #define TUNNEL_MOVEMENT_XENO_DELAY 20 From 4eadf0d4a6e108e774f948bebffa58cb520d5ec6 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Wed, 15 Nov 2023 23:41:33 +0000 Subject: [PATCH 07/17] fixes runtimes --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 9d26b24b2e18..eeff954adeb4 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -350,8 +350,13 @@ /mob/living/carbon/xenomorph/Initialize(mapload, mob/living/carbon/xenomorph/old_xeno, hivenumber) var/datum/hive_status/hive - if(hivenumber) + if(old_xeno && old_xeno.hivenumber) + hive = GLOB.hive_datum[old_xeno.hivenumber] + else if(hivenumber) hive = GLOB.hive_datum[hivenumber] + else + CRASH("Tried to create a xenomorph [src] with no hive datum.") + if(hive) hive.add_xeno(src) @@ -379,7 +384,7 @@ set_movement_intent(old_xeno.m_intent) a_intent_change(old_xeno.a_intent) if(src.client) - a_select_zone(old_xeno.zone_selected, src.client) + src.a_select_zone(old_xeno.zone_selected, src.client) // BIRD: fix this //We are hiding, let's keep hiding if we can! if(old_xeno.layer == XENO_HIDING_LAYER) From 0040681f1451303deca644c54f14d25a838addf6 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 18:50:37 +0000 Subject: [PATCH 08/17] move name updating on evo to life() --- .../modules/mob/living/carbon/xenomorph/Xenomorph.dm | 2 +- .../mob/living/carbon/xenomorph/castes/Larva.dm | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index eeff954adeb4..5bb19e9b5640 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -1097,7 +1097,7 @@ return var/datum/hive_status/hive_status = hive if(length(hive_status.available_nicknumbers)) - nicknumber = pick(hive_status.available_nicknumbers) + nicknumber = pick_n_take(hive_status.available_nicknumbers) else //If we somehow use all 999 numbers fallback on 0 nicknumber = 0 diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index cf85a85b2a36..06fe98ac4fa3 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -50,10 +50,17 @@ var/burrowable = TRUE //Can it be safely burrowed if it has no player? var/state_override + var/is_bloody = TRUE //We're still "bloody" icon_xeno = 'icons/mob/xenos/larva.dmi' icon_xenonid = 'icons/mob/xenonids/larva.dmi' +/mob/living/carbon/xenomorph/larva/Life() + if(bloody && (evolution_stored >= evolution_threshold / 2)) //We're still bloody + generate_name() + is_bloody = FALSE + return ..() + /mob/living/carbon/xenomorph/larva/initialize_pass_flags(datum/pass_flags_container/PF) ..() if (PF) @@ -155,7 +162,10 @@ /mob/living/carbon/xenomorph/larva/is_xeno_grabbable() return TRUE -///Larva name generation, set nickname = (number between 1 & 999) which isn't taken by any other xenos in GLOB.xeno_mob_list +/* +Larva name generation, set nicknumber = (number between 1 & 999) which isn't taken by any other xenos in GLOB.xeno_mob_list if doesn't already exist. +Also handles the "Mature / Bloody naming convention. Call this to update the name." +*/ /mob/living/carbon/xenomorph/larva/generate_name() if(!nicknumber) generate_and_set_nicknumber() From 98075f33700a2f92a4a94720fe83480db84e019b Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 21:12:51 +0000 Subject: [PATCH 09/17] silly funny 666 joke --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 7 ++++--- code/modules/mob/living/carbon/xenomorph/castes/Larva.dm | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 5bb19e9b5640..8f3764d353fe 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -383,8 +383,6 @@ //Carry over intents & targeted limb to the new Xeno set_movement_intent(old_xeno.m_intent) a_intent_change(old_xeno.a_intent) - if(src.client) - src.a_select_zone(old_xeno.zone_selected, src.client) // BIRD: fix this //We are hiding, let's keep hiding if we can! if(old_xeno.layer == XENO_HIDING_LAYER) @@ -571,6 +569,7 @@ var/name_prefix = in_hive.prefix var/name_client_prefix = "" var/name_client_postfix = "" + var/number_decorator = "" if(client) name_client_prefix = "[(client.xeno_prefix||client.xeno_postfix) ? client.xeno_prefix : "XX"]-" name_client_postfix = client.xeno_postfix ? ("-"+client.xeno_postfix) : "" @@ -581,9 +580,11 @@ var/age_display = show_age_prefix ? age_prefix : "" var/name_display = "" + if(nicknumber == 666) + number_decorator = "Infernal " if(show_name_numbers) name_display = show_only_numbers ? " ([nicknumber])" : " ([name_client_prefix][nicknumber][name_client_postfix])" - name = "[name_prefix][age_display][caste.display_name || caste.caste_type][name_display]" + name = "[name_prefix][number_decorator][age_display][caste.display_name || caste.caste_type][name_display]" //Update linked data so they show up properly change_real_name(src, name) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index 06fe98ac4fa3..77be178bdae9 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -56,7 +56,7 @@ icon_xenonid = 'icons/mob/xenonids/larva.dmi' /mob/living/carbon/xenomorph/larva/Life() - if(bloody && (evolution_stored >= evolution_threshold / 2)) //We're still bloody + if(is_bloody && (evolution_stored >= evolution_threshold / 2)) //We're no longer bloody so update our name... generate_name() is_bloody = FALSE return ..() From 2bc6a51c5a6e336cbf4332c41d784f014eab0bd9 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 21:28:07 +0000 Subject: [PATCH 10/17] single letter var treatment --- .../mob/living/carbon/xenomorph/Xenomorph.dm | 83 ++++++++----------- .../living/carbon/xenomorph/castes/Larva.dm | 18 ++-- 2 files changed, 44 insertions(+), 57 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 8f3764d353fe..5c5b7c29b5b1 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -360,10 +360,6 @@ if(hive) hive.add_xeno(src) - var/area/current_area = get_area(src) - if(current_area && current_area.statistic_exempt) - statistic_exempt = TRUE - wound_icon_holder = new(null, src) vis_contents += wound_icon_holder @@ -493,6 +489,10 @@ var/selected_caste = GLOB.xeno_datum_list[caste_type]?.type hive.used_slots[selected_caste]++ + //Statistics + var/area/current_area = get_area(src) + if(current_area && current_area.statistic_exempt) + statistic_exempt = TRUE if(round_statistics && !statistic_exempt) round_statistics.track_new_participant(faction, 1) @@ -516,11 +516,11 @@ return SSminimaps.add_marker(src, z, hud_flags = flags, given_image = caste.get_minimap_icon()) -/mob/living/carbon/xenomorph/initialize_pass_flags(datum/pass_flags_container/PF) +/mob/living/carbon/xenomorph/initialize_pass_flags(datum/pass_flags_container/pass_flags) ..() - if (PF) - PF.flags_pass = PASS_MOB_IS_XENO - PF.flags_can_pass_all = PASS_MOB_THRU_XENO|PASS_AROUND|PASS_HIGH_OVER_ONLY + if (pass_flags) + pass_flags.flags_pass = PASS_MOB_IS_XENO + pass_flags.flags_can_pass_all = PASS_MOB_THRU_XENO|PASS_AROUND|PASS_HIGH_OVER_ONLY /mob/living/carbon/xenomorph/initialize_pain() pain = new /datum/pain/xeno(src) @@ -528,18 +528,18 @@ /mob/living/carbon/xenomorph/initialize_stamina() stamina = new /datum/stamina/none(src) -/mob/living/carbon/xenomorph/proc/fire_immune(mob/living/L) +/mob/living/carbon/xenomorph/proc/fire_immune(mob/living/living_mob) SIGNAL_HANDLER - if(L.fire_reagent?.fire_penetrating && !HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) + if(living_mob.fire_reagent?.fire_penetrating && !HAS_TRAIT(src, TRAIT_ABILITY_BURROWED)) return return COMPONENT_CANCEL_IGNITION -/mob/living/carbon/xenomorph/proc/flamer_crossed_immune(mob/living/L, datum/reagent/R) +/mob/living/carbon/xenomorph/proc/flamer_crossed_immune(mob/living/living_mob, datum/reagent/reagent) SIGNAL_HANDLER - if(R.fire_penetrating) + if(reagent.fire_penetrating) return . = COMPONENT_NO_BURN @@ -564,8 +564,6 @@ //Im putting this in here, because this proc gets called when a player inhabits a SSD xeno and it needs to go somewhere (sorry) hud_set_marks() - //handle_name(in_hive) -> look at queen handle_name() also - var/name_prefix = in_hive.prefix var/name_client_prefix = "" var/name_client_postfix = "" @@ -580,6 +578,7 @@ var/age_display = show_age_prefix ? age_prefix : "" var/name_display = "" + // Rare easter egg if(nicknumber == 666) number_decorator = "Infernal " if(show_name_numbers) @@ -713,44 +712,37 @@ if(hardcore) attack_log?.Cut() // Completely clear out attack_log to limit mem usage if we fail to delete - . = ..() - - // Everything below fits the "we have to clear by principle it but i dont wanna break stuff" bill - mutators = null - - + return ..() /mob/living/carbon/xenomorph/slip(slip_source_name, stun_level, weaken_level, run_only, override_noslip, slide_steps) return FALSE - - -/mob/living/carbon/xenomorph/start_pulling(atom/movable/AM, lunge, no_msg) +/mob/living/carbon/xenomorph/start_pulling(atom/movable/movable_atom, lunge, no_msg) if(SEND_SIGNAL(AM, COMSIG_MOVABLE_XENO_START_PULLING, src) & COMPONENT_ALLOW_PULL) - return do_pull(AM, lunge, no_msg) + return do_pull(movable_atom, lunge, no_msg) if(HAS_TRAIT(src,TRAIT_ABILITY_BURROWED)) return - if(!isliving(AM)) + if(!isliving(movable_atom)) return FALSE - var/mob/living/L = AM - if(issynth(L) && L.health < 0) // no pulling critted or dead synths + var/mob/living/living_mob = movable_atom + if(issynth(living_mob) && living_mob.health < 0) // no pulling critted or dead synths return FALSE - if(L.buckled) + if(living_mob.buckled) return FALSE //to stop xeno from pulling marines on roller beds. - if(!L.is_xeno_grabbable()) + if(!living_mob.is_xeno_grabbable()) return FALSE - var/atom/A = AM.handle_barriers(src) - if(A != AM) - A.attack_alien(src) + var/atom/atom = movable_atom.handle_barriers(src) + if(atom != movable_atom) + atom.attack_alien(src) xeno_attack_delay(src) return FALSE return ..() /mob/living/carbon/xenomorph/pull_response(mob/puller) if(stat != DEAD && has_species(puller,"Human")) // If the Xeno is alive, fight back against a grab/pull - var/mob/living/carbon/human/H = puller - if(H.ally_of_hivenumber(hivenumber)) + var/mob/living/carbon/human/human = puller + if(human.ally_of_hivenumber(hivenumber)) return TRUE puller.apply_effect(rand(caste.tacklestrength_min,caste.tacklestrength_max), WEAKEN) playsound(puller.loc, 'sound/weapons/pierce.ogg', 25, 1) @@ -766,8 +758,6 @@ pulledby.stop_pulling() . = 1 - - /mob/living/carbon/xenomorph/prepare_huds() ..() //updating all the mob's hud images @@ -777,12 +767,10 @@ hud_set_pheromone() hud_set_marks() - //and display them add_to_all_mob_huds() - var/datum/mob_hud/MH = huds[MOB_HUD_XENO_INFECTION] - MH.add_hud_to(src, src) - + var/datum/mob_hud/mob_hud = huds[MOB_HUD_XENO_INFECTION] + mob_hud.add_hud_to(src, src) /mob/living/carbon/xenomorph/check_improved_pointing() //xeno leaders get a big arrow and less cooldown @@ -841,7 +829,6 @@ if(hive && hive.living_xeno_queen && hive.living_xeno_queen == src) hive.recalculate_hive() //Recalculating stuff around Queen maturing - /mob/living/carbon/xenomorph/proc/recalculate_stats() recalculate_health() recalculate_plasma() @@ -1006,14 +993,14 @@ var/displaytime = max(1, round(breakouttime / 600)) //Minutes to_chat(src, SPAN_WARNING("You attempt to remove [legcuffed]. (This will take around [displaytime] minute(s) and you need to stand still)")) - for(var/mob/O in viewers(src)) - O.show_message(SPAN_DANGER("[usr] attempts to remove [legcuffed]!"), SHOW_MESSAGE_VISIBLE) + for(var/mob/viewer in viewers(src)) + viewer.show_message(SPAN_DANGER("[usr] attempts to remove [legcuffed]!"), SHOW_MESSAGE_VISIBLE) if(!do_after(src, breakouttime, INTERRUPT_NO_NEEDHAND^INTERRUPT_RESIST, BUSY_ICON_HOSTILE)) return if(!legcuffed || buckled) return // time leniency for lag which also might make this whole thing pointless but the server - for(var/mob/O in viewers(src))// lags so hard that 40s isn't lenient enough - Quarxink - O.show_message(SPAN_DANGER("[src] manages to remove [legcuffed]!"), SHOW_MESSAGE_VISIBLE) + for(var/mob/viewer in viewers(src))// lags so hard that 40s isn't lenient enough - Quarxink + viewer.show_message(SPAN_DANGER("[src] manages to remove [legcuffed]!"), SHOW_MESSAGE_VISIBLE) to_chat(src, SPAN_NOTICE(" You successfully remove [legcuffed].")) drop_inv_item_on_ground(legcuffed) @@ -1055,9 +1042,9 @@ /mob/living/carbon/xenomorph/handle_blood_splatter(splatter_dir, duration) var/color_override if(special_blood) - var/datum/reagent/D = chemical_reagents_list[special_blood] - if(D) - color_override = D.color + var/datum/reagent/reagent_datum = chemical_reagents_list[special_blood] + if(reagent_datum) + color_override = reagent_datum.color new /obj/effect/temp_visual/dir_setting/bloodsplatter/xenosplatter(loc, splatter_dir, duration, color_override) /mob/living/carbon/xenomorph/Collide(atom/movable/movable_atom) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index 77be178bdae9..50645bd9e859 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -61,11 +61,11 @@ is_bloody = FALSE return ..() -/mob/living/carbon/xenomorph/larva/initialize_pass_flags(datum/pass_flags_container/PF) +/mob/living/carbon/xenomorph/larva/initialize_pass_flags(datum/pass_flags_container/pass_flags) ..() - if (PF) - PF.flags_pass = PASS_MOB_THRU|PASS_FLAGS_CRAWLER - PF.flags_can_pass_all = PASS_ALL^PASS_OVER_THROW_ITEM + if (pass_flags) + pass_flags.flags_pass = PASS_MOB_THRU|PASS_FLAGS_CRAWLER + pass_flags.flags_can_pass_all = PASS_ALL^PASS_OVER_THROW_ITEM /mob/living/carbon/xenomorph/larva/corrupted hivenumber = XENO_HIVE_CORRUPTED @@ -146,15 +146,15 @@ A.attack_larva(src) xeno_attack_delay(src) //Adds some lag to the 'attack' -/proc/spawn_hivenumber_larva(atom/A, hivenumber) - if(!GLOB.hive_datum[hivenumber] || isnull(A)) +/proc/spawn_hivenumber_larva(atom/atom, hivenumber) + if(!GLOB.hive_datum[hivenumber] || isnull(atom)) return - var/mob/living/carbon/xenomorph/larva/L = new /mob/living/carbon/xenomorph/larva(A) + var/mob/living/carbon/xenomorph/larva/larva = new /mob/living/carbon/xenomorph/larva(A) - L.set_hive_and_update(hivenumber) + larva.set_hive_and_update(hivenumber) - return L + return larva /mob/living/carbon/xenomorph/larva/emote(act, m_type, message, intentional, force_silence) playsound(loc, "alien_roar_larva", 15) From 90ff0dc2b8945924f0d92cc28e98e71d00184725 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 21:32:55 +0000 Subject: [PATCH 11/17] starts on hive_status.dm --- .../living/carbon/xenomorph/hive_status.dm | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index 8adb23fd8390..18f85434e804 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -179,38 +179,38 @@ // Adds a xeno to this hive -/datum/hive_status/proc/add_xeno(mob/living/carbon/xenomorph/X) - if(!X || !istype(X)) +/datum/hive_status/proc/add_xeno(mob/living/carbon/xenomorph/xeno) + if(!xeno || !istype(xeno)) return // If the xeno is part of another hive, they should be removed from that one first - if(X.hive && X.hive != src) - X.hive.remove_xeno(X, TRUE) + if(xeno.hive && xeno.hive != src) + xeno.hive.remove_xeno(xeno, TRUE) // Already in the hive - if(X in totalXenos) + if(xeno in totalXenos) return // Can only have one queen. - if(isqueen(X)) - if(!living_xeno_queen && !is_admin_level(X.z)) // Don't consider xenos in admin level - set_living_xeno_queen(X) + if(isqueen(xeno)) + if(!living_xeno_queen && !is_admin_level(xeno.z)) // Don't consider xenos in admin level + set_living_xeno_queen(xeno) - X.hivenumber = hivenumber - X.hive = src + xeno.hivenumber = hivenumber + xeno.hive = src - X.set_faction(internal_faction) + xeno.set_faction(internal_faction) - if(X.hud_list) - X.hud_update() + if(xeno.hud_list) + xeno.hud_update() - var/area/A = get_area(X) - if(!is_admin_level(X.z) || (A.flags_atom & AREA_ALLOW_XENO_JOIN)) - totalXenos += X - if(X.tier == 2) - tier_2_xenos += X - else if(X.tier == 3) - tier_3_xenos += X + var/area/area = get_area(xeno) + if(!is_admin_level(xeno.z) || (area.flags_atom & AREA_ALLOW_XENO_JOIN)) + totalXenos += xeno + if(xeno.tier == 2) + tier_2_xenos += xeno + else if(xeno.tier == 3) + tier_3_xenos += xeno // Xenos are a fuckfest of cross-dependencies of different datums that are initialized at different times // So don't even bother trying updating UI here without large refactors @@ -374,8 +374,8 @@ hive_ui.update_xeno_keys() /datum/hive_status/proc/handle_xeno_leader_pheromones() - for(var/mob/living/carbon/xenomorph/L in xeno_leader_list) - L.handle_xeno_leader_pheromones() + for(var/mob/living/carbon/xenomorph/xeno_leader in xeno_leader_list) + xeno_leader.handle_xeno_leader_pheromones() /* * Helper procs for the Hive Status UI From 631e24cbb94beea170fe72f9e1974766bd769d0d Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 22:03:30 +0000 Subject: [PATCH 12/17] some missed single letter vars --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 2 +- code/modules/mob/living/carbon/xenomorph/castes/Larva.dm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index 5c5b7c29b5b1..b423b90179f5 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -718,7 +718,7 @@ return FALSE /mob/living/carbon/xenomorph/start_pulling(atom/movable/movable_atom, lunge, no_msg) - if(SEND_SIGNAL(AM, COMSIG_MOVABLE_XENO_START_PULLING, src) & COMPONENT_ALLOW_PULL) + if(SEND_SIGNAL(movable_atom, COMSIG_MOVABLE_XENO_START_PULLING, src) & COMPONENT_ALLOW_PULL) return do_pull(movable_atom, lunge, no_msg) if(HAS_TRAIT(src,TRAIT_ABILITY_BURROWED)) diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm index 50645bd9e859..a5dc98524c85 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Larva.dm @@ -135,7 +135,7 @@ /mob/living/carbon/xenomorph/larva/pull_response(mob/puller) return TRUE -/mob/living/carbon/xenomorph/larva/UnarmedAttack(atom/A, proximity, click_parameters, tile_attack, ignores_resin = FALSE) +/mob/living/carbon/xenomorph/larva/UnarmedAttack(atom/atom, proximity, click_parameters, tile_attack, ignores_resin = FALSE) a_intent = INTENT_HELP //Forces help intent for all interactions. if(!caste) return FALSE @@ -143,14 +143,14 @@ if(lying) //No attacks while laying down return FALSE - A.attack_larva(src) + atom.attack_larva(src) xeno_attack_delay(src) //Adds some lag to the 'attack' /proc/spawn_hivenumber_larva(atom/atom, hivenumber) if(!GLOB.hive_datum[hivenumber] || isnull(atom)) return - var/mob/living/carbon/xenomorph/larva/larva = new /mob/living/carbon/xenomorph/larva(A) + var/mob/living/carbon/xenomorph/larva/larva = new /mob/living/carbon/xenomorph/larva(atom) larva.set_hive_and_update(hivenumber) From a73d18647b0c960611c09a47dd14b184eeb135b3 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 22:23:04 +0000 Subject: [PATCH 13/17] addresses unit tests --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index b423b90179f5..ccd919654d36 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -354,8 +354,6 @@ hive = GLOB.hive_datum[old_xeno.hivenumber] else if(hivenumber) hive = GLOB.hive_datum[hivenumber] - else - CRASH("Tried to create a xenomorph [src] with no hive datum.") if(hive) hive.add_xeno(src) @@ -393,7 +391,7 @@ old_xeno.empty_gut() //Set leader to the new mob - if(IS_XENO_LEADER(old_xeno)) + if(hive && IS_XENO_LEADER(old_xeno)) hive.replace_hive_leader(old_xeno, src) if(old_xeno.iff_tag) @@ -401,8 +399,9 @@ iff_tag.forceMove(src) old_xeno.iff_tag = null - for(var/trait in hive.hive_inherant_traits) - ADD_TRAIT(src, trait, TRAIT_SOURCE_HIVE) + if(hive) + for(var/trait in hive.hive_inherant_traits) + ADD_TRAIT(src, trait, TRAIT_SOURCE_HIVE) mutators.xeno = src @@ -485,7 +484,7 @@ lighting_alpha = LIGHTING_PLANE_ALPHA_MOSTLY_INVISIBLE // Only handle free slots if the xeno is not in tdome - if(!is_admin_level(z)) + if(hive && !is_admin_level(z)) var/selected_caste = GLOB.xeno_datum_list[caste_type]?.type hive.used_slots[selected_caste]++ From 1e891fecfc22b980b3725a2998f89fa6a5ec0fa9 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Thu, 16 Nov 2023 23:16:16 +0000 Subject: [PATCH 14/17] bugfixes --- code/modules/mob/living/carbon/xenomorph/Xenomorph.dm | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm index ccd919654d36..3a9ce03d34e9 100644 --- a/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm +++ b/code/modules/mob/living/carbon/xenomorph/Xenomorph.dm @@ -349,11 +349,12 @@ /mob/living/carbon/xenomorph/Initialize(mapload, mob/living/carbon/xenomorph/old_xeno, hivenumber) - var/datum/hive_status/hive if(old_xeno && old_xeno.hivenumber) - hive = GLOB.hive_datum[old_xeno.hivenumber] + src.hivenumber = old_xeno.hivenumber else if(hivenumber) - hive = GLOB.hive_datum[hivenumber] + src.hivenumber = hivenumber + + var/datum/hive_status/hive = GLOB.hive_datum[src.hivenumber] if(hive) hive.add_xeno(src) @@ -365,7 +366,6 @@ ///Handle transferring things from the old Xeno if we have one in the case of evolve, devolve etc. if(old_xeno) - src.hivenumber = old_xeno.hivenumber src.nicknumber = old_xeno.nicknumber src.life_kills_total = old_xeno.life_kills_total src.life_damage_taken_total = old_xeno.life_damage_taken_total From 7900c8a2ff8a0358ee865e5f588d278a9d1652b0 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Sat, 18 Nov 2023 09:36:40 +0000 Subject: [PATCH 15/17] some more single letter vars in hive_status.dm --- .../living/carbon/xenomorph/hive_status.dm | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index 18f85434e804..4592ae94fe98 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -495,55 +495,55 @@ /datum/hive_status/proc/get_xeno_info() var/list/xenos = list() - for(var/mob/living/carbon/xenomorph/X in totalXenos) - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) + if(is_admin_level(xeno.z)) + var/area/area = get_area(xeno) + if(!(area.flags_atom & AREA_ALLOW_XENO_JOIN)) continue - var/xeno_name = X.name + var/xeno_name = xeno.name // goddamn fucking larvas with their weird ass maturing system // its name updates with its icon, unlike other castes which only update the mature/elder, etc. prefix on evolve - if(istype(X, /mob/living/carbon/xenomorph/larva)) - xeno_name = "Larva ([X.nicknumber])" - xenos["[X.nicknumber]"] = list( + if(istype(xeno, /mob/living/carbon/xenomorph/larva)) + xeno_name = "Larva ([xeno.nicknumber])" + xenos["[xeno.nicknumber]"] = list( "name" = xeno_name, - "strain" = X.mutation_type, - "ref" = "\ref[X]" + "strain" = xeno.mutation_type, + "ref" = "\ref[xeno]" ) return xenos -/datum/hive_status/proc/set_hive_location(obj/effect/alien/resin/special/pylon/core/C) - if(!C || C == hive_location) +/datum/hive_status/proc/set_hive_location(obj/effect/alien/resin/special/pylon/core/hive_core) + if(!hive_core || hive_core == hive_location) return - var/area/A = get_area(C) - xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as \the [A]."), 3, hivenumber) - hive_location = C + var/area/area = get_area(hive_core) + xeno_message(SPAN_XENOANNOUNCE("The Queen has set the hive location as [area]."), 3, hivenumber) + hive_location = hive_core hive_ui.update_hive_location() // Returns a list of xeno healths and locations /datum/hive_status/proc/get_xeno_vitals() var/list/xenos = list() - for(var/mob/living/carbon/xenomorph/X in totalXenos) - if(is_admin_level(X.z)) - var/area/A = get_area(X) - if(!(A.flags_atom & AREA_ALLOW_XENO_JOIN)) + for(var/mob/living/carbon/xenomorph/xeno in totalXenos) + if(is_admin_level(xeno.z)) + var/area/area = get_area(xeno) + if(!(area.flags_atom & AREA_ALLOW_XENO_JOIN)) continue - if(!(X in GLOB.living_xeno_list)) + if(!(xeno in GLOB.living_xeno_list)) continue - var/area/A = get_area(X) + var/area/area = get_area(xeno) var/area_name = "Unknown" - if(A) - area_name = A.name + if(area) + area_name = area.name - xenos["[X.nicknumber]"] = list( - "health" = round((X.health / X.maxHealth) * 100, 1), + xenos["[xeno.nicknumber]"] = list( + "health" = round((xeno.health / xeno.maxHealth) * 100, 1), "area" = area_name, - "is_ssd" = (!X.client) + "is_ssd" = (!xeno.client) ) return xenos @@ -623,14 +623,14 @@ /datum/hive_status/proc/has_structure(structure_name) if(!structure_name) return FALSE - if(hive_structures[structure_name] && hive_structures[structure_name].len) + if(hive_structures[structure_name] && length(hive_structures[structure_name])) return TRUE return FALSE -/datum/hive_status/proc/add_construction(obj/effect/alien/resin/construction/S) - if(!S || !S.template) +/datum/hive_status/proc/add_construction(obj/effect/alien/resin/construction/construction) + if(!construction || !construction.template) return FALSE - var/name_ref = initial(S.template.name) + var/name_ref = initial(construction.template.name) if(!hive_constructions[name_ref]) hive_constructions[name_ref] = list() if(hive_constructions[name_ref].len >= hive_structures_limit[name_ref]) @@ -638,52 +638,52 @@ hive_constructions[name_ref] += src return TRUE -/datum/hive_status/proc/remove_construction(obj/effect/alien/resin/construction/S) - if(!S || !S.template) +/datum/hive_status/proc/remove_construction(obj/effect/alien/resin/construction/construction) + if(!construction || !construction.template) return FALSE - var/name_ref = initial(S.template.name) + var/name_ref = initial(construction.template.name) hive_constructions[name_ref] -= src return TRUE -/datum/hive_status/proc/add_special_structure(obj/effect/alien/resin/special/S) - if(!S) +/datum/hive_status/proc/add_special_structure(obj/effect/alien/resin/special/construction) + if(!construction) return FALSE - var/name_ref = initial(S.name) + var/name_ref = initial(construction.name) if(!hive_structures[name_ref]) hive_structures[name_ref] = list() - if(hive_structures[name_ref].len >= hive_structures_limit[name_ref]) + if(length(hive_structures[name_ref]) >= hive_structures_limit[name_ref]) return FALSE - hive_structures[name_ref] += S + hive_structures[name_ref] += construction return TRUE -/datum/hive_status/proc/remove_special_structure(obj/effect/alien/resin/special/S) - if(!S) +/datum/hive_status/proc/remove_special_structure(obj/effect/alien/resin/special/construction) + if(!construction) return FALSE - var/name_ref = initial(S.name) - hive_structures[name_ref] -= S + var/name_ref = initial(construction.name) + hive_structures[name_ref] -= construction return TRUE /datum/hive_status/proc/has_special_structure(name_ref) - if(!name_ref || !hive_structures[name_ref] || !hive_structures[name_ref].len) + if(!name_ref || !hive_structures[name_ref] || length(!hive_structures[name_ref])) return 0 - return hive_structures[name_ref].len + return length(hive_structures[name_ref]) /datum/hive_status/proc/abandon_on_hijack() var/area/hijacked_dropship = get_area(living_xeno_queen) var/shipside_humans_weighted_count = 0 var/xenos_count = 0 for(var/name_ref in hive_structures) - for(var/obj/effect/alien/resin/special/S in hive_structures[name_ref]) - if(get_area(S) == hijacked_dropship) + for(var/obj/effect/alien/resin/special/construction in hive_structures[name_ref]) + if(get_area(construction) == hijacked_dropship) continue - S.hijack_delete = TRUE - hive_structures[name_ref] -= S - qdel(S) + construction.hijack_delete = TRUE + hive_structures[name_ref] -= construction + qdel(construction) for(var/mob/living/carbon/xenomorph/xeno as anything in totalXenos) if(get_area(xeno) != hijacked_dropship && xeno.loc && is_ground_level(xeno.loc.z)) if(isfacehugger(xeno) || islesserdrone(xeno)) to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) - if(xeno.stomach_contents.len) + if(length(xeno.stomach_contents)) xeno.devour_timer = 0 xeno.handle_stomach_contents() qdel(xeno) @@ -693,7 +693,7 @@ xeno.set_hive_and_update(XENO_HIVE_FORSAKEN) else to_chat(xeno, SPAN_XENOANNOUNCE("The Queen has left without you, you quickly find a hiding place to enter hibernation as you lose touch with the hive mind.")) - if(xeno.stomach_contents.len) + if(length(xeno.stomach_contents)) xeno.devour_timer = 0 xeno.handle_stomach_contents() qdel(xeno) @@ -701,14 +701,14 @@ continue if(xeno.tier >= 1) xenos_count++ - for(var/i in GLOB.alive_mob_list) - var/mob/living/potential_host = i + for(var/alive_mob in GLOB.alive_mob_list) + var/mob/living/potential_host = alive_mob if(!(potential_host.status_flags & XENO_HOST)) continue if(!is_ground_level(potential_host.z) || get_area(potential_host) == hijacked_dropship) continue - var/obj/item/alien_embryo/A = locate() in potential_host - if(A && A.hivenumber != hivenumber) + var/obj/item/alien_embryo/alien_embryo = locate() in potential_host + if(alien_embryo && alien_embryo.hivenumber != hivenumber) continue for(var/obj/item/alien_embryo/embryo in potential_host) embryo.hivenumber = XENO_HIVE_FORSAKEN @@ -727,9 +727,9 @@ hivecore_cooldown = FALSE xeno_message(SPAN_XENOBOLDNOTICE("The weeds have recovered! A new hive core can be built!"),3,hivenumber) -/datum/hive_status/proc/free_respawn(client/C) +/datum/hive_status/proc/free_respawn(client/client) stored_larva++ - if(!hive_location || !hive_location.spawn_burrowed_larva(C.mob)) + if(!hive_location || !hive_location.spawn_burrowed_larva(client.mob)) stored_larva-- else hive_ui.update_burrowed_larva() @@ -775,8 +775,8 @@ if(!SSticker.mode.transfer_xeno(xeno_candidate, new_xeno)) qdel(new_xeno) return FALSE - new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of \the [spawning_turf]!"), - SPAN_XENODANGER("You burrow out of \the [spawning_turf] and awaken from your slumber. For the Hive!")) + new_xeno.visible_message(SPAN_XENODANGER("A larva suddenly burrows out of [spawning_turf]!"), + SPAN_XENODANGER("You burrow out of [spawning_turf] and awaken from your slumber. For the Hive!")) msg_admin_niche("[key_name(new_xeno)] burrowed out from \a [spawning_turf]. [ADMIN_JMP(spawning_turf)]") playsound(new_xeno, 'sound/effects/xeno_newlarva.ogg', 50, 1) to_chat(new_xeno, SPAN_XENOANNOUNCE("You are a xenomorph larva awakened from slumber!")) @@ -796,9 +796,9 @@ /datum/hive_status/proc/is_ally(mob/living/living_mob) if(isxeno(living_mob)) - var/mob/living/carbon/xenomorph/zenomorf = living_mob - if(zenomorf.hivenumber == hivenumber) - return !zenomorf.banished + var/mob/living/carbon/xenomorph/xeno = living_mob + if(xeno.hivenumber == hivenumber) + return !xeno.banished if(!living_mob.faction) return FALSE From 5e9a56a58e2044c4cf88e68c18ce933df8ad654d Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Sat, 18 Nov 2023 12:49:44 +0000 Subject: [PATCH 16/17] more single letter vars in hive_status.dm --- .../living/carbon/xenomorph/hive_status.dm | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index 4592ae94fe98..4a3c06819c26 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -866,10 +866,10 @@ return TRUE -/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/A) - var/mob/living/carbon/xenomorph/facehugger/hugger = new /mob/living/carbon/xenomorph/facehugger(A.loc, null, hivenumber) +/datum/hive_status/proc/spawn_as_hugger(mob/dead/observer/user, atom/atom) + var/mob/living/carbon/xenomorph/facehugger/hugger = new /mob/living/carbon/xenomorph/facehugger(atom.loc, null, hivenumber) user.mind.transfer_to(hugger, TRUE) - hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of \the [A]!"), SPAN_XENODANGER("You emerge out of \the [A] and awaken from your slumber. For the Hive!")) + hugger.visible_message(SPAN_XENODANGER("A facehugger suddenly emerges out of [atom]!"), SPAN_XENODANGER("You emerge out of [atom] and awaken from your slumber. For the Hive!")) playsound(hugger, 'sound/effects/xeno_newlarva.ogg', 25, TRUE) hugger.generate_name() hugger.timeofdeath = user.timeofdeath // Keep old death time @@ -1093,45 +1093,45 @@ hive_structures_limit[XENO_STRUCTURE_EGGMORPH] = 0 hive_structures_limit[XENO_STRUCTURE_EVOPOD] = 0 -/datum/hive_status/corrupted/tamed/proc/make_leader(mob/living/carbon/human/H) - if(!istype(H)) +/datum/hive_status/corrupted/tamed/proc/make_leader(mob/living/carbon/human/human) + if(!istype(human)) return if(leader) UnregisterSignal(leader, COMSIG_PARENT_QDELETING) - leader = H + leader = human RegisterSignal(leader, COMSIG_PARENT_QDELETING, PROC_REF(handle_qdelete)) -/datum/hive_status/corrupted/tamed/proc/handle_qdelete(mob/living/carbon/human/H) +/datum/hive_status/corrupted/tamed/proc/handle_qdelete(mob/living/carbon/human/human) SIGNAL_HANDLER - if(H == leader) + if(human == leader) leader = null - var/list/faction_groups = H.faction_group + var/list/faction_groups = human.faction_group if(faction_groups) allied_factions = faction_groups.Copy() if(!(H.faction in allied_factions)) allied_factions += H.faction -/datum/hive_status/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/X) +/datum/hive_status/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/xeno) . = ..() - X.faction_group = allied_factions + xeno.faction_group = allied_factions -/datum/hive_status/corrupted/tamed/remove_xeno(mob/living/carbon/xenomorph/X, hard) +/datum/hive_status/corrupted/tamed/remove_xeno(mob/living/carbon/xenomorph/xeno, hard) . = ..() - X.faction_group = list(X.faction) + xeno.faction_group = list(xeno.faction) -/datum/hive_status/corrupted/tamed/is_ally(mob/living/carbon/C) +/datum/hive_status/corrupted/tamed/is_ally(mob/living/carbon/carbon) if(leader) - if(C.faction in leader.faction_group) + if(carbon.faction in leader.faction_group) return TRUE - if(C.faction == leader.faction) + if(carbon.faction == leader.faction) return TRUE else - if(C.faction in allied_factions) + if(carbon.faction in allied_factions) return TRUE return ..() From ea2fa5a05fb313082067b29a9934fcb4b78658c9 Mon Sep 17 00:00:00 2001 From: Birdtalon Date: Sat, 18 Nov 2023 13:10:34 +0000 Subject: [PATCH 17/17] missed var --- code/modules/mob/living/carbon/xenomorph/hive_status.dm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index ee79c685f00a..48a54bf96f89 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -1113,8 +1113,8 @@ var/list/faction_groups = human.faction_group if(faction_groups) allied_factions = faction_groups.Copy() - if(!(H.faction in allied_factions)) - allied_factions += H.faction + if(!(human.faction in allied_factions)) + allied_factions += human.faction /datum/hive_status/corrupted/tamed/add_xeno(mob/living/carbon/xenomorph/xeno) . = ..()