diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm index 580cb85d696..7412568704d 100644 --- a/code/__DEFINES/mobs.dm +++ b/code/__DEFINES/mobs.dm @@ -177,8 +177,7 @@ #define isbot(A) (istype((A), /mob/living/simple_animal/bot)) #define isswarmer(A) (istype((A), /mob/living/simple_animal/hostile/swarmer)) #define isguardian(A) (istype((A), /mob/living/simple_animal/hostile/guardian)) - - +#define isnymph(A) (istype((A), /mob/living/simple_animal/diona)) #define issilicon(A) (istype((A), /mob/living/silicon)) #define isAI(A) (istype((A), /mob/living/silicon/ai)) diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm index a187dfe3648..44c5e53b31a 100644 --- a/code/_onclick/hud/alert.dm +++ b/code/_onclick/hud/alert.dm @@ -315,6 +315,18 @@ Recharging stations are available in robotics, the dormitory bathrooms, and the desc = "Unit's power cell is running low. Recharging stations are available in robotics, the dormitory bathrooms, and the AI satellite." icon_state = "lowcell" +//Diona Nymph +/obj/screen/alert/nymph + name = "Gestalt merge" + desc = "You have merged with a diona gestalt and are now part of it's biomass. You can still wiggle yourself free though." + +/obj/screen/alert/nymph/Click() + if(!usr || !usr.client) + return + if(isnymph(usr)) + var/mob/living/simple_animal/diona/D = usr + return D.resist() + //Need to cover all use cases - emag, illegal upgrade module, malf AI hack, traitor cyborg /obj/screen/alert/hacked name = "Hacked" diff --git a/code/modules/mob/living/simple_animal/friendly/diona.dm b/code/modules/mob/living/simple_animal/friendly/diona.dm index 669271f3ead..0ca0d287ede 100644 --- a/code/modules/mob/living/simple_animal/friendly/diona.dm +++ b/code/modules/mob/living/simple_animal/friendly/diona.dm @@ -32,24 +32,72 @@ melee_damage_upper = 8 attacktext = "bites" attack_sound = 'sound/weapons/bite.ogg' + var/chirp_sound = 'sound/misc/nymphchirp.ogg' //used in emote speed = 0 stop_automated_movement = 0 turns_per_move = 4 var/list/donors = list() - var/ready_evolve = 0 holder_type = /obj/item/holder/diona - can_collar = 1 + can_collar = TRUE + + a_intent = INTENT_HELP + var/gestalt_alert = "merged with gestalt" //used in adding and clearing alert + var/evolve_donors = 5 //amount of blood donors needed before evolving + var/awareness_donors = 3 //amount of blood donors needed for understand language + var/nutrition_need = 500 //amount of nutrition needed before evolving + + var/datum/action/innate/diona/merge/merge_action = new() + var/datum/action/innate/diona/evolve/evolve_action = new() + var/datum/action/innate/diona/steal_blood/steal_blood_action = new() + +/datum/action/innate/diona/merge + name = "Merge with gestalt" + icon_icon = 'icons/mob/human_races/r_diona.dmi' + button_icon_state = "preview" + +/datum/action/innate/diona/merge/Activate() + var/mob/living/simple_animal/diona/user = owner + user.merge() + +/datum/action/innate/diona/evolve + name = "Evolve" + icon_icon = 'icons/obj/cloning.dmi' + button_icon_state = "pod_1" + +/datum/action/innate/diona/evolve/Activate() + var/mob/living/simple_animal/diona/user = owner + user.evolve() + +/datum/action/innate/diona/steal_blood + name = "Steal blood" + icon_icon = 'icons/obj/bloodpack.dmi' + button_icon_state = "full" + +/datum/action/innate/diona/steal_blood/Activate() + var/mob/living/simple_animal/diona/user = owner + user.steal_blood() /mob/living/simple_animal/diona/New() ..() if(name == initial(name)) //To stop Pun-Pun becoming generic. name = "[name] ([rand(1, 1000)])" real_name = name - add_language("Rootspeak") - verbs += /mob/living/simple_animal/diona/proc/merge + merge_action.Grant(src) + evolve_action.Grant(src) + steal_blood_action.Grant(src) + +/mob/living/simple_animal/diona/UnarmedAttack(var/atom/A) + if(isdiona(A) && (src in A.contents)) //can't attack your gestalt + visible_message("[src] wiggles around a bit.") + else + ..() + +/mob/living/simple_animal/diona/resist() + ..() + split() /mob/living/simple_animal/diona/attack_hand(mob/living/carbon/human/M) //Let people pick the little buggers up. @@ -57,88 +105,76 @@ 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.") - verbs += /mob/living/simple_animal/diona/proc/split - verbs -= /mob/living/simple_animal/diona/proc/merge + throw_alert(gestalt_alert, /obj/screen/alert/nymph, new_master = src) //adds a screen alert that can call resist forceMove(M) else get_scooped(M) - ..() - /mob/living/simple_animal/diona/proc/merge() - set category = "Diona" - set name = "Merge with gestalt" - set desc = "Merge with another diona." - - if(iscarbon(loc)) - verbs -= /mob/living/simple_animal/diona/proc/merge - return + if(stat != CONSCIOUS) + return FALSE var/list/choices = list() - for(var/mob/living/carbon/C in view(1,src)) - - if(!(Adjacent(C)) || !(C.client)) + for(var/mob/living/carbon/human/H in view(1,src)) + if(!(Adjacent(H)) || !isdiona(H)) continue + choices += H - if(ishuman(C)) - var/mob/living/carbon/human/D = C - if(isdiona(D)) - choices += C + if(!choices.len) + to_chat(src, "No suitable diona nearby.") + return FALSE var/mob/living/M = input(src,"Who do you wish to merge with?") in null|choices - if(!M || !src || !(Adjacent(M))) - return + if(!M || !src || !(Adjacent(M)) || stat != CONSCIOUS) //input can take a while, so re-validate + return FALSE - if(ishuman(M)) + 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) - verbs += /mob/living/simple_animal/diona/proc/split - verbs -= /mob/living/simple_animal/diona/proc/merge + throw_alert(gestalt_alert, /obj/screen/alert/nymph, new_master = src) //adds a screen alert that can call resist + return TRUE else - return + return FALSE /mob/living/simple_animal/diona/proc/split() - set category = "Diona" - set name = "Split from gestalt" - set desc = "Split away from your gestalt as a lone nymph." - - if(!(iscarbon(loc))) - verbs -= /mob/living/simple_animal/diona/proc/split - return - + if((stat != CONSCIOUS) || !isdiona(loc)) + return FALSE + var/mob/living/carbon/human/D = loc + 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(istype(A, /mob/) || istype(A, /obj/item/holder)) + hasMobs = TRUE + if(!hasMobs) + D.status_flags &= ~PASSEMOTES - var/mob/living/M = loc + clear_alert(gestalt_alert) + return TRUE - forceMove(get_turf(src)) - verbs -= /mob/living/simple_animal/diona/proc/split - verbs += /mob/living/simple_animal/diona/proc/merge - - if(istype(M)) - for(var/atom/A in M.contents) - if(istype(A, /mob/living/simple_animal/borer) || istype(A, /obj/item/holder)) - return - M.status_flags &= ~PASSEMOTES - -/mob/living/simple_animal/diona/verb/evolve() - set category = "Diona" - set name = "Evolve" - set desc = "Grow to a more complex form." - - if(donors.len < 5) +/mob/living/simple_animal/diona/proc/evolve() + if(stat != CONSCIOUS) + return FALSE + + if(donors.len < evolve_donors) to_chat(src, "You need more blood in order to ascend to a new state of consciousness...") - return + return FALSE - if(nutrition < 500) + if(nutrition < nutrition_need) to_chat(src, "You need to binge on weeds in order to have the energy to grow...") - return + return FALSE - split() + if(isdiona(loc) && !split()) //if it's merged with diona, needs to able to split before evolving + return FALSE + visible_message("[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.","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.") var/mob/living/carbon/human/diona/adult = new(get_turf(loc)) @@ -162,28 +198,33 @@ unEquip(W) qdel(src) + return TRUE -/mob/living/simple_animal/diona/verb/steal_blood() - set category = "Diona" - set name = "Steal Blood" - set desc = "Take a blood sample from a suitable donor." - +/mob/living/simple_animal/diona/proc/steal_blood() + if(stat != CONSCIOUS) + return FALSE + var/list/choices = list() for(var/mob/living/carbon/human/H in oview(1,src)) - choices += H + if(Adjacent(H) && H.dna && !(NO_BLOOD in H.dna.species.species_traits)) + choices += H + + if(!choices.len) + to_chat(src, "No suitable blood donors nearby.") + return FALSE var/mob/living/carbon/human/M = input(src,"Who do you wish to take a sample from?") in null|choices - if(!M || !src) - return + if(!M || !src || !(Adjacent(M)) || stat != CONSCIOUS) //input can take a while, so re-validate + return FALSE - if(NO_BLOOD in M.dna.species.species_traits) + if(!M.dna || (NO_BLOOD in M.dna.species.species_traits)) to_chat(src, "That donor has no blood to take.") - return + return FALSE if(donors.Find(M.real_name)) to_chat(src, "That donor offers you nothing new.") - return + return FALSE visible_message("[src] flicks out a feeler and neatly steals a sample of [M]'s blood.","You flick out a feeler and neatly steal a sample of [M]'s blood.") donors += M.real_name @@ -195,13 +236,12 @@ update_progression() /mob/living/simple_animal/diona/proc/update_progression() - if(!donors.len) - return + if(stat != CONSCIOUS || !donors.len) + return FALSE - if(donors.len == 5) - ready_evolve = 1 + if(donors.len == evolve_donors) to_chat(src, "You feel ready to move on to your next stage of growth.") - else if(donors.len == 3) + else if(donors.len == awareness_donors) universal_understand = 1 to_chat(src, "You feel your awareness expand, and realize you know how to understand the creatures around you.") else @@ -219,7 +259,7 @@ return /mob/living/simple_animal/diona/emote(act, m_type=1, message = null) - if(stat) + if(stat != CONSCIOUS) return var/on_CD = 0 @@ -237,6 +277,6 @@ if("chirp") message = "\The [src] chirps!" m_type = 2 //audible - playsound(src, 'sound/misc/nymphchirp.ogg', 40, 1, 1) + playsound(src, chirp_sound, 40, 1, 1) ..(act, m_type, message) \ No newline at end of file diff --git a/icons/mob/screen_alert.dmi b/icons/mob/screen_alert.dmi index 56c1730fd3a..6ef2a7bead3 100644 Binary files a/icons/mob/screen_alert.dmi and b/icons/mob/screen_alert.dmi differ