/mob/living/basic/nian_caterpillar name = "nian caterpillar" desc = "Fluffier than clouds." icon = 'icons/mob/monkey.dmi' icon_state = "mothroach" icon_living = "mothroach" icon_dead = "mothroach_dead" icon_resting = "mothroach_sleep" pass_flags = PASSTABLE | PASSMOB mob_biotypes = MOB_ORGANIC | MOB_BUG mob_size = MOB_SIZE_SMALL ventcrawler = VENTCRAWLER_ALWAYS butcher_results = list(/obj/item/food/meat = 1) blood_color = "#b9ae9c" maxHealth = 50 health = 50 speed = 0.75 // What they sound like voice_name = "nian caterpillar" speak_emote = list("flutters", "chitters", "chatters") // Special verbs for when someone interacts with a caterpillar response_help_continuous = "pets" response_help_simple = "pet" response_disarm_continuous = "shoos" response_disarm_simple = "shoo" response_harm_continuous = "kicks" response_harm_simple = "kicks" // Xenobiology and cargo are the only ways to get the caterpillar. gold_core_spawnable = FRIENDLY_SPAWN melee_attack_cooldown_min = 1.5 SECONDS melee_attack_cooldown_max = 2.5 SECONDS melee_damage_lower = 5 melee_damage_upper = 8 attack_verb_continuous = "bites" attack_verb_simple = "bite" attack_sound = 'sound/weapons/bite.ogg' holder_type = /obj/item/holder/nian_caterpillar ai_controller = /datum/ai_controller/basic_controller/mothroach var/list/innate_actions = list( /datum/action/innate/nian_caterpillar_emerge, /datum/action/cooldown/mob_cooldown/spin_mothsilk = BB_NIAN_CATERPILLAR_SPIN_MOTHSILK_ACTION, ) /// List of stuff that we want to eat var/static/list/edibles = list( /obj/item/food, ) /// The amount of nutrition the nian caterpillar needs to evolve, default is 500. var/nutrition_need = 500 /mob/living/basic/nian_caterpillar/Initialize(mapload) . = ..() real_name = name AddElement(/datum/element/wears_collar) AddElement(/datum/element/ai_retaliate) AddElement(/datum/element/basic_eating, heal_amt_ = 2, food_types_ = edibles) ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(edibles)) add_language("Tkachi") grant_actions_by_list(innate_actions) RegisterSignal(src, COMSIG_MOB_PRE_EAT, PROC_REF(check_full)) RegisterSignal(src, COMSIG_MOB_ATE, PROC_REF(consume)) make_variety() /mob/living/basic/nian_caterpillar/proc/make_variety() if(prob(50)) icon_state = "mothroach_classic" icon_living = "mothroach_classic" icon_dead = "mothroach_classic_dead" icon_resting = "mothroach_classic_sleep" /mob/living/basic/nian_caterpillar/proc/evolve(obj/structure/moth_cocoon/C, datum/mind/M) if(stat != CONSCIOUS) return FALSE // A changeling caterpillar shouldn't be restricted from evolving. // A caterpillar needs to consume food-- similar to a dioan nymph --to evolve. if((nutrition < nutrition_need) && !IS_CHANGELING(M)) to_chat(src, SPAN_WARNING("You need to binge on food in order to have the energy to evolve...")) return if(master_commander) to_chat(src, SPAN_USERDANGER("As you evolve, your mind grows out of it's restraints. You are no longer loyal to [master_commander]!")) // Worme is the lesser form of nian. The caterpillar evolves into this lesser form. var/mob/living/carbon/human/nian_worme/adult = new(get_turf(loc)) if(istype(loc, /obj/item/holder/nian_caterpillar)) var/turf/cocoon_turf = get_turf(loc) qdel(loc) forceMove(cocoon_turf) for(var/datum/language/L in languages) adult.add_language(L.name) adult.regenerate_icons() adult.name = name adult.real_name = name if(M) // Mind transfer to new worme. M.transfer_to(adult) // [Caterpillar -> worme -> nian] is from xenobio (or cargo) and does not give vampires usuble blood and cannot be converted by cult. ADD_TRAIT(adult.mind, TRAIT_XENOBIO_SPAWNED_HUMAN, ROUNDSTART_TRAIT) // Worme is placed into cacoon. adult.forceMove(C) C.preparing_to_emerge = TRUE adult.apply_status_effect(STATUS_EFFECT_COCOONED) adult.KnockOut() // Zzzz adult.create_log(MISC_LOG, "has woven a cocoon") // For any random generated names. This is for when a new nian caterpillar is spawned. // [ nian caterpillar (042) ] etc. if(findtext(adult.real_name, "nian caterpillar")) adult.real_name = adult.dna.species.get_random_name() adult.name = adult.real_name for(var/obj/item/W in contents) drop_item_to_ground(W) qdel(src) return TRUE /mob/living/basic/nian_caterpillar/proc/check_full(datum/source, obj/item/potential_food) SIGNAL_HANDLER // COMSIG_MOB_PRE_EAT if(nutrition >= nutrition_need) // Prevents griefing by overeating food items without evolving. to_chat(src, SPAN_WARNING("You're too full to consume this! Perhaps it's time to grow bigger...")) return COMSIG_MOB_TERMINATE_EAT /mob/living/basic/nian_caterpillar/proc/consume(datum/source, obj/item/potential_food) SIGNAL_HANDLER // COMSIG_MOB_ATE if(potential_food.reagents.get_reagent_amount("nutriment") + potential_food.reagents.get_reagent_amount("plantmatter") < 1) adjust_nutrition(2) else adjust_nutrition((potential_food.reagents.get_reagent_amount("nutriment") + potential_food.reagents.get_reagent_amount("plantmatter")) * 2) /mob/living/basic/nian_caterpillar/attack_hand(mob/living/carbon/human/M) // Let people pick the little buggers up. if(M.a_intent != INTENT_HELP) return ..() if(isrobot(M)) M.visible_message(SPAN_NOTICE("[M] playfully boops [src] on the head!"), SPAN_NOTICE("You playfully boop [src] on the head!")) else get_scooped(M) /mob/living/basic/nian_caterpillar/attacked_by(obj/item/I, mob/living/user) if(..()) return FINISH_ATTACK if(istype(I, /obj/item/melee/flyswatter) && I.force) gib() // Commit die. /mob/living/basic/nian_caterpillar/melee_attack(atom/target, list/modifiers, ignore_cooldown) . = ..() /datum/action/innate/nian_caterpillar_emerge name = "Evolve" desc = "Weave a cocoon around yourself to evolve into a greater form. The worme." button_icon = 'icons/effects/effects.dmi' button_icon_state = "cocoon1" /datum/action/innate/nian_caterpillar_emerge/proc/emerge(obj/structure/moth_cocoon/C) for(var/mob/living/carbon/human/H in C) H.remove_status_effect(STATUS_EFFECT_COCOONED) H.remove_status_effect(STATUS_EFFECT_BURNT_WINGS) C.preparing_to_emerge = FALSE qdel(C) /datum/action/innate/nian_caterpillar_emerge/Activate() var/mob/living/basic/nian_caterpillar/user = owner user.visible_message(SPAN_NOTICE("[user] begins to hold still and concentrate on weaving a cocoon..."), SPAN_NOTICE("You begin to focus on weaving a cocoon... (This will take [COCOON_WEAVE_DELAY / 10] seconds, and you must hold still.)")) if(do_after(user, COCOON_WEAVE_DELAY, FALSE, user)) var/obj/structure/moth_cocoon/C = new(get_turf(user)) var/datum/mind/H = user.mind user.evolve(C, H) addtimer(CALLBACK(src, PROC_REF(emerge), C), COCOON_EMERGE_DELAY, TIMER_UNIQUE) /// Place some grappling tentacles underfoot /datum/action/cooldown/mob_cooldown/spin_mothsilk name = "Spin Mothsilk" desc = "Spin some mothsilk, consuming nutrition." button_icon = 'icons/obj/stacks/organic.dmi' button_icon_state = "stack-mothsilk-2" cooldown_time = 20 SECONDS click_to_activate = FALSE shared_cooldown = NONE /datum/action/cooldown/mob_cooldown/spin_mothsilk/Activate(atom/target) if(owner.nutrition < 425) to_chat(owner, SPAN_WARNING("You don't have enough nutrition to spin silk!")) return FALSE if(!do_after(owner, 3 SECONDS)) return FALSE new /obj/item/stack/sheet/mothsilk(get_turf(owner)) owner.nutrition -= 15 owner.visible_message(SPAN_NOTICE("[owner] spins some mothsilk!")) StartCooldown() return TRUE /datum/ai_controller/basic_controller/mothroach blackboard = list( BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic, BB_EAT_FOOD_COOLDOWN = 1 MINUTES, // Moth wants to eat, but not too fast. BB_SEARCH_RANGE = 3, ) ai_traits = AI_FLAG_STOP_MOVING_WHEN_PULLED | AI_FLAG_PAUSE_DURING_DO_AFTER ai_movement = /datum/ai_movement/basic_avoidance idle_behavior = /datum/idle_behavior/idle_random_walk planning_subtrees = list( /datum/ai_planning_subtree/random_speech/moth_caterpillar, /datum/ai_planning_subtree/find_nearest_thing_which_attacked_me_to_flee, /datum/ai_planning_subtree/spin_mothsilk, /datum/ai_planning_subtree/flee_target, /datum/ai_planning_subtree/find_food/nian_caterpillar, ) /datum/ai_planning_subtree/find_food/nian_caterpillar /datum/ai_planning_subtree/find_food/nian_caterpillar/select_behaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/nian_caterpillar/caterpillar = controller.pawn if(caterpillar.nutrition >= caterpillar.nutrition_need) // Put eating on cooldown so it doesn't keep trying to eat. var/food_cooldown = controller.blackboard[BB_EAT_FOOD_COOLDOWN] || EAT_FOOD_COOLDOWN controller.set_blackboard_key(BB_NEXT_FOOD_EAT, world.time + food_cooldown) return SUBTREE_RETURN_FINISH_PLANNING return ..() /datum/ai_planning_subtree/random_speech/moth_caterpillar speech_chance = 2 emote_hear = list("flutters.", "chitters.", "chatters.") emote_see = list("flutters.", "chitters.", "chatters.") /datum/ai_planning_subtree/spin_mothsilk /// Key storing the mothsilk spin action var/action_key = BB_NIAN_CATERPILLAR_SPIN_MOTHSILK_ACTION /datum/ai_planning_subtree/spin_mothsilk/select_behaviors(datum/ai_controller/controller, seconds_per_tick) var/mob/living/basic/nian_caterpillar/caterpillar = controller.pawn var/datum/action/cooldown/mob_cooldown/spin_mothsilk/silk_action = controller.blackboard[action_key] if(caterpillar.nutrition >= 425 && silk_action.IsAvailable()) controller.queue_behavior(/datum/ai_behavior/spin_mothsilk, action_key) return SUBTREE_RETURN_FINISH_PLANNING /datum/ai_behavior/spin_mothsilk/perform(seconds_per_tick, datum/ai_controller/controller, action_key) . = ..() var/datum/action/cooldown/mob_cooldown/spin_mothsilk/silk_action = controller.blackboard[action_key] var/result = silk_action.Trigger() if(result) return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_SUCCEEDED return AI_BEHAVIOR_INSTANT | AI_BEHAVIOR_FAILED