diff --git a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
index c24434866ee..b7f4dd7e8ba 100644
--- a/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
+++ b/_maps/map_files/RandomRuins/LavaRuins/lavaland_surface_ash_walker1.dmm
@@ -249,7 +249,7 @@
dir = 1
},
/obj/structure/stone_tile,
-/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck,
+/mob/living/basic/mining/gutlunch/gubbuck,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"aM" = (
@@ -378,7 +378,7 @@
/obj/structure/stone_tile{
dir = 4
},
-/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen,
+/mob/living/basic/mining/gutlunch/guthen,
/turf/simulated/floor/indestructible/boss,
/area/ruin/unpowered/ash_walkers)
"bd" = (
diff --git a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
index 9fb1e246a47..9b351e76dc8 100644
--- a/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
+++ b/_maps/map_files/RandomRuins/SpaceRuins/abandonedzoo.dmm
@@ -98,7 +98,7 @@
/turf/simulated/floor/grass,
/area/ruin/space/unpowered)
"ap" = (
-/mob/living/simple_animal/hostile/asteroid/goldgrub{
+/mob/living/basic/mining/goldgrub{
will_burrow = 0
},
/turf/simulated/floor/plating/asteroid/airless,
diff --git a/code/__DEFINES/ai/blackboard_defines.dm b/code/__DEFINES/ai/blackboard_defines.dm
index 9ed9dc200e0..f41b38f7374 100644
--- a/code/__DEFINES/ai/blackboard_defines.dm
+++ b/code/__DEFINES/ai/blackboard_defines.dm
@@ -14,6 +14,10 @@
#define BB_BASIC_MOB_FLEE_TARGET_HIDING_LOCATION "BB_BASIC_FLEE_TARGET_HIDING_LOCATION"
/// Key defining the targeting strategy for things to flee from
#define BB_FLEE_TARGETING_STRATEGY "BB_FLEE_TARGETING_STRATEGY"
+/// are we ready to breed?
+#define BB_BREED_READY "BB_breed_ready"
+/// maximum kids we can have
+#define BB_MAX_CHILDREN "BB_max_children"
/// Key defining how far we attempt to get away from something we're fleeing from
#define BB_BASIC_MOB_FLEE_DISTANCE "BB_BASIC_FLEE_DISTANCE"
#define DEFAULT_BASIC_FLEE_DISTANCE 9
@@ -59,11 +63,31 @@
/// Key that holds the cooldown for our hunting subtree
#define BB_HUNTING_COOLDOWN(type) "BB_HUNTING_COOLDOWN_[type]"
+
+// Finding adult mob
+/// key holds the adult we found
+#define BB_FOUND_MOM "BB_found_mom"
+/// list of types of mobs we will look for
+#define BB_FIND_MOM_TYPES "BB_find_mom_types"
+/// list of types of mobs we must ignore
+#define BB_IGNORE_MOM_TYPES "BB_ignore_mom_types"
+
+// Baby-making blackboard
+/// Types of animal we can make babies with.
+#define BB_BABIES_PARTNER_TYPES "BB_babies_partner"
+/// Types of animal that we make as a baby.
+#define BB_BABIES_CHILD_TYPES "BB_babies_child"
+/// Current partner target
+#define BB_BABIES_TARGET "BB_babies_target"
+/// Timeout for finding partners when theres too many of us in 1 location
+#define BB_PARTNER_SEARCH_TIMEOUT "BB_partner_search_timeout"
+
/// Chance to randomly acquire a new target
#define BB_RANDOM_AGGRO_CHANCE "BB_random_aggro_chance"
/// Chance to randomly drop all of our targets
#define BB_RANDOM_DEAGGRO_CHANCE "BB_random_deaggro_chance"
+
// Food and eating
/// list of foods this mob likes
diff --git a/code/__HELPERS/trait_helpers.dm b/code/__HELPERS/trait_helpers.dm
index b30e1f20ed0..36724ccf80b 100644
--- a/code/__HELPERS/trait_helpers.dm
+++ b/code/__HELPERS/trait_helpers.dm
@@ -193,6 +193,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_RSG_IMMUNE "rsgimmune" //prevents RSG syringes from piercing your clothing
#define TRAIT_DRASK_SUPERCOOL "drask_supercool"
+/// trait determines if this mob can breed given by /datum/component/breeding
+#define TRAIT_MOB_BREEDER "mob_breeder"
+
#define TRAIT_NO_BONES "no_bones"
#define TRAIT_STURDY_LIMBS "sturdy_limbs"
#define TRAIT_BURN_WOUND_IMMUNE "burn_immune"
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index 4fbe48bfaf5..7b4a3e3e03b 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -57,6 +57,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_NO_BONES" = TRAIT_NO_BONES,
"TRAIT_STURDY_LIMBS" = TRAIT_STURDY_LIMBS,
+ "TRAIT_MOB_BREEDER" = TRAIT_MOB_BREEDER,
"TRAIT_COMIC_SANS" = TRAIT_COMIC_SANS,
"TRAIT_CHAV" = TRAIT_CHAV,
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm b/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm
new file mode 100644
index 00000000000..ff794c1589f
--- /dev/null
+++ b/code/datums/ai/basic_mobs/basic_subtrees/find_parent.dm
@@ -0,0 +1,51 @@
+/datum/ai_planning_subtree/look_for_adult
+ /// how far we must be from the mom
+ var/minimum_distance = 1
+
+/datum/ai_planning_subtree/look_for_adult/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ . = ..()
+ var/mob/target = controller.blackboard[BB_FOUND_MOM]
+ var/mob/baby = controller.pawn
+
+ if(QDELETED(target))
+ controller.queue_behavior(/datum/ai_behavior/find_mom, BB_FIND_MOM_TYPES, BB_IGNORE_MOM_TYPES, BB_FOUND_MOM)
+ return
+
+ if(get_dist(target, baby) > minimum_distance)
+ controller.queue_behavior(/datum/ai_behavior/travel_towards/stop_on_arrival, BB_FOUND_MOM)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ if(!SPT_PROB(15, seconds_per_tick))
+ return
+
+ if(target.stat == DEAD)
+ controller.queue_behavior(/datum/ai_behavior/perform_emote, "cries for their parent!")
+ else
+ controller.queue_behavior(/datum/ai_behavior/perform_emote, "dances around their parent!")
+
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+/datum/ai_behavior/find_mom
+ /// range to look for the mom
+ var/look_range = 7
+
+/datum/ai_behavior/find_mom/perform(seconds_per_tick, datum/ai_controller/controller, mom_key, ignore_mom_key, found_mom)
+ var/mob/living_pawn = controller.pawn
+ var/list/mom_types = controller.blackboard[mom_key]
+ var/list/all_moms = list()
+ var/list/ignore_types = controller.blackboard[ignore_mom_key]
+
+ if(!length(mom_types))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ for(var/mob/mother in oview(look_range, living_pawn))
+ if(!is_type_in_list(mother, mom_types))
+ continue
+ if(is_type_in_list(mother, ignore_types)) // so the not permanent baby and the permanent baby subtype don't follow each other
+ continue
+ all_moms += mother
+
+ if(length(all_moms))
+ controller.set_blackboard_key(found_mom, pick(all_moms))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/make_babies.dm b/code/datums/ai/basic_mobs/basic_subtrees/make_babies.dm
new file mode 100644
index 00000000000..cd1f7e9d465
--- /dev/null
+++ b/code/datums/ai/basic_mobs/basic_subtrees/make_babies.dm
@@ -0,0 +1,97 @@
+/**
+ * Reproduce with a similar mob.
+ */
+/datum/ai_planning_subtree/make_babies
+ operational_datums = list(/datum/component/breed)
+ /// chance to make babies
+ var/chance = 5
+ /// make babies behavior we will use
+ var/datum/ai_behavior/reproduce_behavior = /datum/ai_behavior/make_babies
+
+/datum/ai_planning_subtree/make_babies/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ . = ..()
+
+ if(!SPT_PROB(chance, seconds_per_tick) || controller.blackboard[BB_PARTNER_SEARCH_TIMEOUT] >= world.time)
+ return
+
+ if(controller.blackboard_key_exists(BB_BABIES_TARGET))
+ controller.queue_behavior(reproduce_behavior, BB_BABIES_TARGET, BB_BABIES_CHILD_TYPES)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+ if(controller.pawn.gender == FEMALE || !controller.blackboard[BB_BREED_READY])
+ return
+
+ if(!controller.blackboard[BB_BABIES_PARTNER_TYPES] || !controller.blackboard[BB_BABIES_CHILD_TYPES])
+ return
+
+ // Find target
+ controller.queue_behavior(/datum/ai_behavior/find_partner, BB_BABIES_TARGET, BB_BABIES_PARTNER_TYPES, BB_BABIES_CHILD_TYPES)
+
+#define FIND_PARTNER_COOLDOWN 1 MINUTES
+
+/**
+ * Find a compatible, living partner, if we're also alone.
+ */
+/datum/ai_behavior/find_partner
+ action_cooldown = 5 SECONDS
+ /// Range to look.
+ var/range = 7
+ /// Maximum number of nearby pop
+ var/max_nearby_pop = 3
+
+/datum/ai_behavior/find_partner/perform(seconds_per_tick, datum/ai_controller/controller, target_key, partner_types_key, child_types_key)
+ var/maximum_pop = controller.blackboard[BB_MAX_CHILDREN] || max_nearby_pop
+ var/mob/pawn_mob = controller.pawn
+ var/list/similar_species_types = controller.blackboard[partner_types_key] + controller.blackboard[child_types_key]
+ var/mob/living/living_pawn = controller.pawn
+ var/list/possible_partners = list()
+
+ var/nearby_pop = 0
+ for(var/mob/living/other in oview(range, pawn_mob))
+ if(!pawn_mob.faction_check_mob(other))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ if(!is_type_in_list(other, similar_species_types))
+ continue
+
+ if(++nearby_pop >= maximum_pop)
+ controller.set_blackboard_key(BB_PARTNER_SEARCH_TIMEOUT, world.time + FIND_PARTNER_COOLDOWN)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ if(other.stat != CONSCIOUS) // Check if it's conscious FIRST.
+ continue
+
+ if(other.gender != living_pawn.gender)
+ possible_partners += other
+
+ if(!length(possible_partners))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+
+ controller.set_blackboard_key(target_key, pick(possible_partners))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/**
+ * Reproduce.
+ */
+/datum/ai_behavior/make_babies
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH
+
+/datum/ai_behavior/make_babies/setup(datum/ai_controller/controller, target_key, child_types_key)
+ . = ..()
+ var/atom/target = controller.blackboard[target_key]
+ if(!target)
+ return FALSE
+ set_movement_target(controller, target)
+
+/datum/ai_behavior/make_babies/perform(seconds_per_tick, datum/ai_controller/controller, target_key, child_types_key)
+ var/mob/target = controller.blackboard[target_key]
+ if(QDELETED(target) || target.stat != CONSCIOUS)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+ controller.ai_interact(target = target, intent = INTENT_HELP)
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
+
+/datum/ai_behavior/make_babies/finish_action(datum/ai_controller/controller, succeeded, target_key)
+ . = ..()
+ controller.clear_blackboard_key(target_key)
+
+#undef FIND_PARTNER_COOLDOWN
diff --git a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm b/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
index 0744d33acea..82cd3f47e5b 100644
--- a/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
+++ b/code/datums/ai/basic_mobs/basic_subtrees/mine_walls.dm
@@ -29,3 +29,27 @@
if(!test_turf.is_blocked_turf(source_atom = source))
return TRUE
return FALSE
+
+
+/datum/ai_planning_subtree/mine_walls
+ var/find_wall_behavior = /datum/ai_behavior/find_mineral_wall
+
+/datum/ai_planning_subtree/mine_walls/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ if(controller.blackboard_key_exists(BB_TARGET_MINERAL_WALL))
+ controller.queue_behavior(/datum/ai_behavior/interact_with_target/mine_wall, BB_TARGET_MINERAL_WALL)
+ return SUBTREE_RETURN_FINISH_PLANNING
+ controller.queue_behavior(find_wall_behavior, BB_TARGET_MINERAL_WALL)
+
+/datum/ai_behavior/interact_with_target/mine_wall
+ behavior_flags = AI_BEHAVIOR_REQUIRE_MOVEMENT | AI_BEHAVIOR_REQUIRE_REACH | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+ action_cooldown = 15 SECONDS
+
+/datum/ai_behavior/interact_with_target/mine_wall/perform(seconds_per_tick, datum/ai_controller/controller, target_key)
+ var/mob/living/basic/living_pawn = controller.pawn
+ var/turf/simulated/mineral/target = controller.blackboard[target_key]
+ if(!controller.ai_interact(target = target))
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_FAILED
+ if(istype(target) && istype(target.ore, /datum/ore/gibtonite))
+ living_pawn.manual_emote("sighs...") // accept whats about to happen to us
+
+ return AI_BEHAVIOR_DELAY | AI_BEHAVIOR_SUCCEEDED
diff --git a/code/datums/components/breeding.dm b/code/datums/components/breeding.dm
new file mode 100644
index 00000000000..a585f542121
--- /dev/null
+++ b/code/datums/components/breeding.dm
@@ -0,0 +1,73 @@
+/*
+ * A component to allow us to breed
+ */
+/datum/component/breed
+ /// additional mobs we can breed with
+ var/list/can_breed_with
+ /// weighted list of the possible baby types
+ var/list/baby_paths
+ /// time to wait after breeding
+ var/breed_timer
+ /// AI key we set when we're ready to breed
+ var/breed_key = BB_BREED_READY
+ /// are we ready to breed?
+ var/ready_to_breed = TRUE
+ /// callback after we give birth to the child
+ var/datum/callback/post_birth
+
+/datum/component/breed/Initialize(list/can_breed_with = list(), breed_timer = 40 SECONDS, baby_paths = list(), post_birth)
+ if(!isliving(parent))
+ return COMPONENT_INCOMPATIBLE
+
+ if(ishuman(parent)) // sin detected
+ return COMPONENT_INCOMPATIBLE
+
+ if(!length(baby_paths))
+ stack_trace("attempted to add a breeding component with invalid baby paths!")
+ return
+
+ src.can_breed_with = can_breed_with
+ src.breed_timer = breed_timer
+ src.baby_paths = baby_paths
+ src.post_birth = post_birth
+
+ ADD_TRAIT(parent, TRAIT_SUBTREE_REQUIRED_OPERATIONAL_DATUM, type)
+
+/datum/component/breed/RegisterWithParent()
+ RegisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET, PROC_REF(breed_with_partner))
+ ADD_TRAIT(parent, TRAIT_MOB_BREEDER, ref(src))
+ var/mob/living/parent_mob = parent
+ parent_mob.ai_controller?.set_blackboard_key(breed_key, TRUE)
+
+/datum/component/breed/UnregisterFromParent()
+ UnregisterSignal(parent, COMSIG_HOSTILE_PRE_ATTACKINGTARGET)
+ REMOVE_TRAIT(parent, TRAIT_MOB_BREEDER, ref(src))
+ post_birth = null
+
+
+/datum/component/breed/proc/breed_with_partner(mob/living/source, mob/living/target)
+ SIGNAL_HANDLER // COMSIG_HOSTILE_PRE_ATTACKINGTARGET
+
+ if(!is_type_in_typecache(target, can_breed_with))
+ return
+
+ if(!HAS_TRAIT(target, TRAIT_MOB_BREEDER) || target.gender == source.gender)
+ return
+
+ if(!ready_to_breed)
+ return COMPONENT_HOSTILE_NO_ATTACK
+
+ var/turf/delivery_destination = get_turf(source)
+ var/chosen_baby_path = pickweight(baby_paths)
+ var/atom/baby = new chosen_baby_path(delivery_destination)
+ new /obj/effect/temp_visual/heart(delivery_destination)
+ toggle_status(source)
+
+ addtimer(CALLBACK(src, PROC_REF(toggle_status), source), breed_timer)
+ post_birth?.Invoke(baby, target)
+ return COMPONENT_HOSTILE_NO_ATTACK
+
+/datum/component/breed/proc/toggle_status(mob/living/source)
+ ready_to_breed = !ready_to_breed
+ source.ai_controller?.set_blackboard_key(BB_BREED_READY, ready_to_breed)
+
diff --git a/code/game/objects/effects/landmarks.dm b/code/game/objects/effects/landmarks.dm
index 63371c92f82..44d19b6f38b 100644
--- a/code/game/objects/effects/landmarks.dm
+++ b/code/game/objects/effects/landmarks.dm
@@ -667,14 +667,14 @@ INITIALIZE_IMMEDIATE(/obj/effect/landmark/newplayer_start) //Without this you sp
. = ..()
/obj/effect/landmark/mob_spawner/goldgrub
- mobtype = /mob/living/simple_animal/hostile/asteroid/goldgrub
+ mobtype = /mob/living/basic/mining/goldgrub
/obj/effect/landmark/mob_spawner/gutlunch
- mobtype = /mob/living/simple_animal/hostile/asteroid/gutlunch
+ mobtype = /mob/living/basic/mining/gutlunch
/obj/effect/landmark/mob_spawner/gutlunch/Initialize(mapload)
if(prob(5))
- mobtype = /mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck
+ mobtype = /mob/living/basic/mining/gutlunch/gubbuck
. = ..()
/obj/effect/landmark/mob_spawner/abandoned_minebot
diff --git a/code/game/objects/items/stacks/stack.dm b/code/game/objects/items/stacks/stack.dm
index d7d151a4dd0..c65afc2bfba 100644
--- a/code/game/objects/items/stacks/stack.dm
+++ b/code/game/objects/items/stacks/stack.dm
@@ -307,7 +307,7 @@
return to_transfer
/obj/item/stack/proc/split(mob/user, amount)
- var/obj/item/stack/material = new type(loc, amount)
+ var/obj/item/stack/material = new type(loc, amount, FALSE)
material.copy_evidences(src)
if(isliving(user))
add_fingerprint(user)
diff --git a/code/game/objects/structures/monster_spawner.dm b/code/game/objects/structures/monster_spawner.dm
index f3ced161b44..0a6897c2139 100644
--- a/code/game/objects/structures/monster_spawner.dm
+++ b/code/game/objects/structures/monster_spawner.dm
@@ -53,13 +53,13 @@
max_mobs = 3
icon = 'icons/mob/nest.dmi'
spawn_text = "crawls out of"
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/basic/mining/hivelord, /mob/living/basic/mining/basilisk)
+ mob_types = list(/mob/living/basic/mining/goldgrub, /mob/living/simple_animal/hostile/asteroid/goliath, /mob/living/basic/mining/hivelord, /mob/living/basic/mining/basilisk)
faction = list("mining")
/obj/structure/spawner/mining/goldgrub
name = "goldgrub den"
desc = "A den housing a nest of goldgrubs, annoying but arguably much better than anything else you'll find in a nest."
- mob_types = list(/mob/living/simple_animal/hostile/asteroid/goldgrub)
+ mob_types = list(/mob/living/basic/mining/goldgrub)
/obj/structure/spawner/mining/goliath
name = "goliath den"
diff --git a/code/game/objects/structures/nest.dm b/code/game/objects/structures/nest.dm
index 891d78046b5..46fdcd4a2ec 100644
--- a/code/game/objects/structures/nest.dm
+++ b/code/game/objects/structures/nest.dm
@@ -71,7 +71,7 @@
visible_message("\A [spawned_mob.name] crawls out of \the [name]!")
/obj/structure/nest/lavaland
- spawn_mob_options = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast, /mob/living/simple_animal/hostile/asteroid/goldgrub)
+ spawn_mob_options = list(/mob/living/simple_animal/hostile/asteroid/goliath/beast, /mob/living/basic/mining/goldgrub)
/obj/structure/nest/carppuppy
spawn_mob_options = list(/mob/living/basic/carp, /mob/living/simple_animal/pet/dog/corgi/puppy/void)
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index 2088bb6636f..d47a2e511a2 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -84,7 +84,7 @@
if("lavaland")//Free cult metal, I guess.
NewTerrainFloors = /turf/simulated/floor/plating/false_asteroid
NewTerrainWalls = /turf/simulated/wall/cult
- NewFlora = list(/mob/living/simple_animal/hostile/asteroid/goldgrub)
+ NewFlora = list(/mob/living/basic/mining/goldgrub)
florachance = 1
if("winter") //Snow terrain is slow to move in and cold! Get the assistants to shovel your driveway.
NewTerrainFloors = /turf/simulated/floor/snow // Needs to be updated after turf update
diff --git a/code/modules/mob/living/basic/mining/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/mining/goldgrub/goldgrub.dm
new file mode 100644
index 00000000000..a9528cb7be1
--- /dev/null
+++ b/code/modules/mob/living/basic/mining/goldgrub/goldgrub.dm
@@ -0,0 +1,54 @@
+/// An ore-devouring but easily scared creature
+/mob/living/basic/mining/goldgrub
+ name = "goldgrub"
+ desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name."
+ icon_state = "Goldgrub"
+ icon_living = "Goldgrub"
+ icon_aggro = "Goldgrub_alert"
+ icon_dead = "Goldgrub_dead"
+ icon_gib = "syndicate_gib"
+ friendly_verb_continuous = "harmlessly rolls into"
+ friendly_verb_simple = "harmlessly roll into"
+ speed = 3
+ maxHealth = 45
+ health = 45
+ harm_intent_damage = 5
+ attack_verb_continuous = "barrels into"
+ attack_verb_simple = "barrel into"
+ a_intent = INTENT_HELP
+ speak_emote = list("screeches")
+ throw_blocked_message = "sinks in slowly, before being pushed out of "
+ death_message = "spits up the contents of its stomach before dying!"
+ status_flags = CANPUSH
+ contains_xeno_organ = TRUE
+ surgery_container = /datum/xenobiology_surgery_container/goldgrub
+ ai_controller = /datum/ai_controller/basic_controller/goldgrub
+ /// Hungry hungry goldgrubs
+ var/food_types = list(/obj/item/stack/ore)
+ /// Does this creature burrow?
+ var/will_burrow = TRUE
+
+/mob/living/basic/mining/goldgrub/Initialize(mapload)
+ . = ..()
+ faction |= "goldgrub"
+
+ AddElement(/datum/element/basic_eating, food_types_ = food_types, add_to_contents_ = TRUE)
+ ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(food_types))
+
+ var/i = rand(1,3)
+ while(i)
+ loot += pick(/obj/item/stack/ore/silver, /obj/item/stack/ore/gold, /obj/item/stack/ore/uranium, /obj/item/stack/ore/diamond)
+ i--
+
+/mob/living/basic/mining/goldgrub/death(gibbed)
+ barf_contents(gibbed)
+ return ..()
+
+/mob/living/basic/mining/goldgrub/proc/barf_contents(gibbed)
+ for(var/obj/item/stack/ore/ore in src)
+ ore.forceMove(loc)
+
+/mob/living/basic/mining/goldgrub/bullet_act(obj/item/projectile/P)
+ if(P.armour_penetration_flat + P.armour_penetration_percentage >= 100)
+ return ..()
+ visible_message("[P.name] was repelled by [name]'s girth!")
diff --git a/code/modules/mob/living/basic/mining/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/mining/goldgrub/goldgrub_ai.dm
new file mode 100644
index 00000000000..804e9d0e5a4
--- /dev/null
+++ b/code/modules/mob/living/basic/mining/goldgrub/goldgrub_ai.dm
@@ -0,0 +1,44 @@
+/datum/ai_controller/basic_controller/goldgrub
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_ORE_IGNORE_TYPES = list(/obj/item/stack/ore/iron, /obj/item/stack/ore/glass),
+ BB_AGGRO_RANGE = 5,
+ )
+
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk/less_walking
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/simple_find_target,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/dig_away_from_danger,
+ /datum/ai_planning_subtree/find_food,
+ /datum/ai_planning_subtree/mine_walls,
+ )
+
+/// Only dig away if humans are around
+/datum/ai_planning_subtree/dig_away_from_danger
+
+/datum/ai_planning_subtree/dig_away_from_danger/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
+ var/has_target = controller.blackboard_key_exists(BB_BASIC_MOB_CURRENT_TARGET)
+
+ var/mob/living/basic/mining/goldgrub/grub = controller.pawn
+ // Someone is nearby, its time to escape
+ if(grub.will_burrow && has_target && ishuman(controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET]))
+ controller.queue_behavior(/datum/ai_behavior/burrow_away)
+ return SUBTREE_RETURN_FINISH_PLANNING
+
+/datum/ai_behavior/burrow_away
+ behavior_flags = AI_BEHAVIOR_MOVE_AND_PERFORM | AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION
+ /// How long until the grub burrows
+ var/chase_time = 10 SECONDS
+
+/datum/ai_behavior/burrow_away/perform(seconds_per_tick, datum/ai_controller/controller)
+ . = ..()
+ var/mob/living/basic/mining/goldgrub/grub = controller.pawn
+ grub.addtimer(CALLBACK(src, PROC_REF(burrow), controller.pawn), chase_time)
+
+/// Begin the chase to kill the mob in time
+/datum/ai_behavior/burrow_away/proc/burrow(mob/living/pawn)
+ if(pawn.stat == CONSCIOUS)
+ pawn.visible_message("[pawn] buries into the ground, vanishing from sight!")
+ qdel(pawn)
diff --git a/code/modules/mob/living/basic/mining/gutlunch.dm b/code/modules/mob/living/basic/mining/gutlunch.dm
new file mode 100644
index 00000000000..40aeec5e249
--- /dev/null
+++ b/code/modules/mob/living/basic/mining/gutlunch.dm
@@ -0,0 +1,143 @@
+/// Gutlunches, passive mods that devour blood and gibs
+/mob/living/basic/mining/gutlunch
+ name = "gutlunch"
+ desc = "A scavenger that eats raw meat, often found alongside ash walkers. Produces a thick, medicinally nutritious milk."
+ icon_state = "gutlunch"
+ icon_living = "gutlunch"
+ icon_dead = "gutlunch"
+ speak_emote = list("warbles", "quavers")
+ weather_immunities = list("lava","ash")
+ faction = list("mining", "ashwalker")
+ response_help_continuous = "pets"
+ response_help_simple = "pet"
+ response_disarm_continuous = "gently pushes aside"
+ response_disarm_simple = "gently push aside"
+ response_harm_continuous = "squishes"
+ response_harm_simple = "squish"
+ friendly_verb_continuous = "pinches"
+ friendly_verb_simple = "pinch"
+ density = FALSE
+ obj_damage = 0
+ environment_smash = ENVIRONMENT_SMASH_NONE
+ a_intent = INTENT_HELP
+ ventcrawler = VENTCRAWLER_ALWAYS
+ gold_core_spawnable = FRIENDLY_SPAWN
+ loot = list(/obj/effect/decal/cleanable/blood/gibs)
+ deathmessage = "is pulped into bugmash."
+
+ var/list/food_types = list(
+ /obj/effect/decal/cleanable/blood/gibs,
+ /obj/item/organ/internal/eyes,
+ /obj/item/organ/internal/heart,
+ /obj/item/organ/internal/lungs,
+ /obj/item/organ/internal/liver,
+ /obj/item/organ/internal/kidneys,
+ /obj/item/organ/internal/appendix
+ )
+ var/obj/item/udder/gutlunch/udder
+ /// can we breed?
+ var/can_breed = TRUE
+
+/mob/living/basic/mining/gutlunch/Initialize(mapload)
+ . = ..()
+ udder = new()
+ // Have all eating components share the same exact list per mob subtype
+ var/static/list/static_food_types
+ if(!static_food_types)
+ static_food_types = food_types.Copy()
+
+ AddElement(/datum/element/basic_eating, food_types_ = static_food_types)
+ AddElement(/datum/element/ai_retaliate)
+ ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(static_food_types))
+ if(can_breed)
+ add_breeding_component()
+
+/mob/living/basic/mining/gutlunch/Destroy()
+ QDEL_NULL(udder)
+ return ..()
+
+/mob/living/basic/mining/gutlunch/regenerate_icons()
+ ..()
+ if(udder.reagents.total_volume == udder.reagents.maximum_volume)
+ add_overlay("gl_full")
+
+/mob/living/basic/mining/gutlunch/item_interaction(mob/living/user, obj/item/O, list/modifiers)
+ if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass))
+ udder.milkAnimal(O, user)
+ regenerate_icons()
+ return ITEM_INTERACT_COMPLETE
+
+/obj/item/udder/gutlunch
+ name = "nutrient sac"
+
+/obj/item/udder/gutlunch/generateMilk()
+ if(prob(60))
+ reagents.add_reagent("cream", rand(2, 5))
+ if(prob(45))
+ reagents.add_reagent("salglu_solution", rand(2, 5))
+ if(prob(30))
+ reagents.add_reagent("epinephrine", rand(2, 5))
+
+
+/mob/living/basic/mining/gutlunch/proc/add_breeding_component()
+ var/static/list/partner_paths = typecacheof(list(/mob/living/basic/mining/gutlunch))
+ var/static/list/baby_paths = list(
+ /mob/living/basic/mining/gutlunch/grublunch = 1,
+ )
+
+ AddComponent(\
+ /datum/component/breed,\
+ can_breed_with = partner_paths,\
+ baby_paths = baby_paths,\
+ breed_timer = 3 MINUTES,\
+ )
+
+// Male gutlunch. They're smaller and more colorful!
+/mob/living/basic/mining/gutlunch/gubbuck
+ name = "gubbuck"
+ gender = MALE
+ ai_controller = /datum/ai_controller/basic_controller/gutlunch/gubbuck
+
+/mob/living/basic/mining/gutlunch/gubbuck/Initialize(mapload)
+ . = ..()
+ add_atom_colour(pick("#E39FBB", "#D97D64", "#CF8C4A"), FIXED_COLOUR_PRIORITY)
+ resize = 0.85
+ update_transform()
+
+/// Lady gutlunch. They make the babby.
+/mob/living/basic/mining/gutlunch/guthen
+ name = "guthen"
+ gender = FEMALE
+ ai_controller = /datum/ai_controller/basic_controller/gutlunch/guthen
+
+/// Baby gutlunch
+/mob/living/basic/mining/gutlunch/grublunch
+ name = "grublunch"
+ food_types = list() //They don't eat.
+ gold_core_spawnable = NO_SPAWN
+ ai_controller = /datum/ai_controller/basic_controller/gutlunch/gutlunch_baby
+ var/growth = 0
+
+/mob/living/basic/mining/gutlunch/grublunch/Initialize(mapload)
+ . = ..()
+ add_atom_colour("#9E9E9E", FIXED_COLOUR_PRIORITY) // Somewhat hidden
+ resize = 0.45
+ update_transform()
+
+/mob/living/basic/mining/gutlunch/grublunch/Life()
+ ..()
+ growth++
+ if(growth > 50) //originally used a timer for this but was more problem that it's worth.
+ growUp()
+
+/mob/living/basic/mining/gutlunch/grublunch/proc/growUp()
+ var/mob/living/L
+ if(prob(45))
+ L = new /mob/living/basic/mining/gutlunch/gubbuck(loc)
+ else
+ L = new /mob/living/basic/mining/gutlunch/guthen(loc)
+ mind?.transfer_to(L)
+ L.faction = faction.Copy()
+ L.setDir(dir)
+ visible_message("[src] grows up into [L].")
+ qdel(src)
diff --git a/code/modules/mob/living/basic/mining/gutlunch_ai.dm b/code/modules/mob/living/basic/mining/gutlunch_ai.dm
new file mode 100644
index 00000000000..581174a249f
--- /dev/null
+++ b/code/modules/mob/living/basic/mining/gutlunch_ai.dm
@@ -0,0 +1,47 @@
+/datum/ai_controller/basic_controller/gutlunch
+ ai_movement = /datum/ai_movement/basic_avoidance
+ idle_behavior = /datum/idle_behavior/idle_random_walk
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/target_retaliate,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/make_babies,
+ /datum/ai_planning_subtree/find_food,
+ /datum/ai_planning_subtree/random_speech/gutlunch,
+ )
+
+/datum/ai_controller/basic_controller/gutlunch/on_mob_eat()
+ . = ..()
+ var/mob/living/basic/mining/gutlunch/gut = pawn
+ gut.udder.generateMilk()
+ gut.regenerate_icons()
+
+/datum/ai_controller/basic_controller/gutlunch/gubbuck
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/mining/gutlunch/guthen),
+ BB_BABIES_CHILD_TYPES = list(/mob/living/basic/mining/gutlunch/grublunch),
+ BB_MAX_CHILDREN = 5,
+ )
+
+/datum/ai_controller/basic_controller/gutlunch/guthen
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_BABIES_PARTNER_TYPES = list(/mob/living/basic/mining/gutlunch/gubbuck),
+ BB_BABIES_CHILD_TYPES = list(/mob/living/basic/mining/gutlunch/grublunch),
+ BB_MAX_CHILDREN = 5,
+ )
+
+/datum/ai_controller/basic_controller/gutlunch/gutlunch_baby
+ blackboard = list(
+ BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
+ BB_FIND_MOM_TYPES = list(/mob/living/basic/mining/gutlunch),
+ )
+ planning_subtrees = list(
+ /datum/ai_planning_subtree/target_retaliate,
+ /datum/ai_planning_subtree/flee_target,
+ /datum/ai_planning_subtree/look_for_adult,
+ )
+
+/datum/ai_planning_subtree/random_speech/gutlunch
+ emote_hear = list("trills.")
+ emote_see = list("sniffs.", "burps.")
diff --git a/code/modules/mob/living/carbon/human/species/unathi.dm b/code/modules/mob/living/carbon/human/species/unathi.dm
index a86247eb1ad..04e20ea2042 100644
--- a/code/modules/mob/living/carbon/human/species/unathi.dm
+++ b/code/modules/mob/living/carbon/human/species/unathi.dm
@@ -143,11 +143,13 @@
fire.Remove(H)
var/datum/action/innate/unathi_ignite/ash_walker/fire = new()
fire.Grant(H)
+ H.faction |= "ashwalker"
/datum/species/unathi/ashwalker/on_species_loss(mob/living/carbon/human/H)
..()
for(var/datum/action/innate/unathi_ignite/ash_walker/fire in H.actions)
fire.Remove(H)
+ H.faction -= "ashwalker"
/datum/species/unathi/ashwalker/movement_delay(mob/living/carbon/human/H)
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm b/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm
deleted file mode 100644
index a57ad115237..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/mining/goldgrub.dm
+++ /dev/null
@@ -1,82 +0,0 @@
-//An ore-devouring but easily scared creature
-/mob/living/simple_animal/hostile/asteroid/goldgrub
- name = "goldgrub"
- desc = "A worm that grows fat from eating everything in its sight. Seems to enjoy precious metals and other shiny things, hence the name."
- icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
- icon_state = "Goldgrub"
- icon_living = "Goldgrub"
- icon_aggro = "Goldgrub_alert"
- icon_dead = "Goldgrub_dead"
- icon_gib = "syndicate_gib"
- mob_biotypes = MOB_ORGANIC | MOB_BEAST
- move_to_delay = 5
- friendly = "harmlessly rolls into"
- maxHealth = 45
- health = 45
- harm_intent_damage = 5
- attacktext = "barrels into"
- attack_sound = 'sound/weapons/punch1.ogg'
- a_intent = INTENT_HELP
- speak_emote = list("screeches")
- throw_message = "sinks in slowly, before being pushed out of "
- deathmessage = "spits up the contents of its stomach before dying!"
- status_flags = CANPUSH
- search_objects = 1
- contains_xeno_organ = TRUE
- surgery_container = /datum/xenobiology_surgery_container/goldgrub
- wanted_objects = list(/obj/item/stack/ore/diamond, /obj/item/stack/ore/gold, /obj/item/stack/ore/silver,
- /obj/item/stack/ore/uranium)
-
- var/chase_time = 100
- var/will_burrow = TRUE
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/Initialize(mapload)
- . = ..()
- faction |= "goldgrub"
-
- var/i = rand(1,3)
- while(i)
- loot += pick(/obj/item/stack/ore/silver, /obj/item/stack/ore/gold, /obj/item/stack/ore/uranium, /obj/item/stack/ore/diamond)
- i--
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(new_target)
- target = new_target
- if(target != null)
- if(istype(target, /obj/item/stack/ore) && length(loot) < 10)
- visible_message("[src] looks at [target.name] with hungry eyes.")
- else if(isliving(target))
- Aggro()
- visible_message("[src] tries to flee from [target.name]!")
- retreat_distance = 10
- minimum_distance = 10
- if(will_burrow)
- addtimer(CALLBACK(src, PROC_REF(Burrow)), chase_time)
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/AttackingTarget()
- if(istype(target, /obj/item/stack/ore))
- EatOre(target)
- return
- return ..()
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/EatOre(atom/targeted_ore)
- for(var/obj/item/stack/ore/O in get_turf(targeted_ore))
- if(length(loot) < 10)
- var/using = min(10 - length(loot), O.amount)
- for(var/i in 1 to using)
- loot += O.type
- O.use(using)
- visible_message("The ore was swallowed whole!")
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time
- if(stat == CONSCIOUS)
- visible_message("[src] buries into the ground, vanishing from sight!")
- qdel(src)
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(obj/item/projectile/P)
- if(P.armour_penetration_flat + P.armour_penetration_percentage >= 100)
- return ..()
- visible_message("[P.name] was repelled by [name]'s girth!")
-
-/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(amount, updating_health = TRUE)
- vision_range = 9
- . = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm b/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm
deleted file mode 100644
index 7ff8fbe854c..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/mining/gutlunch.dm
+++ /dev/null
@@ -1,164 +0,0 @@
-//Gutlunches, passive mods that devour blood and gibs
-/mob/living/simple_animal/hostile/asteroid/gutlunch
- name = "gutlunch"
- desc = "A scavenger that eats raw meat, often found alongside ash walkers. Produces a thick, medicinally nutritious milk."
- icon = 'icons/mob/lavaland/lavaland_monsters.dmi'
- icon_state = "gutlunch"
- icon_living = "gutlunch"
- icon_dead = "gutlunch"
- mob_biotypes = MOB_ORGANIC | MOB_BEAST
- speak_emote = list("warbles", "quavers")
- emote_hear = list("trills.")
- emote_see = list("sniffs.", "burps.")
- weather_immunities = list("lava","ash")
- faction = list("mining", "ashwalker")
- density = FALSE
- speak_chance = 1
- turns_per_move = 8
- obj_damage = 0
- environment_smash = ENVIRONMENT_SMASH_NONE
- move_to_delay = 15
- response_help = "pets"
- response_disarm = "gently pushes aside"
- response_harm = "squishes"
- friendly = "pinches"
- a_intent = INTENT_HELP
- ventcrawler = VENTCRAWLER_ALWAYS
- gold_core_spawnable = FRIENDLY_SPAWN
- stat_attack = UNCONSCIOUS
- gender = NEUTER
- stop_automated_movement_when_pulled = TRUE
- stat_exclusive = TRUE
- robust_searching = TRUE
- search_objects = 3 //Ancient simplemob AI shitcode. This makes them ignore all other mobs.
- del_on_death = TRUE
- loot = list(/obj/effect/decal/cleanable/blood/gibs)
- deathmessage = "is pulped into bugmash."
-
- animal_species = /mob/living/simple_animal/hostile/asteroid/gutlunch
- childtype = list(/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch = 100)
-
- wanted_objects = list(/obj/effect/decal/cleanable/blood/gibs, /obj/item/organ/internal/eyes,
- /obj/item/organ/internal/heart, /obj/item/organ/internal/lungs,
- /obj/item/organ/internal/liver, /obj/item/organ/internal/kidneys,
- /obj/item/organ/internal/appendix)
- var/obj/item/udder/gutlunch/udder = null
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/Initialize(mapload)
- udder = new()
- . = ..()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/Destroy()
- QDEL_NULL(udder)
- return ..()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/regenerate_icons()
- ..()
- if(udder.reagents.total_volume == udder.reagents.maximum_volume)
- add_overlay("gl_full")
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/item_interaction(mob/living/user, obj/item/O, list/modifiers)
- if(stat == CONSCIOUS && istype(O, /obj/item/reagent_containers/glass))
- udder.milkAnimal(O, user)
- regenerate_icons()
- return ITEM_INTERACT_COMPLETE
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/CanAttack(atom/the_target) // Gutlunch-specific version of CanAttack to handle stupid stat_exclusive = true crap so we don't have to do it for literally every single simple_animal/hostile except the two that spawn in lavaland
- if(isturf(the_target) || !the_target || the_target.type == /atom/movable/lighting_object) // bail out on invalids
- return FALSE
-
- if(see_invisible < the_target.invisibility)//Target's invisible to us, forget it
- return FALSE
-
- if(isliving(the_target))
- var/mob/living/L = the_target
-
- if(faction_check_mob(L) && !attack_same)
- return FALSE
- if(L.stat > stat_attack || L.stat != stat_attack && stat_exclusive)
- return FALSE
-
- return TRUE
-
- if(isobj(the_target) && is_type_in_typecache(the_target, wanted_objects))
- return TRUE
-
- return FALSE
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/AttackingTarget()
- if(is_type_in_typecache(target,wanted_objects)) //we eats
- udder.generateMilk()
- regenerate_icons()
- visible_message("[src] slurps up [target].")
- qdel(target)
- return ..()
-
-/obj/item/udder/gutlunch
- name = "nutrient sac"
-
-/obj/item/udder/gutlunch/generateMilk()
- if(prob(60))
- reagents.add_reagent("cream", rand(2, 5))
- if(prob(45))
- reagents.add_reagent("salglu_solution", rand(2, 5))
- if(prob(30))
- reagents.add_reagent("epinephrine", rand(2, 5))
-
-
-//Male gutlunch. They're smaller and more colorful!
-/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck
- name = "gubbuck"
- gender = MALE
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck/Initialize(mapload)
- . = ..()
- add_atom_colour(pick("#E39FBB", "#D97D64", "#CF8C4A"), FIXED_COLOUR_PRIORITY)
- resize = 0.85
- update_transform()
-
-//Lady gutlunch. They make the babby.
-/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen
- name = "guthen"
- gender = FEMALE
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen/Life()
- ..()
- if(udder.reagents.total_volume == udder.reagents.maximum_volume) //Only breed when we're full.
- make_babies()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/guthen/make_babies()
- . = ..()
- if(.)
- udder.reagents.clear_reagents()
- regenerate_icons()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch
- name = "grublunch"
- wanted_objects = list() //They don't eat.
- gold_core_spawnable = NO_SPAWN
- var/growth = 0
-
-//Baby gutlunch
-/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch/Initialize(mapload)
- . = ..()
- add_atom_colour("#9E9E9E", FIXED_COLOUR_PRIORITY) //Somewhat hidden
- resize = 0.45
- update_transform()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch/Life()
- ..()
- growth++
- if(growth > 50) //originally used a timer for this but was more problem that it's worth.
- growUp()
-
-/mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch/proc/growUp()
- var/mob/living/L
- if(prob(45))
- L = new /mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck(loc)
- else
- L = new /mob/living/simple_animal/hostile/asteroid/gutlunch/guthen(loc)
- mind?.transfer_to(L)
- L.faction = faction.Copy()
- L.setDir(dir)
- visible_message("[src] grows up into [L].")
- qdel(src)
diff --git a/paradise.dme b/paradise.dme
index cd472d0a44c..317dd1fe2a5 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -476,8 +476,10 @@
#include "code\datums\ai\basic_mobs\basic_subtrees\attack_obstacle_in_path.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\capricious_retaliate.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\find_food.dm"
+#include "code\datums\ai\basic_mobs\basic_subtrees\find_parent.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\flee_target.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\maintain_distance.dm"
+#include "code\datums\ai\basic_mobs\basic_subtrees\make_babies.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\mine_walls.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\prowl.dm"
#include "code\datums\ai\basic_mobs\basic_subtrees\random_speech.dm"
@@ -521,6 +523,7 @@
#include "code\datums\components\basic_mob_aggro_emote.dm"
#include "code\datums\components\boomerang.dm"
#include "code\datums\components\boss_music.dm"
+#include "code\datums\components\breeding.dm"
#include "code\datums\components\caltrop.dm"
#include "code\datums\components\codeword_hearing.dm"
#include "code\datums\components\connect_containers.dm"
@@ -2458,13 +2461,22 @@
#include "code\modules\mob\living\basic\hostile\killertomato.dm"
#include "code\modules\mob\living\basic\hostile\pirate.dm"
#include "code\modules\mob\living\basic\hostile\skeleton_mob.dm"
-#include "code\modules\mob\living\basic\hostile\soviet.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_ai.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_drone.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_hunter.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_maid.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_queen.dm"
#include "code\modules\mob\living\basic\hostile\alien\alien_mob_sentinel.dm"
+#include "code\modules\mob\living\basic\mining\basilisk.dm"
+#include "code\modules\mob\living\basic\mining\basilisk_ai.dm"
+#include "code\modules\mob\living\basic\mining\gutlunch.dm"
+#include "code\modules\mob\living\basic\mining\gutlunch_ai.dm"
+#include "code\modules\mob\living\basic\mining\hivelord.dm"
+#include "code\modules\mob\living\basic\mining\hivelord_ai.dm"
+#include "code\modules\mob\living\basic\mining\goldgrub\goldgrub.dm"
+#include "code\modules\mob\living\basic\mining\goldgrub\goldgrub_ai.dm"
+#include "code\modules\mob\living\basic\mining\mining_mobs.dm"
+#include "code\modules\mob\living\basic\hostile\soviet.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_actions.dm"
#include "code\modules\mob\living\basic\hostile\giant_spider\giant_spider_ai.dm"
@@ -2474,11 +2486,6 @@
#include "code\modules\mob\living\basic\minebots\minebot_abilities.dm"
#include "code\modules\mob\living\basic\minebots\minebot_ai.dm"
#include "code\modules\mob\living\basic\minebots\minebot_upgrades.dm"
-#include "code\modules\mob\living\basic\mining\basilisk.dm"
-#include "code\modules\mob\living\basic\mining\basilisk_ai.dm"
-#include "code\modules\mob\living\basic\mining\hivelord.dm"
-#include "code\modules\mob\living\basic\mining\hivelord_ai.dm"
-#include "code\modules\mob\living\basic\mining\mining_mobs.dm"
#include "code\modules\mob\living\basic\nether_mobs\blankbody.dm"
#include "code\modules\mob\living\basic\nether_mobs\faithless.dm"
#include "code\modules\mob\living\basic\nether_mobs\migo.dm"
@@ -2694,9 +2701,7 @@
#include "code\modules\mob\living\simple_animal\hostile\megafauna\legion.dm"
#include "code\modules\mob\living\simple_animal\hostile\megafauna\megafauna.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\abandoned_minebot.dm"
-#include "code\modules\mob\living\simple_animal\hostile\mining\goldgrub.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\goliath.dm"
-#include "code\modules\mob\living\simple_animal\hostile\mining\gutlunch.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\mining.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\elites\elite.dm"
#include "code\modules\mob\living\simple_animal\hostile\mining\elites\goliath_broodmother.dm"