mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-21 03:55:05 +01:00
b0463d3c83
* spanish? * aaaagain * keep maptext * Update robot_items.dm * Update span_defines.dm * compiles * Update silicon_mob.dm * compile
345 lines
12 KiB
Plaintext
345 lines
12 KiB
Plaintext
/*
|
|
* Tiny babby plant critter plus procs.
|
|
*/
|
|
|
|
//Mob defines.
|
|
#define GESTALT_ALERT "gestalt screen alert"
|
|
#define NYMPH_ALERT "nymph screen alert"
|
|
|
|
/mob/living/basic/diona_nymph
|
|
name = "diona nymph"
|
|
desc = "A small component of a diona gestalt mind."
|
|
icon = 'icons/mob/monkey.dmi'
|
|
icon_state = "nymph"
|
|
icon_living = "nymph"
|
|
icon_dead = "nymph_dead"
|
|
icon_resting = "nymph_sleep"
|
|
pass_flags = PASSTABLE | PASSMOB
|
|
mob_biotypes = MOB_ORGANIC | MOB_PLANT
|
|
mob_size = MOB_SIZE_SMALL
|
|
ventcrawler = VENTCRAWLER_ALWAYS
|
|
atmos_requirements = list("min_oxy" = 0, "max_oxy" = 0, "min_tox" = 0, "max_tox" = 0, "min_co2" = 0, "max_co2" = 0, "min_n2" = 0, "max_n2" = 0)
|
|
minimum_survivable_temperature = 0
|
|
|
|
maxHealth = 50
|
|
health = 50
|
|
|
|
voice_name = "diona nymph"
|
|
speak_emote = list("chirrups")
|
|
|
|
response_help_continuous = "pets"
|
|
response_help_simple = "pet"
|
|
response_disarm_continuous = "pushes"
|
|
response_disarm_simple = "push"
|
|
response_harm_continuous = "kicks"
|
|
response_harm_simple = "kick"
|
|
|
|
melee_damage_lower = 5
|
|
melee_damage_upper = 8
|
|
melee_attack_cooldown_min = 1.5 SECONDS
|
|
melee_attack_cooldown_max = 2.5 SECONDS
|
|
attack_verb_continuous = "bites"
|
|
attack_verb_simple = "bite"
|
|
attack_sound = 'sound/weapons/bite.ogg'
|
|
|
|
ai_controller = /datum/ai_controller/basic_controller/diona_nymph
|
|
|
|
var/list/donors = list()
|
|
holder_type = /obj/item/holder/diona
|
|
|
|
/// Amount of blood donors needed to evolve
|
|
var/evolve_donors = 5
|
|
/// Amount of blood donors needed for understand language
|
|
var/awareness_donors = 3
|
|
/// Amount of nutrition needed before evolving
|
|
var/nutrition_need = 500
|
|
/// List of stuff (mice) that we want to eat
|
|
var/static/list/edibles = list(
|
|
/obj/item/food/grown,
|
|
)
|
|
|
|
var/datum/action/innate/diona_nymph/merge/merge_action = new()
|
|
var/datum/action/innate/diona_nymph/evolve/evolve_action = new()
|
|
var/datum/action/innate/diona_nymph/steal_blood/steal_blood_action = new()
|
|
|
|
/mob/living/basic/diona_nymph/Initialize(mapload)
|
|
. = ..()
|
|
AddElement(/datum/element/wears_collar)
|
|
if(name == initial(name)) // To stop named dionas becoming generic.
|
|
name = "[name] ([rand(1, 1000)])"
|
|
real_name = name
|
|
add_language("Rootspeak")
|
|
merge_action.Grant(src)
|
|
evolve_action.Grant(src)
|
|
steal_blood_action.Grant(src)
|
|
AddElement(/datum/element/ai_retaliate)
|
|
AddElement(/datum/element/basic_eating, heal_amt_ = 2, food_types_ = edibles)
|
|
RegisterSignal(src, COMSIG_MOB_PRE_EAT, PROC_REF(check_full))
|
|
RegisterSignal(src, COMSIG_MOB_ATE, PROC_REF(consume))
|
|
ai_controller.set_blackboard_key(BB_BASIC_FOODS, typecacheof(edibles))
|
|
|
|
/datum/action/innate/diona_nymph/merge
|
|
name = "Merge with gestalt"
|
|
button_icon = 'icons/mob/human_races/r_diona.dmi'
|
|
button_icon_state = "preview"
|
|
|
|
/datum/action/innate/diona_nymph/merge/Activate()
|
|
var/mob/living/basic/diona_nymph/user = owner
|
|
user.merge()
|
|
|
|
/datum/action/innate/diona_nymph/evolve
|
|
name = "Evolve"
|
|
button_icon = 'icons/obj/cloning.dmi'
|
|
button_icon_state = "pod_cloning"
|
|
|
|
/datum/action/innate/diona_nymph/evolve/Activate()
|
|
var/mob/living/basic/diona_nymph/user = owner
|
|
user.evolve()
|
|
|
|
/datum/action/innate/diona_nymph/steal_blood
|
|
name = "Steal blood"
|
|
button_icon = 'icons/goonstation/objects/iv.dmi'
|
|
button_icon_state = "bloodbag"
|
|
|
|
/datum/action/innate/diona_nymph/steal_blood/Activate()
|
|
var/mob/living/basic/diona_nymph/user = owner
|
|
user.steal_blood()
|
|
|
|
/mob/living/basic/diona_nymph/UnarmedAttack(atom/A)
|
|
// Can't attack your gestalt
|
|
if(isdiona(A) && (src in A.contents))
|
|
visible_message("[src] wiggles around a bit.")
|
|
else
|
|
..()
|
|
|
|
/mob/living/basic/diona_nymph/run_resist()
|
|
if(!split())
|
|
..()
|
|
|
|
/mob/living/basic/diona_nymph/attack_hand(mob/living/carbon/human/M)
|
|
// Let people pick the little buggers up.
|
|
if(M.a_intent == INTENT_HELP)
|
|
if(isdiona(M))
|
|
to_chat(M, "You feel your being twine with that of [src] as it merges with your biomass.")
|
|
to_chat(src, "You feel your being twine with that of [M] as you merge with its biomass.")
|
|
// adds a screen alert that can call resist
|
|
throw_alert(GESTALT_ALERT, /atom/movable/screen/alert/nymph, new_master = src)
|
|
M.throw_alert(NYMPH_ALERT, /atom/movable/screen/alert/gestalt, new_master = src)
|
|
forceMove(M)
|
|
else 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)
|
|
else
|
|
..()
|
|
|
|
/mob/living/basic/diona_nymph/proc/merge()
|
|
if(stat != CONSCIOUS)
|
|
return FALSE
|
|
|
|
var/list/choices = list()
|
|
for(var/mob/living/carbon/human/H in view(1,src))
|
|
if(!(Adjacent(H)) || !isdiona(H))
|
|
continue
|
|
choices += H
|
|
|
|
if(!length(choices))
|
|
to_chat(src, SPAN_WARNING("No suitable diona nearby."))
|
|
return FALSE
|
|
|
|
var/mob/living/M = tgui_input_list(src, "Who do you wish to merge with?", "Nymph Merging", choices)
|
|
|
|
// input can take a while, so re-validate
|
|
if(!M || !src || !(Adjacent(M)) || stat != CONSCIOUS)
|
|
return FALSE
|
|
|
|
if(isdiona(M))
|
|
to_chat(M, "You feel your being twine with that of [src] as it merges with your biomass.")
|
|
M.status_flags |= PASSEMOTES
|
|
to_chat(src, "You feel your being twine with that of [M] as you merge with its biomass.")
|
|
forceMove(M)
|
|
// adds a screen alert that can call resist
|
|
throw_alert(GESTALT_ALERT, /atom/movable/screen/alert/nymph, new_master = src)
|
|
M.throw_alert(NYMPH_ALERT, /atom/movable/screen/alert/gestalt, new_master = src)
|
|
return TRUE
|
|
else
|
|
return FALSE
|
|
|
|
/mob/living/basic/diona_nymph/proc/split(forced = FALSE)
|
|
if((stat != CONSCIOUS && !forced) || !isdiona(loc))
|
|
return FALSE
|
|
var/mob/living/carbon/human/D = loc
|
|
var/turf/T = get_turf(src)
|
|
if(!T)
|
|
return FALSE
|
|
to_chat(loc, "You feel a pang of loss as [src] splits away from your biomass.")
|
|
to_chat(src, "You wiggle out of the depths of [loc]'s biomass and plop to the ground.")
|
|
forceMove(T)
|
|
|
|
var/hasMobs = FALSE
|
|
for(var/atom/A in D.contents)
|
|
if(ismob(A) || istype(A, /obj/item/holder))
|
|
hasMobs = TRUE
|
|
if(!hasMobs)
|
|
D.status_flags &= ~PASSEMOTES
|
|
D.clear_alert(NYMPH_ALERT)
|
|
|
|
clear_alert(GESTALT_ALERT)
|
|
return TRUE
|
|
|
|
/mob/living/basic/diona_nymph/proc/evolve()
|
|
if(stat != CONSCIOUS)
|
|
return FALSE
|
|
|
|
if(length(donors) < evolve_donors)
|
|
to_chat(src, SPAN_WARNING("You need more blood in order to ascend to a new state of consciousness..."))
|
|
return FALSE
|
|
|
|
if(nutrition < nutrition_need)
|
|
to_chat(src, SPAN_WARNING("You need to binge on weeds in order to have the energy to grow..."))
|
|
return FALSE
|
|
|
|
if(isdiona(loc) && !split()) // if it's merged with diona, needs to able to split before evolving
|
|
return FALSE
|
|
|
|
visible_message(SPAN_DANGER("[src] begins to shift and quiver, and erupts in a shower of shed bark as it splits into a tangle of nearly a dozen new dionaea."),SPAN_DANGER("You begin to shift and quiver, feeling your awareness splinter. All at once, we consume our stored nutrients to surge with growth, splitting into a tangle of at least a dozen new dionaea. We have attained our gestalt form."))
|
|
|
|
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]!"))
|
|
|
|
var/mob/living/carbon/human/diona/adult = new(get_turf(loc))
|
|
adult.set_species(/datum/species/diona)
|
|
|
|
if(istype(loc, /obj/item/holder/diona))
|
|
var/obj/item/holder/diona/L = loc
|
|
forceMove(L.loc)
|
|
qdel(L)
|
|
|
|
for(var/datum/language/L in languages)
|
|
adult.add_language(L.name)
|
|
adult.regenerate_icons()
|
|
|
|
adult.name = "diona ([rand(100,999)])"
|
|
adult.real_name = adult.name
|
|
adult.ckey = ckey
|
|
adult.real_name = adult.dna.species.get_random_name() // I hate this being here of all places but unfortunately dna is based on real_name!
|
|
// [Nymph -> Diona] is from xenobio (or botany) and does not give vampires usuble blood and cannot be converted by cult.
|
|
ADD_TRAIT(adult.mind, TRAIT_XENOBIO_SPAWNED_HUMAN, ROUNDSTART_TRAIT)
|
|
|
|
for(var/obj/item/W in contents)
|
|
drop_item_to_ground(W)
|
|
|
|
qdel(src)
|
|
return TRUE
|
|
|
|
/mob/living/basic/diona_nymph/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
|
|
|
|
// Consumes plant matter other than weeds to evolve
|
|
/mob/living/basic/diona_nymph/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/diona_nymph/proc/steal_blood()
|
|
if(stat != CONSCIOUS)
|
|
return FALSE
|
|
|
|
var/list/choices = list()
|
|
for(var/mob/living/carbon/human/H in oview(1,src))
|
|
if(Adjacent(H) && H.dna && !(NO_BLOOD in H.dna.species.species_traits))
|
|
choices += H
|
|
|
|
if(!length(choices))
|
|
to_chat(src, SPAN_WARNING("No suitable blood donors nearby."))
|
|
return FALSE
|
|
|
|
var/mob/living/carbon/human/M = tgui_input_list(src, "Who do you wish to take a sample from?", "Blood Sampling", choices)
|
|
|
|
if(!M || !src || !(Adjacent(M)) || stat != CONSCIOUS) //input can take a while, so re-validate
|
|
return FALSE
|
|
|
|
if(!M.dna || (NO_BLOOD in M.dna.species.species_traits))
|
|
to_chat(src, SPAN_WARNING("That donor has no blood to take."))
|
|
return FALSE
|
|
|
|
if(donors.Find(M.real_name))
|
|
to_chat(src, SPAN_WARNING("That donor offers you nothing new."))
|
|
return FALSE
|
|
|
|
visible_message(SPAN_DANGER("[src] flicks out a feeler and neatly steals a sample of [M]'s blood."),SPAN_DANGER("You flick out a feeler and neatly steal a sample of [M]'s blood."))
|
|
donors += M.real_name
|
|
for(var/datum/language/L in M.languages)
|
|
if(!(L.flags & HIVEMIND))
|
|
languages |= L
|
|
|
|
spawn(25)
|
|
update_progression()
|
|
|
|
/mob/living/basic/diona_nymph/proc/update_progression()
|
|
if(stat != CONSCIOUS || !length(donors))
|
|
return FALSE
|
|
|
|
if(length(donors) == evolve_donors)
|
|
to_chat(src, SPAN_NOTICEALIEN("You feel ready to move on to your next stage of growth."))
|
|
else if(length(donors) == awareness_donors)
|
|
universal_understand = TRUE
|
|
to_chat(src, SPAN_NOTICEALIEN("You feel your awareness expand, and realize you know how to understand the creatures around you."))
|
|
else
|
|
to_chat(src, SPAN_NOTICEALIEN("The blood seeps into your small form, and you draw out the echoes of memories and personality from it, working them into your budding mind."))
|
|
|
|
|
|
/mob/living/basic/diona_nymph/put_in_hands(obj/item/W)
|
|
W.forceMove(get_turf(src))
|
|
W.layer = initial(W.layer)
|
|
W.plane = initial(W.plane)
|
|
W.dropped()
|
|
|
|
/mob/living/basic/diona_nymph/put_in_active_hand(obj/item/W)
|
|
to_chat(src, SPAN_WARNING("You don't have any hands!"))
|
|
return
|
|
|
|
/mob/living/basic/diona_nymph/valid_respawn_target_for(mob/user)
|
|
if(!jobban_isbanned(user, ROLE_NYMPH))
|
|
return TRUE
|
|
return FALSE
|
|
|
|
/// Diona nymphs want to eat plant matter to get to evolution stage, but won't evolve or sample blood on their own. They flee when attacked.
|
|
/datum/ai_controller/basic_controller/diona_nymph
|
|
blackboard = list(
|
|
BB_TARGETING_STRATEGY = /datum/targeting_strategy/basic,
|
|
)
|
|
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/find_nearest_thing_which_attacked_me_to_flee,
|
|
/datum/ai_planning_subtree/flee_target,
|
|
/datum/ai_planning_subtree/find_food/diona_nymph,
|
|
/datum/ai_planning_subtree/random_speech/diona_nymph,
|
|
)
|
|
|
|
/datum/ai_planning_subtree/find_food/diona_nymph
|
|
|
|
/datum/ai_planning_subtree/find_food/diona_nymph/select_behaviors(datum/ai_controller/controller, seconds_per_tick)
|
|
var/mob/living/basic/diona_nymph/nymph = controller.pawn
|
|
if(nymph.nutrition >= nymph.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/diona_nymph
|
|
speech_chance = 5
|
|
emote_hear = list("chirrups.")
|
|
emote_see = list("chirrups.")
|
|
|
|
#undef GESTALT_ALERT
|
|
#undef NYMPH_ALERT
|