From 4540f770d440e5c0175b363812e1342f3e6c3fac Mon Sep 17 00:00:00 2001 From: BothNevarBackwards Date: Tue, 26 Dec 2017 14:28:21 -0500 Subject: [PATCH] Lots of species stuff ported from Baystation (#4442) * Ports species based footprints from bay - https://github.com/Baystation12/Baystation12/pull/17935 - https://i.imgur.com/ojAU373.png - Unathi use claw on right - Taj and tesh use paw in center * Flash stuff glasses protection things * Flash stuff actual thing * Organ mult thingy * Floaty eyes and cloak setup * Species falling --- code/__defines/mobs.dm | 7 +- code/__defines/species_languages.dm | 4 + .../effects/decals/Cleanable/tracks.dm | 39 +++++++-- code/game/turfs/simulated.dm | 4 +- code/modules/clothing/clothing.dm | 3 + code/modules/clothing/glasses/glasses.dm | 32 +++++-- code/modules/clothing/head/misc_special.dm | 10 ++- .../clothing/spacesuits/miscellaneous.dm | 1 + .../modules/clothing/spacesuits/spacesuits.dm | 1 + code/modules/mob/living/carbon/human/human.dm | 65 +++++++-------- .../mob/living/carbon/human/human_defines.dm | 2 + code/modules/mob/living/carbon/human/life.dm | 9 +- .../living/carbon/human/species/species.dm | 33 +++++++- .../carbon/human/species/station/seromi.dm | 2 + .../carbon/human/species/station/station.dm | 4 + .../mob/living/carbon/human/update_icons.dm | 69 +++++++++------- code/modules/multiz/movement.dm | 78 ++++++++++++++---- code/modules/organs/internal/eyes.dm | 7 ++ code/modules/organs/organ_external.dm | 1 + code/modules/organs/organ_icon.dm | 9 +- code/modules/organs/subtypes/standard.dm | 2 + icons/effects/fluidtracks.dmi | Bin 4141 -> 7307 bytes 22 files changed, 278 insertions(+), 104 deletions(-) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index 9ebf60632c..84007beae3 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -34,8 +34,9 @@ #define STANCE_FOLLOW 6 // Following somone #define STANCE_BUSY 7 // Do nothing on life ticks (Other code is running) -#define LEFT 1 -#define RIGHT 2 +#define LEFT 0x1 +#define RIGHT 0x2 +#define UNDER 0x4 // Pulse levels, very simplified. #define PULSE_NONE 0 // So !M.pulse checks would be possible. @@ -124,10 +125,12 @@ #define TINT_HEAVY 2 #define TINT_BLIND 3 +#define FLASH_PROTECTION_VULNERABLE -2 #define FLASH_PROTECTION_REDUCED -1 #define FLASH_PROTECTION_NONE 0 #define FLASH_PROTECTION_MODERATE 1 #define FLASH_PROTECTION_MAJOR 2 + #define ANIMAL_SPAWN_DELAY round(config.respawn_delay / 6) #define DRONE_SPAWN_DELAY round(config.respawn_delay / 3) diff --git a/code/__defines/species_languages.dm b/code/__defines/species_languages.dm index 9082482ebe..bd0d02ae80 100644 --- a/code/__defines/species_languages.dm +++ b/code/__defines/species_languages.dm @@ -56,3 +56,7 @@ #define NO_TALK_MSG 128 // Do not show the "\The [speaker] talks into \the [radio]" message #define NO_STUTTER 256 // No stuttering, slurring, or other speech problems #define ALT_TRANSMIT 512 // Language is not based on vision or sound (Todo: add this into the say code and use it for the rootspeak languages) + +#define SKIN_NORMAL 0 +#define SKIN_THREAT 1 +#define SKIN_CLOAK 2 diff --git a/code/game/objects/effects/decals/Cleanable/tracks.dm b/code/game/objects/effects/decals/Cleanable/tracks.dm index f104b77fd7..0ad5ac13f8 100644 --- a/code/game/objects/effects/decals/Cleanable/tracks.dm +++ b/code/game/objects/effects/decals/Cleanable/tracks.dm @@ -154,20 +154,49 @@ var/global/list/image/fluidtrack_cache=list() /obj/effect/decal/cleanable/blood/tracks/footprints name = "wet footprints" dryname = "dried footprints" - desc = "Whoops..." - drydesc = "Whoops..." + desc = "They look like still wet tracks left by footwear." + drydesc = "They look like dried tracks left by footwear." coming_state = "human1" going_state = "human2" amount = 0 +/obj/effect/decal/cleanable/blood/tracks/snake + name = "wet tracks" + dryname = "dried tracks" + desc = "They look like still wet tracks left by a giant snake." + drydesc = "They look like dried tracks left by a giant snake." + coming_state = "snake1" + going_state = "snake2" + random_icon_states = null + amount = 0 + +/obj/effect/decal/cleanable/blood/tracks/paw + name = "wet tracks" + dryname = "dried tracks" + desc = "They look like still wet tracks left by a mammal." + drydesc = "They look like dried tracks left by a mammal." + coming_state = "paw1" + going_state = "paw2" + random_icon_states = null + amount = 0 + +/obj/effect/decal/cleanable/blood/tracks/claw + name = "wet tracks" + dryname = "dried tracks" + desc = "They look like still wet tracks left by a reptile." + drydesc = "They look like dried tracks left by a reptile." + coming_state = "claw1" + going_state = "claw2" + random_icon_states = null + amount = 0 + /obj/effect/decal/cleanable/blood/tracks/wheels name = "wet tracks" dryname = "dried tracks" - desc = "Whoops..." - drydesc = "Whoops..." + desc = "They look like still wet tracks left by wheels." + drydesc = "They look like dried tracks left by wheels." coming_state = "wheels" going_state = "" - desc = "They look like tracks left by wheels." gender = PLURAL random_icon_states = null amount = 0 \ No newline at end of file diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 6a049a4e68..a47590d55d 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -119,10 +119,10 @@ H.track_blood-- if (bloodDNA) - src.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints,bloodDNA,H.dir,0,bloodcolor) // Coming + src.AddTracks(H.species.get_move_trail(H),bloodDNA,H.dir,0,bloodcolor) // Coming var/turf/simulated/from = get_step(H,reverse_direction(H.dir)) if(istype(from) && from) - from.AddTracks(/obj/effect/decal/cleanable/blood/tracks/footprints,bloodDNA,0,H.dir,bloodcolor) // Going + from.AddTracks(H.species.get_move_trail(H),bloodDNA,0,H.dir,bloodcolor) // Going bloodDNA = null diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm index 6653448b1c..5f2a72aeb8 100644 --- a/code/modules/clothing/clothing.dm +++ b/code/modules/clothing/clothing.dm @@ -9,6 +9,9 @@ var/list/restricted_accessory_slots var/list/starting_accessories + var/flash_protection = FLASH_PROTECTION_NONE + var/tint = TINT_NONE + /* Sprites used when the clothing item is refit. This is done by setting icon_override. For best results, if this is set then sprite_sheets should be null and vice versa, but that is by no means necessary. diff --git a/code/modules/clothing/glasses/glasses.dm b/code/modules/clothing/glasses/glasses.dm index 48ab03ed3f..d0a90a86b5 100644 --- a/code/modules/clothing/glasses/glasses.dm +++ b/code/modules/clothing/glasses/glasses.dm @@ -42,12 +42,16 @@ BLIND // can't see anything active = 0 icon_state = off_state user.update_inv_glasses() - usr << "You deactivate the optical matrix on the [src]." + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You deactivate the optical matrix on the [src].") else active = 1 icon_state = initial(icon_state) user.update_inv_glasses() - usr << "You activate the optical matrix on the [src]." + flash_protection = initial(flash_protection) + tint = initial(tint) + to_chat(usr, "You activate the optical matrix on the [src].") user.update_action_buttons() /obj/item/clothing/glasses/meson @@ -128,6 +132,7 @@ BLIND // can't see anything action_button_name = "Toggle Goggles" see_invisible = SEE_INVISIBLE_NOLIGHTING off_state = "denight" + flash_protection = FLASH_PROTECTION_REDUCED /obj/item/clothing/glasses/night/vox name = "Alien Optics" @@ -219,6 +224,7 @@ BLIND // can't see anything icon_state = "sun" item_state_slots = list(slot_r_hand_str = "sunglasses", slot_l_hand_str = "sunglasses") darkness_view = -1 + flash_protection = FLASH_PROTECTION_MODERATE /obj/item/clothing/glasses/sunglasses/aviator name = "aviators" @@ -234,6 +240,8 @@ BLIND // can't see anything matter = list(DEFAULT_WALL_MATERIAL = 1500, "glass" = 1000) item_flags = AIRTIGHT var/up = 0 + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY /obj/item/clothing/glasses/welding/attack_self() toggle() @@ -249,13 +257,17 @@ BLIND // can't see anything flags_inv |= HIDEEYES body_parts_covered |= EYES icon_state = initial(icon_state) - usr << "You flip \the [src] down to protect your eyes." + flash_protection = initial(flash_protection) + tint = initial(tint) + to_chat(usr, "You flip \the [src] down to protect your eyes.") else src.up = !src.up flags_inv &= ~HIDEEYES body_parts_covered &= ~EYES icon_state = "[initial(icon_state)]up" - usr << "You push \the [src] up out of your face." + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push \the [src] up out of your face.") update_clothing_icon() usr.update_action_buttons() @@ -263,13 +275,15 @@ BLIND // can't see anything name = "superior welding goggles" desc = "Welding goggles made from more expensive materials, strangely smells like potatoes." icon_state = "rwelding-g" + tint = TINT_MODERATE /obj/item/clothing/glasses/sunglasses/blindfold name = "blindfold" desc = "Covers the eyes, preventing sight." icon_state = "blindfold" item_state_slots = list(slot_r_hand_str = "blindfold", slot_l_hand_str = "blindfold") - //vision_flags = BLIND // This flag is only supposed to be used if it causes permanent blindness, not temporary because of glasses + flash_protection = FLASH_PROTECTION_MAJOR + tint = BLIND /obj/item/clothing/glasses/sunglasses/blindfold/tape name = "length of tape" @@ -340,11 +354,13 @@ BLIND // can't see anything if(toggleable && !user.incapacitated()) on = !on if(on) + flash_protection = FLASH_PROTECTION_NONE src.hud = hud_holder - to_chat(user, "You switch the [src] to HUD mode.") + to_chat(usr, "You switch the [src] to HUD mode.") else + flash_protection = initial(flash_protection) src.hud = null - to_chat(user, "You switch \the [src] to flash protection mode.") + to_chat(usr, "You switch \the [src] to flash protection mode.") update_icon() user << activation_sound user.update_inv_glasses() @@ -382,7 +398,7 @@ BLIND // can't see anything action_button_name = "Toggle Goggles" vision_flags = SEE_MOBS see_invisible = SEE_INVISIBLE_NOLIGHTING - + flash_protection = FLASH_PROTECTION_REDUCED emp_act(severity) if(istype(src.loc, /mob/living/carbon/human)) diff --git a/code/modules/clothing/head/misc_special.dm b/code/modules/clothing/head/misc_special.dm index 97c5ae4a6d..eea135212d 100644 --- a/code/modules/clothing/head/misc_special.dm +++ b/code/modules/clothing/head/misc_special.dm @@ -25,6 +25,8 @@ siemens_coefficient = 0.9 w_class = ITEMSIZE_NORMAL var/base_state + flash_protection = FLASH_PROTECTION_MAJOR + tint = TINT_HEAVY /obj/item/clothing/head/welding/attack_self() toggle() @@ -44,13 +46,17 @@ body_parts_covered |= (EYES|FACE) flags_inv |= (HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) icon_state = base_state - usr << "You flip the [src] down to protect your eyes." + flash_protection = FLASH_PROTECTION_MAJOR + tint = initial(tint) + to_chat(usr, "You flip the [src] down to protect your eyes.") else src.up = !src.up body_parts_covered &= ~(EYES|FACE) flags_inv &= ~(HIDEMASK|HIDEEARS|HIDEEYES|HIDEFACE) icon_state = "[base_state]up" - usr << "You push the [src] up out of your face." + flash_protection = FLASH_PROTECTION_NONE + tint = TINT_NONE + to_chat(usr, "You push the [src] up out of your face.") update_clothing_icon() //so our mob-overlays if (ismob(src.loc)) //should allow masks to update when it is opened/closed var/mob/M = src.loc diff --git a/code/modules/clothing/spacesuits/miscellaneous.dm b/code/modules/clothing/spacesuits/miscellaneous.dm index c66ff3440c..03466eea22 100644 --- a/code/modules/clothing/spacesuits/miscellaneous.dm +++ b/code/modules/clothing/spacesuits/miscellaneous.dm @@ -93,6 +93,7 @@ name = "Emergency Space Helmet" icon_state = "syndicate-helm-orange" desc = "A simple helmet with a built in light, smells like mothballs." + flash_protection = FLASH_PROTECTION_NONE /obj/item/clothing/suit/space/emergency name = "Emergency Softsuit" diff --git a/code/modules/clothing/spacesuits/spacesuits.dm b/code/modules/clothing/spacesuits/spacesuits.dm index ccbbf56ab2..a39b6809c6 100644 --- a/code/modules/clothing/spacesuits/spacesuits.dm +++ b/code/modules/clothing/spacesuits/spacesuits.dm @@ -17,6 +17,7 @@ species_restricted = list("exclude","Diona") preserve_item = 1 phoronproof = 1 + flash_protection = FLASH_PROTECTION_NONE var/obj/machinery/camera/camera var/list/camera_networks diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 3788735d76..0b5781b67b 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -688,43 +688,40 @@ ///Returns a number between -1 to 2 /mob/living/carbon/human/eyecheck() - var/obj/item/organ/I = internal_organs_by_name[O_EYES] - if(!I || I.status & (ORGAN_CUT_AWAY|ORGAN_DESTROYED)) - return 2 + var/obj/item/organ/internal/eyes/I - var/number = 0 - if(istype(src.head, /obj/item/clothing/head/welding)) - if(!src.head:up) - number += 2 - if(istype(back, /obj/item/weapon/rig)) - var/obj/item/weapon/rig/O = back - if(O.helmet && O.helmet == head && (O.helmet.body_parts_covered & EYES)) - number += 2 - if(istype(src.head, /obj/item/clothing/head/helmet/space)) - number += 2 - if(istype(src.head, /obj/item/clothing/head/helmet/space/emergency)) - number -= 2 - if(istype(src.glasses, /obj/item/clothing/glasses/thermal)) - var/obj/item/clothing/glasses/thermal/T = src.glasses - if(T.active) - number -= 1 - if(istype(src.glasses, /obj/item/clothing/glasses/night)) - var/obj/item/clothing/glasses/night/N = src.glasses - if(N.active) - number -= 1 - if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses)) - if(istype(src.glasses, /obj/item/clothing/glasses/sunglasses/sechud/aviator)) - var/obj/item/clothing/glasses/sunglasses/sechud/aviator/S = src.glasses - if(!S.on) - number += 1 - else - number += 1 - if(istype(src.glasses, /obj/item/clothing/glasses/welding)) - var/obj/item/clothing/glasses/welding/W = src.glasses - if(!W.up) - number += 2 + if(internal_organs_by_name[O_EYES]) // Eyes are fucked, not a 'weak point'. + I = internal_organs_by_name[O_EYES] + if(I.is_broken()) + return FLASH_PROTECTION_MAJOR + else // They can't be flashed if they don't have eyes. + return FLASH_PROTECTION_MAJOR + + var/number = get_equipment_flash_protection() + number = I.get_total_protection(number) + I.additional_flash_effects(number) return number +#define add_clothing_protection(A) \ + var/obj/item/clothing/C = A; \ + flash_protection += C.flash_protection; \ + +/mob/living/carbon/human/proc/get_equipment_flash_protection() + var/flash_protection = 0 + + if(istype(src.head, /obj/item/clothing/head)) + add_clothing_protection(head) + if(istype(src.glasses, /obj/item/clothing/glasses)) + add_clothing_protection(glasses) + if(istype(src.wear_mask, /obj/item/clothing/mask)) + add_clothing_protection(wear_mask) + if(istype(back,/obj/item/weapon/rig)) + add_clothing_protection(back) + + return flash_protection + +#undef add_clothing_protection + //Used by various things that knock people out by applying blunt trauma to the head. //Checks that the species has a "head" (brain containing organ) and that hit_zone refers to it. /mob/living/carbon/human/proc/headcheck(var/target_zone, var/brain_tag = "brain") diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm index fbb98c3f98..8d81a9ce95 100644 --- a/code/modules/mob/living/carbon/human/human_defines.dm +++ b/code/modules/mob/living/carbon/human/human_defines.dm @@ -23,6 +23,8 @@ var/g_skin = 0 var/b_skin = 0 + var/skin_state = SKIN_NORMAL + //Synth colors var/synth_color = 0 //Lets normally uncolorable synth parts be colorable. var/r_synth //Used with synth_color to color synth parts that normaly can't be colored. diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index 2855dd5656..0c14d3d384 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -972,7 +972,7 @@ if(paralysis || sleeping) blinded = 1 - stat = UNCONSCIOUS + set_stat(UNCONSCIOUS) animate_tail_reset() adjustHalLoss(-3) @@ -987,7 +987,7 @@ emote("snore") //CONSCIOUS else - stat = CONSCIOUS + set_stat(CONSCIOUS) //Periodically double-check embedded_flag if(embedded_flag && !(life_tick % 10)) @@ -1075,6 +1075,11 @@ return 1 +/mob/living/carbon/human/proc/set_stat(var/new_stat) + stat = new_stat + if(stat) + update_skin(1) + /mob/living/carbon/human/handle_regular_hud_updates() if(hud_updateflag) // update our mob's hud overlays, AKA what others see flaoting above our head handle_hud_list() diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm index beb531f901..d38ac45d31 100644 --- a/code/modules/mob/living/carbon/human/species/species.dm +++ b/code/modules/mob/living/carbon/human/species/species.dm @@ -158,6 +158,9 @@ var/spawn_flags = 0 // Flags that specify who can spawn as this species var/slowdown = 0 // Passive movement speed malus (or boost, if negative) + var/obj/effect/decal/cleanable/blood/tracks/move_trail = /obj/effect/decal/cleanable/blood/tracks/footprints // What marks are left when walking + var/list/skin_overlays = list() + var/has_floating_eyes = 0 // Whether the eyes can be shown above other icons var/water_movement = 0 // How much faster or slower the species is in water var/snow_movement = 0 // How much faster or slower the species is on snow @@ -358,4 +361,32 @@ // Called when lying down on a water tile. /datum/species/proc/can_breathe_water() - return FALSE \ No newline at end of file + return FALSE + +// Impliments different trails for species depending on if they're wearing shoes. +/datum/species/proc/get_move_trail(var/mob/living/carbon/human/H) + if( H.shoes || ( H.wear_suit && (H.wear_suit.body_parts_covered & FEET) ) ) + return /obj/effect/decal/cleanable/blood/tracks/footprints + else + return move_trail + +/datum/species/proc/update_skin(var/mob/living/carbon/human/H) + return + +/datum/species/proc/get_eyes(var/mob/living/carbon/human/H) + return + +/datum/species/proc/can_overcome_gravity(var/mob/living/carbon/human/H) + return FALSE + +// Used for any extra behaviour when falling and to see if a species will fall at all. +/datum/species/proc/can_fall(var/mob/living/carbon/human/H) + return TRUE + +// Used to find a special target for falling on, such as pouncing on someone from above. +/datum/species/proc/find_fall_target_special(src, landing) + return FALSE + +// Used to override normal fall behaviour. Use only when the species does fall down a level. +/datum/species/proc/fall_impact_special(var/mob/living/carbon/human/H, var/atom/A) + return FALSE diff --git a/code/modules/mob/living/carbon/human/species/station/seromi.dm b/code/modules/mob/living/carbon/human/species/station/seromi.dm index 0a6e1df9a4..c84b9ad64f 100644 --- a/code/modules/mob/living/carbon/human/species/station/seromi.dm +++ b/code/modules/mob/living/carbon/human/species/station/seromi.dm @@ -26,6 +26,8 @@ tail_hair = "feathers" reagent_tag = IS_TESHARI + move_trail = /obj/effect/decal/cleanable/blood/tracks/paw + icobase = 'icons/mob/human_races/r_seromi.dmi' deform = 'icons/mob/human_races/r_seromi.dmi' damage_overlays = 'icons/mob/human_races/masks/dam_seromi.dmi' diff --git a/code/modules/mob/living/carbon/human/species/station/station.dm b/code/modules/mob/living/carbon/human/species/station/station.dm index e7d1083e86..a133b82495 100644 --- a/code/modules/mob/living/carbon/human/species/station/station.dm +++ b/code/modules/mob/living/carbon/human/species/station/station.dm @@ -87,6 +87,8 @@ reagent_tag = IS_UNATHI + move_trail = /obj/effect/decal/cleanable/blood/tracks/claw + has_limbs = list( BP_TORSO = list("path" = /obj/item/organ/external/chest/unathi), BP_GROIN = list("path" = /obj/item/organ/external/groin/unathi), @@ -188,6 +190,8 @@ reagent_tag = IS_TAJARA + move_trail = /obj/effect/decal/cleanable/blood/tracks/paw + heat_discomfort_level = 292 heat_discomfort_strings = list( "Your fur prickles in the heat.", diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm index 3cab75ef2d..8f8974473a 100644 --- a/code/modules/mob/living/carbon/human/update_icons.dm +++ b/code/modules/mob/living/carbon/human/update_icons.dm @@ -107,34 +107,35 @@ Please contact me on #coderbus IRC. ~Carn x //Human Overlays Indexes///////// #define MUTATIONS_LAYER 1 -#define DAMAGE_LAYER 2 -#define SURGERY_LEVEL 3 //bs12 specific. -#define UNDERWEAR_LAYER 4 -#define SHOES_LAYER_ALT 5 -#define UNIFORM_LAYER 6 -#define ID_LAYER 7 -#define SHOES_LAYER 8 -#define GLOVES_LAYER 9 -#define BELT_LAYER 10 -#define SUIT_LAYER 11 -#define TAIL_LAYER 12 //bs12 specific. //In a perfect world the parts of the tail that show between legs would be on a new layer. Until then, sprite's been tweaked -#define GLASSES_LAYER 13 -#define BELT_LAYER_ALT 14 -#define SUIT_STORE_LAYER 15 -#define BACK_LAYER 16 -#define HAIR_LAYER 17 //TODO: make part of head layer? -#define EARS_LAYER 18 -#define FACEMASK_LAYER 19 -#define HEAD_LAYER 20 -#define COLLAR_LAYER 21 -#define HANDCUFF_LAYER 22 -#define LEGCUFF_LAYER 23 -#define L_HAND_LAYER 24 -#define R_HAND_LAYER 25 -#define MODIFIER_EFFECTS_LAYER 26 -#define FIRE_LAYER 27 //If you're on fire -#define WATER_LAYER 28 //If you're submerged in water. -#define TARGETED_LAYER 29 //BS12: Layer for the target overlay from weapon targeting system +#define SKIN_LAYER 2 +#define DAMAGE_LAYER 3 +#define SURGERY_LEVEL 4 //bs12 specific. +#define UNDERWEAR_LAYER 5 +#define SHOES_LAYER_ALT 6 +#define UNIFORM_LAYER 7 +#define ID_LAYER 8 +#define SHOES_LAYER 9 +#define GLOVES_LAYER 10 +#define BELT_LAYER 11 +#define SUIT_LAYER 12 +#define TAIL_LAYER 13 //bs12 specific. //In a perfect world the parts of the tail that show between legs would be on a new layer. Until then, sprite's been tweaked +#define GLASSES_LAYER 14 +#define BELT_LAYER_ALT 15 +#define SUIT_STORE_LAYER 16 +#define BACK_LAYER 17 +#define HAIR_LAYER 18 //TODO: make part of head layer? +#define EARS_LAYER 19 +#define FACEMASK_LAYER 20 +#define HEAD_LAYER 21 +#define COLLAR_LAYER 22 +#define HANDCUFF_LAYER 23 +#define LEGCUFF_LAYER 24 +#define L_HAND_LAYER 25 +#define R_HAND_LAYER 26 +#define MODIFIER_EFFECTS_LAYER 27 +#define FIRE_LAYER 28 //If you're on fire +#define WATER_LAYER 29 //If you're submerged in water. +#define TARGETED_LAYER 30 //BS12: Layer for the target overlay from weapon targeting system #define TOTAL_LAYERS 30 ////////////////////////////////// @@ -158,6 +159,8 @@ Please contact me on #coderbus IRC. ~Carn x else if(istype(entry, /list)) for(var/inner_entry in entry) overlays += inner_entry + if(species && species.has_floating_eyes) + overlays |= species.get_eyes(src) update_transform() @@ -280,6 +283,7 @@ var/global/list/damage_icon_parts = list() icon_key += "[rgb(part.s_col[1],part.s_col[2],part.s_col[3])]" if(part.body_hair && part.h_col && part.h_col.len >= 3) icon_key += "[rgb(part.h_col[1],part.h_col[2],part.h_col[3])]" + icon_key += "[part.s_col_blend]" else icon_key += "#000000" for(var/M in part.markings) @@ -311,7 +315,7 @@ var/global/list/damage_icon_parts = list() var/icon/temp = part.get_icon(skeleton) //That part makes left and right legs drawn topmost and lowermost when human looks WEST or EAST //And no change in rendering for other parts (they icon_position is 0, so goes to 'else' part) - if(part.icon_position&(LEFT|RIGHT)) + if(part.icon_position & (LEFT | RIGHT)) var/icon/temp2 = new('icons/mob/human.dmi',"blank") temp2.Insert(new/icon(temp,dir=NORTH),dir=NORTH) temp2.Insert(new/icon(temp,dir=SOUTH),dir=SOUTH) @@ -325,6 +329,8 @@ var/global/list/damage_icon_parts = list() if(part.icon_position & RIGHT) temp2.Insert(new/icon(temp,dir=WEST),dir=WEST) base_icon.Blend(temp2, ICON_UNDERLAY) + else if(part.icon_position & UNDER) + base_icon.Blend(temp, ICON_UNDERLAY) else base_icon.Blend(temp, ICON_OVERLAY) @@ -354,6 +360,10 @@ var/global/list/damage_icon_parts = list() //tail update_tail_showing(0) +/mob/living/carbon/human/proc/update_skin(var/update_icons=1) + overlays_standing[SKIN_LAYER] = species.update_skin(src) + if(update_icons) update_icons() + //UNDERWEAR OVERLAY /mob/living/carbon/human/proc/update_underwear(var/update_icons=1) overlays_standing[UNDERWEAR_LAYER] = null @@ -463,6 +473,7 @@ var/global/list/damage_icon_parts = list() if(transforming || QDELETED(src)) return update_mutations(0) + update_skin(0) update_body(0) update_underwear(0) update_hair(0) diff --git a/code/modules/multiz/movement.dm b/code/modules/multiz/movement.dm index 25d099155d..59b0b2ebe8 100644 --- a/code/modules/multiz/movement.dm +++ b/code/modules/multiz/movement.dm @@ -38,7 +38,7 @@ return 0 var/area/area = get_area(src) - if(direction == UP && area.has_gravity) + if(direction == UP && area.has_gravity() && !can_overcome_gravity()) var/obj/structure/lattice/lattice = locate() in destination.contents if(lattice) var/pull_up_time = max(5 SECONDS + (src.movement_delay() * 10), 1) @@ -61,6 +61,12 @@ return 0 return 1 +/mob/proc/can_overcome_gravity() + return FALSE + +/mob/living/carbon/human/can_overcome_gravity() + return species && species.can_overcome_gravity(src) + /mob/observer/zMove(direction) var/turf/destination = (direction == UP) ? GetAbove(src) : GetBelow(src) if(destination) @@ -183,6 +189,10 @@ if((locate(/obj/structure/disposalpipe/up) in below) || locate(/obj/machinery/atmospherics/pipe/zpipe/up in below)) return FALSE +/mob/living/carbon/human/can_fall() + if(..()) + return species.can_fall(src) + /mob/living/simple_animal/parrot/can_fall() // Poly can fly. return FALSE @@ -222,7 +232,6 @@ return FALSE return falling_atom.fall_impact(src) - // Actually process the falling movement and impacts. /atom/movable/proc/handle_fall(var/turf/landing) var/turf/oldloc = loc @@ -244,50 +253,89 @@ // Detect if we made a silent landing. if(locate(/obj/structure/stairs) in landing) return 1 + else + var/atom/A = find_fall_target(oldloc, landing) + if(special_fall_handle(A) || !A || !A.check_impact(src)) + return + fall_impact(A) +/atom/movable/proc/special_fall_handle(var/atom/A) + return FALSE + +/mob/living/carbon/human/special_fall_handle(var/atom/A) + if(species) + return species.fall_impact_special(src, A) + return FALSE + +/atom/movable/proc/find_fall_target(var/turf/oldloc, var/turf/landing) if(isopenspace(oldloc)) oldloc.visible_message("\The [src] falls down through \the [oldloc]!", "You hear something falling through the air.") // If the turf has density, we give it first dibs if (landing.density && landing.CheckFall(src)) - return + return landing // First hit objects in the turf! for(var/atom/movable/A in landing) if(A != src && A.CheckFall(src)) - return + return A // If none of them stopped us, then hit the turf itself - landing.CheckFall(src) + if(landing.CheckFall(src)) + return landing +/mob/living/carbon/human/find_fall_target(var/turf/landing) + if(species) + var/atom/A = species.find_fall_target_special(src, landing) + if(A) + return A + return ..() + +//CheckFall landing.fall_impact(src) // ## THE FALLING PROCS ### -// Called on everything that falling_atom might hit. Return 1 if you're handling it so handle_fall() will stop checking. -// If you're soft and break the fall gently, just return 1 -// If the falling atom will hit you hard, call fall_impact() and return its result. +// Called on everything that falling_atom might hit. Return TRUE if you're handling it so find_fall_target() will stop checking. /atom/proc/CheckFall(var/atom/movable/falling_atom) if(density && !(flags & ON_BORDER)) - return falling_atom.fall_impact(src) + return TRUE + +// If you are hit: how is it handled. +// Return TRUE if the generic fall_impact should be called +// Return FALSE if you handled it yourself or if there's no effect from hitting you +/atom/proc/check_impact(var/atom/movable/falling_atom) + if(density && !(flags & ON_BORDER)) + return TRUE // By default all turfs are gonna let you hit them regardless of density. /turf/CheckFall(var/atom/movable/falling_atom) - return falling_atom.fall_impact(src) + return TRUE + +/turf/check_impact(var/atom/movable/falling_atom) + return TRUE // Obviously you can't really hit open space. /turf/simulated/open/CheckFall(var/atom/movable/falling_atom) - // Don't need to print this, the open space it falls into will print it for us! - // visible_message("\The [falling_atom] falls from above through \the [src]!", "You hear a whoosh of displaced air.") - return 0 + return FALSE + +/turf/simulated/open/check_impact(var/atom/movable/falling_atom) + return FALSE // We return 1 without calling fall_impact in order to provide a soft landing. So nice. // Note this really should never even get this far /obj/structure/stairs/CheckFall(var/atom/movable/falling_atom) - return 1 + return TRUE + +/obj/structure/stairs/check_impact(var/atom/movable/falling_atom) + return FALSE // Can't fall onto ghosts /mob/observer/dead/CheckFall() - return 0 + return FALSE + +/mob/observer/dead/check_impact() + return FALSE + // Called by CheckFall when we actually hit something. Various Vars will be described below // hit_atom is the thing we fall on diff --git a/code/modules/organs/internal/eyes.dm b/code/modules/organs/internal/eyes.dm index 4febf8f479..07d70bc6e2 100644 --- a/code/modules/organs/internal/eyes.dm +++ b/code/modules/organs/internal/eyes.dm @@ -7,6 +7,7 @@ organ_tag = O_EYES parent_organ = BP_HEAD var/list/eye_colour = list(0,0,0) + var/innate_flash_protection = FLASH_PROTECTION_NONE /obj/item/organ/internal/eyes/robotize() ..() @@ -86,3 +87,9 @@ if(prob(1)) owner.custom_pain("Your eyes are watering, making it harder to see clearly for a moment.",1) owner.eye_blurry += 10 + +/obj/item/organ/internal/eyes/proc/get_total_protection(var/flash_protection = FLASH_PROTECTION_NONE) + return (flash_protection + innate_flash_protection) + +/obj/item/organ/internal/eyes/proc/additional_flash_effects(var/intensity) + return -1 diff --git a/code/modules/organs/organ_external.dm b/code/modules/organs/organ_external.dm index b6ad2fff2c..8d084f9e0e 100644 --- a/code/modules/organs/organ_external.dm +++ b/code/modules/organs/organ_external.dm @@ -36,6 +36,7 @@ var/gendered_icon = 0 // Whether or not the icon state appends a gender. var/s_tone // Skin tone. var/list/s_col // skin colour + var/s_col_blend = ICON_ADD // How the skin colour is applied. var/list/h_col // hair colour var/body_hair // Icon blend for body hair if any. var/mob/living/applied_pressure diff --git a/code/modules/organs/organ_icon.dm b/code/modules/organs/organ_icon.dm index 3c986b16fd..170475a208 100644 --- a/code/modules/organs/organ_icon.dm +++ b/code/modules/organs/organ_icon.dm @@ -58,7 +58,7 @@ var/global/list/limb_icon_cache = list() if(owner.should_have_organ(O_EYES)) var/obj/item/organ/internal/eyes/eyes = owner.internal_organs_by_name[O_EYES] if(eye_icon) - var/icon/eyes_icon = new/icon('icons/mob/human_face.dmi', eye_icon) + var/icon/eyes_icon = new/icon(eye_icon_location, eye_icon) if(eyes) eyes_icon.Blend(rgb(eyes.eye_colour[1], eyes.eye_colour[2], eyes.eye_colour[3]), ICON_ADD) else @@ -178,9 +178,10 @@ var/global/list/limb_icon_cache = list() else applying.Blend(rgb(-s_tone, -s_tone, -s_tone), ICON_SUBTRACT) icon_cache_key += "_tone_[s_tone]" - else if(s_col && s_col.len >= 3) - applying.Blend(rgb(s_col[1], s_col[2], s_col[3]), ICON_ADD) - icon_cache_key += "_color_[s_col[1]]_[s_col[2]]_[s_col[3]]" + else + if(s_col && s_col.len >= 3) + applying.Blend(rgb(s_col[1], s_col[2], s_col[3]), s_col_blend) + icon_cache_key += "_color_[s_col[1]]_[s_col[2]]_[s_col[3]]_[s_col_blend]" // Translucency. if(nonsolid) applying += rgb(,,,180) // SO INTUITIVE TY BYOND diff --git a/code/modules/organs/subtypes/standard.dm b/code/modules/organs/subtypes/standard.dm index d1eb81cc7f..10123c9ea4 100644 --- a/code/modules/organs/subtypes/standard.dm +++ b/code/modules/organs/subtypes/standard.dm @@ -265,6 +265,8 @@ force = 3 throwforce = 7 + var/eye_icon_location = 'icons/mob/human_face.dmi' + /obj/item/organ/external/head/robotize(var/company, var/skip_prosthetics, var/keep_organs) return ..(company, skip_prosthetics, 1) diff --git a/icons/effects/fluidtracks.dmi b/icons/effects/fluidtracks.dmi index 58e8fae9349e87073044723261958f13e60496b1..58865d6234d2c6ee637fa8e19d7b40b35a678730 100644 GIT binary patch literal 7307 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 HunhkXK6COs literal 4141 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