From 21073724048878f688e4ce5618ac4a8d205a9e40 Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Fri, 16 Mar 2018 00:20:17 -0400 Subject: [PATCH 1/4] Protean species --- code/__defines/damage_organs.dm | 1 + code/__defines/mobs_vr.dm | 4 + .../living/carbon/human/human_defines_vr.dm | 1 + .../living/carbon/human/human_species_vr.dm | 8 +- .../carbon/human/species/station/monkey_vr.dm | 2 +- .../station/protean_vr/protean_blob.dm | 309 ++++++++++++++++++ .../station/protean_vr/protean_powers.dm | 284 ++++++++++++++++ .../station/protean_vr/protean_species.dm | 299 +++++++++++++++++ code/modules/mob/mob_defines_vr.dm | 7 +- code/modules/organs/organ_external.dm | 12 +- code/modules/organs/subtypes/nano.dm | 155 +++++++++ code/modules/surgery/robotics.dm | 2 +- code/modules/vore/eating/living_vr.dm | 2 + icons/mob/species/protean/protean.dmi | Bin 0 -> 17403 bytes icons/mob/species/protean/protean_powers.dmi | Bin 0 -> 1509 bytes vorestation.dme | 4 + 16 files changed, 1080 insertions(+), 10 deletions(-) create mode 100644 code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm create mode 100644 code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm create mode 100644 code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm create mode 100644 code/modules/organs/subtypes/nano.dm create mode 100644 icons/mob/species/protean/protean.dmi create mode 100644 icons/mob/species/protean/protean_powers.dmi diff --git a/code/__defines/damage_organs.dm b/code/__defines/damage_organs.dm index 2e14fb96eb..7bb32fa75c 100644 --- a/code/__defines/damage_organs.dm +++ b/code/__defines/damage_organs.dm @@ -49,6 +49,7 @@ #define ORGAN_ASSISTED 1 // Like pacemakers, not robotic #define ORGAN_ROBOT 2 // Fully robotic, no organic parts #define ORGAN_LIFELIKE 3 // Robotic, made to appear organic +#define ORGAN_NANOFORM 4 // VOREStation Add - Fully nanoswarm organ //Germs and infections. #define GERM_LEVEL_AMBIENT 110 // Maximum germ level you can reach by standing still. diff --git a/code/__defines/mobs_vr.dm b/code/__defines/mobs_vr.dm index b8a1f86a24..2b573e334d 100644 --- a/code/__defines/mobs_vr.dm +++ b/code/__defines/mobs_vr.dm @@ -8,3 +8,7 @@ #define VIS_AUGMENTED 25 #define VIS_COUNT 25 + +//Protean organs +#define O_ORCH "orchestrator" +#define O_FACT "refactory" diff --git a/code/modules/mob/living/carbon/human/human_defines_vr.dm b/code/modules/mob/living/carbon/human/human_defines_vr.dm index fabc19d584..d3bb4f0b57 100644 --- a/code/modules/mob/living/carbon/human/human_defines_vr.dm +++ b/code/modules/mob/living/carbon/human/human_defines_vr.dm @@ -6,3 +6,4 @@ var/wagging = 0 //UGH. var/flapping = 0 var/vantag_pref = VANTAG_NONE //What's my status? + var/impersonate_bodytype //For impersonating a bodytype \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_species_vr.dm b/code/modules/mob/living/carbon/human/human_species_vr.dm index 52ca3f0426..45b615c240 100644 --- a/code/modules/mob/living/carbon/human/human_species_vr.dm +++ b/code/modules/mob/living/carbon/human/human_species_vr.dm @@ -18,4 +18,10 @@ ..(new_loc, "Xenomorph Hybrid") /mob/living/carbon/human/spider/New(var/new_loc) - ..(new_loc, "Vasilissan") \ No newline at end of file + ..(new_loc, "Vasilissan") + +/mob/living/carbon/human/vulpkanin/New(var/new_loc) + ..(new_loc, "Vulpkanin") + +/mob/living/carbon/human/protean/New(var/new_loc) + ..(new_loc, "Protean") diff --git a/code/modules/mob/living/carbon/human/species/station/monkey_vr.dm b/code/modules/mob/living/carbon/human/species/station/monkey_vr.dm index 2ea5ee3c1a..f56faa515a 100644 --- a/code/modules/mob/living/carbon/human/species/station/monkey_vr.dm +++ b/code/modules/mob/living/carbon/human/species/station/monkey_vr.dm @@ -65,5 +65,5 @@ /mob/living/carbon/human/sparram/New(var/new_loc) ..(new_loc, "Sparra") -/mob/living/carbon/human/vulpkaninm/New(var/new_loc) +/mob/living/carbon/human/wolpin/New(var/new_loc) ..(new_loc, "Wolpin") \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm new file mode 100644 index 0000000000..e06ba2af40 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -0,0 +1,309 @@ +// Simple animal nanogoopeyness +/mob/living/simple_animal/protean_blob + name = "protean blob" + desc = "Some sort of big viscous pool of jelly." + tt_desc = "Animated nanogoop" + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "to_puddle" + icon_living = "puddle2" + icon_rest = "rest" + icon_dead = "puddle" + + faction = "neutral" + maxHealth = 200 + health = 200 + + ai_inactive = TRUE //Always off + show_stat_health = FALSE //We will do it ourselves + + response_help = "pats the" + response_disarm = "gently pushes aside the" + response_harm = "hits the" + + harm_intent_damage = 2 + melee_damage_lower = 10 + melee_damage_upper = 10 + attacktext = list("slashed") + + min_oxy = 0 + max_oxy = 0 + min_tox = 0 + max_tox = 0 + min_co2 = 0 + max_co2 = 0 + min_n2 = 0 + max_n2 = 0 + minbodytemp = 0 + maxbodytemp = 900 + + speak_chance = 1 + speak = list("Blrb?","Sqrsh.","Glrsh!") + emote_hear = list("squishes softly","spluts quietly","makes wet noises") + emote_see = list("shifts wetly","undulates placidly") + + var/mob/living/carbon/human/humanform + var/obj/item/organ/internal/nano/refactory/refactory + var/datum/modifier/healing + + player_msg = "In this form, you can move a little faster, your health will regenerate as long as you have metal in you, and you can ventcrawl!" + +//Constructor allows passing the human to sync damages +/mob/living/simple_animal/protean_blob/New(var/newloc, var/mob/living/carbon/human/H) + ..() + if(H) + humanform = H + updatehealth() + refactory = locate() in humanform.internal_organs + verbs |= /mob/living/simple_animal/protean_blob/proc/revert_form + verbs |= /mob/living/proc/ventcrawl + verbs |= /mob/living/proc/hide + else + update_icon() + +/mob/living/simple_animal/protean_blob/Destroy() + humanform = null + refactory = null + vore_organs = null + vore_selected = null + if(healing) + healing.expire() + return ..() + +/mob/living/simple_animal/protean_blob/Stat() + ..() + if(statpanel("Status")) + stat(null, "- -- --- Protean Stats --- -- -") + stat(null, "Condition: [health]/[maxHealth]") + if(refactory) + var/max = refactory.max_storage + for(var/material in refactory.materials) + var/amount = refactory.get_stored_material(material) + stat(null, "[capitalize(material)]: [amount]/[max]") + +/mob/living/simple_animal/protean_blob/proc/revert_form() + set name = "Ref - Humanoid" + set desc = "Regain your humanoid form." + set category = "Abilities" + + if(health < maxHealth*0.5) + to_chat(src,"You're too injured for that! Regenerate using steel first.") + return + + if(humanform) + humanform.nano_outofblob(src) + else + to_chat(src,"Something's gone terribly wrong and we can't find your human body. Admin-help this.") + return + +/mob/living/simple_animal/protean_blob/update_icon() + if(humanform) + //Still have a refactory + if(istype(refactory)) + icon_living = "puddle2" + + //Else missing one + else + icon_living = "puddle1" + + //Not human-based + else + icon_living = "puddle0" + + ..() + +/mob/living/simple_animal/protean_blob/updatehealth() + if(humanform) + //Set the max + maxHealth = humanform.getMaxHealth()*2 //HUMANS, and their 'double health', bleh. + //Set us to their health, but, human health ignores robolimbs so we do it 'the hard way' + health = maxHealth - humanform.getOxyLoss() - humanform.getToxLoss() - humanform.getCloneLoss() - humanform.getActualFireLoss() - humanform.getActualBruteLoss() + + //Alive, becoming dead + if((stat < DEAD) && (health <= 0)) + death() + + //Overhealth + if(health > getMaxHealth()) + health = getMaxHealth() + + //Update our hud if we have one + if(healths) + if(stat != DEAD) + var/heal_per = (health / getMaxHealth()) * 100 + switch(heal_per) + if(100 to INFINITY) + healths.icon_state = "health0" + if(80 to 100) + healths.icon_state = "health1" + if(60 to 80) + healths.icon_state = "health2" + if(40 to 60) + healths.icon_state = "health3" + if(20 to 40) + healths.icon_state = "health4" + if(0 to 20) + healths.icon_state = "health5" + else + healths.icon_state = "health6" + else + healths.icon_state = "health7" + else + ..() + +/mob/living/simple_animal/protean_blob/adjustBruteLoss(var/amount) + if(humanform) + humanform.adjustBruteLoss(amount) + else + ..() + +/mob/living/simple_animal/protean_blob/adjustFireLoss(var/amount) + if(humanform) + humanform.adjustFireLoss(amount) + else + ..() + +/mob/living/simple_animal/protean_blob/death(gibbed, deathmessage = "dissolves away, leaving only a few spare parts!") + if(humanform) + humanform.death(gibbed = gibbed) + for(var/organ in humanform.internal_organs) + var/obj/item/organ/internal/O = organ + O.removed() + O.forceMove(drop_location()) + qdel_null(humanform) //Don't leave it just sitting in nullspace + animate(src,alpha = 0,time = 2 SECONDS) + sleep(2 SECONDS) + qdel(src) + + ..() + +/mob/living/simple_animal/protean_blob/Life() + . = ..() + if(. && istype(refactory) && humanform) + if(!healing && health < maxHealth && refactory.get_stored_material(DEFAULT_WALL_MATERIAL) >= 100) + healing = humanform.add_modifier(/datum/modifier/protean/steel, origin = refactory) + else if(healing && health == maxHealth) + healing.expire() + healing = null + +/mob/living/simple_animal/protean_blob/lay_down() + ..() + if(resting) + animate(src,alpha = 40,time = 1 SECOND) + mouse_opacity = 0 + else + mouse_opacity = 1 + icon_state = "wake" + animate(src,alpha = 255,time = 1 SECOND) + sleep(7) + update_icon() + //Potential glob noms + if(can_be_drop_pred) //Toggleable in vore panel + var/list/potentials = living_mobs(0) + if(potentials.len) + var/mob/living/target = pick(potentials) + if(istype(target) && vore_selected) + target.forceMove(vore_selected) + to_chat(target,"\The [src] quickly engulfs you, [vore_selected.vore_verb]ing you into their [vore_selected.name]!") + +/mob/living/simple_animal/protean_blob/DoPunch(var/atom/A) + if(refactory && istype(A,/obj/item/stack/material)) + var/obj/item/stack/material/S = A + if(refactory.add_stored_material(S.material.name,1*S.perunit) && S.use(1)) + visible_message("[name] gloms over some of \the [S], absorbing it.") + else + return ..() + +/mob/living/simple_animal/protean_blob/attackby(var/obj/item/O, var/mob/user) + if(refactory && istype(O,/obj/item/stack/material)) + var/obj/item/stack/material/S = O + if(refactory.add_stored_material(S.material.name,1*S.perunit) && S.use(1)) + visible_message("[name] gloms over some of \the [S], absorbing it.") + else + return ..() + +/mob/living/simple_animal/protean_blob/MouseEntered(location,control,params) + if(resting) + return + ..() + +// Helpers - Unsafe, WILL perform change. +/mob/living/carbon/human/proc/nano_intoblob() + if(buckled) + buckled.unbuckle_mob() + if(pulledby) + pulledby.stop_pulling() + stop_pulling() + + //Record where they should go + var/atom/creation_spot = drop_location() + + //Create our new blob + var/mob/living/simple_animal/protean_blob/blob = new(creation_spot,src) + + //Put our owner in it (don't transfer var/mind) + blob.ckey = ckey + temporary_form = blob + + //Mail them to nullspace + forceMove(null) + + //Message + blob.visible_message("[src.name] collapses into a gooey blob!") + + //Duration of the to_puddle iconstate that the blob starts with + sleep(13) + blob.update_icon() //Will remove the collapse anim + + //Transfer vore organs + blob.vore_organs = vore_organs + blob.vore_selected = vore_selected + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.forceMove(blob) + + //Return our blob in case someone wants it + return blob + +/mob/living/carbon/human/proc/nano_outofblob(var/mob/living/simple_animal/protean_blob/blob) + if(!istype(blob)) + return + if(buckled) + buckled.unbuckle_mob() + if(pulledby) + pulledby.stop_pulling() + stop_pulling() + + //Stop healing if we are + if(blob.healing) + blob.healing.expire() + + //Play the animation + blob.icon_state = "from_puddle" + + //Message + blob.visible_message("[src.name] reshapes into a humanoid appearance!") + + //Duration of above animation + sleep(8) + + //Record where they should go + var/atom/reform_spot = blob.drop_location() + + //Mail them to nullspace + forceMove(reform_spot) + + //Put our owner in it (don't transfer var/mind) + ckey = blob.ckey + temporary_form = null + Life(1) //Fix my blindness right meow + + //Transfer vore organs + for(var/belly in vore_organs) + var/obj/belly/B = belly + B.forceMove(src) + + //Get rid of friend blob + qdel(blob) + + //Return ourselves in case someone wants it + return src diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm new file mode 100644 index 0000000000..df6b44acdf --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_powers.dm @@ -0,0 +1,284 @@ +#define PER_LIMB_STEEL_COST SHEET_MATERIAL_AMOUNT +//// +// One-part Refactor +//// +/mob/living/carbon/human/proc/nano_partswap() + set name = "Ref - Single Limb" + set desc = "Allows you to replace and reshape your limbs as you see fit." + set category = "Abilities" + set hidden = TRUE + + if(stat) + to_chat(src,"You must be awake and standing to perform this action!") + return + + var/obj/item/organ/internal/nano/refactory/refactory = locate() in internal_organs + if(!refactory || refactory.status & ORGAN_DEAD) + to_chat(src,"You don't have a refactory module!") + return + + var/choice = input(src,"Pick the bodypart to change:", "Refactor - One Bodypart") as null|anything in species.has_limbs + if(!choice) + return + + //Organ is missing, needs restoring + if(!organs_by_name[choice]) + if(refactory.get_stored_material(DEFAULT_WALL_MATERIAL) < PER_LIMB_STEEL_COST) + to_chat(src,"You're missing that limb, and need to store at least [PER_LIMB_STEEL_COST] steel to regenerate it.") + return + var/regen = alert(src,"That limb is missing, do you want to regenerate it in exchange for [PER_LIMB_STEEL_COST] steel?","Regenerate limb?","Yes","No") + if(regen != "Yes") + return + if(!refactory.use_stored_material(DEFAULT_WALL_MATERIAL,PER_LIMB_STEEL_COST)) + return + + var/mob/living/simple_animal/protean_blob/blob = nano_intoblob() + active_regen = TRUE + if(do_after(blob,5 SECONDS)) + var/list/limblist = species.has_limbs[choice] + var/limbpath = limblist["path"] + var/obj/item/organ/external/new_eo = new limbpath(src) + organs_by_name[choice] = new_eo + new_eo.robotize(synthetic ? synthetic.company : null) //Use the base we started with + regenerate_icons() + active_regen = FALSE + nano_outofblob(blob) + return + + //Organ exists, let's reshape it + var/list/usable_manufacturers = list() + for(var/company in chargen_robolimbs) + var/datum/robolimb/M = chargen_robolimbs[company] + if(!(choice in M.parts)) + continue + if(species.name in M.species_cannot_use) + continue + if(M.whitelisted_to && !(ckey in M.whitelisted_to)) + continue + usable_manufacturers[company] = M + if(!usable_manufacturers.len) + return + var/manu_choice = input(src, "Which manufacturer do you wish to mimmic for this limb?", "Manufacturer for [choice]") as null|anything in usable_manufacturers + + if(!manu_choice) + return //Changed mind + + var/obj/item/organ/external/eo = organs_by_name[choice] + if(!eo) + return //Lost it meanwhile + + eo.robotize(manu_choice) + visible_message("[src]'s ") + update_icons_body() +#undef PER_LIMB_STEEL_COST + +//// +// Full Refactor +//// +/mob/living/carbon/human/proc/nano_regenerate() + set name = "Ref - Whole Body" + set desc = "Allows you to regrow limbs and replace organs, given you have enough materials." + set category = "Abilities" + set hidden = TRUE + + if(stat) + to_chat(src,"You must be awake and standing to perform this action!") + return + + var/obj/item/organ/internal/nano/refactory/refactory = locate() in internal_organs + //Missing the organ that does this + if(!istype(refactory)) + to_chat(src,"You don't have a refactory module!") + return + + //Already regenerating + if(active_regen) + to_chat(src, "You are already refactoring!") + return + + var/swap_not_rebuild = alert(src,"Do you want to rebuild, or reshape?","Rebuild or Reshape","Rebuild","Cancel","Reshape") + if(swap_not_rebuild == "Cancel") + return + if(swap_not_rebuild == "Reshape") + var/list/usable_manufacturers = list() + for(var/company in chargen_robolimbs) + var/datum/robolimb/M = chargen_robolimbs[company] + if(!(BP_TORSO in M.parts)) + continue + if(species.name in M.species_cannot_use) + continue + if(M.whitelisted_to && !(ckey in M.whitelisted_to)) + continue + usable_manufacturers[company] = M + if(!usable_manufacturers.len) + return + var/manu_choice = input(src, "Which manufacturer do you wish to mimmic?", "Manufacturer") as null|anything in usable_manufacturers + + if(!manu_choice) + return //Changed mind + if(!organs_by_name[BP_TORSO]) + return //Ain't got a torso! + + var/obj/item/organ/external/torso = organs_by_name[BP_TORSO] + to_chat(src, "Remain still while the process takes place! It will take 5 seconds.") + visible_message("[src]'s form collapses into an amorphous blob of black ichor...") + + var/mob/living/simple_animal/protean_blob/blob = nano_intoblob() + active_regen = TRUE + if(do_after(blob,5 SECONDS)) + synthetic = usable_manufacturers[manu_choice] + torso.robotize(manu_choice) //Will cascade to all other organs. + regenerate_icons() + visible_message("[src]'s form reshapes into a new one...") + active_regen = FALSE + nano_outofblob(blob) + return + + //Not enough resources (AND spends the resources, should be the last check) + if(!refactory.use_stored_material(DEFAULT_WALL_MATERIAL,refactory.max_storage)) + to_chat(src, "You need to be maxed out on normal metal to do this!") + return + + var/delay_length = round(active_regen_delay * species.active_regen_mult) + to_chat(src, "Remain still while the process takes place! It will take [delay_length/10] seconds.") + visible_message("[src]'s form begins to shift and ripple as if made of oil...") + active_regen = TRUE + + nano_intoblob() + if(do_after(src,delay_length)) + if(stat != DEAD && refactory) + var/list/holder = refactory.materials + species.create_organs(src) + var/obj/item/organ/external/torso = organs_by_name[BP_TORSO] + torso.robotize(synthetic.company) + LAZYCLEARLIST(blood_DNA) + LAZYCLEARLIST(feet_blood_DNA) + blood_color = null + feet_blood_color = null + regenerate_icons() //Probably worth it, yeah. + var/obj/item/organ/internal/nano/refactory/new_refactory = locate() in internal_organs + if(!new_refactory) + log_debug("[src] protean-regen'd but lacked a refactory when done.") + else + new_refactory.materials = holder + to_chat(src, "Your refactoring is complete!") + else + to_chat(src, "Your refactoring is interrupted!") + nano_outofblob() + + +//// +// Storing metal +//// +/mob/living/carbon/human/proc/nano_metalnom() + set name = "Ref - Store Metals" + set desc = "If you're holding a stack of material, you can consume some and store it for later." + set category = "Abilities" + set hidden = TRUE + + var/obj/item/organ/internal/nano/refactory/refactory = locate() in internal_organs + //Missing the organ that does this + if(!istype(refactory)) + to_chat(src,"You don't have a refactory module!") + return + + var/held = get_active_hand() + if(!istype(held,/obj/item/stack/material)) + to_chat(src,"You aren't holding a stack of materials in your active hand...!") + return + + var/obj/item/stack/material/matstack = held + var/howmuch = input(src,"How much do you want to store? (0-[matstack.amount])","Select amount") as null|num + if(!howmuch || matstack != get_active_hand() || howmuch > matstack.amount) + return //Quietly fail + + var/substance = matstack.material.name + var/actually_added = refactory.add_stored_material(substance,howmuch*matstack.perunit) + matstack.use(Ceiling(actually_added/matstack.perunit)) + if(actually_added < howmuch) + to_chat(src,"Your refactory module is now full, so only [actually_added] units were stored.") + else + to_chat(src,"You store [actually_added] units of [substance].") + +//// +// Blob Form +//// +/mob/living/carbon/human/proc/nano_blobform() + set name = "Become Amorphous" + set desc = "Drop your shape and assume a more natural form." + set category = "Abilities" + set hidden = TRUE + + if(stat) + to_chat(src,"You must be awake and standing to perform this action!") + return + + nano_intoblob() + +//// +// Change fitting +//// + +/mob/living/carbon/human/proc/nano_change_fitting() + set name = "Change Species Fit" + set desc = "Tweak your shape to change what suits you fit into (and their sprites!)." + set category = "Abilities" + + if(stat) + to_chat(src,"You must be awake and standing to perform this action!") + return + + var/new_species = input("Please select a species to emulate.", "Shapeshifter Body") as null|anything in playable_species + if(new_species) + impersonate_bodytype = new_species + regenerate_icons() + +/// /// /// Ability objects for stat panel +/obj/effect/protean_ability + name = "Activate" + desc = "" + icon = 'icons/mob/species/protean/protean_powers.dmi' + var/ability_name + var/to_call + +/obj/effect/protean_ability/proc/atom_button_text() + return src + +/obj/effect/protean_ability/Click(var/location, var/control, var/params) + var/list/clickprops = params2list(params) + var/opts = clickprops["shift"] + + if(opts) + to_chat(usr,"[ability_name] - [desc]") + else + do_ability(usr) + +/obj/effect/protean_ability/proc/do_ability(var/mob/living/carbon/human/H) + if(istype(H) && !H.stat) + call(H,to_call)() + return FALSE + +/// The actual abilities +/obj/effect/protean_ability/into_blob + ability_name = "Become Amorphous" + desc = "Discard your shape entirely, changing to a low-energy blob that can fit into small spaces. You'll consume steel to repair yourself in this form." + icon_state = "blob" + to_call = /mob/living/carbon/human/proc/nano_blobform + +/obj/effect/protean_ability/reform_limb + ability_name = "Ref - Single Limb" + desc = "Rebuild or replace a single limb, assuming you have 2000 steel." + icon_state = "limb" + to_call = /mob/living/carbon/human/proc/nano_partswap + +/obj/effect/protean_ability/reform_body + ability_name = "Ref - Whole Body" + desc = "Rebuild your entire body into whatever design you want, assuming you have 10,000 metal." + icon_state = "body" + to_call = /mob/living/carbon/human/proc/nano_regenerate + +/obj/effect/protean_ability/metal_nom + ability_name = "Ref - Store Metals" + desc = "Store the metal you're holding. Your refactory can only store steel, and all other metals will be converted into nanites ASAP for various effects." + icon_state = "metal" + to_call = /mob/living/carbon/human/proc/nano_metalnom diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm new file mode 100644 index 0000000000..1e01004abb --- /dev/null +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -0,0 +1,299 @@ +#define DAM_SCALE_FACTOR 0.01 +/datum/species/protean + name = "Protean" + name_plural = "Proteans" + blurb = "Sometimes very advanced civilizations will produce the ability to swap into manufactured, robotic bodies. And sometimes \ + VERY advanced civilizations have the option of 'nanoswarm' bodies. Effectively a single robot body comprised \ + of millions of tiny nanites working in concert to maintain cohesion." + show_ssd = "totally quiescent" + death_message = "rapidly loses cohesion, dissolving into a cloud of gray dust..." + knockout_message = "collapses inwards, forming a disordered puddle of gray goo." + remains_type = /obj/effect/decal/cleanable/ash + + blood_color = "#000000" + flesh_color = "#000000" + + flags = NO_SCAN | NO_SLIP | NO_MINOR_CUT | NO_HALLUCINATION | NO_INFECT | NO_PAIN + appearance_flags = HAS_SKIN_COLOR | HAS_EYE_COLOR | HAS_HAIR_COLOR | HAS_UNDERWEAR | HAS_LIPS + spawn_flags = SPECIES_CAN_JOIN | SPECIES_IS_WHITELISTED + health_hud_intensity = 2 + num_alternate_languages = 3 + species_language = LANGUAGE_SOL_COMMON + + breath_type = null + poison_type = null + + virus_immune = 1 + blood_volume = 0 + min_age = 18 + max_age = 200 + brute_mod = 0.2 //Brute isn't very effective, they're made of dust + burn_mod = 2.0 //Burn, however, is + oxy_mod = 0 + + cold_level_1 = 280 //Default 260 - Lower is better + cold_level_2 = 220 //Default 200 + cold_level_3 = 130 //Default 120 + + heat_level_1 = 320 //Default 360 + heat_level_2 = 370 //Default 400 + heat_level_3 = 600 //Default 1000 + + //Space doesn't bother them + hazard_low_pressure = -1 + hazard_high_pressure = 200 //They can cope with slightly higher pressure + + //Cold/heat does affect them, but it's done in special ways below + cold_level_1 = -INFINITY + cold_level_2 = -INFINITY + cold_level_3 = -INFINITY + heat_level_1 = INFINITY + heat_level_2 = INFINITY + heat_level_3 = INFINITY + + body_temperature = 290 + + siemens_coefficient = 3 //Very bad zappy times + rarity_value = 5 + + has_organ = list( + O_BRAIN = /obj/item/organ/internal/mmi_holder/posibrain/nano, + O_ORCH = /obj/item/organ/internal/nano/orchestrator, + O_FACT = /obj/item/organ/internal/nano/refactory + ) + has_limbs = list( + BP_TORSO = list("path" = /obj/item/organ/external/chest/unbreakable/nano), + BP_GROIN = list("path" = /obj/item/organ/external/groin/unbreakable/nano), + BP_HEAD = list("path" = /obj/item/organ/external/head/unbreakable/nano), + BP_L_ARM = list("path" = /obj/item/organ/external/arm/unbreakable/nano), + BP_R_ARM = list("path" = /obj/item/organ/external/arm/right/unbreakable/nano), + BP_L_LEG = list("path" = /obj/item/organ/external/leg/unbreakable/nano), + BP_R_LEG = list("path" = /obj/item/organ/external/leg/right/unbreakable/nano), + BP_L_HAND = list("path" = /obj/item/organ/external/hand/unbreakable/nano), + BP_R_HAND = list("path" = /obj/item/organ/external/hand/right/unbreakable/nano), + BP_L_FOOT = list("path" = /obj/item/organ/external/foot/unbreakable/nano), + BP_R_FOOT = list("path" = /obj/item/organ/external/foot/right/unbreakable/nano) + ) + + heat_discomfort_strings = list("You feel too warm.") + cold_discomfort_strings = list("You feel too cool.") + + //These verbs are hidden, for hotkey use only + inherent_verbs = list( + /mob/living/carbon/human/proc/nano_regenerate, //These verbs are hidden so you can macro them, + /mob/living/carbon/human/proc/nano_partswap, + /mob/living/carbon/human/proc/nano_metalnom, + /mob/living/carbon/human/proc/nano_blobform, + /mob/living/carbon/human/proc/nano_change_fitting, //These verbs are displayed normally, + /mob/living/carbon/human/proc/shapeshifter_select_hair, + /mob/living/carbon/human/proc/shapeshifter_select_hair_colors, + /mob/living/carbon/human/proc/shapeshifter_select_colour, + /mob/living/carbon/human/proc/shapeshifter_select_eye_colour + ) + + var/global/list/abilities = list() + + var/monochromatic = FALSE //IGNORE ME + +/datum/species/protean/New() + ..() + if(!LAZYLEN(abilities)) + var/list/powertypes = subtypesof(/obj/effect/protean_ability) + for(var/path in powertypes) + abilities += new path() + +/datum/species/protean/create_organs(var/mob/living/carbon/human/H) + var/obj/item/device/nif/saved_nif = H.nif + if(saved_nif) + H.nif.unimplant() + H.nif.forceMove(null) + ..() + if(saved_nif) + saved_nif.quick_implant(H) + +/datum/species/protean/get_bodytype(var/mob/living/carbon/human/H) + if(H) + return H.impersonate_bodytype || ..() + return ..() + +/datum/species/protean/handle_post_spawn(var/mob/living/carbon/human/H) + ..() + H.synth_color = TRUE + +/datum/species/protean/equip_survival_gear(var/mob/living/carbon/human/H) + var/obj/item/stack/material/steel/metal_stack = new() + metal_stack.amount = 3 + + var/obj/item/clothing/accessory/permit/nanotech/permit = new() + permit.set_name(H.real_name) + + if(H.backbag == 1) //Somewhat misleading, 1 == no bag (not boolean) + H.equip_to_slot_or_del(permit, slot_l_hand) + H.equip_to_slot_or_del(metal_stack, slot_r_hand) + else + H.equip_to_slot_or_del(permit, slot_in_backpack) + H.equip_to_slot_or_del(metal_stack, slot_in_backpack) + + spawn(0) //Let their real nif load if they have one + if(!H.nif) + var/obj/item/device/nif/bioadap/new_nif = new() + new_nif.quick_implant(H) + else + H.nif.durability = rand(21,25) + +/datum/species/protean/hug(var/mob/living/carbon/human/H, var/mob/living/target) + return ..() //Wut + +/datum/species/protean/get_blood_colour(var/mob/living/carbon/human/H) + return rgb(80,80,80,230) + +/datum/species/protean/get_flesh_colour(var/mob/living/carbon/human/H) + return rgb(80,80,80,230) + +/datum/species/protean/handle_death(var/mob/living/carbon/human/H) + to_chat(H,"You died as a Protean. Please sit out of the round for at least 30 minutes before respawning, to represent the time it would take to ship a new-you to the station.") + spawn(1) + if(H) + H.gib() + +/datum/species/protean/handle_environment_special(var/mob/living/carbon/human/H) + if((H.getActualBruteLoss() + H.getActualFireLoss()) > H.maxHealth*0.5 && isturf(H.loc)) //So, only if we're not a blob (we're in nullspace) or in someone (or a locker, really, but whatever) + H.nano_intoblob() + + var/obj/item/organ/internal/nano/refactory/refactory = locate() in H.internal_organs + if(refactory && !(refactory.status & ORGAN_DEAD)) + + //MHydrogen adds speeeeeed + if(refactory.get_stored_material("mhydrogen") >= 100) + H.add_modifier(/datum/modifier/protean/mhydrogen, origin = refactory) + + //Plasteel adds brute armor + if(refactory.get_stored_material("plasteel") >= 100) + H.add_modifier(/datum/modifier/protean/plasteel, origin = refactory) + + //Diamond adds burn armor + if(refactory.get_stored_material("diamond") >= 100) + H.add_modifier(/datum/modifier/protean/diamond, origin = refactory) + + return ..() + +/datum/species/protean/get_additional_examine_text(var/mob/living/carbon/human/H) + return ..() //Hmm, what could be done here? + +/datum/species/protean/Stat(var/mob/living/carbon/human/H) + ..() + if(statpanel("Protean")) + var/obj/item/organ/internal/nano/refactory/refactory = locate() in H.internal_organs + if(refactory && !(refactory.status & ORGAN_DEAD)) + stat(null, "- -- --- Refactory Metal Storage --- -- -") + var/max = refactory.max_storage + for(var/material in refactory.materials) + var/amount = refactory.get_stored_material(material) + stat("[capitalize(material)]", "[amount]/[max]") + else + stat(null, "- -- --- REFACTORY ERROR! --- -- -") + + stat(null, "- -- --- Abilities (Shift+LMB Examines) --- -- -") + for(var/ability in abilities) + var/obj/effect/protean_ability/A = ability + stat("[A.ability_name]",A.atom_button_text()) + +// Various modifiers +/datum/modifier/protean + stacks = MODIFIER_STACK_FORBID + var/material_use = 100 + var/material_name = DEFAULT_WALL_MATERIAL + +/datum/modifier/protean/on_applied() + . = ..() + if(holder.temporary_form) + to_chat(holder.temporary_form,on_created_text) + +/datum/modifier/protean/on_expire() + . = ..() + if(holder.temporary_form) + to_chat(holder.temporary_form,on_expired_text) + +/datum/modifier/protean/tick() + //No origin set + if(!istype(origin)) + expire() + + //No refactory + var/obj/item/organ/internal/nano/refactory/refactory = origin.resolve() + if(!istype(refactory) || refactory.status & ORGAN_DEAD) + expire() + + //Out of materials + if(!refactory.use_stored_material(material_name,material_use)) + expire() + +/datum/modifier/protean/mhydrogen + name = "Protean Effect - M.Hydrogen" + desc = "You're affected by the presence of metallic hydrogen." + + on_created_text = "You feel yourself accelerate, the metallic hydrogen increasing your speed temporarily." + on_expired_text = "Your refactory finishes consuming the metallic hydrogen, and you return to normal speed." + + material_use = 100 + material_name = "mhydrogen" + + slowdown = -1 + +/datum/modifier/protean/plasteel + name = "Protean Effect - Plasteel" + desc = "You're affected by the presence of plasteel." + + on_created_text = "You feel yourself become nearly impervious to physical attacks as plasteel nanites are made." + on_expired_text = "Your refactory finishes consuming the plasteel, and you return to your normal nanites." + + material_use = 100 + material_name = "plasteel" + + incoming_brute_damage_percent = 0.5 + +/datum/modifier/protean/diamond + name = "Protean Effect - Diamond" + desc = "You're affected by the presence of diamond." + + on_created_text = "You feel yourself become more reflective, able to resist heat and fire better for a time." + on_expired_text = "Your refactory finishes consuming the diamond, and you return to your normal nanites." + + material_use = 100 + material_name = "diamond" + + incoming_fire_damage_percent = 0.2 + +/datum/modifier/protean/steel + name = "Protean Effect - Steel" + desc = "You're affected by the presence of steel." + + on_created_text = "You feel new nanites being produced from your stockpile of steel, healing you slowly." + on_expired_text = "Your steel supply has either run out, or is no longer needed, and your healing stops." + + material_use = 100 + material_name = "steel" + +/datum/modifier/protean/steel/tick() + ..() + holder.adjustBruteLoss(-10) //Looks high, but these ARE modified by species resistances, so this is really 20% of this + holder.adjustFireLoss(-1) //And this is really double this + var/mob/living/carbon/human/H = holder + for(var/organ in H.internal_organs) + var/obj/item/organ/O = organ + if(O.damage > 0) // Fix internal damage + O.damage = max(0,O.damage-0.1) + +// PAN Card +/obj/item/clothing/accessory/permit/nanotech + name = "\improper P.A.N. card" + desc = "This is a 'Permit for Advanced Nanotechnology' card. It allows the owner to possess and operate advanced nanotechnology on NanoTrasen property. It must be renewed on a monthly basis." + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "permit_pan" +/obj/item/clothing/accessory/permit/nanotech/set_name(var/new_name) + owner = 1 + if(new_name) + src.name += " ([new_name])" + desc += "\nVALID THROUGH END OF: [time2text(world.timeofday, "Month") +" "+ num2text(text2num(time2text(world.timeofday, "YYYY"))+544)]\nREGISTRANT: [new_name]" + +#undef DAM_SCALE_FACTOR \ No newline at end of file diff --git a/code/modules/mob/mob_defines_vr.dm b/code/modules/mob/mob_defines_vr.dm index ab1670560f..829ae9b8fe 100644 --- a/code/modules/mob/mob_defines_vr.dm +++ b/code/modules/mob/mob_defines_vr.dm @@ -1,5 +1,4 @@ /mob - var/vantag_hud = 0 // Do I have the HUD enabled? - var/flying = 0 //Allows flight - var/can_be_drop_prey = 0 - var/can_be_drop_pred = 1 //Mobs are pred by default. \ No newline at end of file + var/vantag_hud = 0 // Do I have the HUD enabled? + var/flying = 0 // Allows flight + var/mob/temporary_form // For holding onto a temporary form diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index ba5de23d8e..ae31a9cdba 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -816,7 +816,11 @@ Note that amputating the affected organ does in fact remove the infection from t if(!cannot_amputate) if(nonsolid && damage >= max_damage) - droplimb(DROPLIMB_BLUNT) + droplimb(true, DROPLIMB_BLUNT) + //VOREStation Add Start + if(robotic >= ORGAN_NANOFORM && damage >= max_damage) + droplimb(true, DROPLIMB_BURN) + //VOREStation Add End /**************************************************** DISMEMBERMENT @@ -827,8 +831,10 @@ Note that amputating the affected organ does in fact remove the infection from t if(cannot_amputate || !owner) return - - if(disintegrate == DROPLIMB_EDGE && nonsolid) + //VOREStation Add + if(robotic >= ORGAN_NANOFORM) + disintegrate = DROPLIMB_BURN //Ashes will be fine + else if(disintegrate == DROPLIMB_EDGE && nonsolid) //VOREStation Add End disintegrate = DROPLIMB_BLUNT //splut switch(disintegrate) diff --git a/code/modules/organs/subtypes/nano.dm b/code/modules/organs/subtypes/nano.dm new file mode 100644 index 0000000000..639fdec4e5 --- /dev/null +++ b/code/modules/organs/subtypes/nano.dm @@ -0,0 +1,155 @@ +// // // External Organs +/obj/item/organ/external/chest/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 50 // <-- This is different from the rest + min_broken_damage = 1000 + vital = TRUE // <-- This is different from the rest +/obj/item/organ/external/groin/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 30 // <-- This is different from the rest + min_broken_damage = 1000 //Multiple + vital = FALSE +/obj/item/organ/external/head/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 //Inheritance + vital = FALSE +/obj/item/organ/external/arm/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 //Please + vital = FALSE +/obj/item/organ/external/arm/right/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/leg/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/leg/right/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/hand/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/hand/right/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/foot/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE +/obj/item/organ/external/foot/right/unbreakable/nano + robotic = ORGAN_NANOFORM + encased = FALSE + max_damage = 10 + min_broken_damage = 1000 + vital = FALSE + +//Sideways override for nanoform limbs (ugh) +/obj/item/organ/external/robotize(var/company, var/skip_prosthetics = FALSE, var/keep_organs = FALSE) + var/original_robotic = robotic + if(original_robotic >= ORGAN_NANOFORM) + var/o_encased = encased + var/o_max_damage = max_damage + var/o_min_broken_damage = min_broken_damage + robotic = FALSE + ..(company = company, keep_organs = TRUE) + robotic = original_robotic + encased = o_encased + max_damage = o_max_damage + min_broken_damage = o_min_broken_damage + else + ..() + +// // // Internal Organs +/obj/item/organ/internal/nano + robotic = ORGAN_ROBOT + +/obj/item/organ/internal/nano/orchestrator + name = "orchestrator module" + desc = "A small computer, designed for highly parallel workloads." + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "orchestrator" + organ_tag = O_ORCH + parent_organ = BP_TORSO + vital = TRUE + +/obj/item/organ/internal/nano/refactory + name = "refactory module" + desc = "A miniature metal processing unit and nanite factory." + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "refactory" + organ_tag = O_FACT + parent_organ = BP_TORSO + + var/list/materials = list(DEFAULT_WALL_MATERIAL = 0) + var/max_storage = 10000 + +/obj/item/organ/internal/nano/refactory/proc/get_stored_material(var/material) + if(status & ORGAN_DEAD) + return 0 + return materials[material] || 0 + +/obj/item/organ/internal/nano/refactory/proc/add_stored_material(var/material,var/amt) + if(status & ORGAN_DEAD) + return 0 + var/increase = min(amt,max(max_storage-materials[material],0)) + if(isnum(materials[material])) + materials[material] += increase + else + materials[material] = increase + + return increase + +/obj/item/organ/internal/nano/refactory/proc/use_stored_material(var/material,var/amt) + if(status & ORGAN_DEAD) + return 0 + + var/available = materials[material] + + //Success + if(available >= amt) + var/new_amt = available-amt + if(new_amt == 0) + materials -= material + else + materials[material] = new_amt + return amt + + //Failure + return 0 + +/obj/item/organ/internal/mmi_holder/posibrain/nano + name = "protean posibrain" + desc = "A more advanced version of the standard posibrain, typically found in protean bodies." + icon = 'icons/mob/species/protean/protean.dmi' + icon_state = "posi" + parent_organ = BP_TORSO + +/obj/item/organ/internal/mmi_holder/posibrain/nano/update_from_mmi() + ..() + icon = initial(icon) + icon_state = "posi1" + + stored_mmi.brainmob.languages = owner.languages diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index bd4699533f..ba8175b1d5 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -18,7 +18,7 @@ return 0 if (affected.status & ORGAN_DESTROYED) return 0 - if (!(affected.robotic >= ORGAN_ROBOT)) + if (!(affected.robotic == ORGAN_ROBOT)) //VOREStation Edit - No good on ORGAN_NANOFORM return 0 return 1 diff --git a/code/modules/vore/eating/living_vr.dm b/code/modules/vore/eating/living_vr.dm index 2afba124df..d433572837 100644 --- a/code/modules/vore/eating/living_vr.dm +++ b/code/modules/vore/eating/living_vr.dm @@ -19,6 +19,8 @@ var/absorbing_prey = 0 // Determines if the person is using the succubus drain or not. See station_special_abilities_vr. var/drain_finalized = 0 // Determines if the succubus drain will be KO'd/absorbed. Can be toggled on at any time. var/fuzzy = 1 // Preference toggle for sharp/fuzzy icon. + var/can_be_drop_prey = 0 + var/can_be_drop_pred = 1 //Mobs are pred by default. // // Hook for generic creation of stuff on new creatures diff --git a/icons/mob/species/protean/protean.dmi b/icons/mob/species/protean/protean.dmi new file mode 100644 index 0000000000000000000000000000000000000000..f644aa051a8be6afe7dcc3f5e149ab86af825101 GIT binary patch literal 17403 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 l$JL3Ic*sQA$Wi%M!~*|Fqc+btT+YoAGMx1j-? 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 literal 0 HcmV?d00001 diff --git a/icons/mob/species/protean/protean_powers.dmi b/icons/mob/species/protean/protean_powers.dmi new file mode 100644 index 0000000000000000000000000000000000000000..48debef8541fb72a62471684e8a3ef5d801bd095 GIT binary patch literal 1509 zcmVo84nK+L_|aYsHh_&BLL>+h#~;eH2^+7K0iM{KtMo2K|w=9 zLp(e@H8nLZE-qVHSzcXTU|wEdUS45fU}9ikV_{)oUteBaTwGaMSy)(9Q&TA^DMCU* zW@Kb%W@c(=Xl!X|Y-?+5YHDj}XlZ9>Wn^SqT3T3FS1c?nXl7QGVPRTWSWQh$b8&HQY;1IM zb9s1peSCa?eSLp@eSLd-d3SeYV`E%fTQM;)dwF?*etv_3f`x>HhlGTOg@tl(a7#-| zad2>Vb#;V;gNTNPiiwGdhK7E9ePCZ-IXO8;M@MjOZg_Tffq;OBg@ucXij9nnjf;zn zi;IDOe|UFygMopLjEstih=PHEZ*6UhiHVSnjgF0tJ3BjFTwHs2c!h$3k&cd#j*gFv zjB##mV`5@7G&EXSS&WK`g@c29dwW$>REdX&Wn*J1Dk^|}eu#&Mf`EXDh=@8mI&f`m zb#!!hc6M}fa$H(kXJut+XlP?#U|CmJD=RBsU0q#UTO=eTFfcGPGczYAC#Qk6wEzGB z0d!JMQvg8b*k%9#0EK!~Sad{Xb7OL8aCB*JZU6vyoKseCa&`CgQ*iP1JDX{`ou#^_%mZLzyVm(Un0IE>ALBR?N2(3V?C~}C1 z2ZHy32l{D!2OdM-!?u}mbljPAGRfQj+u!cKY$fS`GXOB>FUztS1&|e_m{0&kNkeIh zlClCQh*;_Z5~$c;HI)QaHRBJUf`+eDB}ff$1S!`6WQ4Pb{Q>QwAnr8kPy0XtRipr5 zu~uA^(97u1b;w|xsNAC z!3LMnPPk8QQ}`PLO--j7{hrfjcnoxKah{)bH#3Ts*5JAGZSA2}|AmfD&O$o4xDhlm zisr8Fp5DIx?%;sOp3PZE2N&o0MfW8}G1xYAIUK$+G#scHxynPJgUe{wyrYa_Z2bDf zWF*>uW3VMJle17XxawRLjACj!9G#ho#NtyoU0IxkqQO->;$ak_>06Q6+mqp*kk4h~ z3h)QlojEU~xH}(?EG#Zf%m?o+a|Hwl_g6X?MJOJNEG#TNSbf;+F6IkxKiQp+Y8XW* z{x~|jxD;FM8hDby6%c&$*AzxE_Ove+jZCbMhkVOP34EsrbZzvmKU*IfZYp;^=L?7i z7o%th1lu;^e(gK*GXl<=_Oa3Bn<$O)S|1Y&~Q$vM(CAT^n00000 LNkvXXu0mjf=Vyv( literal 0 HcmV?d00001 diff --git a/vorestation.dme b/vorestation.dme index d096cff779..4677710138 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -2019,6 +2019,9 @@ #include "code\modules\mob\living\carbon\human\species\station\station_special_abilities_vr.dm" #include "code\modules\mob\living\carbon\human\species\station\station_special_vr.dm" #include "code\modules\mob\living\carbon\human\species\station\station_vr.dm" +#include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_blob.dm" +#include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_powers.dm" +#include "code\modules\mob\living\carbon\human\species\station\protean_vr\protean_species.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\negative.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\neutral.dm" #include "code\modules\mob\living\carbon\human\species\station\traits_vr\positive.dm" @@ -2274,6 +2277,7 @@ #include "code\modules\organs\internal\organ_internal.dm" #include "code\modules\organs\subtypes\diona.dm" #include "code\modules\organs\subtypes\machine.dm" +#include "code\modules\organs\subtypes\nano.dm" #include "code\modules\organs\subtypes\seromi.dm" #include "code\modules\organs\subtypes\slime.dm" #include "code\modules\organs\subtypes\standard.dm" From 4fb345ae6b6dbe29a5a36c5d3eb29025474c18b4 Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Fri, 16 Mar 2018 00:43:00 -0400 Subject: [PATCH 2/4] Fix protean missing closing span tags+inclrobo --- .../carbon/human/species/station/protean_vr/protean_blob.dm | 2 +- .../human/species/station/protean_vr/protean_species.dm | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index e06ba2af40..00011648ab 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -92,7 +92,7 @@ if(humanform) humanform.nano_outofblob(src) else - to_chat(src,"Something's gone terribly wrong and we can't find your human body. Admin-help this.") + to_chat(src,"Something's gone terribly wrong and we can't find your human body. Admin-help this.") return /mob/living/simple_animal/protean_blob/update_icon() diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index 1e01004abb..af775ca519 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -151,7 +151,7 @@ return rgb(80,80,80,230) /datum/species/protean/handle_death(var/mob/living/carbon/human/H) - to_chat(H,"You died as a Protean. Please sit out of the round for at least 30 minutes before respawning, to represent the time it would take to ship a new-you to the station.") + to_chat(H,"You died as a Protean. Please sit out of the round for at least 30 minutes before respawning, to represent the time it would take to ship a new-you to the station.") spawn(1) if(H) H.gib() @@ -276,8 +276,8 @@ /datum/modifier/protean/steel/tick() ..() - holder.adjustBruteLoss(-10) //Looks high, but these ARE modified by species resistances, so this is really 20% of this - holder.adjustFireLoss(-1) //And this is really double this + holder.adjustBruteLoss(-10,include_robo = TRUE) //Looks high, but these ARE modified by species resistances, so this is really 20% of this + holder.adjustFireLoss(-1,include_robo = TRUE) //And this is really double this var/mob/living/carbon/human/H = holder for(var/organ in H.internal_organs) var/obj/item/organ/O = organ From 99032248ab50063e2d4eab594032eab07a996686 Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Fri, 16 Mar 2018 19:46:49 -0400 Subject: [PATCH 3/4] Lifelike oversights --- code/modules/mob/living/carbon/human/update_icons.dm | 2 +- code/modules/surgery/robotics.dm | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 6c7e0114ab..7ccfbec7d6 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -265,7 +265,7 @@ var/global/list/damage_icon_parts = list() //see UpdateDamageIcon() if(part.robotic >= ORGAN_ROBOT) icon_key += "2[part.model ? "-[part.model]": ""]" robolimb_count++ - if(part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN) + if((part.robotic == ORGAN_ROBOT || part.robotic == ORGAN_LIFELIKE) && (part.organ_tag == BP_HEAD || part.organ_tag == BP_TORSO || part.organ_tag == BP_GROIN)) //VOREStation Edit - Not for nanoform parts robobody_count ++ else if(part.status & ORGAN_DEAD) icon_key += "3" diff --git a/code/modules/surgery/robotics.dm b/code/modules/surgery/robotics.dm index ba8175b1d5..0e83e9ff21 100644 --- a/code/modules/surgery/robotics.dm +++ b/code/modules/surgery/robotics.dm @@ -18,7 +18,7 @@ return 0 if (affected.status & ORGAN_DESTROYED) return 0 - if (!(affected.robotic == ORGAN_ROBOT)) //VOREStation Edit - No good on ORGAN_NANOFORM + if (!(affected.robotic == ORGAN_ROBOT || affected.robotic == ORGAN_LIFELIKE)) //VOREStation Edit - No good on ORGAN_NANOFORM return 0 return 1 From 42e32614285cd591cd5ae0f3ec9d85b9001d122a Mon Sep 17 00:00:00 2001 From: Arokha Sieyes Date: Sat, 17 Mar 2018 12:33:04 -0400 Subject: [PATCH 4/4] Final tweaks --- .../carbon/human/species/station/protean_vr/protean_blob.dm | 2 +- .../human/species/station/protean_vr/protean_species.dm | 6 +++++- code/modules/organs/organ_external.dm | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm index 00011648ab..f51510a535 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_blob.dm @@ -289,7 +289,7 @@ //Record where they should go var/atom/reform_spot = blob.drop_location() - //Mail them to nullspace + //Move them back where the blob was forceMove(reform_spot) //Put our owner in it (don't transfer var/mind) diff --git a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm index af775ca519..53b84c33a7 100644 --- a/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm +++ b/code/modules/mob/living/carbon/human/species/station/protean_vr/protean_species.dm @@ -281,8 +281,12 @@ var/mob/living/carbon/human/H = holder for(var/organ in H.internal_organs) var/obj/item/organ/O = organ - if(O.damage > 0) // Fix internal damage + // Fix internal damage + if(O.damage > 0) O.damage = max(0,O.damage-0.1) + // If not damaged, but dead, fix it + else if(O.status & ORGAN_DEAD) + O.status &= ~ORGAN_DEAD //Unset dead if we repaired it entirely // PAN Card /obj/item/clothing/accessory/permit/nanotech diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index ae31a9cdba..4f85ee689d 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -816,10 +816,10 @@ Note that amputating the affected organ does in fact remove the infection from t if(!cannot_amputate) if(nonsolid && damage >= max_damage) - droplimb(true, DROPLIMB_BLUNT) + droplimb(TRUE, DROPLIMB_BLUNT) //VOREStation Add Start if(robotic >= ORGAN_NANOFORM && damage >= max_damage) - droplimb(true, DROPLIMB_BURN) + droplimb(TRUE, DROPLIMB_BURN) //VOREStation Add End /****************************************************