diff --git a/baystation12.dme b/baystation12.dme
index 9a13707055e..87a5fdaa9fc 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -1432,6 +1432,7 @@
#include "code\modules\mob\living\carbon\human\human_organs.dm"
#include "code\modules\mob\living\carbon\human\human_powers.dm"
#include "code\modules\mob\living\carbon\human\human_species.dm"
+#include "code\modules\mob\living\carbon\human\human_tail.dm"
#include "code\modules\mob\living\carbon\human\intoxication.dm"
#include "code\modules\mob\living\carbon\human\inventory.dm"
#include "code\modules\mob\living\carbon\human\life.dm"
@@ -1449,6 +1450,7 @@
#include "code\modules\mob\living\carbon\human\species\species_hud.dm"
#include "code\modules\mob\living\carbon\human\species\outsider\shadow.dm"
#include "code\modules\mob\living\carbon\human\species\outsider\skeleton.dm"
+#include "code\modules\mob\living\carbon\human\species\outsider\snake.dm"
#include "code\modules\mob\living\carbon\human\species\outsider\vox.dm"
#include "code\modules\mob\living\carbon\human\species\station\golem.dm"
#include "code\modules\mob\living\carbon\human\species\station\human_subspecies.dm"
diff --git a/code/modules/mob/language/outsider.dm b/code/modules/mob/language/outsider.dm
index f35d1a841e2..15ed54d5f51 100644
--- a/code/modules/mob/language/outsider.dm
+++ b/code/modules/mob/language/outsider.dm
@@ -130,4 +130,16 @@
space_chance = 10
/datum/language/terminator/get_random_name()
- return "HK [pick(list("Hera","Zeus","Artemis","Athena","Ares","Hades","Poseidon","Demeter","Apollo","Aphrodite","Hermes","Hestia","Dionysus","Persephone","Kronos","Odysseus","Ajax","Agamemnon","Chiron","Charon"))]-[rand(100, 999)]"
\ No newline at end of file
+ return "HK [pick(list("Hera","Zeus","Artemis","Athena","Ares","Hades","Poseidon","Demeter","Apollo","Aphrodite","Hermes","Hestia","Dionysus","Persephone","Kronos","Odysseus","Ajax","Agamemnon","Chiron","Charon"))]-[rand(100, 999)]"
+
+/datum/language/snake
+ name = "Snake Language"
+ desc = "The tongue of the snake people, a lot of hissing."
+ speech_verb = "hisses"
+ ask_verb = "hisses"
+ exclaim_verb = "hisses"
+ colour = "vox"
+ key = "&"
+ flags = RESTRICTED
+ space_chance = 100
+ syllables = list("ssssss", "Hisss", "hSSSSS", "SSSSSSSSSSSSSS", "his!", "ssssSSSssss", "Sssssshshshshs", "S", "Hisss")
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index e9fa53899a0..f0c934df3bc 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -78,6 +78,8 @@
pixel_y = species.icon_y_offset
/mob/living/carbon/human/Destroy()
+ qdel(tail_trail)
+ tail_trail = null
human_mob_list -= src
for(var/organ in organs)
qdel(organ)
@@ -87,7 +89,7 @@
organs_by_name = null
bad_internal_organs = null
bad_external_organs = null
-
+
QDEL_NULL(DS)
// qdel and null out our equipment.
QDEL_NULL(shoes)
@@ -1210,6 +1212,11 @@
nutrition = (rand(25,100)*0.01)*max_nutrition//Starting nutrition is randomised between 25-100% of max
nutrition_loss = HUNGER_FACTOR * species.nutrition_loss_factor
+
+ if(species.tail_stance)
+ if(!tail_trail) tail_trail = new(src)
+ tail_trail.sync_to_owner()
+
if(species)
return 1
else
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index 23d09be1e95..245757d8221 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -40,18 +40,26 @@
if(shoes)
tally += shoes.slowdown
- for(var/organ_name in list("l_foot","r_foot","l_leg","r_leg"))
- var/obj/item/organ/external/E = get_organ(organ_name)
- if(!E || E.is_stump())
- tally += 4
- else if(E.status & ORGAN_SPLINTED)
- tally += 0.5
- else if(E.status & ORGAN_BROKEN)
+ if(species.tail_stance)
+ // If your groin is missing outright, you are dead, so, whatever. No point tallying stump etc. for it.
+ var/obj/item/organ/external/E = get_organ("groin")
+ if(E.status & ORGAN_BROKEN)
tally += 1.5
+ else
+ for(var/organ_name in list("l_foot","r_foot","l_leg","r_leg"))
+ var/obj/item/organ/external/E = get_organ(organ_name)
+ if(!E || E.is_stump())
+ tally += 4
+ else if(E.status & ORGAN_SPLINTED)
+ tally += 0.5
+ else if(E.status & ORGAN_BROKEN)
+ tally += 1.5
+
if(shock_stage >= 10) tally += 3
+
if(aiming && aiming.aiming_at) tally += 5 // Iron sights make you slower, it's a well-known fact.
if (drowsyness) tally += 6
diff --git a/code/modules/mob/living/carbon/human/human_organs.dm b/code/modules/mob/living/carbon/human/human_organs.dm
index 172927cc474..3eb1777473d 100644
--- a/code/modules/mob/living/carbon/human/human_organs.dm
+++ b/code/modules/mob/living/carbon/human/human_organs.dm
@@ -74,19 +74,32 @@
return
for(var/limb_tag in list("l_leg","r_leg","l_foot","r_foot"))
- var/obj/item/organ/external/E = organs_by_name[limb_tag]
- if(!E || (E.status & (ORGAN_MUTATED|ORGAN_DEAD)) || E.is_stump()) //should just be !E.is_usable() here but dislocation screws that up.
- stance_damage += 2 // let it fail even if just foot&leg
- else if (E.is_malfunctioning())
- //malfunctioning only happens intermittently so treat it as a missing limb when it procs
- stance_damage += 2
- if(prob(10))
- visible_message("\The [src]'s [E.name] [pick("twitches", "shudders")] and sparks!")
- spark(src, 5)
- else if (E.is_broken() || !E.is_usable())
- stance_damage += 1
- else if (E.is_dislocated())
- stance_damage += 0.5
+
+ if(species.tail_stance)
+ var/obj/item/organ/external/groin/checkgroin = organs_by_name["groin"]
+ if(!istype(checkgroin))
+ stance_damage = 4
+ else
+ if (checkgroin.is_broken() || !checkgroin.is_usable() || ((checkgroin.status & ORGAN_ROBOT) && checkgroin.is_malfunctioning()))
+ stance_damage = 2
+ else if ((checkgroin.status & (ORGAN_MUTATED|ORGAN_DEAD)) || checkgroin.is_stump())
+ stance_damage = 4
+ else
+ for(var/limb_tag in list("l_leg","r_leg","l_foot","r_foot"))
+ var/obj/item/organ/external/E = organs_by_name[limb_tag]
+ if(!E || (E.status & (ORGAN_MUTATED|ORGAN_DEAD)) || E.is_stump()) //should just be !E.is_usable() here but dislocation screws that up.
+ stance_damage += 2 // let it fail even if just foot&leg
+ else if (E.is_malfunctioning())
+ //malfunctioning only happens intermittently so treat it as a missing limb when it procs
+ stance_damage += 2
+ if(prob(10))
+ visible_message("\The [src]'s [E.name] [pick("twitches", "shudders")] and sparks!")
+ spark(src, 5)
+ else if (E.is_broken() || !E.is_usable())
+ stance_damage += 1
+ else if (E.is_dislocated())
+ stance_damage += 0.5
+
// Canes and crutches help you stand (if the latter is ever added)
// One cane mitigates a broken leg+foot, or a missing foot.
diff --git a/code/modules/mob/living/carbon/human/human_powers.dm b/code/modules/mob/living/carbon/human/human_powers.dm
index b8b2d64e8ad..cf65181de82 100644
--- a/code/modules/mob/living/carbon/human/human_powers.dm
+++ b/code/modules/mob/living/carbon/human/human_powers.dm
@@ -460,3 +460,37 @@
var/obj/item/weapon/arrow/quill/A = new /obj/item/weapon/arrow/quill(usr.loc)
A.throw_at(target, 10, 30, user)
msg_admin_attack("[key_name_admin(src)] launched a quill at [key_name_admin(target)] (JMP)")
+
+/mob/living/carbon/human/proc/venomspit(mob/target as mob in oview())
+ set name = "Spit Venom"
+ set desc = "Spits venom at your target."
+ set category = "Abilities"
+
+ if(last_special > world.time)
+ return
+
+ if(stat || paralysis || stunned || weakened || lying || restrained() || buckled)
+ src << "You cannot spit venom in your current state."
+ return
+
+ visible_message("[src] spits venom at [target]!", "You spit venom at [target].")
+
+ var/turf/T = loc
+ var/turf/U = (istype(target, /atom/movable) ? target.loc : target)
+
+ if(!U || !T)
+ return
+ while(U && !istype(U,/turf))
+ U = U.loc
+ if(!istype(T, /turf))
+ return
+ if (U == T)
+ usr.bullet_act(new /obj/item/projectile/energy/venom(usr.loc), get_organ_target())
+ return
+ if(!istype(U, /turf))
+ return
+
+ last_special = world.time + 25
+
+ var/obj/item/projectile/energy/venom/A = new /obj/item/projectile/energy/venom(usr.loc)
+ A.launch(target, get_organ_target())
diff --git a/code/modules/mob/living/carbon/human/human_tail.dm b/code/modules/mob/living/carbon/human/human_tail.dm
new file mode 100644
index 00000000000..6db2c65206b
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/human_tail.dm
@@ -0,0 +1,257 @@
+var/list/human_tail_cache = list()
+
+/mob/living/carbon/human/proc/coil_up()
+ set name = "Coil Up"
+ set category = "Abilities"
+ set desc = "Retract your tail around yourself."
+ if(stat || paralysis || !tail_trail)
+ return
+ src.visible_message("\The [src] coils up!")
+ tail_trail.update_moved(src)
+ sleep(-1)
+ if(istype(src.loc, /turf))
+ tail_trail.update_moved(src.loc)
+
+/mob/living/carbon/human
+ var/obj/effect/tail/underlay/tail_trail // Tail ref for critters that drag tails behind them.
+
+/obj/effect/tail
+ icon = null
+ icon_state = ""
+ simulated = 0
+ anchored = 1
+ density = 0
+ opacity = 0
+ w_class = 5
+
+ var/base_colour = "#000000"
+ var/markings_colour = "#000000"
+ var/mob/living/carbon/human/owner
+ var/moved_from
+ var/tail_type
+
+/obj/effect/tail/Crossed(var/atom/movable/crossing)
+ . = ..()
+ if(crossing == owner || loc == owner.loc || prob(30))
+ return .
+ if(istype(crossing, /mob/living))
+ var/mob/living/M = crossing
+ var/damage_taken
+ if(istype(crossing, /mob/living/carbon/human))
+ var/mob/living/carbon/human/H = crossing
+ if(istype(H.shoes, /obj/item/clothing/shoes/magboots) && (H.shoes.item_flags & NOSLIP))
+ damage_taken = rand(5,8)
+ if(!damage_taken)
+ damage_taken = rand(1,3)
+ if(prob(50))
+ M.visible_message("\The [M] trips over \the [owner]'s tail!")
+ M.Weaken(rand(5,8))
+ else
+ M.visible_message("\The [M] treads on \the [owner]'s tail!")
+ owner << "Your tail is being brutalized!"
+ owner.apply_damage(damage_taken, BRUTE, "groin")
+ else if(istype(crossing, /obj/vehicle) || istype(crossing, /obj/mecha))
+ crossing.visible_message("\The [crossing] runs over \the [owner]'s tail!")
+ owner << "Your tail is being brutalized!"
+ owner.apply_damage(rand(5,8), BRUTE, "groin")
+ return .
+
+/obj/effect/tail/Destroy()
+ owner = null
+ return ..()
+
+/obj/effect/tail/underlay/Destroy()
+ for(var/obj/effect/tail/segment in segments)
+ qdel(segment)
+ segments.Cut()
+ return ..()
+
+/obj/effect/tail/New(var/mob/living/carbon/human/newowner)
+ owner = newowner
+ if(!istype(owner))
+ owner = null
+ qdel(src)
+ return
+ ..(get_turf(owner))
+
+/obj/effect/tail/proc/sync_to_owner()
+ name = "[owner]'\s [initial(name)]"
+ base_colour = rgb(owner.r_skin, owner.g_skin, owner.b_skin)
+ markings_colour = rgb(owner.r_hair, owner.g_hair, owner.b_hair)
+ update_icon(1)
+
+/obj/effect/tail/attack_generic(var/mob/user, var/damage, var/attack_message)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ if(owner.loc == loc) return
+ return owner.attack_generic(user, damage, attack_message)
+
+/obj/effect/tail/attackby(var/obj/item/thing, var/mob/user)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ if(owner.loc == loc) return
+ return owner.attack_hand(user, thing)
+
+/obj/effect/tail/attack_hand(var/mob/user)
+ user.setClickCooldown(DEFAULT_ATTACK_COOLDOWN)
+ if(owner.loc == loc) return
+ return owner.attack_hand(user)
+
+/obj/effect/tail/bullet_act(var/obj/item/projectile/proj)
+ if(owner.loc == loc) return
+ return owner.bullet_act(proj)
+
+/obj/effect/tail/fire_act(var/datum/gas_mixture/air, var/exposed_temperature, var/exposed_volume)
+ if(owner.loc == loc) return
+ return owner.fire_act(air, exposed_temperature, exposed_volume)
+
+/obj/effect/tail/ex_act(var/severity)
+ if(owner.loc == loc) return
+ return owner.ex_act(severity) // Damage to the tail should damage the mob in general.
+
+/obj/effect/tail/emp_act(var/severity)
+ if(loc == owner.loc)
+ return
+ return owner.ex_act(severity)
+
+/obj/effect/tail/update_icon(var/force_override)
+ overlays.Cut()
+ // Build base.
+ var/cache_key = "[owner.species.name]-[tail_type]-[base_colour]"
+ if(!human_tail_cache[cache_key] || force_override)
+ var/icon/I = icon(icon=owner.species.icobase, icon_state=tail_type)
+ I.Blend(base_colour, ICON_ADD)
+ human_tail_cache[cache_key] = I
+ overlays += human_tail_cache[cache_key]
+ // Build markings.
+ cache_key = "[owner.species.name]-[tail_type]_markings-[markings_colour]"
+ if(!human_tail_cache[cache_key] || force_override)
+ var/icon/I = icon(icon=owner.species.icobase, icon_state="[tail_type]_markings")
+ I.Blend(markings_colour, ICON_ADD)
+ human_tail_cache[cache_key] = I
+ overlays += human_tail_cache[cache_key]
+
+/obj/effect/tail/underlay
+ name = "coils"
+ var/list/segments = list()
+
+/obj/effect/tail/underlay/New()
+ ..()
+ layer = owner.layer - 0.01
+ if(owner && segments)
+ for(var/i = 1 to owner.species.tail_length)
+ var/obj/effect/tail/trailing/segment= new (owner, (i == owner.species.tail_length))
+ segment.layer = (owner.layer - (0.01 * (i+1)))
+ segments += segment
+
+/obj/effect/tail/underlay/sync_to_owner()
+ ..()
+ for(var/obj/effect/tail/trailing/segment in segments)
+ segment.owner = owner
+ segment.sync_to_owner()
+
+/obj/effect/tail/trailing
+ name = "tail"
+ var/terminating
+ animate_movement = SYNC_STEPS
+
+/obj/effect/tail/trailing/New(var/newloc, var/term)
+ terminating = term
+ ..(newloc)
+
+/obj/effect/tail/trailing/update_icon()
+ if(reverse_dir[moved_from] == dir)
+ tail_type = "tail_straight[terminating ? "_end" : ""]"
+ else
+ // Way, way too tired to work this shit out.
+ // TODO bug GN or Psi about bit magic.
+ if(dir == NORTH)
+ if(moved_from == WEST)
+ tail_type = "tail_bent_1[terminating ? "_end" : ""]"
+ else if(moved_from == EAST)
+ tail_type = "tail_bent_2[terminating ? "_end" : ""]"
+ else if(dir == SOUTH)
+ if(moved_from == WEST)
+ tail_type = "tail_bent_1[terminating ? "_end" : ""]"
+ else if(moved_from == EAST)
+ tail_type = "tail_bent_2[terminating ? "_end" : ""]"
+ else if(dir == EAST)
+ if(moved_from == NORTH)
+ tail_type = "tail_bent_1[terminating ? "_end" : ""]"
+ else if(moved_from == SOUTH)
+ tail_type = "tail_bent_2[terminating ? "_end" : ""]"
+ else if(dir == WEST)
+ if(moved_from == NORTH)
+ tail_type = "tail_bent_1[terminating ? "_end" : ""]"
+ else if(moved_from == SOUTH)
+ tail_type = "tail_bent_2[terminating ? "_end" : ""]"
+ ..()
+
+/obj/effect/tail/proc/update_moved(var/turf/newloc, var/turf/predecessor)
+ if(!newloc || newloc == owner || !istype(owner.loc, /turf))
+ forceMove(owner)
+ return
+ if(src.loc == owner)
+ forceMove(get_turf(owner))
+ var/turf/oldloc = src.loc
+ if(!istype(oldloc))
+ forceMove(owner)
+ return
+ if(newloc == oldloc)
+ return
+ forceMove(newloc)
+ if(predecessor)
+ moved_from = get_dir(newloc, oldloc)
+ dir = get_dir(newloc, predecessor)
+ if(dir in cornerdirs)
+ forceMove(owner)
+ return
+ update_icon()
+
+/obj/effect/tail/trailing/update_moved(var/turf/newloc, var/turf/predecessor)
+ ..(newloc, predecessor)
+ if(loc == owner || owner.loc == loc)
+ invisibility = 101
+ else
+ invisibility = 0
+
+/obj/effect/tail/underlay/update_moved(var/turf/newloc, var/turf/predecessor)
+ var/oldloc = src.loc
+ ..(newloc, predecessor)
+ if(loc == owner)
+ for(var/obj/effect/tail/T in segments)
+ T.update_moved(owner)
+ else
+ var/obj/effect/lastsegment = src
+ for(var/obj/effect/tail/segment in segments)
+ var/segmentloc = segment.loc
+ segment.update_moved(oldloc, get_turf(lastsegment))
+ oldloc = segmentloc
+ lastsegment = segment
+
+ // Do this here instead of update_icon() because we have to wait for all the other segments to have moved.
+ dir = owner.dir
+ tail_type = "tail_underlay"
+ for(var/obj/effect/tail/trailing/segment in segments)
+ if(get_turf(segment) != newloc)
+ return
+ tail_type = "tail_underlay_static"
+ update_icon()
+
+/mob/living/carbon/human/Move()
+ . = ..()
+ if(. && species.tail_stance)
+ if(!tail_trail) tail_trail = new(src)
+ var/turf/T = get_turf(src)
+ if(istype(T))
+ tail_trail.update_moved(T)
+ else
+ tail_trail.update_moved(src)
+
+/mob/living/carbon/human/forceMove()
+ . = ..()
+ if(species.tail_stance)
+ if(!tail_trail) tail_trail = new(src)
+ var/turf/T = get_turf(src)
+ if(istype(T))
+ tail_trail.update_moved(T)
+ else
+ tail_trail.update_moved(src)
diff --git a/code/modules/mob/living/carbon/human/species/outsider/snake.dm b/code/modules/mob/living/carbon/human/species/outsider/snake.dm
new file mode 100644
index 00000000000..c58ce9a36f3
--- /dev/null
+++ b/code/modules/mob/living/carbon/human/species/outsider/snake.dm
@@ -0,0 +1,106 @@
+/mob/living/carbon/human/snake/New(var/new_loc)
+ ..(new_loc, "Snake People")
+
+/datum/species/snake
+ name = "Snake People"
+ short_name = "snek"
+ name_plural = "Snake Folk"
+ bodytype = "Snake"
+ icobase = 'icons/mob/human_races/r_snake.dmi'
+ deform = 'icons/mob/human_races/r_snake.dmi'
+ blurb = "A race of snakelike sapients from somewhere beyond the frontier. They are a new threat, arriving in the galaxy as invaders."
+ eyes = "eyes_snake"
+ breakcuffs = list(MALE,FEMALE,NEUTER)
+
+ tail_stance = 1
+ tail_length = 5
+
+ mob_size = 15
+ gluttonous = 3
+ vision_flags = SEE_SELF | SEE_MOBS
+ rarity_value = 10
+ slowdown = -1 // Compensating for a total inability to wear shoes.
+
+ darksight = 8
+ brute_mod = 0.5
+ ethanol_resistance = 0.7
+
+ virus_immune = 1
+ siemens_coefficient = 0.5
+
+ cold_level_1 = 280
+ cold_level_2 = 220
+ cold_level_3 = 130
+ heat_level_1 = 420
+ heat_level_2 = 480
+ heat_level_3 = 1100
+ heat_discomfort_level = 295
+
+ heat_discomfort_strings = list(
+ "You feel soothingly warm.",
+ "You feel the heat sink into your bones.",
+ "You feel warm enough to take a nap."
+ )
+ cold_discomfort_level = 292
+ cold_discomfort_strings = list(
+ "You feel chilly.",
+ "You feel sluggish and cold.",
+ "Your scales bristle against the cold."
+ )
+
+ hud_type = /datum/hud_data/snake
+ unarmed_types = list(/datum/unarmed_attack/claws/strong, /datum/unarmed_attack/bite/sharp)
+
+ default_language = "Snake Language"
+ language = "Ceti Basic"
+ num_alternate_languages = 1
+
+ spawn_flags = CAN_JOIN | IS_WHITELISTED
+ appearance_flags = HAS_HAIR_COLOR | HAS_SKIN_COLOR | HAS_EYE_COLOR
+ flags = NO_SLIP
+
+ inherent_verbs = list(
+ /mob/living/proc/devour,
+ /mob/living/carbon/human/proc/regurgitate,
+ /mob/living/carbon/human/proc/venomspit,
+ /mob/living/carbon/human/proc/coil_up
+ )
+
+ flesh_color = "#006600"
+ blood_color = "#1D2CBF"
+ base_color = "#006666"
+
+ reagent_tag = IS_UNATHI
+
+ has_limbs = list(
+ "chest" = list("path" = /obj/item/organ/external/chest/snake),
+ "groin" = list("path" = /obj/item/organ/external/groin/snake),
+ "head" = list("path" = /obj/item/organ/external/head/snake),
+ "l_arm" = list("path" = /obj/item/organ/external/arm),
+ "r_arm" = list("path" = /obj/item/organ/external/arm/right),
+ "l_hand" = list("path" = /obj/item/organ/external/hand),
+ "r_hand" = list("path" = /obj/item/organ/external/hand/right)
+ )
+
+ has_organ = list(
+ "heart" = /obj/item/organ/heart,
+ "lungs" = /obj/item/organ/lungs,
+ "liver" = /obj/item/organ/liver,
+ "kidneys" = /obj/item/organ/kidneys,
+ "brain" = /obj/item/organ/brain,
+ "eyes" = /obj/item/organ/eyes,
+ "venom gland" = /obj/item/organ/venomgland
+ )
+
+ stamina = 120
+ sprint_speed_factor = 3
+ stamina_recovery = 1
+ sprint_cost_factor = 1
+
+/datum/species/snake
+ autohiss_basic_map = list(
+ "s" = list("ssss", "sssss", "ssssss"),
+ "c" = list("cksss", "ckssss", "cksssss"),
+ "k" = list("ksss", "kssss", "ksssss"),
+ "x" = list("ksss", "kssss", "ksssss")
+ )
diff --git a/code/modules/mob/living/carbon/human/species/species.dm b/code/modules/mob/living/carbon/human/species/species.dm
index 7a04d4e972e..6c955cdfa8b 100644
--- a/code/modules/mob/living/carbon/human/species/species.dm
+++ b/code/modules/mob/living/carbon/human/species/species.dm
@@ -32,6 +32,8 @@
var/base_color // Used by changelings. Should also be used for icon previes..
var/tail // Name of tail state in species effects icon file.
var/tail_animation // If set, the icon to obtain tail animation states from.
+ var/tail_stance
+ var/tail_length
var/tail_hair
var/race_key = 0 // Used for mob icon cache string.
var/icon/icon_template // Used for mob icon generation for non-32x32 species.
diff --git a/code/modules/mob/living/carbon/human/species/species_hud.dm b/code/modules/mob/living/carbon/human/species/species_hud.dm
index 13354058ebb..65137b6001f 100644
--- a/code/modules/mob/living/carbon/human/species/species_hud.dm
+++ b/code/modules/mob/living/carbon/human/species/species_hud.dm
@@ -73,3 +73,21 @@
"mask" = list("loc" = ui_shoes, "name" = "Mask", "slot" = slot_wear_mask, "state" = "mask", "toggle" = 1),
"back" = list("loc" = ui_sstore1, "name" = "Back", "slot" = slot_back, "state" = "back")
)
+
+/datum/hud_data/snake
+ gear = list(
+ "i_clothing" = list("loc" = ui_iclothing, "name" = "Uniform", "slot" = slot_w_uniform, "state" = "center", "toggle" = 1),
+ "o_clothing" = list("loc" = ui_oclothing, "name" = "Suit", "slot" = slot_wear_suit, "state" = "suit", "toggle" = 1),
+ "mask" = list("loc" = ui_mask, "name" = "Mask", "slot" = slot_wear_mask, "state" = "mask", "toggle" = 1),
+ "gloves" = list("loc" = ui_gloves, "name" = "Gloves", "slot" = slot_gloves, "state" = "gloves", "toggle" = 1),
+ "eyes" = list("loc" = ui_glasses, "name" = "Glasses", "slot" = slot_glasses, "state" = "glasses","toggle" = 1),
+ "l_ear" = list("loc" = ui_l_ear, "name" = "Left Ear", "slot" = slot_l_ear, "state" = "ears", "toggle" = 1),
+ "r_ear" = list("loc" = ui_r_ear, "name" = "Right Ear", "slot" = slot_r_ear, "state" = "ears", "toggle" = 1),
+ "head" = list("loc" = ui_head, "name" = "Hat", "slot" = slot_head, "state" = "hair", "toggle" = 1),
+ "suit storage" = list("loc" = ui_sstore1, "name" = "Suit Storage", "slot" = slot_s_store, "state" = "suitstore"),
+ "back" = list("loc" = ui_back, "name" = "Back", "slot" = slot_back, "state" = "back"),
+ "id" = list("loc" = ui_id, "name" = "ID", "slot" = slot_wear_id, "state" = "id"),
+ "storage1" = list("loc" = ui_storage1, "name" = "Left Pocket", "slot" = slot_l_store, "state" = "pocket"),
+ "storage2" = list("loc" = ui_storage2, "name" = "Right Pocket", "slot" = slot_r_store, "state" = "pocket"),
+ "belt" = list("loc" = ui_belt, "name" = "Belt", "slot" = slot_belt, "state" = "belt")
+ )
diff --git a/code/modules/mob/living/carbon/human/update_icons.dm b/code/modules/mob/living/carbon/human/update_icons.dm
index dd4c8932cd4..7468be9e678 100644
--- a/code/modules/mob/living/carbon/human/update_icons.dm
+++ b/code/modules/mob/living/carbon/human/update_icons.dm
@@ -150,6 +150,9 @@ Please contact me on #coderbus IRC. ~Carn x
update_hud() //TODO: remove the need for this
overlays.Cut()
+ if(species.tail_stance)
+ if(!tail_trail) tail_trail = new(src)
+ tail_trail.sync_to_owner()
if(cloaked)
icon = 'icons/mob/human.dmi'
@@ -393,12 +396,12 @@ var/global/list/damage_icon_parts = list()
var/col = species.get_light_color(h_style)
if (!col)
col = "#FFFFFF"
-
+
set_light(species.light_range, species.light_power, col, uv = 0, angle = LIGHT_WIDE)
-
+
else if (species.light_range)
- set_light(FALSE)
-
+ set_light(FALSE)
+
overlays_standing[HAIR_LAYER] = image(face_standing)
if(update_icons) update_icons()
diff --git a/code/modules/organs/subtypes/xenos.dm b/code/modules/organs/subtypes/xenos.dm
index c4c9bf35625..b0e1d532654 100644
--- a/code/modules/organs/subtypes/xenos.dm
+++ b/code/modules/organs/subtypes/xenos.dm
@@ -104,3 +104,39 @@
name = "skull"
dislocated = -1
vital = 0
+
+/obj/item/organ/external/groin/snake
+ name = "tail"
+ max_damage = 200
+ min_broken_damage = 160
+ body_hair = "markings"
+
+/obj/item/organ/external/head/snake
+ body_hair = "markings"
+
+/obj/item/organ/external/chest/snake
+ body_hair = "markings"
+
+/obj/item/organ/venomgland
+ name = "venom gland"
+ desc = "Oversized glands, filled with a venomous liquid."
+ parent_organ = "head"
+ icon = 'icons/effects/blood.dmi'
+ icon_state = "xgibtorso"
+ organ_tag = "venom gland"
+
+/obj/item/organ/venomgland/process()
+ if (is_broken())
+ owner.verbs -= /mob/living/carbon/human/proc/venomspit
+ owner << " Your fangs dries as the glands are crushed by the violence!"
+ ..()
+
+/obj/item/organ/venomgland/replaced(var/mob/living/carbon/human/target)
+ owner.verbs += /mob/living/carbon/human/proc/venomspit
+ owner << " Venom runs through your fangs!"
+ ..()
+
+/obj/item/organ/venomgland/removed(var/mob/living/carbon/human/target)
+ target.verbs -= /mob/living/carbon/human/proc/venomspit
+ target << "Your fangs dries as the glands are torn away from you."
+ ..()
diff --git a/code/modules/projectiles/projectile/energy.dm b/code/modules/projectiles/projectile/energy.dm
index d7893391d9f..1a3b7699669 100644
--- a/code/modules/projectiles/projectile/energy.dm
+++ b/code/modules/projectiles/projectile/energy.dm
@@ -232,3 +232,15 @@
kill_count = 75
embed = 0
incinerate = 10*/
+
+/obj/item/projectile/energy/venom
+ name = "venom spit"
+ icon_state = "neurotoxin"
+ damage = 10
+ damage_type = TOX
+
+/obj/item/projectile/energy/venom/on_hit(var/atom/target, var/blocked = 0)
+ if(ishuman(target))
+ var/mob/living/carbon/human/M = target
+ if(M.reagents) M.reagents.add_reagent("toxin", 5)
+ ..()
diff --git a/html/changelogs/alberky-PR-snektiddies.yml b/html/changelogs/alberky-PR-snektiddies.yml
new file mode 100644
index 00000000000..0c478ce2ed8
--- /dev/null
+++ b/html/changelogs/alberky-PR-snektiddies.yml
@@ -0,0 +1,7 @@
+author: Alberyk
+
+delete-after: True
+
+changes:
+ - rscadd: "Added snake people."
+ - rscdel: "Removed resomi."
diff --git a/icons/mob/human_face.dmi b/icons/mob/human_face.dmi
index d7bc9ad8ba2..0588e1cba95 100644
Binary files a/icons/mob/human_face.dmi and b/icons/mob/human_face.dmi differ
diff --git a/icons/mob/human_races/r_snake.dmi b/icons/mob/human_races/r_snake.dmi
new file mode 100644
index 00000000000..8d085c43d71
Binary files /dev/null and b/icons/mob/human_races/r_snake.dmi differ