diff --git a/code/__DEFINES/flags.dm b/code/__DEFINES/flags.dm index 3db6e30fa52..b86d5e613b5 100644 --- a/code/__DEFINES/flags.dm +++ b/code/__DEFINES/flags.dm @@ -46,4 +46,19 @@ #define PASSTABLE 1 #define PASSGLASS 2 #define PASSGRILLE 4 -#define PASSBLOB 8 \ No newline at end of file +#define PASSBLOB 8 + +//flags for species + +#define MUTCOLORS 1 +#define HAIR 2 +#define FACEHAIR 4 +#define EYECOLOR 8 +#define LIPS 16 +#define COLDRES 32 +#define HEATRES 64 +#define RADIMMUNE 128 +#define NOBREATH 256 +#define NOGUNS 512 +#define NOBLOOD 1024 +#define NOFIRE 2048 \ No newline at end of file diff --git a/code/__HELPERS/global_lists.dm b/code/__HELPERS/global_lists.dm index 9ab0003633a..d7ecd960955 100644 --- a/code/__HELPERS/global_lists.dm +++ b/code/__HELPERS/global_lists.dm @@ -10,6 +10,15 @@ //underwear init_sprite_accessory_subtypes(/datum/sprite_accessory/underwear, underwear_all, underwear_m, underwear_f) + //Species + for(var/spath in typesof(/datum/species)) + if(spath == /datum/species) + continue + var/datum/species/S = new spath() + if(S.roundstart) + roundstart_species[S.name] = S.type + species_list[S.id] = S.type + //Surgeries for(var/path in typesof(/datum/surgery)) if(path == /datum/surgery) diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm index b382bb1e678..6b53dcaa221 100644 --- a/code/__HELPERS/mobs.dm +++ b/code/__HELPERS/mobs.dm @@ -56,10 +56,8 @@ var/list/skin_tones = list( "african2" ) -var/list/mutant_races = list( - "human", - "lizard", - ) +var/global/list/species_list[0] +var/global/list/roundstart_species[0] proc/age2agedescription(age) switch(age) diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index b029d0b7390..b9feeaa1124 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -86,6 +86,7 @@ var/shuttle_refuel_delay = 12000 var/show_game_type_odds = 0 //if set this allows players to see the odds of each roundtype on the get revision screen var/mutant_races = 0 //players can choose their mutant race before joining the game + var/mutant_colors = 0 var/alert_desc_green = "All threats to the station have passed. Security may not have weapons visible, privacy laws are once again fully enforced." var/alert_desc_blue_upto = "The station has received reliable information about possible hostile activity on the station. Security staff may have weapons visible, random searches are permitted." @@ -388,6 +389,8 @@ config.default_laws = text2num(value) if("join_with_mutant_race") config.mutant_races = 1 + if("mutant_colors") + config.mutant_colors = 1 else diary << "Unknown setting in configuration: '[name]'" diff --git a/code/datums/datumvars.dm b/code/datums/datumvars.dm index 4dba24ce357..7477a1bea43 100644 --- a/code/datums/datumvars.dm +++ b/code/datums/datumvars.dm @@ -249,12 +249,11 @@ client body += "" body += "" body += "" - body += "" body += "" body += "" if(ishuman(D)) body += "" - body += "" + body += "" body += "" body += "" body += "" @@ -581,17 +580,6 @@ client if(usr.client) usr.client.cmd_assume_direct_control(M) - else if(href_list["make_skeleton"]) - if(!check_rights(R_FUN)) return - - var/mob/living/carbon/human/H = locate(href_list["make_skeleton"]) - if(!istype(H)) - usr << "This can only be used on instances of type /mob/living/carbon/human" - return - - H.makeSkeleton() - href_list["datumrefresh"] = href_list["make_skeleton"] - else if(href_list["delall"]) if(!check_rights(R_DEBUG|R_SERVER)) return @@ -786,25 +774,24 @@ client return holder.Topic(href, list("makeai"=href_list["makeai"])) - else if(href_list["setmutantrace"]) + else if(href_list["setspecies"]) if(!check_rights(R_SPAWN)) return - var/mob/living/carbon/human/H = locate(href_list["setmutantrace"]) + var/mob/living/carbon/human/H = locate(href_list["setspecies"]) if(!istype(H)) usr << "This can only be done to instances of type /mob/living/carbon/human" return - var/new_mutantrace = input("Please choose a new mutantrace","Mutantrace",null) as null|anything in list("NONE","golem","lizard","slime","plant","shadow", "fly", "skeleton") - switch(new_mutantrace) - if(null) return - if("NONE") new_mutantrace = "" + var/result = input(usr, "Please choose a new species","Species") as null|anything in species_list + if(!H) usr << "Mob doesn't exist anymore" return - if(H.dna) - H.dna.mutantrace = new_mutantrace - H.update_body() - H.update_hair() + + if(result) + var/newtype = species_list[result] + H.dna.species = new newtype() + H.regenerate_icons() else if(href_list["adjustDamage"] && href_list["mobToDamage"]) if(!check_rights(0)) return diff --git a/code/datums/diseases/transformation.dm b/code/datums/diseases/transformation.dm index f1ee58534ec..8749abed60d 100644 --- a/code/datums/diseases/transformation.dm +++ b/code/datums/diseases/transformation.dm @@ -139,15 +139,14 @@ ..() switch(stage) if(1) - if(ishuman(affected_mob) && affected_mob.dna && affected_mob.dna.mutantrace == "slime") + if(ishuman(affected_mob) && affected_mob.dna && affected_mob.dna.species.id == "slime") stage = 5 if(3) if(ishuman(affected_mob)) var/mob/living/carbon/human/human = affected_mob - if(human.dna && !human.dna.mutantrace) - human.dna.mutantrace = "slime" - human.update_body() - + if(human.dna && human.dna.species.id != "slime") + human.dna.species = new /datum/species/slime() + human.update_icons() /datum/disease/transformation/corgi name = "The Barkening" diff --git a/code/game/atoms.dm b/code/game/atoms.dm index 718fe4db854..d8b0638b193 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -250,6 +250,10 @@ var/list/blood_splatter_icons = list() //returns 1 if made bloody, returns 0 otherwise /atom/proc/add_blood(mob/living/carbon/M) + if(ishuman(M) && M.dna) + var/mob/living/carbon/human/H = M + if(NOBLOOD in H.dna.species.specflags) + return 0 if(rejects_blood()) return 0 if(!istype(M)) @@ -270,7 +274,7 @@ var/list/blood_splatter_icons = list() //try to find a pre-processed blood-splatter. otherwise, make a new one var/index = blood_splatter_index() var/icon/blood_splatter_icon = blood_splatter_icons[index] - if(!blood_splatter_icon ) + if(!blood_splatter_icon) blood_splatter_icon = icon(initial(icon), initial(icon_state), , 1) //we only want to apply blood-splatters to the initial icon_state for each object blood_splatter_icon.Blend("#fff", ICON_ADD) //fills the icon_state with white (except where it's transparent) blood_splatter_icon.Blend(icon('icons/effects/blood.dmi', "itemblood"), ICON_MULTIPLY) //adds blood and the remaining white areas become transparant diff --git a/code/game/dna.dm b/code/game/dna.dm index d969e6bc1a1..35314862a41 100644 --- a/code/game/dna.dm +++ b/code/game/dna.dm @@ -20,7 +20,8 @@ var/struc_enzymes var/uni_identity var/blood_type - var/mutantrace = null //The type of mutant race the player is if applicable (i.e. potato-man) + var/datum/species/species = new /datum/species/human() //The type of mutant race the player is if applicable (i.e. potato-man) + var/mutant_color = "FFF" // What color you are if you have certain speciess var/real_name //Stores the real name of the person who originally got this dna datum. Used primarely for changelings, /datum/dna/proc/generate_uni_identity(mob/living/carbon/character) @@ -30,6 +31,8 @@ L[DNA_GENDER_BLOCK] = construct_block((character.gender!=MALE)+1, 2) if(istype(character, /mob/living/carbon/human)) var/mob/living/carbon/human/H = character + if(!H.dna.species) + H.dna.species = new /datum/species/human() L[DNA_HAIR_STYLE_BLOCK] = construct_block(hair_styles_list.Find(H.hair_style), hair_styles_list.len) L[DNA_HAIR_COLOR_BLOCK] = sanitize_hexcolor(H.hair_color) L[DNA_FACIAL_HAIR_STYLE_BLOCK] = construct_block(facial_hair_styles_list.Find(H.facial_hair_style), facial_hair_styles_list.len) @@ -62,11 +65,17 @@ . += repeat_string(DNA_UNIQUE_ENZYMES_LEN, "0") return . -/proc/hardset_dna(mob/living/carbon/owner, ui, se, real_name, mutantrace, blood_type) +/proc/hardset_dna(mob/living/carbon/owner, ui, se, real_name, blood_type, datum/species/mrace, mcolor) if(!istype(owner, /mob/living/carbon/monkey) && !istype(owner, /mob/living/carbon/human)) return if(!owner.dna) - create_dna(owner) + create_dna(owner, mrace) + + if(mrace) + owner.dna.species = new mrace() + + if(mcolor) + owner.dna.mutant_color = mcolor if(real_name) owner.real_name = real_name @@ -79,18 +88,13 @@ owner.dna.uni_identity = ui updateappearance(owner) - var/update_mutantrace = (mutantrace != owner.dna.mutantrace) - owner.dna.mutantrace = mutantrace - if(update_mutantrace && istype(owner, /mob/living/carbon/human)) - var/mob/living/carbon/human/H = owner - H.update_body() - H.update_hair() - if(se) owner.dna.struc_enzymes = se domutcheck(owner) check_dna_integrity(owner) + + owner.regenerate_icons() return /proc/check_dna_integrity(mob/living/carbon/character) @@ -122,8 +126,9 @@ character.dna.unique_enzymes = character.dna.generate_unique_enzymes(character) return character.dna -/proc/create_dna(mob/living/carbon/C) //don't use this unless you're about to use hardset_dna or ready_dna +/proc/create_dna(mob/living/carbon/C, datum/species/S) //don't use this unless you're about to use hardset_dna or ready_dna C.dna = new /datum/dna() + if(S) C.dna.species = new S() // do not remove; this is here to prevent runtimes /////////////////////////// DNA DATUM @@ -981,7 +986,7 @@ proc/deconstruct_block(value, values, blocksize=DNA_BLOCK_SIZE) /datum/dna/proc/is_same_as(var/datum/dna/D) if(uni_identity == D.uni_identity && struc_enzymes == D.struc_enzymes && real_name == D.real_name) - if(mutantrace == D.mutantrace && blood_type == D.blood_type) + if(species == D.species && mutant_color == D.mutant_color && blood_type == D.blood_type) return 1 return 0 diff --git a/code/game/gamemodes/changeling/powers/absorb.dm b/code/game/gamemodes/changeling/powers/absorb.dm index 946ca14f8c4..bd1b910f90e 100644 --- a/code/game/gamemodes/changeling/powers/absorb.dm +++ b/code/game/gamemodes/changeling/powers/absorb.dm @@ -90,7 +90,8 @@ new_dna.uni_identity = T.dna.uni_identity new_dna.struc_enzymes = T.dna.struc_enzymes new_dna.real_name = T.dna.real_name - new_dna.mutantrace = T.dna.mutantrace + new_dna.species = T.dna.species + new_dna.mutant_color = T.dna.mutant_color new_dna.blood_type = T.dna.blood_type absorbed_dna |= new_dna //And add the target DNA to our absorbed list. absorbedcount++ //all that done, let's increment the objective counter. \ No newline at end of file diff --git a/code/game/gamemodes/changeling/powers/tiny_prick.dm b/code/game/gamemodes/changeling/powers/tiny_prick.dm index 17aae0b9b84..da147e0d128 100644 --- a/code/game/gamemodes/changeling/powers/tiny_prick.dm +++ b/code/game/gamemodes/changeling/powers/tiny_prick.dm @@ -88,7 +88,7 @@ var/datum/dna/NewDNA = selected_dna if(ismonkey(target)) user << "We stealthily sting [target.name]." - hardset_dna(target, NewDNA.uni_identity, NewDNA.struc_enzymes, NewDNA.real_name, NewDNA.mutantrace, NewDNA.blood_type) + hardset_dna(target, NewDNA.uni_identity, NewDNA.struc_enzymes, NewDNA.real_name, NewDNA.blood_type, NewDNA.species, NewDNA.mutant_color) updateappearance(target) feedback_add_details("changeling_powers","TS") return 1 diff --git a/code/game/gamemodes/changeling/powers/transform.dm b/code/game/gamemodes/changeling/powers/transform.dm index 612d7871abc..b6954ccf79c 100644 --- a/code/game/gamemodes/changeling/powers/transform.dm +++ b/code/game/gamemodes/changeling/powers/transform.dm @@ -8,16 +8,17 @@ max_genetic_damage = 3 //Change our DNA to that of somebody we've absorbed. -/obj/effect/proc_holder/changeling/transform/sting_action(var/mob/living/carbon/user) +/obj/effect/proc_holder/changeling/transform/sting_action(var/mob/living/carbon/human/user) var/datum/changeling/changeling = user.mind.changeling var/datum/dna/chosen_dna = changeling.select_dna("Select the target DNA: ", "Target DNA") if(!chosen_dna) return - user.dna = chosen_dna user.real_name = chosen_dna.real_name + user.dna.species = new chosen_dna.species.type(user) + user.dna.mutant_color = chosen_dna.mutant_color updateappearance(user) domutcheck(user, null) diff --git a/code/game/jobs/job/job.dm b/code/game/jobs/job/job.dm index 460b9fbeaa5..93c43bd653d 100644 --- a/code/game/jobs/job/job.dm +++ b/code/game/jobs/job/job.dm @@ -74,8 +74,14 @@ equip_backpack(H) //Equip the rest of the gear + if(H.dna) + H.dna.species.before_equip_job(src, H) + equip_items(H) + if(H.dna) + H.dna.species.after_equip_job(src, H) + //Equip ID var/obj/item/weapon/card/id/C = new default_id(H) C.access = get_access() diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index 85ace07a430..c02e6c596f2 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -698,9 +698,6 @@ Auto Patrol: []"}, if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) threatcount += 2 - if(perp.dna && perp.dna.mutantrace && perp.dna.mutantrace != "none") - threatcount += 2 - //Agent cards lower threatlevel when normal idchecking is off. if((perp.wear_id && istype(perp:wear_id.GetID(), /obj/item/weapon/card/id/syndicate)) && src.idcheck) threatcount -= 2 diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index e14f870ce67..7d1b29986d9 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -630,9 +630,6 @@ Auto Patrol: []"}, if(istype(humanperp.head, /obj/item/clothing/head/wizard) || istype(humanperp.head, /obj/item/clothing/head/helmet/space/rig/wizard)) threatcount += 2 - if(humanperp.dna && humanperp.dna.mutantrace && humanperp.dna.mutantrace != "none") - threatcount += 2 - //Agent cards lower threatlevel. if(humanperp.wear_id && istype(humanperp.wear_id.GetID(), /obj/item/weapon/card/id/syndicate)) threatcount -= 2 @@ -789,4 +786,4 @@ Auto Patrol: []"}, overlays -= "hs_arm" new /obj/item/robot_parts/l_arm(get_turf(src)) user << "You remove the robot arm from [src]." - build_step-- \ No newline at end of file + build_step-- diff --git a/code/game/machinery/cloning.dm b/code/game/machinery/cloning.dm index 16437db55e0..ec9da16ba30 100644 --- a/code/game/machinery/cloning.dm +++ b/code/game/machinery/cloning.dm @@ -123,7 +123,7 @@ //Clonepod //Start growing a human clone in the pod! -/obj/machinery/clonepod/proc/growclone(var/ckey, var/clonename, var/ui, var/se, var/mindref, var/mrace) +/obj/machinery/clonepod/proc/growclone(var/ckey, var/clonename, var/ui, var/se, var/mindref, var/datum/species/mrace, var/mcolor) if(panel_open) return 0 if(mess || attempting) @@ -188,7 +188,8 @@ // -- End mode specific stuff - hardset_dna(H, ui, se, null, mrace) + hardset_dna(H, ui, se, null, null, mrace, mcolor) + if(efficiency > 2) for(var/A in bad_se_blocks) setblock(H.dna.struc_enzymes, A, construct_block(0,2)) @@ -203,6 +204,8 @@ H.facial_hair_style = "Shaved" H.hair_style = pick("Bedhead", "Bedhead 2", "Bedhead 3") + H.regenerate_icons() + H.suiciding = 0 src.attempting = 0 return 1 diff --git a/code/game/machinery/computer/cloning.dm b/code/game/machinery/computer/cloning.dm index 47f4870e719..f4e41af1fd2 100644 --- a/code/game/machinery/computer/cloning.dm +++ b/code/game/machinery/computer/cloning.dm @@ -34,7 +34,7 @@ if(!(pod1.occupant || pod1.mess) && (pod1.efficiency > 5)) for(var/datum/data/record/R in records) if(!(pod1.occupant || pod1.mess)) - if(pod1.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"])) + if(pod1.growclone(R.fields["ckey"], R.fields["name"], R.fields["UI"], R.fields["SE"], R.fields["mind"], R.fields["mrace"], R.fields["mcolor"])) records -= R /obj/machinery/computer/cloning/proc/updatemodules() @@ -328,7 +328,7 @@ temp = "Clonepod malfunction." else if(!config.revival_cloning) temp = "Unable to initiate cloning cycle." - else if(pod1.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"])) + else if(pod1.growclone(C.fields["ckey"], C.fields["name"], C.fields["UI"], C.fields["SE"], C.fields["mind"], C.fields["mrace"], C.fields["mcolor"])) temp = "[C.fields["name"]] => Cloning cycle in progress..." records.Remove(C) if(active_record == C) @@ -368,10 +368,10 @@ return var/datum/data/record/R = new() - if(subject.dna) - R.fields["mrace"] = subject.dna.mutantrace + if(subject.dna.species) + R.fields["mrace"] = subject.dna.species.type else - R.fields["mrace"] = null + R.fields["mrace"] = /datum/species/human R.fields["ckey"] = subject.ckey R.fields["name"] = subject.real_name R.fields["id"] = copytext(md5(subject.real_name), 2, 6) @@ -379,6 +379,7 @@ R.fields["UI"] = subject.dna.uni_identity R.fields["SE"] = subject.dna.struc_enzymes R.fields["blood_type"] = subject.dna.blood_type + R.fields["mcolor"] = subject.dna.mutant_color //Add an implant if needed var/obj/item/weapon/implant/health/imp = locate(/obj/item/weapon/implant/health, subject) diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm index b2386d2b4f4..df07d30f159 100644 --- a/code/game/machinery/teleporter.dm +++ b/code/game/machinery/teleporter.dm @@ -247,11 +247,10 @@ if(prob(30 - (accurate * 10))) //oh dear a problem if(ishuman(M))//don't remove people from the round randomly you jerks var/mob/living/carbon/human/human = M - if(human.dna && !human.dna.mutantrace) + if(human.dna && human.dna.species.id == "human") M << "You hear a buzzing in your ears." - human.dna.mutantrace = "fly" - human.update_body() - human.update_hair() + human.dna.species = new /datum/species/fly() + human.regenerate_icons() human.apply_effect((rand(120 - accurate * 40, 180 - accurate * 60)), IRRADIATE, 0) return diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm index ff296e1dfec..cbf87e7b1c2 100644 --- a/code/game/objects/items.dm +++ b/code/game/objects/items.dm @@ -32,6 +32,8 @@ var/obj/item/device/uplink/hidden/hidden_uplink = null // All items can have an uplink hidden inside, just remember to add the triggers. var/reflect_chance = 0 //This var dictates what % of a time an object will reflect an energy based weapon's shot + var/list/species_exception = list() // even if a species cannot put items in a certain slot, if the species id is in the item's exception list, it will be able to wear that item + /obj/item/device icon = 'icons/obj/device.dmi' diff --git a/code/game/objects/items/devices/laserpointer.dm b/code/game/objects/items/devices/laserpointer.dm index 3910f798064..4e0804e76cf 100644 --- a/code/game/objects/items/devices/laserpointer.dm +++ b/code/game/objects/items/devices/laserpointer.dm @@ -81,10 +81,10 @@ if(user.has_mutation(HULK)) user << "Your meaty finger is too large for the button!" return - if(iscarbon(user)) - var/mob/living/carbon/C = user - if(C.is_mutantrace("adamantine")) - user << "Your metal fingers can't press the button!" + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(H.dna && NOGUNS in H.dna.species.specflags) + user << "Your fingers can't press the button!" return add_fingerprint(user) diff --git a/code/game/objects/items/devices/scanners.dm b/code/game/objects/items/devices/scanners.dm index d96eee665fe..8d25a62be7e 100644 --- a/code/game/objects/items/devices/scanners.dm +++ b/code/game/objects/items/devices/scanners.dm @@ -108,9 +108,14 @@ MASS SPECTROMETER mob_status = "Deceased" oxy_loss = max(rand(1, 40), oxy_loss, (300 - (tox_loss + fire_loss + brute_loss))) // Random oxygen loss - user.show_message("Analyzing Results for [M]:\n\t Overall Status: [mob_status]", 1) + user.show_message(text("Analyzing Results for []:\n\t Overall Status: []", M, mob_status), 1) + if(ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.dna)// Show target's species, if they have one + user.show_message("Species: [H.dna.species.name]", 1) + else // Otherwise we can assume that they are a regular human + user.show_message("Species: Human", 1) user.show_message("\t Damage Specifics: [oxy_loss]-[tox_loss]-[fire_loss]-[brute_loss]", 1) - user.show_message("Key: Suffocation/Toxin/Burn/Brute", 1) user.show_message("Body Temperature: [M.bodytemperature-T0C]°C ([M.bodytemperature*1.8-459.67]°F)", 1) diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm index 9a2d6c758b4..1f0a894cd5a 100644 --- a/code/modules/awaymissions/mission_code/wildwest.dm +++ b/code/modules/awaymissions/mission_code/wildwest.dm @@ -89,20 +89,20 @@ user.see_in_dark = 8 user.see_invisible = SEE_INVISIBLE_LEVEL_TWO user << "\blue The walls suddenly disappear." - user.dna.mutantrace = "shadow" - user.update_body() + user.dna.species = new /datum/species/shadow() + user.regenerate_icons() if("Wealth") user << "Your wish is granted, but at a terrible cost..." user << "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart." new /obj/structure/closet/syndicate/resources/everything(loc) - user.dna.mutantrace = "shadow" - user.update_body() + user.dna.species = new /datum/species/shadow() + user.regenerate_icons() if("Immortality") user << "Your wish is granted, but at a terrible cost..." user << "The Wish Granter punishes you for your selfishness, claiming your soul and warping your body to match the darkness in your heart." user.verbs += /mob/living/carbon/proc/immortality - user.dna.mutantrace = "shadow" - user.update_body() + user.dna.species = new /datum/species/shadow() + user.regenerate_icons() if("To Kill") user << "Your wish is granted, but at a terrible cost..." user << "The Wish Granter punishes you for your wickedness, claiming your soul and warping your body to match the darkness in your heart." @@ -116,8 +116,8 @@ for(var/datum/objective/OBJ in user.mind.objectives) user << "Objective #[obj_count]: [OBJ.explanation_text]" obj_count++ - user.dna.mutantrace = "shadow" - user.update_body() + user.dna.species = new /datum/species/shadow() + user.regenerate_icons() if("Peace") user << "Whatever alien sentience that the Wish Granter possesses is satisfied with your wish. There is a distant wailing as the last of the Faithless begin to die, then silence." user << "You feel as if you just narrowly avoided a terrible fate..." diff --git a/code/modules/client/preferences.dm b/code/modules/client/preferences.dm index 7eaacc46d10..f3085e14e0f 100644 --- a/code/modules/client/preferences.dm +++ b/code/modules/client/preferences.dm @@ -53,7 +53,8 @@ datum/preferences var/facial_hair_color = "000" //Facial hair color var/skin_tone = "caucasian1" //Skin color var/eye_color = "000" //Eye color - var/mutant_race = "human" //Mutant race + var/datum/species/pref_species = new /datum/species/human() //Mutant race + var/mutant_color = "FFF" //Mutant race skin color //Mob preview var/icon/preview_icon_front = null @@ -165,16 +166,17 @@ datum/preferences dat += "
" if(config.mutant_races) - dat += "Mutant Race:
[mutant_race]
" + dat += "Species:
[pref_species.name]
" else - dat += "Mutant Race: Human
" + dat += "Species: Human
" + dat += "Blood Type: [blood_type]
" dat += "Skin Tone:
[skin_tone]
" dat += "Underwear:
[underwear]
" dat += "Backpack:
[backbaglist[backbag]]
" - dat += "
" + dat += "" dat += "

Hair Style

" @@ -182,7 +184,7 @@ datum/preferences dat += "    Change
" - dat += "
" + dat += "" dat += "

Facial Hair Style

" @@ -190,12 +192,17 @@ datum/preferences dat += "    Change
" - dat += "
" + dat += "" dat += "

Eye Color

" dat += "    Change
" + dat += "
" + + dat += "

Alien Color

" // even if choosing your mutantrace is off, this is here in case you gain one during a round + + dat += "    Change
" dat += "
" @@ -618,10 +625,27 @@ datum/preferences if(new_eyes) eye_color = sanitize_hexcolor(new_eyes) - if("mutant_race") - var/new_mutant_race = input(user, "Choose your character's mutant race:", "Character Preference") as null|anything in mutant_races - if(new_mutant_race) - mutant_race = new_mutant_race + if("species") + + var/result = input(user, "Select a species", "Species Selection") as null|anything in roundstart_species + + if(result) + var/newtype = roundstart_species[result] + pref_species = new newtype() + if(!config.mutant_colors) + mutant_color = pref_species.default_color + + if("mutant_color") + if(!config.mutant_colors) + user << "Alien colors are disabled." + return + var/new_mutantcolor = input(user, "Choose your character's alien skin color:", "Character Preference") as color|null + if(new_mutantcolor) + var/temp_hsv = RGBtoHSV(new_mutantcolor) + if(ReadHSV(temp_hsv)[3] >= ReadHSV("#7F7F7F")[3]) // mutantcolors must be bright + mutant_color = sanitize_hexcolor(new_mutantcolor) + else + user << "Invalid color. Your color is not bright enough." if("s_tone") var/new_s_tone = input(user, "Choose your character's skin-tone:", "Character Preference") as null|anything in skin_tones @@ -685,8 +709,10 @@ datum/preferences if("ghost_sight") toggles ^= CHAT_GHOSTSIGHT + if("pull_requests") toggles ^= CHAT_PULLR + if("save") save_preferences() save_character() @@ -722,10 +748,15 @@ datum/preferences character.real_name = real_name character.name = character.real_name + if(character.dna) character.dna.real_name = character.real_name - if(mutant_race != "human" && config.mutant_races) - character.dna.mutantrace = mutant_race + if(pref_species != /datum/species/human && config.mutant_races) + character.dna.species = new pref_species.type() + else + character.dna.species = new /datum/species/human() + character.dna.mutant_color = mutant_color + character.update_mutcolor() character.gender = gender character.age = age diff --git a/code/modules/client/preferences_savefile.dm b/code/modules/client/preferences_savefile.dm index 1204bf5edee..33768cb04c7 100644 --- a/code/modules/client/preferences_savefile.dm +++ b/code/modules/client/preferences_savefile.dm @@ -73,6 +73,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(11) underwear = "Ladies Kinky" if(12) underwear = "Tankini" if(13) underwear = "Nude" + if(!(pref_species in species_list)) + pref_species = new /datum/species/human() return /datum/preferences/proc/load_path(ckey,filename="preferences.sav") @@ -151,6 +153,9 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car if(needs_update == -2) //fatal, can't load any data return 0 + if(!S["species"] || !config.mutant_races) + S["species"] << new /datum/species/human() + //Character S["OOC_Notes"] >> metadata S["real_name"] >> real_name @@ -165,7 +170,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["facial_style_name"] >> facial_hair_style S["underwear"] >> underwear S["backbag"] >> backbag - S["mutant_race"] >> mutant_race + S["species"] >> pref_species + S["mutant_color"] >> mutant_color //Jobs S["userandomjob"] >> userandomjob @@ -186,6 +192,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car //Sanitize metadata = sanitize_text(metadata, initial(metadata)) real_name = reject_bad_name(real_name) + if(!(pref_species in species_list)) + pref_species = new /datum/species/human() if(!real_name) real_name = random_name(gender) be_random_name = sanitize_integer(be_random_name, 0, 1, initial(be_random_name)) gender = sanitize_gender(gender) @@ -203,7 +211,7 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car eye_color = sanitize_hexcolor(eye_color, 3, 0) skin_tone = sanitize_inlist(skin_tone, skin_tones) backbag = sanitize_integer(backbag, 1, backbaglist.len, initial(backbag)) - mutant_race = sanitize_text(mutant_race, initial(mutant_race)) + mutant_color = sanitize_hexcolor(mutant_color, 3, 0) userandomjob = sanitize_integer(userandomjob, 0, 1, initial(userandomjob)) job_civilian_high = sanitize_integer(job_civilian_high, 0, 65535, initial(job_civilian_high)) @@ -240,7 +248,8 @@ SAVEFILE UPDATING/VERSIONING - 'Simplified', or rather, more coder-friendly ~Car S["facial_style_name"] << facial_hair_style S["underwear"] << underwear S["backbag"] << backbag - S["mutant_race"] << mutant_race + S["species"] << pref_species + S["mutant_color"] << mutant_color //Jobs S["userandomjob"] << userandomjob diff --git a/code/modules/clothing/gloves/boxing.dm b/code/modules/clothing/gloves/boxing.dm index 0865c31e942..9467c63e34e 100644 --- a/code/modules/clothing/gloves/boxing.dm +++ b/code/modules/clothing/gloves/boxing.dm @@ -3,6 +3,7 @@ desc = "Because you really needed another excuse to punch your crewmates." icon_state = "boxing" item_state = "boxing" + species_exception = list(/datum/species/golem, /datum/species/golem/adamantine) // now you too can be a golem boxing champion /obj/item/clothing/gloves/boxing/green icon_state = "boxinggreen" diff --git a/code/modules/events/holiday/halloween.dm b/code/modules/events/holiday/halloween.dm index 67536e1dec7..9a7945d7719 100644 --- a/code/modules/events/holiday/halloween.dm +++ b/code/modules/events/holiday/halloween.dm @@ -9,9 +9,9 @@ /datum/round_event/spooky/start() for(var/mob/living/carbon/human/H in mob_list) if(H.dna) - hardset_dna(H, null, null, null, "skeleton") + hardset_dna(H, null, null, null, null, /datum/species/skeleton) for(var/mob/living/simple_animal/corgi/Ian/Ian in mob_list) Ian.place_on_head(new /obj/item/weapon/bedsheet(Ian)) /datum/round_event/spooky/announce() - priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE") \ No newline at end of file + priority_announce(pick("RATTLE ME BONES!","THE RIDE NEVER ENDS!", "A SKELETON POPS OUT!", "SPOOKY SCARY SKELETONS!", "CREWMEMBERS BEWARE, YOU'RE IN FOR A SCARE!") , "THE CALL IS COMING FROM INSIDE THE HOUSE") diff --git a/code/modules/hydroponics/hydroponics.dm b/code/modules/hydroponics/hydroponics.dm index 80642e3957b..10f10353b41 100644 --- a/code/modules/hydroponics/hydroponics.dm +++ b/code/modules/hydroponics/hydroponics.dm @@ -909,7 +909,7 @@ obj/machinery/hydroponics/attackby(var/obj/item/O as obj, var/mob/user as mob) podman.gender = ghost.gender //dna stuff - hardset_dna(podman, ui, se, null, !prob(potency) ? "plant" : null) //makes sure podman has dna and sets the dna's ui/se/mutantrace/real_name etc variables + hardset_dna(podman, ui, se, null, null, null, !prob(potency) ? /datum/species/plant/pod : null, "#59CE00") //makes sure podman has dna and sets the dna's ui/se/mutantrace/real_name etc variables else //else, one packet of seeds. maybe two var/seed_count = 1 diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 5745bcea0e5..498fd4a2c5c 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -205,7 +205,7 @@ src << "You're completely exhausted." else src << "You feel fatigued." - if(dna && (dna.mutantrace == "skeleton") && !H.w_uniform && !H.wear_suit) + if(dna && (dna.species == /datum/species/skeleton) && !H.w_uniform && !H.wear_suit) H.play_xylophone() else if(ishuman(src)) @@ -458,13 +458,6 @@ ..(message, bubble_type) -/mob/living/carbon/proc/is_mutantrace(var/mrace) - if(mrace) - if(src.dna && src.dna.mutantrace == mrace) - return 1 - else - return src.dna && src.dna.mutantrace ? 1 : 0 - /mob/living/carbon/getTrail() if(getBruteLoss() < 300) if(prob(50)) diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm index 473707ff132..045ca305efa 100644 --- a/code/modules/mob/living/carbon/human/death.dm +++ b/code/modules/mob/living/carbon/human/death.dm @@ -8,7 +8,10 @@ ..() /mob/living/carbon/human/spawn_gibs() - hgibs(loc, viruses, dna) + if(dna) + hgibs(loc, viruses, dna) + else + hgibs(loc, viruses, null) /mob/living/carbon/human/spawn_dust() new /obj/effect/decal/remains/human(loc) @@ -31,6 +34,9 @@ update_canmove() if(client) blind.layer = 0 + if(dna) + dna.species.spec_death(gibbed,src) + tod = worldtime2text() //weasellos time of death patch if(mind) mind.store_memory("Time of death: [tod]", 0) if(ticker && ticker.mode) @@ -40,11 +46,9 @@ return ..(gibbed) /mob/living/carbon/human/proc/makeSkeleton() - if(!check_dna_integrity(src) || (dna.mutantrace == "skeleton")) return - dna.mutantrace = "skeleton" + if(!check_dna_integrity(src)) return status_flags |= DISFIGURED - update_hair() - update_body() + dna.species = new /datum/species/skeleton(src) return 1 /mob/living/carbon/proc/ChangeToHusk() diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 765d8689bfd..773c4bf2b23 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -341,7 +341,7 @@ dat += "
Uniform: Obscured by [wear_suit]" else dat += "
Uniform: [(w_uniform && !(w_uniform.flags&ABSTRACT)) ? w_uniform : "Empty"]" - if(w_uniform) + if(w_uniform || dna.species.nojumpsuit) dat += "
[TAB]↳Belt: [(belt && !(belt.flags&ABSTRACT)) ? belt : "Empty"]" if(has_breathable_mask && istype(belt, /obj/item/weapon/tank)) dat += "
[TAB][TAB]↳[internal ? "Disable Internals" : "Set Internals"]" @@ -349,12 +349,31 @@ dat += " [(r_store && !(r_store.flags&ABSTRACT)) ? "Right (Full)" : "Right (Empty)"]" dat += "
[TAB]↳ID: [(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Empty"]" + if(dna) + if(dna.species.nojumpsuit) + dat += "
ID: [(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Nothing"]" + else if(w_uniform) + dat += "
ID: [(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Nothing"]" + else if(w_uniform) + dat += "
ID: [(wear_id && !(wear_id.flags&ABSTRACT)) ? wear_id : "Nothing"]" + dat += "
" if(handcuffed) dat += "
Handcuffed: Remove" if(legcuffed) - dat += "
Legcuffed: Remove" + dat += "
Legcuffed" + + if(dna) + if(dna.species.nojumpsuit) + dat += "

Left Pocket ([(l_store && !(l_store.flags&ABSTRACT)) ? "Full" : "Empty"])" + dat += " - Right Pocket ([(r_store && !(r_store.flags&ABSTRACT)) ? "Full" : "Empty"])" + else if(w_uniform) + dat += "

Left Pocket ([(l_store && !(l_store.flags&ABSTRACT)) ? "Full" : "Empty"])" + dat += " - Right Pocket ([(r_store && !(r_store.flags&ABSTRACT)) ? "Full" : "Empty"])" + else if(w_uniform) + dat += "

Left Pocket ([(l_store && !(l_store.flags&ABSTRACT)) ? "Full" : "Empty"])" + dat += " - Right Pocket ([(r_store && !(r_store.flags&ABSTRACT)) ? "Full" : "Empty"])" dat += {"
diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index 0d1ef146670..d703a7ab610 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -2,163 +2,9 @@ if(..()) //to allow surgery to return properly. return - if((M != src) && check_shields(0, M.name)) - add_logs(M, src, "attempted to touch") - visible_message("[M] attempted to touch [src]!") - return 0 + if(dna) + dna.species.spec_attack_hand(M, src) - switch(M.a_intent) - if("help") - if(health >= 0) - help_shake_act(M) - if(src != M) - add_logs(M, src, "shaked") - return 1 - - //CPR - if((M.head && (M.head.flags & HEADCOVERSMOUTH)) || (M.wear_mask && (M.wear_mask.flags & MASKCOVERSMOUTH))) - M << "Remove your mask!" - return 0 - if((head && (head.flags & HEADCOVERSMOUTH)) || (wear_mask && (wear_mask.flags & MASKCOVERSMOUTH))) - M << "Remove their mask!" - return 0 - - if(cpr_time < world.time + 30) - add_logs(src, M, "CPRed") - visible_message("[M] is trying to perform CPR on [src]!") - if(!do_mob(M, src)) - return 0 - if((health >= -99 && health <= 0)) - cpr_time = world.time - var/suff = min(getOxyLoss(), 7) - adjustOxyLoss(-suff) - updatehealth() - M.visible_message("[M] performs CPR on [src]!") - src << "You feel a breath of fresh air enter your lungs. It feels good." - - if("grab") - if(M == src || anchored) - return 0 - - add_logs(M, src, "grabbed", addition="passively") - - if(w_uniform) - w_uniform.add_fingerprint(M) - - var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, src) - if(buckled) - M << "You cannot grab [src], \he is buckled in!" - if(!G) //the grab will delete itself in New if affecting is anchored - return - M.put_in_active_hand(G) - G.synch() - LAssailant = M - - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - visible_message("[M] has grabbed [src] passively!") - return 1 - - if("harm") - add_logs(M, src, "punched") - - var/attack_verb = "punch" - if(lying) - attack_verb = "kick" - else if(M.dna) - switch(M.dna.mutantrace) - if("lizard") - attack_verb = "scratch" - if("plant") - attack_verb = "slash" - - var/damage = rand(0, 9) - if(!damage) - switch(attack_verb) - if("slash") - playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1) - else - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - - visible_message("[M] has attempted to [attack_verb] [src]!") - return 0 - - - var/obj/item/organ/limb/affecting = get_organ(ran_zone(M.zone_sel.selecting)) - var/armor_block = run_armor_check(affecting, "melee") - - if(HULK in M.mutations) - damage += 5 - - switch(attack_verb) - if("slash") - playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) - else - playsound(loc, "punch", 25, 1, -1) - - visible_message("[M] has [attack_verb]ed [src]!", \ - "[M] has [attack_verb]ed [src]!") - - apply_damage(damage, BRUTE, affecting, armor_block) - if((stat != DEAD) && damage >= 9) - visible_message("[M] has weakened [src]!", \ - "[M] has weakened [src]!") - apply_effect(4, WEAKEN, armor_block) - forcesay(hit_appends) - else if(lying) - forcesay(hit_appends) - - if("disarm") - add_logs(M, src, "disarmed") - - if(w_uniform) - w_uniform.add_fingerprint(M) - var/obj/item/organ/limb/affecting = get_organ(ran_zone(M.zone_sel.selecting)) - var/randn = rand(1, 100) - if(randn <= 25) - apply_effect(2, WEAKEN, run_armor_check(affecting, "melee")) - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - visible_message("[M] has pushed [src]!", - "[M] has pushed [src]!") - forcesay(hit_appends) - return - - var/talked = 0 // BubbleWrap - - if(randn <= 60) - //BubbleWrap: Disarming breaks a pull - if(pulling) - visible_message("[M] has broken [src]'s grip on [pulling]!") - talked = 1 - stop_pulling() - - //BubbleWrap: Disarming also breaks a grab - this will also stop someone being choked, won't it? - if(istype(l_hand, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/lgrab = l_hand - if(lgrab.affecting) - visible_message("[M] has broken [src]'s grip on [lgrab.affecting]!") - talked = 1 - spawn(1) - qdel(lgrab) - if(istype(r_hand, /obj/item/weapon/grab)) - var/obj/item/weapon/grab/rgrab = r_hand - if(rgrab.affecting) - visible_message("[M] has broken [src]'s grip on [rgrab.affecting]!") - talked = 1 - spawn(1) - qdel(rgrab) - //End BubbleWrap - - if(!talked) //BubbleWrap - if(drop_item()) - visible_message("[M] has disarmed [src]!", \ - "[M] has disarmed [src]!") - playsound(loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) - return - - - playsound(loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) - visible_message("[M] attempted to disarm [src]!", \ - "[M] attemped to disarm [src]!") return /mob/living/carbon/human/proc/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, inrange, params) diff --git a/code/modules/mob/living/carbon/human/human_damage.dm b/code/modules/mob/living/carbon/human/human_damage.dm index c1c1e7240f1..633e72059db 100644 --- a/code/modules/mob/living/carbon/human/human_damage.dm +++ b/code/modules/mob/living/carbon/human/human_damage.dm @@ -102,6 +102,7 @@ mob/living/carbon/human/proc/hat_fall_prob() var/obj/item/organ/limb/picked = pick(parts) if(picked.take_damage(brute,burn)) update_damage_overlays(0) + updatehealth() @@ -128,6 +129,7 @@ mob/living/carbon/human/proc/hat_fall_prob() // damage MANY external organs, in random order /mob/living/carbon/human/take_overall_damage(var/brute, var/burn) if(status_flags & GODMODE) return //godmode + var/list/obj/item/organ/limb/parts = get_damageable_organs() var/update = 0 while(parts.len && (brute>0 || burn>0) ) @@ -143,7 +145,9 @@ mob/living/carbon/human/proc/hat_fall_prob() burn -= (picked.burn_dam - burn_was) parts -= picked + updatehealth() + if(update) update_damage_overlays(0) @@ -159,34 +163,38 @@ mob/living/carbon/human/proc/hat_fall_prob() /mob/living/carbon/human/apply_damage(var/damage = 0,var/damagetype = BRUTE, var/def_zone = null, var/blocked = 0) - if((damagetype != BRUTE) && (damagetype != BURN)) - ..(damage, damagetype, def_zone, blocked) - return 1 - - blocked = (100-blocked)/100 - if(blocked <= 0) return 0 - - var/obj/item/organ/limb/organ = null - if(isorgan(def_zone)) - organ = def_zone + if(dna) // if you have a species, it will run the apply_damage code there instead + dna.species.apply_damage(damage, damagetype, def_zone, blocked, src) else - if(!def_zone) def_zone = ran_zone(def_zone) - organ = get_organ(check_zone(def_zone)) - if(!organ) return 0 + if((damagetype != BRUTE) && (damagetype != BURN)) + ..(damage, damagetype, def_zone, blocked) + return 1 - damage = (damage * blocked) + else + blocked = (100-blocked)/100 + if(blocked <= 0) return 0 - switch(damagetype) - if(BRUTE) - damageoverlaytemp = 20 - if(organ.take_damage(damage, 0)) - update_damage_overlays(0) - if(BURN) - damageoverlaytemp = 20 - if(organ.take_damage(0, damage)) - update_damage_overlays(0) + var/obj/item/organ/limb/organ = null + if(isorgan(def_zone)) + organ = def_zone + else + if(!def_zone) def_zone = ran_zone(def_zone) + organ = get_organ(check_zone(def_zone)) + if(!organ) return 0 - // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). + damage = (damage * blocked) - updatehealth() + switch(damagetype) + if(BRUTE) + damageoverlaytemp = 20 + if(organ.take_damage(damage, 0)) + update_damage_overlays(0) + if(BURN) + damageoverlaytemp = 20 + if(organ.take_damage(0, damage)) + update_damage_overlays(0) + + // Will set our damageoverlay icon to the next level, which will then be set back to the normal level the next mob.Life(). + + updatehealth() return 1 diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index 76ef3e5583d..4048c7e762c 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -36,6 +36,11 @@ emp_act protection += C.armor[type] return protection +/mob/living/carbon/human/on_hit(proj_type) + if(dna) + dna.species.on_hit(proj_type, src) + return + /mob/living/carbon/human/bullet_act(var/obj/item/projectile/P, var/def_zone) if(istype(P, /obj/item/projectile/energy) || istype(P, /obj/item/projectile/beam)) if(check_reflect(def_zone)) // Checks if you've passed a reflection% check @@ -118,94 +123,93 @@ emp_act /mob/living/carbon/human/attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone) if(!I || !user) return 0 + var/obj/item/organ/limb/target_limb = get_organ(user.zone_sel.selecting) var/obj/item/organ/limb/affecting = get_organ(ran_zone(user.zone_sel.selecting)) - var/hit_area = parse_zone(affecting.name) + var/target_area = parse_zone(target_limb.name) - if((user != src) && check_shields(I.force, "the [I.name]")) - return 0 + if(dna) // allows your species to affect the attacked_by code + return dna.species.spec_attacked_by(I,user,def_zone,affecting,hit_area,src.a_intent,target_limb,target_area,src) - if(I.attack_verb && I.attack_verb.len) - visible_message("[src] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!", \ - "[src] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!") - else if(I.force) - visible_message("[src] has been attacked in the [hit_area] with [I] by [user]!", \ - "[src] has been attacked in the [hit_area] with [I] by [user]!") else - return 0 + if((user != src) && check_shields(I.force, "the [I.name]")) + return 0 - var/armor = run_armor_check(affecting, "melee", "Your armour has protected your [hit_area].", "Your armour has softened a hit to your [hit_area].") - if(armor >= 100) return 0 - var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + if(I.attack_verb && I.attack_verb.len) + visible_message("[src] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!", \ + "[src] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!") + else if(I.force) + visible_message("[src] has been attacked in the [hit_area] with [I] by [user]!", \ + "[src] has been attacked in the [hit_area] with [I] by [user]!") + else + return 0 - apply_damage(I.force, I.damtype, affecting, armor , I) + var/armor = run_armor_check(affecting, "melee", "Your armour has protected your [hit_area].", "Your armour has softened a hit to your [hit_area].") + if(armor >= 100) return 0 + var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) - var/bloody = 0 - if(((I.damtype == BRUTE) && prob(25 + (I.force * 2)))) - if(affecting.status == ORGAN_ORGANIC) - I.add_blood(src) //Make the weapon bloody, not the person. - if(prob(I.force * 2)) //blood spatter! - bloody = 1 - var/turf/location = loc - if(istype(location, /turf/simulated)) - location.add_blood(src) - if(ishuman(user)) - var/mob/living/carbon/human/H = user - if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood - if(H.wear_suit) - H.wear_suit.add_blood(src) - H.update_inv_wear_suit(0) //updates mob overlays to show the new blood (no refresh) - else if(H.w_uniform) - H.w_uniform.add_blood(src) - H.update_inv_w_uniform(0) //updates mob overlays to show the new blood (no refresh) - if (H.gloves) - var/obj/item/clothing/gloves/G = H.gloves - G.add_blood(H) - else - H.add_blood(H) - H.update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves + apply_damage(I.force, I.damtype, affecting, armor , I) + var/bloody = 0 + if(((I.damtype == BRUTE) && prob(25 + (I.force * 2)))) + if(affecting.status == ORGAN_ORGANIC) + I.add_blood(src) //Make the weapon bloody, not the person. + if(prob(I.force * 2)) //blood spatter! + bloody = 1 + var/turf/location = loc + if(istype(location, /turf/simulated)) + location.add_blood(src) + if(ishuman(user)) + var/mob/living/carbon/human/H = user + if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood + if(H.wear_suit) + H.wear_suit.add_blood(src) + H.update_inv_wear_suit(0) //updates mob overlays to show the new blood (no refresh) + else if(H.w_uniform) + H.w_uniform.add_blood(src) + H.update_inv_w_uniform(0) //updates mob overlays to show the new blood (no refresh) + if (H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + G.add_blood(H) + else + H.add_blood(H) + H.update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves - switch(hit_area) - if("head") //Harder to score a stun but if you do it lasts a bit longer - if(stat == CONSCIOUS && prob(I.force)) - if(Iforce >= 5) + switch(hit_area) + if("head") //Harder to score a stun but if you do it lasts a bit longer + if(stat == CONSCIOUS && prob(I.force)) visible_message("[src] has been knocked unconscious!", \ "[src] has been knocked unconscious!") apply_effect(20, PARALYZE, armor) if(src != user && I.damtype == BRUTE) ticker.mode.remove_revolutionary(mind) + if(bloody) //Apply blood + if(wear_mask) + wear_mask.add_blood(src) + update_inv_wear_mask(0) + if(head) + head.add_blood(src) + update_inv_head(0) + if(glasses && prob(33)) + glasses.add_blood(src) + update_inv_glasses(0) - if(bloody) //Apply blood - if(wear_mask) - wear_mask.add_blood(src) - update_inv_wear_mask(0) - if(head) - head.add_blood(src) - update_inv_head(0) - if(glasses && prob(33)) - glasses.add_blood(src) - update_inv_glasses(0) - - if("chest") //Easier to score a stun but lasts less time - if(stat == CONSCIOUS && prob(I.force + 10)) - if(Iforce >= 5) + if("chest") //Easier to score a stun but lasts less time + if(stat == CONSCIOUS && prob(I.force + 10)) visible_message("[src] has been knocked down!", \ "[src] has been knocked down!") apply_effect(5, WEAKEN, armor) - if(bloody) - if(wear_suit) - wear_suit.add_blood(src) - update_inv_wear_suit(0) - if(w_uniform) - w_uniform.add_blood(src) - update_inv_w_uniform(0) - - if(Iforce > 10 || Iforce >= 5 && prob(33)) - forcesay(hit_appends) //forcesay checks stat already. - + if(bloody) + if(wear_suit) + wear_suit.add_blood(src) + update_inv_wear_suit(0) + if(w_uniform) + w_uniform.add_blood(src) + update_inv_w_uniform(0) + if(Iforce > 10 || Iforce >= 5 && prob(33)) + forcesay(hit_appends) //forcesay checks stat already /mob/living/carbon/human/emp_act(severity) var/informed = 0 diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index ba27693b4c6..3fffa8a5958 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -34,7 +34,7 @@ var/obj/item/l_store = null var/obj/item/s_store = null - var/base_icon_state = "caucasian1_m" + var/icon/base_icon_state = "caucasian1_m" var/list/organs = list() //Gets filled up in the constructor (human.dm, New() proc, line 24. I'm sick and tired of missing comments. -Agouri diff --git a/code/modules/mob/living/carbon/human/human_helpers.dm b/code/modules/mob/living/carbon/human/human_helpers.dm index 6b59a125648..3af3219a129 100644 --- a/code/modules/mob/living/carbon/human/human_helpers.dm +++ b/code/modules/mob/living/carbon/human/human_helpers.dm @@ -119,4 +119,11 @@ return 1//Humans can use guns and such /mob/living/carbon/human/InCritical() - return (health <= config.health_threshold_crit && stat == UNCONSCIOUS) \ No newline at end of file + return (health <= config.health_threshold_crit && stat == UNCONSCIOUS) + +/mob/living/carbon/human/reagent_check(datum/reagent/R) + if(dna) + var/bypass = dna.species.handle_chemicals(R,src) + return bypass // if it returns 0, it will run the usual on_mob_life for that reagent. otherwise, it will stop after running handle_chemicals for the species. + else + return 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm index 905c9adaf4d..438fb8c650b 100644 --- a/code/modules/mob/living/carbon/human/human_movement.dm +++ b/code/modules/mob/living/carbon/human/human_movement.dm @@ -1,29 +1,6 @@ /mob/living/carbon/human/movement_delay() - if(!has_gravity(src)) - return -1 //It's hard to be slowed down in space by... anything - else if(status_flags & GOTTAGOFAST) - return -1 - - . = 0 - var/health_deficiency = (100 - health + staminaloss) - if(health_deficiency >= 40) - . += (health_deficiency / 25) - - var/hungry = (500 - nutrition) / 5 //So overeat would be 100 and default level would be 80 - if(hungry >= 70) - . += hungry / 50 - - if(wear_suit) - . += wear_suit.slowdown - if(shoes) - . += shoes.slowdown - if(back) - . += back.slowdown - - if(FAT in mutations) - . += 1.5 - if(bodytemperature < 283.222) - . += (283.222 - bodytemperature) / 10 * 1.75 + if(dna) + . += dna.species.movement_delay(src) . += ..() . += config.human_delay diff --git a/code/modules/mob/living/carbon/human/inventory.dm b/code/modules/mob/living/carbon/human/inventory.dm index ac1cddac276..9e99030ba8d 100644 --- a/code/modules/mob/living/carbon/human/inventory.dm +++ b/code/modules/mob/living/carbon/human/inventory.dm @@ -1,153 +1,156 @@ /mob/living/carbon/human/can_equip(obj/item/I, slot, disable_warning = 0) - switch(slot) - if(slot_l_hand) - if(l_hand) - return 0 - return 1 - if(slot_r_hand) - if(r_hand) - return 0 - return 1 - if(slot_wear_mask) - if(wear_mask) - return 0 - if( !(I.slot_flags & SLOT_MASK) ) - return 0 - return 1 - if(slot_back) - if(back) - return 0 - if( !(I.slot_flags & SLOT_BACK) ) - return 0 - return 1 - if(slot_wear_suit) - if(wear_suit) - return 0 - if( !(I.slot_flags & SLOT_OCLOTHING) ) - return 0 - return 1 - if(slot_gloves) - if(gloves) - return 0 - if( !(I.slot_flags & SLOT_GLOVES) ) - return 0 - return 1 - if(slot_shoes) - if(shoes) - return 0 - if( !(I.slot_flags & SLOT_FEET) ) - return 0 - return 1 - if(slot_belt) - if(belt) - return 0 - if(!w_uniform) - if(!disable_warning) - src << "\red You need a jumpsuit before you can attach this [I.name]." - return 0 - if( !(I.slot_flags & SLOT_BELT) ) - return - return 1 - if(slot_glasses) - if(glasses) - return 0 - if( !(I.slot_flags & SLOT_EYES) ) - return 0 - return 1 - if(slot_head) - if(head) - return 0 - if( !(I.slot_flags & SLOT_HEAD) ) - return 0 - return 1 - if(slot_ears) - if(ears) - return 0 - if( !(I.slot_flags & SLOT_EARS) ) - return 0 - return 1 - if(slot_w_uniform) - if(w_uniform) - return 0 - if( !(I.slot_flags & SLOT_ICLOTHING) ) - return 0 - return 1 - if(slot_wear_id) - if(wear_id) - return 0 - if(!w_uniform) - if(!disable_warning) - src << "\red You need a jumpsuit before you can attach this [I.name]." - return 0 - if( !(I.slot_flags & SLOT_ID) ) - return 0 - return 1 - if(slot_l_store) - if(I.flags & NODROP) //Pockets aren't visible, so you can't move NODROP items into them. - return 0 - if(l_store) - return 0 - if(!w_uniform) - if(!disable_warning) - src << "\red You need a jumpsuit before you can attach this [I.name]." - return 0 - if(I.slot_flags & SLOT_DENYPOCKET) - return - if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) + if(dna) + return dna.species.can_equip(I, slot, disable_warning, src) + else + switch(slot) + if(slot_l_hand) + if(l_hand) + return 0 return 1 - if(slot_r_store) - if(I.flags & NODROP) - return 0 - if(r_store) - return 0 - if(!w_uniform) - if(!disable_warning) - src << "\red You need a jumpsuit before you can attach this [I.name]." - return 0 - if(I.slot_flags & SLOT_DENYPOCKET) - return 0 - if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) + if(slot_r_hand) + if(r_hand) + return 0 return 1 - return 0 - if(slot_s_store) - if(I.flags & NODROP) //Suit storage NODROP items drop if you take a suit off, this is to prevent people exploiting this. - return 0 - if(s_store) - return 0 - if(!wear_suit) - if(!disable_warning) - src << "\red You need a suit before you can attach this [I.name]." - return 0 - if(!wear_suit.allowed) - if(!disable_warning) - usr << "You somehow have a suit with no defined allowed items for suit storage, stop that." //should be src? - return 0 - if(I.w_class > 4) - if(!disable_warning) - usr << "The [I.name] is too big to attach." //should be src? - return 0 - if( istype(I, /obj/item/device/pda) || istype(I, /obj/item/weapon/pen) || is_type_in_list(I, wear_suit.allowed) ) //ugly and un-polymorphic. + if(slot_wear_mask) + if(wear_mask) + return 0 + if( !(I.slot_flags & SLOT_MASK) ) + return 0 return 1 - return 0 - if(slot_handcuffed) - if(handcuffed) - return 0 - if(!istype(I, /obj/item/weapon/handcuffs)) - return 0 - return 1 - if(slot_legcuffed) - if(legcuffed) - return 0 - if(!istype(I, /obj/item/weapon/legcuffs)) - return 0 - return 1 - if(slot_in_backpack) - if (back && istype(back, /obj/item/weapon/storage/backpack)) - var/obj/item/weapon/storage/backpack/B = back - if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class) + if(slot_back) + if(back) + return 0 + if( !(I.slot_flags & SLOT_BACK) ) + return 0 + return 1 + if(slot_wear_suit) + if(wear_suit) + return 0 + if( !(I.slot_flags & SLOT_OCLOTHING) ) + return 0 + return 1 + if(slot_gloves) + if(gloves) + return 0 + if( !(I.slot_flags & SLOT_GLOVES) ) + return 0 + return 1 + if(slot_shoes) + if(shoes) + return 0 + if( !(I.slot_flags & SLOT_FEET) ) + return 0 + return 1 + if(slot_belt) + if(belt) + return 0 + if(!w_uniform) + if(!disable_warning) + src << "\red You need a jumpsuit before you can attach this [I.name]." + return 0 + if( !(I.slot_flags & SLOT_BELT) ) + return + return 1 + if(slot_glasses) + if(glasses) + return 0 + if( !(I.slot_flags & SLOT_EYES) ) + return 0 + return 1 + if(slot_head) + if(head) + return 0 + if( !(I.slot_flags & SLOT_HEAD) ) + return 0 + return 1 + if(slot_ears) + if(ears) + return 0 + if( !(I.slot_flags & SLOT_EARS) ) + return 0 + return 1 + if(slot_w_uniform) + if(w_uniform) + return 0 + if( !(I.slot_flags & SLOT_ICLOTHING) ) + return 0 + return 1 + if(slot_wear_id) + if(wear_id) + return 0 + if(!w_uniform) + if(!disable_warning) + src << "\red You need a jumpsuit before you can attach this [I.name]." + return 0 + if( !(I.slot_flags & SLOT_ID) ) + return 0 + return 1 + if(slot_l_store) + if(I.flags & NODROP) //Pockets aren't visible, so you can't move NODROP items into them. + return 0 + if(l_store) + return 0 + if(!w_uniform) + if(!disable_warning) + src << "\red You need a jumpsuit before you can attach this [I.name]." + return 0 + if(I.slot_flags & SLOT_DENYPOCKET) + return + if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) return 1 - return 0 - return 0 //Unsupported slot + if(slot_r_store) + if(I.flags & NODROP) + return 0 + if(r_store) + return 0 + if(!w_uniform) + if(!disable_warning) + src << "\red You need a jumpsuit before you can attach this [I.name]." + return 0 + if(I.slot_flags & SLOT_DENYPOCKET) + return 0 + if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) + return 1 + return 0 + if(slot_s_store) + if(I.flags & NODROP) //Suit storage NODROP items drop if you take a suit off, this is to prevent people exploiting this. + return 0 + if(s_store) + return 0 + if(!wear_suit) + if(!disable_warning) + src << "\red You need a suit before you can attach this [I.name]." + return 0 + if(!wear_suit.allowed) + if(!disable_warning) + usr << "You somehow have a suit with no defined allowed items for suit storage, stop that." //should be src? + return 0 + if(I.w_class > 4) + if(!disable_warning) + usr << "The [I.name] is too big to attach." //should be src? + return 0 + if( istype(I, /obj/item/device/pda) || istype(I, /obj/item/weapon/pen) || is_type_in_list(I, wear_suit.allowed) ) //ugly and un-polymorphic. + return 1 + return 0 + if(slot_handcuffed) + if(handcuffed) + return 0 + if(!istype(I, /obj/item/weapon/handcuffs)) + return 0 + return 1 + if(slot_legcuffed) + if(legcuffed) + return 0 + if(!istype(I, /obj/item/weapon/legcuffs)) + return 0 + return 1 + if(slot_in_backpack) + if (back && istype(back, /obj/item/weapon/storage/backpack)) + var/obj/item/weapon/storage/backpack/B = back + if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class) + return 1 + return 0 + return 0 //Unsupported slot @@ -383,5 +386,4 @@ return else src << "\red You are trying to equip this item to an unsupported inventory slot. Report this to a coder!" - return - + return \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 341bc28445e..65d0d00b9b0 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -1,6 +1,11 @@ //This file was auto-corrected by findeclaration.exe on 25.5.2012 20:42:32 //NOTE: Breathing happens once per FOUR TICKS, unless the last breath fails. In which case it happens once per ONE TICK! So oxyloss healing is done once per 4 ticks while oxyloss damage is applied once per tick! + + +#define TINT_IMPAIR 2 //Threshold of tint level to apply weld mask overlay +#define TINT_BLIND 3 //Threshold of tint level to obscure vision fully + #define HUMAN_MAX_OXYLOSS 3 //Defines how much oxyloss humans can get per tick. A tile with no air at all (such as space) applies this value, otherwise it's a percentage of it. #define HUMAN_CRIT_MAX_OXYLOSS ( (last_tick_duration) /3) //The amount of damage you'll get when in critical condition. We want this to be a 5 minute deal = 300s. There are 100HP to get through, so (1/3)*last_tick_duration per second. Breaths however only happen every 4 ticks. @@ -21,9 +26,6 @@ #define COLD_GAS_DAMAGE_LEVEL_2 1.5 //Amount of damage applied when the current breath's temperature passes the 200K point #define COLD_GAS_DAMAGE_LEVEL_3 3 //Amount of damage applied when the current breath's temperature passes the 120K point -#define TINT_IMPAIR 2 //Threshold of tint level to apply weld mask overlay -#define TINT_BLIND 3 //Threshold of tint level to obscure vision fully - /mob/living/carbon/human var/oxygen_alert = 0 var/toxins_alert = 0 @@ -51,7 +53,6 @@ fire_alert = 0 //Reset this here, because both breathe() and handle_environment() have a chance to set it. tinttotal = tintcheck() //here as both hud updates and status updates call it - //TODO: seperate this out var/datum/gas_mixture/environment = loc.return_air() @@ -98,6 +99,9 @@ handle_regular_hud_updates() + if(dna) + dna.species.spec_life(src) // for mutantraces + // Grabbing for(var/obj/item/weapon/grab/G in src) G.process() @@ -163,132 +167,14 @@ proc/handle_mutations_and_radiation() - if(getFireLoss()) - if((COLD_RESISTANCE in mutations) || (prob(1))) - heal_organ_damage(0,1) - - if ((HULK in mutations) && health <= 25) - mutations.Remove(HULK) - update_mutations() //update our mutation overlays - src << "\red You suddenly feel very weak." - Weaken(3) - emote("collapse") - - if (radiation) - if (radiation > 100) - radiation = 100 - Weaken(10) - src << "\red You feel weak." - emote("collapse") - - if (radiation < 0) - radiation = 0 - - else - switch(radiation) - if(1 to 49) - radiation-- - if(prob(25)) - adjustToxLoss(1) - updatehealth() - - if(50 to 74) - radiation -= 2 - adjustToxLoss(1) - if(prob(5)) - radiation -= 5 - Weaken(3) - src << "\red You feel weak." - emote("collapse") - if(prob(15)) - if(!( hair_style == "Shaved") || !(hair_style == "Bald")) - src << "Your hair starts to fall out in clumps..." - spawn(50) - facial_hair_style = "Shaved" - hair_style = "Bald" - update_hair() - updatehealth() - - if(75 to 100) - radiation -= 3 - adjustToxLoss(3) - if(prob(1)) - src << "\red You mutate!" - randmutb(src) - domutcheck(src,null) - emote("gasp") - updatehealth() - + if(dna) + dna.species.handle_mutations_and_radiation(src) proc/breathe() + if(dna) + dna.species.breathe(src) - if(reagents.has_reagent("lexorin")) return - if(istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) return - - var/datum/gas_mixture/environment = loc.return_air() - var/datum/gas_mixture/breath - // HACK NEED CHANGING LATER - if(health <= config.health_threshold_crit) - losebreath++ - - if(losebreath>0) //Suffocating so do not take a breath - losebreath-- - if (prob(10)) //Gasp per 10 ticks? Sounds about right. - spawn emote("gasp") - if(istype(loc, /obj/)) - var/obj/location_as_object = loc - location_as_object.handle_internal_lifeform(src, 0) - else - //First, check for air from internal atmosphere (using an air tank and mask generally) - breath = get_breath_from_internal(BREATH_VOLUME) // Super hacky -- TLE - //breath = get_breath_from_internal(0.5) // Manually setting to old BREATH_VOLUME amount -- TLE - - //No breath from internal atmosphere so get breath from location - if(!breath) - if(isobj(loc)) - var/obj/location_as_object = loc - breath = location_as_object.handle_internal_lifeform(src, BREATH_VOLUME) - else if(isturf(loc)) - var/breath_moles = 0 - /*if(environment.return_pressure() > ONE_ATMOSPHERE) - // Loads of air around (pressure effect will be handled elsewhere), so lets just take a enough to fill our lungs at normal atmos pressure (using n = Pv/RT) - breath_moles = (ONE_ATMOSPHERE*BREATH_VOLUME/R_IDEAL_GAS_EQUATION*environment.temperature) - else*/ - // Not enough air around, take a percentage of what's there to model this properly - breath_moles = environment.total_moles()*BREATH_PERCENTAGE - - breath = loc.remove_air(breath_moles) - // Handle chem smoke effect -- Doohl - var/block = 0 - if(wear_mask) - if(wear_mask.flags & BLOCK_GAS_SMOKE_EFFECT) - block = 1 - if(glasses) - if(glasses.flags & BLOCK_GAS_SMOKE_EFFECT) - block = 1 - if(head) - if(head.flags & BLOCK_GAS_SMOKE_EFFECT) - block = 1 - - if(!block) - - for(var/obj/effect/effect/chem_smoke/smoke in view(1, src)) - if(smoke.reagents.total_volume) - smoke.reagents.reaction(src, INGEST) - spawn(5) - if(smoke) - smoke.reagents.copy_to(src, 10) // I dunno, maybe the reagents enter the blood stream through the lungs? - break // If they breathe in the nasty stuff once, no need to continue checking - - else //Still give containing object the chance to interact - if(istype(loc, /obj/)) - var/obj/location_as_object = loc - location_as_object.handle_internal_lifeform(src, 0) - - handle_breath(breath) - - if(breath) - loc.assume_air(breath) + return proc/get_breath_from_internal(volume_needed) @@ -304,229 +190,45 @@ return null - proc/handle_breath(datum/gas_mixture/breath) + /*proc/handle_breath(datum/gas_mixture/breath) if((status_flags & GODMODE)) return - if(!breath || (breath.total_moles() == 0) || suiciding) - if(reagents.has_reagent("inaprovaline")) - return - if(suiciding) - adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster - failed_last_breath = 1 - oxygen_alert = max(oxygen_alert, 1) - return 0 - if(health >= config.health_threshold_crit) - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_last_breath = 1 - else - adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) - failed_last_breath = 1 + if(dna) + dna.species.handle_breath(breath) - oxygen_alert = max(oxygen_alert, 1) - - return 0 - - var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa - //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now) - var/safe_co2_max = 10 // Yes it's an arbitrary value who cares? - var/safe_toxins_max = 0.005 - var/SA_para_min = 1 - var/SA_sleep_min = 5 - var/oxygen_used = 0 - var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME - - //Partial pressure of the O2 in our breath - var/O2_pp = (breath.oxygen/breath.total_moles())*breath_pressure - // Same, but for the toxins - var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure - // And CO2, lets say a PP of more than 10 will be bad (It's a little less really, but eh, being passed out all round aint no fun) - var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*breath_pressure // Tweaking to fit the hacky bullshit I've done with atmo -- TLE - //var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*0.5 // The default pressure value - - if(O2_pp < safe_oxygen_min) // Too little oxygen - if(prob(20)) - spawn(0) emote("gasp") - if(O2_pp > 0) - var/ratio = safe_oxygen_min/O2_pp - adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) - failed_last_breath = 1 - oxygen_used = breath.oxygen*ratio/6 - else - adjustOxyLoss(HUMAN_MAX_OXYLOSS) - failed_last_breath = 1 - oxygen_alert = max(oxygen_alert, 1) - /*else if (O2_pp > safe_oxygen_max) // Too much oxygen (commented this out for now, I'll deal with pressure damage elsewhere I suppose) - spawn(0) emote("cough") - var/ratio = O2_pp/safe_oxygen_max - oxyloss += 5*ratio - oxygen_used = breath.oxygen*ratio/6 - oxygen_alert = max(oxygen_alert, 1)*/ - else // We're in safe limits - failed_last_breath = 0 - adjustOxyLoss(-5) - oxygen_used = breath.oxygen/6 - oxygen_alert = 0 - - breath.oxygen -= oxygen_used - breath.carbon_dioxide += oxygen_used - - //CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick. - if(CO2_pp > safe_co2_max) - if(!co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so. - co2overloadtime = world.time - else if(world.time - co2overloadtime > 120) - Paralyse(3) - adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business - if(world.time - co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good! - adjustOxyLoss(8) - if(prob(20)) // Lets give them some chance to know somethings not right though I guess. - spawn(0) emote("cough") - - else - co2overloadtime = 0 - - if(Toxins_pp > safe_toxins_max) // Too much toxins - var/ratio = (breath.toxins/safe_toxins_max) * 10 - //adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) //Limit amount of damage toxin exposure can do per second - if(reagents) - reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) - toxins_alert = max(toxins_alert, 1) - else - toxins_alert = 0 - - if(breath.trace_gases.len) // If there's some other shit in the air lets deal with it here. - for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) - var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure - if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit - Paralyse(3) // 3 gives them one second to wake up and run away a bit! - if(SA_pp > SA_sleep_min) // Enough to make us sleep as well - sleeping = max(sleeping+2, 10) - else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning - if(prob(20)) - spawn(0) emote(pick("giggle", "laugh")) - - if( (abs(310.15 - breath.temperature) > 50) && !(COLD_RESISTANCE in mutations)) // Hot air hurts :( - if(breath.temperature < 260.15) - if(prob(20)) - src << "\red You feel your face freezing and an icicle forming in your lungs!" - else if(breath.temperature > 360.15) - if(prob(20)) - src << "\red You feel your face burning and a searing heat in your lungs!" - - switch(breath.temperature) - if(-INFINITY to 120) - apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head") - fire_alert = max(fire_alert, 1) - if(120 to 200) - apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head") - fire_alert = max(fire_alert, 1) - if(200 to 260) - apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head") - fire_alert = max(fire_alert, 1) - if(360 to 400) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head") - fire_alert = max(fire_alert, 2) - if(400 to 1000) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head") - fire_alert = max(fire_alert, 2) - if(1000 to INFINITY) - apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head") - fire_alert = max(fire_alert, 2) - - //Temporary fixes to the alerts. - - return 1 + return 1*/ proc/handle_environment(datum/gas_mixture/environment) - if(!environment) - return - - var/loc_temp = get_temperature(environment) - //world << "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]" - - //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. - if(stat != 2) - stabilize_temperature_from_calories() - - //After then, it reacts to the surrounding atmosphere based on your thermal protection - if(!on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases - if(loc_temp < bodytemperature) - //Place is colder than we are - var/thermal_protection = get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX) - else - //Place is hotter than we are - var/thermal_protection = get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. - if(thermal_protection < 1) - bodytemperature += min((1-thermal_protection) * ((loc_temp - bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX) - - // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. - if(bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT) - //Body temperature is too hot. - fire_alert = max(fire_alert, 1) - switch(bodytemperature) - if(360 to 400) - apply_damage(HEAT_DAMAGE_LEVEL_1, BURN) - fire_alert = max(fire_alert, 2) - if(400 to 460) - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - fire_alert = max(fire_alert, 2) - if(460 to INFINITY) - if(on_fire) - apply_damage(HEAT_DAMAGE_LEVEL_3, BURN) - fire_alert = max(fire_alert, 2) - else - apply_damage(HEAT_DAMAGE_LEVEL_2, BURN) - fire_alert = max(fire_alert, 2) - - else if(bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT) - fire_alert = max(fire_alert, 1) - if(!istype(loc, /obj/machinery/atmospherics/unary/cryo_cell)) - switch(bodytemperature) - if(200 to 260) - apply_damage(COLD_DAMAGE_LEVEL_1, BURN) - fire_alert = max(fire_alert, 1) - if(120 to 200) - apply_damage(COLD_DAMAGE_LEVEL_2, BURN) - fire_alert = max(fire_alert, 1) - if(-INFINITY to 120) - apply_damage(COLD_DAMAGE_LEVEL_3, BURN) - fire_alert = max(fire_alert, 1) - - // Account for massive pressure differences. Done by Polymorph - // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! - - var/pressure = environment.return_pressure() - var/adjusted_pressure = calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob. - switch(adjusted_pressure) - if(HAZARD_HIGH_PRESSURE to INFINITY) - adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) - pressure_alert = 2 - if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) - pressure_alert = 1 - if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE) - pressure_alert = 0 - if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE) - pressure_alert = -1 - else - if( !(COLD_RESISTANCE in mutations) ) - adjustBruteLoss( LOW_PRESSURE_DAMAGE ) - pressure_alert = -2 - else - pressure_alert = -1 + if(dna) + dna.species.handle_environment(environment, src) return ///FIRE CODE handle_fire() + if(dna) + dna.species.handle_fire(src) + if(..()) return var/thermal_protection = get_heat_protection(30000) //If you don't have fire suit level protection, you get a temperature increase if((1 - thermal_protection) > 0.0001) bodytemperature += BODYTEMP_HEATING_MAX return + + IgniteMob() + if(dna) + dna.species.IgniteMob(src) + else + ..() + + ExtinguishMob() + if(dna) + dna.species.ExtinguishMob(src) + else + ..() + //END FIRE CODE /* @@ -650,6 +352,9 @@ if(COLD_RESISTANCE in mutations) return 1 //Fully protected from the cold. + if(dna && COLDRES in dna.species.specflags) + return 1 + temperature = max(temperature, 2.7) //There is an occasional bug where the temperature is miscalculated in ares with a small amount of gas on them, so this is necessary to ensure that that bug does not affect this calculation. Space's temperature is 2.7K and most suits that are intended to protect against any cold, protect down to 2.0K. var/thermal_protection_flags = get_cold_protection_flags(temperature) @@ -739,82 +444,8 @@ */ proc/handle_chemicals_in_body() - if(reagents) reagents.metabolize(src) - - if(dna && dna.mutantrace == "plant") //couldn't think of a better place to place it, since it handles nutrition -- Urist - var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing - if(isturf(loc)) //else, there's considered to be no light - var/turf/T = loc - var/area/A = T.loc - if(A) - if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 //hardcapped so it's not abused by having a ton of flashlights - else light_amount = 5 - nutrition += light_amount - if(nutrition > 500) - nutrition = 500 - if(light_amount > 2) //if there's enough light, heal - heal_overall_damage(1,1) - adjustToxLoss(-1) - adjustOxyLoss(-1) - if(dna && dna.mutantrace == "shadow") - var/light_amount = 0 - if(isturf(loc)) - var/turf/T = loc - var/area/A = T.loc - if(A) - if(A.lighting_use_dynamic) light_amount = T.lighting_lumcount - else light_amount = 10 - if(light_amount > 2) //if there's enough light, start dying - take_overall_damage(1,1) - else if (light_amount < 2) //heal in the dark - heal_overall_damage(1,1) - - //The fucking FAT mutation is the dumbest shit ever. It makes the code so difficult to work with - if(FAT in mutations) - if(overeatduration < 100) - src << "\blue You feel fit again!" - mutations -= FAT - update_inv_w_uniform(0) - update_inv_wear_suit() - else - if(overeatduration > 500) - src << "\red You suddenly feel blubbery!" - mutations |= FAT - update_inv_w_uniform(0) - update_inv_wear_suit() - - // nutrition decrease - if (nutrition > 0 && stat != 2) - nutrition = max (0, nutrition - HUNGER_FACTOR) - - if (nutrition > 450) - if(overeatduration < 600) //capped so people don't take forever to unfat - overeatduration++ - else - if(overeatduration > 1) - overeatduration -= 2 //doubled the unfat rate - - if(dna && dna.mutantrace == "plant") - if(nutrition < 200) - take_overall_damage(2,0) - - if (drowsyness) - drowsyness-- - eye_blurry = max(2, eye_blurry) - if (prob(5)) - sleeping += 1 - Paralyse(5) - - confused = max(0, confused - 1) - // decrement dizziness counter, clamped to 0 - if(resting) - dizziness = max(0, dizziness - 15) - jitteriness = max(0, jitteriness - 15) - else - dizziness = max(0, dizziness - 3) - jitteriness = max(0, jitteriness - 3) - - updatehealth() + if(dna) + dna.species.handle_chemicals_in_body(src) return //TODO: DEFERRED @@ -1048,155 +679,15 @@ damageoverlay.overlays += I damageoverlay.overlays += black - if( stat == DEAD ) - sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) - see_in_dark = 8 - if(!druggy) see_invisible = SEE_INVISIBLE_LEVEL_TWO - if(healths) healths.icon_state = "health7" //DEAD healthmeter - else - sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) - var/see_temp = see_invisible - see_invisible = SEE_INVISIBLE_LIVING - if(dna) - switch(dna.mutantrace) - if("slime") - see_in_dark = 3 - see_invisible = SEE_INVISIBLE_LEVEL_ONE - if("shadow") - see_in_dark = 8 - else - see_in_dark = 2 - - if(XRAY in mutations) - sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS - see_in_dark = 8 - see_invisible = SEE_INVISIBLE_LEVEL_TWO - - if(seer) - see_invisible = SEE_INVISIBLE_OBSERVER - - if(mind && mind.changeling) - hud_used.lingchemdisplay.invisibility = 0 - hud_used.lingchemdisplay.maptext = "
[src.mind.changeling.chem_charges]
" - else - hud_used.lingchemdisplay.invisibility = 101 - - if(istype(wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)) - var/obj/item/clothing/mask/gas/voice/space_ninja/O = wear_mask - switch(O.mode) - if(0) - var/target_list[] = list() - for(var/mob/living/target in oview(src)) - if( target.mind&&(target.mind.special_role||issilicon(target)) )//They need to have a mind. - target_list += target - if(target_list.len)//Everything else is handled by the ninja mask proc. - O.assess_targets(target_list, src) - see_invisible = SEE_INVISIBLE_LIVING - if(1) - see_in_dark = 5 - see_invisible = SEE_INVISIBLE_LIVING - if(2) - sight |= SEE_MOBS - see_invisible = SEE_INVISIBLE_LEVEL_TWO - if(3) - sight |= SEE_TURFS - see_invisible = SEE_INVISIBLE_LIVING - - if(glasses) - if(istype(glasses, /obj/item/clothing/glasses)) - var/obj/item/clothing/glasses/G = glasses - sight |= G.vision_flags - see_in_dark = G.darkness_view - see_invisible = G.invis_view - if(G.hud) - G.process_hud(src) - - if(druggy) //Override for druggy - see_invisible = see_temp - - if(see_override) //Override all - see_invisible = see_override - - if(healths) - switch(hal_screwyhud) - if(1) healths.icon_state = "health6" - if(2) healths.icon_state = "health7" - else - switch(health - staminaloss) - 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" - - if(nutrition_icon) - switch(nutrition) - if(450 to INFINITY) nutrition_icon.icon_state = "nutrition0" - if(350 to 450) nutrition_icon.icon_state = "nutrition1" - if(250 to 350) nutrition_icon.icon_state = "nutrition2" - if(150 to 250) nutrition_icon.icon_state = "nutrition3" - else nutrition_icon.icon_state = "nutrition4" - - if(pressure) - pressure.icon_state = "pressure[pressure_alert]" - - if(pullin) - if(pulling) pullin.icon_state = "pull" - else pullin.icon_state = "pull0" -// if(rest) //Not used with new UI -// if(resting || lying || sleeping) rest.icon_state = "rest1" -// else rest.icon_state = "rest0" - if(toxin) - if(hal_screwyhud == 4 || toxins_alert) toxin.icon_state = "tox1" - else toxin.icon_state = "tox0" - if(oxygen) - if(hal_screwyhud == 3 || oxygen_alert) oxygen.icon_state = "oxy1" - else oxygen.icon_state = "oxy0" - if(fire) - if(fire_alert) fire.icon_state = "fire[fire_alert]" //fire_alert is either 0 if no alert, 1 for cold and 2 for heat. - else fire.icon_state = "fire0" - - if(bodytemp) - switch(bodytemperature) //310.055 optimal body temp - if(370 to INFINITY) bodytemp.icon_state = "temp4" - if(350 to 370) bodytemp.icon_state = "temp3" - if(335 to 350) bodytemp.icon_state = "temp2" - if(320 to 335) bodytemp.icon_state = "temp1" - if(300 to 320) bodytemp.icon_state = "temp0" - if(295 to 300) bodytemp.icon_state = "temp-1" - if(280 to 295) bodytemp.icon_state = "temp-2" - if(260 to 280) bodytemp.icon_state = "temp-3" - else bodytemp.icon_state = "temp-4" - -// This checks how much the mob's eyewear impairs their vision - if(tinttotal >= TINT_IMPAIR) - if(tinted_weldhelh) - if(tinttotal >= TINT_BLIND) - blinded = 1 // You get the sudden urge to learn to play keyboard - client.screen += global_hud.darkMask - else - client.screen += global_hud.darkMask - - if(blind) - if(blinded) blind.layer = 18 - else blind.layer = 0 - - if( disabilities & NEARSIGHTED && !istype(glasses, /obj/item/clothing/glasses/regular) ) - client.screen += global_hud.vimpaired - if(eye_blurry) client.screen += global_hud.blurry - if(druggy) client.screen += global_hud.druggy - - - if(eye_stat > 20) - if(eye_stat > 30) client.screen += global_hud.darkMask - else client.screen += global_hud.vimpaired - if(machine) if(!machine.check_eye(src)) reset_view(null) else if(!client.adminobs) reset_view(null) + + if(dna) + dna.species.handle_vision(src) + dna.species.handle_hud_icons(src) + return 1 proc/handle_random_events() diff --git a/code/modules/mob/living/carbon/human/mutantrace.dm b/code/modules/mob/living/carbon/human/mutantrace.dm new file mode 100644 index 00000000000..30615bb624c --- /dev/null +++ b/code/modules/mob/living/carbon/human/mutantrace.dm @@ -0,0 +1,22 @@ +var/global/list/colored_mutraces = list( // these mutantraces are affected by mutant color + "lizard", + "plant", + "pod", + "slime", + "jelly", + "golem" +) + +var/global/list/mutants_with_eyes = list( // these mutantraces have eyes + "lizard", + "plant", + "pod", + "jelly", +) + +/mob/living/carbon/human/proc/check_mutrace(var/mutneeded = "mut1", var/mutneededalt = "mut2") + if(dna) + if(dna.mutantrace == mutneeded || dna.mutantrace == mutneededalt) + return 1 + + return 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/say.dm b/code/modules/mob/living/carbon/human/say.dm index cdd8b0cc80a..be98cf67ed9 100644 --- a/code/modules/mob/living/carbon/human/say.dm +++ b/code/modules/mob/living/carbon/human/say.dm @@ -22,24 +22,8 @@ return if(dna) - if(dna.mutantrace == "lizard") - if(copytext(message, 1, 2) != "*") - message = replacetext(message, "s", stutter("ss")) + message = dna.species.handle_speech(message,src) - if(dna.mutantrace == "fly") - if(copytext(message, 1, 2) != "*") - message = replacetext(message, "z", stutter("zz")) - - /*if(dna.mutantrace == "slime" && prob(5)) - if(copytext(message, 1, 2) != "*") - if(copytext(message, 1, 2) == ";") - message = ";" - else - message = "" - message += "SKR" - var/imax = rand(5,20) - for(var/i = 0,i= 60) + return "gibbers, \"[text]\""; + if (ending == "?") + return "asks, \"[text]\""; + if (ending == "!") + return "exclaims, \"[text]\""; + + if(dna) + return "[dna.species.say_mod], \"[text]\""; + + return "says, \"[text]\""; /mob/living/carbon/human/proc/forcesay(list/append) if(stat == CONSCIOUS) diff --git a/code/modules/mob/living/carbon/human/species.dm b/code/modules/mob/living/carbon/human/species.dm new file mode 100644 index 00000000000..97143b147b1 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species.dm @@ -0,0 +1,1330 @@ +// This code handles different species in the game. + +#define SPECIES_LAYER 23 +#define BODY_LAYER 22 +#define HAIR_LAYER 8 + +#define TINT_IMPAIR 2 +#define TINT_BLIND 3 + +#define HUMAN_MAX_OXYLOSS 3 +#define HUMAN_CRIT_MAX_OXYLOSS ( (last_tick_duration) /3) + +#define HEAT_DAMAGE_LEVEL_1 2 +#define HEAT_DAMAGE_LEVEL_2 3 +#define HEAT_DAMAGE_LEVEL_3 8 + +#define COLD_DAMAGE_LEVEL_1 0.5 +#define COLD_DAMAGE_LEVEL_2 1.5 +#define COLD_DAMAGE_LEVEL_3 3 + +#define HEAT_GAS_DAMAGE_LEVEL_1 2 +#define HEAT_GAS_DAMAGE_LEVEL_2 4 +#define HEAT_GAS_DAMAGE_LEVEL_3 8 + +#define COLD_GAS_DAMAGE_LEVEL_1 0.5 +#define COLD_GAS_DAMAGE_LEVEL_2 1.5 +#define COLD_GAS_DAMAGE_LEVEL_3 3 + +/datum/species + var/id = null // if the game needs to manually check your race to do something not included in a proc here, it will use this + var/name = null // this is the fluff name. these will be left generic (such as 'Lizardperson' for the lizard race) so servers can change them to whatever + var/roundstart = 0 // can this mob be chosen at roundstart? (assuming the config option is checked?) + var/default_color = "#FFF" // if alien colors are disabled, this is the color that will be used by that race + + var/eyes = "eyes" // which eyes the race uses. at the moment, the only types of eyes are "eyes" (regular eyes) and "jelleyes" (three eyes) + var/sexes = 1 // whether or not the race has sexual characteristics. at the moment this is only 0 for skeletons and shadows + var/hair_color = null // this allows races to have specific hair colors... if null, it uses the H's hair/facial hair colors. if "mutcolor", it uses the H's mutant_color + var/hair_alpha = 255 // the alpha used by the hair. 255 is completely solid, 0 is transparent. + var/use_skintones = 0 // does it use skintones or not? (spoiler alert this is only used by humans) + + var/list/no_equip = list() // slots the race can't equip stuff to + var/nojumpsuit = 0 // this is sorta... weird. it basically lets you equip stuff that usually needs jumpsuits without one, like belts and pockets and ids + + var/say_mod = "says" // affects the speech message + + var/speedmod = 0 // this affects the race's speed. positive numbers make it move slower, negative numbers make it move faster + var/armor = 0 // overall defense for the race... or less defense, if it's negative. + var/brutemod = 1 // multiplier for brute damage + var/burnmod = 1 // multiplier for burn damage + var/coldmod = 1 // multiplier for cold damage + var/heatmod = 1 // multiplier for heat damage + var/punchmod = 0 // adds to the punch damage + + var/invis_sight = SEE_INVISIBLE_LIVING + var/darksight = 2 + + // species flags. these can be found in flags.dm + var/list/specflags = list() + + var/attack_verb = "punch" // punch-specific attack verb + var/sound/attack_sound = 'sound/weapons/punch1.ogg' + var/sound/miss_sound = 'sound/weapons/punchmiss.ogg' + + var/mob/living/list/ignored_by = list() // list of mobs that will ignore this species + + /////////// + // PROCS // + /////////// + + proc/update_base_icon_state(var/mob/living/carbon/human/H) + if(HUSK in H.mutations) + H.remove_overlay(SPECIES_LAYER) // races lose their color + return "husk" + else if(sexes) + if(use_skintones) + return "[H.skin_tone]_[(H.gender == FEMALE) ? "f" : "m"]" + else + return "[id]_[(H.gender == FEMALE) ? "f" : "m"]" + else + return "[id]" + + proc/update_color(var/mob/living/carbon/human/H) + H.remove_overlay(SPECIES_LAYER) + + var/image/standing + + var/g = (H.gender == FEMALE) ? "f" : "m" + + if(MUTCOLORS in specflags) + var/image/spec_base + if(sexes) + spec_base = image("icon" = 'icons/mob/human.dmi', "icon_state" = "[id]_[g]_s", "layer" = -SPECIES_LAYER) + else + spec_base = image("icon" = 'icons/mob/human.dmi', "icon_state" = "[id]_s", "layer" = -SPECIES_LAYER) + if(!config.mutant_colors) + H.dna.mutant_color = default_color + spec_base.color = "#[H.dna.mutant_color]" + standing = spec_base + + if(standing) + H.overlays_standing[SPECIES_LAYER] = standing + + H.apply_overlay(SPECIES_LAYER) + + proc/handle_hair(var/mob/living/carbon/human/H) + H.remove_overlay(HAIR_LAYER) + + var/datum/sprite_accessory/S + var/list/standing = list() + + if(H.facial_hair_style && FACEHAIR in specflags) + S = facial_hair_styles_list[H.facial_hair_style] + if(S) + var/image/img_facial_s + + img_facial_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER) + + if(hair_color) + if(hair_color == "mutcolor") + if(!config.mutant_colors) + img_facial_s.color = "#" + default_color + else + img_facial_s.color = "#" + H.dna.mutant_color + else + img_facial_s.color = "#" + hair_color + else + img_facial_s.color = "#" + H.facial_hair_color + img_facial_s.alpha = hair_alpha + + standing += img_facial_s + + //Applies the debrained overlay if there is no brain + if(!H.getorgan(/obj/item/organ/brain)) + standing += image("icon"='icons/mob/human_face.dmi', "icon_state" = "debrained_s", "layer" = -HAIR_LAYER) + + else if(H.hair_style && HAIR in specflags) + S = hair_styles_list[H.hair_style] + if(S) + var/image/img_hair_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER) + + img_hair_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER) + + if(hair_color) + if(hair_color == "mutcolor") + if(!config.mutant_colors) + img_hair_s.color = "#" + default_color + else + img_hair_s.color = "#" + H.dna.mutant_color + else + img_hair_s.color = "#" + hair_color + else + img_hair_s.color = "#" + H.hair_color + img_hair_s.alpha = hair_alpha + + standing += img_hair_s + + if(standing.len) + H.overlays_standing[HAIR_LAYER] = standing + + H.apply_overlay(HAIR_LAYER) + + return + + proc/handle_body(var/mob/living/carbon/human/H) + H.remove_overlay(BODY_LAYER) + + var/list/standing = list() + + // lipstick + if(H.lip_style && LIPS in specflags) + standing += image("icon"='icons/mob/human_face.dmi', "icon_state"="lips_[H.lip_style]_s", "layer" = -BODY_LAYER) + + // eyes + if(EYECOLOR in specflags) + var/image/img_eyes_s = image("icon" = 'icons/mob/human_face.dmi', "icon_state" = "[eyes]_s", "layer" = -BODY_LAYER) + img_eyes_s.color = "#" + H.eye_color + standing += img_eyes_s + + //Underwear + if(H.underwear) + var/datum/sprite_accessory/underwear/U = underwear_all[H.underwear] + if(U) + standing += image("icon"=U.icon, "icon_state"="[U.icon_state]_s", "layer"=-BODY_LAYER) + + if(standing.len) + H.overlays_standing[BODY_LAYER] = standing + + H.apply_overlay(BODY_LAYER) + + return + + proc/spec_life(var/mob/living/carbon/human/H) + return + + proc/spec_death(var/gibbed, var/mob/living/carbon/human/H) + return + + proc/auto_equip(var/mob/living/carbon/human/H) + // handles the equipping of species-specific gear + return + + proc/can_equip(var/obj/item/I, var/slot, var/disable_warning, var/mob/living/carbon/human/H) + if(slot in no_equip) + if(!(type in I.species_exception)) + return 0 + + switch(slot) + if(slot_l_hand) + if(H.l_hand) + return 0 + return 1 + if(slot_r_hand) + if(H.r_hand) + return 0 + return 1 + if(slot_wear_mask) + if(H.wear_mask) + return 0 + if( !(I.slot_flags & SLOT_MASK) ) + return 0 + return 1 + if(slot_back) + if(H.back) + return 0 + if( !(I.slot_flags & SLOT_BACK) ) + return 0 + return 1 + if(slot_wear_suit) + if(H.wear_suit) + return 0 + if( !(I.slot_flags & SLOT_OCLOTHING) ) + return 0 + return 1 + if(slot_gloves) + if(H.gloves) + return 0 + if( !(I.slot_flags & SLOT_GLOVES) ) + return 0 + return 1 + if(slot_shoes) + if(H.shoes) + return 0 + if( !(I.slot_flags & SLOT_FEET) ) + return 0 + return 1 + if(slot_belt) + if(H.belt) + return 0 + if(!H.w_uniform && !nojumpsuit) + if(!disable_warning) + H << "You need a jumpsuit before you can attach this [I.name]." + return 0 + if( !(I.slot_flags & SLOT_BELT) ) + return + return 1 + if(slot_glasses) + if(H.glasses) + return 0 + if( !(I.slot_flags & SLOT_EYES) ) + return 0 + return 1 + if(slot_head) + if(H.head) + return 0 + if( !(I.slot_flags & SLOT_HEAD) ) + return 0 + return 1 + if(slot_ears) + if(H.ears) + return 0 + if( !(I.slot_flags & SLOT_EARS) ) + return 0 + return 1 + if(slot_w_uniform) + if(H.w_uniform) + return 0 + if( !(I.slot_flags & SLOT_ICLOTHING) ) + return 0 + return 1 + if(slot_wear_id) + if(H.wear_id) + return 0 + if(!H.w_uniform && !nojumpsuit) + if(!disable_warning) + H << "You need a jumpsuit before you can attach this [I.name]." + return 0 + if( !(I.slot_flags & SLOT_ID) ) + return 0 + return 1 + if(slot_l_store) + if(I.flags & NODROP) //Pockets aren't visible, so you can't move NODROP items into them. + return 0 + if(H.l_store) + return 0 + if(!H.w_uniform && !nojumpsuit) + if(!disable_warning) + H << "You need a jumpsuit before you can attach this [I.name]." + return 0 + if(I.slot_flags & SLOT_DENYPOCKET) + return + if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) + return 1 + if(slot_r_store) + if(I.flags & NODROP) + return 0 + if(H.r_store) + return 0 + if(!H.w_uniform && !nojumpsuit) + if(!disable_warning) + H << "You need a jumpsuit before you can attach this [I.name]." + return 0 + if(I.slot_flags & SLOT_DENYPOCKET) + return 0 + if( I.w_class <= 2 || (I.slot_flags & SLOT_POCKET) ) + return 1 + return 0 + if(slot_s_store) + if(I.flags & NODROP) + return 0 + if(H.s_store) + return 0 + if(!H.wear_suit) + if(!disable_warning) + H << "You need a suit before you can attach this [I.name]." + return 0 + if(!H.wear_suit.allowed) + if(!disable_warning) + H << "You somehow have a suit with no defined allowed items for suit storage, stop that." + return 0 + if(I.w_class > 4) + if(!disable_warning) + H << "The [I.name] is too big to attach." //should be src? + return 0 + if( istype(I, /obj/item/device/pda) || istype(I, /obj/item/weapon/pen) || is_type_in_list(I, H.wear_suit.allowed) ) + return 1 + return 0 + if(slot_handcuffed) + if(H.handcuffed) + return 0 + if(!istype(I, /obj/item/weapon/handcuffs)) + return 0 + return 1 + if(slot_legcuffed) + if(H.legcuffed) + return 0 + if(!istype(I, /obj/item/weapon/legcuffs)) + return 0 + return 1 + if(slot_in_backpack) + if (H.back && istype(H.back, /obj/item/weapon/storage/backpack)) + var/obj/item/weapon/storage/backpack/B = H.back + if(B.contents.len < B.storage_slots && I.w_class <= B.max_w_class) + return 1 + return 0 + return 0 //Unsupported slot + + proc/before_equip_job(var/datum/job/J, var/mob/living/carbon/human/H) + return + + proc/after_equip_job(var/datum/job/J, var/mob/living/carbon/human/H) + return + + proc/handle_chemicals(var/datum/reagent/chem, var/mob/living/carbon/human/H) + return 0 + + proc/handle_speech(var/message, var/mob/living/carbon/human/H) + return message + + //////// + //LIFE// + //////// + + proc/handle_chemicals_in_body(var/mob/living/carbon/human/H) + if(H.reagents) H.reagents.metabolize(H) + + //The fucking FAT mutation is the dumbest shit ever. It makes the code so difficult to work with + if(FAT in H.mutations) + if(H.overeatduration < 100) + H << "You feel fit again!" + H.mutations -= FAT + H.update_inv_w_uniform(0) + H.update_inv_wear_suit() + else + if(H.overeatduration > 500) + H << "You suddenly feel blubbery!" + H.mutations |= FAT + H.update_inv_w_uniform(0) + H.update_inv_wear_suit() + + // nutrition decrease + if (H.nutrition > 0 && H.stat != 2) + H.nutrition = max (0, H.nutrition - HUNGER_FACTOR) + + if (H.nutrition > 450) + if(H.overeatduration < 600) //capped so people don't take forever to unfat + H.overeatduration++ + else + if(H.overeatduration > 1) + H.overeatduration -= 2 //doubled the unfat rate + + if (H.drowsyness) + H.drowsyness-- + H.eye_blurry = max(2, H.eye_blurry) + if (prob(5)) + H.sleeping += 1 + H.Paralyse(5) + + H.confused = max(0, H.confused - 1) + // decrement dizziness counter, clamped to 0 + if(H.resting) + H.dizziness = max(0, H.dizziness - 15) + H.jitteriness = max(0, H.jitteriness - 15) + else + H.dizziness = max(0, H.dizziness - 3) + H.jitteriness = max(0, H.jitteriness - 3) + + H.updatehealth() + + return + + proc/handle_vision(var/mob/living/carbon/human/H) + if( H.stat == DEAD ) + H.sight |= (SEE_TURFS|SEE_MOBS|SEE_OBJS) + H.see_in_dark = 8 + if(!H.druggy) H.see_invisible = SEE_INVISIBLE_LEVEL_TWO + else + H.sight &= ~(SEE_TURFS|SEE_MOBS|SEE_OBJS) + var/see_temp = H.see_invisible + H.see_invisible = invis_sight + H.see_in_dark = darksight + + if(XRAY in H.mutations) + H.sight |= SEE_TURFS|SEE_MOBS|SEE_OBJS + H.see_in_dark = 8 + H.see_invisible = SEE_INVISIBLE_LEVEL_TWO + + if(H.seer) + H.see_invisible = SEE_INVISIBLE_OBSERVER + + if(H.mind && H.mind.changeling) + H.hud_used.lingchemdisplay.invisibility = 0 + H.hud_used.lingchemdisplay.maptext = "
[H.mind.changeling.chem_charges]
" + else + H.hud_used.lingchemdisplay.invisibility = 101 + + if(istype(H.wear_mask, /obj/item/clothing/mask/gas/voice/space_ninja)) + var/obj/item/clothing/mask/gas/voice/space_ninja/O = H.wear_mask + switch(O.mode) + if(0) + var/target_list[] = list() + for(var/mob/living/target in oview(H)) + if( target.mind&&(target.mind.special_role||issilicon(target)) )//They need to have a mind. + target_list += target + if(target_list.len)//Everything else is handled by the ninja mask proc. + O.assess_targets(target_list, H) + H.see_invisible = SEE_INVISIBLE_LIVING + if(1) + H.see_in_dark = 5 + H.see_invisible = SEE_INVISIBLE_LIVING + if(2) + H.sight |= SEE_MOBS + H.see_invisible = SEE_INVISIBLE_LEVEL_TWO + if(3) + H.sight |= SEE_TURFS + H.see_invisible = SEE_INVISIBLE_LIVING + + if(H.glasses) + if(istype(H.glasses, /obj/item/clothing/glasses)) + var/obj/item/clothing/glasses/G = H.glasses + H.sight |= G.vision_flags + H.see_in_dark = G.darkness_view + H.see_invisible = G.invis_view + if(G.hud) + G.process_hud(H) + + if(H.druggy) //Override for druggy + H.see_invisible = see_temp + + if(H.see_override) //Override all + H.see_invisible = H.see_override + + // This checks how much the mob's eyewear impairs their vision + if(H.tinttotal >= TINT_IMPAIR) + if(tinted_weldhelh) + if(H.tinttotal >= TINT_BLIND) + H.blinded = 1 // You get the sudden urge to learn to play keyboard + H.client.screen += global_hud.darkMask + else + H.client.screen += global_hud.darkMask + + if(H.blind) + if(H.blinded) H.blind.layer = 18 + else H.blind.layer = 0 + + if( H.disabilities & NEARSIGHTED && !istype(H.glasses, /obj/item/clothing/glasses/regular) ) + H.client.screen += global_hud.vimpaired + if(H.eye_blurry) H.client.screen += global_hud.blurry + if(H.druggy) H.client.screen += global_hud.druggy + + + if(H.eye_stat > 20) + if(H.eye_stat > 30) H.client.screen += global_hud.darkMask + else H.client.screen += global_hud.vimpaired + + return 1 + + proc/handle_hud_icons(var/mob/living/carbon/human/H) + if(H.healths) + if(H.stat == DEAD) + H.healths.icon_state = "health7" + else + switch(H.hal_screwyhud) + if(1) H.healths.icon_state = "health6" + if(2) H.healths.icon_state = "health7" + else + switch(H.health - H.staminaloss) + if(100 to INFINITY) H.healths.icon_state = "health0" + if(80 to 100) H.healths.icon_state = "health1" + if(60 to 80) H.healths.icon_state = "health2" + if(40 to 60) H.healths.icon_state = "health3" + if(20 to 40) H.healths.icon_state = "health4" + if(0 to 20) H.healths.icon_state = "health5" + else H.healths.icon_state = "health6" + + if(H.nutrition_icon) + switch(H.nutrition) + if(450 to INFINITY) H.nutrition_icon.icon_state = "nutrition0" + if(350 to 450) H.nutrition_icon.icon_state = "nutrition1" + if(250 to 350) H.nutrition_icon.icon_state = "nutrition2" + if(150 to 250) H.nutrition_icon.icon_state = "nutrition3" + else H.nutrition_icon.icon_state = "nutrition4" + + if(H.pressure) + H.pressure.icon_state = "pressure[H.pressure_alert]" + + if(H.pullin) + if(H.pulling) H.pullin.icon_state = "pull" + else H.pullin.icon_state = "pull0" +// if(rest) //Not used with new UI +// if(resting || lying || sleeping) rest.icon_state = "rest1" +// else rest.icon_state = "rest0" + if(H.toxin) + if(H.hal_screwyhud == 4 || H.toxins_alert) H.toxin.icon_state = "tox1" + else H.toxin.icon_state = "tox0" + if(H.oxygen) + if(H.hal_screwyhud == 3 || H.oxygen_alert) H.oxygen.icon_state = "oxy1" + else H.oxygen.icon_state = "oxy0" + if(H.fire) + if(H.fire_alert) H.fire.icon_state = "fire[H.fire_alert]" //fire_alert is either 0 if no alert, 1 for cold and 2 for heat. + else H.fire.icon_state = "fire0" + + if(H.bodytemp) + if(!(HEATRES in specflags)) + switch(H.bodytemperature) //310.055 optimal body temp + if(370 to INFINITY) H.bodytemp.icon_state = "temp4" + if(350 to 370) H.bodytemp.icon_state = "temp3" + if(335 to 350) H.bodytemp.icon_state = "temp2" + switch(H.bodytemperature) + if(320 to 335) H.bodytemp.icon_state = "temp1" + if(300 to 320) H.bodytemp.icon_state = "temp0" + if(295 to 300) H.bodytemp.icon_state = "temp-1" + if(!(COLDRES in specflags)) + switch(H.bodytemperature) + if(280 to 295) H.bodytemp.icon_state = "temp-2" + if(260 to 280) H.bodytemp.icon_state = "temp-3" + if(-INFINITY to 260) H.bodytemp.icon_state = "temp-4" + + return 1 + + proc/handle_mutations_and_radiation(var/mob/living/carbon/human/H) + if(H.getFireLoss()) + if((COLD_RESISTANCE in H.mutations) || (prob(1))) + H.heal_organ_damage(0,1) + + if ((HULK in H.mutations) && H.health <= 25) + H.mutations.Remove(HULK) + H.update_mutations() //update our mutation overlays + H << "You suddenly feel very weak." + H.Weaken(3) + H.emote("collapse") + + if (H.radiation && !(RADIMMUNE in specflags)) + if (H.radiation > 100) + H.radiation = 100 + H.Weaken(10) + H << "You feel weak." + H.emote("collapse") + + if (H.radiation < 0) + H.radiation = 0 + + else + switch(H.radiation) + if(1 to 49) + H.radiation-- + if(prob(25)) + H.adjustToxLoss(1) + H.updatehealth() + + if(50 to 74) + H.radiation -= 2 + H.adjustToxLoss(1) + if(prob(5)) + H.radiation -= 5 + H.Weaken(3) + H << "You feel weak." + H.emote("collapse") + if(prob(15)) + if(!( H.hair_style == "Shaved") || !(H.hair_style == "Bald") || HAIR in specflags) + H << "Your hair starts to fall out in clumps..." + spawn(50) + H.facial_hair_style = "Shaved" + H.hair_style = "Bald" + H.update_hair() + H.updatehealth() + + if(75 to 100) + H.radiation -= 3 + H.adjustToxLoss(3) + if(prob(1)) + H << "You mutate!" + randmutb(H) + domutcheck(H,null) + H.emote("gasp") + H.updatehealth() + + //////////////// + // MOVE SPEED // + //////////////// + + proc/movement_delay(var/mob/living/carbon/human/H) + var/mspeed = 0 + + if(!has_gravity(H)) + return -1 //It's hard to be slowed down in space by... anything + else if(H.status_flags & GOTTAGOFAST) + return -1 + + mspeed = 0 + var/health_deficiency = (100 - H.health + H.staminaloss) + if(health_deficiency >= 40) + mspeed += (health_deficiency / 25) + + var/hungry = (500 - H.nutrition) / 5 //So overeat would be 100 and default level would be 80 + if(hungry >= 70) + mspeed += hungry / 50 + + if(H.wear_suit) + mspeed += H.wear_suit.slowdown + if(H.shoes) + mspeed += H.shoes.slowdown + if(H.back) + mspeed += H.back.slowdown + + if(FAT in H.mutations) + mspeed += 1.5 + if(H.bodytemperature < 283.222) + mspeed += (283.222 - H.bodytemperature) / 10 * 1.75 + + mspeed += speedmod + + return mspeed + + ////////////////// + // ATTACK PROCS // + ////////////////// + + proc/spec_attack_hand(var/mob/living/carbon/human/M, var/mob/living/carbon/human/H) + if((M != H) && H.check_shields(0, M.name)) + add_logs(M, H, "attempted to touch") + H.visible_message("[M] attempted to touch [H]!") + return 0 + + switch(M.a_intent) + if("help") + if(H.health >= 0) + H.help_shake_act(M) + if(H != M) + add_logs(M, H, "shaked") + return 1 + + //CPR + if((M.head && (M.head.flags & HEADCOVERSMOUTH)) || (M.wear_mask && (M.wear_mask.flags & MASKCOVERSMOUTH))) + M << "Remove your mask!" + return 0 + if((H.head && (H.head.flags & HEADCOVERSMOUTH)) || (H.wear_mask && (H.wear_mask.flags & MASKCOVERSMOUTH))) + M << "Remove their mask!" + return 0 + + if(H.cpr_time < world.time + 30) + add_logs(H, M, "CPRed") + H.visible_message("[M] is trying to perform CPR on [H]!") + if(!do_mob(M, H)) + return 0 + if((H.health >= -99 && H.health <= 0)) + H.cpr_time = world.time + var/suff = min(H.getOxyLoss(), 7) + H.adjustOxyLoss(-suff) + H.updatehealth() + M.visible_message("[M] performs CPR on [H]!") + H << "You feel a breath of fresh air enter your lungs. It feels good." + + if("grab") + if(M == H || H.anchored) + return 0 + + add_logs(M, H, "grabbed", addition="passively") + + if(H.w_uniform) + H.w_uniform.add_fingerprint(M) + + var/obj/item/weapon/grab/G = new /obj/item/weapon/grab(M, H) + if(H.buckled) + M << "You cannot grab [H], \he is buckled in!" + if(!G) //the grab will delete itself in New if affecting is anchored + return + M.put_in_active_hand(G) + G.synch() + H.LAssailant = M + + playsound(H.loc, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + H.visible_message("[M] has grabbed [H] passively!") + return 1 + + if("harm") + add_logs(M, H, "punched") + + var/atk_verb = "punch" + if(H.lying) + atk_verb = "kick" + else if(M.dna) + atk_verb = M.dna.species.attack_verb + + var/damage = rand(0, 9) + damage += punchmod + + if(!damage) + if(M.dna) + playsound(H.loc, M.dna.species.miss_sound, 25, 1, -1) + else + playsound(H.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + + H.visible_message("[M] has attempted to [atk_verb] [H]!") + return 0 + + + var/obj/item/organ/limb/affecting = H.get_organ(ran_zone(M.zone_sel.selecting)) + var/armor_block = H.run_armor_check(affecting, "melee") + + if(HULK in M.mutations) + damage += 5 + + if(M.dna) + playsound(H.loc, M.dna.species.attack_sound, 25, 1, -1) + else + playsound(H.loc, 'sound/weapons/punch1.ogg', 25, 1, -1) + + + H.visible_message("[M] has [atk_verb]ed [H]!", \ + "[M] has [atk_verb]ed [H]!") + + H.apply_damage(damage, BRUTE, affecting, armor_block) + if((H.stat != DEAD) && damage >= 9) + H.visible_message("[M] has weakened [H]!", \ + "[M] has weakened [H]!") + H.apply_effect(4, WEAKEN, armor_block) + H.forcesay(hit_appends) + else if(H.lying) + H.forcesay(hit_appends) + + if("disarm") + add_logs(M, H, "disarmed") + + if(H.w_uniform) + H.w_uniform.add_fingerprint(M) + var/obj/item/organ/limb/affecting = H.get_organ(ran_zone(M.zone_sel.selecting)) + var/randn = rand(1, 100) + if(randn <= 25) + H.apply_effect(2, WEAKEN, H.run_armor_check(affecting, "melee")) + playsound(H, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + H.visible_message("[M] has pushed [H]!", + "[M] has pushed [H]!") + H.forcesay(hit_appends) + return + + var/talked = 0 // BubbleWrap + + if(randn <= 60) + //BubbleWrap: Disarming breaks a pull + if(H.pulling) + H.visible_message("[M] has broken [H]'s grip on [H.pulling]!") + talked = 1 + H.stop_pulling() + + //BubbleWrap: Disarming also breaks a grab - this will also stop someone being choked, won't it? + if(istype(H.l_hand, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/lgrab = H.l_hand + if(lgrab.affecting) + H.visible_message("[M] has broken [H]'s grip on [lgrab.affecting]!") + talked = 1 + spawn(1) + qdel(lgrab) + if(istype(H.r_hand, /obj/item/weapon/grab)) + var/obj/item/weapon/grab/rgrab = H.r_hand + if(rgrab.affecting) + H.visible_message("[M] has broken [H]'s grip on [rgrab.affecting]!") + talked = 1 + spawn(1) + qdel(rgrab) + //End BubbleWrap + + if(!talked) //BubbleWrap + if(H.drop_item()) + H.visible_message("[M] has disarmed [H]!", \ + "[M] has disarmed [H]!") + playsound(H, 'sound/weapons/thudswoosh.ogg', 50, 1, -1) + return + + + playsound(H, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + H.visible_message("[M] attempted to disarm [H]!", \ + "[M] attemped to disarm [H]!") + return + + proc/spec_attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone, var/obj/item/organ/limb/affecting, var/hit_area, var/intent, var/obj/item/organ/limb/target_limb, target_area, var/mob/living/carbon/human/H) + // Allows you to put in item-specific reactions based on species + if((user != H) && H.check_shields(I.force, "the [I.name]")) + return 0 + + if(I.attack_verb && I.attack_verb.len) + H.visible_message("[H] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!", \ + "[H] has been [pick(I.attack_verb)] in the [hit_area] with [I] by [user]!") + else if(I.force) + H.visible_message("[H] has been attacked in the [hit_area] with [I] by [user]!", \ + "[H] has been attacked in the [hit_area] with [I] by [user]!") + else + return 0 + + var/armor = H.run_armor_check(affecting, "melee", "Your armour has protected your [hit_area].", "Your armour has softened a hit to your [hit_area].") + if(armor >= 100) return 0 + var/Iforce = I.force //to avoid runtimes on the forcesay checks at the bottom. Some items might delete themselves if you drop them. (stunning yourself, ninja swords) + + apply_damage(I.force, I.damtype, affecting, armor, H) + + var/bloody = 0 + if(((I.damtype == BRUTE) && prob(25 + (I.force * 2)))) + if(affecting.status == ORGAN_ORGANIC) + I.add_blood(H) //Make the weapon bloody, not the person. + if(prob(I.force * 2)) //blood spatter! + bloody = 1 + var/turf/location = H.loc + if(istype(location, /turf/simulated)) + location.add_blood(H) + if(get_dist(H, H) <= 1) //people with TK won't get smeared with blood + if(H.wear_suit) + H.wear_suit.add_blood(H) + H.update_inv_wear_suit(0) //updates mob overlays to show the new blood (no refresh) + else if(H.w_uniform) + H.w_uniform.add_blood(H) + H.update_inv_w_uniform(0) //updates mob overlays to show the new blood (no refresh) + if (H.gloves) + var/obj/item/clothing/gloves/G = H.gloves + G.add_blood(H) + else + H.add_blood(H) + H.update_inv_gloves() //updates on-mob overlays for bloody hands and/or bloody gloves + + + switch(hit_area) + if("head") //Harder to score a stun but if you do it lasts a bit longer + if(H.stat == CONSCIOUS && prob(I.force) && armor < 50) + H.visible_message("[H] has been knocked unconscious!", \ + "[H] has been knocked unconscious!") + H.apply_effect(20, PARALYZE, armor) + if(H != user && I.damtype == BRUTE) + ticker.mode.remove_revolutionary(H.mind) + + if(bloody) //Apply blood + if(H.wear_mask) + H.wear_mask.add_blood(H) + H.update_inv_wear_mask(0) + if(H.head) + H.head.add_blood(H) + H.update_inv_head(0) + if(H.glasses && prob(33)) + H.glasses.add_blood(H) + H.update_inv_glasses(0) + + if("chest") //Easier to score a stun but lasts less time + if(H.stat == CONSCIOUS && prob(I.force + 10)) + H.visible_message("[H] has been knocked down!", \ + "[H] has been knocked down!") + H.apply_effect(5, WEAKEN, armor) + + if(bloody) + if(H.wear_suit) + H.wear_suit.add_blood(H) + H.update_inv_wear_suit(0) + if(H.w_uniform) + H.w_uniform.add_blood(H) + H.update_inv_w_uniform(0) + + if(Iforce > 10 || Iforce >= 5 && prob(33)) + H.forcesay(hit_appends) //forcesay checks stat already. + return + + proc/attacked_by(var/obj/item/I, var/mob/living/user, var/def_zone, var/mob/living/carbon/human/H) + H.apply_damage(I.force, I.damtype) + if(I.damtype == "brute") + if(prob(33) && I.force && !(NOBLOOD in specflags)) + var/turf/location = H.loc + if(istype(location, /turf/simulated)) + location.add_blood_floor(H) + + var/showname = "." + if(user) + showname = " by [user]!" + if(!(user in viewers(I, null))) + showname = "." + + if(I.attack_verb && I.attack_verb.len) + H.visible_message("[H] has been [pick(I.attack_verb)] with [I][showname]", + "[H] has been [pick(I.attack_verb)] with [I][showname]") + else if(I.force) + H.visible_message("[H] has been attacked with [I][showname]", + "[H] has been attacked with [I][showname]") + if(!showname && user) + if(user.client) + user << "You attack [H] with [I]. " + + return + + proc/apply_damage(var/damage, var/damagetype = BRUTE, var/def_zone = null, var/blocked, var/mob/living/carbon/human/H) + blocked = (100-(blocked+armor))/100 + if(blocked <= 0) return 0 + + var/obj/item/organ/limb/organ = null + if(isorgan(def_zone)) + organ = def_zone + else + if(!def_zone) def_zone = ran_zone(def_zone) + organ = H.get_organ(check_zone(def_zone)) + if(!organ) return 0 + + damage = (damage * blocked) + + switch(damagetype) + if(BRUTE) + H.damageoverlaytemp = 20 + if(organ.take_damage(damage*brutemod, 0)) + H.update_damage_overlays(0) + if(BURN) + H.damageoverlaytemp = 20 + if(organ.take_damage(0, damage*burnmod)) + H.update_damage_overlays(0) + if(TOX) + H.adjustToxLoss(damage * blocked) + if(OXY) + H.adjustOxyLoss(damage * blocked) + if(CLONE) + H.adjustCloneLoss(damage * blocked) + if(STAMINA) + H.adjustStaminaLoss(damage * blocked) + + proc/on_hit(var/obj/item/projectile/proj_type, var/mob/living/carbon/human/H) + // called when hit by a projectile + switch(proj_type) + if(/obj/item/projectile/energy/floramut) // overwritten by plants/pods + H.show_message("The radiation beam dissipates harmlessly through your body.") + if(/obj/item/projectile/energy/florayield) + H.show_message("The radiation beam dissipates harmlessly through your body.") + return + + ///////////// + //BREATHING// + ///////////// + + proc/breathe(var/mob/living/carbon/human/H) + if(H.reagents.has_reagent("lexorin")) return + if(istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) return + + var/datum/gas_mixture/environment = H.loc.return_air() + var/datum/gas_mixture/breath + // HACK NEED CHANGING LATER + if(H.health <= config.health_threshold_crit) + H.losebreath++ + + if(H.losebreath>0) //Suffocating so do not take a breath + H.losebreath-- + if (prob(10)) //Gasp per 10 ticks? Sounds about right. + spawn H.emote("gasp") + if(istype(H.loc, /obj/)) + var/obj/location_as_object = H.loc + location_as_object.handle_internal_lifeform(H, 0) + else + //First, check for air from internal atmosphere (using an air tank and mask generally) + breath = H.get_breath_from_internal(BREATH_VOLUME) // Super hacky -- TLE + //breath = get_breath_from_internal(0.5) // Manually setting to old BREATH_VOLUME amount -- TLE + + //No breath from internal atmosphere so get breath from location + if(!breath) + if(isobj(H.loc)) + var/obj/location_as_object = H.loc + breath = location_as_object.handle_internal_lifeform(H, BREATH_VOLUME) + else if(isturf(H.loc)) + var/breath_moles = 0 + /*if(environment.return_pressure() > ONE_ATMOSPHERE) + // Loads of air around (pressure effect will be handled elsewhere), so lets just take a enough to fill our lungs at normal atmos pressure (using n = Pv/RT) + breath_moles = (ONE_ATMOSPHERE*BREATH_VOLUME/R_IDEAL_GAS_EQUATION*environment.temperature) + else*/ + // Not enough air around, take a percentage of what's there to model this properly + breath_moles = environment.total_moles()*BREATH_PERCENTAGE + + breath = H.loc.remove_air(breath_moles) + // Handle chem smoke effect -- Doohl + var/block = 0 + if(H.wear_mask) + if(H.wear_mask.flags & BLOCK_GAS_SMOKE_EFFECT) + block = 1 + if(H.glasses) + if(H.glasses.flags & BLOCK_GAS_SMOKE_EFFECT) + block = 1 + if(H.head) + if(H.head.flags & BLOCK_GAS_SMOKE_EFFECT) + block = 1 + + if(!block) + + for(var/obj/effect/effect/chem_smoke/smoke in view(1, H)) + if(smoke.reagents.total_volume) + smoke.reagents.reaction(H, INGEST) + spawn(5) + if(smoke) + smoke.reagents.copy_to(H, 10) // I dunno, maybe the reagents enter the blood stream through the lungs? + break // If they breathe in the nasty stuff once, no need to continue checking + + else //Still give containing object the chance to interact + if(istype(H.loc, /obj/)) + var/obj/location_as_object = H.loc + location_as_object.handle_internal_lifeform(H, 0) + + handle_breath(breath, H) + + if(breath) + H.loc.assume_air(breath) + + proc/handle_breath(datum/gas_mixture/breath, var/mob/living/carbon/human/H) + if((H.status_flags & GODMODE)) + return + + if(!breath || (breath.total_moles() == 0) || H.suiciding) + if(H.reagents.has_reagent("inaprovaline")) + return + if(H.suiciding) + H.adjustOxyLoss(2)//If you are suiciding, you should die a little bit faster + H.failed_last_breath = 1 + H.oxygen_alert = max(H.oxygen_alert, 1) + return 0 + if(H.health >= config.health_threshold_crit) + if(NOBREATH in specflags) return 1 + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.failed_last_breath = 1 + else + H.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS) + H.failed_last_breath = 1 + + H.oxygen_alert = max(H.oxygen_alert, 1) + + return 0 + + var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa + //var/safe_oxygen_max = 140 // Maximum safe partial pressure of O2, in kPa (Not used for now) + var/safe_co2_max = 10 // Yes it's an arbitrary value who cares? + var/safe_toxins_max = 0.005 + var/SA_para_min = 1 + var/SA_sleep_min = 5 + var/oxygen_used = 0 + var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME + + //Partial pressure of the O2 in our breath + var/O2_pp = (breath.oxygen/breath.total_moles())*breath_pressure + // Same, but for the toxins + var/Toxins_pp = (breath.toxins/breath.total_moles())*breath_pressure + // And CO2, lets say a PP of more than 10 will be bad (It's a little less really, but eh, being passed out all round aint no fun) + var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*breath_pressure // Tweaking to fit the hacky bullshit I've done with atmo -- TLE + //var/CO2_pp = (breath.carbon_dioxide/breath.total_moles())*0.5 // The default pressure value + + if(O2_pp < safe_oxygen_min) // Too little oxygen + if(!(NOBREATH in specflags) || (H.health <= config.health_threshold_crit)) + if(prob(20)) + spawn(0) H.emote("gasp") + if(O2_pp > 0) + var/ratio = safe_oxygen_min/O2_pp + H.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!) + H.failed_last_breath = 1 + oxygen_used = breath.oxygen*ratio/6 + else + H.adjustOxyLoss(HUMAN_MAX_OXYLOSS) + H.failed_last_breath = 1 + H.oxygen_alert = max(H.oxygen_alert, 1) + /*else if (O2_pp > safe_oxygen_max) // Too much oxygen (commented this out for now, I'll deal with pressure damage elsewhere I suppose) + spawn(0) emote("cough") + var/ratio = O2_pp/safe_oxygen_max + oxyloss += 5*ratio + oxygen_used = breath.oxygen*ratio/6 + oxygen_alert = max(oxygen_alert, 1)*/ + else // We're in safe limits + H.failed_last_breath = 0 + H.adjustOxyLoss(-5) + oxygen_used = breath.oxygen/6 + H.oxygen_alert = 0 + + breath.oxygen -= oxygen_used + breath.carbon_dioxide += oxygen_used + + //CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick. + if(CO2_pp > safe_co2_max && !(NOBREATH in specflags)) + if(!H.co2overloadtime) // If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so. + H.co2overloadtime = world.time + else if(world.time - H.co2overloadtime > 120) + H.Paralyse(3) + H.adjustOxyLoss(3) // Lets hurt em a little, let them know we mean business + if(world.time - H.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good! + H.adjustOxyLoss(8) + if(prob(20)) // Lets give them some chance to know somethings not right though I guess. + spawn(0) H.emote("cough") + + else + H.co2overloadtime = 0 + + if(Toxins_pp > safe_toxins_max && !(NOBREATH in specflags)) // Too much toxins + var/ratio = (breath.toxins/safe_toxins_max) * 10 + //adjustToxLoss(Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) //Limit amount of damage toxin exposure can do per second + if(H.reagents) + H.reagents.add_reagent("plasma", Clamp(ratio, MIN_PLASMA_DAMAGE, MAX_PLASMA_DAMAGE)) + H.toxins_alert = max(H.toxins_alert, 1) + else + H.toxins_alert = 0 + + if(breath.trace_gases.len && !(NOBREATH in specflags)) // If there's some other shit in the air lets deal with it here. + for(var/datum/gas/sleeping_agent/SA in breath.trace_gases) + var/SA_pp = (SA.moles/breath.total_moles())*breath_pressure + if(SA_pp > SA_para_min) // Enough to make us paralysed for a bit + H.Paralyse(3) // 3 gives them one second to wake up and run away a bit! + if(SA_pp > SA_sleep_min) // Enough to make us sleep as well + H.sleeping = max(H.sleeping+2, 10) + else if(SA_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning + if(prob(20)) + spawn(0) H.emote(pick("giggle", "laugh")) + + handle_temperature(breath) + + return 1 + + proc/handle_temperature(datum/gas_mixture/breath, var/mob/living/carbon/human/H) // called by human/life, handles temperatures + if( (abs(310.15 - breath.temperature) > 50) && !(COLD_RESISTANCE in H.mutations) && !(COLDRES in specflags)) // Hot air hurts :( + if(breath.temperature < 260.15) + if(prob(20)) + H << "You feel your face freezing and an icicle forming in your lungs!" + else if(breath.temperature > 360.15 && !(HEATRES in specflags)) + if(prob(20)) + H << "You feel your face burning and a searing heat in your lungs!" + + if(!(COLDRES in specflags)) // COLD DAMAGE + switch(breath.temperature) + if(-INFINITY to 120) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_3, BURN, "head") + H.fire_alert = max(H.fire_alert, 1) + if(120 to 200) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_2, BURN, "head") + H.fire_alert = max(H.fire_alert, 1) + if(200 to 260) + H.apply_damage(COLD_GAS_DAMAGE_LEVEL_1, BURN, "head") + H.fire_alert = max(H.fire_alert, 1) + + if(!(HEATRES in specflags)) // HEAT DAMAGE + switch(breath.temperature) + if(360 to 400) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_1, BURN, "head") + H.fire_alert = max(H.fire_alert, 2) + if(400 to 1000) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_2, BURN, "head") + H.fire_alert = max(H.fire_alert, 2) + if(1000 to INFINITY) + H.apply_damage(HEAT_GAS_DAMAGE_LEVEL_3, BURN, "head") + H.fire_alert = max(H.fire_alert, 2) + + return + + proc/handle_environment(datum/gas_mixture/environment, var/mob/living/carbon/human/H) + if(!environment) + return + + var/loc_temp = H.get_temperature(environment) + //world << "Loc temp: [loc_temp] - Body temp: [bodytemperature] - Fireloss: [getFireLoss()] - Thermal protection: [get_thermal_protection()] - Fire protection: [thermal_protection + add_fire_protection(loc_temp)] - Heat capacity: [environment_heat_capacity] - Location: [loc] - src: [src]" + + //Body temperature is adjusted in two steps. Firstly your body tries to stabilize itself a bit. + if(H.stat != 2) + H.stabilize_temperature_from_calories() + + //After then, it reacts to the surrounding atmosphere based on your thermal protection + if(!H.on_fire) //If you're on fire, you do not heat up or cool down based on surrounding gases + if(loc_temp < H.bodytemperature) + //Place is colder than we are + var/thermal_protection = H.get_cold_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + H.bodytemperature += min((1-thermal_protection) * ((loc_temp - H.bodytemperature) / BODYTEMP_COLD_DIVISOR), BODYTEMP_COOLING_MAX) + else + //Place is hotter than we are + var/thermal_protection = H.get_heat_protection(loc_temp) //This returns a 0 - 1 value, which corresponds to the percentage of protection based on what you're wearing and what you're exposed to. + if(thermal_protection < 1) + H.bodytemperature += min((1-thermal_protection) * ((loc_temp - H.bodytemperature) / BODYTEMP_HEAT_DIVISOR), BODYTEMP_HEATING_MAX) + + // +/- 50 degrees from 310.15K is the 'safe' zone, where no damage is dealt. + if(H.bodytemperature > BODYTEMP_HEAT_DAMAGE_LIMIT && !(HEATRES in specflags)) + //Body temperature is too hot. + H.fire_alert = max(H.fire_alert, 1) + switch(H.bodytemperature) + if(360 to 400) + H.apply_damage(HEAT_DAMAGE_LEVEL_1*heatmod, BURN) + H.fire_alert = max(H.fire_alert, 2) + if(400 to 460) + H.apply_damage(HEAT_DAMAGE_LEVEL_2*heatmod, BURN) + H.fire_alert = max(H.fire_alert, 2) + if(460 to INFINITY) + if(H.on_fire) + H.apply_damage(HEAT_DAMAGE_LEVEL_3*heatmod, BURN) + H.fire_alert = max(H.fire_alert, 2) + else + H.apply_damage(HEAT_DAMAGE_LEVEL_2*heatmod, BURN) + H.fire_alert = max(H.fire_alert, 2) + + else if(H.bodytemperature < BODYTEMP_COLD_DAMAGE_LIMIT && !(COLDRES in specflags)) + H.fire_alert = max(H.fire_alert, 1) + if(!istype(H.loc, /obj/machinery/atmospherics/unary/cryo_cell)) + switch(H.bodytemperature) + if(200 to 260) + H.apply_damage(COLD_DAMAGE_LEVEL_1*coldmod, BURN) + H.fire_alert = max(H.fire_alert, 1) + if(120 to 200) + H.apply_damage(COLD_DAMAGE_LEVEL_2*coldmod, BURN) + H.fire_alert = max(H.fire_alert, 1) + if(-INFINITY to 120) + H.apply_damage(COLD_DAMAGE_LEVEL_3*coldmod, BURN) + H.fire_alert = max(H.fire_alert, 1) + + // Account for massive pressure differences. Done by Polymorph + // Made it possible to actually have something that can protect against high pressure... Done by Errorage. Polymorph now has an axe sticking from his head for his previous hardcoded nonsense! + + var/pressure = environment.return_pressure() + var/adjusted_pressure = H.calculate_affecting_pressure(pressure) //Returns how much pressure actually affects the mob. + switch(adjusted_pressure) + if(HAZARD_HIGH_PRESSURE to INFINITY) + if(!(HEATRES in specflags)) + H.adjustBruteLoss( min( ( (adjusted_pressure / HAZARD_HIGH_PRESSURE) -1 )*PRESSURE_DAMAGE_COEFFICIENT , MAX_HIGH_PRESSURE_DAMAGE) ) + H.pressure_alert = 2 + else + H.pressure_alert = 1 + if(WARNING_HIGH_PRESSURE to HAZARD_HIGH_PRESSURE) + H.pressure_alert = 1 + if(WARNING_LOW_PRESSURE to WARNING_HIGH_PRESSURE) + H.pressure_alert = 0 + if(HAZARD_LOW_PRESSURE to WARNING_LOW_PRESSURE) + H.pressure_alert = -1 + else + if((COLD_RESISTANCE in H.mutations) || (COLDRES in specflags)) + H.pressure_alert = -1 + else + H.adjustBruteLoss( LOW_PRESSURE_DAMAGE ) + H.pressure_alert = -2 + + return + + ////////// + // FIRE // + ////////// + + proc/handle_fire(var/mob/living/carbon/human/H) + if((HEATRES in specflags) || (NOFIRE in specflags)) + return + if(H.fire_stacks < 0) + H.fire_stacks++ //If we've doused ourselves in water to avoid fire, dry off slowly + H.fire_stacks = min(0, H.fire_stacks)//So we dry ourselves back to default, nonflammable. + if(!H.on_fire) + return + var/datum/gas_mixture/G = H.loc.return_air() // Check if we're standing in an oxygenless environment + if(G.oxygen < 1) + ExtinguishMob() //If there's no oxygen in the tile we're on, put out the fire + return + var/turf/location = get_turf(H) + location.hotspot_expose(700, 50, 1) + + proc/IgniteMob(var/mob/living/carbon/human/H) + if(H.fire_stacks > 0 && !H.on_fire && !(HEATRES in specflags) && !(NOFIRE in specflags)) + H.on_fire = 1 + H.AddLuminosity(3) + H.update_fire() + + proc/ExtinguishMob(var/mob/living/carbon/human/H) + if(H.on_fire) + H.on_fire = 0 + H.fire_stacks = 0 + H.AddLuminosity(-3) + H.update_fire() + +#undef SPECIES_LAYER +#undef BODY_LAYER +#undef HAIR_LAYER + +#undef HUMAN_MAX_OXYLOSS +#undef HUMAN_CRIT_MAX_OXYLOSS + +#undef HEAT_DAMAGE_LEVEL_1 +#undef HEAT_DAMAGE_LEVEL_2 +#undef HEAT_DAMAGE_LEVEL_3 + +#undef COLD_DAMAGE_LEVEL_1 +#undef COLD_DAMAGE_LEVEL_2 +#undef COLD_DAMAGE_LEVEL_3 + +#undef HEAT_GAS_DAMAGE_LEVEL_1 +#undef HEAT_GAS_DAMAGE_LEVEL_2 +#undef HEAT_GAS_DAMAGE_LEVEL_3 + +#undef COLD_GAS_DAMAGE_LEVEL_1 +#undef COLD_GAS_DAMAGE_LEVEL_2 +#undef COLD_GAS_DAMAGE_LEVEL_3 + +#undef TINT_IMPAIR +#undef TINT_BLIND \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/species_types.dm b/code/modules/mob/living/carbon/human/species_types.dm new file mode 100644 index 00000000000..7d159238fc5 --- /dev/null +++ b/code/modules/mob/living/carbon/human/species_types.dm @@ -0,0 +1,221 @@ +/* + HUMANS +*/ + +/datum/species/human + name = "Human" + id = "human" + roundstart = 1 + specflags = list(EYECOLOR,HAIR,FACEHAIR,LIPS) + use_skintones = 1 + +/datum/species/human/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + if(chem.id == "mutationtoxin") + H << "Your flesh rapidly mutates!" + H.dna.species = new /datum/species/slime() + H.regenerate_icons() + H.reagents.del_reagent(chem.type) + return 1 + +/* + LIZARDPEOPLE +*/ + +/datum/species/lizard + // Reptilian humanoids with scaled skin and tails. + name = "Lizardperson" + id = "lizard" + say_mod = "hisses" + default_color = "00FF00" + roundstart = 1 + specflags = list(MUTCOLORS,EYECOLOR,LIPS) + attack_verb = "slash" + attack_sound = 'sound/weapons/slash.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + +/datum/species/lizard/handle_speech(message) + // jesus christ why + if(copytext(message, 1, 2) != "*") + message = replacetext(message, "s", stutter("ss")) + + return message + +/* + PLANTPEOPLE +*/ + +/datum/species/plant + // Creatures made of leaves and plant matter. + name = "Plant" + id = "plant" + default_color = "59CE00" + specflags = list(MUTCOLORS,EYECOLOR) + attack_verb = "slice" + attack_sound = 'sound/weapons/slice.ogg' + miss_sound = 'sound/weapons/slashmiss.ogg' + burnmod = 1.25 + heatmod = 1.5 + +/datum/species/plant/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + if(chem.id == "plantbgone") + H.adjustToxLoss(3) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + return 1 + +/datum/species/plant/on_hit(proj_type, mob/living/carbon/human/H) + switch(proj_type) + if(/obj/item/projectile/energy/floramut) + if(prob(15)) + H.apply_effect((rand(30,80)),IRRADIATE) + H.Weaken(5) + for (var/mob/V in viewers(H)) + V.show_message("[H] writhes in pain as \his vacuoles boil.", 3, "You hear the crunching of leaves.", 2) + if(prob(80)) + randmutb(H) + domutcheck(H,null) + else + randmutg(H) + domutcheck(H,null) + else + H.adjustFireLoss(rand(5,15)) + H.show_message("The radiation beam singes you!") + if(/obj/item/projectile/energy/florayield) + H.nutrition = min(H.nutrition+30, 500) + return + +/* + PODPEOPLE +*/ + +/datum/species/plant/pod + // A mutation caused by a human being ressurected in a revival pod. These regain health in light, and begin to wither in darkness. + name = "Podperson" + id = "pod" + +/datum/species/plant/pod/spec_life(mob/living/carbon/human/H) + var/light_amount = 0 //how much light there is in the place, affects receiving nutrition and healing + if(isturf(H.loc)) //else, there's considered to be no light + var/turf/T = H.loc + var/area/A = T.loc + if(A) + if(A.lighting_use_dynamic) light_amount = min(10,T.lighting_lumcount) - 5 + else light_amount = 5 + H.nutrition += light_amount + if(H.nutrition > 500) + H.nutrition = 500 + if(light_amount > 2) //if there's enough light, heal + H.heal_overall_damage(1,1) + H.adjustToxLoss(-1) + H.adjustOxyLoss(-1) + + if(H.nutrition < 200) + H.take_overall_damage(2,0) + +/* + SHADOWPEOPLE +*/ + +/datum/species/shadow + // Humans cursed to stay in the darkness, lest their life forces drain. They regain health in shadow and die in light. + name = "???" + id = "shadow" + darksight = 8 + sexes = 0 + ignored_by = list(/mob/living/simple_animal/hostile/faithless) + +/datum/species/shadow/spec_life(mob/living/carbon/human/H) + var/light_amount = 0 + if(isturf(H.loc)) + var/turf/T = H.loc + var/area/A = T.loc + if(A) + if(A.lighting_use_dynamic) light_amount = T.lighting_lumcount + else light_amount = 10 + if(light_amount > 2) //if there's enough light, start dying + H.take_overall_damage(1,1) + else if (light_amount < 2) //heal in the dark + H.heal_overall_damage(1,1) + +/* + SLIMEPEOPLE +*/ + +/datum/species/slime + // Humans mutated by slime mutagen, produced from green slimes. They are not targetted by slimes. + name = "Slimeperson" + id = "slime" + default_color = "00FFFF" + darksight = 3 + invis_sight = SEE_INVISIBLE_LEVEL_ONE + specflags = list(MUTCOLORS,EYECOLOR,HAIR,FACEHAIR) + hair_color = "mutcolor" + hair_alpha = 150 + ignored_by = list(/mob/living/carbon/slime) + +/* + JELLYPEOPLE +*/ + +/datum/species/jelly + // Entirely alien beings that seem to be made entirely out of gel. They have three eyes and a skeleton visible within them. + name = "Xenobiological Jelly Entity" + id = "jelly" + default_color = "00FF90" + say_mod = "chirps" + eyes = "jelleyes" + specflags = list(MUTCOLORS,EYECOLOR) + +/* + GOLEMS +*/ + +/datum/species/golem + // Animated beings of stone. They have increased defenses, and do not need to breathe. They're also slow as fuuuck. + name = "Golem" + id = "golem" + specflags = list(NOBREATH,HEATRES,COLDRES,NOGUNS,NOBLOOD,RADIMMUNE) + speedmod = 3 + armor = 55 + punchmod = 5 + no_equip = list(slot_wear_mask, slot_wear_suit, slot_gloves, slot_shoes, slot_head, slot_w_uniform) + nojumpsuit = 1 + +/* + ADAMANTINE GOLEMS +*/ + +/datum/species/golem/adamantine + name = "Adamantine Golem" + id = "adamantine" + +/* + FLIES +*/ + +/datum/species/fly + // Humans turned into fly-like abominations in teleporter accidents. + name = "Human?" + id = "fly" + say_mod = "buzzes" + +/datum/species/fly/handle_chemicals(datum/reagent/chem, mob/living/carbon/human/H) + if(chem.id == "pestkiller") + H.adjustToxLoss(3) + H.reagents.remove_reagent(chem.id, REAGENTS_METABOLISM) + return 1 + +/datum/species/fly/handle_speech(message) + if(copytext(message, 1, 2) != "*") + message = replacetext(message, "z", stutter("zz")) + + return message + +/* + SKELETONS +*/ + +/datum/species/skeleton + // 2spooky + name = "Spooky Scary Skeleton" + id = "skeleton" + sexes = 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index e09d39d53dd..c9edac17e1c 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -56,6 +56,7 @@ Please contact me on #coderbus IRC. ~Carnie x */ //Human Overlays Indexes///////// +#define SPECIES_LAYER 23 // mutantrace colors... these are on a seperate layer in order to prvent #define BODY_LAYER 22 //underwear, eyes, lips(makeup) #define MUTATIONS_LAYER 21 //Tk headglows etc. #define AUGMENTS_LAYER 20 @@ -78,28 +79,28 @@ Please contact me on #coderbus IRC. ~Carnie x #define L_HAND_LAYER 3 #define R_HAND_LAYER 2 //Having the two hands seperate seems rather silly, merge them together? It'll allow for code to be reused on mobs with arbitarily many hands #define FIRE_LAYER 1 //If you're on fire -#define TOTAL_LAYERS 22 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; +#define TOTAL_LAYERS 23 //KEEP THIS UP-TO-DATE OR SHIT WILL BREAK ;_; ////////////////////////////////// + /mob/living/carbon/human var/list/overlays_standing[TOTAL_LAYERS] /mob/living/carbon/human/proc/update_base_icon_state() - var/race = dna ? dna.mutantrace : null - switch(race) - if("lizard","golem","slime","shadow","adamantine","fly","plant") - base_icon_state = "[dna.mutantrace]_[(gender == FEMALE) ? "f" : "m"]" - if("skeleton") - base_icon_state = "skeleton" + //var/race = dna ? dna.mutantrace : null + if(dna) + base_icon_state = dna.species.update_base_icon_state(src) + else + if(HUSK in mutations) + base_icon_state = "husk" else - if(HUSK in mutations) - base_icon_state = "husk" - else - base_icon_state = "[skin_tone]_[(gender == FEMALE) ? "f" : "m"]" + base_icon_state = "[skin_tone]_[(gender == FEMALE) ? "f" : "m"]" + icon_state = "[base_icon_state]_s" /mob/living/carbon/human/proc/apply_overlay(cache_index) var/image/I = overlays_standing[cache_index] + if(I) overlays += I @@ -145,42 +146,11 @@ Please contact me on #coderbus IRC. ~Carnie x //Reset our hair remove_overlay(HAIR_LAYER) - //mutants don't have hair. masks and helmets can obscure our hair too. - if( (HUSK in mutations) || (dna && dna.mutantrace) || (head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR)) ) + if( (HUSK in mutations) || (head && (head.flags & BLOCKHAIR)) || (wear_mask && (wear_mask.flags & BLOCKHAIR)) ) return - //base icons - var/datum/sprite_accessory/S - var/list/standing = list() - - if(facial_hair_style) - S = facial_hair_styles_list[facial_hair_style] - if(S) - var/image/img_facial_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER) - - var/new_color = "#" + facial_hair_color - img_facial_s.color = new_color - - standing += img_facial_s - - //Applies the debrained overlay if there is no brain - if(!getorgan(/obj/item/organ/brain)) - standing += image("icon"='icons/mob/human_face.dmi', "icon_state" = "debrained_s", "layer" = -HAIR_LAYER) - else if(hair_style) - S = hair_styles_list[hair_style] - if(S) - var/image/img_hair_s = image("icon" = S.icon, "icon_state" = "[S.icon_state]_s", "layer" = -HAIR_LAYER) - - var/new_color = "#" + hair_color - img_hair_s.color = new_color - - standing += img_hair_s - - if(standing.len) - overlays_standing[HAIR_LAYER] = standing - - apply_overlay(HAIR_LAYER) - + if(dna) + dna.species.handle_hair(src) /mob/living/carbon/human/update_mutations() remove_overlay(MUTATIONS_LAYER) @@ -188,6 +158,7 @@ Please contact me on #coderbus IRC. ~Carnie x var/list/standing = list() var/g = (gender == FEMALE) ? "f" : "m" + for(var/mut in mutations) switch(mut) if(HULK) @@ -203,41 +174,22 @@ Please contact me on #coderbus IRC. ~Carnie x apply_overlay(MUTATIONS_LAYER) +/mob/living/carbon/human/proc/update_mutcolor() + if(dna && !(HUSK in mutations)) + dna.species.update_color(src) /mob/living/carbon/human/proc/update_body() remove_overlay(BODY_LAYER) - update_base_icon_state() + if(dna) + base_icon_state = dna.species.update_base_icon_state(src) + else + update_base_icon_state() + icon_state = "[base_icon_state]_s" - var/list/standing = list() - - //Mouth (lipstick!) - if(lip_style) - standing += image("icon"='icons/mob/human_face.dmi', "icon_state"="lips_[lip_style]_s", "layer" = -BODY_LAYER) - - //Eyes - if(!dna || dna.mutantrace != "skeleton") - var/image/img_eyes_s = image("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes_s", "layer" = -BODY_LAYER) - - var/new_color = "#" + eye_color - - img_eyes_s.color = new_color - - standing += img_eyes_s - - //Underwear - if(underwear) - var/datum/sprite_accessory/underwear/U = underwear_all[underwear] - if(U) - standing += image("icon"=U.icon, "icon_state"="[U.icon_state]_s", "layer"=-BODY_LAYER) - - - if(standing.len) - overlays_standing[BODY_LAYER] = standing - - apply_overlay(BODY_LAYER) - + if(dna) // didn't want to have a duplicate if(dna) here, but due to the ordering of the code this was the only way + dna.species.handle_body(src) /mob/living/carbon/human/update_fire() @@ -274,8 +226,6 @@ Please contact me on #coderbus IRC. ~Carnie x apply_overlay(AUGMENTS_LAYER) - - /* --------------------------------------- */ //For legacy support. /mob/living/carbon/human/regenerate_icons() @@ -305,6 +255,8 @@ Please contact me on #coderbus IRC. ~Carnie x update_transform() //Hud Stuff update_hud() + // Mutantrace colors + update_mutcolor() /* --------------------------------------- */ //vvvvvv UPDATE_INV PROCS vvvvvv @@ -322,16 +274,18 @@ Please contact me on #coderbus IRC. ~Carnie x var/t_color = w_uniform.item_color if(!t_color) t_color = icon_state var/image/standing = image("icon"='icons/mob/uniform.dmi', "icon_state"="[t_color]_s", "layer"=-UNIFORM_LAYER) + overlays_standing[UNIFORM_LAYER] = standing - var/G = (gender == FEMALE) ? "f" : "m" - if(G == "f" && U.fitted == 1) - var/index = "[t_color]_s" - var/icon/female_uniform_icon = female_uniform_icons[index] - if(!female_uniform_icon ) //Create standing/laying icons if they don't exist - generate_uniform(index,t_color) - standing = image("icon"=female_uniform_icons["[t_color]_s"], "layer"=-UNIFORM_LAYER) - overlays_standing[UNIFORM_LAYER] = standing + if(dna && dna.species.sexes) + var/G = (gender == FEMALE) ? "f" : "m" + if(G == "f" && U.fitted == 1) + var/index = "[t_color]_s" + var/icon/female_uniform_icon = female_uniform_icons[index] + if(!female_uniform_icon ) //Create standing/laying icons if they don't exist + generate_uniform(index,t_color) + standing = image("icon"=female_uniform_icons["[t_color]_s"], "layer"=-UNIFORM_LAYER) + overlays_standing[UNIFORM_LAYER] = standing if(w_uniform.blood_DNA) standing.overlays += image("icon"='icons/effects/blood.dmi', "icon_state"="uniformblood") @@ -375,6 +329,7 @@ Please contact me on #coderbus IRC. ~Carnie x if(gloves.blood_DNA) standing.overlays += image("icon"='icons/effects/blood.dmi', "icon_state"="bloodyhands") + else if(blood_DNA) overlays_standing[GLOVES_LAYER] = image("icon"='icons/effects/blood.dmi', "icon_state"="bloodyhands") @@ -532,7 +487,6 @@ Please contact me on #coderbus IRC. ~Carnie x if(wear_mask.blood_DNA && !istype(wear_mask, /obj/item/clothing/mask/cigarette)) standing.overlays += image("icon"='icons/effects/blood.dmi', "icon_state"="maskblood") - apply_overlay(FACEMASK_LAYER) @@ -628,7 +582,9 @@ Please contact me on #coderbus IRC. ~Carnie x apply_overlay(L_HAND_LAYER) + //Human Overlays Indexes///////// +#undef SPECIES_LAYER #undef BODY_LAYER #undef MUTATIONS_LAYER #undef DAMAGE_LAYER diff --git a/code/modules/mob/living/carbon/metroid/life.dm b/code/modules/mob/living/carbon/metroid/life.dm index 3cfb35adffc..c045f843831 100644 --- a/code/modules/mob/living/carbon/metroid/life.dm +++ b/code/modules/mob/living/carbon/metroid/life.dm @@ -331,13 +331,10 @@ if(issilicon(L) && (rabid || attacked)) // They can't eat silicons, but they can glomp them in defence targets += L // Possible target found! - if(isanimal(L) && (rabid || attacked || hungry >= 2)) //Simple_Animals only get retaliated against. - targets += L - - if(istype(L, /mob/living/carbon/human)) // Ignore slime(wo)men + if(istype(L, /mob/living/carbon/human) && dna) //Ignore slime(wo)men var/mob/living/carbon/human/H = L if(H.dna) - if(H.dna.mutantrace == "slime") + if(/mob/living/carbon/slime in H.dna.species.ignored_by) continue if(!L.canmove) // Only one slime can latch on at a time. @@ -568,4 +565,4 @@ if (hunger == 2 || rabid || attacked) return 1 if (Leader) return 0 if (holding_still) return 0 - return 1 \ No newline at end of file + return 1 diff --git a/code/modules/mob/living/carbon/metroid/metroid.dm b/code/modules/mob/living/carbon/metroid/metroid.dm index 599f790d6b8..6f53f94d6de 100644 --- a/code/modules/mob/living/carbon/metroid/metroid.dm +++ b/code/modules/mob/living/carbon/metroid/metroid.dm @@ -823,6 +823,8 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 ////////Adamantine Golem stuff I dunno where else to put it +// This will eventually be removed. + /obj/item/clothing/under/golem name = "adamantine skin" desc = "a golem's skin" @@ -831,7 +833,6 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 item_color = "golem" flags = ABSTRACT | NODROP has_sensor = 0 - armor = list(melee = 10, bullet = 0, laser = 0,energy = 0, bomb = 0, bio = 0, rad = 0) /obj/item/clothing/suit/golem name = "adamantine shell" @@ -842,14 +843,8 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 gas_transfer_coefficient = 0.90 permeability_coefficient = 0.50 body_parts_covered = FULL_BODY - slowdown = 1.0 flags_inv = HIDEGLOVES | HIDESHOES | HIDEJUMPSUIT - flags = STOPSPRESSUREDMAGE | ABSTRACT | NODROP - heat_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS | HEAD - max_heat_protection_temperature = FIRE_SUIT_MAX_TEMP_PROTECT - cold_protection = CHEST | GROIN | LEGS | FEET | ARMS | HANDS | HEAD - min_cold_protection_temperature = SPACE_SUIT_MIN_TEMP_PROTECT - armor = list(melee = 80, bullet = 20, laser = 20, energy = 10, bomb = 0, bio = 0, rad = 0) + flags = ABSTRACT | NODROP /obj/item/clothing/shoes/golem name = "golem's feet" @@ -857,7 +852,6 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 icon_state = "golem" item_state = null flags = NOSLIP | ABSTRACT | NODROP - slowdown = SHOES_SLOWDOWN+1 /obj/item/clothing/mask/breath/golem @@ -867,7 +861,7 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 item_state = "golem" siemens_coefficient = 0 unacidable = 1 - flags = ABSTRACT | NODROP | MASKINTERNALS | MASKCOVERSMOUTH + flags = ABSTRACT | NODROP /obj/item/clothing/gloves/golem @@ -886,10 +880,7 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 name = "golem's head" desc = "a golem's head" unacidable = 1 - flags = STOPSPRESSUREDMAGE | ABSTRACT | NODROP - heat_protection = HEAD - max_heat_protection_temperature = FIRE_HELM_MAX_TEMP_PROTECT - armor = list(melee = 80, bullet = 20, laser = 20, energy = 10, bomb = 0, bio = 0, rad = 0) + flags = ABSTRACT | NODROP /obj/effect/golemrune anchored = 1 @@ -928,14 +919,9 @@ mob/living/carbon/slime/var/temperature_resistance = T0C+75 return var/mob/living/carbon/human/G = new /mob/living/carbon/human if(prob(50)) G.gender = "female" - hardset_dna(G, null, null, null, "adamantine") + hardset_dna(G, null, null, null, null, /datum/species/golem/adamantine) G.real_name = text("Adamantine Golem ([rand(1, 1000)])") - G.equip_to_slot_or_del(new /obj/item/clothing/under/golem(G), slot_w_uniform) - G.equip_to_slot_or_del(new /obj/item/clothing/suit/golem(G), slot_wear_suit) - G.equip_to_slot_or_del(new /obj/item/clothing/shoes/golem(G), slot_shoes) - G.equip_to_slot_or_del(new /obj/item/clothing/mask/breath/golem(G), slot_wear_mask) - G.equip_to_slot_or_del(new /obj/item/clothing/gloves/golem(G), slot_gloves) - //G.equip_to_slot_or_del(new /obj/item/clothing/head/space/golem(G), slot_head) + G.dna.species.auto_equip(G) G.loc = src.loc G.key = ghost.key G << "You are an adamantine golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. Serve [user], and assist them in completing their goals at any cost." diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index e492bb5d2fb..e903b7cef30 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -16,6 +16,8 @@ /mob/living/proc/getarmor(var/def_zone, var/type) return 0 +/mob/living/proc/on_hit(var/obj/item/projectile/proj_type) + return /mob/living/bullet_act(obj/item/projectile/P, def_zone) var/armor = run_armor_check(def_zone, P.flag) diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm index 522122633b8..c65884dfc63 100644 --- a/code/modules/mob/living/simple_animal/hostile/hostile.dm +++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm @@ -111,6 +111,11 @@ return 0 if(L in friends) return 0 + if(ishuman(L)) + var/mob/living/carbon/human/H = L + if(H.dna) + if(src.type in H.dna.species.ignored_by) + return 0 return 1 if(isobj(the_target)) if(the_target.type in wanted_objects) diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm index e295e0b6553..25d81d559cb 100644 --- a/code/modules/mob/mob_helpers.dm +++ b/code/modules/mob/mob_helpers.dm @@ -420,6 +420,9 @@ proc/is_special_character(mob/M) // returns 1 for special characters and 2 for h var/list/hands = list(M.l_hand, M.r_hand) return hands +/mob/proc/reagent_check(var/datum/reagent/R) // utilized in the species code + return 1 + /proc/item_heal_robotic(var/mob/living/carbon/human/H, var/mob/user, var/brute, var/burn) var/obj/item/organ/limb/affecting = H.get_organ(check_zone(user.zone_sel.selecting)) @@ -442,4 +445,4 @@ proc/is_special_character(mob/M) // returns 1 for special characters and 2 for h user << "[H]'s [affecting.getDisplayName()] is already in good condition" return else - return \ No newline at end of file + return diff --git a/code/modules/mob/new_player/preferences_setup.dm b/code/modules/mob/new_player/preferences_setup.dm index 6badea404f5..17df645d3aa 100644 --- a/code/modules/mob/new_player/preferences_setup.dm +++ b/code/modules/mob/new_player/preferences_setup.dm @@ -12,6 +12,7 @@ datum/preferences hair_color = random_short_color() facial_hair_color = hair_color eye_color = random_eye_color() + pref_species = new /datum/species/human() backbag = 2 age = rand(AGE_MIN,AGE_MAX) @@ -23,7 +24,11 @@ datum/preferences var/g = "m" if(gender == FEMALE) g = "f" - preview_icon = new /icon('icons/mob/human.dmi', "[skin_tone]_[g]_s") + if(pref_species.id == "human" || !config.mutant_races) + preview_icon = new /icon('icons/mob/human.dmi', "[skin_tone]_[g]_s") + else + preview_icon = new /icon('icons/mob/human.dmi', "[pref_species.id]_[g]_s") + preview_icon.Blend("#[mutant_color]", ICON_MULTIPLY) var/datum/sprite_accessory/S if(underwear) @@ -31,17 +36,19 @@ datum/preferences if(S) preview_icon.Blend(new /icon(S.icon, "[S.icon_state]_s"), ICON_OVERLAY) - var/icon/eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "eyes_s") - eyes_s.Blend("#[eye_color]", ICON_MULTIPLY) + var/icon/eyes_s = new/icon() + if(EYECOLOR in pref_species.specflags) + eyes_s = new/icon("icon" = 'icons/mob/human_face.dmi', "icon_state" = "[pref_species.eyes]_s") + eyes_s.Blend("#[eye_color]", ICON_MULTIPLY) S = hair_styles_list[hair_style] - if(S) + if(S && (HAIR in pref_species.specflags)) var/icon/hair_s = new/icon("icon" = S.icon, "icon_state" = "[S.icon_state]_s") hair_s.Blend("#[hair_color]", ICON_MULTIPLY) eyes_s.Blend(hair_s, ICON_OVERLAY) S = facial_hair_styles_list[facial_hair_style] - if(S) + if(S && (FACEHAIR in pref_species.specflags)) var/icon/facial_s = new/icon("icon" = S.icon, "icon_state" = "[S.icon_state]_s") facial_s.Blend("#[facial_hair_color]", ICON_MULTIPLY) eyes_s.Blend(facial_s, ICON_OVERLAY) diff --git a/code/modules/mob/transform_procs.dm b/code/modules/mob/transform_procs.dm index 087480c786c..dae51d0cc97 100644 --- a/code/modules/mob/transform_procs.dm +++ b/code/modules/mob/transform_procs.dm @@ -131,6 +131,12 @@ O.gender = (deconstruct_block(getblock(dna.uni_identity, DNA_GENDER_BLOCK), 2)-1) ? FEMALE : MALE O.dna = dna + + if(!dna.species) + O.dna.species = new /datum/species/human() + else + O.dna.species = new dna.species.type() + dna = null if (newname) //if there's a name as an argument, always take that one over the current name O.real_name = newname diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm index 7d79eaeb7de..ea2a9a8c73e 100644 --- a/code/modules/projectiles/gun.dm +++ b/code/modules/projectiles/gun.dm @@ -80,8 +80,8 @@ return if(ishuman(user)) var/mob/living/carbon/human/H = user - if(H.dna && H.dna.mutantrace == "adamantine") - user << "Your metal fingers don't fit in the trigger guard!" + if(H.dna && NOGUNS in H.dna.species.specflags) + user << "Your fingers don't fit in the trigger guard!" return add_fingerprint(user) diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm index 6fc8094d59f..afd7bf263ab 100644 --- a/code/modules/projectiles/projectile.dm +++ b/code/modules/projectiles/projectile.dm @@ -58,6 +58,7 @@ if(!isliving(target)) return 0 if(isanimal(target)) return 0 var/mob/living/L = target + L.on_hit(type) return L.apply_effects(stun, weaken, paralyze, irradiate, stutter, eyeblur, drowsy, blocked) proc/vol_by_damage() diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm index e65431d7ad7..2efdbab093b 100644 --- a/code/modules/projectiles/projectile/magic.dm +++ b/code/modules/projectiles/projectile/magic.dm @@ -199,8 +199,12 @@ proc/wabbajack(mob/living/M) var/mob/living/carbon/human/H = new_mob ready_dna(H) if(H.dna) - H.dna.mutantrace = pick("lizard","golem","slime","plant","fly","shadow","adamantine","skeleton",8;"") - H.update_body() + var/list/randspecies = list() + for(var/t in typesof(/datum/species)) // returns a bunch of types + var/datum/species/temp = new t() + randspecies += "[temp.type]" + var/datum/species/new_species = pick(randspecies) + H.dna.species = new new_species() else return diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm index afe46c110e3..e067b00b949 100644 --- a/code/modules/projectiles/projectile/special.dm +++ b/code/modules/projectiles/projectile/special.dm @@ -82,32 +82,8 @@ flag = "energy" on_hit(var/atom/target, var/blocked = 0) - if(iscarbon(target)) - var/mob/living/carbon/M = target - if(check_dna_integrity(M) && M.dna.mutantrace == "plant") //Plantmen possibly get mutated and damaged by the rays. - if(prob(15)) - M.apply_effect((rand(30,80)),IRRADIATE) - M.Weaken(5) - for (var/mob/V in viewers(src)) - V.show_message("\red [M] writhes in pain as \his vacuoles boil.", 3, "\red You hear the crunching of leaves.", 2) - if(prob(35)) - // for (var/mob/V in viewers(src)) //Public messages commented out to prevent possible metaish genetics experimentation and stuff. - Cheridan - // V.show_message("\red [M] is mutated by the radiation beam.", 3, "\red You hear the snapping of twigs.", 2) - if(prob(80)) - randmutb(M) - domutcheck(M,null) - else - randmutg(M) - domutcheck(M,null) - else - M.adjustFireLoss(rand(5,15)) - M.show_message("\red The radiation beam singes you!") - // for (var/mob/V in viewers(src)) - // V.show_message("\red [M] is singed by the radiation beam.", 3, "\red You hear the crackle of burning leaves.", 2) - else - // for (var/mob/V in viewers(src)) - // V.show_message("The radiation beam dissipates harmlessly through [M]", 3) - M.show_message("\blue The radiation beam dissipates harmlessly through your body.") + ..() + return /obj/item/projectile/energy/florayield name = "beta somatoray" @@ -117,14 +93,9 @@ nodamage = 1 flag = "energy" - on_hit(mob/living/carbon/target, var/blocked = 0) - if(iscarbon(target)) - if(ishuman(target) && target.dna && target.dna.mutantrace == "plant") //These rays make plantmen fat. - target.nutrition = min(target.nutrition+30, 500) - else - target.show_message("\blue The radiation beam dissipates harmlessly through your body.") - else - return 1 + on_hit(mob/living/carbon/human/target, var/blocked = 0) + ..() + return /obj/item/projectile/beam/mindflayer diff --git a/code/modules/reagents/Chemistry-Holder.dm b/code/modules/reagents/Chemistry-Holder.dm index a675580369a..4eec5cad53a 100644 --- a/code/modules/reagents/Chemistry-Holder.dm +++ b/code/modules/reagents/Chemistry-Holder.dm @@ -200,7 +200,9 @@ datum for(var/A in reagent_list) var/datum/reagent/R = A if(M && R) - R.on_mob_life(M) + if(M.reagent_check(R) != 1) + R.on_mob_life(M) + update_total() conditional_update_move(var/atom/A, var/Running = 0) diff --git a/code/modules/reagents/Chemistry-Reagents.dm b/code/modules/reagents/Chemistry-Reagents.dm index e7a8c57b042..ef9f736fa34 100644 --- a/code/modules/reagents/Chemistry-Reagents.dm +++ b/code/modules/reagents/Chemistry-Reagents.dm @@ -342,18 +342,6 @@ datum reagent_state = LIQUID color = "#13BC5E" // rgb: 19, 188, 94 - on_mob_life(var/mob/living/M as mob) - if(!M) M = holder.my_atom - if(ishuman(M)) - var/mob/living/carbon/human/human = M - if(human.dna && !human.dna.mutantrace) - M << "Your flesh rapidly mutates!" - human.dna.mutantrace = "slime" - human.update_body() - human.update_hair() - ..() - return - aslimetoxin name = "Advanced Mutation Toxin" id = "amutationtoxin" @@ -1507,11 +1495,6 @@ datum var/mob/living/carbon/C = M if(!C.wear_mask) // If not wearing a mask C.adjustToxLoss(2) // 4 toxic damage per application, doubled for some reason - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.dna) - if(H.dna.mutantrace == "plant") //plantmen take a LOT of damage - H.adjustToxLoss(10) toxin/plantbgone/weedkiller name = "Weed Killer" @@ -1534,11 +1517,6 @@ datum var/mob/living/carbon/C = M if(!C.wear_mask) // If not wearing a mask C.adjustToxLoss(2) // 4 toxic damage per application, doubled for some reason - if(ishuman(M)) - var/mob/living/carbon/human/H = M - if(H.dna) - if(H.dna.mutantrace == "fly") //Botanists can now genocide plant and fly people alike. - H.adjustToxLoss(10) toxin/stoxin name = "Sleep Toxin" diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index 7d8ae71c443..697a96fc5bd 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -59,6 +59,12 @@ return if(ismob(target)) //Blood! + if(ishuman(target)) + var/mob/living/carbon/human/H = target + if(H.dna) + if(NOBLOOD in H.dna.species.specflags) + user << "You are unable to locate any blood." + return if(reagents.has_reagent("blood")) user << "There is already a blood sample in this syringe." return diff --git a/config/game_options.txt b/config/game_options.txt index d01a6f3a8a9..d250d80c56d 100644 --- a/config/game_options.txt +++ b/config/game_options.txt @@ -191,5 +191,9 @@ SEC_START_BRIG ## Set to 2 for "random", silicons will start with a random lawset picked from (at the time of writing): P.A.L.A.D.I.N., Corporate, Asimov. More can be added by changing the law datum paths in ai_laws.dm. DEFAULT_LAWS 1 -## Uncoment to give players the choice of their mutantrace before they join the game -#JOIN_WITH_MUTANT_RACE \ No newline at end of file +## Uncoment to give players the choice of their species before they join the game +#JOIN_WITH_MUTANT_RACE + +## Uncomment to allow certain species to have custom colors + +#MUTANT_COLORS diff --git a/icons/mob/human.dmi b/icons/mob/human.dmi index efbb59c0fd7..8b586b4fab9 100644 Binary files a/icons/mob/human.dmi and b/icons/mob/human.dmi differ diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi index 9e2059cafbe..7b9629e0de8 100644 Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ diff --git a/tgstation.dme b/tgstation.dme index 86fb4497c89..c01bdc15e3a 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -963,6 +963,8 @@ #include "code\modules\mob\living\carbon\human\life.dm" #include "code\modules\mob\living\carbon\human\login.dm" #include "code\modules\mob\living\carbon\human\say.dm" +#include "code\modules\mob\living\carbon\human\species.dm" +#include "code\modules\mob\living\carbon\human\species_types.dm" #include "code\modules\mob\living\carbon\human\update_icons.dm" #include "code\modules\mob\living\carbon\human\whisper.dm" #include "code\modules\mob\living\carbon\metroid\death.dm"