diff --git a/code/__DEFINES/ai/swarmer_defines.dm b/code/__DEFINES/ai/swarmer_defines.dm index fd77592ce38..e6650d80d5b 100644 --- a/code/__DEFINES/ai/swarmer_defines.dm +++ b/code/__DEFINES/ai/swarmer_defines.dm @@ -7,3 +7,5 @@ #define BB_SWARMER_TRAP_ACTION "BB_swarmer_trap_action" /// Key where we define the barrier construction ability #define BB_SWARMER_BARRIER_ACTION "BB_swarmer_barrier_action" +/// Key where we define the resource share ability +#define BB_SWARMER_RESOURCE_SHARE_ACTION "BB_swarmer_resource_share_action" diff --git a/code/modules/events/event_container.dm b/code/modules/events/event_container.dm index 955ff075ca3..31967ae96b4 100644 --- a/code/modules/events/event_container.dm +++ b/code/modules/events/event_container.dm @@ -192,6 +192,7 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_bluespace, 7), new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_flux, 7), new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/anomaly/anomaly_grav, 7), + new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/swarmers, 15), new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/disease_outbreak, 15, TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/door_runtime, 10, TRUE), new /datum/event_meta(EVENT_LEVEL_MODERATE, /datum/event/tourist_arrivals, 40, TRUE, _first_run_time = 35 MINUTES), @@ -215,7 +216,6 @@ GLOBAL_LIST_EMPTY(event_last_fired) new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/demon_incursion, 10, TRUE, _first_run_time = 35 MINUTES), new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/disease_outbreak, 8, TRUE), new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/revenant, 9), - new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/swarmers, 9), new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_morph, 16, is_one_shot = TRUE), //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_floor_cluwne, 15, is_one_shot = TRUE) //new /datum/event_meta(EVENT_LEVEL_MAJOR, /datum/event/spawn_pulsedemon, 20, is_one_shot = TRUE) diff --git a/code/modules/events/swarmers.dm b/code/modules/events/swarmers.dm index cdc627ace26..bddd1c65dd1 100644 --- a/code/modules/events/swarmers.dm +++ b/code/modules/events/swarmers.dm @@ -1,6 +1,6 @@ /datum/event/swarmers name = "Swarmer Infestation" - nominal_severity = EVENT_LEVEL_MAJOR + nominal_severity = EVENT_LEVEL_MODERATE role_weights = list(ASSIGNMENT_ENGINEERING = 5, ASSIGNMENT_SECURITY = 3, ASSIGNMENT_CREW = 0.8) role_requirements = list(ASSIGNMENT_ENGINEERING = 1, ASSIGNMENT_SECURITY = 1, ASSIGNMENT_CREW = 20) /// The name of the notification for dchat diff --git a/code/modules/mob/living/basic/hostile/swarmers/swarmer.dm b/code/modules/mob/living/basic/hostile/swarmers/swarmer.dm index bfb28217fd1..0269552d6ca 100644 --- a/code/modules/mob/living/basic/hostile/swarmers/swarmer.dm +++ b/code/modules/mob/living/basic/hostile/swarmers/swarmer.dm @@ -41,7 +41,7 @@ environment_smash = ENVIRONMENT_SMASH_RWALLS // EAT EVERYTHING step_type = FOOTSTEP_MOB_CLAW is_ranged = TRUE - projectile_type = /obj/projectile/beam/disabler + projectile_type = /obj/projectile/beam/disabler/swarmer projectile_sound = 'sound/weapons/taser2.ogg' ranged_burst_count = 2 ranged_burst_interval = 0.5 SECONDS @@ -163,7 +163,7 @@ L.apply_damage(30, STAMINA) var/obj/item/restraints/handcuffs/cable/cyan/cuffs = new /obj/item/restraints/handcuffs/cable/cyan(src) playsound(loc, cuffs.cuffsound, 15, TRUE, -10) - if(do_mob(src, C, 1 SECONDS)) + if(do_mob(src, C, 2 SECONDS)) cuffs.apply_cuffs(target, src) return FALSE // Make it go away. @@ -296,7 +296,10 @@ innate_actions = list( /datum/action/cooldown/mob_cooldown/swarmer_trap = BB_SWARMER_TRAP_ACTION, /datum/action/cooldown/mob_cooldown/swarmer_barrier = BB_SWARMER_BARRIER_ACTION, + /datum/action/cooldown/mob_cooldown/swarmer_share_resources = BB_SWARMER_RESOURCE_SHARE_ACTION, ) + /// Our creator + var/mob/living/basic/swarmer/progenitor /mob/living/basic/swarmer/lesser/Initialize(mapload) . = ..() @@ -306,6 +309,13 @@ real_name = "Lesser Swarmer [rand(100,999)]-[pick("kappa", "sigma", "beta", "omicron", "iota", "epsilon", "omega", "gamma", "delta", "tau", "alpha")]" name = real_name +/mob/living/basic/swarmer/lesser/death(gibbed) + progenitor = null + return ..() + +/mob/living/basic/swarmer/lesser/proc/progenitor_death() + progenitor = null + // ===================== // MARK: Swarmer Structures // ===================== diff --git a/code/modules/mob/living/basic/hostile/swarmers/swarmer_actions.dm b/code/modules/mob/living/basic/hostile/swarmers/swarmer_actions.dm index 50efc801d7b..786dae6b5a5 100644 --- a/code/modules/mob/living/basic/hostile/swarmers/swarmer_actions.dm +++ b/code/modules/mob/living/basic/hostile/swarmers/swarmer_actions.dm @@ -67,6 +67,33 @@ new /obj/structure/swarmer/barricade(T) StartCooldown() +/datum/action/cooldown/mob_cooldown/swarmer_share_resources + name = "Share Resources" + button_icon = 'icons/mob/swarmer.dmi' + button_icon_state = "ui_transfer" + desc = "Share 50 resources with the swarmer that created you." + click_to_activate = FALSE + melee_cooldown_time = CLICK_CD_CLICK_ABILITY + cooldown_time = 15 SECONDS + shared_cooldown = NONE + +/datum/action/cooldown/mob_cooldown/swarmer_share_resources/Activate(atom/target) + var/mob/living/basic/swarmer/lesser/user = target + if(!istype(user)) + to_chat(target, SPAN_WARNING("Incompatible hardware detected. Aborting.")) + return + if(!user.progenitor) + to_chat(target, SPAN_WARNING("Progenitor not responding. Aborting.")) + return + if(user.resources < 50) + to_chat(user, SPAN_WARNING("Insufficient resources. Aborting.")) + return + user.progenitor.resources = clamp(user.progenitor.resources + 50, 0, user.progenitor.resource_max) + user.resources -= 50 + user.Beam(user.progenitor, "light_beam", 'icons/effects/beam.dmi', 0.3 SECONDS, beam_color = LIGHT_COLOR_CYAN) + playsound(get_turf(user), 'sound/effects/empulse.ogg', 15, TRUE) + StartCooldown() + /datum/action/cooldown/mob_cooldown/swarmer_replicate name = "Replicate" button_icon = 'icons/mob/swarmer.dmi' @@ -95,5 +122,7 @@ user.resources -= 50 user.visible_message(SPAN_WARNING("[user] manufactures a new swarmer.")) playsound(get_turf(user), 'sound/items/rped.ogg', 50, TRUE) - new /mob/living/basic/swarmer/lesser(T) + var/mob/living/basic/swarmer/lesser/child = new /mob/living/basic/swarmer/lesser(T) + child.progenitor = user + child.RegisterSignal(user, COMSIG_MOB_DEATH, TYPE_PROC_REF(/mob/living/basic/swarmer/lesser, progenitor_death)) StartCooldown() diff --git a/code/modules/mob/living/basic/hostile/swarmers/swarmer_ai.dm b/code/modules/mob/living/basic/hostile/swarmers/swarmer_ai.dm index e34399fc4dc..6b8fb4e3f38 100644 --- a/code/modules/mob/living/basic/hostile/swarmers/swarmer_ai.dm +++ b/code/modules/mob/living/basic/hostile/swarmers/swarmer_ai.dm @@ -40,6 +40,7 @@ /datum/ai_planning_subtree/swarmer_find_construction_target, /datum/ai_planning_subtree/swarmer_create_trap, /datum/ai_planning_subtree/swarmer_create_barricade, + /datum/ai_planning_subtree/swarmer_share_resources, /datum/ai_planning_subtree/attack_obstacle_in_path/avoid_breaches/walls/swarmer_hunting, /datum/ai_planning_subtree/find_and_hunt_target/corpses/swarmer, /datum/ai_planning_subtree/find_and_hunt_target/swarmer_objects, @@ -296,3 +297,46 @@ /datum/ai_behavior/swarmer_create_barricade/finish_action(datum/ai_controller/controller, succeeded, action_key, target_key) controller.clear_blackboard_key(target_key) return ..() + +/// Run the resource sharing behavior +/datum/ai_planning_subtree/swarmer_share_resources + /// Key where the share action is stored + var/action_key = BB_SWARMER_RESOURCE_SHARE_ACTION + +/datum/ai_planning_subtree/swarmer_share_resources/select_behaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/basic/swarmer/lesser/user = controller.pawn + if(!istype(user)) + return + if(!user.progenitor) + return + if(user.resources < 50) + return + if(!controller.blackboard_key_exists(action_key)) + return + var/datum/action/cooldown/mob_cooldown/swarmer_share_resources/share_action = controller.blackboard[action_key] + if(share_action.IsAvailable()) + controller.queue_behavior(/datum/ai_behavior/swarmer_share_resources, action_key) + return SUBTREE_RETURN_FINISH_PLANNING + +/// Share resources with the parent swarmer +/datum/ai_behavior/swarmer_share_resources + action_cooldown = 15 SECONDS + required_distance = 0 + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +/datum/ai_behavior/swarmer_share_resources/setup(datum/ai_controller/controller, action_key) + var/datum/action/cooldown/mob_cooldown/swarmer_share_resources/share_action = controller.blackboard[action_key] + var/mob/living/basic/swarmer/user = controller.pawn + if(!istype(user)) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + if(!share_action || user.resources < 50) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + return ..() + +/datum/ai_behavior/swarmer_share_resources/perform(seconds_per_tick, datum/ai_controller/controller, action_key) + . = ..() + var/datum/action/cooldown/mob_cooldown/swarmer_share_resources/share_action = controller.blackboard[action_key] + if(share_action?.Trigger()) + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED + return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED + diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm index 2b0c30417e7..a88c1a94e8c 100644 --- a/code/modules/projectiles/projectile/beams.dm +++ b/code/modules/projectiles/projectile/beams.dm @@ -114,6 +114,10 @@ armor_penetration_flat = -10 light_color = LIGHT_COLOR_BLUE +/obj/projectile/beam/disabler/swarmer + name = "weakened disabler beam" + damage = 20 + /obj/projectile/beam/disabler/fake name = "weakened disabler beam" nodamage = 1 diff --git a/icons/mob/swarmer.dmi b/icons/mob/swarmer.dmi index bc495e8199e..5ef766fabcc 100644 Binary files a/icons/mob/swarmer.dmi and b/icons/mob/swarmer.dmi differ