Skip to content

Commit

Permalink
Lesser Drones (#3939)
Browse files Browse the repository at this point in the history
# About the pull request

This PR adds lesser drones that can be joined as from ghost.

You can have up to 1/3rd of the hive as lesser drones.

Lesser drones can build basic resin walls/membranes/doors and can spread
resin nodes.

Lesser drones can give off weak pheromones.

Lesser drones are very weak and die quickly. They will always gib to
avoid intel bloat/clutter.

When lesser drones die they do not change your last time of death so you
do not lose your queue spot. Perfect to play while waiting to get in.

You can spawn as a lesser drone by using the verb in the ghost tab or
clicking on the hive core.

Once you die as a lesser drone you must wait 3 minutes to become one
again.

# Explain why it's good for the game

Having squishy and fast xenos with very little consequence of loss adds
to the "horde-like" feel of xenos.

Having something for xenos to do while waiting as a ghost is good.

Makes the game feel more alive.

# Testing Photographs and Procedure
# Changelog

:cl: Morrow
add: Added lesser drones
/:cl:

---------

Co-authored-by: Ben10083 <[email protected]>
Co-authored-by: harryob <[email protected]>
  • Loading branch information
3 people committed Jul 24, 2023
1 parent 967389d commit cbb0fc4
Show file tree
Hide file tree
Showing 23 changed files with 291 additions and 18 deletions.
1 change: 1 addition & 0 deletions code/__DEFINES/keybinding.dm
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@
#define COMSIG_KB_OBSERVER_JOIN_XENO "keybinding_observer_join_as_xeno"
#define COMSIG_KB_OBSERVER_JOIN_ERT "keybinding_observer_join_ert"
#define COMSIG_KB_OBSERVER_JOIN_PREDATOR "keybinding_observer_join_pred"
#define COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE "keybinding_observer_join_lesser_drone"

#define CATEGORY_CLIENT "CLIENT"
#define CATEGORY_EMOTE "EMOTE"
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/mobs.dm
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@
//Mob sizes
#define MOB_SIZE_SMALL 0
#define MOB_SIZE_HUMAN 1
#define MOB_SIZE_XENO_VERY_SMALL 1.5
#define MOB_SIZE_XENO_SMALL 2
#define MOB_SIZE_XENO 3
#define MOB_SIZE_BIG 4
Expand Down
2 changes: 1 addition & 1 deletion code/__DEFINES/typecheck/humanoids.dm
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
#define isspeciessynth(A) (A.species?.group == SPECIES_SYNTHETIC)

//Size checks for carbon to use instead of typechecks. (Hellhounds are deprecated)
#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_SMALL)
#define iscarbonsizexeno(A) (A.mob_size >= MOB_SIZE_XENO_VERY_SMALL)
#define iscarbonsizehuman(A) (A.mob_size <= MOB_SIZE_HUMAN)

//job/role helpers
Expand Down
1 change: 1 addition & 0 deletions code/__DEFINES/typecheck/xenos.dm
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#define islarva(A) (istype(A, /mob/living/carbon/xenomorph/larva))
#define ispredalienlarva(A) (istype(A, /mob/living/carbon/xenomorph/larva/predalien))
#define isfacehugger(A) (istype(A, /mob/living/carbon/xenomorph/facehugger))
#define islesserdrone(A) (istype(A, /mob/living/carbon/xenomorph/lesser_drone))
#define ispraetorian(A) (istype(A, /mob/living/carbon/xenomorph/praetorian))
#define isqueen(A) (istype(A, /mob/living/carbon/xenomorph/queen))
#define isravager(A) (istype(A, /mob/living/carbon/xenomorph/ravager))
Expand Down
10 changes: 8 additions & 2 deletions code/__DEFINES/xeno.dm
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#define HUD_ARMOR_STATES_XENO 10

/// Multiplier for time taken for a xeno to place down a resin structure
#define BUILD_TIME_MULT_LESSER_DRONE 2
#define BUILD_TIME_MULT_XENO 1
#define BUILD_TIME_MULT_BUILDER 1
#define BUILD_TIME_MULT_HIVELORD 0.5
Expand Down Expand Up @@ -206,6 +207,7 @@

// Health bands
#define XENO_HEALTH_LARVA 35 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_LESSER_DRONE 160 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_RUNNER 230 * XENO_UNIVERSAL_HPMULT // Killed by 1 PB
#define XENO_HEALTH_TIER_1 250 * XENO_UNIVERSAL_HPMULT
#define XENO_HEALTH_TIER_2 300 * XENO_UNIVERSAL_HPMULT
Expand Down Expand Up @@ -603,7 +605,8 @@
#define XENO_CASTE_LARVA "Bloody Larva"
#define XENO_CASTE_PREDALIEN_LARVA "Predalien Larva"
#define XENO_CASTE_FACEHUGGER "Facehugger"
#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER)
#define XENO_CASTE_LESSER_DRONE "Lesser Drone"
#define XENO_T0_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE)

//t1
#define XENO_CASTE_DRONE "Drone"
Expand Down Expand Up @@ -631,7 +634,7 @@
#define XENO_CASTE_HELLHOUND "Hellhound"
#define XENO_SPECIAL_CASTES list(XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)

#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)
#define ALL_XENO_CASTES list(XENO_CASTE_LARVA, XENO_CASTE_PREDALIEN_LARVA, XENO_CASTE_FACEHUGGER, XENO_CASTE_LESSER_DRONE, XENO_CASTE_DRONE, XENO_CASTE_RUNNER, XENO_CASTE_SENTINEL, XENO_CASTE_DEFENDER, XENO_CASTE_BURROWER, XENO_CASTE_CARRIER, XENO_CASTE_HIVELORD, XENO_CASTE_LURKER, XENO_CASTE_WARRIOR, XENO_CASTE_SPITTER, XENO_CASTE_BOILER, XENO_CASTE_PRAETORIAN, XENO_CASTE_CRUSHER, XENO_CASTE_RAVAGER, XENO_CASTE_QUEEN, XENO_CASTE_PREDALIEN, XENO_CASTE_HELLHOUND)

// Checks if two hives are allied to each other.
// PARAMETERS:
Expand Down Expand Up @@ -695,3 +698,6 @@
#define TAILSTAB_AIRLOCK_DAMAGE_MULTIPLIER 2

#define FRENZY_DAMAGE_MULTIPLIER 2

#define JOIN_AS_FACEHUGGER_DELAY (3 MINUTES)
#define JOIN_AS_LESSER_DRONE_DELAY (30 SECONDS)
6 changes: 6 additions & 0 deletions code/_globalvars/global_lists.dm
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ GLOBAL_LIST_EMPTY(mainship_pipes)
// Resin constructions parameters
GLOBAL_LIST_INIT_TYPED(resin_constructions_list, /datum/resin_construction, setup_resin_constructions())

GLOBAL_LIST_INIT(resin_build_order_lesser_drone, list(
/datum/resin_construction/resin_turf/wall,
/datum/resin_construction/resin_turf/membrane,
/datum/resin_construction/resin_obj/door,
))

GLOBAL_LIST_INIT(resin_build_order_drone, list(
/datum/resin_construction/resin_turf/wall,
/datum/resin_construction/resin_turf/membrane,
Expand Down
32 changes: 32 additions & 0 deletions code/game/gamemodes/cm_initialize.dm
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,38 @@ Additional game mode variables.

return TRUE

/datum/game_mode/proc/attempt_to_join_as_lesser_drone(mob/xeno_candidate)
var/list/active_hives = list()
var/datum/hive_status/hive
var/last_active_hive = 0
for(var/hivenumber in GLOB.hive_datum)
hive = GLOB.hive_datum[hivenumber]
if(hive.totalXenos.len <= 0)
continue
active_hives[hive.name] = hive.hivenumber
last_active_hive = hive.hivenumber

if(active_hives.len <= 0)
to_chat(xeno_candidate, SPAN_WARNING("There aren't any Hives active at this point for you to join."))
return FALSE

if(active_hives.len > 1)
var/hive_picked = tgui_input_list(xeno_candidate, "Select which Hive to attempt joining.", "Hive Choice", active_hives, theme="hive_status")
if(!hive_picked)
to_chat(xeno_candidate, SPAN_ALERT("Hive choice error. Aborting."))
return
hive = GLOB.hive_datum[active_hives[hive_picked]]
else
hive = GLOB.hive_datum[last_active_hive]

if(!hive.hive_location)
to_chat(xeno_candidate, SPAN_WARNING("The selected hive does not have a hive core to spawn from!"))
return

hive.hive_location.spawn_lesser_drone(xeno_candidate)

return TRUE

/datum/game_mode/proc/transfer_xeno(xeno_candidate, mob/living/new_xeno)
if(!xeno_candidate || !isxeno(new_xeno) || QDELETED(new_xeno))
return FALSE
Expand Down
2 changes: 2 additions & 0 deletions code/game/jobs/role_authority.dm
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,8 @@ I hope it's easier to tell what the heck this proc is even doing, unlike previou
M = /mob/living/carbon/xenomorph/larva/predalien
if(XENO_CASTE_FACEHUGGER)
M = /mob/living/carbon/xenomorph/facehugger
if(XENO_CASTE_LESSER_DRONE)
M = /mob/living/carbon/xenomorph/lesser_drone
if(XENO_CASTE_RUNNER)
M = /mob/living/carbon/xenomorph/runner
if(XENO_CASTE_DRONE)
Expand Down
5 changes: 5 additions & 0 deletions code/modules/admin/player_panel/actions/transform.dm
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ GLOBAL_LIST_INIT(pp_transformables, list(
name = "Facehugger",
key = /mob/living/carbon/xenomorph/facehugger,
color = "purple"
),
list(
name = "Lesser Drone",
key = /mob/living/carbon/xenomorph/lesser_drone,
color = "purple"
)
),

Expand Down
17 changes: 17 additions & 0 deletions code/modules/cm_aliens/structures/special/pylon_core.dm
Original file line number Diff line number Diff line change
Expand Up @@ -327,5 +327,22 @@
// Tell admins that this condition is reached so they know what has happened if it fails somehow
return

/obj/effect/alien/resin/special/pylon/core/proc/spawn_lesser_drone(mob/xeno_candidate)
if(!linked_hive.can_spawn_as_lesser_drone(xeno_candidate))
return FALSE

var/mob/living/carbon/xenomorph/lesser_drone/new_drone = new /mob/living/carbon/xenomorph/lesser_drone(loc, null, linked_hive.hivenumber)
xeno_candidate.mind.transfer_to(new_drone, TRUE)
new_drone.visible_message(SPAN_XENODANGER("A lesser drone emerges out of [src]!"), SPAN_XENODANGER("You emerge out of [src] and awaken from your slumber. For the Hive!"))
playsound(new_drone, 'sound/effects/xeno_newlarva.ogg', 25, TRUE)
new_drone.generate_name()

return TRUE

/obj/effect/alien/resin/special/pylon/core/attack_ghost(mob/dead/observer/user)
. = ..()
if(SSticker.mode.check_xeno_late_join(user))
SSticker.mode.attempt_to_join_as_lesser_drone(user)

#undef PYLON_REPAIR_TIME
#undef PYLON_WEEDS_REGROWTH_TIME
23 changes: 23 additions & 0 deletions code/modules/mob/dead/observer/actions.dm
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@
if(SSticker.mode.check_xeno_late_join(owner))
SSticker.mode.attempt_to_join_as_xeno(owner)

/datum/action/observer_action/join_lesser_drone
name = "Join as Lesser Drone"
action_icon_state = "join_lesser_drone"
listen_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE

/datum/action/observer_action/join_lesser_drone/action_activate()
if(!owner.client)
return

if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
owner.balloon_alert(owner, "game must start!")
return

if(SSticker.mode.check_xeno_late_join(owner))
SSticker.mode.attempt_to_join_as_lesser_drone(owner)

/datum/keybinding/observer
category = CATEGORY_OBSERVER
weight = WEIGHT_DEAD
Expand Down Expand Up @@ -108,3 +124,10 @@
name = "join_pred"
full_name = "Join the Hunt"
keybind_signal = COMSIG_KB_OBSERVER_JOIN_PREDATOR

/datum/keybinding/observer/join_lesser_drone
hotkey_keys = list("Unbound")
classic_keys = list("Unbound")
name = "join_lesser_drone"
full_name = "Join as Lesser Drone"
keybind_signal = COMSIG_KB_OBSERVER_JOIN_LESSER_DRONE
39 changes: 29 additions & 10 deletions code/modules/mob/dead/observer/observer.dm
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
var/datum/health_scan/last_health_display
var/ghost_orbit = GHOST_ORBIT_CIRCLE
var/own_orbit_size = 0
var/observer_actions = list(/datum/action/observer_action/join_xeno)
var/observer_actions = list(/datum/action/observer_action/join_xeno, /datum/action/observer_action/join_lesser_drone)
var/datum/action/minimap/observer/minimap
var/larva_queue_cached_message
///Used to bypass time of death checks such as when being selected for larva.
Expand Down Expand Up @@ -368,12 +368,13 @@ Works together with spawning an observer, noted above.
if(ghost.client.player_data)
ghost.client.player_data.load_timestat_data()

// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
// We don't change facehugger timeofdeath because they are still on cooldown if they died as a hugger
var/new_tod = isfacehugger(src) ? 1 : ghost.timeofdeath
// if they died as facehugger, bypass typical TOD checks
ghost.bypass_time_of_death_checks = isfacehugger(src)
ghost.client.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath

// if they died as facehugger or lesser drone, bypass typical TOD checks
ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))

ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)

ghost.set_huds_from_prefs()

Expand Down Expand Up @@ -418,9 +419,12 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(ghost && !is_admin_level(z))
ghost.timeofdeath = world.time

// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers
var/new_tod = isfacehugger(src) ? 1 : world.time
ghost.bypass_time_of_death_checks = isfacehugger(src)
// Larva queue: We use the larger of their existing queue time or the new timeofdeath except for facehuggers or lesser drone
var/new_tod = (isfacehugger(src) || islesserdrone(src)) ? 1 : ghost.timeofdeath

// if they died as facehugger or lesser drone, bypass typical TOD checks
ghost.bypass_time_of_death_checks = (isfacehugger(src) || islesserdrone(src))

ghost.client?.player_details.larva_queue_time = max(ghost.client.player_details.larva_queue_time, new_tod)
if(is_nested && nest && !QDELETED(nest))
ghost.can_reenter_corpse = FALSE
Expand Down Expand Up @@ -779,6 +783,21 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
if(SSticker.mode.check_xeno_late_join(src))
SSticker.mode.attempt_to_join_as_facehugger(src)

/mob/dead/verb/join_as_lesser_drone()
set category = "Ghost.Join"
set name = "Join as a Lesser Drone"
set desc = "Try joining as a Lesser Drone to support the hive."

if (!client)
return

if(SSticker.current_state < GAME_STATE_PLAYING || !SSticker.mode)
to_chat(src, SPAN_WARNING("The game hasn't started yet!"))
return

if(SSticker.mode.check_xeno_late_join(src))
SSticker.mode.attempt_to_join_as_lesser_drone(src)

/mob/dead/verb/join_as_zombie() //Adapted from join as hellhoud
set category = "Ghost.Join"
set name = "Join as Zombie"
Expand Down
2 changes: 1 addition & 1 deletion code/modules/mob/living/carbon/xenomorph/XenoAttacks.dm
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@
var/is_shover_queen = isqueen(M)
var/can_resist_shove = M.hivenumber != src.hivenumber || ((isqueen(src) || IS_XENO_LEADER(src)) && !is_shover_queen)
var/can_mega_shove = is_shover_queen || IS_XENO_LEADER(M)
if(can_mega_shove && !can_resist_shove)
if(can_mega_shove && !can_resist_shove || (mob_size < MOB_SIZE_XENO_SMALL && M.mob_size >= MOB_SIZE_XENO_SMALL))
playsound(loc, 'sound/weapons/alien_knockdown.ogg', 25, 1)
M.visible_message(SPAN_WARNING("\The [M] shoves \the [src] out of her way!"), \
SPAN_WARNING("You shove \the [src] out of your way!"), null, 5, CHAT_TYPE_XENO_COMBAT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/datum/action/xeno_action/onclick/plant_weeds/lesser/use_ability(atom/A)
if(!(locate(/obj/effect/alien/weeds/node) in orange(4, owner)))
to_chat(owner, SPAN_XENONOTICE("You can only plant resin nodes near other resin nodes!"))
return

. = ..()
97 changes: 97 additions & 0 deletions code/modules/mob/living/carbon/xenomorph/castes/lesser_drone.dm
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/datum/caste_datum/lesser_drone
caste_type = XENO_CASTE_LESSER_DRONE
tier = 1
melee_damage_lower = XENO_DAMAGE_TIER_1
melee_damage_upper = XENO_DAMAGE_TIER_1
melee_vehicle_damage = XENO_DAMAGE_TIER_1
max_health = XENO_HEALTH_LESSER_DRONE
plasma_gain = XENO_PLASMA_GAIN_TIER_7
plasma_max = XENO_PLASMA_TIER_3
crystal_max = XENO_CRYSTAL_LOW
xeno_explosion_resistance = XENO_NO_EXPLOSIVE_ARMOR
armor_deflection = XENO_NO_ARMOR
evasion = XENO_EVASION_LOW
speed = XENO_SPEED_TIER_6

evolution_allowed = FALSE
can_be_revived = FALSE

build_time_mult = BUILD_TIME_MULT_LESSER_DRONE

caste_desc = "A builder of hives."
can_hold_facehuggers = 1
can_hold_eggs = CAN_HOLD_TWO_HANDS
acid_level = 1
weed_level = WEED_LEVEL_STANDARD
max_build_dist = 1

tackle_min = 4
tackle_max = 5

aura_strength = 1

minimap_icon = "lesser_drone"

/datum/caste_datum/lesser_drone/New()
. = ..()

resin_build_order = GLOB.resin_build_order_lesser_drone

/mob/living/carbon/xenomorph/lesser_drone
caste_type = XENO_CASTE_LESSER_DRONE
name = XENO_CASTE_LESSER_DRONE
desc = "An alien drone. Looks... smaller."
icon = 'icons/mob/xenos/drone.dmi'
icon_size = 48
icon_state = "Lesser Drone Walking"
plasma_types = list(PLASMA_PURPLE)
tier = 0
mob_flags = NOBIOSCAN
mob_size = MOB_SIZE_XENO_VERY_SMALL
life_value = 0
default_honor_value = 0
show_only_numbers = TRUE
counts_for_slots = FALSE
counts_for_roundend = FALSE
refunds_larva_if_banished = FALSE
crit_health = 0
gib_chance = 100
acid_blood_damage = 15
base_actions = list(
/datum/action/xeno_action/onclick/xeno_resting,
/datum/action/xeno_action/onclick/regurgitate,
/datum/action/xeno_action/watch_xeno,
/datum/action/xeno_action/activable/tail_stab,
/datum/action/xeno_action/activable/corrosive_acid/weak,
/datum/action/xeno_action/onclick/emit_pheromones,
/datum/action/xeno_action/onclick/plant_weeds/lesser, //first macro
/datum/action/xeno_action/onclick/choose_resin, //second macro
/datum/action/xeno_action/activable/secrete_resin, //third macro
/datum/action/xeno_action/onclick/tacmap,
)
inherent_verbs = list(
/mob/living/carbon/xenomorph/proc/vent_crawl,
/mob/living/carbon/xenomorph/proc/rename_tunnel,
/mob/living/carbon/xenomorph/proc/set_hugger_reserve_for_morpher,
)

mutation_type = DRONE_NORMAL

icon_xeno = 'icons/mob/xenos/lesser_drone.dmi'
icon_xenonid = 'icons/mob/xenonids/lesser_drone.dmi'

/mob/living/carbon/xenomorph/lesser_drone/age_xeno()
if(stat == DEAD || !caste || QDELETED(src) || !client)
return

age = XENO_NORMAL

hud_update()

xeno_jitter(25)

/mob/living/carbon/xenomorph/lesser_drone/initialize_pass_flags(datum/pass_flags_container/PF)
..()
if (PF)
PF.flags_pass = PASS_MOB_IS_XENO|PASS_MOB_THRU_XENO
PF.flags_can_pass_all = PASS_MOB_IS_XENO|PASS_MOB_THRU_XENO
Loading

0 comments on commit cbb0fc4

Please sign in to comment.