diff --git a/_maps/RandomRuins/SpaceRuins/abandonedzoo.dmm b/_maps/RandomRuins/SpaceRuins/abandonedzoo.dmm index ed62249b2c1..136697eacc1 100644 --- a/_maps/RandomRuins/SpaceRuins/abandonedzoo.dmm +++ b/_maps/RandomRuins/SpaceRuins/abandonedzoo.dmm @@ -364,9 +364,7 @@ /turf/open/floor/iron/dark/side, /area/ruin/space/has_grav/abandonedzoo) "vU" = ( -/mob/living/simple_animal/hostile/asteroid/goldgrub{ - will_burrow = 0 - }, +/mob/living/basic/mining/goldgrub, /turf/open/misc/asteroid, /area/ruin/space/has_grav/abandonedzoo) "wc" = ( diff --git a/code/__DEFINES/ai/monsters.dm b/code/__DEFINES/ai/monsters.dm index 0cfd7c1bb7b..4915ff9cbc8 100644 --- a/code/__DEFINES/ai/monsters.dm +++ b/code/__DEFINES/ai/monsters.dm @@ -106,3 +106,16 @@ /// our acid slip ability #define BB_ARACHNID_SLIP "BB_arachnid_slip" +// goldgrub keys +/// key that tells if a storm is coming +#define BB_STORM_APPROACHING "BB_storm_approaching" +/// key that tells the wall we will mine +#define BB_TARGET_MINERAL_WALL "BB_target_mineral_wall" +/// key that holds our spit ability +#define BB_SPIT_ABILITY "BB_spit_ability" +/// key that holds our dig ability +#define BB_BURROW_ABILITY "BB_burrow_ability" +/// key that holds the ore we will eat +#define BB_ORE_TARGET "BB_ore_target" +/// which ore types we will not eat +#define BB_ORE_IGNORE_TYPES "BB_ore_ignore_types" diff --git a/code/datums/components/ai_listen_to_weather.dm b/code/datums/components/ai_listen_to_weather.dm new file mode 100644 index 00000000000..a7bb95ee8c1 --- /dev/null +++ b/code/datums/components/ai_listen_to_weather.dm @@ -0,0 +1,36 @@ +/** + * given to a mob to set a key on or off when a storm is coming or ending + */ +/datum/component/ai_listen_to_weather + ///what weather type are we listening to + var/weather_type + ///what blackboard key are we setting + var/weather_key + +/datum/component/ai_listen_to_weather/Initialize(weather_type = /datum/weather/ash_storm, weather_key = BB_STORM_APPROACHING) + if(!isliving(parent)) + return COMPONENT_INCOMPATIBLE + src.weather_type = weather_type + src.weather_key = weather_key + +/datum/component/ai_listen_to_weather/RegisterWithParent() + RegisterSignal(SSdcs, COMSIG_WEATHER_START(weather_type), PROC_REF(storm_start)) + RegisterSignal(SSdcs, COMSIG_WEATHER_END(weather_type), PROC_REF(storm_end)) + +/datum/component/ai_listen_to_weather/UnregisterFromParent() + UnregisterSignal(SSdcs, list(COMSIG_WEATHER_START(weather_type), COMSIG_WEATHER_END(weather_type))) + +/datum/component/ai_listen_to_weather/proc/storm_start() + SIGNAL_HANDLER + + var/mob/living/basic/source = parent + if(!source.ai_controller) + return + source.ai_controller.CancelActions() + source.ai_controller.set_blackboard_key(weather_key, TRUE) + +/datum/component/ai_listen_to_weather/proc/storm_end() + SIGNAL_HANDLER + + var/mob/living/basic/source = parent + source.ai_controller?.set_blackboard_key(weather_key, FALSE) diff --git a/code/datums/components/riding/riding_mob.dm b/code/datums/components/riding/riding_mob.dm index b3a0f70ff6a..a546508e505 100644 --- a/code/datums/components/riding/riding_mob.dm +++ b/code/datums/components/riding/riding_mob.dm @@ -473,3 +473,13 @@ if(!istype(charger)) return ..() return charger.summoner == user + +/datum/component/riding/creature/goldgrub + +/datum/component/riding/creature/goldgrub/handle_specials() + . = ..() + set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(11, 3), TEXT_SOUTH = list(11, 3), TEXT_EAST = list(9, 3), TEXT_WEST = list(14, 3))) + set_vehicle_dir_layer(SOUTH, ABOVE_MOB_LAYER) + set_vehicle_dir_layer(NORTH, OBJ_LAYER) + set_vehicle_dir_layer(EAST, OBJ_LAYER) + set_vehicle_dir_layer(WEST, OBJ_LAYER) diff --git a/code/datums/mapgen/CaveGenerator.dm b/code/datums/mapgen/CaveGenerator.dm index 2e880d78f9f..5578b440d23 100644 --- a/code/datums/mapgen/CaveGenerator.dm +++ b/code/datums/mapgen/CaveGenerator.dm @@ -53,9 +53,9 @@ if(!weighted_mob_spawn_list) weighted_mob_spawn_list = list( /mob/living/basic/mining/basilisk = 4, + /mob/living/basic/mining/goldgrub = 1, /mob/living/basic/mining/goliath/ancient = 5, /mob/living/simple_animal/hostile/asteroid/hivelord = 3, - /mob/living/simple_animal/hostile/asteroid/goldgrub = 1, ) mob_spawn_list = expand_weights(weighted_mob_spawn_list) mob_spawn_no_mega_list = expand_weights(weighted_mob_spawn_list - SPAWN_MEGAFAUNA) diff --git a/code/datums/mapgen/Cavegens/IcemoonCaves.dm b/code/datums/mapgen/Cavegens/IcemoonCaves.dm index fcb4e1d712e..89a153aedd7 100644 --- a/code/datums/mapgen/Cavegens/IcemoonCaves.dm +++ b/code/datums/mapgen/Cavegens/IcemoonCaves.dm @@ -5,7 +5,7 @@ weighted_mob_spawn_list = list( /mob/living/basic/mining/lobstrosity = 15, - /mob/living/simple_animal/hostile/asteroid/goldgrub = 10, + /mob/living/basic/mining/goldgrub = 10, /mob/living/simple_animal/hostile/asteroid/hivelord/legion/snow = 50, /mob/living/simple_animal/hostile/asteroid/polarbear = 30, /mob/living/simple_animal/hostile/asteroid/wolf = 50, diff --git a/code/datums/mapgen/Cavegens/LavalandGenerator.dm b/code/datums/mapgen/Cavegens/LavalandGenerator.dm index 12899f6dd4d..be8294fada6 100644 --- a/code/datums/mapgen/Cavegens/LavalandGenerator.dm +++ b/code/datums/mapgen/Cavegens/LavalandGenerator.dm @@ -10,7 +10,7 @@ /mob/living/basic/mining/bileworm = 20, /mob/living/basic/mining/lobstrosity/lava = 20, /mob/living/simple_animal/hostile/asteroid/brimdemon = 20, - /mob/living/simple_animal/hostile/asteroid/goldgrub = 10, + /mob/living/basic/mining/goldgrub = 10, /obj/structure/spawner/lavaland = 2, /obj/structure/spawner/lavaland/goliath = 3, /obj/structure/spawner/lavaland/legion = 3, diff --git a/code/game/objects/effects/spawners/random/lavaland_mobs.dm b/code/game/objects/effects/spawners/random/lavaland_mobs.dm index ab49acec3de..7ad2b3e6e30 100644 --- a/code/game/objects/effects/spawners/random/lavaland_mobs.dm +++ b/code/game/objects/effects/spawners/random/lavaland_mobs.dm @@ -7,11 +7,11 @@ icon_state = "large_egg" loot = list( /mob/living/basic/mining/bileworm = 1, + /mob/living/basic/mining/goldgrub = 1, /mob/living/basic/mining/goliath = 1, /mob/living/basic/mining/lobstrosity/lava = 1, /mob/living/basic/mining/watcher = 1, /mob/living/simple_animal/hostile/asteroid/brimdemon = 1, - /mob/living/simple_animal/hostile/asteroid/goldgrub = 1, /mob/living/simple_animal/hostile/asteroid/hivelord/legion = 1, ) diff --git a/code/game/objects/structures/spawner.dm b/code/game/objects/structures/spawner.dm index 31b6f76bbc2..4e51d0900c5 100644 --- a/code/game/objects/structures/spawner.dm +++ b/code/game/objects/structures/spawner.dm @@ -78,9 +78,9 @@ spawn_text = "crawls out of" mob_types = list( /mob/living/basic/mining/basilisk, + /mob/living/basic/mining/goldgrub, /mob/living/basic/mining/goliath/ancient, /mob/living/basic/wumborian_fugu, - /mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/hivelord, ) faction = list(FACTION_MINING) @@ -88,7 +88,7 @@ /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/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm new file mode 100644 index 00000000000..58b0e1bbbdb --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub.dm @@ -0,0 +1,191 @@ +//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 = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi' + icon_state = "goldgrub" + icon_living = "goldgrub" + icon_dead = "goldgrub_dead" + icon_gib = "syndicate_gib" + speed = 5 + pixel_x = -12 + base_pixel_x = -12 + mob_biotypes = MOB_ORGANIC|MOB_BEAST + friendly_verb_continuous = "harmlessly rolls into" + friendly_verb_simple = "harmlessly roll into" + maxHealth = 45 + health = 45 + melee_damage_lower = 0 + melee_damage_upper = 0 + attack_verb_continuous = "barrels into" + attack_verb_simple = "barrel into" + attack_sound = 'sound/weapons/punch1.ogg' + combat_mode = FALSE + speak_emote = list("screeches") + death_message = "stops moving as green liquid oozes from the carcass!" + status_flags = CANPUSH + gold_core_spawnable = HOSTILE_SPAWN + ai_controller = /datum/ai_controller/basic_controller/goldgrub + ///can this mob lay eggs + var/can_lay_eggs = TRUE + ///can we tame this mob + var/can_tame = TRUE + //pet commands when we tame the grub + var/list/pet_commands = list( + /datum/pet_command/idle, + /datum/pet_command/free, + /datum/pet_command/grub_spit, + /datum/pet_command/follow, + /datum/pet_command/point_targetting/fetch, + ) + +/mob/living/basic/mining/goldgrub/Initialize(mapload) + . = ..() + + if(mapload) + generate_loot() + else + can_lay_eggs = FALSE + + var/datum/action/cooldown/mob_cooldown/spit_ore/spit = new(src) + var/datum/action/cooldown/mob_cooldown/burrow/burrow = new(src) + spit.Grant(src) + burrow.Grant(src) + ai_controller.set_blackboard_key(BB_SPIT_ABILITY, spit) + ai_controller.set_blackboard_key(BB_BURROW_ABILITY, burrow) + AddElement(/datum/element/wall_smasher) + AddComponent(/datum/component/ai_listen_to_weather) + AddComponent(\ + /datum/component/appearance_on_aggro,\ + overlay_icon = 'icons/mob/simple/lavaland/lavaland_monsters_wide.dmi',\ + overlay_state = "goldgrub_alert",\ + ) + if(can_tame) + make_tameable() + if(can_lay_eggs) + make_egg_layer() + +/mob/living/basic/mining/goldgrub/UnarmedAttack(atom/attack_target, proximity_flag, list/modifiers) + . = ..() + if(!.) + return + + if(!proximity_flag) + return + + if(istype(attack_target, /obj/item/stack/ore)) + consume_ore(attack_target) + +/mob/living/basic/mining/goldgrub/bullet_act(obj/projectile/bullet) + if(stat == DEAD) + return BULLET_ACT_FORCE_PIERCE + + visible_message(span_danger("The [bullet.name] is repelled by [src]'s girth!")) + return BULLET_ACT_BLOCK + +/mob/living/basic/mining/goldgrub/proc/barf_contents(gibbed) + playsound(src, 'sound/effects/splat.ogg', 50, TRUE) + for(var/obj/item/ore as anything in src) + ore.forceMove(loc) + if(!gibbed) + visible_message(span_danger("[src] spits out its consumed ores!")) + +/mob/living/basic/mining/goldgrub/proc/generate_loot() + var/loot_amount = rand(1,3) + var/list/weight_lootdrops = list( + /obj/item/stack/ore/silver = 4, + /obj/item/stack/ore/gold = 3, + /obj/item/stack/ore/uranium = 3, + /obj/item/stack/ore/diamond = 1, + ) + for(var/i in 1 to loot_amount) + var/picked_loot = pick_weight(weight_lootdrops) + new picked_loot(src) + +/mob/living/basic/mining/goldgrub/death(gibbed) + barf_contents(gibbed) + return ..() + +/mob/living/basic/mining/goldgrub/proc/make_tameable() + AddComponent(\ + /datum/component/tameable,\ + food_types = list(/obj/item/stack/ore),\ + tame_chance = 25,\ + bonus_tame_chance = 5,\ + after_tame = CALLBACK(src, PROC_REF(tame_grub)),\ + ) + +/mob/living/basic/mining/goldgrub/proc/tame_grub() + new /obj/effect/temp_visual/heart(src.loc) + AddElement(/datum/element/ridable, /datum/component/riding/creature/goldgrub) + AddComponent(/datum/component/obeys_commands, pet_commands) + +/mob/living/basic/mining/goldgrub/proc/make_egg_layer() + AddComponent(\ + /datum/component/egg_layer,\ + /obj/item/food/egg/green/grub_egg,\ + list(/obj/item/stack/ore/bluespace_crystal),\ + lay_messages = EGG_LAYING_MESSAGES,\ + eggs_left = 0,\ + eggs_added_from_eating = 1,\ + max_eggs_held = 1,\ + ) + +/mob/living/basic/mining/goldgrub/proc/consume_ore(obj/item/target_ore) + playsound(src,'sound/items/eatfood.ogg', rand(10,50), TRUE) + target_ore.forceMove(src) + if(!can_lay_eggs) + return + if(!istype(target_ore, /obj/item/stack/ore/bluespace_crystal) || prob(60)) + return + new /obj/item/food/egg/green/grub_egg(get_turf(src)) + +/mob/living/basic/mining/goldgrub/baby + icon = 'icons/mob/simple/lavaland/lavaland_monsters.dmi' + name = "goldgrub baby" + icon_state = "grub_baby" + icon_living = "grub_baby" + icon_dead = "grub_baby_dead" + pixel_x = 0 + base_pixel_x = 0 + speed = 3 + maxHealth = 25 + health = 25 + gold_core_spawnable = NO_SPAWN + can_tame = FALSE + can_lay_eggs = FALSE + ai_controller = /datum/ai_controller/basic_controller/babygrub + +/mob/living/basic/mining/goldgrub/baby/Initialize(mapload) + . = ..() + AddComponent(\ + /datum/component/growth_and_differentiation,\ + growth_time = 5 MINUTES,\ + growth_path = /mob/living/basic/mining/goldgrub,\ + growth_probability = 100,\ + lower_growth_value = 0.5,\ + upper_growth_value = 1,\ + signals_to_kill_on = list(COMSIG_MOB_CLIENT_LOGIN),\ + optional_checks = CALLBACK(src, PROC_REF(ready_to_grow)),\ + ) + +/mob/living/basic/mining/goldgrub/baby/proc/ready_to_grow() + return (stat == CONSCIOUS && !is_jaunting(src)) + +/obj/item/food/egg/green/grub_egg + name = "grub egg" + desc = "Covered in disgusting fluid." + + +/obj/item/food/egg/green/grub_egg/Initialize(mapload) + . = ..() + AddComponent(\ + /datum/component/fertile_egg,\ + embryo_type = /mob/living/basic/mining/goldgrub/baby,\ + minimum_growth_rate = 1,\ + maximum_growth_rate = 2,\ + total_growth_required = 100,\ + current_growth = 0,\ + location_allowlist = typecacheof(list(/turf)),\ + ) + diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_abilities.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_abilities.dm new file mode 100644 index 00000000000..fe8c8096e2c --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_abilities.dm @@ -0,0 +1,85 @@ +/datum/action/cooldown/mob_cooldown/spit_ore + name = "Spit Ore" + desc = "Vomit out all of your consumed ores." + click_to_activate = FALSE + cooldown_time = 5 SECONDS + +/datum/action/cooldown/mob_cooldown/spit_ore/IsAvailable(feedback) + if(is_jaunting(owner)) + if(feedback) + owner.balloon_alert(owner, "currently underground!") + return FALSE + + if(!length(owner.contents)) + if(feedback) + owner.balloon_alert(owner, "no ores to spit!") + return FALSE + return TRUE + +/datum/action/cooldown/mob_cooldown/spit_ore/Activate() + var/mob/living/basic/mining/goldgrub/grub_owner = owner + grub_owner.barf_contents() + StartCooldown() + return TRUE + +/datum/action/cooldown/mob_cooldown/burrow + name = "Burrow" + desc = "Burrow under soft ground, evading predators and increasing your speed." + cooldown_time = 7 SECONDS + click_to_activate = FALSE + +/datum/action/cooldown/mob_cooldown/burrow/IsAvailable(feedback) + . = ..() + if (!.) + return FALSE + var/turf/location = get_turf(owner) + + if(!isasteroidturf(location) && !ismineralturf(location)) + if(feedback) + owner.balloon_alert(owner, "available only on mining floor or wall!") + return FALSE + + return TRUE + +/datum/action/cooldown/mob_cooldown/burrow/Activate() + var/obj/effect/dummy/phased_mob/grub_burrow/holder = null + var/turf/current_loc = get_turf(owner) + + if(!do_after(owner, 3 SECONDS, target = current_loc)) + owner.balloon_alert(owner, "need to stay still!") + return + + if(get_turf(owner) != current_loc) + to_chat(owner, span_warning("Action cancelled, as you moved while reappearing.")) + return + + if(!is_jaunting(owner)) + owner.visible_message(span_danger("[owner] buries into the ground, vanishing from sight!")) + playsound(get_turf(owner), 'sound/effects/break_stone.ogg', 50, TRUE, -1) + holder = new /obj/effect/dummy/phased_mob/grub_burrow(current_loc, owner) + return TRUE + + holder = owner.loc + holder.eject_jaunter() + holder = null + owner.visible_message(span_danger("[owner] emerges from the ground!")) + + if(ismineralturf(current_loc)) + var/turf/closed/mineral/mineral_turf = current_loc + mineral_turf.gets_drilled(owner) + + playsound(current_loc, 'sound/effects/break_stone.ogg', 50, TRUE, -1) + StartCooldown() + return TRUE + +/obj/effect/dummy/phased_mob/grub_burrow + +/obj/effect/dummy/phased_mob/grub_burrow/phased_check(mob/living/user, direction) + . = ..() + + if(!.) + return + + if(!ismineralturf(.) && !isasteroidturf(.)) + to_chat(user, span_warning("You cannot dig through this floor!")) + return null diff --git a/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm new file mode 100644 index 00000000000..12e7d4c8ff1 --- /dev/null +++ b/code/modules/mob/living/basic/lavaland/goldgrub/goldgrub_ai.dm @@ -0,0 +1,209 @@ +/datum/ai_controller/basic_controller/goldgrub + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_PET_TARGETTING_DATUM = new /datum/targetting_datum/not_friends, + BB_ORE_IGNORE_TYPES = list(/obj/item/stack/ore/iron, /obj/item/stack/ore/glass), + BB_BASIC_MOB_FLEEING = TRUE, + BB_STORM_APPROACHING = FALSE, + ) + + 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/pet_planning, + /datum/ai_planning_subtree/dig_away_from_danger, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/find_and_hunt_target/consume_ores, + /datum/ai_planning_subtree/find_and_hunt_target/baby_egg, + /datum/ai_planning_subtree/grub_mine, + ) + +/datum/ai_controller/basic_controller/babygrub + blackboard = list( + BB_TARGETTING_DATUM = new /datum/targetting_datum/basic, + BB_ORE_IGNORE_TYPES = list(/obj/item/stack/ore/glass), + BB_FIND_MOM_TYPES = list(/mob/living/basic/mining/goldgrub), + BB_IGNORE_MOM_TYPES = list(/mob/living/basic/mining/goldgrub/baby), + BB_BASIC_MOB_FLEEING = TRUE, + BB_STORM_APPROACHING = FALSE, + ) + + 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/dig_away_from_danger, + /datum/ai_planning_subtree/find_and_hunt_target/consume_ores, + /datum/ai_planning_subtree/flee_target, + /datum/ai_planning_subtree/look_for_adult, + ) + +///consume food! +/datum/ai_planning_subtree/find_and_hunt_target/consume_ores + target_key = BB_ORE_TARGET + hunting_behavior = /datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores + finding_behavior = /datum/ai_behavior/find_hunt_target/consume_ores + hunt_targets = list(/obj/item/stack/ore) + hunt_chance = 75 + hunt_range = 9 + +/datum/ai_behavior/find_hunt_target/consume_ores + +/datum/ai_behavior/find_hunt_target/consume_ores/valid_dinner(mob/living/basic/source, obj/item/stack/ore/target, radius) + var/list/forbidden_ore = source.ai_controller.blackboard[BB_ORE_IGNORE_TYPES] + + if(is_type_in_list(target, forbidden_ore)) + return FALSE + + if(target in source) + return FALSE + + var/obj/item/pet_target = source.ai_controller.blackboard[BB_CURRENT_PET_TARGET] + if(target == pet_target) //we are currently fetching this ore for master, dont eat it! + return FALSE + + return can_see(source, target, radius) + +/datum/ai_behavior/hunt_target/unarmed_attack_target/consume_ores + always_reset_target = TRUE + +///find our child's egg and pull it! +/datum/ai_planning_subtree/find_and_hunt_target/baby_egg + target_key = BB_LOW_PRIORITY_HUNTING_TARGET + hunting_behavior = /datum/ai_behavior/hunt_target/grub_egg + finding_behavior = /datum/ai_behavior/find_hunt_target + hunt_targets = list(/obj/item/food/egg/green/grub_egg) + hunt_chance = 75 + hunt_range = 9 + +/datum/ai_planning_subtree/find_and_hunt_target/baby_egg + +/datum/ai_planning_subtree/find_and_hunt_target/baby_egg/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/mob/living/living_pawn = controller.pawn + if(living_pawn.pulling) //we are already pulling something + return + return ..() + +/datum/ai_behavior/hunt_target/grub_egg + always_reset_target = TRUE + +/datum/ai_behavior/hunt_target/grub_egg/target_caught(mob/living/hunter, obj/item/target) + hunter.start_pulling(target) + + +///only dig away if storm is coming or if humans are around +/datum/ai_planning_subtree/dig_away_from_danger + +/datum/ai_planning_subtree/dig_away_from_danger/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/currently_underground = is_jaunting(controller.pawn) + var/storm_approaching = controller.blackboard[BB_STORM_APPROACHING] + + //dont do anything until the storm passes + if(currently_underground && storm_approaching) + return SUBTREE_RETURN_FINISH_PLANNING + + var/datum/action/cooldown/dig_ability = controller.blackboard[BB_BURROW_ABILITY] + + if(!dig_ability.IsAvailable()) + return + + var/mob/living/target = controller.blackboard[BB_BASIC_MOB_CURRENT_TARGET] + + //a storm is coming or someone is nearby, its time to escape + if(currently_underground || !currently_underground && storm_approaching || !QDELETED(target)) + controller.queue_behavior(/datum/ai_behavior/use_mob_ability/burrow, BB_BURROW_ABILITY) + return SUBTREE_RETURN_FINISH_PLANNING + +/datum/ai_behavior/use_mob_ability/burrow + behavior_flags = AI_BEHAVIOR_CAN_PLAN_DURING_EXECUTION + +///mine walls to look for food! +/datum/ai_planning_subtree/grub_mine + +/datum/ai_planning_subtree/grub_mine/SelectBehaviors(datum/ai_controller/controller, seconds_per_tick) + var/turf/target_wall = controller.blackboard[BB_TARGET_MINERAL_WALL] + + if(QDELETED(target_wall)) + controller.queue_behavior(/datum/ai_behavior/find_mineral_wall, BB_TARGET_MINERAL_WALL) + return + + controller.queue_behavior(/datum/ai_behavior/mine_wall, BB_TARGET_MINERAL_WALL) + return SUBTREE_RETURN_FINISH_PLANNING + + +/datum/ai_behavior/find_mineral_wall + +/datum/ai_behavior/find_mineral_wall/perform(seconds_per_tick, datum/ai_controller/controller, found_wall_key) + . = ..() + + var/mob/living_pawn = controller.pawn + + for(var/turf/closed/mineral/potential_wall in oview(9, living_pawn)) + if(!check_if_mineable(living_pawn, potential_wall)) //check if its surrounded by walls + continue + controller.set_blackboard_key(found_wall_key, potential_wall) //closest wall first! + finish_action(controller, TRUE) + return + + finish_action(controller, FALSE) + +/datum/ai_behavior/find_mineral_wall/proc/check_if_mineable(mob/living/source, turf/target_wall) + var/direction_to_turf = get_dir(target_wall, source) + if(!ISDIAGONALDIR(direction_to_turf)) + return TRUE + var/list/directions_to_check = list() + for(var/direction_check in GLOB.cardinals) + if(direction_check & direction_to_turf) + directions_to_check += direction_check + + for(var/direction in directions_to_check) + var/turf/test_turf = get_step(target_wall, direction) + if(isnull(test_turf)) + continue + if(!test_turf.is_blocked_turf(ignore_atoms = list(source))) + return TRUE + return FALSE + +/datum/ai_behavior/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/mine_wall/setup(datum/ai_controller/controller, target_key) + . = ..() + var/turf/target = controller.blackboard[target_key] + if(isnull(target)) + return FALSE + set_movement_target(controller, target) + +/datum/ai_behavior/mine_wall/perform(seconds_per_tick, datum/ai_controller/controller, target_key) + . = ..() + var/mob/living/basic/living_pawn = controller.pawn + var/turf/closed/mineral/target = controller.blackboard[target_key] + var/is_gibtonite_turf = istype(target, /turf/closed/mineral/gibtonite) + if(QDELETED(target)) + finish_action(controller, FALSE, target_key) + return + living_pawn.melee_attack(target) + if(is_gibtonite_turf) + living_pawn.manual_emote("sighs...") //accept whats about to happen to us + + finish_action(controller, TRUE, target_key) + return + +/datum/ai_behavior/mine_wall/finish_action(datum/ai_controller/controller, success, target_key) + . = ..() + controller.clear_blackboard_key(target_key) + +/datum/pet_command/grub_spit + command_name = "Spit" + command_desc = "Ask your grub pet to spit out its ores." + speech_commands = list("spit", "ores") + +/datum/pet_command/grub_spit/execute_action(datum/ai_controller/controller) + var/datum/action/cooldown/spit_ability = controller.blackboard[BB_SPIT_ABILITY] + if(QDELETED(spit_ability) || !spit_ability.IsAvailable()) + return + controller.queue_behavior(/datum/ai_behavior/use_mob_ability, BB_SPIT_ABILITY) + controller.clear_blackboard_key(BB_ACTIVE_PET_COMMAND) + return SUBTREE_RETURN_FINISH_PLANNING diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm index 9b2ef878f4e..a9c38d985c1 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/colossus.dm @@ -424,7 +424,7 @@ name = "lavaland" floor = /turf/open/floor/fakebasalt wall = /turf/closed/wall/mineral/cult - flora_and_fauna = list(/mob/living/simple_animal/hostile/asteroid/goldgrub) + flora_and_fauna = list(/mob/living/basic/mining/goldgrub) flora_and_fauna_chance = 1 // Snow terrain is slow to move in and cold! Get the assistants to shovel your driveway. diff --git a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm b/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm deleted file mode 100644 index f06c87363ce..00000000000 --- a/code/modules/mob/living/simple_animal/hostile/mining_mobs/goldgrub.dm +++ /dev/null @@ -1,146 +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/simple/lavaland/lavaland_monsters_wide.dmi' - icon_state = "goldgrub" - icon_living = "goldgrub" - icon_aggro = "goldgrub_alert" - icon_dead = "goldgrub_dead" - icon_gib = "syndicate_gib" - pixel_x = -12 - base_pixel_x = -12 - mob_biotypes = MOB_ORGANIC|MOB_BEAST - vision_range = 2 - aggro_vision_range = 9 - move_to_delay = 5 - friendly_verb_continuous = "harmlessly rolls into" - friendly_verb_simple = "harmlessly roll into" - maxHealth = 45 - health = 45 - harm_intent_damage = 5 - melee_damage_lower = 0 - melee_damage_upper = 0 - attack_verb_continuous = "barrels into" - attack_verb_simple = "barrel into" - attack_sound = 'sound/weapons/punch1.ogg' - combat_mode = FALSE - speak_emote = list("screeches") - throw_message = "sinks in slowly, before being pushed out of " - death_message = "stops moving as green liquid oozes from the carcass!" - status_flags = CANPUSH - gold_core_spawnable = HOSTILE_SPAWN - search_objects = 1 - 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 - var/datum/action/innate/goldgrub/spitore/spit - var/datum/action/innate/goldgrub/burrow/burrow - var/is_burrowed = FALSE - -/mob/living/simple_animal/hostile/asteroid/goldgrub/Initialize(mapload) - . = ..() - 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-- - spit = new - burrow = new - spit.Grant(src) - burrow.Grant(src) - -/datum/action/innate/goldgrub - background_icon_state = "bg_default" - overlay_icon_state = "bg_default_border" - -/datum/action/innate/goldgrub/spitore - name = "Spit Ore" - desc = "Vomit out all of your consumed ores." - -/datum/action/innate/goldgrub/spitore/Activate() - var/mob/living/simple_animal/hostile/asteroid/goldgrub/G = owner - if(G.stat == DEAD || G.is_burrowed) - return - G.barf_contents() - -/datum/action/innate/goldgrub/burrow - name = "Burrow" - desc = "Burrow under soft ground, evading predators and increasing your speed." - -/datum/action/innate/goldgrub/burrow/Activate() - var/mob/living/simple_animal/hostile/asteroid/goldgrub/G = owner - var/obj/effect/dummy/phased_mob/holder = null - if(G.stat == DEAD) - return - var/turf/T = get_turf(G) - if (!isasteroidturf(T) || !do_after(G, 30, target = T)) - to_chat(G, span_warning("You can only burrow in and out of mining turfs and must stay still!")) - return - if (get_dist(G, T) != 0) - to_chat(G, span_warning("Action cancelled, as you moved while reappearing.")) - return - if(G.is_burrowed) - holder = G.loc - holder.eject_jaunter() - holder = null - G.is_burrowed = FALSE - G.visible_message(span_danger("[G] emerges from the ground!")) - playsound(get_turf(G), 'sound/effects/break_stone.ogg', 50, TRUE, -1) - else - G.visible_message(span_danger("[G] buries into the ground, vanishing from sight!")) - playsound(get_turf(G), 'sound/effects/break_stone.ogg', 50, TRUE, -1) - holder = new /obj/effect/dummy/phased_mob(T, G) - G.is_burrowed = TRUE - -/mob/living/simple_animal/hostile/asteroid/goldgrub/GiveTarget(new_target) - add_target(new_target) - if(target != null) - if(istype(target, /obj/item/stack/ore)) - visible_message(span_notice("The [name] looks at [target.name] with hungry eyes.")) - else if(isliving(target)) - Aggro() - visible_message(span_danger("The [name] 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/movable/targeted_ore) - if(targeted_ore && targeted_ore.loc != src) - targeted_ore.forceMove(src) - return TRUE - return FALSE - -/mob/living/simple_animal/hostile/asteroid/goldgrub/death(gibbed) - barf_contents() - return ..() - -/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/barf_contents() - visible_message(span_danger("[src] spits out its consumed ores!")) - playsound(src, 'sound/effects/splat.ogg', 50, TRUE) - for(var/atom/movable/AM in src) - AM.forceMove(loc) - -/mob/living/simple_animal/hostile/asteroid/goldgrub/proc/Burrow()//Begin the chase to kill the goldgrub in time - if(!stat) - visible_message(span_danger("The [name] buries into the ground, vanishing from sight!")) - qdel(src) - -/mob/living/simple_animal/hostile/asteroid/goldgrub/bullet_act(obj/projectile/P) - if(stat == DEAD) - return BULLET_ACT_FORCE_PIERCE - else - visible_message(span_danger("The [P.name] is repelled by [name]'s girth!")) - return BULLET_ACT_BLOCK - -/mob/living/simple_animal/hostile/asteroid/goldgrub/adjustHealth(amount, updating_health = TRUE, forced = FALSE) - vision_range = 9 - . = ..() diff --git a/code/modules/unit_tests/simple_animal_freeze.dm b/code/modules/unit_tests/simple_animal_freeze.dm index f55c4a78f54..aee85111b22 100644 --- a/code/modules/unit_tests/simple_animal_freeze.dm +++ b/code/modules/unit_tests/simple_animal_freeze.dm @@ -61,7 +61,6 @@ /mob/living/simple_animal/hostile/asteroid/elite/legionnaire, /mob/living/simple_animal/hostile/asteroid/elite/legionnairehead, /mob/living/simple_animal/hostile/asteroid/elite/pandora, - /mob/living/simple_animal/hostile/asteroid/goldgrub, /mob/living/simple_animal/hostile/asteroid/gutlunch, /mob/living/simple_animal/hostile/asteroid/gutlunch/grublunch, /mob/living/simple_animal/hostile/asteroid/gutlunch/gubbuck, diff --git a/icons/mob/simple/lavaland/lavaland_monsters.dmi b/icons/mob/simple/lavaland/lavaland_monsters.dmi index a2d840c4ced..1d546bd17d4 100644 Binary files a/icons/mob/simple/lavaland/lavaland_monsters.dmi and b/icons/mob/simple/lavaland/lavaland_monsters.dmi differ diff --git a/icons/mob/simple/lavaland/lavaland_monsters_wide.dmi b/icons/mob/simple/lavaland/lavaland_monsters_wide.dmi index b6a47908b44..2be68ef4c66 100644 Binary files a/icons/mob/simple/lavaland/lavaland_monsters_wide.dmi and b/icons/mob/simple/lavaland/lavaland_monsters_wide.dmi differ diff --git a/tgstation.dme b/tgstation.dme index c999ffb5738..a036a8e75e3 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -903,6 +903,7 @@ #include "code\datums\components\admin_popup.dm" #include "code\datums\components\aggro_emote.dm" #include "code\datums\components\ai_has_target_timer.dm" +#include "code\datums\components\ai_listen_to_weather.dm" #include "code\datums\components\ai_retaliate_advanced.dm" #include "code\datums\components\anti_magic.dm" #include "code\datums\components\appearance_on_aggro.dm" @@ -4170,6 +4171,9 @@ #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_instrument.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_loot.dm" #include "code\modules\mob\living\basic\lavaland\bileworm\bileworm_vileworm.dm" +#include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub.dm" +#include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub_abilities.dm" +#include "code\modules\mob\living\basic\lavaland\goldgrub\goldgrub_ai.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath_actions.dm" #include "code\modules\mob\living\basic\lavaland\goliath\goliath_ai.dm" @@ -4501,7 +4505,6 @@ #include "code\modules\mob\living\simple_animal\hostile\megafauna\wendigo.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\brimdemon.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\curse_blob.dm" -#include "code\modules\mob\living\simple_animal\hostile\mining_mobs\goldgrub.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\gutlunch.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\hivelord.dm" #include "code\modules\mob\living\simple_animal\hostile\mining_mobs\ice_demon.dm" diff --git a/tools/UpdatePaths/Scripts/77733_goldgrub.txt b/tools/UpdatePaths/Scripts/77733_goldgrub.txt new file mode 100644 index 00000000000..00e498f6a59 --- /dev/null +++ b/tools/UpdatePaths/Scripts/77733_goldgrub.txt @@ -0,0 +1 @@ +/mob/living/simple_animal/hostile/asteroid/goldgrub : /mob/living/basic/mining/goldgrub{@OLD;will_burrow=@SKIP} \ No newline at end of file