From 0167ad4e80be11c96a13e9a8a83a64ed3b9c7b13 Mon Sep 17 00:00:00 2001 From: Alberyk Date: Sat, 1 Apr 2017 13:43:43 -0300 Subject: [PATCH] Adds snake people (#2029) Changes: -adds snake people as a whitelisted species -adds tail mechanics, ported from europa station, to the snake people, you can coil it up, people can attack it, stomp the tail and etc -snake people can spit poison -snake people can't use shoes -snake people also have some other features like having a minor brute resist, claws and cuff breaking -snake people can have different scale colors and eyes colors, but green is the best color without doubt -also adds snake people --- baystation12.dme | 2 + code/modules/mob/language/outsider.dm | 14 +- code/modules/mob/living/carbon/human/human.dm | 9 +- .../mob/living/carbon/human/human_movement.dm | 22 +- .../mob/living/carbon/human/human_organs.dm | 39 ++- .../mob/living/carbon/human/human_powers.dm | 34 +++ .../mob/living/carbon/human/human_tail.dm | 257 ++++++++++++++++++ .../carbon/human/species/outsider/snake.dm | 106 ++++++++ .../living/carbon/human/species/species.dm | 2 + .../carbon/human/species/species_hud.dm | 18 ++ .../mob/living/carbon/human/update_icons.dm | 11 +- code/modules/organs/subtypes/xenos.dm | 36 +++ code/modules/projectiles/projectile/energy.dm | 12 + html/changelogs/alberky-PR-snektiddies.yml | 7 + icons/mob/human_face.dmi | Bin 70398 -> 70460 bytes icons/mob/human_races/r_snake.dmi | Bin 0 -> 6390 bytes 16 files changed, 543 insertions(+), 26 deletions(-) create mode 100644 code/modules/mob/living/carbon/human/human_tail.dm create mode 100644 code/modules/mob/living/carbon/human/species/outsider/snake.dm create mode 100644 html/changelogs/alberky-PR-snektiddies.yml create mode 100644 icons/mob/human_races/r_snake.dmi 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 d7bc9ad8ba2a41995d7aaf48cf833bcf3dc0c01c..0588e1cba95020ba1ad093939774e58d817687d9 100644 GIT binary patch delta 7474 zcmYjVcU)6VvkpZ-kuD+~k@AW(3(|`UNJonFA|g^k5lCnP2MZ$7i=b3NdI{1(5eP3W z6d_WBKtP(5&?JUVa!>s3cYim3oSogB?9R?UJM-+43Ta!4XcI*M#t?HWn5K)rv!DAj zn7fZR1QL{$Z8P9Kt;A}Nc66$n{@GzTdOOqe-9;|@I+Rds0h=OQw@Fli-9CQ1BLKSJ zv*q;3kUA^x=^KK|E}n?Ski!p=q(8&~4=4S6-_yo6vTJSg553&eBj3|KZ}_5h7Umc* zE&7H>6U!~jGXV$5N0eJSfDJqD>1Vq@>!>bbaB#%ETvzWY>0Ufn;ktEwkl`0y?WNcj~_7URcE3sB03 z-uUrg1T;f|c1zn%$WPuvTj%?#gcdSc!n<`Y`SNXGn~?k*L#x~>UnGEF*8jfxV21!KGb!uSf%T$v3hgBp%c;dapK`;(2=j>1FVb|;~{N^zv|gP2wyi}M6mZp_g_rx5lKo^5eI}66V$EWND1#~0k?i`HLfPKO3yKt=qD=5(P=q=U;k?Evh6#+##BsL zoToJ&+(~SLK8@ze3oOsfq1l{Bz(-9-`764Hy40yAh58?F_=it%HrSixGcDJc2@KJa z_kgTFEgHU_LFf-Ow$406zkkCUSKkr*1NdHgQ_0MDd2iodskv&FHc+Rz>1CJj4a$|= z;#~!cwb2?|?SFkl+IQb?K;;lPgqQuA;Gqh`uC zF+B#e#yK}j_U+8+bNDl&``KZQd0$*@1~>^mW001i$f`Na_e<|HbW2g{i+RNrNzC3W zlSf&fb#t;_VH0#!z2@0Gm_>F%Ejnc`EoVe3kD6t;bX6{;?!?9Bzrv2kN(7iWNf z_P(yW2U+m{{Lt@F^ZD$r==h+Z@(o(G=Ms77z|67ttdEpGKamT<0H#JCB~;qPF4Lzsr3VIw)R(xFn!r%l)(Ci%$CZ z&Iy#0)GMb0^R(?y%y53x)){@FG2^f-0Dx&&CU)O%tH1jsS4__1Ok8~2-5%>2b|<|C z>^)*ofx9?&>%B@w8z=(a72QQkjcOv>82*6TTKT$m$7Cew1R5luP;69)E zLo7OVT(+gFt-$ju<0{9|Dm6DTCN)GLjd992B1yZgUXKiQ^vyVVDal)@p6=4-HHJc4TFA%()~hB4@};;$)|vqX{vE$( zAT(W4+S`;P_5zKeKD&>^t(Y;p4UACXT_-?Z9j{@^U{btp&h~lh(EdqopKn$)d5Dm8 z({)ycK`GF-~P)-zQg9%)oc?RLb-NM}-iUN<%} z@~{P7X)g6qf1cLYkE__PW`_%8;lHdp(AJ|+bIox4M6xpqj!zq(o`!DFjWsPsltIhF z%QAPC2u?3+i+B>E?R7MB3>0>~67=Dn)8PlC+@X93DZ2LKzP6;8~ZhGv{ zSWbXbkBNn4AdXkQE+=K_s-P<-r{0j3Y6xIvA-0AG1XzUcrsPTz3mrZG83?oPX(n!}Eftx&AAG0j^RwS)HAdT9DgTUaJ5ZG;7vwB45x_>WdH={OBx z)Qdn!L%qF?Y;0{S)lb)qEy}bW*n%<=d0YQ9tO0LqXxNt~Wy{VlgxnlTpLqy$|HH(@ zw70ZW;5pZ3|J2o$L;Xa7hJ~nzD=IE7EG+CcoyL*!%lh{`c)rP$m8IF8sh6u_mXea1 z810Cx9ya;W7WQ@Gk%pII7J@Xn>Ncfiiu*JnXo8-co%J5aVy8XyVv!wJ%`MC)hlEyZ zF>1B(e2S0p4vgI_$v!HE+wH)-7Vdpd@_KbT9LE#W++mFsaAjEsz=r{5q!5AOQml4fRRQk_kw zi62KsMjo%VaT{}aicGZ0e&IwoR>0Z};R}!*dJbrQMG>L%N#NTdIrXfONVPeD+`1C; zsfbZ|6`-Ypw4)~K7|PDwx!@|VbLY+raTJ>|vrtVT5<6lTJj_XAm4IBk*>vez&dxxH zkXW{YZ^!5v-BZ>Z2Ttc9kVjqe3Z`9ST;&`4I94@rGyCWtfo^jQc0ymkS?aTu~UfvLW?JR4XFB7fmiA4c>JdM zHhsM`1oFf4OYx8eps6{P!OTMzsPGU==#IJW`FebAZoA_F1R}eLMUm=T!;S;Ed3jS% zr<*=$T+Rx<3$MctcU>0i@!&`xzM{Jyo7&k)R$CcJ+c!Uf@rfJf)IHo>Sa|#~D=T$x zFTfZ<13-i(>7K7|v_@4~m51@w9T|BU(r=C|*qv3k{6|>$%D&i>Teqc(Gf`tUbLGvY zWBlcHRd&Z7_u%$x5XJD5#!6D-?@-8>WIVR&`4O?F{Aj4zA%{i=eO+E&oJBw-v8hQh z#f4)wY~Nm4UEPsVe$M8iM5l#?{BMJU(SX-8RYS%+yu9rFm|H%1 zG$4^v_Y(xwSZR?*)Mq zlil525rKpOv>@Oa7Z(R`ueG~@ajB=LXK67(9l7wkacOzEFF~F3Pg7G9drWshL4l_2 zW<~}N-d+BOWtBrc)Wf4Lo2CjM^ENg%_TYwFV+gTK^~Q|_qAY~=+au#_+%z%G-t>$0 z!f$SOsFGXy1<$mT6E9P+1;oumyCV9g1zgxeXYd?X;`_eI|NhmzR2R<=tD6 z+4z}ER?Y{Fif{hSQL;TF31T2^%p4~a7_}8sy2Zto6pulrSUOCJ^5+5#c`HAjK}>CI zB!Yv3lT%Wz5C{ZH4b16x8K^br;frCUli-2F$qSr~B}Za3A3&T;S5{Uc%PMC>{#AT4 z0Zdp-!&u?V@0xrX``*2K_arpb4lH8F%B!jxxGr8~pN~2xWDa?9gTZgYoGSL*or@PQ z)>Sc%Oing#9GZnmz9n8sdb`}fsu?Mmr1Sdf1BhMy{thQp;pl<(mZ45K3E34Fq@tG6 zxQ4;ZiBZt3aP*zX>+2fEsS*o7}gzI(g= zO3<>Md*kxUE9%#hLA>n}a=kWIs+j17wp;geM1^lCD_>I%+)9%-yj1-vmF zUeC`Qr4_gzDYzcY_c*}8z-M1yd(Zv<4t?pn_28* z^_<1A+Wfw@yRY|S8{pkEu@l4Df3S1~JTdwzYF7>qr$q#ElZGejKnLm3vU_6#c{WOG zK_#)fLaq$?L(xV)Ic-jgoh+Ohk-67k_4p_Ci<7I@zakk8oSgEk(nK*T1cL>7xpu>K zG4l7kdlRK*#gc4EX2nLT=@%^EE=V2lI9)vXDtk(n7!PA)Ee2Zx-N)4l07Wwa0K6q%i#F5cbU z4R&rEk)xI}PapgvGCn>YK=IAF$eg3Vn@!!9Z_jk^;mpj6aVLI7mt3qRZeZ~=cIO2N{WPuK= zq;ZjpOB9Td02mItkX}j8Sf4;3OtHF}cg#Cj+>#IjonsdgVW=1K#LVE)QG1I4y!-W{ zQX_hq39{@%pURq2nu3xNo$!NsdS`wiQBl$4tNcPr#qDovTMv-KE6p@O38b&-ANDBGLnO$})3eS{!6?ea`N4|ng>%t7oxQzp{}74pXVvqE zOF%)?TWx=PPG(&NjNWT_e2UwxG8fgfr6Y+%_F%qrH99I)AV=Hv;M00};=M?LqE411 zw7n3x*e!@}tf@#S_>3@afa1Pdq&Iz-TrT zWK%I3B^bVX1kmwgngrI0LOx_rCwn))Ds^Wq|G1TZMaX(hvDV8bHiOi0Bxw9(R7r@! zWx&gn58&?Ze%-HSaBwg!!)86IUOgEOgNaN)hOeK|#;vM)6|>pn1WVVIp(Xq92}`GY zj*hvnJLfeI^1MSWr}BEFhO)g84y_pscRJ>uEok>{#$SMsuAEo7o0m8?HfFW1Z53t^ zY2rN_TUe$pD=Rer%&KGl%lY%?-70fc;=yw!1E5Z7G(Qh(x+bsTt2!FpZb`_XPW%>H zJ}7y!>Cz?&C)~Sazo_$2eVn+zpi<)QmZZ})Nwj3VZfR-B+eFfH_eB&;FA%}J?1^La zEgNDXT4<)J+-_nmu}L*I)CyiMwD0jKC*C*Taq>Hgo3=Zv@fY--KWem!ii+w20icA} z4>j}QxX&k?z>CGS#;?pUAb=31@M~V{^0v(x zC8ms~tIC}IEyDhhhlfW6On!1*8@v`Y8sq*XFTOI4!5BOWAd^IM zvGAF9HU~F2IbtH(f{)(F@r8j@taCWZ)F$iN5W1*1@2v{+-C32eP5=xvP6dnwm200= z{d_1*S3bXD>3$C>C@t*^0aH}P{v0ruk)8d1Xvk<&TpO2oyxGI|G&L_XQ94j(|VI_a}-BGwmG7oonv$cf}?; zR=2i{f^Vj6EdomG%EfIZyaFjuNQq5Zo4-Lb6gVYf-D$p5p-47?!M+o#tE*qK9BB;3 zERT+kL@t~YeJL)3AcL-YefKhWsBS*Yz;BljPY9TS_V$RkLH?)obaYFHE4lI^Gi%<> zhPc>rrM20RZZIcMbK&3yzFthM%ityIg`(n4)gpjSupMfMa4+h7Yrv3q!zuQo9(s(u z!KLGpe13KHuu(lh56lDA_S|DkpZw27A^ZpK($h6_wq&+(8KuqR>(iu_<` zkW*JjZ8$686$LRE46kD-hw&|WdE371Cb25O#Khz$YPL9;4?=sDwf8n+gPH~;^Q+0% zR8Ak)=Q8Sr$#_(D$$*(-|4nWWb;TJfhQDl|K?Pgf{#Sw=Fr}l|sCZK}NEQ^{F9giK zp-u$JDAJbZ2g8a2EWXd2|F7mx1k)LzIc9Y~C?E9Kd7*%)01<(0=FRUGW~zaznP|8rwKHQ9fMT8uVM9vK;V;ZgF9` zFW67{56CY~H3tV+SQ=rTL z-cYNf|GJyKn5yK7`6Jl;&wmYa~gjo z$c;PNMlYyOS_Dx>_1uUgG@gsxA4RZiMy{=Gme-W;F+DuH9Vt+}|QA111 zMkEkh{fj&>EjshdsPO730J!P9U@$IEmZ~9dmy_kgD_JP2-j^X%loIj+ec{qyfnr#M zu@}rR+JL-Ylrt(7F2GCapk{4vxZOPIT10+shFQC)p1?$DrDFJL8<|c%(gN*BgK_?$ z%(>7tl3ahEWCo5<$H3|Lx|! zHl8YE2Dkom0qpCQghfDl6~HZ{I7T&-lrPIf2SO0i%SS5&_EX*jKKuf#a7c(eL^#bBI-Dt7$iUx;xHIfaQC zWtHA&y;Nq22A`TBk4_mhBF>vp_Nxi6v%B~SyI=NHDV&G0#)v;xz&W3M$f@ZrR$~qc ztMVvXEUu;)qm#h=UqrytHQ1;mffhsjX;l4ky|R|(=?_A#`W$R_Ew=F%5(Y2NB9aIz zcUH28d|(sA7#Lb60rM}-J!hTZ&E?-j@?HN;)zWpnv=+LYfRo2R<#y_zGIR(k>>O&v z@q8GQo;)4S@%c$t=Qb*CVSZl#t&rAA2NV1{R}jJkPH|HBE2E)n5o)!4X&@OJvE{t~ zc?lblM<(YuK1T;;3ci>A=8wPKurH0i}W*$R9FA|aP-$;U5eEl7fFJL^ASeB?#7*^DC=q&X(H}Edig(I C38*dr delta 7343 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 tAUEIQ(}xH*?&+zwR&5>vpri*Z`cU(t-T!