diff --git a/code/__DEFINES/xeno.dm b/code/__DEFINES/xeno.dm index 8e7fb2af647b..612d9f1f392b 100644 --- a/code/__DEFINES/xeno.dm +++ b/code/__DEFINES/xeno.dm @@ -773,7 +773,7 @@ #define FRENZY_DAMAGE_MULTIPLIER 2 #define JOIN_AS_FACEHUGGER_DELAY (3 MINUTES) -#define JOIN_AS_LESSER_DRONE_DELAY (30 SECONDS) +#define JOIN_AS_LESSER_DRONE_DELAY (1 MINUTES) // larva states #define LARVA_STATE_BLOODY 0 diff --git a/code/__HELPERS/game.dm b/code/__HELPERS/game.dm index 644d5df363c1..aef434cf99d0 100644 --- a/code/__HELPERS/game.dm +++ b/code/__HELPERS/game.dm @@ -256,7 +256,7 @@ // copied from join as xeno var/deathtime = world.time - cur_obs.timeofdeath - if(deathtime < XENO_JOIN_DEAD_TIME && ( !cur_obs.client.admin_holder || !(cur_obs.client.admin_holder.rights & R_ADMIN)) && !cur_obs.bypass_time_of_death_checks) + if(deathtime < XENO_JOIN_DEAD_TIME && !cur_obs.bypass_time_of_death_checks && !check_client_rights(cur_obs.client, R_ADMIN, FALSE)) continue // AFK players cannot be drafted diff --git a/code/_onclick/observer.dm b/code/_onclick/observer.dm index 04c70bbe1112..62026324c594 100644 --- a/code/_onclick/observer.dm +++ b/code/_onclick/observer.dm @@ -34,22 +34,29 @@ do_observe(xeno) return FALSE - if(!SSticker.mode.xeno_bypass_timer) - if((!islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER) || (islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER_LARVA)) - var/to_wait = XENO_LEAVE_TIMER - xeno.away_timer - if(islarva(xeno)) - to_wait = XENO_LEAVE_TIMER_LARVA - xeno.away_timer - if(to_wait > 60 SECONDS) // don't spam for clearly non-AFK xenos - to_chat(src, SPAN_WARNING("That player hasn't been away long enough. Please wait [to_wait] second\s longer.")) - do_observe(target) - return FALSE + if(xeno.health <= 0) + to_chat(src, SPAN_WARNING("You cannot join if the xenomorph is in critical condition or unconscious.")) + do_observe(xeno) + return FALSE + var/required_leave_time = XENO_LEAVE_TIMER + var/required_dead_time = XENO_JOIN_DEAD_TIME + if(islarva(xeno)) + required_leave_time = XENO_LEAVE_TIMER_LARVA + required_dead_time = XENO_JOIN_DEAD_LARVA_TIME + + if(xeno.away_timer < required_leave_time) + var/to_wait = required_leave_time - xeno.away_timer + if(to_wait > 60 SECONDS) // don't spam for clearly non-AFK xenos + to_chat(src, SPAN_WARNING("That player hasn't been away long enough. Please wait [to_wait] second\s longer.")) + do_observe(target) + return FALSE + + if(!SSticker.mode.xeno_bypass_timer) var/deathtime = world.time - timeofdeath - if(deathtime < XENO_JOIN_DEAD_LARVA_TIME) - var/message = "You have been dead for [DisplayTimeText(deathtime)]." - message = SPAN_WARNING("[message]") - to_chat(src, message) - to_chat(src, SPAN_WARNING("You must wait at least 2.5 minutes before rejoining the game!")) + if(deathtime < required_dead_time && !bypass_time_of_death_checks) + to_chat(src, SPAN_WARNING("You have been dead for [DisplayTimeText(deathtime)].")) + to_chat(src, SPAN_WARNING("You must wait at least [required_dead_time / 600] minute\s before rejoining the game!")) do_observe(target) return FALSE @@ -60,13 +67,16 @@ do_observe(target) return FALSE - if(alert(src, "Are you sure you want to transfer yourself into [xeno]?", "Confirm Transfer", "Yes", "No") != "Yes") + if(tgui_alert(src, "Are you sure you want to transfer yourself into [xeno]?", "Confirm Transfer", list("Yes", "No")) != "Yes") return FALSE - if(((!islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER) || (islarva(xeno) && xeno.away_timer < XENO_LEAVE_TIMER_LARVA)) || xeno.stat == DEAD) // Do it again, just in case + + if(xeno.away_timer < required_leave_time || xeno.stat == DEAD || !(xeno in GLOB.living_xeno_list)) // Do it again, just in case to_chat(src, SPAN_WARNING("That xenomorph can no longer be controlled. Please try another.")) return FALSE + SSticker.mode.transfer_xeno(src, xeno) return TRUE + do_observe(target) return TRUE diff --git a/code/game/gamemodes/cm_initialize.dm b/code/game/gamemodes/cm_initialize.dm index cd2cdd18fe88..9c6a804862f5 100644 --- a/code/game/gamemodes/cm_initialize.dm +++ b/code/game/gamemodes/cm_initialize.dm @@ -451,6 +451,10 @@ Additional game mode variables. var/list/available_xenos = list() var/list/available_xenos_non_ssd = list() + var/mob/dead/observer/candidate_observer = null + if(isobserver(xeno_candidate)) + candidate_observer = xeno_candidate + for(var/mob/living/carbon/xenomorph/cur_xeno as anything in GLOB.living_xeno_list) if(cur_xeno.aghosted) continue //aghosted xenos don't count @@ -520,11 +524,9 @@ Additional game mode variables. to_chat(candidate_new_player, SPAN_XENONOTICE(candidate_new_player.larva_queue_cached_message)) return FALSE - if(!isobserver(xeno_candidate)) + if(!candidate_observer) return FALSE - var/mob/dead/observer/candidate_observer = xeno_candidate - // If an observing mod wants to join as a xeno, disable their larva protection so that they can enter the queue. if(check_client_rights(candidate_observer.client, R_MOD, FALSE)) candidate_observer.admin_larva_protection = FALSE @@ -567,7 +569,9 @@ Additional game mode variables. return FALSE var/mob/living/carbon/xenomorph/new_xeno - if(!instant_join) + if(instant_join) + new_xeno = pick(available_xenos_non_ssd) //Just picks something at random. + else var/userInput = tgui_input_list(usr, "Available Xenomorphs", "Join as Xeno", available_xenos, theme="hive_status") if(available_xenos[userInput]) //Free xeno mobs have no associated value and skip this. "Pooled larva" strings have a list of hives. @@ -576,20 +580,21 @@ Additional game mode variables. if(!xeno_bypass_timer) var/deathtime = world.time - xeno_candidate.timeofdeath if(isnewplayer(xeno_candidate)) - deathtime = XENO_JOIN_DEAD_LARVA_TIME //so new players don't have to wait to latejoin as xeno in the round's first 5 mins. - if(deathtime < XENO_JOIN_DEAD_LARVA_TIME && !check_client_rights(xeno_candidate.client, R_ADMIN, FALSE)) - var/message = SPAN_WARNING("You have been dead for [DisplayTimeText(deathtime)].") - to_chat(xeno_candidate, message) - to_chat(xeno_candidate, SPAN_WARNING("You must wait 2 minutes and 30 seconds before rejoining the game as a buried larva!")) + deathtime = INFINITY //so new players don't have to wait to latejoin as xeno in the round's first 2.5 mins. + if(deathtime < XENO_JOIN_DEAD_LARVA_TIME && !candidate_observer.bypass_time_of_death_checks && !check_client_rights(xeno_candidate.client, R_ADMIN, FALSE)) + to_chat(xeno_candidate, SPAN_WARNING("You have been dead for [DisplayTimeText(deathtime)].")) + to_chat(xeno_candidate, SPAN_WARNING("You must wait at least [XENO_JOIN_DEAD_LARVA_TIME / 600] minute\s before rejoining the game as a buried larva!")) return FALSE for(var/mob_name in picked_hive.banished_ckeys) if(picked_hive.banished_ckeys[mob_name] == xeno_candidate.ckey) to_chat(xeno_candidate, SPAN_WARNING("You are banished from the [picked_hive], you may not rejoin unless the Queen re-admits you or dies.")) return FALSE + if(isnewplayer(xeno_candidate)) var/mob/new_player/noob = xeno_candidate noob.close_spawn_windows() + if(picked_hive.hive_location) picked_hive.hive_location.spawn_burrowed_larva(xeno_candidate) else if((world.time < XENO_BURIED_LARVA_TIME_LIMIT + SSticker.round_start_time)) @@ -606,7 +611,7 @@ Additional game mode variables. return FALSE new_xeno = userInput - if(!(new_xeno in GLOB.living_xeno_list) || new_xeno.stat == DEAD) + if(new_xeno.stat == DEAD) to_chat(xeno_candidate, SPAN_WARNING("You cannot join if the xenomorph is dead.")) return FALSE @@ -614,30 +619,34 @@ Additional game mode variables. to_chat(xeno_candidate, SPAN_WARNING("You cannot join if the xenomorph is in critical condition or unconscious.")) return FALSE + var/required_leave_time = XENO_LEAVE_TIMER + var/required_dead_time = XENO_JOIN_DEAD_TIME + if(islarva(new_xeno)) + required_leave_time = XENO_LEAVE_TIMER_LARVA + required_dead_time = XENO_JOIN_DEAD_LARVA_TIME + + if(new_xeno.away_timer < required_leave_time) + var/to_wait = required_leave_time - new_xeno.away_timer + to_chat(xeno_candidate, SPAN_WARNING("That player hasn't been away long enough. Please wait [to_wait] second\s longer.")) + return FALSE + if(!xeno_bypass_timer) var/deathtime = world.time - xeno_candidate.timeofdeath if(istype(xeno_candidate, /mob/new_player)) - deathtime = XENO_JOIN_DEAD_TIME //so new players don't have to wait to latejoin as xeno in the round's first 5 mins. - if(deathtime < XENO_JOIN_DEAD_TIME && !check_client_rights(xeno_candidate.client, R_ADMIN, FALSE)) - var/message = "You have been dead for [DisplayTimeText(deathtime)]." - message = SPAN_WARNING("[message]") - to_chat(xeno_candidate, message) - to_chat(xeno_candidate, SPAN_WARNING("You must wait 5 minutes before rejoining the game!")) - return FALSE - if((!islarva(new_xeno) && new_xeno.away_timer < XENO_LEAVE_TIMER) || (islarva(new_xeno) && new_xeno.away_timer < XENO_LEAVE_TIMER_LARVA)) - var/to_wait = XENO_LEAVE_TIMER - new_xeno.away_timer - if(islarva(new_xeno)) - to_wait = XENO_LEAVE_TIMER_LARVA - new_xeno.away_timer - to_chat(xeno_candidate, SPAN_WARNING("That player hasn't been away long enough. Please wait [to_wait] second\s longer.")) + deathtime = INFINITY //so new players don't have to wait to latejoin as xeno in the round's first 5 mins. + if(deathtime < required_dead_time && !candidate_observer.bypass_time_of_death_checks && !check_client_rights(xeno_candidate.client, R_ADMIN, FALSE)) + to_chat(xeno_candidate, SPAN_WARNING("You have been dead for [DisplayTimeText(deathtime)].")) + to_chat(xeno_candidate, SPAN_WARNING("You must wait at least [required_dead_time / 600] minute\s before rejoining the game!")) return FALSE - if(alert(xeno_candidate, "Everything checks out. Are you sure you want to transfer yourself into [new_xeno]?", "Confirm Transfer", "Yes", "No") == "Yes") - if(((!islarva(new_xeno) && new_xeno.away_timer < XENO_LEAVE_TIMER) || (islarva(new_xeno) && new_xeno.away_timer < XENO_LEAVE_TIMER_LARVA)) || !(new_xeno in GLOB.living_xeno_list) || new_xeno.stat == DEAD || !xeno_candidate) // Do it again, just in case - to_chat(xeno_candidate, SPAN_WARNING("That xenomorph can no longer be controlled. Please try another.")) - return FALSE - else return FALSE - else new_xeno = pick(available_xenos_non_ssd) //Just picks something at random. - if(istype(new_xeno) && xeno_candidate && xeno_candidate.client) + if(tgui_alert(xeno_candidate, "Are you sure you want to transfer yourself into [new_xeno]?", "Confirm Transfer", list("Yes", "No")) != "Yes") + return FALSE + + if(new_xeno.away_timer < required_leave_time || new_xeno.stat == DEAD || !(new_xeno in GLOB.living_xeno_list) || !xeno_candidate) // Do it again, just in case + to_chat(xeno_candidate, SPAN_WARNING("That xenomorph can no longer be controlled. Please try another.")) + return FALSE + + if(istype(new_xeno) && xeno_candidate?.client) if(isnewplayer(xeno_candidate)) var/mob/new_player/noob = xeno_candidate noob.close_spawn_windows() @@ -691,7 +700,7 @@ Additional game mode variables. available_facehugger_sources[descriptive_name] = morpher if(length(available_facehugger_sources) <= 0) - to_chat(xeno_candidate, SPAN_WARNING("There aren't any Carriers or Egg Morphers with available Facehuggers for you to join. Please try again later!")) + to_chat(xeno_candidate, SPAN_WARNING("There aren't any Carriers or Egg Morphers with available Facehuggers for you to join. Find an egg or try again later!")) return FALSE var/source_picked = tgui_input_list(xeno_candidate, "Select a Facehugger source.", "Facehugger Source Choice", available_facehugger_sources, theme="hive_status") diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm index eae5af870b27..34435d9f8318 100644 --- a/code/modules/mob/dead/observer/observer.dm +++ b/code/modules/mob/dead/observer/observer.dm @@ -63,8 +63,10 @@ var/datum/action/minimap/observer/minimap ///The last message for this player with their larva queue information var/larva_queue_cached_message - ///Used to bypass time of death checks such as when being selected for larva. + ///Used to bypass time of death checks such as when being selected for larva var/bypass_time_of_death_checks = FALSE + ///Used to bypass time of death checks for a successful hug + var/bypass_time_of_death_checks_hugger = FALSE alpha = 127 diff --git a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm index 43c5b78514af..6b69211d5bb1 100644 --- a/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm +++ b/code/modules/mob/living/carbon/xenomorph/castes/Facehugger.dm @@ -46,10 +46,7 @@ counts_for_roundend = FALSE refunds_larva_if_banished = FALSE can_hivemind_speak = FALSE - /// The lifetime hugs from this hugger - var/total_facehugs = 0 - /// How many hugs the hugger needs to age - var/next_facehug_goal = FACEHUG_TIER_1 + base_actions = list( /datum/action/xeno_action/onclick/xeno_resting, /datum/action/xeno_action/watch_xeno, @@ -68,6 +65,13 @@ weed_food_states = list("Facehugger_1","Facehugger_2","Facehugger_3") weed_food_states_flipped = list("Facehugger_1","Facehugger_2","Facehugger_3") + /// The lifetime hugs from this hugger + var/total_facehugs = 0 + /// How many hugs the hugger needs to age + var/next_facehug_goal = FACEHUG_TIER_1 + /// Whether a hug was performed successfully + var/hug_successful = FALSE + /mob/living/carbon/xenomorph/facehugger/Login() var/last_ckey_inhabited = persistent_ckey . = ..() @@ -163,9 +167,16 @@ return if(client) client.player_data?.adjust_stat(PLAYER_STAT_FACEHUGS, STAT_CATEGORY_XENO, 1) + hug_successful = TRUE + timeofdeath = world.time qdel(src) return did_hug +/mob/living/carbon/xenomorph/facehugger/ghostize(can_reenter_corpse, aghosted) + var/mob/dead/observer/ghost = ..() + ghost?.bypass_time_of_death_checks_hugger = hug_successful + return ghost + /mob/living/carbon/xenomorph/facehugger/age_xeno() if(stat == DEAD || !caste || QDELETED(src) || !client) return diff --git a/code/modules/mob/living/carbon/xenomorph/hive_status.dm b/code/modules/mob/living/carbon/xenomorph/hive_status.dm index d7aaf7ca30b3..f254f3d6ca39 100644 --- a/code/modules/mob/living/carbon/xenomorph/hive_status.dm +++ b/code/modules/mob/living/carbon/xenomorph/hive_status.dm @@ -869,7 +869,7 @@ 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) + if(!user.bypass_time_of_death_checks_hugger && world.time - user.timeofdeath < JOIN_AS_FACEHUGGER_DELAY) var/time_left = floor((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